From e25c3f1d38505ed96654aecb50c00872e8f15908 Mon Sep 17 00:00:00 2001 From: trenton Date: Mon, 13 Jul 2026 07:41:56 -0400 Subject: [PATCH] feat(desktop): OpenAgentic desktop app (Tauri) + wizard install step Native macOS Tauri 2 + React desktop client for the OpenAgentic platform, in the openagentics.io green/glass design language. Surfaces: - Chat: NDJSON streaming timeline, HITL approval sheets, crash-safe turn resume via the durable ring-buffer tail, session sidebar with rename/search. - Code: the agenticode agent bundled binary-only as a loopback-locked sidecar (openagentic edition; nothing proprietary in this tree), stream-json render with permission gates and diff cards. - Flows: run + observe workflows, live NDJSON run log with async-poll fallback. - Local-executor bridge: platform chat gets read/write/list/search/run hands on a trusted workspace behind a canonicalized-under-root Rust security boundary. Auth mints and keychains a user-bound oa_ API key (never persists the JWT). Surfaces stay mounted across rail switches so a running turn survives; keyboard shortcuts, command palette, offline banner, dock badge, and per-visibility modal key-gating round out the UX. Wizard: install.sh stays TUI/CLI-first and gains a macOS "Desktop app" step that builds, validates, installs to /Applications, and opens the app after the stack is healthy; PTY harness covers it (and fixes a pre-existing Node 26 + Ink keypress stall). CORS for the Tauri webview is handled by the wizard writing ALLOWED_ORIGINS (tauri://localhost, http://tauri.localhost) plus the app's plugin-http transport sending no browser Origin header; the shared fastify config default also carries the origins for the factory used in tests. Co-Authored-By: Claude Fable 5 --- .gitignore | 5 + companions/desktop/.gitignore | 10 + companions/desktop/.npmrc | 1 + companions/desktop/README.md | 155 + companions/desktop/app-icon.svg | 25 + companions/desktop/index.html | 12 + companions/desktop/package.json | 42 + companions/desktop/pnpm-lock.yaml | 3060 +++++++++ companions/desktop/pnpm-workspace.yaml | 12 + companions/desktop/scripts/build-desktop.sh | 41 + companions/desktop/scripts/check-sidecar.sh | 31 + companions/desktop/scripts/install-app.sh | 57 + .../desktop/scripts/stage-agenticode.sh | 106 + companions/desktop/src-tauri/Cargo.lock | 5645 +++++++++++++++++ companions/desktop/src-tauri/Cargo.toml | 26 + companions/desktop/src-tauri/build.rs | 3 + .../src-tauri/capabilities/default.json | 47 + .../desktop/src-tauri/icons/128x128.png | Bin 0 -> 2672 bytes .../desktop/src-tauri/icons/128x128@2x.png | Bin 0 -> 5161 bytes companions/desktop/src-tauri/icons/32x32.png | Bin 0 -> 706 bytes companions/desktop/src-tauri/icons/64x64.png | Bin 0 -> 1362 bytes .../src-tauri/icons/Square107x107Logo.png | Bin 0 -> 2301 bytes .../src-tauri/icons/Square142x142Logo.png | Bin 0 -> 3014 bytes .../src-tauri/icons/Square150x150Logo.png | Bin 0 -> 3165 bytes .../src-tauri/icons/Square284x284Logo.png | Bin 0 -> 5759 bytes .../src-tauri/icons/Square30x30Logo.png | Bin 0 -> 685 bytes .../src-tauri/icons/Square310x310Logo.png | Bin 0 -> 6193 bytes .../src-tauri/icons/Square44x44Logo.png | Bin 0 -> 942 bytes .../src-tauri/icons/Square71x71Logo.png | Bin 0 -> 1560 bytes .../src-tauri/icons/Square89x89Logo.png | Bin 0 -> 1844 bytes .../desktop/src-tauri/icons/StoreLogo.png | Bin 0 -> 1118 bytes .../android/mipmap-anydpi-v26/ic_launcher.xml | 5 + .../icons/android/mipmap-hdpi/ic_launcher.png | Bin 0 -> 1535 bytes .../mipmap-hdpi/ic_launcher_foreground.png | Bin 0 -> 3351 bytes .../android/mipmap-hdpi/ic_launcher_round.png | Bin 0 -> 1362 bytes .../icons/android/mipmap-mdpi/ic_launcher.png | Bin 0 -> 1540 bytes .../mipmap-mdpi/ic_launcher_foreground.png | Bin 0 -> 2372 bytes .../android/mipmap-mdpi/ic_launcher_round.png | Bin 0 -> 1335 bytes .../android/mipmap-xhdpi/ic_launcher.png | Bin 0 -> 3326 bytes .../mipmap-xhdpi/ic_launcher_foreground.png | Bin 0 -> 4552 bytes .../mipmap-xhdpi/ic_launcher_round.png | Bin 0 -> 2913 bytes .../android/mipmap-xxhdpi/ic_launcher.png | Bin 0 -> 5475 bytes .../mipmap-xxhdpi/ic_launcher_foreground.png | Bin 0 -> 6766 bytes .../mipmap-xxhdpi/ic_launcher_round.png | Bin 0 -> 4695 bytes .../android/mipmap-xxxhdpi/ic_launcher.png | Bin 0 -> 7620 bytes .../mipmap-xxxhdpi/ic_launcher_foreground.png | Bin 0 -> 9047 bytes .../mipmap-xxxhdpi/ic_launcher_round.png | Bin 0 -> 6513 bytes .../android/values/ic_launcher_background.xml | 4 + companions/desktop/src-tauri/icons/icon.icns | Bin 0 -> 63733 bytes companions/desktop/src-tauri/icons/icon.ico | Bin 0 -> 10009 bytes companions/desktop/src-tauri/icons/icon.png | Bin 0 -> 10700 bytes .../src-tauri/icons/ios/AppIcon-20x20@1x.png | Bin 0 -> 491 bytes .../icons/ios/AppIcon-20x20@2x-1.png | Bin 0 -> 862 bytes .../src-tauri/icons/ios/AppIcon-20x20@2x.png | Bin 0 -> 862 bytes .../src-tauri/icons/ios/AppIcon-20x20@3x.png | Bin 0 -> 1348 bytes .../src-tauri/icons/ios/AppIcon-29x29@1x.png | Bin 0 -> 657 bytes .../icons/ios/AppIcon-29x29@2x-1.png | Bin 0 -> 1269 bytes .../src-tauri/icons/ios/AppIcon-29x29@2x.png | Bin 0 -> 1269 bytes .../src-tauri/icons/ios/AppIcon-29x29@3x.png | Bin 0 -> 1914 bytes .../src-tauri/icons/ios/AppIcon-40x40@1x.png | Bin 0 -> 862 bytes .../icons/ios/AppIcon-40x40@2x-1.png | Bin 0 -> 1766 bytes .../src-tauri/icons/ios/AppIcon-40x40@2x.png | Bin 0 -> 1766 bytes .../src-tauri/icons/ios/AppIcon-40x40@3x.png | Bin 0 -> 2589 bytes .../src-tauri/icons/ios/AppIcon-512@2x.png | Bin 0 -> 24827 bytes .../src-tauri/icons/ios/AppIcon-60x60@2x.png | Bin 0 -> 2589 bytes .../src-tauri/icons/ios/AppIcon-60x60@3x.png | Bin 0 -> 3695 bytes .../src-tauri/icons/ios/AppIcon-76x76@1x.png | Bin 0 -> 1702 bytes .../src-tauri/icons/ios/AppIcon-76x76@2x.png | Bin 0 -> 3215 bytes .../icons/ios/AppIcon-83.5x83.5@2x.png | Bin 0 -> 3561 bytes .../desktop/src-tauri/src/code_spawn.rs | 79 + companions/desktop/src-tauri/src/keychain.rs | 41 + companions/desktop/src-tauri/src/lib.rs | 46 + companions/desktop/src-tauri/src/main.rs | 7 + companions/desktop/src-tauri/src/workspace.rs | 390 ++ companions/desktop/src-tauri/tauri.conf.json | 35 + companions/desktop/src/App.test.tsx | 184 + companions/desktop/src/App.tsx | 219 + companions/desktop/src/api/client.test.ts | 708 +++ companions/desktop/src/api/client.ts | 407 ++ companions/desktop/src/api/ndjson.test.ts | 219 + companions/desktop/src/api/ndjson.ts | 130 + companions/desktop/src/api/types.ts | 60 + .../desktop/src/assets/fonts/inter-600.woff2 | Bin 0 -> 24368 bytes .../src/assets/fonts/inter-regular.woff2 | Bin 0 -> 23692 bytes companions/desktop/src/auth/LoginScreen.tsx | 116 + .../desktop/src/chat/ApprovalSheet.test.tsx | 107 + companions/desktop/src/chat/ApprovalSheet.tsx | 147 + companions/desktop/src/chat/ChatSurface.tsx | 281 + companions/desktop/src/chat/Composer.tsx | 169 + .../desktop/src/chat/MessageList.test.tsx | 102 + companions/desktop/src/chat/MessageList.tsx | 213 + .../desktop/src/chat/SessionSidebar.test.tsx | 77 + .../desktop/src/chat/SessionSidebar.tsx | 202 + .../desktop/src/chat/blocks/AgentLane.tsx | 33 + .../desktop/src/chat/blocks/TextBlock.tsx | 86 + .../desktop/src/chat/blocks/ThinkingBlock.tsx | 40 + .../desktop/src/chat/blocks/ToolCard.test.tsx | 58 + .../desktop/src/chat/blocks/ToolCard.tsx | 124 + .../desktop/src/chat/streamReducer.test.ts | 708 +++ companions/desktop/src/chat/streamReducer.ts | 333 + companions/desktop/src/chat/useChat.test.ts | 1110 ++++ companions/desktop/src/chat/useChat.ts | 625 ++ companions/desktop/src/code/CodeSurface.tsx | 242 + .../desktop/src/code/CodeTimeline.test.tsx | 44 + companions/desktop/src/code/CodeTimeline.tsx | 212 + companions/desktop/src/code/DiffCard.tsx | 105 + .../desktop/src/code/PermissionSheet.test.tsx | 79 + .../desktop/src/code/PermissionSheet.tsx | 177 + .../desktop/src/code/WorkspacePicker.tsx | 92 + .../desktop/src/code/codeReducer.test.ts | 403 ++ companions/desktop/src/code/codeReducer.ts | 265 + companions/desktop/src/code/codec.test.ts | 179 + companions/desktop/src/code/codec.ts | 142 + companions/desktop/src/code/diff.test.ts | 96 + companions/desktop/src/code/diff.ts | 137 + .../desktop/src/code/useCodeSession.test.ts | 331 + companions/desktop/src/code/useCodeSession.ts | 258 + companions/desktop/src/dev/autoprobe.ts | 93 + .../desktop/src/executor/TrustBanner.tsx | 79 + .../executor/WorkspaceApprovalModal.test.tsx | 82 + .../src/executor/WorkspaceApprovalModal.tsx | 98 + .../src/executor/localExecutor.test.ts | 389 ++ .../desktop/src/executor/localExecutor.ts | 435 ++ companions/desktop/src/executor/toolDefs.ts | 96 + .../desktop/src/executor/trustStore.test.ts | 57 + companions/desktop/src/executor/trustStore.ts | 178 + .../desktop/src/executor/useLocalExecutor.ts | 64 + companions/desktop/src/flows/FlowRunView.tsx | 101 + companions/desktop/src/flows/FlowsSurface.tsx | 375 ++ .../desktop/src/flows/parseRunInput.test.ts | 27 + companions/desktop/src/flows/useFlows.test.ts | 487 ++ companions/desktop/src/flows/useFlows.ts | 548 ++ companions/desktop/src/host/browserHost.ts | 62 + companions/desktop/src/host/codeSpawn.test.ts | 114 + companions/desktop/src/host/codeSpawn.ts | 89 + companions/desktop/src/host/host.test.ts | 39 + companions/desktop/src/host/host.ts | 119 + companions/desktop/src/host/tauriHost.ts | 167 + companions/desktop/src/main.tsx | 16 + .../desktop/src/settings/SettingsSurface.tsx | 246 + .../desktop/src/shell/CommandPalette.tsx | 156 + .../desktop/src/shell/OfflineBanner.tsx | 27 + .../desktop/src/shell/ShortcutsOverlay.tsx | 60 + companions/desktop/src/shell/fuzzy.test.ts | 43 + companions/desktop/src/shell/fuzzy.ts | 48 + companions/desktop/src/shell/shell.test.tsx | 114 + companions/desktop/src/shell/useDockBadge.ts | 33 + .../desktop/src/shell/useGlobalShortcuts.ts | 103 + .../desktop/src/shell/useOfflineMonitor.ts | 68 + companions/desktop/src/stores/appStore.ts | 42 + .../desktop/src/stores/authStore.test.ts | 255 + companions/desktop/src/stores/authStore.ts | 147 + companions/desktop/src/stores/chatStore.ts | 191 + .../desktop/src/stores/codeStore.test.ts | 103 + companions/desktop/src/stores/codeStore.ts | 118 + companions/desktop/src/styles/base.css | 253 + companions/desktop/src/styles/tokens.css | 161 + companions/desktop/tsconfig.app.json | 26 + companions/desktop/tsconfig.json | 4 + companions/desktop/tsconfig.node.json | 15 + companions/desktop/vite.config.ts | 39 + companions/desktop/vitest.config.ts | 11 + .../__tests__/fastify.config.cors.test.ts | 66 + .../src/config/fastify.config.ts | 5 +- tools/setup/src/backends/docker.ts | 91 + tools/setup/src/index.tsx | 41 +- tools/setup/src/lib/types.ts | 8 + tools/setup/src/steps/DesktopApp.tsx | 58 + tools/setup/src/steps/Launch.tsx | 63 +- tools/setup/src/steps/Review.tsx | 2 + tools/setup/tests/pty_harness.py | 224 +- 171 files changed, 25599 insertions(+), 42 deletions(-) create mode 100644 companions/desktop/.gitignore create mode 100644 companions/desktop/.npmrc create mode 100644 companions/desktop/README.md create mode 100644 companions/desktop/app-icon.svg create mode 100644 companions/desktop/index.html create mode 100644 companions/desktop/package.json create mode 100644 companions/desktop/pnpm-lock.yaml create mode 100644 companions/desktop/pnpm-workspace.yaml create mode 100755 companions/desktop/scripts/build-desktop.sh create mode 100755 companions/desktop/scripts/check-sidecar.sh create mode 100755 companions/desktop/scripts/install-app.sh create mode 100755 companions/desktop/scripts/stage-agenticode.sh create mode 100644 companions/desktop/src-tauri/Cargo.lock create mode 100644 companions/desktop/src-tauri/Cargo.toml create mode 100644 companions/desktop/src-tauri/build.rs create mode 100644 companions/desktop/src-tauri/capabilities/default.json create mode 100644 companions/desktop/src-tauri/icons/128x128.png create mode 100644 companions/desktop/src-tauri/icons/128x128@2x.png create mode 100644 companions/desktop/src-tauri/icons/32x32.png create mode 100644 companions/desktop/src-tauri/icons/64x64.png create mode 100644 companions/desktop/src-tauri/icons/Square107x107Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square142x142Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square150x150Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square284x284Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square30x30Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square310x310Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square44x44Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square71x71Logo.png create mode 100644 companions/desktop/src-tauri/icons/Square89x89Logo.png create mode 100644 companions/desktop/src-tauri/icons/StoreLogo.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png create mode 100644 companions/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png create mode 100644 companions/desktop/src-tauri/icons/android/values/ic_launcher_background.xml create mode 100644 companions/desktop/src-tauri/icons/icon.icns create mode 100644 companions/desktop/src-tauri/icons/icon.ico create mode 100644 companions/desktop/src-tauri/icons/icon.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-29x29@1x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-40x40@2x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-40x40@3x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-512@2x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png create mode 100644 companions/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png create mode 100644 companions/desktop/src-tauri/src/code_spawn.rs create mode 100644 companions/desktop/src-tauri/src/keychain.rs create mode 100644 companions/desktop/src-tauri/src/lib.rs create mode 100644 companions/desktop/src-tauri/src/main.rs create mode 100644 companions/desktop/src-tauri/src/workspace.rs create mode 100644 companions/desktop/src-tauri/tauri.conf.json create mode 100644 companions/desktop/src/App.test.tsx create mode 100644 companions/desktop/src/App.tsx create mode 100644 companions/desktop/src/api/client.test.ts create mode 100644 companions/desktop/src/api/client.ts create mode 100644 companions/desktop/src/api/ndjson.test.ts create mode 100644 companions/desktop/src/api/ndjson.ts create mode 100644 companions/desktop/src/api/types.ts create mode 100644 companions/desktop/src/assets/fonts/inter-600.woff2 create mode 100644 companions/desktop/src/assets/fonts/inter-regular.woff2 create mode 100644 companions/desktop/src/auth/LoginScreen.tsx create mode 100644 companions/desktop/src/chat/ApprovalSheet.test.tsx create mode 100644 companions/desktop/src/chat/ApprovalSheet.tsx create mode 100644 companions/desktop/src/chat/ChatSurface.tsx create mode 100644 companions/desktop/src/chat/Composer.tsx create mode 100644 companions/desktop/src/chat/MessageList.test.tsx create mode 100644 companions/desktop/src/chat/MessageList.tsx create mode 100644 companions/desktop/src/chat/SessionSidebar.test.tsx create mode 100644 companions/desktop/src/chat/SessionSidebar.tsx create mode 100644 companions/desktop/src/chat/blocks/AgentLane.tsx create mode 100644 companions/desktop/src/chat/blocks/TextBlock.tsx create mode 100644 companions/desktop/src/chat/blocks/ThinkingBlock.tsx create mode 100644 companions/desktop/src/chat/blocks/ToolCard.test.tsx create mode 100644 companions/desktop/src/chat/blocks/ToolCard.tsx create mode 100644 companions/desktop/src/chat/streamReducer.test.ts create mode 100644 companions/desktop/src/chat/streamReducer.ts create mode 100644 companions/desktop/src/chat/useChat.test.ts create mode 100644 companions/desktop/src/chat/useChat.ts create mode 100644 companions/desktop/src/code/CodeSurface.tsx create mode 100644 companions/desktop/src/code/CodeTimeline.test.tsx create mode 100644 companions/desktop/src/code/CodeTimeline.tsx create mode 100644 companions/desktop/src/code/DiffCard.tsx create mode 100644 companions/desktop/src/code/PermissionSheet.test.tsx create mode 100644 companions/desktop/src/code/PermissionSheet.tsx create mode 100644 companions/desktop/src/code/WorkspacePicker.tsx create mode 100644 companions/desktop/src/code/codeReducer.test.ts create mode 100644 companions/desktop/src/code/codeReducer.ts create mode 100644 companions/desktop/src/code/codec.test.ts create mode 100644 companions/desktop/src/code/codec.ts create mode 100644 companions/desktop/src/code/diff.test.ts create mode 100644 companions/desktop/src/code/diff.ts create mode 100644 companions/desktop/src/code/useCodeSession.test.ts create mode 100644 companions/desktop/src/code/useCodeSession.ts create mode 100644 companions/desktop/src/dev/autoprobe.ts create mode 100644 companions/desktop/src/executor/TrustBanner.tsx create mode 100644 companions/desktop/src/executor/WorkspaceApprovalModal.test.tsx create mode 100644 companions/desktop/src/executor/WorkspaceApprovalModal.tsx create mode 100644 companions/desktop/src/executor/localExecutor.test.ts create mode 100644 companions/desktop/src/executor/localExecutor.ts create mode 100644 companions/desktop/src/executor/toolDefs.ts create mode 100644 companions/desktop/src/executor/trustStore.test.ts create mode 100644 companions/desktop/src/executor/trustStore.ts create mode 100644 companions/desktop/src/executor/useLocalExecutor.ts create mode 100644 companions/desktop/src/flows/FlowRunView.tsx create mode 100644 companions/desktop/src/flows/FlowsSurface.tsx create mode 100644 companions/desktop/src/flows/parseRunInput.test.ts create mode 100644 companions/desktop/src/flows/useFlows.test.ts create mode 100644 companions/desktop/src/flows/useFlows.ts create mode 100644 companions/desktop/src/host/browserHost.ts create mode 100644 companions/desktop/src/host/codeSpawn.test.ts create mode 100644 companions/desktop/src/host/codeSpawn.ts create mode 100644 companions/desktop/src/host/host.test.ts create mode 100644 companions/desktop/src/host/host.ts create mode 100644 companions/desktop/src/host/tauriHost.ts create mode 100644 companions/desktop/src/main.tsx create mode 100644 companions/desktop/src/settings/SettingsSurface.tsx create mode 100644 companions/desktop/src/shell/CommandPalette.tsx create mode 100644 companions/desktop/src/shell/OfflineBanner.tsx create mode 100644 companions/desktop/src/shell/ShortcutsOverlay.tsx create mode 100644 companions/desktop/src/shell/fuzzy.test.ts create mode 100644 companions/desktop/src/shell/fuzzy.ts create mode 100644 companions/desktop/src/shell/shell.test.tsx create mode 100644 companions/desktop/src/shell/useDockBadge.ts create mode 100644 companions/desktop/src/shell/useGlobalShortcuts.ts create mode 100644 companions/desktop/src/shell/useOfflineMonitor.ts create mode 100644 companions/desktop/src/stores/appStore.ts create mode 100644 companions/desktop/src/stores/authStore.test.ts create mode 100644 companions/desktop/src/stores/authStore.ts create mode 100644 companions/desktop/src/stores/chatStore.ts create mode 100644 companions/desktop/src/stores/codeStore.test.ts create mode 100644 companions/desktop/src/stores/codeStore.ts create mode 100644 companions/desktop/src/styles/base.css create mode 100644 companions/desktop/src/styles/tokens.css create mode 100644 companions/desktop/tsconfig.app.json create mode 100644 companions/desktop/tsconfig.json create mode 100644 companions/desktop/tsconfig.node.json create mode 100644 companions/desktop/vite.config.ts create mode 100644 companions/desktop/vitest.config.ts create mode 100644 services/openagentic-api/src/config/__tests__/fastify.config.cors.test.ts create mode 100644 tools/setup/src/steps/DesktopApp.tsx diff --git a/.gitignore b/.gitignore index ed3c9adaae..22738b4bba 100755 --- a/.gitignore +++ b/.gitignore @@ -366,6 +366,11 @@ playwright-report/ companions/* !companions/.gitkeep +# Exception: companions/desktop is a real, committed source tree (the +# OpenAgentic Tauri desktop companion app), not transient vendor-staged docs +# source like the other companions/ dirs this block targets. +!companions/desktop/ + # Local scratch / brainstorming artifacts .superpowers/ diff --git a/companions/desktop/.gitignore b/companions/desktop/.gitignore new file mode 100644 index 0000000000..48841caff0 --- /dev/null +++ b/companions/desktop/.gitignore @@ -0,0 +1,10 @@ +node_modules/ +dist/ +src-tauri/target/ +src-tauri/binaries/ +src-tauri/cli-runtime/ +src-tauri/gen/ + +# The repo-root .gitignore ignores *.png globally (screenshot dumps); the +# generated Tauri app icons under src-tauri/icons/ must be committed. +!src-tauri/icons/** diff --git a/companions/desktop/.npmrc b/companions/desktop/.npmrc new file mode 100644 index 0000000000..25d25d0894 --- /dev/null +++ b/companions/desktop/.npmrc @@ -0,0 +1 @@ +shamefully-hoist=false diff --git a/companions/desktop/README.md b/companions/desktop/README.md new file mode 100644 index 0000000000..bc2be42641 --- /dev/null +++ b/companions/desktop/README.md @@ -0,0 +1,155 @@ +# openagentic-desktop + +The OpenAgentic desktop companion — a [Tauri](https://tauri.app) shell around +the chat, code, and flows surfaces. Standalone pnpm app; not part of the root +workspace (has its own `pnpm-lock.yaml`). + +## Develop + +```bash +pnpm install +pnpm tauri dev +``` + +## Build + +```bash +# preflight: fails fast if the (gitignored) agenticode sidecar isn't staged — +# on a fresh clone run scripts/stage-agenticode.sh first. Not wired into +# tauri.conf's beforeBuildCommand on purpose (it would also gate `pnpm dev`). +./scripts/check-sidecar.sh + +pnpm tauri build --bundles app +``` + +Produces `src-tauri/target/release/bundle/macos/OpenAgentic.app`. + +## Test + +```bash +pnpm test +``` + +## Layout + +- `src/` — React frontend (Vite + Tailwind v4). +- `src/host/` — the Host seam (`getHost()`): one interface over everything + that differs between the Tauri webview and a plain browser tab (HTTP + transport, keychain, pickers, code child, workspace bridge). Tauri host + uses `@tauri-apps/plugin-http` fetch + the Rust `secret_*` keychain + commands; browser host uses `window.fetch` (dev proxy) + a localStorage + fallback (dev only). +- `src/stores/appStore.ts` — Zustand store driving the surface switch + (`chat | code | flows | settings`). +- `src/stores/authStore.ts` — auth state machine: `signIn` = login → + mint durable `oa_` API key → validate → persist key+profile via the host + keychain; `restore()` boots from the keychain; `signOut()` forgets it. +- `src/styles/tokens.css` — design tokens mirroring + `services/openagentic-ui/src/styles/theme.css`. +- `src-tauri/` — Rust shell (window, plugins, tray, vibrancy). + `src-tauri/src/keychain.rs` holds the `secret_get/set/delete` commands + (service name `io.openagentics.desktop`). + +## Streaming path (Task 5, Step 5 verdict) + +NDJSON response streaming inside the Tauri webview goes through +`@tauri-apps/plugin-http`'s `fetch` and was verified to arrive +**incrementally** (per-frame deltas of ~5–13 ms during token emission, with +a `ttft` marker mid-stream), not as one end-of-stream burst. The +`stream_fetch.rs` Rust fallback described in the task brief was therefore +**not needed** and is not implemented. + +To re-verify unattended (signs in, streams one message, logs per-frame +arrival timings to the `tauri dev` terminal via plugin-log): + +```bash +VITE_STREAM_PROBE=1 \ +VITE_PROBE_URL=http://localhost:8000 \ +VITE_PROBE_EMAIL= \ +VITE_PROBE_PASSWORD= \ +pnpm tauri dev +``` + +The same probe is available interactively under Settings → Diagnostics → +"Stream probe". + +Two platform-side gotchas found during verification: + +- **CORS**: the webview sends `Origin: http://localhost:1430` under + `tauri dev` (and `tauri://localhost` in production builds). The platform + api's default allowlist (`server.ts` `allowedOrigins`) does not include + these — set `ALLOWED_ORIGINS` on the api to include them. +- **Abort semantics**: plugin-http rejects an aborted request with a plain + `"Request cancelled"` error, not a DOMException named `AbortError` — + don't gate abort handling on `err.name === "AbortError"` alone. + +## agenticode sidecar — staging + spawn (Task 9) + +The code surface drives the **agenticode CLI** (openagentic edition) as a +Tauri **plugin-shell sidecar**. The CLI is a proprietary binary and is +**never committed** — both staging destinations are gitignored: +`src-tauri/binaries/` (the sidecar) and `src-tauri/cli-runtime/` (a portable +`rg`). + +### Staging the binary + +```bash +# needs an agenticode checkout (default ~/git/agenticode) + bun on PATH +AGENTICODE_DIR=~/git/agenticode ./scripts/stage-agenticode.sh +``` + +`stage-agenticode.sh` compiles the **openagentic edition** for the host triple +(`bun build --compile … --define process.env.AGENTICODE_EDITION="openagentic"`) +into `src-tauri/binaries/agenticode-cli-`, and stages a portable +ripgrep into `src-tauri/cli-runtime/rg`. It does **not** call agenticode's +`scripts/bundle.sh` — that script's positional arg is an OUTDIR (not a target) +and it builds all five OS/arch targets; we mirror the single-target +`bun build --compile` from the agenticode desktop README instead, adding the +edition `--define` and the externals bundle.sh skips. If the checkout is +missing the script exits 2 with instructions. + +The prebuilt CLI checked into `agenticode/desktop/…/binaries` is the **default** +(multi-provider) edition — do not use it; the openagentic edition is +provider-locked to a local instance and must be built fresh. + +### Spawn wiring + +`tauriHost.spawnCode({ cwd, instanceUrl, apiKey })` launches the sidecar via +`Command.sidecar('binaries/agenticode-cli', args, { cwd, env, encoding: 'raw' })`: + +- **args** (fixed): `--print --verbose --output-format stream-json + --input-format stream-json --include-partial-messages --replay-user-messages + --permission-prompt-tool stdio`. +- **env**: `AGENTICWORK_BASE_URL=/api/v1`, + `AGENTICWORK_API_KEY=`, and `PATH=:`. The + rg dir is resolved with `resolveResource('cli-runtime')`; the base PATH comes + from the Rust `code_spawn_base_path` command (`src-tauri/src/code_spawn.rs`), + which returns the app's PATH with Homebrew/local-bin promoted (a + GUI-launched macOS app inherits a stripped launchd PATH). Pure env assembly + + the fixed args live in `src/host/codeSpawn.ts` (unit-tested). +- The bun-compiled CLI can't exec its **own** embedded ripgrep (it lives in + bunfs with no on-disk path), so a real `rg` on PATH is mandatory for the + Grep/Glob tools. +- `CodeChild.onLine` reassembles raw stdout (`encoding: 'raw'` → `Uint8Array` + chunks that are **not** line-aligned) into whole NDJSON frames via a + carry-buffer splitter (`createLineSplitter`). stderr is drained into a + bounded ring buffer exposed as `stderrTail()`. + +Capabilities: `src-tauri/capabilities/default.json` grants `shell:allow-spawn` +scoped to the `binaries/agenticode-cli` sidecar with the exact arg vector, plus +`shell:allow-stdin-write` and `shell:allow-kill`. + +### Live-spawn findings (openagentic edition ↔ this api) + +Spawning the staged binary outside the app against the live api +(`http://localhost:8000`) confirmed the edition lock (a non-loopback +`AGENTICWORK_BASE_URL` is refused with the edition message), the base-URL +attach, and that the API key authenticates (models fetches return 200, no 401). +The CLI does **not** complete a headless turn against this api build, though: +its openagentic edition targets the compose UI shim (default +`http://localhost:8080/api/v1`) and its model-catalog fetch doesn't populate +from this api's `/api/v1/models` response shape (with base `…/api/v1` it even +requests `…/api/v1/v1/models` → 404), so no model resolves and the turn — and +thus the `system`/`init` frame — is skipped. Populating the catalog / selecting +a model is the code-surface UI's job (a later task); the spawn plumbing here is +edition-, auth-, and base-URL-correct up to that hand-off. diff --git a/companions/desktop/app-icon.svg b/companions/desktop/app-icon.svg new file mode 100644 index 0000000000..c63d70494e --- /dev/null +++ b/companions/desktop/app-icon.svg @@ -0,0 +1,25 @@ + + + > + + + + + + + + diff --git a/companions/desktop/index.html b/companions/desktop/index.html new file mode 100644 index 0000000000..dc5466bc83 --- /dev/null +++ b/companions/desktop/index.html @@ -0,0 +1,12 @@ + + + + + + OpenAgentic + + +
+ + + diff --git a/companions/desktop/package.json b/companions/desktop/package.json new file mode 100644 index 0000000000..3514ea5751 --- /dev/null +++ b/companions/desktop/package.json @@ -0,0 +1,42 @@ +{ + "name": "openagentic-desktop", + "private": true, + "version": "0.1.0", + "type": "module", + "packageManager": "pnpm@10.15.0", + "description": "OpenAgentic desktop companion — Tauri shell for chat, code, and flows.", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "test": "vitest run", + "tauri": "tauri" + }, + "dependencies": { + "@tauri-apps/api": "^2.2.0", + "@tauri-apps/plugin-dialog": "^2.2.0", + "@tauri-apps/plugin-http": "^2.2.0", + "@tauri-apps/plugin-log": "^2.2.0", + "@tauri-apps/plugin-os": "^2.2.0", + "@tauri-apps/plugin-shell": "^2.2.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-markdown": "^10.1.0", + "rehype-highlight": "^7.0.2", + "remark-gfm": "^4.0.1", + "zustand": "^5.0.3" + }, + "devDependencies": { + "@tailwindcss/vite": "^4.0.0", + "@tauri-apps/cli": "^2.2.7", + "@testing-library/react": "^16.1.0", + "@types/node": "^22.10.7", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", + "@vitejs/plugin-react": "^4.3.4", + "happy-dom": "^16.5.3", + "tailwindcss": "^4.0.0", + "typescript": "^5.7.3", + "vite": "^6.0.7", + "vitest": "^3.0.4" + } +} diff --git a/companions/desktop/pnpm-lock.yaml b/companions/desktop/pnpm-lock.yaml new file mode 100644 index 0000000000..effdb7d635 --- /dev/null +++ b/companions/desktop/pnpm-lock.yaml @@ -0,0 +1,3060 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@tauri-apps/api': + specifier: ^2.2.0 + version: 2.11.1 + '@tauri-apps/plugin-dialog': + specifier: ^2.2.0 + version: 2.7.1 + '@tauri-apps/plugin-http': + specifier: ^2.2.0 + version: 2.5.9 + '@tauri-apps/plugin-log': + specifier: ^2.2.0 + version: 2.8.0 + '@tauri-apps/plugin-os': + specifier: ^2.2.0 + version: 2.3.2 + '@tauri-apps/plugin-shell': + specifier: ^2.2.0 + version: 2.3.5 + react: + specifier: ^18.3.1 + version: 18.3.1 + react-dom: + specifier: ^18.3.1 + version: 18.3.1(react@18.3.1) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@18.3.31)(react@18.3.1) + rehype-highlight: + specifier: ^7.0.2 + version: 7.0.2 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + zustand: + specifier: ^5.0.3 + version: 5.0.14(@types/react@18.3.31)(react@18.3.1) + devDependencies: + '@tailwindcss/vite': + specifier: ^4.0.0 + version: 4.3.2(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)) + '@tauri-apps/cli': + specifier: ^2.2.7 + version: 2.11.4 + '@testing-library/react': + specifier: ^16.1.0 + version: 16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@types/node': + specifier: ^22.10.7 + version: 22.20.1 + '@types/react': + specifier: ^18.3.18 + version: 18.3.31 + '@types/react-dom': + specifier: ^18.3.5 + version: 18.3.7(@types/react@18.3.31) + '@vitejs/plugin-react': + specifier: ^4.3.4 + version: 4.7.0(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)) + happy-dom: + specifier: ^16.5.3 + version: 16.8.1 + tailwindcss: + specifier: ^4.0.0 + version: 4.3.2 + typescript: + specifier: ^5.7.3 + version: 5.9.3 + vite: + specifier: ^6.0.7 + version: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + vitest: + specifier: ^3.0.4 + version: 3.2.7(@types/debug@4.1.13)(@types/node@22.20.1)(happy-dom@16.8.1)(jiti@2.7.0)(lightningcss@1.32.0) + +packages: + + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/plugin-transform-react-jsx-self@7.29.7': + resolution: {integrity: sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/plugin-transform-react-jsx-source@7.29.7': + resolution: {integrity: sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} + engines: {node: '>=6.9.0'} + + '@esbuild/aix-ppc64@0.25.12': + resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.12': + resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.12': + resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.12': + resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.12': + resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.12': + resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.12': + resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.12': + resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.12': + resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.12': + resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.12': + resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.12': + resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.12': + resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.12': + resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.12': + resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.12': + resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.12': + resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.12': + resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.12': + resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.12': + resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.12': + resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.25.12': + resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.25.12': + resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.12': + resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.12': + resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.12': + resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@rolldown/pluginutils@1.0.0-beta.27': + resolution: {integrity: sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==} + + '@rollup/rollup-android-arm-eabi@4.62.2': + resolution: {integrity: sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.2': + resolution: {integrity: sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.2': + resolution: {integrity: sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.2': + resolution: {integrity: sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.2': + resolution: {integrity: sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.2': + resolution: {integrity: sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + resolution: {integrity: sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + resolution: {integrity: sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + resolution: {integrity: sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.62.2': + resolution: {integrity: sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + resolution: {integrity: sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.62.2': + resolution: {integrity: sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + resolution: {integrity: sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + resolution: {integrity: sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + resolution: {integrity: sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + resolution: {integrity: sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + resolution: {integrity: sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.62.2': + resolution: {integrity: sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.62.2': + resolution: {integrity: sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.62.2': + resolution: {integrity: sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.2': + resolution: {integrity: sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + resolution: {integrity: sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + resolution: {integrity: sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.2': + resolution: {integrity: sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.2': + resolution: {integrity: sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==} + cpu: [x64] + os: [win32] + + '@tailwindcss/node@4.3.2': + resolution: {integrity: sha512-yWP/sqEcBLaD8JuA6zNwxoYKr75qxTioYwlRwekj5Jr/I5GXnoJfjetH/psLUIv74cYTH2lBUEzBkinthoYcBg==} + + '@tailwindcss/oxide-android-arm64@4.3.2': + resolution: {integrity: sha512-WHxqIuHpvZ5VtdX6GTl1Ik/Vp2YuN42Et+0CdeaVd/frQ9jAvGmvR8vLT+jk3e8/Q3x8kECB9+R17pgpp2BulA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [android] + + '@tailwindcss/oxide-darwin-arm64@4.3.2': + resolution: {integrity: sha512-GZypeUY/IDJW3877KeM+O67vbXr3MBnbtEL4aYhNErv/JWZhye2vGSWWG9tB6iiqR2MqRNkY8IOUy4NdSZV26w==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [darwin] + + '@tailwindcss/oxide-darwin-x64@4.3.2': + resolution: {integrity: sha512-UIIzmefR6KO1sDU7MzRqAxC8iBpft/VhkGjTjnhoS6k7Z3rQ9wEgA1ODSiyH/tcSYssulNm4Ci3hOeK1jH7ccQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [darwin] + + '@tailwindcss/oxide-freebsd-x64@4.3.2': + resolution: {integrity: sha512-GN+uAmcI6DNspnCDwtOAZrTz6oukJnp337qZvxqCGLd3BHBzJpO0ZbTLRvJNdztOeAmTzewewGIMPb0tk2R4WA==} + engines: {node: '>= 20'} + cpu: [x64] + os: [freebsd] + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + resolution: {integrity: sha512-4ABn7qSbdHRwTiDiuWNegCyb5+2FJ4vKIKc3DmKrvAFw7MU1Lm11dIkTPwUaFdTzc7IsOpDbqBrlh0x6y36U/w==} + engines: {node: '>= 20'} + cpu: [arm] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + resolution: {integrity: sha512-wDgEIGwoM8w8pufh9LVt1PahDgNdKXrLC2qfAnV3vAmococ9RWbxeAw4pxPttd/TsJfwjyLf90Dg1y9y8I6Emw==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + resolution: {integrity: sha512-J5Nuk0uZQIiMTJj3LEx4sAA9tMFUoXQZFv1J6An+QGYe53HKRJuFDi0rpq/tuouCZeAbOBY3kQ6g8qeD4TUjtA==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + resolution: {integrity: sha512-kqCZpSKOBEJO4mz7OqWoofBZeXTAwaVGPj0ErAj7CojmhKpWVWVOnrt9dE8odoIraZq4oj3ausM37kXi+Tow8w==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + resolution: {integrity: sha512-cixpqbh2toJDmkuCRI68nXA8ZxNmdK9Y+9v5h3MC3ZQKy/0BO8AWzlkWyRM7JAFSGBlfig4YVTPsK6MVgqz1uw==} + engines: {node: '>= 20'} + cpu: [x64] + os: [linux] + + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + resolution: {integrity: sha512-4ec2Z/LOmRsAgU23CS4xeJfcJlmRg94A/XrbGRCF1gyU/zdDfRLYDVsS+ynSZCmGNxQ1jQriQOKMQeQxBA3Isw==} + engines: {node: '>=14.0.0'} + cpu: [wasm32] + bundledDependencies: + - '@napi-rs/wasm-runtime' + - '@emnapi/core' + - '@emnapi/runtime' + - '@tybys/wasm-util' + - '@emnapi/wasi-threads' + - tslib + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + resolution: {integrity: sha512-Zyr/M0+XcYZu3bZrUytc7TXvrk0ftWfl8gN2MwekNDzhqhKRUucMPSeOzM0o0wH5AWOU49BsKRrfKxI2atCPMQ==} + engines: {node: '>= 20'} + cpu: [arm64] + os: [win32] + + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + resolution: {integrity: sha512-QI9BO7KlNZsp2GuO0jwAAj5jCDABOKXRkCk2XuKTSaNEFSdfzqswYVTtCHBNKHLsqyjFyFkqlDiwkNbTYSssMQ==} + engines: {node: '>= 20'} + cpu: [x64] + os: [win32] + + '@tailwindcss/oxide@4.3.2': + resolution: {integrity: sha512-z8ZgnzX8gdNoWLBLqBPoh/sjnxkwvf9ZuWjnO0l0yIzbLa5/9S+eC5QxGZKRobVHIC3/1BoMWjHblqWjcgFgag==} + engines: {node: '>= 20'} + + '@tailwindcss/vite@4.3.2': + resolution: {integrity: sha512-eHpMeX4JXfVNJDEcsouTeCBubJBTcTLigeaw/NTUW6PB5ATKKXdyonnXgTBX2VuRbjz1hjfz6C5XAhr52ImQXA==} + peerDependencies: + vite: ^5.2.0 || ^6 || ^7 || ^8 + + '@tauri-apps/api@2.11.1': + resolution: {integrity: sha512-M2FPuYND2m+wh5hfW9ZpSdxMPdEJovPBWwoHJmwUpysTYNHaOkVFN419m/K0LIgjb/7KU2vBgsUepJWugQCvAA==} + + '@tauri-apps/cli-darwin-arm64@2.11.4': + resolution: {integrity: sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@tauri-apps/cli-darwin-x64@2.11.4': + resolution: {integrity: sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': + resolution: {integrity: sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@tauri-apps/cli-linux-arm64-gnu@2.11.4': + resolution: {integrity: sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-arm64-musl@2.11.4': + resolution: {integrity: sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': + resolution: {integrity: sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==} + engines: {node: '>= 10'} + cpu: [riscv64] + os: [linux] + + '@tauri-apps/cli-linux-x64-gnu@2.11.4': + resolution: {integrity: sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-linux-x64-musl@2.11.4': + resolution: {integrity: sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@tauri-apps/cli-win32-arm64-msvc@2.11.4': + resolution: {integrity: sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@tauri-apps/cli-win32-ia32-msvc@2.11.4': + resolution: {integrity: sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@tauri-apps/cli-win32-x64-msvc@2.11.4': + resolution: {integrity: sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@tauri-apps/cli@2.11.4': + resolution: {integrity: sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==} + engines: {node: '>= 10'} + hasBin: true + + '@tauri-apps/plugin-dialog@2.7.1': + resolution: {integrity: sha512-OK1UBXYt+ojcmxMktzzuyonYIFta8CmAASpX+CA+DTGK24KlHjhYI6x2iOJ/TjZF4N7/ACK1oFmEOjIY9IhzOQ==} + + '@tauri-apps/plugin-http@2.5.9': + resolution: {integrity: sha512-lCiY0+vs4HvIUSvZrBs8TC3TiCB0MOPRmiUjTq4prW7SlcJE2jdLeT6KBsJrT9Tlplufl7W1pY6SFAO3gCWxDA==} + + '@tauri-apps/plugin-log@2.8.0': + resolution: {integrity: sha512-a+7rOq3MJwpTOLLKbL8d0qGZ85hgHw5pNOWusA9o3cf7cEgtYHiGY/+O8fj8MvywQIGqFv0da2bYQDlrqLE7rw==} + + '@tauri-apps/plugin-os@2.3.2': + resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==} + + '@tauri-apps/plugin-shell@2.3.5': + resolution: {integrity: sha512-jewtULhiQ7lI7+owCKAjc8tYLJr92U16bPOeAa472LHJdgaibLP83NcfAF2e+wkEcA53FxKQAZ7byDzs2eeizg==} + + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/react@16.3.2': + resolution: {integrity: sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==} + engines: {node: '>=18'} + peerDependencies: + '@testing-library/dom': ^10.0.0 + '@types/react': ^18.0.0 || ^19.0.0 + '@types/react-dom': ^18.0.0 || ^19.0.0 + react: ^18.0.0 || ^19.0.0 + react-dom: ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + + '@types/babel__core@7.20.5': + resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} + + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} + + '@types/babel__template@7.4.4': + resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} + + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/debug@4.1.13': + resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/hast@3.0.5': + resolution: {integrity: sha512-rp/ezSWaD1m44dPKICGhiskI13nVr7qTloFwDa/IYkhhf5nzwP+zIQcIJh3WIFSBOy/H1PzB40jPjMDksN4F+g==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + + '@types/prop-types@15.7.15': + resolution: {integrity: sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==} + + '@types/react-dom@18.3.7': + resolution: {integrity: sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==} + peerDependencies: + '@types/react': ^18.0.0 + + '@types/react@18.3.31': + resolution: {integrity: sha512-vfEqpXTvwT91yhmwdfouStN2hSKwTvyRs8qpLfADyrq/kxDw0hZM7Wk9Ug1FELj8hIby+S/+kQCSRFF32nv2Qw==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@ungap/structured-clone@1.3.3': + resolution: {integrity: sha512-60YRaenCQcVjYEKOcG824+DRGGIQ3VKErcBoAEDJZz5bKIs2ZG+X/H9Nk+Q6EVkwJk5QNApxbrc5QtBSwtrXAg==} + + '@vitejs/plugin-react@4.7.0': + resolution: {integrity: sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==} + engines: {node: ^14.18.0 || >=16.0.0} + peerDependencies: + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + + '@vitest/expect@3.2.7': + resolution: {integrity: sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==} + + '@vitest/mocker@3.2.7': + resolution: {integrity: sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==} + peerDependencies: + msw: ^2.4.9 + vite: ^5.0.0 || ^6.0.0 || ^7.0.0-0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@3.2.7': + resolution: {integrity: sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==} + + '@vitest/runner@3.2.7': + resolution: {integrity: sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==} + + '@vitest/snapshot@3.2.7': + resolution: {integrity: sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==} + + '@vitest/spy@3.2.7': + resolution: {integrity: sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==} + + '@vitest/utils@3.2.7': + resolution: {integrity: sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + baseline-browser-mapping@2.10.43: + resolution: {integrity: sha512-AjYpR78kDWAY3Efj+cDTFH9t9SCoL7OoTp1BOb0mQV7S+6CiLwnWM3FyxhJtdPufDFKzmCSFoUncKjWgJEZTCQ==} + engines: {node: '>=6.0.0'} + hasBin: true + + browserslist@4.28.6: + resolution: {integrity: sha512-FQBYNK15VMslhLHpA7+n+n1GOlF1kId2xcCg7/j95f24AOF6VDYMNH4mFxF7KuaTdv627faazpOAjFzMrfJOUw==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + caniuse-lite@1.0.30001805: + resolution: {integrity: sha512-52noaS3DubycKSXaU30TwPGIp+POyQSUVa5jBEq3vkRkY0kjyb3LQgvhU6WGyCcyXqVLWO0Cw0Q6BSdD0kUfVA==} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chai@5.3.3: + resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==} + engines: {node: '>=18'} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + check-error@2.1.3: + resolution: {integrity: sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==} + engines: {node: '>= 16'} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decode-named-character-reference@1.3.0: + resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} + + deep-eql@5.0.2: + resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} + engines: {node: '>=6'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + + electron-to-chromium@1.5.389: + resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} + + enhanced-resolve@5.21.6: + resolution: {integrity: sha512-aNnGCvbJ/RIyWo1IuhNdVjnNF+EjH9wpzpNHt+ci/m9He9LJvUN8wrCcXjp9cWsGNAuvSpVFTx/vraAFQ8qGjQ==} + engines: {node: '>=10.13.0'} + + es-module-lexer@1.7.0: + resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + + esbuild@0.25.12: + resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + happy-dom@16.8.1: + resolution: {integrity: sha512-n0QrmT9lD81rbpKsyhnlz3DgnMZlaOkJPpgi746doA+HvaMC79bdWkwjrNnGJRvDrWTI8iOcJiVTJ5CdT/AZRw==} + engines: {node: '>=18.0.0'} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + inline-style-parser@0.2.7: + resolution: {integrity: sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA==} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-tokens@9.0.1: + resolution: {integrity: sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ==} + + jsesc@3.1.0: + resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} + engines: {node: '>=6'} + hasBin: true + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + lightningcss-android-arm64@1.32.0: + resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.32.0: + resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.32.0: + resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.32.0: + resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.32.0: + resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.32.0: + resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.32.0: + resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.32.0: + resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.32.0: + resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.32.0: + resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.32.0: + resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.32.0: + resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} + engines: {node: '>= 12.0.0'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + loupe@3.2.1: + resolution: {integrity: sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==} + + lowlight@3.3.0: + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.3: + resolution: {integrity: sha512-W4mAWTvSlKvf8L6J+VN9yLSqQ9AOAAvHuoDAmPkz4dHf553m5gVj2ejadHJhoJmcmxEnOv6Pa8XJhpxE93kb8Q==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.1: + resolution: {integrity: sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + nanoid@3.3.16: + resolution: {integrity: sha512-bzlKTyNJ7+LdGIIwy8ijFpIqEQIvafahV7eYykJ8Cvh42EdJeODoJ6gUJXpQJvej1BddH8OqTXZNE/KfbWAu8Q==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-releases@2.0.51: + resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} + engines: {node: '>=18'} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + pathval@2.0.1: + resolution: {integrity: sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==} + engines: {node: '>= 14.16'} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + postcss@8.5.17: + resolution: {integrity: sha512-J7EF+8X+CzRPaJPOv9Ck2wNWJvGnnl3PcNPAdGg6GTLjyVpyQ0yATMSXRFRV01BviT/9Gwuc3rjEyJbDJG9a4w==} + engines: {node: ^10 || ^12 || >=14} + + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + + property-information@7.2.0: + resolution: {integrity: sha512-IAtzIB6sUiWaJYrX9smp3V46pBGbBeLFRGdh25kg1334VcBlD8HzhPeNIWQH9zhGmo2itIe25EHt9dQP7G5hmg==} + + react-dom@18.3.1: + resolution: {integrity: sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==} + peerDependencies: + react: ^18.3.1 + + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-refresh@0.17.0: + resolution: {integrity: sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==} + engines: {node: '>=0.10.0'} + + react@18.3.1: + resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} + engines: {node: '>=0.10.0'} + + rehype-highlight@7.0.2: + resolution: {integrity: sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + rollup@4.62.2: + resolution: {integrity: sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@3.10.0: + resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-literal@3.1.0: + resolution: {integrity: sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg==} + + style-to-js@1.1.21: + resolution: {integrity: sha512-RjQetxJrrUJLQPHbLku6U/ocGtzyjbJMP9lCNK7Ag0CNh690nSH8woqWH9u16nMjYBAok+i7JO1NP2pOy8IsPQ==} + + style-to-object@1.0.14: + resolution: {integrity: sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw==} + + tailwindcss@4.3.2: + resolution: {integrity: sha512-WtctNNSH8A9jlMIqxzuYumOHU5uGZyRv0Q5svQl+oEPy5w84YpBxdb7MdqyiSPQge5jTJ6zFQLq0PFygdccSBA==} + + tapable@2.3.3: + resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==} + engines: {node: '>=6'} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinypool@1.1.1: + resolution: {integrity: sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==} + engines: {node: ^18.0.0 || >=20.0.0} + + tinyrainbow@2.0.0: + resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} + engines: {node: '>=14.0.0'} + + tinyspy@4.0.4: + resolution: {integrity: sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==} + engines: {node: '>=14.0.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.1: + resolution: {integrity: sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.2: + resolution: {integrity: sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ==} + + unist-util-visit@5.1.0: + resolution: {integrity: sha512-m+vIdyeCOpdr/QeQCu2EzxX/ohgS8KbnPDgFni4dQsfSCtpz8UqDyY5GjRru8PDKuYn7Fq19j1CQ+nJSsGKOzg==} + + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + vfile-message@4.0.3: + resolution: {integrity: sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + vite-node@3.2.4: + resolution: {integrity: sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + + vite@6.4.3: + resolution: {integrity: sha512-NTKlcQjlAK7MlQoyb6LgaqHc8sso/pVyUJYWMws3jg21uTJw/LddqIFPcPqP6PzpgbIcZyKI85sFE4HBrQDA8A==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + jiti: '>=1.21.0' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + sass-embedded: '*' + stylus: '*' + sugarss: '*' + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + jiti: + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@3.2.7: + resolution: {integrity: sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==} + engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@types/debug': ^4.1.12 + '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 + '@vitest/browser': 3.2.7 + '@vitest/ui': 3.2.7 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@types/debug': + optional: true + '@types/node': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + + whatwg-mimetype@3.0.0: + resolution: {integrity: sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==} + engines: {node: '>=12'} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + zustand@5.0.14: + resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==} + engines: {node: '>=12.20.0'} + peerDependencies: + '@types/react': '>=18.0.0' + immer: '>=9.0.6' + react: '>=18.0.0' + use-sync-external-store: '>=1.2.0' + peerDependenciesMeta: + '@types/react': + optional: true + immer: + optional: true + react: + optional: true + use-sync-external-store: + optional: true + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + + '@babel/compat-data@7.29.7': {} + + '@babel/core@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 + convert-source-map: 2.0.0 + debug: 4.4.3 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.29.7': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + jsesc: 3.1.0 + + '@babel/helper-compilation-targets@7.29.7': + dependencies: + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.6 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': + dependencies: + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 + transitivePeerDependencies: + - supports-color + + '@babel/helper-plugin-utils@7.29.7': {} + + '@babel/helper-string-parser@7.29.7': {} + + '@babel/helper-validator-identifier@7.29.7': {} + + '@babel/helper-validator-option@7.29.7': {} + + '@babel/helpers@7.29.7': + dependencies: + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/parser@7.29.7': + dependencies: + '@babel/types': 7.29.7 + + '@babel/plugin-transform-react-jsx-self@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/plugin-transform-react-jsx-source@7.29.7(@babel/core@7.29.7)': + dependencies: + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 + + '@babel/runtime@7.29.7': {} + + '@babel/template@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@babel/traverse@7.29.7': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.29.7': + dependencies: + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + + '@esbuild/aix-ppc64@0.25.12': + optional: true + + '@esbuild/android-arm64@0.25.12': + optional: true + + '@esbuild/android-arm@0.25.12': + optional: true + + '@esbuild/android-x64@0.25.12': + optional: true + + '@esbuild/darwin-arm64@0.25.12': + optional: true + + '@esbuild/darwin-x64@0.25.12': + optional: true + + '@esbuild/freebsd-arm64@0.25.12': + optional: true + + '@esbuild/freebsd-x64@0.25.12': + optional: true + + '@esbuild/linux-arm64@0.25.12': + optional: true + + '@esbuild/linux-arm@0.25.12': + optional: true + + '@esbuild/linux-ia32@0.25.12': + optional: true + + '@esbuild/linux-loong64@0.25.12': + optional: true + + '@esbuild/linux-mips64el@0.25.12': + optional: true + + '@esbuild/linux-ppc64@0.25.12': + optional: true + + '@esbuild/linux-riscv64@0.25.12': + optional: true + + '@esbuild/linux-s390x@0.25.12': + optional: true + + '@esbuild/linux-x64@0.25.12': + optional: true + + '@esbuild/netbsd-arm64@0.25.12': + optional: true + + '@esbuild/netbsd-x64@0.25.12': + optional: true + + '@esbuild/openbsd-arm64@0.25.12': + optional: true + + '@esbuild/openbsd-x64@0.25.12': + optional: true + + '@esbuild/openharmony-arm64@0.25.12': + optional: true + + '@esbuild/sunos-x64@0.25.12': + optional: true + + '@esbuild/win32-arm64@0.25.12': + optional: true + + '@esbuild/win32-ia32@0.25.12': + optional: true + + '@esbuild/win32-x64@0.25.12': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@rolldown/pluginutils@1.0.0-beta.27': {} + + '@rollup/rollup-android-arm-eabi@4.62.2': + optional: true + + '@rollup/rollup-android-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.2': + optional: true + + '@rollup/rollup-darwin-x64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.2': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.2': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.2': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.2': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.2': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.2': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.2': + optional: true + + '@tailwindcss/node@4.3.2': + dependencies: + '@jridgewell/remapping': 2.3.5 + enhanced-resolve: 5.21.6 + jiti: 2.7.0 + lightningcss: 1.32.0 + magic-string: 0.30.21 + source-map-js: 1.2.1 + tailwindcss: 4.3.2 + + '@tailwindcss/oxide-android-arm64@4.3.2': + optional: true + + '@tailwindcss/oxide-darwin-arm64@4.3.2': + optional: true + + '@tailwindcss/oxide-darwin-x64@4.3.2': + optional: true + + '@tailwindcss/oxide-freebsd-x64@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-gnu@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-arm64-musl@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-x64-gnu@4.3.2': + optional: true + + '@tailwindcss/oxide-linux-x64-musl@4.3.2': + optional: true + + '@tailwindcss/oxide-wasm32-wasi@4.3.2': + optional: true + + '@tailwindcss/oxide-win32-arm64-msvc@4.3.2': + optional: true + + '@tailwindcss/oxide-win32-x64-msvc@4.3.2': + optional: true + + '@tailwindcss/oxide@4.3.2': + optionalDependencies: + '@tailwindcss/oxide-android-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-arm64': 4.3.2 + '@tailwindcss/oxide-darwin-x64': 4.3.2 + '@tailwindcss/oxide-freebsd-x64': 4.3.2 + '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.2 + '@tailwindcss/oxide-linux-arm64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-arm64-musl': 4.3.2 + '@tailwindcss/oxide-linux-x64-gnu': 4.3.2 + '@tailwindcss/oxide-linux-x64-musl': 4.3.2 + '@tailwindcss/oxide-wasm32-wasi': 4.3.2 + '@tailwindcss/oxide-win32-arm64-msvc': 4.3.2 + '@tailwindcss/oxide-win32-x64-msvc': 4.3.2 + + '@tailwindcss/vite@4.3.2(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@tailwindcss/node': 4.3.2 + '@tailwindcss/oxide': 4.3.2 + tailwindcss: 4.3.2 + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + + '@tauri-apps/api@2.11.1': {} + + '@tauri-apps/cli-darwin-arm64@2.11.4': + optional: true + + '@tauri-apps/cli-darwin-x64@2.11.4': + optional: true + + '@tauri-apps/cli-linux-arm-gnueabihf@2.11.4': + optional: true + + '@tauri-apps/cli-linux-arm64-gnu@2.11.4': + optional: true + + '@tauri-apps/cli-linux-arm64-musl@2.11.4': + optional: true + + '@tauri-apps/cli-linux-riscv64-gnu@2.11.4': + optional: true + + '@tauri-apps/cli-linux-x64-gnu@2.11.4': + optional: true + + '@tauri-apps/cli-linux-x64-musl@2.11.4': + optional: true + + '@tauri-apps/cli-win32-arm64-msvc@2.11.4': + optional: true + + '@tauri-apps/cli-win32-ia32-msvc@2.11.4': + optional: true + + '@tauri-apps/cli-win32-x64-msvc@2.11.4': + optional: true + + '@tauri-apps/cli@2.11.4': + optionalDependencies: + '@tauri-apps/cli-darwin-arm64': 2.11.4 + '@tauri-apps/cli-darwin-x64': 2.11.4 + '@tauri-apps/cli-linux-arm-gnueabihf': 2.11.4 + '@tauri-apps/cli-linux-arm64-gnu': 2.11.4 + '@tauri-apps/cli-linux-arm64-musl': 2.11.4 + '@tauri-apps/cli-linux-riscv64-gnu': 2.11.4 + '@tauri-apps/cli-linux-x64-gnu': 2.11.4 + '@tauri-apps/cli-linux-x64-musl': 2.11.4 + '@tauri-apps/cli-win32-arm64-msvc': 2.11.4 + '@tauri-apps/cli-win32-ia32-msvc': 2.11.4 + '@tauri-apps/cli-win32-x64-msvc': 2.11.4 + + '@tauri-apps/plugin-dialog@2.7.1': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@tauri-apps/plugin-http@2.5.9': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@tauri-apps/plugin-log@2.8.0': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@tauri-apps/plugin-os@2.3.2': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@tauri-apps/plugin-shell@2.3.5': + dependencies: + '@tauri-apps/api': 2.11.1 + + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/react@16.3.2(@testing-library/dom@10.4.1)(@types/react-dom@18.3.7(@types/react@18.3.31))(@types/react@18.3.31)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.29.7 + '@testing-library/dom': 10.4.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.31 + '@types/react-dom': 18.3.7(@types/react@18.3.31) + + '@types/aria-query@5.0.4': {} + + '@types/babel__core@7.20.5': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 + '@types/babel__template': 7.4.4 + '@types/babel__traverse': 7.28.0 + + '@types/babel__generator@7.27.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/babel__template@7.4.4': + dependencies: + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + + '@types/babel__traverse@7.28.0': + dependencies: + '@babel/types': 7.29.7 + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/debug@4.1.13': + dependencies: + '@types/ms': 2.1.0 + + '@types/deep-eql@4.0.2': {} + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.9 + + '@types/estree@1.0.9': {} + + '@types/hast@3.0.5': + dependencies: + '@types/unist': 3.0.3 + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/ms@2.1.0': {} + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@types/prop-types@15.7.15': {} + + '@types/react-dom@18.3.7(@types/react@18.3.31)': + dependencies: + '@types/react': 18.3.31 + + '@types/react@18.3.31': + dependencies: + '@types/prop-types': 15.7.15 + csstype: 3.2.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@ungap/structured-clone@1.3.3': {} + + '@vitejs/plugin-react@4.7.0(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@babel/core': 7.29.7 + '@babel/plugin-transform-react-jsx-self': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-transform-react-jsx-source': 7.29.7(@babel/core@7.29.7) + '@rolldown/pluginutils': 1.0.0-beta.27 + '@types/babel__core': 7.20.5 + react-refresh: 0.17.0 + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - supports-color + + '@vitest/expect@3.2.7': + dependencies: + '@types/chai': 5.2.3 + '@vitest/spy': 3.2.7 + '@vitest/utils': 3.2.7 + chai: 5.3.3 + tinyrainbow: 2.0.0 + + '@vitest/mocker@3.2.7(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0))': + dependencies: + '@vitest/spy': 3.2.7 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + + '@vitest/pretty-format@3.2.7': + dependencies: + tinyrainbow: 2.0.0 + + '@vitest/runner@3.2.7': + dependencies: + '@vitest/utils': 3.2.7 + pathe: 2.0.3 + strip-literal: 3.1.0 + + '@vitest/snapshot@3.2.7': + dependencies: + '@vitest/pretty-format': 3.2.7 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@3.2.7': + dependencies: + tinyspy: 4.0.4 + + '@vitest/utils@3.2.7': + dependencies: + '@vitest/pretty-format': 3.2.7 + loupe: 3.2.1 + tinyrainbow: 2.0.0 + + ansi-regex@5.0.1: {} + + ansi-styles@5.2.0: {} + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + assertion-error@2.0.1: {} + + bail@2.0.2: {} + + baseline-browser-mapping@2.10.43: {} + + browserslist@4.28.6: + dependencies: + baseline-browser-mapping: 2.10.43 + caniuse-lite: 1.0.30001805 + electron-to-chromium: 1.5.389 + node-releases: 2.0.51 + update-browserslist-db: 1.2.3(browserslist@4.28.6) + + cac@6.7.14: {} + + caniuse-lite@1.0.30001805: {} + + ccount@2.0.1: {} + + chai@5.3.3: + dependencies: + assertion-error: 2.0.1 + check-error: 2.1.3 + deep-eql: 5.0.2 + loupe: 3.2.1 + pathval: 2.0.1 + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + check-error@2.1.3: {} + + comma-separated-tokens@2.0.3: {} + + convert-source-map@2.0.0: {} + + csstype@3.2.3: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + decode-named-character-reference@1.3.0: + dependencies: + character-entities: 2.0.2 + + deep-eql@5.0.2: {} + + dequal@2.0.3: {} + + detect-libc@2.1.2: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + dom-accessibility-api@0.5.16: {} + + electron-to-chromium@1.5.389: {} + + enhanced-resolve@5.21.6: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.3.3 + + es-module-lexer@1.7.0: {} + + esbuild@0.25.12: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.12 + '@esbuild/android-arm': 0.25.12 + '@esbuild/android-arm64': 0.25.12 + '@esbuild/android-x64': 0.25.12 + '@esbuild/darwin-arm64': 0.25.12 + '@esbuild/darwin-x64': 0.25.12 + '@esbuild/freebsd-arm64': 0.25.12 + '@esbuild/freebsd-x64': 0.25.12 + '@esbuild/linux-arm': 0.25.12 + '@esbuild/linux-arm64': 0.25.12 + '@esbuild/linux-ia32': 0.25.12 + '@esbuild/linux-loong64': 0.25.12 + '@esbuild/linux-mips64el': 0.25.12 + '@esbuild/linux-ppc64': 0.25.12 + '@esbuild/linux-riscv64': 0.25.12 + '@esbuild/linux-s390x': 0.25.12 + '@esbuild/linux-x64': 0.25.12 + '@esbuild/netbsd-arm64': 0.25.12 + '@esbuild/netbsd-x64': 0.25.12 + '@esbuild/openbsd-arm64': 0.25.12 + '@esbuild/openbsd-x64': 0.25.12 + '@esbuild/openharmony-arm64': 0.25.12 + '@esbuild/sunos-x64': 0.25.12 + '@esbuild/win32-arm64': 0.25.12 + '@esbuild/win32-ia32': 0.25.12 + '@esbuild/win32-x64': 0.25.12 + + escalade@3.2.0: {} + + escape-string-regexp@5.0.0: {} + + estree-util-is-identifier-name@3.0.0: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + expect-type@1.4.0: {} + + extend@3.0.2: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fsevents@2.3.3: + optional: true + + gensync@1.0.0-beta.2: {} + + graceful-fs@4.2.11: {} + + happy-dom@16.8.1: + dependencies: + webidl-conversions: 7.0.0 + whatwg-mimetype: 3.0.0 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.9 + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.2.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.21 + unist-util-position: 5.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.5 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.5 + + highlight.js@11.11.1: {} + + html-url-attributes@3.0.1: {} + + inline-style-parser@0.2.7: {} + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-decimal@2.0.1: {} + + is-hexadecimal@2.0.1: {} + + is-plain-obj@4.1.0: {} + + jiti@2.7.0: {} + + js-tokens@4.0.0: {} + + js-tokens@9.0.1: {} + + jsesc@3.1.0: {} + + json5@2.2.3: {} + + lightningcss-android-arm64@1.32.0: + optional: true + + lightningcss-darwin-arm64@1.32.0: + optional: true + + lightningcss-darwin-x64@1.32.0: + optional: true + + lightningcss-freebsd-x64@1.32.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.32.0: + optional: true + + lightningcss-linux-arm64-gnu@1.32.0: + optional: true + + lightningcss-linux-arm64-musl@1.32.0: + optional: true + + lightningcss-linux-x64-gnu@1.32.0: + optional: true + + lightningcss-linux-x64-musl@1.32.0: + optional: true + + lightningcss-win32-arm64-msvc@1.32.0: + optional: true + + lightningcss-win32-x64-msvc@1.32.0: + optional: true + + lightningcss@1.32.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.32.0 + lightningcss-darwin-arm64: 1.32.0 + lightningcss-darwin-x64: 1.32.0 + lightningcss-freebsd-x64: 1.32.0 + lightningcss-linux-arm-gnueabihf: 1.32.0 + lightningcss-linux-arm64-gnu: 1.32.0 + lightningcss-linux-arm64-musl: 1.32.0 + lightningcss-linux-x64-gnu: 1.32.0 + lightningcss-linux-x64-musl: 1.32.0 + lightningcss-win32-arm64-msvc: 1.32.0 + lightningcss-win32-x64-msvc: 1.32.0 + + longest-streak@3.1.0: {} + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + loupe@3.2.1: {} + + lowlight@3.3.0: + dependencies: + '@types/hast': 3.0.5 + devlop: 1.1.0 + highlight.js: 11.11.1 + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lz-string@1.5.0: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + markdown-table@3.0.4: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + mdast-util-from-markdown@2.0.3: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.3 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.0 + mdast-util-gfm-strikethrough: 2.0.0 + mdast-util-gfm-table: 2.0.0 + mdast-util-gfm-task-list-item: 2.0.0 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.3 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.3 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.1 + + mdast-util-to-hast@13.2.1: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.3 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.1.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.3.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.13 + debug: 4.4.3 + decode-named-character-reference: 1.3.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + ms@2.1.3: {} + + nanoid@3.3.16: {} + + node-releases@2.0.51: {} + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.3.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + pathe@2.0.3: {} + + pathval@2.0.1: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + postcss@8.5.17: + dependencies: + nanoid: 3.3.16 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + + property-information@7.2.0: {} + + react-dom@18.3.1(react@18.3.1): + dependencies: + loose-envify: 1.4.0 + react: 18.3.1 + scheduler: 0.23.2 + + react-is@17.0.2: {} + + react-markdown@10.1.0(@types/react@18.3.31)(react@18.3.1): + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + '@types/react': 18.3.31 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.1 + react: 18.3.1 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-refresh@0.17.0: {} + + react@18.3.1: + dependencies: + loose-envify: 1.4.0 + + rehype-highlight@7.0.2: + dependencies: + '@types/hast': 3.0.5 + hast-util-to-text: 4.0.2 + lowlight: 3.3.0 + unist-util-visit: 5.1.0 + vfile: 6.0.3 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.3 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.5 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.1 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + rollup@4.62.2: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@rollup/rollup-android-arm-eabi': 4.62.2 + '@rollup/rollup-android-arm64': 4.62.2 + '@rollup/rollup-darwin-arm64': 4.62.2 + '@rollup/rollup-darwin-x64': 4.62.2 + '@rollup/rollup-freebsd-arm64': 4.62.2 + '@rollup/rollup-freebsd-x64': 4.62.2 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.2 + '@rollup/rollup-linux-arm-musleabihf': 4.62.2 + '@rollup/rollup-linux-arm64-gnu': 4.62.2 + '@rollup/rollup-linux-arm64-musl': 4.62.2 + '@rollup/rollup-linux-loong64-gnu': 4.62.2 + '@rollup/rollup-linux-loong64-musl': 4.62.2 + '@rollup/rollup-linux-ppc64-gnu': 4.62.2 + '@rollup/rollup-linux-ppc64-musl': 4.62.2 + '@rollup/rollup-linux-riscv64-gnu': 4.62.2 + '@rollup/rollup-linux-riscv64-musl': 4.62.2 + '@rollup/rollup-linux-s390x-gnu': 4.62.2 + '@rollup/rollup-linux-x64-gnu': 4.62.2 + '@rollup/rollup-linux-x64-musl': 4.62.2 + '@rollup/rollup-openbsd-x64': 4.62.2 + '@rollup/rollup-openharmony-arm64': 4.62.2 + '@rollup/rollup-win32-arm64-msvc': 4.62.2 + '@rollup/rollup-win32-ia32-msvc': 4.62.2 + '@rollup/rollup-win32-x64-gnu': 4.62.2 + '@rollup/rollup-win32-x64-msvc': 4.62.2 + fsevents: 2.3.3 + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@6.3.1: {} + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + space-separated-tokens@2.0.2: {} + + stackback@0.0.2: {} + + std-env@3.10.0: {} + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-literal@3.1.0: + dependencies: + js-tokens: 9.0.1 + + style-to-js@1.1.21: + dependencies: + style-to-object: 1.0.14 + + style-to-object@1.0.14: + dependencies: + inline-style-parser: 0.2.7 + + tailwindcss@4.3.2: {} + + tapable@2.3.3: {} + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinypool@1.1.1: {} + + tinyrainbow@2.0.0: {} + + tinyspy@4.0.4: {} + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + typescript@5.9.3: {} + + undici-types@6.21.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-is@6.0.1: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + + unist-util-visit@5.1.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.1 + unist-util-visit-parents: 6.0.2 + + update-browserslist-db@1.2.3(browserslist@4.28.6): + dependencies: + browserslist: 4.28.6 + escalade: 3.2.0 + picocolors: 1.1.1 + + vfile-message@4.0.3: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.3 + + vite-node@3.2.4(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0): + dependencies: + cac: 6.7.14 + debug: 4.4.3 + es-module-lexer: 1.7.0 + pathe: 2.0.3 + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + transitivePeerDependencies: + - '@types/node' + - jiti + - less + - lightningcss + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0): + dependencies: + esbuild: 0.25.12 + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + postcss: 8.5.17 + rollup: 4.62.2 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.1 + fsevents: 2.3.3 + jiti: 2.7.0 + lightningcss: 1.32.0 + + vitest@3.2.7(@types/debug@4.1.13)(@types/node@22.20.1)(happy-dom@16.8.1)(jiti@2.7.0)(lightningcss@1.32.0): + dependencies: + '@types/chai': 5.2.3 + '@vitest/expect': 3.2.7 + '@vitest/mocker': 3.2.7(vite@6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0)) + '@vitest/pretty-format': 3.2.7 + '@vitest/runner': 3.2.7 + '@vitest/snapshot': 3.2.7 + '@vitest/spy': 3.2.7 + '@vitest/utils': 3.2.7 + chai: 5.3.3 + debug: 4.4.3 + expect-type: 1.4.0 + magic-string: 0.30.21 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 3.10.0 + tinybench: 2.9.0 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tinypool: 1.1.1 + tinyrainbow: 2.0.0 + vite: 6.4.3(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + vite-node: 3.2.4(@types/node@22.20.1)(jiti@2.7.0)(lightningcss@1.32.0) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/debug': 4.1.13 + '@types/node': 22.20.1 + happy-dom: 16.8.1 + transitivePeerDependencies: + - jiti + - less + - lightningcss + - msw + - sass + - sass-embedded + - stylus + - sugarss + - supports-color + - terser + - tsx + - yaml + + webidl-conversions@7.0.0: {} + + whatwg-mimetype@3.0.0: {} + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + yallist@3.1.1: {} + + zustand@5.0.14(@types/react@18.3.31)(react@18.3.1): + optionalDependencies: + '@types/react': 18.3.31 + react: 18.3.1 + + zwitch@2.0.4: {} diff --git a/companions/desktop/pnpm-workspace.yaml b/companions/desktop/pnpm-workspace.yaml new file mode 100644 index 0000000000..4567d8a22d --- /dev/null +++ b/companions/desktop/pnpm-workspace.yaml @@ -0,0 +1,12 @@ +# Marks companions/desktop as its own pnpm workspace root so `pnpm install` +# run from here resolves against this package only — not the root +# openagentic pnpm-workspace.yaml (which intentionally does not list this +# app; see companions/desktop/README.md). +packages: + - . + +# esbuild's postinstall just re-verifies the platform binary that +# optionalDependencies already resolved (@esbuild/darwin-arm64 etc.) — same +# call the root workspace makes in its own ignoredBuiltDependencies list. +ignoredBuiltDependencies: + - esbuild diff --git a/companions/desktop/scripts/build-desktop.sh b/companions/desktop/scripts/build-desktop.sh new file mode 100755 index 0000000000..b3b23e7198 --- /dev/null +++ b/companions/desktop/scripts/build-desktop.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash +# Build the OpenAgentic desktop app (Tauri) into a macOS .app bundle. +# +# Preflight: a Rust toolchain + the Xcode Command Line Tools are required to +# compile the Rust core and link the bundle. We build ONLY the `.app` bundle — +# never Tauri's dmg bundler, which drives Finder/TCC and hangs headless. +# +# Emits a single machine-readable line on success: BUILT: +# so the caller (the setup wizard's Launch step) can hand the path to +# install-app.sh. Exits non-zero if a prerequisite is missing (3) or the build +# produced no bundle (1). +set -euo pipefail + +HERE="$(cd "$(dirname "$0")/.." && pwd)" + +command -v cargo >/dev/null || { echo "Rust toolchain required: https://rustup.rs" >&2; exit 3; } +xcode-select -p >/dev/null 2>&1 || { echo "Xcode CLT required: xcode-select --install" >&2; exit 3; } + +cd "$HERE" + +# corepack provisions the pinned pnpm from package.json's packageManager field. +corepack enable >/dev/null 2>&1 || true + +# Install JS deps. Prefer a reproducible frozen install; fall back to a plain +# install if the lockfile is out of sync with package.json. +pnpm install --frozen-lockfile || pnpm install + +# Stage the (gitignored, proprietary) agenticode sidecar + ripgrep. Non-fatal: +# without it the Code surface just shows setup instructions instead of a live CLI. +bash scripts/stage-agenticode.sh || echo "WARN: agenticode sidecar not staged — Code surface will show setup instructions" >&2 + +# Build the .app bundle only (see header: never the dmg bundler headless). +pnpm tauri build --bundles app + +APP="$HERE/src-tauri/target/release/bundle/macos/OpenAgentic.app" +if [ -d "$APP" ]; then + echo "BUILT:$APP" +else + echo "build finished but no app bundle at $APP" >&2 + exit 1 +fi diff --git a/companions/desktop/scripts/check-sidecar.sh b/companions/desktop/scripts/check-sidecar.sh new file mode 100755 index 0000000000..04086225d3 --- /dev/null +++ b/companions/desktop/scripts/check-sidecar.sh @@ -0,0 +1,31 @@ +#!/usr/bin/env bash +# Preflight for `pnpm tauri build`: verify the (gitignored, proprietary) +# agenticode sidecar has actually been staged. On a fresh clone the placeholder +# is missing or zero bytes and the Tauri bundler would fail with a confusing +# externalBin error — fail fast with the fix instead. +# +# Deliberately NOT wired into tauri.conf.json's beforeBuildCommand (that would +# also gate `pnpm dev`, which doesn't need the sidecar). Run it manually, or +# from a future build script (Task 14), before a bundle build. +set -euo pipefail + +DESKTOP="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +TRIPLE="${TRIPLE:-$(rustc -vV | sed -n 's/^host: //p')}" +BIN="$DESKTOP/src-tauri/binaries/agenticode-cli-$TRIPLE" +case "$TRIPLE" in *windows*) BIN="$BIN.exe" ;; esac +RG="$DESKTOP/src-tauri/cli-runtime/rg" + +fail=0 +if [ ! -s "$BIN" ]; then + echo "sidecar missing — run scripts/stage-agenticode.sh first" >&2 + echo " expected a non-empty binary at: $BIN" >&2 + fail=1 +fi +if [ ! -s "$RG" ]; then + echo "vendored ripgrep missing — run scripts/stage-agenticode.sh first" >&2 + echo " expected a non-empty binary at: $RG" >&2 + fail=1 +fi +[ "$fail" -eq 0 ] || exit 1 +echo "sidecar ok: $BIN" +echo "rg ok: $RG" diff --git a/companions/desktop/scripts/install-app.sh b/companions/desktop/scripts/install-app.sh new file mode 100755 index 0000000000..732b4d1164 --- /dev/null +++ b/companions/desktop/scripts/install-app.sh @@ -0,0 +1,57 @@ +#!/usr/bin/env bash +# Install the freshly built OpenAgentic.app into the user's Applications folder +# and validate the installed bundle. +# +# Copies with `ditto` (preserves the bundle structure + any code signature) into +# /Applications, falling back to ~/Applications when /Applications isn't writable +# (locked-down machines, no admin). Validates that: the bundle exists, codesign +# verifies it (an ad-hoc / self-signature is fine — only a broken/unsigned bundle +# fails), and the CFBundleExecutable main binary is present and executable. +# +# Emits a single machine-readable line on success: INSTALLED: +# Pass --open to launch the app once it's installed. +set -euo pipefail + +HERE="$(cd "$(dirname "$0")/.." && pwd)" +APP_NAME="OpenAgentic.app" +SRC="$HERE/src-tauri/target/release/bundle/macos/$APP_NAME" + +OPEN_AFTER=0 +for arg in "$@"; do + case "$arg" in + --open) OPEN_AFTER=1 ;; + *) echo "unknown argument: $arg" >&2; exit 2 ;; + esac +done + +[ -d "$SRC" ] || { echo "no built app at $SRC — run build-desktop.sh first" >&2; exit 1; } + +# Prefer /Applications; fall back to ~/Applications when it isn't writable. +DEST_DIR="/Applications" +if [ ! -w "$DEST_DIR" ]; then + DEST_DIR="$HOME/Applications" + mkdir -p "$DEST_DIR" +fi +DEST="$DEST_DIR/$APP_NAME" + +# Replace any prior install atomically-ish: remove then ditto a clean copy. +rm -rf "$DEST" +ditto "$SRC" "$DEST" + +# ── validate the installed bundle ──────────────────────────────────────────── +[ -d "$DEST" ] || { echo "install failed: $DEST missing after copy" >&2; exit 1; } + +# codesign must verify. Ad-hoc (exit 0) is acceptable; a broken signature is not. +codesign -dv "$DEST" >/dev/null 2>&1 || { echo "codesign verification failed for $DEST" >&2; exit 1; } + +# The CFBundleExecutable main binary must exist and be executable. +BIN_NAME="$(defaults read "$DEST/Contents/Info" CFBundleExecutable 2>/dev/null || true)" +[ -n "$BIN_NAME" ] || { echo "could not read CFBundleExecutable from $DEST" >&2; exit 1; } +MAIN_BIN="$DEST/Contents/MacOS/$BIN_NAME" +[ -x "$MAIN_BIN" ] || { echo "main binary not executable: $MAIN_BIN" >&2; exit 1; } + +echo "INSTALLED:$DEST" + +if [ "$OPEN_AFTER" -eq 1 ]; then + open -a "$DEST" +fi diff --git a/companions/desktop/scripts/stage-agenticode.sh b/companions/desktop/scripts/stage-agenticode.sh new file mode 100755 index 0000000000..7cc5aee13a --- /dev/null +++ b/companions/desktop/scripts/stage-agenticode.sh @@ -0,0 +1,106 @@ +#!/usr/bin/env bash +# Stage the agenticode "openagentic"-edition CLI (+ a portable ripgrep) as a +# Tauri sidecar for the desktop app. +# +# PROPRIETARY BINARY — NEVER committed. Both destinations are gitignored: +# src-tauri/binaries/ (the sidecar) src-tauri/cli-runtime/ (rg) +# +# Why a fresh build (not the CLI checked into agenticode/desktop): that prebuilt +# is the DEFAULT edition, which speaks to Bedrock/Vertex/AgenticWork. We need +# the "openagentic" edition, which a bundle `--define` locks to a LOCAL +# openagentic instance only. So we compile it ourselves from the checkout. +# +# Build path: agenticode's scripts/bundle.sh takes its first positional arg as +# an OUTDIR (default dist/bundle) and builds ALL five OS/arch targets — there is +# no single-target flag (SKIP_MULTIARCH forces linux-x64). So we don't call it; +# instead we run the SAME single-target `bun build --compile` the agenticode +# desktop README documents, adding the openagentic edition --define and the +# externals bundle.sh skips (native .node addons + phantom @agentic-work/* type +# stubs, all reached only via guarded dynamic import). +# +# Usage: scripts/stage-agenticode.sh +# AGENTICODE_DIR path to the agenticode checkout (default ~/git/agenticode) +# TRIPLE rustc host triple (default: `rustc -vV | grep host`) +set -euo pipefail + +DIR="${AGENTICODE_DIR:-$HOME/git/agenticode}" +TRIPLE="${TRIPLE:-$(rustc -vV | sed -n 's/^host: //p')}" +DESKTOP="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +OUT="$DESKTOP/src-tauri/binaries" +RG_OUT="$DESKTOP/src-tauri/cli-runtime" +STAGED="$OUT/agenticode-cli-$TRIPLE" + +# Map the rustc triple → bun --compile target and → fetch-ripgrep target. +case "$TRIPLE" in + aarch64-apple-darwin) BUNTARGET="bun-darwin-arm64"; RGTARGET="darwin-arm64" ;; + x86_64-apple-darwin) BUNTARGET="bun-darwin-x64"; RGTARGET="darwin-x64" ;; + x86_64-unknown-linux-gnu) BUNTARGET="bun-linux-x64"; RGTARGET="linux-x64" ;; + aarch64-unknown-linux-gnu) BUNTARGET="bun-linux-arm64"; RGTARGET="linux-x64" ;; + x86_64-pc-windows-msvc) BUNTARGET="bun-windows-x64"; RGTARGET="windows-x64"; STAGED="$STAGED.exe" ;; + *) echo "unsupported TRIPLE: $TRIPLE" >&2; exit 2 ;; +esac + +mkdir -p "$OUT" "$RG_OUT" + +if [ ! -d "$DIR" ]; then + echo "agenticode checkout not found at $DIR." >&2 + echo "Set AGENTICODE_DIR to your checkout, or obtain a prebuilt" >&2 + echo "openagentic-edition CLI from the AgenticWork CDN and place it at:" >&2 + echo " $STAGED" >&2 + echo "then re-run with the checkout present to also stage ripgrep, or drop a" >&2 + echo "portable rg at $RG_OUT/rg yourself." >&2 + echo "Verify staging afterwards with scripts/check-sidecar.sh (it must pass" >&2 + echo "before 'pnpm tauri build' can produce a working bundle)." >&2 + exit 2 +fi + +# Externals bundle.sh skips: native prebuilt addons that can't live inside a +# single-file --compile binary, plus phantom @agentic-work/* / @anthropic-ai +# type-only stubs (no npm package). Every one is reached only via guarded +# `await import(...)`, so leaving them external is safe. +# +# SOURCE OF TRUTH: the EXTERNALS array in agenticode's scripts/bundle.sh. +# This list is a point-in-time copy of the package NAMES only and CAN DRIFT — +# if a freshly staged binary crashes at startup with "Cannot find module" for +# a dependency not listed here, diff this list against bundle.sh's first. +EXTERNALS=( + @anthropic-ai/mcpb + @agentic-work/chrome-mcp @agentic-work/mcpb @agentic-work/sandbox-runtime + @agentic-work/agent-sdk @agentic-work/tokenizer + fsevents cpu-features sharp audio-capture-napi + tree-sitter web-tree-sitter +) +EXTERNAL_ARGS=() +for e in "${EXTERNALS[@]}"; do EXTERNAL_ARGS+=(--external "$e"); done + +echo "==> building agenticode openagentic edition → $BUNTARGET" +( + cd "$DIR" + # bundle.sh's own dist guard: --compile needs the transpiled entrypoint. + if [ ! -f dist/entrypoints/cli.js ]; then + echo " dist/entrypoints/cli.js missing — running tsc" + npx tsc --noEmitOnError false 2>/dev/null || true + fi + # The --define bakes AGENTICODE_EDITION=openagentic into the bundle so the + # shipped binary is pinned to the local-only edition (edition.ts reads it). + bun build dist/entrypoints/cli.js --compile --target="$BUNTARGET" \ + --define 'process.env.AGENTICODE_EDITION="openagentic"' \ + "${EXTERNAL_ARGS[@]}" \ + --outfile "$STAGED" +) +chmod 755 "$STAGED" + +# ripgrep: the bun-compiled CLI can't exec its OWN embedded rg (it lives in +# bunfs, no real on-disk path), so its Grep/Glob tools fall back to a `rg` on +# PATH. tauriHost prepends cli-runtime/ to the child's PATH; stage a real rg +# there. Prefer the one already vendored in the checkout; else fetch it. +RG_SRC="$DIR/desktop/cli-bundle/agenticode-cli/rg" +if [ ! -f "$RG_SRC" ]; then + echo "==> fetching portable ripgrep ($RGTARGET)" + ( cd "$DIR/desktop" && bash scripts/fetch-ripgrep.sh "$RGTARGET" ) +fi +install -m 755 "$RG_SRC" "$RG_OUT/rg" + +echo "==> staged:" +echo " $STAGED ($(du -h "$STAGED" | cut -f1))" +echo " $RG_OUT/rg ($(du -h "$RG_OUT/rg" | cut -f1))" diff --git a/companions/desktop/src-tauri/Cargo.lock b/companions/desktop/src-tauri/Cargo.lock new file mode 100644 index 0000000000..f000a29177 --- /dev/null +++ b/companions/desktop/src-tauri/Cargo.lock @@ -0,0 +1,5645 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom 0.2.17", + "once_cell", + "version_check", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "alloc-no-stdlib" +version = "2.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc7bb162ec39d46ab1ca8c77bf72e890535becd1751bb45f64c597edb4c8c6b3" + +[[package]] +name = "alloc-stdlib" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e76a019e91224d279006ff972f1e984179a6e9feb050adba6ce8274aef23195" +dependencies = [ + "alloc-no-stdlib", +] + +[[package]] +name = "android_log-sys" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84521a3cf562bc62942e294181d9eef17eb38ceb8c68677bc49f144e4c3d4f8d" + +[[package]] +name = "android_logger" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbb4e440d04be07da1f1bf44fb4495ebd58669372fe0cffa6e48595ac5bd88a3" +dependencies = [ + "android_log-sys", + "env_filter", + "log", +] + +[[package]] +name = "android_system_properties" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" + +[[package]] +name = "arrayvec" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fb67a6e08acf24fdeccbac2cb6ac4305825bd1f117462e0e6f2f193345ad56" + +[[package]] +name = "atk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +dependencies = [ + "atk-sys", + "glib", + "libc", +] + +[[package]] +name = "atk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" + +[[package]] +name = "base64" +version = "0.21.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bit-set" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +dependencies = [ + "bit-vec", +] + +[[package]] +name = "bit-vec" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +dependencies = [ + "serde_core", +] + +[[package]] +name = "bitvec" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" +dependencies = [ + "funty", + "radium", + "tap", + "wyz", +] + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "block2" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" +dependencies = [ + "objc2", +] + +[[package]] +name = "borsh" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" +dependencies = [ + "borsh-derive", + "bytes", + "cfg_aliases", +] + +[[package]] +name = "borsh-derive" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" +dependencies = [ + "once_cell", + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "brotli" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cc91aac060a7a1e25823bdccbfb6af1875b88f17c6daac97894eed8207166b3" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", + "brotli-decompressor", +] + +[[package]] +name = "brotli-decompressor" +version = "5.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a32acac15fe1967bc3986b2a6347dffc965602354ea6f450ad07e8bfd253583" +dependencies = [ + "alloc-no-stdlib", + "alloc-stdlib", +] + +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + +[[package]] +name = "bumpalo" +version = "3.20.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" + +[[package]] +name = "byte-unit" +version = "5.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a813de7f2bbedb7dce265b64f1cf5908ebe4d56281ece8d847e98113788b9b0" +dependencies = [ + "rust_decimal", + "schemars 1.2.1", + "serde", + "utf8-width", +] + +[[package]] +name = "bytecheck" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +dependencies = [ + "bytecheck_derive", + "ptr_meta", + "simdutf8", +] + +[[package]] +name = "bytecheck_derive" +version = "0.6.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "bytemuck" +version = "1.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6aedf8ae72766347502cf3cb4f41cf5e9cc37d28bee90f1fdaaae15f9cf9424" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "byteorder-lite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" +dependencies = [ + "serde", +] + +[[package]] +name = "cairo-rs" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +dependencies = [ + "bitflags 2.13.0", + "cairo-sys-rs", + "glib", + "libc", + "once_cell", + "thiserror 1.0.69", +] + +[[package]] +name = "cairo-sys-rs" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "camino" +version = "1.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" +dependencies = [ + "serde_core", +] + +[[package]] +name = "cargo-platform" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e35af189006b9c0f00a064685c727031e3ed2d8020f7ba284d78cc2671bd36ea" +dependencies = [ + "serde", +] + +[[package]] +name = "cargo_metadata" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5eb614ed4c27c5d706420e4320fbe3216ab31fa1c33cd8246ac36dae4479ba" +dependencies = [ + "camino", + "cargo-platform", + "semver", + "serde", + "serde_json", + "thiserror 2.0.18", +] + +[[package]] +name = "cargo_toml" +version = "0.22.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "374b7c592d9c00c1f4972ea58390ac6b18cbb6ab79011f3bdc90a0b82ca06b77" +dependencies = [ + "serde", + "toml 0.9.12+spec-1.1.0", +] + +[[package]] +name = "cc" +version = "1.2.67" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e17dd265a7d0f31ef544e1b20e03add05d3b45b491b633b10d67145d2acc1a38" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfb" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d38f2da7a0a2c4ccf0065be06397cc26a81f4e528be095826eee9d4adbb8c60f" +dependencies = [ + "byteorder", + "fnv", + "uuid", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + +[[package]] +name = "chrono" +version = "0.4.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" +dependencies = [ + "iana-time-zone", + "num-traits", + "serde", + "windows-link 0.2.1", +] + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "cookie" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] + +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-graphics-types", + "foreign-types", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cssparser" +version = "0.36.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dae61cf9c0abb83bd659dab65b7e4e38d8236824c85f0f804f173567bda257d2" +dependencies = [ + "cssparser-macros", + "dtoa-short", + "itoa", + "phf", + "smallvec", +] + +[[package]] +name = "cssparser-macros" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "ctor" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "352d39c2f7bef1d6ad73db6f5160efcaed66d94ef8c6c573a8410c00bf909a98" +dependencies = [ + "ctor-proc-macro", + "dtor", +] + +[[package]] +name = "ctor-proc-macro" +version = "0.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52560adf09603e58c9a7ee1fe1dcb95a16927b17c127f0ac02d6e768a0e25bc1" + +[[package]] +name = "darling" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.118", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "data-url" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" + +[[package]] +name = "dbus" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ab69f03cc8c4340c9c8e315114e1658e6775a9b16a04357973aa21cec22b32e" +dependencies = [ + "libc", + "libdbus-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "deranged" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" +dependencies = [ + "serde_core", +] + +[[package]] +name = "derive_more" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d751e9e49156b02b44f9c1815bcb94b984cdcc4396ecc32521c739452808b134" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "syn 2.0.118", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "dispatch2" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", +] + +[[package]] +name = "displaydoc" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "dlopen2" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e2c5bd4158e66d1e215c49b837e11d62f3267b30c92f1d171c4d3105e3dc4d4" +dependencies = [ + "dlopen2_derive", + "libc", + "once_cell", + "winapi", +] + +[[package]] +name = "dlopen2_derive" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + +[[package]] +name = "dom_query" +version = "0.27.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" +dependencies = [ + "bit-set", + "cssparser", + "foldhash", + "html5ever", + "precomputed-hash", + "selectors", + "tendril", +] + +[[package]] +name = "dpi" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" +dependencies = [ + "serde", +] + +[[package]] +name = "dtoa" +version = "1.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c3cf4824e2d5f025c7b531afcb2325364084a16806f6d47fbc1f5fbd9960590" + +[[package]] +name = "dtoa-short" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd1511a7b6a56299bd043a9c167a6d2bfb37bf84a6dfceaba651168adfb43c87" +dependencies = [ + "dtoa", +] + +[[package]] +name = "dtor" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1057d6c64987086ff8ed0fd3fbf377a6b7d205cc7715868cd401705f715cbe4" +dependencies = [ + "dtor-proc-macro", +] + +[[package]] +name = "dtor-proc-macro" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f678cf4a922c215c63e0de95eb1ff08a958a81d47e485cf9da1e27bf6305cfa5" + +[[package]] +name = "dunce" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" + +[[package]] +name = "dyn-clone" +version = "1.0.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0881ea181b1df73ff77ffaaf9c7544ecc11e82fba9b5f27b262a3c73a332555" + +[[package]] +name = "embed-resource" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbfdaacccebec3b28e4866b8973543c7647797db5ada1bdab552e48fe665fbbd" +dependencies = [ + "cc", + "memchr", + "rustc_version", + "toml 1.1.2+spec-1.1.0", + "vswhom", + "winreg", +] + +[[package]] +name = "embed_plist" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_filter" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +dependencies = [ + "log", + "regex", +] + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" + +[[package]] +name = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + +[[package]] +name = "field-offset" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" +dependencies = [ + "memoffset", + "rustc_version", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" + +[[package]] +name = "flate2" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" +dependencies = [ + "crc32fast", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "funty" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" + +[[package]] +name = "futures-channel" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +dependencies = [ + "futures-core", +] + +[[package]] +name = "futures-core" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" + +[[package]] +name = "futures-executor" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" + +[[package]] +name = "futures-macro" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "futures-sink" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" + +[[package]] +name = "futures-task" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" + +[[package]] +name = "futures-util" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +dependencies = [ + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", +] + +[[package]] +name = "gdk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" +dependencies = [ + "cairo-rs", + "gdk-pixbuf", + "gdk-sys", + "gio", + "glib", + "libc", + "pango", +] + +[[package]] +name = "gdk-pixbuf" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" +dependencies = [ + "gdk-pixbuf-sys", + "gio", + "glib", + "libc", + "once_cell", +] + +[[package]] +name = "gdk-pixbuf-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gdk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" +dependencies = [ + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkwayland-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "140071d506d223f7572b9f09b5e155afbd77428cd5cc7af8f2694c41d98dfe69" +dependencies = [ + "gdk-sys", + "glib-sys", + "gobject-sys", + "libc", + "pkg-config", + "system-deps", +] + +[[package]] +name = "gdkx11" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3caa00e14351bebbc8183b3c36690327eb77c49abc2268dd4bd36b856db3fbfe" +dependencies = [ + "gdk", + "gdkx11-sys", + "gio", + "glib", + "libc", + "x11", +] + +[[package]] +name = "gdkx11-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e7445fe01ac26f11601db260dd8608fe172514eb63b3b5e261ea6b0f4428d" +dependencies = [ + "gdk-sys", + "glib-sys", + "libc", + "system-deps", + "x11", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "gethostname" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" +dependencies = [ + "rustix", + "windows-link 0.2.1", +] + +[[package]] +name = "getrandom" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi 5.3.0", + "wasip2", +] + +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", +] + +[[package]] +name = "gio" +version = "0.18.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "gio-sys", + "glib", + "libc", + "once_cell", + "pin-project-lite", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "gio-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", + "winapi", +] + +[[package]] +name = "glib" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" +dependencies = [ + "bitflags 2.13.0", + "futures-channel", + "futures-core", + "futures-executor", + "futures-task", + "futures-util", + "gio-sys", + "glib-macros", + "glib-sys", + "gobject-sys", + "libc", + "memchr", + "once_cell", + "smallvec", + "thiserror 1.0.69", +] + +[[package]] +name = "glib-macros" +version = "0.18.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" +dependencies = [ + "heck 0.4.1", + "proc-macro-crate 2.0.2", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "glib-sys" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" +dependencies = [ + "libc", + "system-deps", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "gobject-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" +dependencies = [ + "glib-sys", + "libc", + "system-deps", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" +dependencies = [ + "atk-sys", + "cairo-sys-rs", + "gdk-pixbuf-sys", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "pango-sys", + "system-deps", +] + +[[package]] +name = "gtk3-macros" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" +dependencies = [ + "proc-macro-crate 1.3.1", + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "h2" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cb093c84e8bd9b188d4c4a8cb6579fc016968d14c99882163cd3ff402a4f155" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.14.0", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.17.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" + +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "html5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1054432bae2f14e0061e33d23402fbaa67a921d319d56adc6bcf887ddad1cbc2" +dependencies = [ + "log", + "markup5ever", +] + +[[package]] +name = "http" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "tokio", + "tokio-rustls", + "tower-service", + "webpki-roots", +] + +[[package]] +name = "hyper-util" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.65" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core 0.62.2", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "ico" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371" +dependencies = [ + "byteorder", + "png 0.17.16", +] + +[[package]] +name = "icu_collections" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +dependencies = [ + "displaydoc", + "potential_utf", + "utf8_iter", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" + +[[package]] +name = "icu_properties" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" + +[[package]] +name = "icu_provider" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +dependencies = [ + "bytemuck", + "byteorder-lite", + "moxcms", + "num-traits", + "png 0.18.1", +] + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" +dependencies = [ + "equivalent", + "hashbrown 0.17.1", + "serde", + "serde_core", +] + +[[package]] +name = "infer" +version = "0.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" +dependencies = [ + "cfb", +] + +[[package]] +name = "ipnet" +version = "2.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "itoa" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "javascriptcore-rs" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca5671e9ffce8ffba57afc24070e906da7fc4b1ba66f2cabebf61bf2ea257fcc" +dependencies = [ + "bitflags 1.3.2", + "glib", + "javascriptcore-rs-sys", +] + +[[package]] +name = "javascriptcore-rs-sys" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1be78d14ffa4b75b66df31840478fef72b51f8c2465d4ca7c194da9f7a5124" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys 0.3.1", + "log", + "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.118", +] + +[[package]] +name = "js-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +dependencies = [ + "cfg-if", + "futures-util", + "wasm-bindgen", +] + +[[package]] +name = "json-patch" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "863726d7afb6bc2590eeff7135d923545e5e964f004c2ccf8716c25e70a86f08" +dependencies = [ + "jsonptr", + "serde", + "serde_json", + "thiserror 1.0.69", +] + +[[package]] +name = "jsonptr" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dea2b27dd239b2556ed7a25ba842fe47fd602e7fc7433c2a8d6106d4d9edd70" +dependencies = [ + "serde", + "serde_json", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.13.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "keyring" +version = "3.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" +dependencies = [ + "log", + "security-framework 2.11.1", + "security-framework 3.7.0", + "zeroize", +] + +[[package]] +name = "libappindicator" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" +dependencies = [ + "glib", + "gtk", + "gtk-sys", + "libappindicator-sys", + "log", +] + +[[package]] +name = "libappindicator-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" +dependencies = [ + "gtk-sys", + "libloading", + "once_cell", +] + +[[package]] +name = "libc" +version = "0.2.186" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" + +[[package]] +name = "libdbus-sys" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "328c4789d42200f1eeec05bd86c9c13c7f091d2ba9a6ea35acdf51f31bc0f043" +dependencies = [ + "pkg-config", +] + +[[package]] +name = "libloading" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" +dependencies = [ + "cfg-if", + "winapi", +] + +[[package]] +name = "libredox" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +dependencies = [ + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" + +[[package]] +name = "litemap" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" +dependencies = [ + "value-bag", +] + +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + +[[package]] +name = "markup5ever" +version = "0.38.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8983d30f2915feeaaab2d6babdd6bc7e9ed1a00b66b5e6d74df19aa9c0e91862" +dependencies = [ + "log", + "tendril", + "web_atoms", +] + +[[package]] +name = "memchr" +version = "2.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" + +[[package]] +name = "memoffset" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "moxcms" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b" +dependencies = [ + "num-traits", + "pxfm", +] + +[[package]] +name = "muda" +version = "0.19.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dd04e60bc0b07438a6771710ee1698f98f6ebbc7f89b61264af1563b8aeb878" +dependencies = [ + "crossbeam-channel", + "dpi", + "gtk", + "keyboard-types", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.13.0", + "jni-sys 0.3.1", + "log", + "ndk-sys", + "num_enum", + "raw-window-handle", + "thiserror 1.0.69", +] + +[[package]] +name = "ndk-sys" +version = "0.6.0+11769913" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" +dependencies = [ + "jni-sys 0.3.1", +] + +[[package]] +name = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.13.0", + "cfg-if", + "cfg_aliases", + "libc", +] + +[[package]] +name = "num-conv" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" +dependencies = [ + "num_enum_derive", + "rustversion", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" +dependencies = [ + "proc-macro-crate 3.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "num_threads" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" +dependencies = [ + "libc", +] + +[[package]] +name = "objc2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" +dependencies = [ + "bitflags 2.13.0", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-location" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] + +[[package]] +name = "objc2-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" +dependencies = [ + "bitflags 2.13.0", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" +dependencies = [ + "bitflags 2.13.0", + "objc2", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image", + "objc2-core-location", + "objc2-core-text", + "objc2-foundation", + "objc2-quartz-core", + "objc2-user-notifications", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-web-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" +dependencies = [ + "bitflags 2.13.0", + "block2", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" + +[[package]] +name = "open" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5" +dependencies = [ + "dunce", + "is-wsl", + "libc", +] + +[[package]] +name = "openagentic-desktop" +version = "0.1.0" +dependencies = [ + "keyring", + "serde", + "serde_json", + "tauri", + "tauri-build", + "tauri-plugin-dialog", + "tauri-plugin-http", + "tauri-plugin-log", + "tauri-plugin-os", + "tauri-plugin-shell", + "tokio", + "window-vibrancy", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "os_info" +version = "3.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9cf20a545b305cf1da722b236b5155c9bb35f1d5ceb28c048bd96ca842f41b5b" +dependencies = [ + "android_system_properties", + "log", + "nix", + "objc2", + "objc2-foundation", + "objc2-ui-kit", + "serde", + "windows-sys 0.61.2", +] + +[[package]] +name = "os_pipe" +version = "1.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "pango" +version = "0.18.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" +dependencies = [ + "gio", + "glib", + "libc", + "once_cell", + "pango-sys", +] + +[[package]] +name = "pango-sys" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" +dependencies = [ + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "phf" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" +dependencies = [ + "phf_macros", + "phf_shared", + "serde", +] + +[[package]] +name = "phf_codegen" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" +dependencies = [ + "phf_generator", + "phf_shared", +] + +[[package]] +name = "phf_generator" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" +dependencies = [ + "fastrand", + "phf_shared", +] + +[[package]] +name = "phf_macros" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "phf_shared" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e57fef6bc5981e38c2ce2d63bfa546861309f875b8a75f092d1d54ae2d64f266" +dependencies = [ + "siphasher", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" + +[[package]] +name = "pkg-config" +version = "0.3.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" + +[[package]] +name = "plist" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" +dependencies = [ + "base64 0.22.1", + "indexmap 2.14.0", + "quick-xml", + "serde", + "time", +] + +[[package]] +name = "png" +version = "0.17.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" +dependencies = [ + "bitflags 1.3.2", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "png" +version = "0.18.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +dependencies = [ + "bitflags 2.13.0", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", +] + +[[package]] +name = "potential_utf" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "precomputed-hash" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" + +[[package]] +name = "proc-macro-crate" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" +dependencies = [ + "once_cell", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +dependencies = [ + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.12+spec-1.1.0", +] + +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn 1.0.109", + "version_check", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check", +] + +[[package]] +name = "proc-macro2" +version = "1.0.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + +[[package]] +name = "ptr_meta" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +dependencies = [ + "ptr_meta_derive", +] + +[[package]] +name = "ptr_meta_derive" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "pxfm" +version = "0.1.30" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" + +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quinn" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" +dependencies = [ + "bytes", + "getrandom 0.4.3", + "lru-slab", + "rand 0.10.2", + "rand_pcg", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.61.2", +] + +[[package]] +name = "quote" +version = "1.0.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + +[[package]] +name = "radium" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" + +[[package]] +name = "rand" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" +dependencies = [ + "libc", + "rand_chacha", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom 0.2.17", +] + +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.13.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", +] + +[[package]] +name = "ref-cast" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +dependencies = [ + "ref-cast-impl", +] + +[[package]] +name = "ref-cast-impl" +version = "1.0.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "regex" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0e75113e14dc5acb068cd0786884f214f1312650a3d36d269f5c4f3cdee8a2" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f388202e4b80542a0921078cc23b6333bcf1409c1e3f86404cae4766a6131db" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" + +[[package]] +name = "rend" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" +dependencies = [ + "bytecheck", +] + +[[package]] +name = "reqwest" +version = "0.12.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +dependencies = [ + "base64 0.22.1", + "bytes", + "cookie", + "cookie_store", + "encoding_rs", + "futures-core", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", + "js-sys", + "log", + "mime", + "percent-encoding", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "webpki-roots", +] + +[[package]] +name = "reqwest" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "js-sys", + "log", + "percent-encoding", + "pin-project-lite", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-util", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", +] + +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rkyv" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" +dependencies = [ + "bitvec", + "bytecheck", + "bytes", + "hashbrown 0.12.3", + "ptr_meta", + "rend", + "rkyv_derive", + "seahash", + "tinyvec", + "uuid", +] + +[[package]] +name = "rkyv_derive" +version = "0.7.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "rust_decimal" +version = "1.42.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" +dependencies = [ + "arrayvec", + "borsh", + "bytes", + "num-traits", + "rand 0.8.7", + "rkyv", + "serde", + "serde_json", + "wasm-bindgen", +] + +[[package]] +name = "rustc-hash" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b1e7f9a428571be2dc5bc0505c13fb6bf936822b894ec87abf8a08a4e51742d" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] + +[[package]] +name = "rustix" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" +dependencies = [ + "bitflags 2.13.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +dependencies = [ + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +dependencies = [ + "web-time", + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" + +[[package]] +name = "ryu" +version = "1.0.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schemars" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" +dependencies = [ + "dyn-clone", + "indexmap 1.9.3", + "schemars_derive", + "serde", + "serde_json", + "url", + "uuid", +] + +[[package]] +name = "schemars" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd191f9397d57d581cddd31014772520aa448f65ef991055d7f61582c65165f" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +dependencies = [ + "dyn-clone", + "ref-cast", + "serde", + "serde_json", +] + +[[package]] +name = "schemars_derive" +version = "0.8.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e265784ad618884abaea0600a9adf15393368d840e0222d101a072f3f7534d" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.118", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "seahash" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "selectors" +version = "0.36.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" +dependencies = [ + "bitflags 2.13.0", + "cssparser", + "derive_more", + "log", + "new_debug_unreachable", + "phf", + "phf_codegen", + "precomputed-hash", + "rustc-hash", + "servo_arc", + "smallvec", +] + +[[package]] +name = "semver" +version = "1.0.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" +dependencies = [ + "serde", + "serde_core", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-untagged" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" +dependencies = [ + "erased-serde", + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_derive_internals" +version = "0.29.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_json" +version = "1.0.150" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +dependencies = [ + "itoa", + "memchr", + "serde", + "serde_core", + "zmij", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serde_spanned" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_spanned" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" +dependencies = [ + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +dependencies = [ + "base64 0.22.1", + "bs58", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.14.0", + "schemars 0.9.0", + "schemars 1.2.1", + "serde_core", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "serialize-to-javascript" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04f3666a07a197cdb77cdf306c32be9b7f598d7060d50cfd4d5aa04bfd92f6c5" +dependencies = [ + "serde", + "serde_json", + "serialize-to-javascript-impl", +] + +[[package]] +name = "serialize-to-javascript-impl" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "servo_arc" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "170fb83ab34de17dc69aa7c67482b22218ddb85da56546f9bd6b929e32a05930" +dependencies = [ + "stable_deref_trait", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest", +] + +[[package]] +name = "shared_child" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" +dependencies = [ + "libc", + "sigchld", + "windows-sys 0.60.2", +] + +[[package]] +name = "shlex" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" + +[[package]] +name = "sigchld" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1" +dependencies = [ + "libc", + "os_pipe", + "signal-hook", +] + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" +dependencies = [ + "libc", + "signal-hook-registry", +] + +[[package]] +name = "signal-hook-registry" +version = "1.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" +dependencies = [ + "errno", + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" + +[[package]] +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "siphasher" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" + +[[package]] +name = "slab" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" + +[[package]] +name = "smallvec" +version = "1.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" + +[[package]] +name = "socket2" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "softbuffer" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" +dependencies = [ + "bytemuck", + "js-sys", + "ndk", + "objc2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "objc2-quartz-core", + "raw-window-handle", + "redox_syscall", + "tracing", + "wasm-bindgen", + "web-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "soup3" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "471f924a40f31251afc77450e781cb26d55c0b650842efafc9c6cbd2f7cc4f9f" +dependencies = [ + "futures-channel", + "gio", + "glib", + "libc", + "soup3-sys", +] + +[[package]] +name = "soup3-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ebe8950a680a12f24f15ebe1bf70db7af98ad242d9db43596ad3108aab86c27" +dependencies = [ + "gio-sys", + "glib-sys", + "gobject-sys", + "libc", + "system-deps", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "string_cache" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" +dependencies = [ + "new_debug_unreachable", + "parking_lot", + "phf_shared", + "precomputed-hash", +] + +[[package]] +name = "string_cache_codegen" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" +dependencies = [ + "phf_generator", + "phf_shared", + "proc-macro2", + "quote", +] + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "swift-rs" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +dependencies = [ + "base64 0.21.7", + "serde", + "serde_json", +] + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.118" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "sys-locale" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" +dependencies = [ + "libc", +] + +[[package]] +name = "system-configuration" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" +dependencies = [ + "bitflags 2.13.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml 0.8.2", + "version-compare", +] + +[[package]] +name = "tao" +version = "0.35.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" +dependencies = [ + "bitflags 2.13.0", + "block2", + "core-foundation 0.10.1", + "core-graphics", + "crossbeam-channel", + "dbus", + "dispatch2", + "dlopen2", + "dpi", + "gdkwayland-sys", + "gdkx11-sys", + "gtk", + "jni", + "libc", + "log", + "ndk", + "ndk-sys", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "once_cell", + "parking_lot", + "percent-encoding", + "raw-window-handle", + "tao-macros", + "unicode-segmentation", + "url", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "tao-macros" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tap" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + +[[package]] +name = "tauri" +version = "2.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "667b20e2726d572dea2de7370da16e188eb06008faf9a92fab7cdc46791190b5" +dependencies = [ + "anyhow", + "bytes", + "cookie", + "dirs", + "dunce", + "embed_plist", + "getrandom 0.3.4", + "glob", + "gtk", + "heck 0.5.0", + "http", + "image", + "jni", + "libc", + "log", + "mime", + "muda", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "percent-encoding", + "plist", + "raw-window-handle", + "reqwest 0.13.4", + "serde", + "serde_json", + "serde_repr", + "serialize-to-javascript", + "swift-rs", + "tauri-build", + "tauri-macros", + "tauri-runtime", + "tauri-runtime-wry", + "tauri-utils", + "thiserror 2.0.18", + "tokio", + "tray-icon", + "url", + "webkit2gtk", + "webview2-com", + "window-vibrancy", + "windows", +] + +[[package]] +name = "tauri-build" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc9ce40b16101cb6ea63d3e221567affd1c3a9205f95d7bc574941a10636b632" +dependencies = [ + "anyhow", + "cargo_toml", + "dirs", + "glob", + "heck 0.5.0", + "json-patch", + "schemars 0.8.22", + "semver", + "serde", + "serde_json", + "tauri-utils", + "tauri-winres", + "walkdir", +] + +[[package]] +name = "tauri-codegen" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08279169ff42f8fc45a1dbc9dcae888893ba95288142e5880c59b93a26d2cfc5" +dependencies = [ + "base64 0.22.1", + "brotli", + "ico", + "json-patch", + "plist", + "png 0.17.16", + "proc-macro2", + "quote", + "semver", + "serde", + "serde_json", + "sha2", + "syn 2.0.118", + "tauri-utils", + "thiserror 2.0.18", + "time", + "url", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-macros" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8b394794f399a421811d06966343e7933fcae92d59f5180b9388d1174497a45" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.118", + "tauri-codegen", + "tauri-utils", +] + +[[package]] +name = "tauri-plugin" +version = "2.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74be5dd4bed9afbd145e5716b5fa2ec28cbc29c34ffa61c258c9273d896c8020" +dependencies = [ + "anyhow", + "glob", + "plist", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri-utils", + "walkdir", +] + +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7ecc274121aca0c036a2b42d1cbe83d368d348f54e0bb8a735c2b1548e8f371" +dependencies = [ + "anyhow", + "dunce", + "glob", + "log", + "objc2-foundation", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", +] + +[[package]] +name = "tauri-plugin-http" +version = "2.5.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5bd512048e1985b7ec78f96d99083e2ddaf7e0d906b2b63c44ce5bb8b894067" +dependencies = [ + "bytes", + "cookie_store", + "data-url", + "http", + "regex", + "reqwest 0.12.28", + "schemars 0.8.22", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "tokio", + "url", + "urlpattern", +] + +[[package]] +name = "tauri-plugin-log" +version = "2.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7545bd67f070a4500432c826e2e0682146a1d6712aee22a2786490156b574d93" +dependencies = [ + "android_logger", + "byte-unit", + "fern", + "log", + "objc2", + "objc2-foundation", + "serde", + "serde_json", + "serde_repr", + "swift-rs", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "time", +] + +[[package]] +name = "tauri-plugin-os" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" +dependencies = [ + "gethostname", + "log", + "os_info", + "serde", + "serde_json", + "serialize-to-javascript", + "sys-locale", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", +] + +[[package]] +name = "tauri-plugin-shell" +version = "2.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8457dbf9e2bab1edd8df22bb2c20857a59a9868e79cb3eac5ed639eec4d0c73b" +dependencies = [ + "encoding_rs", + "log", + "open", + "os_pipe", + "regex", + "schemars 0.8.22", + "serde", + "serde_json", + "shared_child", + "tauri", + "tauri-plugin", + "thiserror 2.0.18", + "tokio", +] + +[[package]] +name = "tauri-runtime" +version = "2.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0b4bc95aed361b0019067d189a1174a603d460d0f6c72606512d59fc9c12ec8" +dependencies = [ + "cookie", + "dpi", + "gtk", + "http", + "jni", + "objc2", + "objc2-ui-kit", + "objc2-web-kit", + "raw-window-handle", + "serde", + "serde_json", + "tauri-utils", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webview2-com", + "windows", +] + +[[package]] +name = "tauri-runtime-wry" +version = "2.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e6fac707727b7a2f48e4ded90976324267371073edbb415ffb73bb0458d203f" +dependencies = [ + "gtk", + "http", + "jni", + "log", + "objc2", + "objc2-app-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "softbuffer", + "tao", + "tauri-runtime", + "tauri-utils", + "url", + "webkit2gtk", + "webview2-com", + "windows", + "wry", +] + +[[package]] +name = "tauri-utils" +version = "2.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3e176a18e67764923c4f1ce66f25ae4abe5f688384d5eb1a0fa6c77f3d90f887" +dependencies = [ + "anyhow", + "brotli", + "cargo_metadata", + "ctor", + "dom_query", + "dunce", + "glob", + "http", + "infer", + "json-patch", + "log", + "memchr", + "phf", + "plist", + "proc-macro2", + "quote", + "regex", + "schemars 0.8.22", + "semver", + "serde", + "serde-untagged", + "serde_json", + "serde_with", + "swift-rs", + "thiserror 2.0.18", + "toml 1.1.2+spec-1.1.0", + "url", + "urlpattern", + "uuid", + "walkdir", +] + +[[package]] +name = "tauri-winres" +version = "0.3.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" +dependencies = [ + "dunce", + "embed-resource", + "toml 1.1.2+spec-1.1.0", +] + +[[package]] +name = "tendril" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5fed54709c5b3a53d09bb1c113ea4f5ceafd1e772ddcb0030a82e1d56c087b08" +dependencies = [ + "new_debug_unreachable", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "time" +version = "0.3.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +dependencies = [ + "deranged", + "libc", + "num-conv", + "num_threads", + "powerfmt", + "serde_core", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" + +[[package]] +name = "time-macros" +version = "0.2.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tinystr" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.52.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +dependencies = [ + "bytes", + "libc", + "mio", + "parking_lot", + "pin-project-lite", + "signal-hook-registry", + "socket2", + "tokio-macros", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-macros" +version = "2.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +dependencies = [ + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "toml_edit 0.20.2", +] + +[[package]] +name = "toml" +version = "0.9.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf92845e79fc2e2def6a5d828f0801e29a2f8acc037becc5ab08595c7d5e9863" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 0.7.15", +] + +[[package]] +name = "toml" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +dependencies = [ + "indexmap 2.14.0", + "serde_core", + "serde_spanned 1.1.1", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "toml_writer", + "winnow 1.0.3", +] + +[[package]] +name = "toml_datetime" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + +[[package]] +name = "toml_edit" +version = "0.19.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.14.0", + "serde", + "serde_spanned 0.6.9", + "toml_datetime 0.6.3", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.25.12+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +dependencies = [ + "indexmap 2.14.0", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow 1.0.3", +] + +[[package]] +name = "toml_parser" +version = "1.1.2+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +dependencies = [ + "winnow 1.0.3", +] + +[[package]] +name = "toml_writer" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" + +[[package]] +name = "tower" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" +dependencies = [ + "bitflags 2.13.0", + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "url", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-core", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", +] + +[[package]] +name = "tray-icon" +version = "0.24.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.20.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" + +[[package]] +name = "unic-char-property" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221" +dependencies = [ + "unic-char-range", +] + +[[package]] +name = "unic-char-range" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc" + +[[package]] +name = "unic-common" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc" + +[[package]] +name = "unic-ucd-ident" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987" +dependencies = [ + "unic-char-property", + "unic-char-range", + "unic-ucd-version", +] + +[[package]] +name = "unic-ucd-version" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4" +dependencies = [ + "unic-common", +] + +[[package]] +name = "unicode-ident" +version = "1.0.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" + +[[package]] +name = "unicode-segmentation" +version = "1.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", + "serde_derive", +] + +[[package]] +name = "urlpattern" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d" +dependencies = [ + "regex", + "serde", + "unic-ucd-ident", + "url", +] + +[[package]] +name = "utf8-width" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "159a7cadce548703edd50d24069bc294c5415ecab0a480e0cd1ca06d112dc94a" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "uuid" +version = "1.23.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +dependencies = [ + "getrandom 0.4.3", + "js-sys", + "serde_core", + "wasm-bindgen", +] + +[[package]] +name = "value-bag" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "vswhom" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be979b7f07507105799e854203b470ff7c78a1639e330a58f183b5fea574608b" +dependencies = [ + "libc", + "vswhom-sys", +] + +[[package]] +name = "vswhom-sys" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb067e4cbd1ff067d1df46c9194b5de0e98efd2810bbc95c5d5e5f25a3231150" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.4+wasi-0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.118", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.126" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wasm-streams" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9d1ec4f6517c9e11ae630e200b2b65d193279042e28edd4a2cda233e46670bbb" +dependencies = [ + "futures-util", + "js-sys", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "web-sys" +version = "0.3.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web_atoms" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +dependencies = [ + "phf", + "phf_codegen", + "string_cache", + "string_cache_codegen", +] + +[[package]] +name = "webkit2gtk" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1027150013530fb2eaf806408df88461ae4815a45c541c8975e61d6f2fc4793" +dependencies = [ + "bitflags 1.3.2", + "cairo-rs", + "gdk", + "gdk-sys", + "gio", + "gio-sys", + "glib", + "glib-sys", + "gobject-sys", + "gtk", + "gtk-sys", + "javascriptcore-rs", + "libc", + "once_cell", + "soup3", + "webkit2gtk-sys", +] + +[[package]] +name = "webkit2gtk-sys" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "916a5f65c2ef0dfe12fff695960a2ec3d4565359fdbb2e9943c974e06c734ea5" +dependencies = [ + "bitflags 1.3.2", + "cairo-sys-rs", + "gdk-sys", + "gio-sys", + "glib-sys", + "gobject-sys", + "gtk-sys", + "javascriptcore-rs-sys", + "libc", + "pkg-config", + "soup3-sys", + "system-deps", +] + +[[package]] +name = "webpki-roots" +version = "1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "webview2-com" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a" +dependencies = [ + "webview2-com-macros", + "webview2-com-sys", + "windows", + "windows-core 0.61.2", + "windows-implement", + "windows-interface", +] + +[[package]] +name = "webview2-com-macros" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "webview2-com-sys" +version = "0.38.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" +dependencies = [ + "thiserror 2.0.18", + "windows", + "windows-core 0.61.2", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "window-vibrancy" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" +dependencies = [ + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "raw-window-handle", + "windows-sys 0.59.0", + "windows-version", +] + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core 0.61.2", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core 0.61.2", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-core" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core 0.61.2", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-version" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4060a1da109b9d0326b7262c8e12c84df67cc0dbc9e33cf49e01ccc2eb63631" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winnow" +version = "0.5.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" +dependencies = [ + "memchr", +] + +[[package]] +name = "winnow" +version = "0.7.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +dependencies = [ + "memchr", +] + +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + +[[package]] +name = "writeable" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" + +[[package]] +name = "wry" +version = "0.55.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "186f9871daa55fd9c016578b810d149de58367113db7fb72b462d2323ce19514" +dependencies = [ + "base64 0.22.1", + "block2", + "cookie", + "crossbeam-channel", + "dirs", + "dom_query", + "dpi", + "dunce", + "gdkx11", + "gtk", + "http", + "javascriptcore-rs", + "jni", + "libc", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation", + "objc2-ui-kit", + "objc2-web-kit", + "once_cell", + "percent-encoding", + "raw-window-handle", + "sha2", + "soup3", + "tao-macros", + "thiserror 2.0.18", + "url", + "webkit2gtk", + "webkit2gtk-sys", + "webview2-com", + "windows", + "windows-core 0.61.2", + "windows-version", + "x11-dl", +] + +[[package]] +name = "wyz" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" +dependencies = [ + "tap", +] + +[[package]] +name = "x11" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" +dependencies = [ + "libc", + "pkg-config", +] + +[[package]] +name = "x11-dl" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" +dependencies = [ + "libc", + "once_cell", + "pkg-config", +] + +[[package]] +name = "yoke" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "709fe23a0424b6a435d82152b1bd3fdfb0833487d5fa90d05d42762a9891fef5" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zerocopy" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7cbbc0a705a0fd05cc3676525980d2bf5a9bc4adac6d6475209a7887cf59d19" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2e817b7b52d0c7358d3246da9d69935ebb18116b2b102b4230dac079b4862f5" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zerofrom" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13c156562582aa81c60cb29407084cdb54c4164760106ab78e6c5b0858cf64e" + +[[package]] +name = "zerotrie" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.118", +] + +[[package]] +name = "zmij" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" diff --git a/companions/desktop/src-tauri/Cargo.toml b/companions/desktop/src-tauri/Cargo.toml new file mode 100644 index 0000000000..a34d154ac0 --- /dev/null +++ b/companions/desktop/src-tauri/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "openagentic-desktop" +version = "0.1.0" +description = "OpenAgentic desktop companion" +authors = ["OpenAgentic"] +edition = "2021" + +[lib] +name = "openagentic_desktop_lib" +crate-type = ["staticlib", "cdylib", "rlib"] + +[build-dependencies] +tauri-build = { version = "2.5", features = [] } + +[dependencies] +tauri = { version = "2.5", features = ["tray-icon", "image-png", "macos-private-api"] } +tauri-plugin-shell = "2" +tauri-plugin-dialog = "2" +tauri-plugin-http = "2" +tauri-plugin-os = "2" +tauri-plugin-log = "2" +serde = { version = "1", features = ["derive"] } +serde_json = "1" +tokio = { version = "1", features = ["full"] } +keyring = { version = "3", features = ["apple-native"] } +window-vibrancy = "0.6" diff --git a/companions/desktop/src-tauri/build.rs b/companions/desktop/src-tauri/build.rs new file mode 100644 index 0000000000..d860e1e6a7 --- /dev/null +++ b/companions/desktop/src-tauri/build.rs @@ -0,0 +1,3 @@ +fn main() { + tauri_build::build() +} diff --git a/companions/desktop/src-tauri/capabilities/default.json b/companions/desktop/src-tauri/capabilities/default.json new file mode 100644 index 0000000000..9a549d7ef6 --- /dev/null +++ b/companions/desktop/src-tauri/capabilities/default.json @@ -0,0 +1,47 @@ +{ + "$schema": "../gen/schemas/desktop-schema.json", + "identifier": "default", + "description": "Default capability for the main window", + "windows": ["main"], + "permissions": [ + "core:default", + "shell:default", + { + "identifier": "shell:allow-spawn", + "allow": [ + { + "name": "binaries/agenticode-cli", + "sidecar": true, + "args": [ + "--print", + "--verbose", + "--output-format", + "stream-json", + "--input-format", + "stream-json", + "--include-partial-messages", + "--replay-user-messages", + "--permission-prompt-tool", + "stdio" + ] + } + ] + }, + "shell:allow-stdin-write", + "shell:allow-kill", + "dialog:default", + "dialog:allow-open", + "os:default", + "os:allow-hostname", + "log:default", + { + "identifier": "http:default", + "allow": [ + { "url": "http://localhost:*/*" }, + { "url": "http://127.0.0.1:*/*" }, + { "url": "http://**/*" }, + { "url": "https://**/*" } + ] + } + ] +} diff --git a/companions/desktop/src-tauri/icons/128x128.png b/companions/desktop/src-tauri/icons/128x128.png new file mode 100644 index 0000000000000000000000000000000000000000..5a10c0c1e4fb6b5bada1b2e1b79fcadb890c00fd GIT binary patch literal 2672 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 Uw_EKJLjV8(07*qoM6N<$g2xVl@Bjb+ literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/Square107x107Logo.png b/companions/desktop/src-tauri/icons/Square107x107Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..364faa8c1e2f98942a7ade05e044f988b9fbed7f GIT binary patch literal 2301 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 IM6N<$g7KZn_5c6? literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/Square150x150Logo.png b/companions/desktop/src-tauri/icons/Square150x150Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..08ae45f06ec3d00257231bdfea1d481d3206dea7 GIT binary patch literal 3165 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 D;}ZjL literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/Square284x284Logo.png b/companions/desktop/src-tauri/icons/Square284x284Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..47282b71d4645961a56841cef96ed3ac6fd33b91 GIT binary patch literal 5759 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 T(HGzn00000NkvXXu0mjfYiBB) literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/Square310x310Logo.png b/companions/desktop/src-tauri/icons/Square310x310Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9e6fe7927b8791c56d168b15ed4d29f8aa663a35 GIT binary patch literal 6193 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 Qg8%>k07*qoM6N<$f-u0q4gdfE literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/Square71x71Logo.png b/companions/desktop/src-tauri/icons/Square71x71Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..b44b97ab109d46e51ffdc6195b269981e3cfa9bc GIT binary patch literal 1560 zcmV+z2Iu*SP)N43Xz+^CVGNI(1V(&v5iNf z^(HhN>IH}%5R529!;%yd#6ZJVx{=WCZnxdp-Pswx@4tQQ3)AVApek@l9Fnoz1S@@(!ZfEI)r6Y@)Tucf^ZkXQ&Nom9@( zA{Y|%tL*%VLz&NUTBOf3fDqKS1acYGnM`HQEtyrI$2Ag;V!ZTysd;oMKTUcmHE{@JQ>oWQ)gTJ8o7uEjAH$)mvFlF0BICk<0#o1MqFQ zHLpTO)r>760*xNc%-XZ7-o^l(nvy|5K>J#3(6_}2wCIb|85p^nf~j<_HsMqfGOhkr zJA>r52yo_p+7L)p38xU!?glFyY;!>XZSHD33!~9Aycw`Vo5KR<5ZZ~m^BW6?Lhes5 zG~j|nrvAtNN1#o8wTgnOct&um1RxBD@S<=X%qQ{^^_mcluFlT3ZK)DAPCCi zC0A>FR$-=gX(nShoDR1Hr=Rn)NcR0d@|upRyF(^ZI@IQbLr=J%P7=zl;^2)0L{d4! z;2iDnz|J}=GcnU;$mEE>xIG8Y|M~z%r{prwTP^SCw(;nFFDXw)Ra$eI$btovmMvcU0ZQ$MUM0FLO zUvocipEm=6OoiI+4LLD4i>(XifVp1GHZnI@Ky9Hq$x)9ed(B|pY=mmokT5Wj8rz=@*CZ&@0M8O!EMo_+uSTVjYQ_*n@Eb^4qoij9NFf9Ef@)p z+?<7r4>J5-`C_$wxZMp=G@pTR+^}0QRR`yAy9@eSop2W0pTl?NbuAC74h|KlFJYJt zCvxyMhUX5q1wQ#ZscW@em2jvBJ%sIxr4#|1_6M%TbuSC5@<9StqzcRR*dK9a!@!sh zP6Qn;;1g!Cz=l2(D8xC2ibfR|aR)@0gQ&PO zBrMXFVNRm*R%%fbV2q^^Hw?8(Yn^xBmbF+fr&vhI4!X0000< KMNUMnLSTZ{59PZ6 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/Square89x89Logo.png b/companions/desktop/src-tauri/icons/Square89x89Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..72a5eb53c9b3166043f1a057fe00126d9eeea20b GIT binary patch literal 1844 zcmV-42g~@0P)pa31cXEg3Ydl>C8hSVJKNoDclYz0+1-}C6-wuu-OiqqG~I2Rru*x+ z=luWY`(};`GmPfu>gsSXwh`jc3W{h}0IG$6m0;6Q6u2k=MnEXN;D9sU3g?O5-jR_R z{f-Cy!G?ORRT0WA0b&!BcqpJ87m7G^XHY+53S@$dpZ0)&R^Z5_N09g45B9W>8|$^t zF%`5k&HJX5&i+CDof$!&~hC^Zy zU)AWQamWkg>!5ZelaQI1MY98iaut)1%(^5j%^;Lku5HeVPi8qf|qtm1bURir(C!smi5 zjoza9`)mPuVVM)M_I~QzpsB_2@|;?^yJN0Hp6_wO>MB>-Ssf4d7hikC1GR1iZieEZ z#{@&3O+SiPZ7a(72&K{urHL$Y#eXOeb$PmBGqNm+_n+AGsB>1a`?a?K5Ft!xYlhADUDt zRW1h{yB&hh{s|U8WHJXwS`ymz@Xay!sxMSj2B8s)<&K4AuzsEg%CJZ-@TMyff_%^< z(9$5ET2qU9b;?kVq7&Qww+>ymLq)Z+3HH&IRnX!~H@{?PUO6{Z+&WpwIQd+qDsX7I z4}O2K-dN?u68(`F{CGE#byPbL6~%8x+Ajy-vL4O)dK11azgu06A2o(ED~33RY9kbn zr=4>>+&4CN3g6ZRW$^xE74WFnk@p2s{_6|H3JUt0Rn_oRr3xno#^L)rVfbZud~T@= zs@QE<(c2c5L#3L1nF{6WLKep*6)GIXBHfK?WB)aMZuTw+998OPe*Vue0$KkA1W&8| z+zWN+&dMx@OpY*RRBWS@o3NpCXnCd4|IHPB;W*}Wzo~E8>Ea|RAsydMo6is1rwd2< z_jP8AzaLx;SbC_?_K>Oh{>d_5;^-{q-4CzoQk(E40h#P^YKOUJXaaVkb2IkoByH)J zoP15`oH+1RE_ma70q^+rZ>abmMdECGTQ?UY`+c{ZGHX*=YL`QOMv68d5 ziv;eXgn_R}H8H5sL=C6Z_7$}%t?uY??}Aq{L*|fG_)rO5VhmF6ih5Qx29ZYKKWkUB+kl{>dQ!{>cuSiJ>UU3C1DgT1Lef#^p#DQ_;ytWZc$1!PS6l zjA}rs!C1_dZn;KabTAoHqa$l#jy^WtOOsyk;j_$V)~lGt$9&xz#Op iE@l~XtB=ogZuS2dIo!erV(XXy0000Vs6lw)DkTeQLqhmp&;*e6Z99 zDG05Cf3%3TFItLKTO}zm7!e~z6Va8p$?X5n?K!h1S!)D0$?oKad=QdZGJEHn^L^)> zxpN$N31(FW)@6YfA!LJ;Xn>s%xBw~cYQX8Aq>lnBOk^$( zON;!_l7&146(O*iWv12F2ZI!eBTy6+iT#$eybjlA60!yAqPEQEZr1}o=b>i?QWg>- z;jq0%Oo+Z@y{N4;B&Glym?D@aGEJmN(Ck>W+^MNeWR6!y*XkPFTv>%RHEwl@G*`P( zrsw*K&9i;#5;@)%$6x&^^v1Ke9ZBQ-yA{|n#|yV6#$OZdEQn4buxXYTn_lza*GF;u z^dOE(EG1seM-<=39^(TuXt!*TQlS_lR zvb+)-XyJ*%Ns`_AYyB|O5+6~7nkt;w^@hKA9y#|oi2+)y+#zsaaR66WRH12xYr+Wq z_#lROhD0I-IwB2?rSCZVC;?l#UycHszG;SYSZ;?&--ml*1=pFiH7lvkF?zOKcuI>#y>(( za$l>A2rvo8lVldkZqRX&+)9Rik5cyW-gwd0ncj_`Y|yBnwkyv<;G!m_Xj7gDPet%p z#1z-sN!!`nXxGXlOKC zGx+m!vnV{%p%NJ;(H9zM#c0z(OvdoLE|TsVF-eCAta2f+aVQcmo!7$1;8_a~p7G0g k65>j`>qOq**{E6eKbx+In6%rUmH+?%07*qoM6N<$f-MUkfdBvi literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml b/companions/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000000..2ffbf24b68 --- /dev/null +++ b/companions/desktop/src-tauri/icons/android/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png b/companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..9f29380b5fe58d05deafdff933b79729f2d537ce GIT binary patch literal 1535 zcmV4C0M}$4 zz@mosb5tx#Ss`854;o%iAfC*8Q#J@hV2}ae4JH66=RQ&6Anmgu0`gI?>+9h0Uo`#p&coTh&}C z+>zdrAVjqYomH-sdQlFDsze}ZDGEvf;YS5?*;7>q!A3KHT|r4F_cYV1w}kA7xH)*$ zXR%ypZkUShYxUBB?S7(+ML*4D=+j7og1G3>#4?U3H~7`n7A)&Mmcwr;iVx1-PC-(7 zeUb+I42mQzu3U60A~QUZ0H8Ewlr8P{JNTMjf@_mAi^3HU>_CYDZ4nwC$x(s^mrT{gAU#G7u(vjchsRtm_mMRNG#ks`>+}+Vc z!vkS@=e8}hKkTJBxI1P2OR-g}on=uZGVo;r4;&v|ppk`)?VU;d=4(u^_2k{MPtfmu ztz;r$kPGns@AD?Lg1`rnun)I+XuBclYCLNnew-I&&Fnxg9DBBS=*;y5ogPov$!-;S zW-*a|eZ!H^9It*knWV!b^K|Uu93?R4eR^kr&h8D{RP(}+1n3}2s0vfF3)Jz!GiLxG zkyY!iAr&XKaA?8w(VS#`77=;*<{)_y-)o4*yMM;%6Bt&gN^=yVpx`rjE(zm5hf}mN zWY)Eu$PZ-T4RLs!!~e%(mc|lT21|{$;OK{pazs=S)nXv~nmzP$Gy{jI+7>%bz6CR< z?GJVO=*eCmJib1dv z4YYV@5L7s{&1duJDv8&aCT)AT6X!Ao@+GC*=wheTz?>H|DFw+TI@TAUcDVcPt1+4dr8w~w6`nXY#CpQC)yG4x z>C12X!Y#baReAZ#>~En=Ht+M z8jp&be1=y>Jh~h}o`k4QJv*ilN5BD~1g&1diZc7yPc7+Vx7MJKJ-eijeJY;=3_z_R lIE~f~+WfbOO%V0|`~y;!cB+pp@<#vw002ovPDHLkV1i__+-3j( literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png b/companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..e4287682b955a8bde65393553d8e7b5c45b0f49b GIT binary patch literal 3351 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 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png b/companions/desktop/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000000000000000000000000000000000000..efdf78f36dea45af8508697fba1dc474e84c232a GIT binary patch literal 1362 zcmV-Y1+DstP)IxSM3HU;1sCc@)Qua#77;-x zK0pvev{kAiD)^u^QWuJ#6t!5>Cgwd!)0t%6_xk-ece+Y4cW!2;)7T4PLgw6azVH9P z|8dSq5bNLxULoP(=}N?G`J70mRlU|wwFX=5QwwtHQUI?tA=M1v*Gfh=8v#FS=w>6} zXD!ui;Qh!;H#-3@7}iCx)6MP$FIe>DK%f#P-ADp5sb#ON!Y+qRH_D0vURDor#e!Yi zg>@smAOr;bqrE1czb1xFVT~;RXlPXvZv-2WIno4tecZsoUG0vRI*kAx`ehDz%R)S; zAY96xQm*Xk+IBcEaV4(pN!tR*;K1_*3qMcg@W)Kf@xpUg$8q1L2#Opqled<}apLlL z-wmKwDdh+vX8~^hK;Nk7=#j=dcQzM0A6@W>**Ie8t53V_76Q`@LIDyn9m{-sVpqt$Cgdvoo@n~aARg?F;OT)VA_OwFP{L=E zIlS_B+Ocjdpgd<8?Mm&{8(MLcQaVXb*j045zC0nC9?lf;+CK{zJUNRaqgiZWkUrjL z;`r?eJlP+;Xl*Z66aoX$AhfOU!))GDqFB}w)fPGBW*qsYNYC8TYT)IaaopA(qF$CV z&Y?34_=0J5wT+8NZ-JJ0%#*&{;>b~Cfwoe1ICZBgu?7gCuLh8P&HsLoVAubc^BvTcrxQ;si0~A3BhzT_5T=~xdYo5OUa(Hia5eoz& zRaxR>B?9ICsFQWzVH#jBrS$r6+9`yFQdRd(K|dYo!JVzx(-XldTK*%Z#7{`#G*fJc zv1I+iDrHu_kxG1yo_mCbc;VW((=6?X8TdDA!ES_{MBi_nW0htQe#Jl@VovX1geM53 zXs_OZ%6B5OS+`2>qDSZ1ek4;RB-t|{%#YU+?sZUmD94zhAJLVzAE!d80)&nmX+QZ zUPO zm11@MpNiM}>b6#wEUHpF07*qoM6N<$g4?!_6#xJL literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png b/companions/desktop/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..de6de16530826e67cc4bcb7dcdcc6ba880c959af GIT binary patch literal 1540 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 IM6N<$g2-WD0RR91 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png b/companions/desktop/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..3e0b4780d8a0b538f71fd75c45504dcacc0f3b58 GIT binary patch literal 4552 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 LNkvXXu0mjf{zZCB literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png b/companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000000000000000000000000000000000000..473d669000831bb32598d7cc68e05438879e8fc8 GIT binary patch literal 5475 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 UX9te`51s;K;EIwp?>`3p4*}bG@&Et; literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png b/companions/desktop/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..91a7a2f3386b5e33eff7b28d5869d78a387735e4 GIT binary patch literal 6766 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�jH`j!WS4j(vto^K{F`MYeE|@kpM=?lja*0qs5&M96#N1A$XE@}W z*@)B7`CVwUNNBqJjNK(}d0J+K(de~@#X}Zbt`9?3zUMoPAZhBXno)k@wlkpCqBCeJ zKNP}~@`>6;*R6+Q=VTD7pYc}O%GB?##0P&&()Ux~0c{1_gepY-X3T(z$ z?#DpDFQ$NcAQCz@KN)t$ln-faB)vqQGI4R9slcf=PPy+Ls1 zp8Z|kLa$10?YdWY$Kf-TOUq)fp9i6b1Ti{+<1B+vx9hwiX5*nz53VsSf)Hp;&c2Yi z*wPflo}%;fJK9wA+G*U2y0A>n{-MSfC)1lPmTIf#9Ze>fIoo+wM(0!oGmPEOdf}P5 zI_?%n{im$p<>6XFM}ktBWki!>O7VbuvYG;A@mtZS&61~Y3b@Kl4jvzRT=;Q7)yTNG zKCr2@^d0+OpTC6qhaAXLC+~JZ7UExT)+MP+?}<)-1|2c*W|eq*cOTpGAB*G**BY)C z^!Hk0JL0a63rni@1eq5kx#jWb&%DfZVFqnz0dpVFdFVry$(o?PWdD$L)? zA8t7}nFKjIE)d%3kMS8pLacd8%`D%)DQmWc)~>zhnz*M=IBHT94v5tCA3%T*NbZ&Z zJfE?xhEJAn9mfn>>7`Z^c^@be60$S)8ozTXhVCRhEG5B%9}!NF-Ez?oMkqw=AOH2 zia0qDnlB?|bSYdre!w`hJ2yF2c}8b5ZsBBRF+k(mpn*TF2#wD~G>LdO>sl;;&OM_;InW0p)X0p8Yr)oHLu zgTYm9Kfqq;}+$-2PJ!n20dSU!i!rCee4pfWMoS?=_leo`>qCd}t_lK7%eDQSJ)h07@2t z$*$HPBmdNQKCmzK{G@QbyfQhd!Ae(KN_owh#Sb_d^3*uLs4ztC>c(hH4EB=o7=lfB(+qClajCAPgYGbpo z=uUfRE^u$QS#5+D3!P(L0yD5{H$FqLLh}=Ejn+d+>Hw}(0_=io>FONYFctZYj?-wtzoNe?7uX+H!4x9M z7S7$(;A5kwm~C$WSK&ZI-JC z{T3D9tqVXj7LBg92me7%-iT~v^RQ77@v9BwMCsQ-mZXa&1|q#JKoKw zU9z>h`k5a2OFvhMcSySj%Q>tdLs+r;d>IhP!`_u`$nW!o2{D;io%7X0-P2iG`A zoPD0&<0?CI+uR1kya$ykPsvE9K2Fbp*@s=p=cWH@xS%WEK*Y!NsGsvnL3_VM#?Lm) zXB_|=ZRq$W)v4{`Qp4!xU`)*0DPmAP+{Vsj=#1ec=s1US%1P?La1%_jI2==RfN(B; zfiuoXGq$F$RX=kp=jHH6u_as&Nz}1Rsn_9cPktOiOdNGMTFwd?((co{PAW{gYvzJ| z9anYxnY%$$1iZiCT>IxLrkO_qv2a-hC>N5KwoH)^0x1s_{-x5iAbtgz<8rmN`P&!q?=UZgzke$xYXsYZra7NV+uJPFpBmMDnoWnk1CRWRnqJ0Z!^`9)}dAu8650W>f^)d~HkCt4$*!{`^ zUJ_qB@KVnRQ$9tI%+iKQ{89rJKG$?K@4b~hz+BGd<=qxL^)ciOq5sk0W#YVMEv2@c!#!gl>Cq*^ljPH%o5K{nF& z(#(?#^7|%-!lI)pLW$nKqR$;Jh8+(2*9@@KMD(`koPWJo?<&&p^T!aY^(LxuvyG)- z%z*Decsp4t$M9xkmXNkj!SA=9G8{5K-ZL}cQsMw}eiR@nn z6fFLhu1@6mb~3>&+zctwa4r=)e4BvvPg}C$3H{&w<^OL67F2HJl%i*+ZH~Z?{@GbT NO&O+Ctzh;3zW~U5b{hZy literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png b/companions/desktop/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000000000000000000000000000000000000..8d19dde85002b6a8cdd0391d28902189677e97e2 GIT binary patch literal 9047 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 + + #fff + \ No newline at end of file diff --git a/companions/desktop/src-tauri/icons/icon.icns b/companions/desktop/src-tauri/icons/icon.icns new file mode 100644 index 0000000000000000000000000000000000000000..f49d32f614a47b94608cd3e6968ed3f5dcc465ef GIT binary patch literal 63733 zcmeEtby!s0yXcxBMM_#4{FE-~oB>f(L=YsDRFp0OL2`zYl2AmtL_k2gb3h3JDM9HF zq`Pxw?#Ays_uPBWxxf3|`18*5%$~jXTI*eJt+n17YjXz|01o%8&Bd+)K=d$F>#iCF z88aCGfa2CoRc+`OdG;kGhOT+m&nBTx7jLO5-+wW>W~gq*@{~H&xM<34O@ivpn;Vqc zH4@t4{JbhEVl+4VVgyt@18ACIT%(*a2hWkLNP9m+J}NG*%kT@rTA{Z!(#v^x3+T?> z{78PTfGVR2M#q4j9?uD(Xp*x%_sIbdjTlJDq{r`Ud&>pfZO9xgs}= zkb=Ms-p+k%GaMHE_Aig|;ttdH*^WhH3@V4JR^I!O0xL1W=f1Dp&4fxE5j=yTUc~7+ zU)))R6yX|wEneh#d~R>A4o*!*6yTvN-(HubKT9E;Rw~V`aizQJ$)mSC%}zbJqxy4> zBi|SulD4I;M3G8h0#Di1k`jfI`GSD@3*uyg*Q)+JykecwS~8R~9gni*e;m;p*Aa4u z887L(8jn%=hkaPOFcO_8vr=S8w;ST$8}Qi^AdxV?AepE>5M2(eV14j(B6J`3j_Xy! z38_P8eU?+^^}x6jg&El9Jb}rJPu(zB9br5Bt~fFfZWhCx#2TyEz`U&zRaVJ4?9JYO z!2}$7y5#yf z>;46$#IDjzQ4p_^Av(TbfoyS+OHS=-s@m~j8lAC7i!$zVAO~t<4_=Q&wmH+Mwcv>! zYsLleO|HGo=;A7CbP#+O6riKNN5Z-PM1N^d@Sz2v=FI0km2SVyN1u}^-A@^l%XapP z;O16G+HCF}2JHc#-ccQ{NfIG{xXRRN2}-L`AEUEbfx zSBxshvdo)0E2adE_~*OTu+YdO<26eO{b$sjhmtmS2tK? z>}giXhSiZ;=e(FXP*3zk9~Iu;V<`$$h&B=9kHjYGJaRs`h7tO52_bBjT5WxqKf&Vt zrM&2EYeE007j9VLpWNlIH zatdE64J^T}YIY>@^>^Ch@s;BC|H#o{^*KV>`T$;5j? zzm6ziV2ku8{o~N2W+@M*0tfo8S$1F(Zxvbna0pJ&xkK5xD7| zu=eAYC|~*!&n@>DQnm{ksH6|C-oDEoyu)=nT=j7Kp0Yy>!b7NJwKF@i z#s)Gs+KZsogI;I%kbGnrcsB84#BJ)_r`IG&-D~FOmlK~)kC~)qWvQeMiG|{SM4?_X zPb^D47_n_TRdkGs+g5QGRUOkUH*+=j@g>>VeOJ(XsUt%UGkJW9DNER@GF+p%2JMyL z*7dupg(_zL)^_61&I*5v25_c}7hf~MFETOtg9IitNMeEk=42*tu@3sKGr@V#l8HCD0NviSdaGFh|nD#8^XU2{}b2 zZ*hgw66okiGKJ8>W%b3vX$hGm^>u`TXocv?7}D0e(90V|gj za{?_)Un7YYrYyDsVGP~SK?p;n(E9&F#2GnoateyqdxO=_n(~VGL9uK<$qBf)7%4-@;TJD4 zgp(6eGIDd#2b05S7$V6DshGLhX@baMjFi#j1dQC=^a12BD*7mBC>uB1g+Ou`-Gzuh zy*ST$Q8P!9!@2&9=HiAXxj-3A4yR>jj)!n!xDZcHKy`r$!UA@QBZ(Zw!jKOk2;ERX z7eJ9h4m(5sZz9f!@&9iKL<2h+ds_eqNg*{w7H#1Flg~yP5^4k>?LTSz=T+LjZpP!& zfY$$f^jQnC_g}ZC|LbP_e~S74V$A<<{{I~Kf5w5$e+_B#f1$wX$bUT^3H~L3bZ7zo ziw59yIz@DnY-;Uk}oPBZ~nzPyq!ZQN)z&z2!jsiQux|q-rP!>vFh_8l?8Mv_tRsM z8l#Mcd}A8NHrGjQ-p7XI>wV5DkG2_zj}74M_7Bx*Z+&)k|J7Q;);FL1WckWj8Jvsd zdV>qzM=30(M7(jxcm12<)PlS3LUM+guHBfFn3#Oo=Fi&??hJ-Gdtn*79vjllRh~Y& zAa?Iv$wVyt_~8YwULPX{lJ8#Aey5K-9mnG)S#oZ)zw~vQZW?@hzJghVNN6vyn|7>g zbY^9n)AViYzCshrFzlVI41I|RNnk1O!JL!U{XE3U&?TJ{ql>U~+x6vz9L6cse59}1 z+a(gPR>-;9onhAfV$V_cJRw?_ZBvJP;@-Q~p8J^5`8?oStsMavDU2A)Yg_kiieT62dHP3% zzsO0!vsPg&Z8!&Cr*oAuM>_s9@&VmJL}8Iqwj&Z4)Q({+1SL1<)9x@nw!AnOC~^+n zn93c`e~G<7FYkcrIM3gY0>mV>BbFREkh;^Gua_{?;4!DvY>mLg*2ec{s-}btQOBFR zTBCuBIORlkUSjKM%`R0pfgJ*;jwQeM^^RKp$Vby8=;rhA>m1StKFv*3Xiia17^n2X zb)j{!b}D4PCn5g@OpF_|_k9qut`L}`C#>)>p9Afda!pIuYTtc)M9IZtG7m3wF^Sfz4+0XP9Q*fz$G z?>=TfbU)@MOpghsig=}u_z|w*A5`ZHtUN24)Tl6`WzxH#+_4Zrfjh`bi#&cqkgon) z%vyh)B3@P&ug?!MzHp$M~oILcbU6dXV8McZT@_dBed=M~%{K<|stf4ZN?v6xM*6)J4ApMU)*ju-`w zHQ2`(Y|m@{U>UncI~|mQqdKO6rT4}(^^~hjTTT?Kq>o-t!2wP}aux8A3U_aP`v+)h z3PL}>G-h$P8zJQcYnrBNLXS;$P-{sZ5;RQ*q4SzKfE)|FZ77O8Nmlj%+eiWS98eS} z;N-Xc?uP^Ur-#cVAfQSv-JpOCw|xa+VhpN3r+7SFoc}Q_R$R*Lb+WML)9?W}J*^m> zsoEJH8K6gd=S{8d&gf9?!_aeVoDy;-4K*n_q?Hx$=&0SZX`N%oEG!vtr94R>?%heN zkclcVcx^ws^3(2)7vbts0x+xL0Oc)pqZL=vt<(lg9n73QiGcFQ>B8CkvCAY68?L5* zU(Uuntl3P5uz!DjjNt8FiDaz@nOV8q>C~5c1SlVUl79jtOTq%=Zl`w>2_+=#DqA>G z7~88Mg1f)O0Y=BDM6a6o_z3KiSlyyRGM{#z5FpDj9Gp=#{S+E#j}Fsgg0Dj9i9kKIb6Wz+9tb~RI)$a5xRDw;(5D;pOrVlY z4s*N6L4zsLN5O^lsW~Dv?jxLT9S1Yp%mWm^9_ zQs(jaB#Z$0phY>cdkF$w6eQiqE?fblZe$QvtxkV#{jc;+MX8XfSI zfPxSp9i<3NB!HkKr4^08Q4?=84I(3U1j=d8lB_V@Lm==j%OVponugG zLa>X!rU$#aY-a6GrQxMVRA4vfi<)Jax;|iFbgxzg zeW-Um2Q{jIx3lb@L~xbvlCMgf-@o8 z#OV{FM-qtUP=E=jTjG+>f$R4P0bmN`M%q6cs{!dSXx~g{h6z|!g7Nt`07>5Di)`JG z(}xX1EySuvwVp)Nr^bE&jhnhqYyvBWPElg@#-pP9ohn4o?M>)586(6A`taM`s4U)V z(96fg@8H?g)cgR5n!PlF118a@B^r>1D91I#4&L0kY!%w|_XHaS(FGuc~NRk_%K z^Uu69UImnemnR1RK_!Fi;(^G*dGAby8M>{TVKbn^u!(}#paMI z;WTyfV14Of>flplQd{tB=z*Qh$TVg*{+cDb%PQvhM0Q6MvjlQOz#4>8=!?s-r;D9=1 zWD4~(aVlqZf@SCh3<-c77su)VLu+h4p>#yT5+Q_M$d;QVKGqWVJb_cRF)Sh&!p*2y z7*o(g)Se35VZ8qTAAez>?(eS3Qt zH_){Uz6rkD+RV_|+T3vwE7RKH&VYkH8^?zTI_bjgH}wilX$JMXiam+9@1?OHMio6^ ziLowwZ+TbnCyMGL0OdE85)U}0%5sTx(eE?t{dXqtWw{4tHM2)mk>6SadQ-y|7Cx~f zi9ouwi?u4dhWj{XytLuSv|*|DC|mm${`X8LKYf*{G_$lLQ{)W>1nzn+j;=bS$!Z=tF#v*f36{1g) zF@d6%;g*&{T&hIC?0PKPvC-%K(*c-A-LEJ`S^D<77^7p%upo+fMOMoA{;AW zWp;#z6glHUHmP$cf`h*tfj#gN>6ZTSYG|D^TA%!PEP)3R4BBjrE$lwhxZ7R4wSpUJ zJGNP=;_HD;qmWXjogO%E8-7T zOvp78mH2?BSwvcu^O@Ef1|SPb4bBh}MeCW1_>EsOn}aqfWE`>ISfR1`IWFjaSUp`x zdD>1FKtd@Fq=v3AGWe9-Rog$UWwVu08}D_Bm6ZUzE(QA+@6~T& z-%JhK#J)jpoTJD9Nu`pRI$W17 zffDZbeKGK{fdkk7>-o%aWI!0Y@&*JUc}g zH+!tcT(Ee!n10OV3|(J08?nY%Dk8Z?>Y*&8QUWuHzRj%Dy)=vJZw+SZxNk$*(XTm{ zI83y;?!(e^i1GDzWfOVva}Lwv^yI2Tf?TXhI>$vu)~`gu#9Ptd@1w~8F}^J#+dYoz z!<#v_+@zzL@<(6Ad6KOu`)5+{&eC{DRid^e@-Wsu=EkQ(pK@CkOZAxOG%$?6Z=)#! z&ODE_9lXUdnL~vU{2RR=>V@^>^_Pp|G@plx?5Ec;tRXYOLDY-XEknW>AU3L=Ho>9~Dd?w2Mst+e{@^HMkaYv;$iF~f@ z3$&}4Cvr3Gt00q3>z@C3SF~r^v&#Vo#>;t>JiDfoGOQ?UPlUQWRbhdra(Y3hI9F4O zEWD|yYvO2vb3=J^XrsQ?SHW5-m)vUT;Dv_?HPfpDMnWJwbGTHRpLcfxn+bU<>EUj~5n7f)p$`X?%j*x~>Ud^}0;7o?I z^)orkGsv{u^0tvOZ>*5jTvfE>t9JC#<8;rFAn?oMA(g#DZk<46K#`@aV9tR z5K~(9U!FDbgWZ?dft*=$&AWTwO4%a(i(fS*6VE_Pc4Yh%HFCHxzi{<1AM^#dlhw5N zLg%_-W#TVaZ!TRz=V+Mly5r>Y=i@INPBz{P?cu#?x0*LFi;vHP-8^r7ACyh4*P2z< zf}W(e(4NHa`5Woibzl@0(D~rRD%#Iz8|-qiNM?YeFLzeeoX9I`K_l?SvgK=ePo|zY z497mZBAYyqnjuaxg#@n)H1XeB`-aOMsQDQt|33dk967S+OSGPXmTa^v>oMIMH@#A6Ev zZ&Y9edU}TC)$b!r{cg@!FP+n_Xn;f4&5Dd>#7R&14lxSKk(`ISXWgh{C2TzSzLZJ^ zccXivRqw7dOHn_iJ8>+y!%xP2A}SHfIOdUPv<>7%?D{vXWMrR#D;zG(MDbF(Z-Ri`V%4$@>7az65Bhl z$637awf=^(!R3uQ&%^OHg#LEHr9?a-2-%k;^Iay<*}Ktf@=(mlH!~dvJUe(QCG?s* zZSfeF!FMBYpnAeWv=ioMVn_E)EpPrtbNvMx)wm)oYT!683l(?;sgh|{{^{Q4kZJzC z=*8oC3`;wUEv}AdZLR1?lL$bq663swLrYHd5&o{3hDB`Fi#w^ZCFw_+0NuyS)U`qN zHNsz()Qaw}+QsR#8%cOiYXL{XvvID!%F#EJYg_G;)R-M{Uj02NI0(Ga+{b%U=8yyi z?#U zToDKD2Xb;pzpm{+$icFEjJ3Vo$Murny%lsvv|#APpc~C)0>qxV&u(RM{+i|>1XdcE z)5e+{L%gJn)dkI2sF5kQAE!K!*YB1&orB*Hi|`K&K(U?%fF`tug5=e?8_jhC4i?Nr z=Sc8S1E~i355`|PUu+i8QmkRqhH$9EP)1`>dO#ZCpK&kRX_M;Ts1m8c*7mLvAz%bJ#utRRPlIcA}6 zrztINZ12r@Sv1Q{nTl>Al7~K9FH5SQ@7%Q7yu^A6 z`Ahu=6u$Vqy_FS`>A*j*Aiw&+ITs;)Wc^PM=6@hlgZ~CpATg%z4>mBM5@7lV7$l91 zW)X?Q3;*EC$b9;@st_ao4PU*V)9zD-QoG|#MgO4G62^S$Y>9{L%xh=(ru`Y=gV;n_ ztUUZP>P*T=tIhL&9{!N3nkIQT=Zx2wy{0JLiyESevOPl-n!O)S;j7sz*fN1v$ovO9 zL=q_p9#S(rG^WOP=k#yB9XB?MV45efvlj^ve|QE_1_|^`Gp9-Zydd_T?)BQY-3<7H z&>y;#&(<*7*LJf`x_jr~`#&U*f|f9r)AhP%*Lt5r{r_fam;1n>Q$raf0>LRE>x3zx zB?r`sm88^%ypa+5t1cB+zaw5-WG$~F@yIxQ-8Bu^D6;<4&%OEggJIVEcme!{?yNZCRlKQ+H$pCf((>LBxiy-AX$Ly z<^J6(2C}4fccLl^AGXb^jbjHqTYqPle1FR6>3xZV!dEqJw?kojKlGjcx5%u#pY3VM zVb}pZ7^DHj?jP!KBKyOoN)*1X11Wggjgi6OgWlGBygoT>*&S4@&PO46aQb#y2LvU&%*EBlHVp zpso^i_xU{2n?2?y+<$j1Cs{jxmqh4K`m=n#icG9I*_eOo%Y3>>#4)Mx zJ(Z;KPNvydDlH7n6s?F2mONCZb!c#Vl8|rlt{-P=a_q9>GQZ$EPOMA{KB-Q|D6CGC z^nb(Q>@S|O#0$J|mwT%YkdUisQden1f-s1sZur)W@zcaue;b3WD*@k~yy0f5MA1p7 zBUC~P>Zw2)3n7^Kq2VU&wkXWqyR%T}3vpW05i38q&4y>jYOIa8lETn_Pz+(@wFk~< zd1zV~I(~Cle@NS27jDM#x&ZO)dH5_Pk1w3c3hS)l*^!jf9>459o z{f=yYtJW}n60b<6Q)?5k8tXee=Ss>8U&yd~ujp=#5=)boJ{JkFf?Qw>zHM)7GJj|g zWujpYRnVz5-7fJIuyG#VS5zA&LF;ww?9~|JdkSnJ?G@dTI(~F0Y{Drz&Gyl5T`1xts2zse9cm0 z!;-%r`@ZCb!5;64=?MwfRO}}Tyn`jOU|kMz(+8%zk0A&9)5|GeY;Cnq*Jzh-&s+{X zEH>pF^=&*9MMN()d7~V5;JMedCXfq@7EswWJg`(&BzHpPtJHV02|Z|K;rK zDoqKA!?ajEqP8+?m-M5DBk#NY-gV-RdZtpDAs<)uB<^q`cK$@4TOW>df0&8lwGY|r z7%GgQ5KyQ*SmjTuf4~^X4$xM%9f;n8sY;J0tx;PM<#O}5G+~NS{j6BnA2atuyR8H% zD`LPdNwfR@wi*B-KTKz;Sb4q|;NwygLN{0&4u#{ZvULfeoj@bPpXFtE^)tIgBPckr z8gsDUJ9aYqiwXwJV(T3T5|5`*xtD$Qx4(r3L70c2WPhRARj44-p@Rm~<)IL}vVmXf zh<~6}cs7km{06==seO3@C$Psua<}D6u!ZrRvoO!=BOIc^`b(HP&Z7|zIavZnmjGx1 zf?MI@JJ>WI&xak*QO-&16v_dG1B@NiM^g{FFfx;aG#G_VR2PlnSyZBh3c9ZEEEU)e zL_HV4VW)haDd9LNaPub@0QgLUfV}_rC+`k@=DKIaWFJ1*^2Vc1`dt;Ce_Qf`fGYl~ zET8gC-JnuGg`z2+X)soH>Qf@vu2kZ;p&tN!_RcX=`roH9-!JHOE{Q8W@v%7(sFE z`nW{nZe?K3AgvHcG>>J`<5Ctn>vPRnutN?iT4arfJ~9c%$sMf2 z>Mv_jCWi!5b$cp9G8$dV`*mAkyE4?}GA zLyPLuVIiKrGwTBCgLcbauH&@UXsUbR&qHs#Wm>AMWdGR9Q8jWz5k+)Rf?&_CN*q1R*y!z!4)ctUlj6f)`|4A zb9`tv?SQOsGkPB-p##GQOZe@}c=Zk)Skm{mUwRj8|0thpXRv-L4?f|tuRw+Ha{^pk zPE@;2`&};>#0kBzf}zMytLqp$hp}iIGU}L13>?Y^#H6ah`v<0OKQMfj9OrqxY0Ma0dS@I82T==+AG=F7?m;GRwOlU+ z6wnTqu_XUY2l_NpQy(yWT_7d|Ojhlhacg`>?yEJBRa^ZYN+Srdj)gysJw3=j%Zs?S zrCxIBO6w7MC_aR5h@1aA48Hk7&hy|t=SY^reN_a4O{EL-+?9oZ$n;JiQXlxz zr+3uhPm|AJ^py-$yqF$|$=~-rs@ZmdL!s+OXAxN42XSd>mk^Ac&~7jTMHnic^K&BH zd?_JjysyiKkJ7_?C~>zTD-IZ2;dhr_v`=x^FS#OpKb^)Ol|VBs&fIPy!QdZz3w8gH z)}i={kw)C`6?Dh-HH39&m`lwQoBV>5YuxLL!FZ|1UsB}s6jY}kDJYX=%V}s0Ek3I< zk=Lez8>;1(JgN?1X@3bNI(C|sJRI@$4Ml7?2n$d<9)csq^&5P$iVDSrJj%1jywn8+>LPdgLR z*qv?xGfwU%KNnc~D{T%aYK$&3_nljH262Q?;lewRdLCTQ@@%>c<@fYi3V#wHpY1b# zf_6Zr5PGm63HzDZG*v4I`r3O~Rs0DM+Y zOK?U&#>vd4GZJd_sUt9BUlI995baK)MFeeZU2JF*u{M2^5mp0ed+v)n4@_j+7#V&2 zu>f!$yJ7LIIbf9p0wjYsRdD-Nq-&yx(`SF43(||Yn;Vm(7sMvVh!J|av?r8^5RXef_P$R?y+tB(`=yqm^bXi zNDeIis>64^nuDtOGFS3*pJ=hV9D#J64SbYP#2l0|wxlHQI}bp^QvVJW-dYAc`EmP) z1OU6~0ujGjW7*BRB+XAcuTCy&!;!mpcBXbjzS&{M7w*3gC5A&e>%60)Uu4KZP3Sz1 zW?z3U%m-YtSbQ%fN6MsIR&SofzQNozVP3HN`EY7DenJZ{v9Z+lzCnUTzbw?A*hyzz zg!lKs!PV8_W68{=FKoiXz;)%21GPJzG3)hE4|c{%T+NANH9Q?s5vLcV4b4SqVU8CEG|xd#oXcieZxJ=L|Iu>6GX9<=@1^$ebmp-?Y1fP8k^(a zK2Mgus`7+XM|TIe6|i6$Kf+!xpkc4_k`2%Iwd zOEOX_$%xT&(paI04EqA%@*!I(O*_+1LhBK{McZ%SO|eP1yx8SPl;YKjq8x&zE-tfQ zd?Nh7$fV+`t4kdT-s*s^>A@c6X4c``IC>yFwuR6A!E`!SNyaH$a+Onv>NH`_4>)Z# z!;|jMOK(aWr+PoyCUQno1FUPYW)%g_ zm=W~6ME7qk?|YNK7hwUO*qc+YOEL;sPU{@Vrd153A|~{2jL!*>q9<8SW^oHqJ*(?K z5vCPMA|?;7R9oiC79S#ymp+Q{0MAE_2+0%dPe>JU#a#rq54yVjDx801Kr%i43{Ov5 zI1y;=D|8S%I(P!@qHw@_MCAa!UMadb_Bxx8z2ZJKx{&XPajxh7_SBdsSu?vLV4PM zJ|7f5IUYQXDm)5$P9@Yc^fNoHv5WC9kS{ibkW<>V?PZh0WwPVFc7uye-Tv1fth+Tr0$(39 zWoY#Yj=F=8NbkBxV(4>ZW-qoeQ9rN?n!wwmzf0Zrx}23@oDW46ogP2S$%8^ClWS2> zk(}Jkr@k<=geyEemGtK;?qB%0Mneod-TO4vCKc{e>ar_(zgR{KaDz{xl*Ar3Pv#;sN;jl+YeR^#Z4w`m7vNJhljdaKN+g#w4!OO$%yBqV&Ul6Z&QpU+O5w` z*;j49{P4Mb3`dGMs+24GaA_vzGJUYqvQtmew39!$#ZTH~Hr|}((JMgt;h^a_-Gt+J zxHG?fTUoC$cKHzR_$*S^rp#{yI_UrX{|4q_V$g}-`5$XK0VmXr^G{8ui>K!<)G74u zn$A{zbz9ndbU24ejVm@uA2hly4+b4BUUAC`(b8JI9df=oC8Utf`;?$cxP|UQ zF3Ug=C+Yj7ZW8ZBMI=F ztb^}gm4)vz%}ukIaWNT05}XrNiapB39G)~@RG7w)Y%+Pm8G4_DFSNuo95i3vw>EKC z716gGzS)H62PkrSGZ~v9@4{de!jF%d3}n)u@%oEq6OeIT)!>7{<_PWd1WPBazXS=H z*e#dka}^u9Ot(=|3{~2``G&gKA0|kx+)M5ieRX+w{yC?{!HB7@pf2+Stqf))JO{yl zGj_$Ec682tM)as|%DVkUUr_!nww)jwOEssbgTa<0Bu)wIYn>;#Rx=KHo{eKY5lfa{qh*~}MicPMk83R^zZ z3E7V>aabF4`Y;u$|KaOiubdLykcqcQ3f4R0r_W`&*6U18&M%&}=gd1_ZGFh4lKaA{ zDJUUJ<-j`r)yog7kJ|^9oDFS+UIjlXV)1DlZ-D1mw|}n{dZct$j>**9vYC$mK}0YP$JsTGgN5w6z-MMFbVTQ{(jYqu_J zp3FYfowHWu8Rt77IkC?%0WY?WzaX~$wbYS(q<6j=Ye&hW6B={r((G);VbRASREg%s zorB+(@#(*f8x|W1tF4t>shK^fGvT6tMP77n@z@!PYYV=BmaNWb*n3@nGYuQJJp1|D zAYn88Mc37$9rFbJ3+OpbPw4}C+qMIjB`)Fh0I~ISa~ecm#Zxnz<8tYB*t58!ZLlRy z@sf3jwJ=t*EgF z4ye2=zJvy)9gB~Gz-L)qSQ?3@;zX<4JQ>#bi@o5b{deM%6mQ%I~7&#HEqxRAw$VBzBnQ zOyymcc%(2&>fmJ}U%fa;Vq~Na+PL_ANSe!Z#xkT@>i2A{I6k zoe>l`eUyzrvW}ylx=%dNqedT5QB$!Sae+jRmQ#|DD3;=Q3WIXsDNBcDs)0E)GD2aW zBKl1hm}p^5H~}m~SE-nqL{x51&BBGli5qNjk0e^TB#+46M!_D382|Ns_IE@$05@4J ziRBa{Qy9K?-vcdTLP0frAWZ(u= z!49^2K1cM}2R;;>fEt#D@0c*;_f2WMVR|0Qoy7ChRleKiRrg+7s6YcGZ?(vT!OXS@ z70*af1Ro82W5_?6IIwtn>4e374l82S^jZ4VgJXPb3k4J{=bf%ih6w}Jpn;L(_RG?Z zCh{@vb6#GR9SiOf&!Pzuwz zp`D}aZOp-_X zFSaE+VdD$KznN-U$y(DUqu=&C2}o{`TCkP{3}8^*+G)sJe)IkH^aSYta*eyM-Q|mk zVH~*oWeVkGBSTYLF18oyogdx4d*_?;G$foLXYqmH9P~xekwFA83a_V{eh}uUozKaT_$SESnW@+3Y;J z#ljPRgOnPC&@c;rx=$KuoXiyz0+4U}zBQW;XK*Wyozio%HArX(!QkK?oqxOAz9xpN&=`A!^x%_oOb zw|3zx?d^pQ{A4U$x=HSuR``p45jr1AO&1pCX3y8jDluAB(`9?LRV_OaaQBr4*% z@D=J4i*$~ul*$O6IW^sU83hX#o%JfAe9KZ7=Vvb!Tof}4T5<|T3L6MA2aZ(C@7X9% zyAS7}zKhMNU3?pF^{I?viYciX9`dFaf2?RvkGVVHzj3-D={6Y@(y(T8;jcOM%Ix%! zEpr^BA;G;@L@l0IrcVM5LI5x$`qF6wyZ0rtG<+WM7q>jEp1X06OU79(zkUa)#&rYGM25Bn-J5$_ zZyLqdo;nbH0qulha&0u$`q_f<5w=HF&_6y;&CoCMl&D(Q!;BL=Z)veJN%a+Bi6`ZK zsGjcE`o(QuFteXw6L%{+JzR0-gUZWy(pSnPq|%jnG}!Kn z&*^Gq zFVR@HJwF+h^S;=(ZzdgupQxN6)9R)a9DB<2gT{=|OvAMC7UjgdZN&oEZ9L%G7J>0Y8q%X1+yTIuP$?f`wd&dVnz5!Af5Vh>Fv`XCQA<7G#vGOL46? zt-G^7p5o$Dv5-$}e+?!&Q_NU<_PSqApyqum$1b+7#?~rMm@4ljsO_$;rmkyo`gGbi?^Enx4!3Q)oQ$L?xIpP^E8HC}g!lBF8%n92B*{Tm zZ{0r`rqc(xvJ$snlaFbg$G7r5k{7+%Kum@|y#WuR>63UgAyc2YV81C+I2P}Zp zQJj>N&Oz`PlZdaU*&yO3MbZ-l6&WXUGfbcmPwt9L@wKK z=H9*Xb0R=Fvm_{Is%!YXC|gon+TAEI2Q`ta(e$wBW0qUEK%x5{rM9+PZy$~vvU)$~ zdtW{1-^M}6xog$mlVPaiXX<^OB+{n2U-etwgw?4sP`Q`9y;@MfGOA@nCggnN_9};B z;4lE(RNP?ELf~=yM2EXG?LJ$i5eQN=^>oaz6mISbW ziKV!FT=f3JXS=33?2Z4VzD#vC(>>LxBzlH~68KtekaA9U-%4Nd72o><-WRZg<@7`ZBg_nZ_SaVPE+p_vfwM^M0-!QaVr}i2r#G2(_;q+ z+8__S5ia7{Vx?YGmq==yDZ`EQ6g`{gPHp8Zh027ldo@AKnx>7Z9n}KKB_O z?L1o*P=7l0!05DNE{;<`c*x~Q098Ou_=-ijdxED)zx9CExWntW!*m5HAcZv^mwl&^ z^bmq`ZVk8c+}{i$EHnF6$4Ug}^s}G)(dC`lD+6GS%ptB)JYII6vYob!@i_d9tuiV6 zojHCfztQ(@{sHNxy%NXWc^`$nh85W4ao__$il!1k?(7}A#?io(8V)t_{MU}wuk7q4 zRd%N2zLXSheEW$6`RN(VJEQT`Fzr=Gj9aZ{^SBZ9%b`^TS?&|<7)lJIgF5~3X+-dV#$7NN^a@skW;Pcj*ETTpRJ zaymx|VSx|^H>=w`5Gb$w1tr7-ru8l94U72oa3XNe+|et>du9G@PKU^Od2FMb+?U5t zhVy-!Qqr@Lsyf%Q9?bCW%4@2^XIjufF4?Nz`Fkk5_db*d`^%HBk#RLWvWF&XsJ3Gv z>F1PXEL1i@(=dWv-iT9KIK)qk*dCrhWi&#q(oXBOr%m%4P)>)d6|%f<9v%5FqxrrQ1OdOnC8tbF2LEZx#cq< ztSvORL1mg!(cCD=r*wvFmBX#qGuHs}<`(_YNpDJb=a$%w>`Q?2!y)7c=N>3}Yp$kp zRF+iVzb3u5TpY2Hmc&t+q2sl0h%)W`a2V{@QCY z@$|zXR3de;XsRspl)2#PHJO|pheruv(KEC!{Z$rapST>@uJ;*A1(uE6onNzZ*86aH zta;bzC=`Zd^6{Mr(&CfLG(BGR9c2VCwASp>Ns_bSi*D^ZX5G~=$j;(=bvXR}d<#?;ws<3j+en9-VsRF-cF}=W zlE-$T5p@>0P{$9yg_Smjpxj%Avip)p88wNMdeO8n_8}5*(plN#)mN;3{1_?@)Zrz1 zX|-AA@oJ8 zv5aNb`+x4y^Lw7>eXsxfzSq^2d(M59@A-U}^F8N$&$%nA*aQiw+P87{vMDFG@nd6u z!8CP}(xyf<1kF9ellk%2wt)@w)BAgA$r=U~Ab_xi+lB?tv|cOcJ1T)URl1lbUUnP; zafNL4Li>KkGfAbZJ@|&(T2PY;{OAbx7T0VpDAAu~(lfW2t55Y|^wFJo{unrIW<>zC zvY0$~MSg>e+d1f|Ka*ruI5->?td@E7$61utv*60teW-FQ-i{Bh$>Q@0eF0=Mj-fs_ zz4hV;ZRn1_I zJGY@~ev$e&(hFXNhXscN55>f7)z3e?dpf#w+aB!LJR4T>otvf3D(TBy!#d)K`j+zp&#pA(g7W?5U54ge&OlRgIAZ@z7>=CkXkohgJcPIYC2u+x0Wt6v8a5^Kv=V|m>Aub-tsTKheSjH*G z^X~ROeA33P==jzUC}?gOnA0RS(ebTl;l{i$Rlk4{X(##Y%@XbfEaPVd8*HTZYC4UV z#s*kWF21k`o%q8=@Q0MAsWDK&B-ER~%iP?obj$jY^@cKudfIZT<6?aB+u z!Oc1-(~UuK8LVgR8!GuxWucE;w7nv%YD!AJo89X&_$DT?48`3OS@y78BJin9KD1Wh z`uHu?E5#34ACONGPJ;&K^V$_ki{H5~W)eHr;kVZtnDksl{71Qo#n7|cXq1NZ9TrHO0xB@RD*@~ z?>KUNomz#p(jj%Bw16GA`MX#-Yq^{KP4~Jq*KfZi{3YG^b?bF8chBXo4__VqQ&BZ- z%fr6)SH6UnKOUIc@qNp=ZH^IPgP%Tk_e^JguqC9M`cKS$3Udiu^7=QHv1$3gi&A*k&el{z#uU@_SCExt`Qd0cLH%aIib z8Y*;Bi(>zH6~Enkx}AEl+%AKjd#(EB&2$jYH}>THq`h=)_;~cQT|Ir(dXfPUB;e(S z9ynvTPwIQ7cr1PKmvH)eE-1tcLnMc`RX;P%In?DfM|M*Id-}fDA}{H1-+g=M?ZpF9 zd$nnN#cM<$k?=W^=VJ_O_PR9KG{@`}$r9VBIsP21-_UzMonO>X#m(lcFwbsO&j8Cg z!Tvhvb==K-SM-J2C+Tqlc zpw|it>g!4sdYj5!vmruV3cSABGfZ#^EdCi32^V_fmv_E@alUY zdF)3O&Yk7{bl0-n91ztjpd*QJ7b7NPBw$=c+cOK-90~@S1|HO*vzNDR9PG^MMm{n9T+{OA&1!$Rgkd zpb+-fe(~BmKB)yj`hi>prt$hq+clAH=Yw`@ml-nK4}Sq<6n?l6lsqKq%f$nAtbiuC z4{P-OeAS7wRsNk2EW)GZ-}w-XpGVjbRAw`^H5cl;avTpqk~q*uk$*hkH%BbUE2xm- zwQ}0+fo3{rD%-r9zcI+|r(L;Gl#B?XT|Qm5$x$PoJx#g10fG{sK$XiG42Qw5u02s6 zn3z{)7J0k+d7yS@djM~;@svJ+u76n&WK2j!-F)Wu%jHf2gm)+%B|I0>+Qq>{NlgTW zmF``LI6a6D!T^de&>QXqj8jhun`oH3NcpM($VAhVq-|yflBG?IN^KX0>W!OK zpX|zRK?r4eDkNBZ2xObs-V8SlyUm4OQ`mz`zsw?-pPe)xC+nFZqza$keRh(R79p&k z(!v#gPyrnaNgVDz(z^U+@TAJfpH3z_ic;hr$Kn#Eo@9?A|E_rxFKX;?YqYZkHsOJLdeIT0U zTw1LIic5&gL1Wcn@Xw(;@0(V_Q-Q2NQoVUmz-VLyR=vRA=C#SVQEiwrxc3MQUd2UE z=Uc1=N8JuZutRsqn6u9)9ZXPb`up#B z+xl|zyS`zPO}PGqxbXE|#XhWn@e$a(p9s*wZg`y1p0nKMkaWmhJUTvm8>m3`oYM#C zGoNKC*&9Iv_kMK+PX6rdY%u3TVdWU0(lMZQkxBUzw$Hf*pn=yl>}OSX9Y@}_g3M!g z1)cYXKmPeaTAYg?I?MvOsqo9PS8he~uA8`eGac9V+D{aB#c_}*-P>Jz!>`yeG^Q26 zky%J1TaaW~&NMwca9sm5b|dMm){#B4(N#PG&;b(7Wj7xPhywAiwXXQm6JRO04v>!r zCv=qzV&LwkMl_uw~>mewGI^E1^~Nq_T2^q zrNx|X#4WBoaRJ+SQ14OhkW$umu8VZv1+tFN$^jtqSbmWU6SZ})N4|QC;;-d^WkB{v z!(q9Ak&4dFYF>zUIleAZ-8^GtIr7Y&fNtdNe3;v<6pKuMCZeePkbu3ty<2hKFXnp$ z1t?7rb$a53WVlq!tB1s+osF_7sj{-VVO3s^Y~cKdTxv({iqv%vquBbLM`6p4z@6fI z&R$J*0oN@+Y8$!U`u(UZs`C0GiXV{3SkE$}*QWaWfkqpzwB&opw79Q}Cdxx0$}UWt z9BVj`90^JdUAGPJr!s$4CX+Tn=}(9^X;kYYc3kB97quKbb) z`L7(akwZP!i(=By&mW1IYo9{(T|5|2d~j@}`U-JOxpYt921SqcztdIFnM4j88tDWUGVnOXRl6`-5sbu1J`l2C;nD@^i`u zb{io*mCS_F>%qWWnsJ6W=qCU~tdXygfGYb5)caNu%}5&th&J+xu)0xCdXhiVKx1g% z1Yz{nFm5#{v%KwmKI?3S75Ox1j~X)GwQ~CwFut;1H{;U|0mD>(Sd&JiNP(BW=aVf; zrx9)fg`dlmfpyNFMb-;095-6tx(jrzuP&EOC z^*pvSfDW(IM@S0M+#;xInDy)SRL76U@=*U6A{R{sNvM`Ih!F?zbLb8^t|~A)XGDtk zbL-=VI7wp*JbfK@rEcAq!38bpi+?=6^a0-Uu&_l1=o%DYpX2jh`3TULhj%qlN=OH& zh84>Jir`%%W#90G6G`om`>r)PJW6||W)(nc8QN}l7&kaqj|P+(&}*&8K#-vIA@Bb} z;+jDw#PK|$VlbDOpf6=*TR!yO22NYH1V929rb&(tu1|E-KPDmzi6Wgt^M*tafbSLA z)c!j~X_f)6_*z?|yz^HSUo9mAt+IKt`|MO~7ue=g%mPtY!1Ul%T)ycZl3A<~tPZdH zp=BKhnL2A;UjRb}lvT6z?U8#JgE7H_U7}*5BuTTwKoIbzk=gj+!0oOe3v^|_jh7xI z!2m=;4j8eIlCsct>I;AiKEFpj`tvLm*0a_I??Sxnc}j9iAI!8u=u+#x83g%?E&|s# z1?VLpM;54B79xKYuR$7x2V2{NLrMk3a>w181R(!gmH`KUq_cF~5;8OO%5Z|y_4YDH}ouSCD=zt__b}>{_ zdN>zM_o1sc#gNXjSD`stjYfqTL@kikxZ`B2q#hXQ#O|qqtA=OhUVuV)q07#iqNmhQ zO3KPdrE0^hS8703tZi6Z+Nx3684JlNsLU+yMl#o%$4>r;_iL4f5HkauP|}&5uc$YW zS7Ud!^!+$f>h{%MAC*q$DI4&2&*E4pExk>s$nTC6DTe^wgy*{e`A7rdm*@IkuYo$p zNV^`!5s}g6N$PdA?%=GRwNMfaekSN{DVtd8OKyD*E7TvUrvZJ0u%g8EA4}S<0b2v% zzmYps?~+vu*%(;%6K_y=>+tneG}K;u9SsxzN{LlkobMv`?E@Jm(6iFrmVniLF@HDWdfz7 z>`A>=G`CI{IB1Xq@NRlOK68NN)ldfZdB<2t3tXn-%J|JzD7%3AjR+OgU&{DrgGir5 z0&s$TAM~N;QIgB%mq9{Ee90OEt8%lu5oX9wxhK157TQ^mDjN}0Uge9W{fHH7dbkPd zLX;ulWu|9C;0qJznYzMWkV8~|qlq|FGPxN)KK}iCWoOkCr(16=BE#`x3qaOxcP?7@f+vS9bPMH&Q9_)muMNLF zJdTJiL@hGj+lk0Fm_0o4gr46)9O8~19<}egKp;{jsq>VXo^naqeivLf!|F!A=Ru0Rl{{L9+|41^PQ|JGYWc1&e|M7_Dr2bFs{U1rjaw_^? zQT}i3{ZCD!Id%S{fB$&=-^vx<+;nny<=c?K)H{=I=}oO}j?!rrOHK3UY_Do@b8y$! zmQl$JRkdzA9MkUR(R#&#{W~Ft%)|NbqfEi6hl1i{O;KE)$6;r((sTCWMEt8`F>6rz z1oLngg2g%5w{w6CAudEYu$iRTLnZsMiVHM)D9@*!mnA7$J`TPm(RZB_@Pj|#!Xy<2 zq?gVS-bEG9!wdd0xA;&vSO4SSkQAr^?ZWsJRw<*>TKs8_WFazJw&3nMTM?wV1`!~< zUabyuiCjVqs3!5}gyR{6&y5E*@E{_O<@=R?V;3>rncY6|9#``97G5!RN7;jRl$~%gc$|H(#3~m8u;yZ}(Whir_K_wwv`7qfqYXpYJ++x`BY!_=r#I|7k zlyVG^EAU=IqQK=F?d*#E62*lFm244&|7ESt!7;JpLfVMm6y~8&+;(pT{xKU9Y-W}e zo;V=I4UMrNbi)zmuD#UJ?km9S0@7h)aPFe6l{&(1eBg3#c1;;^sTe#rf{v{ZnNDJ@ z$ebVlAdBL(nqWJuenJH4GsIg1&2QVmj~xTg31^OD=gno-ZgSwlBBR*)*k};?BFeKX znAXLCWSg~}(3AtFU_TH!kbiy1LRh)RMoe5dmRUIR zQEqlX8I4O+z;mp8l_31()(CIt&y85HD#eX2Iz8k^AzthlC{0%BfxI>NUpnjo5`SXb zu_94-K@%Rx`_e6Yqw`_lT}G)upZ5<$SK)mi(!S`Wl-Y@JHXzXK5%1EbjrgqX@AE ztbRf6#n;wWj#c?HgE-&7qeR;L35E$rvNeX`(!pYH%9zdJKHErPT+v7RAXD*&N=r`J zJ=HtD-&I7gg@9#^hct<|yu)yOJzFHbor^X3Ixr;G@KiflL2jQpSHAe;d@9&=J)fH)mHQTHqpa z;2Y)L^V)~_S9h)j9!%w1{5nqWiHh1g6Sws|tX8`7c6Mw0k>8c;p)5{`JP~D&<0N+Q zgXKBklTl*1RNePJ)OXsxw~yky_@?5OP!G%cp-P0vk19T5&XoOXQG@kxf3W0$jO2a# zgtEfh2$uf-4Aw|DKXU0AguQ~;S+c+Pwen1^iVxvzQtWn?^?^oxXEW%DrLfWou=)em zMp9N^0pgiB?2NfZc)=BTJ25(wt0cg! zjCQ)2E%)KFZ_U-^s{W2#pFB&CmFC3nC36>QfBr(Tnrh0@F^2M~O z+eIq$^*Q8Gr}6iXN{y0SS;abxT~!t>hh#aJqhomre~iGyB=G^2>Z#gn+uGZ7hV-NN zPu&_a0*uAH`;zae7X74#@uNXo-^eo3@2X3?5mW3>T{&@VcA-(c7&!uLQ`0N=732)-)KB7HKFq1is{ zrlMgGi^-I>Hl=N#O%YinJ$+^BRS}y%(Rk2wg8WSH5wVD07tO)UqSFZGizhs)Zf;|? z7Q2>g1p`l&WsJ7(5q|H97dIlgIf6`kDY37>4>eO7XxScwcN~Prbj7=uSq+L_o?kzE zyxnMB7?>^_49V~yZXsm3mMdp3%33EFzZ^bd^l{mZ{+7|u5{7G=Cy$50F1363ewBLc z(%7sdHu%DjdRb()wB^y<^d{Xse4IgQ-d#7~T|g{*DNn{mJEQnRgU6lB4f?5GOyPLb z$F{bgt$o{Tk7I#+g1$o`2jbTQbJ6j14fm#T<-6%xvjzHVuj?=8ZX_*?&#{xnPs590 zxL$p_Nn*4%|IOoAV=+572oFl)H}jx6JsvDan0#GD8Vbk+S%^yHbDu@U9*giGw*TWZ zG+KRgW33DiFA7)`aO@q)%PC$9ii`d^brGu=E&}Fe$neVl8-UyQ&^F6eZ{oM_Sef4R z=$5O^SMg0qZCHz8{&FipE>JmwlSuHP6H4o#93vFyIa0X<9^pC`JV6IlWq5MYec6%g zT*rRw25bOahSGQOca*wz{3iFxpEhrT@n5qn{x~6m)Ly0&gx4^?e~sLh9WS*5=nJQf z)BIrl7J~W&j$@aa(vW`O!OzuxfWH=1VS}xC`)}vjBIuMEW$lmI#_0>%Sn0$^aSl(B zrYmg1M3}wfl`BgR|DuNKCn5rBM+gtOyLHD)UX+AW^tFgYjdS7+`HN!+PJe9OOxUNo>pm*#a zj#&(zgBUiBAGzVYpihJV3l>N|S7QGPmR*HvN zIR`f05LnTq|HA2OtpxdXC$|^9Z_5vZhlB7yw;Tv=tE}(|7ny2B+b9FhZX~h5$D_z| zib3fzeL$u2ba_C04v6&F5EL!U1th{m{b*n2Vd{!+G0i$Hl|3hviXz6|hfePZLP+C5 zWfyM<5++}aP)v^85IWV+5+BS*AS~ALBisO>kazQYPYs>ersh7b5XrqnF(pKy!|6*W_&{sJQ?oWaBs_lW(Ps*l};b>pJ)gQ9Wfdg-mE^+ zx@1izURhCqr#d@NTbv_g;?+FVQw4`kClCsLqPwRhHyI4eAQ zo)0KU8FI}-cye$akqspS zqlk|8G-hNU2dIAGFTd0srAd=cdrr3xAZQkB!>vTR6Ppr(_vL$h6*gOg?!D6vH?S+| zD^s6l9t#%lR@hhqgeN;7eWUhh&OG+jE(k2}7|Ii)w;SOmUJOf|RUxZ{o3sxtU(nAQ zJ1>;6lxH>fC3wm+4OIO~1+Ne}hZV0oo zC_kIvDfeB;CpNFS34#`xUvrCt%ho~($Vp? z*l1~lB7}xYhYaaMYU*`MIN1J8kN6W0o}VA$x-)DCeX7%vnbv3C=Z#>5V~IaQNf?SK z1f0tB8q0BsBo}tC7F9>`w&&KqarbEdesBSb)Q&<$!5%XC?w2xLRMgNV-QaBJ>Xtvs zgjv&B0o-?iM1)C`T`e7@6_$TeXpOIuy=Q4hPu+zo62e8!rLrsiS_a|Zp~!|Bk~JdaH9bEy;t%b|_G!6r$JI*F`lKFen5M0g*3j zLG09OvprdQ>P9FRDg?D{$4W1i+I!|H7vA%V4Y51aw&g&AzUD%xiU%2RLHjXKBnEA51)o*PWjOZ zPHbITgh2gv8wdmvef+~Zz*<28Jwf;!Y$h^0PTu0AFX(+#K|qc84ul-^9*TBm5?+~w zSHrjVMzzR0BMfGm$FMwZaT&-vh7c%}Ns8kB>PHK`gFv~{$5vo=%wzzq>r-mjH!l}S zC)gK^qvBhgkAi(ubujR7S?TDykdK}C7|&KS-aEUOxUG84%^28mpNI%plQ9`x5YvzyzbNrF}^n6)=Vl+J5!TT{ub0`Mvz?X&g z9&>1wh)ok3oX+{`Xb;waa@vFOxDwSu?LNjE5J_z)91}vK_ucn2LqpUFhFM95w~JD5ckWqa@a7-)Nl)PJ&(c)g z!thFWiRR_Py69RlBx2D>6+1M90hjr?_VWTG#t$CXYl(ptvxb&KM@Q0 zw$?t%NE7CIc06DjREZYlSEQCnlr=*nPf*V(H{w&%KMh3r@ym1H7-A<H(9zX#2uNJUBbpZ`&NJ2l9)$;Te7rWRb_be%ZNgods zJrLvoy#bxQD>T1Azkoh_tOA%UgI)TuW;y&wdarw6QR`jV{$&4+_*eqlHcJYSAW!Xh zcIEt+-d7sx$-Qu!?G?-_WGzgQY=<#G9au#=zat(fua? zc?rwMV)VGYeX=j5yVH}IV7t&EpiNZ9H2?Q}XHCnvZpPB4NnyS9W{-uoC`1no#@rvA zak@4xEMMpC+FQ62nvX0U6`e>7)MHO$>Z{2FagMj2Ud4?egayk=y#U)An*wp>`&z8RDZsx5k z(qJ}FE|$ZdQ&;`rsomDM$0|tgjbiKrxAJzGpoHG)r(<%;J@DUX{Daoptd*+x@!_R7 z+f#XyX~{(Y0JGcdH`4Ko#I;~bCK4AAI&11%GDD}HT@3ea4brko;(jnl@}Za(_x2x< zfuww3&DE*Q!@v>-EDO6H2pnK-MD$(7>~0TG7qOkT)h(w7!B`r0GBtYt8YpdEy1D+9 zmqw+@M8+iCSvY`M`F_;e0>JF^8BG5eUO%sC*WJRGXvDGc3;9(0vX;?bEN%-j!oSO4Kx_c#2mW26}wQysbD!s+=)a)lXEA2>lXU{H}^frB|Nm8e{&l$PLKa1ZB28 zTB!5lpYa>jU^eCLUv7#%Ko0^VT8MH{TCXE%L$uZBtxuc`zE@8U#^xlJwcL8&($snX z6bfy;@fWGIQ6;q&KFUawX0ltC!>WlHNn$d^Mxx%pYoMdR z_cOQ)^im>G_x&c=xKA{aVn72of}enU_3Xn#y3-D*g7mDasQ#Q&-9E@U_jjQVzDPLc!;r@Z|XC7$M`~i>02=i#`7T{P!<{cKa!7J z4#Pdw<~Nv_io;d20TNXFRJdRt@((bTa25O1e|&`oZG9aq6v_;Bnzg4fO}i*FytjrnAOO$I6nDraI4) z7eDkann?1bI}Q@078_<51Z!ivb>OB9m!7y@vxXorZF()yC>W6(w;pj~s`sJRm@4%A z!pyx9q^yv>2jP9`(w&=$CB^=^Iei#?6}Fl66L4MuEU`bk2Q6EX>MQVLdKlo|4l5qJ zg_1bKXx@+mf2FQmf+^ZHos0N988pko`$aWZ&MoE6_j$pxVsSd$C~*6XmFUWg{rnx8 zHxB}1C_;;ZEgwx?FSFaq^|(ZA&2_>JIadB<-^m_$YCO=hYrkLC(bvvYYx2svc)%TC zqfP>*6n#D@#qjEDdl-)EZGflzOO(hT%NoC0eB*7qdGfX9!4+o1%>iNDA1&K?wTPcm z_3yGLf+NSP?-wR#KHq?*Ggok_r7xYCP6)$A(i}2LqPW>ecURawcRJ}$t@_9<_Xn~t zB|CseOV+%H!cr}1vrRQfQzY`jU#-cA% z%_mx^Ynx2{;s`MgRfe@rO8CIphm-dQF(l`=gM{Vs(b2(Y%L4qez$F_arwmSNQorU- zA1xn}k3q$qzv(@a6w`GD++jn>tcxQvrF97G2Ibgh02@{)XQ_`bP_tWzD_%7zFH`b( znTm5}bp)mGKBq+D`VMTH@N9mb(i3EOnEL*PN%?kf7%V;H6@tb?lobX11Fn{p zM~2Jp6X21s$hSW_)6qf0#E`svzYhSrffl$V>P>E1$KhyMLRW6`LJUl!c7VMW9&jD0 zoCr|S%50Yp3nqC&k}PmZ8a`3|xn{;nG>$Rp-A5||LpH70Z_;inkK^O4w;HHTCwmQ{ z;_0e6`*UHQ>;`hMtVI?AoSeWeAZMZ#U{ZGUT+gkFb>n=i;FK6$u<_i9y@1BvNoKm3 zY#+wi{$lZQN(}Y%7-kVKhysrYjR6Z}r!ov{-mu6la1aOjGZB0B)Tz4|y)54Ar|tq{ zr3RQKBL&AkXikOQ1u0LF-m~`!l)y!1r|vezC6=X2BaMtYt};lvn*MwVH|Fr7tGNHc zer~0Z@cJVIFunCEGFF{qJazk?ICTB6Wp}!GUkh0M(G;}I$>;0qVogrASB6t-F3HAS zwy%We3C~AQ00&njz*2POp{>;g(GK43mJ64_7;fxhB80oYU2Qr5tU7`z{;9hJ|B+Y@ zzYVT`RuQcJX=g^022`4cf!S5}sk@yOz#Ia=6Yxtz25hgPFK)3G_Lq^-rvj_D-au3W zR4R~StWYf_`M}+qPP*>80|cZ399Bz-Ea`<+Jdjlf4v9)6;wC>@2Zl+2`^D9gSI9q2 zt1+Wjl3*-YoRAY(4;XvObeGYCpe{ z5}0s?UQT{~l0jm?NCTP4a=;f|ZAR#`!@?Or(HrVGZ1MHd5zx`wbzsd#6f7=qq!G)v z%Bz!7aEbmc7{~=CPF50LUEp!f@?1PwiZva5Nn}QW+;C)B15?HOwsiVZhySabZT2SJN~{&l@RsE zO$Xt|G*1`b#fDrxMvM69waoX0Vqn|@4xbGeAWZ%I+~i<&w<|~W4k#-e0O!f{6~fmm!r)d#lDi~##J|Ix4I_PMaA7ch$JFLuz|_X{W!KxHTG)@y!#*Na`!dN)nMCd|3hFe9GEi$(KCRlq1VV4 zNA)*I0dB0e2r4lU1v7g0y|B9X2?hp$6>?lrfcXUzYl0}$-K+o5k|%)(W7qt8L)Ei0 z7F_{5cpL#1ak%_jUuM|gqG+gh2zYCs7%1R4fIcNm{!=^f5QO*kyU;heiCZZCj`d+6 z3{5@tPxOGXWf2?-M|eR4A@bt9yk_{@GP!J+fZi>HcF5p3-8Y4vwrpyAPgEZSS+wt(8N+=gE3VD|( zS)Eq&9F{uvhQJFj7wJ(DU{)N;;#ejyB6c6?nrom9_{&(G!5~9X#br_6!1-6dk#*S% zcfZi4k+*okC{VM*SZC+3=LMT12eBL-1=|B}-d)l;ng@S|5lIZWdT!N%-d%ba_Zw4w zGo<7NFa!_kacdO-*~`9GS_!P*zr;8l+qw}FvD{1C>VS#s)!gS-Z|>QQOuD+>tZO9w zZGMm*oVt}J?M&8fxIFnC$5}QX1Z!K<@7H5pr)sV^j=pKxlL;ga5A3#dk$Wh)sGgYn*Y-VV(XZiv<*-lk)N-}T;{x;X@771`530`)&RasExAcXM zk3C&i>*{v44;=6V5tf}7U_bEPDd!|v>rT5$M!1!oz8Msn_RoO=>FAq2MNuZ|FJn}v z1?&p=$BPqSt9Gmgt}kRD@PlUF^a;y_^hiEP zG~PSh$X1j<7R9wjL7ju_f}f?AK$XV(BEp}qS{;lGA}&ouLCDH!`;;`G(IFRXJrtb3 z!)|Cy&4UqbI#F&7N+m|r!g>n%1G8ov@xyWwL$9{quaAbM)SRfu2#XeF-V4Gcpb^LZ z{WkWwOKb$W)xHmOM4qIPR{_;Wb{T$WhU{D2er3C=K1hXuuZeJ1fPJD|+F-rkr#pcQ z7&3BCF9eo8IURcvU~~;}JyrpLy%CwyY8izaWZSg$g0kIIb&qpSHZLuq^Rg2}8n=>C}JzW@y>IKsdU2Mh|Z z6a9E`omFHFT%X6^9unaYpbc*@EAC)0yIi)A>v~2~W!8MA>sa$#gO6MQ9n*e0A6yCn z{rNd%wO5e2_CD8ct%b80D-%gE&E`eBqJU*IP|PV`|m2a-d(D7t*+|d7SCR{ z6UZl){Mn)QjBf)eb5%|YRtL@Ba+=r>!^-tzbBJv`^!LcQN-4sZ|7xoP_pdPQi)pfe zgeE^rg*ahn>sS}W>jWeu=wHuZcN*bVduXNb+m!6sjpj%N=sO}Se|avGXaA-;sPG>? z%SoHHe|H|AA@icpDRJTMj1h?UL)=$R_nWH!WgAFB`u%h{Wln8LEyrL!M z=0VVylIoJF4gX5~Ix<*tM(u+P#=#0LRLiaHIoq{*;2%3_1=QMIZ| zW>WgrPQ$DH2P%Y@(-ArLZ?n8=RnQ4ZGi;-t^Xk<|FT`)Dv0}iQKki{MD?thw`h;d}~B9vK242b=%+5xY_HXfOu1$FNBAL|2a1 z*<}3uFkENdW8lvi5*rPEIfuw_|T257`e63$^odi8{2a_{RPjy)t|N0!CC6SR}Zq3H7arEt^Q*b zfVKgJ!he&$!%m_G$gYcXc~9z&DD1qHw<|~TR%=aKdvldMsVM%_^ZU@F@f{yJZ>C8J zSY%M*T$d5>-}FTQ@Sl8T)D!Qt5K1ZOQ?EPKF$21Xz&>hg-7e1P77evHi$s zKX(7~r^glTEp<5CaE7cDO(QHh`7T8&qhkCpkLsPSF~nZ|c`dr)g8+NG2C*+5qtv*a z_`bKOvFK+u3s(}j)n=d5I_7A8Kb69hj|ZZ_R-J(tV-M#!$_^1{ za~Fu-*efqmjcS(rwF$zZiOwDv57?8Et1_KaJ?4|*06 z3GZ13v1`DC)wYU(3sMC-DH@ZmK7FIP#Fv0ler@(iAZ{3Zp<_2(+U@@G48R1+*nEz%Fi@f_@m< z4|!ipV1*5i;TFpUT^aXxtOMgIM4t?hORa-1`FEwZU>NYM?5Mx%-7j+HTxfao99qOL z0XBP9_Rt*PxG)|Zvv94PdByB~;O5)*qJ(kw-jwotXdQsWr+pi`f5->s{L9UC01IUjMxrfVk)-AYjta0o+*C-w=X@+1tEDOV6`M* z4vesk8X(+hCi~N{+_`cx5^|{>p{P0`4CDN~B(IqSjPfzPX5zFN;Tjb$!`H4c+7Qwf zHT%K76}@{-C*yg7j)TYMREB+=-NUVGuV@EwYn>3`wX?dYeEdUV0a(WRKLQl<^CC_*xitb6LU8?9#}`qUl)0|kw?VNR^{r6U?9~OMSAYJ ze2Lsn^h8eqITd7s4{B2)_XmA?w#f^)exo_ON>u ziaZfdz;2dLhPi<%Okl?7uxm}`2W~EhQFVQGig1jv8FGx-#*eFew%*}x9*eJ zV~z*WsAoIUGPdZ>W`xH+wD!N<^c8AEnLpTfPc52U|1?Pb@c=SuY>IMc(~NPTl41*U zaGYk3jJ;#hH`^NWHTK=;!%cMK~xVqJWPS1g`S$HHuK+n_a@`Fub+q3@6wd;j0Y1;|*Il(z)s9wKVW_|> zqr-b}#SHE{7oaCnC*||R?hD@|d14{xxsj$A<;RJx7lLfCn-=)dzl|It3O0jW^A=Q& zQpk_m!9F^axL5~S`z~ADF$yM;Xp4843~OA%XAE}j>!#D%+RRhho|+xmYXCgq-wPVx zoIV$s!z8`%nE;k)`h7Pbvuv^KIA^UNMe&KKW*+U*rwc(az8jL%8^SL zX+^!@YT)$jxi@qUJXQoVvN*ZKR#hx2be0^K=TzOX%^G3+4)8&iy(jp==g+Z40aseE zC56{cig8_37oYXC87vq6{BBgp!0LNP%4Pd4czMsq)M}V1o|OGY@s?jdfi&$^C7n>R z{j-|cw^@MX0#<2rdodXbeDyn~ZeW|h|rh}m`89&%H8*>Cq`(3BGf zOAEMBYcTcMi=}|;J>xkw6@FJ|@-#C!jxp)1S!LWI2oTMjZ*=0GYgK=cgSPqD&jtq? z*($vxk*w>Lq^dxuX_Gm7VSz1d3)h*xSLc&IXmP%nU4pY{A@9KqtN~ybtF2#YmI}^J zYAYx?*_ZjIM)tJS@ICgVtYU>^r^+b|meTQuY1pWydd{z5(gv1UMxaScIJjnXHlo%X zUU2=ymgEOS$T^T9+ij@CRBfh)kM;EZQI^Y%fThW*HS#@H3`GVVQ&%zBIirH#IiAad z&6Q)LeB0Y|Q?jke#m?tR)t!#}%g!y0_z&7^H+im&68>F&_*_ zBjyQc7G^-P?c*jiM&vdt9y)uampN+7c4Dj&l@+n_NO_4zmGrVgiu*;a3|Cejjp9E? zh@f}F-G{caQCAQHnfz3R=BFvoHc=mN$ry=lcC5y26wW*7UkE;4pd`)68Gt)zr-b=L zJVARRrbBwoonG}-(m528@91s+QTAoWFF_WA7)-%~9#xrye)}_RoYf>wE6p#9Wai+j~0?&;U{Lw;=rI%haRf81qo+oA+?N!`A)CQV0~16Sm?v z)w?LAPQiH|467z*R=eCBF=09&8WpepVBk$R{hFVXQ>sP8j{_BoIfvLjE3?J{z1%3I zv>ZVeW8_d9LMr_318Yb8!~kYopw;8$kC!d&m-trA?<|!Wcr_Z!_Qbdhs6;wZFwZvA zO$2Z*bTDFqFt-Zp$&$eN6UP`2%v8A1=Rg{|MrA;eJP&`7Zm7bP;L7U|zZ+^4u>Ssf zY`V6d;Z^hV9*E;orw}4mkaY=gqNgf^vmys+Eu2QgGb3q~h4Y`XV2*Y8sH|p@m_z2- zh*Wq|iaT;|sHzUWmBz7qIRw+hIK2077>R~%lA_P$ac5XOR^*uN?; zt&D=1CGgi6KJ}$7d+LAK*0FDft$>f8b6AW*gTDzEtUC^Wv|xI7Bz(5xl=*b*Z*;2l+{qYg`*^2euHs%V$%FUuFGU%Bv6`w(ewrO zzg}1D16aXjXC4b)Qd`sRDMzlR^&%xG<-EnKy6t{>66vz?7T+K_lyi6~<>rJnL)ve`zL(m~%sJ z380{UW8#b9v8Qp%D&UR~farfHweznaM=rOj`S!xq)%BGT;c;GvILrXr!lA+L=e6a` zM<9kZ2?5O8?}(m->7CUqTI=c=;KWSu@0?#kNKrmBab~}{iBy2}IpmTft)YQ|De?Ju zjc{e)hkx6YOWSW|TmfA`Bw*{}z4P3)l{i_@-|3emr)1kxmwKmG}jHxqV#AZ<| zEIfN*02dxqfpCQbN)nOiq00Dl0MPn{zzlFF5Qj_6|{K=_{0>5K_r3{9swJa0>?mL_iUJ z(Fz`Akpjr{6=v!hAcBGH(MNMV=__8n(tk0I(*VLaz!}Pz5B%FU^{nS0l`%VHYY)&s zNViJ{U({LoWruT=9ye!yI;b8@@+jPDp4E9D=LoKnh_inDD*cS6p|vS{sdk+9@|E?( z#0Fdzp+uyCeWfiJ!oJykEyn)+4ZxuMYi}`#Fl{f+z%YEBD5AYJL3nF_C25St!RP=D zmsLjM$7W;L8fHx}Fv6O1o+Pt*OD^C59I&eUf{&SA|LyC80HFDLuZlm4>R%sa0Skcz zR6hSO3o|ml*4~4wh)X3-1hX^I!FV`uidh{50bxS2DM(-&8o_z>%TkRs`@eDq`&fHruDXQ!Pgu2m`NCz^$IrXG?X54 zSQUu`3wCv|3v+E2IvcnbYeqJC+FIY_n)sa*7@iGtFaK(|aq{{);Pu6(T?v+MMAC zN2vp6vPgGYNyiArw*wEFE#AR1+yBz7?E=sju&b7E@sp5Av^MLMs~<~w7e=6wQ1Rh3 zb2{$j1tvEdR8%vLHD->w)F&lnv0_%DUr!Zu|FXg0f#4-+Pq+?zN#p~eoWNem#bxy# z-6q`AMy4&W&mW0Z=KbNv^;6vNH}6WFuHkma7m?aJg`0u~;Lz^eN%ew42^D+3Cxm4j zHRDh*lUpPlw3i&E!kYZi23G;6U|6m!EK^Z>ZW&zrV+`IS&ccPI0vLx|F4oJZx#;AQ zUYzHBSMlRZwHJyB)VXA~I(Iu4q5)Xibmt#G*#+=fhXQ7`peO3_brzJ8YX$vQ3g3E} z=|BcStGkz8?cu4;B)?i`Hiv7A0`^V=fMPo(F&>d|^2Jj4imtj>X@D8&F6?orLdQ7x<>Lh9IipbJ=4&>yXFZ6*qQN)_CH^>le8D)F%lTgPvjL%Q)`2d#3Z zdx?;u;X4g(+5+X$4&^>sn)(D^a|cBcflN@@_P*gkk1(e|&35R=0j;d?6?t|(2NAnx zz(A05W!Ry=t99Aomda!R_nPu4$SH_<7<@m ziX~+_@N)0}WACk_qU@qa;b(@FmTpi`P|BcdMnY6XP$?xXQbItGoIzAdL`6UvL_k2g zBnMPPKpJV70i?TY;yZ)yuhzZaANT(Ct@XXE<@lW1XP>?I*{An*q$X=V14@?%QP5bz zLekReURj+{1(*>6Zg&O+>@F+y(~-%Vx@f6(mdlp2yW7e(AY!i!PL2$*Orl=B!Cb@B zQE+_+r~pu>zVFyAIc zgGzFzR9Nj@nO~a^t;{Fj_iQ@~z+Ds&TT!_3Gw#XcpT>Mi;zkD(c^p7<><^94-kRVCx;9c4^Q*ME_C9`P*bAR&Vt%gwg*pyHl^#*pk(^zXOO~G0^cc z_m9=jI4{V2pV^zD*(mDx^fxV!yka8jQRQ+zVblVtcg1!xdU6H~9b|O!N{cY8Ky|#* zS+?vi7Z#SmgH`Xyx;H6q?}P&Euy3gZk>9gS@ZDl@?Kg1rIf7BBU`PC-Bh|EGz6u@Y z2wnpHN6m|=QS(+Lda0~Vj$o-{!!^M*$U`!T>}WLG@(!mb^F^4h>OAfWOQ9M)wY*Ie z<@(8|{mZOBvCA`hc$?xSf=;mp>B{(u9^!CtgwQ8vO8~bId2q;gKq(Le^f~+m@Bm*A zKgsin!v|PEME~nk`0$eq;s34N{~pBuz8sni=f78-y81Tmis)MAa}bR_*H4;zCUBU8 z+-t}?7T^y_{`v1Hb&5tW4u1iIE70qFWN8NP9e`E_ZESI+6ux{?iX8Rs%dUP@Xx*io zKome|;Kti)Du>|IQg7((Xa3D&YK6W37KJF!jv?DLgT%A4drCq@$OTwx&rn(4I(P3B z_)dK({Tm+(M~fyE3WK1*kA2dOHVi)``o<0zc7vb??@mSuQA74%?GOI0w+lc~CQHz# zzrhR!NO}pHtuCdoZe}8Ghq2tZx^3ijF{l=@7%AKx5}5*#+v%>!3Ppm(jkbAx}< z1)$cq=`mCwqUBHeZH$|f36h-KoDEVJgv^u4*Fd0m-;K)Z{*tSlmHL#l}fIy&O(B1xaFqd}sg;L)d z{*VHPTaZV=!td?ot$>Mv%r|onztvyVr3SI1C*$IjEre>Pn9#Zig~tqJNL9r|On{zM zWrMy@#a$5YjQ&BU6x5jxL%OA>sFC@39)xG5Z)Vv>UG#+Ri%?7yJ^*5YmVWy^L190Q zj~aTTOa>2){wB$sXK=t9nGJlY=JGG7l2|T%r1c#@lvzCFLW>?6` z`O&@06%5cz^8E$0_~tHVYREqihJJ(wiJi5RBw)^>Tm4Y{*t@PcZgWsUH*fYtU1klI zk^ni_b>4~=Ov(B7z|pOUt<(%aEcC>ssA&CVvq#VwQ}Sz32>FH~uRtCF{ZyfwHlvr6 z0a04V%1H$u4@jq9L9*p#El(@CYbCw@^?5oZ;2W%dN{ZT(TpDT1@8EHm%e?G{nsqIROC=baMTzAS zC@MxQ6E5{U={&TQQ<}YJ#jpOz!E?qT!0CkXsH<=hJ-Lz(No^!!t8kM(c^8B8NDvVM zS0I_WL#aH%AfPp#K+02sOk+OMo^~{D8+`}iy1AeJc>wIGKv4WCZ*Qq|QoJA}xlo|K^aDg8 z+2o4)oNq(GxRF|%XtU!oSv!2Tu)H@@<_WB9ne%2_f(Xc#?wn`Xq#*Xg+4hgd6{N-^&?32cXz}mEB;yr`_OF8A zmh;f1_^T|0m5JLJ0Tt-0G{^?c`YnyDL-mK>Uy4L8TRLtHlRTH8klBUdg%|*-UM$1^ zH(_blJ!ffG)5dlt({X|3?P9BivlJ-(J-UR{akUmTZJU~PirhyNjv7cWSf9sGfM zwEI1KFv+QnZ^-|R{r7+ha=c|<=+KuX7*c&mSm+VX6DS0hAH-+{C`A61BB%}hL5{d! zsAsGUPNUm2Y%I<~fFs&@EJFXvB<{qAU<--pW#qIY{n>Ut* z*H}Jm{BSNvz@-hWxmB04G`A*`xqii%fH8kIfjI+1I@^h+ z1ikc!{qz{Kjcg_EX5&)6==im@l!#6Sla0 z1|ZM!+`l>*zZ4o}8*l;`$)R;!o_N-U2C$yJL0umcf!4b0>P17p36hbfGupQ;b2zF) zd~Cs1pBoVo;T*o#`0%AhRhI~mZ{)Fd!j6g8H9bB3izfUxXIb~2+6Z|T(13vieYcl1 zx9oh1n9k<|o@oRSCx*+mDE9K7TO`W@xXNErh;iV-VZ-wNPYb)&C6VB6S#Wuf%)2GT zPD+jEgrUFJj^626ji9pmv4_o(KIDyHJFgy$uAIGs^X=Ii|4{Ie`AsB98T!3ha65Z> zzo&UF)EU3|+u34!zp^s>wv+4W?t!IaK(nV#oo-gpPgLEwSP_7CP$(cQR)({QECkyW zTR>Lp}z&BPnmNlf`x>-LeXaSOqEeT>)t?@*{$Ct6jQVVJcYF>{t$m1qb zq>WM)k?U`T0@MY0`;n_!zHp2o&28<8#y7L*LpE1ai? z{cv2MUTLcvnd<_)#^tP&rhqzc@*TgDTkEE4+hL_DN*>m6@e?SQ0&}ObSm!+Qu#C2p zB>GB2EPy6ZXLutDn0nLzpHL%8#?rgY#HEs)3ArW%&1SIFhaf?LG48E?kx?QQq`(Kv zYO37*g}3SXLH|qkna=8mkL=LW};uMQV2R zW{mB3GyZlIZNxV$>vl$kY1PXZG(7cn#!@So1^VV`+QGjVm(0ir^4>zoMgdPA#pDjJ z?XzLEXKai|IG><|^b%QI$4!9I(~~HUlVpl|)C^k5Pp>50%$i3Oa=wDJ=*1}cTRbU7 z?#Y2v<(|cYx0_497iDB&FZ=Xq2jUdo7m|S{*>7j^GvB+2`sH{oe4G|JJX&dgf9J;chQ&+u7lEY`s1s~wU#?P-AU5S`E5bWZ zHR<;=Yoh{0g_+FA&7Dr!>2Q>8#6YPmnVFel8|TIM7B3%`D+Pj4)7RoC?Fhgsf?XrX zpiYW3I6Pn5s*-<11^O_c1249l(mTw&>Nu~k5r1V(C=>Ds8XT;gdw8Y(0&pY?wEyxt zMxh{E@`VUwG1HTv<0UOEWz%fr6G`UP7iW8)Qot-C=-@{jkG_IDa`7-h-sV@7b}T@o zT}LmXyxTtRR!|7b(gG58&PEne2v>c+U>)<&se}&zSnb19>8-iYAoaPcYs)3N|D=wA zSfotSR;1`?g4{k|1?L?aWWi6NU3{W$Onr?tAT?U>$pK!&lHwE*jJK94?1k^7`LBYR z6InJgPAf+#nqS`Ec3AKCHq!0%-g0-pSu!I{Of3%(X1;$&^Hc^sVzEg(wsmZm6kIc1 zBQs2$a<$4Uqwj?)4$OrIvD+Q{v;3OL^gtai{2+Y9$(e@;t1{z8b|_ zgjE8U|8tMP<-xW|hT>~I`T;vKc3+)$3hqBAHSb?=Jemm=D(_*Cdw#zpbG(0$;ZphK z;jdbP<2|r(vI(|Qpu`OwsQ&Z;#eJ-rfW2QAIgeBRaqO>!qceCa@CX)!pCiCt6%YmV zS0Na55xm!d=uniy!zg$bqm;zDG=P*IM!7=!-=`Ht>2n-fDTcx|}hdSw*EP-Mb$+V4!bs5iT1tW%x&Hl2-$pe-6Z{ z8H3peuXN8V8A1ti3HQB7xr-7xo+udFk!NO;Ib!i`GNTk@a3V!<4N=J z&2md}m4y#rU*|9*e!_M8?L*bj`}p0t{bLeO$t!fSEXPFB-N$y{xAnYloo)mJtKJ9w zS4HiK#wb;iJebIi#0x{h{5(HjF?^hB7rpE?bOOkIMct@=&;8?kJqmYp0f|DWJ&%Ys zm)}1{Px*GhJ`1#2+qWd;mFl~`_b=0NX0YR4?V(F*K1B(-Jr<53w37I}PUhD8AS|K(1>{roww@nQ%; zq1@UH;Xf_zTP&gZffSNG(b2gYde;^y2M_Th48wI}xaE8JL6BKyP1{c*m3qDV> zKVySyD3fEQ&pc;?xItci34u^Hn4$oHCmID{0l*aO5IcJ?8=ReNBa(qJgaO6`y#zmu z9~oebEa40kEWAgVo-@FBnFAT%Oq>GG7$}(ed6>`)Fcy~Q3~*LKPVmXb_>2M0cNojb z^_&4>0(n_DSV9?K+|1z&5Zf~bi18(e4hJA0CJ@a8xhpz30-Dl7C@kqe|2kYC^nV5p zKU`P-XSm~k2LHbo!u?N_I$i%~*cEc^{I8f!SIGX}8L&wB*SGJ#zJ34o?fb89-+z7k z&Xl3S)0KaH`~K_O_g~+>|N8d**SGJ#zJ34o?fb89-+z7k{_ETKU*Ep}`u6?Tx9`8c zegA*&+xLtDI8%A@zxwut&|uFRedycwoBEwrkV)aMZ{H;gvxoe@d59gq7KbcXevB!; z>a!M0I8CW#P;%1A?o?@h@0H7zmySNAF{FKi%z5_7-Ww_02$N%EHh8NpFO?Db_(HvI zNQ#xz!TJI1Qw-a zD{HDfGB7CEbphL;80m_pb>*bS-4Xr_J>=#VvH=(IOxX>$u1d;LOSxA7M}*%z!rB0=ZA2e6$zGoOYeFce_;MlGN4MDlGbT8{Tt2C zwQZ57Eu~_yyhkP3g!!Fa^czbk5;YOGhJUD9yL#N0B>u@^6~?raEEEwMCp{^Td@%0` zc%UtLPU2X~qit%Mv1^%+yTj^FvTGB(^G;mIKDKG_C+YZ%3iIswZ=L#6a>Lys=)DW$ ze&;f(MPO#GZCs0KDp)m9oM2f+{7HkHZ2!?t`!=sZxBB8ig`CCj#1G>PYJ*M>BE zca9WWr@n-gE*eOfWQwD+iRP5JhlpF+(%ct4QEwm({HW&1H&V1`dS+hUK!lEWmD^2Z zL`oiWy!L75C`^?4{1dICaM$y38aD*$g+E;17n7rqcf3!P&BJmMeu?QgbQF!G6lQ_Z zYC-V-?PvDd>`9@;<%dsIf&`zCe5kLz%EawH=jk^WqJO-rR9UH&U`IWlN59 zenyI;cDO>&1(Q5XMei=yr!t5u21I`1csCK%%uwTY%D#>jCJN1?DH3i^A3Y}TSdW!% zs~)c@3 zJoCgk0b^>o@25;bF*E6UHoH|Dtx+qmuoxs#awhJqx%P`%z62aHvfw4HeURormPYu8 zcNuX}8Xm;;ptY(uspFJS9_W}2R)td6+q7C~*N#SLeZl1Nl+%re+!vQVI`L$MpT;d; zQ7Qg~j(UW_Kx1IJ8oa7F>~KfH9C-{+J7SkR+F!z zp742iOD+*QmGPy^w~s>8*1i8I?R7 zoqOFGff)lL@76bl;hm%AzK`9C4ZbTkG+SKQn5k%FbluHWG*ID=ICdoy!A{35U?#!9 z`D{YP>$6v)!ASL^=s?2LVeGN)qbCftf z!Rqt}QfuTqe{!AHXKjIUalb>ZM8lL`u`>Me^Yr2VxIZvHFBZ+c6V9%pmp33|%AOm~ zCkm>UB-l}G+i{{uhD81M5AK9PoD0-DVXodt@hRPsi+cX6?@W~qgsg!IT?^iL7P9lB zhjE2L+3lq3DUGA_mpxg-5Ed&iR2_}$U0H3-Gzpk|gn>|vs0X|^aic$sqw%)x&-l#B zy&qg*n3B7HUeXqZX&kM#^W+RulPi{b`aG4QHB}J)Pyk#U6IIm{$94dRF-MG->SFdkRIBB6c{0){vr-1$XVX_+C~I=wznc?EILKXPRcS23m(eBmR>3zG{-)Puy1 z)Myuo^xpVIz+AZ>3OTbq!M*+wxpV{#UzyEYkW?DG2JdiKMol=x(-yw2*L*-inv_gZ zj@^xG+3ELA>uD9f_o{YK7eDv{;$D8_9&(e)Qf}tnWcH>{&4;}bvzJxlzMdj^-Lop# zu#M}ut&{|Lvj$I3b@&WVu7!T?Ti?jz$Rp(kW!gzDUV$`n%&||41rVO9ra5V-uDb~u ziNdQnFAPU!Y?rqt8AsP9Lry?R;c2z(iU+}Y7OEj#_U%ocE;kffk~&N^J@M$M%%|7+ z$JHCcOR7Xp`rHY$={RpjU}4w2UGatUd31_QNb8`?{FkK!rjSn}Od(y5M@Uxn810q{ z?L`7c3Nt;K*e*iVV=1&yt4hyIh-<(dO&X@h>IBRe5l!`@*3VCkVYJfb2NaH^+EQOG979x> zm3@h1)>Ky}KGTczI{~ksSL2=+IXdCOMRP+*t&t*!@@ugW!}F$&m!Y=QIHoZK?M(HZ z+ALZ~PLRQqlYiVbI&B5|TNtdfri2kDW5yDjnRWdrYatj!S4h z?slYppll8}&kS%2gLli-xT}NqiVx-9XKh|5)pY2=%>|P}=&mFlE4k!;kaE`F@+`A! zh@=Q3QyU*`Xhfs_P4?H5%L@J$PRB1jxQ)2=b1Kg2d1R|#@_DuMeDu3DyQ)1xZ?esm z=6x1h26}Vlnyjkjchm1`_|U}t(r&k1o%?C3de>h3o#T-D+MCPg5*$YmaU0o<**MdO zcTJ6Y)sF|NBu3V0rAXULs+#cO*pKip`)%GiHe=Pt`JPW@SfnZ~yfm=l&4nbd8JTH6 zT|UiK`qgj)t=HF+M0V@bFFGipniFtU%6Of|lTWd4Lzj}~Bh15h3Yz=Q;O~MdMD|^~ z4mzQIn^WypUGf8+)ToqOH>X$67YE!Ehzt;yTIqW%Bpasn?dS4L{dSkZWx@-k!jwoU z3yB54QTeaI8cTONx72M`X>D0x?O(ORqE+~^ydI(1YI;JHCDD5%@dEL0en$iX? zJ^Z&z5ckf4OP8m5|JQjZ`PL5{I-GZIKIz>DnWFxgcaFNeOfvBoTgAGsytIATp*X2{ ztqDf+P2G6=x)1Q>rV1l4~r@~ zU)LY7{7Vfx;}tNIm}YJCa9IBAS(TF2zALw`_J+Cn61ZCLnz2un~ZZXKFfSQoZ>*SM`( z&o=;zQBve6lA(E4EWR`4YG9ayIOrF-d0@^1OLbhHpUL9FqNZO3=)40j)t2&w7u(XT z+n#K>7|~OrjRaP29v{6P)7Wl^8Jf<4Jigp`4Zo1{Wm(O`WB;s)7GxLK+#KrEcy~$U z!L8vrCZW_}nzD(mPwc*}4pT+ng0q;u9EaD3Q4_=+tAAlBHeDa5eo_@>pas@>qzP={ zLZ@2XDlQ16lD;BuvF}9W7pP~tAR*JLVT_HW+zLm^Rj#|XJX6nPj-cz3kH0@HBA92K zv%|cf^E8kVib`lg%sC4ojc}`P<}fVKU19mjN{P|6mF@=S`na?+E|)e9hMvt5FT@Lq zQ`_V8TeUnTHYi-1=K?>}xEMS|-oeqJ8|dNMLJB+n4fV`u;WNT8VTB!SspYdx%*b3H z%F{a#3kY1L1;LTJwiF~W@K@5z+Lq(V{7TPJwwpIgN=`!pZljrNrQZ6gcF96e4;;M9U$-EQY~B^z_1Zs9 znHm+$9P!cw@jG1a>GSFU$j+y%UWXZTrbJ;ADs{<6FcNn%QeN%9rAXENbJpHunen}n z63OH=l=e*s-JoKXdV|3~hs&7G(AV06l>aH3Br&?@vXMcBtY9{~9)ppWmtS|u<}K8R zOy`V7WQo>QuXmokML=DeH{lN!7q?FZesHYD+j7?B{b5FK?oMB@ml%)qoID-)fp6x3 z%P|~fFJT4kKj=U^WbAi2FVKriu2G=3WjH%v z`oH;xHQ8{_lZedwtgzIMsQUI&4V>+0p+@SEb`lX1rex57J~0!oFR%Xw9tHCo&a(EB*M}H`tmi&`@!t=?}4+0#k{@;GfV!pA0gKVWkVAc8v}zq z9B98B?BeFcO{Q%adP+c8R@t()GAWC;ybS(2a+6%GTlBDvEhn*9ECGs*IcSt34~lP` zCKvi1UiGD1oQs33D}|ua@6|(P=Thx-daRtST|dh}rFT=MGfzj))7-8-m)bp_iMd_5 z3T(tcAGC)l-fb1hRe94{mn!42-)az0>CpY$0~lQb0a%G>@vEm{0?e*(gd?R19eOfE z!#N>nXqfrTIZJ{F~z%Wio;xOq17iYZ_A z4L@-pg9hDxOA^?6FiL-H zAP+qrYcs)w;oC6qifO<&(bDeXYQmGIojzP5cCBUnat=S92gnBPgt=bw!#Is;Zzfu)jZw3WHKSZoyKyZXoa7 z+vcFCUkq(bQYOJty|$@-s%*<6s0_kozPyJzRlDZ%?Psd%SM>}CLu-p4Iz+Jp9T;>)`oYO(YLLCYjvtWJCyLYehCiH-U z5m@zck*Bbfg(T&e?1&U-OBd?T`g01rkVDyX3u!)R^8yEs_2zzbcn2T5(4%H3Rj5~e z&;T+Z(x7J#?9pNC29ngiN260mN1pa3u4V}>@fzUkP8>6$>CTwY(a zpiWdw`%a}6B;HbZAOvYyEi>$fCE(NOi3NKo;N@49A4lNIgZ@mRys_3Mw9vl060Y2d zjjezVDl0DkHU&cyaKWS*R1^(Aav9u%`x111e!^i{XW`V%7&%z<8V` z-73+95AzfQy;t@#O5L_DOes&z`^k}#eN(HtN?xym>By7u}k?FS(lA%5T z*|;5R0viA+)LMI0K2$ioMIeMGVviPD!V1ACO08SBS76wp0d9FxUjmleFH3 z$`IECk#`GY;F&Ber-9Q@>lfy5i2KZgB0V4>7oy}L{^BDn(6dwES|%8cKVWU3F!D>J zGuAEQVgN1Ln;;uV0N0%VkN*<}O2w!j|C_v;@#+lce z^;`j}Y)#kt&9#8DTg$gJL=|AFc(oo?B)nQ*A~xxWP#VAc6kM0D&V3=V_H+1Td-QHX zbQY~TQU7KRtTQ!C7y4YD@FRFKbmyVNAP%$nUei|4eSnG@xbJLHAyw#q{_+Pk*mN!O z7HaX1ixVJ{g~_%{6OI(d@;VueM!iC!}3w>)`7Aww2Ug zQl(yGsrpg^X5*P%bQEXi!(+G+cjt)Vv@V+1Ze6WF>t z1#n%IG>An#h{t9vj`Hb3yigjG*G?5UVRXS-Zd(8X7k#5z)@y`;Tp)GS)Ji%7g$w_PWpUP$xt-*S_n5gR zfGy5sMfprpx56%f7b3$AgN?tQ@$g9zg_V}zNYSD7x<`Pis{Pb&TVRd57)e} z5JfO&hThC1U-=zeLKRX}dK~9(QV=>UAK8Z>#zvLBFNn4jrrtPkbL)9u5Rke=^lq9S zC~VPPUmqa$v~EJbBxBZA({8SBcCjBzwoYa&oWY)vv~& zj^wbJna_eqDk#<7-Cj#j&uauTQe3-dRXf+QmwDqd>CZ&VX^sjj1zrUg?pNP7$`n;? z9Uu+}ok?h`&VR*cR)s*VcpCYec zT%PiErph2mC?&!hQxIO~1W9{|TyM^RSM#kYbOXMd9*c?m5pS+IYSynkKS%sViVG9s zM_9;AMV6hppNM&O#um5~lRBnF1zBrHqh0F!KRoDxc~_4|swr_aUBj5~qp+EBj}BtG z29l>Y(itTfB#&u-h?U+{Ud*>i8RS-JXVC}P~CuQmh(G&X8q+-w0;XS zC2J9y?#5&baFP|gWSM)^I{oHF5K?e==i>=qFVd4~4lhD%$%Bu)DRlu(BUP1Z!!R?A z5a6Ox!@GdUPg$NB;VsX=HIqr}7K#u$U5X&=_{y{?{C?TLEd1Jp;ZHP$Hx=1jl!eX1 zPpn=Kc`h#y`+w{^EL5Cohv87_URPLv$j(j+yDpTapH=GK*SrB*3S!(UGERl)yI6?P z0@N5BSbic7Tg8oBvebAVR6mJGsc<7ptyvH6JxJ(<7}5$h?Sq>!=Hfy4P0 z_l_I`-vhd-QWtO!jUd33!e_t-1{y{O$10z5JWOR+Wz`_NJWUM`suwW3IBAk^$u?T` z?2J&oM{Aia)150~=f-5~JZO7}bIWs-h%Kwi2tg5}bhKj#Iwm)imndOtq>Cy*IX0a@S zsz%{DW9mf{)x=gK3U%TiLpln1DOPCK+xyxoPzAj!tm~?Goaua&PY@EfF>jW|{n9lz z0Se~UPn;Z9&3F$sFfB2`XfGn+dNc$>TW+=5cVbq4FlMvuuDhty^awPX^o(GwGHisg ztDQb8!|x>E=~A~e-qVnzP8qy3Y_HxZutoP#2l@rArioc@PHctD4^+wRd||lnjl=A` zD0g#9`9)dWy}L zU$xe{#9#fHuipsG30dAaW(Z5oq9)Z`Q;HWSO*!L6I2g40C6Do|-`p=Sw|^-UcD@na zZHT6Ws7XH}GQDD%Kfaw3$WGX+EWPvdyjY?=Q`bZi$xVR-L?v=fHV0$xf6oHn|2g~n zY_TynyB>z?&lNOd5P9X1vO%($r*kedhyUUTL_M(|(|$2K!uqwpzzMgEX>{YjaqS4M z9nXe3+v@gK6w3WB8zc_iS$tL6b(y%*b6(dh6rOiJ#mCtVRi93h=gR77GP@CnT70@P z#n12~ILwGt647_?l+zGS1y%jdv#;zz`Lqp{tLs=^eDB5ADodwv@Dw?=Ia@Dd64Rjgo1MO^FNj zZ_;lv=&D4t>fO_{WPzcb%Z9M2-Z|sMikD3YVvF3?7)7f81zgENWK&vY`{Rj7uXFyw zkgK-~&&pMejs%9OAK!phxfg$&ds_pUfUkc1JEmaEs(n)k4q43SFo|tijVbb@2<;KN zD$MzLJ{MB+TFxJ)(0eZOOv@a)*l&lAhR| zE6&Y1%1d>ptgfkbBahJHV z8h6gP&Jyo6we%#mTHAH<4%qsGaX_;Z)j=LZ1K%w8;Tq|}vQulN{Hw*)uAEcn5@`9N zQqCh30LqKG_86k&K&DB$aA_KyfoIM-QjsCV`L*t11BUChQX}FK!{Q1~uOv)t`noUl4k-ChzHA+xk^35b?C|Wql&`1n9DZBY4!{ z?#%Sex!qh~n8k~(@_PUn*VW47$36Ux8Bw~uhDk5Gj6Hrl^2GUI<%84~$&YQVVFfdL zmmb>8@iXy9IaK*B*=5WCYXeeSvVW#8rfT1WG1`Ff!Il3^7nftOJ5PaPk5Fg!q}Dwu z-^dxgXBX#f->CR-x5r|HwguG$RK#@5h-yhRB-IFUKY)X+7vs*@8P19nzgC zFN#V_w*}aWGSgUGr-n)9+Z{m|6%-7u9^H?_l42OwmLtBY8Ml3ET+WL0n#vFEJmOUw z;O}>wYCktPyw|`iv~>opOM~|7Pmy@;ed;V6I?GLf?bLZM*4TXL6sJ|YuRo-J?{k&x z{0!;g6{EVXxuA=-RO|IA{d+TX3Dp5GG|WRM-mxQi$BzBk*jk;Q<3y{AAZC`-dQ6z~ zQ|+k|cVg1(+HxW%DSEIxnw%HC;NSa8n2s^j5XLB(_D~njCU2TKz;9qzLNz1D?uCNi29&u z*}|&1RI+mBEn^66dbx?<=daQ^ib#AlW81{w1@@W<;mo;u^aU@~fdt(OSkk$bo)kavL!jK7{TR=WbnL{u%L9iPnx?v}zWYaxsDAV=v)CNiOFxUW&eWp{mg-L5J6c z=-btffh_>C6>tA5XPTo!5NY{b)NK`4HF z5LA!$R+T$9b+Mtk$JvIL>Ie-9B+%-i4>A5J{A4wcjd6*P(oaO~hH_b);eco(o~B)Y z?YheRAFYz9Bvf_1Kya?_xSGIQhwcAkTf~AWOfW%syDe)6KWTVtfg#Eud>ZzPcv_gC=fB zP`jn$mW8f4vWt?N08vaY*elePL_Do!r3BRnq5f8%=fIA)9y(|A4-Wqm@!9Q*4KibA z@)4<#es^1ke)JQClDryNAmQ7c;!k!WvwW932nqcKc`0wnY*^i1Y=pv|{XaQ)|AEW`ELs02 zsAtre&O>ZqkVcTzAuzy=v<4a3-J}2D%EgQSD=O58|G?L{59ft7liwVvFZc(g?_s=n zH(L^zXKIr1O*t&#k61<7EZjaUMdmUvhfRMNIBcp`irns$8%b|cQ%#`*)z2L1NJbRY z-bG>z(C?7^K1xze{|7v15-kl0h#3i#sSMb_|E1f0U4sm!VT>@zLxDJ?8E6?`C|NRb zG=~w?ez*?bZO7H1Lxc|7b%ET6(E*OD)e3DJJKg^@ffo86#)n_7ex%v)807y;)K;&a zUDw(Yzye9ElzkjlYR(z8U?(U4F=ud)@&%IB$>B^TJx}< z0~3@xG-Sr?(9D4Oa@XcbQzhDmS0l^vZ~vIo86oueH2%pf>V6>X<0m4-7@!rq*{r(0 z9U5cu>s3ZhUsFnA7@@})1|)#m>ti)hW_zGmoiV_pCyB(iGT1w?)6tkqGGTzN;1AMs z%xIy~J}%(Q1X~l}WVagB9!)5C$GNR9^Bwf&E~=~(r=?-udZ+>hVr9dMqdksf?ZYO! z+atAnwXHaUnb?|LSX*rPIz?n>8V&54e^>-A)ehW(uqro>$YS>-f>k>aKet)yqpVs*Vd$(>mgvr=bzOUzy@s$TF z(+>i8@v~GyW2)WBG<8?gt%sA@U})~wYJ_09-3x5awVwCma&2O|h*p;S?i=pYGXW#i z7igi+T4PbFi#VFDUqqr451#M6#1k*&ce)S~EHy2w%N=MCrqL|5zZx)s`WX9P!(etL z5wMXn(7+sjX3TXD6_E~{eupTf51i%5$C_$xFd zx2X`vwRF5z?-G&u6&s)-+}C7#eio1r65VrgdV?ge216SCF&3z{SA z@PhFcHMn*}e1g2`kN0&}RA6yq^QU^$7jb6&T0M~?^ZomE;k5|_!hm*f5bJAzset*21gD{z*nw` zQ75Y{Et*zs{LIi1ATWFw?w<3-5capu8cRu6mTkvN#K7YD2=2Q?+>X`eU9iCZ`~n+5 zsQTjHI@B7_K9vOzi%vSia)ktT5z({tekkWn6%4S*?tL29Ida!r!L5kJKwfPnCANPLyad+Ofp!4GYLF)hTsc5E^)SmSCP#O{tpPal{Z zH-QtqZl|NfokF&n`|~3hB~;6I7EdSC+~RsB2%+s9n-Lv5*mCdtjge~+rOMO96lumG zlZ6 zr+hEMzdU+4YYsLi7Nd5yJBAO2#+hM|b##qOPy9X(m3<<>Wc^p@bAWjWN@*M|sKE@T z4$Z8XR&Uklg%#3V^ZQ!{`Q&OW->;Bb5}M{mi4t34G}pd=3%0Q!yWX08vqwbK+J6hv zC3@G9z#>axPXyRXf+W|%&ukcLFv|<#;dRZ~5tEg61 zHS(^+cV_UryfK&O*c16!f=Ivu&#FXWM&k4VFk0}H6#-WN9}Z%6{ioU{&nn%%wdO}c z9dvo9KK?c53xKMPmzQD#l2@2@@nARQD=WtCp)OX2(E2hw6zoyN>;{4t|4wdESd(rX zru#%d@QlM+wS66y74e@{&(`e`v8c)YCo8`ljq%*ff~q{IfYo>ZRi4G-%lDOvo>KvB z59bc55%HljGn^Jz$vdg5zAn5aWc-CC*Bl4HdbH@D0I-0T%IAN#)oz5$bqDNPPxhe8 z4{}?`?FGNHUrz+=38&bUZxU|AuaMfiyt#lcNdSgia`?|pyfd~~KK|B(7fD|~eg#Y@ zLj>biNIiTCrW3%L1ZbCbIw0^UYUF>we2Yq6ak0_Gx&U_|(+B!r-UIo*6DkCL^E`E{ z?qNbx$#Zu8fX z4MOMLzvBqhUh&vTPJQRa)>Z>4C+!FI^1V|Ge^^$Z3w3-i>XAF7zRzXB-IbaEi*FR9 z#;5gGs$Qq_`LcVf2`2OUOo<`E%xyjw0FQ>|a>lQyu9t_ppCCqKazEk>V$PaK^tAhx znMz8n1$9yYlQZPaX3(iV@8**@S~8BJKHm7gmFAFO+vYe<`plMj zxo7iRl;yCCt`*>gksFwOx3%qY$;9Gi#Pn&*)y%(#XXzd`>BBc&f_hXHA)$87<;&1( zPq=D%;gkGRtJN~?O+p_VteU|r+?vB*U24bd)*Na51WC8~CM==*-M5Yz$3x-sXbCm} z?poqb1W1%gKc*nYW<@sLY`W$P1C%fx&g*~md2t!@(0TZ^107402&d2mQ)*hR;O!kN z&)=9+wnC{dyN{HL8Nk4Y!)SlO3C79KI`2da) zr1*!!A4KDKa>?f+9zT*r+*?!HWp1DEC)LJI|J?>(eWL8Mv#U`#ujap}B;>Uy17I^7 z!F)EA01(BFQAF~s0FKn=Y7#z?45PECr|b!CFe-Q3Z?AIQ9eC>MyhGlBHM$j>k|KiO z5(e5}0_-r9Jw7dr@C;xA#Q0F12_NEsw=)s1fLR=5VMp4Wd(wmza+>o%27Jc9-zx$& z%}!jYr@@f!`bo9@R=COd6Qh8*7$E6I?5quI*1IP^O>H?2glok2o9Rfgx2QY=2O~4y zI|*euZ;JzcX!cQsrOFLvxS39F(VZ_Ld`&OFLB~dey0;6drnW$U2(W;vMqcaw-yQY1 z22Pk}h)>Xf(?*8wU8`lj(;}JXM-ES&kf>a(Tl}s(kTv>a5$5eQj1nl-zF2di zpsW>;5I2Ku6TT%bMbPy!WG#HWKF}SO`tpVl*fmC%+zXgmbc13k!Nr9bD48D8&hV)} z0nYbK`11QGkdL;xJ_8NN9l`<4$ie#3>#(2ME`0EYrzGXK8n{~GzL^av9$`IEzkZiu^i+# z1?ssomL9TH`oYB&@RR^S!uy^CQezghNCN;VlB`rc#|w-s)rcm7kF%lFg5&jL64Yq7 zOW=Giz^$l=U*18`sqyiEK&L4gi%T{~ObZI=z`SSL=hItRU{ z26YdwlOAM3H|~mu9hfUav*XpI)|XSU3{d()ZuWfxe)l~nRcwV6DHSmVj*M-Y7&_^J zXREoc4Q7(PB6R=vmEW?!=Y6U~#CT)0pmnRWAiuQ$jz%8*bSLq4>2P%h95$y zfr}cSoV|i^I%Kat7>{2IEBtp~eFZ%elvL4uw$gNt?V9EJa)p*B7+q#aK!QM88t1a{{Cr07^VKr$8=x*G2OG)! z{7r>xDT!*TbG+(!kz*rb$T(Rb2&tO^U za2}&1LqAgJ{0GUi=4ikjkhhSzOf6kP2asvfl1 z&Z?gTWFOV{waGE5MsIcG*VnP)mlZxhbc{BLYe6$s?*|3*MlTdTo*$;<(yZR3fRGE* zgxDW_eL3a(unp=9_wI=V_sBQ+z@gMRv#p7WWJGaI6j7NKx|=Ozoe}`5Xs994pGOU3 zHaQE?P{Zoyg*Mr8M=4y>x{A`0%IT=lQwjvBh%~1>>C%2jdHsi0pQV-~#0%En!t0|G zh&j>ouTW~|$_lb5>RXRlkDrn`4Vhb(o%3+7rXktwu-D%Lb_+9h-^Fr3(!*<{?BCq@ z;c_}*>7sMOQq1_csX)ket$_rt8wDHZbr;5?D)!gwx6Wk^6I>WW*q!J9P-T1417VMPgwq z#g(1bw!kvC@if5G{jc!UgqZ`0#?E|a$-SNXK#Rg5@jYf|=;z4-p5Zr{l!9f3Ea?1G zdt6iPhU?g2UDW)Q0*h(Cz@F2APGortXF|8f;KQ5N-}gEOq4bTRb}w*ADyb~w;D#LT zaUBE9v~hgHb}k=fBEy95qR)l$@9+2GBlGv3KW3I{@9)b@scYpDg>ujKBb3!Q9XkY+ ziRpqQ-_78C`L9!7{pVBzcMjGmQw0+C4a3t$Ydgmg1~A;`=C;Y|51c#iIe|z{5qr;6 z9CUWg=^V_t7%f%Ao=TB?pGC!at$(^Z+|zvkNsH$2+G!HXa3kdhbqHbmgKzQ>9EZz8 zLkHuYkJ=C7oLi}3ray}74!{k(Wfh=sb0U3t_EUr*bRY_%0Ozaq(rR!hvEt)O+6A6n znsd;1s-~Ow>pN?`CyO6*x0!qHz4vupdout40|WtOWdQmq z1L}MLa2>irBLB4C!T=x%>XVoM(>?(Q07C%)(AE9Zrt$+oln?-5Fn`)nf&hROu-HE-+Qp^Uw{AH%X)I6AnPx^om6&E`Hj%mamJoM3l@Ri%#l#pZ8Cra9boK!)(|t^#J4qe6)gYF`}j zyI5}7=plwJWAt#1P5;0=9#N45Zhe?x#5l6d`Y4 z<_mg(1sz3cEZa+5zN2Nf@XJwgE*dWoIQ&+NNelcSGQwChDlgbBG5BVv^akfwV^mmg z3mI_6-Ds;pqI}x~$>BjA3vFK#eiscY75LZqztF6q&Su$0e1hR?4e8-Xj))oec{*o6 zGjn2mM^#IacJOHvAN+)|k8AP8#Y5lsKU3_4wIY@O<)LK#O@wS*0Qq>$=4 zpScYQFScd*>N2#-l^AflGK00wsaS_x_UD9M=+M=>p6>Nc=)e?1GrjS*mWhM*iE>j~ z`I$^UT%JR22u$5kVZ)udvoF0}hv>YEl2?$+pcihYr{Aq-)`oXwTm!r^_s;HI>WnQX#d`aa*f?CtD?#~*3wrQ zI@yal!RzF{$(8ftT~BM&M<&v?;cEtie}8XC*gRH_&>JaQM0Yf~Pl5g3dY8`5y`YVu zj&M^IrQ>Tmi)x%yfy?A*+OctK-S);E-AQ{~79={ESj^<;tQ^∓o;MJT_Bx)^Um~ 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 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/icon.png b/companions/desktop/src-tauri/icons/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..f32c3b54bdaf0d3206f232fe4e3b1ca072ded047 GIT binary patch literal 10700 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 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..c8e957c2d5440fec0bfda18ae72f1b239f72714e GIT binary patch literal 491 zcmV3_7Gp)92rh>SnE)fz*2}#9*h{O-Dk+AS1Y%D|~BoeW-w6J1D;s>xqNlFF5 z&H^Paq0SV&nK|Z6Vo^Fw)4r>j$(-k$_j|wdNz9O{QcO=xVlT9V=*<=Kk-xqXy?yA|OID_EPRGcbb;>V`=n+o`2PIu(Aa7li5#s zw9bX$NDSlW>Ms~C#bj$Ka%5(mvXz?LA{sm*c3(2k87r(0*MsG#b&1%1PFuqAx+z;m ztMHosdsF=W23wYwuRs2ujRs5L!?9T6EOWqicLrEIP79ieWn~IGJUJ%v002ovPDHLkV1j!l+w}kd literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png b/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..31688c2b98d48d758801e683b37171ed136f63f8 GIT binary patch literal 862 zcmV-k1EKthP)R6=cO`5lTN=T}ffl?F#ZD#FE}|epGrZ{{P|~!N=)y=1 z=_-u6i%N*~gVVw;`q`|(q@U%Sn>tV5CtChoIFCE_5j_{%&c?nyd*A1M-tWAhs1RTl zJRUb%uQ$Tw>P64fCs+yWF4%*doUJH5Q310#YtCofY=YGn&f!M$HT+GKm7hdS?d638 zPgOe5+1>^gU^`lj`i7>d*F1fwI)7GSg&YOtLOD%E(Cz-D?AUGri+KL3AA(WVRlj`U z1X}T!o_aI5r7p>dDf? zT}v_z$V-w`ucMjGnDqk8vVg(RBo3rWSZ@&UU?_~oU&pi|Xid<;6NGux zO-62#4sVAiuq#=_U2h28zHn>^dJEIxpHR@^4x-gFs+y=l;BeKNh=W9%&hO(m?EDrR z4VLu!sibgbLp&bsOo45UUKPP$L_rsc+~8;gZC^%XtHDGa54%|pdzJwvk;5|*fh+Gv z$n(;4b)2j*5Qv~Bitni^5?TN>lD5_I)O9xzfAzZnJR~ZM+6Kp=(Q1Oj<=4K7^OGfX zvrn+=G)fD}K2~-UPqDplE`BQhI(4K07v^Xwh z8<9vfNIj0Gwj;_PTuXdW;yHW_Mq*EprnAmsBIe8EC$Sq^94$+*8>-uy>;ya<3Tv}j zn9*7%v4|=XwWa!C?IsiUla3;iB5Btm#ncD;NC$|fQs-j}=|ah*lX%0ExbF+G(Titr zC|$HcrCQA8%G*jt_;TtpVk?5L?f-rPotiHN57 zwSU>Z1k*_9QMa=Je>*K z%N)$5nR24s{)~Vt%9YNJ+f2*}ZAXqN^^MKkoK{EQU?Gx}!wk6vQ+`YzL$owCD1H4d odJMs>RJGEAusbTb`Nt5y0Jljy{ATtR5dZ)H07*qoM6N<$g1y9^7XSbN literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..31688c2b98d48d758801e683b37171ed136f63f8 GIT binary patch literal 862 zcmV-k1EKthP)R6=cO`5lTN=T}ffl?F#ZD#FE}|epGrZ{{P|~!N=)y=1 z=_-u6i%N*~gVVw;`q`|(q@U%Sn>tV5CtChoIFCE_5j_{%&c?nyd*A1M-tWAhs1RTl zJRUb%uQ$Tw>P64fCs+yWF4%*doUJH5Q310#YtCofY=YGn&f!M$HT+GKm7hdS?d638 zPgOe5+1>^gU^`lj`i7>d*F1fwI)7GSg&YOtLOD%E(Cz-D?AUGri+KL3AA(WVRlj`U z1X}T!o_aI5r7p>dDf? zT}v_z$V-w`ucMjGnDqk8vVg(RBo3rWSZ@&UU?_~oU&pi|Xid<;6NGux zO-62#4sVAiuq#=_U2h28zHn>^dJEIxpHR@^4x-gFs+y=l;BeKNh=W9%&hO(m?EDrR z4VLu!sibgbLp&bsOo45UUKPP$L_rsc+~8;gZC^%XtHDGa54%|pdzJwvk;5|*fh+Gv z$n(;4b)2j*5Qv~Bitni^5?TN>lD5_I)O9xzfAzZnJR~ZM+6Kp=(Q1Oj<=4K7^OGfX zvrn+=G)fD}K2~-UPqDplE`BQhI(4K07v^Xwh z8<9vfNIj0Gwj;_PTuXdW;yHW_Mq*EprnAmsBIe8EC$Sq^94$+*8>-uy>;ya<3Tv}j zn9*7%v4|=XwWa!C?IsiUla3;iB5Btm#ncD;NC$|fQs-j}=|ah*lX%0ExbF+G(Titr zC|$HcrCQA8%G*jt_;TtpVk?5L?f-rPotiHN57 zwSU>Z1k*_9QMa=Je>*K z%N)$5nR24s{)~Vt%9YNJ+f2*}ZAXqN^^MKkoK{EQU?Gx}!wk6vQ+`YzL$owCD1H4d odJMs>RJGEAusbTb`Nt5y0Jljy{ATtR5dZ)H07*qoM6N<$g1y9^7XSbN literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-20x20@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..fb7d24e9140496d9fd6aebd8e94ef4faf3e8cc6b GIT binary patch literal 1348 zcmV-K1-tr*P)yG zY)nx$hW`k%xiRLtwt}G4{*bb{jwYl2U}CSVc8)fev`x}Wb3V_NOH-Q^)?Aazr4Iy> z(B|%w=Xt*G_kHuetc(E}A`}v#wWS%p$A=*N4gS79&|_?<-w1Br4UhUZgBKqz7oYKO znRLJe`u@9bE1+U12!#~9%MH8tHo=~~ucQT^1Kid5Gwgcd`GNyY2Y&W^3wYM9o%Oyc z^~I@gzJ@JN`3fGEf*2YesZ*HJX1MKQGrYLtIWS`U@MBjp(+aR43_#Fj8isc(%QYL?ATyY)~Y(8m}ZI2Mb->ecsxIe2UM zFuf>}kXa-5?%ZA$fw^0!|M_p!H_VSitGR$(mq$rA+}3OER1soX`Y3E8rT=0@WM~r*A|S72bzJ zwyVm4UjhSvj!!{jxeaJ#>enfDgR|>Pp{~@rXuvI4(!JtLM#cE;=gRDKSPw7s2F|0v#1e86 zlFY;Q6?X7ong1G!LnyAjJlM<()S`l4z-u4q4=dNtxo8pSn>d!LN~^~4fe3v1*Eon$ zLNnlx?&Dx6mVl%9xp`M<$~1v*D7DNgyAuNw@X7V@tUtffW`=LqyJ0Je)lh6I|Hh+h zbW#zKEqFtN(dn#`sL_|uv$0GWVzLu0E2?yS!LWS;hs?X^m^)>ahi=9~kCirgrGKA>uXNasOXJMdyMA8>9*~AalIMF4|7BI@p!QaOg zEMPPSw~tK1SUjN*U;^D;RiaGw_>`>D#9-gG5D1Z}tZb$i-PNmskUmyCNQ^$jtfqnn z#^P}Bw=t!kvyF%Du)se!6kQRe`~uT3bOfhzBuF#vYuaX>(!ge_?a^3fgLA{t{Lhw# z8Y*T95{J4nTEmhe(-IGd0@&ma!5`?X47p6Jnu{~&m50$f?z}2$ z4y&t$_^oJE_Th@L0Tn>W!SKy|*1)X?_tWX`Ugw{>ai;W}Iy z@uP*xW@2HOh4`i4hqdZq++1BW875YipJ(?t7;gI^ZZ3Vyx0xvxU}9xNF(}vG?~%h{ z(a_)=$IF+l1{f3VA>PyUvf;B!jKr#J5T`>OM$WRD-Q)&wdUEc&R>PJwv8g9qlGc*W z1ROc`h2da{DfMK_oMSuzJdE}9liq?aZz^NT?7YW#)J#*o?|7$X$XR-y(0`NNh=s1^&=(yK)b}?bN3#>L+?aUWOZWfA`ozol`o?m{~2>;j<;H)!Edr_Z0{3X+9d>(HqKHsq^yL^Gv^3>dVU za7r}8-*3e}YQ*J_Yt4sO;0c}#i3p!`Y98&TpT!y?=_1ytp>8YJT24bNe{lNUZ|bc| z!4AxMgocV{dA))QOKYtcl0@?}`$fjYXBmfR_&%I-;l`03i018oWkx9#RZENRK0Qbm zJ8tKZD%Q12scgTfr;0$J0ul$1e$3XAMN20TP?K?y>rZtYi=7B zeid=^+s2NtExj3bL8jk4NN%>N0c z*J~)0@wgHHB}2WO49pO1-#b1!qAn*A#0W26F2)GQw)%JP2T8lb%+_Zd#v@y28MgPG rfAJg(^UwCwYX)N>4NPDCf0p?Rt={h>JM^T;00000NkvXXu0mjf63H-L literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png b/companions/desktop/src-tauri/icons/ios/AppIcon-29x29@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..c6a27983da83f4b723c0ed382cd4e86c899dfab8 GIT binary patch literal 1269 zcmVtrUGnauOL_f01mO`|sR%t;pHF)%P^ zoxRW6YoFQIs1dLL2K)Qb*?9rIJr^<5KLD?90{MheWg`@)g4+70u(Prn6vT4hK?6QdH3egp! zD@5}ja;_KfpxuPex39(9<#sqsvbYgFzkLlHMhQFDn&6&};fyOFW<;ggUqC~#1vTr; z8TlN8t7*;{#@j+=#0#ZX6vz^Wrega0Qo&2K zkuEY6-`ou4)-Z6YwhsF6`1c-M^2{hPJDykXU!|YlMoRNkP)iDY@y~GnI3q4M&tCDM z;mQPAy7)Z7;*xZ?c&x}w>8jz|TjBijx{B365wxJxGPjRDTIg5fp+(i*x_b-Id1E?% zOg*2`eI!hruE{u~262LF?P7$9MW~wmZ`<`ieh_8VeT4`*l9acLtlL}HCQ~XkHAN=; z;EP}^6w579776Ypm83LfF=DsW>j|g4fsMshoOK6s*N^f!Jh^4H-bW_jzHJBF(_jbL zjJ&(q4l^alk>Pmm`q3sb+X<2^G<*gh>Asm3l8o0j*bd@&r3gZz0!dBufx$_f9}7a@uOwzlhd&n2%BN-hUYE@U|5(TfbWszX zshjyFaj(1bZCWbjrL@#3d_c@2Y)L5}hb>G*(sB2p`ef`X?ascs5Ng zYvM+;UgPObeT-FAUD&a$0>Y26@;^MDEgG0pV!nR1d*K=M#-`_mNnz%aJXb&O;Q^lC zJ$PWha1Rg6>qq$woYzZHpjdc#?f)dX;BzlXdj6${=Iy&WFKFth3vRbd$~VWuW`OG? fmykO>?~=e@_BHJmaMSD^00000NkvXXu0mjfCUb7x literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-29x29@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..c6a27983da83f4b723c0ed382cd4e86c899dfab8 GIT binary patch literal 1269 zcmVtrUGnauOL_f01mO`|sR%t;pHF)%P^ zoxRW6YoFQIs1dLL2K)Qb*?9rIJr^<5KLD?90{MheWg`@)g4+70u(Prn6vT4hK?6QdH3egp! zD@5}ja;_KfpxuPex39(9<#sqsvbYgFzkLlHMhQFDn&6&};fyOFW<;ggUqC~#1vTr; z8TlN8t7*;{#@j+=#0#ZX6vz^Wrega0Qo&2K zkuEY6-`ou4)-Z6YwhsF6`1c-M^2{hPJDykXU!|YlMoRNkP)iDY@y~GnI3q4M&tCDM z;mQPAy7)Z7;*xZ?c&x}w>8jz|TjBijx{B365wxJxGPjRDTIg5fp+(i*x_b-Id1E?% zOg*2`eI!hruE{u~262LF?P7$9MW~wmZ`<`ieh_8VeT4`*l9acLtlL}HCQ~XkHAN=; z;EP}^6w579776Ypm83LfF=DsW>j|g4fsMshoOK6s*N^f!Jh^4H-bW_jzHJBF(_jbL zjJ&(q4l^alk>Pmm`q3sb+X<2^G<*gh>Asm3l8o0j*bd@&r3gZz0!dBufx$_f9}7a@uOwzlhd&n2%BN-hUYE@U|5(TfbWszX zshjyFaj(1bZCWbjrL@#3d_c@2Y)L5}hb>G*(sB2p`ef`X?ascs5Ng zYvM+;UgPObeT-FAUD&a$0>Y26@;^MDEgG0pV!nR1d*K=M#-`_mNnz%aJXb&O;Q^lC zJ$PWha1Rg6>qq$woYzZHpjdc#?f)dX;BzlXdj6${=Iy&WFKFth3vRbd$~VWuW`OG? fmykO>?~=e@_BHJmaMSD^00000NkvXXu0mjfCUb7x literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-29x29@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..27ffb834e4ff3de3b386785c93031e8c5f8a1cfb GIT binary patch literal 1914 zcmV-=2Zi{FP)jFkDPF41~BuK`=4Kbpw|`U?j$fqKQ8gNnA8Qf?`0T!308r z3JN5OGH@DhYx%1n5ke4Y5tL|X8zG{w?!wlt+wpt8Ubb#+pxt-6_UwC-<(#syo<4og z^Sig^i!v>z<<;dmY(q6)F7= zf4Gf&-WxPpQ8i}{J!@Y=jc;!dL#`w>aa1?Hvz{7OJ)a)AZEb#9_xfw( zXgML-OoGQ3ETjWp9-^``Yr^lF68)q5@@0B@@d8QgeQ@^7XoxkYnkK99FAC$xj0{VgNAAO+3$ld$dZpn&3oN}B{a8#Wq zPsr{|hvWti#^iGUs!2M;!@+_!&r+QKuFvaHBpqTA2y|;IvkWD%Izg3HfGRh0FnoW{i(Wojl)47EY(bdK5>#1^Tm{OEA z*Ov1g8i=(OB1WPXiAL3D{v&T!uZ-N`f)lGKoK*hBPG^;vr7Vbmue*T|6;1pWGcG z^7r)d$MdXaT4O7sxn*IAxajZU#N#N*6$FsnFu9Nxj?0gceE4QJ{dgxR$rT0Y@5>cM zWG^q^k8d%p{=#>U9RCDpB`e=*%Jb>$-7xXd=+9bm-+H)&->N^|4btb{E@2leisVHs zl1uUwDl#cFC05F5*-3jn_p&CEEvkvhMFDM@T{`4@ z%P+L@pvcNn%vKa%0$Eume}9&R7yR}K1zACEM~KM+yQBJ>5And#`g@{(shize6^!7@ zQgsN)^%DzuWTQUycK2q5Co$jJ-*lkj*m}Jqd&vcytiD8o0LglaaIE(7 zL_#B;^cIZ^Yb|P9Ll`%xcQ;*Y=OjCC2NSwn`yREjQf@3ye0pNVvb~zcK(V}$`&v`4*r>je*jO{uKMM6TfS`#!7Xn)Z>fLOgzJ0U+(eg$)X#|;w4=2@kYgCglM63lGwZlAovf0{5v~Oq50B{7?I7jsUNI!BZ&?G7NCNauR+MB_ zB%C}IQWcHrCKm8F-__oed*q_T8cb{^l2!8SG52q3;0KMbndKz8@N(Ni7Vds!GFc^q zSsCy~SwM1vF;i(#0U4Ix-@KmyH_-W!tK6)btZIlY9JTs!~j%t+Z~_?_jDfsHdt=tANqQ& zvzIT6XB)9O7r9jyHP*gH@mqTj$-hGr(fJMB_7rJp{UJ?~D*~`6(N0IB7q(?KZA&#o zhX&UThL9_ZC1F%;+m{K=U@%RcS|KA>2Y^?TZCZt7LI;3XDGPeaLXCZ?{6st*0A3Z3 z=Z022uTruh80Rj!$%K(8l~q+TsR;!51h2e9tEpWo84)Q75k6z13?7?Xsrh{Jqh3Xn zVCR2vD{8be6@Y;}#tq?ER&UqX$MH*&DeYN3+IL4CN+_gt3EwxI5Qvmn^Z-b{x4e@g^yyyF44jI8IL@eiP(9om%&O>9fW~2t+M`Ixc zDa}KRN(97t$F5IEI%h-6E${+1zSdjJ3c07*qoM6N<$f(V#_ Ar2qf` literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-40x40@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..31688c2b98d48d758801e683b37171ed136f63f8 GIT binary patch literal 862 zcmV-k1EKthP)R6=cO`5lTN=T}ffl?F#ZD#FE}|epGrZ{{P|~!N=)y=1 z=_-u6i%N*~gVVw;`q`|(q@U%Sn>tV5CtChoIFCE_5j_{%&c?nyd*A1M-tWAhs1RTl zJRUb%uQ$Tw>P64fCs+yWF4%*doUJH5Q310#YtCofY=YGn&f!M$HT+GKm7hdS?d638 zPgOe5+1>^gU^`lj`i7>d*F1fwI)7GSg&YOtLOD%E(Cz-D?AUGri+KL3AA(WVRlj`U z1X}T!o_aI5r7p>dDf? zT}v_z$V-w`ucMjGnDqk8vVg(RBo3rWSZ@&UU?_~oU&pi|Xid<;6NGux zO-62#4sVAiuq#=_U2h28zHn>^dJEIxpHR@^4x-gFs+y=l;BeKNh=W9%&hO(m?EDrR z4VLu!sibgbLp&bsOo45UUKPP$L_rsc+~8;gZC^%XtHDGa54%|pdzJwvk;5|*fh+Gv z$n(;4b)2j*5Qv~Bitni^5?TN>lD5_I)O9xzfAzZnJR~ZM+6Kp=(Q1Oj<=4K7^OGfX zvrn+=G)fD}K2~-UPqDplE`BQhI(4K07v^Xwh z8<9vfNIj0Gwj;_PTuXdW;yHW_Mq*EprnAmsBIe8EC$Sq^94$+*8>-uy>;ya<3Tv}j zn9*7%v4|=XwWa!C?IsiUla3;iB5Btm#ncD;NC$|fQs-j}=|ah*lX%0ExbF+G(Titr zC|$HcrCQA8%G*jt_;TtpVk?5L?f-rPotiHN57 zwSU>Z1k*_9QMa=Je>*K z%N)$5nR24s{)~Vt%9YNJ+f2*}ZAXqN^^MKkoK{EQU?Gx}!wk6vQ+`YzL$owCD1H4d odJMs>RJGEAusbTb`Nt5y0Jljy{ATtR5dZ)H07*qoM6N<$g1y9^7XSbN literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png b/companions/desktop/src-tauri/icons/ios/AppIcon-40x40@2x-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1df84a0fe85e67f2b5715df258de2fddd464bfde GIT binary patch literal 1766 zcmV>W~5_4qEm^97c^>ONZdrD^Mkrg zV`3!wW$aS2ndpbH4@;J8z_JT&Kud-$Qz?$cjZ0hZExexdw1pPRXvqFpd~5qbFQ2+ zh8)rlk5rVVf|FSE{=CWHD?gS;R~>Hn246_8bI(YxRIIEzkiy~JPS>2rOENTYX;0+0P!*FCcG${3r=!$N~V8Fj&(VgZIyWp)y1umqtTxztKi3t-eL z;qbp>u>XcMsU@Z^T!an(2M?rR<3G1kuw$9=6^0c#mqMUgVLB5`wEMaHjO%bPJFgIMbaTb+4&FI7q&s-0qT9Q_bR3Tl01BTn< zQ>-3@c$i3&Y1HCi)TEpH<89cZ?(G_qtb2JVMTzeT@a^BjAp4O%t*C=IK`shW*7W$w z8jHkbP3hW1_VIk5TfP9v6llefQOp*(GMP)AeN>X^NGSJhLi6YypOha!0{0qDL&y*U zd*r}oo=Tfofb);Fva+AQfZ3l&^D)v~cUNp&9U?99$ z8$@V;vmIV!kI4ccL85sexvhO{GNuMGU(2>GIq<(EFn|_`2M7?4n@UtL*F30YMRS|e z16dkxlT~LQiRq;uadCAPjj9M9t+$#kS4>?u?f|~CJ{d~0l5iv41E1rR`Ojew@T;@J z%y;`A*0LS9Ml6&M{WTKP4fMDINcrwNlb&@MG$d%aH3t0pz%ca^I(+r|6xLz9f_ocF z9kQ|mXvUVXmbHWmxH;-!1I#Y$Xx_59mL+6mRmRZxi^lvQ(Di`cr+Mb?1XNxdYX;-pT?0MH>!~ z%V#2Z_#h8qi2C40k*~nTu0WG9k5h=YLO^zcwY^<@=IJ6%BMO~4+X4zYa5XzSm*>F< zMa+mD>qN}E%Q=w<0yGotO5_AF6X&$0TCl&74j5c3gw+Uo)>LaD_@NvWHgz?Uw%6+1fJv@G|r+g&^ORG za$F0;3po$;L*(Hc9JZn0EqoL_o>q&;{;pbgCbuQS2+VoW-i$!B96bU(J>9};DUzwq z!>W~5_4qEm^97c^>ONZdrD^Mkrg zV`3!wW$aS2ndpbH4@;J8z_JT&Kud-$Qz?$cjZ0hZExexdw1pPRXvqFpd~5qbFQ2+ zh8)rlk5rVVf|FSE{=CWHD?gS;R~>Hn246_8bI(YxRIIEzkiy~JPS>2rOENTYX;0+0P!*FCcG${3r=!$N~V8Fj&(VgZIyWp)y1umqtTxztKi3t-eL z;qbp>u>XcMsU@Z^T!an(2M?rR<3G1kuw$9=6^0c#mqMUgVLB5`wEMaHjO%bPJFgIMbaTb+4&FI7q&s-0qT9Q_bR3Tl01BTn< zQ>-3@c$i3&Y1HCi)TEpH<89cZ?(G_qtb2JVMTzeT@a^BjAp4O%t*C=IK`shW*7W$w z8jHkbP3hW1_VIk5TfP9v6llefQOp*(GMP)AeN>X^NGSJhLi6YypOha!0{0qDL&y*U zd*r}oo=Tfofb);Fva+AQfZ3l&^D)v~cUNp&9U?99$ z8$@V;vmIV!kI4ccL85sexvhO{GNuMGU(2>GIq<(EFn|_`2M7?4n@UtL*F30YMRS|e z16dkxlT~LQiRq;uadCAPjj9M9t+$#kS4>?u?f|~CJ{d~0l5iv41E1rR`Ojew@T;@J z%y;`A*0LS9Ml6&M{WTKP4fMDINcrwNlb&@MG$d%aH3t0pz%ca^I(+r|6xLz9f_ocF z9kQ|mXvUVXmbHWmxH;-!1I#Y$Xx_59mL+6mRmRZxi^lvQ(Di`cr+Mb?1XNxdYX;-pT?0MH>!~ z%V#2Z_#h8qi2C40k*~nTu0WG9k5h=YLO^zcwY^<@=IJ6%BMO~4+X4zYa5XzSm*>F< zMa+mD>qN}E%Q=w<0yGotO5_AF6X&$0TCl&74j5c3gw+Uo)>LaD_@NvWHgz?Uw%6+1fJv@G|r+g&^ORG za$F0;3po$;L*(Hc9JZn0EqoL_o>q&;{;pbgCbuQS2+VoW-i$!B96bU(J>9};DUzwq z!6hF7?UaSws#4s~vB=`v13`)eX0sf%|H%R=$WP^Z4Q4$l45+xB)=3CTgWGX?K zuf!3yAu0;njPW1XMwAfHZNvvr+2*Far0rhq)?LrJx9!%o>)J8e`+YmVq*+-9*}cDe z&i6aNbG{F=ihwCVbJJn%b+WYuE;=s4k)}pqM2{s!OJSycCM?J=fWo3>+G{GHTnfQ8 z)w|)_x&u&;FUHiPxMUTStzHK!N>;{~;L2S)Vb{(b(AjaCS%?F2=3Wh(o_ZR|UVC{7 zZa8=Vwm!R=iA^BU74PkgDSbypu+!;)jgLOesn{SW#kBp>tFM90X1{cT+ge)S{`=N& z)EW%=1qHDEoev-nzfPK9BKMxVOTgj0$amC$u-j(BzWsF(Nj5RL;)1v#j)<$62xfBS zAvq*Plc2i&wjy401zeN&?XM3JULg@oYMIFu53Lx%TeobEoT91D^Sv5--#aaZ{AN+>Nl`M)$PX!33 zwjV<-)Kph#+Dd6}KdmmlDUa`ngy=r!0&{cYA-)rmf=VV+b7P~*w?bNiRdYutmqxOp zt>tGh<8Yk9w*n+>L^wd1lV#x)Eiue|3z%RA6U< znh8E%G!yK}CMH;PY?wR4_fFAYivHDZR-iOHjZ?Jfuq7+LcWTfVBKn35Ge@xK=;$8~ z{{e2@x?^?gZ+hVO389DQqOV-NHWsql|`g#6!oy-?fT5A|o< zLR?ppWk$;LOfSG0&oGungHSXp1#D(LUDtLGz?$O@INk4sYcWk*@cUUQCRmO?Q!+aZ zTwWDUA>kaslAr{YHoX?wMpE@ZSW4BM9l(dd4Lw7{Ot1`~wfMog=|M#IU^Lfu^&5_8 zCioISX}k2QG$@{(8ub0Tvu^mLy&t;WUM5%uWTz&<`rP!eMDOhyfK-zLPt2R4eeOWf z)_~uC?zbLr3=YdnFnPW&TxUTe!Gu=e5PaCthoy>FXre86EoIrM7}e>Jk)#VXDzE7Q z7;A>nHQ)Ke1$kBUzD1c(l$oLty$IK|4Y|6Lbh5__?qM;LiU)u1R&eu;`{3? zW{uqRq|n_#SEibv69rlu5-pw4K{aQ-HPjsi%)*6ADU)y?yv>PK5$mycxXnvq!p=j5z z@uU-uIR>XknN)_Vlh)syDAuGm|L8@<-5-|{W2*4^&6)Usn&9`oVc7k5A2j2G9k1Wt z5MDGzb7gyep?gvxqD*x4`5#4asGocXMha)v4#3=HO zXnZW2r9Xi6X9j>9{$rH(13%$m*T*huVk{C0Gxc8Xc5KQ=5^V9t@>$7X@tGWTXFS{w z4omcaS1TvT^`R*B_iDo%C}zzjq&3_Kj@Xv@R;_=0!a1n*mN|kWpaxrUw=|xEay029 zl}fZe`yDYN8xq)nxHcon7rkzt$%@)IK($Tl^IIs$j4Z@RITL!ZPs{{QLWublH<=&N z3bA<3H~VeYLl-SG_E2#BfPe?JEqyq<$$KhZ_zU3#v2A<22_K@nM{! zmuB^6jWJzmHnM;shbkHY^*9PBBg}JAV_}Xj%N)Mo;mv0v3MeBt77ROLqiDde`1H9Ao2nSkAN6$r*fTH%_9k>%f1ab+p zQWWrxLoQv9QI1|p=-8~D=Ja`Ik;Y)xqiSA{r}EGSvpK1zu}iQvW@MD*g}0B!SnF=P z-V!*Bg}Ozw;d=!h(n1d^jTcqozF;FAoW=x0^qal6U2hFok>oX3{Ml#7V;lIKEQsBL z+%n%1`13n|^(MTL&je3VN`CB`8G!~q%~x&y#RbP5gM88*NN_B}t2bB!laaLEKHBCo zd`f15A-b6=O3D7Ys=a8HR{Yrud{(6d@RvaTluX3d3393(6Fk9s`^@}|K+BI(@++r$ z6FxM@1jFRIKw9leo62zu>M=YxWed)H`AeWioW^(=$rWwAWr9K9Y`z`alp|^iPsc$g zpbJe3%*aBOKuj=%KbtE6U$X#hWv#ol_Jl*I5{Lhfmtz0M=tPz;3LQ0 zsG#zHt0OvsJ3NZ73#9r!8fe_C=8`i%O~-B$ZS5@0PS@7s zR_sL+=_2)`nP3JxSxhj431%?Cj6|17ONy4V$dVMqt=Vp~^PP|s#H}f>;6}a^l7hH3 z6&5X1_*TdX;#N@+*;iQkZb*pkb1vPBzN>g83oXe|TDDdVG#koRuj4c=8H!6*X`E^) zn%>;9Fi-95Y-ge+Ku%7sdh|q_64Wl-^wiT#t|S=EWgvn%isg&mkcR(Due{FW3XX~$ z?QO!luKk)sbW`)(YvD7jNs)@)AtD+;Jx#=)G{>a0uuoGxvP@Oxr6q+)E{ixYf z1#iB-jfsvUv1C7%tyvfI-<=e}el#3B05#QBYD4`&Ci*g^V(JQ!0X11%62W8BH@?)i zmY>0aX33GJMkXC%8<3+JDjKt)NsL(1a3uT>Dz1cOmjo^D00000NkvXXu0mjfMRndl literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-512@2x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-512@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..912423842e90908f77c0e70c7611c37ec69b11db GIT binary patch literal 24827 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 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-60x60@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..20caa751e73385fb3895ac71f79eb8783566ddb8 GIT binary patch literal 2589 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 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-60x60@3x.png new file mode 100644 index 0000000000000000000000000000000000000000..3f77dc9e9af6c2077f00452fc786c2af29b6a2c9 GIT binary patch literal 3695 zcmV-#4v_JQP)yP$mY~Z|mC%M?u7QsXM$E_Fci>M{xcHgK z3wrynx6q-3f5fu@M#Wmc^6G1m5j49POTM=d={G>5o$fpFL0QNyh&hx8+k}Xp`c6K? z3`QN+7#mJDprc};ZR#&r1b-Ps+^;_IL21x=>!1V;F@v!d^(Pl=gK|I2!T8KS{qZu0 z8H^dMyC*gRBN1Sz1p|it@Nmm!tUDQHU8x1NYvuN8wY@ZyrMT9FPkhD)B04RyuOdKt?NV zbJFdgpbsDXgM#A$GE#i(h~g3ya*2D!^h}%#kdjR66#Dc+?U02oJN0**3Xm2~WN>AB zP|)S2C2%)@)KpcTQ3hR(j0aM1B7kHc**Yx>Ez$0IJOD`2PCSkU%|g&{H-Jt=eVL-| zH|uZ`Kqr3mStZ(Dk5V_#2W*_98o$2%GL8c10ZUpcMdKKNo-n0PNJdZyNH3Etu+V_? zG|2)%16=_@0|*)b2pRwg8UP3yK+pg{&;UTt06@?HK+ph!1^|Kv0D=Ypf(8JB1`sp= z5HuK3tO-#xXIv~<;t;sE#{vjiIvfHQ!hSRb@jL{raqJzvUR)0E z!d{b=KnJodG%Ga*2j@goa$@YuI0kLdEHW4#ezX_5`V?yDRwzTb9g8N#(JV`h>WN%$ z>xG~pXl+^OED`Kw2XH5Db@oxOQ?iwXT`)eD7EO$&7V#HG7B&Q}F}NC-8e^s>ytoo< z!IxiXqWAyTNfwd5%W}(fB#WOpgw1-d>*!OhIiauu#+saz7%H3?E39>lYPlVy@22j) zAMcj*MFl`SnK0ow$t%joz&XX2SEclU|}y6zBl{RNl_-R!amc`Meo$L z3wNa#f(CMs9Brnej5s0g1h2woN`;lzv#=p(oeH~PdzFqwe zK?7-EH^fN7hM)~X!Gu^^nh{Umw0JMX+!h&;x9aax*;oBN*PLnLCa6LYaB@Qw4YaQw zwzsQ+CIn5R(*&O>aa3ww%c^_b^!v}+#P=?!Hpt9~uaw^uVJM;Vl;7qDlhrC@Ff0+1Gg^B`G)vzz>;tyo=Y;TiK95}#;2*9ez9j;_ ztF65uOxVPreKexJwy-ojY zs<3%(5BR3cWmSTkH|qAsP@e|~+K=xR&SB<=w#*Yxq>0aQn91|OaS?lPSD)u_f$N_+ zAFajsMCv>=yA!Lj5`zw65I6?k=~ps)9Jv-3hq4^i!qxcx`Fn(Z=n;26y6MOl9W+Ut ze0CT!c5Jbq%S;eYoQbZ8V5qYXMN{U!cztv0YP>j?7-6X$#=2eKNWz5_ej5zezeeCVgmd+< zP4rTqyxobZ@3G8+%>TGSR0~_ww@3tz6msRzd6Jw517w2_>RLf|NIcEhce^z7Yk2pWMSN!x8Vcl^+c zv_b@fG7i!jx($s3$JrHMBXCr6&#@lU;e?q&d0|0v;1_7&xWCQ%heZM6O^xLBO=M)THZ|jkIs}k10`)PLKy%fF{AqK+H znO3avW+&dTlTKh zfz&Vu26&2tQ-HE$Ib?4{f}ni{MG2~}YmE;tq~PFjy@b0!WN$=*pvm*1OLi}E!n{$6 z%jes!wMx6KN%lr02-+#&+b5=5!rdz&OTef1MkK=vdZYrr!;)45NoS2nh86Tk1bm#% z+NP5UalJGm8B)+j4fu@Eh-4^18$ICj`yx+IaLqm6?Sh~Y@X3ju8j*A@Xas!5Yedqe zpb_u|cSZ|DR^!3MD0((Mo_fTFQIU3*z7a`xg64p4hp@`w3R#ZS+ zkABfZxTjDf7U%7msbTxcdJg!U*)b!cpcCG_^z&A&{Fn8hbv|d_;@gu2W0gw zX3p}sirI5LF5hy+0W@3=8rxN`p^Dik0iQEPlLH0E%HW_cXNL4*rKJ;esJo!`9`I4n zchM+*9}7W`5%!K=1biVyNx}dqioYA$;Bt)h5G#w-*B)r21bl($bi5%cUX<&0nPxaU zNe~o{AS*66c}!#*Dd3|Zu0NidXb$KV*u8NoALCydk{1rO}2!-^iYz^N&?n$tni^Hs{x|ok@;D1sY!Q+05gT8 zkA|b&qKK02)*@RX8jtpWexIg_v>f+vzsAd5@~6MYiXPNo7dJh(|_A~40$FHw50y>0kOjO;Uw4M$;IYwdhu$j z^t&}6XfjqxZug@p9xle;L`8J}=WT{R9|&3^ejtVy)=x|Fa4~iYe_2msR1mZQQ*t!0 zDRk%cHpBYM5H!h|lH+TN-TmdVJN+p+Bm2t`H0dKH_xg=C-OU0*(1es6fS{%Dmv_%d zc3E@l@R!%xTHs~k>lMU9;TnduN85kjw9&YYu|eIJ`Wa*DQGY(Tc}%96e27L(n>gC#?M~ zbm9FNyl(_R&{DvbcmBBskIUiJMUtXYxa54J;r8zlDutx0+%$_BE(Qb*0HLq~AZP#} zXaFE+03c`pTu4btRm^kp=OW7ibV1I{Tv%wJFU;1=DL4h74-1_wEVOk>7ES`_0!vyN zne+1Jp`#q=0@kvrsi{$!F+CHf0i@^JXZ2*ZSW+oFCkL4hAT_LE*0S0@nwK{ZCj#Vl zHwHA^FX;RN_|ZUe|G8hoCPnF=1A1gyrqa^V1YrZ|$jr=AKD}r&xlFA5aM^O443LtA zix!i|2v*Uu=U|J*I$rIau+lRg+o^7|cUE)7;!d-}**2^3s5`I+^nC zOEwdix`W!B#1DM^je`&~&?Kx)Z({bl3aoJPGjKhCCS48IrZ2p@9L~)D?i|HdTMKIq zNDCcxAN{M^G^}NfCqFDVp}iDnLCmbhK#vP(p&7fqj-D#WLy!nG7X*g2PS6 zGx0x+;{}lxGixsxF^9Atnsa{qDuMd}BI#zRn)8xx9+8FT{3?M@4I=3?2Wy$b-r&!_ zfFn($^Z<=wYK}D)s+fZg3Vl(2s)W|9eICIg7%_jDizfc^%%gh83jbpEabT^%NLXdo z(s0B)Yzys7RpnXQyJweDU0DHZ4JecrQ0|%wCGd-gpdoA!s*t%=*}b1xbAonYtEr*l zV@H&CkNuV4V*?tP&gOwuo&g*wiySdQy9bX|m1mTysk literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-76x76@1x.png new file mode 100644 index 0000000000000000000000000000000000000000..0eece31625c3e7dcf33d1bf5db0731f7d12c6bb9 GIT binary patch literal 1702 zcmV;X23h%uP)v8Q|1qW-ZEu@aPNZgI<5+QJ?r&~TyG$1KfR#6E@k2OPi#lE!xLyu}~cxA(biUscdlPyqES}Gk_=hYvf z>F{e{cT7P#8iwqdbDu-?!6%pejF0|C+of;d#pj<(TUcoL-jM;aSkc4q7tphqEm-T< zmm)kZ3=DJDo?Ru3P>k@re5E6t)50N%37%Q(@O=BNW05%wLowZ|;c5)opu7qL`|8W{ zj7l6sElP9PK!3k@V1GFyBEfF_G6=lI@`K1wNHYu3rz%5c#=FBdE5~reH=R~1s)ue3 zFj$f+rX9F~JOc~KRcJCb!>~F{#(=HT7%&Em0b{@zurxAWNe^3=n*nuO^dM+>=3(*d z#C9VbGU%ciD!hMej-hljVv7(x9vrz-t zBpe4_e(H6*1P#1ILiN#X4Y&xj_J3!feZ&JUe}Dm7$q*dow8~8xVQ`jFFN}ERQXiN} zuUePtg;uClL4A=PYVw1*tz*msy%Rn-{9q>a%=VF4xL}=59bgxC3iM%ZvjJ!tbU^Q< zciq6%ZP7t3DGFwT8hCzwMoNxg>-+7*`#kGkC9(8}K3S8Fhs(Rk0qE{rns<_I7>Z~A zc0=Qk6DTV@tDZmLFEOZ7LsnoC?wj;Qe6KuL1K0NE!kLn68mh^*+fE<+;}I805^{Yj z>KWHnRvC2QG%{S_@13yhb6Djvt92OfY^=o@K!5a@{C1l=JMfy&Iii7JDUgb|K(BrHz zx&h2@Ma0XZ3&mQjmT1EJo&a=>dzCvt#E?6_%@7v2iw$!e7K%i42bk=!uWYqVgCZxc z7-Fv%XTi%w`XxWZ&_qye36vT8@-zug_DKVVISKWc5EW)P-@-;1VI{me7U1l3*&ijaP(DQ41=|dKOh7G6QWHDsDIl+Tad;XOaR`E96uk=NLbP>`d5Ow*$~OMN z32Z&_W*=$2Iz~OI5A66hW6V62$b=3no+@S1nLjCxvd!fZI^Kzfg$W%s7m8N7XJf%r zrC?jERmpd%q)ucTgeo5AO>!pmSY}Z3!SM~9d2!*{7o_@3AH58Lg$^l_VOz7*(EP9g z9uw5CSJ{sG=q9tRlLVHWR{<1RB~hj0Fp&-t=QY(wMALn8 z*A48-W-MSt8qn#+qL0$)7ul7~1SWC(2O!O70H%1Zli-j>9FR-w-93yF7D6j>**44 w%h5ppEl_I6V#r}5jlChZJ2k|cq^?%}A3PVeZg$KhtN;K207*qoM6N<$f)}PNhX4Qo literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-76x76@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..3cc38464ca32115c0c467e50f1ec7b86f306617b GIT binary patch literal 3215 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 BE7br1 literal 0 HcmV?d00001 diff --git a/companions/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png b/companions/desktop/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0523d9892cb918bd4e62ad8e5d791543d571fc77 GIT binary patch literal 3561 zcmVTD3&gI#HFBW7EXOC1`^N3MFgl=tk%e z-AD^7gq8lov1A2_i6*J`$F$~PFm<#fMu?$O;;7J44(Gmqd!Jw8@UvqZJBjVz@%>07 z*f|idzkc5LIbVw;pa|e}Hc)L%6*(Jj$e)?Zb@k+Gx=%ozq}ivEJ>5>Jwp5y(nI--{ zl`=EtP)1f(ME-qKxWw7idh&!U zN(fT_*iusI+1%$TZ^cSku&NX%i*n!IB05)7jaST|EL@aB+X@OtO{60hr=zZp-gvzL zq68iyEk1CV?Dq8NiSy0Zw^PZXKjYN^KG|BZgVt@>L@6n@$cdA!<(601Q@!{MKp--O zXTSg5!^86M&7uB4>B(dC^i$aoCI|}F$%`-K%DM@w<-|4)M#5V~dui>ujiWEl{LLGw z?61eL!eB%!e|fcVmfjEQdo2(qh$LVt@9yv%PSkP znjkFffA4*fSy~;sI5}LzUO501#?}8lC=zkdl0yfjptf}`;NYMs0FmUvMaPg_yxCWv9e_xazj=M&ZJoVyq$NQ# z;HoxvhlI~m!dVkg6ie++pQ#vXIV(Rt3t<9PSVT+Idg8d`v8{7w z`?iAWi@vP5^NN0C5@?dtwY_SS|ABbN9NbzPTHV0&zwcTdbLO&9qY9D?CUbPI4GRTvMxNzU z6211Wp<@H=31%VSScnsHiLeut;I-u5ca)j>KV&4s9e_BadgAvnY(?t-FhA8oR*SJ; zo*3(xxZgr0k%PDE=}9pU{k~Ko%$46yq)7=ebgi|QT6!gj6NH!BU44`{CBdI5l|MH3 zNYp$a#u-T#aZXK;MS82VkM4H&L7X5wI6Hc&QN%N!n-K5w>sN)9{P=b!O&jBBU0I|* zos>ZHQex?r5I74JaZV5xvd6~J+Vn9(q;c|NM@tV?e%nd=zHF5rPmYhF+=+3rcb^n9 z^lW<3)lFqLJE-bz7sLso13N`|ljHr0^p>!~A2ha6g%CEx38E&_CF5dgjeQJFiZ}Zd zY3a>&s<_=nEj@h@Cx{NY6XX4gl)d`0uiM3z->G?#LYx|Ao-LMsDbk@oOo{a=(oe;C z^uvaBy4l$aaf0ZHlq;Na#>M&kdb#la-6G|}lV1rz=ZQ0{J6l!X+(Uo7-m1EDtER-s z3%fA2-%6aj-%Tz9#r%SSJ^VH4iL}&DnCiS(vnR#+JvzpB5};Ig|63eQ5ND_?73Zu! z%}ybkrQc(9i9mIwun2EnZGlMrh;)gtq)HEeVw{eFrl_J*ek;E9^NhyT_c#D7L{9QufN04wf#pdT7&(ClBL6R)q zA?0WWhrRU=#GCfT&+gM%f7VbM%$ibv?%#r{q>#><7E@5AXvMRWWs!3B1$c_|pWk(% zWRAj;kM0Yh>c>K-o=6pc#>&S5&`(oOfYY1w;LYFI(FbvcLa7{7?x4_e*RgbA(RkXQ zks^EfMu&TnxWm)`-u_^%2~|m<&;^ahJhwqI#c%^GQ{WbVJB66ee}ow6kirHvs$2$RpDFNl&e_U;|RBQ+x5 zic&icGtIKbP^{M*oOh;;^-ZdZcxX05#1-XliQeypV`GQ0#r4SX(b{`mh7)L|a2Ty3 z_8BX*S$(&gPTlH|Ev6d7aGFnFvPCQw=gPE%(9e~%;taOsauYe*d&wvTBZbqggO6Vl zAtp8F-8Jtiu=Sf?{hbh5bI6{i(VV6wiro9P)*jUtgy^PCgU^wCZsNl{Z~1VVXh=$P z+oMT?j}M8g+W%U65$B8)d~V`4hXplvLJmFYJ=4T3A8uOZa=LB=;iDzed{JG)BK05J zBSU4HmQHxk@yT_bS|~);^+Agron@chlXJ`Lk%krx&`+j`>pdqa9L92u^cxu>b;3^5 z)BnC}Sf`1-bT`~m%$=KrzI8ZFq=K?Jo(1aWsOZ$xN)p0wNoS)uP291mr0PyLs*>O| zse_j$KX?;W3r9#x&2EM-*U_`n#3O8GD29e{qZ?r+JZWweHs|Qm5@Lo`C&?94YD|GD z9U(ckd8Z5FB#liP<^qyns_ngbN1R=csJM4&sY0CP8XQ++SaI?Tw`*qd&`HBVz>X9< zRcbC6VXJ9XbY7Ny-2riu2CA603XeN`7^QaXII$?TP->^OA`e50lN}ZorT5)G!N7t0 zomZPRGs%sMJe1QvLJgTgoDp<(c>D^-Cx_$gu%+a_ctVi}#5tPqa8|aK!*O=l(P4*2 zi-Q`pSma?CagM~v_6?$k=ocuz3^muB> zvm^E7?0Ca(RL|W*@)&@`&fzD*dR8Em6ke8#g$_lSdUSTY&zHgMAitiL1aYcic&whC z9r97+>~Sr&_nqwnRy0%NceY#fGCWpq&Q2gwl=rYp`W@+L(aLPj#*QSP!@Bh7>^zL? zZGC<$rswSgp|+h6dWdLP&1l@`qijSwCr^lLmY+A0Fu5FVHbwU<+bQ03NpA6ArWIP+C>;T5hI)G=#BnwBqPRdQJjNBEu) z=z>&>E+ZA_z9jV*bL@G{Dwo13`Ow)W-!Vi3p(Xo3=ct~JIIx^5hWpOh5o#=JKP;v{v3i9T=Yrq{&z(n#Al z#7XK36a8j-5-lDV=Qm7bWFSsLm3s@kJWLyahaI4MGXP9FDaN&}YCG+;6% z{WP9IoEl`vWRbz0UGnJd!Fdznj5yr4!~OIAV2io)Y6}*MaBFZ>IP*=1ypyLVTEy@J z(_qutfv?(VyQngP)kG*sH6A<*)Cf~Z2)FaLT zJ<8j9QY31*-|7j#7ta9XN2EGGyXtS{CX4QEzT-ip;Ye}b^E>-T?< z^%V%8XHIYOH*Jv0N{_>ef>B|4-YTj1;33nfOqt_f#J>>B3L+)UkVYW#FT@rPM!J;; zE6kv`btR=IkI@^2uc5pSgyfa8^YgGX=7_~f16ic}SJoj83MiO3CWngxdF7)jP8tZ* zmmE4My|Z@@1PaJs=4ai8O@8B;VH2ktr?Y|bH?Nn@)l@^IfZRnK#Rm?Xe5E8KDo!`G zHC424ZxKXla8YvE+h`5y7&UP!MOu2|xKvSomf$oQ!`b213a(%u?ddV2E>3UHzP7qr zV!<{xHbAJPpT2SyB6mrK>Vd09oZcROef<}FMwJ|Om&hd?D(8&`h*sqy String { + let mut seen: HashSet<&str> = HashSet::new(); + let mut ordered: Vec<&str> = Vec::new(); + + let candidates = priority_dirs + .iter() + .map(String::as_str) + .chain(existing.split(PATH_SEP)); + + for dir in candidates { + if dir.is_empty() { + continue; + } + // HashSet::insert is false on a repeat — only first sightings are kept. + if seen.insert(dir) { + ordered.push(dir); + } + } + + ordered.join(PATH_SEP) +} + +/// Return the PATH the spawned CLI should inherit as its base — the app's own +/// PATH with Homebrew / local-bin promoted. A GUI-launched macOS app inherits a +/// stripped launchd PATH (`/usr/bin:/bin:/usr/sbin:/sbin`) that omits +/// `/opt/homebrew/bin` and `/usr/local/bin`, so without this promotion the +/// CLI's Bash tool can't find user-installed binaries. The host (JS) prepends +/// the vendored `rg` dir onto this before spawning. +#[tauri::command] +pub fn code_spawn_base_path() -> String { + let inherited = std::env::var("PATH").unwrap_or_default(); + #[cfg(unix)] + let promoted: Vec = vec!["/opt/homebrew/bin".into(), "/usr/local/bin".into()]; + #[cfg(not(unix))] + let promoted: Vec = Vec::new(); + compose_path(&promoted, &inherited) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compose_dedupes_and_orders() { + let got = compose_path( + &["/a".into(), "/b".into()], + &format!("/b{sep}/c{sep}{sep}/a", sep = PATH_SEP), + ); + assert_eq!(got, format!("/a{sep}/b{sep}/c", sep = PATH_SEP)); + } + + #[test] + fn compose_handles_empty_inherited() { + assert_eq!(compose_path(&["/a".into()], ""), "/a"); + } +} diff --git a/companions/desktop/src-tauri/src/keychain.rs b/companions/desktop/src-tauri/src/keychain.rs new file mode 100644 index 0000000000..95b0e83e0e --- /dev/null +++ b/companions/desktop/src-tauri/src/keychain.rs @@ -0,0 +1,41 @@ +//! OS keychain access for the desktop app's durable secrets (the API key and +//! the JSON profile). Everything is stored under one service name so the +//! entries group together in Keychain Access / the platform credential store. +//! +//! The frontend reaches these via `invoke("secret_get" | "secret_set" | +//! "secret_delete", …)` behind the Tauri `Host` (`src/host/tauriHost.ts`). + +use keyring::{Entry, Error as KeyringError}; + +const SERVICE: &str = "io.openagentics.desktop"; + +/// Read a secret. Returns `Ok(None)` when the key was never set (a normal +/// first-run state), and `Err` only for genuine keychain failures. +#[tauri::command] +pub fn secret_get(key: String) -> Result, String> { + let entry = Entry::new(SERVICE, &key).map_err(|e| e.to_string())?; + match entry.get_password() { + Ok(v) => Ok(Some(v)), + Err(KeyringError::NoEntry) => Ok(None), + Err(e) => Err(e.to_string()), + } +} + +/// Create or overwrite a secret. +#[tauri::command] +pub fn secret_set(key: String, value: String) -> Result<(), String> { + let entry = Entry::new(SERVICE, &key).map_err(|e| e.to_string())?; + entry.set_password(&value).map_err(|e| e.to_string()) +} + +/// Delete a secret. Deleting a key that isn't present is a no-op success so +/// sign-out is idempotent. +#[tauri::command] +pub fn secret_delete(key: String) -> Result<(), String> { + let entry = Entry::new(SERVICE, &key).map_err(|e| e.to_string())?; + match entry.delete_credential() { + Ok(()) => Ok(()), + Err(KeyringError::NoEntry) => Ok(()), + Err(e) => Err(e.to_string()), + } +} diff --git a/companions/desktop/src-tauri/src/lib.rs b/companions/desktop/src-tauri/src/lib.rs new file mode 100644 index 0000000000..8a17a62afa --- /dev/null +++ b/companions/desktop/src-tauri/src/lib.rs @@ -0,0 +1,46 @@ +#[cfg(target_os = "macos")] +use tauri::Manager; +#[cfg(target_os = "macos")] +use window_vibrancy::{apply_vibrancy, NSVisualEffectMaterial}; + +mod code_spawn; +mod keychain; +mod workspace; + +#[cfg_attr(mobile, tauri::mobile_entry_point)] +pub fn run() { + tauri::Builder::default() + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_dialog::init()) + .plugin(tauri_plugin_http::init()) + .plugin(tauri_plugin_os::init()) + .plugin(tauri_plugin_log::Builder::new().build()) + .manage(workspace::WorkspaceState::default()) + .setup(|_app| { + #[cfg(target_os = "macos")] + { + let window = _app + .get_webview_window("main") + .expect("main window not found during setup"); + apply_vibrancy(&window, NSVisualEffectMaterial::Sidebar, None, None) + .expect( + "failed to apply macOS vibrancy — requires macOS 10.14+ and the \ + macos-private-api feature", + ); + } + Ok(()) + }) + .invoke_handler(tauri::generate_handler![ + keychain::secret_get, + keychain::secret_set, + keychain::secret_delete, + code_spawn::code_spawn_base_path, + workspace::ws_set_roots, + workspace::ws_read_file, + workspace::ws_write_file, + workspace::ws_list_dir, + workspace::ws_exec + ]) + .run(tauri::generate_context!()) + .expect("error while running the OpenAgentic desktop application"); +} diff --git a/companions/desktop/src-tauri/src/main.rs b/companions/desktop/src-tauri/src/main.rs new file mode 100644 index 0000000000..e23c9ee26f --- /dev/null +++ b/companions/desktop/src-tauri/src/main.rs @@ -0,0 +1,7 @@ +// Prevents an additional console window from appearing on Windows in release +// builds. Do not remove. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +fn main() { + openagentic_desktop_lib::run() +} diff --git a/companions/desktop/src-tauri/src/workspace.rs b/companions/desktop/src-tauri/src/workspace.rs new file mode 100644 index 0000000000..038faca4ce --- /dev/null +++ b/companions/desktop/src-tauri/src/workspace.rs @@ -0,0 +1,390 @@ +//! Workspace file/exec bridge — the local hands the PLATFORM chat gets when the +//! user gives it permission (Task 13). Every command here is invoked from JS +//! (`src/host/tauriHost.ts` → `host.ws.*`) on behalf of a `workspace_*` tool the +//! chat agent called. +//! +//! # Security boundary +//! +//! This module is the ONE place path safety is enforced — the TS layer never +//! assumes a path is safe. The rules, applied to EVERY path: +//! +//! 1. Roots are approved up front (`ws_set_roots`) and stored **canonicalized** +//! (symlinks + `..` resolved, must be existing directories). +//! 2. Every target path is canonicalized before use. For a write to a +//! not-yet-existing file we canonicalize the *parent* and re-append the leaf +//! — so a symlinked parent still resolves and can't smuggle the write out of +//! the root. +//! 3. The canonical target must be `starts_with` one of the canonical roots +//! (component-wise, not a string prefix). Traversal (`..`), absolute paths +//! outside a root, and symlink escapes all fail this check. +//! 4. `ws_exec` refuses entirely when no root is approved, and runs with a cwd +//! that itself must resolve under a root. +//! +//! Outputs are bounded (file-read cap, exec-output cap, dir-entry cap) so a +//! hostile or accidental huge result can't blow up the webview or the request. + +use serde::Serialize; +use std::fs; +use std::path::{Path, PathBuf}; +use std::sync::Mutex; +use tauri::State; + +/// Max bytes returned from `ws_read_file` (larger files are truncated). +const MAX_READ_BYTES: usize = 2 * 1024 * 1024; +/// Max bytes accepted by `ws_write_file`. +const MAX_WRITE_BYTES: usize = 8 * 1024 * 1024; +/// Max entries returned from `ws_list_dir`. +const MAX_LIST_ENTRIES: usize = 2000; +/// Max bytes captured per stdout/stderr stream in `ws_exec`. +const MAX_EXEC_OUTPUT: usize = 256 * 1024; + +/// Managed state: the canonicalized approved workspace roots. +#[derive(Default)] +pub struct WorkspaceState { + roots: Mutex>, +} + +#[derive(Serialize)] +pub struct WsDirEntry { + name: String, + path: String, + #[serde(rename = "isDir")] + is_dir: bool, +} + +#[derive(Serialize)] +pub struct WsExecResult { + code: i32, + stdout: String, + stderr: String, +} + +// --- path safety (pure, unit-tested) --------------------------------------- + +/// Canonicalize `input`. With `allow_missing`, a not-yet-existing leaf is +/// tolerated by canonicalizing its parent and re-appending the (non-traversal) +/// file name — the parent chain is still fully resolved (symlinks included). +fn canonicalize_target(input: &str, allow_missing: bool) -> Result { + if input.is_empty() { + return Err("empty path".into()); + } + let p = Path::new(input); + match fs::canonicalize(p) { + Ok(c) => Ok(c), + Err(_) if allow_missing => { + let parent = p + .parent() + .filter(|pp| !pp.as_os_str().is_empty()) + .ok_or_else(|| "path has no parent directory".to_string())?; + let name = p + .file_name() + .ok_or_else(|| "path has no file name".to_string())?; + if name == std::ffi::OsStr::new("..") || name == std::ffi::OsStr::new(".") { + return Err("invalid file name".into()); + } + let cparent = + fs::canonicalize(parent).map_err(|e| format!("parent directory not found: {e}"))?; + Ok(cparent.join(name)) + } + Err(e) => Err(format!("cannot resolve path: {e}")), + } +} + +/// Require `target` to sit inside one of the approved (canonical) `roots`. +fn ensure_within(roots: &[PathBuf], target: &Path) -> Result<(), String> { + if roots.is_empty() { + return Err("no approved workspace root".into()); + } + if roots.iter().any(|r| target.starts_with(r)) { + Ok(()) + } else { + Err("path is outside every approved workspace root".into()) + } +} + +/// Resolve an EXISTING path (read / list / exec cwd) under the roots. +fn resolve_readable(roots: &[PathBuf], input: &str) -> Result { + let t = canonicalize_target(input, false)?; + ensure_within(roots, &t)?; + Ok(t) +} + +/// Resolve a path to write (leaf may not exist yet) under the roots. +fn resolve_writable(roots: &[PathBuf], input: &str) -> Result { + let t = canonicalize_target(input, true)?; + ensure_within(roots, &t)?; + Ok(t) +} + +fn roots_snapshot(state: &WorkspaceState) -> Result, String> { + Ok(state + .roots + .lock() + .map_err(|_| "workspace state poisoned".to_string())? + .clone()) +} + +// --- commands --------------------------------------------------------------- + +/// Approve the given directories as workspace roots (replaces the prior set). +/// Each must exist and be a directory; stored canonicalized. Returns the +/// canonical paths actually approved. +#[tauri::command] +pub fn ws_set_roots(state: State, roots: Vec) -> Result, String> { + let mut canon = Vec::with_capacity(roots.len()); + for r in &roots { + let c = fs::canonicalize(r).map_err(|e| format!("root not found: {r}: {e}"))?; + if !c.is_dir() { + return Err(format!("root is not a directory: {r}")); + } + canon.push(c); + } + let out: Vec = canon.iter().map(|p| p.to_string_lossy().to_string()).collect(); + *state + .roots + .lock() + .map_err(|_| "workspace state poisoned".to_string())? = canon; + Ok(out) +} + +#[tauri::command] +pub fn ws_read_file(state: State, path: String) -> Result { + let roots = roots_snapshot(&state)?; + let target = resolve_readable(&roots, &path)?; + if target.is_dir() { + return Err("path is a directory, not a file".into()); + } + let bytes = fs::read(&target).map_err(|e| e.to_string())?; + if bytes.len() > MAX_READ_BYTES { + let mut s = String::from_utf8_lossy(&bytes[..MAX_READ_BYTES]).to_string(); + s.push_str("\n… [truncated: file exceeds the read cap]"); + Ok(s) + } else { + Ok(String::from_utf8_lossy(&bytes).to_string()) + } +} + +#[tauri::command] +pub fn ws_write_file(state: State, path: String, content: String) -> Result<(), String> { + if content.len() > MAX_WRITE_BYTES { + return Err("content exceeds the write cap".into()); + } + let roots = roots_snapshot(&state)?; + let target = resolve_writable(&roots, &path)?; + if target.is_dir() { + return Err("path is a directory".into()); + } + fs::write(&target, content).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub fn ws_list_dir(state: State, path: String) -> Result, String> { + let roots = roots_snapshot(&state)?; + let target = resolve_readable(&roots, &path)?; + if !target.is_dir() { + return Err("path is not a directory".into()); + } + let mut out = Vec::new(); + for entry in fs::read_dir(&target).map_err(|e| e.to_string())? { + let entry = entry.map_err(|e| e.to_string())?; + let ft = entry.file_type().map_err(|e| e.to_string())?; + out.push(WsDirEntry { + name: entry.file_name().to_string_lossy().to_string(), + path: entry.path().to_string_lossy().to_string(), + is_dir: ft.is_dir(), + }); + if out.len() >= MAX_LIST_ENTRIES { + break; + } + } + Ok(out) +} + +/// Read up to `MAX_EXEC_OUTPUT` bytes, then keep draining (so the child never +/// blocks on a full pipe) but discard the overflow. +async fn read_capped(reader: Option) -> String +where + R: tokio::io::AsyncRead + Unpin + Send + 'static, +{ + use tokio::io::AsyncReadExt; + let Some(mut r) = reader else { + return String::new(); + }; + let mut buf: Vec = Vec::new(); + let mut chunk = [0u8; 8192]; + loop { + match r.read(&mut chunk).await { + Ok(0) => break, + Ok(n) => { + if buf.len() < MAX_EXEC_OUTPUT { + let take = n.min(MAX_EXEC_OUTPUT - buf.len()); + buf.extend_from_slice(&chunk[..take]); + } + } + Err(_) => break, + } + } + String::from_utf8_lossy(&buf).to_string() +} + +#[tauri::command] +pub async fn ws_exec( + state: State<'_, WorkspaceState>, + command: String, + cwd: String, + timeout_ms: u64, +) -> Result { + let roots = roots_snapshot(&state)?; + if roots.is_empty() { + return Err("no approved workspace root — refusing to run commands".into()); + } + let dir = resolve_readable(&roots, &cwd)?; + if !dir.is_dir() { + return Err("cwd is not a directory".into()); + } + + #[cfg(windows)] + let (shell, flag) = ("cmd", "/C"); + #[cfg(not(windows))] + let (shell, flag) = ("/bin/sh", "-c"); + + let mut child = tokio::process::Command::new(shell) + .arg(flag) + .arg(&command) + .current_dir(&dir) + .stdin(std::process::Stdio::null()) + .stdout(std::process::Stdio::piped()) + .stderr(std::process::Stdio::piped()) + .spawn() + .map_err(|e| format!("failed to spawn command: {e}"))?; + + let out_task = tokio::spawn(read_capped(child.stdout.take())); + let err_task = tokio::spawn(read_capped(child.stderr.take())); + + let dur = std::time::Duration::from_millis(timeout_ms.max(1)); + match tokio::time::timeout(dur, child.wait()).await { + Ok(Ok(status)) => { + let stdout = out_task.await.unwrap_or_default(); + let stderr = err_task.await.unwrap_or_default(); + Ok(WsExecResult { + code: status.code().unwrap_or(-1), + stdout, + stderr, + }) + } + Ok(Err(e)) => Err(format!("command failed: {e}")), + Err(_) => { + let _ = child.start_kill(); + let _ = child.wait().await; + let stdout = out_task.await.unwrap_or_default(); + let stderr = err_task.await.unwrap_or_default(); + Ok(WsExecResult { + code: -1, + stdout, + stderr: format!("{stderr}\n[timed out after {timeout_ms}ms]"), + }) + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + fn tmp_root(tag: &str) -> PathBuf { + let mut d = std::env::temp_dir(); + d.push(format!("oa-ws-test-{}-{}", tag, std::process::id())); + let _ = fs::remove_dir_all(&d); + fs::create_dir_all(&d).unwrap(); + fs::canonicalize(&d).unwrap() + } + + #[test] + fn accepts_a_file_inside_the_root() { + let root = tmp_root("ok"); + let f = root.join("a.txt"); + fs::write(&f, "hi").unwrap(); + let roots = vec![root.clone()]; + let got = resolve_readable(&roots, f.to_str().unwrap()).unwrap(); + assert!(got.starts_with(&root)); + } + + #[test] + fn rejects_parent_traversal_out_of_root() { + let base = tmp_root("trav"); + let root = base.join("inner"); + fs::create_dir_all(&root).unwrap(); + let secret = base.join("secret.txt"); + fs::write(&secret, "s").unwrap(); + let roots = vec![fs::canonicalize(&root).unwrap()]; + // /base/inner/../secret.txt canonicalizes to /base/secret.txt (outside inner). + let attack = root.join("..").join("secret.txt"); + let err = resolve_readable(&roots, attack.to_str().unwrap()).unwrap_err(); + assert!(err.contains("outside"), "unexpected: {err}"); + } + + #[test] + fn rejects_absolute_path_outside_root() { + let root = tmp_root("abs"); + let roots = vec![root]; + let err = resolve_readable(&roots, "/etc/hosts").unwrap_err(); + assert!(err.contains("outside"), "unexpected: {err}"); + } + + #[cfg(unix)] + #[test] + fn rejects_symlink_escaping_root() { + let base = tmp_root("sym"); + let root = base.join("inner"); + fs::create_dir_all(&root).unwrap(); + let outside = base.join("outside.txt"); + fs::write(&outside, "s").unwrap(); + let link = root.join("link.txt"); + std::os::unix::fs::symlink(&outside, &link).unwrap(); + let roots = vec![fs::canonicalize(&root).unwrap()]; + let err = resolve_readable(&roots, link.to_str().unwrap()).unwrap_err(); + assert!(err.contains("outside"), "unexpected: {err}"); + } + + #[cfg(unix)] + #[test] + fn write_rejects_symlinked_parent_escape() { + // A symlinked parent dir pointing outside the root must be resolved so a + // write to a NEW leaf underneath it is still caught. + let base = tmp_root("symparent"); + let root = base.join("inner"); + fs::create_dir_all(&root).unwrap(); + let elsewhere = base.join("elsewhere"); + fs::create_dir_all(&elsewhere).unwrap(); + let link_dir = root.join("evil"); + std::os::unix::fs::symlink(&elsewhere, &link_dir).unwrap(); + let roots = vec![fs::canonicalize(&root).unwrap()]; + let attack = link_dir.join("new.txt"); + let err = resolve_writable(&roots, attack.to_str().unwrap()).unwrap_err(); + assert!(err.contains("outside"), "unexpected: {err}"); + } + + #[test] + fn write_allows_a_new_file_under_root() { + let root = tmp_root("wnew"); + let roots = vec![root.clone()]; + let target = root.join("created.txt"); + let got = resolve_writable(&roots, target.to_str().unwrap()).unwrap(); + assert!(got.starts_with(&root)); + } + + #[test] + fn exec_and_reads_refuse_when_no_root_is_approved() { + let dir = tmp_root("noroot"); + // With an empty root set, even an existing dir is rejected — this is the + // guard ws_exec relies on to refuse when no root is set. + let err = resolve_readable(&[], dir.to_str().unwrap()).unwrap_err(); + assert!(err.contains("no approved"), "unexpected: {err}"); + } + + #[test] + fn ensure_within_rejects_empty_roots() { + let err = ensure_within(&[], Path::new("/anything")).unwrap_err(); + assert!(err.contains("no approved"), "unexpected: {err}"); + } +} diff --git a/companions/desktop/src-tauri/tauri.conf.json b/companions/desktop/src-tauri/tauri.conf.json new file mode 100644 index 0000000000..83e19f5bc0 --- /dev/null +++ b/companions/desktop/src-tauri/tauri.conf.json @@ -0,0 +1,35 @@ +{ + "$schema": "https://schema.tauri.app/config/2", + "productName": "OpenAgentic", + "version": "0.1.0", + "identifier": "io.openagentics.desktop", + "build": { + "beforeDevCommand": "pnpm dev", + "devUrl": "http://localhost:1430", + "beforeBuildCommand": "pnpm build", + "frontendDist": "../dist" + }, + "app": { + "windows": [ + { + "title": "OpenAgentic", + "width": 1240, + "height": 800, + "minWidth": 900, + "minHeight": 600, + "transparent": true, + "titleBarStyle": "Overlay", + "hiddenTitle": true + } + ], + "security": { "csp": null }, + "macOSPrivateApi": true + }, + "bundle": { + "active": true, + "targets": ["app"], + "icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns"], + "externalBin": ["binaries/agenticode-cli"], + "resources": ["cli-runtime/rg"] + } +} diff --git a/companions/desktop/src/App.test.tsx b/companions/desktop/src/App.test.tsx new file mode 100644 index 0000000000..02564fcfaf --- /dev/null +++ b/companions/desktop/src/App.test.tsx @@ -0,0 +1,184 @@ +import { describe, expect, it, afterEach, beforeEach, vi } from "vitest"; +import { act, cleanup, fireEvent, render, screen, waitFor } from "@testing-library/react"; +import App from "./App"; +import { useAppStore } from "./stores/appStore"; +import { useAuthStore } from "./stores/authStore"; +import { useCodeStore } from "./stores/codeStore"; +import { setHost } from "./host/host"; +import type { CodeChild, Host } from "./host/host"; + +// The shell is auth-gated: render it in the signed-in (`ready`) state so these +// tests exercise the rail/surfaces rather than the LoginScreen. +beforeEach(() => { + useAuthStore.setState({ + status: "ready", + client: null, + profile: { instanceUrl: "http://localhost:8080", userId: "u1", email: "a@b.c", isAdmin: true }, + error: null, + }); +}); + +afterEach(() => { + cleanup(); + setHost(null); + useAppStore.setState({ surface: "chat", pendingGates: {} }); + useAuthStore.setState({ status: "boot", client: null, profile: null, error: null }); + useCodeStore.setState({ workspace: null }); +}); + +/** A fake host whose `spawnCode` returns a child with a `kill` spy — enough to + * assert the Code session's lifecycle across surface switches / sign-out. */ +function fakeCodeHost(): { host: Host; spawnCode: ReturnType; kill: ReturnType } { + const kill = vi.fn(async () => {}); + const child: CodeChild = { + write: vi.fn(async () => {}), + kill, + onLine: vi.fn(), + onExit: vi.fn(), + stderrTail: () => "", + }; + const spawnCode = vi.fn(async () => child); + const host = { + kind: "browser", + fetch: vi.fn(), + secretGet: async () => "oa_testkey_46charslongxxxxxxxxxxxxxxxxxxxxxx", + secretSet: async () => {}, + secretDelete: async () => {}, + pickDirectory: async () => null, + spawnCode, + ws: {}, + } as unknown as Host; + return { host, spawnCode, kill }; +} + +describe("App", () => { + it("renders the rail with chat, code, flows, and settings buttons", () => { + render(); + + expect(screen.getByTestId("rail-chat")).toBeTruthy(); + expect(screen.getByTestId("rail-code")).toBeTruthy(); + expect(screen.getByTestId("rail-flows")).toBeTruthy(); + expect(screen.getByTestId("rail-settings")).toBeTruthy(); + }); + + it("defaults to the chat surface (ChatSurface: sidebar + composer)", () => { + render(); + + // ChatSurface replaces the old placeholder panel — assert its shell renders. + expect(screen.getByTestId("session-new")).toBeTruthy(); + expect(screen.getByTestId("composer-input")).toBeTruthy(); + expect(screen.queryByTestId("panel-chat")).toBeNull(); + }); + + it("switches to the Flows surface when its rail button is clicked", () => { + render(); + + fireEvent.click(screen.getByTestId("rail-flows")); + + // FlowsSurface replaces the old placeholder panel — assert its shell renders. + expect(screen.getByTestId("flows-surface")).toBeTruthy(); + expect(screen.getByRole("heading", { name: "Flows" })).toBeTruthy(); + expect(screen.queryByTestId("panel-flows")).toBeNull(); + expect(screen.getByTestId("rail-flows").getAttribute("aria-pressed")).toBe("true"); + }); + + it("has a draggable titlebar region", () => { + const { container } = render(); + + expect(container.querySelector("[data-tauri-drag-region]")).toBeTruthy(); + }); + + it("shows the LoginScreen instead of the shell when signed out", () => { + useAuthStore.setState({ status: "signedOut", client: null, profile: null, error: null }); + + render(); + + expect(screen.getByTestId("login-form")).toBeTruthy(); + expect(screen.queryByTestId("rail-chat")).toBeNull(); + }); + + it("renders the Settings surface with diagnostics when selected", () => { + useAppStore.setState({ surface: "settings" }); + + render(); + + expect(screen.getByTestId("panel-settings")).toBeTruthy(); + expect(screen.getByTestId("settings-probe")).toBeTruthy(); + expect(screen.getByTestId("settings-signout")).toBeTruthy(); + }); + + it("keeps every surface mounted and only toggles visibility on a rail switch", () => { + render(); + + // All four surface hosts exist regardless of which one is active. + for (const id of ["chat", "code", "flows", "settings"]) { + expect(screen.getByTestId(`surface-${id}`)).toBeTruthy(); + } + // Chat visible (default); the others hidden. + expect(screen.getByTestId("surface-chat").hasAttribute("hidden")).toBe(false); + expect(screen.getByTestId("surface-code").hasAttribute("hidden")).toBe(true); + + fireEvent.click(screen.getByTestId("rail-flows")); + + // Switching only flips visibility — both hosts stay in the DOM (mounted). + expect(screen.getByTestId("surface-chat").hasAttribute("hidden")).toBe(true); + expect(screen.getByTestId("surface-flows").hasAttribute("hidden")).toBe(false); + }); + + it("does NOT unmount the Code session across a rail switch, but DOES on sign-out", async () => { + const { host, spawnCode, kill } = fakeCodeHost(); + setHost(host); + // Simulate a workspace the user already opened — Code drives a live session. + useCodeStore.setState({ workspace: "/w" }); + + render(); + + // Code stays mounted app-wide, so its session spawns once (even while hidden). + await waitFor(() => expect(spawnCode).toHaveBeenCalledTimes(1)); + + // Detour: chat → code → chat. The child must survive every switch — no kill, + // no respawn (which would force the ~25s cold restart this fix exists for). + await act(async () => { + fireEvent.click(screen.getByTestId("rail-code")); + }); + await act(async () => { + fireEvent.click(screen.getByTestId("rail-chat")); + }); + expect(kill).not.toHaveBeenCalled(); + expect(spawnCode).toHaveBeenCalledTimes(1); + + // Sign-out unmounts the whole shell → the child IS killed (teardown holds). + await act(async () => { + useAuthStore.setState({ status: "signedOut", client: null, profile: null, error: null }); + }); + await waitFor(() => expect(kill).toHaveBeenCalled()); + expect(screen.getByTestId("login-form")).toBeTruthy(); + }); + + it("shows a rail badge for a backgrounded surface with a pending gate, cleared when visited", async () => { + render(); + + // A hidden Code surface pauses on a permission gate → its rail dot appears. + await act(async () => { + useAppStore.getState().setPendingGate("code", true); + }); + expect(screen.getByTestId("rail-gate-code")).toBeTruthy(); + expect(screen.getByTestId("rail-code").getAttribute("aria-label")).toContain("approval pending"); + // The visible surface never badges (its modal is already on screen). + expect(screen.queryByTestId("rail-gate-chat")).toBeNull(); + + // Visiting the surface removes the badge (the modal is now visible). + fireEvent.click(screen.getByTestId("rail-code")); + expect(screen.queryByTestId("rail-gate-code")).toBeNull(); + + // Leaving again while still pending re-surfaces it. + fireEvent.click(screen.getByTestId("rail-chat")); + expect(screen.getByTestId("rail-gate-code")).toBeTruthy(); + + // Gate resolved → badge gone everywhere. + await act(async () => { + useAppStore.getState().setPendingGate("code", false); + }); + expect(screen.queryByTestId("rail-gate-code")).toBeNull(); + }); +}); diff --git a/companions/desktop/src/App.tsx b/companions/desktop/src/App.tsx new file mode 100644 index 0000000000..57e5a45476 --- /dev/null +++ b/companions/desktop/src/App.tsx @@ -0,0 +1,219 @@ +import { useEffect, type ReactNode } from "react"; +import { useAppStore, type Surface } from "./stores/appStore"; +import { useAuthStore } from "./stores/authStore"; +import LoginScreen from "./auth/LoginScreen"; +import SettingsSurface from "./settings/SettingsSurface"; +import ChatSurface from "./chat/ChatSurface"; +import CodeSurface from "./code/CodeSurface"; +import FlowsSurface from "./flows/FlowsSurface"; +import CommandPalette from "./shell/CommandPalette"; +import ShortcutsOverlay from "./shell/ShortcutsOverlay"; +import OfflineBanner from "./shell/OfflineBanner"; +import { useGlobalShortcuts } from "./shell/useGlobalShortcuts"; +import { useOfflineMonitor } from "./shell/useOfflineMonitor"; + +const PRIMARY_SURFACES: { id: Surface; label: string }[] = [ + { id: "chat", label: "Chat" }, + { id: "code", label: "Code" }, + { id: "flows", label: "Flows" }, +]; + +function RailIcon({ surface }: { surface: Surface }) { + switch (surface) { + case "chat": + return ( + + + + ); + case "code": + return ( + + + + ); + case "flows": + return ( + + + + + + + ); + case "settings": + return ( + + + + + ); + } +} + +function RailButton({ + id, + label, + active, + attention, + onSelect, +}: { + id: Surface; + label: string; + active: boolean; + /** True when this (backgrounded) surface holds an unresolved approval gate — + * renders the ochre badge so an invisible pending consent is discoverable. */ + attention?: boolean; + onSelect: (s: Surface) => void; +}) { + return ( + + ); +} + +function Rail() { + const surface = useAppStore((s) => s.surface); + const setSurface = useAppStore((s) => s.setSurface); + const pendingGates = useAppStore((s) => s.pendingGates); + + // Badge only while backgrounded: the visible surface already shows its modal. + const attention = (id: Surface): boolean => !!pendingGates[id] && surface !== id; + + return ( + + ); +} + +/** + * SurfaceHost — a surface stays MOUNTED across a rail switch; we toggle its + * visibility instead of unmounting. That is what lets a running turn survive a + * detour: an inactive Code surface keeps its `useCodeSession` child (no SIGKILL + * → no ~25s cold restart), an inactive Chat keeps its `useChat` stream + the + * local-executor subscribe, and an inactive Flows keeps its poll. Only sign-out + * (auth status !== 'ready') unmounts the whole shell. `hidden` drops the + * inactive surface from layout, tab order, and the accessibility tree so + * keyboard/focus only reach the active surface. + * + * `display:none` does NOT silence window-level keydown listeners, so anything + * inside a surface that binds to `window` (the approval/permission modals, + * Code's Shift+Tab) is additionally gated on the surface's `visible` prop — a + * keystroke on the visible surface must never resolve a hidden consent gate. + */ +function SurfaceHost({ id, active, children }: { id: Surface; active: boolean; children: ReactNode }) { + return ( + + ); +} + +function BootSplash() { + return ( +
+ Loading… +
+ ); +} + +export default function App() { + const surface = useAppStore((s) => s.surface); + const setShortcutsOpen = useAppStore((s) => s.setShortcutsOpen); + const status = useAuthStore((s) => s.status); + const restore = useAuthStore((s) => s.restore); + + // App-wide keyboard map + reachability watch (both no-op until signed in). + useGlobalShortcuts(); + const offline = useOfflineMonitor(); + + // Boot: rebuild auth from the keychain exactly once. Guarded on the live + // status so tests can seed a `ready` store and skip the restore round-trip. + useEffect(() => { + if (useAuthStore.getState().status === "boot") void restore(); + }, [restore]); + + if (status === "boot") return ; + if (status !== "ready") return ; + + return ( +
+
+ OpenAgentic + +
+ +
+ +
+ {/* All authenticated surfaces stay mounted; only the active one is + visible. Switching the rail toggles visibility, never unmounts — + so an in-flight code/chat/flows turn is never torn down. */} + + + + + + + + + + + + +
+
+ + +
+ ); +} diff --git a/companions/desktop/src/api/client.test.ts b/companions/desktop/src/api/client.test.ts new file mode 100644 index 0000000000..b9be79e708 --- /dev/null +++ b/companions/desktop/src/api/client.test.ts @@ -0,0 +1,708 @@ +import { describe, expect, it, vi } from "vitest"; +import { ApiError, PasswordChangeRequiredError, PlatformClient } from "./client"; +import type { PlatformFrame } from "./types"; + +// --------------------------------------------------------------------------- +// Test helpers +// --------------------------------------------------------------------------- + +function jsonResponse(status: number, body: unknown, statusText = ""): Response { + return new Response(JSON.stringify(body), { + status, + statusText, + headers: { "content-type": "application/json" }, + }); +} + +function ndjsonResponse(lines: string[]): Response { + return new Response(lines.map((l) => l + "\n").join(""), { + status: 200, + headers: { "content-type": "application/x-ndjson" }, + }); +} + +/** A fetchImpl stub that returns queued responses in order and records every + * `[url, init]` call so tests can assert exact request shape. */ +function stubFetch(...responses: Response[]) { + const calls: Array<[string, RequestInit]> = []; + let i = 0; + const fn = vi.fn(async (url: string, init: RequestInit = {}) => { + calls.push([url, init]); + const res = responses[i] ?? responses[responses.length - 1]; + i += 1; + return res; + }); + return { fn: fn as unknown as typeof fetch, calls }; +} + +function headersOf(init: RequestInit): Record { + return (init.headers ?? {}) as Record; +} + +const BASE = "http://localhost:8080"; + +function makeClient(opts: Partial<{ apiKey: string | null; userId: string | null }> = {}, fetchImpl: typeof fetch) { + return new PlatformClient({ + baseUrl: BASE, + apiKey: opts.apiKey ?? "oa_test_key", + userId: opts.userId ?? "user_1", + fetchImpl, + }); +} + +// --------------------------------------------------------------------------- +// health() +// --------------------------------------------------------------------------- + +describe("PlatformClient.health", () => { + it("GETs /api/health unauthed and reports ok:true with the raw body", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { status: "healthy" })); + const client = makeClient({}, fn); + + const result = await client.health(); + + expect(calls).toHaveLength(1); + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/health`); + expect(init.method).toBe("GET"); + expect(headersOf(init).authorization).toBeUndefined(); + expect(result).toEqual({ ok: true, raw: { status: "healthy" } }); + }); + + it("reports ok:false on a non-2xx without throwing", async () => { + const { fn } = stubFetch(jsonResponse(503, { status: "down" })); + const client = makeClient({}, fn); + + const result = await client.health(); + + expect(result.ok).toBe(false); + expect(result.raw).toEqual({ status: "down" }); + }); +}); + +// --------------------------------------------------------------------------- +// login() +// --------------------------------------------------------------------------- + +describe("PlatformClient.login", () => { + it("POSTs /api/auth/local/login with {username,password} and no auth header", async () => { + const { fn, calls } = stubFetch( + jsonResponse(200, { + success: true, + token: "jwt.abc", + user: { id: "u1", email: "a@b.com", name: "Ann", isAdmin: false }, + }), + ); + const client = makeClient({ apiKey: null, userId: null }, fn); + + const result = await client.login("a@b.com", "hunter2"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/auth/local/login`); + expect(init.method).toBe("POST"); + expect(JSON.parse(init.body as string)).toEqual({ username: "a@b.com", password: "hunter2" }); + expect(headersOf(init).authorization).toBeUndefined(); + expect(result).toEqual({ + token: "jwt.abc", + user: { id: "u1", email: "a@b.com", name: "Ann", isAdmin: false }, + }); + }); + + it("surfaces a 403 requiresPasswordChange as PasswordChangeRequiredError", async () => { + const { fn } = stubFetch( + jsonResponse(403, { + error: "Password change required", + requiresPasswordChange: true, + userId: "u1", + email: "a@b.com", + }), + ); + const client = makeClient({ apiKey: null, userId: null }, fn); + + await expect(client.login("a@b.com", "temp")).rejects.toBeInstanceOf(PasswordChangeRequiredError); + }); + + it("throws a plain ApiError on other non-2xx login failures", async () => { + const { fn } = stubFetch(jsonResponse(401, { error: "Invalid credentials" })); + const client = makeClient({ apiKey: null, userId: null }, fn); + + const err = await client.login("a@b.com", "wrong").catch((e) => e); + expect(err).toBeInstanceOf(ApiError); + expect(err).not.toBeInstanceOf(PasswordChangeRequiredError); + expect((err as ApiError).status).toBe(401); + expect((err as ApiError).message).toBe("Invalid credentials"); + }); +}); + +// --------------------------------------------------------------------------- +// changePassword() +// --------------------------------------------------------------------------- + +describe("PlatformClient.changePassword", () => { + it("POSTs /api/auth/local/change-password with email + current/new password, no auth header", async () => { + const { fn, calls } = stubFetch( + jsonResponse(200, { success: true, message: "ok", token: "jwt.new" }), + ); + const client = makeClient({ apiKey: null, userId: null }, fn); + + const result = await client.changePassword("a@b.com", "temp", "newpass1"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/auth/local/change-password`); + expect(init.method).toBe("POST"); + expect(JSON.parse(init.body as string)).toEqual({ + currentPassword: "temp", + newPassword: "newpass1", + email: "a@b.com", + }); + expect(headersOf(init).authorization).toBeUndefined(); + expect(result).toEqual({ token: "jwt.new" }); + }); +}); + +// --------------------------------------------------------------------------- +// mintApiKey() +// --------------------------------------------------------------------------- + +describe("PlatformClient.mintApiKey", () => { + it("POSTs /api/workflows/user/api-keys with the login JWT as Bearer and unwraps key.plaintext_key", async () => { + const { fn, calls } = stubFetch( + jsonResponse(201, { + key: { id: "key_1", name: "desktop", plaintext_key: "oa_secret123", created_at: "2026-01-01" }, + warning: "Save this key now.", + }), + ); + const client = makeClient({ apiKey: "oa_stale_stored_key" }, fn); + + const result = await client.mintApiKey("login.jwt.xyz", "desktop"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/workflows/user/api-keys`); + expect(init.method).toBe("POST"); + expect(headersOf(init).authorization).toBe("Bearer login.jwt.xyz"); + expect(JSON.parse(init.body as string)).toEqual({ name: "desktop" }); + expect(result).toEqual({ id: "key_1", key: "oa_secret123" }); + }); + + it("does not send x-user-id (not an /api/chat/* route)", async () => { + const { fn, calls } = stubFetch( + jsonResponse(201, { key: { id: "k", plaintext_key: "p" } }), + ); + const client = makeClient({}, fn); + + await client.mintApiKey("jwt", "name"); + + expect(headersOf(calls[0][1])["x-user-id"]).toBeUndefined(); + }); +}); + +// --------------------------------------------------------------------------- +// validateToken() +// --------------------------------------------------------------------------- + +describe("PlatformClient.validateToken", () => { + it("POSTs /api/auth/validate-token with Bearer apiKey and no x-user-id", async () => { + const { fn, calls } = stubFetch( + jsonResponse(200, { + userId: "u1", + email: "a@b.com", + isAdmin: true, + groups: [], + authMethod: "api-key", + }), + ); + const client = makeClient({ apiKey: "oa_live_key", userId: "user_1" }, fn); + + const result = await client.validateToken(); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/auth/validate-token`); + expect(init.method).toBe("POST"); + expect(headersOf(init).authorization).toBe("Bearer oa_live_key"); + expect(headersOf(init)["x-user-id"]).toBeUndefined(); + expect(result).toEqual({ userId: "u1", email: "a@b.com", isAdmin: true }); + }); +}); + +// --------------------------------------------------------------------------- +// Sessions CRUD +// --------------------------------------------------------------------------- + +describe("PlatformClient sessions", () => { + it("listSessions() GETs /api/chat/sessions with Bearer + x-user-id and unwraps .sessions", async () => { + const sessions = [{ id: "s1", title: "Chat", createdAt: "t1", updatedAt: "t1" }]; + const { fn, calls } = stubFetch(jsonResponse(200, { sessions, lastActiveSessionId: "s1", success: true })); + const client = makeClient({}, fn); + + const result = await client.listSessions(); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/sessions`); + expect(init.method).toBe("GET"); + expect(headersOf(init).authorization).toBe("Bearer oa_test_key"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(result).toEqual(sessions); + }); + + it("createSession() POSTs /api/chat/sessions with {title} and unwraps .session", async () => { + const session = { id: "s2", title: "New Chat", createdAt: "t2", updatedAt: "t2" }; + const { fn, calls } = stubFetch(jsonResponse(201, { session, success: true })); + const client = makeClient({}, fn); + + const result = await client.createSession("New Chat"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/sessions`); + expect(init.method).toBe("POST"); + expect(JSON.parse(init.body as string)).toEqual({ title: "New Chat" }); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(result).toEqual(session); + }); + + it("deleteSession() DELETEs /api/chat/sessions/:id", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { success: true, message: "deleted" })); + const client = makeClient({}, fn); + + await client.deleteSession("s2"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/sessions/s2`); + expect(init.method).toBe("DELETE"); + }); + + it("renameSession() PUTs /api/chat/sessions/:id with {title} and unwraps .session", async () => { + // Endpoint shape verified against the API: + // services/openagentic-api/src/routes/chat/index.ts:1060 registers + // `fastify.put('/sessions/:sessionId')` with body `{title?, metadata?, + // isActive?}`; session.handler.ts `update` (319-396) replies + // `{ session: updatedSession, success: true }`. + const session = { id: "s2", title: "Renamed chat", createdAt: "t2", updatedAt: "t3" }; + const { fn, calls } = stubFetch(jsonResponse(200, { session, success: true })); + const client = makeClient({}, fn); + + const result = await client.renameSession("s2", "Renamed chat"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/sessions/s2`); + expect(init.method).toBe("PUT"); + expect(JSON.parse(init.body as string)).toEqual({ title: "Renamed chat" }); + expect(headersOf(init).authorization).toBe("Bearer oa_test_key"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(headersOf(init)["content-type"]).toBe("application/json"); + expect(result).toEqual(session); + }); + + it("getMessages() GETs /api/chat/sessions/:id/messages and unwraps .messages", async () => { + const messages = [{ id: "m1", role: "user", content: "hi" }]; + const { fn, calls } = stubFetch(jsonResponse(200, { messages, session: {}, success: true })); + const client = makeClient({}, fn); + + const result = await client.getMessages("s2"); + + const [url] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/sessions/s2/messages`); + expect(result).toEqual(messages); + }); +}); + +// --------------------------------------------------------------------------- +// listModels() +// --------------------------------------------------------------------------- + +describe("PlatformClient.listModels", () => { + it("GETs /api/chat/models with auth + x-user-id and returns the report as-is", async () => { + const report = { + models: [{ id: "m1", name: "GPT", provider: "openai" }], + defaultModel: "m1", + codemodeDefault: "m1", + provider_status: { openai: "ok" }, + }; + const { fn, calls } = stubFetch(jsonResponse(200, report)); + const client = makeClient({}, fn); + + const result = await client.listModels(); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/models`); + expect(init.method).toBe("GET"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(result).toEqual(report); + }); +}); + +// --------------------------------------------------------------------------- +// chatStream() +// --------------------------------------------------------------------------- + +describe("PlatformClient.chatStream", () => { + it("POSTs /api/chat/stream with Accept: application/x-ndjson and feeds frames through readNdjson", async () => { + const { fn, calls } = stubFetch( + ndjsonResponse(['{"type":"delta","content":"hi"}', '{"type":"done"}']), + ); + const client = makeClient({}, fn); + const frames: PlatformFrame[] = []; + + await client.chatStream({ sessionId: "s1", message: "hello" }, (f) => { + frames.push(f); + }); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/stream`); + expect(init.method).toBe("POST"); + expect(headersOf(init).accept).toBe("application/x-ndjson"); + expect(headersOf(init).authorization).toBe("Bearer oa_test_key"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(JSON.parse(init.body as string)).toEqual({ sessionId: "s1", message: "hello" }); + expect(frames).toEqual([ + { type: "delta", content: "hi" }, + { type: "done" }, + ]); + }); + + it("never sends a model field in the request body", async () => { + const { fn, calls } = stubFetch(ndjsonResponse(['{"type":"done"}'])); + const client = makeClient({}, fn); + + await client.chatStream({ sessionId: "s1", message: "hi", attachments: [{ id: "f1" }] }, () => {}); + + const body = JSON.parse(calls[0][1].body as string); + expect(body).not.toHaveProperty("model"); + expect(body).toEqual({ sessionId: "s1", message: "hi", attachments: [{ id: "f1" }] }); + }); + + it("passes the AbortSignal through to fetch", async () => { + const { fn, calls } = stubFetch(ndjsonResponse(['{"type":"done"}'])); + const client = makeClient({}, fn); + const controller = new AbortController(); + + await client.chatStream({ sessionId: "s1", message: "hi" }, () => {}, controller.signal); + + expect(calls[0][1].signal).toBe(controller.signal); + }); + + it("throws ApiError on a non-2xx response and never invokes onFrame", async () => { + const { fn } = stubFetch(jsonResponse(500, { error: "boom" })); + const client = makeClient({}, fn); + const onFrame = vi.fn(); + + await expect(client.chatStream({ sessionId: "s1", message: "hi" }, onFrame)).rejects.toBeInstanceOf( + ApiError, + ); + expect(onFrame).not.toHaveBeenCalled(); + }); +}); + +// --------------------------------------------------------------------------- +// tailStream() +// --------------------------------------------------------------------------- + +describe("PlatformClient.tailStream", () => { + it("GETs /api/chat/stream/:sessionId/tail?turnId=&after= and streams frames", async () => { + const { fn, calls } = stubFetch(ndjsonResponse(['{"type":"delta","_seq":3}'])); + const client = makeClient({}, fn); + const frames: PlatformFrame[] = []; + + await client.tailStream("s1", "turn-9", 2, (f) => { + frames.push(f); + }); + + const [url, init] = calls[0]; + const parsed = new URL(url); + expect(parsed.origin + parsed.pathname).toBe(`${BASE}/api/chat/stream/s1/tail`); + expect(parsed.searchParams.get("turnId")).toBe("turn-9"); + expect(parsed.searchParams.get("after")).toBe("2"); + expect(init.method).toBe("GET"); + expect(headersOf(init).accept).toBe("application/x-ndjson"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(frames).toEqual([{ type: "delta", _seq: 3 }]); + }); +}); + +// --------------------------------------------------------------------------- +// approve() +// --------------------------------------------------------------------------- + +describe("PlatformClient.approve", () => { + it("POSTs /api/approvals/:auditId/approve with an empty body", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { ok: true, auditId: "a1", approved: true })); + const client = makeClient({}, fn); + + await client.approve("a1", "approve"); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/approvals/a1/approve`); + expect(init.method).toBe("POST"); + expect(init.body).toBeUndefined(); + expect(headersOf(init)["content-type"]).toBeUndefined(); + expect(headersOf(init)["x-user-id"]).toBeUndefined(); + }); + + it("POSTs /api/approvals/:auditId/deny with the verdict in the path", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { ok: true, auditId: "a1", approved: false })); + const client = makeClient({}, fn); + + await client.approve("a1", "deny"); + + expect(calls[0][0]).toBe(`${BASE}/api/approvals/a1/deny`); + expect(calls[0][1].body).toBeUndefined(); + }); +}); + +// --------------------------------------------------------------------------- +// Workflows +// --------------------------------------------------------------------------- + +describe("PlatformClient workflows", () => { + it("listWorkflows() GETs /api/workflows and unwraps .workflows", async () => { + const workflows = [{ id: "w1", name: "Flow" }]; + const { fn, calls } = stubFetch(jsonResponse(200, { workflows, total: 1 })); + const client = makeClient({}, fn); + + const result = await client.listWorkflows(); + + expect(calls[0][0]).toBe(`${BASE}/api/workflows`); + expect(headersOf(calls[0][1])["x-user-id"]).toBeUndefined(); + expect(result).toEqual(workflows); + }); + + it("executeWorkflowStream() POSTs /:id/execute with Accept ndjson and streams frames", async () => { + const { fn, calls } = stubFetch(ndjsonResponse(['{"type":"execution_start"}'])); + const client = makeClient({}, fn); + const frames: PlatformFrame[] = []; + + await client.executeWorkflowStream("w1", (f) => { + frames.push(f); + }); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/workflows/w1/execute`); + expect(init.method).toBe("POST"); + expect(headersOf(init).accept).toBe("application/x-ndjson"); + // Default (no-input) run still sends an empty input object. + expect(JSON.parse(init.body as string)).toEqual({ input: {}, trigger_type: "manual" }); + expect(frames).toEqual([{ type: "execution_start" }]); + }); + + it("executeWorkflowStream() threads a provided input into the request body", async () => { + const { fn, calls } = stubFetch(ndjsonResponse(['{"type":"execution_start"}'])); + const client = makeClient({}, fn); + + await client.executeWorkflowStream("w1", () => {}, undefined, { topic: "release notes", count: 3 }); + + // Body carries the caller's input verbatim (verified against + // execution.routes.ts: `const { input = {}, trigger_type = 'manual' } = body`). + expect(JSON.parse(calls[0][1].body as string)).toEqual({ + input: { topic: "release notes", count: 3 }, + trigger_type: "manual", + }); + }); + + it("executeWorkflowAsync() POSTs /:id/execute?async=true and returns {executionId}", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { executionId: "e1", status: "running" })); + const client = makeClient({}, fn); + + const result = await client.executeWorkflowAsync("w1"); + + expect(calls[0][0]).toBe(`${BASE}/api/workflows/w1/execute?async=true`); + expect(calls[0][1].method).toBe("POST"); + expect(JSON.parse(calls[0][1].body as string)).toEqual({ input: {}, trigger_type: "manual" }); + expect(result).toEqual({ executionId: "e1" }); + }); + + it("executeWorkflowAsync() threads a provided input into the request body", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { executionId: "e2", status: "running" })); + const client = makeClient({}, fn); + + await client.executeWorkflowAsync("w1", { seed: 42 }); + + expect(JSON.parse(calls[0][1].body as string)).toEqual({ input: { seed: 42 }, trigger_type: "manual" }); + }); + + it("getExecution() GETs /:id/executions/:execId and returns the envelope as-is", async () => { + const envelope = { execution: { id: "e1", status: "completed" }, logs: [], nodeSummary: {} }; + const { fn, calls } = stubFetch(jsonResponse(200, envelope)); + const client = makeClient({}, fn); + + const result = await client.getExecution("w1", "e1"); + + expect(calls[0][0]).toBe(`${BASE}/api/workflows/w1/executions/e1`); + expect(result).toEqual(envelope); + }); + + it("listExecutions() GETs /:id/executions and unwraps .executions", async () => { + const executions = [{ id: "e1", status: "completed" }]; + const { fn, calls } = stubFetch(jsonResponse(200, { executions, total: 1 })); + const client = makeClient({}, fn); + + const result = await client.listExecutions("w1"); + + expect(calls[0][0]).toBe(`${BASE}/api/workflows/w1/executions`); + expect(result).toEqual(executions); + }); +}); + +// --------------------------------------------------------------------------- +// uploadFile() +// --------------------------------------------------------------------------- + +describe("PlatformClient.uploadFile", () => { + it("POSTs multipart FormData to /api/files/upload and unwraps {id, isDuplicate}", async () => { + // Assert via a spy on FormData.append rather than FormData.get(): the + // happy-dom test environment has a verified bug where the filename + // passed to `append(name, blob, filename)` is dropped on retrieval via + // `.get()` (confirmed independently against the same happy-dom package + // outside vitest) — the spy checks what our code actually sent instead + // of routing the assertion through happy-dom's lossy round-trip. + const appendSpy = vi.spyOn(FormData.prototype, "append"); + const { fn, calls } = stubFetch( + jsonResponse(200, { + file: { id: "f1", filename: "note.txt", mimeType: "text/plain", size: 3 }, + }), + ); + const client = makeClient({}, fn); + + const result = await client.uploadFile({ + name: "note.txt", + mime: "text/plain", + bytes: new Uint8Array([104, 105, 33]), + }); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/files/upload`); + expect(init.method).toBe("POST"); + expect(headersOf(init).authorization).toBe("Bearer oa_test_key"); + expect(headersOf(init)["content-type"]).toBeUndefined(); + expect(init.body).toBeInstanceOf(FormData); + + expect(appendSpy).toHaveBeenCalledTimes(1); + const [field, blob, filename] = appendSpy.mock.calls[0]; + expect(field).toBe("file"); + expect(blob).toBeInstanceOf(Blob); + expect((blob as Blob).type).toBe("text/plain"); + expect((blob as Blob).size).toBe(3); + expect(filename).toBe("note.txt"); + appendSpy.mockRestore(); + + expect(result).toEqual({ id: "f1", isDuplicate: undefined }); + }); + + it("surfaces isDuplicate:true when the server dedupes the upload", async () => { + const { fn } = stubFetch( + jsonResponse(200, { file: { id: "f1" }, isDuplicate: true }), + ); + const client = makeClient({}, fn); + + const result = await client.uploadFile({ name: "a.txt", mime: "text/plain", bytes: new Uint8Array() }); + + expect(result).toEqual({ id: "f1", isDuplicate: true }); + }); +}); + +// --------------------------------------------------------------------------- +// Local executor bridge +// --------------------------------------------------------------------------- + +describe("PlatformClient local executor", () => { + it("subscribeLocalExecutor() POSTs /api/chat/local-executor/subscribe with tools and streams frames", async () => { + const { fn, calls } = stubFetch(ndjsonResponse(['{"type":"connected"}'])); + const client = makeClient({}, fn); + const tools = [{ name: "workspace_read_file" }]; + const frames: PlatformFrame[] = []; + + await client.subscribeLocalExecutor(tools, (f) => { + frames.push(f); + }); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/local-executor/subscribe`); + expect(init.method).toBe("POST"); + expect(headersOf(init).accept).toBe("application/x-ndjson"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(JSON.parse(init.body as string)).toEqual({ tools }); + expect(frames).toEqual([{ type: "connected" }]); + }); + + it("postToolResult() POSTs /api/chat/local-executor/tool-result with the result body", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { ok: true, tool_use_id: "t1", resolved: true })); + const client = makeClient({}, fn); + + await client.postToolResult({ tool_use_id: "t1", content: "output", is_error: false }); + + const [url, init] = calls[0]; + expect(url).toBe(`${BASE}/api/chat/local-executor/tool-result`); + expect(init.method).toBe("POST"); + expect(headersOf(init)["x-user-id"]).toBe("user_1"); + expect(JSON.parse(init.body as string)).toEqual({ + tool_use_id: "t1", + content: "output", + is_error: false, + }); + }); +}); + +// --------------------------------------------------------------------------- +// Error mapping tolerance (mirrors tools/oa/src/client.ts errorMessage()) +// --------------------------------------------------------------------------- + +describe("PlatformClient error mapping", () => { + it("extracts a plain string error field", async () => { + const { fn } = stubFetch(jsonResponse(500, { error: "flat string error" })); + const client = makeClient({}, fn); + + const err = await client.listSessions().catch((e) => e); + expect((err as ApiError).message).toBe("flat string error"); + }); + + it("extracts a message field when error is absent", async () => { + const { fn } = stubFetch(jsonResponse(500, { message: "message field error" })); + const client = makeClient({}, fn); + + const err = await client.listSessions().catch((e) => e); + expect((err as ApiError).message).toBe("message field error"); + }); + + it("extracts a nested error.message field", async () => { + const { fn } = stubFetch(jsonResponse(500, { error: { message: "nested error" } })); + const client = makeClient({}, fn); + + const err = await client.listSessions().catch((e) => e); + expect((err as ApiError).message).toBe("nested error"); + }); + + it("falls back to statusText, then HTTP , when the body has neither", async () => { + const { fn } = stubFetch(jsonResponse(500, {}, "Internal Server Error")); + const client = makeClient({}, fn); + + const err = await client.listSessions().catch((e) => e); + expect((err as ApiError).message).toBe("Internal Server Error"); + expect((err as ApiError).status).toBe(500); + }); + + it("falls back to 'HTTP ' when there is no body and no statusText", async () => { + const { fn } = stubFetch(new Response("", { status: 500 })); + const client = makeClient({}, fn); + + const err = await client.listSessions().catch((e) => e); + expect((err as ApiError).message).toBe("HTTP 500"); + }); +}); + +// --------------------------------------------------------------------------- +// Constructor defaults +// --------------------------------------------------------------------------- + +describe("PlatformClient constructor", () => { + it("defaults fetchImpl to the global fetch when not provided", () => { + const client = new PlatformClient({ baseUrl: BASE }); + expect(client).toBeInstanceOf(PlatformClient); + }); + + it("strips a trailing slash from baseUrl", async () => { + const { fn, calls } = stubFetch(jsonResponse(200, { status: "ok" })); + const client = new PlatformClient({ baseUrl: `${BASE}/`, fetchImpl: fn }); + + await client.health(); + + expect(calls[0][0]).toBe(`${BASE}/api/health`); + }); +}); diff --git a/companions/desktop/src/api/client.ts b/companions/desktop/src/api/client.ts new file mode 100644 index 0000000000..b8e231e9c4 --- /dev/null +++ b/companions/desktop/src/api/client.ts @@ -0,0 +1,407 @@ +/** + * PlatformClient — the desktop app's only entry point onto the OpenAgentic + * platform API. Pure request/response mapping; no UI/state lives here. + * + * Endpoint shapes and unwrap rules are taken verbatim from the reference CLI + * client (`tools/oa/src/client.ts`) and the route handlers under + * `services/openagentic-api/src/routes/**`. Envelopes are inconsistent + * per-endpoint by design of the upstream API — this file is the one place + * that absorbs that inconsistency so the rest of the desktop app only ever + * sees the shapes declared in `./types`. + * + * Streaming methods pipe `res.body` through `readNdjson` (task 3) rather + * than re-implementing NDJSON parsing here. + */ +import { readNdjson } from "./ndjson"; +import type { + ChatSession, + FetchLike, + ModelsReport, + PlatformFrame, + WorkflowSummary, +} from "./types"; + +/** Error carrying the HTTP status and the server's error message. Mirrors + * `tools/oa/src/client.ts` ApiError so callers can branch on `.status`. */ +export class ApiError extends Error { + readonly status: number; + constructor(status: number, message: string) { + super(message); + this.name = "ApiError"; + this.status = status; + } +} + +/** Thrown by `login()` when the server responds 403 with + * `requiresPasswordChange: true` — the caller should route to a + * change-password screen rather than treating this as a generic auth + * failure (see `services/openagentic-api/src/routes/local-auth.ts`). */ +export class PasswordChangeRequiredError extends Error { + readonly userId?: string; + readonly email?: string; + constructor(message: string, userId?: string, email?: string) { + super(message); + this.name = "PasswordChangeRequiredError"; + this.userId = userId; + this.email = email; + } +} + +/** Extract a human-readable message from an error response body of any + * shape. Copied from `tools/oa/src/client.ts` errorMessage() — tolerates + * `{error: string}`, `{message: string}`, and `{error: {message}}`, and + * falls back to statusText, then `HTTP `. */ +function errorMessage(data: unknown, statusText: string, status: number): string { + const d = data as { error?: unknown; message?: unknown } | null; + const err = d?.error; + if (typeof err === "string" && err) return err; + if (typeof d?.message === "string" && d.message) return d.message; + if (err && typeof err === "object") { + const nested = (err as { message?: unknown }).message; + if (typeof nested === "string" && nested) return nested; + try { + return JSON.stringify(err); + } catch { + /* fall through */ + } + } + return statusText || `HTTP ${status}`; +} + +async function parseJsonBody(res: Response): Promise { + const text = await res.text(); + if (!text) return {}; + try { + return JSON.parse(text); + } catch { + return {}; + } +} + +interface RequestOptions { + body?: unknown; + /** Attach the Authorization header from the stored apiKey (default true). */ + auth?: boolean; + /** Override the Bearer token for this call only (e.g. mintApiKey's login JWT). */ + token?: string; +} + +export class PlatformClient { + private readonly baseUrl: string; + private readonly apiKey: string | null; + private readonly userId: string | null; + private readonly fetchImpl: FetchLike; + + constructor(opts: { + baseUrl: string; + apiKey?: string | null; + userId?: string | null; + fetchImpl?: FetchLike; + }) { + this.baseUrl = opts.baseUrl.replace(/\/+$/, ""); + this.apiKey = opts.apiKey ?? null; + this.userId = opts.userId ?? null; + this.fetchImpl = opts.fetchImpl ?? fetch; + } + + // ---- header construction -------------------------------------------- + + /** `/api/chat/*` routes additionally require `x-user-id` alongside the + * Bearer token — see the plan's Global Constraints. */ + private buildHeaders( + path: string, + opts: { hasBody: boolean; auth: boolean; token?: string; accept?: string }, + ): Record { + const headers: Record = {}; + if (opts.hasBody) headers["content-type"] = "application/json"; + if (opts.accept) headers.accept = opts.accept; + + const bearer = opts.token ?? (opts.auth ? this.apiKey : null); + if (bearer) headers.authorization = `Bearer ${bearer}`; + + if (opts.auth && this.userId && path.startsWith("/api/chat/")) { + headers["x-user-id"] = this.userId; + } + return headers; + } + + // ---- low-level request helpers --------------------------------------- + + private async request(method: string, path: string, opts: RequestOptions = {}): Promise { + const auth = opts.auth ?? true; + const headers = this.buildHeaders(path, { hasBody: opts.body !== undefined, auth, token: opts.token }); + const res = await this.fetchImpl(`${this.baseUrl}${path}`, { + method, + headers, + body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined, + }); + const data = await parseJsonBody(res); + if (!res.ok) { + throw new ApiError(res.status, errorMessage(data, res.statusText, res.status)); + } + return data as T; + } + + /** Shared implementation for every NDJSON-streaming endpoint: sends + * `Accept: application/x-ndjson`, then pipes `res.body` through + * `readNdjson` (task 3) rather than re-parsing the wire format here. */ + private async streamRequest( + method: string, + path: string, + onFrame: (f: PlatformFrame) => void | Promise, + opts: { body?: unknown; signal?: AbortSignal } = {}, + ): Promise { + const headers = this.buildHeaders(path, { + hasBody: opts.body !== undefined, + auth: true, + accept: "application/x-ndjson", + }); + const res = await this.fetchImpl(`${this.baseUrl}${path}`, { + method, + headers, + body: opts.body !== undefined ? JSON.stringify(opts.body) : undefined, + signal: opts.signal, + }); + if (!res.ok) { + const data = await parseJsonBody(res); + throw new ApiError(res.status, errorMessage(data, res.statusText, res.status)); + } + const body = res.body; + if (!body) return; + await readNdjson(body, (frame) => onFrame(frame as PlatformFrame), opts.signal); + } + + // ---- health / auth ---------------------------------------------------- + + async health(): Promise<{ ok: boolean; raw: unknown }> { + const res = await this.fetchImpl(`${this.baseUrl}/api/health`, { method: "GET" }); + const raw = await parseJsonBody(res); + return { ok: res.ok, raw }; + } + + async login( + email: string, + password: string, + ): Promise<{ token: string; user: { id: string; email: string; name: string; isAdmin: boolean } }> { + const res = await this.fetchImpl(`${this.baseUrl}/api/auth/local/login`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ username: email, password }), + }); + const data = (await parseJsonBody(res)) as { + requiresPasswordChange?: boolean; + userId?: string; + email?: string; + token?: string; + user?: { id: string; email: string; name: string; isAdmin: boolean }; + }; + + if (res.status === 403 && data.requiresPasswordChange) { + throw new PasswordChangeRequiredError( + errorMessage(data, res.statusText, res.status), + data.userId, + data.email, + ); + } + if (!res.ok) { + throw new ApiError(res.status, errorMessage(data, res.statusText, res.status)); + } + return { token: data.token as string, user: data.user as { id: string; email: string; name: string; isAdmin: boolean } }; + } + + async changePassword(email: string, current: string, next: string): Promise<{ token: string }> { + const data = await this.request<{ token: string }>("POST", "/api/auth/local/change-password", { + body: { currentPassword: current, newPassword: next, email }, + auth: false, + }); + return { token: data.token }; + } + + /** POST /api/workflows/user/api-keys, Bearer=jwt (the login JWT passed in + * — NOT the client's stored apiKey, which doesn't exist yet at mint time). */ + async mintApiKey(jwt: string, name: string): Promise<{ id: string; key: string }> { + const data = await this.request<{ key: { id: string; plaintext_key: string } }>( + "POST", + "/api/workflows/user/api-keys", + { body: { name }, token: jwt }, + ); + return { id: data.key.id, key: data.key.plaintext_key }; + } + + async validateToken(): Promise<{ userId: string; email: string; isAdmin: boolean }> { + const data = await this.request<{ userId: string; email: string; isAdmin: boolean }>( + "POST", + "/api/auth/validate-token", + ); + return { userId: data.userId, email: data.email, isAdmin: data.isAdmin }; + } + + // ---- sessions ----------------------------------------------------------- + + async listSessions(): Promise { + const data = await this.request<{ sessions: ChatSession[] }>("GET", "/api/chat/sessions"); + return data.sessions; + } + + async createSession(title: string): Promise { + const data = await this.request<{ session: ChatSession }>("POST", "/api/chat/sessions", { + body: { title }, + }); + return data.session; + } + + async deleteSession(id: string): Promise { + await this.request("DELETE", `/api/chat/sessions/${encodeURIComponent(id)}`); + } + + /** PUT /api/chat/sessions/:sessionId with `{title}` — the update handler + * (session.handler.ts `update`, wired in routes/chat/index.ts:1060) accepts + * `{title?, metadata?, isActive?}` and replies `{session, success:true}`. */ + async renameSession(id: string, title: string): Promise { + const data = await this.request<{ session: ChatSession }>( + "PUT", + `/api/chat/sessions/${encodeURIComponent(id)}`, + { body: { title } }, + ); + return data.session; + } + + async getMessages(sessionId: string): Promise { + const data = await this.request<{ messages: unknown[] }>( + "GET", + `/api/chat/sessions/${encodeURIComponent(sessionId)}/messages`, + ); + return data.messages; + } + + async listModels(): Promise { + return this.request("GET", "/api/chat/models"); + } + + // ---- chat streaming ------------------------------------------------- + + async chatStream( + body: { sessionId: string; message: string; attachments?: unknown[] }, + onFrame: (f: PlatformFrame) => void | Promise, + signal?: AbortSignal, + ): Promise { + await this.streamRequest("POST", "/api/chat/stream", onFrame, { body, signal }); + } + + async tailStream( + sessionId: string, + turnId: string, + afterSeq: number, + onFrame: (f: PlatformFrame) => void | Promise, + signal?: AbortSignal, + ): Promise { + const qs = new URLSearchParams({ turnId, after: String(afterSeq) }); + const path = `/api/chat/stream/${encodeURIComponent(sessionId)}/tail?${qs.toString()}`; + await this.streamRequest("GET", path, onFrame, { signal }); + } + + /** POST /api/approvals/:auditId/:verdict — verb-in-path, EMPTY body. */ + async approve(auditId: string, verdict: "approve" | "deny"): Promise { + await this.request("POST", `/api/approvals/${encodeURIComponent(auditId)}/${verdict}`); + } + + // ---- workflows ---------------------------------------------------------- + + async listWorkflows(): Promise { + const data = await this.request<{ workflows: WorkflowSummary[] }>("GET", "/api/workflows"); + return data.workflows; + } + + /** `input` is the workflow's trigger input (default `{}`) — the execute + * route destructures `{ input = {}, trigger_type = 'manual' }` off the body + * (execution.routes.ts), so trigger-input workflows need it populated to run. */ + async executeWorkflowStream( + id: string, + onFrame: (f: PlatformFrame) => void | Promise, + signal?: AbortSignal, + input: Record = {}, + ): Promise { + await this.streamRequest("POST", `/api/workflows/${encodeURIComponent(id)}/execute`, onFrame, { + body: { input, trigger_type: "manual" }, + signal, + }); + } + + async executeWorkflowAsync( + id: string, + input: Record = {}, + ): Promise<{ executionId: string }> { + const data = await this.request<{ executionId: string }>( + "POST", + `/api/workflows/${encodeURIComponent(id)}/execute?async=true`, + { body: { input, trigger_type: "manual" } }, + ); + return { executionId: data.executionId }; + } + + async getExecution( + id: string, + execId: string, + ): Promise<{ execution: unknown; logs?: unknown; nodeSummary?: unknown }> { + return this.request( + "GET", + `/api/workflows/${encodeURIComponent(id)}/executions/${encodeURIComponent(execId)}`, + ); + } + + async listExecutions(id: string): Promise { + const data = await this.request<{ executions: unknown[] }>( + "GET", + `/api/workflows/${encodeURIComponent(id)}/executions`, + ); + return data.executions; + } + + // ---- files ---------------------------------------------------------- + + /** POST multipart/form-data to /api/files/upload — field name `file`. + * Content-Type is intentionally left unset so fetch fills in the + * multipart boundary itself (see openagentic-ui's uploadFilesToApi.ts). */ + async uploadFile(file: { name: string; mime: string; bytes: Uint8Array }): Promise<{ + id: string; + isDuplicate?: boolean; + }> { + const form = new FormData(); + // Cast needed under TS 5.7's stricter typed-array lib types: the + // `bytes: Uint8Array` param's buffer is typed `ArrayBufferLike`, but + // `BlobPart` wants an `ArrayBufferView`. A plain Uint8Array + // is a valid Blob part at runtime regardless of that generic parameter. + form.append("file", new Blob([file.bytes as unknown as BlobPart], { type: file.mime }), file.name); + + const headers: Record = {}; + if (this.apiKey) headers.authorization = `Bearer ${this.apiKey}`; + + const res = await this.fetchImpl(`${this.baseUrl}/api/files/upload`, { + method: "POST", + headers, + body: form, + }); + const data = (await parseJsonBody(res)) as { file?: { id: string }; isDuplicate?: boolean }; + if (!res.ok) { + throw new ApiError(res.status, errorMessage(data, res.statusText, res.status)); + } + return { id: data.file?.id as string, isDuplicate: data.isDuplicate }; + } + + // ---- local-executor bridge --------------------------------------------- + + async subscribeLocalExecutor( + tools: unknown[], + onFrame: (f: PlatformFrame) => void | Promise, + signal?: AbortSignal, + ): Promise { + await this.streamRequest("POST", "/api/chat/local-executor/subscribe", onFrame, { + body: { tools }, + signal, + }); + } + + async postToolResult(r: { tool_use_id: string; content: unknown; is_error?: boolean }): Promise { + await this.request("POST", "/api/chat/local-executor/tool-result", { body: r }); + } +} diff --git a/companions/desktop/src/api/ndjson.test.ts b/companions/desktop/src/api/ndjson.test.ts new file mode 100644 index 0000000000..19c56cdaee --- /dev/null +++ b/companions/desktop/src/api/ndjson.test.ts @@ -0,0 +1,219 @@ +import { describe, expect, it, vi } from "vitest"; +import { readNdjson } from "./ndjson"; + +/** Build a ReadableStream from a list of string chunks. */ +function streamFromChunks(chunks: string[]): ReadableStream { + const encoder = new TextEncoder(); + return new ReadableStream({ + start(controller) { + for (const chunk of chunks) { + controller.enqueue(encoder.encode(chunk)); + } + controller.close(); + }, + }); +} + +/** A promise you can resolve from the outside, for asserting ordering. */ +function deferred(): { + promise: Promise; + resolve: (value: T) => void; +} { + let resolve!: (value: T) => void; + const promise = new Promise((res) => { + resolve = res; + }); + return { promise, resolve }; +} + +describe("readNdjson", () => { + it("delivers a frame split across chunks", async () => { + const frames: Record[] = []; + const stream = streamFromChunks(['{"type":"a",', '"value":1}\n']); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ type: "a", value: 1 }]); + }); + + it("delivers two frames in a single chunk", async () => { + const frames: Record[] = []; + const stream = streamFromChunks(['{"type":"a"}\n{"type":"b"}\n']); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ type: "a" }, { type: "b" }]); + }); + + it("strips an optional 'data: ' SSE-style prefix", async () => { + const frames: Record[] = []; + const stream = streamFromChunks(['data: {"type":"a"}\n']); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ type: "a" }]); + }); + + it("skips [DONE] sentinel lines", async () => { + const frames: Record[] = []; + const stream = streamFromChunks(['{"type":"a"}\n[DONE]\n{"type":"b"}\n']); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ type: "a" }, { type: "b" }]); + }); + + it("silently skips garbage / non-JSON keepalive lines", async () => { + const frames: Record[] = []; + const stream = streamFromChunks([ + '{"type":"a"}\n', + ": keepalive\n", + "not json at all\n", + '{"type":"b"}\n', + ]); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ type: "a" }, { type: "b" }]); + }); + + it("flushes a trailing unterminated frame at stream end", async () => { + const frames: Record[] = []; + const stream = streamFromChunks(['{"type":"a"}\n{"type":"b"}']); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ type: "a" }, { type: "b" }]); + }); + + it("awaits onFrame sequentially before starting the next frame (backpressure)", async () => { + const order: string[] = []; + const gate1 = deferred(); + const gate2 = deferred(); + + const stream = streamFromChunks(['{"type":"1"}\n{"type":"2"}\n{"type":"3"}\n']); + + const onFrame = vi.fn(async (frame: Record) => { + order.push(`start:${frame.type}`); + if (frame.type === "1") await gate1.promise; + if (frame.type === "2") await gate2.promise; + order.push(`end:${frame.type}`); + }); + + const readPromise = readNdjson(stream, onFrame); + + // Only the first frame's callback should have started; the second + // frame must not begin until frame 1's promise resolves. + await Promise.resolve(); + await Promise.resolve(); + expect(order).toEqual(["start:1"]); + + gate1.resolve(); + await Promise.resolve(); + await Promise.resolve(); + await Promise.resolve(); + expect(order).toEqual(["start:1", "end:1", "start:2"]); + + gate2.resolve(); + await readPromise; + + expect(order).toEqual(["start:1", "end:1", "start:2", "end:2", "start:3", "end:3"]); + }); + + it("skips lines that parse to non-object JSON values (null, numbers, arrays)", async () => { + const frames: Record[] = []; + const stream = streamFromChunks(['null\n42\n[1,2]\n"str"\ntrue\n{"ok":1}\n']); + + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ ok: 1 }]); + }); + + it("decodes a multibyte character split across chunks, including in a trailing frame", async () => { + // '{"t":"é"}' with the two-byte é (0xC3 0xA9) split across chunks and + // no trailing newline — the decoder must carry the partial sequence + // across the boundary and the reader must flush the completed frame + // (with a final decoder flush) at stream end. + const bytes = new TextEncoder().encode('{"t":"é"}'); + const stream = new ReadableStream({ + start(controller) { + controller.enqueue(bytes.slice(0, 7)); // ends mid-é + controller.enqueue(bytes.slice(7)); + controller.close(); + }, + }); + + const frames: Record[] = []; + await readNdjson(stream, (frame) => { + frames.push(frame); + }); + + expect(frames).toEqual([{ t: "é" }]); + }); + + it("removes its abort listener on normal completion (no leak on a reused signal)", async () => { + const controller = new AbortController(); + const addSpy = vi.spyOn(controller.signal, "addEventListener"); + const removeSpy = vi.spyOn(controller.signal, "removeEventListener"); + + await readNdjson(streamFromChunks(['{"a":1}\n']), () => {}, controller.signal); + await readNdjson(streamFromChunks(['{"b":2}\n']), () => {}, controller.signal); + + const abortAdds = addSpy.mock.calls.filter(([type]) => type === "abort"); + const abortRemoves = removeSpy.mock.calls.filter(([type]) => type === "abort"); + expect(abortAdds).toHaveLength(2); + expect(abortRemoves).toHaveLength(2); + // The exact listener that was added must be the one removed. + expect(abortRemoves.map((call) => call[1])).toEqual(abortAdds.map((call) => call[1])); + }); + + it("rejects with a DOMException AbortError when aborted mid-stream", async () => { + const controller = new AbortController(); + const frames: Record[] = []; + const encoder = new TextEncoder(); + + // Enqueue one frame, then never close — the reader will block on the + // *next* read() call, which is where a real mid-stream abort happens. + const stream = new ReadableStream({ + start(streamController) { + streamController.enqueue(encoder.encode('{"type":"a"}\n')); + }, + }); + + const gotFirstFrame = deferred(); + const onFrame = vi.fn((frame: Record) => { + frames.push(frame); + gotFirstFrame.resolve(); + }); + + const readPromise = readNdjson(stream, onFrame, controller.signal); + + await gotFirstFrame.promise; // frame "a" delivered; reader is now blocked awaiting more data + controller.abort(); + + let error: unknown; + try { + await readPromise; + } catch (err) { + error = err; + } + + expect(error).toBeInstanceOf(DOMException); + expect((error as DOMException).name).toBe("AbortError"); + expect(frames).toEqual([{ type: "a" }]); + }); +}); diff --git a/companions/desktop/src/api/ndjson.ts b/companions/desktop/src/api/ndjson.ts new file mode 100644 index 0000000000..505acfde11 --- /dev/null +++ b/companions/desktop/src/api/ndjson.ts @@ -0,0 +1,130 @@ +/** + * Tolerant reader for the platform's `application/x-ndjson` streams. + * + * Every streaming endpoint (chat, flows, local-executor) emits one JSON + * object per line. This is the single place that reads those bytes off + * the wire — everything downstream consumes frames through `readNdjson`. + * + * Tolerance rules (see `tools/oa/src/chat-text.ts` and + * `services/openagentic-ui/src/utils/ndjsonStream.ts` for prior art): + * - UTF-8 decode with `stream: true` so multi-byte characters split + * across chunk boundaries decode correctly. + * - Split on `\n`; the trailing, possibly-incomplete segment is held + * over to the next chunk (and flushed at stream end). + * - Per line: trim, skip blank lines, strip an optional `data: ` SSE + * prefix, skip the `[DONE]` sentinel, and silently skip lines that + * don't parse as JSON (keepalive noise, proxy artifacts) or parse to + * a non-object value (`null`, numbers, arrays) — a single bad line + * must not kill the stream. + * + * Backpressure: `onFrame` is awaited *sequentially*, one frame at a time. + * This is required for HITL (human-in-the-loop) approval flows — the + * reader must not start pulling frame N+1 off the wire until whatever + * frame N triggered (e.g. rendering an approval prompt) has resolved. + * + * Cancellation: passing `signal` lets a caller abort mid-stream (e.g. the + * user navigates away). The returned promise rejects with the signal's + * abort reason, which is a `DOMException` named `AbortError` for a + * default `controller.abort()` call. + */ +export async function readNdjson( + body: ReadableStream, + onFrame: (frame: Record) => void | Promise, + signal?: AbortSignal, +): Promise { + if (signal?.aborted) { + throw toAbortError(signal); + } + + const reader = body.getReader(); + const decoder = new TextDecoder(); + let buffer = ""; + + /** Rejects the moment `signal` aborts, so a pending `reader.read()` can be raced away. + * The listener is captured so `finally` can remove it on non-abort exits — + * a long-lived AbortController reused across many sequential readNdjson + * calls must not accumulate one dangling listener per completed call. */ + let onAbort: (() => void) | undefined; + const abortPromise = signal + ? new Promise((_, reject) => { + onAbort = () => reject(toAbortError(signal)); + signal.addEventListener("abort", onAbort, { once: true }); + }) + : undefined; + + try { + while (true) { + const { done, value } = abortPromise + ? await Promise.race([reader.read(), abortPromise]) + : await reader.read(); + + if (done) { + // Flush any partial multibyte sequence the decoder is still holding, + // then the trailing unterminated line (if any). + buffer += decoder.decode(); + await handleLine(buffer, onFrame); + return; + } + + buffer += decoder.decode(value, { stream: true }); + + const lines = buffer.split("\n"); + buffer = lines.pop() ?? ""; + + for (const line of lines) { + await handleLine(line, onFrame); + } + } + } finally { + if (signal && onAbort) { + signal.removeEventListener("abort", onAbort); + } + try { + await reader.cancel(); + } catch { + // Already closed/cancelled — ignore. + } + try { + reader.releaseLock(); + } catch { + // Already released — ignore. + } + } +} + +async function handleLine( + rawLine: string, + onFrame: (frame: Record) => void | Promise, +): Promise { + let line = rawLine.trim(); + if (!line) return; + + if (line.startsWith("data: ")) { + line = line.slice("data: ".length).trim(); + } else if (line.startsWith("data:")) { + line = line.slice("data:".length).trim(); + } + if (!line || line === "[DONE]") return; + + let parsed: unknown; + try { + parsed = JSON.parse(line); + } catch { + // Not JSON — a keepalive comment or proxy artifact. Skip silently. + return; + } + + // Frames are always objects; skip primitives / null / arrays (e.g. a bare + // `null` keepalive) rather than forwarding a non-frame to consumers. + if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) { + return; + } + + await onFrame(parsed as Record); +} + +function toAbortError(signal: AbortSignal): unknown { + return signal.reason instanceof DOMException + ? signal.reason + : new DOMException("The operation was aborted.", "AbortError"); +} diff --git a/companions/desktop/src/api/types.ts b/companions/desktop/src/api/types.ts new file mode 100644 index 0000000000..4875ef2da7 --- /dev/null +++ b/companions/desktop/src/api/types.ts @@ -0,0 +1,60 @@ +/** + * Shared wire types for the platform API client. + * + * These mirror the response envelopes documented in `tools/oa/src/client.ts` + * (the reference CLI client) and the route handlers under + * `services/openagentic-api/src/routes/**`. `client.ts` unwraps each + * endpoint's (inconsistent) envelope down to these shapes before handing + * data to callers. + */ + +/** The subset of the global `fetch` signature the client depends on — lets + * tests and (eventually) a Tauri HTTP shim substitute their own impl. */ +export type FetchLike = typeof fetch; + +/** Local auth/session state the rest of the app reads from a store; the + * client itself is stateless and only produces the pieces of this. */ +export interface AuthState { + baseUrl: string; + apiKey: string | null; + userId: string | null; + email: string | null; + isAdmin: boolean; +} + +/** One NDJSON line off any streaming endpoint (chat, workflow execute, + * local-executor subscribe). `type` is the only field every frame shares; + * everything else is endpoint-specific and passed through verbatim. */ +export interface PlatformFrame { + type: string; + _seq?: number; + _runId?: string; + _ts?: number; + [k: string]: unknown; +} + +export interface ChatSession { + id: string; + title: string; + createdAt: string; + updatedAt: string; +} + +export interface ModelInfo { + id: string; + name?: string; + provider?: string; +} + +export interface ModelsReport { + models: ModelInfo[]; + defaultModel?: string; + codemodeDefault?: string; + provider_status?: Record; +} + +export interface WorkflowSummary { + id: string; + name: string; + description?: string; +} diff --git a/companions/desktop/src/assets/fonts/inter-600.woff2 b/companions/desktop/src/assets/fonts/inter-600.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..03aaea1ce7dcce7ccef4cf7fc116a904d50aa204 GIT binary patch literal 24368 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 aUbWq0We;bU+VQC=KKz{j$8G)n1^@ua=Md%q literal 0 HcmV?d00001 diff --git a/companions/desktop/src/assets/fonts/inter-regular.woff2 b/companions/desktop/src/assets/fonts/inter-regular.woff2 new file mode 100644 index 0000000000000000000000000000000000000000..33002f12853a3d37d7b7eba39623fae3bca94a3b GIT binary patch literal 23692 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 s.status); + const error = useAuthStore((s) => s.error); + const signIn = useAuthStore((s) => s.signIn); + + const [instanceUrl, setInstanceUrl] = useState(DEFAULT_INSTANCE); + const [email, setEmail] = useState(""); + const [password, setPassword] = useState(""); + + const busy = status === "signingIn"; + + function onSubmit(e: FormEvent) { + e.preventDefault(); + if (busy) return; + void signIn(instanceUrl.trim(), email.trim(), password); + } + + return ( +
+
+
+
OPENAGENTIC
+

Sign in to your platform instance.

+
+ + + + + + + + {error ? ( +

+ {error} +

+ ) : null} + + +
+
+ ); +} diff --git a/companions/desktop/src/chat/ApprovalSheet.test.tsx b/companions/desktop/src/chat/ApprovalSheet.test.tsx new file mode 100644 index 0000000000..da5ab45608 --- /dev/null +++ b/companions/desktop/src/chat/ApprovalSheet.test.tsx @@ -0,0 +1,107 @@ +import { describe, it, expect, afterEach, vi } from "vitest"; +import { cleanup, fireEvent, render, screen } from "@testing-library/react"; +import ApprovalSheet from "./ApprovalSheet"; +import type { Block } from "./streamReducer"; + +type ApprovalBlock = Extract; + +function approvalBlock(overrides: Partial = {}): ApprovalBlock { + return { + kind: "approval", + auditId: "aud-1", + toolName: "k8s_delete_pod", + serverName: "kubernetes", + args: { pod: "web-7f8", namespace: "prod" }, + preview: "DELETE pod web-7f8", + classification: "mutating", + ...overrides, + }; +} + +afterEach(cleanup); + +describe("ApprovalSheet — render", () => { + it("shows the tool, server, classification, preview and pretty-printed args (approval_required shape)", () => { + render(); + + expect(screen.getByTestId("approval-tool").textContent).toBe("k8s_delete_pod"); + expect(screen.getByTestId("approval-server").textContent).toContain("kubernetes"); + expect(screen.getByTestId("approval-classification").textContent).toContain("mutating"); + expect(screen.getByTestId("approval-preview").textContent).toContain("DELETE pod web-7f8"); + // Args are pretty-printed JSON (indented, keys visible). + const args = screen.getByTestId("approval-args").textContent ?? ""; + expect(args).toContain("web-7f8"); + expect(args).toContain("namespace"); + expect(args).toContain("\n"); // multi-line = JSON.stringify(..., 2) + }); + + it("renders the mcp_approval_required shape (no preview, args normalized from `arguments`)", () => { + // The reducer already normalizes mcp_approval_required (requestId→auditId, + // arguments→args, no preview) into this block shape; the sheet renders it. + render( + , + ); + expect(screen.getByTestId("approval-tool").textContent).toBe("aws_terminate_instance"); + expect(screen.getByTestId("approval-args").textContent).toContain("i-0abc"); + // No preview block when the frame carried none. + expect(screen.queryByTestId("approval-preview")).toBeNull(); + }); +}); + +describe("ApprovalSheet — decisions", () => { + it("Allow button calls onDecision(auditId, 'approve')", () => { + const onDecision = vi.fn(); + render(); + fireEvent.click(screen.getByTestId("approval-allow")); + expect(onDecision).toHaveBeenCalledWith("aud-9", "approve"); + }); + + it("Deny button calls onDecision(auditId, 'deny')", () => { + const onDecision = vi.fn(); + render(); + fireEvent.click(screen.getByTestId("approval-deny")); + expect(onDecision).toHaveBeenCalledWith("aud-9", "deny"); + }); + + it("⌘↵ approves and Escape denies", () => { + const onDecision = vi.fn(); + render(); + + fireEvent.keyDown(window, { key: "Enter", metaKey: true }); + expect(onDecision).toHaveBeenCalledWith("aud-k", "approve"); + + fireEvent.keyDown(window, { key: "Escape" }); + expect(onDecision).toHaveBeenCalledWith("aud-k", "deny"); + }); + + it("mounted on a hidden surface (active=false): window ⌘↵/Esc must NOT resolve the gate", () => { + // Surfaces stay mounted while hidden; a keystroke aimed at the visible + // surface must never silently resolve this unseen approval. + const onDecision = vi.fn(); + const { rerender } = render( + , + ); + + fireEvent.keyDown(window, { key: "Enter", metaKey: true }); + fireEvent.keyDown(window, { key: "Escape" }); + expect(onDecision).not.toHaveBeenCalled(); + + // Back on the chat surface the same keys work again. + rerender( + , + ); + fireEvent.keyDown(window, { key: "Enter", metaKey: true }); + expect(onDecision).toHaveBeenCalledWith("aud-bg", "approve"); + }); +}); diff --git a/companions/desktop/src/chat/ApprovalSheet.tsx b/companions/desktop/src/chat/ApprovalSheet.tsx new file mode 100644 index 0000000000..0a5fa678fc --- /dev/null +++ b/companions/desktop/src/chat/ApprovalSheet.tsx @@ -0,0 +1,147 @@ +/** + * ApprovalSheet — the HITL gate. When the live turn pauses on a trailing, + * unresolved `approval` block, this modal sheet surfaces the tool, its server, + * a risk/mutation classification, an optional preview, and the pretty-printed + * call arguments so the operator can make an informed Allow / Deny call. + * + * Both wire shapes are already normalized by the reducer into one `approval` + * block: `approval_required` (mutating gate — `auditId`, `args`, `preview`, + * `classification`) and `mcp_approval_required` (HITL risk gate — `requestId` + * → auditId, `arguments` → args, `riskLevel` → classification, no preview). + * The sheet just renders whatever the block carries. + * + * Allow = ⌘↵ / Deny = esc. The decision goes to `onDecision(auditId, verdict)` + * (useChat.approve), which posts to the server and optimistically resolves the + * block; the paused stream then resumes on its existing connection. The + * optimistic resolve unmounts this sheet on the next render, and useChat's + * per-auditId in-flight guard collapses rapid double-fires (click or ⌘↵) into + * a single POST — so the sheet carries no busy/disabled state of its own. + */ +import { useEffect } from "react"; +import type { Block } from "./streamReducer"; + +type ApprovalBlock = Extract; + +function formatArgs(value: unknown): string { + if (value === undefined) return ""; + if (typeof value === "string") return value; + try { + return JSON.stringify(value, null, 2); + } catch { + return String(value); + } +} + +export default function ApprovalSheet({ + approval, + onDecision, + active, +}: { + approval: ApprovalBlock; + onDecision: (auditId: string, verdict: "approve" | "deny") => void; + /** Whether the hosting surface is the visible one. Surfaces stay mounted + * while hidden, and `display:none` does not silence a window keydown + * listener — so the listener only registers while active, or a keystroke on + * the VISIBLE surface could silently resolve this unseen gate. */ + active: boolean; +}) { + const { auditId } = approval; + + useEffect(() => { + if (!active) return; // hidden surface: never capture window keys + function onKey(e: KeyboardEvent) { + if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + onDecision(auditId, "approve"); + } else if (e.key === "Escape") { + e.preventDefault(); + onDecision(auditId, "deny"); + } + } + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + // Re-bind when the pending approval changes so the verdict targets it, and + // when the surface becomes active/inactive. + }, [auditId, onDecision, active]); + + const args = formatArgs(approval.args); + + return ( +
+
+
+
+ Approval required +
+
+ + {approval.toolName} + + {approval.serverName ? ( + + {approval.serverName} + + ) : null} + {approval.classification ? ( + + {approval.classification} + + ) : null} +
+
+ +
+ {approval.preview ? ( +
+ {approval.preview} +
+ ) : null} + {args ? ( +
+
+ Arguments +
+
+                {args}
+              
+
+ ) : null} +
+ +
+ + +
+
+
+ ); +} diff --git a/companions/desktop/src/chat/ChatSurface.tsx b/companions/desktop/src/chat/ChatSurface.tsx new file mode 100644 index 0000000000..e0a1fb0531 --- /dev/null +++ b/companions/desktop/src/chat/ChatSurface.tsx @@ -0,0 +1,281 @@ +/** + * ChatSurface — the chat product surface: session rail + models strip + + * transcript + composer, wired to `chatStore` (sessions/models) and `useChat` + * (the active session's turns and streaming). + * + * Sending from a cold start (no active session) creates one first, then flushes + * the queued message once `useChat` re-binds to the new session id. + */ +import { useEffect, useState } from "react"; +import { useAppStore } from "../stores/appStore"; +import { useAuthStore } from "../stores/authStore"; +import { useChatStore } from "../stores/chatStore"; +import { useChat, type AttachmentRef, type AssistantTurn } from "./useChat"; +import type { ModelsReport } from "../api/types"; +import type { Block } from "./streamReducer"; +import SessionSidebar from "./SessionSidebar"; +import MessageList from "./MessageList"; +import Composer from "./Composer"; +import ApprovalSheet from "./ApprovalSheet"; +import { useTrustStore } from "../executor/trustStore"; +import { useLocalExecutor } from "../executor/useLocalExecutor"; +import TrustBanner from "../executor/TrustBanner"; +import WorkspaceApprovalModal from "../executor/WorkspaceApprovalModal"; +import { useDockBadge } from "../shell/useDockBadge"; + +/** The live turn pauses on an approval by emitting it as the trailing block; + * an unresolved trailing approval is what the modal sheet gates on. */ +function pendingApproval(turn: AssistantTurn | null): Extract | null { + if (!turn) return null; + const last = turn.blocks[turn.blocks.length - 1]; + return last && last.kind === "approval" && !last.resolved ? last : null; +} + +const SUGGESTED_PROMPTS = [ + "Summarize the health of my Kubernetes cluster.", + "What were my top 5 AWS cost drivers last month?", + "Search the web for the latest on the OpenAgentic project.", +]; + +function isHealthy(v: unknown): boolean { + if (v === true) return true; + if (typeof v === "string") return ["ok", "connected", "healthy", "ready", "up"].includes(v.toLowerCase()); + if (v && typeof v === "object") { + const o = v as { connected?: unknown; status?: unknown; healthy?: unknown }; + if (o.connected === true || o.healthy === true) return true; + if (typeof o.status === "string") return isHealthy(o.status); + } + return false; +} + +function ModelsStrip({ models }: { models: ModelsReport | null }) { + if (!models) return null; + const providers = Object.entries(models.provider_status ?? {}); + return ( +
+ {providers.length > 0 ? ( +
+ {providers.map(([name, status]) => ( + + + ))} +
+ ) : null} + +
+ {models.models.slice(0, 6).map((m) => { + const active = m.id === models.defaultModel; + return ( + + {m.name || m.id} + + ); + })} +
+ + + routing: smart + +
+ ); +} + +/** "Give chat local hands" — the chat-header switch that arms the + * local-executor bridge. Disabled (with a hint) until a trusted workspace + * exists, so turning it on can never grant hands with nowhere scoped to work. */ +function LocalHandsToggle() { + const enabled = useTrustStore((s) => s.enabled); + const setEnabled = useTrustStore((s) => s.setEnabled); + const rootsCount = useTrustStore((s) => s.roots.length); + const noRoots = rootsCount === 0; + + return ( + + ); +} + +function EmptyState({ onPrompt }: { onPrompt: (text: string) => void }) { + return ( +
+
+ +
+
◇ Try asking
+ {SUGGESTED_PROMPTS.map((p, i) => ( + + ))} +
+
+ ); +} + +export default function ChatSurface({ + visible = true, +}: { + /** Whether Chat is the active (visible) surface. Chat stays mounted while + * hidden; this gates its modals' window key-listeners and drives the rail's + * pending-gate badge. */ + visible?: boolean; +}) { + const activeSessionId = useChatStore((s) => s.activeSessionId); + const sessions = useChatStore((s) => s.sessions); + const models = useChatStore((s) => s.models); + const prefill = useChatStore((s) => s.prefill); + const setPrefill = useChatStore((s) => s.setPrefill); + const loadSessions = useChatStore((s) => s.loadSessions); + const loadModels = useChatStore((s) => s.loadModels); + const createSession = useChatStore((s) => s.createSession); + + const { turns, liveTurn, loading, send, stop, approve } = useChat(activeSessionId); + const [pending, setPending] = useState<{ text: string; attachments?: AttachmentRef[] } | null>(null); + const approval = pendingApproval(liveTurn); + const loadTrust = useTrustStore((s) => s.loadFromStorage); + const trustPending = useTrustStore((s) => s.pending); + const setPendingGate = useAppStore((s) => s.setPendingGate); + + // Run the local-executor subscribe loop while "local hands" is on. + useLocalExecutor(); + + // Surface chat's unresolved gates (HITL approval / workspace consent) on the + // rail so a gate paused behind a hidden surface is discoverable. + useEffect(() => { + setPendingGate("chat", approval !== null || trustPending !== null); + }, [approval, trustPending, setPendingGate]); + useEffect(() => () => setPendingGate("chat", false), [setPendingGate]); + + // Boot: hydrate sessions + models + persisted trust config once. + useEffect(() => { + void loadSessions(); + void loadModels(); + loadTrust(); + }, [loadSessions, loadModels, loadTrust]); + + // Flush a queued cold-start message once the freshly-created session is live. + useEffect(() => { + if (pending && activeSessionId) { + const p = pending; + setPending(null); + void send(p.text, p.attachments); + } + }, [pending, activeSessionId, send]); + + async function handleSend(text: string, attachments?: AttachmentRef[]) { + if (!activeSessionId) { + const session = await createSession(text); + if (!session) return; + setPending({ text, attachments }); + return; + } + void send(text, attachments); + } + + async function handleUpload(file: File): Promise { + const client = useAuthStore.getState().client; + if (!client) return null; + try { + const bytes = new Uint8Array(await file.arrayBuffer()); + const res = await client.uploadFile({ + name: file.name, + mime: file.type || "application/octet-stream", + bytes, + }); + return { id: res.id, name: file.name, mime: file.type || undefined, size: file.size }; + } catch { + return null; + } + } + + const showEmpty = !activeSessionId && sessions.length === 0; + const busy = liveTurn !== null; + + // macOS dock badge while a turn streams (no-op off the Tauri host). + useDockBadge(busy); + + return ( +
+ +
+
+ Chat + +
+ + + {showEmpty ? ( + + ) : ( + + )} + setPrefill("")} + onUpload={handleUpload} + /> +
+ {approval ? : null} + +
+ ); +} diff --git a/companions/desktop/src/chat/Composer.tsx b/companions/desktop/src/chat/Composer.tsx new file mode 100644 index 0000000000..90ca99bc21 --- /dev/null +++ b/companions/desktop/src/chat/Composer.tsx @@ -0,0 +1,169 @@ +/** + * Composer — the message input. + * + * Autosizing textarea (1→8 rows); ⌘↵ / Ctrl+↵ sends, plain ↵ and Shift+↵ + * insert newlines (the field is multiline-first). Files dropped onto the + * composer are uploaded via the injected `onUpload` and held as pending + * attachment chips until the next send. While a turn streams, the send button + * becomes a Stop button wired to `onStop`. + */ +import { useEffect, useRef, useState, type DragEvent, type KeyboardEvent } from "react"; +import type { AttachmentRef } from "./useChat"; + +const MAX_HEIGHT = 200; // ~8 rows + +export default function Composer({ + onSend, + onStop, + busy = false, + disabled = false, + prefill, + onPrefillConsumed, + onUpload, +}: { + onSend: (text: string, attachments?: AttachmentRef[]) => void; + onStop: () => void; + busy?: boolean; + disabled?: boolean; + prefill?: string; + onPrefillConsumed?: () => void; + onUpload?: (file: File) => Promise; +}) { + const [text, setText] = useState(""); + const [attachments, setAttachments] = useState([]); + const [dragging, setDragging] = useState(false); + const [uploading, setUploading] = useState(0); + const ref = useRef(null); + + // Autosize. + useEffect(() => { + const el = ref.current; + if (!el) return; + el.style.height = "auto"; + el.style.height = `${Math.min(el.scrollHeight, MAX_HEIGHT)}px`; + }, [text]); + + // Apply a prefill (suggested prompt / empty-state click) then clear it. + useEffect(() => { + if (prefill) { + setText(prefill); + onPrefillConsumed?.(); + ref.current?.focus(); + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [prefill]); + + const canSend = !busy && !disabled && (text.trim().length > 0 || attachments.length > 0); + + function submit() { + if (!canSend) return; + onSend(text, attachments.length ? attachments : undefined); + setText(""); + setAttachments([]); + } + + function onKeyDown(e: KeyboardEvent) { + if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { + e.preventDefault(); + submit(); + } + } + + async function ingestFiles(files: FileList) { + if (!onUpload) return; + for (const file of Array.from(files)) { + setUploading((n) => n + 1); + try { + const ref2 = await onUpload(file); + if (ref2) setAttachments((prev) => [...prev, ref2]); + } finally { + setUploading((n) => n - 1); + } + } + } + + function onDrop(e: DragEvent) { + e.preventDefault(); + setDragging(false); + if (e.dataTransfer.files?.length) void ingestFiles(e.dataTransfer.files); + } + + return ( +
+
{ + e.preventDefault(); + if (onUpload) setDragging(true); + }} + onDragLeave={() => setDragging(false)} + onDrop={onDrop} + className={`mx-auto flex max-w-3xl flex-col gap-2 rounded-lg border bg-bg p-2 transition-colors focus-within:border-accent-ink ${ + dragging ? "border-accent" : "border-border" + }`} + > + {attachments.length || uploading > 0 ? ( +
+ {attachments.map((a) => ( + + {a.name} + + + ))} + {uploading > 0 ? ( + + Uploading… ({uploading}) + + ) : null} +
+ ) : null} + +
+