diff --git a/.github/workflows/build-macos-arm64-dmg.yml b/.github/workflows/build-macos-arm64-dmg.yml index 551ccca0..71aeaa04 100644 --- a/.github/workflows/build-macos-arm64-dmg.yml +++ b/.github/workflows/build-macos-arm64-dmg.yml @@ -123,3 +123,79 @@ jobs: name: dmg-${{ inputs.macos_version }}-arm64 path: artifacts/*.dmg retention-days: 7 + + build-macos-dmg-arm64-electron: + name: Build Electron DMG (arm64, ${{ inputs.macos_version }}) + runs-on: ${{ inputs.macos_version }} + steps: + - name: Checkout + uses: actions/checkout@v4 + with: + ref: ${{ inputs.ref || github.ref }} + + - name: Setup bun + uses: oven-sh/setup-bun@v2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Install Apple Certificate + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + KEYCHAIN_PATH=$RUNNER_TEMP/electron-signing.keychain-db + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + echo "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12 + security import $RUNNER_TEMP/certificate.p12 \ + -P "$APPLE_CERTIFICATE_PASSWORD" \ + -A -t cert -f pkcs12 \ + -k "$KEYCHAIN_PATH" + + security list-keychain -d user -s "$KEYCHAIN_PATH" + security set-key-partition-list -S apple-tool:,apple:,codesign: \ + -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + - name: Build Electron app (arm64) + working-directory: packages/electron + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + ELECTRON_BUILDER_ARCH: arm64 + run: | + bun run build:web-assets + bun run bundle:main + bun run rebuild:native + ./node_modules/.bin/electron-builder --mac --arm64 --publish=never + + - name: Prepare DMG artifact + run: | + set -euo pipefail + mkdir -p artifacts + DMG_PATH="packages/electron/dist/*.dmg" + if ls $DMG_PATH 1> /dev/null 2>&1; then + DMG_FILE=$(ls $DMG_PATH | head -n 1) + DMG_NAME="OpenChamber_Electron_${{ inputs.macos_version }}_arm64.dmg" + cp "$DMG_FILE" "artifacts/$DMG_NAME" + else + echo "Error: DMG file not found at $DMG_PATH" + exit 1 + fi + + - name: Upload DMG artifact + uses: actions/upload-artifact@v4 + with: + name: dmg-electron-${{ inputs.macos_version }}-arm64 + path: artifacts/*.dmg + retention-days: 7 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index e4688e7f..b1865b18 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -494,8 +494,170 @@ jobs: env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + build-desktop-electron-macos: + needs: create-release + runs-on: macos-26 + strategy: + fail-fast: false + matrix: + include: + - target: aarch64-apple-darwin + arch: arm64 + platform: darwin-aarch64 + - target: x86_64-apple-darwin + arch: x64 + platform: darwin-x86_64 + steps: + - uses: actions/checkout@v4 + + - name: Setup bun + uses: oven-sh/setup-bun@v2 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Install dependencies + run: bun install --frozen-lockfile + + - name: Install Apple Certificate + env: + APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} + APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} + run: | + KEYCHAIN_PATH=$RUNNER_TEMP/electron-signing.keychain-db + KEYCHAIN_PASSWORD=$(openssl rand -base64 32) + + security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH" + security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + echo "$APPLE_CERTIFICATE" | base64 --decode > $RUNNER_TEMP/certificate.p12 + security import $RUNNER_TEMP/certificate.p12 \ + -P "$APPLE_CERTIFICATE_PASSWORD" \ + -A -t cert -f pkcs12 \ + -k "$KEYCHAIN_PATH" + + security list-keychain -d user -s "$KEYCHAIN_PATH" + security set-key-partition-list -S apple-tool:,apple:,codesign: \ + -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH" + + - name: Build Electron app + working-directory: packages/electron + env: + APPLE_ID: ${{ secrets.APPLE_ID }} + APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_PASSWORD }} + APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }} + # rebuild-native.mjs reads this to target the right arch when + # cross-building (runner is arm64; x64 matrix needs the hint). + ELECTRON_BUILDER_ARCH: ${{ matrix.arch }} + run: | + bun run build:web-assets + bun run bundle:main + # npmRebuild=false in package.json, so electron-builder won't + # recompile native deps on its own — we must rebuild against the + # target Electron ABI before packaging, otherwise better-sqlite3/ + # node-pty/bun-pty crash on require inside the packaged app. + bun run rebuild:native + ./node_modules/.bin/electron-builder --mac --${{ matrix.arch }} --publish=never + + - name: Verify signature + entitlements + notarization + run: | + set -euo pipefail + + APP_DIR="packages/electron/dist/mac" + [ -d "packages/electron/dist/mac-arm64" ] && APP_DIR="packages/electron/dist/mac-arm64" + + APP_PATH=$(find "$APP_DIR" -maxdepth 2 -name "*.app" -print -quit) + if [ -z "$APP_PATH" ]; then + echo "Error: .app not found under packages/electron/dist/mac*" + ls -la packages/electron/dist/ + exit 1 + fi + + echo "Verifying $APP_PATH" + codesign -vv --deep --strict "$APP_PATH" + + # Require hardened runtime + CS_INFO=$(codesign -dv --verbose=4 "$APP_PATH" 2>&1) + echo "$CS_INFO" + if ! echo "$CS_INFO" | grep -q "flags=.*runtime"; then + echo "Error: hardened runtime flag missing" + exit 1 + fi + + # Require notary ticket stapled + xcrun stapler validate "$APP_PATH" + + ENTITLEMENTS=$(codesign -d --entitlements :- "$APP_PATH" 2>&1 || true) + if echo "$ENTITLEMENTS" | grep -q "com.apple.security.app-sandbox"; then + echo "Error: app sandbox entitlement is present" + exit 1 + fi + for key in \ + com.apple.security.cs.allow-jit \ + com.apple.security.cs.allow-unsigned-executable-memory \ + com.apple.security.cs.disable-library-validation + do + if ! echo "$ENTITLEMENTS" | grep -q "$key"; then + echo "Error: required entitlement missing: $key" + exit 1 + fi + done + + - name: Upload DMG / ZIP / blockmaps to release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.create-release.outputs.version }} + files: | + packages/electron/dist/*.dmg + packages/electron/dist/*.zip + packages/electron/dist/*.blockmap + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload per-arch latest-mac.yml for merge + uses: actions/upload-artifact@v4 + with: + name: latest-yml-${{ matrix.target }} + path: packages/electron/dist/latest-mac.yml + retention-days: 1 + + combine-electron-manifests: + needs: [create-release, build-desktop-electron-macos] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '20' + + - name: Download per-arch latest-mac.yml + uses: actions/download-artifact@v4 + with: + pattern: latest-yml-*-apple-darwin + path: artifacts + + - name: Finalize combined latest-mac.yml + env: + LATEST_YML_DIR: ${{ github.workspace }}/artifacts + GH_REPO: ${{ github.repository }} + OPENCHAMBER_VERSION: ${{ needs.create-release.outputs.version }} + run: node packages/electron/scripts/finalize-latest-yml.mjs + + - name: Upload combined latest-mac.yml to release + uses: softprops/action-gh-release@v2 + with: + tag_name: v${{ needs.create-release.outputs.version }} + files: ${{ runner.temp }}/latest-mac.yml + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + finalize-release: - needs: [create-release, build-desktop-macos, publish-npm, combine-manifests] + needs: [create-release, build-desktop-macos, build-desktop-electron-macos, publish-npm, combine-manifests, combine-electron-manifests] runs-on: ubuntu-latest env: DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }} diff --git a/AGENTS.md b/AGENTS.md index 880a054f..174812e0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,9 +6,19 @@ OpenChamber provides UI runtimes (web/desktop/VS Code) for interacting with an O ## Runtime architecture (IMPORTANT) -- `Desktop` is a thin Tauri shell that starts the web server sidecar and loads the web UI from `http://127.0.0.1:`. -- All backend logic lives in `packages/web/server/*` (and `packages/vscode/*` for the VS Code runtime). Desktop Rust is not a feature backend. -- Tauri is used only for stable native integrations: menu, dialog (open folder), notifications, updater, deep-links. +- `Desktop` (Electron) boots the web server **in the same Node process** as the Electron main, then loads the web UI from `http://127.0.0.1:`. No sidecar subprocess. +- `Desktop` (Tauri, legacy) still spawns `openchamber-server` as a bun-compiled sidecar binary. Kept only for auto-update compatibility with existing Tauri installs. +- All backend logic lives in `packages/web/server/*` (and `packages/vscode/*` for the VS Code runtime). The native shell is not a feature backend. +- The shell is used only for stable native integrations: menu, dialog (open folder), notifications, updater, deep-links, quit confirmation. + +### Desktop shell: Electron is the target, Tauri is legacy + +- **New desktop work goes into `packages/electron/`.** This is the forward path. +- `packages/desktop/` (Tauri) is kept running in parallel only to preserve auto-update for existing installs until the cutover. Do **not** add features to it; do **not** port bug fixes back unless they actually affect currently-released Tauri users. +- Desktop-side changes (IPC handlers, native integrations, window/quit/notification behavior) land in `packages/electron/main.mjs` + `packages/electron/preload.mjs`. The `__TAURI__` shim exposed by the preload keeps the shared UI working against both shells, so renderer-side code should not branch on shell type. +- Electron imports the server via `@openchamber/web/server/index.js` (workspace dep) and calls `startWebUiServer({...})`. The returned handle has `getPort()` / `stop()`. Notifications flow via an `onDesktopNotification` callback injected at startup — no stdout-parsing IPC. +- Build/release: both shells ship in the same GitHub release today (`.github/workflows/release.yml`). The one-shot Tauri → Electron auto-update migration is documented in `docs/TAURI_TO_ELECTRON_CUTOVER.md`; run that when the user decides to flip. +- After the cutover ships and stabilises, `packages/desktop/` is deleted; this note collapses back to "Desktop is Electron". ## Tech stack (source of truth: `package.json`, resolved: `bun.lock`) @@ -17,7 +27,8 @@ OpenChamber provides UI runtimes (web/desktop/VS Code) for interacting with an O - State: Zustand (`packages/ui/src/stores/`) - UI primitives: Base UI (`@base-ui/react`, primary source for dropdown/select/dialog/menu/tooltip/etc. — wrappers live in `packages/ui/src/components/ui/`), Radix UI (`package.json` deps, legacy usages being migrated), HeroUI (`package.json` deps), Remixicon (`package.json` deps) - Server: Express (`packages/web/server/index.js`) -- Desktop: Tauri v2 (`packages/desktop/src-tauri/`) +- Desktop (forward): Electron 41 (`packages/electron/`) +- Desktop (legacy, maintenance-only): Tauri v2 (`packages/desktop/src-tauri/`) - VS Code: extension + webview (`packages/vscode/`) ## Monorepo layout @@ -26,7 +37,8 @@ Workspaces are `packages/*` (see `package.json`). - Shared UI: `packages/ui` - Web app + server + CLI: `packages/web` -- Desktop app (Tauri): `packages/desktop` +- Desktop shell (Electron — forward): `packages/electron` +- Desktop shell (Tauri — legacy, maintenance-only): `packages/desktop` - VS Code extension: `packages/vscode` ## Documentation map @@ -95,7 +107,9 @@ All scripts are in `package.json`. - Validate: `bun run type-check`, `bun run lint` - Build all: `bun run build` -- Desktop build: `bun run desktop:build` +- Desktop build (Electron — primary): `bun run electron:build` +- Desktop dev (Electron): `bun run electron:dev` +- Desktop build (Tauri — legacy): `bun run desktop:build` - VS Code build: `bun run vscode:build` - Release smoke build: `bun run release:test` (shell script: `scripts/test-release-build.sh`) @@ -104,8 +118,8 @@ All scripts are in `package.json`. - Web bootstrap: `packages/web/src/main.tsx` - Web server: `packages/web/server/index.js` - Web CLI: `packages/web/bin/cli.js` (package bin: `packages/web/package.json`) -- Desktop: Tauri entry `packages/desktop/src-tauri/src/main.rs` (spawns web server sidecar + loads web UI) -- Tauri backend: `packages/desktop/src-tauri/src/main.rs` +- Desktop (Electron — primary): `packages/electron/main.mjs` (boots the web server in-process via `startWebUiServer`, loads web UI over loopback; preload at `packages/electron/preload.mjs` exposes the `__TAURI__` IPC shim so shared UI code is shell-agnostic) +- Desktop (Tauri — legacy): `packages/desktop/src-tauri/src/main.rs` - VS Code extension host: `packages/vscode/src/extension.ts` - VS Code webview bootstrap: `packages/vscode/webview/main.tsx` diff --git a/bun.lock b/bun.lock index 750637ed..0c009676 100644 --- a/bun.lock +++ b/bun.lock @@ -102,6 +102,21 @@ "typescript": "~5.8.3", }, }, + "packages/electron": { + "name": "@openchamber/electron", + "version": "1.9.6", + "dependencies": { + "@openchamber/web": "workspace:*", + "electron-context-menu": "^4.1.2", + "electron-log": "^5.4.3", + "electron-updater": "^6.8.3", + }, + "devDependencies": { + "@electron/rebuild": "^3.7.0", + "electron": "^41.2.1", + "electron-builder": "^26.0.0", + }, + }, "packages/ui": { "name": "@openchamber/ui", "version": "1.9.6", @@ -231,15 +246,37 @@ "openchamber": "./bin/cli.js", }, "dependencies": { - "@base-ui/react": "^1.4.0", "@clack/prompts": "^1.1.0", + "@octokit/rest": "^22.0.1", + "@opencode-ai/sdk": "^1.4.6", + "@simplewebauthn/server": "13.3.0", + "adm-zip": "^0.5.16", + "better-sqlite3": "^11.7.0", + "bun-pty": "^0.4.5", + "compression": "^1.8.1", + "cron-parser": "^4.9.0", + "express": "^5.1.0", + "http-proxy-middleware": "^3.0.5", + "jose": "^6.1.3", + "jsonc-parser": "^3.3.1", + "luxon": "^3.5.0", + "node-pty": "1.2.0-beta.12", + "openai": "^4.79.0", + "qrcode-terminal": "^0.12.0", + "reflect-metadata": "^0.2.2", + "simple-git": "^3.28.0", + "web-push": "^3.6.7", + "ws": "^8.18.3", + "yaml": "^2.8.1", + }, + "devDependencies": { + "@base-ui/react": "^1.4.0", "@codemirror/lang-cpp": "^6.0.3", "@codemirror/lang-go": "^6.0.1", + "@eslint/js": "^9.33.0", "@fontsource/ibm-plex-mono": "^5.2.7", "@fontsource/ibm-plex-sans": "^5.1.1", "@ibm/plex": "^6.4.1", - "@octokit/rest": "^22.0.1", - "@opencode-ai/sdk": "^1.4.6", "@radix-ui/react-collapsible": "^1.1.12", "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", @@ -251,57 +288,35 @@ "@radix-ui/react-tooltip": "^1.2.8", "@remixicon/react": "^4.7.0", "@simplewebauthn/browser": "13.3.0", - "@simplewebauthn/server": "13.3.0", - "@types/react-syntax-highlighter": "^15.5.13", - "adm-zip": "^0.5.16", - "bun-pty": "^0.4.5", - "class-variance-authority": "^0.7.1", - "clsx": "^2.1.1", - "cmdk": "^1.1.1", - "compression": "^1.8.1", - "cron-parser": "^4.9.0", - "express": "^5.1.0", - "ghostty-web": "0.3.0", - "http-proxy-middleware": "^3.0.5", - "jose": "^6.1.3", - "jsonc-parser": "^3.3.1", - "luxon": "^3.5.0", - "next-themes": "^0.4.6", - "node-pty": "1.2.0-beta.12", - "openai": "^4.79.0", - "qrcode-terminal": "^0.12.0", - "react": "^19.1.1", - "react-dom": "^19.1.1", - "react-markdown": "^10.1.0", - "react-syntax-highlighter": "^15.6.6", - "reflect-metadata": "^0.2.2", - "remark-gfm": "^4.0.1", - "simple-git": "^3.28.0", - "sonner": "^2.0.7", - "strip-json-comments": "^5.0.3", - "tailwind-merge": "^3.3.1", - "web-push": "^3.6.7", - "ws": "^8.18.3", - "yaml": "^2.8.1", - "zustand": "^5.0.8", - }, - "devDependencies": { - "@eslint/js": "^9.33.0", "@tailwindcss/postcss": "^4.0.0", "@types/adm-zip": "^0.5.7", "@types/node": "^24.3.1", "@types/react": "^19.1.10", "@types/react-dom": "^19.1.7", + "@types/react-syntax-highlighter": "^15.5.13", "@vitejs/plugin-react": "^5.0.0", "autoprefixer": "^10.4.21", + "class-variance-authority": "^0.7.1", + "clsx": "^2.1.1", + "cmdk": "^1.1.1", "concurrently": "^9.2.1", "cors": "^2.8.5", "cross-env": "^7.0.3", "eslint": "^9.33.0", "eslint-plugin-react-hooks": "^5.2.0", "eslint-plugin-react-refresh": "^0.4.20", + "ghostty-web": "0.3.0", "globals": "^16.3.0", + "next-themes": "^0.4.6", "nodemon": "^3.1.7", + "react": "^19.1.1", + "react-dom": "^19.1.1", + "react-markdown": "^10.1.0", + "react-syntax-highlighter": "^15.6.6", + "remark-gfm": "^4.0.1", + "sonner": "^2.0.7", + "strip-json-comments": "^5.0.3", + "tailwind-merge": "^3.3.1", "tailwindcss": "^4.0.0", "tsx": "^4.20.6", "tw-animate-css": "^1.3.8", @@ -309,7 +324,9 @@ "typescript-eslint": "^8.39.1", "vite": "^7.1.2", "vite-plugin-pwa": "^1.0.3", + "vitest": "^3.0.5", "workbox-window": "^7.4.0", + "zustand": "^5.0.8", }, }, }, @@ -318,6 +335,8 @@ "@codemirror/view": "6.39.13", }, "packages": { + "7zip-bin": ["7zip-bin@5.2.0", "", {}, "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A=="], + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@apideck/better-ajv-errors": ["@apideck/better-ajv-errors@0.3.6", "", { "dependencies": { "json-schema": "^0.4.0", "jsonpointer": "^5.0.0", "leven": "^3.1.0" }, "peerDependencies": { "ajv": ">=8" } }, "sha512-P+ZygBLZtkp0qqOAJJVX4oX/sFo5JR3eBWwwuqHHhK0GIgQOKWrAfiAaWX0aArHkRWHMuggFEgAZNxVPwPZYaA=="], @@ -602,6 +621,8 @@ "@date-fns/tz": ["@date-fns/tz@1.4.1", "", {}, "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA=="], + "@develar/schema-utils": ["@develar/schema-utils@2.6.5", "", { "dependencies": { "ajv": "^6.12.0", "ajv-keywords": "^3.4.1" } }, "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig=="], + "@dnd-kit/accessibility": ["@dnd-kit/accessibility@3.1.1", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-2P+YgaXF+gRsIihwwY1gCsQSYnu9Zyj2py8kY5fFvUM1qm2WA2u639R6YNVfU4GWr+ZM5mqEsfHZZLoRONbemw=="], "@dnd-kit/core": ["@dnd-kit/core@6.3.1", "", { "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0", "react-dom": ">=16.8.0" } }, "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ=="], @@ -610,6 +631,24 @@ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], + "@electron/asar": ["@electron/asar@3.4.1", "", { "dependencies": { "commander": "^5.0.0", "glob": "^7.1.6", "minimatch": "^3.0.4" }, "bin": { "asar": "bin/asar.js" } }, "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA=="], + + "@electron/fuses": ["@electron/fuses@1.8.0", "", { "dependencies": { "chalk": "^4.1.1", "fs-extra": "^9.0.1", "minimist": "^1.2.5" }, "bin": { "electron-fuses": "dist/bin.js" } }, "sha512-zx0EIq78WlY/lBb1uXlziZmDZI4ubcCXIMJ4uGjXzZW0nS19TjSPeXPAjzzTmKQlJUZm0SbmZhPKP7tuQ1SsEw=="], + + "@electron/get": ["@electron/get@2.0.3", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ=="], + + "@electron/node-gyp": ["@electron/node-gyp@github:electron/node-gyp#06b29aa", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "glob": "^8.1.0", "graceful-fs": "^4.2.6", "make-fetch-happen": "^10.2.1", "nopt": "^6.0.0", "proc-log": "^2.0.1", "semver": "^7.3.5", "tar": "^6.2.1", "which": "^2.0.2" }, "bin": "./bin/node-gyp.js" }, "electron-node-gyp-06b29aa"], + + "@electron/notarize": ["@electron/notarize@2.5.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.1", "promise-retry": "^2.0.1" } }, "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A=="], + + "@electron/osx-sign": ["@electron/osx-sign@1.3.3", "", { "dependencies": { "compare-version": "^0.1.2", "debug": "^4.3.4", "fs-extra": "^10.0.0", "isbinaryfile": "^4.0.8", "minimist": "^1.2.6", "plist": "^3.0.5" }, "bin": { "electron-osx-flat": "bin/electron-osx-flat.js", "electron-osx-sign": "bin/electron-osx-sign.js" } }, "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg=="], + + "@electron/rebuild": ["@electron/rebuild@3.7.2", "", { "dependencies": { "@electron/node-gyp": "git+https://github.com/electron/node-gyp.git#06b29aafb7708acef8b3669835c8a7857ebc92d2", "@malept/cross-spawn-promise": "^2.0.0", "chalk": "^4.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "fs-extra": "^10.0.0", "got": "^11.7.0", "node-abi": "^3.45.0", "node-api-version": "^0.2.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", "tar": "^6.0.5", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-19/KbIR/DAxbsCkiaGMXIdPnMCJLkcf8AvGnduJtWBs/CBwiAjY1apCqOLVxrXg+rtXFCngbXhBanWjxLUt1Mg=="], + + "@electron/universal": ["@electron/universal@2.0.3", "", { "dependencies": { "@electron/asar": "^3.3.1", "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.3.1", "dir-compare": "^4.2.0", "fs-extra": "^11.1.1", "minimatch": "^9.0.3", "plist": "^3.1.0" } }, "sha512-Wn9sPYIVFRFl5HmwMJkARCCf7rqK/EurkfQ/rJZ14mHP3iYTjZSIOSVonEAnhWeAXwtw7zOekGRlc6yTtZ0t+g=="], + + "@electron/windows-sign": ["@electron/windows-sign@1.2.2", "", { "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", "fs-extra": "^11.1.1", "minimist": "^1.2.8", "postject": "^1.0.0-alpha.6" }, "bin": { "electron-windows-sign": "bin/electron-windows-sign.js" } }, "sha512-dfZeox66AvdPtb2lD8OsIIQh12Tp0GNCRUDfBHIKGpbmopZto2/A8nSpYYLoedPIHpqkeblZ/k8OV0Gy7PYuyQ=="], + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.24.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-thpVCb/rhxE/BnMLQ7GReQLLN8q9qbHmI55F4489/ByVg2aQaQ6kbcLb6FHkocZzQhxc4gx0sCk0tJkKBFzDhA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.24.2", "", { "os": "android", "cpu": "arm" }, "sha512-tmwl4hJkCfNHwFB3nBa8z1Uy3ypZpxqxfTQOcHX+xRByyYgunVbZ9MzUUfb0RxaHIMnbHagwAxuTL+tnNM+1/Q=="], @@ -702,6 +741,8 @@ "@formatjs/intl-localematcher": ["@formatjs/intl-localematcher@0.6.2", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA=="], + "@gar/promisify": ["@gar/promisify@1.1.3", "", {}, "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw=="], + "@heroui/react-rsc-utils": ["@heroui/react-rsc-utils@2.1.9", "", { "peerDependencies": { "react": ">=18 || >=19.0.0-rc.0" } }, "sha512-e77OEjNCmQxE9/pnLDDb93qWkX58/CcgIqdNAczT/zUP+a48NxGq2A2WRimvc1uviwaNL2StriE2DmyZPyYW7Q=="], "@heroui/react-utils": ["@heroui/react-utils@2.1.14", "", { "dependencies": { "@heroui/react-rsc-utils": "2.1.9", "@heroui/shared-utils": "2.1.12" }, "peerDependencies": { "react": ">=18 || >=19.0.0-rc.0" } }, "sha512-hhKklYKy9sRH52C9A8P0jWQ79W4MkIvOnKBIuxEMHhigjfracy0o0lMnAUdEsJni4oZKVJYqNGdQl+UVgcmeDA=="], @@ -742,6 +783,8 @@ "@isaacs/cliui": ["@isaacs/cliui@9.0.0", "", {}, "sha512-AokJm4tuBHillT+FpMtxQ60n8ObyXBatq7jD2/JA9dxbDDokKQm8KMht5ibGzLVU9IJDIKK4TPKgMHEYMn3lMg=="], + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@jridgewell/remapping": ["@jridgewell/remapping@2.3.5", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ=="], @@ -794,6 +837,10 @@ "@lezer/yaml": ["@lezer/yaml@1.0.4", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.4.0" } }, "sha512-2lrrHqxalACEbxIbsjhqGpSW8kWpUKuY6RHgnSAFZa6qK62wvnPxA8hGOwOoDbwHcOFs5M4o27mjGu+P7TvBmw=="], + "@malept/cross-spawn-promise": ["@malept/cross-spawn-promise@2.0.0", "", { "dependencies": { "cross-spawn": "^7.0.1" } }, "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg=="], + + "@malept/flatpak-bundler": ["@malept/flatpak-bundler@0.4.0", "", { "dependencies": { "debug": "^4.1.1", "fs-extra": "^9.0.0", "lodash": "^4.17.15", "tmp-promise": "^3.0.2" } }, "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q=="], + "@marijn/find-cluster-break": ["@marijn/find-cluster-break@1.0.2", "", {}, "sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g=="], "@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="], @@ -802,6 +849,12 @@ "@nodelib/fs.walk": ["@nodelib/fs.walk@1.2.8", "", { "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg=="], + "@npmcli/agent": ["@npmcli/agent@3.0.0", "", { "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", "lru-cache": "^10.0.1", "socks-proxy-agent": "^8.0.3" } }, "sha512-S79NdEgDQd/NGCay6TCoVzXSj74skRZIKJcpJjC5lOq34SZzyI6MqtiiWoiVWoVrTcGjNeC4ipbh1VIHlpfF5Q=="], + + "@npmcli/fs": ["@npmcli/fs@2.1.2", "", { "dependencies": { "@gar/promisify": "^1.1.3", "semver": "^7.3.5" } }, "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ=="], + + "@npmcli/move-file": ["@npmcli/move-file@2.0.1", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ=="], + "@octokit/auth-token": ["@octokit/auth-token@6.0.0", "", {}, "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w=="], "@octokit/core": ["@octokit/core@7.0.6", "", { "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" } }, "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q=="], @@ -828,6 +881,8 @@ "@openchamber/desktop": ["@openchamber/desktop@workspace:packages/desktop"], + "@openchamber/electron": ["@openchamber/electron@workspace:packages/electron"], + "@openchamber/ui": ["@openchamber/ui@workspace:packages/ui"], "@openchamber/web": ["@openchamber/web@workspace:packages/web"], @@ -860,6 +915,8 @@ "@pierre/diffs": ["@pierre/diffs@1.1.0-beta.13", "", { "dependencies": { "@shikijs/transformers": "^3.0.0", "diff": "8.0.3", "hast-util-to-html": "9.0.5", "lru_map": "0.4.1", "shiki": "^3.0.0" }, "peerDependencies": { "react": "^18.3.1 || ^19.0.0", "react-dom": "^18.3.1 || ^19.0.0" } }, "sha512-D35rxDu5V7XHX5aVGU6PF12GhscL+I+9QYgxK/i3h0d2XSirAxDdVNm49aYwlOhgmdvL0NbS1IHxPswVB5yJvw=="], + "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], @@ -1066,12 +1123,16 @@ "@simplewebauthn/server": ["@simplewebauthn/server@13.3.0", "", { "dependencies": { "@hexagon/base64": "^1.1.27", "@levischuck/tiny-cbor": "^0.2.2", "@peculiar/asn1-android": "^2.6.0", "@peculiar/asn1-ecc": "^2.6.1", "@peculiar/asn1-rsa": "^2.6.1", "@peculiar/asn1-schema": "^2.6.0", "@peculiar/asn1-x509": "^2.6.1", "@peculiar/x509": "^1.14.3" } }, "sha512-MLHYFrYG8/wK2i+86XMhiecK72nMaHKKt4bo+7Q1TbuG9iGjlSdfkPWKO5ZFE/BX+ygCJ7pr8H/AJeyAj1EaTQ=="], + "@sindresorhus/is": ["@sindresorhus/is@4.6.0", "", {}, "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw=="], + "@sindresorhus/merge-streams": ["@sindresorhus/merge-streams@2.3.0", "", {}, "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg=="], "@surma/rollup-plugin-off-main-thread": ["@surma/rollup-plugin-off-main-thread@2.2.3", "", { "dependencies": { "ejs": "^3.1.6", "json5": "^2.2.0", "magic-string": "^0.25.0", "string.prototype.matchall": "^4.0.6" } }, "sha512-lR8q/9W7hZpMWweNiAKU7NQerBnzQQLvi8qnTDU/fxItPhtZVMbPV3lbCwjhIlNBe9Bbr5V+KHshvWmVSG9cxQ=="], "@swc/helpers": ["@swc/helpers@0.5.19", "", { "dependencies": { "tslib": "^2.8.0" } }, "sha512-QamiFeIK3txNjgUTNppE6MiG3p7TdninpZu0E0PbqVh1a9FNLT2FRhisaa4NcaX52XVhA5l7Pk58Ft7Sqi/2sA=="], + "@szmarczak/http-timer": ["@szmarczak/http-timer@4.0.6", "", { "dependencies": { "defer-to-connect": "^2.0.0" } }, "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w=="], + "@tailwindcss/node": ["@tailwindcss/node@4.2.1", "", { "dependencies": { "@jridgewell/remapping": "^2.3.5", "enhanced-resolve": "^5.19.0", "jiti": "^2.6.1", "lightningcss": "1.31.1", "magic-string": "^0.30.21", "source-map-js": "^1.2.1", "tailwindcss": "4.2.1" } }, "sha512-jlx6sLk4EOwO6hHe1oCGm1Q4AN/s0rSrTTPBGPM0/RQ6Uylwq17FuU8IeJJKEjtc6K6O07zsvP+gDO6MMWo7pg=="], "@tailwindcss/oxide": ["@tailwindcss/oxide@4.2.1", "", { "optionalDependencies": { "@tailwindcss/oxide-android-arm64": "4.2.1", "@tailwindcss/oxide-darwin-arm64": "4.2.1", "@tailwindcss/oxide-darwin-x64": "4.2.1", "@tailwindcss/oxide-freebsd-x64": "4.2.1", "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.1", "@tailwindcss/oxide-linux-arm64-gnu": "4.2.1", "@tailwindcss/oxide-linux-arm64-musl": "4.2.1", "@tailwindcss/oxide-linux-x64-gnu": "4.2.1", "@tailwindcss/oxide-linux-x64-musl": "4.2.1", "@tailwindcss/oxide-wasm32-wasi": "4.2.1", "@tailwindcss/oxide-win32-arm64-msvc": "4.2.1", "@tailwindcss/oxide-win32-x64-msvc": "4.2.1" } }, "sha512-yv9jeEFWnjKCI6/T3Oq50yQEOqmpmpfzG1hcZsAOaXFQPfzWprWrlHSdGPEF3WQTi8zu8ohC9Mh9J470nT5pUw=="], @@ -1142,6 +1203,8 @@ "@textlint/types": ["@textlint/types@15.5.2", "", { "dependencies": { "@textlint/ast-node-types": "15.5.2" } }, "sha512-sJOrlVLLXp4/EZtiWKWq9y2fWyZlI8GP+24rnU5avtPWBIMm/1w97yzKrAqYF8czx2MqR391z5akhnfhj2f/AQ=="], + "@tootallnate/once": ["@tootallnate/once@2.0.0", "", {}, "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A=="], + "@types/adm-zip": ["@types/adm-zip@0.5.7", "", { "dependencies": { "@types/node": "*" } }, "sha512-DNEs/QvmyRLurdQPChqq0Md4zGvPwHerAJYWk9l2jCbD1VPpnzRJorOdiq4zsw09NFbYnhfsoEhWtxIzXpn2yw=="], "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], @@ -1152,22 +1215,34 @@ "@types/babel__traverse": ["@types/babel__traverse@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.2" } }, "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q=="], + "@types/cacheable-request": ["@types/cacheable-request@6.0.3", "", { "dependencies": { "@types/http-cache-semantics": "*", "@types/keyv": "^3.1.4", "@types/node": "*", "@types/responselike": "^1.0.0" } }, "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw=="], + + "@types/chai": ["@types/chai@5.2.3", "", { "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA=="], + "@types/debug": ["@types/debug@4.1.12", "", { "dependencies": { "@types/ms": "*" } }, "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ=="], + "@types/deep-eql": ["@types/deep-eql@4.0.2", "", {}, "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw=="], + "@types/dom-speech-recognition": ["@types/dom-speech-recognition@0.0.7", "", {}, "sha512-NjiUoJbBlKhyufNsMZLSp+pbPNtPAFnR738RCJvtZy/HVQ2TZjmqpMyaeOSMXgxdfZM60nt8QGbtfmQrJAH2sw=="], "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], + "@types/fs-extra": ["@types/fs-extra@9.0.13", "", { "dependencies": { "@types/node": "*" } }, "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA=="], + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + "@types/http-cache-semantics": ["@types/http-cache-semantics@4.2.0", "", {}, "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q=="], + "@types/http-proxy": ["@types/http-proxy@1.17.17", "", { "dependencies": { "@types/node": "*" } }, "sha512-ED6LB+Z1AVylNTu7hdzuBqOgMnvG/ld6wGCG8wFnAzKX5uyW2K3WD52v0gnLCTK/VLpXtKckgWuyScYK6cSPaw=="], "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], "@types/katex": ["@types/katex@0.16.8", "", {}, "sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg=="], + "@types/keyv": ["@types/keyv@3.1.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg=="], + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="], @@ -1178,6 +1253,8 @@ "@types/normalize-package-data": ["@types/normalize-package-data@2.4.4", "", {}, "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA=="], + "@types/plist": ["@types/plist@3.0.5", "", { "dependencies": { "@types/node": "*", "xmlbuilder": ">=11.0.1" } }, "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA=="], + "@types/prismjs": ["@types/prismjs@1.26.6", "", {}, "sha512-vqlvI7qlMvcCBbVe0AKAb4f97//Hy0EBTaiW8AalRnG/xAN5zOiWWyrNqNXeq8+KAuvRewjCVY1+IPxk4RdNYw=="], "@types/qrcode": ["@types/qrcode@1.5.6", "", { "dependencies": { "@types/node": "*" } }, "sha512-te7NQcV2BOvdj2b1hCAHzAoMNuj65kNBMz0KBaxM6c3VGBOhU0dURQKOtH8CFNI/dsKkwlv32p26qYQTWoB5bw=="], @@ -1190,14 +1267,20 @@ "@types/resolve": ["@types/resolve@1.20.2", "", {}, "sha512-60BCwRFOZCQhDncwQdxxeOEEkbc5dIMccYLwbxsS4TUNeVECQ/pBJ0j09mrHOl/JJvpRPGwO9SvE4nR2Nb/a4Q=="], + "@types/responselike": ["@types/responselike@1.0.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw=="], + "@types/sarif": ["@types/sarif@2.1.7", "", {}, "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ=="], "@types/trusted-types": ["@types/trusted-types@2.0.7", "", {}, "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="], "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "@types/verror": ["@types/verror@1.10.11", "", {}, "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg=="], + "@types/vscode": ["@types/vscode@1.109.0", "", {}, "sha512-0Pf95rnwEIwDbmXGC08r0B4TQhAbsHQ5UyTIgVgoieDe4cOnf92usuR5dEczb6bTKEp7ziZH4TV1TRGPPCExtw=="], + "@types/yauzl": ["@types/yauzl@2.10.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q=="], + "@typescript-eslint/eslint-plugin": ["@typescript-eslint/eslint-plugin@8.56.1", "", { "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/type-utils": "8.56.1", "@typescript-eslint/utils": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "peerDependencies": { "@typescript-eslint/parser": "^8.56.1", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-Jz9ZztpB37dNC+HU2HI28Bs9QXpzCz+y/twHOwhyrIRdbuVDxSytJNDl6z/aAKlaRIwC7y8wJdkBv7FxYGgi0A=="], "@typescript-eslint/parser": ["@typescript-eslint/parser@8.56.1", "", { "dependencies": { "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "sha512-klQbnPAAiGYFyI02+znpBRLyjL4/BrBd0nyWkdC0s/6xFLkXYQ8OoRrSkqacS1ddVxf/LDyODIKbQ5TgKAf/Fg=="], @@ -1224,6 +1307,20 @@ "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.4", "", { "dependencies": { "@babel/core": "^7.29.0", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-rc.3", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA=="], + "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], + + "@vitest/mocker": ["@vitest/mocker@3.2.4", "", { "dependencies": { "@vitest/spy": "3.2.4", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "optionalPeers": ["msw", "vite"] }, "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ=="], + + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], + + "@vitest/runner": ["@vitest/runner@3.2.4", "", { "dependencies": { "@vitest/utils": "3.2.4", "pathe": "^2.0.3", "strip-literal": "^3.0.0" } }, "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ=="], + + "@vitest/snapshot": ["@vitest/snapshot@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "magic-string": "^0.30.17", "pathe": "^2.0.3" } }, "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ=="], + + "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], + + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], + "@vscode/vsce": ["@vscode/vsce@3.7.1", "", { "dependencies": { "@azure/identity": "^4.1.0", "@secretlint/node": "^10.1.2", "@secretlint/secretlint-formatter-sarif": "^10.1.2", "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", "chalk": "^4.1.2", "cheerio": "^1.0.0-rc.9", "cockatiel": "^3.1.2", "commander": "^12.1.0", "form-data": "^4.0.0", "glob": "^11.0.0", "hosted-git-info": "^4.0.2", "jsonc-parser": "^3.2.0", "leven": "^3.1.0", "markdown-it": "^14.1.0", "mime": "^1.3.4", "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", "secretlint": "^10.1.2", "semver": "^7.5.2", "tmp": "^0.2.3", "typed-rest-client": "^1.8.4", "url-join": "^4.0.1", "xml2js": "^0.5.0", "yauzl": "^2.3.1", "yazl": "^2.2.2" }, "optionalDependencies": { "keytar": "^7.7.0" }, "bin": { "vsce": "vsce" } }, "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g=="], "@vscode/vsce-sign": ["@vscode/vsce-sign@2.0.9", "", { "optionalDependencies": { "@vscode/vsce-sign-alpine-arm64": "2.0.6", "@vscode/vsce-sign-alpine-x64": "2.0.6", "@vscode/vsce-sign-darwin-arm64": "2.0.6", "@vscode/vsce-sign-darwin-x64": "2.0.6", "@vscode/vsce-sign-linux-arm": "2.0.6", "@vscode/vsce-sign-linux-arm64": "2.0.6", "@vscode/vsce-sign-linux-x64": "2.0.6", "@vscode/vsce-sign-win32-arm64": "2.0.6", "@vscode/vsce-sign-win32-x64": "2.0.6" } }, "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g=="], @@ -1246,8 +1343,12 @@ "@vscode/vsce-sign-win32-x64": ["@vscode/vsce-sign-win32-x64@2.0.6", "", { "os": "win32", "cpu": "x64" }, "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ=="], + "@xmldom/xmldom": ["@xmldom/xmldom@0.8.13", "", {}, "sha512-KRYzxepc14G/CEpEGc3Yn+JKaAeT63smlDr+vjB8jRfgTBBI9wRj/nkQEO+ucV8p8I9bfKLWp37uHgFrbntPvw=="], + "@yarnpkg/lockfile": ["@yarnpkg/lockfile@1.1.0", "", {}, "sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ=="], + "abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="], + "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], @@ -1262,8 +1363,12 @@ "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], + "aggregate-error": ["aggregate-error@3.1.0", "", { "dependencies": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" } }, "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA=="], + "ajv": ["ajv@6.14.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw=="], + "ajv-keywords": ["ajv-keywords@3.5.2", "", { "peerDependencies": { "ajv": "^6.9.1" } }, "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ=="], + "ansi-escapes": ["ansi-escapes@7.3.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -1272,6 +1377,10 @@ "anymatch": ["anymatch@3.1.3", "", { "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" } }, "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw=="], + "app-builder-bin": ["app-builder-bin@5.0.0-alpha.12", "", {}, "sha512-j87o0j6LqPL3QRr8yid6c+Tt5gC7xNfYo6uQIQkorAC6MpeayVMZrEDzKmJJ/Hlv7EnOQpaRm53k6ktDYZyB6w=="], + + "app-builder-lib": ["app-builder-lib@26.8.1", "", { "dependencies": { "@develar/schema-utils": "~2.6.5", "@electron/asar": "3.4.1", "@electron/fuses": "^1.8.0", "@electron/get": "^3.0.0", "@electron/notarize": "2.5.0", "@electron/osx-sign": "1.3.3", "@electron/rebuild": "^4.0.3", "@electron/universal": "2.0.3", "@malept/flatpak-bundler": "^0.4.0", "@types/fs-extra": "9.0.13", "async-exit-hook": "^2.0.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chromium-pickle-js": "^0.2.0", "ci-info": "4.3.1", "debug": "^4.3.4", "dotenv": "^16.4.5", "dotenv-expand": "^11.0.6", "ejs": "^3.1.8", "electron-publish": "26.8.1", "fs-extra": "^10.1.0", "hosted-git-info": "^4.1.0", "isbinaryfile": "^5.0.0", "jiti": "^2.4.2", "js-yaml": "^4.1.0", "json5": "^2.2.3", "lazy-val": "^1.0.5", "minimatch": "^10.0.3", "plist": "3.1.0", "proper-lockfile": "^4.1.2", "resedit": "^1.7.0", "semver": "~7.7.3", "tar": "^7.5.7", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0", "which": "^5.0.0" }, "peerDependencies": { "dmg-builder": "26.8.1", "electron-builder-squirrel-windows": "26.8.1" } }, "sha512-p0Im/Dx5C4tmz8QEE1Yn4MkuPC8PrnlRneMhWJj7BBXQfNTJUshM/bp3lusdEsDbvvfJZpXWnYesgSLvwtM2Zw=="], + "argparse": ["argparse@2.0.1", "", {}, "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q=="], "aria-hidden": ["aria-hidden@1.2.6", "", { "dependencies": { "tslib": "^2.0.0" } }, "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA=="], @@ -1284,10 +1393,16 @@ "asn1js": ["asn1js@3.0.7", "", { "dependencies": { "pvtsutils": "^1.3.6", "pvutils": "^1.1.3", "tslib": "^2.8.1" } }, "sha512-uLvq6KJu04qoQM6gvBfKFjlh6Gl0vOKQuR5cJMDHQkmwfMOQeN3F3SHCv9SNYSL+CRoHvOGFfllDlVz03GQjvQ=="], + "assert-plus": ["assert-plus@1.0.0", "", {}, "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw=="], + + "assertion-error": ["assertion-error@2.0.1", "", {}, "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA=="], + "astral-regex": ["astral-regex@2.0.0", "", {}, "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ=="], "async": ["async@3.2.6", "", {}, "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA=="], + "async-exit-hook": ["async-exit-hook@2.0.1", "", {}, "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw=="], + "async-function": ["async-function@1.0.0", "", {}, "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA=="], "asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="], @@ -1320,10 +1435,14 @@ "before-after-hook": ["before-after-hook@4.0.0", "", {}, "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ=="], + "better-sqlite3": ["better-sqlite3@11.10.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-EwhOpyXiOEL/lKzHz9AW1msWFNzGc/z+LzeB3/jnFJpxu+th2yqvzsSWas1v9jgs9+xiXJcD5A8CJxAG2TaghQ=="], + "binary-extensions": ["binary-extensions@2.3.0", "", {}, "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw=="], "binaryextensions": ["binaryextensions@6.11.0", "", { "dependencies": { "editions": "^6.21.0" } }, "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw=="], + "bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="], + "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], "bn.js": ["bn.js@4.12.3", "", {}, "sha512-fGTi3gxV/23FTYdAoUtLYp6qySe2KE3teyZitipKNRuVYcBkoP/bB3guXN/XVKUe9mxCHXnc9C4ocyz8OmgN0g=="], @@ -1332,6 +1451,8 @@ "boolbase": ["boolbase@1.0.0", "", {}, "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww=="], + "boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="], + "boundary": ["boundary@2.0.0", "", {}, "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA=="], "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], @@ -1348,12 +1469,24 @@ "buffer-from": ["buffer-from@1.1.2", "", {}, "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="], + "builder-util": ["builder-util@26.8.1", "", { "dependencies": { "7zip-bin": "~5.2.0", "@types/debug": "^4.1.6", "app-builder-bin": "5.0.0-alpha.12", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "cross-spawn": "^7.0.6", "debug": "^4.3.4", "fs-extra": "^10.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "js-yaml": "^4.1.0", "sanitize-filename": "^1.6.3", "source-map-support": "^0.5.19", "stat-mode": "^1.0.0", "temp-file": "^3.4.0", "tiny-async-pool": "1.3.0" } }, "sha512-pm1lTYbGyc90DHgCDO7eo8Rl4EqKLciayNbZqGziqnH9jrlKe8ZANGdityLZU+pJh16dfzjAx2xQq9McuIPEtw=="], + + "builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="], + "bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="], "bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="], "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "cacache": ["cacache@16.1.3", "", { "dependencies": { "@npmcli/fs": "^2.1.0", "@npmcli/move-file": "^2.0.0", "chownr": "^2.0.0", "fs-minipass": "^2.1.0", "glob": "^8.0.1", "infer-owner": "^1.0.4", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "mkdirp": "^1.0.4", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^9.0.0", "tar": "^6.1.11", "unique-filename": "^2.0.0" } }, "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ=="], + + "cacheable-lookup": ["cacheable-lookup@5.0.4", "", {}, "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA=="], + + "cacheable-request": ["cacheable-request@7.0.4", "", { "dependencies": { "clone-response": "^1.0.2", "get-stream": "^5.1.0", "http-cache-semantics": "^4.0.0", "keyv": "^4.0.0", "lowercase-keys": "^2.0.0", "normalize-url": "^6.0.1", "responselike": "^2.0.0" } }, "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg=="], + "call-bind": ["call-bind@1.0.8", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.0", "es-define-property": "^1.0.0", "get-intrinsic": "^1.2.4", "set-function-length": "^1.2.2" } }, "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], @@ -1368,6 +1501,8 @@ "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + "chai": ["chai@5.3.3", "", { "dependencies": { "assertion-error": "^2.0.1", "check-error": "^2.1.1", "deep-eql": "^5.0.1", "loupe": "^3.1.0", "pathval": "^2.0.0" } }, "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw=="], + "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], "character-entities": ["character-entities@1.2.4", "", {}, "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw=="], @@ -1378,20 +1513,36 @@ "character-reference-invalid": ["character-reference-invalid@1.1.4", "", {}, "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg=="], + "check-error": ["check-error@2.1.3", "", {}, "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA=="], + "cheerio": ["cheerio@1.2.0", "", { "dependencies": { "cheerio-select": "^2.1.0", "dom-serializer": "^2.0.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "encoding-sniffer": "^0.2.1", "htmlparser2": "^10.1.0", "parse5": "^7.3.0", "parse5-htmlparser2-tree-adapter": "^7.1.0", "parse5-parser-stream": "^7.1.2", "undici": "^7.19.0", "whatwg-mimetype": "^4.0.0" } }, "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg=="], "cheerio-select": ["cheerio-select@2.1.0", "", { "dependencies": { "boolbase": "^1.0.0", "css-select": "^5.1.0", "css-what": "^6.1.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1" } }, "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g=="], "chokidar": ["chokidar@3.6.0", "", { "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" }, "optionalDependencies": { "fsevents": "~2.3.2" } }, "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw=="], - "chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="], + + "chromium-pickle-js": ["chromium-pickle-js@0.2.0", "", {}, "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw=="], "ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="], "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "clean-stack": ["clean-stack@2.2.0", "", {}, "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A=="], + + "cli-cursor": ["cli-cursor@3.1.0", "", { "dependencies": { "restore-cursor": "^3.1.0" } }, "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw=="], + + "cli-spinners": ["cli-spinners@2.9.2", "", {}, "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg=="], + + "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], + "cliui": ["cliui@8.0.1", "", { "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ=="], + "clone": ["clone@1.0.4", "", {}, "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg=="], + + "clone-response": ["clone-response@1.0.3", "", { "dependencies": { "mimic-response": "^1.0.0" } }, "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA=="], + "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], @@ -1418,6 +1569,8 @@ "common-tags": ["common-tags@1.8.2", "", {}, "sha512-gk/Z852D2Wtb//0I+kRFNKKE9dIIVirjoqPoA1wJU+XePVXZfGeBpk45+A1rKO4Q43prqWBNY/MiIeRLbPWUaA=="], + "compare-version": ["compare-version@0.1.2", "", {}, "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A=="], + "compressible": ["compressible@2.0.18", "", { "dependencies": { "mime-db": ">= 1.43.0 < 2" } }, "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg=="], "compression": ["compression@1.8.1", "", { "dependencies": { "bytes": "3.1.2", "compressible": "~2.0.18", "debug": "2.6.9", "negotiator": "~0.6.4", "on-headers": "~1.1.0", "safe-buffer": "5.2.1", "vary": "~1.1.2" } }, "sha512-9mAqGPHLakhCLeNyxPkK4xVo746zQ/czLH1Ky+vkitMnWfWZps8r0qXuwhwizagCRttsL4lfG4pIOvaWLpAP0w=="], @@ -1438,12 +1591,18 @@ "core-js-compat": ["core-js-compat@3.48.0", "", { "dependencies": { "browserslist": "^4.28.1" } }, "sha512-OM4cAF3D6VtH/WkLtWvyNC56EZVXsZdU3iqaMG2B4WvYrlqU831pc4UtG5yp0sE9z8Y02wVN7PjW5Zf9Gt0f1Q=="], + "core-util-is": ["core-util-is@1.0.2", "", {}, "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ=="], + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "crc": ["crc@3.8.0", "", { "dependencies": { "buffer": "^5.1.0" } }, "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ=="], + "crelt": ["crelt@1.0.6", "", {}, "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g=="], "cron-parser": ["cron-parser@4.9.0", "", { "dependencies": { "luxon": "^3.2.1" } }, "sha512-p0SaNjrHOnQeR8/VnfGbmg9te2kfyYSQ7Sc/j/6DtPL3JQvKxmjO9TSjNFpujqV3vEYYBvNNvXSxzyksBWAx1Q=="], + "cross-dirname": ["cross-dirname@0.1.0", "", {}, "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q=="], + "cross-env": ["cross-env@7.0.3", "", { "dependencies": { "cross-spawn": "^7.0.1" }, "bin": { "cross-env": "src/bin/cross-env.js", "cross-env-shell": "src/bin/cross-env-shell.js" } }, "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -1474,6 +1633,8 @@ "decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="], + "deep-eql": ["deep-eql@5.0.2", "", {}, "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q=="], + "deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="], "deep-is": ["deep-is@0.1.4", "", {}, "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ=="], @@ -1484,6 +1645,10 @@ "default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="], + "defaults": ["defaults@1.0.4", "", { "dependencies": { "clone": "^1.0.2" } }, "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A=="], + + "defer-to-connect": ["defer-to-connect@2.0.1", "", {}, "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg=="], + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], "define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="], @@ -1498,6 +1663,8 @@ "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="], + "detect-node-es": ["detect-node-es@1.1.0", "", {}, "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ=="], "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], @@ -1506,6 +1673,12 @@ "dijkstrajs": ["dijkstrajs@1.0.3", "", {}, "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA=="], + "dir-compare": ["dir-compare@4.2.0", "", { "dependencies": { "minimatch": "^3.0.5", "p-limit": "^3.1.0 " } }, "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ=="], + + "dmg-builder": ["dmg-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "fs-extra": "^10.1.0", "iconv-lite": "^0.6.2", "js-yaml": "^4.1.0" }, "optionalDependencies": { "dmg-license": "^1.0.11" } }, "sha512-glMJgnTreo8CFINujtAhCgN96QAqApDMZ8Vl1r8f0QT8QprvC1UCltV4CcWj20YoIyLZx6IUskaJZ0NV8fokcg=="], + + "dmg-license": ["dmg-license@1.0.11", "", { "dependencies": { "@types/plist": "^3.0.1", "@types/verror": "^1.10.3", "ajv": "^6.10.0", "crc": "^3.8.0", "iconv-corefoundation": "^1.1.7", "plist": "^3.0.4", "smart-buffer": "^4.0.2", "verror": "^1.10.0" }, "os": "darwin", "bin": { "dmg-license": "bin/dmg-license.js" } }, "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q=="], + "dom-serializer": ["dom-serializer@2.0.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" } }, "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg=="], "domelementtype": ["domelementtype@2.3.0", "", {}, "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw=="], @@ -1516,8 +1689,14 @@ "domutils": ["domutils@3.2.2", "", { "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" } }, "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw=="], + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], + + "dotenv-expand": ["dotenv-expand@11.0.7", "", { "dependencies": { "dotenv": "^16.4.5" } }, "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA=="], + "dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="], + "eastasianwidth": ["eastasianwidth@0.2.0", "", {}, "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA=="], + "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], "editions": ["editions@6.22.0", "", { "dependencies": { "version-range": "^4.15.0" } }, "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ=="], @@ -1526,14 +1705,36 @@ "ejs": ["ejs@3.1.10", "", { "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" } }, "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA=="], + "electron": ["electron@41.2.1", "", { "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { "electron": "cli.js" } }, "sha512-teeRThiYGTPKf/2yOW7zZA1bhb91KEQ4yLBPOg7GxpmnkLFLugKgQaAKOrCgdzwsXh/5mFIfmkm+4+wACJKwaA=="], + + "electron-builder": ["electron-builder@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "ci-info": "^4.2.0", "dmg-builder": "26.8.1", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "simple-update-notifier": "2.0.0", "yargs": "^17.6.2" }, "bin": { "electron-builder": "cli.js", "install-app-deps": "install-app-deps.js" } }, "sha512-uWhx1r74NGpCagG0ULs/P9Nqv2nsoo+7eo4fLUOB8L8MdWltq9odW/uuLXMFCDGnPafknYLZgjNX0ZIFRzOQAw=="], + + "electron-builder-squirrel-windows": ["electron-builder-squirrel-windows@26.8.1", "", { "dependencies": { "app-builder-lib": "26.8.1", "builder-util": "26.8.1", "electron-winstaller": "5.4.0" } }, "sha512-o288fIdgPLHA76eDrFADHPoo7VyGkDCYbLV1GzndaMSAVBoZrGvM9m2IehdcVMzdAZJ2eV9bgyissQXHv5tGzA=="], + + "electron-context-menu": ["electron-context-menu@4.1.2", "", { "dependencies": { "cli-truncate": "^4.0.0", "electron-dl": "^4.0.0", "electron-is-dev": "^3.0.1" } }, "sha512-9xYTUV0oRqKL50N9W71IrXNdVRB0LuBp3R1zkUdUc2wfIa2/QZwYYj5RLuO7Tn7ZSLVIaO3X6u+EIBK+cBvzrQ=="], + + "electron-dl": ["electron-dl@4.0.0", "", { "dependencies": { "ext-name": "^5.0.0", "pupa": "^3.1.0", "unused-filename": "^4.0.1" } }, "sha512-USiB9816d2JzKv0LiSbreRfTg5lDk3lWh0vlx/gugCO92ZIJkHVH0UM18EHvKeadErP6Xn4yiTphWzYfbA2Ong=="], + + "electron-is-dev": ["electron-is-dev@3.0.1", "", {}, "sha512-8TjjAh8Ec51hUi3o4TaU0mD3GMTOESi866oRNavj9A3IQJ7pmv+MJVmdZBFGw4GFT36X7bkqnuDNYvkQgvyI8Q=="], + + "electron-log": ["electron-log@5.4.3", "", {}, "sha512-sOUsM3LjZdugatazSQ/XTyNcw8dfvH1SYhXWiJyfYodAAKOZdHs0txPiLDXFzOZbhXgAgshQkshH2ccq0feyLQ=="], + + "electron-publish": ["electron-publish@26.8.1", "", { "dependencies": { "@types/fs-extra": "^9.0.11", "builder-util": "26.8.1", "builder-util-runtime": "9.5.1", "chalk": "^4.1.2", "form-data": "^4.0.5", "fs-extra": "^10.1.0", "lazy-val": "^1.0.5", "mime": "^2.5.2" } }, "sha512-q+jrSTIh/Cv4eGZa7oVR+grEJo/FoLMYBAnSL5GCtqwUpr1T+VgKB/dn1pnzxIxqD8S/jP1yilT9VrwCqINR4w=="], + "electron-to-chromium": ["electron-to-chromium@1.5.302", "", {}, "sha512-sM6HAN2LyK82IyPBpznDRqlTQAtuSaO+ShzFiWTvoMJLHyZ+Y39r8VMfHzwbU8MVBzQ4Wdn85+wlZl2TLGIlwg=="], + "electron-updater": ["electron-updater@6.8.3", "", { "dependencies": { "builder-util-runtime": "9.5.1", "fs-extra": "^10.1.0", "js-yaml": "^4.1.0", "lazy-val": "^1.0.5", "lodash.escaperegexp": "^4.1.2", "lodash.isequal": "^4.5.0", "semver": "~7.7.3", "tiny-typed-emitter": "^2.1.0" } }, "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ=="], + + "electron-winstaller": ["electron-winstaller@5.4.0", "", { "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", "fs-extra": "^7.0.1", "lodash": "^4.17.21", "temp": "^0.9.0" }, "optionalDependencies": { "@electron/windows-sign": "^1.1.2" } }, "sha512-bO3y10YikuUwUuDUQRM4KfwNkKhnpVO7IPdbsrejwN9/AABJzzTQ4GeHwyzNSrVO+tEH3/Np255a3sVZpZDjvg=="], + "elkjs": ["elkjs@0.11.0", "", {}, "sha512-u4J8h9mwEDaYMqo0RYJpqNMFDoMK7f+pu4GjcV+N8jIC7TRdORgzkfSjTJemhqONFfH6fBI3wpysgWbhgVWIXw=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], + "encoding-sniffer": ["encoding-sniffer@0.2.1", "", { "dependencies": { "iconv-lite": "^0.6.3", "whatwg-encoding": "^3.1.1" } }, "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw=="], "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], @@ -1542,24 +1743,34 @@ "entities": ["entities@7.0.1", "", {}, "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA=="], + "env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], + "err-code": ["err-code@2.0.3", "", {}, "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA=="], + "es-abstract": ["es-abstract@1.24.1", "", { "dependencies": { "array-buffer-byte-length": "^1.0.2", "arraybuffer.prototype.slice": "^1.0.4", "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "data-view-buffer": "^1.0.2", "data-view-byte-length": "^1.0.2", "data-view-byte-offset": "^1.0.1", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "es-set-tostringtag": "^2.1.0", "es-to-primitive": "^1.3.0", "function.prototype.name": "^1.1.8", "get-intrinsic": "^1.3.0", "get-proto": "^1.0.1", "get-symbol-description": "^1.1.0", "globalthis": "^1.0.4", "gopd": "^1.2.0", "has-property-descriptors": "^1.0.2", "has-proto": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "internal-slot": "^1.1.0", "is-array-buffer": "^3.0.5", "is-callable": "^1.2.7", "is-data-view": "^1.0.2", "is-negative-zero": "^2.0.3", "is-regex": "^1.2.1", "is-set": "^2.0.3", "is-shared-array-buffer": "^1.0.4", "is-string": "^1.1.1", "is-typed-array": "^1.1.15", "is-weakref": "^1.1.1", "math-intrinsics": "^1.1.0", "object-inspect": "^1.13.4", "object-keys": "^1.1.1", "object.assign": "^4.1.7", "own-keys": "^1.0.1", "regexp.prototype.flags": "^1.5.4", "safe-array-concat": "^1.1.3", "safe-push-apply": "^1.0.0", "safe-regex-test": "^1.1.0", "set-proto": "^1.0.0", "stop-iteration-iterator": "^1.1.0", "string.prototype.trim": "^1.2.10", "string.prototype.trimend": "^1.0.9", "string.prototype.trimstart": "^1.0.8", "typed-array-buffer": "^1.0.3", "typed-array-byte-length": "^1.0.3", "typed-array-byte-offset": "^1.0.4", "typed-array-length": "^1.0.7", "unbox-primitive": "^1.1.0", "which-typed-array": "^1.1.19" } }, "sha512-zHXBLhP+QehSSbsS9Pt23Gg964240DPd6QCf8WpkqEXxQ7fhdZzYsocOr5u7apWonsS5EjZDmTF+/slGMyasvw=="], "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + "es-module-lexer": ["es-module-lexer@1.7.0", "", {}, "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA=="], + "es-object-atoms": ["es-object-atoms@1.1.1", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA=="], "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], "es-to-primitive": ["es-to-primitive@1.3.0", "", { "dependencies": { "is-callable": "^1.2.7", "is-date-object": "^1.0.5", "is-symbol": "^1.0.4" } }, "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g=="], + "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], + "esbuild": ["esbuild@0.24.2", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.24.2", "@esbuild/android-arm": "0.24.2", "@esbuild/android-arm64": "0.24.2", "@esbuild/android-x64": "0.24.2", "@esbuild/darwin-arm64": "0.24.2", "@esbuild/darwin-x64": "0.24.2", "@esbuild/freebsd-arm64": "0.24.2", "@esbuild/freebsd-x64": "0.24.2", "@esbuild/linux-arm": "0.24.2", "@esbuild/linux-arm64": "0.24.2", "@esbuild/linux-ia32": "0.24.2", "@esbuild/linux-loong64": "0.24.2", "@esbuild/linux-mips64el": "0.24.2", "@esbuild/linux-ppc64": "0.24.2", "@esbuild/linux-riscv64": "0.24.2", "@esbuild/linux-s390x": "0.24.2", "@esbuild/linux-x64": "0.24.2", "@esbuild/netbsd-arm64": "0.24.2", "@esbuild/netbsd-x64": "0.24.2", "@esbuild/openbsd-arm64": "0.24.2", "@esbuild/openbsd-x64": "0.24.2", "@esbuild/sunos-x64": "0.24.2", "@esbuild/win32-arm64": "0.24.2", "@esbuild/win32-ia32": "0.24.2", "@esbuild/win32-x64": "0.24.2" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-+9egpBW8I3CD5XPe0n6BfT5fxLzxrlDzqydF3aviG+9ni1lDC/OvMHcxqEFV0+LANZG5R1bFMWfUrjVsdwxJvA=="], "escalade": ["escalade@3.2.0", "", {}, "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA=="], + "escape-goat": ["escape-goat@4.0.0", "", {}, "sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], @@ -1584,7 +1795,7 @@ "estree-util-is-identifier-name": ["estree-util-is-identifier-name@3.0.0", "", {}, "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg=="], - "estree-walker": ["estree-walker@1.0.1", "", {}, "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="], + "estree-walker": ["estree-walker@3.0.3", "", { "dependencies": { "@types/estree": "^1.0.0" } }, "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g=="], "esutils": ["esutils@2.0.3", "", {}, "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g=="], @@ -1596,10 +1807,22 @@ "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], + "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], + + "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + "ext-list": ["ext-list@2.2.2", "", { "dependencies": { "mime-db": "^1.28.0" } }, "sha512-u+SQgsubraE6zItfVA0tBuCBhfU9ogSRnsvygI7wht9TS510oLkBRXBsqopeUG/GBOIQyKZO9wjTqIu/sf5zFA=="], + + "ext-name": ["ext-name@5.0.0", "", { "dependencies": { "ext-list": "^2.0.0", "sort-keys-length": "^1.0.0" } }, "sha512-yblEwXAbGv1VQDmow7s38W77hzAgJAO50ztBLMcUyUBfxv1HC+LGwtiEN+Co6LtlqT/5uwVOxsD4TNIilWhwdQ=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "extract-zip": ["extract-zip@2.0.1", "", { "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" }, "bin": { "extract-zip": "cli.js" } }, "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg=="], + + "extsprintf": ["extsprintf@1.4.1", "", {}, "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA=="], + "fast-content-type-parse": ["fast-content-type-parse@3.0.0", "", {}, "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg=="], "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], @@ -1622,6 +1845,8 @@ "file-entry-cache": ["file-entry-cache@8.0.0", "", { "dependencies": { "flat-cache": "^4.0.0" } }, "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ=="], + "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], + "filelist": ["filelist@1.0.6", "", { "dependencies": { "minimatch": "^5.0.1" } }, "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], @@ -1662,6 +1887,10 @@ "fs-extra": ["fs-extra@10.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ=="], + "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], + + "fs.realpath": ["fs.realpath@1.0.0", "", {}, "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -1678,6 +1907,8 @@ "get-caller-file": ["get-caller-file@2.0.5", "", {}, "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg=="], + "get-east-asian-width": ["get-east-asian-width@1.5.0", "", {}, "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA=="], + "get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="], "get-nonce": ["get-nonce@1.0.1", "", {}, "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q=="], @@ -1686,6 +1917,8 @@ "get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="], + "get-stream": ["get-stream@5.2.0", "", { "dependencies": { "pump": "^3.0.0" } }, "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="], + "get-symbol-description": ["get-symbol-description@1.1.0", "", { "dependencies": { "call-bound": "^1.0.3", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6" } }, "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg=="], "get-tsconfig": ["get-tsconfig@4.13.6", "", { "dependencies": { "resolve-pkg-maps": "^1.0.0" } }, "sha512-shZT/QMiSHc/YBLxxOkMtgSid5HFoauqCE3/exfsEcwg1WkeqjG+V40yBbBrsD+jW2HDXcs28xOfcbm2jI8Ddw=="], @@ -1698,6 +1931,8 @@ "glob-parent": ["glob-parent@6.0.2", "", { "dependencies": { "is-glob": "^4.0.3" } }, "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A=="], + "global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="], + "globals": ["globals@16.5.0", "", {}, "sha512-c/c15i26VrJ4IRt5Z89DnIzCGDn9EcebibhAOjw5ibqEHsE1wLUgkPn9RDmNcUKyU87GeaL633nyJ+pplFR2ZQ=="], "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], @@ -1706,6 +1941,8 @@ "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + "got": ["got@11.8.6", "", { "dependencies": { "@sindresorhus/is": "^4.0.0", "@szmarczak/http-timer": "^4.0.5", "@types/cacheable-request": "^6.0.1", "@types/responselike": "^1.0.0", "cacheable-lookup": "^5.0.3", "cacheable-request": "^7.0.2", "decompress-response": "^6.0.0", "http2-wrapper": "^1.0.0-beta.5.2", "lowercase-keys": "^2.0.0", "p-cancelable": "^2.0.0", "responselike": "^2.0.0" } }, "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g=="], + "graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="], "has-bigints": ["has-bigints@1.1.0", "", {}, "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg=="], @@ -1760,6 +1997,8 @@ "htmlparser2": ["htmlparser2@10.1.0", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ=="], + "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], "http-proxy": ["http-proxy@1.18.1", "", { "dependencies": { "eventemitter3": "^4.0.0", "follow-redirects": "^1.0.0", "requires-port": "^1.0.0" } }, "sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ=="], @@ -1768,12 +2007,16 @@ "http-proxy-middleware": ["http-proxy-middleware@3.0.5", "", { "dependencies": { "@types/http-proxy": "^1.17.15", "debug": "^4.3.6", "http-proxy": "^1.18.1", "is-glob": "^4.0.3", "is-plain-object": "^5.0.0", "micromatch": "^4.0.8" } }, "sha512-GLZZm1X38BPY4lkXA01jhwxvDoOkkXqjgVyUzVxiEK4iuRu03PZoYHhHRwxnfhQMDuaxi3vVri0YgSro/1oWqg=="], + "http2-wrapper": ["http2-wrapper@1.0.3", "", { "dependencies": { "quick-lru": "^5.1.1", "resolve-alpn": "^1.0.0" } }, "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg=="], + "http_ece": ["http_ece@1.2.0", "", {}, "sha512-JrF8SSLVmcvc5NducxgyOrKXe3EsyHMgBFgSaIUGmArKe+rwr0uphRkRXvwiom3I+fpIfoItveHrfudL8/rxuA=="], "https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="], "humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="], + "iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "idb": ["idb@7.1.1", "", {}, "sha512-gchesWBzyvGHRO9W8tzUWFDycow5gwjvFKfyV9FF32Y7F50yZMp7mP+T2mJIWFx49zicqyC4uefHM17o6xKIVQ=="], @@ -1788,8 +2031,14 @@ "imurmurhash": ["imurmurhash@0.1.4", "", {}, "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA=="], + "indent-string": ["indent-string@4.0.0", "", {}, "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg=="], + "index-to-position": ["index-to-position@1.2.0", "", {}, "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw=="], + "infer-owner": ["infer-owner@1.0.4", "", {}, "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A=="], + + "inflight": ["inflight@1.0.6", "", { "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="], + "inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="], "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], @@ -1800,6 +2049,8 @@ "intl-messageformat": ["intl-messageformat@10.7.18", "", { "dependencies": { "@formatjs/ecma402-abstract": "2.3.6", "@formatjs/fast-memoize": "2.2.7", "@formatjs/icu-messageformat-parser": "2.11.4", "tslib": "^2.8.0" } }, "sha512-m3Ofv/X/tV8Y3tHXLohcuVuhWKo7BBq62cqY15etqmLxg2DZ34AGGgQDeR+SCta2+zICb1NX83af0GJmbQ1++g=="], + "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "is-alphabetical": ["is-alphabetical@1.0.4", "", {}, "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg=="], @@ -1844,6 +2095,10 @@ "is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="], + "is-interactive": ["is-interactive@1.0.0", "", {}, "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w=="], + + "is-lambda": ["is-lambda@1.0.1", "", {}, "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ=="], + "is-map": ["is-map@2.0.3", "", {}, "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw=="], "is-module": ["is-module@1.0.0", "", {}, "sha512-51ypPSPCoTEIN9dy5Oy+h4pShgJmPCygKfyRCISBI+JoWT/2oJvK8QPxmwv7b/p239jXrm9M1mlQbyKJ5A152g=="], @@ -1878,6 +2133,8 @@ "is-typed-array": ["is-typed-array@1.1.15", "", { "dependencies": { "which-typed-array": "^1.1.16" } }, "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ=="], + "is-unicode-supported": ["is-unicode-supported@0.1.0", "", {}, "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw=="], + "is-weakmap": ["is-weakmap@2.0.2", "", {}, "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w=="], "is-weakref": ["is-weakref@1.1.1", "", { "dependencies": { "call-bound": "^1.0.3" } }, "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew=="], @@ -1888,6 +2145,8 @@ "isarray": ["isarray@2.0.5", "", {}, "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw=="], + "isbinaryfile": ["isbinaryfile@5.0.7", "", {}, "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "istextorbinary": ["istextorbinary@9.5.0", "", { "dependencies": { "binaryextensions": "^6.11.0", "editions": "^6.21.0", "textextensions": "^6.11.0" } }, "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw=="], @@ -1916,6 +2175,8 @@ "json-stable-stringify-without-jsonify": ["json-stable-stringify-without-jsonify@1.0.1", "", {}, "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw=="], + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + "json-with-bigint": ["json-with-bigint@3.5.7", "", {}, "sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw=="], "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], @@ -1942,6 +2203,8 @@ "klaw-sync": ["klaw-sync@6.0.0", "", { "dependencies": { "graceful-fs": "^4.1.11" } }, "sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ=="], + "lazy-val": ["lazy-val@1.0.5", "", {}, "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q=="], + "leven": ["leven@3.1.0", "", {}, "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A=="], "levn": ["levn@0.4.1", "", { "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" } }, "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ=="], @@ -1980,10 +2243,14 @@ "lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="], + "lodash.escaperegexp": ["lodash.escaperegexp@4.1.2", "", {}, "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw=="], + "lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="], "lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="], + "lodash.isequal": ["lodash.isequal@4.5.0", "", {}, "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ=="], + "lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="], "lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="], @@ -2000,8 +2267,14 @@ "lodash.truncate": ["lodash.truncate@4.4.2", "", {}, "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw=="], + "log-symbols": ["log-symbols@4.1.0", "", { "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" } }, "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg=="], + "longest-streak": ["longest-streak@3.1.0", "", {}, "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g=="], + "loupe": ["loupe@3.2.1", "", {}, "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ=="], + + "lowercase-keys": ["lowercase-keys@2.0.0", "", {}, "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="], + "lowlight": ["lowlight@1.20.0", "", { "dependencies": { "fault": "^1.0.0", "highlight.js": "~10.7.0" } }, "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw=="], "lru-cache": ["lru-cache@6.0.0", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="], @@ -2012,12 +2285,16 @@ "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + "make-fetch-happen": ["make-fetch-happen@10.2.1", "", { "dependencies": { "agentkeepalive": "^4.2.1", "cacache": "^16.1.0", "http-cache-semantics": "^4.1.0", "http-proxy-agent": "^5.0.0", "https-proxy-agent": "^5.0.0", "is-lambda": "^1.0.1", "lru-cache": "^7.7.1", "minipass": "^3.1.6", "minipass-collect": "^1.0.2", "minipass-fetch": "^2.0.3", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^0.6.3", "promise-retry": "^2.0.1", "socks-proxy-agent": "^7.0.0", "ssri": "^9.0.0" } }, "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w=="], + "markdown-it": ["markdown-it@14.1.1", "", { "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA=="], "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], "marked": ["marked@17.0.3", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-jt1v2ObpyOKR8p4XaUJVk3YWRJ5n+i4+rjQopxvV32rSndTJXvIzuUdWWIy/1pFQMkQmvTXawzDNqOH/CUmx6A=="], + "matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="], + "math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="], "mdast-util-find-and-replace": ["mdast-util-find-and-replace@3.0.2", "", { "dependencies": { "@types/mdast": "^4.0.0", "escape-string-regexp": "^5.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg=="], @@ -2126,6 +2403,8 @@ "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], + "mimic-fn": ["mimic-fn@2.1.0", "", {}, "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="], + "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], "minimalistic-assert": ["minimalistic-assert@1.0.1", "", {}, "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="], @@ -2134,7 +2413,21 @@ "minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="], - "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "minipass": ["minipass@5.0.0", "", {}, "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ=="], + + "minipass-collect": ["minipass-collect@1.0.2", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA=="], + + "minipass-fetch": ["minipass-fetch@2.1.2", "", { "dependencies": { "minipass": "^3.1.6", "minipass-sized": "^1.0.3", "minizlib": "^2.1.2" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA=="], + + "minipass-flush": ["minipass-flush@1.0.7", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-TbqTz9cUwWyHS2Dy89P3ocAGUGxKjjLuR9z8w4WUTGAVgEj17/4nhgo2Du56i0Fm3Pm30g4iA8Lcqctc76jCzA=="], + + "minipass-pipeline": ["minipass-pipeline@1.2.4", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A=="], + + "minipass-sized": ["minipass-sized@1.0.3", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g=="], + + "minizlib": ["minizlib@2.1.2", "", { "dependencies": { "minipass": "^3.0.0", "yallist": "^4.0.0" } }, "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg=="], + + "mkdirp": ["mkdirp@1.0.4", "", { "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw=="], "mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="], @@ -2164,10 +2457,14 @@ "node-addon-api": ["node-addon-api@7.1.1", "", {}, "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ=="], + "node-api-version": ["node-api-version@0.2.1", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q=="], + "node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="], "node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="], + "node-gyp": ["node-gyp@11.5.0", "", { "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", "graceful-fs": "^4.2.6", "make-fetch-happen": "^14.0.3", "nopt": "^8.0.0", "proc-log": "^5.0.0", "semver": "^7.3.5", "tar": "^7.4.3", "tinyglobby": "^0.2.12", "which": "^5.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" } }, "sha512-ra7Kvlhxn5V9Slyus0ygMa2h+UqExPqUIkfk7Pc8QTLT956JLSy51uWFwHtIYy0vI8cB4BDhc/S03+880My/LQ=="], + "node-pty": ["node-pty@1.2.0-beta.12", "", { "dependencies": { "node-addon-api": "^7.1.0" } }, "sha512-uExTCG/4VmSJa4+TjxFwPXv8BfacmfFEBL6JpxCMDghcwqzvD0yTcGmZ1fKOK6HY33tp0CelLblqTECJizc+Yw=="], "node-releases": ["node-releases@2.0.27", "", {}, "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA=="], @@ -2176,10 +2473,14 @@ "nodemon": ["nodemon@3.1.14", "", { "dependencies": { "chokidar": "^3.5.2", "debug": "^4", "ignore-by-default": "^1.0.1", "minimatch": "^10.2.1", "pstree.remy": "^1.1.8", "semver": "^7.5.3", "simple-update-notifier": "^2.0.0", "supports-color": "^5.5.0", "touch": "^3.1.0", "undefsafe": "^2.0.5" }, "bin": { "nodemon": "bin/nodemon.js" } }, "sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw=="], + "nopt": ["nopt@6.0.0", "", { "dependencies": { "abbrev": "^1.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g=="], + "normalize-package-data": ["normalize-package-data@6.0.2", "", { "dependencies": { "hosted-git-info": "^7.0.0", "semver": "^7.3.5", "validate-npm-package-license": "^3.0.4" } }, "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g=="], "normalize-path": ["normalize-path@3.0.0", "", {}, "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="], + "normalize-url": ["normalize-url@6.1.0", "", {}, "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A=="], + "nth-check": ["nth-check@2.1.1", "", { "dependencies": { "boolbase": "^1.0.0" } }, "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w=="], "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], @@ -2196,6 +2497,8 @@ "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], + "onetime": ["onetime@5.1.2", "", { "dependencies": { "mimic-fn": "^2.1.0" } }, "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="], + "oniguruma-parser": ["oniguruma-parser@0.12.1", "", {}, "sha512-8Unqkvk1RYc6yq2WBYRj4hdnsAxVze8i7iPfQr8e4uSP3tRv0rpZcbGUDvxfQQcdwHt/e9PrMvGCsa8OqG9X3w=="], "oniguruma-to-es": ["oniguruma-to-es@4.3.4", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-3VhUGN3w2eYxnTzHn+ikMI+fp/96KoRSVK9/kMTcFqj1NRDh2IhQCKvYxDnWePKRXY/AqH+Fuiyb7VHSzBjHfA=="], @@ -2208,8 +2511,12 @@ "optionator": ["optionator@0.9.4", "", { "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" } }, "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g=="], + "ora": ["ora@5.4.1", "", { "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", "cli-cursor": "^3.1.0", "cli-spinners": "^2.5.0", "is-interactive": "^1.0.0", "is-unicode-supported": "^0.1.0", "log-symbols": "^4.1.0", "strip-ansi": "^6.0.0", "wcwidth": "^1.0.1" } }, "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ=="], + "own-keys": ["own-keys@1.0.1", "", { "dependencies": { "get-intrinsic": "^1.2.6", "object-keys": "^1.1.1", "safe-push-apply": "^1.0.0" } }, "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg=="], + "p-cancelable": ["p-cancelable@2.1.1", "", {}, "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg=="], + "p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], "p-locate": ["p-locate@5.0.0", "", { "dependencies": { "p-limit": "^3.0.2" } }, "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw=="], @@ -2240,6 +2547,8 @@ "path-exists": ["path-exists@4.0.0", "", {}, "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w=="], + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], + "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-parse": ["path-parse@1.0.7", "", {}, "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw=="], @@ -2250,12 +2559,20 @@ "path-type": ["path-type@6.0.0", "", {}, "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ=="], + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "pathval": ["pathval@2.0.1", "", {}, "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ=="], + + "pe-library": ["pe-library@0.4.1", "", {}, "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw=="], + "pend": ["pend@1.2.0", "", {}, "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg=="], "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + "plist": ["plist@3.1.0", "", { "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", "xmlbuilder": "^15.1.1" } }, "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ=="], + "pluralize": ["pluralize@8.0.0", "", {}, "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA=="], "pngjs": ["pngjs@5.0.0", "", {}, "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="], @@ -2266,6 +2583,8 @@ "postcss-value-parser": ["postcss-value-parser@4.2.0", "", {}, "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="], + "postject": ["postject@1.0.0-alpha.6", "", { "dependencies": { "commander": "^9.4.0" }, "bin": { "postject": "dist/cli.js" } }, "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A=="], + "prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="], "prelude-ls": ["prelude-ls@1.2.1", "", {}, "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g=="], @@ -2274,6 +2593,16 @@ "prismjs": ["prismjs@1.30.0", "", {}, "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw=="], + "proc-log": ["proc-log@2.0.1", "", {}, "sha512-Kcmo2FhfDTXdcbfDH76N7uBYHINxc/8GW7UAVuVP9I+Va3uHSerrnKV6dLooga/gh7GlgzuCCr/eoldnL1muGw=="], + + "progress": ["progress@2.0.3", "", {}, "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="], + + "promise-inflight": ["promise-inflight@1.0.1", "", {}, "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g=="], + + "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], + + "proper-lockfile": ["proper-lockfile@4.1.2", "", { "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA=="], + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], @@ -2286,6 +2615,8 @@ "punycode.js": ["punycode.js@2.3.1", "", {}, "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA=="], + "pupa": ["pupa@3.3.0", "", { "dependencies": { "escape-goat": "^4.0.0" } }, "sha512-LjgDO2zPtoXP2wJpDjZrGdojii1uqO0cnwKoIoUzkfS98HDmbeiGmYiXo3lXeFlq2xvne1QFQhwYXSUCLKtEuA=="], + "pvtsutils": ["pvtsutils@1.3.6", "", { "dependencies": { "tslib": "^2.8.1" } }, "sha512-PLgQXQ6H2FWCaeRak8vvk1GW462lMxB5s3Jm673N82zI4vqtVUPuZdffdZbPDFRoU8kAhItWFtPCWiPpp4/EDg=="], "pvutils": ["pvutils@1.1.5", "", {}, "sha512-KTqnxsgGiQ6ZAzZCVlJH5eOjSnvlyEgx1m8bkRJfOhmGRqfo5KLvmAlACQkrjEtOQ4B7wF9TdSLIs9O90MX9xA=="], @@ -2298,6 +2629,8 @@ "queue-microtask": ["queue-microtask@1.2.3", "", {}, "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A=="], + "quick-lru": ["quick-lru@5.1.1", "", {}, "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA=="], + "randombytes": ["randombytes@2.1.0", "", { "dependencies": { "safe-buffer": "^5.1.0" } }, "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ=="], "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], @@ -2326,6 +2659,8 @@ "read": ["read@1.0.7", "", { "dependencies": { "mute-stream": "~0.0.4" } }, "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ=="], + "read-binary-file-arch": ["read-binary-file-arch@1.0.6", "", { "dependencies": { "debug": "^4.3.4" }, "bin": { "read-binary-file-arch": "cli.js" } }, "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg=="], + "read-pkg": ["read-pkg@9.0.1", "", { "dependencies": { "@types/normalize-package-data": "^2.4.3", "normalize-package-data": "^6.0.0", "parse-json": "^8.0.0", "type-fest": "^4.6.0", "unicorn-magic": "^0.1.0" } }, "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA=="], "readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="], @@ -2378,16 +2713,30 @@ "requires-port": ["requires-port@1.0.0", "", {}, "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ=="], + "resedit": ["resedit@1.7.2", "", { "dependencies": { "pe-library": "^0.4.1" } }, "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA=="], + "reselect": ["reselect@5.1.1", "", {}, "sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w=="], "resolve": ["resolve@1.22.11", "", { "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" } }, "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ=="], + "resolve-alpn": ["resolve-alpn@1.2.1", "", {}, "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g=="], + "resolve-from": ["resolve-from@4.0.0", "", {}, "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g=="], "resolve-pkg-maps": ["resolve-pkg-maps@1.0.0", "", {}, "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw=="], + "responselike": ["responselike@2.0.1", "", { "dependencies": { "lowercase-keys": "^2.0.0" } }, "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw=="], + + "restore-cursor": ["restore-cursor@3.1.0", "", { "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" } }, "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA=="], + + "retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + "reusify": ["reusify@1.1.0", "", {}, "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw=="], + "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + + "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], + "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], @@ -2408,6 +2757,8 @@ "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], + "sanitize-filename": ["sanitize-filename@1.6.4", "", { "dependencies": { "truncate-utf8-bytes": "^1.0.0" } }, "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg=="], + "sax": ["sax@1.4.4", "", {}, "sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw=="], "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], @@ -2416,8 +2767,12 @@ "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="], + "serialize-javascript": ["serialize-javascript@6.0.2", "", { "dependencies": { "randombytes": "^2.1.0" } }, "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g=="], "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], @@ -2448,7 +2803,9 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], + + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], @@ -2464,12 +2821,22 @@ "slash": ["slash@2.0.0", "", {}, "sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A=="], - "slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="], + "slice-ansi": ["slice-ansi@5.0.0", "", { "dependencies": { "ansi-styles": "^6.0.0", "is-fullwidth-code-point": "^4.0.0" } }, "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ=="], + + "smart-buffer": ["smart-buffer@4.2.0", "", {}, "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg=="], "smob": ["smob@1.6.1", "", {}, "sha512-KAkBqZl3c2GvNgNhcoyJae1aKldDW0LO279wF9bk1PnluRTETKBq0WyzRXxEhoQLk56yHaOY4JCBEKDuJIET5g=="], + "socks": ["socks@2.8.7", "", { "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" } }, "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A=="], + + "socks-proxy-agent": ["socks-proxy-agent@7.0.0", "", { "dependencies": { "agent-base": "^6.0.2", "debug": "^4.3.3", "socks": "^2.6.2" } }, "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww=="], + "sonner": ["sonner@2.0.7", "", { "peerDependencies": { "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc", "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w=="], + "sort-keys": ["sort-keys@1.1.2", "", { "dependencies": { "is-plain-obj": "^1.0.0" } }, "sha512-vzn8aSqKgytVik0iwdBEi+zevbTYZogewTUM6dtpmGwEcdzbub/TX4bCzRhebDCRC3QzXgJsLRKB2V/Oof7HXg=="], + + "sort-keys-length": ["sort-keys-length@1.0.1", "", { "dependencies": { "sort-keys": "^1.0.0" } }, "sha512-GRbEOUqCxemTAk/b32F2xa8wDTs+Z1QHOkbhJDQTvv/6G3ZkbJ+frYWsTcc7cBB3Fu4wy4XlLCuNtJuMn7Gsvw=="], + "source-map": ["source-map@0.8.0-beta.0", "", { "dependencies": { "whatwg-url": "^7.0.0" } }, "sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA=="], "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], @@ -2488,12 +2855,24 @@ "spdx-license-ids": ["spdx-license-ids@3.0.23", "", {}, "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw=="], + "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], + + "ssri": ["ssri@9.0.1", "", { "dependencies": { "minipass": "^3.1.1" } }, "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q=="], + + "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], + + "stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "std-env": ["std-env@3.10.0", "", {}, "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg=="], + "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], + "string.prototype.matchall": ["string.prototype.matchall@4.0.12", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.3", "define-properties": "^1.2.1", "es-abstract": "^1.23.6", "es-errors": "^1.3.0", "es-object-atoms": "^1.0.0", "get-intrinsic": "^1.2.6", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "internal-slot": "^1.1.0", "regexp.prototype.flags": "^1.5.3", "set-function-name": "^2.0.2", "side-channel": "^1.1.0" } }, "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA=="], "string.prototype.trim": ["string.prototype.trim@1.2.10", "", { "dependencies": { "call-bind": "^1.0.8", "call-bound": "^1.0.2", "define-data-property": "^1.1.4", "define-properties": "^1.2.1", "es-abstract": "^1.23.5", "es-object-atoms": "^1.0.0", "has-property-descriptors": "^1.0.2" } }, "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA=="], @@ -2510,10 +2889,14 @@ "strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-ansi-cjs": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], + "strip-comments": ["strip-comments@2.0.1", "", {}, "sha512-ZprKx+bBLXv067WTCALv8SSz5l2+XhpYCsVtSqlMnkAXMWDq+/ekVbl1ghqP9rUHTzv6sm/DwCOiYutU/yp1fw=="], "strip-json-comments": ["strip-json-comments@5.0.3", "", {}, "sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw=="], + "strip-literal": ["strip-literal@3.1.0", "", { "dependencies": { "js-tokens": "^9.0.1" } }, "sha512-8r3mkIM/2+PpjHoOtiAW8Rg3jJLHaV7xPwG+YRGrv6FP0wwk/toTpATxWYOW0BKdWwl82VT2tFYi5DlROa0Mxg=="], + "structured-source": ["structured-source@4.0.0", "", { "dependencies": { "boundary": "^2.0.0" } }, "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA=="], "style-mod": ["style-mod@4.1.3", "", {}, "sha512-i/n8VsZydrugj3Iuzll8+x/00GH2vnYsk1eomD8QiRrSAeW6ItbCQDtfXCeJHd0iwiNagqjQkvpvREEPtW3IoQ=="], @@ -2522,6 +2905,8 @@ "style-to-object": ["style-to-object@1.0.14", "", { "dependencies": { "inline-style-parser": "0.2.7" } }, "sha512-LIN7rULI0jBscWQYaSswptyderlarFkjQ+t79nzty8tcIAceVomEVlLzH5VP4Cmsv6MtKhs7qaAiwlcp+Mgaxw=="], + "sumchecker": ["sumchecker@3.0.1", "", { "dependencies": { "debug": "^4.1.0" } }, "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="], + "supports-color": ["supports-color@8.1.1", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q=="], "supports-hyperlinks": ["supports-hyperlinks@3.2.0", "", { "dependencies": { "has-flag": "^4.0.0", "supports-color": "^7.0.0" } }, "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig=="], @@ -2538,12 +2923,18 @@ "tapable": ["tapable@2.3.0", "", {}, "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg=="], + "tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="], + "tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="], "tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="], + "temp": ["temp@0.9.4", "", { "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" } }, "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA=="], + "temp-dir": ["temp-dir@2.0.0", "", {}, "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg=="], + "temp-file": ["temp-file@3.4.0", "", { "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" } }, "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg=="], + "tempy": ["tempy@0.6.0", "", { "dependencies": { "is-stream": "^2.0.0", "temp-dir": "^2.0.0", "type-fest": "^0.16.0", "unique-string": "^2.0.0" } }, "sha512-G13vtMYPT/J8A4X2SjdtBTphZlrp1gKv6hZiOjw14RCWg6GbHuQBGtjlx75xLbYV/wEc0D7G5K4rxKP/cXk8Bw=="], "terminal-link": ["terminal-link@4.0.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "supports-hyperlinks": "^3.2.0" } }, "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA=="], @@ -2554,10 +2945,26 @@ "textextensions": ["textextensions@6.11.0", "", { "dependencies": { "editions": "^6.21.0" } }, "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ=="], + "tiny-async-pool": ["tiny-async-pool@1.3.0", "", { "dependencies": { "semver": "^5.5.0" } }, "sha512-01EAw5EDrcVrdgyCLgoSPvqznC0sVxDSVeiOz09FUpjh71G79VCqneOr+xvt7T1r76CF6ZZfPjHorN2+d+3mqA=="], + + "tiny-typed-emitter": ["tiny-typed-emitter@2.1.0", "", {}, "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="], + + "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + "tinypool": ["tinypool@1.1.1", "", {}, "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg=="], + + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], + + "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], + "tmp": ["tmp@0.2.5", "", {}, "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow=="], + "tmp-promise": ["tmp-promise@3.0.3", "", { "dependencies": { "tmp": "^0.2.0" } }, "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ=="], + "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], @@ -2572,6 +2979,8 @@ "trough": ["trough@2.2.0", "", {}, "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw=="], + "truncate-utf8-bytes": ["truncate-utf8-bytes@1.0.2", "", { "dependencies": { "utf8-byte-length": "^1.0.1" } }, "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ=="], + "ts-api-utils": ["ts-api-utils@2.4.0", "", { "peerDependencies": { "typescript": ">=4.8.4" } }, "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], @@ -2630,6 +3039,10 @@ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + "unique-filename": ["unique-filename@2.0.1", "", { "dependencies": { "unique-slug": "^3.0.0" } }, "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A=="], + + "unique-slug": ["unique-slug@3.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w=="], + "unique-string": ["unique-string@2.0.0", "", { "dependencies": { "crypto-random-string": "^2.0.0" } }, "sha512-uNaeirEPvpZWSgzwsPGtU2zVSTrn/8L5q/IexZmH0eH6SA73CmAA5U4GwORTxQAZs95TAXLNqeLoPPNO5gZfWg=="], "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], @@ -2652,6 +3065,8 @@ "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "unused-filename": ["unused-filename@4.0.1", "", { "dependencies": { "escape-string-regexp": "^5.0.0", "path-exists": "^5.0.0" } }, "sha512-ZX6U1J04K1FoSUeoX1OicAhw4d0aro2qo+L8RhJkiGTNtBNkd/Fi1Wxoc9HzcVu6HfOzm0si/N15JjxFmD1z6A=="], + "upath": ["upath@1.2.0", "", {}, "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg=="], "update-browserslist-db": ["update-browserslist-db@1.2.3", "", { "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "peerDependencies": { "browserslist": ">= 4.21.0" }, "bin": { "update-browserslist-db": "cli.js" } }, "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w=="], @@ -2666,6 +3081,8 @@ "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], + "utf8-byte-length": ["utf8-byte-length@1.0.5", "", {}, "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "uuid": ["uuid@8.3.2", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="], @@ -2674,6 +3091,8 @@ "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "verror": ["verror@1.10.1", "", { "dependencies": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", "extsprintf": "^1.2.0" } }, "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg=="], + "version-range": ["version-range@4.15.0", "", {}, "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg=="], "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], @@ -2684,10 +3103,16 @@ "vite": ["vite@7.3.1", "", { "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA=="], + "vite-node": ["vite-node@3.2.4", "", { "dependencies": { "cac": "^6.7.14", "debug": "^4.4.1", "es-module-lexer": "^1.7.0", "pathe": "^2.0.3", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0" }, "bin": { "vite-node": "vite-node.mjs" } }, "sha512-EbKSKh+bh1E1IFxeO0pg1n4dvoOTt0UDiXMd/qn++r98+jPO1xtJilvXldeuQ8giIB5IkpjCgMleHMNEsGH6pg=="], + "vite-plugin-pwa": ["vite-plugin-pwa@1.2.0", "", { "dependencies": { "debug": "^4.3.6", "pretty-bytes": "^6.1.1", "tinyglobby": "^0.2.10", "workbox-build": "^7.4.0", "workbox-window": "^7.4.0" }, "peerDependencies": { "@vite-pwa/assets-generator": "^1.0.0", "vite": "^3.1.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" }, "optionalPeers": ["@vite-pwa/assets-generator"] }, "sha512-a2xld+SJshT9Lgcv8Ji4+srFJL4k/1bVbd1x06JIkvecpQkwkvCncD1+gSzcdm3s+owWLpMJerG3aN5jupJEVw=="], + "vitest": ["vitest@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/expect": "3.2.4", "@vitest/mocker": "3.2.4", "@vitest/pretty-format": "^3.2.4", "@vitest/runner": "3.2.4", "@vitest/snapshot": "3.2.4", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", "magic-string": "^0.30.17", "pathe": "^2.0.3", "picomatch": "^4.0.2", "std-env": "^3.9.0", "tinybench": "^2.9.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.14", "tinypool": "^1.1.1", "tinyrainbow": "^2.0.0", "vite": "^5.0.0 || ^6.0.0 || ^7.0.0-0", "vite-node": "3.2.4", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "@vitest/browser": "3.2.4", "@vitest/ui": "3.2.4", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@types/debug", "@types/node", "@vitest/browser", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A=="], + "w3c-keyname": ["w3c-keyname@2.2.8", "", {}, "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ=="], + "wcwidth": ["wcwidth@1.0.1", "", { "dependencies": { "defaults": "^1.0.3" } }, "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg=="], + "web-namespaces": ["web-namespaces@2.0.1", "", {}, "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ=="], "web-push": ["web-push@3.6.7", "", { "dependencies": { "asn1.js": "^5.3.0", "http_ece": "1.2.0", "https-proxy-agent": "^7.0.0", "jws": "^4.0.0", "minimist": "^1.2.5" }, "bin": { "web-push": "src/cli.js" } }, "sha512-OpiIUe8cuGjrj3mMBFWY+e4MMIkW3SVT+7vEIjvD9kejGUypv8GPDf84JdPWskK8zMRIJ6xYGm+Kxr8YkPyA0A=="], @@ -2714,6 +3139,8 @@ "which-typed-array": ["which-typed-array@1.1.20", "", { "dependencies": { "available-typed-arrays": "^1.0.7", "call-bind": "^1.0.8", "call-bound": "^1.0.4", "for-each": "^0.3.5", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2" } }, "sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg=="], + "why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], + "word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="], "workbox-background-sync": ["workbox-background-sync@7.4.0", "", { "dependencies": { "idb": "^7.0.1", "workbox-core": "7.4.0" } }, "sha512-8CB9OxKAgKZKyNMwfGZ1XESx89GryWTfI+V5yEj8sHjFH8MFelUwYXEyldEK6M6oKMmn807GoJFUEA1sC4XS9w=="], @@ -2750,6 +3177,8 @@ "wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="], + "wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="], "ws": ["ws@8.19.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg=="], @@ -2800,6 +3229,28 @@ "@babel/preset-env/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "@electron/asar/commander": ["commander@5.1.0", "", {}, "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg=="], + + "@electron/asar/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "@electron/fuses/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + + "@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "@electron/node-gyp/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], + + "@electron/notarize/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + + "@electron/osx-sign/isbinaryfile": ["isbinaryfile@4.0.10", "", {}, "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw=="], + + "@electron/universal/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="], + + "@electron/universal/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "@electron/windows-sign/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="], + "@eslint-community/eslint-utils/eslint-visitor-keys": ["eslint-visitor-keys@3.4.3", "", {}, "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag=="], "@eslint/eslintrc/globals": ["globals@14.0.0", "", {}, "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ=="], @@ -2808,6 +3259,14 @@ "@heroui/theme/tailwind-merge": ["tailwind-merge@3.4.0", "", {}, "sha512-uSaO4gnW+b3Y2aWoWfFpX62vn2sR3skfhbjsEnaBI81WD1wBLlHZe5sWf0AqjksNdYTbGBEd0UasQMT3SNV15g=="], + "@isaacs/fs-minipass/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "@malept/flatpak-bundler/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + + "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "@npmcli/agent/socks-proxy-agent": ["socks-proxy-agent@8.0.5", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" } }, "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw=="], + "@openchamber/ui/ghostty-web": ["ghostty-web@0.4.0", "", {}, "sha512-0puDBik2qapbD/QQBW9o5ZHfXnZBqZWx/ctBiVtKZ6ZLds4NYb+wZuw1cRLXZk9zYovIQ908z3rvFhexAvc5Hg=="], "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], @@ -2834,6 +3293,8 @@ "@rollup/pluginutils/@types/estree": ["@types/estree@0.0.39", "", {}, "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw=="], + "@rollup/pluginutils/estree-walker": ["estree-walker@1.0.1", "", {}, "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg=="], + "@rollup/pluginutils/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], "@rollup/pluginutils/rollup": ["rollup@2.80.0", "", { "optionalDependencies": { "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-cIFJOD1DESzpjOBl763Kp1AH7UE/0fcdHe6rZXUdQ9c50uvgigvW97u3IcSeBwOkgqL/PXPBktBCh0KEu5L8XQ=="], @@ -2860,6 +3321,8 @@ "@textlint/linter-formatter/pluralize": ["pluralize@2.0.0", "", {}, "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw=="], + "@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], + "@typescript-eslint/eslint-plugin/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "@typescript-eslint/typescript-estree/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], @@ -2872,30 +3335,70 @@ "anymatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "app-builder-lib/@electron/get": ["@electron/get@3.1.0", "", { "dependencies": { "debug": "^4.1.1", "env-paths": "^2.2.0", "fs-extra": "^8.1.0", "got": "^11.8.5", "progress": "^2.0.3", "semver": "^6.2.0", "sumchecker": "^3.0.1" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "sha512-F+nKc0xW+kVbBRhFzaMgPy3KwmuNTYX1fx6+FxxoSnNgwYX6LD7AKBTWkU0MQ6IBoe7dz069CNkR673sPAgkCQ=="], + + "app-builder-lib/@electron/rebuild": ["@electron/rebuild@4.0.3", "", { "dependencies": { "@malept/cross-spawn-promise": "^2.0.0", "debug": "^4.1.1", "detect-libc": "^2.0.1", "got": "^11.7.0", "graceful-fs": "^4.2.11", "node-abi": "^4.2.0", "node-api-version": "^0.2.1", "node-gyp": "^11.2.0", "ora": "^5.1.0", "read-binary-file-arch": "^1.0.6", "semver": "^7.3.5", "tar": "^7.5.6", "yargs": "^17.0.1" }, "bin": { "electron-rebuild": "lib/cli.js" } }, "sha512-u9vpTHRMkOYCs/1FLiSVAFZ7FbjsXK+bQuzviJZa+lG7BHZl1nz52/IcGvwa3sk80/fc3llutBkbCq10Vh8WQA=="], + + "app-builder-lib/ci-info": ["ci-info@4.3.1", "", {}, "sha512-Wdy2Igu8OcBpI2pZePZ5oWjPC38tmDVx5WKUXKwlLYkA0ozo85sLsLvkBbBn/sZaSCMFOGZJ14fvW9t5/d7kdA=="], + + "app-builder-lib/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + + "app-builder-lib/tar": ["tar@7.5.13", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng=="], + + "app-builder-lib/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "babel-plugin-polyfill-corejs2/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + "cacache/glob": ["glob@8.1.0", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^5.0.1", "once": "^1.3.0" } }, "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ=="], + + "cacache/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + + "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "cacache/p-map": ["p-map@4.0.0", "", { "dependencies": { "aggregate-error": "^3.0.0" } }, "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ=="], + "chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "chokidar/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], + "cli-truncate/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], + + "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], + "cmdk/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], "compression/debug": ["debug@2.6.9", "", { "dependencies": { "ms": "2.0.0" } }, "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="], "decode-named-character-reference/character-entities": ["character-entities@2.0.2", "", {}, "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ=="], + "dmg-builder/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "dom-serializer/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], + "electron-builder/ci-info": ["ci-info@4.4.0", "", {}, "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg=="], + + "electron-publish/mime": ["mime@2.6.0", "", { "bin": { "mime": "cli.js" } }, "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg=="], + + "electron-winstaller/fs-extra": ["fs-extra@7.0.1", "", { "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw=="], + + "encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "encoding-sniffer/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "fast-glob/glob-parent": ["glob-parent@5.1.2", "", { "dependencies": { "is-glob": "^4.0.1" } }, "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow=="], "filelist/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "glob/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], + "glob/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + "globby/ignore": ["ignore@7.0.5", "", {}, "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg=="], "globby/slash": ["slash@5.1.0", "", {}, "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg=="], @@ -2912,10 +3415,22 @@ "hastscript/space-separated-tokens": ["space-separated-tokens@1.1.5", "", {}, "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA=="], + "iconv-corefoundation/cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="], + + "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], + "is-inside-container/is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="], "keytar/node-addon-api": ["node-addon-api@4.3.0", "", {}, "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ=="], + "make-fetch-happen/http-proxy-agent": ["http-proxy-agent@5.0.0", "", { "dependencies": { "@tootallnate/once": "2", "agent-base": "6", "debug": "4" } }, "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w=="], + + "make-fetch-happen/https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], + + "make-fetch-happen/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], + + "make-fetch-happen/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "markdown-it/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "mdast-util-find-and-replace/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], @@ -2924,6 +3439,28 @@ "micromatch/picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "minizlib/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + + "node-gyp/make-fetch-happen": ["make-fetch-happen@14.0.3", "", { "dependencies": { "@npmcli/agent": "^3.0.0", "cacache": "^19.0.1", "http-cache-semantics": "^4.1.1", "minipass": "^7.0.2", "minipass-fetch": "^4.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "negotiator": "^1.0.0", "proc-log": "^5.0.0", "promise-retry": "^2.0.1", "ssri": "^12.0.0" } }, "sha512-QMjGbFTP0blj97EeidG5hk/QhKQ3T4ICckQGLgz38QF7Vgbk6e6FTARN8KhKxyBbWn8R0HU+bnw8aSoFPD4qtQ=="], + + "node-gyp/nopt": ["nopt@8.1.0", "", { "dependencies": { "abbrev": "^3.0.0" }, "bin": { "nopt": "bin/nopt.js" } }, "sha512-ieGu42u/Qsa4TFktmaKEwM6MQH0pOWnaB3htzh0JRtx84+Mebc0cbZYN5bC+6WTZ4+77xrL9Pn5m7CV6VIkV7A=="], + + "node-gyp/proc-log": ["proc-log@5.0.0", "", {}, "sha512-Azwzvl90HaF0aCz1JrDdXQykFakSSNPaPoiZ9fm5qJIMHioDZEi7OAdRwSm6rSoPtY3Qutnm3L7ogmg3dc+wbQ=="], + + "node-gyp/tar": ["tar@7.5.13", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng=="], + + "node-gyp/which": ["which@5.0.0", "", { "dependencies": { "isexe": "^3.1.1" }, "bin": { "node-which": "bin/which.js" } }, "sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ=="], + "node-sarif-builder/fs-extra": ["fs-extra@11.3.3", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg=="], "nodemon/minimatch": ["minimatch@10.2.4", "", { "dependencies": { "brace-expansion": "^5.0.2" } }, "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg=="], @@ -2942,6 +3479,12 @@ "path-scurry/lru-cache": ["lru-cache@11.2.6", "", {}, "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ=="], + "path-scurry/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], + + "postject/commander": ["commander@9.5.0", "", {}, "sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ=="], + "qrcode/yargs": ["yargs@15.4.1", "", { "dependencies": { "cliui": "^6.0.0", "decamelize": "^1.2.0", "find-up": "^4.1.0", "get-caller-file": "^2.0.1", "require-directory": "^2.1.1", "require-main-filename": "^2.0.0", "set-blocking": "^2.0.0", "string-width": "^4.2.0", "which-module": "^2.0.0", "y18n": "^4.0.0", "yargs-parser": "^18.1.2" } }, "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A=="], "rc/strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="], @@ -2956,22 +3499,52 @@ "refractor/prismjs": ["prismjs@1.27.0", "", {}, "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA=="], + "rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + + "serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], + + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], + + "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], + + "socks-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "sort-keys/is-plain-obj": ["is-plain-obj@1.1.0", "", {}, "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg=="], + "source-map/whatwg-url": ["whatwg-url@7.1.0", "", { "dependencies": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", "webidl-conversions": "^4.0.2" } }, "sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg=="], "source-map-support/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="], + "ssri/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "stringify-entities/character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + "strip-literal/js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], + "supports-hyperlinks/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], "table/ajv": ["ajv@8.18.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A=="], + "table/slice-ansi": ["slice-ansi@4.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ=="], + + "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + + "temp/mkdirp": ["mkdirp@0.5.6", "", { "dependencies": { "minimist": "^1.2.6" }, "bin": { "mkdirp": "bin/cmd.js" } }, "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw=="], + + "temp/rimraf": ["rimraf@2.6.3", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "./bin.js" } }, "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA=="], + "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], + "tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], + "tsx/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "tsyringe/tslib": ["tslib@1.14.1", "", {}, "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg=="], + "unused-filename/escape-string-regexp": ["escape-string-regexp@5.0.0", "", {}, "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw=="], + + "unused-filename/path-exists": ["path-exists@5.0.0", "", {}, "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ=="], + "vite/esbuild": ["esbuild@0.27.3", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.3", "@esbuild/android-arm": "0.27.3", "@esbuild/android-arm64": "0.27.3", "@esbuild/android-x64": "0.27.3", "@esbuild/darwin-arm64": "0.27.3", "@esbuild/darwin-x64": "0.27.3", "@esbuild/freebsd-arm64": "0.27.3", "@esbuild/freebsd-x64": "0.27.3", "@esbuild/linux-arm": "0.27.3", "@esbuild/linux-arm64": "0.27.3", "@esbuild/linux-ia32": "0.27.3", "@esbuild/linux-loong64": "0.27.3", "@esbuild/linux-mips64el": "0.27.3", "@esbuild/linux-ppc64": "0.27.3", "@esbuild/linux-riscv64": "0.27.3", "@esbuild/linux-s390x": "0.27.3", "@esbuild/linux-x64": "0.27.3", "@esbuild/netbsd-arm64": "0.27.3", "@esbuild/netbsd-x64": "0.27.3", "@esbuild/openbsd-arm64": "0.27.3", "@esbuild/openbsd-x64": "0.27.3", "@esbuild/openharmony-arm64": "0.27.3", "@esbuild/sunos-x64": "0.27.3", "@esbuild/win32-arm64": "0.27.3", "@esbuild/win32-ia32": "0.27.3", "@esbuild/win32-x64": "0.27.3" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg=="], "whatwg-encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], @@ -2992,6 +3565,14 @@ "@babel/helper-compilation-targets/lru-cache/yallist": ["yallist@3.1.1", "", {}, "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="], + "@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + + "@electron/node-gyp/glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], + + "@electron/universal/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@rollup/plugin-node-resolve/@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], "@secretlint/config-loader/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], @@ -3000,8 +3581,36 @@ "@typescript-eslint/typescript-estree/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + "app-builder-lib/@electron/get/fs-extra": ["fs-extra@8.1.0", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^4.0.0", "universalify": "^0.1.0" } }, "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="], + + "app-builder-lib/@electron/get/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], + + "app-builder-lib/@electron/rebuild/node-abi": ["node-abi@4.28.0", "", { "dependencies": { "semver": "^7.6.3" } }, "sha512-Qfp5XZL1cJDOabOT8H5gnqMTmM4NjvYzHp4I/Kt/Sl76OVkOBBHRFlPspGV0hYvMoqQsypFjT/Yp7Km0beXW9g=="], + + "app-builder-lib/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], + + "app-builder-lib/tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "app-builder-lib/tar/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "app-builder-lib/tar/minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + + "app-builder-lib/tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "app-builder-lib/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + + "cacache/glob/minimatch": ["minimatch@5.1.9", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw=="], + + "cli-truncate/string-width/emoji-regex": ["emoji-regex@10.6.0", "", {}, "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A=="], + + "cli-truncate/string-width/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + "compression/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="], + "electron-winstaller/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "electron-winstaller/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + "filelist/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], @@ -3014,6 +3623,12 @@ "hastscript/@types/hast/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], + "iconv-corefoundation/cli-truncate/slice-ansi": ["slice-ansi@3.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", "is-fullwidth-code-point": "^3.0.0" } }, "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ=="], + + "make-fetch-happen/http-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + + "make-fetch-happen/https-proxy-agent/agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], + "mdast-util-mdx-jsx/parse-entities/@types/unist": ["@types/unist@2.0.11", "", {}, "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA=="], "mdast-util-mdx-jsx/parse-entities/character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], @@ -3026,6 +3641,28 @@ "mdast-util-mdx-jsx/parse-entities/is-hexadecimal": ["is-hexadecimal@2.0.1", "", {}, "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg=="], + "node-gyp/make-fetch-happen/cacache": ["cacache@19.0.1", "", { "dependencies": { "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", "glob": "^10.2.2", "lru-cache": "^10.0.1", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", "p-map": "^7.0.2", "ssri": "^12.0.0", "tar": "^7.4.3", "unique-filename": "^4.0.0" } }, "sha512-hdsUxulXCi5STId78vRVYEtDAjq99ICAUktLTeTYsLoTE6Z8dS0c8pWNCxwdrk9YfJeobDZc2Y186hD/5ZQgFQ=="], + + "node-gyp/make-fetch-happen/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "node-gyp/make-fetch-happen/minipass-fetch": ["minipass-fetch@4.0.1", "", { "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", "minizlib": "^3.0.1" }, "optionalDependencies": { "encoding": "^0.1.13" } }, "sha512-j7U11C5HXigVuutxebFadoYBbd7VSdZWggSe64NVdvWNBqGAiXPL2QVCehjmw7lY1oF9gOllYbORh+hiNgfPgQ=="], + + "node-gyp/make-fetch-happen/negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], + + "node-gyp/make-fetch-happen/ssri": ["ssri@12.0.0", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-S7iGNosepx9RadX82oimUkvr0Ct7IjJbEbs4mJcTxst8um95J3sDYU1RBEOvdu6oL1Wek2ODI5i4MAw+dZ6cAQ=="], + + "node-gyp/nopt/abbrev": ["abbrev@3.0.1", "", {}, "sha512-AO2ac6pjRB3SJmGJo+v5/aK6Omggp6fsLrs6wN9bd35ulu4cCwaAU9+7ZhXjeqHVkaHThLuzH0nZr0YpCDhygg=="], + + "node-gyp/tar/chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "node-gyp/tar/minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "node-gyp/tar/minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + + "node-gyp/tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "node-gyp/which/isexe": ["isexe@3.1.5", "", {}, "sha512-6B3tLtFqtQS4ekarvLVMZ+X+VlvQekbe4taUkf/rhVO3d/h0M2rfARm/pXLcPEsjjMsFgrFgSrhQIxcSVrBz8w=="], + "nodemon/minimatch/brace-expansion": ["brace-expansion@5.0.4", "", { "dependencies": { "balanced-match": "^4.0.2" } }, "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg=="], "nodemon/supports-color/has-flag": ["has-flag@3.0.0", "", {}, "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="], @@ -3048,6 +3685,8 @@ "table/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "temp/rimraf/glob": ["glob@7.2.3", "", { "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" } }, "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q=="], + "tsx/esbuild/@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.3", "", { "os": "aix", "cpu": "ppc64" }, "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg=="], "tsx/esbuild/@esbuild/android-arm": ["@esbuild/android-arm@0.27.3", "", { "os": "android", "cpu": "arm" }, "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA=="], @@ -3150,20 +3789,70 @@ "workbox-build/ajv/json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + "@electron/node-gyp/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "@typescript-eslint/typescript-estree/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + "app-builder-lib/@electron/get/fs-extra/jsonfile": ["jsonfile@4.0.0", "", { "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg=="], + + "app-builder-lib/@electron/get/fs-extra/universalify": ["universalify@0.1.2", "", {}, "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="], + + "app-builder-lib/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], + + "cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + + "cli-truncate/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + "glob/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "mdast-util-mdx-jsx/parse-entities/is-alphanumerical/is-alphabetical": ["is-alphabetical@2.0.1", "", {}, "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ=="], + "node-gyp/make-fetch-happen/cacache/@npmcli/fs": ["@npmcli/fs@4.0.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q=="], + + "node-gyp/make-fetch-happen/cacache/fs-minipass": ["fs-minipass@3.0.3", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-XUBA9XClHbnJWSfBzjkm6RvPsyg3sryZt06BEQoXcF7EK/xpGaQYJgQKDJSUH5SGZ76Y7pFx1QBnXz09rU5Fbw=="], + + "node-gyp/make-fetch-happen/cacache/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], + + "node-gyp/make-fetch-happen/cacache/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + + "node-gyp/make-fetch-happen/cacache/minipass-collect": ["minipass-collect@2.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-D7V8PO9oaz7PWGLbCACuI1qEOsq7UKfLotx/C0Aet43fCUB/wfQ7DYeq2oR/svFJGYDHPr38SHATeaj/ZoKHKw=="], + + "node-gyp/make-fetch-happen/cacache/unique-filename": ["unique-filename@4.0.0", "", { "dependencies": { "unique-slug": "^5.0.0" } }, "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ=="], + + "node-gyp/make-fetch-happen/minipass-fetch/minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + "nodemon/minimatch/brace-expansion/balanced-match": ["balanced-match@4.0.4", "", {}, "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA=="], "qrcode/yargs/cliui/wrap-ansi": ["wrap-ansi@6.2.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA=="], "qrcode/yargs/find-up/locate-path": ["locate-path@5.0.0", "", { "dependencies": { "p-locate": "^4.1.0" } }, "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g=="], + "node-gyp/make-fetch-happen/cacache/glob/jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], + + "node-gyp/make-fetch-happen/cacache/glob/minimatch": ["minimatch@9.0.9", "", { "dependencies": { "brace-expansion": "^2.0.2" } }, "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg=="], + + "node-gyp/make-fetch-happen/cacache/glob/path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + + "node-gyp/make-fetch-happen/cacache/unique-filename/unique-slug": ["unique-slug@5.0.0", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg=="], + "qrcode/yargs/find-up/locate-path/p-locate": ["p-locate@4.1.0", "", { "dependencies": { "p-limit": "^2.2.0" } }, "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A=="], + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], + + "node-gyp/make-fetch-happen/cacache/glob/minimatch/brace-expansion": ["brace-expansion@2.0.2", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ=="], + "qrcode/yargs/find-up/locate-path/p-locate/p-limit": ["p-limit@2.3.0", "", { "dependencies": { "p-try": "^2.0.0" } }, "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="], + + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], + + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/strip-ansi": ["strip-ansi@7.2.0", "", { "dependencies": { "ansi-regex": "^6.2.2" } }, "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w=="], + + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi": ["wrap-ansi@8.1.0", "", { "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" } }, "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ=="], + + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], + + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/strip-ansi/ansi-regex": ["ansi-regex@6.2.2", "", {}, "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg=="], + + "node-gyp/make-fetch-happen/cacache/glob/jackspeak/@isaacs/cliui/wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], } } diff --git a/docs/TAURI_TO_ELECTRON_CUTOVER.md b/docs/TAURI_TO_ELECTRON_CUTOVER.md new file mode 100644 index 00000000..28a21b53 --- /dev/null +++ b/docs/TAURI_TO_ELECTRON_CUTOVER.md @@ -0,0 +1,349 @@ +# Tauri → Electron auto-update cutover + +> Self-contained playbook. The branch and conversation where this plan was +> designed will not be around when the cutover happens — read this file top to +> bottom and execute; do not assume prior context. + +## What this is + +OpenChamber historically shipped as a Tauri app. A parallel Electron shell was +added on branch `electron-app` (merged to `main` as part of a larger migration). +Since then, both desktop shells have been released in the same GitHub release +and each has its own auto-update channel: + +| Shell | Manifest | Update format | Secret used to sign | +|----------|-------------------|---------------------|---------------------| +| Tauri | `latest.json` | `.tar.gz` + `.sig` | `TAURI_SIGNING_PRIVATE_KEY` (minisign) | +| Electron | `latest-mac.yml` | `.zip` + `blockmap` | Developer ID codesign (APPLE_* secrets) | + +Existing Tauri installs keep their own auto-update path (`latest.json`). +Electron installs auto-update through `latest-mac.yml`. They coexist without +conflict because filenames and manifests differ. + +At some point the user wants to **stop maintaining the Tauri build** and make +the Tauri installs migrate themselves into Electron via auto-update. This +document describes how to do that in a single "transition release". + +## The core trick + +Tauri's updater downloads whatever `.tar.gz` the `latest.json` points at, +verifies the minisign signature, unpacks the contents **over** the existing +`.app` directory, and restarts. It does **not** introspect the payload — it +just replaces files. + +So: produce a `.tar.gz` of the Electron `.app`, sign it with the existing +Tauri minisign key, point `latest.json` at it. Tauri users receive the update, +their `OpenChamber.app` becomes the Electron bundle in-place, and next launch +starts Electron. Subsequent updates go through `latest-mac.yml` +(electron-updater). One-way migration, one-shot workflow change. + +## Prerequisites before running the cutover + +Check all of these before making any release: + +1. **Electron has shipped stable through its own `latest-mac.yml` path for at + least 2 releases.** Verify: + ``` + gh release list --repo btriapitsyn/openchamber + gh release view vX.Y.Z --repo btriapitsyn/openchamber \ + | grep -E 'OpenChamber-.*\.zip|latest-mac\.yml' + ``` + A user on Electron should have successfully auto-updated at least once. + If not, pause and stabilise that path first — don't stack risk. + +2. **`~/.config/openchamber/settings.json` is still the shared state path.** + Tauri `src-tauri/src/main.rs:settings_file_path` and Electron + `packages/electron/main.mjs:settingsFilePath` must both resolve to + `$HOME/.config/openchamber/settings.json`. If either has moved, data parity + breaks and this migration loses user data. Audit both paths, update the + non-migrated shell to match before proceeding. + +3. **Electron `appId` is `dev.openchamber.desktop`** (check + `packages/electron/package.json` `build.appId`). Tauri identifier is + `ai.opencode.openchamber`. These differ intentionally — it means macOS + LaunchServices will re-register after the in-place replace. That's fine but + see "Risks" below. + +4. **All GitHub secrets still valid:** `APPLE_CERTIFICATE`, + `APPLE_CERTIFICATE_PASSWORD`, `APPLE_ID`, `APPLE_PASSWORD`, `APPLE_TEAM_ID`, + `TAURI_SIGNING_PRIVATE_KEY`, `TAURI_SIGNING_PRIVATE_KEY_PASSWORD`. A + workflow_dispatch dry-run should succeed before the real tag. + +5. **`minisign` CLI is available on the macOS runner** (or installable via + brew). Used to sign the Electron tarball with the Tauri key. + +## Release workflow changes + +The file to edit: `.github/workflows/release.yml`. + +Today it has these jobs (simplified): + +``` +create-release +├── build-desktop-macos (Tauri .dmg/.tar.gz/.tar.gz.sig) +├── build-desktop-electron-macos (Electron .dmg/.zip/blockmap/latest-mac.yml) +├── publish-npm +├── combine-manifests (merges Tauri per-arch JSONs → latest.json) +├── combine-electron-manifests (merges Electron per-arch YMLs → latest-mac.yml) +└── finalize-release +``` + +### Step 1 — Remove the Tauri build + +Delete these jobs entirely: +- `build-desktop-macos` +- `combine-manifests` + +They are replaced by the repackage job (below). `finalize-release` `needs:` +list must be updated to drop both. + +### Step 2 — Add a repackage job + +Insert after `build-desktop-electron-macos`: + +```yaml +repackage-electron-as-tauri-update: + needs: [create-release, build-desktop-electron-macos] + runs-on: macos-26 + strategy: + fail-fast: false + matrix: + include: + - arch: arm64 + platform: darwin-aarch64 + - arch: x64 + platform: darwin-x86_64 + steps: + - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@v2 + - uses: actions/setup-node@v4 + with: + node-version: '20' + + # Pull the signed+notarized Electron .app that build-desktop-electron-macos + # already produced. Either re-download the dmg and mount+copy the .app, or + # (cleaner) modify build-desktop-electron-macos to upload the .app itself + # as an artifact so this job can download it. Prefer the latter — adds one + # `actions/upload-artifact@v4` step uploading `packages/electron/dist/mac-/OpenChamber.app`. + + - name: Download signed Electron .app + uses: actions/download-artifact@v4 + with: + name: electron-app-${{ matrix.arch }} + path: staged + + - name: Install minisign + run: brew install minisign + + - name: Tar and sign Electron .app as Tauri update payload + env: + TAURI_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} + TAURI_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} + VERSION: ${{ needs.create-release.outputs.version }} + run: | + set -euo pipefail + cd staged + # The tarball name convention Tauri's updater expects. Must end in + # `.app.tar.gz`. Name stays stable — Tauri updater does not care about + # the inner .app name. + TARBALL="OpenChamber.app.tar.gz" + tar -czf "$TARBALL" OpenChamber.app + + # minisign needs the private key written to a file and a non-interactive + # password via -W (or env). The key in the secret is a minisign secret + # key block (base64-ish multi-line blob). Write to a file verbatim. + echo "$TAURI_KEY" > ../tauri-signing.key + echo "$TAURI_KEY_PASSWORD" | minisign -S -s ../tauri-signing.key \ + -m "$TARBALL" -W + + # Rename per platform so the release has distinct names for arm64/x64. + mv "$TARBALL" "OpenChamber-${VERSION}-${{ matrix.platform }}.app.tar.gz" + mv "${TARBALL}.minisig" "OpenChamber-${VERSION}-${{ matrix.platform }}.app.tar.gz.sig" + + - name: Generate Tauri latest-.json + env: + VERSION: ${{ needs.create-release.outputs.version }} + REPO: ${{ github.repository }} + run: | + SIG=$(cat staged/OpenChamber-${VERSION}-${{ matrix.platform }}.app.tar.gz.sig) + TAR=OpenChamber-${VERSION}-${{ matrix.platform }}.app.tar.gz + cat > staged/latest-${{ matrix.platform }}.json <'); + +try { + process.chdir(os.homedir()); +} catch { +} + +log.initialize(); +log.transports.file.maxSize = 5 * 1024 * 1024; +log.transports.file.level = 'info'; +log.transports.console.level = isDev ? 'debug' : 'warn'; + +const LOG_MAX_AGE_MS = 7 * 24 * 60 * 60 * 1000; +try { + const logPath = log.transports.file.getFile().path; + const logDir = path.dirname(logPath); + const cutoff = Date.now() - LOG_MAX_AGE_MS; + for (const entry of fs.readdirSync(logDir)) { + const candidate = path.join(logDir, entry); + try { + const info = fs.statSync(candidate); + if (info.isFile() && info.mtimeMs < cutoff) { + fs.unlinkSync(candidate); + } + } catch { + } + } +} catch { +} + +try { + if (!app.isDefaultProtocolClient(DEEP_LINK_PROTOCOL)) { + app.setAsDefaultProtocolClient(DEEP_LINK_PROTOCOL); + } +} catch (error) { + // log.* not yet initialized at this point; fall back to console. + console.warn('[electron] failed to register deep-link protocol:', error); +} + +const readAppMetadata = () => { + const candidates = [ + path.join(__dirname, 'package.json'), + path.join(__dirname, '..', 'package.json'), + path.join(app.getAppPath?.() || '', 'package.json'), + ].filter(Boolean); + for (const candidate of candidates) { + try { + const raw = fs.readFileSync(candidate, 'utf8'); + const parsed = JSON.parse(raw); + if (parsed?.name === '@openchamber/electron' && typeof parsed.version === 'string') { + return { name: parsed.name, version: parsed.version }; + } + } catch { + } + } + return { name: '@openchamber/electron', version: app.getVersion() }; +}; + +const APP_METADATA = readAppMetadata(); +const APP_VERSION = APP_METADATA.version; + +const DEFAULT_DESKTOP_PORT = 57123; +const MIN_WINDOW_WIDTH = 800; +const MIN_WINDOW_HEIGHT = 520; +const MIN_RESTORE_WINDOW_WIDTH = 900; +const MIN_RESTORE_WINDOW_HEIGHT = 560; +const LOCAL_HOST_ID = 'local'; +const ENV_OVERRIDE_HOST_ID = '__env'; +const CHANGELOG_URL = 'https://raw.githubusercontent.com/btriapitsyn/openchamber/main/CHANGELOG.md'; +const UPDATE_METADATA_URL = 'https://github.com/btriapitsyn/openchamber/releases/latest/download/latest.json'; +const GITHUB_BUG_REPORT_URL = 'https://github.com/btriapitsyn/openchamber/issues/new?template=bug_report.yml'; +const GITHUB_FEATURE_REQUEST_URL = 'https://github.com/btriapitsyn/openchamber/issues/new?template=feature_request.yml'; +const DISCORD_INVITE_URL = 'https://discord.gg/ZYRSdnwwKA'; +const INSTALLED_APPS_CACHE_TTL_SECS = 60 * 60 * 24; +const INSTALLED_APPS_CACHE_FILE = 'discovered-apps.json'; + +const { autoUpdater } = updaterPkg; + +const state = { + serverHandle: null, + sidecarUrl: null, + localOrigin: null, + bootOutcome: null, + initScript: null, + mainWindow: null, + quitRequested: false, + quitConfirmed: false, + quitConfirmationPending: false, + quitRiskPollerStarted: false, + pendingUpdate: null, + unreachableHosts: new Set(), + windowCounter: 1, + focusedWindowIds: new Set(), + windowGeometryRevisions: new Map(), + sshStatuses: new Map(), + sshLogs: new Map(), +}; + +const QUIT_RISK_POLL_INTERVAL_MS = 5_000; +const quitRisk = { + hasActiveTunnel: false, + hasRunningScheduledTasks: false, + hasEnabledScheduledTasks: false, + runningScheduledTasksCount: 0, + enabledScheduledTasksCount: 0, +}; + +const shouldRequireQuitConfirmation = () => + quitRisk.hasActiveTunnel + || quitRisk.hasRunningScheduledTasks + || quitRisk.hasEnabledScheduledTasks; + +const quitConfirmationMessage = () => { + const reasons = []; + if (quitRisk.hasActiveTunnel) { + reasons.push('an active tunnel'); + } + if (quitRisk.runningScheduledTasksCount > 0) { + reasons.push(`${quitRisk.runningScheduledTasksCount} running scheduled task${quitRisk.runningScheduledTasksCount === 1 ? '' : 's'}`); + } + if (quitRisk.enabledScheduledTasksCount > 0) { + reasons.push(`${quitRisk.enabledScheduledTasksCount} enabled scheduled task${quitRisk.enabledScheduledTasksCount === 1 ? '' : 's'}`); + } + if (reasons.length === 0) { + return 'Background processes (sidecar, SSH sessions) will be stopped.'; + } + return `OpenChamber detected ${reasons.join(', ')}. Quitting now will stop sidecar/background processes and may interrupt pending work.`; +}; + +const performConfirmedQuit = () => { + if (state.quitConfirmed) return; + state.quitConfirmed = true; + state.quitRequested = true; + + if (state.mainWindow && !state.mainWindow.isDestroyed()) { + try { + debounceWindowStatePersist(state.mainWindow, true); + } catch { + } + } + + try { + killSidecar(); + } catch { + } + void sshManager.shutdownAll().catch(() => {}); + + // Safety net: force-exit if normal quit sequence stalls (e.g. background + // handles in electron-updater / fetch refs) after a short grace period. + const safety = setTimeout(() => { + app.exit(0); + }, 1500); + if (typeof safety?.unref === 'function') safety.unref(); + + app.quit(); +}; + +const requestQuitWithConfirmation = async () => { + if (!shouldRequireQuitConfirmation()) { + performConfirmedQuit(); + return; + } + + if (state.quitConfirmationPending) { + return; + } + state.quitConfirmationPending = true; + + const windows = BrowserWindow.getAllWindows().filter((window) => !window.isDestroyed()); + const visible = windows.find((window) => window.isVisible()); + if (!visible) { + const hidden = windows.find((window) => !window.isVisible()); + if (hidden) { + hidden.show(); + hidden.focus(); + } + } + + try { + const result = await dialog.showMessageBox({ + type: 'warning', + title: 'Quit OpenChamber?', + message: 'Quit OpenChamber?', + detail: quitConfirmationMessage(), + buttons: ['Quit', 'Cancel'], + defaultId: 1, + cancelId: 1, + }); + state.quitConfirmationPending = false; + if (result.response === 0) { + performConfirmedQuit(); + } + } catch (error) { + state.quitConfirmationPending = false; + log.warn('[electron] quit confirmation dialog failed:', error); + } +}; + +const refreshQuitRiskFlags = async () => { + const base = typeof state.sidecarUrl === 'string' ? state.sidecarUrl.trim().replace(/\/$/, '') : ''; + if (!base) return; + + const scheduledUrl = `${base}/api/openchamber/scheduled-tasks/status`; + const tunnelUrl = `${base}/api/openchamber/tunnel/status`; + + const fetchJson = async (url) => { + try { + const response = await fetch(url, { signal: AbortSignal.timeout(2_000) }); + if (!response.ok) return null; + return await response.json(); + } catch { + return null; + } + }; + + const [scheduled, tunnel] = await Promise.all([fetchJson(scheduledUrl), fetchJson(tunnelUrl)]); + + if (scheduled && typeof scheduled === 'object') { + const enabledCount = Number(scheduled.enabledScheduledTasksCount ?? 0); + const runningCount = Number(scheduled.runningScheduledTasksCount ?? 0); + quitRisk.enabledScheduledTasksCount = Number.isFinite(enabledCount) ? enabledCount : 0; + quitRisk.runningScheduledTasksCount = Number.isFinite(runningCount) ? runningCount : 0; + quitRisk.hasEnabledScheduledTasks = Boolean(scheduled.hasEnabledScheduledTasks) || quitRisk.enabledScheduledTasksCount > 0; + quitRisk.hasRunningScheduledTasks = Boolean(scheduled.hasRunningScheduledTasks) || quitRisk.runningScheduledTasksCount > 0; + } + + if (tunnel && typeof tunnel === 'object') { + quitRisk.hasActiveTunnel = Boolean(tunnel.active); + } +}; + +const startQuitRiskPoller = () => { + if (process.platform !== 'darwin') return; + if (state.quitRiskPollerStarted) return; + state.quitRiskPollerStarted = true; + + const loop = async () => { + while (!state.quitConfirmed && !state.quitRequested) { + await refreshQuitRiskFlags(); + if (state.quitConfirmed || state.quitRequested) break; + await new Promise((resolve) => { + const timer = setTimeout(resolve, QUIT_RISK_POLL_INTERVAL_MS); + if (typeof timer?.unref === 'function') timer.unref(); + }); + } + }; + void loop(); +}; + +const settingsFilePath = () => { + if (typeof process.env.OPENCHAMBER_DATA_DIR === 'string' && process.env.OPENCHAMBER_DATA_DIR.trim()) { + return path.join(process.env.OPENCHAMBER_DATA_DIR.trim(), 'settings.json'); + } + return path.join(os.homedir(), '.config', 'openchamber', 'settings.json'); +}; + +const sshManager = new ElectronSshManager({ + settingsFilePath: settingsFilePath(), + appVersion: APP_VERSION, + emit: (event, detail) => emitToAllWindows(event, detail), +}); + +const readJsonFile = (filePath) => { + try { + return JSON.parse(fs.readFileSync(filePath, 'utf8')); + } catch { + return {}; + } +}; + +const writeJsonFile = async (filePath, data) => { + await fsp.mkdir(path.dirname(filePath), { recursive: true }); + await fsp.writeFile(filePath, JSON.stringify(data, null, 2)); +}; + +const readSettingsRoot = () => { + const root = readJsonFile(settingsFilePath()); + return root && typeof root === 'object' && !Array.isArray(root) ? root : {}; +}; + +const writeSettingsRoot = async (root) => writeJsonFile(settingsFilePath(), root); + +const normalizeHostUrl = (raw) => { + const trimmed = typeof raw === 'string' ? raw.trim() : ''; + if (!trimmed) return null; + try { + const parsed = new URL(trimmed); + if (!['http:', 'https:'].includes(parsed.protocol)) return null; + parsed.hash = ''; + return parsed.toString(); + } catch { + return null; + } +}; + +const sanitizeHostUrlForStorage = (raw) => normalizeHostUrl(raw); + +const readDesktopHostsConfig = () => { + const root = readSettingsRoot(); + const hostsRaw = Array.isArray(root.desktopHosts) ? root.desktopHosts : []; + const hosts = hostsRaw + .map((entry) => { + const id = typeof entry?.id === 'string' ? entry.id.trim() : ''; + const url = sanitizeHostUrlForStorage(entry?.url); + if (!id || id === LOCAL_HOST_ID || !url) return null; + const label = typeof entry?.label === 'string' && entry.label.trim() ? entry.label.trim() : url; + return { id, label, url }; + }) + .filter(Boolean); + + return { + hosts, + defaultHostId: typeof root.desktopDefaultHostId === 'string' && root.desktopDefaultHostId.trim() + ? root.desktopDefaultHostId.trim() + : null, + initialHostChoiceCompleted: root.desktopInitialHostChoiceCompleted === true, + }; +}; + +const writeDesktopHostsConfig = async (config) => { + const root = readSettingsRoot(); + root.desktopHosts = Array.isArray(config?.hosts) + ? config.hosts + .map((entry) => { + const id = typeof entry?.id === 'string' ? entry.id.trim() : ''; + const url = sanitizeHostUrlForStorage(entry?.url); + if (!id || id === LOCAL_HOST_ID || !url) return null; + return { + id, + label: typeof entry?.label === 'string' && entry.label.trim() ? entry.label.trim() : url, + url, + }; + }) + .filter(Boolean) + : []; + root.desktopDefaultHostId = typeof config?.defaultHostId === 'string' && config.defaultHostId.trim() + ? config.defaultHostId.trim() + : null; + if (typeof config?.initialHostChoiceCompleted === 'boolean') { + root.desktopInitialHostChoiceCompleted = config.initialHostChoiceCompleted; + } + await writeSettingsRoot(root); +}; + +const readWindowState = () => { + const stateValue = readSettingsRoot().desktopWindowState; + return stateValue && typeof stateValue === 'object' ? stateValue : null; +}; + +const writeWindowState = async (browserWindow) => { + if (!browserWindow || browserWindow.isDestroyed()) return; + if (!state.mainWindow || browserWindow.id !== state.mainWindow.id) return; + + const bounds = browserWindow.getBounds(); + const root = readSettingsRoot(); + root.desktopWindowState = { + x: bounds.x, + y: bounds.y, + width: Math.max(bounds.width, MIN_WINDOW_WIDTH), + height: Math.max(bounds.height, MIN_WINDOW_HEIGHT), + maximized: browserWindow.isMaximized(), + fullscreen: browserWindow.isFullScreen(), + }; + await writeSettingsRoot(root); +}; + +const debounceWindowStatePersist = (browserWindow, immediate = false) => { + if (!browserWindow || browserWindow.isDestroyed()) return; + const key = String(browserWindow.id); + const revision = (state.windowGeometryRevisions.get(key) || 0) + 1; + state.windowGeometryRevisions.set(key, revision); + + const persist = async () => { + if (state.windowGeometryRevisions.get(key) !== revision) return; + await writeWindowState(browserWindow); + }; + + if (immediate) { + void persist(); + return; + } + + setTimeout(() => { + void persist(); + }, 300); +}; + +const buildHealthUrl = (url) => { + try { + const parsed = new URL(url); + parsed.pathname = `${parsed.pathname.replace(/\/$/, '') || ''}/health`; + return parsed.toString(); + } catch { + return null; + } +}; + +const probeHostWithTimeout = async (url, timeoutMs) => { + const healthUrl = buildHealthUrl(url); + if (!healthUrl) { + throw new Error('Invalid URL'); + } + + const started = Date.now(); + try { + const response = await fetch(healthUrl, { signal: AbortSignal.timeout(timeoutMs) }); + const status = response.status; + return { + status: status >= 200 && status < 300 ? 'ok' : (status === 401 || status === 403 ? 'auth' : 'unreachable'), + latencyMs: Date.now() - started, + }; + } catch { + return { status: 'unreachable', latencyMs: Date.now() - started }; + } +}; + +const waitForHealth = async (url, timeoutMs = 20_000, initialPollMs = 250, maxPollMs = 2000) => { + const deadline = Date.now() + timeoutMs; + let pollMs = initialPollMs; + while (Date.now() < deadline) { + try { + const response = await fetch(buildHealthUrl(url), { signal: AbortSignal.timeout(Math.min(pollMs * 4, 1500)) }); + if (response.ok) { + return true; + } + } catch { + } + await new Promise((resolve) => setTimeout(resolve, pollMs)); + pollMs = Math.min(pollMs * 2, maxPollMs); + } + return false; +}; + +const pickUnusedPort = async () => { + const net = await import('node:net'); + return await new Promise((resolve, reject) => { + const server = net.createServer(); + server.listen(0, '127.0.0.1', () => { + const address = server.address(); + const port = typeof address === 'object' && address ? address.port : 0; + server.close(() => resolve(port)); + }); + server.on('error', reject); + }); +}; + +const isPortFree = async (port) => { + if (!Number.isFinite(port) || port <= 0) return false; + const net = await import('node:net'); + return await new Promise((resolve) => { + const test = net.createServer(); + const done = (value) => { + try { test.close(); } catch {} + resolve(value); + }; + test.once('error', () => done(false)); + test.listen(port, '127.0.0.1', () => done(true)); + }); +}; + +// Return the LAN IPv4 of the interface that routes to the public internet. +// UDP "connect" is a kernel-side route lookup — no packet actually goes out — +// and it picks the same interface as a real outbound connection, which is what +// a phone on the same Wi-Fi needs to reach us. Falls back to scanning +// os.networkInterfaces() if the socket trick fails (e.g. no default route). +const detectLanIPv4Address = async () => { + const ip = await new Promise((resolve) => { + const socket = dgram.createSocket('udp4'); + const finish = (value) => { + try { socket.close(); } catch {} + resolve(value); + }; + socket.once('error', () => finish(null)); + try { + socket.connect(80, '8.8.8.8', (error) => { + if (error) return finish(null); + try { + const addr = socket.address(); + finish(addr && typeof addr.address === 'string' ? addr.address : null); + } catch { + finish(null); + } + }); + } catch { + finish(null); + } + }); + if (ip && ip !== '0.0.0.0' && !ip.startsWith('127.')) return ip; + + for (const entries of Object.values(os.networkInterfaces() || {})) { + for (const entry of entries || []) { + if (entry.family === 'IPv4' && !entry.internal && entry.address) { + return entry.address; + } + } + } + return null; +}; + +const buildLocalUrl = (port) => `http://127.0.0.1:${port}`; + +const resourceRoot = () => isDev ? path.join(__dirname, 'resources') : process.resourcesPath; +const resolveWebDistDir = () => path.join(resourceRoot(), 'web-dist'); + +const normalizeNotificationInput = (raw) => { + if (!raw || typeof raw !== 'object') return {}; + // UI IPC path wraps in { payload: {...} }; sidecar stdout path is flat. + if (raw.payload && typeof raw.payload === 'object') { + return { ...raw, ...raw.payload }; + } + return raw; +}; + +const isAnyWindowFocused = () => + BrowserWindow.getAllWindows().some( + (window) => !window.isDestroyed() && window.isFocused(), + ); + +const focusForegroundWindow = () => { + const windows = BrowserWindow.getAllWindows().filter((window) => !window.isDestroyed()); + if (windows.length === 0) return; + const target = state.mainWindow && !state.mainWindow.isDestroyed() + ? state.mainWindow + : windows.find((window) => window.isVisible()) || windows[0]; + if (target.isMinimized()) target.restore(); + if (!target.isVisible()) target.show(); + target.focus(); + if (process.platform === 'darwin') app.focus({ steal: true }); +}; + +const maybeShowNativeNotification = (rawInput) => { + const payload = normalizeNotificationInput(rawInput); + const requireHidden = Boolean(payload.requireHidden ?? payload.require_hidden); + + if (requireHidden && isAnyWindowFocused()) { + return; + } + + if (!Notification.isSupported()) { + return; + } + + const title = typeof payload.title === 'string' && payload.title.trim() + ? payload.title.trim() + : 'OpenChamber'; + const body = typeof payload.body === 'string' ? payload.body : ''; + const sessionId = typeof payload.sessionId === 'string' && payload.sessionId.trim() + ? payload.sessionId.trim() + : null; + + const notification = new Notification({ + title, + body, + silent: false, + ...(process.platform === 'darwin' ? { sound: 'Glass' } : {}), + }); + + notification.on('click', () => { + focusForegroundWindow(); + if (sessionId) { + emitToAllWindows('openchamber:open-session', { sessionId }); + } + }); + + notification.show(); +}; + +const mapUpdaterProgressEvent = (payload) => ({ + event: payload.event, + data: payload.data, +}); + +const SHELL_ENV_TIMEOUT_MS = 5_000; +let cachedShellEnv = null; +let shellEnvProbed = false; + +const isNushell = (shell) => { + const name = path.basename(shell).toLowerCase(); + return name === 'nu' || name === 'nu.exe'; +}; + +const parseShellEnv = (buf) => { + const result = {}; + for (const line of buf.toString('utf8').split('\0')) { + if (!line) continue; + const idx = line.indexOf('='); + if (idx <= 0) continue; + result[line.slice(0, idx)] = line.slice(idx + 1); + } + return result; +}; + +const probeShellEnv = (shell, mode) => { + const result = spawnSync(shell, [mode, '-c', 'env -0'], { + stdio: ['ignore', 'pipe', 'ignore'], + timeout: SHELL_ENV_TIMEOUT_MS, + windowsHide: true, + }); + if (result.error || result.status !== 0) return null; + const env = parseShellEnv(result.stdout); + return Object.keys(env).length > 0 ? env : null; +}; + +// Finder-launched apps on macOS inherit a minimal PATH (no /opt/homebrew, mise, asdf, etc.). +// Probe the user's login shell once so the sidecar sees the same PATH / tool env as `$SHELL -il`. +const loadShellEnv = () => { + if (shellEnvProbed) return cachedShellEnv; + shellEnvProbed = true; + if (process.platform === 'win32') return null; + const shell = process.env.SHELL || '/bin/sh'; + if (isNushell(shell)) return null; + cachedShellEnv = probeShellEnv(shell, '-il') || probeShellEnv(shell, '-l'); + return cachedShellEnv; +}; + +// Merge the user's login-shell env (PATH, etc.) into this process before we +// import/start the server in-process. The server and its children (opencode +// CLI, git, etc.) inherit process.env directly now — there is no sidecar +// subprocess to hand a custom env to. +const inheritUserShellEnv = () => { + const shellEnv = loadShellEnv(); + if (!shellEnv) return; + + const homeDir = os.homedir(); + const shellPathSegments = typeof shellEnv.PATH === 'string' ? shellEnv.PATH.split(':') : []; + const processPathSegments = typeof process.env.PATH === 'string' ? process.env.PATH.split(':') : []; + const pathSegments = [ + ...shellPathSegments, + '/opt/homebrew/bin', + '/usr/local/bin', + '/usr/bin', + '/bin', + '/usr/sbin', + '/sbin', + path.join(homeDir, '.opencode', 'bin'), + path.join(homeDir, '.local', 'bin'), + path.join(homeDir, '.bun', 'bin'), + path.join(homeDir, '.cargo', 'bin'), + path.join(homeDir, 'bin'), + ...processPathSegments, + ].filter(Boolean); + const uniquePath = Array.from(new Set(pathSegments)).join(':'); + + for (const [key, value] of Object.entries(shellEnv)) { + if (key === 'PATH') continue; + if (typeof process.env[key] === 'undefined') { + process.env[key] = value; + } + } + process.env.PATH = uniquePath; +}; + +const spawnLocalServer = async () => { + inheritUserShellEnv(); + + const settings = readSettingsRoot(); + const storedPort = Number.isFinite(settings.desktopLocalPort) ? settings.desktopLocalPort : null; + // When the user enables "Desktop Network Access" we bind on all interfaces + // so phones/tablets on the same Wi-Fi can reach the app. UI shows a clear + // warning and persists the flag via /api/config/settings. + const lanAccessEnabled = settings.desktopLanAccessEnabled === true; + const bindHost = lanAccessEnabled ? '0.0.0.0' : '127.0.0.1'; + + // Probe before starting the server — main() in the server module sets up a + // lot of global state before binding, and calling it twice after a listen + // failure would double-wire runtimes. Pick a known-free port in one shot. + const candidates = [storedPort, DEFAULT_DESKTOP_PORT].filter((v) => Number.isFinite(v) && v > 0); + let chosenPort = 0; + for (const candidate of candidates) { + if (await isPortFree(candidate)) { + chosenPort = candidate; + break; + } + } + if (chosenPort === 0) { + chosenPort = await pickUnusedPort(); + } + + // The server module reads ENV_DESKTOP_NOTIFY / OPENCHAMBER_DIST_DIR / + // OPENCHAMBER_RUNTIME at import time (top-level const), so these must be + // set before the first import. After this point, the same env is used by + // both the Electron main and the server running inside it. + process.env.OPENCHAMBER_HOST = bindHost; + process.env.OPENCHAMBER_DIST_DIR = resolveWebDistDir(); + process.env.OPENCHAMBER_RUNTIME = 'desktop'; + process.env.OPENCHAMBER_DESKTOP_NOTIFY = 'true'; + process.env.NO_PROXY = process.env.NO_PROXY || 'localhost,127.0.0.1'; + process.env.no_proxy = process.env.no_proxy || 'localhost,127.0.0.1'; + + const { startWebUiServer } = await import('@openchamber/web/server/index.js'); + + const handle = await startWebUiServer({ + port: chosenPort, + host: bindHost, + attachSignals: false, + exitOnShutdown: false, + onDesktopNotification: (payload) => maybeShowNativeNotification(payload), + }); + + const port = handle.getPort(); + const url = buildLocalUrl(port); + + state.serverHandle = handle; + state.sidecarUrl = url; + + const root = readSettingsRoot(); + root.desktopLocalPort = port; + await writeSettingsRoot(root); + + return url; +}; + +const killSidecar = () => { + if (state.serverHandle) { + try { + const result = state.serverHandle.stop({ exitProcess: false }); + if (result && typeof result.then === 'function') { + result.catch(() => {}); + } + } catch { + } + state.serverHandle = null; + } + state.sidecarUrl = null; +}; + +const macosMajorVersion = () => { + if (process.platform !== 'darwin') return 0; + const result = spawnSync('/usr/bin/sw_vers', ['-productVersion'], { encoding: 'utf8' }); + const raw = (result.stdout || '').trim(); + const [majorRaw, minorRaw] = raw.split('.'); + const major = Number.parseInt(majorRaw || '0', 10); + const minor = Number.parseInt(minorRaw || '0', 10); + return major === 10 ? minor : major; +}; + +const buildInitScript = (localOrigin, bootOutcome) => { + const home = JSON.stringify(os.homedir() || ''); + const local = JSON.stringify(localOrigin || ''); + const macVersion = macosMajorVersion(); + const outcome = JSON.stringify(bootOutcome ?? null); + return [ + '(function(){', + `try{window.__OPENCHAMBER_HOME__=${home};window.__OPENCHAMBER_MACOS_MAJOR__=${macVersion};window.__OPENCHAMBER_LOCAL_ORIGIN__=${local};var __oc_bo=${outcome};if(__oc_bo){window.__OPENCHAMBER_DESKTOP_BOOT_OUTCOME__=__oc_bo;}}catch(_e){}`, + '}())', + ].join(''); +}; + +const computeBootOutcome = ({ envTargetUrl, probe, config, localAvailable }) => { + if (envTargetUrl) { + const status = probe && probe.status === 'unreachable' ? 'unreachable' : 'ok'; + return { target: 'remote', status, hostId: ENV_OVERRIDE_HOST_ID, url: envTargetUrl }; + } + + const defaultId = config.defaultHostId || ''; + if (!defaultId) { + return { target: null, status: 'not-configured' }; + } + + if (defaultId === LOCAL_HOST_ID) { + return localAvailable + ? { target: 'local', status: 'ok' } + : { target: 'local', status: 'unreachable' }; + } + + const host = config.hosts.find((entry) => entry.id === defaultId); + if (!host) { + return { target: 'remote', status: 'missing', hostId: defaultId }; + } + + const status = probe && probe.status === 'unreachable' ? 'unreachable' : 'ok'; + return { target: 'remote', status, hostId: host.id, url: host.url }; +}; + +const buildStartupSplashHtml = () => { + const settings = readSettingsRoot(); + const splashBgLight = typeof settings.splashBgLight === 'string' ? settings.splashBgLight.trim() : '#f5f5f4'; + const splashFgLight = typeof settings.splashFgLight === 'string' ? settings.splashFgLight.trim() : '#1c1917'; + const splashBgDark = typeof settings.splashBgDark === 'string' ? settings.splashBgDark.trim() : '#0c0a09'; + const splashFgDark = typeof settings.splashFgDark === 'string' ? settings.splashFgDark.trim() : '#fafaf9'; + + return ` + + + + + + + +
OpenChamber
+ + `; +}; + +const isBenignNavigationAbort = (error) => { + if (!error || typeof error !== 'object') { + return false; + } + + if (error.errno === -3) { + return true; + } + + const message = typeof error.message === 'string' ? error.message : ''; + return message.includes('ERR_ABORTED') || message.includes(' (-3) loading '); +}; + +const navigateWindow = async (browserWindow, url, { allowAbort = false } = {}) => { + try { + await browserWindow.loadURL(url); + } catch (error) { + if (allowAbort && isBenignNavigationAbort(error)) { + return; + } + throw error; + } +}; + +const emitToWindow = (browserWindow, event, detail) => { + if (!browserWindow || browserWindow.isDestroyed()) return; + browserWindow.webContents.send('openchamber:emit', { event, detail }); +}; + +const emitToAllWindows = (event, detail) => { + for (const browserWindow of BrowserWindow.getAllWindows()) { + emitToWindow(browserWindow, event, detail); + } +}; + +const pendingDeepLinks = []; + +const parseDeepLink = (raw) => { + if (typeof raw !== 'string') return null; + const trimmed = raw.trim(); + if (!trimmed) return null; + try { + const url = new URL(trimmed); + if (url.protocol !== `${DEEP_LINK_PROTOCOL}:`) return null; + const type = url.hostname; + if (!type) return null; + const segments = url.pathname.split('/').filter(Boolean); + const value = segments.length > 0 + ? decodeURIComponent(segments.join('/')) + : ''; + return { type, value }; + } catch { + return null; + } +}; + +const switchToHostById = async (rawId) => { + const id = typeof rawId === 'string' ? rawId.trim() : ''; + if (!id) return; + const config = readDesktopHostsConfig(); + let targetUrl = null; + if (id === LOCAL_HOST_ID) { + targetUrl = state.sidecarUrl || state.localOrigin; + } else { + const host = config.hosts.find((entry) => entry.id === id); + if (!host) { + log.warn('[electron] deep-link host not found:', id); + return; + } + targetUrl = host.url; + } + if (!targetUrl) { + log.warn('[electron] deep-link host has no target URL:', id); + return; + } + const bootOutcome = id === LOCAL_HOST_ID + ? { target: 'local', status: 'ok' } + : { target: 'remote', status: 'ok', hostId: id, url: targetUrl }; + log.info('[electron] switching to host', { id, bootOutcome }); + await activateMainWindow(targetUrl, state.localOrigin, bootOutcome); +}; + +const dispatchDeepLink = (link) => { + if (!link) return; + log.info('[electron] dispatching deep-link', { type: link.type, valueLen: link.value?.length || 0 }); + if (link.type === 'session' && link.value) { + emitToAllWindows('openchamber:open-session', { sessionId: link.value }); + return; + } + if (link.type === 'project' && link.value) { + emitToAllWindows('openchamber:open-project', { projectPath: link.value }); + return; + } + if (link.type === 'host' && link.value) { + void switchToHostById(link.value); + return; + } + log.warn('[electron] unknown deep-link action:', link.type); +}; + +const flushPendingDeepLinks = () => { + while (pendingDeepLinks.length > 0) { + dispatchDeepLink(pendingDeepLinks.shift()); + } +}; + +const isMainWindowReadyForDeepLink = () => + Boolean(state.mainWindow) + && !state.mainWindow.isDestroyed() + && !state.mainWindow.webContents.isLoading(); + +const handleDeepLinks = (urls) => { + for (const raw of urls) { + const parsed = parseDeepLink(raw); + if (!parsed) continue; + if (isMainWindowReadyForDeepLink()) { + dispatchDeepLink(parsed); + } else { + pendingDeepLinks.push(parsed); + } + } +}; + +const extractInitialDeepLinks = () => + process.argv.filter((arg) => typeof arg === 'string' && arg.startsWith(`${DEEP_LINK_PROTOCOL}://`)); + +const dispatchDomEventToWindow = (browserWindow, event, detail) => { + if (!browserWindow || browserWindow.isDestroyed()) return; + + const eventLiteral = JSON.stringify(event); + const script = detail === undefined + ? `window.dispatchEvent(new Event(${eventLiteral}));` + : `window.dispatchEvent(new CustomEvent(${eventLiteral}, { detail: ${JSON.stringify(detail)} }));`; + + void browserWindow.webContents.executeJavaScript(script, true).catch(() => {}); +}; + +const getMenuTargetWindow = () => { + const focused = BrowserWindow.getFocusedWindow(); + if (focused && !focused.isDestroyed()) return focused; + if (state.mainWindow && !state.mainWindow.isDestroyed()) return state.mainWindow; + const [firstWindow] = BrowserWindow.getAllWindows(); + return firstWindow && !firstWindow.isDestroyed() ? firstWindow : null; +}; + +const dispatchMenuAction = (action) => { + const target = getMenuTargetWindow(); + emitToWindow(target, 'openchamber:menu-action', action); + dispatchDomEventToWindow(target, 'openchamber:menu-action', action); +}; + +const dispatchCheckForUpdates = () => { + emitToAllWindows('openchamber:check-for-updates'); + for (const browserWindow of BrowserWindow.getAllWindows()) { + dispatchDomEventToWindow(browserWindow, 'openchamber:check-for-updates'); + } +}; + +const nextWindowLabel = () => { + const value = state.windowCounter++; + return value === 1 ? 'main' : `main-${value}`; +}; + +const readThemeSource = () => { + const settings = readSettingsRoot(); + if (settings.useSystemTheme === true) return 'system'; + if (settings.themeMode === 'light' || settings.themeVariant === 'light') return 'light'; + if (settings.themeMode === 'dark' || settings.themeVariant === 'dark') return 'dark'; + return 'system'; +}; + +const createBrowserWindow = ({ label, restoreGeometry, url }) => { + const saved = restoreGeometry ? readWindowState() : null; + const useSaved = saved && typeof saved.width === 'number' && typeof saved.height === 'number'; + const desktopLocalOrigin = state.localOrigin || ''; + const desktopHome = os.homedir() || ''; + const desktopMacosMajor = String(macosMajorVersion()); + const options = { + title: 'OpenChamber', + width: useSaved ? Math.max(saved.width, MIN_RESTORE_WINDOW_WIDTH) : 1280, + height: useSaved ? Math.max(saved.height, MIN_RESTORE_WINDOW_HEIGHT) : 800, + minWidth: MIN_WINDOW_WIDTH, + minHeight: MIN_WINDOW_HEIGHT, + show: false, + backgroundColor: '#151313', + // Tauri used an overlay title bar with explicit traffic-light placement. + // Electron's hiddenInset adds its own extra inset, which leaves the controls + // visibly lower than the app header. Use a plain hidden title bar instead. + titleBarStyle: process.platform === 'darwin' ? 'hidden' : 'default', + trafficLightPosition: process.platform === 'darwin' ? { x: 16, y: 18 } : undefined, + webPreferences: { + additionalArguments: [ + `--openchamber-local-origin=${desktopLocalOrigin}`, + `--openchamber-home=${desktopHome}`, + `--openchamber-macos-major=${desktopMacosMajor}`, + `--openchamber-boot-outcome=${JSON.stringify(state.bootOutcome || null)}`, + ], + preload: isDev ? path.join(__dirname, 'preload.mjs') : path.join(app.getAppPath(), 'preload.mjs'), + backgroundThrottling: true, + contextIsolation: true, + nodeIntegration: false, + // sandbox must stay off: the preload uses contextBridge + ipcRenderer + // from Electron's Node layer. contextIsolation + nodeIntegration:false + // keep the renderer world walled off from Node. Do NOT flip to true — + // the preload would fail to load and __TAURI__ would go undefined. + sandbox: false, + }, + }; + + const browserWindow = new BrowserWindow(options); + browserWindow.__ocLabel = label || nextWindowLabel(); + + if (useSaved && Number.isFinite(saved.x) && Number.isFinite(saved.y)) { + browserWindow.setPosition(saved.x, saved.y); + } + + if (useSaved && saved.maximized) { + browserWindow.maximize(); + } + + browserWindow.on('focus', () => { + state.focusedWindowIds.add(browserWindow.id); + }); + browserWindow.on('blur', () => { + state.focusedWindowIds.delete(browserWindow.id); + }); + browserWindow.on('resize', () => { + emitToWindow(browserWindow, 'openchamber:window-resized'); + debounceWindowStatePersist(browserWindow, false); + }); + browserWindow.on('move', () => { + debounceWindowStatePersist(browserWindow, false); + }); + browserWindow.on('close', (event) => { + if (process.platform === 'darwin' && !state.quitRequested) { + const remainingVisible = BrowserWindow.getAllWindows().filter( + (window) => !window.isDestroyed() && window.isVisible(), + ).length; + + if (remainingVisible <= 1) { + debounceWindowStatePersist(browserWindow, true); + event.preventDefault(); + browserWindow.hide(); + return; + } + } + + debounceWindowStatePersist(browserWindow, true); + }); + browserWindow.on('closed', () => { + state.focusedWindowIds.delete(browserWindow.id); + if (state.mainWindow && browserWindow.id === state.mainWindow.id) { + state.mainWindow = null; + } + if (BrowserWindow.getAllWindows().length === 0) { + killSidecar(); + if (process.platform !== 'darwin') { + app.quit(); + } + } + }); + + // Any navigation target that isn't our own UI (local server / configured + // desktop hosts) should open in the user's default browser, not spawn + // another Electron window loading arbitrary web content. + const isAllowedNavigationUrl = (raw) => { + try { + const url = new URL(raw); + if (url.protocol === 'file:' || url.protocol === 'about:' || url.protocol === 'devtools:') return true; + if (url.protocol !== 'http:' && url.protocol !== 'https:') return false; + const hostname = url.hostname; + if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') return true; + if (state.localOrigin) { + try { + if (new URL(state.localOrigin).origin === url.origin) return true; + } catch { + } + } + const hosts = readDesktopHostsConfig()?.hosts || []; + for (const entry of hosts) { + if (typeof entry?.url !== 'string') continue; + try { + if (new URL(entry.url).origin === url.origin) return true; + } catch { + } + } + return false; + } catch { + return false; + } + }; + + browserWindow.webContents.setWindowOpenHandler(({ url }) => { + if (isAllowedNavigationUrl(url)) { + return { action: 'allow' }; + } + void shell.openExternal(url).catch(() => {}); + return { action: 'deny' }; + }); + + browserWindow.webContents.on('will-navigate', (event, url) => { + if (isAllowedNavigationUrl(url)) return; + event.preventDefault(); + void shell.openExternal(url).catch(() => {}); + }); + + browserWindow.webContents.setZoomFactor(1); + browserWindow.webContents.on('zoom-changed', () => { + browserWindow.webContents.setZoomFactor(1); + }); + + browserWindow.webContents.on('dom-ready', () => { + if (state.initScript) { + void browserWindow.webContents.executeJavaScript(state.initScript).catch(() => {}); + } + }); + + browserWindow.webContents.on('did-finish-load', () => { + browserWindow.webContents.setZoomFactor(1); + if (state.mainWindow && browserWindow.id === state.mainWindow.id && pendingDeepLinks.length > 0) { + const timer = setTimeout(flushPendingDeepLinks, 400); + if (typeof timer?.unref === 'function') timer.unref(); + } + }); + + browserWindow.once('ready-to-show', () => { + browserWindow.show(); + browserWindow.focus(); + }); + + if (url) { + void navigateWindow(browserWindow, url); + } else { + void navigateWindow( + browserWindow, + `data:text/html;charset=utf-8,${encodeURIComponent(buildStartupSplashHtml())}`, + { allowAbort: true }, + ); + } + + return browserWindow; +}; + +const activateMainWindow = async (url, localOrigin, bootOutcome) => { + state.localOrigin = localOrigin; + state.bootOutcome = bootOutcome ?? null; + state.initScript = buildInitScript(localOrigin, state.bootOutcome); + + const mainWindow = state.mainWindow; + if (mainWindow && !mainWindow.isDestroyed()) { + await navigateWindow(mainWindow, url, { allowAbort: true }); + mainWindow.show(); + mainWindow.focus(); + return mainWindow; + } + + state.mainWindow = createBrowserWindow({ + label: 'main', + restoreGeometry: true, + url, + }); + return state.mainWindow; +}; + +const createAdditionalWindow = async (url) => { + if (!state.localOrigin) { + return null; + } + const browserWindow = createBrowserWindow({ + label: nextWindowLabel(), + restoreGeometry: false, + url, + }); + return browserWindow; +}; + +const resolveInitialUrl = async () => { + const localUrl = isDev && await waitForHealth('http://127.0.0.1:3901', 5_000, 100) + ? 'http://127.0.0.1:3901' + : await spawnLocalServer(); + + const localUiUrl = isDev && await waitForHealth('http://127.0.0.1:5173', 8_000, 100) + ? 'http://127.0.0.1:5173' + : localUrl; + + state.sidecarUrl = localUrl; + const localAvailable = Boolean(localUrl); + + const localOrigin = new URL(localUiUrl).origin; + let initialUrl = localUiUrl; + let remoteProbe = null; + + const envTarget = normalizeHostUrl(process.env.OPENCHAMBER_SERVER_URL || ''); + const config = readDesktopHostsConfig(); + if (envTarget) { + initialUrl = envTarget; + } else if (config.defaultHostId && config.defaultHostId !== LOCAL_HOST_ID) { + const host = config.hosts.find((entry) => entry.id === config.defaultHostId); + if (host?.url) { + initialUrl = host.url; + } + } + + if (initialUrl !== localUiUrl) { + remoteProbe = await probeHostWithTimeout(initialUrl, 2_000); + if (remoteProbe.status === 'unreachable') { + remoteProbe = await probeHostWithTimeout(initialUrl, 10_000); + } + if (remoteProbe.status === 'unreachable') { + state.unreachableHosts.add(initialUrl); + initialUrl = localUiUrl; + } + } + + const bootOutcome = computeBootOutcome({ + envTargetUrl: envTarget || null, + probe: remoteProbe, + config, + localAvailable, + }); + + return { initialUrl, localOrigin, localUiUrl, bootOutcome }; +}; + +const compareSemver = (left, right) => { + const a = String(left || '').replace(/^v/, '').split('.').map((value) => Number.parseInt(value || '0', 10)); + const b = String(right || '').replace(/^v/, '').split('.').map((value) => Number.parseInt(value || '0', 10)); + const length = Math.max(a.length, b.length); + for (let index = 0; index < length; index += 1) { + const diff = (a[index] || 0) - (b[index] || 0); + if (diff !== 0) return diff; + } + return 0; +}; + +const parseGithubRepo = () => { + return { owner: 'btriapitsyn', repo: 'openchamber' }; +}; + +const setupAutoUpdater = () => { + if (!app.isPackaged) { + return; + } + autoUpdater.autoDownload = false; + autoUpdater.autoInstallOnAppQuit = false; + autoUpdater.allowPrerelease = false; + autoUpdater.fullChangelog = true; + autoUpdater.disableWebInstaller = false; + autoUpdater.logger = log; + + const { owner, repo } = parseGithubRepo(); + autoUpdater.setFeedURL({ + provider: 'github', + owner, + repo, + }); + + autoUpdater.on('download-progress', (progress) => { + emitToAllWindows('openchamber:update-progress', mapUpdaterProgressEvent({ + event: 'Progress', + data: { + chunkLength: Math.max(0, Math.round(progress.bytesPerSecond || 0)), + downloaded: Math.round(progress.transferred || 0), + total: Math.round(progress.total || 0), + }, + })); + }); +}; + +const parseRelevantChangelogNotes = async (fromVersion, toVersion) => { + try { + const response = await fetch(CHANGELOG_URL, { signal: AbortSignal.timeout(10_000) }); + if (!response.ok) return null; + const changelog = await response.text(); + const sections = changelog.split(/^##\s+\[/m).slice(1); + const relevant = []; + for (const section of sections) { + const version = section.split(']')[0]; + if (compareSemver(version, fromVersion) > 0 && compareSemver(version, toVersion) <= 0) { + relevant.push(`## [${section}`.trim()); + } + } + return relevant.length > 0 ? relevant.join('\n\n') : null; + } catch { + return null; + } +}; + +const buildInstalledAppsCachePath = () => path.join(path.dirname(settingsFilePath()), INSTALLED_APPS_CACHE_FILE); + +// Async variants. sips + mdfind via spawnSync blocked the Electron main event +// loop for 2-3s on boot (22 OPEN_IN_APPS × ~200 ms each). Use execFile promises +// so each child-process wait yields to the loop and the UI stays responsive. +const pathExists = async (candidate) => { + try { + await fsp.access(candidate); + return true; + } catch { + return false; + } +}; + +const resolveAppBundlePath = async (appName) => { + if (process.platform !== 'darwin') return null; + const bundleName = appName.endsWith('.app') ? appName : `${appName}.app`; + const candidates = [ + `/Applications/${bundleName}`, + `/System/Applications/${bundleName}`, + `/System/Applications/Utilities/${bundleName}`, + path.join(os.homedir(), 'Applications', bundleName), + ]; + for (const candidate of candidates) { + if (await pathExists(candidate)) return candidate; + } + try { + const { stdout } = await execFileAsync('mdfind', ['-name', bundleName], { encoding: 'utf8' }); + const first = (stdout || '').split('\n').map((line) => line.trim()).find(Boolean); + return first || null; + } catch { + return null; + } +}; + +const isAppBundleInstalled = async (appName) => Boolean(await resolveAppBundlePath(appName)); + +const iconToDataUrl = async (iconPath, appName) => { + if (!iconPath || !(await pathExists(iconPath))) return null; + const safeName = String(appName || 'app').replace(/[^a-z0-9]/gi, '_'); + const tempPath = path.join(os.tmpdir(), `openchamber-icon-${safeName}-${Date.now()}.png`); + try { + await execFileAsync('sips', ['-s', 'format', 'png', '-Z', '32', iconPath, '--out', tempPath], { stdio: 'ignore' }); + } catch { + return null; + } + if (!(await pathExists(tempPath))) return null; + try { + const bytes = await fsp.readFile(tempPath); + return `data:image/png;base64,${bytes.toString('base64')}`; + } finally { + await fsp.rm(tempPath, { force: true }).catch(() => {}); + } +}; + +const resolveAppIconPath = async (appPath) => { + if (!appPath || !(await pathExists(appPath))) return null; + const resourcesPath = path.join(appPath, 'Contents', 'Resources'); + if (!(await pathExists(resourcesPath))) return null; + let entries; + try { + entries = await fsp.readdir(resourcesPath); + } catch { + return null; + } + const icon = entries.find((entry) => entry.toLowerCase().endsWith('.icns')); + return icon ? path.join(resourcesPath, icon) : null; +}; + +const buildInstalledApps = async (apps) => { + const seen = new Set(); + const names = apps + .map((raw) => String(raw || '').trim()) + .filter((raw) => raw && !seen.has(raw) && seen.add(raw)); + const results = []; + for (const name of names) { + const appPath = await resolveAppBundlePath(name); + if (!appPath) continue; + const iconDataUrl = await iconToDataUrl(await resolveAppIconPath(appPath), name); + results.push({ name, iconDataUrl }); + } + return results; +}; + +const parseSshConfigImports = () => { + const sshConfigPath = path.join(os.homedir(), '.ssh', 'config'); + if (!fs.existsSync(sshConfigPath)) return []; + const lines = fs.readFileSync(sshConfigPath, 'utf8').split(/\r?\n/); + const results = []; + for (const line of lines) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith('#') || !trimmed.toLowerCase().startsWith('host ')) { + continue; + } + const hosts = trimmed.slice(5).trim().split(/\s+/).filter(Boolean); + for (const host of hosts) { + results.push({ + host, + pattern: /[*?]/.test(host), + source: sshConfigPath, + sshCommand: `ssh ${host}`, + }); + } + } + return results; +}; + +const readDesktopSshInstances = () => { + const root = readSettingsRoot(); + return { instances: Array.isArray(root.desktopSshInstances) ? root.desktopSshInstances : [] }; +}; + +const writeDesktopSshInstances = async (config) => { + const root = readSettingsRoot(); + root.desktopSshInstances = Array.isArray(config?.instances) ? config.instances : []; + await writeSettingsRoot(root); + return { instances: root.desktopSshInstances }; +}; + +const updateHostUrlForSshInstance = async (id, label, localUrl) => { + const config = readDesktopHostsConfig(); + const nextHosts = config.hosts.filter((entry) => entry.id !== id); + nextHosts.push({ id, label, url: localUrl }); + await writeDesktopHostsConfig({ hosts: nextHosts, defaultHostId: config.defaultHostId }); +}; + +const JETBRAINS_APP_IDS = new Set([ + 'pycharm', + 'intellij', + 'webstorm', + 'phpstorm', + 'rider', + 'rustrover', + 'android-studio', +]); + +const CLI_BY_APP_ID = { + vscode: 'code', + cursor: 'cursor', + vscodium: 'codium', + windsurf: 'windsurf', + zed: 'zed', +}; + +const buildOpenProjectSpecs = ({ projectPath, appId, appName }) => { + if (appId === 'finder') { + return [{ program: 'open', args: [projectPath] }]; + } + + if (appId === 'terminal' || appId === 'iterm2' || appId === 'ghostty') { + return [{ program: 'open', args: ['-a', appName, projectPath] }]; + } + + const specs = []; + + const cli = CLI_BY_APP_ID[appId]; + if (cli) { + specs.push({ program: cli, args: ['-n', projectPath] }); + } + + if (JETBRAINS_APP_IDS.has(appId)) { + specs.push({ program: 'open', args: ['-na', appName, '--args', projectPath] }); + } + + specs.push({ program: 'open', args: ['-a', appName, projectPath] }); + return specs; +}; + +const buildOpenFileSpecs = ({ filePath, appId, appName }) => { + if (appId === 'finder') { + return [{ program: 'open', args: ['-R', filePath] }]; + } + + const parentDir = path.dirname(filePath); + if (appId === 'terminal' || appId === 'iterm2' || appId === 'ghostty') { + return [{ program: 'open', args: ['-a', appName, parentDir] }]; + } + + const specs = []; + + const cli = CLI_BY_APP_ID[appId]; + if (cli) { + specs.push({ program: cli, args: [filePath] }); + } + + specs.push({ program: 'open', args: ['-a', appName, filePath] }); + return specs; +}; + +const runSpecChain = (specs, appName) => { + const failures = []; + for (const spec of specs) { + const result = spawnSync(spec.program, spec.args, { stdio: 'ignore' }); + if (result.error) { + failures.push(`${spec.program}: ${result.error.message}`); + continue; + } + if (result.status === 0) { + return; + } + failures.push(`${spec.program} exited ${result.status}`); + } + throw new Error(`Failed to open in ${appName}: ${failures.join('; ')}`); +}; + +const handleInvoke = async (browserWindow, command, args = {}) => { + switch (command) { + case 'desktop_start_window_drag': + return null; + + case 'desktop_is_window_fullscreen': + return Boolean(browserWindow?.isFullScreen()); + + case 'desktop_set_window_title': + if (browserWindow && typeof args.title === 'string') { + browserWindow.setTitle(args.title); + } + return null; + + case 'desktop_get_app_version': + return APP_VERSION; + + case 'desktop_save_markdown_file': { + const defaultPath = typeof args.defaultFileName === 'string' ? args.defaultFileName.trim() : ''; + if (!defaultPath) { + throw new Error('Default file name is required'); + } + + const content = typeof args.content === 'string' ? args.content : ''; + const result = await dialog.showSaveDialog(browserWindow || undefined, { + defaultPath, + filters: [{ name: 'Markdown', extensions: ['md'] }], + }); + if (result.canceled || !result.filePath) { + return null; + } + + await fsp.writeFile(result.filePath, content, 'utf8'); + return result.filePath; + } + + case 'desktop_read_file': { + const rawPath = typeof args.path === 'string' ? args.path : ''; + if (!rawPath) throw new Error('Path is required'); + // Defense in depth behind the IPC origin gate: even our own UI (or a + // prompt-injected agent) can't read credential stores. Resolve the + // path, require it under $HOME or tmpdir, and refuse known secret dirs + // / dotfiles commonly holding keys. + const filePath = path.resolve(rawPath); + const home = os.homedir() || ''; + const tmp = os.tmpdir() || ''; + const underHome = home && (filePath === home || filePath.startsWith(home + path.sep)); + const underTmp = tmp && (filePath === tmp || filePath.startsWith(tmp + path.sep)); + if (!underHome && !underTmp) { + throw new Error('File is outside the allowed workspace'); + } + const DENIED_SEGMENTS = ['.ssh', '.aws', '.gnupg', '.gpg', '.config/gh', '.config/openchamber/credentials']; + const relFromHome = underHome ? filePath.slice(home.length + 1) : ''; + const relNormalized = relFromHome.split(path.sep).join('/'); + if (DENIED_SEGMENTS.some((segment) => relNormalized === segment || relNormalized.startsWith(`${segment}/`))) { + throw new Error('Access to this path is not allowed'); + } + const basename = path.basename(filePath).toLowerCase(); + if (basename === '.env' || basename.startsWith('.env.') || basename.endsWith('.pem') || basename.endsWith('.key')) { + throw new Error('Access to this path is not allowed'); + } + const stats = await fsp.stat(filePath); + if (stats.size > 50 * 1024 * 1024) { + throw new Error('File is too large. Maximum size is 50MB.'); + } + const bytes = await fsp.readFile(filePath); + const ext = path.extname(filePath).toLowerCase(); + const mime = ({ + '.png': 'image/png', + '.jpg': 'image/jpeg', + '.jpeg': 'image/jpeg', + '.gif': 'image/gif', + '.webp': 'image/webp', + '.svg': 'image/svg+xml', + '.bmp': 'image/bmp', + '.ico': 'image/x-icon', + '.pdf': 'application/pdf', + '.txt': 'text/plain', + '.md': 'text/markdown', + '.json': 'application/json', + '.js': 'text/javascript', + '.ts': 'text/typescript', + '.tsx': 'text/typescript-jsx', + '.jsx': 'text/javascript-jsx', + '.html': 'text/html', + '.css': 'text/css', + '.py': 'text/x-python', + })[ext] || 'application/octet-stream'; + return { mime, base64: bytes.toString('base64'), size: bytes.length }; + } + + case 'desktop_notify': + maybeShowNativeNotification(args); + return null; + + case 'desktop_clear_cache': + await session.defaultSession.clearStorageData(); + for (const browserWindow of BrowserWindow.getAllWindows()) { + browserWindow.webContents.reload(); + } + return null; + + case 'desktop_open_path': { + const targetPath = typeof args.path === 'string' ? args.path.trim() : ''; + const appName = typeof args.app === 'string' ? args.app.trim() : ''; + if (!targetPath) throw new Error('Path is required'); + if (process.platform === 'darwin') { + const openArgs = appName ? ['-a', appName, targetPath] : [targetPath]; + spawn('open', openArgs, { detached: true, stdio: 'ignore' }).unref(); + return null; + } + await shell.openPath(targetPath); + return null; + } + + case 'desktop_reveal_path': { + const targetPath = typeof args.path === 'string' ? args.path.trim() : ''; + if (!targetPath) { + throw new Error('Path is required'); + } + + const stats = await fsp.stat(targetPath).catch(() => null); + if (stats?.isDirectory()) { + await shell.openPath(targetPath); + return null; + } + + shell.showItemInFolder(targetPath); + return null; + } + + case 'desktop_open_in_app': { + if (process.platform !== 'darwin') { + throw new Error('desktop_open_in_app is only supported on macOS'); + } + const projectPath = typeof args.projectPath === 'string' ? args.projectPath.trim() : ''; + const appId = typeof args.appId === 'string' ? args.appId.trim().toLowerCase() : ''; + const appName = typeof args.appName === 'string' ? args.appName.trim() : ''; + if (!projectPath || !appId || !appName) { + throw new Error('Project path, app id, and app name are required'); + } + runSpecChain(buildOpenProjectSpecs({ projectPath, appId, appName }), appName); + return null; + } + + case 'desktop_open_file_in_app': { + if (process.platform !== 'darwin') { + throw new Error('desktop_open_file_in_app is only supported on macOS'); + } + const filePath = typeof args.filePath === 'string' ? args.filePath.trim() : ''; + const appId = typeof args.appId === 'string' ? args.appId.trim().toLowerCase() : ''; + const appName = typeof args.appName === 'string' ? args.appName.trim() : ''; + if (!filePath || !appId || !appName) { + throw new Error('File path, app id, and app name are required'); + } + runSpecChain(buildOpenFileSpecs({ filePath, appId, appName }), appName); + return null; + } + + case 'desktop_filter_installed_apps': { + if (process.platform !== 'darwin') { + throw new Error('desktop_filter_installed_apps is only supported on macOS'); + } + if (!Array.isArray(args.apps)) return []; + const results = await Promise.all( + args.apps.map(async (appName) => (await isAppBundleInstalled(String(appName))) ? String(appName) : null) + ); + return results.filter(Boolean); + } + + case 'desktop_fetch_app_icons': { + if (process.platform !== 'darwin') { + throw new Error('desktop_fetch_app_icons is only supported on macOS'); + } + const names = Array.isArray(args.apps) ? args.apps : []; + const results = []; + for (const name of names) { + const appPath = await resolveAppBundlePath(String(name)); + if (!appPath) continue; + const dataUrl = await iconToDataUrl(await resolveAppIconPath(appPath), String(name)); + if (dataUrl) results.push({ app: String(name), dataUrl }); + } + return results; + } + + case 'desktop_get_installed_apps': { + if (process.platform !== 'darwin') { + throw new Error('desktop_get_installed_apps is only supported on macOS'); + } + const cachePath = buildInstalledAppsCachePath(); + const now = Math.floor(Date.now() / 1000); + let cache = null; + try { + cache = JSON.parse(await fsp.readFile(cachePath, 'utf8')); + } catch { + } + const cachedApps = Array.isArray(cache?.apps) ? cache.apps : []; + const hasCache = Boolean(cache); + const isCacheStale = !cache || (now - Number(cache.updatedAt || 0)) > INSTALLED_APPS_CACHE_TTL_SECS; + const refresh = async () => { + const apps = await buildInstalledApps(Array.isArray(args.apps) ? args.apps : []); + await fsp.mkdir(path.dirname(cachePath), { recursive: true }); + await fsp.writeFile(cachePath, JSON.stringify({ updatedAt: now, apps }, null, 2)); + emitToAllWindows('openchamber:installed-apps-updated', apps); + }; + if (!hasCache || isCacheStale || args.force === true) { + void refresh(); + } + return { apps: cachedApps, hasCache, isCacheStale }; + } + + case 'desktop_hosts_get': + return readDesktopHostsConfig(); + + case 'desktop_hosts_set': { + await writeDesktopHostsConfig(args.input || args.config || {}); + const updatedConfig = readDesktopHostsConfig(); + const envTarget = normalizeHostUrl(process.env.OPENCHAMBER_SERVER_URL || ''); + state.bootOutcome = computeBootOutcome({ + envTargetUrl: envTarget || null, + probe: null, + config: updatedConfig, + localAvailable: Boolean(state.sidecarUrl || state.localOrigin), + }); + state.initScript = buildInitScript(state.localOrigin, state.bootOutcome); + log.info('[electron] hosts config updated, recomputed bootOutcome', state.bootOutcome); + return null; + } + + case 'desktop_host_probe': + return probeHostWithTimeout(String(args.url || ''), 2_000); + + case 'desktop_set_window_theme': { + const mode = typeof args.themeMode === 'string' ? args.themeMode : ''; + const variant = typeof args.themeVariant === 'string' ? args.themeVariant : ''; + nativeTheme.themeSource = mode === 'dark' || variant === 'dark' + ? 'dark' + : (mode === 'light' || variant === 'light' ? 'light' : 'system'); + return null; + } + + case 'desktop_set_vibrancy': { + // Vibrancy (macOS blur) is not supported in the Electron shell — the + // Tauri build used NSVisualEffectView via Tauri plugin, Electron has + // no equivalent for our titleBarStyle:'hidden' setup. Persist the + // disabled state so settings UI reflects it; args.enabled is ignored. + const root = readSettingsRoot(); + root.desktopVibrancy = false; + await writeSettingsRoot(root); + return { enabled: false, requiresRestart: false }; + } + + case 'desktop_check_for_updates': { + const currentVersion = APP_VERSION; + let payload = null; + try { + const response = await fetch(UPDATE_METADATA_URL, { signal: AbortSignal.timeout(10_000) }); + payload = await response.json(); + } catch { + } + + let updateResult = null; + try { + updateResult = await autoUpdater.checkForUpdates(); + } catch { + } + + const updateInfo = updateResult?.updateInfo; + const nextVersion = + (typeof updateInfo?.version === 'string' && updateInfo.version) || + (typeof payload?.version === 'string' && payload.version) || + currentVersion; + const available = compareSemver(nextVersion, currentVersion) > 0; + const body = + (typeof payload?.notes === 'string' && payload.notes.trim() ? payload.notes : null) || + (typeof updateInfo?.releaseNotes === 'string' && updateInfo.releaseNotes.trim() ? updateInfo.releaseNotes : null) || + await parseRelevantChangelogNotes(currentVersion, nextVersion); + state.pendingUpdate = available ? { version: nextVersion, metadata: payload, electronUpdate: updateResult } : null; + return { + available, + currentVersion, + version: available ? nextVersion : null, + body: body || null, + date: + (typeof updateInfo?.releaseDate === 'string' && updateInfo.releaseDate) || + (typeof payload?.pub_date === 'string' ? payload.pub_date : null), + }; + } + + case 'desktop_download_and_install_update': + if (!state.pendingUpdate) { + throw new Error('No pending update'); + } + emitToAllWindows('openchamber:update-progress', mapUpdaterProgressEvent({ + event: 'Started', + data: { + contentLength: null, + }, + })); + if (!state.pendingUpdate.electronUpdate) { + throw new Error('Electron updater metadata is not available for this build'); + } + await autoUpdater.downloadUpdate(); + state.pendingUpdate.downloaded = true; + emitToAllWindows('openchamber:update-progress', mapUpdaterProgressEvent({ + event: 'Finished', + data: {}, + })); + return null; + + case 'desktop_restart': + if (state.pendingUpdate?.downloaded && app.isPackaged) { + autoUpdater.quitAndInstall(false, true); + return null; + } + app.relaunch(); + app.exit(0); + return null; + + case 'desktop_get_lan_address': + return await detectLanIPv4Address(); + + case 'desktop_new_window': { + const config = readDesktopHostsConfig(); + const localUiUrl = state.sidecarUrl || state.localOrigin; + let targetUrl = localUiUrl; + if (config.defaultHostId && config.defaultHostId !== LOCAL_HOST_ID) { + const host = config.hosts.find((entry) => entry.id === config.defaultHostId); + if (host?.url && !state.unreachableHosts.has(host.url)) { + targetUrl = host.url; + } + } + await createAdditionalWindow(targetUrl); + return null; + } + + case 'desktop_new_window_at_url': { + const targetUrl = normalizeHostUrl(String(args.url || '')); + if (!targetUrl) { + throw new Error('Invalid URL'); + } + await createAdditionalWindow(targetUrl); + return null; + } + + case 'desktop_ssh_instances_get': + return sshManager.readInstances(); + + case 'desktop_ssh_instances_set': + await sshManager.setInstances(args.config || {}); + return null; + + case 'desktop_ssh_import_hosts': + return await sshManager.importHosts(); + + case 'desktop_ssh_connect': { + const id = String(args.id || '').trim(); + await sshManager.connect(id); + return null; + } + + case 'desktop_ssh_disconnect': { + const id = String(args.id || '').trim(); + await sshManager.disconnect(id); + return null; + } + + case 'desktop_ssh_status': { + const id = String(args.id || '').trim(); + return await sshManager.statusesWithDefaults(id || undefined); + } + + case 'desktop_ssh_logs': + return sshManager.logsForInstance(String(args.id || '').trim(), Number(args.limit) || 200); + + case 'desktop_ssh_logs_clear': + sshManager.clearLogsForInstance(String(args.id || '').trim()); + return null; + + default: + throw new Error(`Unknown desktop command: ${command}`); + } +}; + +const buildMacMenu = () => { + const dispatchAction = (action) => dispatchMenuAction(action); + const handleCopyAction = () => { + BrowserWindow.getFocusedWindow()?.webContents.copy(); + dispatchAction('copy'); + }; + + return Menu.buildFromTemplate([ + { + label: app.name, + submenu: [ + { role: 'about' }, + { + label: 'Check for Updates', + click: () => dispatchCheckForUpdates(), + }, + { type: 'separator' }, + { label: 'Settings', accelerator: 'Cmd+,', click: () => dispatchAction('settings') }, + { label: 'Command Palette', accelerator: 'Cmd+K', click: () => dispatchAction('command-palette') }, + { label: 'Quick Open…', accelerator: 'Cmd+P', click: () => dispatchAction('quick-open') }, + { type: 'separator' }, + { role: 'services' }, + { type: 'separator' }, + { role: 'hide' }, + { role: 'hideOthers' }, + { type: 'separator' }, + { role: 'quit' }, + ], + }, + { + label: 'File', + submenu: [ + { label: 'New Window', accelerator: 'Cmd+Shift+Alt+N', click: () => void handleInvoke(null, 'desktop_new_window') }, + { type: 'separator' }, + { label: 'New Session', accelerator: 'Cmd+N', click: () => dispatchAction('new-session') }, + { label: 'New Worktree', accelerator: 'Cmd+Shift+N', click: () => dispatchAction('new-worktree-session') }, + { type: 'separator' }, + { label: 'Add Workspace', click: () => dispatchAction('change-workspace') }, + { type: 'separator' }, + { role: 'close' }, + ], + }, + { + label: 'Edit', + submenu: [ + { role: 'undo' }, + { role: 'redo' }, + { type: 'separator' }, + { role: 'cut' }, + { label: 'Copy', accelerator: 'Cmd+C', click: () => handleCopyAction() }, + { role: 'paste' }, + { role: 'selectAll' }, + ], + }, + { + label: 'View', + submenu: [ + { label: 'Git', accelerator: 'Cmd+G', click: () => dispatchAction('open-git-tab') }, + { label: 'Diff', accelerator: 'Cmd+E', click: () => dispatchAction('open-diff-tab') }, + { label: 'Files', click: () => dispatchAction('open-files-tab') }, + { label: 'Terminal', accelerator: 'Cmd+T', click: () => dispatchAction('open-terminal-tab') }, + { type: 'separator' }, + { label: 'Light Theme', click: () => dispatchAction('theme-light') }, + { label: 'Dark Theme', click: () => dispatchAction('theme-dark') }, + { label: 'System Theme', click: () => dispatchAction('theme-system') }, + { type: 'separator' }, + { label: 'Toggle Session Sidebar', accelerator: 'Cmd+L', click: () => dispatchAction('toggle-sidebar') }, + { label: 'Toggle Memory Debug', accelerator: 'Cmd+Shift+D', click: () => dispatchAction('toggle-memory-debug') }, + { type: 'separator' }, + { role: 'togglefullscreen' }, + ], + }, + { + label: 'Window', + submenu: [ + { role: 'minimize' }, + { role: 'zoom' }, + { type: 'separator' }, + { role: 'close' }, + ], + }, + { + label: 'Help', + submenu: [ + { label: 'Keyboard Shortcuts', accelerator: 'Cmd+.', click: () => dispatchAction('help-dialog') }, + { label: 'Show Diagnostics', accelerator: 'Cmd+Shift+L', click: () => dispatchAction('download-logs') }, + { type: 'separator' }, + { label: 'Clear Cache', click: () => void handleInvoke(null, 'desktop_clear_cache') }, + { type: 'separator' }, + { label: 'Report a Bug', click: () => shell.openExternal(GITHUB_BUG_REPORT_URL) }, + { label: 'Request a Feature', click: () => shell.openExternal(GITHUB_FEATURE_REQUEST_URL) }, + { type: 'separator' }, + { label: 'Join Discord', click: () => shell.openExternal(DISCORD_INVITE_URL) }, + ], + }, + ]); +}; + +contextMenu({ + showInspectElement: isDev, + showSaveImageAs: true, + showCopyImage: true, + showCopyLink: true, +}); + +// All desktop_* IPC and dialog:open run with full Electron main privileges +// (fs access, shell.openPath, spawn, app.relaunch, …). The preload shim is +// injected into every webContents in the window, including remote hosts the +// user switches to via DesktopHostSwitcher. Without a gate, a malicious +// remote page could read arbitrary local files, open arbitrary apps, etc. +// +// Strategy: commands fall into two buckets by capability, not by origin. +// Window/host-switcher operations (probe a URL, open a new window, set +// title, read the hosts list) are safe for any renderer. Filesystem, +// shell.openPath, installed-app scans, app relaunch, and file dialogs +// are gated to local senders — even the user's own remote UI shouldn't +// need them, and a compromised remote can't use them either. +const isLocalSender = (webContents) => { + try { + const raw = typeof webContents?.getURL === 'function' ? webContents.getURL() : ''; + if (!raw) return false; + if (raw.startsWith('file://') || raw === 'about:blank') return true; + const url = new URL(raw); + if (url.protocol !== 'http:' && url.protocol !== 'https:') return false; + const hostname = url.hostname; + if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') return true; + if (state.localOrigin) { + try { + const allowed = new URL(state.localOrigin); + if (allowed.origin === url.origin) return true; + } catch { + } + } + return false; + } catch { + return false; + } +}; + +const COMMANDS_SAFE_FOR_REMOTE = new Set([ + 'desktop_hosts_get', + 'desktop_host_probe', + 'desktop_new_window', + 'desktop_new_window_at_url', + 'desktop_set_window_title', + 'desktop_set_window_theme', + 'desktop_is_window_fullscreen', + 'desktop_start_window_drag', + 'desktop_get_app_version', + 'desktop_get_lan_address', +]); + +ipcMain.handle('openchamber:invoke', async (event, command, args) => { + if (!isLocalSender(event.sender) && !COMMANDS_SAFE_FOR_REMOTE.has(command)) { + log.warn(`[ipc] rejected ${command} from non-local origin: ${event.sender?.getURL?.() || '(unknown)'}`); + throw new Error('IPC not available for this origin'); + } + const browserWindow = BrowserWindow.fromWebContents(event.sender); + return handleInvoke(browserWindow, command, args); +}); + +ipcMain.handle('openchamber:dialog:open', async (event, options) => { + // Native file dialogs expose absolute local paths; never grant to remote. + if (!isLocalSender(event.sender)) { + log.warn(`[ipc] rejected dialog:open from non-local origin: ${event.sender?.getURL?.() || '(unknown)'}`); + throw new Error('IPC not available for this origin'); + } + const browserWindow = BrowserWindow.fromWebContents(event.sender); + const result = await dialog.showOpenDialog(browserWindow || undefined, { + title: typeof options?.title === 'string' ? options.title : undefined, + filters: Array.isArray(options?.filters) + ? options.filters + .filter((filter) => filter && typeof filter === 'object') + .map((filter) => ({ + name: typeof filter.name === 'string' && filter.name.trim().length > 0 ? filter.name : 'Files', + extensions: Array.isArray(filter.extensions) + ? filter.extensions.filter((extension) => typeof extension === 'string' && extension.trim().length > 0) + : [], + })) + : undefined, + properties: [ + options?.directory ? 'openDirectory' : 'openFile', + options?.multiple ? 'multiSelections' : null, + 'createDirectory', + ].filter(Boolean), + }); + if (result.canceled) return null; + if (options?.multiple) return result.filePaths; + return result.filePaths[0] || null; +}); + +app.on('window-all-closed', () => { + if (process.platform === 'darwin' && !state.quitRequested) { + return; + } + + killSidecar(); + void sshManager.shutdownAll(); + if (process.platform !== 'darwin') { + app.quit(); + } +}); + +app.on('before-quit', (event) => { + if (state.quitConfirmed || process.platform !== 'darwin') { + state.quitRequested = true; + return; + } + event.preventDefault(); + void requestQuitWithConfirmation(); +}); + +app.on('second-instance', (_event, argv) => { + const urls = Array.isArray(argv) + ? argv.filter((arg) => typeof arg === 'string' && arg.startsWith(`${DEEP_LINK_PROTOCOL}://`)) + : []; + if (urls.length > 0) handleDeepLinks(urls); + focusForegroundWindow(); +}); + +app.on('open-url', (event, url) => { + event.preventDefault(); + handleDeepLinks([url]); +}); + +app.on('activate', async () => { + const windows = BrowserWindow.getAllWindows().filter((window) => !window.isDestroyed()); + if (windows.length > 0) { + const visibleWindow = windows.find((window) => window.isVisible()); + const targetWindow = visibleWindow || state.mainWindow || windows[0]; + if (!targetWindow.isVisible()) { + targetWindow.show(); + } + targetWindow.focus(); + return; + } + + if (state.localOrigin) { + const config = readDesktopHostsConfig(); + const localUiUrl = state.sidecarUrl || state.localOrigin; + const host = config.defaultHostId && config.defaultHostId !== LOCAL_HOST_ID + ? config.hosts.find((entry) => entry.id === config.defaultHostId) + : null; + const targetUrl = host?.url && !state.unreachableHosts.has(host.url) ? host.url : localUiUrl; + await createAdditionalWindow(targetUrl); + } +}); + +app.whenReady().then(async () => { + log.info('[electron] app starting', { + version: APP_VERSION, + packaged: app.isPackaged, + platform: process.platform, + arch: process.arch, + }); + nativeTheme.themeSource = readThemeSource(); + setupAutoUpdater(); + + if (process.platform === 'darwin') { + Menu.setApplicationMenu(buildMacMenu()); + } + + const initial = extractInitialDeepLinks(); + if (initial.length > 0) handleDeepLinks(initial); + + const { initialUrl, localOrigin, bootOutcome } = await resolveInitialUrl(); + await activateMainWindow(initialUrl, localOrigin, bootOutcome); + startQuitRiskPoller(); +}).catch((error) => { + log.error('[electron] startup failed:', error); + app.exit(1); +}); diff --git a/packages/electron/package.json b/packages/electron/package.json new file mode 100644 index 00000000..80d0318b --- /dev/null +++ b/packages/electron/package.json @@ -0,0 +1,89 @@ +{ + "name": "@openchamber/electron", + "version": "1.9.6", + "private": true, + "description": "Electron desktop runtime for OpenChamber", + "author": "OpenChamber", + "type": "module", + "main": "./dist-bundle/main.mjs", + "dependencies": { + "@openchamber/web": "workspace:*", + "electron-context-menu": "^4.1.2", + "electron-log": "^5.4.3", + "electron-updater": "^6.8.3" + }, + "devDependencies": { + "@electron/rebuild": "^3.7.0", + "electron": "^41.2.1", + "electron-builder": "^26.0.0" + }, + "desktopPrerequisites": [ + "Electron runtime dependencies installed via bun install", + "Bun available for sidecar compilation", + "macOS build tools installed for notarized packaging" + ], + "scripts": { + "dev": "node ./scripts/electron-dev.mjs", + "build:web-assets": "node ./scripts/build-web-assets.mjs", + "build": "bun -e \"process.exit(0)\"", + "bundle:main": "bun ./scripts/bundle-main.mjs", + "rebuild:native": "node ./scripts/rebuild-native.mjs", + "package": "bun run build:web-assets && bun run bundle:main && bun run rebuild:native && electron-builder", + "finalize:latest-yml": "node ./scripts/finalize-latest-yml.mjs", + "type-check": "node --check ./main.mjs && node --check ./preload.mjs", + "lint": "node -e \"process.exit(0)\"" + }, + "build": { + "appId": "dev.openchamber.desktop", + "productName": "OpenChamber", + "files": [ + "dist-bundle/main.mjs", + "preload.mjs" + ], + "extraResources": [ + { + "from": "resources/web-dist", + "to": "web-dist" + } + ], + "directories": { + "buildResources": "resources/icons", + "output": "dist" + }, + "artifactName": "${productName}-${version}-${arch}.${ext}", + "npmRebuild": false, + "mac": { + "category": "public.app-category.developer-tools", + "icon": "resources/icons/icon.icns", + "hardenedRuntime": true, + "gatekeeperAssess": false, + "entitlements": "resources/entitlements.mac.plist", + "entitlementsInherit": "resources/entitlements.mac.plist", + "notarize": true, + "target": [ + "dmg", + "zip" + ] + }, + "dmg": { + "sign": true, + "title": "${productName} ${version}", + "backgroundColor": "#FFFCF0", + "iconSize": 100, + "iconTextSize": 13, + "window": { + "width": 540, + "height": 340 + }, + "contents": [ + { "x": 180, "y": 140, "type": "file" }, + { "x": 360, "y": 140, "type": "link", "path": "/Applications" } + ] + }, + "publish": { + "provider": "github", + "owner": "btriapitsyn", + "repo": "openchamber" + } + } +} diff --git a/packages/electron/preload.mjs b/packages/electron/preload.mjs new file mode 100644 index 00000000..3259ec5f --- /dev/null +++ b/packages/electron/preload.mjs @@ -0,0 +1,143 @@ +import { contextBridge, ipcRenderer } from 'electron'; + +const eventListeners = new Map(); + +const readArgValue = (name) => { + const prefix = `${name}=`; + const entry = process.argv.find((value) => typeof value === 'string' && value.startsWith(prefix)); + if (!entry) { + return ''; + } + return entry.slice(prefix.length); +}; + +const localOrigin = readArgValue('--openchamber-local-origin'); +const homeDirectory = readArgValue('--openchamber-home'); +const macosMajorRaw = readArgValue('--openchamber-macos-major'); +const macosMajor = Number.parseInt(macosMajorRaw, 10); + +// Preload re-executes on every cross-origin navigation (we run with +// sandbox:false, per-document). Two separate concerns to balance: +// - __OPENCHAMBER_ELECTRON__ is a shell-identity flag (no capability). +// Remote UIs still need it so isDesktopShell() returns true and the +// window renders with desktop affordances (DesktopHostSwitcher, +// title bar offsets, etc.). Expose unconditionally. +// - __TAURI__ is the IPC channel to the main process. Remote pages must +// not get it — otherwise any page loaded via DesktopHostSwitcher could +// read local files, open apps, relaunch, etc. Expose only on local +// pages (loopback / state.localOrigin / file:// for dev). +// Everything driven by localOrigin (home dir, macOS hints) also stays +// local-only since it leaks info about the Electron host machine. +const currentOrigin = (() => { + try { + return typeof location !== 'undefined' ? location.origin : ''; + } catch { + return ''; + } +})(); +const isLoopbackOrigin = /^https?:\/\/(localhost|127\.0\.0\.1|\[::1\])(?::\d+)?$/i.test(currentOrigin); +const isLocalPage = currentOrigin === 'null' + || isLoopbackOrigin + || (localOrigin && currentOrigin === localOrigin); + +// Remote pages need __OPENCHAMBER_LOCAL_ORIGIN__ so the HostSwitcher knows +// the URL of the Local entry (isDesktopLocalOriginActive() falls back to +// window.location.origin otherwise — wrong on remote). Low risk: the value +// is just "http://127.0.0.1:" which is not exploitable without the +// IPC channel, and CORS on the local server prevents remote-origin fetches. +if (localOrigin) { + contextBridge.exposeInMainWorld('__OPENCHAMBER_LOCAL_ORIGIN__', localOrigin); +} + +// Home directory leaks the OS username — keep local-only. Remote pages +// operate on the REMOTE server's filesystem, local home is irrelevant +// (and would be misleading if consumed as a workspace hint). +if (isLocalPage && homeDirectory) { + contextBridge.exposeInMainWorld('__OPENCHAMBER_HOME__', homeDirectory); +} + +// macOS major version drives window chrome offsets (traffic lights) — UI +// presentation only, safe to expose. +if (Number.isFinite(macosMajor) && macosMajor > 0) { + contextBridge.exposeInMainWorld('__OPENCHAMBER_MACOS_MAJOR__', macosMajor); +} + +contextBridge.exposeInMainWorld('__OPENCHAMBER_ELECTRON__', { + runtime: 'electron', +}); + +// Note: bootOutcome must stay writable from the main world's initScript so +// re-navigations (host switch via deep link) can refresh it. contextBridge- +// exposed globals are read-only, which blocks that update — rely solely on +// the main-process initScript injection (dispatched on did-finish-load). + +const addListener = (event, handler) => { + const listeners = eventListeners.get(event) || new Set(); + listeners.add(handler); + eventListeners.set(event, listeners); + + return () => { + const current = eventListeners.get(event); + if (!current) { + return; + } + current.delete(handler); + if (current.size === 0) { + eventListeners.delete(event); + } + }; +}; + +const dispatchNativeEvent = (event, detail) => { + const listeners = eventListeners.get(event); + if (listeners) { + for (const listener of listeners) { + try { + listener({ payload: detail }); + } catch (error) { + console.error(`[electron:preload] listener failed for ${event}:`, error); + } + } + } + + try { + const domEvent = detail === undefined + ? new Event(event) + : new CustomEvent(event, { detail }); + window.dispatchEvent(domEvent); + } catch (error) { + console.error(`[electron:preload] failed to dispatch DOM event ${event}:`, error); + } +}; + +// Main-process events are read-only notifications (update progress, +// window focus, etc.) — safe to deliver to any page rendered in this +// webContents. The events themselves don't grant capability. +ipcRenderer.on('openchamber:emit', (_evt, payload) => { + if (!payload || typeof payload !== 'object') { + return; + } + + const event = typeof payload.event === 'string' ? payload.event : ''; + if (!event) { + return; + } + + dispatchNativeEvent(event, payload.detail); +}); + +// __TAURI__ is exposed on all pages; the main-process gate in +// ipcMain.handle('openchamber:invoke') decides per-command what is safe +// for non-local callers (window/host-switcher ops yes, file/shell ops +// no). See COMMANDS_SAFE_FOR_REMOTE in main.mjs. +contextBridge.exposeInMainWorld('__TAURI__', { + core: { + invoke: (cmd, args) => ipcRenderer.invoke('openchamber:invoke', cmd, args || {}), + }, + dialog: { + open: (options) => ipcRenderer.invoke('openchamber:dialog:open', options || {}), + }, + event: { + listen: async (event, handler) => addListener(event, handler), + }, +}); diff --git a/packages/electron/resources/entitlements.mac.plist b/packages/electron/resources/entitlements.mac.plist new file mode 100644 index 00000000..03649c44 --- /dev/null +++ b/packages/electron/resources/entitlements.mac.plist @@ -0,0 +1,35 @@ + + + + + + com.apple.security.cs.allow-jit + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.disable-executable-page-protection + + com.apple.security.cs.disable-library-validation + + com.apple.security.cs.allow-dyld-environment-variables + + com.apple.security.device.audio-input + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + com.apple.security.inherit + + + diff --git a/packages/electron/resources/icons/app-icon.png b/packages/electron/resources/icons/app-icon.png new file mode 100644 index 00000000..c3f9a038 Binary files /dev/null and b/packages/electron/resources/icons/app-icon.png differ diff --git a/packages/electron/resources/icons/app-icon.svg b/packages/electron/resources/icons/app-icon.svg new file mode 100644 index 00000000..f271cda8 --- /dev/null +++ b/packages/electron/resources/icons/app-icon.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/electron/resources/icons/dev-icon.icns b/packages/electron/resources/icons/dev-icon.icns new file mode 100644 index 00000000..04ac8d54 Binary files /dev/null and b/packages/electron/resources/icons/dev-icon.icns differ diff --git a/packages/electron/resources/icons/dev-icon.png b/packages/electron/resources/icons/dev-icon.png new file mode 100644 index 00000000..bab0ca21 Binary files /dev/null and b/packages/electron/resources/icons/dev-icon.png differ diff --git a/packages/electron/resources/icons/icon.icns b/packages/electron/resources/icons/icon.icns new file mode 100644 index 00000000..b6def080 Binary files /dev/null and b/packages/electron/resources/icons/icon.icns differ diff --git a/packages/electron/resources/icons/icon.png b/packages/electron/resources/icons/icon.png new file mode 100644 index 00000000..b9c1331d Binary files /dev/null and b/packages/electron/resources/icons/icon.png differ diff --git a/packages/electron/scripts/build-web-assets.mjs b/packages/electron/scripts/build-web-assets.mjs new file mode 100644 index 00000000..9cca3521 --- /dev/null +++ b/packages/electron/scripts/build-web-assets.mjs @@ -0,0 +1,75 @@ +import fs from 'node:fs/promises'; +import path from 'node:path'; +import { spawnSync } from 'node:child_process'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const repoRoot = path.resolve(__dirname, '..', '..', '..'); +const webDir = path.join(repoRoot, 'packages', 'web'); +const electronDir = path.join(repoRoot, 'packages', 'electron'); + +const resourcesDir = path.join(electronDir, 'resources'); +const resourcesWebDistDir = path.join(resourcesDir, 'web-dist'); +const webDistDir = path.join(webDir, 'dist'); + +const run = (cmd, args, cwd) => { + const result = spawnSync(cmd, args, { cwd, stdio: 'inherit' }); + if (result.error) throw result.error; + if (result.status !== 0) { + throw new Error(`Command failed: ${cmd} ${args.join(' ')}`); + } +}; + +const resolveBun = () => { + if (typeof process.env.BUN === 'string' && process.env.BUN.trim()) { + return process.env.BUN.trim(); + } + const result = spawnSync('/bin/bash', ['-lc', 'command -v bun'], { encoding: 'utf8' }); + const resolved = (result.stdout || '').trim(); + return resolved || 'bun'; +}; + +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)); + +const removeDir = async (target) => { + for (let attempt = 0; attempt < 5; attempt += 1) { + try { + await fs.rm(target, { recursive: true, force: true }); + return; + } catch (error) { + if (attempt === 4) throw error; + if (!['ENOTEMPTY', 'EBUSY', 'EPERM'].includes(error?.code)) throw error; + await sleep(100 * (attempt + 1)); + } + } +}; + +const copyDir = async (src, dst) => { + await fs.mkdir(dst, { recursive: true }); + const entries = await fs.readdir(src, { withFileTypes: true }); + for (const entry of entries) { + const from = path.join(src, entry.name); + const to = path.join(dst, entry.name); + if (entry.isDirectory()) { + await copyDir(from, to); + } else { + await fs.copyFile(from, to); + } + } +}; + +const bunExe = resolveBun(); + +console.log('[electron] building web UI dist...'); +run(bunExe, ['run', 'build'], webDir); + +console.log('[electron] staging packaged resources...'); +await fs.mkdir(resourcesDir, { recursive: true }); +const stagedWebDistDir = await fs.mkdtemp(path.join(resourcesDir, 'web-dist-staging-')); +await copyDir(webDistDir, stagedWebDistDir); +await removeDir(resourcesWebDistDir); +await fs.rename(stagedWebDistDir, resourcesWebDistDir); + +console.log(`[electron] web assets ready: ${resourcesWebDistDir}`); diff --git a/packages/electron/scripts/bundle-main.mjs b/packages/electron/scripts/bundle-main.mjs new file mode 100644 index 00000000..e8d06d36 --- /dev/null +++ b/packages/electron/scripts/bundle-main.mjs @@ -0,0 +1,44 @@ +/** + * Bundle main.mjs into a single file. Small electron-* helper deps are + * inlined; everything else — including the in-process web server + * (@openchamber/web) and native modules — stays external so it resolves + * from node_modules at runtime inside the packaged app. + * + * Why external matters: packages/web/server pulls in bun-pty, which has + * a top-level `import { dlopen } from "bun:ffi"`. If we inline it here, + * Node's ESM loader sees `bun:ffi` at package load time and crashes with + * ERR_UNSUPPORTED_ESM_URL_SCHEME before any runtime guard can skip it. + * Leaving @openchamber/web external means the conditional + * `if (isBunRuntime) await import('bun-pty')` stays dynamic and is never + * reached under Electron. + */ +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const root = path.resolve(__dirname, '..'); + +const result = await Bun.build({ + entrypoints: [path.join(root, 'main.mjs')], + outdir: path.join(root, 'dist-bundle'), + target: 'node', + format: 'esm', + external: [ + 'electron', + '@openchamber/web', + '@openchamber/web/*', + 'bun-pty', + 'node-pty', + 'better-sqlite3', + ], + minify: false, + sourcemap: 'none', + naming: '[name].mjs', +}); + +if (!result.success) { + for (const msg of result.logs) console.error(msg); + process.exit(1); +} + +console.log('[electron] main.mjs bundled -> dist-bundle/main.mjs'); diff --git a/packages/electron/scripts/electron-dev.mjs b/packages/electron/scripts/electron-dev.mjs new file mode 100644 index 00000000..46c2ce43 --- /dev/null +++ b/packages/electron/scripts/electron-dev.mjs @@ -0,0 +1,131 @@ +#!/usr/bin/env node +import { spawn } from 'node:child_process'; +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, '../../..'); +const electronDir = path.join(repoRoot, 'packages/electron'); + +function spawnProcess(command, args, options = {}) { + return spawn(command, args, { + cwd: repoRoot, + env: { ...process.env, OPENCHAMBER_ELECTRON_DEV: '1' }, + stdio: 'inherit', + detached: process.platform !== 'win32', + ...options, + }); +} + +function waitForExit(child, timeoutMs) { + return new Promise((resolve) => { + if (!child || child.exitCode !== null || child.signalCode !== null) { + resolve(); + return; + } + + const onExit = () => { + clearTimeout(timer); + resolve(); + }; + + const timer = setTimeout(() => { + child.off('exit', onExit); + resolve(); + }, timeoutMs); + + child.once('exit', onExit); + }); +} + +function signalChild(child, signal) { + if (!child || child.exitCode !== null || child.signalCode !== null) { + return; + } + + try { + if (process.platform !== 'win32') { + process.kill(-child.pid, signal); + return; + } + } catch { + } + + try { + child.kill(signal); + } catch { + } +} + +async function stopChildTree(child) { + if (!child || child.exitCode !== null || child.signalCode !== null) { + return; + } + + signalChild(child, 'SIGINT'); + await waitForExit(child, 2500); + + if (child.exitCode === null && child.signalCode === null) { + signalChild(child, 'SIGTERM'); + await waitForExit(child, 2500); + } + + if (child.exitCode === null && child.signalCode === null) { + signalChild(child, 'SIGKILL'); + await waitForExit(child, 1000); + } +} + +async function main() { + const devServer = spawnProcess('node', ['./scripts/dev-web-hmr.mjs'], { + env: { + ...process.env, + OPENCHAMBER_ELECTRON_DEV: '1', + OPENCHAMBER_HMR_UI_PORT: '5173', + OPENCHAMBER_HMR_API_PORT: '3901', + OPENCHAMBER_DISABLE_PWA_DEV: '1', + }, + }); + const electron = spawnProcess('npx', ['electron', './main.mjs'], { cwd: electronDir }); + + let cleaning = false; + const teardown = async (code) => { + if (cleaning) { + return; + } + cleaning = true; + + await Promise.all([stopChildTree(electron), stopChildTree(devServer)]); + process.exit(typeof code === 'number' ? code : 0); + }; + + const onChildExit = (label) => (code, signal) => { + if (code !== 0 || signal) { + console.warn(`[electron:dev] ${label} exited with code ${code ?? 'null'} signal ${signal ?? 'none'}.`); + } + void teardown(code ?? 1); + }; + + devServer.on('exit', onChildExit('dev server')); + electron.on('exit', onChildExit('electron')); + devServer.on('error', (error) => { + console.error('[electron:dev] failed to start dev server:', error); + void teardown(1); + }); + electron.on('error', (error) => { + console.error('[electron:dev] failed to start electron:', error); + void teardown(1); + }); + + for (const [signal, exitCode] of Object.entries({ SIGINT: 130, SIGTERM: 143, SIGQUIT: 131, SIGHUP: 129 })) { + process.on(signal, () => { + void teardown(exitCode); + }); + } +} + +main().catch((error) => { + console.error('[electron:dev] unexpected error:', error); + process.exit(1); +}); diff --git a/packages/electron/scripts/finalize-latest-yml.mjs b/packages/electron/scripts/finalize-latest-yml.mjs new file mode 100644 index 00000000..68b2de18 --- /dev/null +++ b/packages/electron/scripts/finalize-latest-yml.mjs @@ -0,0 +1,113 @@ +#!/usr/bin/env node +import fs from 'node:fs/promises'; +import path from 'node:path'; + +const dir = process.env.LATEST_YML_DIR; +const repo = process.env.GH_REPO; +const version = process.env.OPENCHAMBER_VERSION; + +if (!dir) throw new Error('LATEST_YML_DIR is required'); +if (!repo) throw new Error('GH_REPO is required'); +if (!version) throw new Error('OPENCHAMBER_VERSION is required'); + +const parse = (content) => { + const lines = content.split('\n'); + let releaseDate = ''; + let parsedVersion = ''; + const files = []; + let current; + + const flush = () => { + if (current?.url && current?.sha512 && current?.size) { + files.push(current); + } + current = undefined; + }; + + for (const line of lines) { + const trimmed = line.trim(); + const indented = line.startsWith(' ') || line.startsWith(' -'); + if (line.startsWith('version:')) { + parsedVersion = line.slice('version:'.length).trim(); + } else if (line.startsWith('releaseDate:')) { + releaseDate = line.slice('releaseDate:'.length).trim().replace(/^'|'$/g, ''); + } else if (trimmed.startsWith('- url:')) { + flush(); + current = { url: trimmed.slice('- url:'.length).trim() }; + } else if (indented && current && trimmed.startsWith('sha512:')) { + current.sha512 = trimmed.slice('sha512:'.length).trim(); + } else if (indented && current && trimmed.startsWith('size:')) { + current.size = Number(trimmed.slice('size:'.length).trim()); + } else if (indented && current && trimmed.startsWith('blockMapSize:')) { + current.blockMapSize = Number(trimmed.slice('blockMapSize:'.length).trim()); + } else if (!indented && current) { + flush(); + } + } + + flush(); + return { version: parsedVersion, releaseDate, files }; +}; + +const serialize = (data) => { + const lines = [`version: ${data.version}`, 'files:']; + for (const file of data.files) { + lines.push(` - url: ${file.url}`); + lines.push(` sha512: ${file.sha512}`); + lines.push(` size: ${file.size}`); + if (file.blockMapSize) { + lines.push(` blockMapSize: ${file.blockMapSize}`); + } + } + lines.push(`releaseDate: '${data.releaseDate}'`); + return `${lines.join('\n')}\n`; +}; + +const read = async (subdir, filename) => { + const filePath = path.join(dir, subdir, filename); + try { + return parse(await fs.readFile(filePath, 'utf8')); + } catch { + return null; + } +}; + +const output = {}; + +const winX64 = await read('latest-yml-x86_64-pc-windows-msvc', 'latest.yml'); +const winArm64 = await read('latest-yml-aarch64-pc-windows-msvc', 'latest.yml'); +if (winX64 || winArm64) { + const base = winArm64 || winX64; + output['latest.yml'] = serialize({ + version: base.version, + files: [...(winArm64?.files || []), ...(winX64?.files || [])], + releaseDate: base.releaseDate, + }); +} + +const linuxX64 = await read('latest-yml-x86_64-unknown-linux-gnu', 'latest-linux.yml'); +if (linuxX64) output['latest-linux.yml'] = serialize(linuxX64); + +const linuxArm64 = await read('latest-yml-aarch64-unknown-linux-gnu', 'latest-linux-arm64.yml'); +if (linuxArm64) output['latest-linux-arm64.yml'] = serialize(linuxArm64); + +const macX64 = await read('latest-yml-x86_64-apple-darwin', 'latest-mac.yml'); +const macArm64 = await read('latest-yml-aarch64-apple-darwin', 'latest-mac.yml'); +if (macX64 || macArm64) { + const base = macArm64 || macX64; + output['latest-mac.yml'] = serialize({ + version: base.version, + files: [...(macArm64?.files || []), ...(macX64?.files || [])], + releaseDate: base.releaseDate, + }); +} + +const tag = `v${version}`; +const tmp = process.env.RUNNER_TEMP || '/tmp'; +for (const [filename, content] of Object.entries(output)) { + const outputPath = path.join(tmp, filename); + await fs.writeFile(outputPath, content); + console.log(`prepared ${outputPath} for upload to ${repo} release ${tag}`); +} + +console.log('finalized latest yml files'); diff --git a/packages/electron/scripts/rebuild-native.mjs b/packages/electron/scripts/rebuild-native.mjs new file mode 100644 index 00000000..eb381df7 --- /dev/null +++ b/packages/electron/scripts/rebuild-native.mjs @@ -0,0 +1,30 @@ +#!/usr/bin/env node +import path from 'node:path'; +import { fileURLToPath } from 'node:url'; +import { createRequire } from 'node:module'; +import { rebuild } from '@electron/rebuild'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const electronDir = path.resolve(__dirname, '..'); +const repoRoot = path.resolve(electronDir, '..', '..'); +const require = createRequire(import.meta.url); + +const electronPkg = require('electron/package.json'); +const electronVersion = electronPkg.version; + +console.log(`[electron] rebuilding native modules against Electron ${electronVersion}...`); + +// Rebuild against the hoisted root node_modules (bun workspace layout). +// force=true re-links regardless of cached state; prebuild-install lookup is +// bypassed by @electron/rebuild in favor of direct node-gyp builds. +await rebuild({ + buildPath: repoRoot, + electronVersion, + force: true, + arch: process.env.ELECTRON_BUILDER_ARCH || process.arch, + onlyModules: ['better-sqlite3', 'node-pty', 'bun-pty'], +}); + +console.log('[electron] native modules rebuilt successfully'); diff --git a/packages/electron/ssh-manager.mjs b/packages/electron/ssh-manager.mjs new file mode 100644 index 00000000..ed64093b --- /dev/null +++ b/packages/electron/ssh-manager.mjs @@ -0,0 +1,1240 @@ +import fs from 'node:fs'; +import fsp from 'node:fs/promises'; +import net from 'node:net'; +import os from 'node:os'; +import path from 'node:path'; +import { spawn } from 'node:child_process'; + +const LOCAL_HOST_ID = 'local'; +const DEFAULT_CONNECTION_TIMEOUT_SEC = 60; +const DEFAULT_LOCAL_BIND_HOST = '127.0.0.1'; +const DEFAULT_CONTROL_PERSIST_SEC = 300; +const DEFAULT_READY_TIMEOUT_SEC = 30; +const DEFAULT_RECONNECT_MAX_ATTEMPTS = 5; +const MAX_LOG_LINES_PER_INSTANCE = 1200; + +const MONITOR_INITIAL_POLL_MS = 2000; +const MONITOR_STEADY_POLL_MS = 10000; +const MONITOR_STABILIZE_TICKS = 5; +const SSH_STATUS_EVENT = 'openchamber:ssh-instance-status'; + +const nowMillis = () => Date.now(); + +const shellQuote = (value) => `'${String(value).replace(/'/g, `'\\''`)}'`; + +const hasGlobWildcard = (value) => /[*?]/.test(value); + +const expandSshIncludeToken = (token, baseDir) => { + const trimmed = String(token || '').trim(); + if (!trimmed) return []; + + const expandedHome = trimmed.startsWith('~/') + ? path.join(os.homedir(), trimmed.slice(2)) + : (trimmed === '~' ? os.homedir() : trimmed); + const resolved = path.isAbsolute(expandedHome) + ? expandedHome + : path.resolve(baseDir, expandedHome); + + if (!hasGlobWildcard(resolved)) { + return fs.existsSync(resolved) ? [resolved] : []; + } + + const dir = path.dirname(resolved); + const namePattern = path.basename(resolved); + if (hasGlobWildcard(dir) || !fs.existsSync(dir)) { + return []; + } + + const matcher = new RegExp(`^${namePattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') + .replace(/\*/g, '.*') + .replace(/\?/g, '.')}$`); + + try { + return fs.readdirSync(dir) + .filter((name) => matcher.test(name)) + .map((name) => path.join(dir, name)) + .filter((candidate) => fs.existsSync(candidate)) + .sort((left, right) => left.localeCompare(right)); + } catch { + return []; + } +}; + +const readJsonRoot = (settingsFilePath) => { + try { + const parsed = JSON.parse(fs.readFileSync(settingsFilePath, 'utf8')); + return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {}; + } catch { + return {}; + } +}; + +const writeJsonRoot = async (settingsFilePath, root) => { + await fsp.mkdir(path.dirname(settingsFilePath), { recursive: true }); + await fsp.writeFile(settingsFilePath, JSON.stringify(root, null, 2)); +}; + +const defaultTrue = () => true; + +const sanitizeBindHost = (raw) => { + const trimmed = typeof raw === 'string' ? raw.trim() : ''; + if (!trimmed) return DEFAULT_LOCAL_BIND_HOST; + return ['127.0.0.1', 'localhost', '0.0.0.0'].includes(trimmed) ? trimmed : DEFAULT_LOCAL_BIND_HOST; +}; + +const splitShellWords = (input) => { + const tokens = []; + let current = ''; + let inSingle = false; + let inDouble = false; + const chars = [...String(input)]; + + for (let index = 0; index < chars.length; index += 1) { + const ch = chars[index]; + if (ch === '\\' && !inSingle) { + index += 1; + if (index < chars.length) current += chars[index]; + continue; + } + if (ch === '\'' && !inDouble) { + inSingle = !inSingle; + continue; + } + if (ch === '"' && !inSingle) { + inDouble = !inDouble; + continue; + } + if (/\s/.test(ch) && !inSingle && !inDouble) { + if (current) { + tokens.push(current); + current = ''; + } + continue; + } + current += ch; + } + + if (inSingle || inDouble) { + throw new Error('Unclosed quote in SSH command'); + } + if (current) tokens.push(current); + return tokens; +}; + +const isDisallowedPrimaryFlag = (token) => { + return ['-M', '-S', '-O', '-N', '-t', '-T', '-f', '-G', '-W', '-v', '-V', '-q', '-n', '-s', '-e', '-E', '-g'].includes(token); +}; + +const hasDisallowedOOption = (value) => { + const lower = String(value).trim().toLowerCase(); + return ['controlmaster', 'controlpath', 'controlpersist', 'batchmode', 'proxycommand'].some((prefix) => lower.startsWith(prefix)); +}; + +const parseSshCommand = (raw) => { + const tokens = splitShellWords(raw); + if (tokens.length === 0) { + throw new Error('SSH command is empty'); + } + + if (tokens[0] === 'ssh') { + tokens.shift(); + } + + if (tokens.length === 0) { + throw new Error('SSH command must include destination'); + } + + const allowedFlags = new Set(['-4', '-6', '-A', '-a', '-C', '-K', '-k', '-X', '-x', '-Y', '-y']); + const allowedWithValues = ['-B', '-b', '-c', '-D', '-F', '-I', '-i', '-J', '-l', '-m', '-o', '-P', '-p', '-R']; + + const args = []; + let destination = null; + for (let index = 0; index < tokens.length;) { + const token = tokens[index]; + if (destination) { + throw new Error(`SSH command has unsupported trailing argument: ${token}`); + } + + if (!token.startsWith('-')) { + destination = token.trim(); + index += 1; + continue; + } + + if (isDisallowedPrimaryFlag(token)) { + throw new Error(`SSH option ${token} is not allowed`); + } + + if (allowedFlags.has(token)) { + args.push(token); + index += 1; + continue; + } + + let matched = false; + for (const option of allowedWithValues) { + if (token === option) { + const value = tokens[index + 1]; + if (!value) { + throw new Error(`SSH option ${option} requires a value`); + } + if (option === '-o' && hasDisallowedOOption(value)) { + throw new Error(`SSH option -o ${value} is not allowed`); + } + args.push(token, value); + index += 2; + matched = true; + break; + } + + if (token.startsWith(option) && token.length > option.length) { + const value = token.slice(option.length); + if (option === '-o' && hasDisallowedOOption(value)) { + throw new Error(`SSH option -o ${value} is not allowed`); + } + args.push(token); + index += 1; + matched = true; + break; + } + } + + if (!matched) { + throw new Error(`Unsupported SSH option: ${token}`); + } + } + + if (!destination) { + throw new Error('SSH command must include destination'); + } + + return { destination, args }; +}; + +const runOutput = async (command, args, options = {}) => { + return await new Promise((resolve, reject) => { + const child = spawn(command, args, { + stdio: ['pipe', 'pipe', 'pipe'], + ...options, + }); + + let stdout = ''; + let stderr = ''; + child.stdout?.on('data', (chunk) => { + stdout += chunk.toString(); + }); + child.stderr?.on('data', (chunk) => { + stderr += chunk.toString(); + }); + child.on('error', reject); + child.on('close', (code) => { + resolve({ code: typeof code === 'number' ? code : -1, stdout, stderr }); + }); + }); +}; + +const buildSshArgs = (parsed, preDestinationArgs = [], remoteCommand = null) => { + const args = [...parsed.args, ...preDestinationArgs, parsed.destination]; + if (remoteCommand) args.push(remoteCommand); + return args; +}; + +const runRemoteCommand = async (parsed, controlPath, script, timeoutSec = DEFAULT_CONNECTION_TIMEOUT_SEC) => { + const args = buildSshArgs(parsed, [ + '-o', 'ControlMaster=no', + '-o', `ControlPath=${controlPath}`, + '-o', `ConnectTimeout=${timeoutSec}`, + '-T', + ], `sh -lc ${shellQuote(script)}`); + const { code, stdout, stderr } = await runOutput('ssh', args); + if (code !== 0) { + throw new Error((stderr || stdout || 'Remote command failed').trim()); + } + return stdout; +}; + +const controlMasterOperation = async (parsed, controlPath, op) => { + return await runOutput('ssh', buildSshArgs(parsed, [ + '-o', 'ControlMaster=no', + '-o', `ControlPath=${controlPath}`, + '-o', 'BatchMode=yes', + '-o', 'ConnectTimeout=3', + '-O', op, + ])); +}; + +const isControlMasterAlive = async (parsed, controlPath) => { + const { code } = await controlMasterOperation(parsed, controlPath, 'check'); + return code === 0; +}; + +const stopControlMasterBestEffort = async (parsed, controlPath) => { + try { + await controlMasterOperation(parsed, controlPath, 'exit'); + } catch { + } +}; + +const askpassScriptContent = () => `#!/bin/bash +PROMPT="$1" + +if [[ -n "$OPENCHAMBER_SSH_ASKPASS_VALUE" ]]; then + if [[ "$PROMPT" == *"assword"* || "$PROMPT" == *"passphrase"* ]]; then + printf '%s\\n' "$OPENCHAMBER_SSH_ASKPASS_VALUE" + exit 0 + fi +fi + +DEFAULT_ANSWER="" +HIDDEN_INPUT="true" + +if [[ "$PROMPT" == *"yes/no"* ]]; then + DEFAULT_ANSWER="yes" + HIDDEN_INPUT="false" +fi + +if command -v osascript >/dev/null 2>&1; then + /usr/bin/osascript <<'APPLESCRIPT' "$PROMPT" "$DEFAULT_ANSWER" "$HIDDEN_INPUT" +on run argv + set promptText to item 1 of argv + set defaultAnswer to item 2 of argv + set hiddenInput to item 3 of argv + + try + if hiddenInput is "true" then + set response to display dialog promptText default answer defaultAnswer with hidden answer buttons {"Cancel", "OK"} default button "OK" + else + set response to display dialog promptText default answer defaultAnswer buttons {"Cancel", "OK"} default button "OK" + end if + return text returned of response + on error + error number -128 + end try +end run +APPLESCRIPT + exit $? +fi + +printf '%s\\n' "$DEFAULT_ANSWER" +`; + +const writeAskpassScript = async (scriptPath) => { + await fsp.writeFile(scriptPath, askpassScriptContent(), { mode: 0o700 }); + await fsp.chmod(scriptPath, 0o700); +}; + +const randomPortCandidate = (seed) => { + let hash = 0; + const source = `${seed}:${Date.now()}`; + for (let index = 0; index < source.length; index += 1) { + hash = ((hash << 5) - hash + source.charCodeAt(index)) | 0; + } + const base = 20000; + const span = 30000; + return base + Math.abs(hash % span); +}; + +const pickUnusedLocalPort = async () => { + return await new Promise((resolve, reject) => { + const server = net.createServer(); + server.listen(0, '127.0.0.1', () => { + const address = server.address(); + const port = typeof address === 'object' && address ? address.port : 0; + server.close((error) => error ? reject(error) : resolve(port)); + }); + server.on('error', reject); + }); +}; + +const isLocalPortAvailable = async (bindHost, port) => { + return await new Promise((resolve) => { + const server = net.createServer(); + server.once('error', () => resolve(false)); + server.listen(port, bindHost, () => { + server.close(() => resolve(true)); + }); + }); +}; + +const isLocalTunnelReachable = async (localPort) => { + return await new Promise((resolve) => { + const socket = net.createConnection({ host: '127.0.0.1', port: localPort }); + const finish = (value) => { + socket.destroy(); + resolve(value); + }; + socket.once('connect', () => finish(true)); + socket.once('error', () => finish(false)); + socket.setTimeout(500, () => finish(false)); + }); +}; + +const waitLocalForwardReady = async (localPort) => { + const deadline = Date.now() + (DEFAULT_READY_TIMEOUT_SEC * 1000); + let pollMs = 250; + while (Date.now() < deadline) { + try { + const response = await fetch(`http://127.0.0.1:${localPort}/health`, { signal: AbortSignal.timeout(1000) }); + if (response.ok || response.status === 401) { + return; + } + } catch { + } + await new Promise((resolve) => setTimeout(resolve, pollMs)); + pollMs = Math.min(pollMs * 2, 2000); + } + throw new Error('Timed out waiting for forwarded OpenChamber health'); +}; + +const parseVersionToken = (raw) => { + for (const token of String(raw).split(/\s+/)) { + let candidate = token.trim().replace(/^v/, ''); + candidate = candidate.replace(/[,)]+$/g, ''); + const parts = candidate.split('.'); + if (parts.length >= 2 && parts.every((part) => /^\d+$/.test(part))) { + return candidate; + } + } + return null; +}; + +const parseProbeStatusLine = (line, prefix) => { + if (!line || !line.startsWith(prefix)) return null; + const value = Number.parseInt(line.slice(prefix.length).trim(), 10); + return Number.isFinite(value) ? value : null; +}; + +const isAuthHttpStatus = (status) => status === 401 || status === 403; +const isLivenessHttpStatus = (status) => (status >= 200 && status <= 299) || isAuthHttpStatus(status); + +export class ElectronSshManager { + constructor(options) { + this.settingsFilePath = options.settingsFilePath; + this.appVersion = options.appVersion; + this.emit = options.emit; + this.logs = new Map(); + this.statuses = new Map(); + this.sessions = new Map(); + this.monitorTimers = new Map(); + this.reconnectAttempts = new Map(); + this.connectAttempts = new Map(); + this.connecting = new Map(); + } + + appendLogWithLevel(id, level, message) { + const line = `[${nowMillis()}] [${level}] ${message}`; + const current = this.logs.get(id) || []; + current.push(line); + if (current.length > MAX_LOG_LINES_PER_INSTANCE) { + current.splice(0, current.length - MAX_LOG_LINES_PER_INSTANCE); + } + this.logs.set(id, current); + } + + appendLog(id, message) { + this.appendLogWithLevel(id, 'INFO', message); + } + + appendAttemptSeparator(id, connectAttempt, retryAttempt) { + const scope = retryAttempt > 0 ? `retry ${retryAttempt}` : 'manual'; + this.appendLogWithLevel(id, 'INFO', `---------------- attempt #${connectAttempt} (${scope}) ----------------`); + } + + statusSnapshotForInstance(id) { + return this.statuses.get(id) || { + id, + phase: 'idle', + detail: null, + localUrl: null, + localPort: null, + remotePort: null, + startedByUs: false, + retryAttempt: 0, + requiresUserAction: false, + updatedAtMs: nowMillis(), + }; + } + + setStatus(id, phase, detail = null, localUrl = null, localPort = null, remotePort = null, startedByUs = false, retryAttempt = 0, requiresUserAction = false) { + const level = phase === 'error' ? 'ERROR' : (phase === 'degraded' ? 'WARN' : 'INFO'); + this.appendLogWithLevel( + id, + level, + `phase=${JSON.stringify(phase)} detail=${detail || ''} retry=${retryAttempt} requires_user_action=${requiresUserAction}`, + ); + + const status = { + id, + phase, + detail, + localUrl, + localPort, + remotePort, + startedByUs, + retryAttempt, + requiresUserAction, + updatedAtMs: nowMillis(), + }; + this.statuses.set(id, status); + this.emit(SSH_STATUS_EVENT, status); + } + + clearRetryAttempt(id) { + this.reconnectAttempts.delete(id); + } + + nextRetryAttempt(id) { + const next = (this.reconnectAttempts.get(id) || 0) + 1; + this.reconnectAttempts.set(id, next); + return next; + } + + currentRetryAttempt(id) { + return this.reconnectAttempts.get(id) || 0; + } + + nextConnectAttempt(id) { + const next = (this.connectAttempts.get(id) || 0) + 1; + this.connectAttempts.set(id, next); + return next; + } + + logsForInstance(id, limit = 200) { + const lines = [...(this.logs.get(id) || [])]; + return limit > 0 && lines.length > limit ? lines.slice(-limit) : lines; + } + + clearLogsForInstance(id) { + this.logs.delete(id); + } + + parseSshConfigCandidates(filePath, source, visited = new Set()) { + const resolvedPath = path.resolve(filePath); + if (visited.has(resolvedPath) || !fs.existsSync(resolvedPath)) return []; + visited.add(resolvedPath); + + const content = fs.readFileSync(resolvedPath, 'utf8'); + const candidates = []; + const baseDir = path.dirname(resolvedPath); + for (const line of content.split(/\r?\n/)) { + const trimmed = (line.split('#')[0] || '').trim(); + if (!trimmed) continue; + + if (/^include(?:\s|$)/i.test(trimmed)) { + const includeExpr = trimmed.replace(/^include\s+/i, '').trim(); + if (!includeExpr) continue; + let includeTokens = []; + try { + includeTokens = splitShellWords(includeExpr); + } catch { + includeTokens = includeExpr.split(/\s+/).filter(Boolean); + } + for (const includeToken of includeTokens) { + const includePaths = expandSshIncludeToken(includeToken, baseDir); + for (const includePath of includePaths) { + candidates.push(...this.parseSshConfigCandidates(includePath, source, visited)); + } + } + continue; + } + + if (!/^host(?:\s|$)/i.test(trimmed)) continue; + const rest = trimmed.replace(/^host\s+/i, '').trim(); + if (!rest) continue; + for (const token of rest.split(/\s+/)) { + const host = token.trim(); + if (!host || host.startsWith('!') || host === '*') continue; + candidates.push({ + host, + pattern: /[*?]/.test(host), + source, + sshCommand: `ssh ${host}`, + }); + } + } + return candidates; + } + + async importHosts() { + const candidates = [ + ...this.parseSshConfigCandidates(path.join(os.homedir(), '.ssh', 'config'), 'user'), + ...this.parseSshConfigCandidates('/etc/ssh/ssh_config', 'global'), + ]; + const seen = new Set(); + return candidates + .filter((item) => !seen.has(item.host) && seen.add(item.host)) + .sort((left, right) => left.host.localeCompare(right.host)); + } + + readInstances() { + const root = readJsonRoot(this.settingsFilePath); + return { instances: Array.isArray(root.desktopSshInstances) ? root.desktopSshInstances : [] }; + } + + async setInstances(config) { + const root = readJsonRoot(this.settingsFilePath); + const instances = Array.isArray(config?.instances) ? config.instances.map((instance) => this.sanitizeInstance(instance)) : []; + root.desktopSshInstances = instances; + + const previousIds = new Set((Array.isArray(root.desktopHosts) ? root.desktopHosts : []).map((entry) => String(entry?.id || '').trim()).filter(Boolean)); + const hosts = Array.isArray(root.desktopHosts) ? root.desktopHosts.filter(Boolean) : []; + const nextIds = new Set(instances.map((instance) => instance.id)); + + const filteredHosts = hosts.filter((entry) => { + const id = String(entry?.id || '').trim(); + return id && !(previousIds.has(id) && !nextIds.has(id)); + }); + + for (const instance of instances) { + const label = instance.nickname?.trim() || instance.sshParsed?.destination || instance.id; + const existing = filteredHosts.find((entry) => entry?.id === instance.id); + if (existing) { + existing.label = label; + if (!existing.url || !String(existing.url).trim()) { + existing.url = 'http://127.0.0.1/'; + } + } else { + filteredHosts.push({ id: instance.id, label, url: 'http://127.0.0.1/' }); + } + } + + root.desktopHosts = filteredHosts; + if (typeof root.desktopDefaultHostId === 'string' && previousIds.has(root.desktopDefaultHostId) && !nextIds.has(root.desktopDefaultHostId)) { + root.desktopDefaultHostId = LOCAL_HOST_ID; + } + + await writeJsonRoot(this.settingsFilePath, root); + } + + sanitizeStoredSecret(secret) { + if (!secret || typeof secret !== 'object') return undefined; + return { + enabled: Boolean(secret.enabled), + store: secret.store === 'settings' ? 'settings' : 'never', + ...(typeof secret.value === 'string' && secret.value.trim() ? { value: secret.value } : {}), + }; + } + + sanitizeForward(forward) { + const id = typeof forward?.id === 'string' ? forward.id.trim() : ''; + if (!id) return null; + const type = forward?.type === 'remote' || forward?.type === 'dynamic' ? forward.type : 'local'; + const normalized = { + id, + enabled: forward?.enabled !== false, + type, + ...(forward?.localHost ? { localHost: sanitizeBindHost(forward.localHost) } : {}), + ...(Number.isFinite(forward?.localPort) ? { localPort: Number(forward.localPort) } : {}), + ...(forward?.remoteHost ? { remoteHost: String(forward.remoteHost).trim() || '127.0.0.1' } : {}), + ...(Number.isFinite(forward?.remotePort) ? { remotePort: Number(forward.remotePort) } : {}), + }; + + if (type === 'local' || type === 'remote') { + if (!normalized.localPort || !normalized.remotePort) return null; + normalized.remoteHost = normalized.remoteHost || '127.0.0.1'; + normalized.localHost = normalized.localHost || '127.0.0.1'; + } + if (type === 'dynamic' && !normalized.localPort) { + return null; + } + return normalized; + } + + sanitizeInstance(instance) { + const id = typeof instance?.id === 'string' ? instance.id.trim() : ''; + const sshCommand = typeof instance?.sshCommand === 'string' ? instance.sshCommand.trim() : ''; + if (!id || id === LOCAL_HOST_ID) { + throw new Error('SSH instance id is required'); + } + if (!sshCommand) { + throw new Error('SSH command is required'); + } + + const parsed = parseSshCommand(sshCommand); + const seen = new Set(); + const portForwards = Array.isArray(instance?.portForwards) + ? instance.portForwards + .map((forward) => this.sanitizeForward(forward)) + .filter((forward) => forward && !seen.has(forward.id) && seen.add(forward.id)) + : []; + + return { + id, + ...(typeof instance?.nickname === 'string' && instance.nickname.trim() ? { nickname: instance.nickname.trim() } : {}), + sshCommand, + sshParsed: parsed, + connectionTimeoutSec: Number.isFinite(instance?.connectionTimeoutSec) && Number(instance.connectionTimeoutSec) > 0 + ? Number(instance.connectionTimeoutSec) + : DEFAULT_CONNECTION_TIMEOUT_SEC, + remoteOpenchamber: { + mode: instance?.remoteOpenchamber?.mode === 'external' ? 'external' : 'managed', + keepRunning: instance?.remoteOpenchamber?.keepRunning !== false, + ...(Number.isFinite(instance?.remoteOpenchamber?.preferredPort) ? { preferredPort: Number(instance.remoteOpenchamber.preferredPort) } : {}), + installMethod: ['npm', 'bun', 'download_release', 'upload_bundle'].includes(instance?.remoteOpenchamber?.installMethod) + ? instance.remoteOpenchamber.installMethod + : 'bun', + uploadBundleOverSsh: Boolean(instance?.remoteOpenchamber?.uploadBundleOverSsh), + }, + localForward: { + bindHost: sanitizeBindHost(instance?.localForward?.bindHost), + ...(Number.isFinite(instance?.localForward?.preferredLocalPort) ? { preferredLocalPort: Number(instance.localForward.preferredLocalPort) } : {}), + }, + auth: { + ...(this.sanitizeStoredSecret(instance?.auth?.sshPassword) ? { sshPassword: this.sanitizeStoredSecret(instance.auth.sshPassword) } : {}), + ...(this.sanitizeStoredSecret(instance?.auth?.openchamberPassword) ? { openchamberPassword: this.sanitizeStoredSecret(instance.auth.openchamberPassword) } : {}), + }, + portForwards, + }; + } + + async updateHostUrl(instanceId, label, localUrl) { + const root = readJsonRoot(this.settingsFilePath); + const hosts = Array.isArray(root.desktopHosts) ? root.desktopHosts : []; + const existing = hosts.find((entry) => entry?.id === instanceId); + if (existing) { + existing.label = label; + existing.url = localUrl; + } else { + hosts.push({ id: instanceId, label, url: localUrl }); + } + root.desktopHosts = hosts; + await writeJsonRoot(this.settingsFilePath, root); + } + + async persistLocalPort(instanceId, localPort) { + const root = readJsonRoot(this.settingsFilePath); + const instances = Array.isArray(root.desktopSshInstances) ? root.desktopSshInstances : []; + for (const instance of instances) { + if (instance?.id !== instanceId) continue; + instance.localForward = instance.localForward && typeof instance.localForward === 'object' ? instance.localForward : {}; + instance.localForward.preferredLocalPort = localPort; + } + root.desktopSshInstances = instances; + await writeJsonRoot(this.settingsFilePath, root); + } + + async resolveSshConfig(parsed) { + const { code, stdout, stderr } = await runOutput('ssh', buildSshArgs(parsed, ['-G'])); + if (code !== 0) { + throw new Error(stderr.trim() || 'Failed to resolve SSH config'); + } + const map = new Map(); + for (const line of stdout.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed) continue; + const [key, ...rest] = trimmed.split(' '); + if (!key || rest.length === 0) continue; + map.set(key.toLowerCase(), rest.join(' ').trim()); + } + return map; + } + + ensureSessionDir(id) { + const base = path.join(path.dirname(this.settingsFilePath), 'ssh', id); + fs.mkdirSync(base, { recursive: true }); + return base; + } + + controlPathForInstance(id) { + let hash = 0; + for (const char of id) { + hash = ((hash << 5) - hash + char.charCodeAt(0)) | 0; + } + return path.join(os.tmpdir(), `ocssh-${Math.abs(hash).toString(16)}.sock`); + } + + async spawnMasterProcess(parsed, controlPath, askpassPath, sshPassword) { + const child = spawn('ssh', buildSshArgs(parsed, [ + '-o', 'ControlMaster=yes', + '-o', `ControlPath=${controlPath}`, + '-o', `ControlPersist=${DEFAULT_CONTROL_PERSIST_SEC}`, + '-N', + ]), { + stdio: ['ignore', 'pipe', 'pipe'], + env: { + ...process.env, + SSH_ASKPASS_REQUIRE: 'force', + SSH_ASKPASS: askpassPath, + DISPLAY: '1', + ...(sshPassword ? { OPENCHAMBER_SSH_ASKPASS_VALUE: sshPassword.trim() } : {}), + }, + }); + return child; + } + + async waitForMasterReady(parsed, controlPath, timeoutSec, master) { + const deadline = Date.now() + (timeoutSec * 1000); + let pollMs = 250; + while (Date.now() < deadline) { + const { code } = await runOutput('ssh', buildSshArgs(parsed, [ + '-o', 'ControlMaster=no', + '-o', `ControlPath=${controlPath}`, + '-O', 'check', + ])); + if (code === 0) return; + + const exited = master.exitCode; + if (typeof exited === 'number') { + throw new Error('SSH master process exited before ready'); + } + await new Promise((resolve) => setTimeout(resolve, pollMs)); + pollMs = Math.min(pollMs * 2, 2000); + } + throw new Error('SSH ControlMaster connection timed out'); + } + + configuredOpenChamberPassword(instance) { + const secret = instance?.auth?.openchamberPassword; + return secret?.enabled && typeof secret.value === 'string' && secret.value.trim() ? secret.value.trim() : null; + } + + async remoteCommandExists(parsed, controlPath, commandName) { + try { + const output = await runRemoteCommand(parsed, controlPath, `command -v ${commandName} >/dev/null 2>&1 && echo yes || echo no`); + return output.trim() === 'yes'; + } catch { + return false; + } + } + + async currentRemoteOpenChamberVersion(parsed, controlPath) { + try { + const output = await runRemoteCommand(parsed, controlPath, 'openchamber --version 2>/dev/null || true'); + return parseVersionToken(output); + } catch { + return null; + } + } + + async installOpenChamberManaged(parsed, controlPath, version, preferred) { + const hasBun = await this.remoteCommandExists(parsed, controlPath, 'bun'); + const hasNpm = await this.remoteCommandExists(parsed, controlPath, 'npm'); + const commands = []; + + if (preferred === 'bun') { + if (hasBun) commands.push(`bun add -g @openchamber/web@${version}`); + if (hasNpm) commands.push(`npm install -g @openchamber/web@${version}`); + } else if (preferred === 'npm') { + if (hasNpm) commands.push(`npm install -g @openchamber/web@${version}`); + if (hasBun) commands.push(`bun add -g @openchamber/web@${version}`); + } else { + if (hasBun) commands.push(`bun add -g @openchamber/web@${version}`); + if (hasNpm) commands.push(`npm install -g @openchamber/web@${version}`); + } + + if (commands.length === 0) { + throw new Error('Remote host has neither bun nor npm available'); + } + + let lastError = null; + for (const command of commands) { + try { + await runRemoteCommand(parsed, controlPath, command); + return; + } catch (error) { + lastError = error; + } + } + throw lastError || new Error('Failed to install OpenChamber on remote host'); + } + + async probeRemoteSystemInfo(parsed, controlPath, port, openchamberPassword) { + const authPayload = openchamberPassword ? JSON.stringify({ password: openchamberPassword }) : '{}'; + const authEnabled = openchamberPassword ? '1' : '0'; + const script = `AUTH_STATUS=0; INFO_STATUS=0; HEALTH_STATUS=0; BODY_FILE="$(mktemp)"; COOKIE_FILE="$(mktemp)"; cleanup(){ rm -f "$BODY_FILE" "$COOKIE_FILE"; }; trap cleanup EXIT; if command -v curl >/dev/null 2>&1; then if [ "${authEnabled}" = "1" ]; then AUTH_STATUS="$(curl -sS --max-time 3 -o /dev/null -w '%{http_code}' -c "$COOKIE_FILE" -H 'content-type: application/json' --data ${shellQuote(authPayload)} http://127.0.0.1:${port}/auth/session || true)"; if [ "$AUTH_STATUS" = "200" ]; then INFO_STATUS="$(curl -sS --max-time 3 -b "$COOKIE_FILE" -o "$BODY_FILE" -w '%{http_code}' http://127.0.0.1:${port}/api/system/info || true)"; else INFO_STATUS="$(curl -sS --max-time 3 -o "$BODY_FILE" -w '%{http_code}' http://127.0.0.1:${port}/api/system/info || true)"; fi; else INFO_STATUS="$(curl -sS --max-time 3 -o "$BODY_FILE" -w '%{http_code}' http://127.0.0.1:${port}/api/system/info || true)"; fi; HEALTH_STATUS="$(curl -sS --max-time 3 -o /dev/null -w '%{http_code}' http://127.0.0.1:${port}/health || true)"; elif command -v wget >/dev/null 2>&1; then wget -qO "$BODY_FILE" http://127.0.0.1:${port}/api/system/info >/dev/null 2>&1; if [ $? -eq 0 ]; then INFO_STATUS=200; fi; wget -qO- http://127.0.0.1:${port}/health >/dev/null 2>&1; if [ $? -eq 0 ]; then HEALTH_STATUS=200; fi; else exit 127; fi; printf 'INFO_STATUS=%s\\nAUTH_STATUS=%s\\nHEALTH_STATUS=%s\\n' "$INFO_STATUS" "$AUTH_STATUS" "$HEALTH_STATUS"; cat "$BODY_FILE" 2>/dev/null || true`; + const output = await runRemoteCommand(parsed, controlPath, script); + const lines = output.split(/\r?\n/); + const infoStatus = parseProbeStatusLine(lines[0], 'INFO_STATUS=') || 0; + const authStatus = parseProbeStatusLine(lines[1], 'AUTH_STATUS=') || 0; + const healthStatus = parseProbeStatusLine(lines[2], 'HEALTH_STATUS=') || 0; + const body = lines.slice(3).join('\n'); + + if (isLivenessHttpStatus(infoStatus)) { + if (isAuthHttpStatus(infoStatus)) { + if (openchamberPassword && authStatus !== 200) { + throw new Error(`Remote OpenChamber requires UI authentication and configured password was rejected (auth status ${authStatus})`); + } + if (isLivenessHttpStatus(healthStatus)) return {}; + throw new Error('Remote OpenChamber requires UI authentication on /api/system/info; configure OpenChamber UI password'); + } + } else if (isLivenessHttpStatus(healthStatus)) { + return {}; + } else { + throw new Error(`Remote OpenChamber probe failed (info status ${infoStatus}, health status ${healthStatus})`); + } + + try { + return JSON.parse(body); + } catch { + return {}; + } + } + + async remoteServerRunning(parsed, controlPath, port, openchamberPassword) { + try { + await this.probeRemoteSystemInfo(parsed, controlPath, port, openchamberPassword); + return true; + } catch { + return false; + } + } + + async startRemoteServerManaged(parsed, controlPath, instance, desiredPort) { + let envPrefix = 'OPENCHAMBER_RUNTIME=ssh-remote'; + const secret = this.configuredOpenChamberPassword(instance); + if (secret) { + envPrefix += ` OPENCHAMBER_UI_PASSWORD=${shellQuote(secret)}`; + } + const output = await runRemoteCommand(parsed, controlPath, `${envPrefix} openchamber serve --daemon --hostname 127.0.0.1 --port ${desiredPort}`); + const port = output.split(/\s+/).map((token) => Number.parseInt(token, 10)).find((value) => Number.isFinite(value)); + return port || desiredPort; + } + + async stopRemoteServerBestEffort(parsed, controlPath, remotePort) { + try { + await runRemoteCommand( + parsed, + controlPath, + `if command -v curl >/dev/null 2>&1; then curl -fsS -X POST http://127.0.0.1:${remotePort}/api/system/shutdown >/dev/null 2>&1 || true; elif command -v wget >/dev/null 2>&1; then wget -qO- --method=POST http://127.0.0.1:${remotePort}/api/system/shutdown >/dev/null 2>&1 || true; fi`, + ); + } catch { + } + } + + async spawnMainForward(parsed, controlPath, bindHost, localPort, remotePort) { + return spawn('ssh', buildSshArgs(parsed, [ + '-o', 'ControlMaster=no', + '-o', `ControlPath=${controlPath}`, + '-N', + '-L', `${bindHost}:${localPort}:127.0.0.1:${remotePort}`, + ]), { + stdio: ['ignore', 'ignore', 'pipe'], + }); + } + + async spawnExtraForward(parsed, controlPath, forward) { + const args = [ + '-o', 'ControlMaster=no', + '-o', `ControlPath=${controlPath}`, + '-O', 'forward', + ]; + if (forward.type === 'local') { + args.push('-L', `${forward.localHost || '127.0.0.1'}:${forward.localPort}:${forward.remoteHost || '127.0.0.1'}:${forward.remotePort}`); + } else if (forward.type === 'remote') { + args.push('-R', `${forward.remoteHost || '127.0.0.1'}:${forward.remotePort}:${forward.localHost || '127.0.0.1'}:${forward.localPort}`); + } else { + args.push('-D', `${forward.localHost || '127.0.0.1'}:${forward.localPort}`); + } + const { code, stdout, stderr } = await runOutput('ssh', buildSshArgs(parsed, args)); + if (code !== 0) { + throw new Error((stderr || stdout || `Failed to configure extra SSH forward ${forward.id}`).trim()); + } + } + + async ensureRemoteServer(instance, parsed, controlPath) { + if (instance.remoteOpenchamber.mode === 'external') { + if (!instance.remoteOpenchamber.preferredPort) { + throw new Error('External mode requires a preferred remote OpenChamber port'); + } + const port = instance.remoteOpenchamber.preferredPort; + this.setStatus(instance.id, 'server_detecting', 'Probing external OpenChamber server', null, null, port, false, 0, false); + await this.probeRemoteSystemInfo(parsed, controlPath, port, this.configuredOpenChamberPassword(instance)); + return { remotePort: port, startedByUs: false }; + } + + this.setStatus(instance.id, 'remote_probe', 'Checking remote OpenChamber installation'); + const installedVersion = await this.currentRemoteOpenChamberVersion(parsed, controlPath); + if (!installedVersion) { + this.setStatus(instance.id, 'installing', 'Installing OpenChamber on remote host'); + await this.installOpenChamberManaged(parsed, controlPath, this.appVersion, instance.remoteOpenchamber.installMethod); + } else if (installedVersion !== this.appVersion) { + this.setStatus(instance.id, 'updating', `Updating remote OpenChamber from ${installedVersion} to ${this.appVersion}`); + await this.installOpenChamberManaged(parsed, controlPath, this.appVersion, instance.remoteOpenchamber.installMethod); + } + + this.setStatus(instance.id, 'server_detecting', 'Detecting managed OpenChamber server'); + let remotePort = instance.remoteOpenchamber.preferredPort || null; + let startedByUs = false; + if (remotePort && !(await this.remoteServerRunning(parsed, controlPath, remotePort, this.configuredOpenChamberPassword(instance)))) { + remotePort = null; + } + if (!remotePort) { + this.setStatus(instance.id, 'server_starting', 'Starting managed OpenChamber server'); + const desiredPort = instance.remoteOpenchamber.preferredPort || randomPortCandidate(instance.id); + remotePort = await this.startRemoteServerManaged(parsed, controlPath, instance, desiredPort); + startedByUs = true; + } + if (!(await this.remoteServerRunning(parsed, controlPath, remotePort, this.configuredOpenChamberPassword(instance)))) { + throw new Error('Managed OpenChamber server failed to become reachable'); + } + return { remotePort, startedByUs }; + } + + async disconnectInternal(id, reportIdle) { + const timer = this.monitorTimers.get(id); + if (timer) { + clearTimeout(timer); + this.monitorTimers.delete(id); + } + + const session = this.sessions.get(id); + this.sessions.delete(id); + + if (session) { + if (session.startedByUs && session.instance.remoteOpenchamber.mode === 'managed' && !session.instance.remoteOpenchamber.keepRunning) { + await this.stopRemoteServerBestEffort(session.parsed, session.controlPath, session.remotePort); + } + await stopControlMasterBestEffort(session.parsed, session.controlPath); + for (const child of [session.mainForward, session.master]) { + try { + child.kill('SIGTERM'); + } catch { + } + } + try { + await fsp.rm(session.controlPath, { force: true }); + } catch { + } + try { + await fsp.rm(path.join(session.sessionDir, 'askpass.sh'), { force: true }); + } catch { + } + } + + this.clearRetryAttempt(id); + if (reportIdle) { + this.setStatus(id, 'idle', null, null, null, null, false, 0, false); + } + } + + async connectBlocking(instance) { + const id = instance.id; + this.setStatus(id, 'config_resolved', 'Resolving SSH command'); + const parsed = instance.sshParsed || parseSshCommand(instance.sshCommand); + await this.resolveSshConfig(parsed); + + this.setStatus(id, 'auth_check', 'Checking SSH connectivity'); + const sessionDir = this.ensureSessionDir(id); + const controlPath = this.controlPathForInstance(id); + try { await fsp.rm(controlPath, { force: true }); } catch {} + const askpassPath = path.join(sessionDir, 'askpass.sh'); + await writeAskpassScript(askpassPath); + + this.setStatus(id, 'master_connecting', 'Establishing SSH ControlMaster'); + const sshPassword = instance.auth?.sshPassword?.enabled ? instance.auth.sshPassword.value : null; + const master = await this.spawnMasterProcess(parsed, controlPath, askpassPath, sshPassword); + await this.waitForMasterReady(parsed, controlPath, instance.connectionTimeoutSec || DEFAULT_CONNECTION_TIMEOUT_SEC, master); + + this.setStatus(id, 'remote_probe', 'Probing remote platform'); + const remoteOs = (await runRemoteCommand(parsed, controlPath, 'uname -s', instance.connectionTimeoutSec || DEFAULT_CONNECTION_TIMEOUT_SEC)).trim().toLowerCase(); + if (!['linux', 'darwin'].includes(remoteOs)) { + master.kill('SIGTERM'); + throw new Error(`Unsupported remote OS: ${remoteOs}`); + } + + const { remotePort, startedByUs } = await this.ensureRemoteServer(instance, parsed, controlPath); + this.setStatus(id, 'forwarding', 'Setting up port forwards', null, null, remotePort, startedByUs, 0, false); + + const bindHost = sanitizeBindHost(instance.localForward?.bindHost); + let localPort = Number(instance.localForward?.preferredLocalPort) || 0; + if (!localPort) { + localPort = await pickUnusedLocalPort(); + } + if (!(await isLocalPortAvailable(bindHost, localPort))) { + localPort = await pickUnusedLocalPort(); + } + + const mainForward = await this.spawnMainForward(parsed, controlPath, bindHost, localPort, remotePort); + let mainForwardDetached = false; + await new Promise((resolve) => setTimeout(resolve, 250)); + if (typeof mainForward.exitCode === 'number') { + if (mainForward.exitCode === 0) { + mainForwardDetached = true; + this.appendLogWithLevel(id, 'INFO', 'Main tunnel helper exited after ControlMaster handoff'); + } else { + master.kill('SIGTERM'); + throw new Error(`Failed to start main port forward (status: ${mainForward.exitCode})`); + } + } + + const extraErrors = []; + for (const forward of instance.portForwards.filter((item) => item.enabled)) { + try { + await this.spawnExtraForward(parsed, controlPath, forward); + if (forward.type === 'local' && forward.localPort) { + await new Promise((resolve) => setTimeout(resolve, 100)); + if (!(await isLocalTunnelReachable(forward.localPort))) { + extraErrors.push(`${forward.id}: local listener 127.0.0.1:${forward.localPort} is not reachable`); + } + } + } catch (error) { + extraErrors.push(`${forward.id}: ${error instanceof Error ? error.message : String(error)}`); + } + } + + await waitLocalForwardReady(localPort); + + const localUrl = `http://127.0.0.1:${localPort}`; + const label = instance.nickname?.trim() || parsed.destination || id; + await this.updateHostUrl(id, label, localUrl); + if (instance.localForward?.preferredLocalPort !== localPort) { + await this.persistLocalPort(id, localPort); + } + + this.sessions.set(id, { + instance, + parsed, + sessionDir, + controlPath, + localPort, + remotePort, + startedByUs, + master, + masterDetached: false, + mainForward, + mainForwardDetached, + }); + + this.clearRetryAttempt(id); + this.setStatus( + id, + 'ready', + extraErrors.length === 0 ? 'SSH instance is ready' : `SSH instance is ready with forward warnings: ${extraErrors.join('; ')}`, + localUrl, + localPort, + remotePort, + startedByUs, + 0, + false, + ); + this.spawnMonitor(id); + } + + spawnMonitor(id) { + const existing = this.monitorTimers.get(id); + if (existing) clearTimeout(existing); + let healthyTicks = 0; + const tick = async () => { + const session = this.sessions.get(id); + if (!session) { + this.monitorTimers.delete(id); + return; + } + + let droppedReason = null; + let detachedNotice = null; + + if (!session.mainForwardDetached) { + if (typeof session.mainForward.exitCode === 'number') { + if (session.mainForward.exitCode === 0) { + session.mainForwardDetached = true; + detachedNotice = 'Main tunnel helper exited after ControlMaster handoff'; + } else { + droppedReason = `Main SSH forward exited (${session.mainForward.exitCode})`; + } + } + } + + if (!droppedReason) { + if (session.mainForwardDetached) { + // Fast path: cheap TCP probe before expensive SSH subprocess + if (await isLocalTunnelReachable(session.localPort)) { + // Tunnel alive — skip SSH check + } else if (!await isControlMasterAlive(session.parsed, session.controlPath)) { + droppedReason = 'SSH ControlMaster is not reachable'; + } else { + detachedNotice = 'Local tunnel unreachable but ControlMaster is alive'; + } + } + } + + if (detachedNotice) { + this.appendLogWithLevel(id, 'INFO', detachedNotice); + } + if (!droppedReason) { + healthyTicks++; + const pollMs = healthyTicks >= MONITOR_STABILIZE_TICKS ? MONITOR_STEADY_POLL_MS : MONITOR_INITIAL_POLL_MS; + this.monitorTimers.set(id, setTimeout(tick, pollMs)); + return; + } + + this.appendLogWithLevel(id, 'WARN', droppedReason); + await this.disconnectInternal(id, false); + const attempt = this.nextRetryAttempt(id); + if (attempt > DEFAULT_RECONNECT_MAX_ATTEMPTS) { + this.setStatus(id, 'error', `${droppedReason}. Retry limit reached`, null, null, null, false, attempt, true); + return; + } + + this.setStatus(id, 'degraded', `${droppedReason}. Reconnecting`, null, null, null, false, attempt, false); + const delayMs = Math.min((2 ** Math.max(attempt - 1, 0)) * 1000 + (nowMillis() % 700) + 100, 30000); + await new Promise((resolve) => setTimeout(resolve, delayMs)); + try { + await this.connect(id); + } catch (error) { + this.setStatus(id, 'error', error instanceof Error ? error.message : String(error), null, null, null, false, attempt, true); + } + }; + this.monitorTimers.set(id, setTimeout(tick, MONITOR_INITIAL_POLL_MS)); + } + + async connect(id) { + const trimmed = String(id || '').trim(); + if (!trimmed || trimmed === LOCAL_HOST_ID) { + throw new Error('SSH instance id is required'); + } + + if (this.connecting.has(trimmed)) { + this.appendLogWithLevel(trimmed, 'INFO', 'Connection already in progress'); + return this.connecting.get(trimmed); + } + + const instance = this.readInstances().instances.find((entry) => entry?.id === trimmed); + if (!instance) { + throw new Error('SSH instance not found'); + } + + const retryAttempt = this.currentRetryAttempt(trimmed); + const connectAttempt = this.nextConnectAttempt(trimmed); + this.appendAttemptSeparator(trimmed, connectAttempt, retryAttempt); + this.appendLog(trimmed, 'Starting SSH connection'); + await this.disconnectInternal(trimmed, false); + + const task = this.connectBlocking(this.sanitizeInstance(instance)) + .catch(async (error) => { + this.setStatus(trimmed, 'error', error instanceof Error ? error.message : String(error), null, null, null, false, 0, true); + await this.disconnectInternal(trimmed, false); + throw error; + }) + .finally(() => { + this.connecting.delete(trimmed); + }); + this.connecting.set(trimmed, task); + return task; + } + + async disconnect(id) { + const trimmed = String(id || '').trim(); + if (!trimmed || trimmed === LOCAL_HOST_ID) { + throw new Error('SSH instance id is required'); + } + await this.disconnectInternal(trimmed, true); + } + + async statusesWithDefaults(id) { + if (id) { + return [this.statusSnapshotForInstance(id)]; + } + return this.readInstances().instances + .map((instance) => this.statusSnapshotForInstance(instance.id)) + .sort((left, right) => left.id.localeCompare(right.id)); + } + + async shutdownAll() { + const ids = [...new Set([...this.sessions.keys(), ...this.connecting.keys(), ...this.monitorTimers.keys()])]; + for (const id of ids) { + await this.disconnectInternal(id, false); + } + } +} diff --git a/packages/ui/src/App.tsx b/packages/ui/src/App.tsx index a690a42e..35649756 100644 --- a/packages/ui/src/App.tsx +++ b/packages/ui/src/App.tsx @@ -35,6 +35,7 @@ import { OnboardingScreen } from '@/components/onboarding/OnboardingScreen'; import type { RecoveryVariant } from '@/components/onboarding/DesktopConnectionRecovery'; import { useSessionUIStore } from '@/sync/session-ui-store'; import { useDirectoryStore } from '@/stores/useDirectoryStore'; +import { useProjectsStore } from '@/stores/useProjectsStore'; import { opencodeClient } from '@/lib/opencode/client'; import { SyncProvider, useSessions } from '@/sync/sync-context'; import { useSync } from '@/sync/use-sync'; @@ -452,6 +453,40 @@ function App({ apis }: AppProps) { }; }, [embeddedSessionChat]); + React.useEffect(() => { + if (typeof window === 'undefined') return; + + const handler = (event: Event) => { + const detail = (event as CustomEvent<{ sessionId?: string }>).detail; + const sessionId = typeof detail?.sessionId === 'string' ? detail.sessionId.trim() : ''; + if (!sessionId) return; + void useSessionUIStore.getState().setCurrentSession(sessionId); + }; + + window.addEventListener('openchamber:open-session', handler as EventListener); + return () => window.removeEventListener('openchamber:open-session', handler as EventListener); + }, []); + + React.useEffect(() => { + if (typeof window === 'undefined') return; + + const handler = (event: Event) => { + const detail = (event as CustomEvent<{ projectPath?: string }>).detail; + const projectPath = typeof detail?.projectPath === 'string' ? detail.projectPath.trim() : ''; + if (!projectPath) return; + const projectsStore = useProjectsStore.getState(); + const existing = projectsStore.projects.find((project) => project.path === projectPath); + if (existing) { + projectsStore.setActiveProject(existing.id); + } else { + projectsStore.addProject(projectPath); + } + }; + + window.addEventListener('openchamber:open-project', handler as EventListener); + return () => window.removeEventListener('openchamber:open-project', handler as EventListener); + }, []); + React.useEffect(() => { if (typeof window === 'undefined') return; if (!isInitialized || isSwitchingDirectory) return; diff --git a/packages/ui/src/components/chat/ChatInput.tsx b/packages/ui/src/components/chat/ChatInput.tsx index 5b7b8508..b0cf079e 100644 --- a/packages/ui/src/components/chat/ChatInput.tsx +++ b/packages/ui/src/components/chat/ChatInput.tsx @@ -756,7 +756,7 @@ const ChatInputComponent: React.FC = ({ onOpenSettings, scrollTo const composerHighlightRef = React.useRef(null); const isDesktopExpanded = isExpandedInput && !isMobile; - const chatInputRadius = 'var(--radius-lg)'; + const chatInputRadius = 'var(--radius-xl)'; const sendableAttachedFiles = attachedFiles; diff --git a/packages/ui/src/components/chat/ChatMessage.tsx b/packages/ui/src/components/chat/ChatMessage.tsx index 53fe4475..68e63bf5 100644 --- a/packages/ui/src/components/chat/ChatMessage.tsx +++ b/packages/ui/src/components/chat/ChatMessage.tsx @@ -966,7 +966,7 @@ const ChatMessage: React.FC = ({ const assistantTopPaddingClass = !isUser && shouldShowHeader ? (stickyUserHeader ? (isMobile ? 'pt-4' : 'pt-6') : 'pt-0') : 'pt-0'; - const userMessageRadius = 'var(--radius-lg)'; + const userMessageRadius = 'var(--radius-xl)'; return ( <> diff --git a/packages/ui/src/components/chat/components/ScrollToBottomButton.tsx b/packages/ui/src/components/chat/components/ScrollToBottomButton.tsx index 1971bacd..c3234ae4 100644 --- a/packages/ui/src/components/chat/components/ScrollToBottomButton.tsx +++ b/packages/ui/src/components/chat/components/ScrollToBottomButton.tsx @@ -21,7 +21,7 @@ const ScrollToBottomButton: React.FC = ({ visible, on variant="outline" size="sm" onClick={onClick} - className="rounded-full h-8 w-8 p-0 shadow-none bg-background/95 hover:bg-interactive-hover" + className="size-8 rounded-full [corner-shape:round] p-0 shadow-none bg-background/95 hover:bg-interactive-hover" aria-label="Scroll to bottom" > diff --git a/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx b/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx index a9d560c0..7109bf13 100644 --- a/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx +++ b/packages/ui/src/components/desktop/DesktopHostSwitcher.tsx @@ -549,6 +549,10 @@ export function DesktopHostSwitcherDialog({ setEditUrl(''); }, []); + const stopDropdownTypeahead = React.useCallback((event: React.KeyboardEvent) => { + event.stopPropagation(); + }, []); + const commitEdit = React.useCallback(async () => { if (!editingId) return; if (editingId === LOCAL_HOST_ID) { @@ -972,12 +976,14 @@ export function DesktopHostSwitcherDialog({ setEditLabel(e.target.value)} + onKeyDown={stopDropdownTypeahead} placeholder="Label" disabled={isSaving} /> setEditUrl(e.target.value)} + onKeyDown={stopDropdownTypeahead} placeholder="https://host:port" disabled={isSaving} /> @@ -1033,12 +1039,14 @@ export function DesktopHostSwitcherDialog({ setNewLabel(e.target.value)} + onKeyDown={stopDropdownTypeahead} placeholder="Label (optional)" disabled={!tauriAvailable || isSaving} /> setNewUrl(e.target.value)} + onKeyDown={stopDropdownTypeahead} placeholder="https://host:port" disabled={!tauriAvailable || isSaving} /> diff --git a/packages/ui/src/components/desktop/OpenInAppButton.tsx b/packages/ui/src/components/desktop/OpenInAppButton.tsx index 15938b52..8be58585 100644 --- a/packages/ui/src/components/desktop/OpenInAppButton.tsx +++ b/packages/ui/src/components/desktop/OpenInAppButton.tsx @@ -69,11 +69,10 @@ const AppIcon = ({ type OpenInAppButtonProps = { directory: string; - activeFilePath?: string | null; className?: string; }; -export const OpenInAppButton = ({ directory, activeFilePath, className }: OpenInAppButtonProps) => { +export const OpenInAppButton = ({ directory, className }: OpenInAppButtonProps) => { const selectedAppId = useOpenInAppsStore((state) => state.selectedAppId); const availableApps = useOpenInAppsStore((state) => state.availableApps); const isCacheStale = useOpenInAppsStore((state) => state.isCacheStale); @@ -108,7 +107,7 @@ export const OpenInAppButton = ({ directory, activeFilePath, className }: OpenIn } const handleOpen = async (app: OpenInAppOption) => { - const opened = await openDesktopProjectInApp(directory, app.id, app.appName, activeFilePath); + const opened = await openDesktopProjectInApp(directory, app.id, app.appName); if (!opened) { await openDesktopPath(directory, app.appName); } diff --git a/packages/ui/src/components/layout/Header.tsx b/packages/ui/src/components/layout/Header.tsx index ae29420e..183dc34e 100644 --- a/packages/ui/src/components/layout/Header.tsx +++ b/packages/ui/src/components/layout/Header.tsx @@ -27,7 +27,6 @@ import { useAllLiveSessions, useSession, useSessionMessagesResolved } from '@/sy import { getAllSyncSessions } from '@/sync/sync-refs'; import { useProjectsStore } from '@/stores/useProjectsStore'; import { useQuotaAutoRefresh, useQuotaStore } from '@/stores/useQuotaStore'; -import { useFilesViewTabsStore } from '@/stores/useFilesViewTabsStore'; import { useGitBranchLabel } from '@/stores/useGitStore'; import { useGlobalSessionsStore } from '@/stores/useGlobalSessionsStore'; import { useFeatureFlagsStore } from '@/stores/useFeatureFlagsStore'; @@ -1105,14 +1104,6 @@ export const Header: React.FC = ({ }, [currentSessionDiffStats]); const hasNonZeroSessionChanges = currentSessionChanges.additions > 0 || currentSessionChanges.deletions > 0; - const selectedFilePath = useFilesViewTabsStore((state) => { - const directory = normalize(openDirectory || ''); - if (!directory) { - return null; - } - return state.byRoot[directory]?.selectedPath ?? null; - }); - const actionDirectory = React.useMemo(() => { return normalize(openDirectory || activeProject?.path || ''); }, [activeProject?.path, openDirectory]); @@ -1643,7 +1634,7 @@ export const Header: React.FC = ({ )} - + = ({ {todos.map((todo) => { const isExpandedTodo = expandedTodoIds.has(todo.id); return ( -
  • - handleToggleTodo(todo.id, checked)} - ariaLabel={`Mark "${todo.text}" complete`} - className="mt-[3px] self-start" - /> - -
    +
  • +
    + handleToggleTodo(todo.id, checked)} + ariaLabel={`Mark "${todo.text}" complete`} + /> +
    - - - - - - handleSendToCurrentSession(todo.text)}> - Send to current session - - handleSendToNewSession(todo.id, todo.text)}> - Send to new session - - void handleSendToNewWorktreeSession(todo.id, todo.text)} - disabled={!canCreateWorktree} - > - Send to new worktree session - - - - -
  • +
    + + + + + + + handleSendToCurrentSession(todo.text)}> + Send to current session + + handleSendToNewSession(todo.id, todo.text)}> + Send to new session + + void handleSendToNewWorktreeSession(todo.id, todo.text)} + disabled={!canCreateWorktree} + > + Send to new worktree session + + + +
    + ); })} diff --git a/packages/ui/src/components/session/SessionSidebar.tsx b/packages/ui/src/components/session/SessionSidebar.tsx index dc8deafd..6a28a67e 100644 --- a/packages/ui/src/components/session/SessionSidebar.tsx +++ b/packages/ui/src/components/session/SessionSidebar.tsx @@ -3,7 +3,8 @@ import type { Session } from '@opencode-ai/sdk/v2'; import { RiLayoutLeftLine } from '@remixicon/react'; import { toast } from '@/components/ui'; import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip'; -import { isDesktopLocalOriginActive, isDesktopShell, isTauriShell, startDesktopWindowDrag } from '@/lib/desktop'; +import { isDesktopLocalOriginActive, isDesktopShell, isTauriShell } from '@/lib/desktop'; +import { isDesktopWindowFullscreen as getDesktopWindowFullscreen, onDesktopWindowResized, startDesktopWindowDrag } from '@/lib/desktopNative'; import { sessionEvents } from '@/lib/sessionEvents'; import { formatDirectoryName, cn } from '@/lib/utils'; import { useSessionUIStore } from '@/sync/session-ui-store'; @@ -403,9 +404,7 @@ export const SessionSidebar: React.FC = ({ const syncFullscreenState = async () => { try { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - const currentWindow = getCurrentWindow(); - const fullscreen = await currentWindow.isFullscreen(); + const fullscreen = await getDesktopWindowFullscreen(); if (!disposed) { setIsDesktopWindowFullscreen(fullscreen); } @@ -418,9 +417,7 @@ export const SessionSidebar: React.FC = ({ const attach = async () => { try { - const { getCurrentWindow } = await import('@tauri-apps/api/window'); - const currentWindow = getCurrentWindow(); - unlistenResize = await currentWindow.onResized(() => { + unlistenResize = onDesktopWindowResized(() => { void syncFullscreenState(); }); } catch { diff --git a/packages/ui/src/components/views/FilesView.tsx b/packages/ui/src/components/views/FilesView.tsx index a35741c0..7ab196e2 100644 --- a/packages/ui/src/components/views/FilesView.tsx +++ b/packages/ui/src/components/views/FilesView.tsx @@ -80,8 +80,7 @@ import { useEffectiveDirectory } from '@/hooks/useEffectiveDirectory'; import { FileTypeIcon } from '@/components/icons/FileTypeIcon'; import { ensurePierreThemeRegistered } from '@/lib/shiki/appThemeRegistry'; import { getDefaultTheme } from '@/lib/theme/themes'; -import { openDesktopPath, openDesktopProjectInApp } from '@/lib/desktop'; -import { OPEN_DIRECTORY_APP_IDS } from '@/lib/openInApps'; +import { openDesktopFileInApp, openDesktopPath } from '@/lib/desktop'; import { useOpenInAppsStore } from '@/stores/useOpenInAppsStore'; import { eventMatchesShortcut, getEffectiveShortcutCombo } from '@/lib/shortcuts'; @@ -510,17 +509,32 @@ export const FilesView: React.FC = ({ mode = 'full' }) => { const [isSearchOpen, setIsSearchOpen] = React.useState(false); const [isFloatingToolbarOpen, setIsFloatingToolbarOpen] = React.useState(false); const floatingToolbarRef = React.useRef(null); + const toolbarDropdownOpenCountRef = React.useRef(0); + + const handleToolbarDropdownOpenChange = React.useCallback((open: boolean) => { + toolbarDropdownOpenCountRef.current = Math.max( + 0, + toolbarDropdownOpenCountRef.current + (open ? 1 : -1), + ); + }, []); + + const isClickInsidePortalledMenu = React.useCallback((target: EventTarget | null) => { + if (!(target instanceof Element)) return false; + return target.closest('[data-slot="dropdown-menu-content"], [data-slot="dropdown-menu-item"]') !== null; + }, []); React.useEffect(() => { if (!isFloatingToolbarOpen) return; const handler = (event: MouseEvent) => { + if (toolbarDropdownOpenCountRef.current > 0) return; + if (isClickInsidePortalledMenu(event.target)) return; if (floatingToolbarRef.current && !floatingToolbarRef.current.contains(event.target as Node)) { setIsFloatingToolbarOpen(false); } }; document.addEventListener('mousedown', handler); return () => document.removeEventListener('mousedown', handler); - }, [isFloatingToolbarOpen]); + }, [isClickInsidePortalledMenu, isFloatingToolbarOpen]); const [textViewMode, setTextViewMode] = React.useState<'view' | 'edit'>('edit'); const [mdViewMode, setMdViewMode] = React.useState<'preview' | 'edit'>('edit'); const [jsonViewMode, setJsonViewMode] = React.useState<'tree' | 'text'>('tree'); @@ -664,21 +678,11 @@ export const FilesView: React.FC = ({ mode = 'full' }) => { }, [files]); const handleOpenInApp = React.useCallback(async (app: { id: string; appName: string }) => { - if (!selectedFile?.path || !root) { + if (!selectedFile?.path) { return; } - const fileDirectory = getParentDirectoryPath(selectedFile.path) || root; - - if (OPEN_DIRECTORY_APP_IDS.has(app.id)) { - const openedDirectory = await openDesktopPath(fileDirectory, app.appName); - if (!openedDirectory) { - toast.error(`Failed to open in ${app.appName}`); - } - return; - } - - const openedInApp = await openDesktopProjectInApp(root, app.id, app.appName, selectedFile.path); + const openedInApp = await openDesktopFileInApp(selectedFile.path, app.id, app.appName); if (openedInApp) { return; } @@ -688,10 +692,14 @@ export const FilesView: React.FC = ({ mode = 'full' }) => { return; } - const openedDirectory = await openDesktopPath(fileDirectory, app.appName); - if (!openedDirectory) { - toast.error(`Failed to open in ${app.appName}`); + const fileDirectory = getParentDirectoryPath(selectedFile.path) || root; + if (fileDirectory) { + const openedDirectory = await openDesktopPath(fileDirectory, app.appName); + if (openedDirectory) { + return; + } } + toast.error(`Failed to open in ${app.appName}`); }, [root, selectedFile?.path]); const handleOpenDialog = React.useCallback((type: 'createFile' | 'createFolder' | 'rename' | 'delete', data: { path: string; name?: string; type?: 'file' | 'directory' }) => { @@ -2354,7 +2362,7 @@ export const FilesView: React.FC = ({ mode = 'full' }) => { ) : null )} - +