diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..491cb0c1 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,43 @@ +# Ignore build artifacts +target/ +dist/ +build/ +*.log +web/native/vibetunnel +web/native/*.node +web/native/spawn-helper +web/public/bundle/ + +# Ignore node_modules - CRITICAL for cross-platform builds +node_modules/ +**/node_modules/ +web/node_modules/ +tauri/node_modules/ +mac/node_modules/ +ios/node_modules/ + +# Ignore git +.git/ +.gitignore + +# Ignore macOS files +.DS_Store + +# Ignore IDE files +.vscode/ +.idea/ +*.swp +*.swo + +# Ignore test files +*.test.* +*.spec.* + +# Ignore documentation +*.md +docs/ + +# Ignore cache directories +.cache/ +.npm/ +.pnpm-store/ \ No newline at end of file diff --git a/.github/workflows/build-tauri.yml b/.github/workflows/build-tauri.yml new file mode 100644 index 00000000..279da3ee --- /dev/null +++ b/.github/workflows/build-tauri.yml @@ -0,0 +1,174 @@ +name: Build Tauri App + +on: + push: + branches: [ main ] + paths: + - 'tauri/**' + - '.github/workflows/build-tauri.yml' + pull_request: + branches: [ main ] + paths: + - 'tauri/**' + - '.github/workflows/build-tauri.yml' + workflow_dispatch: + +jobs: + # Check if we should run the build + changes: + name: Detect changes + runs-on: ubuntu-latest + outputs: + tauri: ${{ steps.filter.outputs.tauri }} + steps: + - uses: actions/checkout@v4 + - uses: dorny/paths-filter@v3 + id: filter + with: + filters: | + tauri: + - 'tauri/**' + - '.github/workflows/build-tauri.yml' + + build: + needs: changes + if: ${{ needs.changes.outputs.tauri == 'true' || github.event_name == 'workflow_dispatch' }} + strategy: + fail-fast: false + matrix: + include: + - platform: 'macos-latest' + target: 'x86_64-apple-darwin' + - platform: 'macos-latest' + target: 'aarch64-apple-darwin' + - platform: 'blacksmith-8vcpu-ubuntu-2404-arm' + target: 'aarch64-unknown-linux-gnu' + - platform: 'windows-latest' + target: 'x86_64-pc-windows-msvc' + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v2 + with: + version: 8 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + cache: 'pnpm' + cache-dependency-path: web/pnpm-lock.yaml + + - name: Setup Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Install Python setuptools + run: pip install setuptools + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Setup Rust target + if: startsWith(matrix.platform, 'blacksmith') || startsWith(matrix.platform, 'ubuntu') + run: | + rustup target add ${{ matrix.target }} + # Set appropriate linker based on target + if [[ "${{ matrix.target }}" == "aarch64-unknown-linux-gnu" ]]; then + echo "CARGO_TARGET_AARCH64_UNKNOWN_LINUX_GNU_LINKER=gcc" >> $GITHUB_ENV + else + echo "CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=gcc" >> $GITHUB_ENV + fi + + - name: Rust cache + uses: swatinem/rust-cache@v2 + with: + workspaces: './tauri/src-tauri -> target' + + - name: Install dependencies (Linux) + if: startsWith(matrix.platform, 'blacksmith') || startsWith(matrix.platform, 'ubuntu') + run: | + sudo apt-get update + sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libayatana-appindicator3-dev librsvg2-dev libpam0g-dev + + - name: Install dependencies (Windows) + if: matrix.platform == 'windows-latest' + run: | + choco install llvm -y + + - name: Get pnpm store directory + shell: bash + run: | + echo "STORE_PATH=$(pnpm store path --silent)" >> $GITHUB_ENV + + - name: Setup pnpm cache + uses: ${{ startsWith(matrix.platform, 'blacksmith') && 'useblacksmith/cache@v5' || 'actions/cache@v3' }} + with: + path: ${{ env.STORE_PATH }} + key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-pnpm-store- + + - name: Install web dependencies + working-directory: ./web + run: | + # Install dependencies (without frozen lockfile due to config differences) + pnpm install + # Rebuild native modules for the target platform + pnpm rebuild + + - name: Build web assets + working-directory: ./web + run: pnpm run build + + - name: Cache tauri-cli + id: cache-tauri + uses: ${{ startsWith(matrix.platform, 'blacksmith') && 'useblacksmith/cache@v5' || 'actions/cache@v3' }} + with: + path: ~/.cargo/bin/cargo-tauri + key: ${{ runner.os }}-tauri-cli-2.0 + + - name: Install tauri-cli + if: steps.cache-tauri.outputs.cache-hit != 'true' + run: cargo install tauri-cli --version "^2.0.0" --locked + + - name: Build Tauri app + working-directory: ./tauri/src-tauri + run: cargo tauri build --target ${{ matrix.target }} --ci + + - name: Upload artifacts (macOS) + if: matrix.platform == 'macos-latest' + uses: actions/upload-artifact@v4 + with: + name: vibetunnel-${{ matrix.target }} + path: | + tauri/src-tauri/target/${{ matrix.target }}/release/bundle/dmg/*.dmg + tauri/src-tauri/target/${{ matrix.target }}/release/bundle/macos/*.app + if-no-files-found: error + + - name: Upload artifacts (Linux) + if: startsWith(matrix.platform, 'blacksmith') || startsWith(matrix.platform, 'ubuntu') + uses: actions/upload-artifact@v4 + with: + name: vibetunnel-${{ matrix.target }} + path: | + tauri/src-tauri/target/${{ matrix.target }}/release/bundle/deb/*.deb + tauri/src-tauri/target/${{ matrix.target }}/release/bundle/appimage/*.AppImage + if-no-files-found: error + + - name: Upload artifacts (Windows) + if: matrix.platform == 'windows-latest' + uses: actions/upload-artifact@v4 + with: + name: vibetunnel-${{ matrix.target }} + path: | + tauri/src-tauri/target/${{ matrix.target }}/release/bundle/msi/*.msi + tauri/src-tauri/target/${{ matrix.target }}/release/bundle/nsis/*.exe + if-no-files-found: error \ No newline at end of file diff --git a/mac/VibeTunnel/Core/Services/WindowTracking/WindowFocuser.swift b/mac/VibeTunnel/Core/Services/WindowTracking/WindowFocuser.swift index 320db043..1127c325 100644 --- a/mac/VibeTunnel/Core/Services/WindowTracking/WindowFocuser.swift +++ b/mac/VibeTunnel/Core/Services/WindowTracking/WindowFocuser.swift @@ -98,11 +98,16 @@ final class WindowFocuser { if let tabRef = windowInfo.tabReference { // Use stored tab reference to select the tab // The tabRef format is "tab id X of window id Y" + // Escape the tab reference to prevent injection + let escapedTabRef = tabRef.replacingOccurrences(of: "\"", with: "\\\"") + .replacingOccurrences(of: "\n", with: "\\n") + .replacingOccurrences(of: "\r", with: "\\r") + let script = """ tell application "Terminal" activate - set selected of \(tabRef) to true - set frontmost of window id \(windowInfo.windowID) to true + set selected of \(escapedTabRef) to true + set frontmost of window id \(AppleScriptSecurity.escapeNumber(windowInfo.windowID)) to true end tell """ @@ -121,7 +126,7 @@ final class WindowFocuser { activate set allWindows to windows repeat with w in allWindows - if id of w is \(windowInfo.windowID) then + if id of w is \(AppleScriptSecurity.escapeNumber(windowInfo.windowID)) then set frontmost of w to true exit repeat end if @@ -146,6 +151,11 @@ final class WindowFocuser { let sessionInfo = SessionMonitor.shared.sessions[windowInfo.sessionID] let workingDir = sessionInfo?.workingDir ?? "" let dirName = (workingDir as NSString).lastPathComponent + + // Escape all user-provided values to prevent injection + let escapedSessionID = AppleScriptSecurity.escapeString(windowInfo.sessionID) + let escapedDirName = AppleScriptSecurity.escapeString(dirName) + let escapedTabID = windowInfo.tabID.map { AppleScriptSecurity.escapeString($0) } ?? "" // Try to find and focus the tab with matching content let script = """ @@ -162,7 +172,7 @@ final class WindowFocuser { set sessionName to name of s -- Try to match by session content - if sessionName contains "\(windowInfo.sessionID)" or sessionName contains "\(dirName)" then + if sessionName contains "\(escapedSessionID)" or sessionName contains "\(escapedDirName)" then -- Found it! Select this tab and window select w select t @@ -174,9 +184,9 @@ final class WindowFocuser { end repeat -- If we have a window ID, at least focus that window - if "\(windowInfo.tabID ?? "")" is not "" then + if "\(escapedTabID)" is not "" then try - tell window id "\(windowInfo.tabID ?? "")" + tell window id "\(escapedTabID)" select end tell end try diff --git a/mac/VibeTunnel/Core/Utilities/AppleScriptSecurity.swift b/mac/VibeTunnel/Core/Utilities/AppleScriptSecurity.swift new file mode 100644 index 00000000..5a65cda7 --- /dev/null +++ b/mac/VibeTunnel/Core/Utilities/AppleScriptSecurity.swift @@ -0,0 +1,88 @@ +import Foundation +import CoreGraphics + +/// Security utilities for AppleScript execution +enum AppleScriptSecurity { + + /// Escapes a string for safe use in AppleScript + /// + /// This function properly escapes all special characters that could be used + /// for AppleScript injection attacks, including: + /// - Double quotes (") + /// - Backslashes (\) + /// - Newlines and carriage returns + /// - Tabs + /// - Other control characters + /// + /// - Parameter string: The string to escape + /// - Returns: The escaped string safe for use in AppleScript + static func escapeString(_ string: String) -> String { + var escaped = string + + // Order matters: escape backslashes first + escaped = escaped.replacingOccurrences(of: "\\", with: "\\\\") + escaped = escaped.replacingOccurrences(of: "\"", with: "\\\"") + escaped = escaped.replacingOccurrences(of: "\n", with: "\\n") + escaped = escaped.replacingOccurrences(of: "\r", with: "\\r") + escaped = escaped.replacingOccurrences(of: "\t", with: "\\t") + + // Remove any other control characters that could cause issues + let controlCharacterSet = CharacterSet.controlCharacters + escaped = escaped.components(separatedBy: controlCharacterSet) + .joined(separator: " ") + + return escaped + } + + /// Validates an identifier (like an application name) for safe use in AppleScript + /// + /// This function ensures the identifier only contains safe characters and + /// isn't trying to inject AppleScript commands. + /// + /// - Parameter identifier: The identifier to validate + /// - Returns: The validated identifier, or nil if invalid + static func validateIdentifier(_ identifier: String) -> String? { + // Allow alphanumeric, spaces, dots, hyphens, and underscores + let allowedCharacterSet = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789 .-_") + let identifierCharacterSet = CharacterSet(charactersIn: identifier) + + guard allowedCharacterSet.isSuperset(of: identifierCharacterSet) else { + return nil + } + + // Additional check: ensure it doesn't contain AppleScript keywords that could be dangerous + let dangerousKeywords = ["tell", "end", "do", "script", "run", "activate", "quit", "delete", "set", "get"] + let lowercased = identifier.lowercased() + for keyword in dangerousKeywords { + if lowercased.contains(keyword) { + return nil + } + } + + return identifier + } + + /// Escapes a numeric value for safe use in AppleScript + /// + /// - Parameter value: The numeric value + /// - Returns: The string representation of the number + static func escapeNumber(_ value: Int) -> String { + return String(value) + } + + /// Escapes a numeric value for safe use in AppleScript + /// + /// - Parameter value: The numeric value (UInt32/CGWindowID) + /// - Returns: The string representation of the number + static func escapeNumber(_ value: UInt32) -> String { + return String(value) + } + + /// Creates a safe AppleScript string literal + /// + /// - Parameter string: The string to make into a literal + /// - Returns: A properly quoted and escaped AppleScript string literal + static func createStringLiteral(_ string: String) -> String { + return "\"\(escapeString(string))\"" + } +} \ No newline at end of file diff --git a/mac/VibeTunnel/Presentation/Views/Settings/AdvancedSettingsView.swift b/mac/VibeTunnel/Presentation/Views/Settings/AdvancedSettingsView.swift index ad4ea874..fa2fd488 100644 --- a/mac/VibeTunnel/Presentation/Views/Settings/AdvancedSettingsView.swift +++ b/mac/VibeTunnel/Presentation/Views/Settings/AdvancedSettingsView.swift @@ -15,7 +15,7 @@ struct AdvancedSettingsView: View { @AppStorage("cleanupOnStartup") private var cleanupOnStartup = true @AppStorage("showInDock") - private var showInDock = false + private var showInDock = true @State private var cliInstaller = CLIInstaller() @State private var showingVtConflictAlert = false diff --git a/mac/VibeTunnel/VibeTunnelApp.swift b/mac/VibeTunnel/VibeTunnelApp.swift index d605cccf..d5db166f 100644 --- a/mac/VibeTunnel/VibeTunnelApp.swift +++ b/mac/VibeTunnel/VibeTunnelApp.swift @@ -159,6 +159,11 @@ final class AppDelegate: NSObject, NSApplicationDelegate, @preconcurrency UNUser } #endif + // Register default values + UserDefaults.standard.register(defaults: [ + "showInDock": true // Default to showing in dock + ]) + // Initialize Sparkle updater manager sparkleUpdaterManager = SparkleUpdaterManager.shared diff --git a/tauri/.cargo/config.toml b/tauri/.cargo/config.toml new file mode 100644 index 00000000..a1ac6f79 --- /dev/null +++ b/tauri/.cargo/config.toml @@ -0,0 +1,5 @@ +[env] +CARGO_UNSTABLE_EDITION2024 = "true" + +[build] +rustflags = ["-Z", "unstable-options"] \ No newline at end of file diff --git a/tauri/.dockerignore b/tauri/.dockerignore new file mode 100644 index 00000000..80eb9a44 --- /dev/null +++ b/tauri/.dockerignore @@ -0,0 +1,29 @@ +# Ignore node_modules to avoid platform conflicts +**/node_modules/ +**/dist/ +**/target/ +**/.next/ +**/.cache/ +**/.turbo/ +**/.git/ +**/.gitignore +**/Dockerfile* +**/.dockerignore +**/.DS_Store +**/npm-debug.log* +**/yarn-debug.log* +**/yarn-error.log* +**/pnpm-debug.log* + +# Ignore build artifacts +web/public/bundle/ +web/dist/ +mac/ +ios/ + +# Keep the source files we need +!web/src/ +!web/public/ +!web/package.json +!web/pnpm-lock.yaml +!tauri/ \ No newline at end of file diff --git a/tauri/Dockerfile.linux b/tauri/Dockerfile.linux new file mode 100644 index 00000000..6feb7f7f --- /dev/null +++ b/tauri/Dockerfile.linux @@ -0,0 +1,51 @@ +# Dockerfile for building Linux binaries +FROM rust:latest + +# Install required dependencies for Tauri +RUN apt-get update && apt-get install -y \ + libwebkit2gtk-4.0-dev \ + build-essential \ + curl \ + wget \ + libssl-dev \ + libgtk-3-dev \ + libayatana-appindicator3-dev \ + librsvg2-dev \ + libpam0g-dev \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs + +# Install pnpm +RUN npm install -g pnpm + +# Install nightly toolchain for edition2024 support +RUN rustup toolchain install nightly && \ + rustup default nightly + +# Set environment variable to enable edition2024 +ENV CARGO_UNSTABLE_EDITION2024=true + +# Install latest tauri-cli +RUN cargo install tauri-cli --locked + +# Set working directory +WORKDIR /app + +# Copy the entire project +COPY . . + +# Remove any existing node_modules to avoid platform conflicts +RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' + || true + +# Install web dependencies and rebuild native modules for Linux +WORKDIR /app/web +RUN pnpm install --frozen-lockfile && pnpm rebuild + +# Go back to main directory +WORKDIR /app + +# Build the Linux binary +CMD ["cargo", "tauri", "build"] \ No newline at end of file diff --git a/tauri/Dockerfile.windows b/tauri/Dockerfile.windows new file mode 100644 index 00000000..4fd40958 --- /dev/null +++ b/tauri/Dockerfile.windows @@ -0,0 +1,48 @@ +# Dockerfile for building Windows binaries +FROM rust:latest + +# Install Windows cross-compilation tools +RUN apt-get update && apt-get install -y \ + mingw-w64 \ + nsis \ + wine64 \ + && rm -rf /var/lib/apt/lists/* + +# Install Node.js +RUN curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ + && apt-get install -y nodejs + +# Install pnpm +RUN npm install -g pnpm + +# Install nightly toolchain for edition2024 support +RUN rustup toolchain install nightly && \ + rustup default nightly + +# Add Windows target +RUN rustup target add x86_64-pc-windows-gnu + +# Set environment variable to enable edition2024 +ENV CARGO_UNSTABLE_EDITION2024=true + +# Install latest tauri-cli +RUN cargo install tauri-cli --locked + +# Set working directory +WORKDIR /app + +# Copy the entire project +COPY . . + +# Remove any existing node_modules to avoid platform conflicts +RUN find . -name "node_modules" -type d -prune -exec rm -rf '{}' + || true + +# Install web dependencies and rebuild native modules for Windows target +WORKDIR /app/web +RUN pnpm install --frozen-lockfile && pnpm rebuild + +# Go back to main directory +WORKDIR /app + +# Build the Windows binary +CMD ["cargo", "tauri", "build", "--target", "x86_64-pc-windows-gnu"] \ No newline at end of file diff --git a/tauri/build-cross-platform.sh b/tauri/build-cross-platform.sh new file mode 100755 index 00000000..5e36f009 --- /dev/null +++ b/tauri/build-cross-platform.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Build script for cross-platform Tauri builds + +set -e + +echo "🚀 Building VibeTunnel for all platforms..." + +# Colors for output +GREEN='\033[0;32m' +BLUE='\033[0;34m' +RED='\033[0;31m' +NC='\033[0m' # No Color + +# Build for macOS (native) +echo -e "${BLUE}Building for macOS...${NC}" +cargo tauri build +echo -e "${GREEN}✅ macOS build complete${NC}" + +# Build for Linux using Docker +echo -e "${BLUE}Building for Linux...${NC}" +if command -v docker &> /dev/null; then + docker build -f Dockerfile.linux -t vibetunnel-linux-builder .. + docker run --rm -v "$(pwd)/..:/app" vibetunnel-linux-builder + echo -e "${GREEN}✅ Linux build complete${NC}" +else + echo -e "${RED}❌ Docker not found. Skipping Linux build.${NC}" +fi + +# Build for Windows using Docker +echo -e "${BLUE}Building for Windows...${NC}" +if command -v docker &> /dev/null; then + docker build -f Dockerfile.windows -t vibetunnel-windows-builder .. + docker run --rm -v "$(pwd)/..:/app" vibetunnel-windows-builder + echo -e "${GREEN}✅ Windows build complete${NC}" +else + echo -e "${RED}❌ Docker not found. Skipping Windows build.${NC}" +fi + +echo -e "${GREEN}🎉 All builds complete!${NC}" +echo "Build artifacts can be found in:" +echo " - macOS: src-tauri/target/release/bundle/" +echo " - Linux: src-tauri/target/x86_64-unknown-linux-gnu/release/bundle/" +echo " - Windows: src-tauri/target/x86_64-pc-windows-gnu/release/bundle/" \ No newline at end of file diff --git a/tauri/rust-toolchain.toml b/tauri/rust-toolchain.toml new file mode 100644 index 00000000..2797ca2a --- /dev/null +++ b/tauri/rust-toolchain.toml @@ -0,0 +1,4 @@ +[toolchain] +channel = "nightly" +components = ["rustfmt", "clippy"] +profile = "minimal" \ No newline at end of file diff --git a/tauri/src-tauri/.cargo/config.toml b/tauri/src-tauri/.cargo/config.toml index cbfb4196..a1788345 100644 --- a/tauri/src-tauri/.cargo/config.toml +++ b/tauri/src-tauri/.cargo/config.toml @@ -12,4 +12,27 @@ rustflags = [ "-A", "clippy::too_many_lines", "-A", "clippy::cargo_common_metadata", "-A", "clippy::multiple_crate_versions", -] \ No newline at end of file +] + +[target.x86_64-pc-windows-gnu] +linker = "x86_64-w64-mingw32-gcc" +ar = "x86_64-w64-mingw32-ar" + +[target.x86_64-pc-windows-gnu.env] +CC_x86_64_pc_windows_gnu = "x86_64-w64-mingw32-gcc" +CXX_x86_64_pc_windows_gnu = "x86_64-w64-mingw32-g++" +AR_x86_64_pc_windows_gnu = "x86_64-w64-mingw32-ar" + +[target.x86_64-unknown-linux-gnu] +linker = "x86_64-unknown-linux-gnu-gcc" +ar = "x86_64-unknown-linux-gnu-ar" + +[target.x86_64-unknown-linux-gnu.env] +CC_x86_64_unknown_linux_gnu = "x86_64-unknown-linux-gnu-gcc" +CXX_x86_64_unknown_linux_gnu = "x86_64-unknown-linux-gnu-g++" +AR_x86_64_unknown_linux_gnu = "x86_64-unknown-linux-gnu-ar" +PKG_CONFIG_ALLOW_CROSS = "1" +PKG_CONFIG_PATH = "/opt/homebrew/opt/x86_64-unknown-linux-gnu/lib/pkgconfig" + +[target.x86_64-unknown-linux-musl] +linker = "x86_64-linux-musl-gcc" \ No newline at end of file diff --git a/tauri/src-tauri/Cargo.toml b/tauri/src-tauri/Cargo.toml index 35bf025b..17049945 100644 --- a/tauri/src-tauri/Cargo.toml +++ b/tauri/src-tauri/Cargo.toml @@ -32,7 +32,6 @@ tauri-plugin-window-state = "2.2.3" serde = { version = "1", features = ["derive"] } serde_json = "1" tokio = { version = "1", features = ["full"] } -uuid = { version = "1", features = ["v4", "serde"] } chrono = { version = "0.4", features = ["serde"] } # Terminal handling @@ -59,6 +58,10 @@ toml = "0.8" # Utilities open = "5" +url = "2" +urlencoding = "2" +uuid = { version = "1", features = ["v4", "serde"] } +lazy_static = "1.5" # File system dirs = "6" @@ -99,6 +102,13 @@ windows = { version = "0.61", features = ["Win32_Foundation", "Win32_Security", [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] tauri-plugin-single-instance = "2.2.4" +[target.'cfg(target_os = "macos")'.dependencies] +cocoa = "0.26" +objc = "0.2" +core-graphics = "0.24" +core-foundation = "0.10" +libc = "0.2" + [profile.release] panic = "abort" codegen-units = 1 diff --git a/tauri/src-tauri/Info.plist b/tauri/src-tauri/Info.plist new file mode 100644 index 00000000..9cf41dc8 --- /dev/null +++ b/tauri/src-tauri/Info.plist @@ -0,0 +1,17 @@ + + + + + CFBundleURLTypes + + + CFBundleURLName + com.vibetunnel.app + CFBundleURLSchemes + + vibetunnel + + + + + \ No newline at end of file diff --git a/tauri/src-tauri/gen/schemas/acl-manifests.json b/tauri/src-tauri/gen/schemas/acl-manifests.json index c0f9ac58..b5e536a8 100644 --- a/tauri/src-tauri/gen/schemas/acl-manifests.json +++ b/tauri/src-tauri/gen/schemas/acl-manifests.json @@ -1 +1 @@ -{"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"]},"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-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-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-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-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-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-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-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-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-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-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-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-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},"fs":{"default_permission":{"identifier":"default","description":"This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n","permissions":["create-app-specific-dirs","read-app-specific-dirs-recursive","deny-default"]},"permissions":{"allow-copy-file":{"identifier":"allow-copy-file","description":"Enables the copy_file command without any pre-configured scope.","commands":{"allow":["copy_file"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-exists":{"identifier":"allow-exists","description":"Enables the exists command without any pre-configured scope.","commands":{"allow":["exists"],"deny":[]}},"allow-fstat":{"identifier":"allow-fstat","description":"Enables the fstat command without any pre-configured scope.","commands":{"allow":["fstat"],"deny":[]}},"allow-ftruncate":{"identifier":"allow-ftruncate","description":"Enables the ftruncate command without any pre-configured scope.","commands":{"allow":["ftruncate"],"deny":[]}},"allow-lstat":{"identifier":"allow-lstat","description":"Enables the lstat command without any pre-configured scope.","commands":{"allow":["lstat"],"deny":[]}},"allow-mkdir":{"identifier":"allow-mkdir","description":"Enables the mkdir command without any pre-configured scope.","commands":{"allow":["mkdir"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-read":{"identifier":"allow-read","description":"Enables the read command without any pre-configured scope.","commands":{"allow":["read"],"deny":[]}},"allow-read-dir":{"identifier":"allow-read-dir","description":"Enables the read_dir command without any pre-configured scope.","commands":{"allow":["read_dir"],"deny":[]}},"allow-read-file":{"identifier":"allow-read-file","description":"Enables the read_file command without any pre-configured scope.","commands":{"allow":["read_file"],"deny":[]}},"allow-read-text-file":{"identifier":"allow-read-text-file","description":"Enables the read_text_file command without any pre-configured scope.","commands":{"allow":["read_text_file"],"deny":[]}},"allow-read-text-file-lines":{"identifier":"allow-read-text-file-lines","description":"Enables the read_text_file_lines command without any pre-configured scope.","commands":{"allow":["read_text_file_lines","read_text_file_lines_next"],"deny":[]}},"allow-read-text-file-lines-next":{"identifier":"allow-read-text-file-lines-next","description":"Enables the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":["read_text_file_lines_next"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-rename":{"identifier":"allow-rename","description":"Enables the rename command without any pre-configured scope.","commands":{"allow":["rename"],"deny":[]}},"allow-seek":{"identifier":"allow-seek","description":"Enables the seek command without any pre-configured scope.","commands":{"allow":["seek"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"allow-stat":{"identifier":"allow-stat","description":"Enables the stat command without any pre-configured scope.","commands":{"allow":["stat"],"deny":[]}},"allow-truncate":{"identifier":"allow-truncate","description":"Enables the truncate command without any pre-configured scope.","commands":{"allow":["truncate"],"deny":[]}},"allow-unwatch":{"identifier":"allow-unwatch","description":"Enables the unwatch command without any pre-configured scope.","commands":{"allow":["unwatch"],"deny":[]}},"allow-watch":{"identifier":"allow-watch","description":"Enables the watch command without any pre-configured scope.","commands":{"allow":["watch"],"deny":[]}},"allow-write":{"identifier":"allow-write","description":"Enables the write command without any pre-configured scope.","commands":{"allow":["write"],"deny":[]}},"allow-write-file":{"identifier":"allow-write-file","description":"Enables the write_file command without any pre-configured scope.","commands":{"allow":["write_file","open","write"],"deny":[]}},"allow-write-text-file":{"identifier":"allow-write-text-file","description":"Enables the write_text_file command without any pre-configured scope.","commands":{"allow":["write_text_file"],"deny":[]}},"create-app-specific-dirs":{"identifier":"create-app-specific-dirs","description":"This permissions allows to create the application specific directories.\n","commands":{"allow":["mkdir","scope-app-index"],"deny":[]}},"deny-copy-file":{"identifier":"deny-copy-file","description":"Denies the copy_file command without any pre-configured scope.","commands":{"allow":[],"deny":["copy_file"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-exists":{"identifier":"deny-exists","description":"Denies the exists command without any pre-configured scope.","commands":{"allow":[],"deny":["exists"]}},"deny-fstat":{"identifier":"deny-fstat","description":"Denies the fstat command without any pre-configured scope.","commands":{"allow":[],"deny":["fstat"]}},"deny-ftruncate":{"identifier":"deny-ftruncate","description":"Denies the ftruncate command without any pre-configured scope.","commands":{"allow":[],"deny":["ftruncate"]}},"deny-lstat":{"identifier":"deny-lstat","description":"Denies the lstat command without any pre-configured scope.","commands":{"allow":[],"deny":["lstat"]}},"deny-mkdir":{"identifier":"deny-mkdir","description":"Denies the mkdir command without any pre-configured scope.","commands":{"allow":[],"deny":["mkdir"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-read":{"identifier":"deny-read","description":"Denies the read command without any pre-configured scope.","commands":{"allow":[],"deny":["read"]}},"deny-read-dir":{"identifier":"deny-read-dir","description":"Denies the read_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["read_dir"]}},"deny-read-file":{"identifier":"deny-read-file","description":"Denies the read_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_file"]}},"deny-read-text-file":{"identifier":"deny-read-text-file","description":"Denies the read_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file"]}},"deny-read-text-file-lines":{"identifier":"deny-read-text-file-lines","description":"Denies the read_text_file_lines command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines"]}},"deny-read-text-file-lines-next":{"identifier":"deny-read-text-file-lines-next","description":"Denies the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines_next"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-rename":{"identifier":"deny-rename","description":"Denies the rename command without any pre-configured scope.","commands":{"allow":[],"deny":["rename"]}},"deny-seek":{"identifier":"deny-seek","description":"Denies the seek command without any pre-configured scope.","commands":{"allow":[],"deny":["seek"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}},"deny-stat":{"identifier":"deny-stat","description":"Denies the stat command without any pre-configured scope.","commands":{"allow":[],"deny":["stat"]}},"deny-truncate":{"identifier":"deny-truncate","description":"Denies the truncate command without any pre-configured scope.","commands":{"allow":[],"deny":["truncate"]}},"deny-unwatch":{"identifier":"deny-unwatch","description":"Denies the unwatch command without any pre-configured scope.","commands":{"allow":[],"deny":["unwatch"]}},"deny-watch":{"identifier":"deny-watch","description":"Denies the watch command without any pre-configured scope.","commands":{"allow":[],"deny":["watch"]}},"deny-webview-data-linux":{"identifier":"deny-webview-data-linux","description":"This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-webview-data-windows":{"identifier":"deny-webview-data-windows","description":"This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-write":{"identifier":"deny-write","description":"Denies the write command without any pre-configured scope.","commands":{"allow":[],"deny":["write"]}},"deny-write-file":{"identifier":"deny-write-file","description":"Denies the write_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_file"]}},"deny-write-text-file":{"identifier":"deny-write-text-file","description":"Denies the write_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text_file"]}},"read-all":{"identifier":"read-all","description":"This enables all read related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists","watch","unwatch"],"deny":[]}},"read-app-specific-dirs-recursive":{"identifier":"read-app-specific-dirs-recursive","description":"This permission allows recursive read functionality on the application\nspecific base directories. \n","commands":{"allow":["read_dir","read_file","read_text_file","read_text_file_lines","read_text_file_lines_next","exists","scope-app-recursive"],"deny":[]}},"read-dirs":{"identifier":"read-dirs","description":"This enables directory read and file metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists"],"deny":[]}},"read-files":{"identifier":"read-files","description":"This enables file read related commands without any pre-configured accessible paths.","commands":{"allow":["read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists"],"deny":[]}},"read-meta":{"identifier":"read-meta","description":"This enables all index or metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists","size"],"deny":[]}},"scope":{"identifier":"scope","description":"An empty permission you can use to modify the global scope.","commands":{"allow":[],"deny":[]}},"scope-app":{"identifier":"scope-app","description":"This scope permits access to all files and list content of top level directories in the application folders.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"},{"path":"$APPDATA"},{"path":"$APPDATA/*"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"},{"path":"$APPCACHE"},{"path":"$APPCACHE/*"},{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-app-index":{"identifier":"scope-app-index","description":"This scope permits to list all files and folders in the application directories.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPDATA"},{"path":"$APPLOCALDATA"},{"path":"$APPCACHE"},{"path":"$APPLOG"}]}},"scope-app-recursive":{"identifier":"scope-app-recursive","description":"This scope permits recursive access to the complete application folders, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"},{"path":"$APPDATA"},{"path":"$APPDATA/**"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"},{"path":"$APPCACHE"},{"path":"$APPCACHE/**"},{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-appcache":{"identifier":"scope-appcache","description":"This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/*"}]}},"scope-appcache-index":{"identifier":"scope-appcache-index","description":"This scope permits to list all files and folders in the `$APPCACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"}]}},"scope-appcache-recursive":{"identifier":"scope-appcache-recursive","description":"This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/**"}]}},"scope-appconfig":{"identifier":"scope-appconfig","description":"This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"}]}},"scope-appconfig-index":{"identifier":"scope-appconfig-index","description":"This scope permits to list all files and folders in the `$APPCONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"}]}},"scope-appconfig-recursive":{"identifier":"scope-appconfig-recursive","description":"This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"}]}},"scope-appdata":{"identifier":"scope-appdata","description":"This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/*"}]}},"scope-appdata-index":{"identifier":"scope-appdata-index","description":"This scope permits to list all files and folders in the `$APPDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"}]}},"scope-appdata-recursive":{"identifier":"scope-appdata-recursive","description":"This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]}},"scope-applocaldata":{"identifier":"scope-applocaldata","description":"This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"}]}},"scope-applocaldata-index":{"identifier":"scope-applocaldata-index","description":"This scope permits to list all files and folders in the `$APPLOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"}]}},"scope-applocaldata-recursive":{"identifier":"scope-applocaldata-recursive","description":"This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"}]}},"scope-applog":{"identifier":"scope-applog","description":"This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-applog-index":{"identifier":"scope-applog-index","description":"This scope permits to list all files and folders in the `$APPLOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"}]}},"scope-applog-recursive":{"identifier":"scope-applog-recursive","description":"This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-audio":{"identifier":"scope-audio","description":"This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/*"}]}},"scope-audio-index":{"identifier":"scope-audio-index","description":"This scope permits to list all files and folders in the `$AUDIO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"}]}},"scope-audio-recursive":{"identifier":"scope-audio-recursive","description":"This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/**"}]}},"scope-cache":{"identifier":"scope-cache","description":"This scope permits access to all files and list content of top level directories in the `$CACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/*"}]}},"scope-cache-index":{"identifier":"scope-cache-index","description":"This scope permits to list all files and folders in the `$CACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"}]}},"scope-cache-recursive":{"identifier":"scope-cache-recursive","description":"This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/**"}]}},"scope-config":{"identifier":"scope-config","description":"This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/*"}]}},"scope-config-index":{"identifier":"scope-config-index","description":"This scope permits to list all files and folders in the `$CONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"}]}},"scope-config-recursive":{"identifier":"scope-config-recursive","description":"This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/**"}]}},"scope-data":{"identifier":"scope-data","description":"This scope permits access to all files and list content of top level directories in the `$DATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/*"}]}},"scope-data-index":{"identifier":"scope-data-index","description":"This scope permits to list all files and folders in the `$DATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"}]}},"scope-data-recursive":{"identifier":"scope-data-recursive","description":"This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/**"}]}},"scope-desktop":{"identifier":"scope-desktop","description":"This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/*"}]}},"scope-desktop-index":{"identifier":"scope-desktop-index","description":"This scope permits to list all files and folders in the `$DESKTOP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"}]}},"scope-desktop-recursive":{"identifier":"scope-desktop-recursive","description":"This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/**"}]}},"scope-document":{"identifier":"scope-document","description":"This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/*"}]}},"scope-document-index":{"identifier":"scope-document-index","description":"This scope permits to list all files and folders in the `$DOCUMENT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"}]}},"scope-document-recursive":{"identifier":"scope-document-recursive","description":"This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/**"}]}},"scope-download":{"identifier":"scope-download","description":"This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/*"}]}},"scope-download-index":{"identifier":"scope-download-index","description":"This scope permits to list all files and folders in the `$DOWNLOAD`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"}]}},"scope-download-recursive":{"identifier":"scope-download-recursive","description":"This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/**"}]}},"scope-exe":{"identifier":"scope-exe","description":"This scope permits access to all files and list content of top level directories in the `$EXE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/*"}]}},"scope-exe-index":{"identifier":"scope-exe-index","description":"This scope permits to list all files and folders in the `$EXE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"}]}},"scope-exe-recursive":{"identifier":"scope-exe-recursive","description":"This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/**"}]}},"scope-font":{"identifier":"scope-font","description":"This scope permits access to all files and list content of top level directories in the `$FONT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/*"}]}},"scope-font-index":{"identifier":"scope-font-index","description":"This scope permits to list all files and folders in the `$FONT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"}]}},"scope-font-recursive":{"identifier":"scope-font-recursive","description":"This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/**"}]}},"scope-home":{"identifier":"scope-home","description":"This scope permits access to all files and list content of top level directories in the `$HOME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/*"}]}},"scope-home-index":{"identifier":"scope-home-index","description":"This scope permits to list all files and folders in the `$HOME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"}]}},"scope-home-recursive":{"identifier":"scope-home-recursive","description":"This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/**"}]}},"scope-localdata":{"identifier":"scope-localdata","description":"This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/*"}]}},"scope-localdata-index":{"identifier":"scope-localdata-index","description":"This scope permits to list all files and folders in the `$LOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"}]}},"scope-localdata-recursive":{"identifier":"scope-localdata-recursive","description":"This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/**"}]}},"scope-log":{"identifier":"scope-log","description":"This scope permits access to all files and list content of top level directories in the `$LOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/*"}]}},"scope-log-index":{"identifier":"scope-log-index","description":"This scope permits to list all files and folders in the `$LOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"}]}},"scope-log-recursive":{"identifier":"scope-log-recursive","description":"This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/**"}]}},"scope-picture":{"identifier":"scope-picture","description":"This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/*"}]}},"scope-picture-index":{"identifier":"scope-picture-index","description":"This scope permits to list all files and folders in the `$PICTURE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"}]}},"scope-picture-recursive":{"identifier":"scope-picture-recursive","description":"This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/**"}]}},"scope-public":{"identifier":"scope-public","description":"This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/*"}]}},"scope-public-index":{"identifier":"scope-public-index","description":"This scope permits to list all files and folders in the `$PUBLIC`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"}]}},"scope-public-recursive":{"identifier":"scope-public-recursive","description":"This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/**"}]}},"scope-resource":{"identifier":"scope-resource","description":"This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/*"}]}},"scope-resource-index":{"identifier":"scope-resource-index","description":"This scope permits to list all files and folders in the `$RESOURCE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"}]}},"scope-resource-recursive":{"identifier":"scope-resource-recursive","description":"This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/**"}]}},"scope-runtime":{"identifier":"scope-runtime","description":"This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/*"}]}},"scope-runtime-index":{"identifier":"scope-runtime-index","description":"This scope permits to list all files and folders in the `$RUNTIME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"}]}},"scope-runtime-recursive":{"identifier":"scope-runtime-recursive","description":"This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/**"}]}},"scope-temp":{"identifier":"scope-temp","description":"This scope permits access to all files and list content of top level directories in the `$TEMP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/*"}]}},"scope-temp-index":{"identifier":"scope-temp-index","description":"This scope permits to list all files and folders in the `$TEMP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"}]}},"scope-temp-recursive":{"identifier":"scope-temp-recursive","description":"This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/**"}]}},"scope-template":{"identifier":"scope-template","description":"This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/*"}]}},"scope-template-index":{"identifier":"scope-template-index","description":"This scope permits to list all files and folders in the `$TEMPLATE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"}]}},"scope-template-recursive":{"identifier":"scope-template-recursive","description":"This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/**"}]}},"scope-video":{"identifier":"scope-video","description":"This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/*"}]}},"scope-video-index":{"identifier":"scope-video-index","description":"This scope permits to list all files and folders in the `$VIDEO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"}]}},"scope-video-recursive":{"identifier":"scope-video-recursive","description":"This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/**"}]}},"write-all":{"identifier":"write-all","description":"This enables all write related commands without any pre-configured accessible paths.","commands":{"allow":["mkdir","create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}},"write-files":{"identifier":"write-files","description":"This enables all file write related commands without any pre-configured accessible paths.","commands":{"allow":["create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}}},"permission_sets":{"allow-app-meta":{"identifier":"allow-app-meta","description":"This allows non-recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-index"]},"allow-app-meta-recursive":{"identifier":"allow-app-meta-recursive","description":"This allows full recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-recursive"]},"allow-app-read":{"identifier":"allow-app-read","description":"This allows non-recursive read access to the application folders.","permissions":["read-all","scope-app"]},"allow-app-read-recursive":{"identifier":"allow-app-read-recursive","description":"This allows full recursive read access to the complete application folders, files and subdirectories.","permissions":["read-all","scope-app-recursive"]},"allow-app-write":{"identifier":"allow-app-write","description":"This allows non-recursive write access to the application folders.","permissions":["write-all","scope-app"]},"allow-app-write-recursive":{"identifier":"allow-app-write-recursive","description":"This allows full recursive write access to the complete application folders, files and subdirectories.","permissions":["write-all","scope-app-recursive"]},"allow-appcache-meta":{"identifier":"allow-appcache-meta","description":"This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-index"]},"allow-appcache-meta-recursive":{"identifier":"allow-appcache-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-recursive"]},"allow-appcache-read":{"identifier":"allow-appcache-read","description":"This allows non-recursive read access to the `$APPCACHE` folder.","permissions":["read-all","scope-appcache"]},"allow-appcache-read-recursive":{"identifier":"allow-appcache-read-recursive","description":"This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["read-all","scope-appcache-recursive"]},"allow-appcache-write":{"identifier":"allow-appcache-write","description":"This allows non-recursive write access to the `$APPCACHE` folder.","permissions":["write-all","scope-appcache"]},"allow-appcache-write-recursive":{"identifier":"allow-appcache-write-recursive","description":"This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["write-all","scope-appcache-recursive"]},"allow-appconfig-meta":{"identifier":"allow-appconfig-meta","description":"This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-index"]},"allow-appconfig-meta-recursive":{"identifier":"allow-appconfig-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-recursive"]},"allow-appconfig-read":{"identifier":"allow-appconfig-read","description":"This allows non-recursive read access to the `$APPCONFIG` folder.","permissions":["read-all","scope-appconfig"]},"allow-appconfig-read-recursive":{"identifier":"allow-appconfig-read-recursive","description":"This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["read-all","scope-appconfig-recursive"]},"allow-appconfig-write":{"identifier":"allow-appconfig-write","description":"This allows non-recursive write access to the `$APPCONFIG` folder.","permissions":["write-all","scope-appconfig"]},"allow-appconfig-write-recursive":{"identifier":"allow-appconfig-write-recursive","description":"This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["write-all","scope-appconfig-recursive"]},"allow-appdata-meta":{"identifier":"allow-appdata-meta","description":"This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-index"]},"allow-appdata-meta-recursive":{"identifier":"allow-appdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-recursive"]},"allow-appdata-read":{"identifier":"allow-appdata-read","description":"This allows non-recursive read access to the `$APPDATA` folder.","permissions":["read-all","scope-appdata"]},"allow-appdata-read-recursive":{"identifier":"allow-appdata-read-recursive","description":"This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["read-all","scope-appdata-recursive"]},"allow-appdata-write":{"identifier":"allow-appdata-write","description":"This allows non-recursive write access to the `$APPDATA` folder.","permissions":["write-all","scope-appdata"]},"allow-appdata-write-recursive":{"identifier":"allow-appdata-write-recursive","description":"This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["write-all","scope-appdata-recursive"]},"allow-applocaldata-meta":{"identifier":"allow-applocaldata-meta","description":"This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-index"]},"allow-applocaldata-meta-recursive":{"identifier":"allow-applocaldata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-recursive"]},"allow-applocaldata-read":{"identifier":"allow-applocaldata-read","description":"This allows non-recursive read access to the `$APPLOCALDATA` folder.","permissions":["read-all","scope-applocaldata"]},"allow-applocaldata-read-recursive":{"identifier":"allow-applocaldata-read-recursive","description":"This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-applocaldata-recursive"]},"allow-applocaldata-write":{"identifier":"allow-applocaldata-write","description":"This allows non-recursive write access to the `$APPLOCALDATA` folder.","permissions":["write-all","scope-applocaldata"]},"allow-applocaldata-write-recursive":{"identifier":"allow-applocaldata-write-recursive","description":"This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-applocaldata-recursive"]},"allow-applog-meta":{"identifier":"allow-applog-meta","description":"This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-index"]},"allow-applog-meta-recursive":{"identifier":"allow-applog-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-recursive"]},"allow-applog-read":{"identifier":"allow-applog-read","description":"This allows non-recursive read access to the `$APPLOG` folder.","permissions":["read-all","scope-applog"]},"allow-applog-read-recursive":{"identifier":"allow-applog-read-recursive","description":"This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["read-all","scope-applog-recursive"]},"allow-applog-write":{"identifier":"allow-applog-write","description":"This allows non-recursive write access to the `$APPLOG` folder.","permissions":["write-all","scope-applog"]},"allow-applog-write-recursive":{"identifier":"allow-applog-write-recursive","description":"This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["write-all","scope-applog-recursive"]},"allow-audio-meta":{"identifier":"allow-audio-meta","description":"This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-index"]},"allow-audio-meta-recursive":{"identifier":"allow-audio-meta-recursive","description":"This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-recursive"]},"allow-audio-read":{"identifier":"allow-audio-read","description":"This allows non-recursive read access to the `$AUDIO` folder.","permissions":["read-all","scope-audio"]},"allow-audio-read-recursive":{"identifier":"allow-audio-read-recursive","description":"This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["read-all","scope-audio-recursive"]},"allow-audio-write":{"identifier":"allow-audio-write","description":"This allows non-recursive write access to the `$AUDIO` folder.","permissions":["write-all","scope-audio"]},"allow-audio-write-recursive":{"identifier":"allow-audio-write-recursive","description":"This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["write-all","scope-audio-recursive"]},"allow-cache-meta":{"identifier":"allow-cache-meta","description":"This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-index"]},"allow-cache-meta-recursive":{"identifier":"allow-cache-meta-recursive","description":"This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-recursive"]},"allow-cache-read":{"identifier":"allow-cache-read","description":"This allows non-recursive read access to the `$CACHE` folder.","permissions":["read-all","scope-cache"]},"allow-cache-read-recursive":{"identifier":"allow-cache-read-recursive","description":"This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.","permissions":["read-all","scope-cache-recursive"]},"allow-cache-write":{"identifier":"allow-cache-write","description":"This allows non-recursive write access to the `$CACHE` folder.","permissions":["write-all","scope-cache"]},"allow-cache-write-recursive":{"identifier":"allow-cache-write-recursive","description":"This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.","permissions":["write-all","scope-cache-recursive"]},"allow-config-meta":{"identifier":"allow-config-meta","description":"This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-index"]},"allow-config-meta-recursive":{"identifier":"allow-config-meta-recursive","description":"This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-recursive"]},"allow-config-read":{"identifier":"allow-config-read","description":"This allows non-recursive read access to the `$CONFIG` folder.","permissions":["read-all","scope-config"]},"allow-config-read-recursive":{"identifier":"allow-config-read-recursive","description":"This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["read-all","scope-config-recursive"]},"allow-config-write":{"identifier":"allow-config-write","description":"This allows non-recursive write access to the `$CONFIG` folder.","permissions":["write-all","scope-config"]},"allow-config-write-recursive":{"identifier":"allow-config-write-recursive","description":"This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["write-all","scope-config-recursive"]},"allow-data-meta":{"identifier":"allow-data-meta","description":"This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-index"]},"allow-data-meta-recursive":{"identifier":"allow-data-meta-recursive","description":"This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-recursive"]},"allow-data-read":{"identifier":"allow-data-read","description":"This allows non-recursive read access to the `$DATA` folder.","permissions":["read-all","scope-data"]},"allow-data-read-recursive":{"identifier":"allow-data-read-recursive","description":"This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.","permissions":["read-all","scope-data-recursive"]},"allow-data-write":{"identifier":"allow-data-write","description":"This allows non-recursive write access to the `$DATA` folder.","permissions":["write-all","scope-data"]},"allow-data-write-recursive":{"identifier":"allow-data-write-recursive","description":"This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.","permissions":["write-all","scope-data-recursive"]},"allow-desktop-meta":{"identifier":"allow-desktop-meta","description":"This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-index"]},"allow-desktop-meta-recursive":{"identifier":"allow-desktop-meta-recursive","description":"This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-recursive"]},"allow-desktop-read":{"identifier":"allow-desktop-read","description":"This allows non-recursive read access to the `$DESKTOP` folder.","permissions":["read-all","scope-desktop"]},"allow-desktop-read-recursive":{"identifier":"allow-desktop-read-recursive","description":"This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["read-all","scope-desktop-recursive"]},"allow-desktop-write":{"identifier":"allow-desktop-write","description":"This allows non-recursive write access to the `$DESKTOP` folder.","permissions":["write-all","scope-desktop"]},"allow-desktop-write-recursive":{"identifier":"allow-desktop-write-recursive","description":"This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["write-all","scope-desktop-recursive"]},"allow-document-meta":{"identifier":"allow-document-meta","description":"This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-index"]},"allow-document-meta-recursive":{"identifier":"allow-document-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-recursive"]},"allow-document-read":{"identifier":"allow-document-read","description":"This allows non-recursive read access to the `$DOCUMENT` folder.","permissions":["read-all","scope-document"]},"allow-document-read-recursive":{"identifier":"allow-document-read-recursive","description":"This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["read-all","scope-document-recursive"]},"allow-document-write":{"identifier":"allow-document-write","description":"This allows non-recursive write access to the `$DOCUMENT` folder.","permissions":["write-all","scope-document"]},"allow-document-write-recursive":{"identifier":"allow-document-write-recursive","description":"This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["write-all","scope-document-recursive"]},"allow-download-meta":{"identifier":"allow-download-meta","description":"This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-index"]},"allow-download-meta-recursive":{"identifier":"allow-download-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-recursive"]},"allow-download-read":{"identifier":"allow-download-read","description":"This allows non-recursive read access to the `$DOWNLOAD` folder.","permissions":["read-all","scope-download"]},"allow-download-read-recursive":{"identifier":"allow-download-read-recursive","description":"This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["read-all","scope-download-recursive"]},"allow-download-write":{"identifier":"allow-download-write","description":"This allows non-recursive write access to the `$DOWNLOAD` folder.","permissions":["write-all","scope-download"]},"allow-download-write-recursive":{"identifier":"allow-download-write-recursive","description":"This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["write-all","scope-download-recursive"]},"allow-exe-meta":{"identifier":"allow-exe-meta","description":"This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-index"]},"allow-exe-meta-recursive":{"identifier":"allow-exe-meta-recursive","description":"This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-recursive"]},"allow-exe-read":{"identifier":"allow-exe-read","description":"This allows non-recursive read access to the `$EXE` folder.","permissions":["read-all","scope-exe"]},"allow-exe-read-recursive":{"identifier":"allow-exe-read-recursive","description":"This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.","permissions":["read-all","scope-exe-recursive"]},"allow-exe-write":{"identifier":"allow-exe-write","description":"This allows non-recursive write access to the `$EXE` folder.","permissions":["write-all","scope-exe"]},"allow-exe-write-recursive":{"identifier":"allow-exe-write-recursive","description":"This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.","permissions":["write-all","scope-exe-recursive"]},"allow-font-meta":{"identifier":"allow-font-meta","description":"This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-index"]},"allow-font-meta-recursive":{"identifier":"allow-font-meta-recursive","description":"This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-recursive"]},"allow-font-read":{"identifier":"allow-font-read","description":"This allows non-recursive read access to the `$FONT` folder.","permissions":["read-all","scope-font"]},"allow-font-read-recursive":{"identifier":"allow-font-read-recursive","description":"This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.","permissions":["read-all","scope-font-recursive"]},"allow-font-write":{"identifier":"allow-font-write","description":"This allows non-recursive write access to the `$FONT` folder.","permissions":["write-all","scope-font"]},"allow-font-write-recursive":{"identifier":"allow-font-write-recursive","description":"This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.","permissions":["write-all","scope-font-recursive"]},"allow-home-meta":{"identifier":"allow-home-meta","description":"This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-index"]},"allow-home-meta-recursive":{"identifier":"allow-home-meta-recursive","description":"This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-recursive"]},"allow-home-read":{"identifier":"allow-home-read","description":"This allows non-recursive read access to the `$HOME` folder.","permissions":["read-all","scope-home"]},"allow-home-read-recursive":{"identifier":"allow-home-read-recursive","description":"This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.","permissions":["read-all","scope-home-recursive"]},"allow-home-write":{"identifier":"allow-home-write","description":"This allows non-recursive write access to the `$HOME` folder.","permissions":["write-all","scope-home"]},"allow-home-write-recursive":{"identifier":"allow-home-write-recursive","description":"This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.","permissions":["write-all","scope-home-recursive"]},"allow-localdata-meta":{"identifier":"allow-localdata-meta","description":"This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-index"]},"allow-localdata-meta-recursive":{"identifier":"allow-localdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-recursive"]},"allow-localdata-read":{"identifier":"allow-localdata-read","description":"This allows non-recursive read access to the `$LOCALDATA` folder.","permissions":["read-all","scope-localdata"]},"allow-localdata-read-recursive":{"identifier":"allow-localdata-read-recursive","description":"This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-localdata-recursive"]},"allow-localdata-write":{"identifier":"allow-localdata-write","description":"This allows non-recursive write access to the `$LOCALDATA` folder.","permissions":["write-all","scope-localdata"]},"allow-localdata-write-recursive":{"identifier":"allow-localdata-write-recursive","description":"This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-localdata-recursive"]},"allow-log-meta":{"identifier":"allow-log-meta","description":"This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-index"]},"allow-log-meta-recursive":{"identifier":"allow-log-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-recursive"]},"allow-log-read":{"identifier":"allow-log-read","description":"This allows non-recursive read access to the `$LOG` folder.","permissions":["read-all","scope-log"]},"allow-log-read-recursive":{"identifier":"allow-log-read-recursive","description":"This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.","permissions":["read-all","scope-log-recursive"]},"allow-log-write":{"identifier":"allow-log-write","description":"This allows non-recursive write access to the `$LOG` folder.","permissions":["write-all","scope-log"]},"allow-log-write-recursive":{"identifier":"allow-log-write-recursive","description":"This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.","permissions":["write-all","scope-log-recursive"]},"allow-picture-meta":{"identifier":"allow-picture-meta","description":"This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-index"]},"allow-picture-meta-recursive":{"identifier":"allow-picture-meta-recursive","description":"This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-recursive"]},"allow-picture-read":{"identifier":"allow-picture-read","description":"This allows non-recursive read access to the `$PICTURE` folder.","permissions":["read-all","scope-picture"]},"allow-picture-read-recursive":{"identifier":"allow-picture-read-recursive","description":"This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["read-all","scope-picture-recursive"]},"allow-picture-write":{"identifier":"allow-picture-write","description":"This allows non-recursive write access to the `$PICTURE` folder.","permissions":["write-all","scope-picture"]},"allow-picture-write-recursive":{"identifier":"allow-picture-write-recursive","description":"This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["write-all","scope-picture-recursive"]},"allow-public-meta":{"identifier":"allow-public-meta","description":"This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-index"]},"allow-public-meta-recursive":{"identifier":"allow-public-meta-recursive","description":"This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-recursive"]},"allow-public-read":{"identifier":"allow-public-read","description":"This allows non-recursive read access to the `$PUBLIC` folder.","permissions":["read-all","scope-public"]},"allow-public-read-recursive":{"identifier":"allow-public-read-recursive","description":"This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["read-all","scope-public-recursive"]},"allow-public-write":{"identifier":"allow-public-write","description":"This allows non-recursive write access to the `$PUBLIC` folder.","permissions":["write-all","scope-public"]},"allow-public-write-recursive":{"identifier":"allow-public-write-recursive","description":"This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["write-all","scope-public-recursive"]},"allow-resource-meta":{"identifier":"allow-resource-meta","description":"This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-index"]},"allow-resource-meta-recursive":{"identifier":"allow-resource-meta-recursive","description":"This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-recursive"]},"allow-resource-read":{"identifier":"allow-resource-read","description":"This allows non-recursive read access to the `$RESOURCE` folder.","permissions":["read-all","scope-resource"]},"allow-resource-read-recursive":{"identifier":"allow-resource-read-recursive","description":"This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["read-all","scope-resource-recursive"]},"allow-resource-write":{"identifier":"allow-resource-write","description":"This allows non-recursive write access to the `$RESOURCE` folder.","permissions":["write-all","scope-resource"]},"allow-resource-write-recursive":{"identifier":"allow-resource-write-recursive","description":"This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["write-all","scope-resource-recursive"]},"allow-runtime-meta":{"identifier":"allow-runtime-meta","description":"This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-index"]},"allow-runtime-meta-recursive":{"identifier":"allow-runtime-meta-recursive","description":"This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-recursive"]},"allow-runtime-read":{"identifier":"allow-runtime-read","description":"This allows non-recursive read access to the `$RUNTIME` folder.","permissions":["read-all","scope-runtime"]},"allow-runtime-read-recursive":{"identifier":"allow-runtime-read-recursive","description":"This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["read-all","scope-runtime-recursive"]},"allow-runtime-write":{"identifier":"allow-runtime-write","description":"This allows non-recursive write access to the `$RUNTIME` folder.","permissions":["write-all","scope-runtime"]},"allow-runtime-write-recursive":{"identifier":"allow-runtime-write-recursive","description":"This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["write-all","scope-runtime-recursive"]},"allow-temp-meta":{"identifier":"allow-temp-meta","description":"This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-index"]},"allow-temp-meta-recursive":{"identifier":"allow-temp-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-recursive"]},"allow-temp-read":{"identifier":"allow-temp-read","description":"This allows non-recursive read access to the `$TEMP` folder.","permissions":["read-all","scope-temp"]},"allow-temp-read-recursive":{"identifier":"allow-temp-read-recursive","description":"This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.","permissions":["read-all","scope-temp-recursive"]},"allow-temp-write":{"identifier":"allow-temp-write","description":"This allows non-recursive write access to the `$TEMP` folder.","permissions":["write-all","scope-temp"]},"allow-temp-write-recursive":{"identifier":"allow-temp-write-recursive","description":"This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.","permissions":["write-all","scope-temp-recursive"]},"allow-template-meta":{"identifier":"allow-template-meta","description":"This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-index"]},"allow-template-meta-recursive":{"identifier":"allow-template-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-recursive"]},"allow-template-read":{"identifier":"allow-template-read","description":"This allows non-recursive read access to the `$TEMPLATE` folder.","permissions":["read-all","scope-template"]},"allow-template-read-recursive":{"identifier":"allow-template-read-recursive","description":"This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["read-all","scope-template-recursive"]},"allow-template-write":{"identifier":"allow-template-write","description":"This allows non-recursive write access to the `$TEMPLATE` folder.","permissions":["write-all","scope-template"]},"allow-template-write-recursive":{"identifier":"allow-template-write-recursive","description":"This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["write-all","scope-template-recursive"]},"allow-video-meta":{"identifier":"allow-video-meta","description":"This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-index"]},"allow-video-meta-recursive":{"identifier":"allow-video-meta-recursive","description":"This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-recursive"]},"allow-video-read":{"identifier":"allow-video-read","description":"This allows non-recursive read access to the `$VIDEO` folder.","permissions":["read-all","scope-video"]},"allow-video-read-recursive":{"identifier":"allow-video-read-recursive","description":"This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["read-all","scope-video-recursive"]},"allow-video-write":{"identifier":"allow-video-write","description":"This allows non-recursive write access to the `$VIDEO` folder.","permissions":["write-all","scope-video"]},"allow-video-write-recursive":{"identifier":"allow-video-write-recursive","description":"This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["write-all","scope-video-recursive"]},"deny-default":{"identifier":"deny-default","description":"This denies access to dangerous Tauri relevant files and folders by default.","permissions":["deny-webview-data-linux","deny-webview-data-windows"]}},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"description":"A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern 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`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},{"properties":{"path":{"description":"A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern 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`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}},"required":["path"],"type":"object"}],"description":"FS scope entry.","title":"FsScopeEntry"}},"http":{"default_permission":{"identifier":"default","description":"This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n","permissions":["allow-fetch","allow-fetch-cancel","allow-fetch-read-body","allow-fetch-send"]},"permissions":{"allow-fetch":{"identifier":"allow-fetch","description":"Enables the fetch command without any pre-configured scope.","commands":{"allow":["fetch"],"deny":[]}},"allow-fetch-cancel":{"identifier":"allow-fetch-cancel","description":"Enables the fetch_cancel command without any pre-configured scope.","commands":{"allow":["fetch_cancel"],"deny":[]}},"allow-fetch-read-body":{"identifier":"allow-fetch-read-body","description":"Enables the fetch_read_body command without any pre-configured scope.","commands":{"allow":["fetch_read_body"],"deny":[]}},"allow-fetch-send":{"identifier":"allow-fetch-send","description":"Enables the fetch_send command without any pre-configured scope.","commands":{"allow":["fetch_send"],"deny":[]}},"deny-fetch":{"identifier":"deny-fetch","description":"Denies the fetch command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch"]}},"deny-fetch-cancel":{"identifier":"deny-fetch-cancel","description":"Denies the fetch_cancel command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_cancel"]}},"deny-fetch-read-body":{"identifier":"deny-fetch-read-body","description":"Denies the fetch_read_body command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_read_body"]}},"deny-fetch-send":{"identifier":"deny-fetch-send","description":"Denies the fetch_send command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_send"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"description":"A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"","type":"string"},{"properties":{"url":{"description":"A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"","type":"string"}},"required":["url"],"type":"object"}],"description":"HTTP scope entry.","title":"HttpScopeEntry"}},"notification":{"default_permission":{"identifier":"default","description":"This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n","permissions":["allow-is-permission-granted","allow-request-permission","allow-notify","allow-register-action-types","allow-register-listener","allow-cancel","allow-get-pending","allow-remove-active","allow-get-active","allow-check-permissions","allow-show","allow-batch","allow-list-channels","allow-delete-channel","allow-create-channel","allow-permission-state"]},"permissions":{"allow-batch":{"identifier":"allow-batch","description":"Enables the batch command without any pre-configured scope.","commands":{"allow":["batch"],"deny":[]}},"allow-cancel":{"identifier":"allow-cancel","description":"Enables the cancel command without any pre-configured scope.","commands":{"allow":["cancel"],"deny":[]}},"allow-check-permissions":{"identifier":"allow-check-permissions","description":"Enables the check_permissions command without any pre-configured scope.","commands":{"allow":["check_permissions"],"deny":[]}},"allow-create-channel":{"identifier":"allow-create-channel","description":"Enables the create_channel command without any pre-configured scope.","commands":{"allow":["create_channel"],"deny":[]}},"allow-delete-channel":{"identifier":"allow-delete-channel","description":"Enables the delete_channel command without any pre-configured scope.","commands":{"allow":["delete_channel"],"deny":[]}},"allow-get-active":{"identifier":"allow-get-active","description":"Enables the get_active command without any pre-configured scope.","commands":{"allow":["get_active"],"deny":[]}},"allow-get-pending":{"identifier":"allow-get-pending","description":"Enables the get_pending command without any pre-configured scope.","commands":{"allow":["get_pending"],"deny":[]}},"allow-is-permission-granted":{"identifier":"allow-is-permission-granted","description":"Enables the is_permission_granted command without any pre-configured scope.","commands":{"allow":["is_permission_granted"],"deny":[]}},"allow-list-channels":{"identifier":"allow-list-channels","description":"Enables the list_channels command without any pre-configured scope.","commands":{"allow":["list_channels"],"deny":[]}},"allow-notify":{"identifier":"allow-notify","description":"Enables the notify command without any pre-configured scope.","commands":{"allow":["notify"],"deny":[]}},"allow-permission-state":{"identifier":"allow-permission-state","description":"Enables the permission_state command without any pre-configured scope.","commands":{"allow":["permission_state"],"deny":[]}},"allow-register-action-types":{"identifier":"allow-register-action-types","description":"Enables the register_action_types command without any pre-configured scope.","commands":{"allow":["register_action_types"],"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-active":{"identifier":"allow-remove-active","description":"Enables the remove_active command without any pre-configured scope.","commands":{"allow":["remove_active"],"deny":[]}},"allow-request-permission":{"identifier":"allow-request-permission","description":"Enables the request_permission command without any pre-configured scope.","commands":{"allow":["request_permission"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"deny-batch":{"identifier":"deny-batch","description":"Denies the batch command without any pre-configured scope.","commands":{"allow":[],"deny":["batch"]}},"deny-cancel":{"identifier":"deny-cancel","description":"Denies the cancel command without any pre-configured scope.","commands":{"allow":[],"deny":["cancel"]}},"deny-check-permissions":{"identifier":"deny-check-permissions","description":"Denies the check_permissions command without any pre-configured scope.","commands":{"allow":[],"deny":["check_permissions"]}},"deny-create-channel":{"identifier":"deny-create-channel","description":"Denies the create_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["create_channel"]}},"deny-delete-channel":{"identifier":"deny-delete-channel","description":"Denies the delete_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["delete_channel"]}},"deny-get-active":{"identifier":"deny-get-active","description":"Denies the get_active command without any pre-configured scope.","commands":{"allow":[],"deny":["get_active"]}},"deny-get-pending":{"identifier":"deny-get-pending","description":"Denies the get_pending command without any pre-configured scope.","commands":{"allow":[],"deny":["get_pending"]}},"deny-is-permission-granted":{"identifier":"deny-is-permission-granted","description":"Denies the is_permission_granted command without any pre-configured scope.","commands":{"allow":[],"deny":["is_permission_granted"]}},"deny-list-channels":{"identifier":"deny-list-channels","description":"Denies the list_channels command without any pre-configured scope.","commands":{"allow":[],"deny":["list_channels"]}},"deny-notify":{"identifier":"deny-notify","description":"Denies the notify command without any pre-configured scope.","commands":{"allow":[],"deny":["notify"]}},"deny-permission-state":{"identifier":"deny-permission-state","description":"Denies the permission_state command without any pre-configured scope.","commands":{"allow":[],"deny":["permission_state"]}},"deny-register-action-types":{"identifier":"deny-register-action-types","description":"Denies the register_action_types command without any pre-configured scope.","commands":{"allow":[],"deny":["register_action_types"]}},"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-active":{"identifier":"deny-remove-active","description":"Denies the remove_active command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_active"]}},"deny-request-permission":{"identifier":"deny-request-permission","description":"Denies the request_permission command without any pre-configured scope.","commands":{"allow":[],"deny":["request_permission"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}}},"permission_sets":{},"global_scope_schema":null},"process":{"default_permission":{"identifier":"default","description":"This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n","permissions":["allow-exit","allow-restart"]},"permissions":{"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-restart":{"identifier":"allow-restart","description":"Enables the restart command without any pre-configured scope.","commands":{"allow":["restart"],"deny":[]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-restart":{"identifier":"deny-restart","description":"Denies the restart command without any pre-configured scope.","commands":{"allow":[],"deny":["restart"]}}},"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"}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null},"window-state":{"default_permission":{"identifier":"default","description":"This permission set configures what kind of\noperations are available from the window state plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n","permissions":["allow-filename","allow-restore-state","allow-save-window-state"]},"permissions":{"allow-filename":{"identifier":"allow-filename","description":"Enables the filename command without any pre-configured scope.","commands":{"allow":["filename"],"deny":[]}},"allow-restore-state":{"identifier":"allow-restore-state","description":"Enables the restore_state command without any pre-configured scope.","commands":{"allow":["restore_state"],"deny":[]}},"allow-save-window-state":{"identifier":"allow-save-window-state","description":"Enables the save_window_state command without any pre-configured scope.","commands":{"allow":["save_window_state"],"deny":[]}},"deny-filename":{"identifier":"deny-filename","description":"Denies the filename command without any pre-configured scope.","commands":{"allow":[],"deny":["filename"]}},"deny-restore-state":{"identifier":"deny-restore-state","description":"Denies the restore_state command without any pre-configured scope.","commands":{"allow":[],"deny":["restore_state"]}},"deny-save-window-state":{"identifier":"deny-save-window-state","description":"Denies the save_window_state command without any pre-configured scope.","commands":{"allow":[],"deny":["save_window_state"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file +{"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"]},"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-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-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-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-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-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-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-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-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-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-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},"fs":{"default_permission":{"identifier":"default","description":"This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n","permissions":["create-app-specific-dirs","read-app-specific-dirs-recursive","deny-default"]},"permissions":{"allow-copy-file":{"identifier":"allow-copy-file","description":"Enables the copy_file command without any pre-configured scope.","commands":{"allow":["copy_file"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-exists":{"identifier":"allow-exists","description":"Enables the exists command without any pre-configured scope.","commands":{"allow":["exists"],"deny":[]}},"allow-fstat":{"identifier":"allow-fstat","description":"Enables the fstat command without any pre-configured scope.","commands":{"allow":["fstat"],"deny":[]}},"allow-ftruncate":{"identifier":"allow-ftruncate","description":"Enables the ftruncate command without any pre-configured scope.","commands":{"allow":["ftruncate"],"deny":[]}},"allow-lstat":{"identifier":"allow-lstat","description":"Enables the lstat command without any pre-configured scope.","commands":{"allow":["lstat"],"deny":[]}},"allow-mkdir":{"identifier":"allow-mkdir","description":"Enables the mkdir command without any pre-configured scope.","commands":{"allow":["mkdir"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-read":{"identifier":"allow-read","description":"Enables the read command without any pre-configured scope.","commands":{"allow":["read"],"deny":[]}},"allow-read-dir":{"identifier":"allow-read-dir","description":"Enables the read_dir command without any pre-configured scope.","commands":{"allow":["read_dir"],"deny":[]}},"allow-read-file":{"identifier":"allow-read-file","description":"Enables the read_file command without any pre-configured scope.","commands":{"allow":["read_file"],"deny":[]}},"allow-read-text-file":{"identifier":"allow-read-text-file","description":"Enables the read_text_file command without any pre-configured scope.","commands":{"allow":["read_text_file"],"deny":[]}},"allow-read-text-file-lines":{"identifier":"allow-read-text-file-lines","description":"Enables the read_text_file_lines command without any pre-configured scope.","commands":{"allow":["read_text_file_lines","read_text_file_lines_next"],"deny":[]}},"allow-read-text-file-lines-next":{"identifier":"allow-read-text-file-lines-next","description":"Enables the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":["read_text_file_lines_next"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-rename":{"identifier":"allow-rename","description":"Enables the rename command without any pre-configured scope.","commands":{"allow":["rename"],"deny":[]}},"allow-seek":{"identifier":"allow-seek","description":"Enables the seek command without any pre-configured scope.","commands":{"allow":["seek"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"allow-stat":{"identifier":"allow-stat","description":"Enables the stat command without any pre-configured scope.","commands":{"allow":["stat"],"deny":[]}},"allow-truncate":{"identifier":"allow-truncate","description":"Enables the truncate command without any pre-configured scope.","commands":{"allow":["truncate"],"deny":[]}},"allow-unwatch":{"identifier":"allow-unwatch","description":"Enables the unwatch command without any pre-configured scope.","commands":{"allow":["unwatch"],"deny":[]}},"allow-watch":{"identifier":"allow-watch","description":"Enables the watch command without any pre-configured scope.","commands":{"allow":["watch"],"deny":[]}},"allow-write":{"identifier":"allow-write","description":"Enables the write command without any pre-configured scope.","commands":{"allow":["write"],"deny":[]}},"allow-write-file":{"identifier":"allow-write-file","description":"Enables the write_file command without any pre-configured scope.","commands":{"allow":["write_file","open","write"],"deny":[]}},"allow-write-text-file":{"identifier":"allow-write-text-file","description":"Enables the write_text_file command without any pre-configured scope.","commands":{"allow":["write_text_file"],"deny":[]}},"create-app-specific-dirs":{"identifier":"create-app-specific-dirs","description":"This permissions allows to create the application specific directories.\n","commands":{"allow":["mkdir","scope-app-index"],"deny":[]}},"deny-copy-file":{"identifier":"deny-copy-file","description":"Denies the copy_file command without any pre-configured scope.","commands":{"allow":[],"deny":["copy_file"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-exists":{"identifier":"deny-exists","description":"Denies the exists command without any pre-configured scope.","commands":{"allow":[],"deny":["exists"]}},"deny-fstat":{"identifier":"deny-fstat","description":"Denies the fstat command without any pre-configured scope.","commands":{"allow":[],"deny":["fstat"]}},"deny-ftruncate":{"identifier":"deny-ftruncate","description":"Denies the ftruncate command without any pre-configured scope.","commands":{"allow":[],"deny":["ftruncate"]}},"deny-lstat":{"identifier":"deny-lstat","description":"Denies the lstat command without any pre-configured scope.","commands":{"allow":[],"deny":["lstat"]}},"deny-mkdir":{"identifier":"deny-mkdir","description":"Denies the mkdir command without any pre-configured scope.","commands":{"allow":[],"deny":["mkdir"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-read":{"identifier":"deny-read","description":"Denies the read command without any pre-configured scope.","commands":{"allow":[],"deny":["read"]}},"deny-read-dir":{"identifier":"deny-read-dir","description":"Denies the read_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["read_dir"]}},"deny-read-file":{"identifier":"deny-read-file","description":"Denies the read_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_file"]}},"deny-read-text-file":{"identifier":"deny-read-text-file","description":"Denies the read_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file"]}},"deny-read-text-file-lines":{"identifier":"deny-read-text-file-lines","description":"Denies the read_text_file_lines command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines"]}},"deny-read-text-file-lines-next":{"identifier":"deny-read-text-file-lines-next","description":"Denies the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines_next"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-rename":{"identifier":"deny-rename","description":"Denies the rename command without any pre-configured scope.","commands":{"allow":[],"deny":["rename"]}},"deny-seek":{"identifier":"deny-seek","description":"Denies the seek command without any pre-configured scope.","commands":{"allow":[],"deny":["seek"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}},"deny-stat":{"identifier":"deny-stat","description":"Denies the stat command without any pre-configured scope.","commands":{"allow":[],"deny":["stat"]}},"deny-truncate":{"identifier":"deny-truncate","description":"Denies the truncate command without any pre-configured scope.","commands":{"allow":[],"deny":["truncate"]}},"deny-unwatch":{"identifier":"deny-unwatch","description":"Denies the unwatch command without any pre-configured scope.","commands":{"allow":[],"deny":["unwatch"]}},"deny-watch":{"identifier":"deny-watch","description":"Denies the watch command without any pre-configured scope.","commands":{"allow":[],"deny":["watch"]}},"deny-webview-data-linux":{"identifier":"deny-webview-data-linux","description":"This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-webview-data-windows":{"identifier":"deny-webview-data-windows","description":"This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-write":{"identifier":"deny-write","description":"Denies the write command without any pre-configured scope.","commands":{"allow":[],"deny":["write"]}},"deny-write-file":{"identifier":"deny-write-file","description":"Denies the write_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_file"]}},"deny-write-text-file":{"identifier":"deny-write-text-file","description":"Denies the write_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text_file"]}},"read-all":{"identifier":"read-all","description":"This enables all read related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists","watch","unwatch"],"deny":[]}},"read-app-specific-dirs-recursive":{"identifier":"read-app-specific-dirs-recursive","description":"This permission allows recursive read functionality on the application\nspecific base directories. \n","commands":{"allow":["read_dir","read_file","read_text_file","read_text_file_lines","read_text_file_lines_next","exists","scope-app-recursive"],"deny":[]}},"read-dirs":{"identifier":"read-dirs","description":"This enables directory read and file metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists"],"deny":[]}},"read-files":{"identifier":"read-files","description":"This enables file read related commands without any pre-configured accessible paths.","commands":{"allow":["read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists"],"deny":[]}},"read-meta":{"identifier":"read-meta","description":"This enables all index or metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists","size"],"deny":[]}},"scope":{"identifier":"scope","description":"An empty permission you can use to modify the global scope.","commands":{"allow":[],"deny":[]}},"scope-app":{"identifier":"scope-app","description":"This scope permits access to all files and list content of top level directories in the application folders.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"},{"path":"$APPDATA"},{"path":"$APPDATA/*"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"},{"path":"$APPCACHE"},{"path":"$APPCACHE/*"},{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-app-index":{"identifier":"scope-app-index","description":"This scope permits to list all files and folders in the application directories.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPDATA"},{"path":"$APPLOCALDATA"},{"path":"$APPCACHE"},{"path":"$APPLOG"}]}},"scope-app-recursive":{"identifier":"scope-app-recursive","description":"This scope permits recursive access to the complete application folders, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"},{"path":"$APPDATA"},{"path":"$APPDATA/**"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"},{"path":"$APPCACHE"},{"path":"$APPCACHE/**"},{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-appcache":{"identifier":"scope-appcache","description":"This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/*"}]}},"scope-appcache-index":{"identifier":"scope-appcache-index","description":"This scope permits to list all files and folders in the `$APPCACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"}]}},"scope-appcache-recursive":{"identifier":"scope-appcache-recursive","description":"This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/**"}]}},"scope-appconfig":{"identifier":"scope-appconfig","description":"This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"}]}},"scope-appconfig-index":{"identifier":"scope-appconfig-index","description":"This scope permits to list all files and folders in the `$APPCONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"}]}},"scope-appconfig-recursive":{"identifier":"scope-appconfig-recursive","description":"This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"}]}},"scope-appdata":{"identifier":"scope-appdata","description":"This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/*"}]}},"scope-appdata-index":{"identifier":"scope-appdata-index","description":"This scope permits to list all files and folders in the `$APPDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"}]}},"scope-appdata-recursive":{"identifier":"scope-appdata-recursive","description":"This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]}},"scope-applocaldata":{"identifier":"scope-applocaldata","description":"This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"}]}},"scope-applocaldata-index":{"identifier":"scope-applocaldata-index","description":"This scope permits to list all files and folders in the `$APPLOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"}]}},"scope-applocaldata-recursive":{"identifier":"scope-applocaldata-recursive","description":"This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"}]}},"scope-applog":{"identifier":"scope-applog","description":"This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-applog-index":{"identifier":"scope-applog-index","description":"This scope permits to list all files and folders in the `$APPLOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"}]}},"scope-applog-recursive":{"identifier":"scope-applog-recursive","description":"This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-audio":{"identifier":"scope-audio","description":"This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/*"}]}},"scope-audio-index":{"identifier":"scope-audio-index","description":"This scope permits to list all files and folders in the `$AUDIO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"}]}},"scope-audio-recursive":{"identifier":"scope-audio-recursive","description":"This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/**"}]}},"scope-cache":{"identifier":"scope-cache","description":"This scope permits access to all files and list content of top level directories in the `$CACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/*"}]}},"scope-cache-index":{"identifier":"scope-cache-index","description":"This scope permits to list all files and folders in the `$CACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"}]}},"scope-cache-recursive":{"identifier":"scope-cache-recursive","description":"This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/**"}]}},"scope-config":{"identifier":"scope-config","description":"This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/*"}]}},"scope-config-index":{"identifier":"scope-config-index","description":"This scope permits to list all files and folders in the `$CONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"}]}},"scope-config-recursive":{"identifier":"scope-config-recursive","description":"This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/**"}]}},"scope-data":{"identifier":"scope-data","description":"This scope permits access to all files and list content of top level directories in the `$DATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/*"}]}},"scope-data-index":{"identifier":"scope-data-index","description":"This scope permits to list all files and folders in the `$DATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"}]}},"scope-data-recursive":{"identifier":"scope-data-recursive","description":"This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/**"}]}},"scope-desktop":{"identifier":"scope-desktop","description":"This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/*"}]}},"scope-desktop-index":{"identifier":"scope-desktop-index","description":"This scope permits to list all files and folders in the `$DESKTOP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"}]}},"scope-desktop-recursive":{"identifier":"scope-desktop-recursive","description":"This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/**"}]}},"scope-document":{"identifier":"scope-document","description":"This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/*"}]}},"scope-document-index":{"identifier":"scope-document-index","description":"This scope permits to list all files and folders in the `$DOCUMENT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"}]}},"scope-document-recursive":{"identifier":"scope-document-recursive","description":"This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/**"}]}},"scope-download":{"identifier":"scope-download","description":"This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/*"}]}},"scope-download-index":{"identifier":"scope-download-index","description":"This scope permits to list all files and folders in the `$DOWNLOAD`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"}]}},"scope-download-recursive":{"identifier":"scope-download-recursive","description":"This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/**"}]}},"scope-exe":{"identifier":"scope-exe","description":"This scope permits access to all files and list content of top level directories in the `$EXE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/*"}]}},"scope-exe-index":{"identifier":"scope-exe-index","description":"This scope permits to list all files and folders in the `$EXE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"}]}},"scope-exe-recursive":{"identifier":"scope-exe-recursive","description":"This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/**"}]}},"scope-font":{"identifier":"scope-font","description":"This scope permits access to all files and list content of top level directories in the `$FONT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/*"}]}},"scope-font-index":{"identifier":"scope-font-index","description":"This scope permits to list all files and folders in the `$FONT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"}]}},"scope-font-recursive":{"identifier":"scope-font-recursive","description":"This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/**"}]}},"scope-home":{"identifier":"scope-home","description":"This scope permits access to all files and list content of top level directories in the `$HOME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/*"}]}},"scope-home-index":{"identifier":"scope-home-index","description":"This scope permits to list all files and folders in the `$HOME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"}]}},"scope-home-recursive":{"identifier":"scope-home-recursive","description":"This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/**"}]}},"scope-localdata":{"identifier":"scope-localdata","description":"This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/*"}]}},"scope-localdata-index":{"identifier":"scope-localdata-index","description":"This scope permits to list all files and folders in the `$LOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"}]}},"scope-localdata-recursive":{"identifier":"scope-localdata-recursive","description":"This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/**"}]}},"scope-log":{"identifier":"scope-log","description":"This scope permits access to all files and list content of top level directories in the `$LOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/*"}]}},"scope-log-index":{"identifier":"scope-log-index","description":"This scope permits to list all files and folders in the `$LOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"}]}},"scope-log-recursive":{"identifier":"scope-log-recursive","description":"This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/**"}]}},"scope-picture":{"identifier":"scope-picture","description":"This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/*"}]}},"scope-picture-index":{"identifier":"scope-picture-index","description":"This scope permits to list all files and folders in the `$PICTURE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"}]}},"scope-picture-recursive":{"identifier":"scope-picture-recursive","description":"This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/**"}]}},"scope-public":{"identifier":"scope-public","description":"This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/*"}]}},"scope-public-index":{"identifier":"scope-public-index","description":"This scope permits to list all files and folders in the `$PUBLIC`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"}]}},"scope-public-recursive":{"identifier":"scope-public-recursive","description":"This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/**"}]}},"scope-resource":{"identifier":"scope-resource","description":"This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/*"}]}},"scope-resource-index":{"identifier":"scope-resource-index","description":"This scope permits to list all files and folders in the `$RESOURCE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"}]}},"scope-resource-recursive":{"identifier":"scope-resource-recursive","description":"This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/**"}]}},"scope-runtime":{"identifier":"scope-runtime","description":"This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/*"}]}},"scope-runtime-index":{"identifier":"scope-runtime-index","description":"This scope permits to list all files and folders in the `$RUNTIME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"}]}},"scope-runtime-recursive":{"identifier":"scope-runtime-recursive","description":"This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/**"}]}},"scope-temp":{"identifier":"scope-temp","description":"This scope permits access to all files and list content of top level directories in the `$TEMP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/*"}]}},"scope-temp-index":{"identifier":"scope-temp-index","description":"This scope permits to list all files and folders in the `$TEMP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"}]}},"scope-temp-recursive":{"identifier":"scope-temp-recursive","description":"This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/**"}]}},"scope-template":{"identifier":"scope-template","description":"This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/*"}]}},"scope-template-index":{"identifier":"scope-template-index","description":"This scope permits to list all files and folders in the `$TEMPLATE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"}]}},"scope-template-recursive":{"identifier":"scope-template-recursive","description":"This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/**"}]}},"scope-video":{"identifier":"scope-video","description":"This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/*"}]}},"scope-video-index":{"identifier":"scope-video-index","description":"This scope permits to list all files and folders in the `$VIDEO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"}]}},"scope-video-recursive":{"identifier":"scope-video-recursive","description":"This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/**"}]}},"write-all":{"identifier":"write-all","description":"This enables all write related commands without any pre-configured accessible paths.","commands":{"allow":["mkdir","create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}},"write-files":{"identifier":"write-files","description":"This enables all file write related commands without any pre-configured accessible paths.","commands":{"allow":["create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}}},"permission_sets":{"allow-app-meta":{"identifier":"allow-app-meta","description":"This allows non-recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-index"]},"allow-app-meta-recursive":{"identifier":"allow-app-meta-recursive","description":"This allows full recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-recursive"]},"allow-app-read":{"identifier":"allow-app-read","description":"This allows non-recursive read access to the application folders.","permissions":["read-all","scope-app"]},"allow-app-read-recursive":{"identifier":"allow-app-read-recursive","description":"This allows full recursive read access to the complete application folders, files and subdirectories.","permissions":["read-all","scope-app-recursive"]},"allow-app-write":{"identifier":"allow-app-write","description":"This allows non-recursive write access to the application folders.","permissions":["write-all","scope-app"]},"allow-app-write-recursive":{"identifier":"allow-app-write-recursive","description":"This allows full recursive write access to the complete application folders, files and subdirectories.","permissions":["write-all","scope-app-recursive"]},"allow-appcache-meta":{"identifier":"allow-appcache-meta","description":"This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-index"]},"allow-appcache-meta-recursive":{"identifier":"allow-appcache-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-recursive"]},"allow-appcache-read":{"identifier":"allow-appcache-read","description":"This allows non-recursive read access to the `$APPCACHE` folder.","permissions":["read-all","scope-appcache"]},"allow-appcache-read-recursive":{"identifier":"allow-appcache-read-recursive","description":"This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["read-all","scope-appcache-recursive"]},"allow-appcache-write":{"identifier":"allow-appcache-write","description":"This allows non-recursive write access to the `$APPCACHE` folder.","permissions":["write-all","scope-appcache"]},"allow-appcache-write-recursive":{"identifier":"allow-appcache-write-recursive","description":"This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["write-all","scope-appcache-recursive"]},"allow-appconfig-meta":{"identifier":"allow-appconfig-meta","description":"This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-index"]},"allow-appconfig-meta-recursive":{"identifier":"allow-appconfig-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-recursive"]},"allow-appconfig-read":{"identifier":"allow-appconfig-read","description":"This allows non-recursive read access to the `$APPCONFIG` folder.","permissions":["read-all","scope-appconfig"]},"allow-appconfig-read-recursive":{"identifier":"allow-appconfig-read-recursive","description":"This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["read-all","scope-appconfig-recursive"]},"allow-appconfig-write":{"identifier":"allow-appconfig-write","description":"This allows non-recursive write access to the `$APPCONFIG` folder.","permissions":["write-all","scope-appconfig"]},"allow-appconfig-write-recursive":{"identifier":"allow-appconfig-write-recursive","description":"This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["write-all","scope-appconfig-recursive"]},"allow-appdata-meta":{"identifier":"allow-appdata-meta","description":"This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-index"]},"allow-appdata-meta-recursive":{"identifier":"allow-appdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-recursive"]},"allow-appdata-read":{"identifier":"allow-appdata-read","description":"This allows non-recursive read access to the `$APPDATA` folder.","permissions":["read-all","scope-appdata"]},"allow-appdata-read-recursive":{"identifier":"allow-appdata-read-recursive","description":"This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["read-all","scope-appdata-recursive"]},"allow-appdata-write":{"identifier":"allow-appdata-write","description":"This allows non-recursive write access to the `$APPDATA` folder.","permissions":["write-all","scope-appdata"]},"allow-appdata-write-recursive":{"identifier":"allow-appdata-write-recursive","description":"This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["write-all","scope-appdata-recursive"]},"allow-applocaldata-meta":{"identifier":"allow-applocaldata-meta","description":"This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-index"]},"allow-applocaldata-meta-recursive":{"identifier":"allow-applocaldata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-recursive"]},"allow-applocaldata-read":{"identifier":"allow-applocaldata-read","description":"This allows non-recursive read access to the `$APPLOCALDATA` folder.","permissions":["read-all","scope-applocaldata"]},"allow-applocaldata-read-recursive":{"identifier":"allow-applocaldata-read-recursive","description":"This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-applocaldata-recursive"]},"allow-applocaldata-write":{"identifier":"allow-applocaldata-write","description":"This allows non-recursive write access to the `$APPLOCALDATA` folder.","permissions":["write-all","scope-applocaldata"]},"allow-applocaldata-write-recursive":{"identifier":"allow-applocaldata-write-recursive","description":"This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-applocaldata-recursive"]},"allow-applog-meta":{"identifier":"allow-applog-meta","description":"This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-index"]},"allow-applog-meta-recursive":{"identifier":"allow-applog-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-recursive"]},"allow-applog-read":{"identifier":"allow-applog-read","description":"This allows non-recursive read access to the `$APPLOG` folder.","permissions":["read-all","scope-applog"]},"allow-applog-read-recursive":{"identifier":"allow-applog-read-recursive","description":"This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["read-all","scope-applog-recursive"]},"allow-applog-write":{"identifier":"allow-applog-write","description":"This allows non-recursive write access to the `$APPLOG` folder.","permissions":["write-all","scope-applog"]},"allow-applog-write-recursive":{"identifier":"allow-applog-write-recursive","description":"This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["write-all","scope-applog-recursive"]},"allow-audio-meta":{"identifier":"allow-audio-meta","description":"This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-index"]},"allow-audio-meta-recursive":{"identifier":"allow-audio-meta-recursive","description":"This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-recursive"]},"allow-audio-read":{"identifier":"allow-audio-read","description":"This allows non-recursive read access to the `$AUDIO` folder.","permissions":["read-all","scope-audio"]},"allow-audio-read-recursive":{"identifier":"allow-audio-read-recursive","description":"This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["read-all","scope-audio-recursive"]},"allow-audio-write":{"identifier":"allow-audio-write","description":"This allows non-recursive write access to the `$AUDIO` folder.","permissions":["write-all","scope-audio"]},"allow-audio-write-recursive":{"identifier":"allow-audio-write-recursive","description":"This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["write-all","scope-audio-recursive"]},"allow-cache-meta":{"identifier":"allow-cache-meta","description":"This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-index"]},"allow-cache-meta-recursive":{"identifier":"allow-cache-meta-recursive","description":"This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-recursive"]},"allow-cache-read":{"identifier":"allow-cache-read","description":"This allows non-recursive read access to the `$CACHE` folder.","permissions":["read-all","scope-cache"]},"allow-cache-read-recursive":{"identifier":"allow-cache-read-recursive","description":"This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.","permissions":["read-all","scope-cache-recursive"]},"allow-cache-write":{"identifier":"allow-cache-write","description":"This allows non-recursive write access to the `$CACHE` folder.","permissions":["write-all","scope-cache"]},"allow-cache-write-recursive":{"identifier":"allow-cache-write-recursive","description":"This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.","permissions":["write-all","scope-cache-recursive"]},"allow-config-meta":{"identifier":"allow-config-meta","description":"This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-index"]},"allow-config-meta-recursive":{"identifier":"allow-config-meta-recursive","description":"This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-recursive"]},"allow-config-read":{"identifier":"allow-config-read","description":"This allows non-recursive read access to the `$CONFIG` folder.","permissions":["read-all","scope-config"]},"allow-config-read-recursive":{"identifier":"allow-config-read-recursive","description":"This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["read-all","scope-config-recursive"]},"allow-config-write":{"identifier":"allow-config-write","description":"This allows non-recursive write access to the `$CONFIG` folder.","permissions":["write-all","scope-config"]},"allow-config-write-recursive":{"identifier":"allow-config-write-recursive","description":"This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["write-all","scope-config-recursive"]},"allow-data-meta":{"identifier":"allow-data-meta","description":"This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-index"]},"allow-data-meta-recursive":{"identifier":"allow-data-meta-recursive","description":"This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-recursive"]},"allow-data-read":{"identifier":"allow-data-read","description":"This allows non-recursive read access to the `$DATA` folder.","permissions":["read-all","scope-data"]},"allow-data-read-recursive":{"identifier":"allow-data-read-recursive","description":"This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.","permissions":["read-all","scope-data-recursive"]},"allow-data-write":{"identifier":"allow-data-write","description":"This allows non-recursive write access to the `$DATA` folder.","permissions":["write-all","scope-data"]},"allow-data-write-recursive":{"identifier":"allow-data-write-recursive","description":"This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.","permissions":["write-all","scope-data-recursive"]},"allow-desktop-meta":{"identifier":"allow-desktop-meta","description":"This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-index"]},"allow-desktop-meta-recursive":{"identifier":"allow-desktop-meta-recursive","description":"This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-recursive"]},"allow-desktop-read":{"identifier":"allow-desktop-read","description":"This allows non-recursive read access to the `$DESKTOP` folder.","permissions":["read-all","scope-desktop"]},"allow-desktop-read-recursive":{"identifier":"allow-desktop-read-recursive","description":"This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["read-all","scope-desktop-recursive"]},"allow-desktop-write":{"identifier":"allow-desktop-write","description":"This allows non-recursive write access to the `$DESKTOP` folder.","permissions":["write-all","scope-desktop"]},"allow-desktop-write-recursive":{"identifier":"allow-desktop-write-recursive","description":"This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["write-all","scope-desktop-recursive"]},"allow-document-meta":{"identifier":"allow-document-meta","description":"This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-index"]},"allow-document-meta-recursive":{"identifier":"allow-document-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-recursive"]},"allow-document-read":{"identifier":"allow-document-read","description":"This allows non-recursive read access to the `$DOCUMENT` folder.","permissions":["read-all","scope-document"]},"allow-document-read-recursive":{"identifier":"allow-document-read-recursive","description":"This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["read-all","scope-document-recursive"]},"allow-document-write":{"identifier":"allow-document-write","description":"This allows non-recursive write access to the `$DOCUMENT` folder.","permissions":["write-all","scope-document"]},"allow-document-write-recursive":{"identifier":"allow-document-write-recursive","description":"This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["write-all","scope-document-recursive"]},"allow-download-meta":{"identifier":"allow-download-meta","description":"This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-index"]},"allow-download-meta-recursive":{"identifier":"allow-download-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-recursive"]},"allow-download-read":{"identifier":"allow-download-read","description":"This allows non-recursive read access to the `$DOWNLOAD` folder.","permissions":["read-all","scope-download"]},"allow-download-read-recursive":{"identifier":"allow-download-read-recursive","description":"This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["read-all","scope-download-recursive"]},"allow-download-write":{"identifier":"allow-download-write","description":"This allows non-recursive write access to the `$DOWNLOAD` folder.","permissions":["write-all","scope-download"]},"allow-download-write-recursive":{"identifier":"allow-download-write-recursive","description":"This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["write-all","scope-download-recursive"]},"allow-exe-meta":{"identifier":"allow-exe-meta","description":"This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-index"]},"allow-exe-meta-recursive":{"identifier":"allow-exe-meta-recursive","description":"This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-recursive"]},"allow-exe-read":{"identifier":"allow-exe-read","description":"This allows non-recursive read access to the `$EXE` folder.","permissions":["read-all","scope-exe"]},"allow-exe-read-recursive":{"identifier":"allow-exe-read-recursive","description":"This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.","permissions":["read-all","scope-exe-recursive"]},"allow-exe-write":{"identifier":"allow-exe-write","description":"This allows non-recursive write access to the `$EXE` folder.","permissions":["write-all","scope-exe"]},"allow-exe-write-recursive":{"identifier":"allow-exe-write-recursive","description":"This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.","permissions":["write-all","scope-exe-recursive"]},"allow-font-meta":{"identifier":"allow-font-meta","description":"This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-index"]},"allow-font-meta-recursive":{"identifier":"allow-font-meta-recursive","description":"This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-recursive"]},"allow-font-read":{"identifier":"allow-font-read","description":"This allows non-recursive read access to the `$FONT` folder.","permissions":["read-all","scope-font"]},"allow-font-read-recursive":{"identifier":"allow-font-read-recursive","description":"This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.","permissions":["read-all","scope-font-recursive"]},"allow-font-write":{"identifier":"allow-font-write","description":"This allows non-recursive write access to the `$FONT` folder.","permissions":["write-all","scope-font"]},"allow-font-write-recursive":{"identifier":"allow-font-write-recursive","description":"This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.","permissions":["write-all","scope-font-recursive"]},"allow-home-meta":{"identifier":"allow-home-meta","description":"This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-index"]},"allow-home-meta-recursive":{"identifier":"allow-home-meta-recursive","description":"This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-recursive"]},"allow-home-read":{"identifier":"allow-home-read","description":"This allows non-recursive read access to the `$HOME` folder.","permissions":["read-all","scope-home"]},"allow-home-read-recursive":{"identifier":"allow-home-read-recursive","description":"This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.","permissions":["read-all","scope-home-recursive"]},"allow-home-write":{"identifier":"allow-home-write","description":"This allows non-recursive write access to the `$HOME` folder.","permissions":["write-all","scope-home"]},"allow-home-write-recursive":{"identifier":"allow-home-write-recursive","description":"This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.","permissions":["write-all","scope-home-recursive"]},"allow-localdata-meta":{"identifier":"allow-localdata-meta","description":"This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-index"]},"allow-localdata-meta-recursive":{"identifier":"allow-localdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-recursive"]},"allow-localdata-read":{"identifier":"allow-localdata-read","description":"This allows non-recursive read access to the `$LOCALDATA` folder.","permissions":["read-all","scope-localdata"]},"allow-localdata-read-recursive":{"identifier":"allow-localdata-read-recursive","description":"This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-localdata-recursive"]},"allow-localdata-write":{"identifier":"allow-localdata-write","description":"This allows non-recursive write access to the `$LOCALDATA` folder.","permissions":["write-all","scope-localdata"]},"allow-localdata-write-recursive":{"identifier":"allow-localdata-write-recursive","description":"This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-localdata-recursive"]},"allow-log-meta":{"identifier":"allow-log-meta","description":"This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-index"]},"allow-log-meta-recursive":{"identifier":"allow-log-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-recursive"]},"allow-log-read":{"identifier":"allow-log-read","description":"This allows non-recursive read access to the `$LOG` folder.","permissions":["read-all","scope-log"]},"allow-log-read-recursive":{"identifier":"allow-log-read-recursive","description":"This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.","permissions":["read-all","scope-log-recursive"]},"allow-log-write":{"identifier":"allow-log-write","description":"This allows non-recursive write access to the `$LOG` folder.","permissions":["write-all","scope-log"]},"allow-log-write-recursive":{"identifier":"allow-log-write-recursive","description":"This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.","permissions":["write-all","scope-log-recursive"]},"allow-picture-meta":{"identifier":"allow-picture-meta","description":"This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-index"]},"allow-picture-meta-recursive":{"identifier":"allow-picture-meta-recursive","description":"This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-recursive"]},"allow-picture-read":{"identifier":"allow-picture-read","description":"This allows non-recursive read access to the `$PICTURE` folder.","permissions":["read-all","scope-picture"]},"allow-picture-read-recursive":{"identifier":"allow-picture-read-recursive","description":"This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["read-all","scope-picture-recursive"]},"allow-picture-write":{"identifier":"allow-picture-write","description":"This allows non-recursive write access to the `$PICTURE` folder.","permissions":["write-all","scope-picture"]},"allow-picture-write-recursive":{"identifier":"allow-picture-write-recursive","description":"This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["write-all","scope-picture-recursive"]},"allow-public-meta":{"identifier":"allow-public-meta","description":"This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-index"]},"allow-public-meta-recursive":{"identifier":"allow-public-meta-recursive","description":"This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-recursive"]},"allow-public-read":{"identifier":"allow-public-read","description":"This allows non-recursive read access to the `$PUBLIC` folder.","permissions":["read-all","scope-public"]},"allow-public-read-recursive":{"identifier":"allow-public-read-recursive","description":"This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["read-all","scope-public-recursive"]},"allow-public-write":{"identifier":"allow-public-write","description":"This allows non-recursive write access to the `$PUBLIC` folder.","permissions":["write-all","scope-public"]},"allow-public-write-recursive":{"identifier":"allow-public-write-recursive","description":"This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["write-all","scope-public-recursive"]},"allow-resource-meta":{"identifier":"allow-resource-meta","description":"This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-index"]},"allow-resource-meta-recursive":{"identifier":"allow-resource-meta-recursive","description":"This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-recursive"]},"allow-resource-read":{"identifier":"allow-resource-read","description":"This allows non-recursive read access to the `$RESOURCE` folder.","permissions":["read-all","scope-resource"]},"allow-resource-read-recursive":{"identifier":"allow-resource-read-recursive","description":"This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["read-all","scope-resource-recursive"]},"allow-resource-write":{"identifier":"allow-resource-write","description":"This allows non-recursive write access to the `$RESOURCE` folder.","permissions":["write-all","scope-resource"]},"allow-resource-write-recursive":{"identifier":"allow-resource-write-recursive","description":"This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["write-all","scope-resource-recursive"]},"allow-runtime-meta":{"identifier":"allow-runtime-meta","description":"This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-index"]},"allow-runtime-meta-recursive":{"identifier":"allow-runtime-meta-recursive","description":"This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-recursive"]},"allow-runtime-read":{"identifier":"allow-runtime-read","description":"This allows non-recursive read access to the `$RUNTIME` folder.","permissions":["read-all","scope-runtime"]},"allow-runtime-read-recursive":{"identifier":"allow-runtime-read-recursive","description":"This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["read-all","scope-runtime-recursive"]},"allow-runtime-write":{"identifier":"allow-runtime-write","description":"This allows non-recursive write access to the `$RUNTIME` folder.","permissions":["write-all","scope-runtime"]},"allow-runtime-write-recursive":{"identifier":"allow-runtime-write-recursive","description":"This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["write-all","scope-runtime-recursive"]},"allow-temp-meta":{"identifier":"allow-temp-meta","description":"This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-index"]},"allow-temp-meta-recursive":{"identifier":"allow-temp-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-recursive"]},"allow-temp-read":{"identifier":"allow-temp-read","description":"This allows non-recursive read access to the `$TEMP` folder.","permissions":["read-all","scope-temp"]},"allow-temp-read-recursive":{"identifier":"allow-temp-read-recursive","description":"This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.","permissions":["read-all","scope-temp-recursive"]},"allow-temp-write":{"identifier":"allow-temp-write","description":"This allows non-recursive write access to the `$TEMP` folder.","permissions":["write-all","scope-temp"]},"allow-temp-write-recursive":{"identifier":"allow-temp-write-recursive","description":"This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.","permissions":["write-all","scope-temp-recursive"]},"allow-template-meta":{"identifier":"allow-template-meta","description":"This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-index"]},"allow-template-meta-recursive":{"identifier":"allow-template-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-recursive"]},"allow-template-read":{"identifier":"allow-template-read","description":"This allows non-recursive read access to the `$TEMPLATE` folder.","permissions":["read-all","scope-template"]},"allow-template-read-recursive":{"identifier":"allow-template-read-recursive","description":"This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["read-all","scope-template-recursive"]},"allow-template-write":{"identifier":"allow-template-write","description":"This allows non-recursive write access to the `$TEMPLATE` folder.","permissions":["write-all","scope-template"]},"allow-template-write-recursive":{"identifier":"allow-template-write-recursive","description":"This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["write-all","scope-template-recursive"]},"allow-video-meta":{"identifier":"allow-video-meta","description":"This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-index"]},"allow-video-meta-recursive":{"identifier":"allow-video-meta-recursive","description":"This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-recursive"]},"allow-video-read":{"identifier":"allow-video-read","description":"This allows non-recursive read access to the `$VIDEO` folder.","permissions":["read-all","scope-video"]},"allow-video-read-recursive":{"identifier":"allow-video-read-recursive","description":"This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["read-all","scope-video-recursive"]},"allow-video-write":{"identifier":"allow-video-write","description":"This allows non-recursive write access to the `$VIDEO` folder.","permissions":["write-all","scope-video"]},"allow-video-write-recursive":{"identifier":"allow-video-write-recursive","description":"This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["write-all","scope-video-recursive"]},"deny-default":{"identifier":"deny-default","description":"This denies access to dangerous Tauri relevant files and folders by default.","permissions":["deny-webview-data-linux","deny-webview-data-windows"]}},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"description":"A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern 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`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},{"properties":{"path":{"description":"A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern 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`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}},"required":["path"],"type":"object"}],"description":"FS scope entry.","title":"FsScopeEntry"}},"http":{"default_permission":{"identifier":"default","description":"This permission set configures what kind of\nfetch operations are available from the http plugin.\n\nThis enables all fetch operations but does not\nallow explicitly any origins to be fetched. This needs to\nbe manually configured before usage.\n\n#### Granted Permissions\n\nAll fetch operations are enabled.\n\n","permissions":["allow-fetch","allow-fetch-cancel","allow-fetch-read-body","allow-fetch-send"]},"permissions":{"allow-fetch":{"identifier":"allow-fetch","description":"Enables the fetch command without any pre-configured scope.","commands":{"allow":["fetch"],"deny":[]}},"allow-fetch-cancel":{"identifier":"allow-fetch-cancel","description":"Enables the fetch_cancel command without any pre-configured scope.","commands":{"allow":["fetch_cancel"],"deny":[]}},"allow-fetch-read-body":{"identifier":"allow-fetch-read-body","description":"Enables the fetch_read_body command without any pre-configured scope.","commands":{"allow":["fetch_read_body"],"deny":[]}},"allow-fetch-send":{"identifier":"allow-fetch-send","description":"Enables the fetch_send command without any pre-configured scope.","commands":{"allow":["fetch_send"],"deny":[]}},"deny-fetch":{"identifier":"deny-fetch","description":"Denies the fetch command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch"]}},"deny-fetch-cancel":{"identifier":"deny-fetch-cancel","description":"Denies the fetch_cancel command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_cancel"]}},"deny-fetch-read-body":{"identifier":"deny-fetch-read-body","description":"Denies the fetch_read_body command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_read_body"]}},"deny-fetch-send":{"identifier":"deny-fetch-send","description":"Denies the fetch_send command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_send"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"description":"A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"","type":"string"},{"properties":{"url":{"description":"A URL that can be accessed by the webview when using the HTTP APIs. Wildcards can be used following the URL pattern standard.\n\nSee [the URL Pattern spec](https://urlpattern.spec.whatwg.org/) for more information.\n\nExamples:\n\n- \"https://*\" : allows all HTTPS origin on port 443\n\n- \"https://*:*\" : allows all HTTPS origin on any port\n\n- \"https://*.github.com/tauri-apps/tauri\": allows any subdomain of \"github.com\" with the \"tauri-apps/api\" path\n\n- \"https://myapi.service.com/users/*\": allows access to any URLs that begins with \"https://myapi.service.com/users/\"","type":"string"}},"required":["url"],"type":"object"}],"description":"HTTP scope entry.","title":"HttpScopeEntry"}},"notification":{"default_permission":{"identifier":"default","description":"This permission set configures which\nnotification features are by default exposed.\n\n#### Granted Permissions\n\nIt allows all notification related features.\n\n","permissions":["allow-is-permission-granted","allow-request-permission","allow-notify","allow-register-action-types","allow-register-listener","allow-cancel","allow-get-pending","allow-remove-active","allow-get-active","allow-check-permissions","allow-show","allow-batch","allow-list-channels","allow-delete-channel","allow-create-channel","allow-permission-state"]},"permissions":{"allow-batch":{"identifier":"allow-batch","description":"Enables the batch command without any pre-configured scope.","commands":{"allow":["batch"],"deny":[]}},"allow-cancel":{"identifier":"allow-cancel","description":"Enables the cancel command without any pre-configured scope.","commands":{"allow":["cancel"],"deny":[]}},"allow-check-permissions":{"identifier":"allow-check-permissions","description":"Enables the check_permissions command without any pre-configured scope.","commands":{"allow":["check_permissions"],"deny":[]}},"allow-create-channel":{"identifier":"allow-create-channel","description":"Enables the create_channel command without any pre-configured scope.","commands":{"allow":["create_channel"],"deny":[]}},"allow-delete-channel":{"identifier":"allow-delete-channel","description":"Enables the delete_channel command without any pre-configured scope.","commands":{"allow":["delete_channel"],"deny":[]}},"allow-get-active":{"identifier":"allow-get-active","description":"Enables the get_active command without any pre-configured scope.","commands":{"allow":["get_active"],"deny":[]}},"allow-get-pending":{"identifier":"allow-get-pending","description":"Enables the get_pending command without any pre-configured scope.","commands":{"allow":["get_pending"],"deny":[]}},"allow-is-permission-granted":{"identifier":"allow-is-permission-granted","description":"Enables the is_permission_granted command without any pre-configured scope.","commands":{"allow":["is_permission_granted"],"deny":[]}},"allow-list-channels":{"identifier":"allow-list-channels","description":"Enables the list_channels command without any pre-configured scope.","commands":{"allow":["list_channels"],"deny":[]}},"allow-notify":{"identifier":"allow-notify","description":"Enables the notify command without any pre-configured scope.","commands":{"allow":["notify"],"deny":[]}},"allow-permission-state":{"identifier":"allow-permission-state","description":"Enables the permission_state command without any pre-configured scope.","commands":{"allow":["permission_state"],"deny":[]}},"allow-register-action-types":{"identifier":"allow-register-action-types","description":"Enables the register_action_types command without any pre-configured scope.","commands":{"allow":["register_action_types"],"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-active":{"identifier":"allow-remove-active","description":"Enables the remove_active command without any pre-configured scope.","commands":{"allow":["remove_active"],"deny":[]}},"allow-request-permission":{"identifier":"allow-request-permission","description":"Enables the request_permission command without any pre-configured scope.","commands":{"allow":["request_permission"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"deny-batch":{"identifier":"deny-batch","description":"Denies the batch command without any pre-configured scope.","commands":{"allow":[],"deny":["batch"]}},"deny-cancel":{"identifier":"deny-cancel","description":"Denies the cancel command without any pre-configured scope.","commands":{"allow":[],"deny":["cancel"]}},"deny-check-permissions":{"identifier":"deny-check-permissions","description":"Denies the check_permissions command without any pre-configured scope.","commands":{"allow":[],"deny":["check_permissions"]}},"deny-create-channel":{"identifier":"deny-create-channel","description":"Denies the create_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["create_channel"]}},"deny-delete-channel":{"identifier":"deny-delete-channel","description":"Denies the delete_channel command without any pre-configured scope.","commands":{"allow":[],"deny":["delete_channel"]}},"deny-get-active":{"identifier":"deny-get-active","description":"Denies the get_active command without any pre-configured scope.","commands":{"allow":[],"deny":["get_active"]}},"deny-get-pending":{"identifier":"deny-get-pending","description":"Denies the get_pending command without any pre-configured scope.","commands":{"allow":[],"deny":["get_pending"]}},"deny-is-permission-granted":{"identifier":"deny-is-permission-granted","description":"Denies the is_permission_granted command without any pre-configured scope.","commands":{"allow":[],"deny":["is_permission_granted"]}},"deny-list-channels":{"identifier":"deny-list-channels","description":"Denies the list_channels command without any pre-configured scope.","commands":{"allow":[],"deny":["list_channels"]}},"deny-notify":{"identifier":"deny-notify","description":"Denies the notify command without any pre-configured scope.","commands":{"allow":[],"deny":["notify"]}},"deny-permission-state":{"identifier":"deny-permission-state","description":"Denies the permission_state command without any pre-configured scope.","commands":{"allow":[],"deny":["permission_state"]}},"deny-register-action-types":{"identifier":"deny-register-action-types","description":"Denies the register_action_types command without any pre-configured scope.","commands":{"allow":[],"deny":["register_action_types"]}},"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-active":{"identifier":"deny-remove-active","description":"Denies the remove_active command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_active"]}},"deny-request-permission":{"identifier":"deny-request-permission","description":"Denies the request_permission command without any pre-configured scope.","commands":{"allow":[],"deny":["request_permission"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}}},"permission_sets":{},"global_scope_schema":null},"process":{"default_permission":{"identifier":"default","description":"This permission set configures which\nprocess features are by default exposed.\n\n#### Granted Permissions\n\nThis enables to quit via `allow-exit` and restart via `allow-restart`\nthe application.\n","permissions":["allow-exit","allow-restart"]},"permissions":{"allow-exit":{"identifier":"allow-exit","description":"Enables the exit command without any pre-configured scope.","commands":{"allow":["exit"],"deny":[]}},"allow-restart":{"identifier":"allow-restart","description":"Enables the restart command without any pre-configured scope.","commands":{"allow":["restart"],"deny":[]}},"deny-exit":{"identifier":"deny-exit","description":"Denies the exit command without any pre-configured scope.","commands":{"allow":[],"deny":["exit"]}},"deny-restart":{"identifier":"deny-restart","description":"Denies the restart command without any pre-configured scope.","commands":{"allow":[],"deny":["restart"]}}},"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"}},"updater":{"default_permission":{"identifier":"default","description":"This permission set configures which kind of\nupdater functions are exposed to the frontend.\n\n#### Granted Permissions\n\nThe full workflow from checking for updates to installing them\nis enabled.\n\n","permissions":["allow-check","allow-download","allow-install","allow-download-and-install"]},"permissions":{"allow-check":{"identifier":"allow-check","description":"Enables the check command without any pre-configured scope.","commands":{"allow":["check"],"deny":[]}},"allow-download":{"identifier":"allow-download","description":"Enables the download command without any pre-configured scope.","commands":{"allow":["download"],"deny":[]}},"allow-download-and-install":{"identifier":"allow-download-and-install","description":"Enables the download_and_install command without any pre-configured scope.","commands":{"allow":["download_and_install"],"deny":[]}},"allow-install":{"identifier":"allow-install","description":"Enables the install command without any pre-configured scope.","commands":{"allow":["install"],"deny":[]}},"deny-check":{"identifier":"deny-check","description":"Denies the check command without any pre-configured scope.","commands":{"allow":[],"deny":["check"]}},"deny-download":{"identifier":"deny-download","description":"Denies the download command without any pre-configured scope.","commands":{"allow":[],"deny":["download"]}},"deny-download-and-install":{"identifier":"deny-download-and-install","description":"Denies the download_and_install command without any pre-configured scope.","commands":{"allow":[],"deny":["download_and_install"]}},"deny-install":{"identifier":"deny-install","description":"Denies the install command without any pre-configured scope.","commands":{"allow":[],"deny":["install"]}}},"permission_sets":{},"global_scope_schema":null},"window-state":{"default_permission":{"identifier":"default","description":"This permission set configures what kind of\noperations are available from the window state plugin.\n\n#### Granted Permissions\n\nAll operations are enabled by default.\n\n","permissions":["allow-filename","allow-restore-state","allow-save-window-state"]},"permissions":{"allow-filename":{"identifier":"allow-filename","description":"Enables the filename command without any pre-configured scope.","commands":{"allow":["filename"],"deny":[]}},"allow-restore-state":{"identifier":"allow-restore-state","description":"Enables the restore_state command without any pre-configured scope.","commands":{"allow":["restore_state"],"deny":[]}},"allow-save-window-state":{"identifier":"allow-save-window-state","description":"Enables the save_window_state command without any pre-configured scope.","commands":{"allow":["save_window_state"],"deny":[]}},"deny-filename":{"identifier":"deny-filename","description":"Denies the filename command without any pre-configured scope.","commands":{"allow":[],"deny":["filename"]}},"deny-restore-state":{"identifier":"deny-restore-state","description":"Denies the restore_state command without any pre-configured scope.","commands":{"allow":[],"deny":["restore_state"]}},"deny-save-window-state":{"identifier":"deny-save-window-state","description":"Denies the save_window_state command without any pre-configured scope.","commands":{"allow":[],"deny":["save_window_state"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/tauri/src-tauri/gen/schemas/desktop-schema.json b/tauri/src-tauri/gen/schemas/desktop-schema.json index e93ad2c2..9d570c27 100644 --- a/tauri/src-tauri/gen/schemas/desktop-schema.json +++ b/tauri/src-tauri/gen/schemas/desktop-schema.json @@ -37,7 +37,7 @@ ], "definitions": { "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", "type": "object", "required": [ "identifier", @@ -49,7 +49,7 @@ "type": "string" }, "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", "default": "", "type": "string" }, @@ -3152,6 +3152,12 @@ "const": "core:webview:allow-reparent", "markdownDescription": "Enables the reparent command without any pre-configured scope." }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Enables the set_webview_background_color command without any pre-configured scope.", "type": "string", @@ -3254,6 +3260,12 @@ "const": "core:webview:deny-reparent", "markdownDescription": "Denies the reparent command without any pre-configured scope." }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Denies the set_webview_background_color command without any pre-configured scope.", "type": "string", diff --git a/tauri/src-tauri/gen/schemas/macOS-schema.json b/tauri/src-tauri/gen/schemas/macOS-schema.json index e93ad2c2..9d570c27 100644 --- a/tauri/src-tauri/gen/schemas/macOS-schema.json +++ b/tauri/src-tauri/gen/schemas/macOS-schema.json @@ -37,7 +37,7 @@ ], "definitions": { "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", + "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows' and webviews' fine grained access to the Tauri core, application, or plugin commands. If a webview or its window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", "type": "object", "required": [ "identifier", @@ -49,7 +49,7 @@ "type": "string" }, "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", + "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programmatic access to files selected by the user.", "default": "", "type": "string" }, @@ -3152,6 +3152,12 @@ "const": "core:webview:allow-reparent", "markdownDescription": "Enables the reparent command without any pre-configured scope." }, + { + "description": "Enables the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:allow-set-webview-auto-resize", + "markdownDescription": "Enables the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Enables the set_webview_background_color command without any pre-configured scope.", "type": "string", @@ -3254,6 +3260,12 @@ "const": "core:webview:deny-reparent", "markdownDescription": "Denies the reparent command without any pre-configured scope." }, + { + "description": "Denies the set_webview_auto_resize command without any pre-configured scope.", + "type": "string", + "const": "core:webview:deny-set-webview-auto-resize", + "markdownDescription": "Denies the set_webview_auto_resize command without any pre-configured scope." + }, { "description": "Denies the set_webview_background_color command without any pre-configured scope.", "type": "string", diff --git a/tauri/src-tauri/icons/128x128.png b/tauri/src-tauri/icons/128x128.png index c7c23200..47cc1d74 100644 Binary files a/tauri/src-tauri/icons/128x128.png and b/tauri/src-tauri/icons/128x128.png differ diff --git a/tauri/src-tauri/icons/128x128@2x.png b/tauri/src-tauri/icons/128x128@2x.png index 3b597184..8f0ed1ff 100644 Binary files a/tauri/src-tauri/icons/128x128@2x.png and b/tauri/src-tauri/icons/128x128@2x.png differ diff --git a/tauri/src-tauri/icons/32x32.png b/tauri/src-tauri/icons/32x32.png index 7a2cc1ae..552867da 100644 Binary files a/tauri/src-tauri/icons/32x32.png and b/tauri/src-tauri/icons/32x32.png differ diff --git a/tauri/src-tauri/icons/icon.icns b/tauri/src-tauri/icons/icon.icns index b715b5a2..8ef9ad24 100644 Binary files a/tauri/src-tauri/icons/icon.icns and b/tauri/src-tauri/icons/icon.icns differ diff --git a/tauri/src-tauri/icons/icon.ico b/tauri/src-tauri/icons/icon.ico index e69de29b..1a89074e 100644 Binary files a/tauri/src-tauri/icons/icon.ico and b/tauri/src-tauri/icons/icon.ico differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_128x128.png b/tauri/src-tauri/icons/icon.iconset/icon_128x128.png index 0412439c..47cc1d74 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_128x128.png and b/tauri/src-tauri/icons/icon.iconset/icon_128x128.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_128x128@2x.png b/tauri/src-tauri/icons/icon.iconset/icon_128x128@2x.png index fa0b9768..8f0ed1ff 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_128x128@2x.png and b/tauri/src-tauri/icons/icon.iconset/icon_128x128@2x.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_16x16.png b/tauri/src-tauri/icons/icon.iconset/icon_16x16.png index 9e9973bd..e26f07f1 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_16x16.png and b/tauri/src-tauri/icons/icon.iconset/icon_16x16.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_16x16@2x.png b/tauri/src-tauri/icons/icon.iconset/icon_16x16@2x.png index 0153828b..552867da 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_16x16@2x.png and b/tauri/src-tauri/icons/icon.iconset/icon_16x16@2x.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_256x256.png b/tauri/src-tauri/icons/icon.iconset/icon_256x256.png index fa0b9768..8f0ed1ff 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_256x256.png and b/tauri/src-tauri/icons/icon.iconset/icon_256x256.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_256x256@2x.png b/tauri/src-tauri/icons/icon.iconset/icon_256x256@2x.png index ba4bce20..daa4bde2 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_256x256@2x.png and b/tauri/src-tauri/icons/icon.iconset/icon_256x256@2x.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_32x32.png b/tauri/src-tauri/icons/icon.iconset/icon_32x32.png index 0153828b..552867da 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_32x32.png and b/tauri/src-tauri/icons/icon.iconset/icon_32x32.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_32x32@2x.png b/tauri/src-tauri/icons/icon.iconset/icon_32x32@2x.png index 2983a208..4fb554b0 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_32x32@2x.png and b/tauri/src-tauri/icons/icon.iconset/icon_32x32@2x.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_512x512.png b/tauri/src-tauri/icons/icon.iconset/icon_512x512.png index ba4bce20..daa4bde2 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_512x512.png and b/tauri/src-tauri/icons/icon.iconset/icon_512x512.png differ diff --git a/tauri/src-tauri/icons/icon.iconset/icon_512x512@2x.png b/tauri/src-tauri/icons/icon.iconset/icon_512x512@2x.png index 1a5ee251..e95734c1 100644 Binary files a/tauri/src-tauri/icons/icon.iconset/icon_512x512@2x.png and b/tauri/src-tauri/icons/icon.iconset/icon_512x512@2x.png differ diff --git a/tauri/src-tauri/icons/icon.png b/tauri/src-tauri/icons/icon.png index 1a5ee251..ee39b5d5 100644 Binary files a/tauri/src-tauri/icons/icon.png and b/tauri/src-tauri/icons/icon.png differ diff --git a/tauri/src-tauri/src/applescript.rs b/tauri/src-tauri/src/applescript.rs new file mode 100644 index 00000000..56061f6f --- /dev/null +++ b/tauri/src-tauri/src/applescript.rs @@ -0,0 +1,277 @@ +use std::process::Command; +use tracing::{debug, error, info}; + +/// AppleScript integration for advanced terminal control on macOS +pub struct AppleScriptRunner; + +impl AppleScriptRunner { + /// Run an AppleScript command + pub fn run_script(script: &str) -> Result { + debug!("Running AppleScript: {}", script); + + let output = Command::new("osascript") + .arg("-e") + .arg(script) + .output() + .map_err(|e| format!("Failed to execute AppleScript: {}", e))?; + + if output.status.success() { + let result = String::from_utf8_lossy(&output.stdout).trim().to_string(); + debug!("AppleScript output: {}", result); + Ok(result) + } else { + let error = String::from_utf8_lossy(&output.stderr).trim().to_string(); + error!("AppleScript error: {}", error); + Err(error) + } + } + + /// Launch Terminal.app with a new window for a session + pub fn launch_terminal_app(session_id: &str, command: Option<&str>) -> Result { + let default_cmd = format!("vt connect localhost:4022/{}", session_id); + let cmd = command.unwrap_or(&default_cmd); + + let script = format!( + r#"tell application "Terminal" + activate + do script "{}" + set newWindow to front window + set newTab to selected tab of newWindow + return "tab id " & (id of newTab) & " of window id " & (id of newWindow) + end tell"#, + cmd.replace("\"", "\\\"") + ); + + Self::run_script(&script) + } + + /// Launch iTerm2 with a new window/tab for a session + pub fn launch_iterm2(session_id: &str, command: Option<&str>, new_window: bool) -> Result { + let default_cmd = format!("vt connect localhost:4022/{}", session_id); + let cmd = command.unwrap_or(&default_cmd); + + let script = if new_window { + format!( + r#"tell application "iTerm" + activate + set newWindow to (create window with default profile) + tell current session of newWindow + write text "{}" + end tell + return id of newWindow as string + end tell"#, + cmd.replace("\"", "\\\"") + ) + } else { + format!( + r#"tell application "iTerm" + activate + tell current window + set newTab to (create tab with default profile) + tell current session of newTab + write text "{}" + end tell + return id of newTab as string + end tell + end tell"#, + cmd.replace("\"", "\\\"") + ) + }; + + Self::run_script(&script) + } + + /// Focus a specific Terminal.app window/tab + pub fn focus_terminal_window(window_id: u64, tab_id: Option) -> Result<(), String> { + let script = if let Some(tid) = tab_id { + format!( + r#"tell application "Terminal" + activate + set targetWindow to window id {} + set selected tab of targetWindow to tab id {} of targetWindow + set frontmost of targetWindow to true + end tell"#, + window_id, tid + ) + } else { + format!( + r#"tell application "Terminal" + activate + set targetWindow to window id {} + set frontmost of targetWindow to true + end tell"#, + window_id + ) + }; + + Self::run_script(&script)?; + Ok(()) + } + + /// Focus iTerm2 window + pub fn focus_iterm2_window(window_id: &str) -> Result<(), String> { + let script = format!( + r#"tell application "iTerm" + activate + set targetWindow to (first window whose id is {}) + select targetWindow + end tell"#, + window_id + ); + + Self::run_script(&script)?; + Ok(()) + } + + /// Get list of Terminal.app windows with their titles + pub fn get_terminal_windows() -> Result, String> { + let script = r#"tell application "Terminal" + set windowList to {} + repeat with w in windows + set windowInfo to (id of w as string) & "|" & (name of w as string) + set end of windowList to windowInfo + end repeat + return windowList + end tell"#; + + let output = Self::run_script(script)?; + let mut windows = Vec::new(); + + for line in output.lines() { + if let Some((id_str, title)) = line.split_once('|') { + if let Ok(id) = id_str.trim().parse::() { + windows.push((id, title.trim().to_string())); + } + } + } + + Ok(windows) + } + + /// Check if an application is running + pub fn is_app_running(app_name: &str) -> Result { + let script = format!( + r#"tell application "System Events" + return exists (processes where name is "{}") + end tell"#, + app_name + ); + + let output = Self::run_script(&script)?; + Ok(output.trim() == "true") + } + + /// Launch an application by path + pub fn launch_app(app_path: &str) -> Result<(), String> { + let script = format!( + r#"tell application "Finder" + open POSIX file "{}" + end tell"#, + app_path + ); + + Self::run_script(&script)?; + Ok(()) + } + + /// Send keystrokes to the frontmost application + pub fn send_keystrokes(text: &str) -> Result<(), String> { + let script = format!( + r#"tell application "System Events" + keystroke "{}" + end tell"#, + text.replace("\"", "\\\"") + ); + + Self::run_script(&script)?; + Ok(()) + } + + /// Get the frontmost application name + pub fn get_frontmost_app() -> Result { + let script = r#"tell application "System Events" + return name of first application process whose frontmost is true + end tell"#; + + Self::run_script(script) + } +} + +/// Enhanced terminal launching with AppleScript +pub struct AppleScriptTerminalLauncher; + +impl AppleScriptTerminalLauncher { + /// Launch a terminal with enhanced AppleScript control + pub async fn launch_terminal( + terminal_type: &str, + session_id: &str, + command: Option<&str>, + working_directory: Option<&str>, + ) -> Result { + info!("Launching {} for session {} via AppleScript", terminal_type, session_id); + + // Build the full command with working directory + let full_command = if let Some(cwd) = working_directory { + if let Some(cmd) = command { + format!("cd '{}' && {}", cwd, cmd) + } else { + format!("cd '{}' && vt connect localhost:4022/{}", cwd, session_id) + } + } else { + command.map(|c| c.to_string()) + .unwrap_or_else(|| format!("vt connect localhost:4022/{}", session_id)) + }; + + match terminal_type { + "Terminal" | "Terminal.app" => { + AppleScriptRunner::launch_terminal_app(session_id, Some(&full_command)) + } + "iTerm2" | "iTerm" => { + AppleScriptRunner::launch_iterm2(session_id, Some(&full_command), true) + } + _ => { + // For other terminals, try to launch via open command + let mut cmd = Command::new("open"); + cmd.arg("-a").arg(terminal_type); + + if let Some(cwd) = working_directory { + cmd.arg("--args").arg("--working-directory").arg(cwd); + } + + cmd.output() + .map_err(|e| format!("Failed to launch {}: {}", terminal_type, e))?; + + Ok(String::new()) + } + } + } + + /// Focus a terminal window using AppleScript + pub async fn focus_terminal_window( + terminal_type: &str, + window_info: &str, + ) -> Result<(), String> { + match terminal_type { + "Terminal" | "Terminal.app" => { + // Parse window ID from window_info + if let Ok(window_id) = window_info.parse::() { + AppleScriptRunner::focus_terminal_window(window_id, None) + } else { + Err("Invalid window ID".to_string()) + } + } + "iTerm2" | "iTerm" => { + AppleScriptRunner::focus_iterm2_window(window_info) + } + _ => { + // For other terminals, just activate the app + let script = format!( + r#"tell application "{}" to activate"#, + terminal_type + ); + AppleScriptRunner::run_script(&script)?; + Ok(()) + } + } + } +} \ No newline at end of file diff --git a/tauri/src-tauri/src/backend_manager.rs b/tauri/src-tauri/src/backend_manager.rs index d784dd77..144b6efd 100644 --- a/tauri/src-tauri/src/backend_manager.rs +++ b/tauri/src-tauri/src/backend_manager.rs @@ -6,6 +6,7 @@ use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::{Child, Command}; use tokio::sync::RwLock; use tracing::{error, info, warn}; +use crate::log_collector::SERVER_LOG_COLLECTOR; /// Server state enumeration #[derive(Debug, Clone, Copy, PartialEq, Eq)] @@ -414,14 +415,24 @@ impl NodeJsServer { /// Log server output fn log_output(line: &str, is_error: bool) { let line_lower = line.to_lowercase(); - - if is_error || line_lower.contains("error") || line_lower.contains("failed") { + + let level = if is_error || line_lower.contains("error") || line_lower.contains("failed") { error!("Server: {}", line); + "error" } else if line_lower.contains("warn") { warn!("Server: {}", line); + "warn" } else { info!("Server: {}", line); - } + "info" + }; + + // Add to log collector + let collector = SERVER_LOG_COLLECTOR.clone(); + let line_owned = line.to_string(); + tokio::spawn(async move { + collector.add_log(level, line_owned).await; + }); } /// Monitor process for unexpected termination diff --git a/tauri/src-tauri/src/commands.rs b/tauri/src-tauri/src/commands.rs index 6f855a88..e29a2f9f 100644 --- a/tauri/src-tauri/src/commands.rs +++ b/tauri/src-tauri/src/commands.rs @@ -753,6 +753,35 @@ pub async fn show_welcome_window(state: State<'_, AppState>) -> Result<(), Strin welcome_manager.show_welcome_window().await } +#[tauri::command] +pub fn open_folder(path: String) -> Result<(), String> { + #[cfg(target_os = "macos")] + { + std::process::Command::new("open") + .arg(&path) + .spawn() + .map_err(|e| format!("Failed to open folder: {}", e))?; + } + + #[cfg(target_os = "windows")] + { + std::process::Command::new("explorer") + .arg(&path) + .spawn() + .map_err(|e| format!("Failed to open folder: {}", e))?; + } + + #[cfg(target_os = "linux")] + { + std::process::Command::new("xdg-open") + .arg(&path) + .spawn() + .map_err(|e| format!("Failed to open folder: {}", e))?; + } + + Ok(()) +} + // Advanced Settings Commands #[tauri::command] @@ -1006,6 +1035,30 @@ pub async fn get_permission_stats( Ok(stats) } +#[tauri::command] +pub async fn register_permission_monitoring( + state: State<'_, AppState>, +) -> Result<(), String> { + state.permissions_manager.register_for_monitoring().await; + Ok(()) +} + +#[tauri::command] +pub async fn unregister_permission_monitoring( + state: State<'_, AppState>, +) -> Result<(), String> { + state.permissions_manager.unregister_from_monitoring().await; + Ok(()) +} + +#[tauri::command] +pub async fn show_permission_alert( + state: State<'_, AppState>, + permission_type: crate::permissions::PermissionType, +) -> Result<(), String> { + state.permissions_manager.show_permission_alert(permission_type).await +} + // Update Manager Commands #[tauri::command] pub async fn check_for_updates( @@ -2072,22 +2125,26 @@ pub struct ServerLog { #[tauri::command] pub async fn get_server_logs(limit: usize) -> Result, String> { - // TODO: Implement actual log collection from the server - // For now, return dummy logs for the UI - let logs = vec![ - ServerLog { - timestamp: chrono::Utc::now().to_rfc3339(), - level: "info".to_string(), - message: "Server started on port 4022".to_string(), - }, - ServerLog { - timestamp: chrono::Utc::now().to_rfc3339(), - level: "info".to_string(), - message: "Health check endpoint accessed".to_string(), - }, - ]; + let logs = crate::log_collector::SERVER_LOG_COLLECTOR.get_logs().await; + + // Convert LogEntry to ServerLog + let server_logs: Vec = logs + .into_iter() + .take(limit) + .map(|entry| ServerLog { + timestamp: entry.timestamp, + level: entry.level, + message: entry.message, + }) + .collect(); + + Ok(server_logs) +} - Ok(logs.into_iter().take(limit).collect()) +#[tauri::command] +pub async fn clear_server_logs() -> Result<(), String> { + crate::log_collector::SERVER_LOG_COLLECTOR.clear().await; + Ok(()) } #[tauri::command] @@ -2355,6 +2412,209 @@ pub async fn finish_welcome(state: State<'_, AppState>) -> Result<(), String> { Ok(()) } +// Tailscale commands +#[tauri::command] +pub async fn get_tailscale_status(state: State<'_, AppState>) -> Result { + Ok(state.tailscale_service.check_tailscale_status().await) +} + +#[tauri::command] +pub async fn start_tailscale_monitoring(state: State<'_, AppState>) -> Result<(), String> { + state.tailscale_service.start_monitoring().await; + Ok(()) +} + +#[tauri::command] +pub async fn open_tailscale_app() -> Result<(), String> { + crate::tailscale::TailscaleService::open_tailscale_app() +} + +#[tauri::command] +pub async fn open_tailscale_download() -> Result<(), String> { + crate::tailscale::TailscaleService::open_download_page() +} + +#[tauri::command] +pub async fn open_tailscale_setup_guide() -> Result<(), String> { + crate::tailscale::TailscaleService::open_setup_guide() +} + +// Window tracking commands +#[tauri::command] +pub async fn register_terminal_window( + session_id: String, + terminal_app: String, + tab_reference: Option, + tab_id: Option, + state: State<'_, AppState>, +) -> Result<(), String> { + state.window_tracker + .register_window(session_id, terminal_app, tab_reference, tab_id) + .await; + Ok(()) +} + +#[tauri::command] +pub async fn unregister_terminal_window( + session_id: String, + state: State<'_, AppState>, +) -> Result<(), String> { + state.window_tracker.unregister_window(&session_id).await; + Ok(()) +} + +#[tauri::command] +pub async fn focus_terminal_window( + session_id: String, + state: State<'_, AppState>, +) -> Result<(), String> { + state.window_tracker.focus_window(&session_id).await +} + +#[tauri::command] +pub async fn get_terminal_window_info( + session_id: String, + state: State<'_, AppState>, +) -> Result, String> { + Ok(state.window_tracker.window_info(&session_id).await) +} + +#[tauri::command] +pub async fn update_window_tracking(state: State<'_, AppState>) -> Result<(), String> { + let sessions = state.api_client.list_sessions().await?; + state.window_tracker.update_from_sessions(&sessions).await; + Ok(()) +} + +// AppleScript commands (macOS only) +#[cfg(target_os = "macos")] +#[tauri::command] +pub async fn run_applescript(script: String) -> Result { + crate::applescript::AppleScriptRunner::run_script(&script) +} + +#[cfg(target_os = "macos")] +#[tauri::command] +pub async fn launch_terminal_with_applescript( + terminal_type: String, + session_id: String, + command: Option, + working_directory: Option, +) -> Result { + crate::applescript::AppleScriptTerminalLauncher::launch_terminal( + &terminal_type, + &session_id, + command.as_deref(), + working_directory.as_deref(), + ).await +} + +#[cfg(target_os = "macos")] +#[tauri::command] +pub async fn focus_terminal_with_applescript( + terminal_type: String, + window_info: String, +) -> Result<(), String> { + crate::applescript::AppleScriptTerminalLauncher::focus_terminal_window( + &terminal_type, + &window_info, + ).await +} + +#[cfg(target_os = "macos")] +#[tauri::command] +pub async fn get_terminal_windows_applescript() -> Result, String> { + crate::applescript::AppleScriptRunner::get_terminal_windows() +} + +#[cfg(target_os = "macos")] +#[tauri::command] +pub async fn is_app_running_applescript(app_name: String) -> Result { + crate::applescript::AppleScriptRunner::is_app_running(&app_name) +} + +// Git commands +#[tauri::command] +pub async fn get_git_repository( + file_path: String, + state: State<'_, AppState>, +) -> Result, String> { + Ok(state + .git_monitor + .find_repository(&file_path) + .await) +} + +#[tauri::command] +pub async fn get_cached_git_repository( + file_path: String, + state: State<'_, AppState>, +) -> Result, String> { + Ok(state.git_monitor.get_cached_repository(&file_path).await) +} + +#[tauri::command] +pub async fn clear_git_cache(state: State<'_, AppState>) -> Result<(), String> { + state.git_monitor.clear_cache().await; + Ok(()) +} + +#[tauri::command] +pub async fn start_git_monitoring(state: State<'_, AppState>, app: tauri::AppHandle) -> Result<(), String> { + state.git_monitor.start_monitoring(app).await; + Ok(()) +} + +// Dock Manager Commands +#[tauri::command] +pub fn set_dock_visible(state: State<'_, AppState>, visible: bool) -> Result<(), String> { + super::dock_manager::set_dock_visible(state, visible) +} + +#[tauri::command] +pub fn get_dock_visible(state: State<'_, AppState>) -> bool { + super::dock_manager::get_dock_visible(state) +} + +#[tauri::command] +pub fn update_dock_visibility(state: State<'_, AppState>) -> Result<(), String> { + super::dock_manager::update_dock_visibility(state) +} + +// Status Indicator Commands +#[tauri::command] +pub async fn update_status_indicator( + state: State<'_, AppState>, + server_running: bool, + active_sessions: usize, + total_sessions: usize, +) -> Result<(), String> { + super::status_indicator::update_status_indicator(state, server_running, active_sessions, total_sessions).await +} + +#[tauri::command] +pub async fn flash_activity_indicator( + state: State<'_, AppState>, +) -> Result<(), String> { + super::status_indicator::flash_activity_indicator(state).await +} + +// Power Manager Commands +#[tauri::command] +pub fn prevent_sleep(state: State<'_, AppState>) -> Result<(), String> { + super::power_manager::prevent_sleep(state) +} + +#[tauri::command] +pub fn allow_sleep(state: State<'_, AppState>) -> Result<(), String> { + super::power_manager::allow_sleep(state) +} + +#[tauri::command] +pub fn is_sleep_prevented(state: State<'_, AppState>) -> bool { + super::power_manager::is_sleep_prevented(state) +} + #[cfg(test)] mod tests { use super::*; diff --git a/tauri/src-tauri/src/dock_manager.rs b/tauri/src-tauri/src/dock_manager.rs new file mode 100644 index 00000000..81b7f46a --- /dev/null +++ b/tauri/src-tauri/src/dock_manager.rs @@ -0,0 +1,142 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tauri::{AppHandle, Manager}; +use tracing::{debug, info}; + +/// Manages dock icon visibility on macOS +pub struct DockManager { + should_show_in_dock: Arc, + #[cfg(target_os = "macos")] + window_count: Arc>, +} + +impl DockManager { + pub fn new() -> Self { + Self { + should_show_in_dock: Arc::new(AtomicBool::new(true)), + #[cfg(target_os = "macos")] + window_count: Arc::new(std::sync::Mutex::new(0)), + } + } + + /// Updates the user preference for dock visibility + pub fn set_show_in_dock(&self, show: bool) { + self.should_show_in_dock.store(show, Ordering::Relaxed); + debug!("Dock preference updated: {}", show); + } + + /// Gets the current dock preference + pub fn get_show_in_dock(&self) -> bool { + self.should_show_in_dock.load(Ordering::Relaxed) + } + + /// Updates dock visibility based on window count and user preference + #[cfg(target_os = "macos")] + pub fn update_dock_visibility(&self, app_handle: &AppHandle) { + use tauri::ActivationPolicy; + + let user_wants_dock_hidden = !self.should_show_in_dock.load(Ordering::Relaxed); + + // Count visible windows + let visible_window_count = app_handle + .webview_windows() + .values() + .filter(|window| { + window.is_visible().unwrap_or(false) && + !window.is_minimized().unwrap_or(false) + }) + .count(); + + // Update stored window count + { + let mut count = self.window_count.lock().unwrap(); + *count = visible_window_count; + } + + // Show dock if user wants it shown OR if any windows are open + if !user_wants_dock_hidden || visible_window_count > 0 { + debug!("Showing dock icon (windows: {}, user_hidden: {})", visible_window_count, user_wants_dock_hidden); + let _ = app_handle.set_activation_policy(ActivationPolicy::Regular); + } else { + debug!("Hiding dock icon (windows: {}, user_hidden: {})", visible_window_count, user_wants_dock_hidden); + let _ = app_handle.set_activation_policy(ActivationPolicy::Accessory); + } + } + + /// Force shows the dock icon temporarily + #[cfg(target_os = "macos")] + pub fn temporarily_show_dock(&self, app_handle: &AppHandle) { + use tauri::ActivationPolicy; + info!("Temporarily showing dock icon"); + let _ = app_handle.set_activation_policy(ActivationPolicy::Regular); + } + + /// Called when a window is created + pub fn on_window_created(&self, app_handle: &AppHandle) { + #[cfg(target_os = "macos")] + { + self.temporarily_show_dock(app_handle); + self.update_dock_visibility(app_handle); + } + } + + /// Called when a window is closed + pub fn on_window_closed(&self, app_handle: &AppHandle) { + #[cfg(target_os = "macos")] + { + // Add a small delay to let window state settle + let app_handle = app_handle.clone(); + let dock_manager = DockManager::new(); + dock_manager.set_show_in_dock(self.get_show_in_dock()); + + std::thread::spawn(move || { + std::thread::sleep(std::time::Duration::from_millis(100)); + dock_manager.update_dock_visibility(&app_handle); + }); + } + } + + /// Called when window visibility changes + pub fn on_window_visibility_changed(&self, app_handle: &AppHandle) { + #[cfg(target_os = "macos")] + { + self.update_dock_visibility(app_handle); + } + } + + #[cfg(not(target_os = "macos"))] + pub fn update_dock_visibility(&self, _app_handle: &AppHandle) { + // No-op on non-macOS platforms + } + + #[cfg(not(target_os = "macos"))] + pub fn temporarily_show_dock(&self, _app_handle: &AppHandle) { + // No-op on non-macOS platforms + } +} + +// Public functions for commands (without tauri::command attribute) +pub fn set_dock_visible(state: tauri::State, visible: bool) -> Result<(), String> { + state.dock_manager.set_show_in_dock(visible); + + // Update immediately on the app handle + if let Some(app_handle) = state.get_app_handle() { + state.dock_manager.update_dock_visibility(&app_handle); + } + + Ok(()) +} + +pub fn get_dock_visible(state: tauri::State) -> bool { + state.dock_manager.get_show_in_dock() +} + +pub fn update_dock_visibility(state: tauri::State) -> Result<(), String> { + if let Some(app_handle) = state.get_app_handle() { + state.dock_manager.update_dock_visibility(&app_handle); + Ok(()) + } else { + Err("App handle not available".to_string()) + } +} + diff --git a/tauri/src-tauri/src/git_app_launcher.rs b/tauri/src-tauri/src/git_app_launcher.rs new file mode 100644 index 00000000..1667d505 --- /dev/null +++ b/tauri/src-tauri/src/git_app_launcher.rs @@ -0,0 +1,351 @@ +use serde::{Deserialize, Serialize}; +use std::process::Command; +use tracing::info; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +pub enum GitApp { + Cursor, + Fork, + GitHubDesktop, + GitUp, + SourceTree, + SublimeMerge, + Tower, + VSCode, + Windsurf, +} + +impl GitApp { + pub fn all() -> Vec { + vec![ + Self::Cursor, + Self::Fork, + Self::GitHubDesktop, + Self::GitUp, + Self::SourceTree, + Self::SublimeMerge, + Self::Tower, + Self::VSCode, + Self::Windsurf, + ] + } + + pub fn raw_value(&self) -> &'static str { + match self { + Self::Cursor => "Cursor", + Self::Fork => "Fork", + Self::GitHubDesktop => "GitHub Desktop", + Self::GitUp => "GitUp", + Self::SourceTree => "SourceTree", + Self::SublimeMerge => "Sublime Merge", + Self::Tower => "Tower", + Self::VSCode => "Visual Studio Code", + Self::Windsurf => "Windsurf", + } + } + + pub fn from_raw_value(value: &str) -> Option { + match value { + "Cursor" => Some(Self::Cursor), + "Fork" => Some(Self::Fork), + "GitHub Desktop" => Some(Self::GitHubDesktop), + "GitUp" => Some(Self::GitUp), + "SourceTree" => Some(Self::SourceTree), + "Sublime Merge" => Some(Self::SublimeMerge), + "Tower" => Some(Self::Tower), + "Visual Studio Code" => Some(Self::VSCode), + "Windsurf" => Some(Self::Windsurf), + _ => None, + } + } + + #[cfg(target_os = "macos")] + pub fn bundle_identifier(&self) -> &'static str { + match self { + Self::Cursor => "com.todesktop.230313mzl4w4u92", + Self::Fork => "com.DanPristupov.Fork", + Self::GitHubDesktop => "com.github.GitHubClient", + Self::GitUp => "co.gitup.mac", + Self::SourceTree => "com.torusknot.SourceTreeNotMAS", + Self::SublimeMerge => "com.sublimemerge", + Self::Tower => "com.fournova.Tower3", + Self::VSCode => "com.microsoft.VSCode", + Self::Windsurf => "com.codeiumapp.windsurf", + } + } + + pub fn detection_priority(&self) -> u8 { + match self { + Self::Cursor => 70, + Self::Fork => 75, + Self::GitHubDesktop => 90, + Self::GitUp => 60, + Self::SourceTree => 80, + Self::SublimeMerge => 85, + Self::Tower => 100, + Self::VSCode => 95, + Self::Windsurf => 65, + } + } + + pub fn display_name(&self) -> &'static str { + self.raw_value() + } + + #[cfg(target_os = "macos")] + pub fn is_installed(&self) -> bool { + // Check if app is installed using mdfind + let output = Command::new("mdfind") + .arg(format!("kMDItemCFBundleIdentifier == '{}'", self.bundle_identifier())) + .output() + .ok(); + + output + .map(|o| !o.stdout.is_empty()) + .unwrap_or(false) + } + + #[cfg(target_os = "windows")] + pub fn is_installed(&self) -> bool { + // Check common installation paths on Windows + match self { + Self::VSCode => { + // Check if VS Code is in PATH + Command::new("code") + .arg("--version") + .output() + .is_ok() + } + Self::GitHubDesktop => { + // Check for GitHub Desktop in AppData + let app_data = std::env::var("LOCALAPPDATA").unwrap_or_default(); + std::path::Path::new(&app_data) + .join("GitHubDesktop") + .join("GitHubDesktop.exe") + .exists() + } + Self::Fork => { + // Check for Fork in Program Files + let program_files = std::env::var("ProgramFiles").unwrap_or_default(); + std::path::Path::new(&program_files) + .join("Fork") + .join("Fork.exe") + .exists() + } + Self::SourceTree => { + // Check for SourceTree in AppData + let app_data = std::env::var("LOCALAPPDATA").unwrap_or_default(); + std::path::Path::new(&app_data) + .join("SourceTree") + .join("SourceTree.exe") + .exists() + } + Self::SublimeMerge => { + // Check if Sublime Merge is in PATH + Command::new("smerge") + .arg("--version") + .output() + .is_ok() + } + _ => false, + } + } + + #[cfg(target_os = "linux")] + pub fn is_installed(&self) -> bool { + // Check if application is available in PATH + match self { + Self::VSCode => Command::new("code").arg("--version").output().is_ok(), + Self::SublimeMerge => Command::new("smerge").arg("--version").output().is_ok(), + _ => false, + } + } + + pub fn installed_apps() -> Vec { + Self::all() + .into_iter() + .filter(|app| app.is_installed()) + .collect() + } +} + +pub struct GitAppLauncher; + +impl GitAppLauncher { + /// Open a repository in the preferred Git app + pub fn open_repository(path: &str) -> Result<(), String> { + let git_app = Self::get_preferred_git_app(); + + #[cfg(target_os = "macos")] + { + if let Some(app) = git_app { + // Use open command with bundle identifier + let output = Command::new("open") + .arg("-b") + .arg(app.bundle_identifier()) + .arg(path) + .output() + .map_err(|e| format!("Failed to launch Git app: {}", e))?; + + if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + return Err(format!("Failed to open Git app: {}", stderr)); + } + } else { + // Fallback to opening in Finder + Command::new("open") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to open in Finder: {}", e))?; + } + } + + #[cfg(target_os = "windows")] + { + if let Some(app) = git_app { + match app { + GitApp::VSCode => { + Command::new("code") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to launch VS Code: {}", e))?; + } + GitApp::GitHubDesktop => { + let app_data = std::env::var("LOCALAPPDATA").unwrap_or_default(); + let github_desktop = std::path::Path::new(&app_data) + .join("GitHubDesktop") + .join("GitHubDesktop.exe"); + + Command::new(github_desktop) + .arg(path) + .spawn() + .map_err(|e| format!("Failed to launch GitHub Desktop: {}", e))?; + } + _ => { + // Fallback to Explorer + Command::new("explorer") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to open in Explorer: {}", e))?; + } + } + } else { + // Fallback to Explorer + Command::new("explorer") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to open in Explorer: {}", e))?; + } + } + + #[cfg(target_os = "linux")] + { + if let Some(app) = git_app { + match app { + GitApp::VSCode => { + Command::new("code") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to launch VS Code: {}", e))?; + } + _ => { + // Fallback to file manager + Command::new("xdg-open") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to open in file manager: {}", e))?; + } + } + } else { + // Fallback to file manager + Command::new("xdg-open") + .arg(path) + .spawn() + .map_err(|e| format!("Failed to open in file manager: {}", e))?; + } + } + + Ok(()) + } + + /// Get the preferred Git app from settings + pub fn get_preferred_git_app() -> Option { + if let Ok(settings) = crate::settings::Settings::load() { + if let Some(app_name) = settings.advanced.preferred_git_app { + return GitApp::from_raw_value(&app_name); + } + } + + // If no preference set, auto-detect the best available + Self::auto_detect_git_app() + } + + /// Auto-detect the best available Git app + pub fn auto_detect_git_app() -> Option { + let installed = GitApp::installed_apps(); + + // Sort by priority and return the highest priority app + installed + .into_iter() + .max_by_key(|app| app.detection_priority()) + } + + /// Set the preferred Git app and save to settings + pub fn set_preferred_git_app(app: Option<&GitApp>) -> Result<(), String> { + let mut settings = crate::settings::Settings::load().unwrap_or_default(); + settings.advanced.preferred_git_app = app.map(|a| a.raw_value().to_string()); + settings.save().map_err(|e| e.to_string()) + } + + /// Verify that the preferred Git app is still installed + pub fn verify_preferred_git_app() -> Result<(), String> { + if let Some(app) = Self::get_preferred_git_app() { + if !app.is_installed() { + // Clear the preference if app is no longer installed + Self::set_preferred_git_app(None)?; + info!("Cleared preferred Git app as it's no longer installed"); + } + } + Ok(()) + } +} + +// Commands +#[derive(serde::Serialize)] +pub struct GitAppOption { + pub value: String, + pub label: String, +} + +#[tauri::command] +pub fn get_installed_git_apps() -> Vec { + GitApp::installed_apps() + .into_iter() + .map(|app| GitAppOption { + value: app.raw_value().to_string(), + label: app.raw_value().to_string(), + }) + .collect() +} + +#[tauri::command] +pub fn get_preferred_git_app() -> Option { + GitAppLauncher::get_preferred_git_app() + .map(|app| app.raw_value().to_string()) +} + +#[tauri::command] +pub fn set_preferred_git_app(app: Option) -> Result<(), String> { + let git_app = app.and_then(|name| GitApp::from_raw_value(&name)); + GitAppLauncher::set_preferred_git_app(git_app.as_ref()) +} + +#[tauri::command] +pub fn open_repository_in_git_app(path: String) -> Result<(), String> { + GitAppLauncher::open_repository(&path) +} + +#[tauri::command] +pub fn verify_git_app_installation() -> Result<(), String> { + GitAppLauncher::verify_preferred_git_app() +} \ No newline at end of file diff --git a/tauri/src-tauri/src/git_monitor.rs b/tauri/src-tauri/src/git_monitor.rs new file mode 100644 index 00000000..4efda876 --- /dev/null +++ b/tauri/src-tauri/src/git_monitor.rs @@ -0,0 +1,291 @@ +use crate::git_repository::GitRepository; +use std::collections::HashMap; +use std::path::{Path, PathBuf}; +use std::process::Command; +use std::sync::Arc; +use std::time::Duration; +use tauri::async_runtime::Mutex; +use tauri::{AppHandle, Emitter}; +use tokio::sync::RwLock; +use tokio::time::interval; + +pub struct GitMonitor { + // Cache for repository information by repository path + repository_cache: Arc>>, + // Cache mapping file paths to their repository paths + file_to_repo_cache: Arc>>, + // Cache for GitHub URLs by repository path + github_url_cache: Arc>>, + // Track in-progress GitHub URL fetches + github_url_fetches: Arc>>, +} + +impl GitMonitor { + pub fn new() -> Self { + Self { + repository_cache: Arc::new(RwLock::new(HashMap::new())), + file_to_repo_cache: Arc::new(RwLock::new(HashMap::new())), + github_url_cache: Arc::new(RwLock::new(HashMap::new())), + github_url_fetches: Arc::new(Mutex::new(std::collections::HashSet::new())), + } + } + + /// Get cached repository information synchronously + pub async fn get_cached_repository(&self, file_path: &str) -> Option { + let file_to_repo = self.file_to_repo_cache.read().await; + if let Some(repo_path) = file_to_repo.get(file_path) { + let repos = self.repository_cache.read().await; + return repos.get(repo_path).cloned(); + } + None + } + + /// Find Git repository for a given file path and return its status + pub async fn find_repository(&self, file_path: &str) -> Option { + // Validate path first + if !Self::validate_path(file_path) { + return None; + } + + // Check cache first + if let Some(cached) = self.get_cached_repository(file_path).await { + return Some(cached); + } + + // Find the Git repository root + let repo_path = Self::find_git_root(file_path)?; + + // Check if we already have this repository cached + { + let repos = self.repository_cache.read().await; + if let Some(cached_repo) = repos.get(&repo_path) { + // Cache the file->repo mapping + let mut file_to_repo = self.file_to_repo_cache.write().await; + file_to_repo.insert(file_path.to_string(), repo_path.clone()); + return Some(cached_repo.clone()); + } + } + + // Get repository status + let repository = self.get_repository_status(&repo_path).await?; + + // Cache the result + self.cache_repository(&repository, Some(file_path)).await; + + Some(repository) + } + + /// Clear all caches + pub async fn clear_cache(&self) { + self.repository_cache.write().await.clear(); + self.file_to_repo_cache.write().await.clear(); + self.github_url_cache.write().await.clear(); + self.github_url_fetches.lock().await.clear(); + } + + /// Start monitoring and refreshing all cached repositories + pub async fn start_monitoring(&self, app_handle: AppHandle) { + let cache = self.repository_cache.clone(); + let github_cache = self.github_url_cache.clone(); + let fetches = self.github_url_fetches.clone(); + + tokio::spawn(async move { + let mut refresh_interval = interval(Duration::from_secs(5)); + loop { + refresh_interval.tick().await; + Self::refresh_all_cached(&cache, &github_cache, &fetches).await; + // Emit event to update UI + let _ = app_handle.emit("git-repos-updated", ()); + } + }); + } + + /// Validate and sanitize paths + fn validate_path(path: &str) -> bool { + let path = Path::new(path); + path.is_absolute() && path.exists() + } + + /// Find the Git repository root starting from a given path + fn find_git_root(path: &str) -> Option { + let mut current_path = PathBuf::from(path); + + // If it's a file, start from its directory + if current_path.is_file() { + current_path = current_path.parent()?.to_path_buf(); + } + + // Search up the directory tree to the root + loop { + let git_path = current_path.join(".git"); + if git_path.exists() { + return current_path.to_str().map(|s| s.to_string()); + } + + if !current_path.pop() { + break; + } + } + + None + } + + /// Get repository status by running git status + async fn get_repository_status(&self, repo_path: &str) -> Option { + // Get basic git status + let mut repository = Self::get_basic_git_status(repo_path)?; + + // Check if we have a cached GitHub URL + let github_urls = self.github_url_cache.read().await; + if let Some(url) = github_urls.get(repo_path) { + repository.github_url = Some(url.clone()); + } else { + // Fetch GitHub URL in background + let repo_path_clone = repo_path.to_string(); + let github_cache = self.github_url_cache.clone(); + let fetches = self.github_url_fetches.clone(); + tokio::spawn(async move { + Self::fetch_github_url_background(repo_path_clone, github_cache, fetches).await; + }); + } + + Some(repository) + } + + /// Get basic repository status without GitHub URL + fn get_basic_git_status(repo_path: &str) -> Option { + let output = Command::new("git") + .args(&["status", "--porcelain", "--branch"]) + .current_dir(repo_path) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + let output_str = String::from_utf8(output.stdout).ok()?; + Some(Self::parse_git_status(&output_str, repo_path)) + } + + /// Parse git status --porcelain output + fn parse_git_status(output: &str, repo_path: &str) -> GitRepository { + let lines: Vec<&str> = output.lines().collect(); + let mut current_branch = None; + let mut modified_count = 0; + let mut added_count = 0; + let mut deleted_count = 0; + let mut untracked_count = 0; + + for line in &lines { + let trimmed = line.trim(); + + // Parse branch information (first line with --branch flag) + if trimmed.starts_with("##") { + let branch_info = trimmed[2..].trim(); + // Extract branch name (format: "branch...tracking" or just "branch") + if let Some(dot_index) = branch_info.find('.') { + current_branch = Some(branch_info[..dot_index].to_string()); + } else { + current_branch = Some(branch_info.to_string()); + } + continue; + } + + // Skip empty lines + if trimmed.len() < 2 { + continue; + } + + // Get status code (first two characters) + let status_code = &trimmed[..2]; + + // Count files based on status codes + match status_code { + "??" => untracked_count += 1, + code if code.contains('M') => modified_count += 1, + code if code.contains('A') => added_count += 1, + code if code.contains('D') => deleted_count += 1, + code if code.contains('R') || code.contains('C') => modified_count += 1, + code if code.contains('U') => modified_count += 1, + _ => {} + } + } + + GitRepository { + path: repo_path.to_string(), + modified_count, + added_count, + deleted_count, + untracked_count, + current_branch, + github_url: None, + } + } + + /// Fetch GitHub URL in background and cache it + async fn fetch_github_url_background( + repo_path: String, + github_cache: Arc>>, + fetches: Arc>>, + ) { + // Check if already fetching + { + let mut fetches_guard = fetches.lock().await; + if fetches_guard.contains(&repo_path) { + return; + } + fetches_guard.insert(repo_path.clone()); + } + + // Fetch GitHub URL + if let Some(github_url) = GitRepository::get_github_url(&repo_path) { + github_cache.write().await.insert(repo_path.clone(), github_url); + } + + // Remove from fetches + fetches.lock().await.remove(&repo_path); + } + + /// Refresh all cached repositories + async fn refresh_all_cached( + cache: &Arc>>, + github_cache: &Arc>>, + _fetches: &Arc>>, + ) { + let repo_paths: Vec = { + let repos = cache.read().await; + repos.keys().cloned().collect() + }; + + for repo_path in repo_paths { + if let Some(mut fresh) = Self::get_basic_git_status(&repo_path) { + // Add GitHub URL if cached + let github_urls = github_cache.read().await; + if let Some(url) = github_urls.get(&repo_path) { + fresh.github_url = Some(url.clone()); + } + + cache.write().await.insert(repo_path, fresh); + } + } + } + + /// Cache repository information + async fn cache_repository(&self, repository: &GitRepository, original_file_path: Option<&str>) { + self.repository_cache + .write() + .await + .insert(repository.path.clone(), repository.clone()); + + // Also map the original file path if different from repository path + if let Some(file_path) = original_file_path { + if file_path != repository.path { + self.file_to_repo_cache + .write() + .await + .insert(file_path.to_string(), repository.path.clone()); + } + } + } +} \ No newline at end of file diff --git a/tauri/src-tauri/src/git_repository.rs b/tauri/src-tauri/src/git_repository.rs new file mode 100644 index 00000000..82cbe543 --- /dev/null +++ b/tauri/src-tauri/src/git_repository.rs @@ -0,0 +1,113 @@ +use serde::{Deserialize, Serialize}; +use std::path::Path; +use std::process::Command; + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, Hash)] +pub struct GitRepository { + pub path: String, + pub modified_count: usize, + pub added_count: usize, + pub deleted_count: usize, + pub untracked_count: usize, + pub current_branch: Option, + pub github_url: Option, +} + +impl GitRepository { + pub fn new(path: String) -> Self { + Self { + path, + modified_count: 0, + added_count: 0, + deleted_count: 0, + untracked_count: 0, + current_branch: None, + github_url: None, + } + } + + pub fn has_changes(&self) -> bool { + self.modified_count > 0 + || self.added_count > 0 + || self.deleted_count > 0 + || self.untracked_count > 0 + } + + pub fn total_changed_files(&self) -> usize { + self.modified_count + self.added_count + self.deleted_count + self.untracked_count + } + + pub fn folder_name(&self) -> &str { + Path::new(&self.path) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("") + } + + pub fn status_text(&self) -> String { + if !self.has_changes() { + return "clean".to_string(); + } + + let mut parts = Vec::new(); + if self.modified_count > 0 { + parts.push(format!("{}M", self.modified_count)); + } + if self.added_count > 0 { + parts.push(format!("{}A", self.added_count)); + } + if self.deleted_count > 0 { + parts.push(format!("{}D", self.deleted_count)); + } + if self.untracked_count > 0 { + parts.push(format!("{}U", self.untracked_count)); + } + parts.join(" ") + } + + /// Extract GitHub URL from a repository path + pub fn get_github_url(repo_path: &str) -> Option { + let output = Command::new("git") + .args(&["remote", "get-url", "origin"]) + .current_dir(repo_path) + .output() + .ok()?; + + if !output.status.success() { + return None; + } + + let remote_url = String::from_utf8(output.stdout) + .ok()? + .trim() + .to_string(); + + Self::parse_github_url(&remote_url) + } + + /// Parse GitHub URL from git remote output + fn parse_github_url(remote_url: &str) -> Option { + // Handle HTTPS URLs: https://github.com/user/repo.git + if remote_url.starts_with("https://github.com/") { + let clean_url = if remote_url.ends_with(".git") { + &remote_url[..remote_url.len() - 4] + } else { + remote_url + }; + return Some(clean_url.to_string()); + } + + // Handle SSH URLs: git@github.com:user/repo.git + if remote_url.starts_with("git@github.com:") { + let path_part = &remote_url["git@github.com:".len()..]; + let clean_path = if path_part.ends_with(".git") { + &path_part[..path_part.len() - 4] + } else { + path_part + }; + return Some(format!("https://github.com/{}", clean_path)); + } + + None + } +} \ No newline at end of file diff --git a/tauri/src-tauri/src/lib.rs b/tauri/src-tauri/src/lib.rs index 275d1639..400b9e1f 100644 --- a/tauri/src-tauri/src/lib.rs +++ b/tauri/src-tauri/src/lib.rs @@ -1,23 +1,35 @@ pub mod api_client; pub mod api_testing; pub mod app_mover; +#[cfg(target_os = "macos")] +pub mod applescript; pub mod auth_cache; pub mod auto_launch; pub mod backend_manager; pub mod cli_installer; pub mod commands; pub mod debug_features; +pub mod dock_manager; pub mod errors; pub mod fs_api; +pub mod git_app_launcher; +pub mod git_monitor; +pub mod git_repository; pub mod keychain; +pub mod log_collector; +pub mod menubar_popover; pub mod network_utils; pub mod ngrok; pub mod notification_manager; pub mod permissions; pub mod port_conflict; +pub mod power_manager; +pub mod process_tracker; +pub mod status_indicator; pub mod session_monitor; pub mod settings; pub mod state; +pub mod tailscale; pub mod terminal; pub mod terminal_detector; pub mod terminal_integrations; @@ -27,7 +39,11 @@ pub mod tty_forward; #[cfg(unix)] pub mod unix_socket_server; pub mod updater; +pub mod url_scheme; pub mod welcome; +pub mod window_enumerator; +pub mod window_matcher; +pub mod window_tracker; #[cfg(mobile)] pub fn init() { diff --git a/tauri/src-tauri/src/log_collector.rs b/tauri/src-tauri/src/log_collector.rs new file mode 100644 index 00000000..852f426a --- /dev/null +++ b/tauri/src-tauri/src/log_collector.rs @@ -0,0 +1,73 @@ +use std::collections::VecDeque; +use std::sync::Arc; +use tokio::sync::RwLock; +use serde::{Deserialize, Serialize}; +use tauri::{AppHandle, Emitter}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct LogEntry { + pub timestamp: String, + pub level: String, + pub message: String, +} + +pub struct LogCollector { + buffer: Arc>>, + max_size: usize, + app_handle: Arc>>, +} + +impl LogCollector { + pub fn new(max_size: usize) -> Self { + Self { + buffer: Arc::new(RwLock::new(VecDeque::with_capacity(max_size))), + max_size, + app_handle: Arc::new(RwLock::new(None)), + } + } + + pub async fn set_app_handle(&self, app_handle: AppHandle) { + *self.app_handle.write().await = Some(app_handle); + } + + pub async fn add_log(&self, level: &str, message: String) { + let timestamp = chrono::Local::now().format("%Y-%m-%d %H:%M:%S%.3f").to_string(); + let entry = LogEntry { + timestamp, + level: level.to_string(), + message, + }; + + // Emit to frontend if app handle is available + if let Some(ref app) = *self.app_handle.read().await { + let _ = app.emit("server-log", &entry); + } + + // Add to buffer + let mut buffer = self.buffer.write().await; + if buffer.len() >= self.max_size { + buffer.pop_front(); + } + buffer.push_back(entry); + } + + pub async fn get_logs(&self) -> Vec { + self.buffer.read().await.iter().cloned().collect() + } + + pub async fn clear(&self) { + self.buffer.write().await.clear(); + } +} + +// Global log collector instance +lazy_static::lazy_static! { + pub static ref SERVER_LOG_COLLECTOR: Arc = Arc::new(LogCollector::new(1000)); +} + +// These functions are exposed through commands.rs which already has the tauri::command attributes + +// Initialize the log collector with app handle +pub async fn init_log_collector(app_handle: AppHandle) { + SERVER_LOG_COLLECTOR.set_app_handle(app_handle).await; +} \ No newline at end of file diff --git a/tauri/src-tauri/src/main.rs b/tauri/src-tauri/src/main.rs index 60eca0ff..2304b62a 100644 --- a/tauri/src-tauri/src/main.rs +++ b/tauri/src-tauri/src/main.rs @@ -11,23 +11,35 @@ use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; mod api_client; mod api_testing; mod app_mover; +#[cfg(target_os = "macos")] +mod applescript; mod auth_cache; mod auto_launch; mod backend_manager; mod cli_installer; mod commands; mod debug_features; +mod dock_manager; mod errors; mod fs_api; +mod git_app_launcher; +pub mod git_monitor; +pub mod git_repository; mod keychain; +mod log_collector; +mod menubar_popover; mod network_utils; mod ngrok; mod notification_manager; mod permissions; mod port_conflict; +mod power_manager; +mod process_tracker; mod session_monitor; +mod status_indicator; mod settings; mod state; +mod tailscale; mod terminal; mod terminal_detector; mod terminal_integrations; @@ -37,7 +49,11 @@ mod tty_forward; #[cfg(unix)] mod unix_socket_server; mod updater; +mod url_scheme; mod welcome; +mod window_enumerator; +mod window_matcher; +mod window_tracker; use commands::ServerStatus; use commands::*; @@ -92,7 +108,7 @@ fn open_settings_window(app: AppHandle, tab: Option) -> Result<(), Strin } #[tauri::command] -fn focus_terminal_window(session_id: String) -> Result<(), String> { +fn focus_terminal_window_legacy(session_id: String) -> Result<(), String> { // Focus the terminal window for the given session #[cfg(target_os = "macos")] { @@ -224,7 +240,7 @@ fn main() { show_main_window, open_settings_window, open_session_detail_window, - focus_terminal_window, + focus_terminal_window_legacy, quit_app, settings::get_settings, settings::save_settings, @@ -291,6 +307,9 @@ fn main() { all_required_permissions_granted, open_system_permission_settings, get_permission_stats, + register_permission_monitoring, + unregister_permission_monitoring, + show_permission_alert, check_for_updates, download_update, install_update, @@ -405,6 +424,61 @@ fn main() { save_dashboard_password, open_dashboard, finish_welcome, + open_folder, + // Tailscale commands + get_tailscale_status, + start_tailscale_monitoring, + open_tailscale_app, + open_tailscale_download, + open_tailscale_setup_guide, + // Window tracking commands + register_terminal_window, + unregister_terminal_window, + focus_terminal_window, + get_terminal_window_info, + update_window_tracking, + // AppleScript commands (macOS only) + #[cfg(target_os = "macos")] + run_applescript, + #[cfg(target_os = "macos")] + launch_terminal_with_applescript, + #[cfg(target_os = "macos")] + focus_terminal_with_applescript, + #[cfg(target_os = "macos")] + get_terminal_windows_applescript, + #[cfg(target_os = "macos")] + is_app_running_applescript, + // Git commands + get_git_repository, + get_cached_git_repository, + clear_git_cache, + start_git_monitoring, + // Git app launcher commands + git_app_launcher::get_installed_git_apps, + git_app_launcher::get_preferred_git_app, + git_app_launcher::set_preferred_git_app, + git_app_launcher::open_repository_in_git_app, + git_app_launcher::verify_git_app_installation, + // URL scheme commands + url_scheme::handle_url_scheme, + url_scheme::parse_url_scheme, + // Menubar popover commands + menubar_popover::show_menubar_popover, + menubar_popover::hide_menubar_popover, + menubar_popover::toggle_menubar_popover, + // Server log commands + clear_server_logs, + // Dock manager commands + set_dock_visible, + get_dock_visible, + update_dock_visibility, + // Status indicator commands + update_status_indicator, + flash_activity_indicator, + // Power manager commands + prevent_sleep, + allow_sleep, + is_sleep_prevented, ]) .setup(|app| { // Set app handle in managers @@ -414,6 +488,11 @@ fn main() { let app_handle3 = app.handle().clone(); let app_handle4 = app.handle().clone(); let app_handle_for_move = app.handle().clone(); + let app_handle_for_url_scheme = app.handle().clone(); + let app_handle_for_log_collector = app.handle().clone(); + let app_handle_for_state = app.handle().clone(); + let app_handle_for_dock = app.handle().clone(); + let app_handle_for_status = app.handle().clone(); tauri::async_runtime::spawn(async move { let state = state_clone; @@ -421,7 +500,15 @@ fn main() { state.welcome_manager.set_app_handle(app_handle2).await; state.permissions_manager.set_app_handle(app_handle3).await; state.update_manager.set_app_handle(app_handle4).await; + + // Set app handles for new managers + state.set_app_handle(app_handle_for_state).await; + state.dock_manager.on_window_created(&app_handle_for_dock); + state.status_indicator.set_app_handle(app_handle_for_status); + // Initialize log collector + log_collector::init_log_collector(app_handle_for_log_collector).await; + // Start background workers now that we have a runtime state.terminal_spawn_service.clone().start_worker().await; state.auth_cache_manager.start_cleanup_task().await; @@ -460,7 +547,32 @@ fn main() { // Load updater settings and start auto-check let _ = state.update_manager.load_settings().await; state.update_manager.clone().start_auto_check().await; + + // Start Git repository monitoring + let git_app_handle = app_handle_for_move.clone(); + state.git_monitor.start_monitoring(git_app_handle).await; + + // Start Tailscale monitoring + state.tailscale_service.start_monitoring().await; + // Check initial status + let _ = state.tailscale_service.check_tailscale_status().await; + + // Start window tracking updates + let window_tracker = state.window_tracker.clone(); + let api_client = state.api_client.clone(); + tokio::spawn(async move { + let mut interval = tokio::time::interval(tokio::time::Duration::from_secs(5)); + loop { + interval.tick().await; + if let Ok(sessions) = api_client.list_sessions().await { + window_tracker.update_from_sessions(&sessions).await; + } + } + }); }); + + // Set up URL scheme handler for deep links + url_scheme::URLSchemeHandler::setup_deep_link_handler(app_handle_for_url_scheme); // Create system tray icon using tray-icon.png for macOS (menu-bar-icon.png is for Windows/Linux) let tray_icon = if let Ok(resource_dir) = app.path().resource_dir() { @@ -503,27 +615,38 @@ fn main() { handle_tray_menu_event(app, event.id.as_ref()); }) .on_tray_icon_event(|tray, event| { - if let TrayIconEvent::Click { - button: MouseButton::Left, - button_state: MouseButtonState::Up, - .. - } = event - { - // Get server status and open dashboard in browser - let app = tray.app_handle(); - let state = app.state::(); - if state.backend_manager.blocking_is_running() { - let settings = crate::settings::Settings::load().unwrap_or_default(); - let url = format!("http://127.0.0.1:{}", settings.dashboard.server_port); - let _ = open::that(url); + match event { + TrayIconEvent::Click { + button: MouseButton::Left, + button_state: MouseButtonState::Up, + position, + .. + } => { + // Show custom menubar popover on left click + let app = tray.app_handle(); + + // Update popover position based on tray icon location + let _ = menubar_popover::MenubarPopover::update_position(&app, position.x, position.y); + + // Toggle the popover + let _ = menubar_popover::MenubarPopover::toggle(&app); } + TrayIconEvent::Click { + button: MouseButton::Right, + button_state: MouseButtonState::Up, + .. + } => { + // Right click shows the traditional menu + // The menu is already set, so it will show automatically + } + _ => {} } }) .build(app)?; } // Load settings to determine initial dock icon visibility - let settings = settings::Settings::load().unwrap_or_default(); + let _settings = settings::Settings::load().unwrap_or_default(); // Set initial dock icon visibility on macOS #[cfg(target_os = "macos")] diff --git a/tauri/src-tauri/src/menubar_popover.rs b/tauri/src-tauri/src/menubar_popover.rs new file mode 100644 index 00000000..f3f629a1 --- /dev/null +++ b/tauri/src-tauri/src/menubar_popover.rs @@ -0,0 +1,143 @@ +use tauri::{AppHandle, LogicalPosition, Manager, WebviewUrl, WebviewWindowBuilder}; +use tracing::{debug, error}; + +/// Manages the menubar popover window +pub struct MenubarPopover; + +impl MenubarPopover { + /// Show the menubar popover window + pub fn show(app: &AppHandle) -> Result<(), String> { + debug!("Showing menubar popover"); + + // Check if popover already exists + if let Some(window) = app.get_webview_window("menubar-popover") { + window.show().map_err(|e| e.to_string())?; + window.set_focus().map_err(|e| e.to_string())?; + return Ok(()); + } + + // Get the current mouse position to position the popover + // On macOS, we want to position it near the menu bar + let position = Self::calculate_popover_position(); + + // Create the popover window + let window = WebviewWindowBuilder::new( + app, + "menubar-popover", + WebviewUrl::App("menubar.html".into()) + ) + .title("") + .decorations(false) + .always_on_top(true) + .skip_taskbar(true) + .resizable(false) + .inner_size(360.0, 700.0) + .position(position.0, position.1) + .visible(false) // Start hidden, show after setup + .build() + .map_err(|e| { + error!("Failed to create menubar popover: {}", e); + e.to_string() + })?; + + // Configure window for popover behavior + #[cfg(target_os = "macos")] + { + // For now, skip the macOS-specific configuration + // This would require proper cocoa integration which is complex with Tauri v2 + // The window will still work but won't have the exact native popover behavior + } + + // Handle window events + let app_handle = app.clone(); + window.on_window_event(move |event| { + match event { + tauri::WindowEvent::Focused(false) => { + // Hide popover when it loses focus + if let Some(window) = app_handle.get_webview_window("menubar-popover") { + let _ = window.hide(); + } + } + _ => {} + } + }); + + // Show the window after configuration + window.show().map_err(|e| e.to_string())?; + window.set_focus().map_err(|e| e.to_string())?; + + Ok(()) + } + + /// Hide the menubar popover window + pub fn hide(app: &AppHandle) -> Result<(), String> { + if let Some(window) = app.get_webview_window("menubar-popover") { + window.hide().map_err(|e| e.to_string())?; + } + Ok(()) + } + + /// Toggle the menubar popover window + pub fn toggle(app: &AppHandle) -> Result<(), String> { + if let Some(window) = app.get_webview_window("menubar-popover") { + if window.is_visible().unwrap_or(false) { + Self::hide(app) + } else { + Self::show(app) + } + } else { + Self::show(app) + } + } + + /// Calculate the position for the popover based on screen and menu bar + fn calculate_popover_position() -> (f64, f64) { + // Default position near the top-right of the screen (where menu bar items typically are) + // This is a simplified implementation - in a real app, you'd get the actual + // tray icon position + #[cfg(target_os = "macos")] + { + // Position near the right side of the menu bar + // Menu bar is typically 24px tall on macOS + let x = 100.0; // This should be calculated based on actual tray icon position + let y = 30.0; // Just below the menu bar + return (x, y); + } + + #[cfg(not(target_os = "macos"))] + { + // For other platforms, position at top-right + (100.0, 30.0) + } + } + + /// Update the popover position based on the tray icon location + pub fn update_position(app: &AppHandle, x: f64, y: f64) -> Result<(), String> { + if let Some(window) = app.get_webview_window("menubar-popover") { + // Adjust position to center the popover on the tray icon + let popover_width = 360.0; + let adjusted_x = x - (popover_width / 2.0); + let adjusted_y = y + 10.0; // Small gap below the menu bar + + window.set_position(LogicalPosition::new(adjusted_x, adjusted_y)) + .map_err(|e| e.to_string())?; + } + Ok(()) + } +} + +/// Commands for menubar popover +#[tauri::command] +pub fn show_menubar_popover(app: AppHandle) -> Result<(), String> { + MenubarPopover::show(&app) +} + +#[tauri::command] +pub fn hide_menubar_popover(app: AppHandle) -> Result<(), String> { + MenubarPopover::hide(&app) +} + +#[tauri::command] +pub fn toggle_menubar_popover(app: AppHandle) -> Result<(), String> { + MenubarPopover::toggle(&app) +} \ No newline at end of file diff --git a/tauri/src-tauri/src/permissions.rs b/tauri/src-tauri/src/permissions.rs index feeea910..2a847f80 100644 --- a/tauri/src-tauri/src/permissions.rs +++ b/tauri/src-tauri/src/permissions.rs @@ -2,8 +2,10 @@ use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::sync::Arc; -use tauri::AppHandle; +use std::sync::atomic::{AtomicUsize, Ordering}; +use tauri::{AppHandle, Emitter}; use tokio::sync::RwLock; +use tracing::{debug, info}; /// Permission type enumeration #[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash)] @@ -65,6 +67,8 @@ pub struct PermissionsManager { permissions: Arc>>, app_handle: Arc>>, notification_manager: Option>, + monitor_registration_count: Arc, + monitor_handle: Arc>>>, } impl Default for PermissionsManager { @@ -80,6 +84,8 @@ impl PermissionsManager { permissions: Arc::new(RwLock::new(Self::initialize_permissions())), app_handle: Arc::new(RwLock::new(None)), notification_manager: None, + monitor_registration_count: Arc::new(AtomicUsize::new(0)), + monitor_handle: Arc::new(RwLock::new(None)), } } @@ -390,6 +396,114 @@ impl PermissionsManager { } } + /// Register for permission monitoring (call when a view appears) + pub async fn register_for_monitoring(&self) { + let prev_count = self.monitor_registration_count.fetch_add(1, Ordering::SeqCst); + debug!("Registered for monitoring, count: {}", prev_count + 1); + + if prev_count == 0 { + // First registration, start monitoring + self.start_monitoring().await; + } + } + + /// Unregister from permission monitoring (call when a view disappears) + pub async fn unregister_from_monitoring(&self) { + let prev_count = self.monitor_registration_count.fetch_sub(1, Ordering::SeqCst); + debug!("Unregistered from monitoring, count: {}", prev_count.saturating_sub(1)); + + if prev_count == 1 { + // No more registrations, stop monitoring + self.stop_monitoring().await; + } + } + + /// Start monitoring permissions + async fn start_monitoring(&self) { + info!("Starting permission monitoring"); + + // Initial check + let _ = self.check_all_permissions().await; + + // Clone necessary references for the monitoring task + let permissions = self.permissions.clone(); + let app_handle = self.app_handle.clone(); + let _notification_manager = self.notification_manager.clone(); + + // Start monitoring task + let handle = tokio::spawn(async move { + let mut interval = tokio::time::interval(std::time::Duration::from_secs(1)); + + loop { + interval.tick().await; + + // Check permissions + let mut perms = permissions.write().await; + let mut changed = false; + + for (permission_type, info) in perms.iter_mut() { + let old_status = info.status; + + // Use platform-specific checks + let platform = std::env::consts::OS; + let new_status = match (platform, permission_type) { + #[cfg(target_os = "macos")] + ("macos", PermissionType::ScreenRecording) => { + check_screen_recording_permission_macos_static().await + } + #[cfg(target_os = "macos")] + ("macos", PermissionType::Accessibility) => { + check_accessibility_permission_macos_static().await + } + #[cfg(target_os = "macos")] + ("macos", PermissionType::NotificationAccess) => { + PermissionStatus::Granted // Tauri handles this + } + _ => info.status, // Keep existing status for other permissions + }; + + if old_status != new_status { + info.status = new_status; + info.last_checked = Some(Utc::now()); + changed = true; + } + } + + // Emit permission change event if any changed + if changed { + if let Some(app) = app_handle.read().await.as_ref() { + let _ = app.emit("permissions_updated", ()); + } + } + } + }); + + *self.monitor_handle.write().await = Some(handle); + } + + /// Stop monitoring permissions + async fn stop_monitoring(&self) { + info!("Stopping permission monitoring"); + + if let Some(handle) = self.monitor_handle.write().await.take() { + handle.abort(); + } + } + + /// Show permission alert dialog + pub async fn show_permission_alert(&self, permission_type: PermissionType) -> Result<(), String> { + let permission_info = self.get_permission_info(permission_type).await + .ok_or_else(|| "Permission not found".to_string())?; + + if let Some(app_handle) = self.app_handle.read().await.as_ref() { + // Emit event for frontend to show dialog + app_handle.emit("show_permission_dialog", &permission_info) + .map_err(|e| format!("Failed to emit permission dialog event: {}", e))?; + } + + Ok(()) + } + // Platform-specific implementations #[cfg(target_os = "macos")] async fn check_screen_recording_permission_macos(&self) -> PermissionStatus { @@ -591,3 +705,62 @@ pub struct PermissionStats { pub missing_required: usize, pub platform: String, } + +// Static permission check functions for use in monitoring task +#[cfg(target_os = "macos")] +async fn check_screen_recording_permission_macos_static() -> PermissionStatus { + use std::process::Command; + + let output = Command::new("osascript") + .arg("-e") + .arg("tell application \"System Events\" to get properties") + .output(); + + match output { + Ok(output) if output.status.success() => PermissionStatus::Granted, + _ => PermissionStatus::NotDetermined, + } +} + +// Add enhanced screen recording check for macOS +#[cfg(target_os = "macos")] +fn check_screen_recording_with_cg() -> PermissionStatus { + use core_graphics::display::CGDisplay; + + // Try to get display information - this requires screen recording permission + match CGDisplay::active_displays() { + Ok(displays) => { + if displays.is_empty() { + // No displays found could mean no permission + PermissionStatus::NotDetermined + } else { + PermissionStatus::Granted + } + } + Err(_) => PermissionStatus::Denied, + } +} + +// Tauri commands for enhanced permission functionality are in commands.rs + +#[cfg(target_os = "macos")] +async fn check_accessibility_permission_macos_static() -> PermissionStatus { + use std::process::Command; + + let output = Command::new("osascript") + .arg("-e") + .arg("tell application \"System Events\" to get UI elements enabled") + .output(); + + match output { + Ok(output) if output.status.success() => { + let result = String::from_utf8_lossy(&output.stdout); + if result.trim() == "true" { + PermissionStatus::Granted + } else { + PermissionStatus::Denied + } + } + _ => PermissionStatus::NotDetermined, + } +} diff --git a/tauri/src-tauri/src/power_manager.rs b/tauri/src-tauri/src/power_manager.rs new file mode 100644 index 00000000..14d635b1 --- /dev/null +++ b/tauri/src-tauri/src/power_manager.rs @@ -0,0 +1,243 @@ +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Arc; +use tracing::{debug, info}; + +#[cfg(target_os = "macos")] +use core_foundation::base::TCFType; +#[cfg(target_os = "macos")] +use core_foundation::string::CFString; + +/// Manages system power assertions to prevent the system from sleeping +pub struct PowerManager { + is_prevented: Arc, + #[cfg(target_os = "macos")] + assertion_id: std::sync::Mutex>, + #[cfg(target_os = "windows")] + _previous_state: std::sync::Mutex>, +} + +impl PowerManager { + pub fn new() -> Self { + Self { + is_prevented: Arc::new(AtomicBool::new(false)), + #[cfg(target_os = "macos")] + assertion_id: std::sync::Mutex::new(None), + #[cfg(target_os = "windows")] + _previous_state: std::sync::Mutex::new(None), + } + } + + /// Prevents the system from sleeping + pub fn prevent_sleep(&self) -> Result<(), String> { + if self.is_prevented.load(Ordering::Relaxed) { + debug!("Sleep is already prevented"); + return Ok(()); + } + + #[cfg(target_os = "macos")] + { + self.prevent_sleep_macos()?; + } + + #[cfg(target_os = "windows")] + { + self.prevent_sleep_windows()?; + } + + #[cfg(target_os = "linux")] + { + self.prevent_sleep_linux()?; + } + + self.is_prevented.store(true, Ordering::Relaxed); + info!("System sleep prevention enabled"); + Ok(()) + } + + /// Allows the system to sleep normally + pub fn allow_sleep(&self) -> Result<(), String> { + if !self.is_prevented.load(Ordering::Relaxed) { + debug!("Sleep is already allowed"); + return Ok(()); + } + + #[cfg(target_os = "macos")] + { + self.allow_sleep_macos()?; + } + + #[cfg(target_os = "windows")] + { + self.allow_sleep_windows()?; + } + + #[cfg(target_os = "linux")] + { + self.allow_sleep_linux()?; + } + + self.is_prevented.store(false, Ordering::Relaxed); + info!("System sleep prevention disabled"); + Ok(()) + } + + /// Returns whether sleep is currently prevented + pub fn is_sleep_prevented(&self) -> bool { + self.is_prevented.load(Ordering::Relaxed) + } + + #[cfg(target_os = "macos")] + fn prevent_sleep_macos(&self) -> Result<(), String> { + use std::os::raw::c_void; + + #[link(name = "IOKit", kind = "framework")] + extern "C" { + fn IOPMAssertionCreateWithName( + assertion_type: *const c_void, + assertion_level: u32, + reason: *const c_void, + assertion_id: *mut u32, + ) -> i32; + } + + const kIOPMAssertionLevelOn: u32 = 255; + const kIOReturnSuccess: i32 = 0; + + let assertion_type = CFString::from("NoIdleSleepAssertion"); + let reason = CFString::from("VibeTunnel is running terminal sessions"); + + let mut assertion_id: u32 = 0; + let result = unsafe { + IOPMAssertionCreateWithName( + assertion_type.as_concrete_TypeRef() as *const c_void, + kIOPMAssertionLevelOn, + reason.as_concrete_TypeRef() as *const c_void, + &mut assertion_id, + ) + }; + + if result == kIOReturnSuccess { + let mut guard = self.assertion_id.lock().unwrap(); + *guard = Some(assertion_id); + Ok(()) + } else { + Err(format!("Failed to create power assertion: {}", result)) + } + } + + #[cfg(target_os = "macos")] + fn allow_sleep_macos(&self) -> Result<(), String> { + + #[link(name = "IOKit", kind = "framework")] + extern "C" { + fn IOPMAssertionRelease(assertion_id: u32) -> i32; + } + + const kIOReturnSuccess: i32 = 0; + + let mut guard = self.assertion_id.lock().unwrap(); + if let Some(assertion_id) = guard.take() { + let result = unsafe { IOPMAssertionRelease(assertion_id) }; + + if result == kIOReturnSuccess { + Ok(()) + } else { + Err(format!("Failed to release power assertion: {}", result)) + } + } else { + Ok(()) + } + } + + #[cfg(target_os = "windows")] + fn prevent_sleep_windows(&self) -> Result<(), String> { + use std::os::raw::c_uint; + + #[link(name = "kernel32")] + extern "system" { + fn SetThreadExecutionState(flags: c_uint) -> c_uint; + } + + const ES_CONTINUOUS: c_uint = 0x80000000; + const ES_SYSTEM_REQUIRED: c_uint = 0x00000001; + const ES_DISPLAY_REQUIRED: c_uint = 0x00000002; + + let flags = ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_DISPLAY_REQUIRED; + let result = unsafe { SetThreadExecutionState(flags) }; + + if result == 0 { + Err("Failed to set thread execution state".to_string()) + } else { + let mut guard = self._previous_state.lock().unwrap(); + *guard = Some(result); + Ok(()) + } + } + + #[cfg(target_os = "windows")] + fn allow_sleep_windows(&self) -> Result<(), String> { + use std::os::raw::c_uint; + + #[link(name = "kernel32")] + extern "system" { + fn SetThreadExecutionState(flags: c_uint) -> c_uint; + } + + const ES_CONTINUOUS: c_uint = 0x80000000; + + let result = unsafe { SetThreadExecutionState(ES_CONTINUOUS) }; + + if result == 0 { + Err("Failed to reset thread execution state".to_string()) + } else { + Ok(()) + } + } + + #[cfg(target_os = "linux")] + fn prevent_sleep_linux(&self) -> Result<(), String> { + // On Linux, we can use systemd-inhibit or DBus to prevent sleep + // For now, we'll use a simple implementation + debug!("Linux sleep prevention not implemented"); + Ok(()) + } + + #[cfg(target_os = "linux")] + fn allow_sleep_linux(&self) -> Result<(), String> { + debug!("Linux sleep allowance not implemented"); + Ok(()) + } +} + +impl Drop for PowerManager { + fn drop(&mut self) { + if self.is_prevented.load(Ordering::Relaxed) { + let _ = self.allow_sleep(); + } + } +} + +// Public functions for commands (without tauri::command attribute) +pub fn prevent_sleep(state: tauri::State) -> Result<(), String> { + state.power_manager.prevent_sleep() +} + +pub fn allow_sleep(state: tauri::State) -> Result<(), String> { + state.power_manager.allow_sleep() +} + +pub fn is_sleep_prevented(state: tauri::State) -> bool { + state.power_manager.is_sleep_prevented() +} + +// Integration with session monitoring +impl PowerManager { + /// Updates sleep prevention based on session count and user preferences + pub async fn update_for_sessions(&self, session_count: usize, prevent_sleep_enabled: bool) { + if prevent_sleep_enabled && session_count > 0 { + let _ = self.prevent_sleep(); + } else { + let _ = self.allow_sleep(); + } + } +} \ No newline at end of file diff --git a/tauri/src-tauri/src/process_tracker.rs b/tauri/src-tauri/src/process_tracker.rs new file mode 100644 index 00000000..40d12625 --- /dev/null +++ b/tauri/src-tauri/src/process_tracker.rs @@ -0,0 +1,287 @@ +use tracing::{debug, info, warn}; + +/// Process information +#[derive(Debug, Clone)] +pub struct ProcessInfo { + pub pid: u32, + pub ppid: u32, + pub name: String, +} + +/// Handles process tree traversal and process information extraction +pub struct ProcessTracker; + +impl ProcessTracker { + /// Get the parent process ID of a given process + pub fn get_parent_process_id(pid: u32) -> Option { + #[cfg(target_os = "macos")] + { + Self::get_parent_pid_macos(pid) + } + #[cfg(target_os = "windows")] + { + Self::get_parent_pid_windows(pid) + } + #[cfg(target_os = "linux")] + { + Self::get_parent_pid_linux(pid) + } + } + + /// Get process info including name and parent PID + pub fn get_process_info(pid: u32) -> Option { + #[cfg(target_os = "macos")] + { + Self::get_process_info_macos(pid) + } + #[cfg(target_os = "windows")] + { + Self::get_process_info_windows(pid) + } + #[cfg(target_os = "linux")] + { + Self::get_process_info_linux(pid) + } + } + + /// Log the process tree for debugging + pub fn log_process_tree(pid: u32) { + debug!("Process tree for PID {}:", pid); + + let mut current_pid = pid; + let mut depth = 0; + + while depth < 20 { + if let Some(info) = Self::get_process_info(current_pid) { + let indent = " ".repeat(depth); + debug!("{}PID {}: {} (parent: {})", indent, current_pid, info.name, info.ppid); + + if info.ppid == 0 || info.ppid == 1 { + break; + } + + current_pid = info.ppid; + depth += 1; + } else { + break; + } + } + } + + /// Find the terminal process in the ancestry of a given PID + pub fn find_terminal_ancestor(pid: u32, max_depth: usize) -> Option { + let mut current_pid = pid; + let mut depth = 0; + + while depth < max_depth { + if let Some(parent_pid) = Self::get_parent_process_id(current_pid) { + debug!("Checking ancestor process PID: {} at depth {}", parent_pid, depth + 1); + + // Check if this is a terminal process + if let Some(info) = Self::get_process_info(parent_pid) { + let terminal_processes = vec![ + "Terminal", "iTerm2", "alacritty", "kitty", "wezterm", + "gnome-terminal", "konsole", "xterm", "cmd.exe", "powershell.exe", + "WindowsTerminal.exe" + ]; + + if terminal_processes.iter().any(|&tp| info.name.contains(tp)) { + info!("Found terminal ancestor: {} (PID: {})", info.name, parent_pid); + return Some(parent_pid); + } + } + + current_pid = parent_pid; + depth += 1; + } else { + break; + } + } + + None + } + + #[cfg(target_os = "macos")] + fn get_parent_pid_macos(pid: u32) -> Option { + use std::process::Command; + use std::str; + + // Use ps command which is more reliable and doesn't require unsafe kernel structs + let output = Command::new("ps") + .args(&["-o", "ppid=", "-p", &pid.to_string()]) + .output() + .ok()?; + + if output.status.success() { + let ppid_str = str::from_utf8(&output.stdout).ok()?.trim(); + ppid_str.parse::().ok() + } else { + None + } + } + + #[cfg(target_os = "macos")] + fn get_process_info_macos(pid: u32) -> Option { + use std::process::Command; + + // Use ps command as a fallback for process info + match Command::new("ps") + .args(&["-p", &pid.to_string(), "-o", "ppid=,comm="]) + .output() + { + Ok(output) => { + if output.status.success() { + let output_str = String::from_utf8_lossy(&output.stdout); + let parts: Vec<&str> = output_str.trim().split_whitespace().collect(); + if parts.len() >= 2 { + let ppid = parts[0].parse::().unwrap_or(0); + let name = parts[1..].join(" "); + return Some(ProcessInfo { pid, ppid, name }); + } + } + } + Err(e) => { + warn!("Failed to run ps command: {}", e); + } + } + + // Try to at least get parent PID + if let Some(ppid) = Self::get_parent_pid_macos(pid) { + Some(ProcessInfo { + pid, + ppid, + name: format!("Process {}", pid), + }) + } else { + None + } + } + + #[cfg(target_os = "windows")] + fn get_parent_pid_windows(pid: u32) -> Option { + use windows::Win32::System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, + }; + use windows::Win32::Foundation::HANDLE; + + unsafe { + let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0).ok()?; + + let mut process_entry = PROCESSENTRY32 { + dwSize: std::mem::size_of::() as u32, + ..Default::default() + }; + + if Process32First(snapshot, &mut process_entry).is_ok() { + loop { + if process_entry.th32ProcessID == pid { + let _ = windows::Win32::Foundation::CloseHandle(snapshot); + return Some(process_entry.th32ParentProcessID); + } + + if !Process32Next(snapshot, &mut process_entry).is_ok() { + break; + } + } + } + + let _ = windows::Win32::Foundation::CloseHandle(snapshot); + } + + None + } + + #[cfg(target_os = "windows")] + fn get_process_info_windows(pid: u32) -> Option { + use windows::Win32::System::Diagnostics::ToolHelp::{ + CreateToolhelp32Snapshot, Process32First, Process32Next, PROCESSENTRY32, TH32CS_SNAPPROCESS, + }; + + unsafe { + let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0).ok()?; + + let mut process_entry = PROCESSENTRY32 { + dwSize: std::mem::size_of::() as u32, + ..Default::default() + }; + + if Process32First(snapshot, &mut process_entry).is_ok() { + loop { + if process_entry.th32ProcessID == pid { + let name = String::from_utf16_lossy( + &process_entry.szExeFile + .iter() + .take_while(|&&c| c != 0) + .copied() + .collect::>() + ); + + let _ = windows::Win32::Foundation::CloseHandle(snapshot); + return Some(ProcessInfo { + pid, + ppid: process_entry.th32ParentProcessID, + name, + }); + } + + if !Process32Next(snapshot, &mut process_entry).is_ok() { + break; + } + } + } + + let _ = windows::Win32::Foundation::CloseHandle(snapshot); + } + + None + } + + #[cfg(target_os = "linux")] + fn get_parent_pid_linux(pid: u32) -> Option { + use std::fs; + + // Read /proc/[pid]/stat + let stat_path = format!("/proc/{}/stat", pid); + match fs::read_to_string(&stat_path) { + Ok(contents) => { + // Format: pid (comm) state ppid ... + // Find the closing parenthesis to skip the command name + if let Some(close_paren) = contents.rfind(')') { + let after_name = &contents[close_paren + 1..]; + let fields: Vec<&str> = after_name.split_whitespace().collect(); + + // ppid is the second field after the command name + if fields.len() > 1 { + return fields[1].parse::().ok(); + } + } + } + Err(e) => { + debug!("Failed to read {}: {}", stat_path, e); + } + } + + None + } + + #[cfg(target_os = "linux")] + fn get_process_info_linux(pid: u32) -> Option { + use std::fs; + + // Read /proc/[pid]/stat for ppid + let ppid = Self::get_parent_pid_linux(pid)?; + + // Read /proc/[pid]/comm for process name + let comm_path = format!("/proc/{}/comm", pid); + let name = match fs::read_to_string(&comm_path) { + Ok(contents) => contents.trim().to_string(), + Err(_) => format!("Process {}", pid), + }; + + Some(ProcessInfo { pid, ppid, name }) + } +} + +// Platform-specific dependencies +#[cfg(target_os = "macos")] +extern crate libc; \ No newline at end of file diff --git a/tauri/src-tauri/src/session_monitor.rs b/tauri/src-tauri/src/session_monitor.rs index b7cec5b6..bcd8f109 100644 --- a/tauri/src-tauri/src/session_monitor.rs +++ b/tauri/src-tauri/src/session_monitor.rs @@ -19,6 +19,8 @@ pub struct SessionInfo { pub last_activity: String, pub is_active: bool, pub client_count: usize, + pub working_directory: Option, + pub git_repository: Option, } /// Session state change event @@ -76,6 +78,8 @@ impl SessionMonitor { last_activity: Utc::now().to_rfc3339(), is_active: true, client_count: 0, // TODO: Track actual client count + working_directory: None, // TODO: Get from session API when available + git_repository: None, // Will be populated by Git monitor separately }; // Check if this is a new session diff --git a/tauri/src-tauri/src/settings.rs b/tauri/src-tauri/src/settings.rs index f075ad49..fb74fad1 100644 --- a/tauri/src-tauri/src/settings.rs +++ b/tauri/src-tauri/src/settings.rs @@ -41,6 +41,7 @@ pub struct AdvancedSettings { pub ngrok_subdomain: Option, pub enable_telemetry: Option, pub experimental_features: Option, + pub preferred_git_app: Option, } #[derive(Debug, Serialize, Deserialize, Clone)] @@ -223,6 +224,7 @@ impl Default for Settings { ngrok_subdomain: None, enable_telemetry: Some(false), experimental_features: Some(false), + preferred_git_app: None, }, tty_forward: Some(TTYForwardSettings { enabled: false, diff --git a/tauri/src-tauri/src/state.rs b/tauri/src-tauri/src/state.rs index f38f31a8..77906f55 100644 --- a/tauri/src-tauri/src/state.rs +++ b/tauri/src-tauri/src/state.rs @@ -3,10 +3,15 @@ use crate::api_testing::APITestingManager; use crate::auth_cache::AuthCacheManager; use crate::backend_manager::BackendManager; use crate::debug_features::DebugFeaturesManager; +use crate::dock_manager::DockManager; +use crate::git_monitor::GitMonitor; use crate::ngrok::NgrokManager; use crate::notification_manager::NotificationManager; use crate::permissions::PermissionsManager; +use crate::power_manager::PowerManager; use crate::session_monitor::SessionMonitor; +use crate::status_indicator::StatusIndicator; +use crate::tailscale::TailscaleService; use crate::terminal::TerminalManager; use crate::terminal_integrations::TerminalIntegrationsManager; use crate::terminal_spawn_service::TerminalSpawnService; @@ -15,8 +20,10 @@ use crate::tty_forward::TTYForwardManager; use crate::unix_socket_server::UnixSocketServer; use crate::updater::UpdateManager; use crate::welcome::WelcomeManager; +use crate::window_tracker::WindowTracker; use std::sync::atomic::AtomicBool; use std::sync::Arc; +use tauri::AppHandle; use tokio::sync::RwLock; #[derive(Clone)] @@ -38,6 +45,13 @@ pub struct AppState { pub auth_cache_manager: Arc, pub terminal_integrations_manager: Arc, pub terminal_spawn_service: Arc, + pub git_monitor: Arc, + pub tailscale_service: Arc, + pub window_tracker: Arc, + pub dock_manager: Arc, + pub status_indicator: Arc, + pub power_manager: Arc, + app_handle: Arc>>, #[cfg(unix)] pub unix_socket_server: Arc, } @@ -104,10 +118,27 @@ impl AppState { auth_cache_manager: Arc::new(auth_cache_manager), terminal_integrations_manager, terminal_spawn_service, + git_monitor: Arc::new(GitMonitor::new()), + tailscale_service: Arc::new(TailscaleService::new()), + window_tracker: Arc::new(WindowTracker::new()), + dock_manager: Arc::new(DockManager::new()), + status_indicator: Arc::new(StatusIndicator::new()), + power_manager: Arc::new(PowerManager::new()), + app_handle: Arc::new(RwLock::new(None)), #[cfg(unix)] unix_socket_server, } } + + /// Set the app handle for managers that need it + pub async fn set_app_handle(&self, app_handle: AppHandle) { + *self.app_handle.write().await = Some(app_handle); + } + + /// Get the app handle if available + pub fn get_app_handle(&self) -> Option { + self.app_handle.blocking_read().clone() + } } #[cfg(test)] @@ -135,6 +166,12 @@ mod tests { assert!(Arc::strong_count(&state.terminal_integrations_manager) >= 1); assert!(Arc::strong_count(&state.terminal_spawn_service) >= 1); assert!(Arc::strong_count(&state.tty_forward_manager) >= 1); + assert!(Arc::strong_count(&state.git_monitor) >= 1); + assert!(Arc::strong_count(&state.tailscale_service) >= 1); + assert!(Arc::strong_count(&state.window_tracker) >= 1); + assert!(Arc::strong_count(&state.dock_manager) >= 1); + assert!(Arc::strong_count(&state.status_indicator) >= 1); + assert!(Arc::strong_count(&state.power_manager) >= 1); #[cfg(unix)] assert!(Arc::strong_count(&state.unix_socket_server) >= 1); diff --git a/tauri/src-tauri/src/status_indicator.rs b/tauri/src-tauri/src/status_indicator.rs new file mode 100644 index 00000000..194ec72b --- /dev/null +++ b/tauri/src-tauri/src/status_indicator.rs @@ -0,0 +1,173 @@ +use std::sync::Arc; +use tauri::{AppHandle, Manager}; +use tracing::{debug, error}; + +/// Visual status indicators for the tray icon +pub struct StatusIndicator { + app_handle: Arc>>, +} + +impl StatusIndicator { + pub fn new() -> Self { + Self { + app_handle: Arc::new(std::sync::Mutex::new(None)), + } + } + + /// Set the app handle for the indicator + pub fn set_app_handle(&self, handle: AppHandle) { + let mut guard = self.app_handle.lock().unwrap(); + *guard = Some(handle); + } + + /// Update the tray icon based on server and session status + pub fn update_tray_icon(&self, server_running: bool, active_sessions: usize, total_sessions: usize) { + let guard = self.app_handle.lock().unwrap(); + if let Some(app_handle) = guard.as_ref() { + if let Some(tray) = app_handle.tray_by_id("main") { + // Update icon based on status + let icon_name = if server_running { + if active_sessions > 0 { + "tray-icon-active" // Green/active indicator + } else if total_sessions > 0 { + "tray-icon-idle" // Yellow/idle indicator + } else { + "tray-icon" // Normal running state + } + } else { + "tray-icon-inactive" // Gray/inactive state + }; + + // Try to load the appropriate icon + match Self::load_icon_data(app_handle, icon_name) { + Ok(icon_data) => { + match tauri::image::Image::from_bytes(&icon_data) { + Ok(image) => { + if let Err(e) = tray.set_icon(Some(image)) { + error!("Failed to update tray icon: {}", e); + } + } + Err(e) => { + error!("Failed to create image from icon data: {}", e); + } + } + } + Err(_) => { + // Fall back to default icon with tooltip to indicate status + debug!("Icon {} not found, using default", icon_name); + } + } + + // Update tooltip with session information + let tooltip = self.format_tooltip(server_running, active_sessions, total_sessions); + let _ = tray.set_tooltip(Some(&tooltip)); + + // Update title (visible on macOS) with session count + if total_sessions > 0 { + let title = self.format_title(active_sessions, total_sessions); + let _ = tray.set_title(Some(&title)); + } else { + let _ = tray.set_title::<&str>(None); + } + } + } + } + + /// Format the tray tooltip + fn format_tooltip(&self, server_running: bool, active_sessions: usize, total_sessions: usize) -> String { + if !server_running { + return "VibeTunnel - Server Stopped".to_string(); + } + + if total_sessions == 0 { + return "VibeTunnel - No Sessions".to_string(); + } + + if active_sessions == 0 { + format!("VibeTunnel - {} idle session{}", total_sessions, if total_sessions == 1 { "" } else { "s" }) + } else if active_sessions == total_sessions { + format!("VibeTunnel - {} active session{}", active_sessions, if active_sessions == 1 { "" } else { "s" }) + } else { + format!("VibeTunnel - {} active, {} idle", active_sessions, total_sessions - active_sessions) + } + } + + /// Format the tray title (visible text on macOS) + fn format_title(&self, active_sessions: usize, total_sessions: usize) -> String { + if active_sessions == 0 { + total_sessions.to_string() + } else if active_sessions == total_sessions { + format!("● {}", active_sessions) + } else { + format!("{} | {}", active_sessions, total_sessions - active_sessions) + } + } + + /// Load icon data from resources + fn load_icon_data(app_handle: &AppHandle, name: &str) -> Result, String> { + // Try to load from different icon paths + let icon_paths = vec![ + format!("icons/{}.png", name), + format!("icons/{}@2x.png", name), + format!("{}.png", name), + ]; + + for path in icon_paths { + if let Ok(icon_path) = app_handle.path().resolve(&path, tauri::path::BaseDirectory::Resource) { + if let Ok(contents) = std::fs::read(&icon_path) { + return Ok(contents); + } + } + } + + Err(format!("Icon {} not found", name)) + } + + /// Animate the tray icon for notifications or activity + pub async fn animate_activity(&self) { + // Simple animation: briefly change icon to indicate activity + let app_handle = { + let guard = self.app_handle.lock().unwrap(); + guard.clone() + }; + + if let Some(app_handle) = app_handle { + if let Some(tray) = app_handle.tray_by_id("main") { + // Flash the icon by changing it briefly + if let Ok(active_icon_data) = Self::load_icon_data(&app_handle, "tray-icon-flash") { + if let Ok(active_image) = tauri::image::Image::from_bytes(&active_icon_data) { + let _ = tray.set_icon(Some(active_image)); + + // Restore after a short delay + tokio::time::sleep(tokio::time::Duration::from_millis(200)).await; + + if let Ok(normal_icon_data) = Self::load_icon_data(&app_handle, "tray-icon") { + if let Ok(normal_image) = tauri::image::Image::from_bytes(&normal_icon_data) { + let _ = tray.set_icon(Some(normal_image)); + } + } + } + } + } + } + } +} + +// Public functions for commands (without tauri::command attribute) +pub async fn update_status_indicator( + state: tauri::State<'_, crate::state::AppState>, + server_running: bool, + active_sessions: usize, + total_sessions: usize, +) -> Result<(), String> { + state.status_indicator.update_tray_icon(server_running, active_sessions, total_sessions); + Ok(()) +} + +pub async fn flash_activity_indicator( + state: tauri::State<'_, crate::state::AppState>, +) -> Result<(), String> { + state.status_indicator.animate_activity().await; + Ok(()) +} + diff --git a/tauri/src-tauri/src/tailscale.rs b/tauri/src-tauri/src/tailscale.rs new file mode 100644 index 00000000..c42bf41e --- /dev/null +++ b/tauri/src-tauri/src/tailscale.rs @@ -0,0 +1,205 @@ +use serde::{Deserialize, Serialize}; +use std::sync::Arc; +use std::time::Duration; +use tokio::sync::RwLock; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TailscaleStatus { + pub is_installed: bool, + pub is_running: bool, + pub hostname: Option, + pub ip_address: Option, + pub status_error: Option, +} + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "PascalCase")] +struct TailscaleAPIResponse { + status: String, + device_name: String, + tailnet_name: String, + #[serde(rename = "IPv4")] + ipv4: Option, +} + +pub struct TailscaleService { + status: Arc>, +} + +impl TailscaleService { + const TAILSCALE_API_ENDPOINT: &'static str = "http://100.100.100.100/api/data"; + const API_TIMEOUT: Duration = Duration::from_secs(5); + + pub fn new() -> Self { + Self { + status: Arc::new(RwLock::new(TailscaleStatus { + is_installed: false, + is_running: false, + hostname: None, + ip_address: None, + status_error: None, + })), + } + } + + /// Get the current Tailscale status + pub async fn get_status(&self) -> TailscaleStatus { + self.status.read().await.clone() + } + + /// Check if Tailscale app is installed + fn check_app_installation() -> bool { + #[cfg(target_os = "macos")] + { + std::path::Path::new("/Applications/Tailscale.app").exists() + } + #[cfg(target_os = "linux")] + { + // Check common Linux installation paths + std::path::Path::new("/usr/bin/tailscale").exists() + || std::path::Path::new("/usr/local/bin/tailscale").exists() + || std::path::Path::new("/opt/tailscale/tailscale").exists() + } + #[cfg(target_os = "windows")] + { + // Check Windows installation + std::path::Path::new("C:\\Program Files\\Tailscale\\tailscale.exe").exists() + || std::path::Path::new("C:\\Program Files (x86)\\Tailscale\\tailscale.exe").exists() + } + } + + /// Fetch Tailscale status from the API + async fn fetch_tailscale_status() -> Option { + let client = reqwest::Client::builder() + .timeout(Self::API_TIMEOUT) + .build() + .ok()?; + + match client.get(Self::TAILSCALE_API_ENDPOINT).send().await { + Ok(response) if response.status().is_success() => { + response.json::().await.ok() + } + _ => None, + } + } + + /// Check the current Tailscale status and update properties + pub async fn check_tailscale_status(&self) -> TailscaleStatus { + let is_installed = Self::check_app_installation(); + + if !is_installed { + let status = TailscaleStatus { + is_installed: false, + is_running: false, + hostname: None, + ip_address: None, + status_error: Some("Tailscale is not installed".to_string()), + }; + *self.status.write().await = status.clone(); + return status; + } + + // Try to fetch status from API + match Self::fetch_tailscale_status().await { + Some(api_response) => { + let is_running = api_response.status.to_lowercase() == "running"; + + let (hostname, ip_address, status_error) = if is_running { + // Extract hostname from device name and tailnet name + let device_name = api_response.device_name + .to_lowercase() + .replace(' ', "-"); + let tailnet_name = api_response.tailnet_name + .replace(".ts.net", "") + .replace(".tailscale.net", ""); + + let hostname = format!("{}.{}.ts.net", device_name, tailnet_name); + + (Some(hostname), api_response.ipv4, None) + } else { + (None, None, Some("Tailscale is not running".to_string())) + }; + + let status = TailscaleStatus { + is_installed, + is_running, + hostname, + ip_address, + status_error, + }; + *self.status.write().await = status.clone(); + status + } + None => { + // API not responding - Tailscale not running + let status = TailscaleStatus { + is_installed, + is_running: false, + hostname: None, + ip_address: None, + status_error: Some("Please start the Tailscale app".to_string()), + }; + *self.status.write().await = status.clone(); + status + } + } + } + + /// Start monitoring Tailscale status + pub async fn start_monitoring(&self) { + let status = self.status.clone(); + let service = Self::new(); + + tokio::spawn(async move { + let mut interval = tokio::time::interval(Duration::from_secs(30)); + loop { + interval.tick().await; + let new_status = service.check_tailscale_status().await; + *status.write().await = new_status; + } + }); + } + + /// Open the Tailscale app + pub fn open_tailscale_app() -> Result<(), String> { + #[cfg(target_os = "macos")] + { + open::that("/Applications/Tailscale.app") + .map_err(|e| format!("Failed to open Tailscale app: {}", e)) + } + #[cfg(target_os = "linux")] + { + // Try to launch via desktop file or command + std::process::Command::new("tailscale") + .arg("up") + .spawn() + .map_err(|e| format!("Failed to start Tailscale: {}", e))?; + Ok(()) + } + #[cfg(target_os = "windows")] + { + open::that("C:\\Program Files\\Tailscale\\tailscale.exe") + .or_else(|_| open::that("C:\\Program Files (x86)\\Tailscale\\tailscale.exe")) + .map_err(|e| format!("Failed to open Tailscale app: {}", e)) + } + } + + /// Open the Tailscale download page + pub fn open_download_page() -> Result<(), String> { + let url = if cfg!(target_os = "macos") { + "https://tailscale.com/download/macos" + } else if cfg!(target_os = "windows") { + "https://tailscale.com/download/windows" + } else { + "https://tailscale.com/download/linux" + }; + + open::that(url).map_err(|e| format!("Failed to open download page: {}", e)) + } + + /// Open the Tailscale setup guide + pub fn open_setup_guide() -> Result<(), String> { + open::that("https://tailscale.com/kb/1017/install/") + .map_err(|e| format!("Failed to open setup guide: {}", e)) + } +} \ No newline at end of file diff --git a/tauri/src-tauri/src/terminal_integrations.rs b/tauri/src-tauri/src/terminal_integrations.rs index 21c2b3b0..c98dc761 100644 --- a/tauri/src-tauri/src/terminal_integrations.rs +++ b/tauri/src-tauri/src/terminal_integrations.rs @@ -470,6 +470,35 @@ impl TerminalIntegrationsManager { ) -> Result<(), String> { let emulator = emulator.unwrap_or(*self.default_terminal.read().await); + // Use AppleScript for enhanced terminal control on macOS + #[cfg(target_os = "macos")] + { + match emulator { + TerminalEmulator::Terminal | TerminalEmulator::ITerm2 => { + let session_id = uuid::Uuid::new_v4().to_string(); + let command = options.command.as_deref(); + let working_dir = options.working_directory + .as_ref() + .and_then(|p| p.to_str()); + + let terminal_name = match emulator { + TerminalEmulator::Terminal => "Terminal", + TerminalEmulator::ITerm2 => "iTerm2", + _ => unreachable!(), + }; + + return crate::applescript::AppleScriptTerminalLauncher::launch_terminal( + terminal_name, + &session_id, + command, + working_dir, + ).await + .map(|_| ()); + } + _ => {} + } + } + match emulator { TerminalEmulator::SystemDefault => self.launch_system_terminal(options).await, _ => self.launch_specific_terminal(emulator, options).await, diff --git a/tauri/src-tauri/src/tray_menu.rs b/tauri/src-tauri/src/tray_menu.rs index 19e78e2e..1b450789 100644 --- a/tauri/src-tauri/src/tray_menu.rs +++ b/tauri/src-tauri/src/tray_menu.rs @@ -17,7 +17,7 @@ impl TrayMenuManager { session_count: usize, access_mode: Option, ) -> Result, tauri::Error> { - Self::create_menu_with_sessions(app, server_running, port, session_count, access_mode, None) + Self::create_menu_with_sessions(app, server_running, port, session_count, access_mode, None, None) } pub fn create_menu_with_sessions( @@ -27,6 +27,7 @@ impl TrayMenuManager { session_count: usize, access_mode: Option, sessions: Option>, + tailscale_status: Option, ) -> Result, tauri::Error> { // Server status let status_text = if server_running { @@ -92,7 +93,19 @@ impl TrayMenuManager { dir_name.to_string() }; - let session_text = format!(" • {} (PID: {})", display_name, session.pid); + // Add Git info if available + let session_text = if let Some(git_repo) = &session.git_repository { + let branch = git_repo.current_branch.as_deref().unwrap_or("main"); + let status = if git_repo.has_changes() { + format!(" ({})", git_repo.status_text()) + } else { + String::new() + }; + format!(" • {} [{}{}] (PID: {})", display_name, branch, status, session.pid) + } else { + format!(" • {} (PID: {})", display_name, session.pid) + }; + let session_item = MenuItemBuilder::new(&session_text) .id(format!("session_{}", session.id)) .build(app)?; @@ -165,6 +178,18 @@ impl TrayMenuManager { if let Some(network_item) = network_info { menu_builder = menu_builder.item(&network_item); } + + // Add Tailscale info if available + if let Some(ts_status) = &tailscale_status { + if ts_status.is_running && ts_status.hostname.is_some() { + let tailscale_text = format!("Tailscale: {}", ts_status.hostname.as_ref().unwrap()); + let tailscale_item = MenuItemBuilder::new(&tailscale_text) + .id("tailscale_info") + .enabled(false) + .build(app)?; + menu_builder = menu_builder.item(&tailscale_item); + } + } // Build menu with sessions let mut menu_builder = menu_builder @@ -208,6 +233,9 @@ impl TrayMenuManager { } else { None }; + + // Get Tailscale status + let tailscale_status = state.tailscale_service.get_status().await; // Rebuild menu with new state and sessions if let Ok(menu) = Self::create_menu_with_sessions( @@ -217,6 +245,7 @@ impl TrayMenuManager { session_count, access_mode, Some(sessions), + Some(tailscale_status), ) { if let Err(e) = tray.set_menu(Some(menu)) { tracing::error!("Failed to update tray menu: {}", e); @@ -250,6 +279,9 @@ impl TrayMenuManager { } else { None }; + + // Get Tailscale status + let tailscale_status = state.tailscale_service.get_status().await; // Rebuild menu with new state and sessions if let Ok(menu) = Self::create_menu_with_sessions( @@ -259,6 +291,7 @@ impl TrayMenuManager { count, access_mode, Some(sessions), + Some(tailscale_status), ) { if let Err(e) = tray.set_menu(Some(menu)) { tracing::error!("Failed to update tray menu: {}", e); diff --git a/tauri/src-tauri/src/url_scheme.rs b/tauri/src-tauri/src/url_scheme.rs new file mode 100644 index 00000000..0b2924a5 --- /dev/null +++ b/tauri/src-tauri/src/url_scheme.rs @@ -0,0 +1,241 @@ +use serde::{Deserialize, Serialize}; +use tauri::{Emitter, Listener, Manager}; +use tracing::{debug, error, info}; + +/// URL scheme handler for vibetunnel:// URLs +pub struct URLSchemeHandler; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum URLSchemeAction { + OpenSession { session_id: String }, + CreateSession { name: Option, command: Option }, + OpenSettings { tab: Option }, + ShowWelcome, +} + +impl URLSchemeHandler { + /// Parse a vibetunnel:// URL into an action + pub fn parse_url(url: &str) -> Result { + debug!("Parsing URL scheme: {}", url); + + // Remove the scheme prefix + let url = url.strip_prefix("vibetunnel://") + .ok_or_else(|| "Invalid URL scheme: must start with vibetunnel://".to_string())?; + + // Parse the path and query + let parts: Vec<&str> = url.split('?').collect(); + let path = parts.get(0).unwrap_or(&""); + let query = parts.get(1).unwrap_or(&""); + + // Parse query parameters + let params = Self::parse_query(query); + + // Route based on path + match *path { + "session" | "sessions" => { + if let Some(session_id) = params.get("id") { + Ok(URLSchemeAction::OpenSession { + session_id: session_id.clone(), + }) + } else { + Err("Missing session ID parameter".to_string()) + } + } + "create" | "new" => { + Ok(URLSchemeAction::CreateSession { + name: params.get("name").cloned(), + command: params.get("command").cloned(), + }) + } + "settings" | "preferences" => { + Ok(URLSchemeAction::OpenSettings { + tab: params.get("tab").cloned(), + }) + } + "welcome" => { + Ok(URLSchemeAction::ShowWelcome) + } + "" => { + // Default action - show welcome or main window + Ok(URLSchemeAction::ShowWelcome) + } + _ => { + Err(format!("Unknown URL path: {}", path)) + } + } + } + + /// Parse query string into key-value pairs + fn parse_query(query: &str) -> std::collections::HashMap { + let mut params = std::collections::HashMap::new(); + + for pair in query.split('&') { + if let Some((key, value)) = pair.split_once('=') { + if let Ok(decoded_value) = urlencoding::decode(value) { + params.insert(key.to_string(), decoded_value.to_string()); + } + } + } + + params + } + + /// Handle a URL scheme action + pub async fn handle_action( + action: URLSchemeAction, + app_handle: &tauri::AppHandle, + ) -> Result<(), String> { + info!("Handling URL scheme action: {:?}", action); + + match action { + URLSchemeAction::OpenSession { session_id } => { + // Open session detail window + app_handle.emit("open-session", &session_id) + .map_err(|e| format!("Failed to emit open-session event: {}", e))?; + + // Show main window if needed (synchronous) + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.show(); + let _ = window.set_focus(); + } else { + // Create main window + let window = tauri::WebviewWindowBuilder::new(app_handle, "main", tauri::WebviewUrl::App("index.html".into())) + .title("VibeTunnel") + .inner_size(1200.0, 800.0) + .center() + .resizable(true) + .decorations(true) + .build(); + + if let Ok(window) = window { + let _ = window.show(); + let _ = window.set_focus(); + } + } + } + URLSchemeAction::CreateSession { name, command } => { + // Create new session + let state = app_handle.state::(); + let req = crate::api_client::CreateSessionRequest { + name, + rows: None, + cols: None, + cwd: None, + env: None, + shell: command, + }; + + if let Ok(session) = state.api_client.create_session(req).await { + // Emit event to open the new session + app_handle.emit("open-session", &session.id) + .map_err(|e| format!("Failed to emit open-session event: {}", e))?; + } + + // Show main window (synchronous) + if let Some(window) = app_handle.get_webview_window("main") { + let _ = window.show(); + let _ = window.set_focus(); + } else { + // Create main window + let window = tauri::WebviewWindowBuilder::new(app_handle, "main", tauri::WebviewUrl::App("index.html".into())) + .title("VibeTunnel") + .inner_size(1200.0, 800.0) + .center() + .resizable(true) + .decorations(true) + .build(); + + if let Ok(window) = window { + let _ = window.show(); + let _ = window.set_focus(); + } + } + } + URLSchemeAction::OpenSettings { tab } => { + // Open settings window + let url = if let Some(tab_name) = tab { + format!("settings.html?tab={}", tab_name) + } else { + "settings.html".to_string() + }; + + // Check if settings window already exists + if let Some(window) = app_handle.get_webview_window("settings") { + // Navigate to the URL with the tab parameter if window exists + let _ = window.eval(&format!("window.location.href = '{}'", url)); + let _ = window.show(); + let _ = window.set_focus(); + } else { + // Create new settings window + let window = tauri::WebviewWindowBuilder::new(app_handle, "settings", tauri::WebviewUrl::App(url.into())) + .title("VibeTunnel Settings") + .inner_size(1200.0, 800.0) + .resizable(true) + .decorations(true) + .center() + .build(); + + if let Ok(window) = window { + let _ = window.show(); + let _ = window.set_focus(); + } + } + } + URLSchemeAction::ShowWelcome => { + // Show welcome window through the welcome manager + let state = app_handle.state::(); + let welcome_manager = state.welcome_manager.clone(); + tauri::async_runtime::spawn(async move { + if let Err(e) = welcome_manager.show_welcome_window().await { + error!("Failed to show welcome window: {}", e); + } + }); + } + } + + Ok(()) + } + + /// Set up URL scheme handling for deep links + pub fn setup_deep_link_handler(app_handle: tauri::AppHandle) { + // Clone app_handle for use in the closure + let app_handle_for_closure = app_handle.clone(); + + // Set up listener for deep link events + app_handle.listen("tauri://deep-link", move |event| { + // In Tauri v2, the payload is already a string + let payload = event.payload(); + if let Ok(urls) = serde_json::from_str::>(payload) { + for url in urls { + debug!("Received deep link: {}", url); + + match Self::parse_url(&url) { + Ok(action) => { + let app_handle_clone = app_handle_for_closure.clone(); + tauri::async_runtime::spawn(async move { + if let Err(e) = Self::handle_action(action, &app_handle_clone).await { + error!("Failed to handle URL scheme action: {}", e); + } + }); + } + Err(e) => { + error!("Failed to parse URL scheme: {}", e); + } + } + } + } + }); + } +} + +// Commands for testing URL scheme handling +#[tauri::command] +pub async fn handle_url_scheme(url: String, app: tauri::AppHandle) -> Result<(), String> { + let action = URLSchemeHandler::parse_url(&url)?; + URLSchemeHandler::handle_action(action, &app).await +} + +#[tauri::command] +pub fn parse_url_scheme(url: String) -> Result { + URLSchemeHandler::parse_url(&url) +} \ No newline at end of file diff --git a/tauri/src-tauri/src/window_enumerator.rs b/tauri/src-tauri/src/window_enumerator.rs new file mode 100644 index 00000000..79747869 --- /dev/null +++ b/tauri/src-tauri/src/window_enumerator.rs @@ -0,0 +1,271 @@ +use serde::{Deserialize, Serialize}; +use tracing::debug; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowInfo { + pub window_id: u64, + pub owner_pid: u32, + pub terminal_app: String, + pub session_id: String, + pub created_at: chrono::DateTime, + pub tab_reference: Option, + pub tab_id: Option, + pub bounds: Option, + pub title: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowBounds { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +/// Window enumerator for finding and tracking terminal windows +pub struct WindowEnumerator; + +impl WindowEnumerator { + /// Get all terminal windows currently visible on screen + pub fn get_all_terminal_windows() -> Vec { + #[cfg(target_os = "macos")] + { + Self::get_terminal_windows_macos() + } + #[cfg(target_os = "windows")] + { + Self::get_terminal_windows_windows() + } + #[cfg(target_os = "linux")] + { + Self::get_terminal_windows_linux() + } + } + + #[cfg(target_os = "macos")] + fn get_terminal_windows_macos() -> Vec { + use std::process::Command; + let mut terminal_windows = Vec::new(); + + // Use AppleScript to get window information as a simpler approach + let script = r#" + tell application "System Events" + set terminalApps to {"Terminal", "iTerm2", "Alacritty", "kitty", "WezTerm", "Hyper"} + set windowList to {} + + repeat with appName in terminalApps + if exists application process appName then + tell application process appName + repeat with w in windows + set windowInfo to {appName, (id of w), (name of w), (position of w), (size of w)} + set end of windowList to windowInfo + end repeat + end tell + end if + end repeat + + return windowList + end tell + "#; + + if let Ok(output) = Command::new("osascript") + .arg("-e") + .arg(script) + .output() + { + if output.status.success() { + // Parse the AppleScript output + // This is a simplified version - real implementation would parse the structured output + debug!("Window enumeration via AppleScript completed"); + } + } + + // Fallback: Use ps to find terminal processes + if let Ok(output) = Command::new("ps") + .args(&["-eo", "pid,comm"]) + .output() + { + if output.status.success() { + let output_str = String::from_utf8_lossy(&output.stdout); + for line in output_str.lines() { + let parts: Vec<&str> = line.trim().split_whitespace().collect(); + if parts.len() >= 2 { + if let Ok(pid) = parts[0].parse::() { + let process_name = parts[1..].join(" "); + for terminal in &["Terminal", "iTerm2", "Alacritty", "kitty", "WezTerm", "Hyper"] { + if process_name.contains(terminal) { + terminal_windows.push(WindowInfo { + window_id: pid as u64, + owner_pid: pid, + terminal_app: terminal.to_string(), + session_id: String::new(), + created_at: chrono::Utc::now(), + tab_reference: None, + tab_id: None, + bounds: None, + title: None, + }); + } + } + } + } + } + } + } + + terminal_windows + } + + #[cfg(target_os = "windows")] + fn get_terminal_windows_windows() -> Vec { + use windows::Win32::Foundation::{BOOL, HWND, LPARAM, RECT}; + use windows::Win32::UI::WindowsAndMessaging::{ + EnumWindows, GetClassNameW, GetWindowRect, GetWindowTextW, GetWindowThreadProcessId, IsWindowVisible, + }; + use std::sync::Mutex; + + let terminal_windows = Arc::new(Mutex::new(Vec::new())); + let terminal_windows_clone = terminal_windows.clone(); + + unsafe { + let _ = EnumWindows( + Some(enum_window_callback), + LPARAM(&terminal_windows_clone as *const _ as isize), + ); + } + + unsafe extern "system" fn enum_window_callback(hwnd: HWND, lparam: LPARAM) -> BOOL { + let terminal_windows = &*(lparam.0 as *const Arc>>); + + if IsWindowVisible(hwnd).as_bool() { + let mut class_name = [0u16; 256]; + let class_len = GetClassNameW(hwnd, &mut class_name); + + if class_len > 0 { + let class_str = String::from_utf16_lossy(&class_name[..class_len as usize]); + + // Check for known terminal window classes + let terminal_classes = vec![ + "ConsoleWindowClass", // Windows Terminal, CMD + "CASCADIA_HOSTING_WINDOW_CLASS", // Windows Terminal + "VirtualConsoleClass", // ConEmu + "PuTTY", // PuTTY + ]; + + if terminal_classes.iter().any(|&tc| class_str.contains(tc)) { + let mut title = [0u16; 512]; + let title_len = GetWindowTextW(hwnd, &mut title); + let title_str = if title_len > 0 { + Some(String::from_utf16_lossy(&title[..title_len as usize])) + } else { + None + }; + + let mut process_id: u32 = 0; + GetWindowThreadProcessId(hwnd, Some(&mut process_id)); + + let mut rect = RECT::default(); + let bounds = if GetWindowRect(hwnd, &mut rect).is_ok() { + Some(WindowBounds { + x: rect.left as f64, + y: rect.top as f64, + width: (rect.right - rect.left) as f64, + height: (rect.bottom - rect.top) as f64, + }) + } else { + None + }; + + terminal_windows.lock().unwrap().push(WindowInfo { + window_id: hwnd.0 as u64, + owner_pid: process_id, + terminal_app: class_str, + session_id: String::new(), + created_at: chrono::Utc::now(), + tab_reference: None, + tab_id: None, + bounds, + title: title_str, + }); + } + } + } + + BOOL(1) // Continue enumeration + } + + let result = terminal_windows.lock().unwrap().clone(); + result + } + + #[cfg(target_os = "linux")] + fn get_terminal_windows_linux() -> Vec { + // Use wmctrl or xwininfo to enumerate windows + let mut terminal_windows = Vec::new(); + + // Try using wmctrl first + match Command::new("wmctrl").arg("-lp").output() { + Ok(output) => { + if output.status.success() { + let output_str = String::from_utf8_lossy(&output.stdout); + for line in output_str.lines() { + let parts: Vec<&str> = line.split_whitespace().collect(); + if parts.len() >= 4 { + let window_id = u64::from_str_radix(parts[0].trim_start_matches("0x"), 16).unwrap_or(0); + let pid = parts[2].parse::().unwrap_or(0); + let title = parts[4..].join(" "); + + // Check if it's a terminal by title or other heuristics + let terminal_keywords = vec!["terminal", "konsole", "gnome-terminal", "xterm", "alacritty", "kitty"]; + if terminal_keywords.iter().any(|&kw| title.to_lowercase().contains(kw)) { + terminal_windows.push(WindowInfo { + window_id, + owner_pid: pid, + terminal_app: "Unknown".to_string(), + session_id: String::new(), + created_at: chrono::Utc::now(), + tab_reference: None, + tab_id: None, + bounds: None, + title: Some(title), + }); + } + } + } + } + } + Err(e) => { + warn!("Failed to run wmctrl: {}", e); + } + } + + terminal_windows + } + + /// Extract window ID from terminal tab reference + pub fn extract_window_id(tab_reference: &str) -> Option { + // Extract window ID from tab reference (format: "tab id X of window id Y") + if let Some(pos) = tab_reference.find("window id ") { + let id_str = &tab_reference[pos + 10..]; + if let Some(end_pos) = id_str.find(|c: char| !c.is_numeric()) { + return id_str[..end_pos].parse().ok(); + } else { + return id_str.parse().ok(); + } + } + None + } + + /// Check if a window title contains a specific identifier + pub fn window_title_contains(window: &WindowInfo, identifier: &str) -> bool { + if let Some(ref title) = window.title { + title.contains(identifier) + } else { + false + } + } +} + +// Platform-specific imports +#[cfg(target_os = "windows")] +use std::sync::Arc; \ No newline at end of file diff --git a/tauri/src-tauri/src/window_matcher.rs b/tauri/src-tauri/src/window_matcher.rs new file mode 100644 index 00000000..1b6fb66c --- /dev/null +++ b/tauri/src-tauri/src/window_matcher.rs @@ -0,0 +1,279 @@ +use crate::process_tracker::ProcessTracker; +use crate::window_enumerator::{WindowEnumerator, WindowInfo}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use tracing::{debug, info, warn}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SessionInfo { + pub id: String, + pub pid: Option, + pub working_dir: String, + pub name: Option, + pub activity_status: Option, +} + +/// Handles window matching and session-to-window mapping algorithms +pub struct WindowMatcher { + /// Cache of session to window mappings + session_window_cache: HashMap, +} + +impl WindowMatcher { + pub fn new() -> Self { + Self { + session_window_cache: HashMap::new(), + } + } + + /// Find a window for a specific terminal and session + pub fn find_window<'a>( + &mut self, + terminal_app: &str, + session_id: &str, + session_info: Option<&SessionInfo>, + tab_reference: Option<&str>, + tab_id: Option<&str>, + terminal_windows: &'a [WindowInfo], + ) -> Option<&'a WindowInfo> { + // Check cache first + if let Some(&cached_window_id) = self.session_window_cache.get(session_id) { + if let Some(window) = terminal_windows.iter().find(|w| w.window_id == cached_window_id) { + debug!("Found cached window for session {}: {}", session_id, cached_window_id); + return Some(window); + } + } + + // Filter windows for the specific terminal + let filtered_windows: Vec<&WindowInfo> = terminal_windows + .iter() + .filter(|w| w.terminal_app == terminal_app) + .collect(); + + // First try to find window by process PID traversal + if let Some(session_info) = session_info { + if let Some(session_pid) = session_info.pid { + debug!("Attempting to find window by process PID: {}", session_pid); + + // Log the process tree for debugging + ProcessTracker::log_process_tree(session_pid); + + // Try to find the parent process (shell) that owns this session + if let Some(parent_pid) = ProcessTracker::get_parent_process_id(session_pid) { + debug!("Found parent process PID: {}", parent_pid); + + // Look for a window owned by the parent process + if let Some(matching_window) = filtered_windows.iter().find(|window| { + window.owner_pid == parent_pid + }) { + info!("Found window by parent process match: PID {}", parent_pid); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + + // If direct parent match fails, try to find grandparent or higher ancestors + let mut current_pid = parent_pid; + let mut depth = 0; + while depth < 10 { + if let Some(grandparent_pid) = ProcessTracker::get_parent_process_id(current_pid) { + debug!("Checking ancestor process PID: {} at depth {}", grandparent_pid, depth + 2); + + if let Some(matching_window) = filtered_windows.iter().find(|window| { + window.owner_pid == grandparent_pid + }) { + info!("Found window by ancestor process match: PID {} at depth {}", grandparent_pid, depth + 2); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + + current_pid = grandparent_pid; + depth += 1; + } else { + break; + } + } + } + } + } + + // Fallback: try to find window by title containing session path or command + if let Some(session_info) = session_info { + let working_dir = &session_info.working_dir; + let dir_name = std::path::Path::new(working_dir) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(""); + + // Look for windows whose title contains the directory name + if let Some(matching_window) = filtered_windows.iter().find(|window| { + WindowEnumerator::window_title_contains(window, dir_name) || + WindowEnumerator::window_title_contains(window, working_dir) + }) { + debug!("Found window by directory match: {}", dir_name); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + } + + // For Terminal.app with specific tab reference + if terminal_app == "Terminal" { + if let Some(tab_ref) = tab_reference { + if let Some(window_id) = WindowEnumerator::extract_window_id(tab_ref) { + if let Some(matching_window) = filtered_windows.iter().find(|w| { + w.window_id == window_id + }) { + debug!("Found Terminal.app window by ID: {}", window_id); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + } + } + } + + // For iTerm2 with tab ID + if terminal_app == "iTerm2" { + if let Some(tab_id) = tab_id { + // Try to match by window title which often includes the window ID + if let Some(matching_window) = filtered_windows.iter().find(|window| { + WindowEnumerator::window_title_contains(window, tab_id) + }) { + debug!("Found iTerm2 window by ID in title: {}", tab_id); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + } + } + + // Fallback: return the most recently created window (highest window ID) + if let Some(latest_window) = filtered_windows.iter().max_by_key(|w| w.window_id) { + debug!("Using most recent window as fallback for session: {}", session_id); + self.session_window_cache.insert(session_id.to_string(), latest_window.window_id); + return Some(latest_window); + } + + None + } + + /// Find a terminal window for a session that was attached via `vt` + pub fn find_window_for_session<'a>( + &mut self, + session_id: &str, + session_info: &SessionInfo, + all_windows: &'a [WindowInfo], + ) -> Option<&'a WindowInfo> { + // Check cache first + if let Some(&cached_window_id) = self.session_window_cache.get(session_id) { + if let Some(window) = all_windows.iter().find(|w| w.window_id == cached_window_id) { + debug!("Found cached window for session {}: {}", session_id, cached_window_id); + return Some(window); + } + } + + // First try to find window by process PID traversal + if let Some(session_pid) = session_info.pid { + debug!("Scanning for window by process PID: {} for session {}", session_pid, session_id); + + // Log the process tree for debugging + ProcessTracker::log_process_tree(session_pid); + + // Try to traverse up the process tree to find a terminal window + let mut current_pid = session_pid; + let mut depth = 0; + let max_depth = 20; + + while depth < max_depth { + // Check if any window is owned by this PID + if let Some(matching_window) = all_windows.iter().find(|window| { + window.owner_pid == current_pid + }) { + info!("Found window by PID {} at depth {} for session {}", current_pid, depth, session_id); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + + // Move up to parent process + if let Some(parent_pid) = ProcessTracker::get_parent_process_id(current_pid) { + if parent_pid == 0 || parent_pid == 1 { + // Reached root process + break; + } + current_pid = parent_pid; + depth += 1; + } else { + break; + } + } + + debug!("Process traversal completed at depth {} without finding window", depth); + } + + // Fallback: Find by working directory + let working_dir = &session_info.working_dir; + let dir_name = std::path::Path::new(working_dir) + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or(""); + + debug!("Trying to match by directory: {} or full path: {}", dir_name, working_dir); + + // Look for windows whose title contains the directory name + if let Some(matching_window) = all_windows.iter().find(|window| { + if let Some(ref title) = window.title { + let matches = title.contains(dir_name) || title.contains(working_dir); + if matches { + debug!("Window title '{}' matches directory", title); + } + matches + } else { + false + } + }) { + info!("Found window by directory match: {} for session {}", dir_name, session_id); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + + // Try to match by activity status (for sessions with specific activities) + if let Some(ref activity) = session_info.activity_status { + if !activity.is_empty() { + debug!("Trying to match by activity: {}", activity); + + if let Some(matching_window) = all_windows.iter().find(|window| { + if let Some(ref title) = window.title { + title.contains(activity) + } else { + false + } + }) { + info!("Found window by activity match: {} for session {}", activity, session_id); + self.session_window_cache.insert(session_id.to_string(), matching_window.window_id); + return Some(matching_window); + } + } + } + + warn!("Could not find window for session {} after all attempts", session_id); + debug!("Available windows: {}", all_windows.len()); + for (index, window) in all_windows.iter().enumerate() { + debug!( + " Window {}: PID={}, Terminal={}, Title={}", + index, + window.owner_pid, + window.terminal_app, + window.title.as_deref().unwrap_or("") + ); + } + + None + } + + /// Clear cached window mapping for a session + pub fn clear_session_cache(&mut self, session_id: &str) { + self.session_window_cache.remove(session_id); + } + + /// Clear all cached window mappings + pub fn clear_all_cache(&mut self) { + self.session_window_cache.clear(); + } +} \ No newline at end of file diff --git a/tauri/src-tauri/src/window_tracker.rs b/tauri/src-tauri/src/window_tracker.rs new file mode 100644 index 00000000..17b01516 --- /dev/null +++ b/tauri/src-tauri/src/window_tracker.rs @@ -0,0 +1,341 @@ +use crate::window_enumerator::WindowEnumerator; +use crate::window_matcher::{SessionInfo as MatcherSessionInfo, WindowMatcher}; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; +use std::sync::Arc; +use tokio::sync::RwLock; +use tracing::{debug, info, warn}; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowInfo { + pub window_id: u64, // Changed from u32 to u64 to support all platforms + pub owner_pid: u32, + pub terminal_app: String, + pub session_id: String, + pub created_at: String, + pub tab_reference: Option, + pub tab_id: Option, + pub bounds: Option, + pub title: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct WindowBounds { + pub x: f64, + pub y: f64, + pub width: f64, + pub height: f64, +} + +pub struct WindowTracker { + // Maps session IDs to their terminal window information + session_window_map: Arc>>, + // Window matcher for advanced window finding + window_matcher: Arc>, +} + +impl WindowTracker { + pub fn new() -> Self { + Self { + session_window_map: Arc::new(RwLock::new(HashMap::new())), + window_matcher: Arc::new(RwLock::new(WindowMatcher::new())), + } + } + + /// Register a terminal window for a session + pub async fn register_window( + &self, + session_id: String, + terminal_app: String, + tab_reference: Option, + tab_id: Option, + ) { + info!("Registering window for session: {}, terminal: {}", session_id, terminal_app); + + // For terminals with explicit window/tab info, register immediately + if (terminal_app == "Terminal" && tab_reference.is_some()) || + (terminal_app == "iTerm2" && tab_id.is_some()) { + tokio::time::sleep(tokio::time::Duration::from_millis(500)).await; + + if let Some(window_info) = self.find_window(&terminal_app, &session_id, &tab_reference, &tab_id).await { + self.session_window_map.write().await.insert(session_id.clone(), window_info); + info!("Successfully registered window for session {} with explicit ID", session_id); + } + return; + } + + // For other terminals, use progressive delays to find the window + let delays = [0.5, 1.0, 2.0, 3.0]; + for (index, delay) in delays.iter().enumerate() { + tokio::time::sleep(tokio::time::Duration::from_secs_f64(*delay)).await; + + if let Some(window_info) = self.find_window(&terminal_app, &session_id, &tab_reference, &tab_id).await { + self.session_window_map.write().await.insert(session_id.clone(), window_info); + info!("Successfully registered window for session {} after {} attempts", session_id, index + 1); + return; + } + } + + warn!("Failed to register window for session {} after all attempts", session_id); + } + + /// Unregister a window for a session + pub async fn unregister_window(&self, session_id: &str) { + if self.session_window_map.write().await.remove(session_id).is_some() { + info!("Unregistered window for session: {}", session_id); + } + } + + /// Get window information for a specific session + pub async fn window_info(&self, session_id: &str) -> Option { + self.session_window_map.read().await.get(session_id).cloned() + } + + /// Get all tracked windows + pub async fn all_tracked_windows(&self) -> Vec { + self.session_window_map.read().await.values().cloned().collect() + } + + /// Focus the terminal window for a specific session + pub async fn focus_window(&self, session_id: &str) -> Result<(), String> { + let window_info = self.window_info(session_id).await + .ok_or_else(|| format!("No window registered for session: {}", session_id))?; + + info!("Focusing window for session: {}, terminal: {}", session_id, window_info.terminal_app); + + // Platform-specific window focusing + #[cfg(target_os = "macos")] + { + self.focus_window_macos(&window_info).await + } + #[cfg(target_os = "windows")] + { + self.focus_window_windows(&window_info).await + } + #[cfg(target_os = "linux")] + { + self.focus_window_linux(&window_info).await + } + } + + /// Update window tracking based on current sessions + pub async fn update_from_sessions(&self, sessions: &[crate::api_client::SessionResponse]) { + let session_ids: std::collections::HashSet = sessions.iter() + .map(|s| s.id.clone()) + .collect(); + + // Remove windows for sessions that no longer exist + let mut window_map = self.session_window_map.write().await; + let tracked_sessions: Vec = window_map.keys().cloned().collect(); + + for session_id in tracked_sessions { + if !session_ids.contains(&session_id) { + window_map.remove(&session_id); + info!("Removed window tracking for terminated session: {}", session_id); + } + } + drop(window_map); + + // Try to find windows for sessions without registered windows + for session in sessions { + if self.window_info(&session.id).await.is_none() { + debug!("Session {} has no window registered, attempting to find it...", session.id); + + if let Some(window_info) = self.find_window_for_session(&session.id).await { + self.session_window_map.write().await.insert(session.id.clone(), window_info); + info!("Found and registered window for session: {}", session.id); + } else { + debug!("Could not find window for session: {}", session.id); + } + } + } + } + + // Advanced window finding using the new components + async fn find_window( + &self, + terminal_app: &str, + session_id: &str, + tab_reference: &Option, + tab_id: &Option, + ) -> Option { + // Get all terminal windows using WindowEnumerator + let terminal_windows = WindowEnumerator::get_all_terminal_windows(); + + // For testing, also try to get session info from API if available + let session_info = None; // In a real implementation, this would query the server + + // Use WindowMatcher to find the matching window + let mut matcher = self.window_matcher.write().await; + + if let Some(matched_window) = matcher.find_window( + terminal_app, + session_id, + session_info, + tab_reference.as_deref(), + tab_id.as_deref(), + &terminal_windows, + ) { + // Convert from EnumeratedWindowInfo to our WindowInfo + Some(WindowInfo { + window_id: matched_window.window_id, // No cast needed, already u64 + owner_pid: matched_window.owner_pid, + terminal_app: matched_window.terminal_app.clone(), + session_id: session_id.to_string(), + created_at: matched_window.created_at.to_rfc3339(), + tab_reference: matched_window.tab_reference.clone(), + tab_id: matched_window.tab_id.clone(), + bounds: matched_window.bounds.as_ref().map(|b| WindowBounds { + x: b.x, + y: b.y, + width: b.width, + height: b.height, + }), + title: matched_window.title.clone(), + }) + } else { + None + } + } + + async fn find_window_for_session(&self, session_id: &str) -> Option { + // Get all terminal windows + let terminal_windows = WindowEnumerator::get_all_terminal_windows(); + + // Create a minimal session info for matching + let session_info = MatcherSessionInfo { + id: session_id.to_string(), + pid: None, // Would be filled from actual session data + working_dir: String::new(), + name: None, + activity_status: None, + }; + + // Use WindowMatcher to find the window + let mut matcher = self.window_matcher.write().await; + + if let Some(matched_window) = matcher.find_window_for_session( + session_id, + &session_info, + &terminal_windows, + ) { + // Convert from EnumeratedWindowInfo to our WindowInfo + Some(WindowInfo { + window_id: matched_window.window_id, // No cast needed, already u64 + owner_pid: matched_window.owner_pid, + terminal_app: matched_window.terminal_app.clone(), + session_id: session_id.to_string(), + created_at: matched_window.created_at.to_rfc3339(), + tab_reference: matched_window.tab_reference.clone(), + tab_id: matched_window.tab_id.clone(), + bounds: matched_window.bounds.as_ref().map(|b| WindowBounds { + x: b.x, + y: b.y, + width: b.width, + height: b.height, + }), + title: matched_window.title.clone(), + }) + } else { + None + } + } + + // Platform-specific window focusing implementations + #[cfg(target_os = "macos")] + async fn focus_window_macos(&self, window_info: &WindowInfo) -> Result<(), String> { + use std::process::Command; + + // First activate the application + let script = format!( + r#"tell application "{}" to activate"#, + window_info.terminal_app + ); + + let output = Command::new("osascript") + .arg("-e") + .arg(&script) + .output() + .map_err(|e| format!("Failed to run AppleScript: {}", e))?; + + if !output.status.success() { + let error = String::from_utf8_lossy(&output.stderr); + return Err(format!("AppleScript failed: {}", error)); + } + + // For Terminal.app, also try to focus specific tab if we have tab reference + if window_info.terminal_app == "Terminal" { + if let Some(tab_ref) = &window_info.tab_reference { + let focus_script = format!( + r#"tell application "Terminal" + set selected of {} to true + activate + end tell"#, + tab_ref + ); + + let _ = Command::new("osascript") + .arg("-e") + .arg(&focus_script) + .output(); + } + } + + Ok(()) + } + + #[cfg(target_os = "windows")] + async fn focus_window_windows(&self, window_info: &WindowInfo) -> Result<(), String> { + // Use Windows API to focus window + #[cfg(windows)] + { + use windows::Win32::Foundation::HWND; + use windows::Win32::UI::WindowsAndMessaging::{SetForegroundWindow, ShowWindow, SW_RESTORE}; + + let hwnd = HWND(window_info.window_id as isize); + unsafe { + ShowWindow(hwnd, SW_RESTORE); + SetForegroundWindow(hwnd); + } + Ok(()) + } + #[cfg(not(windows))] + { + Err("Window focusing not implemented for Windows".to_string()) + } + } + + #[cfg(target_os = "linux")] + async fn focus_window_linux(&self, window_info: &WindowInfo) -> Result<(), String> { + use std::process::Command; + + // Try using wmctrl to focus the window + let output = Command::new("wmctrl") + .arg("-i") + .arg("-a") + .arg(format!("0x{:x}", window_info.window_id)) + .output(); + + match output { + Ok(result) => { + if result.status.success() { + Ok(()) + } else { + Err("wmctrl failed to focus window".to_string()) + } + } + Err(_) => { + // Try xdotool as fallback + let xdotool_output = Command::new("xdotool") + .arg("windowactivate") + .arg(window_info.window_id.to_string()) + .output(); + + match xdotool_output { + Ok(result) if result.status.success() => Ok(()), + _ => Err("Failed to focus window on Linux".to_string()) + } + } + } + } +} \ No newline at end of file diff --git a/tauri/src-tauri/tauri.conf.json b/tauri/src-tauri/tauri.conf.json index d01b290e..284c5967 100644 --- a/tauri/src-tauri/tauri.conf.json +++ b/tauri/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2.0.0", "productName": "VibeTunnel", - "identifier": "com.vibetunnel.app", + "identifier": "sh.vibetunnel.tauri", "build": { "beforeDevCommand": "cd ../web && npm run build", "beforeBuildCommand": "cd ../web && npm run build", @@ -16,6 +16,8 @@ "bundle": { "active": true, "targets": "all", + "publisher": "VibeTunnel Team", + "homepage": "https://vibetunnel.sh", "icon": [ "icons/32x32.png", "icons/128x128.png", @@ -39,7 +41,10 @@ "exceptionDomain": "localhost", "signingIdentity": null, "providerShortName": null, - "entitlements": "entitlements.plist" + "entitlements": "entitlements.plist", + "files": { + "Info.plist": "./Info.plist" + } }, "windows": { "certificateThumbprint": null, diff --git a/tauri/src/components/settings-app.ts b/tauri/src/components/settings-app.ts index 823e2210..14cf41b8 100644 --- a/tauri/src/components/settings-app.ts +++ b/tauri/src/components/settings-app.ts @@ -28,6 +28,7 @@ interface SettingsData { debug_mode?: boolean; cleanup_on_startup?: boolean; preferred_terminal?: string; + preferred_git_app?: string; }; } @@ -93,6 +94,9 @@ export class SettingsApp extends TauriBase { @state() private diagnosticReport: any = null; + + @state() + private installedGitApps: Array<{value: string, label: string}> = []; static override styles = [ formStyles, css` @@ -879,6 +883,9 @@ export class SettingsApp extends TauriBase { if (this.debugMode) { await this.loadDebugData(); } + + // Load installed Git apps + await this.loadInstalledGitApps(); } } @@ -906,6 +913,15 @@ export class SettingsApp extends TauriBase { } } + private async loadInstalledGitApps(): Promise { + try { + const apps = await this.safeInvoke>('get_installed_git_apps'); + this.installedGitApps = apps || []; + } catch (error) { + console.error('Failed to load installed Git apps:', error); + } + } + private applyTheme(theme: 'system' | 'light' | 'dark'): void { const htmlElement = document.documentElement; @@ -1291,6 +1307,21 @@ export class SettingsApp extends TauriBase { > +
+

Git Applications

+ +
+

Advanced Options

+ + + + + VibeTunnel Menu + + + + + + + + \ No newline at end of file diff --git a/tauri/src/server-console.html b/tauri/src/server-console.html index 37c3c0c9..7f0469f1 100644 --- a/tauri/src/server-console.html +++ b/tauri/src/server-console.html @@ -5,30 +5,467 @@ Server Console - VibeTunnel - - +
+

Server Console

+
+ + +
+
+ +
+
+
+ +
+ + + + + +
+
+ +
+ +
+
+ + + + + + Waiting for server logs... +
+
+
+ +
+
+
+ Connected to server +
+
0 logs
+
+ + \ No newline at end of file diff --git a/web/build-native.js b/web/build-native.js index b6e88fe7..b4f536d6 100755 --- a/web/build-native.js +++ b/web/build-native.js @@ -356,40 +356,51 @@ async function main() { console.warn('Warning: Using current time for build - output will not be reproducible'); } - let esbuildCmd = `NODE_NO_WARNINGS=1 npx esbuild src/cli.ts \\ - --bundle \\ - --platform=node \\ - --target=node20 \\ - --outfile=build/bundle.js \\ - --format=cjs \\ - --keep-names \\ - --external:authenticate-pam \\ - --external:../build/Release/pty.node \\ - --external:./build/Release/pty.node \\ - --define:process.env.BUILD_DATE='"${buildDate}"' \\ - --define:process.env.BUILD_TIMESTAMP='"${buildTimestamp}"' \\ - --define:process.env.VIBETUNNEL_SEA='"true"'`; + // Build command array for cross-platform compatibility + const esbuildArgs = [ + 'src/cli.ts', + '--bundle', + '--platform=node', + '--target=node20', + '--outfile=build/bundle.js', + '--format=cjs', + '--keep-names', + '--external:authenticate-pam', + '--external:../build/Release/pty.node', + '--external:./build/Release/pty.node', + `--define:process.env.BUILD_DATE='"${buildDate}"'`, + `--define:process.env.BUILD_TIMESTAMP='"${buildTimestamp}"'`, + '--define:process.env.VIBETUNNEL_SEA=\'"true"\'' + ]; // Also inject git commit hash for version tracking try { const gitCommit = execSync('git rev-parse --short HEAD', { encoding: 'utf8' }).trim(); - esbuildCmd += ` \\\n --define:process.env.GIT_COMMIT='"${gitCommit}"'`; + esbuildArgs.push(`--define:process.env.GIT_COMMIT='"${gitCommit}"'`); } catch (e) { - esbuildCmd += ` \\\n --define:process.env.GIT_COMMIT='"unknown"'`; + esbuildArgs.push('--define:process.env.GIT_COMMIT=\'"unknown"\''); } if (includeSourcemaps) { - esbuildCmd += ' \\\n --sourcemap=inline \\\n --source-root=/'; + esbuildArgs.push('--sourcemap=inline', '--source-root=/'); } - console.log('Running:', esbuildCmd); - execSync(esbuildCmd, { + console.log('Running: npx esbuild', esbuildArgs.join(' ')); + + // Use spawn for better cross-platform compatibility + const { spawnSync } = require('child_process'); + const result = spawnSync('npx', ['esbuild', ...esbuildArgs], { stdio: 'inherit', env: { ...process.env, NODE_NO_WARNINGS: '1' - } + }, + shell: process.platform === 'win32' }); + + if (result.status !== 0) { + throw new Error(`esbuild failed with exit code ${result.status}`); + } // 2b. Post-process bundle to ensure VIBETUNNEL_SEA is properly set console.log('\nPost-processing bundle for SEA compatibility...'); diff --git a/web/package.json b/web/package.json index 78f8e453..7ecad39e 100644 --- a/web/package.json +++ b/web/package.json @@ -53,42 +53,42 @@ ] }, "dependencies": { - "@codemirror/commands": "^6.6.2", - "@codemirror/lang-css": "^6.2.1", + "@codemirror/commands": "^6.8.1", + "@codemirror/lang-css": "^6.3.1", "@codemirror/lang-html": "^6.4.9", - "@codemirror/lang-javascript": "^6.2.2", - "@codemirror/lang-json": "^6.0.1", - "@codemirror/lang-markdown": "^6.2.5", - "@codemirror/lang-python": "^6.1.6", - "@codemirror/state": "^6.4.1", - "@codemirror/theme-one-dark": "^6.1.2", - "@codemirror/view": "^6.28.0", + "@codemirror/lang-javascript": "^6.2.4", + "@codemirror/lang-json": "^6.0.2", + "@codemirror/lang-markdown": "^6.3.3", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/state": "^6.5.2", + "@codemirror/theme-one-dark": "^6.1.3", + "@codemirror/view": "^6.38.0", "@xterm/headless": "^5.5.0", "authenticate-pam": "^1.0.5", "chalk": "^4.1.2", - "express": "^4.19.2", + "express": "^4.21.2", "jsonwebtoken": "^9.0.2", "lit": "^3.3.0", "mime-types": "^3.0.1", "monaco-editor": "^0.52.2", "multer": "^2.0.1", "node-pty": "github:microsoft/node-pty#v1.1.0-beta34", - "postject": "^1.0.0-alpha.6", + "postject": "1.0.0-alpha.6", "signal-exit": "^4.1.0", "web-push": "^3.6.7", - "ws": "^8.18.2" + "ws": "^8.18.3" }, "devDependencies": { - "@biomejs/biome": "^2.0.5", + "@biomejs/biome": "^2.0.6", "@open-wc/testing": "^4.0.0", - "@playwright/test": "^1.53.1", + "@playwright/test": "^1.53.2", "@prettier/plugin-oxc": "^0.0.4", "@testing-library/dom": "^10.4.0", - "@types/express": "^4.17.21", + "@types/express": "^4.17.23", "@types/jsonwebtoken": "^9.0.10", "@types/mime-types": "^3.0.1", "@types/multer": "^1.4.13", - "@types/node": "^24.0.3", + "@types/node": "^24.0.10", "@types/supertest": "^6.0.3", "@types/uuid": "^10.0.0", "@types/web-push": "^3.6.4", @@ -98,15 +98,15 @@ "autoprefixer": "^10.4.21", "chokidar": "^4.0.3", "chokidar-cli": "^3.0.0", - "concurrently": "^9.1.2", + "concurrently": "^9.2.0", "esbuild": "^0.25.5", "happy-dom": "^18.0.1", "husky": "^9.1.7", "lint-staged": "^16.1.2", "node-fetch": "^3.3.2", "postcss": "^8.5.6", - "prettier": "^3.6.1", - "puppeteer": "^24.10.2", + "prettier": "^3.6.2", + "puppeteer": "^24.11.2", "supertest": "^7.1.1", "tailwindcss": "^3.4.17", "tsx": "^4.20.3", diff --git a/web/playwright.config.ts b/web/playwright.config.ts index aa495f8e..eb0b7327 100644 --- a/web/playwright.config.ts +++ b/web/playwright.config.ts @@ -127,6 +127,7 @@ export default defineConfig({ env: { ...process.env, // Include all existing env vars NODE_ENV: 'test', + PLAYWRIGHT_TEST: 'true', // Enable test mode for CSP VIBETUNNEL_DISABLE_PUSH_NOTIFICATIONS: 'true', SUPPRESS_CLIENT_ERRORS: 'true', VIBETUNNEL_SEA: '', // Explicitly set to empty to disable SEA loader diff --git a/web/pnpm-lock.yaml b/web/pnpm-lock.yaml index 0927cad2..80d0525b 100644 --- a/web/pnpm-lock.yaml +++ b/web/pnpm-lock.yaml @@ -9,35 +9,35 @@ importers: .: dependencies: '@codemirror/commands': - specifier: ^6.6.2 + specifier: ^6.8.1 version: 6.8.1 '@codemirror/lang-css': - specifier: ^6.2.1 + specifier: ^6.3.1 version: 6.3.1 '@codemirror/lang-html': specifier: ^6.4.9 version: 6.4.9 '@codemirror/lang-javascript': - specifier: ^6.2.2 + specifier: ^6.2.4 version: 6.2.4 '@codemirror/lang-json': - specifier: ^6.0.1 + specifier: ^6.0.2 version: 6.0.2 '@codemirror/lang-markdown': - specifier: ^6.2.5 + specifier: ^6.3.3 version: 6.3.3 '@codemirror/lang-python': - specifier: ^6.1.6 + specifier: ^6.2.1 version: 6.2.1 '@codemirror/state': - specifier: ^6.4.1 + specifier: ^6.5.2 version: 6.5.2 '@codemirror/theme-one-dark': - specifier: ^6.1.2 + specifier: ^6.1.3 version: 6.1.3 '@codemirror/view': - specifier: ^6.28.0 - version: 6.37.2 + specifier: ^6.38.0 + version: 6.38.0 '@xterm/headless': specifier: ^5.5.0 version: 5.5.0 @@ -48,7 +48,7 @@ importers: specifier: ^4.1.2 version: 4.1.2 express: - specifier: ^4.19.2 + specifier: ^4.21.2 version: 4.21.2 jsonwebtoken: specifier: ^9.0.2 @@ -69,7 +69,7 @@ importers: specifier: github:microsoft/node-pty#v1.1.0-beta34 version: https://codeload.github.com/microsoft/node-pty/tar.gz/d738123f1faf7287513b0df8b9e327be54702e94 postject: - specifier: ^1.0.0-alpha.6 + specifier: 1.0.0-alpha.6 version: 1.0.0-alpha.6 signal-exit: specifier: ^4.1.0 @@ -78,17 +78,17 @@ importers: specifier: ^3.6.7 version: 3.6.7 ws: - specifier: ^8.18.2 - version: 8.18.2 + specifier: ^8.18.3 + version: 8.18.3 devDependencies: '@biomejs/biome': - specifier: ^2.0.5 - version: 2.0.5 + specifier: ^2.0.6 + version: 2.0.6 '@open-wc/testing': specifier: ^4.0.0 version: 4.0.0 '@playwright/test': - specifier: ^1.53.1 + specifier: ^1.53.2 version: 1.53.2 '@prettier/plugin-oxc': specifier: ^0.0.4 @@ -97,7 +97,7 @@ importers: specifier: ^10.4.0 version: 10.4.0 '@types/express': - specifier: ^4.17.21 + specifier: ^4.17.23 version: 4.17.23 '@types/jsonwebtoken': specifier: ^9.0.10 @@ -109,8 +109,8 @@ importers: specifier: ^1.4.13 version: 1.4.13 '@types/node': - specifier: ^24.0.3 - version: 24.0.4 + specifier: ^24.0.10 + version: 24.0.10 '@types/supertest': specifier: ^6.0.3 version: 6.0.3 @@ -139,7 +139,7 @@ importers: specifier: ^3.0.0 version: 3.0.0 concurrently: - specifier: ^9.1.2 + specifier: ^9.2.0 version: 9.2.0 esbuild: specifier: ^0.25.5 @@ -160,11 +160,11 @@ importers: specifier: ^8.5.6 version: 8.5.6 prettier: - specifier: ^3.6.1 - version: 3.6.1 + specifier: ^3.6.2 + version: 3.6.2 puppeteer: - specifier: ^24.10.2 - version: 24.10.2(typescript@5.8.3) + specifier: ^24.11.2 + version: 24.11.2(typescript@5.8.3) supertest: specifier: ^7.1.1 version: 7.1.1 @@ -182,7 +182,7 @@ importers: version: 11.1.0 vitest: specifier: ^3.2.4 - version: 3.2.4(@types/node@24.0.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + version: 3.2.4(@types/node@24.0.10)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) ws-mock: specifier: ^0.1.0 version: 0.1.0 @@ -209,8 +209,8 @@ packages: resolution: {integrity: sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==} engines: {node: '>=6.9.0'} - '@babel/parser@7.27.5': - resolution: {integrity: sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg==} + '@babel/parser@7.28.0': + resolution: {integrity: sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==} engines: {node: '>=6.0.0'} hasBin: true @@ -218,63 +218,63 @@ packages: resolution: {integrity: sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==} engines: {node: '>=6.9.0'} - '@babel/types@7.27.6': - resolution: {integrity: sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q==} + '@babel/types@7.28.0': + resolution: {integrity: sha512-jYnje+JyZG5YThjHiF28oT4SIZLnYOcSBb6+SDaFIyzDVSkXQmQQYclJ2R+YxcdmK0AX6x1E5OQNtuh3jHDrUg==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@1.0.2': resolution: {integrity: sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==} engines: {node: '>=18'} - '@biomejs/biome@2.0.5': - resolution: {integrity: sha512-MztFGhE6cVjf3QmomWu83GpTFyWY8KIcskgRf2AqVEMSH4qI4rNdBLdpAQ11TNK9pUfLGz3IIOC1ZYwgBePtig==} + '@biomejs/biome@2.0.6': + resolution: {integrity: sha512-RRP+9cdh5qwe2t0gORwXaa27oTOiQRQvrFf49x2PA1tnpsyU7FIHX4ZOFMtBC4QNtyWsN7Dqkf5EDbg4X+9iqA==} engines: {node: '>=14.21.3'} hasBin: true - '@biomejs/cli-darwin-arm64@2.0.5': - resolution: {integrity: sha512-VIIWQv9Rcj9XresjCf3isBFfWjFStsdGZvm8SmwJzKs/22YQj167ge7DkxuaaZbNf2kmYif0AcjAKvtNedEoEw==} + '@biomejs/cli-darwin-arm64@2.0.6': + resolution: {integrity: sha512-AzdiNNjNzsE6LfqWyBvcL29uWoIuZUkndu+wwlXW13EKcBHbbKjNQEZIJKYDc6IL+p7bmWGx3v9ZtcRyIoIz5A==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [darwin] - '@biomejs/cli-darwin-x64@2.0.5': - resolution: {integrity: sha512-DRpGxBgf5Z7HUFcNUB6n66UiD4VlBlMpngNf32wPraxX8vYU6N9cb3xQWOXIQVBBQ64QfsSLJnjNu79i/LNmSg==} + '@biomejs/cli-darwin-x64@2.0.6': + resolution: {integrity: sha512-wJjjP4E7bO4WJmiQaLnsdXMa516dbtC6542qeRkyJg0MqMXP0fvs4gdsHhZ7p9XWTAmGIjZHFKXdsjBvKGIJJQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [darwin] - '@biomejs/cli-linux-arm64-musl@2.0.5': - resolution: {integrity: sha512-OpflTCOw/ElEs7QZqN/HFaSViPHjAsAPxFJ22LhWUWvuJgcy/Z8+hRV0/3mk/ZRWy5A6fCDKHZqAxU+xB6W4mA==} + '@biomejs/cli-linux-arm64-musl@2.0.6': + resolution: {integrity: sha512-CVPEMlin3bW49sBqLBg2x016Pws7eUXA27XYDFlEtponD0luYjg2zQaMJ2nOqlkKG9fqzzkamdYxHdMDc2gZFw==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-arm64@2.0.5': - resolution: {integrity: sha512-FQTfDNMXOknf8+g9Eede2daaduRjTC2SNbfWPNFMadN9K3UKjeZ62jwiYxztPaz9zQQsZU8VbddQIaeQY5CmIA==} + '@biomejs/cli-linux-arm64@2.0.6': + resolution: {integrity: sha512-ZSVf6TYo5rNMUHIW1tww+rs/krol7U5A1Is/yzWyHVZguuB0lBnIodqyFuwCNqG9aJGyk7xIMS8HG0qGUPz0SA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [linux] - '@biomejs/cli-linux-x64-musl@2.0.5': - resolution: {integrity: sha512-9lmjCnajAzpZXbav2P6D87ugkhnaDpJtDvOH5uQbY2RXeW6Rq18uOUltxgacGBP+d8GusTr+s3IFOu7SN0Ok8g==} + '@biomejs/cli-linux-x64-musl@2.0.6': + resolution: {integrity: sha512-mKHE/e954hR/hSnAcJSjkf4xGqZc/53Kh39HVW1EgO5iFi0JutTN07TSjEMg616julRtfSNJi0KNyxvc30Y4rQ==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-linux-x64@2.0.5': - resolution: {integrity: sha512-znpfydUDPuDkyBTulnODrQVK2FaG/4hIOPcQSsF2GeauQOYrBAOplj0etGB0NUrr0dFsvaQ15nzDXYb60ACoiw==} + '@biomejs/cli-linux-x64@2.0.6': + resolution: {integrity: sha512-geM1MkHTV1Kh2Cs/Xzot9BOF3WBacihw6bkEmxkz4nSga8B9/hWy5BDiOG3gHDGIBa8WxT0nzsJs2f/hPqQIQw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [linux] - '@biomejs/cli-win32-arm64@2.0.5': - resolution: {integrity: sha512-CP2wKQB+gh8HdJTFKYRFETqReAjxlcN9AlYDEoye8v2eQp+L9v+PUeDql/wsbaUhSsLR0sjj3PtbBtt+02AN3A==} + '@biomejs/cli-win32-arm64@2.0.6': + resolution: {integrity: sha512-290V4oSFoKaprKE1zkYVsDfAdn0An5DowZ+GIABgjoq1ndhvNxkJcpxPsiYtT7slbVe3xmlT0ncdfOsN7KruzA==} engines: {node: '>=14.21.3'} cpu: [arm64] os: [win32] - '@biomejs/cli-win32-x64@2.0.5': - resolution: {integrity: sha512-Sw3rz2m6bBADeQpr3+MD7Ch4E1l15DTt/+dfqKnwkm3cn4BrYwnArmvKeZdVsFRDjMyjlKIP88bw1r7o+9aqzw==} + '@biomejs/cli-win32-x64@2.0.6': + resolution: {integrity: sha512-bfM1Bce0d69Ao7pjTjUS+AWSZ02+5UHdiAP85Th8e9yV5xzw6JrHXbL5YWlcEKQ84FIZMdDc7ncuti1wd2sdbw==} engines: {node: '>=14.21.3'} cpu: [x64] os: [win32] @@ -303,8 +303,8 @@ packages: '@codemirror/lang-python@6.2.1': resolution: {integrity: sha512-IRjC8RUBhn9mGR9ywecNhB51yePWCGgvHfY1lWN/Mrp3cKuHr0isDKia+9HnvhiWNnMpbGhWrkhuWOc09exRyw==} - '@codemirror/language@6.11.1': - resolution: {integrity: sha512-5kS1U7emOGV84vxC+ruBty5sUgcD0te6dyupyRVG2zaSjhTDM73LhVKUtVwiqSe6QwmEoA4SCiU8AKPFyumAWQ==} + '@codemirror/language@6.11.2': + resolution: {integrity: sha512-p44TsNArL4IVXDTbapUmEkAlvWs2CFQbcfc0ymDsis1kH2wh0gcY96AS29c/vp2d0y2Tquk1EDSaawpzilUiAw==} '@codemirror/lint@6.8.5': resolution: {integrity: sha512-s3n3KisH7dx3vsoeGMxsbRAgKe4O1vbrnKBClm99PU0fWxmxsx5rR2PfqQgIt+2MMJBHbiJ5rfIdLYfB9NNvsA==} @@ -315,8 +315,8 @@ packages: '@codemirror/theme-one-dark@6.1.3': resolution: {integrity: sha512-NzBdIvEJmx6fjeremiGp3t/okrLPYT0d9orIc7AFun8oZcRk58aejkqhv6spnz4MLAevrKNPMQYXEWMg4s+sKA==} - '@codemirror/view@6.37.2': - resolution: {integrity: sha512-XD3LdgQpxQs5jhOOZ2HRVT+Rj59O4Suc7g2ULvZ+Yi8eCkickrkZ5JFuoDhs2ST1mNI5zSsNYgR3NGa4OUrbnw==} + '@codemirror/view@6.38.0': + resolution: {integrity: sha512-yvSchUwHOdupXkd7xJ0ob36jdsSR/I+/C+VbY0ffBiL5NiSTEBDfB1ZGWbbIlDd5xgdUkody+lukAdOxYrOBeg==} '@emnapi/core@1.4.3': resolution: {integrity: sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g==} @@ -491,23 +491,18 @@ packages: resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} engines: {node: '>=8'} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.12': + resolution: {integrity: sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.4': + resolution: {integrity: sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.29': + resolution: {integrity: sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==} '@lezer/common@1.2.3': resolution: {integrity: sha512-w7ojc8ejBqr2REPsWxJjrMFsA/ysDCFICn8zEOR9mrqzOu2amhITYuLD8ag6XZf0CFXDrhKqw7+tW8cX66NaDA==} @@ -564,11 +559,11 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@open-wc/dedupe-mixin@1.4.0': - resolution: {integrity: sha512-Sj7gKl1TLcDbF7B6KUhtvr+1UCxdhMbNY5KxdU5IfMFWqL8oy1ZeAcCANjoB1TL0AJTcPmcCFsCbHf8X2jGDUA==} + '@open-wc/dedupe-mixin@2.0.1': + resolution: {integrity: sha512-+R4VxvceUxHAUJXJQipkkoV9fy10vNo+OnUnGKZnVmcwxMl460KLzytnUM4S35SI073R0yZQp9ra0MbPUwVcEA==} - '@open-wc/scoped-elements@3.0.5': - resolution: {integrity: sha512-q4U+hFTQQRyorJILOpmBm6PY2hgjCnQe214nXJNjbJMQ9EvT55oyZ7C8BY5aFYJkytUyBoawlMpZt4F2xjdzHw==} + '@open-wc/scoped-elements@3.0.6': + resolution: {integrity: sha512-w1ayJaUUmBw8tALtqQ6cBueld+op+bufujzbrOdH0uCTXnSQkONYZzOH+9jyQ8auVgKLqcxZ8oU6SzfqQhQkPg==} '@open-wc/semantic-dom-diff@0.20.1': resolution: {integrity: sha512-mPF/RPT2TU7Dw41LEDdaeP6eyTOWBD4z0+AHP4/d0SbgcfJZVRymlIB6DQmtz0fd2CImIS9kszaMmwMt92HBPA==} @@ -695,103 +690,103 @@ packages: engines: {node: '>=18'} hasBin: true - '@rollup/rollup-android-arm-eabi@4.44.0': - resolution: {integrity: sha512-xEiEE5oDW6tK4jXCAyliuntGR+amEMO7HLtdSshVuhFnKTYoeYMyXQK7pLouAJJj5KHdwdn87bfHAR2nSdNAUA==} + '@rollup/rollup-android-arm-eabi@4.44.1': + resolution: {integrity: sha512-JAcBr1+fgqx20m7Fwe1DxPUl/hPkee6jA6Pl7n1v2EFiktAHenTaXl5aIFjUIEsfn9w3HE4gK1lEgNGMzBDs1w==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.44.0': - resolution: {integrity: sha512-uNSk/TgvMbskcHxXYHzqwiyBlJ/lGcv8DaUfcnNwict8ba9GTTNxfn3/FAoFZYgkaXXAdrAA+SLyKplyi349Jw==} + '@rollup/rollup-android-arm64@4.44.1': + resolution: {integrity: sha512-RurZetXqTu4p+G0ChbnkwBuAtwAbIwJkycw1n6GvlGlBuS4u5qlr5opix8cBAYFJgaY05TWtM+LaoFggUmbZEQ==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.44.0': - resolution: {integrity: sha512-VGF3wy0Eq1gcEIkSCr8Ke03CWT+Pm2yveKLaDvq51pPpZza3JX/ClxXOCmTYYq3us5MvEuNRTaeyFThCKRQhOA==} + '@rollup/rollup-darwin-arm64@4.44.1': + resolution: {integrity: sha512-fM/xPesi7g2M7chk37LOnmnSTHLG/v2ggWqKj3CCA1rMA4mm5KVBT1fNoswbo1JhPuNNZrVwpTvlCVggv8A2zg==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.44.0': - resolution: {integrity: sha512-fBkyrDhwquRvrTxSGH/qqt3/T0w5Rg0L7ZIDypvBPc1/gzjJle6acCpZ36blwuwcKD/u6oCE/sRWlUAcxLWQbQ==} + '@rollup/rollup-darwin-x64@4.44.1': + resolution: {integrity: sha512-gDnWk57urJrkrHQ2WVx9TSVTH7lSlU7E3AFqiko+bgjlh78aJ88/3nycMax52VIVjIm3ObXnDL2H00e/xzoipw==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.44.0': - resolution: {integrity: sha512-u5AZzdQJYJXByB8giQ+r4VyfZP+walV+xHWdaFx/1VxsOn6eWJhK2Vl2eElvDJFKQBo/hcYIBg/jaKS8ZmKeNQ==} + '@rollup/rollup-freebsd-arm64@4.44.1': + resolution: {integrity: sha512-wnFQmJ/zPThM5zEGcnDcCJeYJgtSLjh1d//WuHzhf6zT3Md1BvvhJnWoy+HECKu2bMxaIcfWiu3bJgx6z4g2XA==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.44.0': - resolution: {integrity: sha512-qC0kS48c/s3EtdArkimctY7h3nHicQeEUdjJzYVJYR3ct3kWSafmn6jkNCA8InbUdge6PVx6keqjk5lVGJf99g==} + '@rollup/rollup-freebsd-x64@4.44.1': + resolution: {integrity: sha512-uBmIxoJ4493YATvU2c0upGz87f99e3wop7TJgOA/bXMFd2SvKCI7xkxY/5k50bv7J6dw1SXT4MQBQSLn8Bb/Uw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.44.0': - resolution: {integrity: sha512-x+e/Z9H0RAWckn4V2OZZl6EmV0L2diuX3QB0uM1r6BvhUIv6xBPL5mrAX2E3e8N8rEHVPwFfz/ETUbV4oW9+lQ==} + '@rollup/rollup-linux-arm-gnueabihf@4.44.1': + resolution: {integrity: sha512-n0edDmSHlXFhrlmTK7XBuwKlG5MbS7yleS1cQ9nn4kIeW+dJH+ExqNgQ0RrFRew8Y+0V/x6C5IjsHrJmiHtkxQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.44.0': - resolution: {integrity: sha512-1exwiBFf4PU/8HvI8s80icyCcnAIB86MCBdst51fwFmH5dyeoWVPVgmQPcKrMtBQ0W5pAs7jBCWuRXgEpRzSCg==} + '@rollup/rollup-linux-arm-musleabihf@4.44.1': + resolution: {integrity: sha512-8WVUPy3FtAsKSpyk21kV52HCxB+me6YkbkFHATzC2Yd3yuqHwy2lbFL4alJOLXKljoRw08Zk8/xEj89cLQ/4Nw==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.44.0': - resolution: {integrity: sha512-ZTR2mxBHb4tK4wGf9b8SYg0Y6KQPjGpR4UWwTFdnmjB4qRtoATZ5dWn3KsDwGa5Z2ZBOE7K52L36J9LueKBdOQ==} + '@rollup/rollup-linux-arm64-gnu@4.44.1': + resolution: {integrity: sha512-yuktAOaeOgorWDeFJggjuCkMGeITfqvPgkIXhDqsfKX8J3jGyxdDZgBV/2kj/2DyPaLiX6bPdjJDTu9RB8lUPQ==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.44.0': - resolution: {integrity: sha512-GFWfAhVhWGd4r6UxmnKRTBwP1qmModHtd5gkraeW2G490BpFOZkFtem8yuX2NyafIP/mGpRJgTJ2PwohQkUY/Q==} + '@rollup/rollup-linux-arm64-musl@4.44.1': + resolution: {integrity: sha512-W+GBM4ifET1Plw8pdVaecwUgxmiH23CfAUj32u8knq0JPFyK4weRy6H7ooxYFD19YxBulL0Ktsflg5XS7+7u9g==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loongarch64-gnu@4.44.0': - resolution: {integrity: sha512-xw+FTGcov/ejdusVOqKgMGW3c4+AgqrfvzWEVXcNP6zq2ue+lsYUgJ+5Rtn/OTJf7e2CbgTFvzLW2j0YAtj0Gg==} + '@rollup/rollup-linux-loongarch64-gnu@4.44.1': + resolution: {integrity: sha512-1zqnUEMWp9WrGVuVak6jWTl4fEtrVKfZY7CvcBmUUpxAJ7WcSowPSAWIKa/0o5mBL/Ij50SIf9tuirGx63Ovew==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': - resolution: {integrity: sha512-bKGibTr9IdF0zr21kMvkZT4K6NV+jjRnBoVMt2uNMG0BYWm3qOVmYnXKzx7UhwrviKnmK46IKMByMgvpdQlyJQ==} + '@rollup/rollup-linux-powerpc64le-gnu@4.44.1': + resolution: {integrity: sha512-Rl3JKaRu0LHIx7ExBAAnf0JcOQetQffaw34T8vLlg9b1IhzcBgaIdnvEbbsZq9uZp3uAH+JkHd20Nwn0h9zPjA==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.44.0': - resolution: {integrity: sha512-vV3cL48U5kDaKZtXrti12YRa7TyxgKAIDoYdqSIOMOFBXqFj2XbChHAtXquEn2+n78ciFgr4KIqEbydEGPxXgA==} + '@rollup/rollup-linux-riscv64-gnu@4.44.1': + resolution: {integrity: sha512-j5akelU3snyL6K3N/iX7otLBIl347fGwmd95U5gS/7z6T4ftK288jKq3A5lcFKcx7wwzb5rgNvAg3ZbV4BqUSw==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.44.0': - resolution: {integrity: sha512-TDKO8KlHJuvTEdfw5YYFBjhFts2TR0VpZsnLLSYmB7AaohJhM8ctDSdDnUGq77hUh4m/djRafw+9zQpkOanE2Q==} + '@rollup/rollup-linux-riscv64-musl@4.44.1': + resolution: {integrity: sha512-ppn5llVGgrZw7yxbIm8TTvtj1EoPgYUAbfw0uDjIOzzoqlZlZrLJ/KuiE7uf5EpTpCTrNt1EdtzF0naMm0wGYg==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.44.0': - resolution: {integrity: sha512-8541GEyktXaw4lvnGp9m84KENcxInhAt6vPWJ9RodsB/iGjHoMB2Pp5MVBCiKIRxrxzJhGCxmNzdu+oDQ7kwRA==} + '@rollup/rollup-linux-s390x-gnu@4.44.1': + resolution: {integrity: sha512-Hu6hEdix0oxtUma99jSP7xbvjkUM/ycke/AQQ4EC5g7jNRLLIwjcNwaUy95ZKBJJwg1ZowsclNnjYqzN4zwkAw==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.44.0': - resolution: {integrity: sha512-iUVJc3c0o8l9Sa/qlDL2Z9UP92UZZW1+EmQ4xfjTc1akr0iUFZNfxrXJ/R1T90h/ILm9iXEY6+iPrmYB3pXKjw==} + '@rollup/rollup-linux-x64-gnu@4.44.1': + resolution: {integrity: sha512-EtnsrmZGomz9WxK1bR5079zee3+7a+AdFlghyd6VbAjgRJDbTANJ9dcPIPAi76uG05micpEL+gPGmAKYTschQw==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.44.0': - resolution: {integrity: sha512-PQUobbhLTQT5yz/SPg116VJBgz+XOtXt8D1ck+sfJJhuEsMj2jSej5yTdp8CvWBSceu+WW+ibVL6dm0ptG5fcA==} + '@rollup/rollup-linux-x64-musl@4.44.1': + resolution: {integrity: sha512-iAS4p+J1az6Usn0f8xhgL4PaU878KEtutP4hqw52I4IO6AGoyOkHCxcc4bqufv1tQLdDWFx8lR9YlwxKuv3/3g==} cpu: [x64] os: [linux] - '@rollup/rollup-win32-arm64-msvc@4.44.0': - resolution: {integrity: sha512-M0CpcHf8TWn+4oTxJfh7LQuTuaYeXGbk0eageVjQCKzYLsajWS/lFC94qlRqOlyC2KvRT90ZrfXULYmukeIy7w==} + '@rollup/rollup-win32-arm64-msvc@4.44.1': + resolution: {integrity: sha512-NtSJVKcXwcqozOl+FwI41OH3OApDyLk3kqTJgx8+gp6On9ZEt5mYhIsKNPGuaZr3p9T6NWPKGU/03Vw4CNU9qg==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.44.0': - resolution: {integrity: sha512-3XJ0NQtMAXTWFW8FqZKcw3gOQwBtVWP/u8TpHP3CRPXD7Pd6s8lLdH3sHWh8vqKCyyiI8xW5ltJScQmBU9j7WA==} + '@rollup/rollup-win32-ia32-msvc@4.44.1': + resolution: {integrity: sha512-JYA3qvCOLXSsnTR3oiyGws1Dm0YTuxAAeaYGVlGpUsHqloPcFjPg+X0Fj2qODGLNwQOAcCiQmHub/V007kiH5A==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.44.0': - resolution: {integrity: sha512-Q2Mgwt+D8hd5FIPUuPDsvPR7Bguza6yTkJxspDGkZj7tBRn2y4KSWYuIXpftFSjBra76TbKerCV7rgFPQrn+wQ==} + '@rollup/rollup-win32-x64-msvc@4.44.1': + resolution: {integrity: sha512-J8o22LuF0kTe7m+8PvW9wk3/bRq5+mRo5Dqo6+vXb7otCm3TPhYOJqOaQtGU9YMWQSL3krMnoOxMr0+9E6F3Ug==} cpu: [x64] os: [win32] @@ -901,11 +896,11 @@ packages: '@types/multer@1.4.13': resolution: {integrity: sha512-bhhdtPw7JqCiEfC9Jimx5LqX9BDIPJEh2q/fQ4bqbBPtyEZYr3cvF22NwG0DmPZNYA0CAf2CnqDB4KIGGpJcaw==} - '@types/node@20.19.1': - resolution: {integrity: sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==} + '@types/node@20.19.4': + resolution: {integrity: sha512-OP+We5WV8Xnbuvw0zC2m4qfB/BJvjyCwtNjhHdJxV1639SGSKrLmJkc3fMnp2Qy8nJyHp8RO6umxELN/dS1/EA==} - '@types/node@24.0.4': - resolution: {integrity: sha512-ulyqAkrhnuNq9pB76DRBTkcS6YsmDALy6Ua63V8OhrOBgbcYt6IOdzpw5P1+dyRIyMerzLkeYWBeOXPpA9GMAA==} + '@types/node@24.0.10': + resolution: {integrity: sha512-ENHwaH+JIRTDIEEbDK6QSQntAYGtbvdDXnMXnZaZ6k13Du1dPMmprkEHIL7ok2Wl2aZevetwTAb5S+7yIF+enA==} '@types/parse5@6.0.3': resolution: {integrity: sha512-SuT16Q1K51EAVPz1K29DJ/sXjhSQ0zjvsypYJ6tlwVsRV9jwW5Adq2ch8Dq8kDBCkYnELS7N7VNCSB5nC56t/g==} @@ -1190,8 +1185,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.25.0: - resolution: {integrity: sha512-PJ8gYKeS5e/whHBh8xrwYK+dAvEj7JXtz6uTucnMRB8OiGTsKccFekoRrjajPBHV8oOY+2tI4uxeceSimKwMFA==} + browserslist@4.25.1: + resolution: {integrity: sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1240,8 +1235,8 @@ packages: resolution: {integrity: sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==} engines: {node: '>=6'} - caniuse-lite@1.0.30001724: - resolution: {integrity: sha512-WqJo7p0TbHDOythNTqYujmaJTvtYRZrjpP8TCvH6Vb9CYJerJNKamKzIWOM4BkQatWj9H2lYulpdAQNBe7QhNA==} + caniuse-lite@1.0.30001726: + resolution: {integrity: sha512-VQAUIUzBiZ/UnlM28fSp2CRF3ivUn1BWEvxMcVTNwpw91Py1pGbPIyIKtd+tzct9C3ouceCVdGAXxZOpZAsgdw==} chai-a11y-axe@1.5.0: resolution: {integrity: sha512-V/Vg/zJDr9aIkaHJ2KQu7lGTQQm5ZOH4u1k5iTMvIXuSVlSuUo0jcSpSqf9wUn9zl6oQXa4e4E0cqH18KOgKlQ==} @@ -1487,8 +1482,8 @@ packages: resolution: {integrity: sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==} engines: {node: '>= 0.8', npm: 1.2.8000 || >= 1.4.16} - devtools-protocol@0.0.1452169: - resolution: {integrity: sha512-FOFDVMGrAUNp0dDKsAU1TorWJUx2JOU1k9xdgBKKJF3IBh/Uhl2yswG5r3TEAOrCiGY2QRp1e6LVDQrCsTKO4g==} + devtools-protocol@0.0.1464554: + resolution: {integrity: sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw==} dezalgo@1.0.4: resolution: {integrity: sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig==} @@ -1519,8 +1514,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.173: - resolution: {integrity: sha512-2bFhXP2zqSfQHugjqJIDFVwa+qIxyNApenmXTp9EjaKtdPrES5Qcn9/aSFy/NaP2E+fWG/zxKu/LBvY36p5VNQ==} + electron-to-chromium@1.5.179: + resolution: {integrity: sha512-UWKi/EbBopgfFsc5k61wFpV7WrnnSlSzW/e2XcBmS6qKYTivZlLtoll5/rdqRTxGglGHkmkW0j0pFNJG10EUIQ==} emoji-regex@10.4.0: resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} @@ -2394,8 +2389,8 @@ packages: pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pathval@2.0.0: - resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} engines: {node: '>= 14.16'} pend@1.2.0: @@ -2481,8 +2476,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - prettier@3.6.1: - resolution: {integrity: sha512-5xGWRa90Sp2+x1dQtNpIpeOQpTDBs9cZDmA/qs2vDNN2i18PdapqY7CmBeyLlMuGqXJRIOPaCaVZTLNQRWUH/A==} + prettier@3.6.2: + resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} engines: {node: '>=14'} hasBin: true @@ -2508,12 +2503,12 @@ packages: pump@3.0.3: resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - puppeteer-core@24.10.2: - resolution: {integrity: sha512-CnzhOgrZj8DvkDqI+Yx+9or33i3Y9uUYbKyYpP4C13jWwXx/keQ38RMTMmxuLCWQlxjZrOH0Foq7P2fGP7adDQ==} + puppeteer-core@24.11.2: + resolution: {integrity: sha512-c49WifNb8hix+gQH17TldmD6TC/Md2HBaTJLHexIUq4sZvo2pyHY/Pp25qFQjibksBu/SJRYUY7JsoaepNbiRA==} engines: {node: '>=18'} - puppeteer@24.10.2: - resolution: {integrity: sha512-+k26rCz6akFZntx0hqUoFjCojgOLIxZs6p2k53LmEicwsT8F/FMBKfRfiBw1sitjiCvlR/15K7lBqfjXa251FA==} + puppeteer@24.11.2: + resolution: {integrity: sha512-HopdRZWHa5zk0HSwd8hU+GlahQ3fmesTAqMIDHVY9HasCvppcYuHYXyjml0nlm+nbwVCqAQWV+dSmiNCrZGTGQ==} engines: {node: '>=18'} hasBin: true @@ -2592,8 +2587,8 @@ packages: rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} - rollup@4.44.0: - resolution: {integrity: sha512-qHcdEzLCiktQIfwBq420pn2dP+30uzqYxv9ETm91wdt2R9AFcWfjNAmje4NWlnCIQ5RMTzVf0ZyisOKqHR6RwA==} + rollup@4.44.1: + resolution: {integrity: sha512-x8H8aPvD+xbl0Do8oez5f5o8eMS3trfCghc4HhLAnCkj7Vl0d1JWGs0UF/D886zLW2rOj2QymV/JcSSsw+XDNg==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2816,8 +2811,8 @@ packages: engines: {node: '>=14.0.0'} hasBin: true - tar-fs@3.0.10: - resolution: {integrity: sha512-C1SwlQGNLe/jPNqapK8epDsXME7CAJR5RL3GcE6KWx1d9OUByzoHVcbu1VPI8tevg9H8Alae0AApHHFGzrD5zA==} + tar-fs@3.1.0: + resolution: {integrity: sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w==} tar-stream@3.1.7: resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} @@ -2944,8 +2939,8 @@ packages: engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true - vite@7.0.0: - resolution: {integrity: sha512-ixXJB1YRgDIw2OszKQS9WxGHKwLdCsbQNkpJN171udl6szi/rIySHL6/Os3s2+oE4P/FLD4dxg4mD7Wust+u5g==} + vite@7.0.1: + resolution: {integrity: sha512-BiKOQoW5HGR30E6JDeNsati6HnSPMVEKbkIWbCiol+xKeu3g5owrjy7kbk/QEMuzCV87dSUTvycYKmlcfGKq3Q==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -3079,8 +3074,8 @@ packages: utf-8-validate: optional: true - ws@8.18.2: - resolution: {integrity: sha512-DMricUmwGZUVr++AEAe2uiVM7UoO9MAVZMDu05UQOaUII0lp+zOzLLU4Xqh/JvTqklB1T4uELaaPBKyjE1r4fQ==} + ws@8.18.3: + resolution: {integrity: sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3128,8 +3123,8 @@ packages: resolution: {integrity: sha512-2OQsPNEmBCvXuFlIni/a+Rn+R2pHW9INm0BxXJ4hVDA8TirqMj+J/Rp9ItLatT/5pZqWwefVrTQcHpixsxnVlA==} engines: {node: '>= 4.0.0'} - zod@3.25.67: - resolution: {integrity: sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==} + zod@3.25.71: + resolution: {integrity: sha512-BsBc/NPk7h8WsUWYWYL+BajcJPY8YhjelaWu2NMLuzgraKAz4Lb4/6K11g9jpuDetjMiqhZ6YaexFLOC0Ogi3Q==} snapshots: @@ -3137,8 +3132,8 @@ snapshots: '@ampproject/remapping@2.3.0': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/gen-mapping': 0.3.12 + '@jridgewell/trace-mapping': 0.3.29 '@babel/code-frame@7.27.1': dependencies: @@ -3150,72 +3145,72 @@ snapshots: '@babel/helper-validator-identifier@7.27.1': {} - '@babel/parser@7.27.5': + '@babel/parser@7.28.0': dependencies: - '@babel/types': 7.27.6 + '@babel/types': 7.28.0 '@babel/runtime@7.27.6': {} - '@babel/types@7.27.6': + '@babel/types@7.28.0': dependencies: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.27.1 '@bcoe/v8-coverage@1.0.2': {} - '@biomejs/biome@2.0.5': + '@biomejs/biome@2.0.6': optionalDependencies: - '@biomejs/cli-darwin-arm64': 2.0.5 - '@biomejs/cli-darwin-x64': 2.0.5 - '@biomejs/cli-linux-arm64': 2.0.5 - '@biomejs/cli-linux-arm64-musl': 2.0.5 - '@biomejs/cli-linux-x64': 2.0.5 - '@biomejs/cli-linux-x64-musl': 2.0.5 - '@biomejs/cli-win32-arm64': 2.0.5 - '@biomejs/cli-win32-x64': 2.0.5 + '@biomejs/cli-darwin-arm64': 2.0.6 + '@biomejs/cli-darwin-x64': 2.0.6 + '@biomejs/cli-linux-arm64': 2.0.6 + '@biomejs/cli-linux-arm64-musl': 2.0.6 + '@biomejs/cli-linux-x64': 2.0.6 + '@biomejs/cli-linux-x64-musl': 2.0.6 + '@biomejs/cli-win32-arm64': 2.0.6 + '@biomejs/cli-win32-x64': 2.0.6 - '@biomejs/cli-darwin-arm64@2.0.5': + '@biomejs/cli-darwin-arm64@2.0.6': optional: true - '@biomejs/cli-darwin-x64@2.0.5': + '@biomejs/cli-darwin-x64@2.0.6': optional: true - '@biomejs/cli-linux-arm64-musl@2.0.5': + '@biomejs/cli-linux-arm64-musl@2.0.6': optional: true - '@biomejs/cli-linux-arm64@2.0.5': + '@biomejs/cli-linux-arm64@2.0.6': optional: true - '@biomejs/cli-linux-x64-musl@2.0.5': + '@biomejs/cli-linux-x64-musl@2.0.6': optional: true - '@biomejs/cli-linux-x64@2.0.5': + '@biomejs/cli-linux-x64@2.0.6': optional: true - '@biomejs/cli-win32-arm64@2.0.5': + '@biomejs/cli-win32-arm64@2.0.6': optional: true - '@biomejs/cli-win32-x64@2.0.5': + '@biomejs/cli-win32-x64@2.0.6': optional: true '@codemirror/autocomplete@6.18.6': dependencies: - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/common': 1.2.3 '@codemirror/commands@6.8.1': dependencies: - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/common': 1.2.3 '@codemirror/lang-css@6.3.1': dependencies: '@codemirror/autocomplete': 6.18.6 - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 '@lezer/common': 1.2.3 '@lezer/css': 1.2.1 @@ -3225,9 +3220,9 @@ snapshots: '@codemirror/autocomplete': 6.18.6 '@codemirror/lang-css': 6.3.1 '@codemirror/lang-javascript': 6.2.4 - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/common': 1.2.3 '@lezer/css': 1.2.1 '@lezer/html': 1.3.10 @@ -3235,40 +3230,40 @@ snapshots: '@codemirror/lang-javascript@6.2.4': dependencies: '@codemirror/autocomplete': 6.18.6 - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/lint': 6.8.5 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/common': 1.2.3 '@lezer/javascript': 1.5.1 '@codemirror/lang-json@6.0.2': dependencies: - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@lezer/json': 1.0.3 '@codemirror/lang-markdown@6.3.3': dependencies: '@codemirror/autocomplete': 6.18.6 '@codemirror/lang-html': 6.4.9 - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/common': 1.2.3 '@lezer/markdown': 1.4.3 '@codemirror/lang-python@6.2.1': dependencies: '@codemirror/autocomplete': 6.18.6 - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 '@lezer/common': 1.2.3 '@lezer/python': 1.1.18 - '@codemirror/language@6.11.1': + '@codemirror/language@6.11.2': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/common': 1.2.3 '@lezer/highlight': 1.2.1 '@lezer/lr': 1.4.2 @@ -3277,7 +3272,7 @@ snapshots: '@codemirror/lint@6.8.5': dependencies: '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 crelt: 1.0.6 '@codemirror/state@6.5.2': @@ -3286,12 +3281,12 @@ snapshots: '@codemirror/theme-one-dark@6.1.3': dependencies: - '@codemirror/language': 6.11.1 + '@codemirror/language': 6.11.2 '@codemirror/state': 6.5.2 - '@codemirror/view': 6.37.2 + '@codemirror/view': 6.38.0 '@lezer/highlight': 1.2.1 - '@codemirror/view@6.37.2': + '@codemirror/view@6.38.0': dependencies: '@codemirror/state': 6.5.2 crelt: 1.0.6 @@ -3406,22 +3401,19 @@ snapshots: '@istanbuljs/schema@0.1.3': {} - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/gen-mapping@0.3.12': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.4 + '@jridgewell/trace-mapping': 0.3.29 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/sourcemap-codec@1.5.4': {} - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.29': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 '@lezer/common@1.2.3': {} @@ -3497,11 +3489,11 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.19.1 - '@open-wc/dedupe-mixin@1.4.0': {} + '@open-wc/dedupe-mixin@2.0.1': {} - '@open-wc/scoped-elements@3.0.5': + '@open-wc/scoped-elements@3.0.6': dependencies: - '@open-wc/dedupe-mixin': 1.4.0 + '@open-wc/dedupe-mixin': 2.0.1 lit: 3.3.0 '@open-wc/semantic-dom-diff@0.20.1': @@ -3515,7 +3507,7 @@ snapshots: '@open-wc/testing-helpers@3.0.1': dependencies: - '@open-wc/scoped-elements': 3.0.5 + '@open-wc/scoped-elements': 3.0.6 lit: 3.3.0 lit-html: 3.3.0 @@ -3605,70 +3597,70 @@ snapshots: progress: 2.0.3 proxy-agent: 6.5.0 semver: 7.7.2 - tar-fs: 3.0.10 + tar-fs: 3.1.0 yargs: 17.7.2 transitivePeerDependencies: - bare-buffer - supports-color - '@rollup/rollup-android-arm-eabi@4.44.0': + '@rollup/rollup-android-arm-eabi@4.44.1': optional: true - '@rollup/rollup-android-arm64@4.44.0': + '@rollup/rollup-android-arm64@4.44.1': optional: true - '@rollup/rollup-darwin-arm64@4.44.0': + '@rollup/rollup-darwin-arm64@4.44.1': optional: true - '@rollup/rollup-darwin-x64@4.44.0': + '@rollup/rollup-darwin-x64@4.44.1': optional: true - '@rollup/rollup-freebsd-arm64@4.44.0': + '@rollup/rollup-freebsd-arm64@4.44.1': optional: true - '@rollup/rollup-freebsd-x64@4.44.0': + '@rollup/rollup-freebsd-x64@4.44.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.44.0': + '@rollup/rollup-linux-arm-gnueabihf@4.44.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.44.0': + '@rollup/rollup-linux-arm-musleabihf@4.44.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.44.0': + '@rollup/rollup-linux-arm64-gnu@4.44.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.44.0': + '@rollup/rollup-linux-arm64-musl@4.44.1': optional: true - '@rollup/rollup-linux-loongarch64-gnu@4.44.0': + '@rollup/rollup-linux-loongarch64-gnu@4.44.1': optional: true - '@rollup/rollup-linux-powerpc64le-gnu@4.44.0': + '@rollup/rollup-linux-powerpc64le-gnu@4.44.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.44.0': + '@rollup/rollup-linux-riscv64-gnu@4.44.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.44.0': + '@rollup/rollup-linux-riscv64-musl@4.44.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.44.0': + '@rollup/rollup-linux-s390x-gnu@4.44.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.44.0': + '@rollup/rollup-linux-x64-gnu@4.44.1': optional: true - '@rollup/rollup-linux-x64-musl@4.44.0': + '@rollup/rollup-linux-x64-musl@4.44.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.44.0': + '@rollup/rollup-win32-arm64-msvc@4.44.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.44.0': + '@rollup/rollup-win32-ia32-msvc@4.44.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.44.0': + '@rollup/rollup-win32-x64-msvc@4.44.1': optional: true '@testing-library/dom@10.4.0': @@ -3691,7 +3683,7 @@ snapshots: '@types/accepts@1.3.7': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/aria-query@5.0.4': {} @@ -3700,7 +3692,7 @@ snapshots: '@types/body-parser@1.19.6': dependencies: '@types/connect': 3.4.38 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/chai-dom@1.11.3': dependencies: @@ -3714,12 +3706,12 @@ snapshots: '@types/co-body@6.1.3': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/qs': 6.14.0 '@types/connect@3.4.38': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/content-disposition@0.5.9': {} @@ -3732,7 +3724,7 @@ snapshots: '@types/connect': 3.4.38 '@types/express': 4.17.23 '@types/keygrip': 1.0.6 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/debounce@1.2.4': {} @@ -3742,7 +3734,7 @@ snapshots: '@types/express-serve-static-core@4.19.6': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/qs': 6.14.0 '@types/range-parser': 1.2.7 '@types/send': 0.17.5 @@ -3771,7 +3763,7 @@ snapshots: '@types/jsonwebtoken@9.0.10': dependencies: '@types/ms': 2.1.0 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/keygrip@1.0.6': {} @@ -3788,7 +3780,7 @@ snapshots: '@types/http-errors': 2.0.5 '@types/keygrip': 1.0.6 '@types/koa-compose': 3.2.8 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/methods@1.1.4': {} @@ -3802,11 +3794,11 @@ snapshots: dependencies: '@types/express': 4.17.23 - '@types/node@20.19.1': + '@types/node@20.19.4': dependencies: undici-types: 6.21.0 - '@types/node@24.0.4': + '@types/node@24.0.10': dependencies: undici-types: 7.8.0 @@ -3819,12 +3811,12 @@ snapshots: '@types/send@0.17.5': dependencies: '@types/mime': 1.3.5 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/serve-static@1.15.8': dependencies: '@types/http-errors': 2.0.5 - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/send': 0.17.5 '@types/sinon-chai@3.2.12': @@ -3842,7 +3834,7 @@ snapshots: dependencies: '@types/cookiejar': 2.1.5 '@types/methods': 1.1.4 - '@types/node': 24.0.4 + '@types/node': 24.0.10 form-data: 4.0.3 '@types/supertest@6.0.3': @@ -3856,21 +3848,21 @@ snapshots: '@types/web-push@3.6.4': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/whatwg-mimetype@3.0.2': {} '@types/ws@7.4.7': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/ws@8.18.1': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@types/yauzl@2.10.3': dependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 optional: true '@vitest/coverage-v8@3.2.4(vitest@3.2.4)': @@ -3888,7 +3880,7 @@ snapshots: std-env: 3.9.0 test-exclude: 7.0.1 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@24.0.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vitest: 3.2.4(@types/node@24.0.10)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - supports-color @@ -3900,13 +3892,13 @@ snapshots: chai: 5.2.0 tinyrainbow: 2.0.0 - '@vitest/mocker@3.2.4(vite@7.0.0(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0))': + '@vitest/mocker@3.2.4(vite@7.0.1(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0))': dependencies: '@vitest/spy': 3.2.4 estree-walker: 3.0.3 magic-string: 0.30.17 optionalDependencies: - vite: 7.0.0(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.1(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) '@vitest/pretty-format@3.2.4': dependencies: @@ -3937,7 +3929,7 @@ snapshots: sirv: 3.0.1 tinyglobby: 0.2.14 tinyrainbow: 2.0.0 - vitest: 3.2.4(@types/node@24.0.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vitest: 3.2.4(@types/node@24.0.10)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) '@vitest/utils@3.2.4': dependencies: @@ -4094,7 +4086,7 @@ snapshots: ast-v8-to-istanbul@0.3.3: dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 estree-walker: 3.0.3 js-tokens: 9.0.1 @@ -4108,8 +4100,8 @@ snapshots: autoprefixer@10.4.21(postcss@8.5.6): dependencies: - browserslist: 4.25.0 - caniuse-lite: 1.0.30001724 + browserslist: 4.25.1 + caniuse-lite: 1.0.30001726 fraction.js: 4.3.7 normalize-range: 0.1.2 picocolors: 1.1.1 @@ -4178,12 +4170,12 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.25.0: + browserslist@4.25.1: dependencies: - caniuse-lite: 1.0.30001724 - electron-to-chromium: 1.5.173 + caniuse-lite: 1.0.30001726 + electron-to-chromium: 1.5.179 node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.25.0) + update-browserslist-db: 1.1.3(browserslist@4.25.1) buffer-crc32@0.2.13: {} @@ -4220,7 +4212,7 @@ snapshots: camelcase@5.3.1: {} - caniuse-lite@1.0.30001724: {} + caniuse-lite@1.0.30001726: {} chai-a11y-axe@1.5.0: dependencies: @@ -4232,7 +4224,7 @@ snapshots: check-error: 2.1.1 deep-eql: 5.0.2 loupe: 3.1.4 - pathval: 2.0.0 + pathval: 2.0.1 chalk@4.1.2: dependencies: @@ -4266,11 +4258,11 @@ snapshots: dependencies: readdirp: 4.1.2 - chromium-bidi@5.1.0(devtools-protocol@0.0.1452169): + chromium-bidi@5.1.0(devtools-protocol@0.0.1464554): dependencies: - devtools-protocol: 0.0.1452169 + devtools-protocol: 0.0.1464554 mitt: 3.0.1 - zod: 3.25.67 + zod: 3.25.71 cli-cursor@3.1.0: dependencies: @@ -4440,7 +4432,7 @@ snapshots: destroy@1.2.0: {} - devtools-protocol@0.0.1452169: {} + devtools-protocol@0.0.1464554: {} dezalgo@1.0.4: dependencies: @@ -4471,7 +4463,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.173: {} + electron-to-chromium@1.5.179: {} emoji-regex@10.4.0: {} @@ -4790,7 +4782,7 @@ snapshots: happy-dom@18.0.1: dependencies: - '@types/node': 20.19.1 + '@types/node': 20.19.4 '@types/whatwg-mimetype': 3.0.2 whatwg-mimetype: 3.0.0 @@ -4957,7 +4949,7 @@ snapshots: istanbul-lib-source-maps@5.0.6: dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.29 debug: 4.4.1 istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: @@ -5177,12 +5169,12 @@ snapshots: magic-string@0.30.17: dependencies: - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.4 magicast@0.3.5: dependencies: - '@babel/parser': 7.27.5 - '@babel/types': 7.27.6 + '@babel/parser': 7.28.0 + '@babel/types': 7.28.0 source-map-js: 1.2.1 make-dir@4.0.0: @@ -5428,7 +5420,7 @@ snapshots: pathe@2.0.3: {} - pathval@2.0.0: {} + pathval@2.0.1: {} pend@1.2.0: {} @@ -5493,7 +5485,7 @@ snapshots: dependencies: commander: 9.5.0 - prettier@3.6.1: {} + prettier@3.6.2: {} pretty-format@27.5.1: dependencies: @@ -5528,27 +5520,27 @@ snapshots: end-of-stream: 1.4.5 once: 1.4.0 - puppeteer-core@24.10.2: + puppeteer-core@24.11.2: dependencies: '@puppeteer/browsers': 2.10.5 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169) + chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) debug: 4.4.1 - devtools-protocol: 0.0.1452169 + devtools-protocol: 0.0.1464554 typed-query-selector: 2.12.0 - ws: 8.18.2 + ws: 8.18.3 transitivePeerDependencies: - bare-buffer - bufferutil - supports-color - utf-8-validate - puppeteer@24.10.2(typescript@5.8.3): + puppeteer@24.11.2(typescript@5.8.3): dependencies: '@puppeteer/browsers': 2.10.5 - chromium-bidi: 5.1.0(devtools-protocol@0.0.1452169) + chromium-bidi: 5.1.0(devtools-protocol@0.0.1464554) cosmiconfig: 9.0.0(typescript@5.8.3) - devtools-protocol: 0.0.1452169 - puppeteer-core: 24.10.2 + devtools-protocol: 0.0.1464554 + puppeteer-core: 24.11.2 typed-query-selector: 2.12.0 transitivePeerDependencies: - bare-buffer @@ -5627,30 +5619,30 @@ snapshots: rfdc@1.4.1: {} - rollup@4.44.0: + rollup@4.44.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.44.0 - '@rollup/rollup-android-arm64': 4.44.0 - '@rollup/rollup-darwin-arm64': 4.44.0 - '@rollup/rollup-darwin-x64': 4.44.0 - '@rollup/rollup-freebsd-arm64': 4.44.0 - '@rollup/rollup-freebsd-x64': 4.44.0 - '@rollup/rollup-linux-arm-gnueabihf': 4.44.0 - '@rollup/rollup-linux-arm-musleabihf': 4.44.0 - '@rollup/rollup-linux-arm64-gnu': 4.44.0 - '@rollup/rollup-linux-arm64-musl': 4.44.0 - '@rollup/rollup-linux-loongarch64-gnu': 4.44.0 - '@rollup/rollup-linux-powerpc64le-gnu': 4.44.0 - '@rollup/rollup-linux-riscv64-gnu': 4.44.0 - '@rollup/rollup-linux-riscv64-musl': 4.44.0 - '@rollup/rollup-linux-s390x-gnu': 4.44.0 - '@rollup/rollup-linux-x64-gnu': 4.44.0 - '@rollup/rollup-linux-x64-musl': 4.44.0 - '@rollup/rollup-win32-arm64-msvc': 4.44.0 - '@rollup/rollup-win32-ia32-msvc': 4.44.0 - '@rollup/rollup-win32-x64-msvc': 4.44.0 + '@rollup/rollup-android-arm-eabi': 4.44.1 + '@rollup/rollup-android-arm64': 4.44.1 + '@rollup/rollup-darwin-arm64': 4.44.1 + '@rollup/rollup-darwin-x64': 4.44.1 + '@rollup/rollup-freebsd-arm64': 4.44.1 + '@rollup/rollup-freebsd-x64': 4.44.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.44.1 + '@rollup/rollup-linux-arm-musleabihf': 4.44.1 + '@rollup/rollup-linux-arm64-gnu': 4.44.1 + '@rollup/rollup-linux-arm64-musl': 4.44.1 + '@rollup/rollup-linux-loongarch64-gnu': 4.44.1 + '@rollup/rollup-linux-powerpc64le-gnu': 4.44.1 + '@rollup/rollup-linux-riscv64-gnu': 4.44.1 + '@rollup/rollup-linux-riscv64-musl': 4.44.1 + '@rollup/rollup-linux-s390x-gnu': 4.44.1 + '@rollup/rollup-linux-x64-gnu': 4.44.1 + '@rollup/rollup-linux-x64-musl': 4.44.1 + '@rollup/rollup-win32-arm64-msvc': 4.44.1 + '@rollup/rollup-win32-ia32-msvc': 4.44.1 + '@rollup/rollup-win32-x64-msvc': 4.44.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -5865,7 +5857,7 @@ snapshots: sucrase@3.35.0: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.12 commander: 4.1.1 glob: 10.4.5 lines-and-columns: 1.2.4 @@ -5931,7 +5923,7 @@ snapshots: transitivePeerDependencies: - ts-node - tar-fs@3.0.10: + tar-fs@3.1.0: dependencies: pump: 3.0.3 tar-stream: 3.1.7 @@ -6022,9 +6014,9 @@ snapshots: unpipe@1.0.0: {} - update-browserslist-db@1.1.3(browserslist@4.25.0): + update-browserslist-db@1.1.3(browserslist@4.25.1): dependencies: - browserslist: 4.25.0 + browserslist: 4.25.1 escalade: 3.2.0 picocolors: 1.1.1 @@ -6036,13 +6028,13 @@ snapshots: vary@1.1.2: {} - vite-node@3.2.4(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): + vite-node@3.2.4(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): dependencies: cac: 6.7.14 debug: 4.4.1 es-module-lexer: 1.7.0 pathe: 2.0.3 - vite: 7.0.0(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.1(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) transitivePeerDependencies: - '@types/node' - jiti @@ -6057,26 +6049,26 @@ snapshots: - tsx - yaml - vite@7.0.0(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): + vite@7.0.1(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): dependencies: esbuild: 0.25.5 fdir: 6.4.6(picomatch@4.0.2) picomatch: 4.0.2 postcss: 8.5.6 - rollup: 4.44.0 + rollup: 4.44.1 tinyglobby: 0.2.14 optionalDependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 fsevents: 2.3.3 jiti: 1.21.7 tsx: 4.20.3 yaml: 2.8.0 - vitest@3.2.4(@types/node@24.0.4)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): + vitest@3.2.4(@types/node@24.0.10)(@vitest/ui@3.2.4)(happy-dom@18.0.1)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0): dependencies: '@types/chai': 5.2.2 '@vitest/expect': 3.2.4 - '@vitest/mocker': 3.2.4(vite@7.0.0(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) + '@vitest/mocker': 3.2.4(vite@7.0.1(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0)) '@vitest/pretty-format': 3.2.4 '@vitest/runner': 3.2.4 '@vitest/snapshot': 3.2.4 @@ -6094,11 +6086,11 @@ snapshots: tinyglobby: 0.2.14 tinypool: 1.1.1 tinyrainbow: 2.0.0 - vite: 7.0.0(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) - vite-node: 3.2.4(@types/node@24.0.4)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vite: 7.0.1(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) + vite-node: 3.2.4(@types/node@24.0.10)(jiti@1.21.7)(tsx@4.20.3)(yaml@2.8.0) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 24.0.4 + '@types/node': 24.0.10 '@vitest/ui': 3.2.4(vitest@3.2.4) happy-dom: 18.0.1 transitivePeerDependencies: @@ -6178,7 +6170,7 @@ snapshots: ws@7.5.10: {} - ws@8.18.2: {} + ws@8.18.3: {} xtend@4.0.2: {} @@ -6225,4 +6217,4 @@ snapshots: ylru@1.4.0: {} - zod@3.25.67: {} + zod@3.25.71: {} diff --git a/web/scripts/ensure-native-modules.js b/web/scripts/ensure-native-modules.js index bf353b1a..769cd755 100755 --- a/web/scripts/ensure-native-modules.js +++ b/web/scripts/ensure-native-modules.js @@ -16,8 +16,10 @@ let nodePtyPath; try { nodePtyPath = require.resolve('node-pty/package.json'); } catch (e) { - console.error('Could not find node-pty module'); - process.exit(1); + console.log('Could not find node-pty module'); + // In CI or during initial install, node-pty might not be installed yet + // This is expected behavior, so exit successfully + process.exit(0); } const nodePtyDir = path.dirname(nodePtyPath); diff --git a/web/src/client/components/terminal.test.ts b/web/src/client/components/terminal.test.ts index b384a2d3..d31b2254 100644 --- a/web/src/client/components/terminal.test.ts +++ b/web/src/client/components/terminal.test.ts @@ -597,7 +597,8 @@ describe('Terminal', () => { element.rows = currentRows; // Mock character width measurement - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Calculate container dimensions that would result in the same size const lineHeight = element.fontSize * 1.2; @@ -605,10 +606,12 @@ describe('Terminal', () => { clientWidth: (currentCols + 1) * 8, // Account for -1 in calculation clientHeight: currentRows * lineHeight, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Terminal resize should NOT be called since dimensions haven't changed expect(mockTerminal?.resize).not.toHaveBeenCalled(); @@ -626,22 +629,26 @@ describe('Terminal', () => { clientWidth: 800, // Would result in 100 cols (minus 1 for scrollbar prevention) clientHeight: 600, // Let fitTerminal calculate the actual rows }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; // Mock character width measurement - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Spy on dispatchEvent const dispatchEventSpy = vi.spyOn(element, 'dispatchEvent'); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Terminal resize SHOULD be called - verify it was called expect(mockTerminal?.resize).toHaveBeenCalled(); // Get the actual values it was called with - const [cols, rows] = mockTerminal!.resize.mock.calls[0]; + if (!mockTerminal) throw new Error('mockTerminal is undefined'); + const [cols, rows] = mockTerminal.resize.mock.calls[0]; // Verify cols is different from original (80) expect(cols).toBe(99); // (800/8) - 1 = 99 @@ -677,14 +684,19 @@ describe('Terminal', () => { clientWidth: (currentCols + 1) * 8, clientHeight: currentRows * lineHeight, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Call fitTerminal multiple times - (element as any).fitTerminal(); - (element as any).fitTerminal(); - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Resize should not be called at all (dimensions unchanged) expect(mockTerminal?.resize).not.toHaveBeenCalled(); @@ -707,12 +719,15 @@ describe('Terminal', () => { clientHeight: 480, style: { fontSize: '14px' }, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // In fitHorizontally mode, terminal should maintain its column count expect(element.cols).toBe(80); @@ -740,16 +755,20 @@ describe('Terminal', () => { clientWidth: 1000, // Would result in 125 cols without constraint clientHeight: 480, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Terminal should resize respecting maxCols constraint expect(mockTerminal?.resize).toHaveBeenCalled(); - const [cols] = mockTerminal!.resize.mock.calls[0]; + if (!mockTerminal) throw new Error('mockTerminal is undefined'); + const [cols] = mockTerminal.resize.mock.calls[0]; expect(cols).toBe(100); // Should be limited to maxCols }); @@ -759,7 +778,7 @@ describe('Terminal', () => { element.initialCols = 120; element.initialRows = 30; element.maxCols = 0; // No manual width selection - (element as any).userOverrideWidth = false; + element.userOverrideWidth = false; // Set terminal's current dimensions (different from initial) if (mockTerminal) { @@ -772,16 +791,20 @@ describe('Terminal', () => { clientWidth: 1200, // Would result in 150 cols without constraint clientHeight: 600, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Terminal should be limited to initial cols for tunneled sessions expect(mockTerminal?.resize).toHaveBeenCalled(); - const [cols] = mockTerminal!.resize.mock.calls[0]; + if (!mockTerminal) throw new Error('mockTerminal is undefined'); + const [cols] = mockTerminal.resize.mock.calls[0]; expect(cols).toBe(120); // Should be limited to initialCols }); @@ -791,7 +814,7 @@ describe('Terminal', () => { element.initialCols = 120; element.initialRows = 30; element.maxCols = 0; - (element as any).userOverrideWidth = false; + element.userOverrideWidth = false; // Set terminal's current dimensions if (mockTerminal) { @@ -804,17 +827,21 @@ describe('Terminal', () => { clientWidth: 1200, clientHeight: 600, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Terminal should NOT be limited by initial dimensions for frontend sessions // Should use calculated width: (1200/8) - 1 = 149 expect(mockTerminal?.resize).toHaveBeenCalled(); - const [cols] = mockTerminal!.resize.mock.calls[0]; + if (!mockTerminal) throw new Error('mockTerminal is undefined'); + const [cols] = mockTerminal.resize.mock.calls[0]; expect(cols).toBe(149); // Should use full calculated width }); @@ -835,15 +862,18 @@ describe('Terminal', () => { clientWidth: 808, // (100 + 1) * 8 = 808 (accounting for the -1 in calculation) clientHeight: 30 * lineHeight, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Clear previous calls mockTerminal?.resize.mockClear(); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Resize should NOT be called since calculated dimensions match current expect(mockTerminal?.resize).not.toHaveBeenCalled(); @@ -861,16 +891,20 @@ describe('Terminal', () => { clientWidth: 100, clientHeight: 50, }; - (element as any).container = mockContainer; + // @ts-expect-error: accessing private property for testing + element.container = mockContainer; - vi.spyOn(element as any, 'measureCharacterWidth').mockReturnValue(8); + // @ts-expect-error: accessing private method for testing + vi.spyOn(element, 'measureCharacterWidth').mockReturnValue(8); // Call fitTerminal - (element as any).fitTerminal(); + // @ts-expect-error: accessing private method for testing + element.fitTerminal(); // Should resize to minimum allowed dimensions expect(mockTerminal?.resize).toHaveBeenCalled(); - const [cols, rows] = mockTerminal!.resize.mock.calls[0]; + if (!mockTerminal) throw new Error('mockTerminal is undefined'); + const [cols, rows] = mockTerminal.resize.mock.calls[0]; // The calculation is: Math.max(20, Math.floor(100 / 8) - 1) = Math.max(20, 11) = 20 // But if we're getting 19, it might be due to some other factor diff --git a/web/src/server/server.ts b/web/src/server/server.ts index 0939b833..0cb8d8db 100644 --- a/web/src/server/server.ts +++ b/web/src/server/server.ts @@ -367,6 +367,55 @@ export async function createApp(): Promise { app.use(express.json()); logger.debug('Configured express middleware'); + // Add security headers middleware + app.use((_req, res, next) => { + // Detect if we're in Playwright test environment + // Check multiple conditions to ensure test environment is detected + const isPlaywrightTest = + process.env.PLAYWRIGHT_TEST === 'true' || + process.env.NODE_ENV === 'test' || + config.port === 4022; // Test port from test-config.ts + + // Log once on startup + if (!app.locals.cspLogged) { + logger.debug(`PLAYWRIGHT_TEST env var: ${process.env.PLAYWRIGHT_TEST}`); + logger.debug(`NODE_ENV: ${process.env.NODE_ENV}`); + logger.debug(`Server port: ${config.port}`); + logger.debug( + `CSP mode: ${isPlaywrightTest ? 'test (with unsafe-eval)' : 'production (no unsafe-eval)'}` + ); + app.locals.cspLogged = true; + } + + // Content Security Policy to prevent XSS and other injection attacks + const scriptSrc = isPlaywrightTest + ? "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://unpkg.com; " // Add unsafe-eval for Playwright tests + : "script-src 'self' 'unsafe-inline' https://unpkg.com; "; // Production CSP without unsafe-eval + + res.setHeader( + 'Content-Security-Policy', + "default-src 'self'; " + + scriptSrc + + "style-src 'self' 'unsafe-inline'; " + // Allow inline styles + "img-src 'self' data: blob:; " + // Allow data and blob URLs for images + "font-src 'self' data:; " + // Allow data URLs for fonts + "connect-src 'self' ws: wss:; " + // Allow WebSocket connections + "frame-ancestors 'none'; " + // Prevent clickjacking + "base-uri 'self'; " + // Restrict base tag usage + "form-action 'self'" // Restrict form submissions + ); + + // Additional security headers + res.setHeader('X-Content-Type-Options', 'nosniff'); + res.setHeader('X-Frame-Options', 'DENY'); + res.setHeader('X-XSS-Protection', '1; mode=block'); + res.setHeader('Referrer-Policy', 'strict-origin-when-cross-origin'); + res.setHeader('Permissions-Policy', 'camera=(), microphone=(), geolocation=()'); + + next(); + }); + logger.debug('Configured security headers middleware'); + // Control directory for session data const CONTROL_DIR = process.env.VIBETUNNEL_CONTROL_DIR || path.join(os.homedir(), '.vibetunnel/control'); @@ -494,6 +543,31 @@ export async function createApp(): Promise { app.use( express.static(publicPath, { extensions: ['html'], // This allows /logs to resolve to /logs.html + setHeaders: (res, path) => { + // Apply stricter CSP for HTML files + if (path.endsWith('.html')) { + const isPlaywrightTest = + process.env.PLAYWRIGHT_TEST === 'true' || + process.env.NODE_ENV === 'test' || + config.port === 4022; + const scriptSrc = isPlaywrightTest + ? "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://unpkg.com; " + : "script-src 'self' 'unsafe-inline' https://unpkg.com; "; + + res.setHeader( + 'Content-Security-Policy', + "default-src 'self'; " + + scriptSrc + + "style-src 'self' 'unsafe-inline'; " + + "img-src 'self' data: blob:; " + + "font-src 'self' data:; " + + "connect-src 'self' ws: wss:; " + + "frame-ancestors 'none'; " + + "base-uri 'self'; " + + "form-action 'self'" + ); + } + }, }) ); logger.debug(`Serving static files from: ${publicPath}`); diff --git a/web/src/test/playwright/helpers/assertion.helper.ts b/web/src/test/playwright/helpers/assertion.helper.ts index 43236463..51c123dd 100644 --- a/web/src/test/playwright/helpers/assertion.helper.ts +++ b/web/src/test/playwright/helpers/assertion.helper.ts @@ -8,7 +8,7 @@ export async function assertSessionInList( sessionName: string, options: { timeout?: number; status?: 'RUNNING' | 'EXITED' | 'KILLED' } = {} ): Promise { - const { timeout = 5000, status } = options; + const { timeout = 15000, status } = options; // Ensure we're on the session list page if (page.url().includes('?session=')) { diff --git a/web/src/test/playwright/helpers/test-data-manager.helper.ts b/web/src/test/playwright/helpers/test-data-manager.helper.ts index 6989aa84..1a766275 100644 --- a/web/src/test/playwright/helpers/test-data-manager.helper.ts +++ b/web/src/test/playwright/helpers/test-data-manager.helper.ts @@ -122,13 +122,11 @@ export class TestSessionManager { try { // Wait for page to be ready - either session cards or "no sessions" message - await this.page.waitForFunction( - () => { - const cards = document.querySelectorAll('session-card'); - const noSessionsMsg = document.querySelector('.text-dark-text-muted'); - return cards.length > 0 || noSessionsMsg?.textContent?.includes('No terminal sessions'); - }, - { timeout: 5000 } + await this.page.waitForSelector( + 'session-card, .text-dark-text-muted:has-text("No terminal sessions")', + { + timeout: 5000, + } ); // Check if session exists diff --git a/web/src/test/playwright/specs/session-creation.spec.ts b/web/src/test/playwright/specs/session-creation.spec.ts index 6df65419..a803d6db 100644 --- a/web/src/test/playwright/specs/session-creation.spec.ts +++ b/web/src/test/playwright/specs/session-creation.spec.ts @@ -55,6 +55,14 @@ test.describe('Session Creation', () => { // Navigate back and verify await page.goto('/'); + + // Wait for session list to be ready + await page.waitForLoadState('networkidle'); + await page.waitForSelector('session-card, .text-dark-text-muted', { + state: 'visible', + timeout: 10000, + }); + await assertSessionInList(page, sessionName, { status: 'RUNNING' }); }); diff --git a/web/src/test/playwright/specs/session-management-advanced.spec.ts b/web/src/test/playwright/specs/session-management-advanced.spec.ts index 34062c5c..ec3794c6 100644 --- a/web/src/test/playwright/specs/session-management-advanced.spec.ts +++ b/web/src/test/playwright/specs/session-management-advanced.spec.ts @@ -123,11 +123,14 @@ test.describe('Advanced Session Management', () => { test('should display session metadata correctly', async ({ page }) => { // Create a session with the default command const sessionName = sessionManager.generateSessionName('metadata-test'); - await sessionManager.createTrackedSession(sessionName, false, 'bash'); + const { sessionId } = await sessionManager.createTrackedSession(sessionName, false, 'bash'); - // The session is created with default working directory (~) - // Since we can't set a custom working directory without shell operators, - // we'll just check the default behavior + // Navigate to the session to see its metadata + await page.goto(`/?session=${sessionId}`); + await page.waitForLoadState('networkidle'); + + // Wait for the session view to be fully loaded + await page.waitForSelector('vibe-terminal', { state: 'visible', timeout: 10000 }); // Check that the path is displayed const pathElement = page.locator('[title="Click to copy path"]'); diff --git a/web/src/test/playwright/specs/session-management-global.spec.ts b/web/src/test/playwright/specs/session-management-global.spec.ts index 693910d8..f90b5618 100644 --- a/web/src/test/playwright/specs/session-management-global.spec.ts +++ b/web/src/test/playwright/specs/session-management-global.spec.ts @@ -21,7 +21,7 @@ test.describe('Global Session Management', () => { await sessionManager.cleanupAllSessions(); }); - test('should kill all sessions at once', async ({ page, sessionListPage }) => { + test('should kill all sessions at once', async ({ page }) => { // Increase timeout for this test as it involves multiple sessions test.setTimeout(TIMEOUTS.KILL_ALL_OPERATION * 3); // 90 seconds