diff --git a/.github/workflows/botbrowser-control-build.yml b/.github/workflows/botbrowser-control-build.yml
new file mode 100644
index 0000000..9df1922
--- /dev/null
+++ b/.github/workflows/botbrowser-control-build.yml
@@ -0,0 +1,135 @@
+name: BotBrowser Control — Build & Release
+
+on:
+ push:
+ tags:
+ - 'control-v*.*.*'
+ workflow_dispatch:
+
+jobs:
+ build-mac:
+ name: macOS
+ runs-on: macos-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: add-botbrowser-control
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install dependencies
+ run: npm install
+ working-directory: botbrowser-control
+
+ - name: Build macOS
+ run: npm run build:mac
+ working-directory: botbrowser-control
+ env:
+ CSC_IDENTITY_AUTO_DISCOVERY: false
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: mac-builds
+ path: |
+ botbrowser-control/dist/*.dmg
+ botbrowser-control/dist/*.zip
+ retention-days: 7
+
+ build-windows:
+ name: Windows
+ runs-on: windows-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: add-botbrowser-control
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install dependencies
+ run: npm install
+ working-directory: botbrowser-control
+
+ - name: Build Windows
+ run: npx electron-builder --win
+ working-directory: botbrowser-control
+ env:
+ CSC_IDENTITY_AUTO_DISCOVERY: "false"
+ DEBUG: electron-builder
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: windows-builds
+ path: |
+ botbrowser-control/dist/*.exe
+ botbrowser-control/dist/*.zip
+ retention-days: 7
+
+ build-linux:
+ name: Linux
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: add-botbrowser-control
+
+ - uses: actions/setup-node@v4
+ with:
+ node-version: 20
+
+ - name: Install Linux build dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y libnss3 libxss1 libxtst6 fakeroot
+
+ - name: Install dependencies
+ run: npm install
+ working-directory: botbrowser-control
+
+ - name: Build Linux
+ run: npm run build:linux
+ working-directory: botbrowser-control
+
+ - uses: actions/upload-artifact@v4
+ with:
+ name: linux-builds
+ path: |
+ botbrowser-control/dist/*.AppImage
+ botbrowser-control/dist/*.deb
+ botbrowser-control/dist/*.tar.gz
+ retention-days: 7
+
+ release:
+ name: Upload to Release
+ needs: [build-mac, build-windows, build-linux]
+ runs-on: ubuntu-latest
+ permissions:
+ contents: write
+ steps:
+ - name: Download all artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts/
+
+ - name: List artifacts
+ run: find artifacts/ -type f | sort
+
+ - name: Upload to GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ tag_name: ${{ github.ref_name }}
+ draft: false
+ prerelease: false
+ files: |
+ artifacts/mac-builds/*.dmg
+ artifacts/mac-builds/*.zip
+ artifacts/windows-builds/*.exe
+ artifacts/windows-builds/*.zip
+ artifacts/linux-builds/*.AppImage
+ artifacts/linux-builds/*.deb
+ artifacts/linux-builds/*.tar.gz
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/botbrowser-control/.github/workflows/build.yml b/botbrowser-control/.github/workflows/build.yml
new file mode 100644
index 0000000..59da6fd
--- /dev/null
+++ b/botbrowser-control/.github/workflows/build.yml
@@ -0,0 +1,168 @@
+name: Build & Release
+
+on:
+ push:
+ tags:
+ - 'v*.*.*'
+ workflow_dispatch:
+ inputs:
+ version:
+ description: 'Version to build (e.g. 1.0.0)'
+ required: false
+ default: ''
+
+jobs:
+ # ─────────────────────────────────────────
+ # macOS — DMG + ZIP (x64 & arm64)
+ # ─────────────────────────────────────────
+ build-mac:
+ name: macOS
+ runs-on: macos-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build macOS (x64 + arm64)
+ run: npm run build:mac
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload macOS artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: mac-builds
+ path: |
+ dist/*.dmg
+ dist/*-mac.zip
+ dist/*-arm64-mac.zip
+ retention-days: 7
+
+ # ─────────────────────────────────────────
+ # Windows — NSIS installer + Portable + ZIP
+ # ─────────────────────────────────────────
+ build-windows:
+ name: Windows
+ runs-on: windows-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build Windows (x64 + arm64)
+ run: npm run build:win
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload Windows artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: windows-builds
+ path: |
+ dist/*.exe
+ dist/*-win.zip
+ retention-days: 7
+
+ # ─────────────────────────────────────────
+ # Linux — AppImage + DEB + RPM + tar.gz
+ # ─────────────────────────────────────────
+ build-linux:
+ name: Linux
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v4
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v4
+ with:
+ node-version: 20
+ cache: 'npm'
+
+ - name: Install Linux build dependencies
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ libgtk-3-dev \
+ libnotify-dev \
+ libgconf-2-4 \
+ libnss3 \
+ libxss1 \
+ libxtst6 \
+ xauth \
+ xvfb \
+ rpm \
+ fakeroot
+
+ - name: Install dependencies
+ run: npm ci
+
+ - name: Build Linux (x64 + arm64)
+ run: npm run build:linux
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Upload Linux artifacts
+ uses: actions/upload-artifact@v4
+ with:
+ name: linux-builds
+ path: |
+ dist/*.AppImage
+ dist/*.deb
+ dist/*.rpm
+ dist/*.tar.gz
+ retention-days: 7
+
+ # ─────────────────────────────────────────
+ # Release — attach all artifacts to GitHub Release
+ # ─────────────────────────────────────────
+ release:
+ name: Create Release
+ needs: [build-mac, build-windows, build-linux]
+ runs-on: ubuntu-latest
+ if: startsWith(github.ref, 'refs/tags/v')
+ permissions:
+ contents: write
+ steps:
+ - name: Download all artifacts
+ uses: actions/download-artifact@v4
+ with:
+ path: artifacts/
+
+ - name: List artifacts
+ run: find artifacts/ -type f | sort
+
+ - name: Create GitHub Release
+ uses: softprops/action-gh-release@v2
+ with:
+ name: BotBrowser Control ${{ github.ref_name }}
+ draft: false
+ prerelease: false
+ generate_release_notes: true
+ files: |
+ artifacts/mac-builds/*.dmg
+ artifacts/mac-builds/*.zip
+ artifacts/windows-builds/*.exe
+ artifacts/windows-builds/*.zip
+ artifacts/linux-builds/*.AppImage
+ artifacts/linux-builds/*.deb
+ artifacts/linux-builds/*.rpm
+ artifacts/linux-builds/*.tar.gz
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
\ No newline at end of file
diff --git a/botbrowser-control/.gitignore b/botbrowser-control/.gitignore
new file mode 100644
index 0000000..5b48d8c
--- /dev/null
+++ b/botbrowser-control/.gitignore
@@ -0,0 +1,54 @@
+# Dependencies
+node_modules/
+.npm/
+
+# Build output
+dist/
+out/
+build/
+
+# Electron builder cache
+.electron-builder-cache/
+
+# macOS
+.DS_Store
+*.DS_Store
+__MACOSX/
+
+# Windows
+Thumbs.db
+desktop.ini
+
+# Logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+*.pid
+*.seed
+*.pid.lock
+
+# Temp files
+tmp/
+temp/
+*.tmp
+
+# Editor
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# OS
+.env
+.env.local
+
+# electron-store data (user data - never commit)
+/data/
+
+# Coverage
+coverage/
+.nyc_output/
\ No newline at end of file
diff --git a/botbrowser-control/BUILD.md b/botbrowser-control/BUILD.md
new file mode 100644
index 0000000..9f15b7b
--- /dev/null
+++ b/botbrowser-control/BUILD.md
@@ -0,0 +1,173 @@
+# Building BotBrowser Control
+
+Complete guide to building distributable packages for macOS, Windows, and Linux.
+
+---
+
+## Quick Start
+
+```bash
+npm install # install all dependencies first
+npm run build # build for ALL platforms
+```
+
+All output goes to the `dist/` folder.
+
+---
+
+## Prerequisites
+
+### Node.js 18+
+```bash
+node --version # must be v18.0.0+
+npm --version # must be v9.0.0+
+```
+
+Install via [nvm](https://github.com/nvm-sh/nvm) (macOS/Linux) or [winget](https://winget.run/) (Windows):
+```bash
+# macOS / Linux
+nvm install 20 && nvm use 20
+
+# Windows
+winget install OpenJS.NodeJS
+```
+
+### Install project dependencies
+```bash
+cd botbrowser-control
+npm install
+```
+
+---
+
+## Build Commands
+
+| Command | Platforms | Output |
+|---------|-----------|--------|
+| `npm run build` | All | Mac DMG+ZIP, Win NSIS+Portable+ZIP, Linux AppImage+DEB+RPM+tar.gz |
+| `npm run build:mac` | macOS | `.dmg` + `.zip` (x64 + arm64) |
+| `npm run build:mac:x64` | macOS Intel | `.dmg` + `.zip` |
+| `npm run build:mac:arm64` | macOS Apple Silicon | `.dmg` + `.zip` |
+| `npm run build:win` | Windows | NSIS `.exe` + Portable + `.zip` (x64 + arm64) |
+| `npm run build:win:x64` | Windows x64 | NSIS `.exe` + Portable + `.zip` |
+| `npm run build:win:ia32` | Windows 32-bit | NSIS `.exe` |
+| `npm run build:win:arm64` | Windows ARM64 | NSIS `.exe` |
+| `npm run build:linux` | Linux | `.AppImage` + `.deb` + `.rpm` + `.tar.gz` (x64 + arm64) |
+| `npm run build:linux:x64` | Linux x64 | All Linux formats |
+| `npm run build:linux:arm64` | Linux ARM64 | `.AppImage` + `.deb` + `.tar.gz` |
+
+---
+
+## Platform-specific Notes
+
+### Building on macOS
+
+No extra tools needed. Builds macOS and Linux targets natively.
+
+```bash
+npm run build:mac # native, always works
+npm run build:linux # also works on macOS (cross-compile)
+npm run build:win # requires Wine for NSIS
+```
+
+Install Wine for Windows builds:
+```bash
+brew install --cask wine-stable
+```
+
+### Building on Windows
+
+```powershell
+npm run build:win # native, always works
+# Mac + Linux targets require WSL or GitHub Actions
+```
+
+### Building on Linux
+
+```bash
+# Install build tools
+sudo apt-get install -y libgtk-3-dev libnotify-dev libnss3 libxss1 libxtst6 rpm fakeroot
+
+npm run build:linux # native, always works
+npm run build:win # requires Wine: sudo apt install wine64
+# macOS targets require GitHub Actions
+```
+
+---
+
+## Output Directory
+
+After a full `npm run build`, the `dist/` folder contains:
+
+```
+dist/
+├── BotBrowser Control-1.0.0.dmg ← macOS Intel DMG
+├── BotBrowser Control-1.0.0-arm64.dmg ← macOS Apple Silicon DMG
+├── BotBrowser Control-1.0.0-mac.zip ← macOS Intel ZIP
+├── BotBrowser Control-1.0.0-arm64-mac.zip ← macOS Apple Silicon ZIP
+│
+├── BotBrowser Control Setup 1.0.0.exe ← Windows x64 NSIS installer
+├── BotBrowser Control Setup 1.0.0-arm64.exe ← Windows ARM64 installer
+├── BotBrowser Control 1.0.0.exe ← Windows portable
+├── BotBrowser Control-1.0.0-win.zip ← Windows ZIP
+│
+├── BotBrowser Control-1.0.0.AppImage ← Linux x64 AppImage
+├── BotBrowser Control-1.0.0-arm64.AppImage ← Linux ARM64 AppImage
+├── botbrowser-control_1.0.0_amd64.deb ← Debian/Ubuntu x64
+├── botbrowser-control_1.0.0_arm64.deb ← Debian/Ubuntu ARM64
+├── botbrowser-control-1.0.0.x86_64.rpm ← Red Hat/Fedora
+└── botbrowser-control-1.0.0.tar.gz ← Generic Linux tarball
+```
+
+---
+
+## Automated Builds with GitHub Actions
+
+The repository includes `.github/workflows/build.yml` which:
+
+1. Triggers on any tag push matching `v*.*.*` (e.g. `v1.0.0`)
+2. Runs **3 parallel jobs**: `macos-latest`, `windows-latest`, `ubuntu-latest`
+3. Uploads all artifacts to the GitHub Release automatically
+
+### How to trigger a release
+
+```bash
+# Tag and push to trigger the workflow
+git tag v1.0.0
+git push origin v1.0.0
+```
+
+The Actions workflow will build all three platforms in parallel and attach every artifact to the GitHub Release.
+
+### Workflow file location
+`.github/workflows/build.yml`
+
+---
+
+## Troubleshooting
+
+| Problem | Solution |
+|---------|----------|
+| `electron-builder: command not found` | Run `npm install` first |
+| `fpm not found` (RPM/DEB on Linux) | `sudo apt install ruby-dev && gem install fpm` or `sudo apt install rpm fakeroot` |
+| Windows NSIS build fails on Linux/macOS | Install Wine: `brew install --cask wine-stable` or `sudo apt install wine64` |
+| macOS DMG build fails on Linux | Use `macos-latest` GitHub Actions runner |
+| Electron download hangs / slow | Set mirror: `ELECTRON_MIRROR=https://npmmirror.com/mirrors/electron/ npm install` |
+| Icon error during build | Ensure `src/assets/icon.png` exists and is at least 512×512 pixels |
+| `ENOTFOUND` / network errors | Check internet connection; try with VPN off |
+| `code signing` errors on macOS | Set `hardenedRuntime: false` and `gatekeeperAssess: false` in package.json (already set) |
+
+---
+
+## Development Mode
+
+Run without building:
+```bash
+npm start # production mode
+npm run dev # development mode (enables devtools, --dev flag)
+```
+
+Package without installer (just the unpacked app directory):
+```bash
+npm run pack # outputs to dist/linux-unpacked, dist/mac, or dist/win-unpacked
+```
\ No newline at end of file
diff --git a/botbrowser-control/INSTALL.md b/botbrowser-control/INSTALL.md
new file mode 100644
index 0000000..726eab1
--- /dev/null
+++ b/botbrowser-control/INSTALL.md
@@ -0,0 +1,216 @@
+# Installing BotBrowser Control
+
+Step-by-step installation guide for macOS, Windows, and Linux.
+
+---
+
+## macOS
+
+### From DMG (recommended)
+
+1. Download `BotBrowser.Control-*-arm64.dmg` (Apple Silicon) or `BotBrowser.Control-*-x64.dmg` (Intel) from [Releases](https://github.com/botswin/BotBrowser/releases)
+2. Double-click the `.dmg` file to mount it
+3. Drag **BotBrowser Control** into your `/Applications` folder
+4. Eject the DMG
+5. On first launch, right-click the app → **Open** (to bypass Gatekeeper on unsigned builds)
+
+### From source
+
+```bash
+# Requires Node.js 18+ (https://nodejs.org)
+git clone https://github.com/botswin/BotBrowser.git
+cd BotBrowser/botbrowser-control
+npm install
+npm start
+```
+
+### Build your own DMG
+
+```bash
+npm install
+npm run build:mac # builds both x64 + arm64 DMG & ZIP into dist/
+```
+
+---
+
+## Windows
+
+### From NSIS Installer (recommended)
+
+1. Download `BotBrowser.Control.Setup-*.exe` from [Releases](https://github.com/botswin/BotBrowser/releases)
+2. Double-click the installer
+3. Follow the setup wizard (choose install directory, create shortcuts)
+4. Launch from the Start Menu or Desktop shortcut
+
+### Portable (no install needed)
+
+1. Download `BotBrowser.Control-*-portable.exe`
+2. Run it directly — no installation required
+
+### From source
+
+```powershell
+# Requires Node.js 18+ (https://nodejs.org)
+git clone https://github.com/botswin/BotBrowser.git
+cd BotBrowser\botbrowser-control
+npm install
+npm start
+```
+
+### Build your own installer
+
+```powershell
+npm install
+npm run build:win # builds NSIS + portable + ZIP into dist\
+```
+
+---
+
+## Linux
+
+### AppImage (works on any distro)
+
+```bash
+# Download
+wget https://github.com/botswin/BotBrowser/releases/download/vX.X.X/BotBrowser.Control-X.X.X.AppImage
+
+# Make executable
+chmod +x BotBrowser.Control-*.AppImage
+
+# Run
+./BotBrowser.Control-*.AppImage
+```
+
+### Debian / Ubuntu (.deb)
+
+```bash
+# Download and install
+wget https://github.com/botswin/BotBrowser/releases/download/vX.X.X/botbrowser-control_X.X.X_amd64.deb
+sudo dpkg -i botbrowser-control_*.deb
+
+# Fix any missing dependencies
+sudo apt install -f
+
+# Launch
+botbrowser-control
+```
+
+### Red Hat / Fedora / CentOS (.rpm)
+
+```bash
+# Download and install
+wget https://github.com/botswin/BotBrowser/releases/download/vX.X.X/botbrowser-control-X.X.X.x86_64.rpm
+sudo rpm -i botbrowser-control-*.rpm
+
+# Or with dnf
+sudo dnf install botbrowser-control-*.rpm
+
+# Launch
+botbrowser-control
+```
+
+### From source
+
+```bash
+# Requires Node.js 18+ — install via nvm or your package manager
+curl -fsSL https://fnm.vercel.app/install | bash
+fnm install 20
+
+git clone https://github.com/botswin/BotBrowser.git
+cd BotBrowser/botbrowser-control
+npm install
+npm start
+```
+
+### Build your own packages
+
+```bash
+npm install
+npm run build:linux # builds AppImage + .deb + .rpm + tar.gz into dist/
+```
+
+---
+
+## Verifying Your Node.js Version
+
+```bash
+node --version # must be v18.0.0 or later
+npm --version # must be v9.0.0 or later
+```
+
+If you need to upgrade Node.js:
+
+```bash
+# Using nvm (macOS/Linux)
+curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.7/install.sh | bash
+nvm install 20
+nvm use 20
+
+# Using fnm (faster, cross-platform)
+curl -fsSL https://fnm.vercel.app/install | bash
+fnm install 20
+
+# Using winget (Windows)
+winget install OpenJS.NodeJS
+```
+
+---
+
+## First-time Configuration
+
+After installation, configure the path to your BotBrowser binary:
+
+1. Open BotBrowser Control
+2. Click **Settings** in the left sidebar
+3. Under **BotBrowser Executable**, click **Browse** and locate:
+ - **macOS:** `/Applications/Chromium.app/Contents/MacOS/Chromium`
+ - **Windows:** `C:\Program Files\BotBrowser\chrome.exe`
+ - **Linux:** `/usr/bin/botbrowser` or wherever you extracted BotBrowser
+4. Click **Save Settings**
+
+> **Don't have BotBrowser yet?** Download it from [github.com/botswin/BotBrowser](https://github.com/botswin/BotBrowser)
+
+---
+
+## Uninstalling
+
+### macOS
+- Drag `BotBrowser Control.app` from `/Applications` to Trash
+- Remove app data (optional): `rm -rf ~/Library/Application\ Support/BotBrowser\ Control`
+
+### Windows
+- Go to **Settings → Apps → Installed Apps**, find **BotBrowser Control**, click **Uninstall**
+- Or run `%LOCALAPPDATA%\Programs\BotBrowser Control\Uninstall BotBrowser Control.exe`
+- Remove app data (optional): delete `%APPDATA%\BotBrowser Control`
+
+### Linux (AppImage)
+- Delete the `.AppImage` file
+- Remove app data (optional): `rm -rf ~/.config/BotBrowser\ Control`
+
+### Linux (.deb)
+```bash
+sudo apt remove botbrowser-control
+```
+
+### Linux (.rpm)
+```bash
+sudo rpm -e botbrowser-control
+```
+
+---
+
+## Troubleshooting
+
+| Issue | Solution |
+|-------|----------|
+| App shows "Electron" in title bar | Update to latest version from source |
+| "BotBrowser executable not found" | Set the correct path in Settings |
+| App won't open on macOS (Gatekeeper) | Right-click → Open, then click Open in the dialog |
+| White screen on startup | Run `npm start` in terminal to see error output |
+| `ENOENT: node_modules` error | Run `npm install` in the project directory |
+| Profiles stuck as "Running" after force-quit | Restart the app — stale status is reset automatically on launch |
+| Can't install `.deb` — missing deps | Run `sudo apt install -f` after `dpkg -i` |
+
+---
+
+For more help, open an issue at [github.com/botswin/BotBrowser/issues](https://github.com/botswin/BotBrowser/issues)
\ No newline at end of file
diff --git a/botbrowser-control/LICENSE b/botbrowser-control/LICENSE
new file mode 100644
index 0000000..24cd588
--- /dev/null
+++ b/botbrowser-control/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2025 BotBrowser
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
\ No newline at end of file
diff --git a/botbrowser-control/README.md b/botbrowser-control/README.md
new file mode 100644
index 0000000..16523b4
--- /dev/null
+++ b/botbrowser-control/README.md
@@ -0,0 +1,275 @@
+
+
+
+
+# BotBrowser Control
+
+**Professional desktop profile manager for [BotBrowser](https://github.com/botswin/BotBrowser)**
+
+[](#)
+[](#)
+[](#)
+
+[Features](#features) · [Install](#installation) · [Build](#building-from-source) · [Usage](#usage) · [Configuration](#profile-configuration)
+
+
+
+---
+
+## Overview
+
+BotBrowser Control is an open-source Electron desktop application that provides a GoLogin-style GUI for managing and launching [BotBrowser](https://github.com/botswin/BotBrowser) profiles. It handles per-profile user data isolation, fingerprint configuration, proxy assignment, cookie persistence, and live session monitoring — all without touching the command line.
+
+
+
+### Profiles View
+
+
+### Settings
+
+
+### Profile Editor
+
+
+
+
+---
+
+## Features
+
+- **Profile Manager** — Create, edit, duplicate, and delete browser profiles with a clean table UI
+- **VS Code-style Profile Editor** — 7-tab sidebar editor (General, Network, Identity, Fingerprint, Behavior, Session, Advanced)
+- **One-click Launch / Stop** — Spawn and kill BotBrowser instances per profile
+- **Per-profile User Data** — Each profile gets an isolated `--user-data-dir` automatically
+- **Proxy Support** — HTTP, HTTPS, SOCKS4, SOCKS5 with credentials, per-profile
+- **Fingerprint Configuration** — Map to all BotBrowser `--bot-config-*` flags: locale, timezone, WebGL, WebRTC, canvas noise, seeds, etc.
+- **`.enc` Profile File Support** — Load BotBrowser encrypted fingerprint profiles
+- **Cookie Persistence** — Auto-save and reload cookies via CDP on stop/start
+- **Session Monitor** — Live view of all running browser instances with PID and start time
+- **Settings Page** — Configure BotBrowser executable path, default user data directory, default proxy
+- **Dark / Light Theme** — Follows system theme; manual override available
+- **macOS native** — Hidden titlebar, traffic lights, vibrancy effect
+- **Cross-platform** — Works on macOS, Windows, and Linux
+
+---
+
+## Requirements
+
+| Requirement | Version |
+|-------------|---------|
+| [BotBrowser](https://github.com/botswin/BotBrowser) | Any recent release |
+| Node.js | 18 or later |
+| npm | 9 or later |
+
+---
+
+## Installation
+
+### Option 1 — Download a pre-built release
+
+Go to [Releases](https://github.com/botswin/BotBrowser/releases) and download the installer for your platform:
+
+| Platform | File |
+|----------|------|
+| macOS (Apple Silicon) | `BotBrowser.Control-*-arm64.dmg` |
+| macOS (Intel) | `BotBrowser.Control-*-x64.dmg` |
+| Windows 64-bit | `BotBrowser.Control.Setup-*.exe` |
+| Linux (AppImage) | `BotBrowser.Control-*.AppImage` |
+| Linux (Debian/Ubuntu) | `botbrowser-control_*_amd64.deb` |
+
+### Option 2 — Run from source
+
+```bash
+# 1. Clone the repository
+git clone https://github.com/botswin/BotBrowser.git
+cd BotBrowser/botbrowser-control # adjust path if this is a standalone repo
+
+# 2. Install dependencies
+npm install
+
+# 3. Launch in development mode
+npm start
+```
+
+---
+
+## Building from Source
+
+### Prerequisites
+
+```bash
+node --version # v18+
+npm --version # v9+
+npm install # install all dependencies first
+```
+
+### Build commands
+
+```bash
+# Build for ALL platforms (Mac + Windows + Linux)
+npm run build
+
+# macOS only (produces .dmg and .zip for x64 + arm64)
+npm run build:mac
+
+# Windows only (produces NSIS installer + portable + zip)
+npm run build:win
+
+# Linux only (produces AppImage + .deb + .rpm + tar.gz)
+npm run build:linux
+```
+
+All artifacts are written to the `dist/` directory.
+
+### Build a specific architecture
+
+```bash
+npm run build:mac:x64 # macOS Intel
+npm run build:mac:arm64 # macOS Apple Silicon
+npm run build:win:x64 # Windows 64-bit
+npm run build:win:ia32 # Windows 32-bit
+npm run build:linux:x64 # Linux x64
+npm run build:linux:arm64 # Linux ARM64
+```
+
+### Cross-platform builds
+
+You can build **macOS** and **Linux** targets from any Unix machine. Building **Windows** targets from macOS/Linux requires [Wine](https://www.winehq.org/):
+
+```bash
+# macOS
+brew install --cask wine-stable
+
+# Ubuntu/Debian
+sudo apt install wine64
+```
+
+> **Recommended:** Use GitHub Actions to produce official releases for all three platforms in parallel. See [BUILD.md](BUILD.md) for the full workflow YAML.
+
+---
+
+## First-time Setup
+
+1. Launch the app (`npm start` or open the installed `.app` / `.exe`)
+2. Click **Settings** in the left sidebar
+3. Set the **BotBrowser Executable** path to your BotBrowser binary:
+ - macOS: `/Applications/Chromium.app/Contents/MacOS/Chromium`
+ - Windows: `C:\Program Files\BotBrowser\chrome.exe`
+ - Linux: `/usr/bin/botbrowser`
+4. (Optional) Set a **Default User Data Directory** — defaults to `~/Library/Application Support/BotBrowser Control/browser-profiles`
+5. Click **Save Settings**
+
+---
+
+## Usage
+
+### Creating a Profile
+
+1. Click **+ New Profile** in the Profiles view
+2. Fill in the **General** tab: name, browser brand, start URL, `.enc` profile file
+3. Configure **Network** tab: proxy server, IP override
+4. Optionally set fingerprint details in **Identity**, **Fingerprint**, **Behavior** tabs
+5. Click **Save Changes**
+
+### Launching a Profile
+
+- Click the **▶ Launch** button on any profile row
+- The profile opens as a standalone BotBrowser window
+- Status changes to **Running** (green pill)
+- Click **■ Stop** to close the browser and auto-save cookies
+
+### Managing Sessions
+
+- Click **Sessions** in the left sidebar to see all live instances
+- Each entry shows: profile name, PID, start time, and URL
+- Use **Stop All** to terminate every running instance at once
+
+### Keyboard Shortcuts
+
+| Action | macOS | Windows / Linux |
+|--------|-------|-----------------|
+| New Profile | `⌘N` | `Ctrl+N` |
+| Go to Profiles | `⌘1` | `Ctrl+1` |
+| Go to Sessions | `⌘2` | `Ctrl+2` |
+| Go to Settings | `⌘3` | `Ctrl+3` |
+| Reload UI | `⌘R` | `Ctrl+R` |
+| DevTools | `⌘⌥I` | `Ctrl+Shift+I` |
+
+---
+
+## Profile Configuration
+
+The profile editor maps directly to BotBrowser CLI flags. The most important fields:
+
+| Tab | Field | BotBrowser Flag |
+|-----|-------|-----------------|
+| General | Browser Brand | `--bot-config-browser-brand` |
+| General | Profile File (.enc) | `--bot-profile` |
+| General | Start URL | positional argument |
+| Network | Proxy Server | `--proxy-server` |
+| Network | Proxy IP Override | `--proxy-ip` |
+| Identity | Locale | `--bot-config-locale` |
+| Identity | Timezone | `--bot-config-timezone` |
+| Identity | Color Scheme | `--bot-config-color-scheme` |
+| Fingerprint | WebGL | `--bot-config-webgl` |
+| Fingerprint | WebRTC | `--bot-config-webrtc` |
+| Fingerprint | Canvas Noise | `--bot-config-noise-canvas` |
+| Fingerprint | Noise Seed | `--bot-noise-seed` |
+| Behavior | Disable Debugger | `--bot-disable-debugger` |
+| Behavior | Always Active | `--bot-always-active` |
+| Session | Remote Debug Port | `--remote-debugging-port` |
+| Session | Bot Script | `--bot-script` |
+| Advanced | Custom Headers | `--bot-custom-headers` |
+| Advanced | Time Scale | `--bot-time-scale` |
+
+For the full list of supported flags see [BotBrowser CLI_FLAGS.md](https://github.com/botswin/BotBrowser).
+
+---
+
+## Project Structure
+
+```
+botbrowser-control/
+├── src/
+│ ├── main/
+│ │ └── main.js # Electron main process (IPC, browser launch, store)
+│ ├── preload/
+│ │ └── preload.js # contextBridge API exposed to renderer
+│ ├── renderer/
+│ │ ├── index.html # App shell HTML
+│ │ ├── styles/
+│ │ │ └── main.css # Full UI stylesheet (dark theme, GoLogin-inspired)
+│ │ └── js/
+│ │ └── app.js # Renderer logic (renderProfiles, editor, settings)
+│ └── assets/
+│ ├── icon.png # App icon (1024×1024, used for all platforms)
+│ └── logo.svg # Sidebar SVG logo
+├── preview/
+│ └── index.html # Standalone HTML UI preview (no Electron needed)
+├── package.json # Electron + electron-builder config
+├── BUILD.md # Detailed build instructions
+└── README.md # This file
+```
+
+---
+
+## Contributing
+
+1. Fork the repository
+2. Create a feature branch: `git checkout -b feature/my-feature`
+3. Make your changes
+4. Run the app to verify: `npm start`
+5. Commit and push: `git push origin feature/my-feature`
+6. Open a Pull Request
+
+---
+
+## License
+
+MIT — see [LICENSE](LICENSE) for details.
+
+---
+
+
\ No newline at end of file
diff --git a/botbrowser-control/package-lock.json b/botbrowser-control/package-lock.json
new file mode 100644
index 0000000..a0c00c8
--- /dev/null
+++ b/botbrowser-control/package-lock.json
@@ -0,0 +1,4435 @@
+{
+ "name": "botbrowser-control",
+ "version": "1.2.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "botbrowser-control",
+ "version": "1.2.0",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "electron-store": "^8.1.0",
+ "uuid": "^9.0.0"
+ },
+ "devDependencies": {
+ "electron": "^28.0.0",
+ "electron-builder": "^24.0.0"
+ }
+ },
+ "node_modules/@develar/schema-utils": {
+ "version": "2.6.5",
+ "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz",
+ "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^6.12.0",
+ "ajv-keywords": "^3.4.1"
+ },
+ "engines": {
+ "node": ">= 8.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/webpack"
+ }
+ },
+ "node_modules/@electron/asar": {
+ "version": "3.4.1",
+ "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz",
+ "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "commander": "^5.0.0",
+ "glob": "^7.1.6",
+ "minimatch": "^3.0.4"
+ },
+ "bin": {
+ "asar": "bin/asar.js"
+ },
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/@electron/asar/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@electron/asar/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@electron/get": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
+ "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "optionalDependencies": {
+ "global-agent": "^3.0.0"
+ }
+ },
+ "node_modules/@electron/notarize": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.2.1.tgz",
+ "integrity": "sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "fs-extra": "^9.0.1",
+ "promise-retry": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@electron/notarize/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@electron/notarize/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@electron/notarize/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@electron/osx-sign": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.0.5.tgz",
+ "integrity": "sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "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"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/@electron/osx-sign/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@electron/osx-sign/node_modules/isbinaryfile": {
+ "version": "4.0.10",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
+ "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 8.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/gjtorikian/"
+ }
+ },
+ "node_modules/@electron/osx-sign/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@electron/osx-sign/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@electron/universal": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz",
+ "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@electron/asar": "^3.2.1",
+ "@malept/cross-spawn-promise": "^1.1.0",
+ "debug": "^4.3.1",
+ "dir-compare": "^3.0.0",
+ "fs-extra": "^9.0.1",
+ "minimatch": "^3.0.4",
+ "plist": "^3.0.4"
+ },
+ "engines": {
+ "node": ">=8.6"
+ }
+ },
+ "node_modules/@electron/universal/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/@electron/universal/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@electron/universal/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@electron/universal/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/@electron/universal/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@isaacs/cliui": {
+ "version": "8.0.2",
+ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+ "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+ "dev": true,
+ "license": "ISC",
+ "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"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-regex": {
+ "version": "6.2.2",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz",
+ "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/ansi-styles": {
+ "version": "6.2.3",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz",
+ "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/emoji-regex": {
+ "version": "9.2.2",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+ "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@isaacs/cliui/node_modules/string-width": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+ "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "eastasianwidth": "^0.2.0",
+ "emoji-regex": "^9.2.2",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@isaacs/cliui/node_modules/wrap-ansi": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+ "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^6.1.0",
+ "string-width": "^5.0.1",
+ "strip-ansi": "^7.0.1"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/@malept/cross-spawn-promise": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
+ "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "individual",
+ "url": "https://github.com/sponsors/malept"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
+ }
+ ],
+ "license": "Apache-2.0",
+ "dependencies": {
+ "cross-spawn": "^7.0.1"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@malept/flatpak-bundler": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz",
+ "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "fs-extra": "^9.0.0",
+ "lodash": "^4.17.15",
+ "tmp-promise": "^3.0.2"
+ },
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
+ "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "at-least-node": "^1.0.0",
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/@malept/flatpak-bundler/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/@pkgjs/parseargs": {
+ "version": "0.11.0",
+ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=14"
+ }
+ },
+ "node_modules/@sindresorhus/is": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
+ "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/is?sponsor=1"
+ }
+ },
+ "node_modules/@szmarczak/http-timer": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
+ "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "defer-to-connect": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/@tootallnate/once": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz",
+ "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@types/cacheable-request": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
+ "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/http-cache-semantics": "*",
+ "@types/keyv": "^3.1.4",
+ "@types/node": "*",
+ "@types/responselike": "^1.0.0"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.13",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.13.tgz",
+ "integrity": "sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/fs-extra": {
+ "version": "9.0.13",
+ "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz",
+ "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/keyv": {
+ "version": "3.1.4",
+ "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
+ "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "18.19.130",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz",
+ "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~5.26.4"
+ }
+ },
+ "node_modules/@types/plist": {
+ "version": "3.0.5",
+ "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz",
+ "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*",
+ "xmlbuilder": ">=11.0.1"
+ }
+ },
+ "node_modules/@types/responselike": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
+ "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@types/verror": {
+ "version": "1.10.11",
+ "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz",
+ "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/@types/yauzl": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
+ "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@types/node": "*"
+ }
+ },
+ "node_modules/@xmldom/xmldom": {
+ "version": "0.8.12",
+ "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.12.tgz",
+ "integrity": "sha512-9k/gHF6n/pAi/9tqr3m3aqkuiNosYTurLLUtc7xQ9sxB/wm7WPygCv8GYa6mS0fLJEHhqMC1ATYhz++U/lRHqg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/7zip-bin": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz",
+ "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/agent-base": {
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+ "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6.0.0"
+ }
+ },
+ "node_modules/ajv": {
+ "version": "6.14.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz",
+ "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz",
+ "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependencies": {
+ "ajv": "^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "ajv": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/ajv-formats/node_modules/ajv": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/ajv-formats/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/ajv-keywords": {
+ "version": "3.5.2",
+ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz",
+ "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==",
+ "dev": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "ajv": "^6.9.1"
+ }
+ },
+ "node_modules/ansi-regex": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+ "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/app-builder-bin": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-4.0.0.tgz",
+ "integrity": "sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/app-builder-lib": {
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-24.13.3.tgz",
+ "integrity": "sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@develar/schema-utils": "~2.6.5",
+ "@electron/notarize": "2.2.1",
+ "@electron/osx-sign": "1.0.5",
+ "@electron/universal": "1.5.1",
+ "@malept/flatpak-bundler": "^0.4.0",
+ "@types/fs-extra": "9.0.13",
+ "async-exit-hook": "^2.0.1",
+ "bluebird-lst": "^1.0.9",
+ "builder-util": "24.13.1",
+ "builder-util-runtime": "9.2.4",
+ "chromium-pickle-js": "^0.2.0",
+ "debug": "^4.3.4",
+ "ejs": "^3.1.8",
+ "electron-publish": "24.13.1",
+ "form-data": "^4.0.0",
+ "fs-extra": "^10.1.0",
+ "hosted-git-info": "^4.1.0",
+ "is-ci": "^3.0.0",
+ "isbinaryfile": "^5.0.0",
+ "js-yaml": "^4.1.0",
+ "lazy-val": "^1.0.5",
+ "minimatch": "^5.1.1",
+ "read-config-file": "6.3.2",
+ "sanitize-filename": "^1.6.3",
+ "semver": "^7.3.8",
+ "tar": "^6.1.12",
+ "temp-file": "^3.4.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ },
+ "peerDependencies": {
+ "dmg-builder": "24.13.3",
+ "electron-builder-squirrel-windows": "24.13.3"
+ }
+ },
+ "node_modules/app-builder-lib/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/app-builder-lib/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/app-builder-lib/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/app-builder-lib/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/archiver": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz",
+ "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "archiver-utils": "^2.1.0",
+ "async": "^3.2.4",
+ "buffer-crc32": "^0.2.1",
+ "readable-stream": "^3.6.0",
+ "readdir-glob": "^1.1.2",
+ "tar-stream": "^2.2.0",
+ "zip-stream": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/archiver-utils": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz",
+ "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "glob": "^7.1.4",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.union": "^4.6.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^2.0.0"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/archiver-utils/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/archiver-utils/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/argparse": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+ "dev": true,
+ "license": "Python-2.0"
+ },
+ "node_modules/assert-plus": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
+ "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/astral-regex": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+ "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/async": {
+ "version": "3.2.6",
+ "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz",
+ "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/async-exit-hook": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz",
+ "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.12.0"
+ }
+ },
+ "node_modules/asynckit": {
+ "version": "0.4.0",
+ "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
+ "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/at-least-node": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+ "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/atomically": {
+ "version": "1.7.0",
+ "resolved": "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz",
+ "integrity": "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.12.0"
+ }
+ },
+ "node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/bl": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz",
+ "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "buffer": "^5.5.0",
+ "inherits": "^2.0.4",
+ "readable-stream": "^3.4.0"
+ }
+ },
+ "node_modules/bluebird": {
+ "version": "3.7.2",
+ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
+ "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/bluebird-lst": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz",
+ "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "bluebird": "^3.5.5"
+ }
+ },
+ "node_modules/boolean": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
+ "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
+ "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/brace-expansion": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.0.tgz",
+ "integrity": "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0"
+ }
+ },
+ "node_modules/buffer": {
+ "version": "5.7.1",
+ "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
+ "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.3.1",
+ "ieee754": "^1.1.13"
+ }
+ },
+ "node_modules/buffer-crc32": {
+ "version": "0.2.13",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/buffer-equal": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
+ "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/buffer-from": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
+ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/builder-util": {
+ "version": "24.13.1",
+ "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-24.13.1.tgz",
+ "integrity": "sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.1.6",
+ "7zip-bin": "~5.2.0",
+ "app-builder-bin": "4.0.0",
+ "bluebird-lst": "^1.0.9",
+ "builder-util-runtime": "9.2.4",
+ "chalk": "^4.1.2",
+ "cross-spawn": "^7.0.3",
+ "debug": "^4.3.4",
+ "fs-extra": "^10.1.0",
+ "http-proxy-agent": "^5.0.0",
+ "https-proxy-agent": "^5.0.1",
+ "is-ci": "^3.0.0",
+ "js-yaml": "^4.1.0",
+ "source-map-support": "^0.5.19",
+ "stat-mode": "^1.0.0",
+ "temp-file": "^3.4.0"
+ }
+ },
+ "node_modules/builder-util-runtime": {
+ "version": "9.2.4",
+ "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.4.tgz",
+ "integrity": "sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "debug": "^4.3.4",
+ "sax": "^1.2.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/builder-util/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/builder-util/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/builder-util/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/cacheable-lookup": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
+ "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.6.0"
+ }
+ },
+ "node_modules/cacheable-request": {
+ "version": "7.0.4",
+ "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
+ "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/call-bind-apply-helpers": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
+ "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
+ "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/chromium-pickle-js": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz",
+ "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ci-info": {
+ "version": "3.9.0",
+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz",
+ "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/sibiraj-s"
+ }
+ ],
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/cli-truncate": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+ "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "slice-ansi": "^3.0.0",
+ "string-width": "^4.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/cliui": {
+ "version": "8.0.1",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+ "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.1",
+ "wrap-ansi": "^7.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/clone-response": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
+ "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/combined-stream": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
+ "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "delayed-stream": "~1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.8"
+ }
+ },
+ "node_modules/commander": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
+ "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/compare-version": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
+ "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/compress-commons": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz",
+ "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "buffer-crc32": "^0.2.13",
+ "crc32-stream": "^4.0.2",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/concat-map": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+ "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/conf": {
+ "version": "10.2.0",
+ "resolved": "https://registry.npmjs.org/conf/-/conf-10.2.0.tgz",
+ "integrity": "sha512-8fLl9F04EJqjSqH+QjITQfJF8BrOVaYr1jewVgSRAEWePfxT0sku4w2hrGQ60BC/TNLGQ2pgxNlTbWQmMPFvXg==",
+ "license": "MIT",
+ "dependencies": {
+ "ajv": "^8.6.3",
+ "ajv-formats": "^2.1.1",
+ "atomically": "^1.7.0",
+ "debounce-fn": "^4.0.0",
+ "dot-prop": "^6.0.1",
+ "env-paths": "^2.2.1",
+ "json-schema-typed": "^7.0.3",
+ "onetime": "^5.1.2",
+ "pkg-up": "^3.1.0",
+ "semver": "^7.3.5"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/conf/node_modules/ajv": {
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz",
+ "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==",
+ "license": "MIT",
+ "dependencies": {
+ "fast-deep-equal": "^3.1.3",
+ "fast-uri": "^3.0.1",
+ "json-schema-traverse": "^1.0.0",
+ "require-from-string": "^2.0.2"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/epoberezkin"
+ }
+ },
+ "node_modules/conf/node_modules/json-schema-traverse": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+ "license": "MIT"
+ },
+ "node_modules/conf/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/config-file-ts": {
+ "version": "0.2.6",
+ "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.6.tgz",
+ "integrity": "sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "glob": "^10.3.10",
+ "typescript": "^5.3.3"
+ }
+ },
+ "node_modules/config-file-ts/node_modules/glob": {
+ "version": "10.5.0",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz",
+ "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "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"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/config-file-ts/node_modules/minimatch": {
+ "version": "9.0.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz",
+ "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.2"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/config-file-ts/node_modules/minipass": {
+ "version": "7.1.3",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz",
+ "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/core-util-is": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
+ "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/crc": {
+ "version": "3.8.0",
+ "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz",
+ "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "buffer": "^5.1.0"
+ }
+ },
+ "node_modules/crc-32": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz",
+ "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "bin": {
+ "crc32": "bin/crc32.njs"
+ },
+ "engines": {
+ "node": ">=0.8"
+ }
+ },
+ "node_modules/crc32-stream": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz",
+ "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "crc-32": "^1.2.0",
+ "readable-stream": "^3.4.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/cross-spawn": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
+ "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "path-key": "^3.1.0",
+ "shebang-command": "^2.0.0",
+ "which": "^2.0.1"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/debounce-fn": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz",
+ "integrity": "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decompress-response": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
+ "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mimic-response": "^3.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/decompress-response/node_modules/mimic-response": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
+ "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/defer-to-connect": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
+ "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/define-data-property": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
+ "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/define-properties": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
+ "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "define-data-property": "^1.0.1",
+ "has-property-descriptors": "^1.0.0",
+ "object-keys": "^1.1.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/delayed-stream": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
+ "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/detect-node": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
+ "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/dir-compare": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
+ "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-equal": "^1.0.0",
+ "minimatch": "^3.0.4"
+ }
+ },
+ "node_modules/dir-compare/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/dir-compare/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/dmg-builder": {
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-24.13.3.tgz",
+ "integrity": "sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "app-builder-lib": "24.13.3",
+ "builder-util": "24.13.1",
+ "builder-util-runtime": "9.2.4",
+ "fs-extra": "^10.1.0",
+ "iconv-lite": "^0.6.2",
+ "js-yaml": "^4.1.0"
+ },
+ "optionalDependencies": {
+ "dmg-license": "^1.0.11"
+ }
+ },
+ "node_modules/dmg-builder/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/dmg-builder/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/dmg-builder/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/dmg-license": {
+ "version": "1.0.11",
+ "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz",
+ "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "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"
+ },
+ "bin": {
+ "dmg-license": "bin/dmg-license.js"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/dot-prop": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz",
+ "integrity": "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA==",
+ "license": "MIT",
+ "dependencies": {
+ "is-obj": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/dotenv": {
+ "version": "9.0.2",
+ "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-9.0.2.tgz",
+ "integrity": "sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/dotenv-expand": {
+ "version": "5.1.0",
+ "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-5.1.0.tgz",
+ "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/dunder-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
+ "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "call-bind-apply-helpers": "^1.0.1",
+ "es-errors": "^1.3.0",
+ "gopd": "^1.2.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/eastasianwidth": {
+ "version": "0.2.0",
+ "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/ejs": {
+ "version": "3.1.10",
+ "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz",
+ "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "jake": "^10.8.5"
+ },
+ "bin": {
+ "ejs": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/electron": {
+ "version": "28.3.3",
+ "resolved": "https://registry.npmjs.org/electron/-/electron-28.3.3.tgz",
+ "integrity": "sha512-ObKMLSPNhomtCOBAxFS8P2DW/4umkh72ouZUlUKzXGtYuPzgr1SYhskhFWgzAsPtUzhL2CzyV2sfbHcEW4CXqw==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "@electron/get": "^2.0.0",
+ "@types/node": "^18.11.18",
+ "extract-zip": "^2.0.1"
+ },
+ "bin": {
+ "electron": "cli.js"
+ },
+ "engines": {
+ "node": ">= 12.20.55"
+ }
+ },
+ "node_modules/electron-builder": {
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-24.13.3.tgz",
+ "integrity": "sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "app-builder-lib": "24.13.3",
+ "builder-util": "24.13.1",
+ "builder-util-runtime": "9.2.4",
+ "chalk": "^4.1.2",
+ "dmg-builder": "24.13.3",
+ "fs-extra": "^10.1.0",
+ "is-ci": "^3.0.0",
+ "lazy-val": "^1.0.5",
+ "read-config-file": "6.3.2",
+ "simple-update-notifier": "2.0.0",
+ "yargs": "^17.6.2"
+ },
+ "bin": {
+ "electron-builder": "cli.js",
+ "install-app-deps": "install-app-deps.js"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/electron-builder-squirrel-windows": {
+ "version": "24.13.3",
+ "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-24.13.3.tgz",
+ "integrity": "sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "app-builder-lib": "24.13.3",
+ "archiver": "^5.3.1",
+ "builder-util": "24.13.1",
+ "fs-extra": "^10.1.0"
+ }
+ },
+ "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/electron-builder-squirrel-windows/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/electron-builder/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/electron-builder/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/electron-builder/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/electron-publish": {
+ "version": "24.13.1",
+ "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-24.13.1.tgz",
+ "integrity": "sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@types/fs-extra": "^9.0.11",
+ "builder-util": "24.13.1",
+ "builder-util-runtime": "9.2.4",
+ "chalk": "^4.1.2",
+ "fs-extra": "^10.1.0",
+ "lazy-val": "^1.0.5",
+ "mime": "^2.5.2"
+ }
+ },
+ "node_modules/electron-publish/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/electron-publish/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/electron-publish/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/electron-store": {
+ "version": "8.2.0",
+ "resolved": "https://registry.npmjs.org/electron-store/-/electron-store-8.2.0.tgz",
+ "integrity": "sha512-ukLL5Bevdil6oieAOXz3CMy+OgaItMiVBg701MNlG6W5RaC0AHN7rvlqTCmeb6O7jP0Qa1KKYTE0xV0xbhF4Hw==",
+ "license": "MIT",
+ "dependencies": {
+ "conf": "^10.2.0",
+ "type-fest": "^2.17.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/emoji-regex": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/end-of-stream": {
+ "version": "1.4.5",
+ "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
+ "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "once": "^1.4.0"
+ }
+ },
+ "node_modules/env-paths": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+ "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/err-code": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz",
+ "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/es-define-property": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
+ "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-errors": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
+ "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-object-atoms": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
+ "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es-set-tostringtag": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
+ "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "es-errors": "^1.3.0",
+ "get-intrinsic": "^1.2.6",
+ "has-tostringtag": "^1.0.2",
+ "hasown": "^2.0.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/es6-error": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
+ "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/extract-zip": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
+ "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "debug": "^4.1.1",
+ "get-stream": "^5.1.0",
+ "yauzl": "^2.10.0"
+ },
+ "bin": {
+ "extract-zip": "cli.js"
+ },
+ "engines": {
+ "node": ">= 10.17.0"
+ },
+ "optionalDependencies": {
+ "@types/yauzl": "^2.9.1"
+ }
+ },
+ "node_modules/extsprintf": {
+ "version": "1.4.1",
+ "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz",
+ "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==",
+ "dev": true,
+ "engines": [
+ "node >=0.6.0"
+ ],
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/fast-json-stable-stringify": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+ "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/fast-uri": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz",
+ "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/fd-slicer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
+ "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pend": "~1.2.0"
+ }
+ },
+ "node_modules/filelist": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz",
+ "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "minimatch": "^5.0.1"
+ }
+ },
+ "node_modules/find-up": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+ "license": "MIT",
+ "dependencies": {
+ "locate-path": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/foreground-child": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz",
+ "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "cross-spawn": "^7.0.6",
+ "signal-exit": "^4.0.1"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/form-data": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz",
+ "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "asynckit": "^0.4.0",
+ "combined-stream": "^1.0.8",
+ "es-set-tostringtag": "^2.1.0",
+ "hasown": "^2.0.2",
+ "mime-types": "^2.1.12"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/fs-constants": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz",
+ "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/fs-extra": {
+ "version": "8.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
+ "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^4.0.0",
+ "universalify": "^0.1.0"
+ },
+ "engines": {
+ "node": ">=6 <7 || >=8"
+ }
+ },
+ "node_modules/fs-minipass": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz",
+ "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^3.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/fs-minipass/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/fs.realpath": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+ "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/function-bind": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
+ "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-caller-file": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": "6.* || 8.* || >= 10.*"
+ }
+ },
+ "node_modules/get-intrinsic": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
+ "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/get-proto": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
+ "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "dunder-proto": "^1.0.1",
+ "es-object-atoms": "^1.0.0"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/get-stream": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
+ "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "pump": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/glob": {
+ "version": "7.2.3",
+ "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+ "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+ "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "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"
+ },
+ "engines": {
+ "node": "*"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/glob/node_modules/brace-expansion": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.14.tgz",
+ "integrity": "sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
+ }
+ },
+ "node_modules/glob/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
+ "engines": {
+ "node": "*"
+ }
+ },
+ "node_modules/global-agent": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
+ "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "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"
+ },
+ "engines": {
+ "node": ">=10.0"
+ }
+ },
+ "node_modules/global-agent/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true,
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/globalthis": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
+ "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "define-properties": "^1.2.1",
+ "gopd": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/gopd": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
+ "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/got": {
+ "version": "11.8.6",
+ "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
+ "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
+ "dev": true,
+ "license": "MIT",
+ "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"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ },
+ "funding": {
+ "url": "https://github.com/sindresorhus/got?sponsor=1"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/has-property-descriptors": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
+ "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "es-define-property": "^1.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-symbols": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
+ "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/has-tostringtag": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
+ "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-symbols": "^1.0.3"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/hasown": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
+ "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "function-bind": "^1.1.2"
+ },
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/hosted-git-info": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+ "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "lru-cache": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/http-cache-semantics": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
+ "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
+ "dev": true,
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/http-proxy-agent": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz",
+ "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tootallnate/once": "2",
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/http2-wrapper": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
+ "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "quick-lru": "^5.1.1",
+ "resolve-alpn": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=10.19.0"
+ }
+ },
+ "node_modules/https-proxy-agent": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+ "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "agent-base": "6",
+ "debug": "4"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/iconv-corefoundation": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
+ "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "dependencies": {
+ "cli-truncate": "^2.1.0",
+ "node-addon-api": "^1.6.3"
+ },
+ "engines": {
+ "node": "^8.11.2 || >=10"
+ }
+ },
+ "node_modules/iconv-lite": {
+ "version": "0.6.3",
+ "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
+ "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "safer-buffer": ">= 2.1.2 < 3.0.0"
+ },
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/ieee754": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
+ "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "BSD-3-Clause"
+ },
+ "node_modules/inflight": {
+ "version": "1.0.6",
+ "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+ "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+ "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "once": "^1.3.0",
+ "wrappy": "1"
+ }
+ },
+ "node_modules/inherits": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+ "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/is-ci": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz",
+ "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ci-info": "^3.2.0"
+ },
+ "bin": {
+ "is-ci": "bin.js"
+ }
+ },
+ "node_modules/is-fullwidth-code-point": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+ "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/is-obj": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz",
+ "integrity": "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/isarray": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+ "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/isbinaryfile": {
+ "version": "5.0.7",
+ "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz",
+ "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 18.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/gjtorikian/"
+ }
+ },
+ "node_modules/isexe": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+ "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/jackspeak": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz",
+ "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "@isaacs/cliui": "^8.0.2"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ },
+ "optionalDependencies": {
+ "@pkgjs/parseargs": "^0.11.0"
+ }
+ },
+ "node_modules/jake": {
+ "version": "10.9.4",
+ "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz",
+ "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "async": "^3.2.6",
+ "filelist": "^1.0.4",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "jake": "bin/cli.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/js-yaml": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz",
+ "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
+ },
+ "node_modules/json-buffer": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+ "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-traverse": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+ "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/json-schema-typed": {
+ "version": "7.0.3",
+ "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz",
+ "integrity": "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A==",
+ "license": "BSD-2-Clause"
+ },
+ "node_modules/json-stringify-safe": {
+ "version": "5.0.1",
+ "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
+ "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
+ "dev": true,
+ "license": "ISC",
+ "optional": true
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/jsonfile": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
+ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
+ "dev": true,
+ "license": "MIT",
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/keyv": {
+ "version": "4.5.4",
+ "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+ "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "json-buffer": "3.0.1"
+ }
+ },
+ "node_modules/lazy-val": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz",
+ "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lazystream": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz",
+ "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "readable-stream": "^2.0.5"
+ },
+ "engines": {
+ "node": ">= 0.6.3"
+ }
+ },
+ "node_modules/lazystream/node_modules/readable-stream": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "core-util-is": "~1.0.0",
+ "inherits": "~2.0.3",
+ "isarray": "~1.0.0",
+ "process-nextick-args": "~2.0.0",
+ "safe-buffer": "~5.1.1",
+ "string_decoder": "~1.1.1",
+ "util-deprecate": "~1.0.1"
+ }
+ },
+ "node_modules/lazystream/node_modules/safe-buffer": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+ "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lazystream/node_modules/string_decoder": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "safe-buffer": "~5.1.0"
+ }
+ },
+ "node_modules/locate-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
+ "license": "MIT",
+ "dependencies": {
+ "p-locate": "^3.0.0",
+ "path-exists": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lodash": {
+ "version": "4.18.1",
+ "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz",
+ "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/lodash.defaults": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz",
+ "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lodash.difference": {
+ "version": "4.5.0",
+ "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz",
+ "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lodash.flatten": {
+ "version": "4.4.0",
+ "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz",
+ "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lodash.isplainobject": {
+ "version": "4.0.6",
+ "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz",
+ "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lodash.union": {
+ "version": "4.6.0",
+ "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz",
+ "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/lowercase-keys": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
+ "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+ "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/matcher": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
+ "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "escape-string-regexp": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/math-intrinsics": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
+ "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/mime": {
+ "version": "2.6.0",
+ "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz",
+ "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mime": "cli.js"
+ },
+ "engines": {
+ "node": ">=4.0.0"
+ }
+ },
+ "node_modules/mime-db": {
+ "version": "1.52.0",
+ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
+ "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mime-types": {
+ "version": "2.1.35",
+ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
+ "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "mime-db": "1.52.0"
+ },
+ "engines": {
+ "node": ">= 0.6"
+ }
+ },
+ "node_modules/mimic-fn": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz",
+ "integrity": "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mimic-response": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
+ "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/minimatch": {
+ "version": "5.1.9",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz",
+ "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/minimist": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
+ "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/ljharb"
+ }
+ },
+ "node_modules/minipass": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz",
+ "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz",
+ "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^3.0.0",
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/minizlib/node_modules/minipass": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz",
+ "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz",
+ "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "bin/cmd.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/node-addon-api": {
+ "version": "1.7.2",
+ "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz",
+ "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/normalize-path": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+ "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/normalize-url": {
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
+ "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/object-keys": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
+ "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 0.4"
+ }
+ },
+ "node_modules/once": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+ "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "wrappy": "1"
+ }
+ },
+ "node_modules/onetime": {
+ "version": "5.1.2",
+ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz",
+ "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==",
+ "license": "MIT",
+ "dependencies": {
+ "mimic-fn": "^2.1.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/onetime/node_modules/mimic-fn": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz",
+ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/p-cancelable": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
+ "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-limit": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+ "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+ "license": "MIT",
+ "dependencies": {
+ "p-try": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-locate": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
+ "license": "MIT",
+ "dependencies": {
+ "p-limit": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/p-try": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/package-json-from-dist": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
+ "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0"
+ },
+ "node_modules/path-exists": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
+ "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/path-is-absolute": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+ "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/path-key": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+ "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/path-scurry": {
+ "version": "1.11.1",
+ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz",
+ "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "dependencies": {
+ "lru-cache": "^10.2.0",
+ "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+ },
+ "engines": {
+ "node": ">=16 || 14 >=14.18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/path-scurry/node_modules/lru-cache": {
+ "version": "10.4.3",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz",
+ "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/pend": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
+ "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/pkg-up": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz",
+ "integrity": "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA==",
+ "license": "MIT",
+ "dependencies": {
+ "find-up": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/plist": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz",
+ "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@xmldom/xmldom": "^0.8.8",
+ "base64-js": "^1.5.1",
+ "xmlbuilder": "^15.1.1"
+ },
+ "engines": {
+ "node": ">=10.4.0"
+ }
+ },
+ "node_modules/process-nextick-args": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+ "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/progress": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
+ "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.4.0"
+ }
+ },
+ "node_modules/promise-retry": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz",
+ "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "err-code": "^2.0.2",
+ "retry": "^0.12.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/pump": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
+ "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "end-of-stream": "^1.1.0",
+ "once": "^1.3.1"
+ }
+ },
+ "node_modules/punycode": {
+ "version": "2.3.1",
+ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+ "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/quick-lru": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
+ "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/read-config-file": {
+ "version": "6.3.2",
+ "resolved": "https://registry.npmjs.org/read-config-file/-/read-config-file-6.3.2.tgz",
+ "integrity": "sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "config-file-ts": "^0.2.4",
+ "dotenv": "^9.0.2",
+ "dotenv-expand": "^5.1.0",
+ "js-yaml": "^4.1.0",
+ "json5": "^2.2.0",
+ "lazy-val": "^1.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/readable-stream": {
+ "version": "3.6.2",
+ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz",
+ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "inherits": "^2.0.3",
+ "string_decoder": "^1.1.1",
+ "util-deprecate": "^1.0.1"
+ },
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/readdir-glob": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz",
+ "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "peer": true,
+ "dependencies": {
+ "minimatch": "^5.1.0"
+ }
+ },
+ "node_modules/require-directory": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+ "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/require-from-string": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+ "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/resolve-alpn": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
+ "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/responselike": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
+ "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "lowercase-keys": "^2.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/roarr": {
+ "version": "2.15.4",
+ "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
+ "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true,
+ "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"
+ },
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/safe-buffer": {
+ "version": "5.2.1",
+ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+ "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/safer-buffer": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
+ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/sanitize-filename": {
+ "version": "1.6.4",
+ "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.4.tgz",
+ "integrity": "sha512-9ZyI08PsvdQl2r/bBIGubpVdR3RR9sY6RDiWFPreA21C/EFlQhmgo20UZlNjZMMZNubusLhAQozkA0Od5J21Eg==",
+ "dev": true,
+ "license": "WTFPL OR ISC",
+ "dependencies": {
+ "truncate-utf8-bytes": "^1.0.0"
+ }
+ },
+ "node_modules/sax": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.0.tgz",
+ "integrity": "sha512-6R3J5M4AcbtLUdZmRv2SygeVaM7IhrLXu9BmnOGmmACak8fiUtOsYNWUS4uK7upbmHIBbLBeFeI//477BKLBzA==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=11.0.0"
+ }
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/semver-compare": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
+ "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/serialize-error": {
+ "version": "7.0.1",
+ "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
+ "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "type-fest": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/serialize-error/node_modules/type-fest": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
+ "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
+ "dev": true,
+ "license": "(MIT OR CC0-1.0)",
+ "optional": true,
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/shebang-command": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+ "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "shebang-regex": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/shebang-regex": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/signal-exit": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+ "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/simple-update-notifier": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+ "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "semver": "^7.5.3"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/simple-update-notifier/node_modules/semver": {
+ "version": "7.7.4",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz",
+ "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/slice-ansi": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+ "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "astral-regex": "^2.0.0",
+ "is-fullwidth-code-point": "^3.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/smart-buffer": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz",
+ "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "engines": {
+ "node": ">= 6.0.0",
+ "npm": ">= 3.0.0"
+ }
+ },
+ "node_modules/source-map": {
+ "version": "0.6.1",
+ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
+ "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/source-map-support": {
+ "version": "0.5.21",
+ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz",
+ "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-from": "^1.0.0",
+ "source-map": "^0.6.0"
+ }
+ },
+ "node_modules/sprintf-js": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
+ "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "optional": true
+ },
+ "node_modules/stat-mode": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz",
+ "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 6"
+ }
+ },
+ "node_modules/string_decoder": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
+ "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "safe-buffer": "~5.2.0"
+ }
+ },
+ "node_modules/string-width": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/string-width-cjs": {
+ "name": "string-width",
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+ "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "emoji-regex": "^8.0.0",
+ "is-fullwidth-code-point": "^3.0.0",
+ "strip-ansi": "^6.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/strip-ansi-cjs": {
+ "name": "strip-ansi",
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+ "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^5.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/sumchecker": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
+ "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.1.0"
+ },
+ "engines": {
+ "node": ">= 8.0"
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tar": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz",
+ "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==",
+ "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
+ "dev": true,
+ "license": "ISC",
+ "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"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/tar-stream": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz",
+ "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "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"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/temp-file": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz",
+ "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "async-exit-hook": "^2.0.1",
+ "fs-extra": "^10.0.0"
+ }
+ },
+ "node_modules/temp-file/node_modules/fs-extra": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
+ "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.0",
+ "jsonfile": "^6.0.1",
+ "universalify": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/temp-file/node_modules/jsonfile": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
+ "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
+ },
+ "node_modules/temp-file/node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
+ "node_modules/tmp": {
+ "version": "0.2.5",
+ "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
+ "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=14.14"
+ }
+ },
+ "node_modules/tmp-promise": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz",
+ "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tmp": "^0.2.0"
+ }
+ },
+ "node_modules/truncate-utf8-bytes": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz",
+ "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==",
+ "dev": true,
+ "license": "WTFPL",
+ "dependencies": {
+ "utf8-byte-length": "^1.0.1"
+ }
+ },
+ "node_modules/type-fest": {
+ "version": "2.19.0",
+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz",
+ "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==",
+ "license": "(MIT OR CC0-1.0)",
+ "engines": {
+ "node": ">=12.20"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.9.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
+ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "5.26.5",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+ "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/universalify": {
+ "version": "0.1.2",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
+ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4.0.0"
+ }
+ },
+ "node_modules/uri-js": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+ "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+ "dev": true,
+ "license": "BSD-2-Clause",
+ "dependencies": {
+ "punycode": "^2.1.0"
+ }
+ },
+ "node_modules/utf8-byte-length": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz",
+ "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==",
+ "dev": true,
+ "license": "(WTFPL OR MIT)"
+ },
+ "node_modules/util-deprecate": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true
+ },
+ "node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/verror": {
+ "version": "1.10.1",
+ "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz",
+ "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==",
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "assert-plus": "^1.0.0",
+ "core-util-is": "1.0.2",
+ "extsprintf": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=0.6.0"
+ }
+ },
+ "node_modules/which": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+ "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "isexe": "^2.0.0"
+ },
+ "bin": {
+ "node-which": "bin/node-which"
+ },
+ "engines": {
+ "node": ">= 8"
+ }
+ },
+ "node_modules/wrap-ansi": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrap-ansi-cjs": {
+ "name": "wrap-ansi",
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+ "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/wrap-ansi?sponsor=1"
+ }
+ },
+ "node_modules/wrappy": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/xmlbuilder": {
+ "version": "15.1.1",
+ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
+ "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=8.0"
+ }
+ },
+ "node_modules/y18n": {
+ "version": "5.0.8",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+ "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/yargs": {
+ "version": "17.7.2",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+ "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^8.0.1",
+ "escalade": "^3.1.1",
+ "get-caller-file": "^2.0.5",
+ "require-directory": "^2.1.1",
+ "string-width": "^4.2.3",
+ "y18n": "^5.0.5",
+ "yargs-parser": "^21.1.1"
+ },
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yargs-parser": {
+ "version": "21.1.1",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+ "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=12"
+ }
+ },
+ "node_modules/yauzl": {
+ "version": "2.10.0",
+ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
+ "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "buffer-crc32": "~0.2.3",
+ "fd-slicer": "~1.1.0"
+ }
+ },
+ "node_modules/zip-stream": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz",
+ "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "archiver-utils": "^3.0.4",
+ "compress-commons": "^4.1.2",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/zip-stream/node_modules/archiver-utils": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz",
+ "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==",
+ "dev": true,
+ "license": "MIT",
+ "peer": true,
+ "dependencies": {
+ "glob": "^7.2.3",
+ "graceful-fs": "^4.2.0",
+ "lazystream": "^1.0.0",
+ "lodash.defaults": "^4.2.0",
+ "lodash.difference": "^4.5.0",
+ "lodash.flatten": "^4.4.0",
+ "lodash.isplainobject": "^4.0.6",
+ "lodash.union": "^4.6.0",
+ "normalize-path": "^3.0.0",
+ "readable-stream": "^3.6.0"
+ },
+ "engines": {
+ "node": ">= 10"
+ }
+ }
+ }
+}
diff --git a/botbrowser-control/package.json b/botbrowser-control/package.json
new file mode 100644
index 0000000..b4563d3
--- /dev/null
+++ b/botbrowser-control/package.json
@@ -0,0 +1,182 @@
+{
+ "name": "botbrowser-control",
+ "version": "1.2.2",
+ "description": "BotBrowser Control Center - Professional Browser Profile Manager",
+ "main": "src/main/main.js",
+ "scripts": {
+ "start": "electron .",
+ "dev": "electron . --dev",
+ "build": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --mac --win --linux",
+ "build:mac": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --mac",
+ "build:mac:x64": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --mac --x64",
+ "build:mac:arm64": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --mac --arm64",
+ "build:win": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --win",
+ "build:win:x64": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --win --x64",
+ "build:win:ia32": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --win --ia32",
+ "build:win:arm64": "CSC_IDENTITY_AUTO_DISCOVERY=false electron-builder --win --arm64",
+ "build:linux": "electron-builder --linux",
+ "build:linux:x64": "electron-builder --linux --x64",
+ "build:linux:arm64": "electron-builder --linux --arm64",
+ "build:linux:rpm": "electron-builder --linux --x64 --config.linux.target=rpm",
+ "pack": "electron-builder --dir",
+ "postinstall": "electron-builder install-app-deps"
+ },
+ "keywords": [
+ "botbrowser",
+ "antidetect",
+ "fingerprint",
+ "privacy",
+ "browser"
+ ],
+ "author": {
+ "name": "BotBrowser Control",
+ "email": "support@botbrowser.io"
+ },
+ "homepage": "https://github.com/botswin/BotBrowser",
+ "license": "MIT",
+ "devDependencies": {
+ "electron": "^28.0.0",
+ "electron-builder": "^24.0.0"
+ },
+ "dependencies": {
+ "electron-store": "^8.1.0",
+ "uuid": "^9.0.0"
+ },
+ "build": {
+ "appId": "io.botbrowser.control",
+ "productName": "BotBrowser Control",
+ "copyright": "Copyright \u00a9 2025 BotBrowser",
+ "directories": {
+ "output": "dist",
+ "buildResources": "build-resources"
+ },
+ "files": [
+ "src/**/*",
+ "node_modules/**/*",
+ "!node_modules/.cache",
+ "!**/*.map"
+ ],
+ "mac": {
+ "category": "public.app-category.developer-tools",
+ "icon": "src/assets/icon.png",
+ "target": [
+ {
+ "target": "dmg",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ },
+ {
+ "target": "zip",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ }
+ ],
+ "darkModeSupport": true,
+ "hardenedRuntime": false,
+ "gatekeeperAssess": false,
+ "identity": null,
+ "minimumSystemVersion": "11.0"
+ },
+ "dmg": {
+ "title": "BotBrowser Control",
+ "contents": [
+ {
+ "x": 130,
+ "y": 220
+ },
+ {
+ "x": 410,
+ "y": 220,
+ "type": "link",
+ "path": "/Applications"
+ }
+ ],
+ "window": {
+ "width": 540,
+ "height": 380
+ }
+ },
+ "win": {
+ "icon": "src/assets/icon.ico",
+ "target": [
+ {
+ "target": "nsis",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ },
+ {
+ "target": "portable",
+ "arch": [
+ "x64"
+ ]
+ },
+ {
+ "target": "zip",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ }
+ ],
+ "verifyUpdateCodeSignature": false,
+ "forceCodeSigning": false
+ },
+ "nsis": {
+ "oneClick": false,
+ "allowToChangeInstallationDirectory": true,
+ "createDesktopShortcut": true,
+ "createStartMenuShortcut": true,
+ "shortcutName": "BotBrowser Control"
+ },
+ "linux": {
+ "icon": "src/assets/icon.png",
+ "category": "Development",
+ "target": [
+ {
+ "target": "AppImage",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ },
+ {
+ "target": "deb",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ },
+ {
+ "target": "tar.gz",
+ "arch": [
+ "x64",
+ "arm64"
+ ]
+ }
+ ],
+ "maintainer": "BotBrowser ",
+ "description": "Professional Browser Profile Manager for BotBrowser"
+ },
+ "deb": {
+ "depends": [
+ "libnotify4",
+ "libxtst6",
+ "libnss3"
+ ]
+ },
+ "rpm": {
+ "depends": [
+ "libnotify",
+ "libXtst",
+ "nss"
+ ]
+ },
+ "publish": null
+ }
+}
\ No newline at end of file
diff --git a/botbrowser-control/preview/screenshot_editor.png b/botbrowser-control/preview/screenshot_editor.png
new file mode 100644
index 0000000..8efaa2c
Binary files /dev/null and b/botbrowser-control/preview/screenshot_editor.png differ
diff --git a/botbrowser-control/preview/screenshot_profiles.png b/botbrowser-control/preview/screenshot_profiles.png
new file mode 100644
index 0000000..c54f5d8
Binary files /dev/null and b/botbrowser-control/preview/screenshot_profiles.png differ
diff --git a/botbrowser-control/preview/screenshot_settings.png b/botbrowser-control/preview/screenshot_settings.png
new file mode 100644
index 0000000..ee3b232
Binary files /dev/null and b/botbrowser-control/preview/screenshot_settings.png differ
diff --git a/botbrowser-control/src/assets/icon.ico b/botbrowser-control/src/assets/icon.ico
new file mode 100644
index 0000000..68bfedd
Binary files /dev/null and b/botbrowser-control/src/assets/icon.ico differ
diff --git a/botbrowser-control/src/assets/icon.png b/botbrowser-control/src/assets/icon.png
new file mode 100644
index 0000000..f6a92c0
Binary files /dev/null and b/botbrowser-control/src/assets/icon.png differ
diff --git a/botbrowser-control/src/assets/logo.svg b/botbrowser-control/src/assets/logo.svg
new file mode 100644
index 0000000..ad926db
--- /dev/null
+++ b/botbrowser-control/src/assets/logo.svg
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/botbrowser-control/src/main/main.js b/botbrowser-control/src/main/main.js
new file mode 100644
index 0000000..c1444e2
--- /dev/null
+++ b/botbrowser-control/src/main/main.js
@@ -0,0 +1,1374 @@
+const { app, BrowserWindow, ipcMain, dialog, shell, Menu, nativeTheme, Notification } = require('electron');
+const path = require('path');
+const fs = require('fs');
+const os = require('os');
+const net = require('net');
+const { spawn, execFile } = require('child_process');
+const https = require('https');
+const http = require('http');
+const Store = require('electron-store');
+const { v4: uuidv4 } = require('uuid');
+
+// ─── Fix app name BEFORE anything else ───
+app.setName('BotBrowser Control');
+
+// ─── Platform-aware defaults ──────────────────────────────────────────────────
+const IS_WIN = process.platform === 'win32';
+const IS_MAC = process.platform === 'darwin';
+const IS_LINUX = process.platform === 'linux';
+
+function getDefaultBotBrowserPath() {
+ if (IS_MAC) return '/Applications/Chromium.app/Contents/MacOS/Chromium';
+ if (IS_WIN) return 'C:\\Program Files\\BotBrowser\\chrome.exe';
+ return '/usr/bin/botbrowser';
+}
+
+function getDefaultUserDataDir() {
+ return path.join(app.getPath('userData'), 'browser-profiles');
+}
+
+const DEFAULT_BOTBROWSER_PATH = getDefaultBotBrowserPath();
+
+// ─── Persistent store ─────────────────────────────────────────────────────────
+const store = new Store({
+ name: 'botbrowser-control',
+ defaults: {
+ profiles: [],
+ settings: {
+ botBrowserPath: DEFAULT_BOTBROWSER_PATH,
+ defaultUserDataDir: getDefaultUserDataDir(),
+ theme: 'dark',
+ defaultProxy: '',
+ autoLaunch: false,
+ },
+ windowBounds: { width: 1280, height: 800 },
+ lastSeenKernelRelease: null,
+ lastSeenControlRelease: null,
+ cachedKernelReleases: null,
+ }
+});
+
+// ─── Runtime state ────────────────────────────────────────────────────────────
+const runningInstances = new Map();
+const tempFiles = new Map();
+let mainWindow = null;
+
+// ─── Window ───────────────────────────────────────────────────────────────────
+function createWindow() {
+ const bounds = store.get('windowBounds');
+
+ mainWindow = new BrowserWindow({
+ width: bounds.width || 1280,
+ height: bounds.height || 800,
+ minWidth: 960,
+ minHeight: 600,
+ ...(IS_MAC ? {
+ titleBarStyle: 'hiddenInset',
+ trafficLightPosition: { x: 16, y: 18 },
+ vibrancy: 'under-window',
+ visualEffectState: 'active',
+ } : {
+ frame: true,
+ }),
+ backgroundColor: '#2c3e50',
+ webPreferences: {
+ preload: path.join(__dirname, '../preload/preload.js'),
+ contextIsolation: true,
+ nodeIntegration: false,
+ sandbox: false,
+ devTools: false, // disable devtools
+ }
+ });
+
+ mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
+
+ // Prevent devtools from opening
+ mainWindow.webContents.on('devtools-opened', () => {
+ mainWindow.webContents.closeDevTools();
+ });
+
+ mainWindow.on('resize', () => {
+ const [width, height] = mainWindow.getSize();
+ store.set('windowBounds', { width, height });
+ });
+
+ mainWindow.on('closed', () => { mainWindow = null; });
+
+ buildMenu();
+}
+
+function buildMenu() {
+ const template = [
+ ...(IS_MAC ? [{
+ label: app.getName(),
+ submenu: [
+ { role: 'about' },
+ { type: 'separator' },
+ { role: 'services' },
+ { type: 'separator' },
+ { role: 'hide' },
+ { role: 'hideOthers' },
+ { role: 'unhide' },
+ { type: 'separator' },
+ { role: 'quit' }
+ ]
+ }] : []),
+ {
+ label: 'File',
+ submenu: [
+ { label: 'New Profile', accelerator: IS_MAC ? 'Cmd+N' : 'Ctrl+N', click: () => mainWindow?.webContents.send('action', 'new-profile') },
+ { type: 'separator' },
+ IS_MAC ? { role: 'close' } : { role: 'quit' }
+ ]
+ },
+ {
+ label: 'Edit',
+ submenu: [
+ { role: 'undo' },
+ { role: 'redo' },
+ { type: 'separator' },
+ { role: 'cut' },
+ { role: 'copy' },
+ { role: 'paste' },
+ { role: 'selectAll' },
+ ]
+ },
+ {
+ label: 'View',
+ submenu: [
+ { label: 'Profiles', accelerator: IS_MAC ? 'Cmd+1' : 'Ctrl+1', click: () => mainWindow?.webContents.send('navigate', 'profiles') },
+ { label: 'Running Sessions', accelerator: IS_MAC ? 'Cmd+2' : 'Ctrl+2', click: () => mainWindow?.webContents.send('navigate', 'sessions') },
+ { label: 'Settings', accelerator: IS_MAC ? 'Cmd+3' : 'Ctrl+3', click: () => mainWindow?.webContents.send('navigate', 'settings') },
+ { type: 'separator' },
+ { role: 'reload' },
+ { type: 'separator' },
+ { role: 'togglefullscreen' }
+ ]
+ },
+ {
+ label: 'Window',
+ submenu: [
+ { role: 'minimize' },
+ { role: 'zoom' },
+ ...(IS_MAC ? [{ type: 'separator' }, { role: 'front' }] : [])
+ ]
+ }
+ ];
+ Menu.setApplicationMenu(Menu.buildFromTemplate(template));
+}
+
+// ─── IPC: Profile Management ──────────────────────────────────────────────────
+
+ipcMain.handle('profiles:getAll', () => store.get('profiles', []));
+
+ipcMain.handle('profiles:create', (_, profileData) => {
+ const profiles = store.get('profiles', []);
+ const newProfile = {
+ id: uuidv4(),
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ status: 'stopped',
+ ...profileData
+ };
+ profiles.push(newProfile);
+ store.set('profiles', profiles);
+ return newProfile;
+});
+
+ipcMain.handle('profiles:update', (_, { id, updates }) => {
+ const profiles = store.get('profiles', []);
+ const idx = profiles.findIndex(p => p.id === id);
+ if (idx === -1) throw new Error('Profile not found');
+ profiles[idx] = { ...profiles[idx], ...updates, updatedAt: new Date().toISOString() };
+ store.set('profiles', profiles);
+ return profiles[idx];
+});
+
+ipcMain.handle('profiles:delete', (_, id) => {
+ const profiles = store.get('profiles', []);
+ store.set('profiles', profiles.filter(p => p.id !== id));
+ if (runningInstances.has(id)) {
+ try { runningInstances.get(id).process.kill('SIGTERM'); } catch {}
+ runningInstances.delete(id);
+ }
+ return true;
+});
+
+ipcMain.handle('profiles:deleteMultiple', (_, ids) => {
+ const idSet = new Set(ids);
+ const profiles = store.get('profiles', []);
+ store.set('profiles', profiles.filter(p => !idSet.has(p.id)));
+ for (const id of ids) {
+ if (runningInstances.has(id)) {
+ try { runningInstances.get(id).process.kill('SIGTERM'); } catch {}
+ runningInstances.delete(id);
+ }
+ }
+ return true;
+});
+
+ipcMain.handle('profiles:duplicate', async (_, id) => {
+ const profiles = store.get('profiles', []);
+ const original = profiles.find(p => p.id === id);
+ if (!original) throw new Error('Profile not found');
+
+ const newId = uuidv4();
+ const copy = {
+ ...original,
+ id: newId,
+ name: original.name + ' (Copy)',
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString(),
+ status: 'stopped',
+ cookieCount: 0,
+ cookiesSavedAt: null,
+ savedCookiesPath: null,
+ };
+
+ const settings = store.get('settings');
+ const srcDir = path.join(settings.defaultUserDataDir, id);
+ const dstDir = path.join(settings.defaultUserDataDir, newId);
+
+ if (fs.existsSync(srcDir)) {
+ try {
+ copyDirRecursive(srcDir, dstDir);
+ const newCookiesPath = path.join(dstDir, 'saved-cookies.json');
+ if (fs.existsSync(newCookiesPath)) {
+ copy.savedCookiesPath = newCookiesPath;
+ try {
+ const cookies = JSON.parse(fs.readFileSync(newCookiesPath, 'utf8'));
+ copy.cookieCount = Array.isArray(cookies) ? cookies.length : 0;
+ copy.cookiesSavedAt = original.cookiesSavedAt;
+ } catch {}
+ }
+ } catch (e) {}
+ }
+
+ profiles.push(copy);
+ store.set('profiles', profiles);
+ return copy;
+});
+
+function copyDirRecursive(src, dst) {
+ fs.mkdirSync(dst, { recursive: true });
+ for (const entry of fs.readdirSync(src, { withFileTypes: true })) {
+ const srcPath = path.join(src, entry.name);
+ const dstPath = path.join(dst, entry.name);
+ if (entry.isDirectory()) {
+ copyDirRecursive(srcPath, dstPath);
+ } else {
+ fs.copyFileSync(srcPath, dstPath);
+ }
+ }
+}
+
+// ─── IPC: Browser Launch ──────────────────────────────────────────────────────
+
+ipcMain.handle('browser:launch', async (_, profileId) => {
+ let profiles = store.get('profiles', []);
+ let profile = profiles.find(p => p.id === profileId);
+ if (!profile) throw new Error('Profile not found');
+
+ if (runningInstances.has(profileId)) {
+ throw new Error('Profile is already running.');
+ }
+
+ if (profile.status === 'running') {
+ updateProfileStatus(profileId, 'stopped');
+ profile = { ...profile, status: 'stopped' };
+ }
+
+ const settings = store.get('settings');
+ const botBrowserPath = settings.botBrowserPath || DEFAULT_BOTBROWSER_PATH;
+
+ if (!fs.existsSync(botBrowserPath)) {
+ throw new Error(
+ `BotBrowser executable not found at:\n${botBrowserPath}\n\nPlease install BotBrowser or update the path in Settings.`
+ );
+ }
+
+ const userDataDir = path.join(settings.defaultUserDataDir, profileId);
+ fs.mkdirSync(userDataDir, { recursive: true });
+
+ const savedCookiesPath = path.join(userDataDir, 'saved-cookies.json');
+ if (fs.existsSync(savedCookiesPath) && !profile.cookies) {
+ profile = { ...profile, cookies: `@${savedCookiesPath}` };
+ }
+
+ let botProfileArg = '';
+ if (profile.profileFilePath && fs.existsSync(profile.profileFilePath)) {
+ botProfileArg = injectConfigsIntoEncFile(profile.profileFilePath, profile, profileId);
+ } else if (profile.profileDirPath && fs.existsSync(profile.profileDirPath)) {
+ botProfileArg = null;
+ } else {
+ botProfileArg = writeStandaloneConfigFile(profile, profileId);
+ }
+
+ const args = buildLaunchArgs(profile, userDataDir, botProfileArg);
+
+ const proc = spawn(botBrowserPath, args, {
+ detached: false,
+ stdio: ['ignore', 'pipe', 'pipe'],
+ ...(IS_WIN ? { shell: false } : {})
+ });
+
+ const rdpArg = args.find(a => a.startsWith('--remote-debugging-port='));
+ const remoteDebuggingPort = rdpArg ? parseInt(rdpArg.split('=')[1], 10) : null;
+
+ const instance = {
+ process: proc,
+ pid: proc.pid,
+ profileId,
+ profileName: profile.name,
+ startTime: new Date().toISOString(),
+ url: profile.startUrl || 'about:blank',
+ userDataDir,
+ remoteDebuggingPort,
+ args
+ };
+
+ runningInstances.set(profileId, instance);
+ updateProfileStatus(profileId, 'running');
+
+ proc.stdout.on('data', (_data) => {});
+ proc.stderr.on('data', (_data) => {});
+
+ proc.on('close', (code) => {
+ runningInstances.delete(profileId);
+ updateProfileStatus(profileId, 'stopped');
+ cleanupTempFile(profileId);
+ mainWindow?.webContents.send('instance:stopped', { profileId, code });
+ });
+
+ proc.on('error', (err) => {
+ runningInstances.delete(profileId);
+ updateProfileStatus(profileId, 'stopped');
+ cleanupTempFile(profileId);
+ mainWindow?.webContents.send('instance:error', { profileId, error: err.message });
+ });
+
+ mainWindow?.webContents.send('instance:started', { profileId, pid: proc.pid });
+ return { pid: proc.pid, args };
+});
+
+ipcMain.handle('browser:stop', async (_, profileId) => {
+ if (!runningInstances.has(profileId)) {
+ updateProfileStatus(profileId, 'stopped');
+ return false;
+ }
+ const inst = runningInstances.get(profileId);
+
+ if (inst.remoteDebuggingPort) {
+ try {
+ await saveCookiesViaCDP(profileId, inst.remoteDebuggingPort, inst.userDataDir);
+ } catch (e) {}
+ }
+
+ try { inst.process.kill(IS_WIN ? undefined : 'SIGTERM'); } catch {}
+ runningInstances.delete(profileId);
+ cleanupTempFile(profileId);
+ updateProfileStatus(profileId, 'stopped');
+ return true;
+});
+
+ipcMain.handle('browser:stopAll', async () => {
+ const savePromises = [];
+ for (const [profileId, inst] of runningInstances) {
+ if (inst.remoteDebuggingPort) {
+ savePromises.push(
+ saveCookiesViaCDP(profileId, inst.remoteDebuggingPort, inst.userDataDir)
+ .catch(_e => {})
+ );
+ }
+ }
+ await Promise.allSettled(savePromises);
+
+ for (const [profileId, inst] of runningInstances) {
+ try { inst.process.kill(IS_WIN ? undefined : 'SIGTERM'); } catch {}
+ cleanupTempFile(profileId);
+ updateProfileStatus(profileId, 'stopped');
+ }
+ runningInstances.clear();
+ return true;
+});
+
+ipcMain.handle('browser:getRunning', () => {
+ const result = [];
+ for (const [profileId, inst] of runningInstances) {
+ result.push({
+ profileId,
+ pid: inst.pid,
+ profileName: inst.profileName,
+ startTime: inst.startTime,
+ url: inst.url
+ });
+ }
+ return result;
+});
+
+// ─── IPC: Settings ────────────────────────────────────────────────────────────
+
+ipcMain.handle('settings:get', () => store.get('settings'));
+ipcMain.handle('settings:set', (_, newSettings) => {
+ store.set('settings', { ...store.get('settings'), ...newSettings });
+ return true;
+});
+
+// ─── IPC: Dialogs ─────────────────────────────────────────────────────────────
+
+ipcMain.handle('dialog:openFile', async (_, options = {}) => {
+ const result = await dialog.showOpenDialog(mainWindow, {
+ properties: ['openFile'],
+ filters: options.filters || [{ name: 'All Files', extensions: ['*'] }],
+ ...options
+ });
+ return result.canceled ? null : result.filePaths[0];
+});
+
+ipcMain.handle('dialog:saveFile', async (_, options = {}) => {
+ const result = await dialog.showSaveDialog(mainWindow, options);
+ return result.canceled ? null : result.filePath;
+});
+
+ipcMain.handle('dialog:selectExecutable', async () => {
+ const filters = IS_WIN
+ ? [{ name: 'Executable', extensions: ['exe'] }]
+ : [{ name: 'All Files', extensions: ['*'] }];
+ const result = await dialog.showOpenDialog(mainWindow, {
+ properties: ['openFile'],
+ filters
+ });
+ return result.canceled ? null : result.filePaths[0];
+});
+
+ipcMain.handle('dialog:selectDirectory', async () => {
+ const result = await dialog.showOpenDialog(mainWindow, {
+ properties: ['openDirectory']
+ });
+ return result.canceled ? null : result.filePaths[0];
+});
+
+ipcMain.handle('shell:openPath', (_, p) => shell.openPath(p));
+ipcMain.handle('shell:showItemInFolder', (_, p) => shell.showItemInFolder(p));
+
+// ─── IPC: Proxy / IP Check ────────────────────────────────────────────────────
+
+/**
+ * Parse a proxy URL string into { protocol, host, port, username, password }
+ */
+function parseProxy(proxyStr) {
+ if (!proxyStr || !proxyStr.trim()) return null;
+ let s = proxyStr.trim();
+ // Strip accidental double-scheme e.g. "socks5://socks5://host:port"
+ s = s.replace(/^(socks5?[ah]?|https?):\/\/(socks5?[ah]?|https?):\/\//i, '$1://');
+ if (!/^[a-z][a-z0-9+\-.]*:\/\//i.test(s)) s = 'socks5://' + s;
+ try {
+ const u = new URL(s);
+ const proto = u.protocol.replace(':', '').toLowerCase();
+ const host = u.hostname;
+ if (!host) return null;
+ return {
+ protocol: proto,
+ host,
+ port: parseInt(u.port) || (proto.startsWith('http') ? 8080 : 1080),
+ username: u.username ? decodeURIComponent(u.username) : '',
+ password: u.password ? decodeURIComponent(u.password) : '',
+ };
+ } catch { return null; }
+}
+
+/**
+ * Connect through SOCKS5 proxy using raw TCP (no external deps).
+ * Returns a socket connected to targetHost:targetPort via the proxy.
+ */
+function connectViaSocks5(proxyHost, proxyPort, targetHost, targetPort, username, password) {
+ return new Promise((resolve, reject) => {
+ const sock = net.connect(proxyPort, proxyHost, () => {
+ // SOCKS5 greeting
+ const authMethods = (username && password) ? [0x00, 0x02] : [0x00];
+ const greeting = Buffer.from([0x05, authMethods.length, ...authMethods]);
+ sock.write(greeting);
+ });
+
+ sock.setTimeout(15000);
+ sock.on('timeout', () => { sock.destroy(); reject(new Error('SOCKS5 connect timeout')); });
+ sock.on('error', reject);
+
+ let state = 'greeting';
+ let buf = Buffer.alloc(0);
+
+ sock.on('data', (chunk) => {
+ buf = Buffer.concat([buf, chunk]);
+
+ if (state === 'greeting') {
+ if (buf.length < 2) return;
+ if (buf[0] !== 0x05) { sock.destroy(); reject(new Error('Not a SOCKS5 server')); return; }
+ const method = buf[1];
+ buf = buf.slice(2);
+
+ if (method === 0xFF) { sock.destroy(); reject(new Error('SOCKS5: no acceptable auth method')); return; }
+
+ if (method === 0x02) {
+ // Username/password auth
+ state = 'auth';
+ const uBuf = Buffer.from(username || '', 'utf8');
+ const pBuf = Buffer.from(password || '', 'utf8');
+ const authPkt = Buffer.from([0x01, uBuf.length, ...uBuf, pBuf.length, ...pBuf]);
+ sock.write(authPkt);
+ } else {
+ // No auth — send CONNECT
+ state = 'connect';
+ sendSocks5Connect(sock, targetHost, targetPort);
+ }
+ return;
+ }
+
+ if (state === 'auth') {
+ if (buf.length < 2) return;
+ if (buf[1] !== 0x00) { sock.destroy(); reject(new Error('SOCKS5 auth failed')); return; }
+ buf = buf.slice(2);
+ state = 'connect';
+ sendSocks5Connect(sock, targetHost, targetPort);
+ return;
+ }
+
+ if (state === 'connect') {
+ if (buf.length < 10) return; // minimum response
+ if (buf[0] !== 0x05 || buf[1] !== 0x00) {
+ const errCodes = { 1: 'General failure', 2: 'Connection not allowed', 3: 'Network unreachable', 4: 'Host unreachable', 5: 'Connection refused' };
+ sock.destroy();
+ reject(new Error('SOCKS5 connect error: ' + (errCodes[buf[1]] || `code ${buf[1]}`)));
+ return;
+ }
+ // Success — socket is now connected to target
+ state = 'done';
+ sock.removeAllListeners('data');
+ resolve({ socket: sock, remaining: buf.slice(10) });
+ }
+ });
+ });
+}
+
+function sendSocks5Connect(sock, host, port) {
+ const hostBuf = Buffer.from(host, 'utf8');
+ const pkt = Buffer.from([
+ 0x05, 0x01, 0x00, 0x03,
+ hostBuf.length, ...hostBuf,
+ (port >> 8) & 0xFF, port & 0xFF
+ ]);
+ sock.write(pkt);
+}
+
+/**
+ * Do an HTTP GET through a raw socket (used after SOCKS5 tunnel is established).
+ */
+function httpGetThroughSocket(socket, host, path, remainingData) {
+ return new Promise((resolve, reject) => {
+ let data = '';
+ const req = `GET ${path} HTTP/1.1\r\nHost: ${host}\r\nConnection: close\r\nUser-Agent: BotBrowserControl/1.0\r\n\r\n`;
+ socket.write(req);
+ if (remainingData && remainingData.length > 0) {
+ data += remainingData.toString();
+ }
+ socket.on('data', (chunk) => { data += chunk.toString(); });
+ socket.on('end', () => {
+ const bodyStart = data.indexOf('\r\n\r\n');
+ const body = bodyStart !== -1 ? data.slice(bodyStart + 4) : data;
+ try { resolve(JSON.parse(body)); } catch (e) { reject(new Error('Invalid JSON response from ip-api.com')); }
+ });
+ socket.on('error', reject);
+ });
+}
+
+/**
+ * Check proxy exit IP via ip-api.com.
+ * Supports HTTP, HTTPS (CONNECT tunnel), SOCKS4, SOCKS5.
+ */
+ipcMain.handle('proxy:checkIp', async (_, proxyServer) => {
+ const API_HOST = 'ip-api.com';
+ const API_PATH = '/json/?fields=66846719';
+ const API_PORT = 80;
+
+ // No proxy — direct request
+ if (!proxyServer || !proxyServer.trim()) {
+ return doDirectHttpGet(`http://${API_HOST}${API_PATH}`);
+ }
+
+ const proxy = parseProxy(proxyServer);
+ if (!proxy) throw new Error('Invalid proxy URL');
+
+ const proto = proxy.protocol.toLowerCase();
+
+ // SOCKS5 / SOCKS5H
+ if (proto === 'socks5' || proto === 'socks5h' || proto === 'socks4' || proto === 'socks4a') {
+ const { socket, remaining } = await connectViaSocks5(
+ proxy.host, proxy.port, API_HOST, API_PORT,
+ proxy.username, proxy.password
+ );
+ return httpGetThroughSocket(socket, API_HOST, API_PATH, remaining);
+ }
+
+ // HTTP / HTTPS proxy — use CONNECT tunnel
+ return doHttpProxyIpCheck(proxy, API_HOST, API_PATH, API_PORT);
+});
+
+function doDirectHttpGet(url) {
+ return new Promise((resolve, reject) => {
+ const to = setTimeout(() => reject(new Error('Request timeout')), 15000);
+ const req = http.get(url, { headers: { 'User-Agent': 'BotBrowserControl/1.0' } }, (res) => {
+ let data = '';
+ res.on('data', d => { data += d; });
+ res.on('end', () => {
+ clearTimeout(to);
+ try { resolve(JSON.parse(data)); } catch { reject(new Error('Invalid JSON')); }
+ });
+ });
+ req.on('error', e => { clearTimeout(to); reject(e); });
+ req.setTimeout(12000, () => { req.destroy(); clearTimeout(to); reject(new Error('Timeout')); });
+ });
+}
+
+function doHttpProxyIpCheck(proxy, targetHost, targetPath, targetPort) {
+ return new Promise((resolve, reject) => {
+ const to = setTimeout(() => reject(new Error('Proxy timeout')), 15000);
+
+ const connectTarget = `${targetHost}:${targetPort}`;
+ const headers = { 'User-Agent': 'BotBrowserControl/1.0' };
+ if (proxy.username && proxy.password) {
+ headers['Proxy-Authorization'] = 'Basic ' + Buffer.from(`${proxy.username}:${proxy.password}`).toString('base64');
+ }
+
+ const connectReq = http.request({
+ method: 'CONNECT',
+ hostname: proxy.host,
+ port: proxy.port,
+ path: connectTarget,
+ headers,
+ });
+
+ connectReq.on('connect', (res, socket) => {
+ if (res.statusCode !== 200) {
+ socket.destroy();
+ clearTimeout(to);
+ reject(new Error(`Proxy CONNECT rejected: ${res.statusCode}`));
+ return;
+ }
+ const req = `GET ${targetPath} HTTP/1.1\r\nHost: ${targetHost}\r\nConnection: close\r\nUser-Agent: BotBrowserControl/1.0\r\n\r\n`;
+ socket.write(req);
+ let data = '';
+ socket.on('data', d => { data += d.toString(); });
+ socket.on('end', () => {
+ clearTimeout(to);
+ const bodyStart = data.indexOf('\r\n\r\n');
+ const body = bodyStart !== -1 ? data.slice(bodyStart + 4) : data;
+ try { resolve(JSON.parse(body)); } catch { reject(new Error('Invalid JSON')); }
+ });
+ socket.on('error', e => { clearTimeout(to); reject(e); });
+ });
+
+ connectReq.on('error', e => { clearTimeout(to); reject(e); });
+ connectReq.setTimeout(12000, () => { connectReq.destroy(); clearTimeout(to); reject(new Error('Connect timeout')); });
+ connectReq.end();
+ });
+}
+
+// ─── IPC: Update Checker ──────────────────────────────────────────────────────
+
+const BOTBROWSER_RELEASES_API = 'https://api.github.com/repos/botswin/BotBrowser/releases/latest';
+const CONTROL_RELEASES_API = 'https://api.github.com/repos/tombaki/BotBrowser/releases/latest';
+const CONTROL_VERSION = '1.2.1';
+
+ipcMain.handle('app:checkForUpdates', async () => {
+ const results = { kernel: null, control: null };
+
+ try {
+ const kernelRes = await httpsGet(BOTBROWSER_RELEASES_API);
+ if (kernelRes.statusCode === 200) {
+ const release = JSON.parse(kernelRes.body);
+ results.kernel = {
+ tagName: release.tag_name,
+ name: release.name || release.tag_name,
+ publishedAt: release.published_at,
+ url: release.html_url,
+ };
+ }
+ } catch {}
+
+ try {
+ const controlRes = await httpsGet(CONTROL_RELEASES_API);
+ if (controlRes.statusCode === 200) {
+ const release = JSON.parse(controlRes.body);
+ const tag = release.tag_name || '';
+ const remoteVer = tag.replace(/^control-v/, '');
+ results.control = {
+ tagName: tag,
+ version: remoteVer,
+ name: release.name || tag,
+ publishedAt: release.published_at,
+ url: release.html_url,
+ isNewer: remoteVer !== CONTROL_VERSION && remoteVer > CONTROL_VERSION,
+ };
+ }
+ } catch {}
+
+ // Persist last seen so we can detect "new since last check"
+ const lastKernel = store.get('lastSeenKernelRelease');
+ const lastControl = store.get('lastSeenControlRelease');
+
+ const newKernel = results.kernel && results.kernel.tagName !== lastKernel;
+ const newControl = results.control && results.control.tagName !== lastControl && results.control.isNewer;
+
+ if (results.kernel?.tagName) store.set('lastSeenKernelRelease', results.kernel.tagName);
+ if (results.control?.tagName) store.set('lastSeenControlRelease', results.control.tagName);
+
+ results.newKernel = newKernel;
+ results.newControl = newControl;
+
+ return results;
+});
+
+// ─── IPC: Kernel Manager ──────────────────────────────────────────────────────
+
+const KERNEL_GITHUB_API = 'https://api.github.com/repos/botswin/BotBrowser/releases';
+
+function httpsGet(url, redirectCount = 0) {
+ return new Promise((resolve, reject) => {
+ if (redirectCount > 5) { reject(new Error('Too many redirects')); return; }
+ const req = https.get(url, {
+ headers: {
+ 'User-Agent': 'BotBrowserControl/1.0',
+ 'Accept': 'application/vnd.github+json',
+ }
+ }, (res) => {
+ if ([301, 302, 303, 307, 308].includes(res.statusCode)) {
+ const location = res.headers.location;
+ res.resume();
+ if (!location) { reject(new Error('Redirect without location')); return; }
+ resolve(httpsGet(location, redirectCount + 1));
+ return;
+ }
+ let data = '';
+ res.on('data', d => { data += d; });
+ res.on('end', () => resolve({ statusCode: res.statusCode, body: data, headers: res.headers }));
+ });
+ req.on('error', reject);
+ req.setTimeout(20000, () => { req.destroy(); reject(new Error('Request timeout')); });
+ });
+}
+
+ipcMain.handle('kernel:fetchReleases', async () => {
+ const res = await httpsGet(KERNEL_GITHUB_API);
+ if (res.statusCode !== 200) throw new Error(`GitHub API error: ${res.statusCode}`);
+ const releases = JSON.parse(res.body);
+ const mapped = releases.slice(0, 20).map(r => ({
+ id: r.id,
+ tagName: r.tag_name,
+ name: r.name || r.tag_name,
+ publishedAt: r.published_at,
+ prerelease: r.prerelease,
+ body: (r.body || '').slice(0, 500),
+ assets: (r.assets || []).map(a => ({
+ id: a.id,
+ name: a.name,
+ size: a.size,
+ downloadUrl: a.browser_download_url,
+ contentType: a.content_type,
+ }))
+ }));
+ // Cache releases to store so they persist across restarts
+ store.set('cachedKernelReleases', mapped);
+ return mapped;
+});
+
+// Return cached releases (fast, no network)
+ipcMain.handle('kernel:getCachedReleases', () => {
+ return store.get('cachedKernelReleases', null);
+});
+
+function getKernelsDir() {
+ return path.join(app.getPath('userData'), 'kernels');
+}
+
+ipcMain.handle('kernel:getDir', () => getKernelsDir());
+
+ipcMain.handle('kernel:listInstalled', () => {
+ const dir = getKernelsDir();
+ if (!fs.existsSync(dir)) return [];
+ return fs.readdirSync(dir, { withFileTypes: true })
+ .filter(e => e.isDirectory())
+ .map(e => {
+ const vdir = path.join(dir, e.name);
+ const meta = path.join(vdir, '.meta.json');
+ let info = { version: e.name, installedAt: null, platform: null, execPath: null };
+ if (fs.existsSync(meta)) {
+ try { info = { ...info, ...JSON.parse(fs.readFileSync(meta, 'utf8')) }; } catch {}
+ }
+ return info;
+ });
+});
+
+ipcMain.handle('kernel:delete', (_, version) => {
+ const dir = path.join(getKernelsDir(), version);
+ if (fs.existsSync(dir)) {
+ fs.rmSync(dir, { recursive: true, force: true });
+ return true;
+ }
+ return false;
+});
+
+/**
+ * Download a kernel asset, then auto-install it.
+ * - macOS .dmg: mount with hdiutil, copy .app to /Applications (xattr -rd + codesign -f)
+ * - Linux .deb: install with dpkg -i (requires sudo) or just mark as ready
+ * - Linux .AppImage: chmod +x
+ * - Windows .7z/.zip: extract with built-in tools
+ */
+ipcMain.handle('kernel:download', async (_, { downloadUrl, fileName, version }) => {
+ const kernelsDir = getKernelsDir();
+ const versionDir = path.join(kernelsDir, version);
+ fs.mkdirSync(versionDir, { recursive: true });
+
+ const destPath = path.join(versionDir, fileName);
+
+ // Download first
+ await new Promise((resolve, reject) => {
+ function doDownload(url, redirectCount) {
+ if (redirectCount > 5) { reject(new Error('Too many redirects')); return; }
+ const parsedUrl = new URL(url);
+ const protocol = parsedUrl.protocol === 'https:' ? https : http;
+ const req = protocol.get(url, { headers: { 'User-Agent': 'BotBrowserControl/1.0' } }, (res) => {
+ if ([301, 302, 303, 307, 308].includes(res.statusCode)) {
+ const location = res.headers.location;
+ res.resume();
+ if (!location) { reject(new Error('Redirect without location')); return; }
+ doDownload(location, redirectCount + 1);
+ return;
+ }
+ if (res.statusCode !== 200) { reject(new Error(`Download failed: HTTP ${res.statusCode}`)); return; }
+
+ const total = parseInt(res.headers['content-length'] || '0', 10);
+ let downloaded = 0;
+ const fileStream = fs.createWriteStream(destPath);
+
+ res.on('data', (chunk) => {
+ downloaded += chunk.length;
+ if (total > 0) {
+ const progress = Math.round((downloaded / total) * 100);
+ mainWindow?.webContents.send('kernel:downloadProgress', { version, progress, downloaded, total });
+ }
+ });
+
+ res.pipe(fileStream);
+ fileStream.on('finish', () => { fileStream.close(resolve); });
+ fileStream.on('error', reject);
+ });
+ req.on('error', reject);
+ req.setTimeout(180000, () => { req.destroy(); reject(new Error('Download timeout')); });
+ }
+ doDownload(downloadUrl, 0);
+ });
+
+ // Auto-install
+ let execPath = null;
+ let installStatus = 'downloaded';
+ let installNote = '';
+
+ try {
+ if (IS_MAC && fileName.endsWith('.dmg')) {
+ // Mount DMG, copy .app to /Applications, unmount
+ const mountResult = await runCmd('hdiutil', ['attach', '-nobrowse', '-noverify', '-noautoopen', destPath]);
+ // Find mount point from output (last line with /Volumes/)
+ const mountPoint = (mountResult.stdout || '').split('\n')
+ .map(l => l.trim())
+ .filter(l => l.includes('/Volumes/'))
+ .pop()?.split(/\s+/).pop();
+
+ if (mountPoint && fs.existsSync(mountPoint)) {
+ // Find .app bundle in mount
+ const apps = fs.readdirSync(mountPoint).filter(f => f.endsWith('.app'));
+ if (apps.length > 0) {
+ const appName = apps[0];
+ const srcApp = path.join(mountPoint, appName);
+ const dstApp = path.join('/Applications', appName);
+
+ // Remove old if exists
+ if (fs.existsSync(dstApp)) {
+ await runCmd('rm', ['-rf', dstApp]);
+ }
+
+ // Copy .app to /Applications
+ await runCmd('cp', ['-R', srcApp, '/Applications/']);
+
+ // Remove quarantine flag (xattr) so unsigned app can open
+ try { await runCmd('xattr', ['-rd', 'com.apple.quarantine', dstApp]); } catch {}
+ // Ad-hoc codesign to allow launch
+ try { await runCmd('codesign', ['--force', '--deep', '--sign', '-', dstApp]); } catch {}
+
+ // Find the actual executable inside .app
+ const infoPlistPath = path.join(dstApp, 'Contents', 'Info.plist');
+ let execName = 'Chromium';
+ if (fs.existsSync(infoPlistPath)) {
+ const plistContent = fs.readFileSync(infoPlistPath, 'utf8');
+ const match = plistContent.match(/CFBundleExecutable<\/key>\s*([^<]+)<\/string>/);
+ if (match) execName = match[1];
+ }
+ execPath = path.join(dstApp, 'Contents', 'MacOS', execName);
+ installStatus = 'installed';
+ installNote = `Installed to /Applications/${appName}`;
+ }
+ // Unmount
+ try { await runCmd('hdiutil', ['detach', mountPoint, '-quiet']); } catch {}
+ }
+ } else if (IS_LINUX && fileName.endsWith('.AppImage')) {
+ fs.chmodSync(destPath, 0o755);
+ execPath = destPath;
+ installStatus = 'ready';
+ installNote = 'AppImage is ready to use';
+ } else if (IS_LINUX && fileName.endsWith('.deb')) {
+ // Try to install with pkexec/sudo dpkg
+ try {
+ await runCmd('pkexec', ['dpkg', '-i', destPath]);
+ installStatus = 'installed';
+ installNote = 'Installed via dpkg';
+ execPath = '/usr/bin/botbrowser';
+ } catch {
+ installStatus = 'downloaded';
+ installNote = `Run: sudo dpkg -i ${destPath}`;
+ execPath = null;
+ }
+ } else if (IS_WIN && (fileName.endsWith('.zip') || fileName.endsWith('.7z'))) {
+ // Extract to versionDir
+ if (fileName.endsWith('.zip')) {
+ await runCmd('powershell', ['-Command', `Expand-Archive -Force -Path "${destPath}" -DestinationPath "${versionDir}"`]);
+ }
+ // Find .exe
+ const exes = findFilesRecursive(versionDir, '.exe').filter(f => !f.includes('Uninstall'));
+ execPath = exes[0] || null;
+ installStatus = 'extracted';
+ installNote = 'Extracted successfully';
+ } else if (IS_WIN && fileName.endsWith('.exe')) {
+ execPath = destPath;
+ installStatus = 'ready';
+ installNote = 'Installer ready — run to install';
+ }
+ } catch (installErr) {
+ installNote = `Install step failed: ${installErr.message}`;
+ }
+
+ const meta = {
+ version, installedAt: new Date().toISOString(),
+ platform: process.platform, fileName, execPath, downloadUrl,
+ installStatus, installNote,
+ };
+ fs.writeFileSync(path.join(versionDir, '.meta.json'), JSON.stringify(meta, null, 2), 'utf8');
+
+ mainWindow?.webContents.send('kernel:downloadComplete', { version, execPath, destPath, installStatus, installNote });
+ return { version, execPath, destPath, installStatus, installNote };
+});
+
+function runCmd(cmd, args) {
+ return new Promise((resolve, reject) => {
+ const proc = execFile(cmd, args, { timeout: 120000 }, (err, stdout, stderr) => {
+ if (err) reject(new Error(stderr || err.message));
+ else resolve({ stdout, stderr });
+ });
+ });
+}
+
+function findFilesRecursive(dir, ext) {
+ const results = [];
+ if (!fs.existsSync(dir)) return results;
+ for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
+ const full = path.join(dir, entry.name);
+ if (entry.isDirectory()) results.push(...findFilesRecursive(full, ext));
+ else if (entry.name.toLowerCase().endsWith(ext)) results.push(full);
+ }
+ return results;
+}
+
+// ─── Config File Helpers ──────────────────────────────────────────────────────
+
+function writeStandaloneConfigFile(profile, profileId) {
+ const configs = buildConfigsBlock(profile);
+ const json = JSON.stringify({ configs }, null, 2);
+ const tmpPath = path.join(os.tmpdir(), `botbrowser-config-${profileId}.json`);
+ fs.writeFileSync(tmpPath, json, 'utf8');
+ tempFiles.set(profileId, tmpPath);
+ return tmpPath;
+}
+
+function injectConfigsIntoEncFile(encFilePath, profile, profileId) {
+ try {
+ const raw = fs.readFileSync(encFilePath, 'utf8');
+ const enc = JSON.parse(raw);
+ enc.configs = buildConfigsBlock(profile);
+ const tmpPath = path.join(os.tmpdir(), `botbrowser-enc-${profileId}.json`);
+ fs.writeFileSync(tmpPath, JSON.stringify(enc, null, 2), 'utf8');
+ tempFiles.set(profileId, tmpPath);
+ return tmpPath;
+ } catch (e) {
+ return writeStandaloneConfigFile(profile, profileId);
+ }
+}
+
+function cleanupTempFile(profileId) {
+ const p = tempFiles.get(profileId);
+ if (p && fs.existsSync(p)) {
+ try { fs.unlinkSync(p); } catch {}
+ }
+ tempFiles.delete(profileId);
+}
+
+function buildConfigsBlock(profile) {
+ const configs = {};
+
+ if (profile.locale) configs.locale = profile.locale;
+ if (profile.languages) configs.languages = profile.languages;
+ if (profile.timezone) configs.timezone = profile.timezone;
+
+ if (profile.location) {
+ if (typeof profile.location === 'string' && /^-?\d+(\.\d+)?,-?\d+(\.\d+)?$/.test(profile.location.trim())) {
+ const [lat, lon] = profile.location.split(',').map(Number);
+ configs.location = { latitude: lat, longitude: lon };
+ } else {
+ configs.location = profile.location;
+ }
+ }
+
+ if (profile.colorScheme) configs.colorScheme = profile.colorScheme;
+ if (profile.browserBrand) configs.browserBrand = profile.browserBrand;
+ if (profile.brandFullVersion) configs.brandFullVersion = profile.brandFullVersion;
+ if (profile.uaFullVersion) configs.uaFullVersion = profile.uaFullVersion;
+
+ if (profile.platform) configs.platform = profile.platform;
+ if (profile.platformVersion) configs.platformVersion = profile.platformVersion;
+ if (profile.model) configs.model = profile.model;
+ if (profile.architecture) configs.architecture = profile.architecture;
+ if (profile.bitness) configs.bitness = profile.bitness;
+ if (profile.mobile !== undefined && profile.mobile !== '') configs.mobile = !!profile.mobile;
+
+ if (profile.proxyServer) {
+ configs.proxy = { server: profile.proxyServer };
+ if (profile.proxyIp) configs.proxy.ip = profile.proxyIp;
+ }
+
+ if (profile.customHeaders && typeof profile.customHeaders === 'object' && Object.keys(profile.customHeaders).length > 0) {
+ configs.customHeaders = profile.customHeaders;
+ }
+
+ if (profile.windowSize) configs.window = profile.windowSize;
+ if (profile.screenSize) configs.screen = profile.screenSize;
+ if (profile.orientation) configs.orientation = profile.orientation;
+ if (profile.disableDeviceScaleFactorOnGUI) configs.disableDeviceScaleFactorOnGUI = true;
+
+ if (profile.webgl) configs.webgl = profile.webgl;
+ if (profile.webgpu) configs.webgpu = profile.webgpu;
+ if (profile.webrtc) configs.webrtc = profile.webrtc;
+ if (profile.webrtcICE) configs.webrtcICE = profile.webrtcICE;
+ if (profile.speechVoices) configs.speechVoices = profile.speechVoices;
+ if (profile.mediaDevices) configs.mediaDevices = profile.mediaDevices;
+ if (profile.mediaTypes) configs.mediaTypes = profile.mediaTypes;
+ if (profile.fonts) configs.fonts = profile.fonts;
+ if (profile.keyboard) configs.keyboard = profile.keyboard;
+
+ if (profile.noiseCanvas !== undefined && profile.noiseCanvas !== '') configs.noiseCanvas = !!profile.noiseCanvas;
+ if (profile.noiseWebglImage !== undefined && profile.noiseWebglImage !== '') configs.noiseWebglImage = !!profile.noiseWebglImage;
+ if (profile.noiseAudioContext !== undefined && profile.noiseAudioContext !== '') configs.noiseAudioContext = !!profile.noiseAudioContext;
+ if (profile.noiseClientRects !== undefined && profile.noiseClientRects !== '') configs.noiseClientRects = !!profile.noiseClientRects;
+ if (profile.noiseTextRects !== undefined && profile.noiseTextRects !== '') configs.noiseTextRects = !!profile.noiseTextRects;
+
+ if (profile.alwaysActive !== undefined) configs.alwaysActive = !!profile.alwaysActive;
+ if (profile.disableDebugger !== undefined) configs.disableDebugger = !!profile.disableDebugger;
+ if (profile.disableConsoleMessage !== undefined) configs.disableConsoleMessage = !!profile.disableConsoleMessage;
+ if (profile.portProtection !== undefined) configs.portProtection = !!profile.portProtection;
+ if (profile.mobileForceTouch !== undefined) configs.mobileForceTouch = !!profile.mobileForceTouch;
+ if (profile.networkInfoOverride !== undefined) configs.networkInfoOverride = !!profile.networkInfoOverride;
+ if (profile.enableVariationsInContext !== undefined) configs.enableVariationsInContext = !!profile.enableVariationsInContext;
+
+ if (profile.injectRandomHistory !== undefined && profile.injectRandomHistory !== '') {
+ const v = profile.injectRandomHistory;
+ if (v === true || v === 'true') configs.injectRandomHistory = true;
+ else if (v === false || v === 'false') configs.injectRandomHistory = false;
+ else if (!isNaN(Number(v))) configs.injectRandomHistory = Number(v);
+ }
+
+ if (profile.fps !== undefined && profile.fps !== '' && profile.fps !== 'profile') configs.fps = isNaN(Number(profile.fps)) ? profile.fps : Number(profile.fps);
+ if (profile.timeScale !== undefined && profile.timeScale !== '') { const ts = parseFloat(profile.timeScale); if (!isNaN(ts)) configs.timeScale = ts; }
+ if (profile.noiseSeed !== undefined && profile.noiseSeed !== '') { const ns = parseInt(profile.noiseSeed); if (!isNaN(ns)) configs.noiseSeed = ns; }
+ if (profile.timeSeed !== undefined && profile.timeSeed !== '') { const ts = parseInt(profile.timeSeed); if (!isNaN(ts)) configs.timeSeed = ts; }
+ if (profile.stackSeed !== undefined && profile.stackSeed !== '') {
+ const ss = profile.stackSeed;
+ if (ss === 'profile' || ss === 'real') configs.stackSeed = ss;
+ else if (!isNaN(parseInt(ss))) configs.stackSeed = parseInt(ss);
+ }
+
+ return configs;
+}
+
+function buildLaunchArgs(profile, userDataDir, botProfileArg) {
+ const args = [];
+
+ if (botProfileArg) args.push(`--bot-profile=${botProfileArg}`);
+ if (profile.profileDirPath && fs.existsSync(profile.profileDirPath)) {
+ args.push(`--bot-profile-dir=${profile.profileDirPath}`);
+ }
+
+ args.push(`--user-data-dir=${userDataDir}`);
+ args.push('--restore-last-session');
+ args.push(`--no-first-run`);
+
+ if (profile.name) args.push(`--bot-title=${profile.name}`);
+
+ if (profile.proxyServer && profile.proxyServer.trim()) args.push(`--proxy-server=${profile.proxyServer.trim()}`);
+ if (profile.proxyIp && profile.proxyIp.trim()) args.push(`--proxy-ip=${profile.proxyIp.trim()}`);
+ if (profile.proxyBypassRgx && profile.proxyBypassRgx.trim()) args.push(`--proxy-bypass-rgx=${profile.proxyBypassRgx.trim()}`);
+
+ if (profile.browserBrand && profile.browserBrand !== '') args.push(`--bot-config-browser-brand=${profile.browserBrand}`);
+ if (profile.brandFullVersion && profile.brandFullVersion !== '') args.push(`--bot-config-brand-full-version=${profile.brandFullVersion}`);
+ if (profile.uaFullVersion && profile.uaFullVersion !== '') args.push(`--bot-config-ua-full-version=${profile.uaFullVersion}`);
+ if (profile.userAgent && profile.userAgent.trim()) args.push(`--user-agent=${profile.userAgent.trim()}`);
+
+ if (profile.locale && profile.locale !== '') args.push(`--bot-config-locale=${profile.locale}`);
+ if (profile.timezone && profile.timezone !== '') args.push(`--bot-config-timezone=${profile.timezone}`);
+ if (profile.languages && profile.languages !== '') args.push(`--bot-config-languages=${profile.languages}`);
+ if (profile.location && profile.location !== '') args.push(`--bot-config-location=${profile.location}`);
+ if (profile.colorScheme) args.push(`--bot-config-color-scheme=${profile.colorScheme}`);
+
+ if (profile.platform && profile.platform !== '') args.push(`--bot-config-platform=${profile.platform}`);
+ if (profile.platformVersion) args.push(`--bot-config-platform-version=${profile.platformVersion}`);
+ if (profile.model) args.push(`--bot-config-model=${profile.model}`);
+ if (profile.architecture) args.push(`--bot-config-architecture=${profile.architecture}`);
+ if (profile.bitness) args.push(`--bot-config-bitness=${profile.bitness}`);
+ if (profile.mobile !== undefined && profile.mobile !== '') args.push(`--bot-config-mobile=${!!profile.mobile}`);
+
+ if (profile.windowSize) args.push(`--bot-config-window=${profile.windowSize}`);
+ if (profile.screenSize) args.push(`--bot-config-screen=${profile.screenSize}`);
+ if (profile.orientation) args.push(`--bot-config-orientation=${profile.orientation}`);
+ if (profile.keyboard) args.push(`--bot-config-keyboard=${profile.keyboard}`);
+ if (profile.fonts) args.push(`--bot-config-fonts=${profile.fonts}`);
+ if (profile.disableDeviceScaleFactorOnGUI) args.push('--bot-config-disable-device-scale-factor');
+
+ if (profile.webgl) args.push(`--bot-config-webgl=${profile.webgl}`);
+ if (profile.webgpu) args.push(`--bot-config-webgpu=${profile.webgpu}`);
+ if (profile.webrtc) args.push(`--bot-config-webrtc=${profile.webrtc}`);
+ if (profile.speechVoices) args.push(`--bot-config-speech-voices=${profile.speechVoices}`);
+ if (profile.mediaDevices) args.push(`--bot-config-media-devices=${profile.mediaDevices}`);
+ if (profile.mediaTypes) args.push(`--bot-config-media-types=${profile.mediaTypes}`);
+
+ if (profile.noiseCanvas !== undefined && profile.noiseCanvas !== '') args.push(`--bot-config-noise-canvas=${!!profile.noiseCanvas}`);
+ if (profile.noiseWebglImage !== undefined && profile.noiseWebglImage !== '') args.push(`--bot-config-noise-webgl-image=${!!profile.noiseWebglImage}`);
+ if (profile.noiseAudioContext !== undefined && profile.noiseAudioContext !== '') args.push(`--bot-config-noise-audio-context=${!!profile.noiseAudioContext}`);
+ if (profile.noiseClientRects !== undefined && profile.noiseClientRects !== '') args.push(`--bot-config-noise-client-rects=${!!profile.noiseClientRects}`);
+ if (profile.noiseTextRects !== undefined && profile.noiseTextRects !== '') args.push(`--bot-config-noise-text-rects=${!!profile.noiseTextRects}`);
+
+ if (profile.disableDebugger === true || profile.disableDebugger === 'true') args.push('--bot-disable-debugger');
+ if (!(profile.disableConsoleMessage === false || profile.disableConsoleMessage === 'false')) args.push('--bot-disable-console-message');
+ args.push('--bot-always-active');
+ if (profile.portProtection === true || profile.portProtection === 'true') args.push('--bot-port-protection');
+ if (profile.localDns === true || profile.localDns === 'true') args.push('--bot-local-dns');
+ if (profile.mobileForceTouch === true || profile.mobileForceTouch === 'true') args.push('--bot-mobile-force-touch');
+ if (profile.enableVariationsInContext === true || profile.enableVariationsInContext === 'true') args.push('--bot-enable-variations-in-context');
+ if (profile.networkInfoOverride === true || profile.networkInfoOverride === 'true') args.push('--bot-network-info-override');
+
+ if (profile.injectRandomHistory !== undefined && profile.injectRandomHistory !== '') {
+ const v = profile.injectRandomHistory;
+ if (v === true || v === 'true') args.push('--bot-inject-random-history=true');
+ else if (v !== false && v !== 'false' && !isNaN(Number(v))) args.push(`--bot-inject-random-history=${Number(v)}`);
+ }
+
+ if (profile.webrtcICE && profile.webrtcICE !== 'profile' && profile.webrtcICE !== '') {
+ args.push(`--bot-webrtc-ice=${profile.webrtcICE}`);
+ }
+
+ if (profile.noiseSeed !== undefined && profile.noiseSeed !== '') { const ns = parseInt(profile.noiseSeed); if (!isNaN(ns) && ns >= 0) args.push(`--bot-noise-seed=${ns}`); }
+ if (profile.timeSeed !== undefined && profile.timeSeed !== '') { const ts = parseInt(profile.timeSeed); if (!isNaN(ts) && ts >= 0) args.push(`--bot-time-seed=${ts}`); }
+ if (profile.stackSeed !== undefined && profile.stackSeed !== '') {
+ const ss = profile.stackSeed;
+ if (ss === 'profile' || ss === 'real') args.push(`--bot-stack-seed=${ss}`);
+ else if (!isNaN(parseInt(ss))) args.push(`--bot-stack-seed=${parseInt(ss)}`);
+ }
+ if (profile.timeScale !== undefined && profile.timeScale !== '') { const ts = parseFloat(profile.timeScale); if (!isNaN(ts) && ts > 0 && ts < 1) args.push(`--bot-time-scale=${ts}`); }
+ if (profile.fps !== undefined && profile.fps !== '' && profile.fps !== 'profile') args.push(`--bot-fps=${profile.fps}`);
+
+ if (profile.gpuEmulation === false || profile.gpuEmulation === 'false') args.push('--bot-gpu-emulation=false');
+
+ if (profile.customHeaders && typeof profile.customHeaders === 'object' && Object.keys(profile.customHeaders).length > 0) {
+ args.push('--bot-custom-headers=' + JSON.stringify(profile.customHeaders));
+ }
+
+ if (profile.ipService) args.push(`--bot-ip-service=${profile.ipService}`);
+
+ if (profile.mirrorController) args.push(`--bot-mirror-controller-endpoint=${profile.mirrorController}`);
+ if (profile.mirrorClient) args.push(`--bot-mirror-client-endpoint=${profile.mirrorClient}`);
+
+ if (profile.canvasRecordFile) args.push(`--bot-canvas-record-file=${profile.canvasRecordFile}`);
+ if (profile.audioRecordFile) args.push(`--bot-audio-record-file=${profile.audioRecordFile}`);
+
+ if (profile.botScript) args.push(`--bot-script=${profile.botScript}`);
+
+ if (profile.remoteDebuggingPort) args.push(`--remote-debugging-port=${profile.remoteDebuggingPort}`);
+
+ if (profile.cookies && profile.cookies.trim()) {
+ const cookieVal = profile.cookies.trim();
+ if (cookieVal.startsWith('@')) {
+ args.push(`--bot-cookies=${cookieVal}`);
+ } else {
+ try { JSON.parse(cookieVal); args.push('--bot-cookies=' + cookieVal); } catch {; }
+ }
+ }
+
+ if (profile.bookmarks && profile.bookmarks.trim()) {
+ try { JSON.parse(profile.bookmarks.trim()); args.push('--bot-bookmarks=' + profile.bookmarks.trim()); } catch {; }
+ }
+
+ if (profile.startUrl && profile.startUrl.trim()) args.push(profile.startUrl.trim());
+
+ return args;
+}
+
+// ─── CDP Cookie Save ──────────────────────────────────────────────────────────
+
+async function saveCookiesViaCDP(profileId, port, userDataDir) {
+ const cookies = await fetchCookiesViaCDP(port);
+ if (!Array.isArray(cookies)) return;
+
+ const savePath = path.join(userDataDir, 'saved-cookies.json');
+ fs.mkdirSync(userDataDir, { recursive: true });
+ fs.writeFileSync(savePath, JSON.stringify(cookies, null, 2), 'utf8');
+
+ const profiles = store.get('profiles', []);
+ const idx = profiles.findIndex(p => p.id === profileId);
+ if (idx !== -1) {
+ profiles[idx].cookieCount = cookies.length;
+ profiles[idx].cookiesSavedAt = new Date().toISOString();
+ profiles[idx].savedCookiesPath = savePath;
+ store.set('profiles', profiles);
+ }
+
+ mainWindow?.webContents.send('profile:cookiesSaved', { profileId, count: cookies.length, path: savePath });
+}
+
+function fetchCookiesViaCDP(port) {
+ return new Promise((resolve, reject) => {
+ const crypto = require('crypto');
+ const timeout = setTimeout(() => reject(new Error('CDP timeout')), 5000);
+
+ const httpClient = net.createConnection({ port, host: '127.0.0.1' }, () => {
+ httpClient.write('GET /json/list HTTP/1.1\r\nHost: 127.0.0.1:' + port + '\r\nConnection: close\r\n\r\n');
+ });
+ let httpData = '';
+ httpClient.on('data', d => { httpData += d.toString(); });
+ httpClient.on('end', () => {
+ try {
+ const body = httpData.slice(httpData.indexOf('\r\n\r\n') + 4);
+ const tabs = JSON.parse(body);
+ const tab = tabs.find(t => t.type === 'page') || tabs[0];
+ if (!tab?.webSocketDebuggerUrl) { clearTimeout(timeout); reject(new Error('No debuggable tab')); return; }
+ const wsPath = tab.webSocketDebuggerUrl.replace(/^ws:\/\/[^/]+/, '');
+
+ const wsClient = net.createConnection({ port, host: '127.0.0.1' }, () => {
+ const key = crypto.randomBytes(16).toString('base64');
+ wsClient.write('GET ' + wsPath + ' HTTP/1.1\r\nHost: 127.0.0.1:' + port + '\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Key: ' + key + '\r\nSec-WebSocket-Version: 13\r\n\r\n');
+ });
+
+ let wsHandshakeDone = false, wsBuffer = Buffer.alloc(0);
+
+ function sendWsFrame(payload) {
+ const data = Buffer.from(JSON.stringify(payload));
+ const maskKey = crypto.randomBytes(4);
+ const masked = Buffer.alloc(data.length);
+ for (let i = 0; i < data.length; i++) masked[i] = data[i] ^ maskKey[i % 4];
+ let header;
+ if (data.length < 126) { header = Buffer.alloc(6); header[0] = 0x81; header[1] = 0x80 | data.length; maskKey.copy(header, 2); }
+ else if (data.length < 65536) { header = Buffer.alloc(8); header[0] = 0x81; header[1] = 0x80 | 126; header.writeUInt16BE(data.length, 2); maskKey.copy(header, 4); }
+ else { header = Buffer.alloc(14); header[0] = 0x81; header[1] = 0x80 | 127; header.writeBigUInt64BE(BigInt(data.length), 2); maskKey.copy(header, 10); }
+ wsClient.write(Buffer.concat([header, masked]));
+ }
+
+ function parseWsFrames(buf) {
+ const messages = []; let offset = 0;
+ while (offset + 2 <= buf.length) {
+ const b1 = buf[offset + 1]; const isMasked = (b1 & 0x80) !== 0;
+ let payloadLen = b1 & 0x7f, headerLen = 2;
+ if (payloadLen === 126) { if (offset + 4 > buf.length) break; payloadLen = buf.readUInt16BE(offset + 2); headerLen = 4; }
+ else if (payloadLen === 127) { if (offset + 10 > buf.length) break; payloadLen = Number(buf.readBigUInt64BE(offset + 2)); headerLen = 10; }
+ const maskLen = isMasked ? 4 : 0;
+ const frameEnd = offset + headerLen + maskLen + payloadLen;
+ if (frameEnd > buf.length) break;
+ let payload = buf.slice(offset + headerLen + maskLen, frameEnd);
+ if (isMasked) { const mask = buf.slice(offset + headerLen, offset + headerLen + 4); payload = Buffer.from(payload); for (let i = 0; i < payload.length; i++) payload[i] ^= mask[i % 4]; }
+ messages.push(payload.toString('utf8')); offset = frameEnd;
+ }
+ return { messages, remaining: buf.slice(offset) };
+ }
+
+ wsClient.on('data', (chunk) => {
+ if (!wsHandshakeDone) {
+ if (chunk.indexOf('\r\n\r\n') !== -1) {
+ wsHandshakeDone = true;
+ const rest = chunk.slice(chunk.indexOf('\r\n\r\n') + 4);
+ if (rest.length > 0) wsBuffer = Buffer.concat([wsBuffer, rest]);
+ sendWsFrame({ id: 1, method: 'Network.getAllCookies', params: {} });
+ }
+ return;
+ }
+ wsBuffer = Buffer.concat([wsBuffer, chunk]);
+ const { messages, remaining } = parseWsFrames(wsBuffer);
+ wsBuffer = remaining;
+ for (const msg of messages) {
+ try {
+ const parsed = JSON.parse(msg);
+ if (parsed.id === 1 && parsed.result?.cookies) {
+ clearTimeout(timeout); wsClient.destroy(); resolve(parsed.result.cookies); return;
+ }
+ } catch {}
+ }
+ });
+ wsClient.on('error', e => { clearTimeout(timeout); reject(e); });
+ } catch (e) { clearTimeout(timeout); reject(e); }
+ });
+ httpClient.on('error', e => { clearTimeout(timeout); reject(e); });
+ });
+}
+
+// ─── Profile Status ───────────────────────────────────────────────────────────
+
+function updateProfileStatus(profileId, status) {
+ const profiles = store.get('profiles', []);
+ const idx = profiles.findIndex(p => p.id === profileId);
+ if (idx !== -1) {
+ profiles[idx].status = status;
+ store.set('profiles', profiles);
+ mainWindow?.webContents.send('profile:statusChanged', { profileId, status });
+ }
+}
+
+// ─── App Lifecycle ────────────────────────────────────────────────────────────
+
+app.whenReady().then(() => {
+ nativeTheme.themeSource = 'dark';
+
+ const profiles = store.get('profiles', []);
+ const hadStale = profiles.some(p => p.status === 'running');
+ if (hadStale) {
+ store.set('profiles', profiles.map(p =>
+ p.status === 'running' ? { ...p, status: 'stopped' } : p
+ ));
+ }
+
+ createWindow();
+ app.on('activate', () => {
+ if (BrowserWindow.getAllWindows().length === 0) createWindow();
+ });
+});
+
+app.on('window-all-closed', () => {
+ for (const [, inst] of runningInstances) { try { inst.process.kill(); } catch {} }
+ for (const [profileId] of tempFiles) { cleanupTempFile(profileId); }
+ if (!IS_MAC) app.quit();
+});
+
+app.on('before-quit', () => {
+ for (const [, inst] of runningInstances) { try { inst.process.kill(); } catch {} }
+ for (const [profileId] of tempFiles) { cleanupTempFile(profileId); }
+});
\ No newline at end of file
diff --git a/botbrowser-control/src/preload/preload.js b/botbrowser-control/src/preload/preload.js
new file mode 100644
index 0000000..91cc9c2
--- /dev/null
+++ b/botbrowser-control/src/preload/preload.js
@@ -0,0 +1,85 @@
+const { contextBridge, ipcRenderer } = require('electron');
+
+contextBridge.exposeInMainWorld('api', {
+ // Profiles
+ profiles: {
+ getAll: () => ipcRenderer.invoke('profiles:getAll'),
+ create: (data) => ipcRenderer.invoke('profiles:create', data),
+ update: (id, updates) => ipcRenderer.invoke('profiles:update', { id, updates }),
+ delete: (id) => ipcRenderer.invoke('profiles:delete', id),
+ deleteMultiple: (ids) => ipcRenderer.invoke('profiles:deleteMultiple', ids),
+ duplicate: (id) => ipcRenderer.invoke('profiles:duplicate', id),
+ },
+
+ // Browser instances
+ browser: {
+ launch: (profileId) => ipcRenderer.invoke('browser:launch', profileId),
+ stop: (profileId) => ipcRenderer.invoke('browser:stop', profileId),
+ stopAll: () => ipcRenderer.invoke('browser:stopAll'),
+ getRunning: () => ipcRenderer.invoke('browser:getRunning'),
+ },
+
+ // Settings
+ settings: {
+ get: () => ipcRenderer.invoke('settings:get'),
+ set: (data) => ipcRenderer.invoke('settings:set', data),
+ },
+
+ // Dialogs
+ dialog: {
+ openFile: (options) => ipcRenderer.invoke('dialog:openFile', options),
+ saveFile: (options) => ipcRenderer.invoke('dialog:saveFile', options),
+ selectExecutable: () => ipcRenderer.invoke('dialog:selectExecutable'),
+ selectDirectory: () => ipcRenderer.invoke('dialog:selectDirectory'),
+ },
+
+ // Shell
+ shell: {
+ openPath: (p) => ipcRenderer.invoke('shell:openPath', p),
+ showItemInFolder: (p) => ipcRenderer.invoke('shell:showItemInFolder', p),
+ },
+
+ // Proxy / IP Check
+ proxy: {
+ checkIp: (proxyServer) => ipcRenderer.invoke('proxy:checkIp', proxyServer),
+ },
+
+ // Kernel Manager
+ kernel: {
+ fetchReleases: () => ipcRenderer.invoke('kernel:fetchReleases'),
+ getCachedReleases: () => ipcRenderer.invoke('kernel:getCachedReleases'),
+ getDir: () => ipcRenderer.invoke('kernel:getDir'),
+ listInstalled: () => ipcRenderer.invoke('kernel:listInstalled'),
+ delete: (version) => ipcRenderer.invoke('kernel:delete', version),
+ download: (opts) => ipcRenderer.invoke('kernel:download', opts),
+ },
+
+ // App / Update checker
+ app: {
+ checkForUpdates: () => ipcRenderer.invoke('app:checkForUpdates'),
+ },
+
+ // Platform info
+ platform: process.platform,
+ arch: process.arch,
+
+ // Event listeners
+ on: (channel, callback) => {
+ const validChannels = [
+ 'navigate', 'action',
+ 'instance:started', 'instance:stopped', 'instance:error',
+ 'profile:statusChanged', 'profile:cookiesSaved',
+ 'kernel:downloadProgress', 'kernel:downloadComplete', 'kernel:downloadError',
+ 'app:updateAvailable',
+ ];
+ if (validChannels.includes(channel)) {
+ const sub = (_, ...args) => callback(...args);
+ ipcRenderer.on(channel, sub);
+ return () => ipcRenderer.removeListener(channel, sub);
+ }
+ },
+
+ once: (channel, callback) => {
+ ipcRenderer.once(channel, (_, ...args) => callback(...args));
+ }
+});
\ No newline at end of file
diff --git a/botbrowser-control/src/renderer/index.html b/botbrowser-control/src/renderer/index.html
new file mode 100644
index 0000000..ff6de56
--- /dev/null
+++ b/botbrowser-control/src/renderer/index.html
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+ BotBrowser Control
+
+
+
+
+
+
+
+
+
+
+
+
BotBrowser Control
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/botbrowser-control/src/renderer/js/app.js b/botbrowser-control/src/renderer/js/app.js
new file mode 100644
index 0000000..1ff69ae
--- /dev/null
+++ b/botbrowser-control/src/renderer/js/app.js
@@ -0,0 +1,1918 @@
+/* BotBrowser Control — Renderer */
+'use strict';
+
+(function () {
+ // ─── State ────────────────────────────────────────────────────────────────────
+ let profiles = [];
+ let runningSessions = [];
+ let currentView = 'profiles';
+ let editingProfileId = null;
+ let searchQuery = '';
+ let selectedProfileIds = new Set();
+ let settings = {};
+ const IS_WIN = window.api.platform === 'win32';
+ const IS_MAC = window.api.platform === 'darwin';
+
+ // Kernel manager state
+ let kernelReleases = null;
+ let kernelInstalled = [];
+ let kernelDownloads = {};
+
+ // IP check state (profileId -> result)
+ let ipCheckResults = {};
+ let ipCheckLoading = {};
+
+ // Inline proxy edit state (profileId being edited)
+ let inlineProxyEditId = null;
+
+ // Update notification state
+ let updateInfo = null;
+
+ // ─── Icons ────────────────────────────────────────────────────────────────────
+ const I = {
+ play: ' ',
+ stop: ' ',
+ plus: ' ',
+ edit: ' ',
+ trash: ' ',
+ copy: ' ',
+ settings: ' ',
+ profile: ' ',
+ session: ' ',
+ shield: ' ',
+ network: ' ',
+ cpu: ' ',
+ zap: ' ',
+ user: ' ',
+ cookie: ' ',
+ check: ' ',
+ folder: ' ',
+ globe: ' ',
+ info: ' ',
+ download: ' ',
+ kernel: ' ',
+ proxy: ' ',
+ android: ' ',
+ refresh: ' ',
+ use: ' ',
+ bell: ' ',
+ close: ' ',
+ };
+
+ // ─── Helpers ──────────────────────────────────────────────────────────────────
+ function esc(s) { return String(s||'').replace(/&/g,'&').replace(//g,'>').replace(/"/g,'"'); }
+ function el(id) { return document.getElementById(id); }
+ function val(id) { const e = el(id); return e ? e.value.trim() : ''; }
+ function chk(id) { const e = el(id); return e ? e.checked : false; }
+ function selVal(id) { const e = el(id); return e ? e.value : ''; }
+
+ function timeAgo(iso) {
+ if (!iso) return '';
+ const s = Math.floor((Date.now() - new Date(iso)) / 1000);
+ if (s < 60) return 'just now';
+ if (s < 3600) return Math.floor(s/60) + 'm ago';
+ if (s < 86400) return Math.floor(s/3600) + 'h ago';
+ return Math.floor(s/86400) + 'd ago';
+ }
+
+ function showToast(msg, type = 'info', duration = 3500) {
+ const t = document.createElement('div');
+ t.className = `toast toast-${type}`;
+ t.innerHTML = `${msg} `;
+ document.getElementById('toast-container').appendChild(t);
+ requestAnimationFrame(() => t.classList.add('show'));
+ setTimeout(() => { t.classList.remove('show'); setTimeout(() => t.remove(), 300); }, duration);
+ }
+
+ function countryCodeToEmoji(cc) {
+ if (!cc || cc.length !== 2) return '🌐';
+ return String.fromCodePoint(...[...cc.toUpperCase()].map(c => 0x1F1E6 - 65 + c.charCodeAt(0)));
+ }
+
+ // Normalize proxy string: strip duplicate schemes, then default to socks5:// if no scheme
+ function normalizeProxy(s) {
+ if (!s || !s.trim()) return '';
+ s = s.trim();
+ // Strip duplicate scheme: e.g. socks5://socks5:// → socks5://
+ s = s.replace(/^(socks5?[ah]?|https?):\/\/(socks5?[ah]?|https?):\/\//i, '$1://');
+ if (!/^[a-z]+:\/\//i.test(s)) return 'socks5://' + s;
+ return s;
+ }
+
+ // ─── Init ─────────────────────────────────────────────────────────────────────
+ async function init() {
+ settings = await window.api.settings.get();
+ await loadProfiles();
+ await refreshRunningSessions();
+ // Load cached kernel releases so Kernel Manager shows immediately on open
+ await loadCachedKernelReleases();
+ render();
+ bindNav();
+ bindEvents();
+ // Check for updates in background after 2s
+ setTimeout(checkForUpdates, 2000);
+ }
+
+ async function loadProfiles() {
+ profiles = await window.api.profiles.getAll();
+ }
+
+ async function refreshRunningSessions() {
+ runningSessions = await window.api.browser.getRunning();
+ }
+
+ async function checkForUpdates() {
+ try {
+ const info = await window.api.app.checkForUpdates();
+ updateInfo = info;
+ if (info.newKernel || info.newControl) {
+ showUpdateBanner(info);
+ }
+ } catch {}
+ }
+
+ function showUpdateBanner(info) {
+ const existing = document.getElementById('update-banner');
+ if (existing) existing.remove();
+
+ const parts = [];
+ if (info.newKernel && info.kernel) {
+ parts.push(`🧠 New BotBrowser kernel: ${esc(info.kernel.tagName)} `);
+ }
+ if (info.newControl && info.control) {
+ parts.push(`🚀 New Control app: ${esc(info.control.tagName)} `);
+ }
+ if (!parts.length) return;
+
+ const banner = document.createElement('div');
+ banner.id = 'update-banner';
+ banner.className = 'update-banner';
+ banner.innerHTML = `
+ ${I.bell}
+ ${parts.join(' · ')}
+ Download
+ ${I.close}
+ `;
+ // Insert after sidebar, before main content
+ const layout = document.querySelector('.app-layout') || document.body;
+ const main = document.getElementById('main-content');
+ if (main && main.parentNode) {
+ main.parentNode.insertBefore(banner, main);
+ } else {
+ layout.prepend(banner);
+ }
+ }
+
+ // ─── Navigation ───────────────────────────────────────────────────────────────
+ function bindNav() {
+ document.querySelectorAll('[data-nav]').forEach(btn => {
+ btn.addEventListener('click', () => {
+ currentView = btn.dataset.nav;
+ selectedProfileIds.clear();
+ render();
+ });
+ });
+ window.api.on('navigate', (view) => { currentView = view; render(); });
+ window.api.on('action', (action) => { if (action === 'new-profile') openProfileEditor(null); });
+ }
+
+ // ─── Event Delegation ─────────────────────────────────────────────────────────
+ function bindEvents() {
+ document.addEventListener('click', (e) => {
+ if (!e.target.closest('.context-menu')) {
+ document.querySelectorAll('.context-menu').forEach(m => m.remove());
+ }
+ // Close inline proxy editor on outside click (not on the input itself or recheck button)
+ if (inlineProxyEditId && !e.target.closest('.proxy-inline-input') && !e.target.closest('[data-action="open-proxy-editor"]') && !e.target.closest('[data-action="check-ip"]')) {
+ closeInlineProxyEditor();
+ }
+ const btn = e.target.closest('[data-action]');
+ if (!btn) return;
+ const action = btn.dataset.action;
+ const data = { ...btn.dataset };
+ handleAction(action, data, e);
+ });
+
+ document.addEventListener('change', (e) => {
+ if (e.target.id === 'search-input') {
+ searchQuery = e.target.value.toLowerCase();
+ renderProfiles();
+ }
+ if (e.target.dataset.action === 'select-profile') {
+ const id = e.target.dataset.id;
+ if (e.target.checked) selectedProfileIds.add(id);
+ else selectedProfileIds.delete(id);
+ updateBulkBar();
+ }
+ if (e.target.id === 'select-all-profiles') {
+ const visible = getVisibleProfiles();
+ if (e.target.checked) visible.forEach(p => selectedProfileIds.add(p.id));
+ else selectedProfileIds.clear();
+ renderProfiles();
+ updateBulkBar();
+ }
+ });
+
+ document.addEventListener('input', (e) => {
+ if (e.target.id === 'search-input') {
+ searchQuery = e.target.value.toLowerCase();
+ renderProfiles();
+ }
+ });
+
+ // IPC events
+ window.api.on('instance:started', ({ profileId }) => {
+ const p = profiles.find(x => x.id === profileId);
+ if (p) { p.status = 'running'; }
+ refreshRunningSessions().then(() => renderView());
+ });
+ window.api.on('instance:stopped', ({ profileId }) => {
+ const p = profiles.find(x => x.id === profileId);
+ if (p) { p.status = 'stopped'; }
+ refreshRunningSessions().then(() => renderView());
+ });
+ window.api.on('instance:error', ({ profileId, error }) => {
+ showToast(`Error: ${error}`, 'error', 5000);
+ const p = profiles.find(x => x.id === profileId);
+ if (p) p.status = 'stopped';
+ refreshRunningSessions().then(() => renderView());
+ });
+ window.api.on('profile:statusChanged', ({ profileId, status }) => {
+ const p = profiles.find(x => x.id === profileId);
+ if (p) p.status = status;
+ renderView();
+ });
+ window.api.on('profile:cookiesSaved', ({ profileId, count, path: cookiePath }) => {
+ const p = profiles.find(x => x.id === profileId);
+ if (p) { p.savedCookiesPath = cookiePath; p.cookieCount = count; p.cookiesSavedAt = new Date().toISOString(); }
+ showToast(`${count} cookies saved for profile`, 'success');
+ renderView();
+ });
+
+ // Kernel download events
+ window.api.on('kernel:downloadProgress', ({ version, progress }) => {
+ kernelDownloads[version] = { ...(kernelDownloads[version] || {}), progress, status: 'downloading' };
+ updateKernelProgressUI(version, progress);
+ });
+ window.api.on('kernel:downloadComplete', ({ version, installStatus, installNote }) => {
+ kernelDownloads[version] = { progress: 100, status: 'done' };
+ const msg = installStatus === 'installed' ? `Kernel ${version} installed! ✅` :
+ installStatus === 'ready' ? `Kernel ${version} ready to use.` :
+ `Kernel ${version} downloaded. ${installNote || ''}`;
+ showToast(msg, 'success', 6000);
+ window.api.kernel.listInstalled().then(list => {
+ kernelInstalled = list;
+ if (currentView === 'settings') renderSettings();
+ });
+ });
+ }
+
+ function handleAction(action, data, e) {
+ switch (action) {
+ case 'launch-profile': launchProfile(data.id); break;
+ case 'stop-profile': stopProfile(data.id); break;
+ case 'stop-all': stopAll(); break;
+ case 'edit-profile': openProfileEditor(data.id); break;
+ case 'duplicate-profile': duplicateProfile(data.id); break;
+ case 'delete-profile': deleteProfile(data.id); break;
+ case 'new-profile': openProfileEditor(null); break;
+ case 'save-profile': saveProfile(); break;
+ case 'cancel-edit': closeProfileEditor(); break;
+ case 'show-context': showContextMenu(data.id, e); break;
+ case 'delete-selected': deleteSelected(); break;
+ case 'clear-selection': clearSelection(); break;
+ case 'browse-file': browseFile(data.target, data.filter); break;
+ case 'browse-dir': browseDir(data.target); break;
+ case 'save-settings': saveSettings(); break;
+ case 'browse-exe': browseExe(); break;
+ case 'browse-userdata': browseUserData(); break;
+ case 'tab-switch': switchTab(data.tab); break;
+ case 'add-header': addCustomHeader(); break;
+ case 'remove-header': removeCustomHeader(data.key); break;
+ // IP check
+ case 'check-ip': checkProfileIp(data.id); break;
+ // Inline proxy editor
+ case 'open-proxy-editor': openInlineProxyEditor(data.id); break;
+ case 'save-inline-proxy': saveInlineProxy(data.id); break;
+ case 'cancel-inline-proxy': closeInlineProxyEditor(); break;
+ // Kernel manager
+ case 'kernel-refresh': fetchKernelReleases(true); break;
+ case 'kernel-download': downloadKernel(data.version, data.url, data.filename); break;
+ case 'kernel-delete': deleteKernel(data.version); break;
+ case 'kernel-use': useKernelPath(data.execpath); break;
+ case 'kernel-open-dir': window.api.shell.openPath(data.dir); break;
+ }
+ }
+
+ // ─── Render ───────────────────────────────────────────────────────────────────
+ function render() {
+ updateNavActive();
+ renderView();
+ }
+
+ function renderView() {
+ const main = el('main-content');
+ if (!main) return;
+ if (currentView === 'profiles') renderProfiles();
+ else if (currentView === 'sessions') renderSessions();
+ else if (currentView === 'settings') renderSettings();
+ }
+
+ function updateNavActive() {
+ document.querySelectorAll('[data-nav]').forEach(btn => {
+ btn.classList.toggle('active', btn.dataset.nav === currentView);
+ });
+ }
+
+ function getVisibleProfiles() {
+ if (!searchQuery) return profiles;
+ return profiles.filter(p =>
+ (p.name||'').toLowerCase().includes(searchQuery) ||
+ (p.proxyServer||'').toLowerCase().includes(searchQuery) ||
+ (p.browserBrand||'').toLowerCase().includes(searchQuery) ||
+ (p.startUrl||'').toLowerCase().includes(searchQuery)
+ );
+ }
+
+ function updateBulkBar() {
+ const bar = el('bulk-action-bar');
+ if (!bar) return;
+ if (selectedProfileIds.size > 0) {
+ bar.style.display = 'flex';
+ const countEl = bar.querySelector('.bulk-count');
+ if (countEl) countEl.textContent = `${selectedProfileIds.size} selected`;
+ } else {
+ bar.style.display = 'none';
+ }
+ }
+
+ // ─── Profile List Rendering ───────────────────────────────────────────────────
+ function renderProfiles() {
+ const main = el('main-content');
+ if (!main) return;
+ const visible = getVisibleProfiles();
+ const allSelected = visible.length > 0 && visible.every(p => selectedProfileIds.has(p.id));
+ const runningSet = new Set(runningSessions.map(s => s.profileId));
+
+ const brandMeta = {
+ chrome: { bg: 'linear-gradient(135deg,#4285f4,#34a853)', label: 'Ch' },
+ edge: { bg: 'linear-gradient(135deg,#0078d4,#00bcf2)', label: 'Ed' },
+ brave: { bg: 'linear-gradient(135deg,#fb542b,#ff7f4d)', label: 'Br' },
+ opera: { bg: 'linear-gradient(135deg,#cc0f16,#ff4444)', label: 'Op' },
+ firefox: { bg: 'linear-gradient(135deg,#ff9500,#ff6611)', label: 'Fx' },
+ chromium: { bg: 'linear-gradient(135deg,#6366f1,#8b5cf6)', label: 'Cr' },
+ webview: { bg: 'linear-gradient(135deg,#10b981,#059669)', label: 'Wv' },
+ };
+
+ const statP = el('stat-profiles'); if (statP) statP.textContent = profiles.length;
+ const statR = el('stat-running'); if (statR) statR.textContent = runningSessions.length;
+ const statInd = el('stat-indicator'); if (statInd) statInd.classList.toggle('running', runningSessions.length > 0);
+ const badgeP = el('badge-profiles'); if (badgeP) badgeP.textContent = profiles.length || '';
+ const badgeS = el('badge-sessions'); if (badgeS) { badgeS.textContent = runningSessions.length || ''; badgeS.style.display = runningSessions.length ? '' : 'none'; }
+
+ main.innerHTML = `
+
+
+ ${selectedProfileIds.size > 0 ? `
+
+ ${selectedProfileIds.size} selected
+ ${I.trash} Delete Selected
+ ✕ Clear
+
` : `
`}
+
+ ${visible.length === 0 ? `
+
+
${I.profile}
+ ${profiles.length === 0
+ ? `
No profiles yet Create your first browser profile to get started.
+
${I.plus} Create Profile `
+ : `
No results No profiles match your search query.
`
+ }
+
` : `
+
+
+ ${visible.map(profile => {
+ const isRunning = runningSet.has(profile.id) || profile.status === 'running';
+ const isSelected = selectedProfileIds.has(profile.id);
+ const brandKey = (profile.browserBrand || '').toLowerCase();
+ const bm = brandMeta[brandKey] || { bg: 'linear-gradient(135deg,#6366f1,#8b5cf6)', label: (profile.name || 'P').charAt(0).toUpperCase() };
+ const letter = profile.browserBrand ? bm.label : (profile.name || 'P').charAt(0).toUpperCase();
+
+ // Proxy display
+ const ipResult = ipCheckResults[profile.id];
+ const proxyDisplay = renderProxyCell(profile, ipResult);
+
+ const brandTagClass = brandKey === 'firefox' ? 'tag-firefox' : 'tag-chrome';
+ return `
+
+
+
+
+
+
+
+
+
+
+
${esc(profile.name || 'Unnamed')}
+
+ ${profile.startUrl
+ ? `${I.globe}${esc(profile.startUrl)} `
+ : `No start URL `
+ }
+ ${profile.cookieCount
+ ? `${I.cookie} ${profile.cookieCount}${profile.cookiesSavedAt ? ' · ' + timeAgo(profile.cookiesSavedAt) : ''} `
+ : ''
+ }
+
+
+
+
+ ${proxyDisplay}
+
+
+ ${profile.browserBrand
+ ? `${profile.browserBrand.charAt(0).toUpperCase() + profile.browserBrand.slice(1)} `
+ : ''
+ }
+ ${profile.profileFilePath ? `.enc ` : ''}
+ ${profile.remoteDebuggingPort ? `CDP ` : ''}
+
+
+
+
+ ${isRunning ? 'Running' : 'Idle'}
+
+
+
+ ${isRunning
+ ? `${I.stop} Stop `
+ : `${I.play} Launch `
+ }
+ ${I.edit}
+ ${I.copy}
+ ${I.trash}
+ ⋮
+
+
`;
+ }).join('')}
+
`}
+ `;
+
+ // If inline proxy editor is open for a profile, re-mount the input
+ if (inlineProxyEditId) {
+ mountInlineProxyInput(inlineProxyEditId);
+ }
+ }
+
+ // ─── Proxy Cell Rendering ─────────────────────────────────────────────────────
+ function renderProxyCell(profile, ipResult) {
+ const proxyStr = profile.proxyServer || '';
+ const isLoading = ipCheckLoading[profile.id];
+
+ // Parse scheme for badge color
+ let schemeBadge = '';
+ if (proxyStr) {
+ const m = proxyStr.match(/^([a-z0-9+\-]+):\/\//i);
+ const scheme = m ? m[1].toLowerCase() : 'socks5';
+ const schemeColor = scheme.startsWith('socks5') ? '#9b59b6' :
+ scheme.startsWith('socks4') ? '#8e44ad' :
+ scheme.startsWith('http') ? '#27ae60' : '#7f8c8d';
+ schemeBadge = `${scheme.toUpperCase()} `;
+ }
+
+ // Host:port display (strip scheme+auth)
+ const proxyHost = proxyStr
+ ? proxyStr.replace(/^[a-z+\-]+:\/\/[^@]+@/i, '').replace(/^[a-z+\-]+:\/\//i, '')
+ : '';
+
+ // Status dot: green=ok, red=fail/error, yellow=loading, grey=unchecked
+ let statusDot = '';
+ let flagDisplay = '';
+ if (proxyStr) {
+ if (isLoading) {
+ statusDot = ` `;
+ } else if (ipCheckResults[profile.id] === null) {
+ statusDot = ` `;
+ } else if (ipResult && ipResult.status !== 'fail') {
+ const flag = ipResult.countryCode ? countryCodeToEmoji(ipResult.countryCode) : '';
+ const country = ipResult.countryCode || '';
+ const isHosting = ipResult.hosting || ipResult.proxy;
+ statusDot = ` `;
+ flagDisplay = `${flag} ${esc(country)} ${isHosting ? ' ⚠' : ''} `;
+ } else {
+ statusDot = ` `;
+ }
+ }
+
+ if (!proxyStr) {
+ return `
+ ${I.proxy} Set proxy…
+ `;
+ }
+
+ return `
+
+ ${statusDot}
+ ${schemeBadge}
+ ${esc(proxyHost)}
+
+ ${flagDisplay ? `
${flagDisplay}
+ ↺
+
` : isLoading ? `
⟳ Checking…
` : ''}
+
`;
+ }
+
+ // ─── GoLogin-style Inline Proxy Editor ──────────────────────────────────────────────
+ // Clicking the proxy host text converts it directly into an in-place.
+ // No popup, no expanded editor — just a plain input field inline.
+
+ function openInlineProxyEditor(profileId) {
+ if (inlineProxyEditId === profileId) return; // already editing
+ if (inlineProxyEditId) closeInlineProxyEditor();
+ inlineProxyEditId = profileId;
+ mountInlineProxyInput(profileId);
+ }
+
+ function mountInlineProxyInput(profileId) {
+ const profile = profiles.find(p => p.id === profileId);
+ if (!profile) return;
+ const current = profile.proxyServer || '';
+
+ // Find the proxy-host-text span and replace it with an input in-place
+ const cell = document.getElementById(`proxy-cell-${profileId}`);
+ if (!cell) return;
+ const hostSpan = cell.querySelector('.proxy-host-text');
+ if (!hostSpan) return;
+
+ const inp = document.createElement('input');
+ inp.type = 'text';
+ inp.className = 'proxy-inline-input';
+ inp.id = `proxy-inline-input-${profileId}`;
+ inp.value = current;
+ inp.placeholder = 'socks5://host:port';
+ inp.autocomplete = 'off';
+ inp.spellcheck = false;
+ hostSpan.replaceWith(inp);
+ inp.focus();
+ inp.select();
+
+ // Debounced auto-check as user types
+ let debounceTimer = null;
+ inp.addEventListener('input', () => {
+ clearTimeout(debounceTimer);
+ debounceTimer = setTimeout(() => {
+ const val = normalizeProxy(inp.value.trim());
+ if (val) checkProxyValueQuick(profileId, val);
+ }, 900);
+ });
+
+ inp.addEventListener('keydown', (ev) => {
+ if (ev.key === 'Enter') { ev.preventDefault(); saveInlineProxy(profileId); }
+ if (ev.key === 'Escape') { ev.preventDefault(); closeInlineProxyEditor(); }
+ });
+
+ inp.addEventListener('blur', () => {
+ // Small delay so a click on the recheck button doesn't prematurely cancel
+ setTimeout(() => {
+ if (inlineProxyEditId === profileId) saveInlineProxy(profileId);
+ }, 150);
+ });
+ }
+
+ // Quick IP check while typing (updates dot + flag row without disturbing the input)
+ async function checkProxyValueQuick(profileId, proxyValue) {
+ if (ipCheckLoading[profileId]) return;
+ ipCheckLoading[profileId] = true;
+ const cell = document.getElementById(`proxy-cell-${profileId}`);
+ if (cell) {
+ const dot = cell.querySelector('.proxy-status-dot');
+ if (dot) { dot.className = 'proxy-status-dot checking'; dot.title = 'Checking…'; }
+ }
+ try {
+ const result = await window.api.proxy.checkIp(proxyValue);
+ ipCheckResults[profileId] = result;
+ } catch (e) {
+ ipCheckResults[profileId] = null;
+ } finally {
+ ipCheckLoading[profileId] = false;
+ const cell2 = document.getElementById(`proxy-cell-${profileId}`);
+ if (cell2 && inlineProxyEditId === profileId) {
+ const ipResult = ipCheckResults[profileId];
+ // Update dot
+ const dot = cell2.querySelector('.proxy-status-dot');
+ if (dot) {
+ if (ipResult && ipResult.status !== 'fail') {
+ const isHosting = ipResult.hosting || ipResult.proxy;
+ dot.className = `proxy-status-dot ok${isHosting ? ' dc' : ''}`;
+ dot.title = `${ipResult.query} · ${ipResult.city||''}, ${ipResult.country||''}`;
+ } else {
+ dot.className = 'proxy-status-dot error';
+ dot.title = 'Check failed';
+ }
+ }
+ // Update or create the flag/ip row
+ const proxyContent = cell2.querySelector('.proxy-cell-content');
+ let ipRow = cell2.querySelector('.proxy-ip-row');
+ if (ipResult && ipResult.status !== 'fail') {
+ const flag = ipResult.countryCode ? countryCodeToEmoji(ipResult.countryCode) : '';
+ const country = ipResult.countryCode || '';
+ const isHosting = ipResult.hosting || ipResult.proxy;
+ const flagHtml = `${flag} ${esc(country)} ${isHosting ? ' ⚠' : ''}
+ ↺ `;
+ if (!ipRow) {
+ ipRow = document.createElement('div');
+ ipRow.className = 'proxy-ip-row';
+ if (proxyContent) proxyContent.appendChild(ipRow);
+ }
+ if (ipRow) ipRow.innerHTML = flagHtml;
+ } else if (ipRow) {
+ ipRow.remove();
+ }
+ }
+ }
+ }
+
+ function closeInlineProxyEditor() {
+ const prevId = inlineProxyEditId;
+ inlineProxyEditId = null;
+ if (prevId) {
+ const cell = document.getElementById(`proxy-cell-${prevId}`);
+ const profile = profiles.find(p => p.id === prevId);
+ if (cell && profile) cell.innerHTML = renderProxyCell(profile, ipCheckResults[prevId]);
+ }
+ }
+
+ async function saveInlineProxy(profileId) {
+ const inp = document.getElementById(`proxy-inline-input-${profileId}`);
+ const proxyRaw = inp ? inp.value.trim() : '';
+ const proxyServer = normalizeProxy(proxyRaw);
+
+ try {
+ const updated = await window.api.profiles.update(profileId, { proxyServer });
+ const idx = profiles.findIndex(p => p.id === profileId);
+ if (idx !== -1) profiles[idx] = { ...profiles[idx], ...updated };
+ showToast('Proxy saved.', 'success');
+ inlineProxyEditId = null;
+ // Auto-check IP after saving
+ if (proxyServer) {
+ setTimeout(() => checkProfileIp(profileId), 300);
+ }
+ renderProfiles();
+ } catch (err) {
+ showToast(`Failed: ${err.message}`, 'error');
+ }
+ }
+
+ // ─── IP Check ─────────────────────────────────────────────────────────────────
+ async function checkProfileIp(profileId) {
+ const profile = profiles.find(p => p.id === profileId);
+ if (!profile) return;
+ if (ipCheckLoading[profileId]) return;
+
+ ipCheckLoading[profileId] = true;
+ // Update just the proxy cell
+ const cell = document.getElementById(`proxy-cell-${profileId}`);
+ if (cell && inlineProxyEditId !== profileId) {
+ cell.innerHTML = renderProxyCell(profile, null);
+ }
+
+ try {
+ const result = await window.api.proxy.checkIp(profile.proxyServer || '');
+ ipCheckResults[profileId] = result;
+ if (result.status === 'fail') {
+ showToast(`IP check failed: ${result.message || 'Unknown error'}`, 'error');
+ } else {
+ const isHosting = result.hosting || result.proxy;
+ const loc = [result.city, result.regionName, result.country].filter(Boolean).join(', ');
+ const flag = countryCodeToEmoji(result.countryCode);
+ showToast(
+ `${flag} ${result.query} · ${loc}${isHosting ? ' · ⚠ Datacenter/Proxy' : ''}`,
+ isHosting ? 'warning' : 'success',
+ 6000
+ );
+ }
+ } catch (e) {
+ showToast(`IP check error: ${e.message}`, 'error');
+ ipCheckResults[profileId] = null;
+ } finally {
+ ipCheckLoading[profileId] = false;
+ // Update proxy cell
+ const profile2 = profiles.find(p => p.id === profileId);
+ const cell2 = document.getElementById(`proxy-cell-${profileId}`);
+ if (cell2 && inlineProxyEditId !== profileId && profile2) {
+ cell2.innerHTML = renderProxyCell(profile2, ipCheckResults[profileId]);
+ }
+ // If inline editor open, re-mount the input field
+ if (inlineProxyEditId === profileId) {
+ mountInlineProxyInput(profileId);
+ }
+ }
+ }
+
+ // ─── Sessions View ────────────────────────────────────────────────────────────
+ function renderSessions() {
+ const main = el('main-content');
+ if (!main) return;
+
+ const statP = el('stat-profiles'); if (statP) statP.textContent = profiles.length;
+ const statR = el('stat-running'); if (statR) statR.textContent = runningSessions.length;
+ const statInd = el('stat-indicator'); if (statInd) statInd.classList.toggle('running', runningSessions.length > 0);
+ const badgeS = el('badge-sessions'); if (badgeS) { badgeS.textContent = runningSessions.length || ''; badgeS.style.display = runningSessions.length ? '' : 'none'; }
+
+ main.innerHTML = `
+
+ ${runningSessions.length === 0
+ ? `
+
${I.session}
+
No active sessions
+
Launch a profile from the Profiles tab to start a browser session.
+
Go to Profiles
+
`
+ : `
+
+
+
+ Profile
+ PID
+ Started
+ URL
+ Action
+
+
+
+ ${runningSessions.map(s => `
+
+ ${esc(s.profileName)}
+ ${s.pid}
+ ${timeAgo(s.startTime)}
+ ${esc(s.url || 'about:blank')}
+
+ ${I.stop} Stop
+
+ `).join('')}
+
+
+
`
+ }
+ `;
+ }
+
+ // ─── Settings View ────────────────────────────────────────────────────────────
+ function renderSettings() {
+ const main = el('main-content');
+ if (!main) return;
+ const s = settings;
+ const defaultPath = IS_WIN
+ ? 'C:\\Program Files\\BotBrowser\\chrome.exe'
+ : IS_MAC ? '/Applications/Chromium.app/Contents/MacOS/Chromium' : '/usr/bin/botbrowser';
+
+ main.innerHTML = `
+
+
+
+ `;
+ }
+
+ // ─── Profile Editor ───────────────────────────────────────────────────────────
+ function openProfileEditor(profileId) {
+ editingProfileId = profileId;
+ const profile = profileId ? profiles.find(p => p.id === profileId) : null;
+ const d = profile ? { ...getDefaultProfile(), ...profile } : getDefaultProfile();
+
+ const tabs = [
+ { id: 'general', label: 'General', icon: ' ' },
+ { id: 'network', label: 'Network', icon: ' ' },
+ { id: 'identity', label: 'Identity', icon: ' ' },
+ { id: 'fingerprint', label: 'Fingerprint', icon: ' ' },
+ { id: 'behavior', label: 'Behavior', icon: ' ' },
+ { id: 'session', label: 'Session', icon: ' ' },
+ { id: 'advanced', label: 'Advanced', icon: ' ' },
+ ];
+
+ const overlay = document.createElement('div');
+ overlay.id = 'profile-editor-overlay';
+ overlay.className = 'editor-overlay';
+ overlay.innerHTML = `
+
+
+
+
+ Profile
+ ${tabs.slice(0,2).map((t,i) =>
+ `${t.icon}${t.label} `
+ ).join('')}
+ Browser
+ ${tabs.slice(2,5).map(t =>
+ `${t.icon}${t.label} `
+ ).join('')}
+ More
+ ${tabs.slice(5).map(t =>
+ `${t.icon}${t.label} `
+ ).join('')}
+
+
+ ${renderTabGeneral(d)}
+ ${renderTabNetwork(d)}
+ ${renderTabIdentity(d)}
+ ${renderTabFingerprint(d)}
+ ${renderTabBehavior(d)}
+ ${renderTabSession(d)}
+ ${renderTabAdvanced(d)}
+
+
+
+
+ `;
+ document.body.appendChild(overlay);
+ overlay.addEventListener('click', e => { if (e.target === overlay) closeProfileEditor(); });
+ renderCustomHeadersUI(d.customHeaders || {});
+ }
+
+ function closeProfileEditor() {
+ const overlay = el('profile-editor-overlay');
+ if (overlay) overlay.remove();
+ editingProfileId = null;
+ }
+
+ function switchTab(tab) {
+ document.querySelectorAll('.editor-tab').forEach(t => t.classList.toggle('active', t.dataset.tab === tab));
+ document.querySelectorAll('.editor-panel').forEach(p => p.classList.toggle('active', p.id === `tab-${tab}`));
+ const body = document.querySelector('.editor-body');
+ if (body) body.scrollTop = 0;
+ }
+
+ function getDefaultProfile() {
+ return {
+ name: '', browserBrand: '', colorScheme: 'light',
+ locale: 'auto', timezone: 'auto', languages: 'auto', location: 'auto',
+ startUrl: '', proxyServer: '', proxyIp: '', proxyBypassRgx: '',
+ profileFilePath: '', profileDirPath: '',
+ windowSize: 'real', screenSize: 'real', orientation: 'profile',
+ disableDeviceScaleFactorOnGUI: false,
+ noiseCanvas: true, noiseWebglImage: true, noiseAudioContext: true,
+ noiseClientRects: false, noiseTextRects: true,
+ webrtc: 'profile', webrtcICE: 'google', webgl: 'profile', webgpu: 'profile',
+ fonts: 'profile', mediaDevices: 'profile', speechVoices: 'profile', mediaTypes: 'expand',
+ alwaysActive: true, disableDebugger: true, disableConsoleMessage: true,
+ portProtection: false, localDns: false, injectRandomHistory: '',
+ mobileForceTouch: false, keyboard: 'profile',
+ uaFullVersion: '', brandFullVersion: '', userAgent: '',
+ platform: '', platformVersion: '', model: '', architecture: '', bitness: '', mobile: false,
+ cookies: '', bookmarks: '', remoteDebuggingPort: '',
+ customHeaders: {}, networkInfoOverride: false,
+ noiseSeed: '', timeSeed: '', stackSeed: 'profile', timeScale: '',
+ fps: 'profile', mirrorController: '', mirrorClient: '',
+ canvasRecordFile: '', audioRecordFile: '',
+ botScript: '', ipService: '', enableVariationsInContext: false,
+ gpuEmulation: true,
+ };
+ }
+
+ // ─── Tab Renderers ────────────────────────────────────────────────────────────
+
+ function badge(tier) {
+ if (!tier) return '';
+ if (tier === 'PRO') return 'PRO ';
+ return `${tier} `;
+ }
+
+ function renderToggle(id, label, hint, checked, tier) {
+ return `
+
+
${label}${badge(tier)}
+ ${hint ? `
${hint}
` : ''}
+
+
+
`;
+ }
+
+ function renderTabGeneral(d) {
+ return ``;
+ }
+
+ function renderTabNetwork(d) {
+ return ``;
+ }
+
+ function renderTabIdentity(d) {
+ return ``;
+ }
+
+ function renderTabFingerprint(d) {
+ return ``;
+ }
+
+ function renderTabBehavior(d) {
+ return ``;
+ }
+
+ function renderTabSession(d) {
+ return ``;
+ }
+
+ function renderTabAdvanced(d) {
+ return ``;
+ }
+
+ // ─── Custom Headers UI ────────────────────────────────────────────────────────
+ let customHeadersState = {};
+
+ function renderCustomHeadersUI(headers) {
+ customHeadersState = { ...headers };
+ rebuildCustomHeadersDOM();
+ }
+
+ function rebuildCustomHeadersDOM() {
+ const container = el('custom-headers-container');
+ if (!container) return;
+ const entries = Object.entries(customHeadersState);
+ if (entries.length === 0) {
+ container.innerHTML = 'No custom headers added.
';
+ return;
+ }
+ container.innerHTML = entries.map(([key, value]) => `
+
+ `).join('');
+ }
+
+ function addCustomHeader() {
+ customHeadersState['X-Custom-Header'] = '';
+ rebuildCustomHeadersDOM();
+ }
+
+ function removeCustomHeader(key) {
+ delete customHeadersState[key];
+ rebuildCustomHeadersDOM();
+ }
+
+ function readCustomHeaders() {
+ const rows = document.querySelectorAll('.custom-header-row');
+ const result = {};
+ rows.forEach(row => {
+ const inputs = row.querySelectorAll('input');
+ const k = inputs[0]?.value?.trim();
+ const v = inputs[1]?.value?.trim();
+ if (k) result[k] = v || '';
+ });
+ return result;
+ }
+
+ // ─── Profile Actions ──────────────────────────────────────────────────────────
+
+ async function launchProfile(id) {
+ try {
+ await window.api.browser.launch(id);
+ } catch (e) {
+ showToast(`Launch failed: ${e.message}`, 'error', 6000);
+ }
+ }
+
+ async function stopProfile(id) {
+ await window.api.browser.stop(id);
+ await refreshRunningSessions();
+ renderView();
+ }
+
+ async function stopAll() {
+ await window.api.browser.stopAll();
+ await refreshRunningSessions();
+ renderView();
+ }
+
+ async function duplicateProfile(id) {
+ try {
+ showToast('Duplicating profile…', 'info', 2000);
+ const copy = await window.api.profiles.duplicate(id);
+ profiles.push(copy);
+ showToast(`Profile "${copy.name}" created.`, 'success');
+ renderView();
+ } catch (e) {
+ showToast(`Duplicate failed: ${e.message}`, 'error');
+ }
+ }
+
+ async function deleteProfile(id) {
+ const p = profiles.find(x => x.id === id);
+ if (!confirm(`Delete profile "${p?.name || id}"? This cannot be undone.`)) return;
+ await window.api.profiles.delete(id);
+ profiles = profiles.filter(x => x.id !== id);
+ selectedProfileIds.delete(id);
+ renderView();
+ }
+
+ async function deleteSelected() {
+ if (selectedProfileIds.size === 0) return;
+ if (!confirm(`Delete ${selectedProfileIds.size} selected profile(s)?`)) return;
+ const ids = [...selectedProfileIds];
+ await window.api.profiles.deleteMultiple(ids);
+ profiles = profiles.filter(p => !selectedProfileIds.has(p.id));
+ selectedProfileIds.clear();
+ showToast(`${ids.length} profile(s) deleted.`, 'success');
+ renderView();
+ }
+
+ function clearSelection() {
+ selectedProfileIds.clear();
+ renderView();
+ }
+
+ async function saveProfile() {
+ const name = val('f-name');
+ if (!name) { showToast('Profile name is required.', 'error'); return; }
+
+ const proxyRaw = val('f-proxyServer');
+ const profileData = {
+ name,
+ browserBrand: val('f-browserBrand') || '',
+ colorScheme: selVal('f-colorScheme'),
+ startUrl: val('f-startUrl'),
+ profileFilePath: val('f-profileFilePath'),
+ profileDirPath: val('f-profileDirPath'),
+ proxyServer: normalizeProxy(proxyRaw),
+ proxyIp: val('f-proxyIp'),
+ proxyBypassRgx: val('f-proxyBypassRgx'),
+ timezone: val('f-timezone') || 'auto',
+ locale: val('f-locale') || 'auto',
+ languages: val('f-languages') || 'auto',
+ location: val('f-location') || 'auto',
+ ipService: val('f-ipService'),
+ webrtcICE: val('f-webrtcICE') || 'google',
+ localDns: chk('f-localDns'),
+ portProtection: chk('f-portProtection'),
+ networkInfoOverride: chk('f-networkInfoOverride'),
+ customHeaders: readCustomHeaders(),
+ userAgent: val('f-userAgent'),
+ uaFullVersion: val('f-uaFullVersion'),
+ brandFullVersion: val('f-brandFullVersion'),
+ platform: selVal('f-platform'),
+ platformVersion: val('f-platformVersion'),
+ model: val('f-model'),
+ architecture: selVal('f-architecture'),
+ bitness: selVal('f-bitness'),
+ mobile: selVal('f-mobile') === '' ? '' : selVal('f-mobile') === 'true',
+ windowSize: val('f-windowSize') || 'real',
+ screenSize: val('f-screenSize') || 'real',
+ orientation: selVal('f-orientation'),
+ keyboard: selVal('f-keyboard'),
+ fonts: selVal('f-fonts'),
+ disableDeviceScaleFactorOnGUI: chk('f-disableDeviceScaleFactorOnGUI'),
+ webgl: selVal('f-webgl'),
+ webgpu: selVal('f-webgpu'),
+ webrtc: selVal('f-webrtc'),
+ mediaDevices: selVal('f-mediaDevices'),
+ speechVoices: selVal('f-speechVoices'),
+ mediaTypes: selVal('f-mediaTypes'),
+ noiseCanvas: chk('f-noiseCanvas'),
+ noiseWebglImage: chk('f-noiseWebglImage'),
+ noiseAudioContext: chk('f-noiseAudioContext'),
+ noiseClientRects: chk('f-noiseClientRects'),
+ noiseTextRects: chk('f-noiseTextRects'),
+ disableDebugger: chk('f-disableDebugger'),
+ disableConsoleMessage: chk('f-disableConsoleMessage'),
+ alwaysActive: chk('f-alwaysActive'),
+ mobileForceTouch: chk('f-mobileForceTouch'),
+ enableVariationsInContext: chk('f-enableVariationsInContext'),
+ fps: val('f-fps') || 'profile',
+ timeScale: val('f-timeScale'),
+ noiseSeed: val('f-noiseSeed'),
+ timeSeed: val('f-timeSeed'),
+ stackSeed: val('f-stackSeed') || 'profile',
+ injectRandomHistory: val('f-injectRandomHistory'),
+ cookies: val('f-cookies'),
+ bookmarks: val('f-bookmarks'),
+ mirrorController: val('f-mirrorController'),
+ mirrorClient: val('f-mirrorClient'),
+ remoteDebuggingPort: val('f-remoteDebuggingPort'),
+ botScript: val('f-botScript'),
+ gpuEmulation: chk('f-gpuEmulation'),
+ canvasRecordFile: val('f-canvasRecordFile'),
+ audioRecordFile: val('f-audioRecordFile'),
+ };
+
+ // Auto-detect Android
+ const isAndroid = profileData.platform === 'Android';
+ const hasMobileModel = profileData.model && /android|samsung|pixel|xiaomi|huawei|oneplus|oppo|vivo|lg|htc|sony|moto/i.test(profileData.model);
+ if (isAndroid || hasMobileModel) {
+ if (profileData.mobile === '' || profileData.mobile === false) profileData.mobile = true;
+ if (profileData.orientation === 'profile' || !profileData.orientation) profileData.orientation = 'portrait';
+ if (!profileData.mobileForceTouch) profileData.mobileForceTouch = true;
+ if (!profileData.architecture) profileData.architecture = 'arm64';
+ if (!profileData.bitness) profileData.bitness = '64';
+ if (!editingProfileId) showToast('Android detected — mobile settings auto-applied.', 'info', 4000);
+ }
+
+ try {
+ if (editingProfileId) {
+ const updated = await window.api.profiles.update(editingProfileId, profileData);
+ const idx = profiles.findIndex(p => p.id === editingProfileId);
+ if (idx !== -1) profiles[idx] = { ...profiles[idx], ...updated };
+ showToast('Profile saved.', 'success');
+ } else {
+ const created = await window.api.profiles.create(profileData);
+ profiles.push(created);
+ showToast(`Profile "${created.name}" created.`, 'success');
+ }
+ closeProfileEditor();
+ renderView();
+ } catch (e) {
+ showToast(`Save failed: ${e.message}`, 'error');
+ }
+ }
+
+ // ─── Context Menu ─────────────────────────────────────────────────────────────
+ function showContextMenu(profileId, e) {
+ document.querySelectorAll('.context-menu').forEach(m => m.remove());
+ const menu = document.createElement('div');
+ menu.className = 'context-menu';
+ const isRunning = runningSessions.some(s => s.profileId === profileId);
+ menu.innerHTML = `
+
+
+
+
+
+ `;
+ document.body.appendChild(menu);
+ const rect = e.target.getBoundingClientRect();
+ let x = rect.right, y = rect.bottom;
+ if (x + 200 > window.innerWidth) x = rect.left - 200;
+ if (y + 180 > window.innerHeight) y = rect.top - 180;
+ menu.style.left = x + 'px';
+ menu.style.top = y + 'px';
+ }
+
+ // ─── File/Dir Browsing ────────────────────────────────────────────────────────
+ async function browseFile(targetId, filter) {
+ const filters = filter === 'enc' ? [{ name: 'BotBrowser Profile', extensions: ['enc', 'json'] }]
+ : filter === 'js' ? [{ name: 'JavaScript', extensions: ['js'] }]
+ : filter === 'jsonl' ? [{ name: 'JSONL', extensions: ['jsonl', 'json'] }]
+ : [{ name: 'All Files', extensions: ['*'] }];
+ const p = await window.api.dialog.openFile({ filters });
+ if (p) { const inp = el(targetId); if (inp) inp.value = p; }
+ }
+
+ async function browseDir(targetId) {
+ const p = await window.api.dialog.selectDirectory();
+ if (p) { const inp = el(targetId); if (inp) inp.value = p; }
+ }
+
+ async function browseExe() {
+ const p = await window.api.dialog.selectExecutable();
+ if (p) { const inp = el('s-botBrowserPath'); if (inp) inp.value = p; }
+ }
+
+ async function browseUserData() {
+ const p = await window.api.dialog.selectDirectory();
+ if (p) { const inp = el('s-defaultUserDataDir'); if (inp) inp.value = p; }
+ }
+
+ // ─── Settings ─────────────────────────────────────────────────────────────────
+ async function saveSettings() {
+ const proxyRaw = val('s-defaultProxy');
+ const newSettings = {
+ botBrowserPath: val('s-botBrowserPath'),
+ defaultUserDataDir: val('s-defaultUserDataDir'),
+ defaultProxy: normalizeProxy(proxyRaw),
+ };
+ await window.api.settings.set(newSettings);
+ settings = { ...settings, ...newSettings };
+ showToast('Settings saved.', 'success');
+ }
+
+ // ─── Kernel Manager ───────────────────────────────────────────────────────────
+ function renderKernelManager() {
+ const platform = window.api.platform;
+ const platformAssetExt = platform === 'win32' ? ['.7z', '-win', '.exe'] :
+ platform === 'darwin' ? ['.dmg', '-mac'] :
+ ['.deb', '.AppImage', '-linux'];
+
+ if (!kernelReleases) {
+ return `
+
+ ${I.download} Click Refresh to fetch available releases from GitHub.
+
+
`;
+ }
+
+ if (kernelReleases.length === 0) {
+ return ``;
+ }
+
+ const installedVersions = new Set(kernelInstalled.map(k => k.version));
+
+ const installedSection = `
+
+
Installed
+ ${kernelInstalled.length === 0
+ ? `
No kernels installed yet.
`
+ : kernelInstalled.map(k => `
+
+
+ ${esc(k.version)}
+ ${k.installedAt ? timeAgo(k.installedAt) : ''}
+ ${k.installStatus ? `${esc(k.installStatus)} ` : ''}
+ ${k.installNote ? `${esc(k.installNote.slice(0,40))} ` : ''}
+
+
+ ${k.execPath ? `${I.use} Use ` : ''}
+ ${I.trash}
+
+
+ `).join('')
+ }
+
`;
+
+ const releasesSection = `
+ Available Releases
+
+ ${kernelReleases.map(release => {
+ const isInstalled = installedVersions.has(release.tagName);
+ const dl = kernelDownloads[release.tagName];
+ // Pick the single best asset for this platform to avoid double-downloads
+ const platformAssets = release.assets.filter(a =>
+ platformAssetExt.some(ext => a.name.toLowerCase().includes(ext.toLowerCase()))
+ );
+ // Prefer arch-specific asset matching current platform arch
+ const arch = window.api.arch || '';
+ const archKeywords = arch === 'arm64' ? ['arm64', 'aarch64'] : ['x64', 'amd64', 'x86_64'];
+ let bestAssets = platformAssets.filter(a => archKeywords.some(k => a.name.toLowerCase().includes(k)));
+ if (bestAssets.length === 0) bestAssets = platformAssets;
+ // On macOS prefer .dmg over .zip; on Windows prefer .exe installer over .zip
+ if (window.api.platform === 'darwin') {
+ const dmg = bestAssets.filter(a => a.name.toLowerCase().endsWith('.dmg'));
+ if (dmg.length > 0) bestAssets = dmg;
+ } else if (window.api.platform === 'win32') {
+ const exe = bestAssets.filter(a => a.name.toLowerCase().endsWith('.exe'));
+ if (exe.length > 0) bestAssets = exe;
+ }
+ // Fallback: show up to 3 assets if nothing matched
+ const displayAssets = (bestAssets.length > 0 ? bestAssets.slice(0, 1) : (platformAssets.length > 0 ? platformAssets.slice(0, 2) : release.assets.slice(0, 3)));
+
+ return `
+
+
+ ${dl && dl.status === 'downloading'
+ ? `
`
+ : ''
+ }
+
+ ${displayAssets.map(asset => `
+
+ ${esc(asset.name)}
+ ${formatBytes(asset.size)}
+ ${(!dl || dl.status !== 'downloading')
+ ? `${I.download} Download `
+ : `Downloading… `
+ }
+
+ `).join('')}
+
+
`;
+ }).join('')}
+
`;
+
+ return installedSection + releasesSection;
+ }
+
+ function formatBytes(bytes) {
+ if (!bytes) return '';
+ if (bytes < 1024) return bytes + ' B';
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
+ return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
+ }
+
+ async function fetchKernelReleases(force) {
+ if (kernelReleases && !force) return;
+ const btn = document.getElementById('kernel-refresh-btn');
+ if (btn) { btn.disabled = true; btn.textContent = '⟳ Fetching…'; }
+ try {
+ // Always refresh available releases from GitHub + installed from disk
+ const [releases, installed] = await Promise.all([
+ window.api.kernel.fetchReleases(),
+ window.api.kernel.listInstalled(),
+ ]);
+ kernelReleases = releases;
+ // Merge installed: keep existing entries, add/update from disk
+ const installedMap = {};
+ kernelInstalled.forEach(k => { installedMap[k.version] = k; });
+ installed.forEach(k => { installedMap[k.version] = k; });
+ kernelInstalled = Object.values(installedMap);
+ } catch (e) {
+ showToast(`Failed to fetch releases: ${e.message}`, 'error', 5000);
+ if (!kernelReleases) kernelReleases = [];
+ } finally {
+ if (btn) { btn.disabled = false; btn.textContent = '⟳ Refresh'; }
+ if (currentView === 'settings') renderSettings();
+ }
+ }
+
+ // Load cached releases from store on startup (so kernel manager shows immediately)
+ async function loadCachedKernelReleases() {
+ try {
+ const [cached, installed] = await Promise.all([
+ window.api.kernel.getCachedReleases(),
+ window.api.kernel.listInstalled(),
+ ]);
+ if (cached && cached.length > 0) kernelReleases = cached;
+ const installedMap = {};
+ installed.forEach(k => { installedMap[k.version] = k; });
+ kernelInstalled = Object.values(installedMap);
+ } catch {}
+ }
+
+ async function downloadKernel(version, url, filename) {
+ if (kernelDownloads[version]?.status === 'downloading') return;
+ kernelDownloads[version] = { progress: 0, status: 'downloading' };
+ if (currentView === 'settings') renderSettings();
+ try {
+ await window.api.kernel.download({ downloadUrl: url, fileName: filename, version });
+ } catch (e) {
+ kernelDownloads[version] = { status: 'error' };
+ showToast(`Download failed: ${e.message}`, 'error', 5000);
+ if (currentView === 'settings') renderSettings();
+ }
+ }
+
+ function updateKernelProgressUI(version, progress) {
+ const safeV = version.replace(/[^a-zA-Z0-9]/g, '_');
+ const wrap = document.getElementById(`kp-${safeV}`);
+ if (wrap) {
+ const fill = wrap.querySelector('.kernel-progress-bar');
+ const label = wrap.querySelector('.kernel-progress-label');
+ if (fill) fill.style.width = progress + '%';
+ if (label) label.textContent = progress + '%';
+ }
+ }
+
+ async function deleteKernel(version) {
+ if (!confirm(`Delete kernel ${version}?`)) return;
+ await window.api.kernel.delete(version);
+ kernelInstalled = kernelInstalled.filter(k => k.version !== version);
+ showToast(`Kernel ${version} deleted.`, 'success');
+ if (currentView === 'settings') renderSettings();
+ }
+
+ async function useKernelPath(execPath) {
+ if (!execPath) { showToast('No executable path for this kernel.', 'error'); return; }
+ await window.api.settings.set({ botBrowserPath: execPath });
+ settings.botBrowserPath = execPath;
+ showToast('BotBrowser path updated to this kernel.', 'success');
+ if (currentView === 'settings') renderSettings();
+ }
+
+ // ─── Bootstrap ────────────────────────────────────────────────────────────────
+ if (document.readyState === 'loading') {
+ document.addEventListener('DOMContentLoaded', init);
+ } else {
+ init();
+ }
+})();
\ No newline at end of file
diff --git a/botbrowser-control/src/renderer/styles/main.css b/botbrowser-control/src/renderer/styles/main.css
new file mode 100644
index 0000000..487edad
--- /dev/null
+++ b/botbrowser-control/src/renderer/styles/main.css
@@ -0,0 +1,1696 @@
+/* ═══════════════════════════════════════════════════════════════
+ BotBrowser Control — GoLogin-inspired Professional UI
+ Dark: #1B2436 base, #232F45 surface, #2980b9/#3498db accent
+ Light: #F4F6FA base, #FFFFFF surface, #2980b9 accent
+═══════════════════════════════════════════════════════════════ */
+
+/* ── Dark Theme ──────────────────────────────────────────────── */
+body.dark {
+ --bg-base: #161e2d;
+ --bg-surface: #1d2740;
+ --bg-elevated: #222f47;
+ --bg-card: #1d2740;
+ --bg-hover: #273350;
+ --bg-active: #2d3a58;
+ --bg-input: #151e30;
+ --bg-input-f: #1a2438;
+
+ --accent: #3498db;
+ --accent-h: #5dade2;
+ --accent-dim: rgba(52,152,219,0.12);
+ --accent-border: rgba(52,152,219,0.28);
+ --accent-glow: 0 2px 12px rgba(52,152,219,0.3);
+
+ --success: #27ae60;
+ --success-h: #2ecc71;
+ --success-dim: rgba(39,174,96,0.12);
+ --success-b: rgba(39,174,96,0.28);
+
+ --warning: #f39c12;
+ --warning-dim: rgba(243,156,18,0.1);
+ --warning-b: rgba(243,156,18,0.25);
+
+ --danger: #e74c3c;
+ --danger-dim: rgba(231,76,60,0.1);
+ --danger-b: rgba(231,76,60,0.25);
+
+ --info: #2980b9;
+ --info-dim: rgba(41,128,185,0.1);
+ --info-b: rgba(41,128,185,0.25);
+
+ --text-1: #e8edf5;
+ --text-2: #8899b4;
+ --text-3: #4a5c78;
+ --text-link: #5dade2;
+
+ --border: rgba(255,255,255,0.055);
+ --border-s: rgba(255,255,255,0.09);
+ --border-a: rgba(52,152,219,0.28);
+
+ --sidebar-bg: #111827;
+ --sidebar-text: #4a5c78;
+ --sidebar-hover: rgba(255,255,255,0.05);
+ --sidebar-active-bg: rgba(52,152,219,0.12);
+ --sidebar-active-color: #5dade2;
+ --sidebar-active-border: rgba(52,152,219,0.3);
+
+ --shadow-sm: 0 1px 4px rgba(0,0,0,0.3);
+ --shadow: 0 4px 16px rgba(0,0,0,0.4);
+ --shadow-lg: 0 8px 32px rgba(0,0,0,0.55);
+ --shadow-modal: 0 20px 60px rgba(0,0,0,0.7);
+}
+
+/* ── Light Theme ─────────────────────────────────────────────── */
+body.light {
+ --bg-base: #f0f3f9;
+ --bg-surface: #ffffff;
+ --bg-elevated: #f7f9fc;
+ --bg-card: #ffffff;
+ --bg-hover: #eaeff8;
+ --bg-active: #dde5f4;
+ --bg-input: #f0f3f9;
+ --bg-input-f: #ffffff;
+
+ --accent: #2980b9;
+ --accent-h: #1a6fa3;
+ --accent-dim: rgba(41,128,185,0.08);
+ --accent-border: rgba(41,128,185,0.22);
+ --accent-glow: 0 2px 12px rgba(41,128,185,0.2);
+
+ --success: #27ae60;
+ --success-h: #219a52;
+ --success-dim: rgba(39,174,96,0.08);
+ --success-b: rgba(39,174,96,0.2);
+
+ --warning: #e67e22;
+ --warning-dim: rgba(230,126,34,0.08);
+ --warning-b: rgba(230,126,34,0.2);
+
+ --danger: #c0392b;
+ --danger-dim: rgba(192,57,43,0.07);
+ --danger-b: rgba(192,57,43,0.18);
+
+ --info: #2980b9;
+ --info-dim: rgba(41,128,185,0.07);
+ --info-b: rgba(41,128,185,0.18);
+
+ --text-1: #0f1923;
+ --text-2: #4a5c78;
+ --text-3: #94a3b8;
+ --text-link: #2980b9;
+
+ --border: rgba(0,0,0,0.06);
+ --border-s: rgba(0,0,0,0.1);
+ --border-a: rgba(41,128,185,0.2);
+
+ --sidebar-bg: #111827;
+ --sidebar-text: #4a5c78;
+ --sidebar-hover: rgba(255,255,255,0.05);
+ --sidebar-active-bg: rgba(52,152,219,0.12);
+ --sidebar-active-color: #5dade2;
+ --sidebar-active-border: rgba(52,152,219,0.3);
+
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.07);
+ --shadow: 0 4px 16px rgba(0,0,0,0.08);
+ --shadow-lg: 0 8px 32px rgba(0,0,0,0.12);
+ --shadow-modal: 0 20px 60px rgba(0,0,0,0.18);
+}
+
+/* Light mode — main area overrides (sidebar stays dark always) */
+body.light .content,
+body.light #main-content { background: var(--bg-base); }
+body.light .view-header { background: var(--bg-surface); border-color: var(--border); }
+body.light .profile-card { background: var(--bg-card); }
+body.light .profile-card:hover { background: var(--bg-hover); }
+body.light .toolbar { background: var(--bg-surface); border-color: var(--border); }
+body.light .sessions-table th { background: var(--bg-surface); }
+body.light .form-select option { background: #fff; color: #0f1923; }
+body.light .editor-modal { background: var(--bg-surface); }
+body.light .settings-card { background: var(--bg-card); }
+body.light .settings-card-header { background: var(--bg-elevated); }
+body.light .about-item { background: var(--bg-elevated); }
+body.light .editor-footer { background: var(--bg-elevated); }
+
+/* ── Reset & Base ─────────────────────────────────────────────── */
+*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
+
+html, body {
+ height: 100%;
+ overflow: hidden;
+ background: var(--bg-base);
+ color: var(--text-1);
+ font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ font-size: 13px;
+ line-height: 1.5;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+}
+
+#app { display: flex; flex-direction: column; height: 100vh; overflow: hidden; }
+
+/* ── Scrollbar ────────────────────────────────────────────────── */
+::-webkit-scrollbar { width: 5px; height: 5px; }
+::-webkit-scrollbar-track { background: transparent; }
+::-webkit-scrollbar-thumb { background: var(--border-s); border-radius: 6px; }
+::-webkit-scrollbar-thumb:hover { background: var(--text-3); }
+
+/* ── Titlebar ─────────────────────────────────────────────────── */
+.titlebar {
+ height: 44px;
+ background: var(--sidebar-bg);
+ border-bottom: 1px solid rgba(255,255,255,0.05);
+ display: flex;
+ align-items: center;
+ flex-shrink: 0;
+ -webkit-app-region: drag;
+ z-index: 200;
+}
+
+.titlebar-drag { width: 76px; flex-shrink: 0; }
+
+.titlebar-title {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 13px;
+ font-weight: 600;
+ color: #ffffff;
+ flex: 1;
+ letter-spacing: 0.01em;
+ opacity: 0.9;
+}
+
+.titlebar-title span { color: rgba(255,255,255,0.7); font-weight: 500; }
+
+.titlebar-right {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ padding-right: 14px;
+ -webkit-app-region: no-drag;
+}
+
+.theme-btn {
+ width: 30px; height: 30px;
+ border-radius: 8px;
+ border: none;
+ background: transparent;
+ color: rgba(255,255,255,0.35);
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background 0.15s, color 0.15s;
+}
+.theme-btn:hover { background: rgba(255,255,255,0.08); color: rgba(255,255,255,0.8); }
+
+/* ── Layout ───────────────────────────────────────────────────── */
+.layout { display: flex; flex: 1; overflow: hidden; }
+
+/* ── Sidebar ──────────────────────────────────────────────────── */
+.sidebar {
+ width: 216px;
+ background: var(--sidebar-bg);
+ border-right: 1px solid rgba(255,255,255,0.05);
+ display: flex;
+ flex-direction: column;
+ flex-shrink: 0;
+ overflow: hidden;
+}
+
+.sidebar-nav {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ padding: 12px 8px;
+ gap: 2px;
+ overflow-y: auto;
+}
+
+.nav-section { display: flex; flex-direction: column; gap: 2px; }
+
+.nav-section-label {
+ font-size: 9px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.12em;
+ color: rgba(255,255,255,0.2);
+ padding: 8px 10px 4px;
+}
+
+.nav-section-bottom {
+ margin-top: auto;
+ padding-top: 8px;
+ border-top: 1px solid rgba(255,255,255,0.05);
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 9px 10px;
+ border-radius: 8px;
+ border: 1px solid transparent;
+ background: transparent;
+ color: var(--sidebar-text);
+ font-size: 13px;
+ font-weight: 500;
+ font-family: inherit;
+ cursor: pointer;
+ transition: all 0.14s;
+ text-align: left;
+ width: 100%;
+ position: relative;
+}
+
+.nav-item:hover {
+ background: var(--sidebar-hover);
+ color: rgba(255,255,255,0.7);
+}
+
+.nav-item.active {
+ background: var(--sidebar-active-bg);
+ color: var(--sidebar-active-color);
+ border-color: var(--sidebar-active-border);
+ font-weight: 600;
+}
+
+.nav-icon { width: 15px; height: 15px; flex-shrink: 0; opacity: 0.85; }
+.nav-item.active .nav-icon { opacity: 1; }
+
+.nav-badge {
+ margin-left: auto;
+ font-size: 10px;
+ font-weight: 700;
+ padding: 1px 6px;
+ border-radius: 20px;
+ min-width: 18px;
+ text-align: center;
+ background: rgba(255,255,255,0.07);
+ color: rgba(255,255,255,0.3);
+}
+
+.nav-badge.live {
+ background: rgba(39,174,96,0.18);
+ color: #2ecc71;
+}
+
+/* Sidebar footer stats */
+.sidebar-stats {
+ display: flex;
+ align-items: center;
+ padding: 12px 14px;
+ border-top: 1px solid rgba(255,255,255,0.05);
+ gap: 0;
+}
+
+.stat-item {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ position: relative;
+}
+
+.stat-value {
+ font-size: 16px;
+ font-weight: 700;
+ color: rgba(255,255,255,0.7);
+ line-height: 1;
+ font-variant-numeric: tabular-nums;
+}
+
+.stat-label {
+ font-size: 9px;
+ color: rgba(255,255,255,0.2);
+ text-transform: uppercase;
+ letter-spacing: 0.1em;
+}
+
+.stat-divider { width: 1px; height: 28px; background: rgba(255,255,255,0.07); margin: 0 8px; }
+
+.stat-dot {
+ width: 6px; height: 6px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.15);
+ position: absolute;
+ top: 0; right: 4px;
+}
+
+.stat-dot.running {
+ background: var(--success);
+ box-shadow: 0 0 6px var(--success-h);
+ animation: pulse 2s infinite;
+}
+
+@keyframes pulse { 0%,100%{opacity:1;transform:scale(1)} 50%{opacity:0.5;transform:scale(0.85)} }
+
+/* ── Content Area ─────────────────────────────────────────────── */
+.content {
+ flex: 1;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ background: var(--bg-base);
+}
+
+#main-content {
+ flex: 1;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+}
+
+/* ── View Header (GoLogin-style top bar) ─────────────────────── */
+.view-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 20px;
+ height: 52px;
+ border-bottom: 1px solid var(--border);
+ background: var(--bg-surface);
+ flex-shrink: 0;
+ position: sticky;
+ top: 0;
+ z-index: 30;
+ box-shadow: var(--shadow-sm);
+}
+
+.view-header-left { display: flex; align-items: center; gap: 10px; }
+.view-header-right { display: flex; align-items: center; gap: 6px; }
+
+.view-title {
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--text-1);
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+
+.view-title-icon {
+ width: 30px; height: 30px;
+ border-radius: 8px;
+ background: var(--accent-dim);
+ border: 1px solid var(--accent-border);
+ display: flex; align-items: center; justify-content: center;
+ flex-shrink: 0;
+}
+.view-title-icon svg { width: 15px; height: 15px; color: var(--accent); }
+
+.profile-count-pill {
+ font-size: 11px;
+ font-weight: 600;
+ padding: 2px 9px;
+ border-radius: 20px;
+ background: var(--bg-active);
+ color: var(--text-3);
+ border: 1px solid var(--border-s);
+}
+
+/* ── Toolbar (below header) ───────────────────────────────────── */
+.toolbar {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 8px 20px;
+ border-bottom: 1px solid var(--border);
+ background: var(--bg-surface);
+ flex-shrink: 0;
+}
+
+.toolbar-sep { width: 1px; height: 20px; background: var(--border-s); margin: 0 2px; }
+.toolbar-spacer { flex: 1; }
+
+/* ── Buttons ──────────────────────────────────────────────────── */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 7px 14px;
+ border-radius: 8px;
+ border: 1px solid transparent;
+ font-size: 12.5px;
+ font-weight: 500;
+ font-family: inherit;
+ cursor: pointer;
+ transition: all 0.14s;
+ white-space: nowrap;
+ line-height: 1.3;
+}
+
+.btn svg { width: 13px; height: 13px; flex-shrink: 0; }
+
+.btn-primary {
+ background: var(--accent);
+ color: #fff;
+ border-color: var(--accent);
+ font-weight: 600;
+ box-shadow: 0 1px 4px rgba(52,152,219,0.25);
+}
+.btn-primary:hover {
+ background: var(--accent-h);
+ box-shadow: var(--accent-glow);
+ transform: translateY(-1px);
+}
+.btn-primary:active { transform: translateY(0); }
+
+.btn-secondary {
+ background: var(--bg-elevated);
+ color: var(--text-1);
+ border-color: var(--border-s);
+}
+.btn-secondary:hover { background: var(--bg-active); border-color: var(--text-3); }
+
+.btn-ghost {
+ background: transparent;
+ color: var(--text-2);
+ border-color: transparent;
+}
+.btn-ghost:hover { background: var(--bg-hover); color: var(--text-1); border-color: var(--border-s); }
+
+.btn-danger {
+ background: var(--danger-dim);
+ color: var(--danger);
+ border-color: var(--danger-b);
+}
+.btn-danger:hover { background: rgba(231,76,60,0.18); }
+
+.btn-success {
+ background: var(--success-dim);
+ color: var(--success);
+ border-color: var(--success-b);
+}
+.btn-success:hover { background: rgba(39,174,96,0.18); }
+
+.btn-sm { padding: 5px 10px; font-size: 12px; border-radius: 7px; }
+.btn-xs { padding: 3px 8px; font-size: 11px; border-radius: 6px; }
+.btn-icon { padding: 0; width: 30px; height: 30px; justify-content: center; border-radius: 8px; }
+.btn-icon-sm { padding: 0; width: 26px; height: 26px; justify-content: center; border-radius: 7px; }
+.btn:disabled { opacity: 0.38; cursor: not-allowed; pointer-events: none; transform: none !important; }
+
+/* ── Search ───────────────────────────────────────────────────── */
+.search-wrap { position: relative; display: flex; align-items: center; }
+
+.search-wrap .search-icon {
+ position: absolute;
+ left: 9px;
+ color: var(--text-3);
+ display: flex; align-items: center;
+ pointer-events: none;
+}
+
+.search-input {
+ background: var(--bg-input);
+ border: 1px solid var(--border-s);
+ border-radius: 8px;
+ color: var(--text-1);
+ font-family: inherit;
+ font-size: 12.5px;
+ padding: 7px 12px 7px 32px;
+ width: 220px;
+ outline: none;
+ transition: border-color 0.14s, box-shadow 0.14s;
+}
+.search-input:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px var(--accent-dim);
+}
+.search-input::placeholder { color: var(--text-3); }
+
+/* ── Bulk Action Bar ──────────────────────────────────────────── */
+.bulk-action-bar {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 8px 20px;
+ background: rgba(52,152,219,0.06);
+ border-bottom: 1px solid var(--accent-border);
+ flex-shrink: 0;
+ animation: slideDown 0.14s ease;
+}
+@keyframes slideDown { from{opacity:0;transform:translateY(-6px)} to{opacity:1;transform:none} }
+.bulk-count { font-size: 12px; font-weight: 600; color: var(--accent); }
+
+/* ── Profile list table (GoLogin-style rows) ─────────────────── */
+.profiles-table-wrap {
+ flex: 1;
+ overflow-y: auto;
+}
+
+/* Column header row */
+.profiles-col-header {
+ display: grid;
+ grid-template-columns: 28px 36px 1fr 150px 120px 110px 164px;
+ align-items: center;
+ gap: 0;
+ padding: 0 16px;
+ height: 34px;
+ background: var(--bg-surface);
+ border-bottom: 1px solid var(--border-s);
+ position: sticky;
+ top: 0;
+ z-index: 10;
+}
+
+.col-hdr {
+ font-size: 10px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ color: var(--text-3);
+ padding: 0 8px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.col-hdr:first-child { padding-left: 0; }
+
+/* Profile row */
+.profile-card {
+ display: grid;
+ grid-template-columns: 28px 36px 1fr 150px 120px 110px 164px;
+ align-items: center;
+ gap: 0;
+ padding: 0 16px;
+ height: 48px;
+ background: var(--bg-card);
+ border-bottom: 1px solid var(--border);
+ position: relative;
+ cursor: default;
+ transition: background 0.12s;
+}
+
+.profile-card:last-child { border-bottom: none; }
+.profile-card:hover { background: var(--bg-hover); }
+.profile-card:hover .profile-actions { opacity: 1; }
+.profile-card.selected { background: var(--accent-dim); }
+.profile-card.selected:hover { background: rgba(52,152,219,0.15); }
+
+.profile-card.running { background: rgba(39,174,96,0.05); }
+.profile-card.running:hover { background: rgba(39,174,96,0.09); }
+.profile-card.running::before {
+ content: '';
+ position: absolute;
+ left: 0; top: 0; bottom: 0;
+ width: 3px;
+ background: var(--success);
+ border-radius: 0 2px 2px 0;
+}
+.profile-card.running .profile-actions { opacity: 1; }
+
+/* Grid cells */
+.profile-cell {
+ padding: 0 8px;
+ overflow: hidden;
+ display: flex;
+ align-items: center;
+}
+.profile-cell:first-child { padding-left: 0; }
+
+/* Checkbox */
+.checkbox-wrap {
+ display: inline-flex; align-items: center;
+ cursor: pointer; position: relative; flex-shrink: 0;
+}
+.checkbox-wrap input[type="checkbox"] { position: absolute; opacity: 0; width: 0; height: 0; }
+.checkmark {
+ width: 15px; height: 15px;
+ border: 1.5px solid var(--border-s);
+ border-radius: 4px;
+ background: var(--bg-input);
+ transition: all 0.13s;
+ position: relative;
+ flex-shrink: 0;
+}
+.checkbox-wrap input:checked + .checkmark { background: var(--accent); border-color: var(--accent); }
+.checkbox-wrap input:checked + .checkmark::after {
+ content: '';
+ position: absolute;
+ left: 3px; top: 1px;
+ width: 5px; height: 8px;
+ border: 2px solid #fff;
+ border-top: none; border-left: none;
+ transform: rotate(44deg);
+}
+.checkbox-wrap:hover .checkmark { border-color: var(--accent); }
+
+/* Profile identity cell */
+.profile-identity {
+ display: flex; align-items: center; gap: 10px;
+ overflow: hidden;
+ padding: 0 8px;
+}
+
+.profile-avatar {
+ width: 30px; height: 30px;
+ border-radius: 8px;
+ display: flex; align-items: center; justify-content: center;
+ font-size: 11px; font-weight: 700;
+ flex-shrink: 0;
+ color: rgba(255,255,255,0.95);
+ letter-spacing: 0.02em;
+}
+
+.profile-name-wrap { overflow: hidden; }
+
+.profile-name {
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--text-1);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ line-height: 1.3;
+}
+
+.profile-sub {
+ font-size: 11px;
+ color: var(--text-3);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ line-height: 1.3;
+ display: flex;
+ align-items: center;
+ gap: 4px;
+}
+
+/* Status pill */
+.status-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 5px;
+ font-size: 11px;
+ font-weight: 600;
+ padding: 3px 9px;
+ border-radius: 20px;
+ letter-spacing: 0.02em;
+ white-space: nowrap;
+}
+.status-pill-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }
+
+.status-pill.running {
+ background: rgba(39,174,96,0.12);
+ color: #2ecc71;
+ border: 1px solid rgba(39,174,96,0.25);
+}
+.status-pill.running .status-pill-dot { background: #2ecc71; box-shadow: 0 0 5px #2ecc71; animation: pulse 2s infinite; }
+
+.status-pill.idle {
+ background: var(--bg-active);
+ color: var(--text-3);
+ border: 1px solid var(--border);
+}
+.status-pill.idle .status-pill-dot { background: var(--text-3); }
+
+/* Tags in cells */
+.profile-tag {
+ font-size: 10px;
+ padding: 1px 7px;
+ border-radius: 20px;
+ font-weight: 500;
+ white-space: nowrap;
+ border: 1px solid transparent;
+}
+.profile-tag.tag-chrome { background: rgba(52,152,219,0.1); color: #5dade2; border-color: rgba(52,152,219,0.2); }
+.profile-tag.tag-firefox { background: rgba(243,156,18,0.1); color: #f39c12; border-color: rgba(243,156,18,0.2); }
+.profile-tag.tag-cookies { background: rgba(243,156,18,0.08); color: var(--warning); border-color: var(--warning-b); }
+.profile-tag.tag-running { background: var(--success-dim); color: var(--success); border-color: var(--success-b); }
+.profile-tag.tag-proxy { background: var(--bg-active); color: var(--text-3); border-color: var(--border-s); font-family: 'JetBrains Mono', monospace; font-size: 9.5px; max-width: 130px; overflow: hidden; text-overflow: ellipsis; display: inline-block; }
+
+/* Actions cell */
+.profile-actions {
+ display: flex;
+ align-items: center;
+ gap: 3px;
+ flex-shrink: 0;
+ opacity: 0;
+ transition: opacity 0.13s;
+ justify-content: flex-end;
+}
+
+/* ── Empty State ─────────────────────────────────────────────── */
+.empty-state {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 14px;
+ padding: 80px 20px;
+ text-align: center;
+ flex: 1;
+}
+
+.empty-icon {
+ width: 60px; height: 60px;
+ border-radius: 18px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-s);
+ display: flex; align-items: center; justify-content: center;
+}
+.empty-icon svg { width: 26px; height: 26px; color: var(--text-3); }
+.empty-state h3 { font-size: 15px; font-weight: 700; color: var(--text-1); }
+.empty-state p { font-size: 13px; color: var(--text-3); max-width: 300px; line-height: 1.65; }
+
+/* ── Sessions (table style) ───────────────────────────────────── */
+.sessions-wrap { flex: 1; overflow-y: auto; padding: 0 20px 20px; }
+
+.sessions-table { width: 100%; border-collapse: collapse; }
+.sessions-table th {
+ text-align: left;
+ padding: 10px 14px;
+ font-size: 10px;
+ font-weight: 700;
+ color: var(--text-3);
+ text-transform: uppercase;
+ letter-spacing: 0.08em;
+ border-bottom: 1px solid var(--border-s);
+ background: var(--bg-surface);
+ position: sticky; top: 0; z-index: 5;
+ white-space: nowrap;
+}
+.sessions-table td {
+ padding: 11px 14px;
+ font-size: 12.5px;
+ border-bottom: 1px solid var(--border);
+ color: var(--text-2);
+ vertical-align: middle;
+}
+.sessions-table tbody tr { transition: background 0.1s; }
+.sessions-table tbody tr:hover td { background: var(--bg-hover); }
+.sessions-table code {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11px;
+ background: var(--bg-active);
+ padding: 2px 7px;
+ border-radius: 5px;
+ color: var(--text-2);
+}
+.url-cell {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11px; color: var(--text-3);
+ max-width: 200px; overflow: hidden;
+ text-overflow: ellipsis; white-space: nowrap;
+ display: block;
+}
+
+/* ── Profile Editor Modal ─────────────────────────────────────── */
+.editor-overlay {
+ position: fixed; inset: 0;
+ background: rgba(0,0,0,0.65);
+ backdrop-filter: blur(8px);
+ z-index: 1000;
+ display: flex; align-items: center; justify-content: center;
+ padding: 20px;
+ animation: oFade 0.15s ease;
+}
+@keyframes oFade { from{opacity:0} to{opacity:1} }
+
+.editor-modal {
+ background: var(--bg-card);
+ border: 1px solid var(--border-s);
+ border-radius: 14px;
+ width: 100%;
+ max-width: 900px;
+ max-height: 92vh;
+ display: flex;
+ flex-direction: column;
+ box-shadow: var(--shadow-modal);
+ overflow: hidden;
+ animation: mSlide 0.2s cubic-bezier(0.34,1.56,0.64,1);
+}
+@keyframes mSlide { from{transform:translateY(24px) scale(0.96);opacity:0} to{transform:none;opacity:1} }
+
+/* Editor Header */
+.editor-header {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 14px 20px;
+ border-bottom: 1px solid var(--border);
+ flex-shrink: 0;
+ background: var(--bg-surface);
+ gap: 10px;
+}
+.editor-header-left { display: flex; align-items: center; gap: 10px; }
+.editor-header-icon {
+ width: 32px; height: 32px; border-radius: 9px;
+ background: var(--accent-dim); border: 1px solid var(--accent-border);
+ display: flex; align-items: center; justify-content: center; flex-shrink: 0;
+}
+.editor-header-icon svg { width: 15px; height: 15px; color: var(--accent); }
+.editor-header h2 { font-size: 14px; font-weight: 700; color: var(--text-1); }
+.editor-header-sub { font-size: 11px; color: var(--text-3); margin-top: 1px; }
+
+/* Editor Layout: sidebar + content */
+.editor-layout {
+ display: flex;
+ flex: 1;
+ overflow: hidden;
+}
+
+/* Left sidebar nav */
+.editor-sidenav {
+ width: 168px;
+ flex-shrink: 0;
+ background: var(--sidebar-bg);
+ border-right: 1px solid rgba(255,255,255,0.06);
+ padding: 10px 8px;
+ display: flex;
+ flex-direction: column;
+ gap: 2px;
+ overflow-y: auto;
+}
+
+.editor-nav-section {
+ padding: 6px 8px 3px;
+ font-size: 9.5px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.09em;
+ color: rgba(255,255,255,0.2);
+ margin-top: 4px;
+}
+.editor-nav-section:first-child { margin-top: 0; }
+
+.editor-tab {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 7px 10px;
+ font-size: 12px;
+ font-weight: 500;
+ color: rgba(255,255,255,0.35);
+ cursor: pointer;
+ border: none;
+ background: transparent;
+ border-radius: 7px;
+ transition: all 0.13s;
+ white-space: nowrap;
+ font-family: inherit;
+ width: 100%;
+ text-align: left;
+}
+.editor-tab svg { width: 14px; height: 14px; flex-shrink: 0; opacity: 0.7; }
+.editor-tab:hover { color: rgba(255,255,255,0.7); background: rgba(255,255,255,0.06); }
+.editor-tab.active {
+ color: #5dade2;
+ background: rgba(52,152,219,0.14);
+ font-weight: 600;
+}
+.editor-tab.active svg { opacity: 1; }
+
+/* Right content */
+.editor-body { flex: 1; overflow-y: auto; padding: 22px 24px 28px; background: var(--bg-card); }
+.editor-panel { display: none; }
+.editor-panel.active { display: block; }
+
+.editor-footer {
+ display: flex; align-items: center; justify-content: flex-end; gap: 8px;
+ padding: 12px 20px;
+ border-top: 1px solid var(--border);
+ flex-shrink: 0;
+ background: var(--bg-surface);
+}
+
+/* ── Form Elements ─────────────────────────────────────────────── */
+.form-section { margin-bottom: 28px; }
+
+.form-section-title {
+ font-size: 11px;
+ font-weight: 700;
+ color: var(--text-2);
+ letter-spacing: 0.03em;
+ margin-bottom: 12px;
+ padding: 8px 12px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--border);
+ border-left: 3px solid var(--accent);
+ border-radius: 0 8px 8px 0;
+ display: flex; align-items: center; gap: 7px;
+}
+.form-section-title svg { width: 13px; height: 13px; color: var(--accent); flex-shrink: 0; }
+
+.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
+.form-grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
+.form-group { display: flex; flex-direction: column; gap: 5px; }
+.form-group.full { grid-column: 1 / -1; }
+
+.form-label {
+ font-size: 11.5px;
+ font-weight: 500;
+ color: var(--text-2);
+ display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
+}
+
+/* Tier badges */
+.badge-pro {
+ font-size: 8px; padding: 1px 5px; border-radius: 3px;
+ background: rgba(243,156,18,0.12); color: var(--warning);
+ font-weight: 700; letter-spacing: 0.04em;
+ border: 1px solid var(--warning-b);
+}
+.badge-ent {
+ font-size: 8px; padding: 1px 5px; border-radius: 3px;
+ background: rgba(52,152,219,0.1); color: var(--accent);
+ font-weight: 700; letter-spacing: 0.04em;
+ border: 1px solid var(--accent-border);
+}
+
+.form-input, .form-select, .form-textarea {
+ background: var(--bg-input);
+ border: 1px solid var(--border-s);
+ border-radius: 8px;
+ color: var(--text-1);
+ font-family: inherit;
+ font-size: 13px;
+ padding: 8px 12px;
+ outline: none;
+ transition: border-color 0.14s, box-shadow 0.14s, background 0.14s;
+ width: 100%;
+}
+.form-input:focus, .form-select:focus, .form-textarea:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 3px var(--accent-dim);
+ background: var(--bg-input-f);
+}
+.form-input::placeholder, .form-textarea::placeholder { color: var(--text-3); }
+
+.form-select {
+ cursor: pointer;
+ appearance: none;
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' viewBox='0 0 24 24' fill='none' stroke='%234a5c78' stroke-width='2.5'%3E%3Cpolyline points='6,9 12,15 18,9'/%3E%3C/svg%3E");
+ background-repeat: no-repeat;
+ background-position: right 10px center;
+ padding-right: 30px;
+}
+.form-select option { background: #1d2740; color: var(--text-1); }
+
+.form-textarea {
+ resize: vertical;
+ min-height: 76px;
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11.5px;
+ line-height: 1.6;
+}
+
+.form-hint { font-size: 11px; color: var(--text-3); line-height: 1.5; margin-top: 2px; }
+.form-hint code {
+ font-family: 'JetBrains Mono', monospace;
+ background: var(--bg-active);
+ padding: 1px 5px; border-radius: 3px;
+ font-size: 10.5px; color: var(--text-2);
+}
+.form-hint-inline {
+ font-size: 10px; font-family: 'JetBrains Mono', monospace;
+ color: var(--text-3); background: var(--bg-active);
+ padding: 1px 5px; border-radius: 3px;
+}
+.form-hint-banner {
+ padding: 10px 13px; margin-bottom: 14px;
+ border-radius: 8px;
+ background: var(--info-dim);
+ border: 1px solid var(--info-b);
+ color: var(--text-2);
+ font-size: 12px; line-height: 1.55;
+}
+
+/* ── Toggle Switch ─────────────────────────────────────────────── */
+.toggle-row {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 9px 12px;
+ background: var(--bg-input);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+ margin-bottom: 6px;
+ transition: background 0.12s, border-color 0.12s;
+}
+.toggle-row:hover { background: var(--bg-hover); border-color: var(--border-s); }
+.toggle-info { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
+
+.toggle-switch {
+ position: relative; width: 36px; height: 20px;
+ flex-shrink: 0; margin-left: 14px;
+}
+.toggle-switch input { opacity: 0; width: 0; height: 0; position: absolute; }
+.toggle-slider {
+ position: absolute; inset: 0;
+ background: var(--bg-active);
+ border-radius: 20px;
+ cursor: pointer;
+ border: 1.5px solid var(--border-s);
+ transition: background 0.18s, border-color 0.18s;
+}
+.toggle-slider::before {
+ content: '';
+ position: absolute;
+ width: 13px; height: 13px;
+ border-radius: 50%;
+ background: var(--text-3);
+ top: 2px; left: 2px;
+ transition: all 0.2s cubic-bezier(0.34,1.56,0.64,1);
+ box-shadow: 0 1px 3px rgba(0,0,0,0.3);
+}
+.toggle-switch input:checked + .toggle-slider { background: var(--accent); border-color: var(--accent); }
+.toggle-switch input:checked + .toggle-slider::before { background: #fff; left: 19px; }
+
+/* ── Input with Button ─────────────────────────────────────────── */
+.input-with-btn { display: flex; gap: 6px; }
+.input-with-btn .form-input { flex: 1; }
+
+/* ── Custom Headers ────────────────────────────────────────────── */
+.custom-header-row { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
+.custom-header-row .form-input { flex: 1; }
+.custom-header-sep { color: var(--text-3); font-weight: 700; flex-shrink: 0; }
+
+/* ── Settings ──────────────────────────────────────────────────── */
+.settings-scroll-wrap {
+ flex: 1;
+ overflow-y: auto;
+ min-height: 0;
+}
+.settings-form { padding: 24px; max-width: 720px; display: flex; flex-direction: column; gap: 16px; }
+
+/* ── Settings Cards ─────────────────────────────────────────────────────────── */
+.settings-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border);
+ border-radius: 12px;
+ overflow: hidden;
+}
+
+.settings-card-header {
+ display: flex;
+ align-items: center;
+ gap: 14px;
+ padding: 16px 20px;
+ border-bottom: 1px solid var(--border);
+ background: var(--bg-surface);
+}
+
+.settings-card-icon {
+ width: 38px; height: 38px;
+ border-radius: 10px;
+ border: 1px solid transparent;
+ display: flex; align-items: center; justify-content: center;
+ flex-shrink: 0;
+}
+
+.settings-card-title {
+ font-size: 13px;
+ font-weight: 700;
+ color: var(--text-1);
+ line-height: 1.3;
+}
+
+.settings-card-desc {
+ font-size: 11px;
+ color: var(--text-3);
+ margin-top: 2px;
+ line-height: 1.4;
+}
+
+.settings-card-body {
+ padding: 18px 20px;
+}
+
+/* About grid */
+.about-grid {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+}
+
+.about-item {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ padding: 10px 14px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--border);
+ border-radius: 8px;
+}
+
+.about-item-label {
+ font-size: 10px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.07em;
+ color: var(--text-3);
+}
+
+.about-item-value {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--text-1);
+}
+.form-actions { margin-top: 20px; display: flex; gap: 8px; }
+
+/* ── Toast ─────────────────────────────────────────────────────── */
+.toast-container {
+ position: fixed; bottom: 20px; right: 20px;
+ z-index: 9000;
+ display: flex; flex-direction: column; gap: 7px;
+ pointer-events: none;
+}
+.toast {
+ display: flex; align-items: center; gap: 9px;
+ padding: 10px 14px;
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-s);
+ border-left-width: 3px;
+ border-radius: 10px;
+ box-shadow: var(--shadow-lg);
+ font-size: 12.5px;
+ color: var(--text-1);
+ pointer-events: all;
+ max-width: 340px;
+ opacity: 0; transform: translateX(20px);
+ transition: opacity 0.2s ease, transform 0.2s ease;
+}
+.toast.show { opacity: 1; transform: translateX(0); }
+.toast.toast-success { border-left-color: var(--success); }
+.toast.toast-error { border-left-color: var(--danger); }
+.toast.toast-warning { border-left-color: var(--warning); }
+.toast.toast-info { border-left-color: var(--accent); }
+
+/* ── Spinner ───────────────────────────────────────────────────── */
+.spinner {
+ width: 16px; height: 16px;
+ border: 2px solid var(--border-s);
+ border-top-color: var(--accent);
+ border-radius: 50%;
+ animation: spin 0.65s linear infinite;
+ flex-shrink: 0;
+}
+@keyframes spin { to { transform: rotate(360deg); } }
+
+/* ── Context Menu ──────────────────────────────────────────────── */
+.context-menu {
+ position: fixed;
+ background: var(--bg-elevated);
+ border: 1px solid var(--border-s);
+ border-radius: 10px;
+ padding: 4px;
+ z-index: 5000;
+ min-width: 185px;
+ box-shadow: var(--shadow-lg);
+ animation: ctxIn 0.1s ease;
+}
+@keyframes ctxIn { from{opacity:0;transform:scale(0.94) translateY(-4px)} to{opacity:1;transform:none} }
+
+.context-menu-item {
+ display: flex; align-items: center; gap: 9px;
+ padding: 7px 10px;
+ border-radius: 7px;
+ font-size: 12.5px;
+ color: var(--text-2);
+ cursor: pointer;
+ transition: all 0.1s;
+}
+.context-menu-item:hover { background: var(--bg-hover); color: var(--text-1); }
+.context-menu-item.danger { color: var(--danger); }
+.context-menu-item.danger:hover { background: var(--danger-dim); }
+.context-menu-item svg { width: 13px; height: 13px; flex-shrink: 0; }
+.context-menu-sep, .context-menu-divider { height: 1px; background: var(--border); margin: 3px 4px; }
+
+/* ── Info box ──────────────────────────────────────────────────── */
+.info-box {
+ display: flex; align-items: flex-start; gap: 9px;
+ padding: 10px 13px; border-radius: 8px;
+ font-size: 12px; line-height: 1.5;
+}
+.info-box.info { background: var(--info-dim); border: 1px solid var(--info-b); color: var(--text-2); }
+.info-box.warning { background: var(--warning-dim); border: 1px solid var(--warning-b); color: var(--warning); }
+.info-box.success { background: var(--success-dim); border: 1px solid var(--success-b); color: var(--success); }
+.info-box svg { width: 14px; height: 14px; flex-shrink: 0; margin-top: 1px; }
+
+/* ── Misc ──────────────────────────────────────────────────────── */
+.divider { height: 1px; background: var(--border); margin: 14px 0; }
+.hidden { display: none !important; }
+.font-mono { font-family: 'JetBrains Mono', monospace; font-size: 12px; }
+.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.text-muted { color: var(--text-3); font-size: 12px; }
+.text-sm { font-size: 12px; }
+
+/* ── Badge standalone ─────────────────────────────────────────── */
+.badge { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600; padding: 2px 8px; border-radius: 20px; }
+.badge-green { background: var(--success-dim); color: var(--success); border: 1px solid var(--success-b); }
+.badge-orange { background: var(--warning-dim); color: var(--warning); border: 1px solid var(--warning-b); }
+.badge-red { background: var(--danger-dim); color: var(--danger); border: 1px solid var(--danger-b); }
+.badge-blue { background: var(--accent-dim); color: var(--accent); border: 1px solid var(--accent-border); }
+
+/* ── Modal legacy ─────────────────────────────────────────────── */
+.modal-overlay {
+ position: fixed; inset: 0;
+ background: rgba(0,0,0,0.65);
+ backdrop-filter: blur(6px);
+ z-index: 1000;
+ display: flex; align-items: center; justify-content: center;
+ padding: 20px;
+}
+.modal-overlay.hidden { display: none; }
+
+/* ── About box in settings ─────────────────────────────────────── */
+.about-box {
+ display: flex; align-items: center; gap: 14px;
+ padding: 16px;
+ background: var(--bg-input);
+ border: 1px solid var(--border-s);
+ border-radius: 10px;
+ margin-top: 24px;
+}
+.about-logo { width: 40px; height: 40px; border-radius: 10px; flex-shrink: 0; }
+.about-info { flex: 1; min-width: 0; }
+.about-name { font-size: 14px; font-weight: 700; color: var(--text-1); }
+.about-desc { font-size: 11.5px; color: var(--text-3); margin-top: 2px; }
+/* ── IP Check Result Bar ─────────────────────────────────────────────────── */
+.ip-result-bar {
+ display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
+ padding: 5px 10px;
+ background: rgba(39,174,96,0.08);
+ border: 1px solid rgba(39,174,96,0.2);
+ border-radius: 6px;
+ margin-top: 5px;
+ font-size: 11.5px;
+ width: 100%;
+ box-sizing: border-box;
+}
+.ip-flag { font-size: 15px; line-height: 1; }
+.ip-result-ip { font-weight: 700; color: var(--text-1); font-family: var(--font-mono); }
+.ip-result-loc { color: var(--text-2); }
+.ip-result-isp { color: var(--text-3); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 160px; }
+.ip-badge-dc {
+ background: rgba(231,76,60,0.15); color: var(--danger);
+ border: 1px solid rgba(231,76,60,0.3);
+ border-radius: 4px; font-size: 10px; font-weight: 700;
+ padding: 1px 5px;
+}
+.ip-result-close {
+ margin-left: auto; background: none; border: none;
+ color: var(--text-3); cursor: pointer; font-size: 11px; padding: 2px 4px;
+ border-radius: 3px;
+}
+.ip-result-close:hover { color: var(--text-1); background: var(--hover-bg); }
+
+/* spinner */
+@keyframes spin { to { transform: rotate(360deg); } }
+.spin { display: inline-block; animation: spin 0.8s linear infinite; }
+.btn.loading { opacity: 0.7; pointer-events: none; }
+
+/* ── Quick Proxy Popover ─────────────────────────────────────────────────── */
+.quick-proxy-popover {
+ position: fixed;
+ z-index: 9999;
+ background: var(--bg-card);
+ border: 1px solid var(--border-m);
+ border-radius: 10px;
+ box-shadow: 0 8px 32px rgba(0,0,0,0.45);
+ width: 320px;
+ overflow: hidden;
+}
+.quick-proxy-header {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 10px 14px 8px;
+ border-bottom: 1px solid var(--border-s);
+ font-size: 12.5px; font-weight: 600; color: var(--text-1);
+ background: var(--bg-sidebar);
+}
+.quick-proxy-body { padding: 12px 14px 12px; }
+.form-input-sm {
+ height: 28px; font-size: 12px;
+ padding: 0 8px;
+}
+
+/* ── Kernel Manager ──────────────────────────────────────────────────────── */
+.kernel-loading { padding: 8px 0; }
+
+.kernel-section-label {
+ font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em;
+ text-transform: uppercase; color: var(--text-3);
+ margin-bottom: 8px;
+}
+
+.kernel-installed-section { margin-bottom: 4px; }
+
+.kernel-installed-row {
+ display: flex; align-items: center; justify-content: space-between;
+ padding: 6px 10px; margin-bottom: 4px;
+ background: var(--bg-input);
+ border: 1px solid var(--border-s);
+ border-radius: 6px;
+ gap: 8px;
+}
+.kernel-installed-info { display: flex; align-items: center; gap: 8px; }
+
+.kernel-version-tag {
+ display: inline-block;
+ font-family: var(--font-mono); font-size: 11px; font-weight: 600;
+ padding: 2px 8px; border-radius: 4px;
+ background: var(--bg-toolbar); color: var(--text-2);
+ border: 1px solid var(--border-s);
+}
+.kernel-version-tag.installed {
+ background: rgba(39,174,96,0.12);
+ color: var(--success);
+ border-color: rgba(39,174,96,0.3);
+}
+
+.kernel-releases-list {
+ display: flex; flex-direction: column; gap: 8px;
+ max-height: 420px; overflow-y: auto;
+ padding-right: 2px;
+}
+.kernel-releases-list::-webkit-scrollbar { width: 4px; }
+.kernel-releases-list::-webkit-scrollbar-thumb { background: var(--border-m); border-radius: 2px; }
+
+.kernel-release-row {
+ background: var(--bg-input);
+ border: 1px solid var(--border-s);
+ border-radius: 8px;
+ padding: 10px 12px;
+}
+.kernel-release-row.kernel-installed-row-highlight {
+ border-color: rgba(39,174,96,0.25);
+ background: rgba(39,174,96,0.04);
+}
+
+.kernel-release-header {
+ display: flex; align-items: center; justify-content: space-between;
+ margin-bottom: 8px;
+ flex-wrap: wrap; gap: 4px;
+}
+
+.kernel-progress-wrap {
+ position: relative; height: 6px;
+ background: var(--border-s); border-radius: 3px;
+ overflow: hidden; margin-bottom: 8px;
+}
+.kernel-progress-bar {
+ position: absolute; left: 0; top: 0; bottom: 0;
+ background: var(--accent); border-radius: 3px;
+ transition: width 0.3s ease;
+}
+.kernel-progress-label {
+ position: absolute; right: 0; top: -18px;
+ font-size: 10px; color: var(--text-3);
+}
+
+.kernel-assets { display: flex; flex-direction: column; gap: 4px; }
+
+.kernel-asset-row {
+ display: flex; align-items: center; gap: 8px;
+ padding: 4px 6px;
+ background: var(--bg-card);
+ border: 1px solid var(--border-xs, var(--border-s));
+ border-radius: 5px;
+}
+.kernel-asset-name {
+ flex: 1; min-width: 0;
+ font-family: var(--font-mono); font-size: 11px;
+ color: var(--text-2);
+ overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
+}
+.kernel-asset-size {
+ font-size: 10.5px; color: var(--text-3);
+ flex-shrink: 0; white-space: nowrap;
+}
+
+/* ── Profile card: allow rows to expand vertically when inline editor open ── */
+.profile-card {
+ height: auto;
+ min-height: 48px;
+ align-items: flex-start;
+ padding-top: 0;
+ padding-bottom: 0;
+}
+.profile-card > .profile-cell,
+.profile-card > .profile-identity,
+.profile-card > .profile-cell.profile-actions {
+ min-height: 48px;
+ align-items: center;
+}
+/* Proxy cell stretches to content */
+.profile-cell.proxy-cell {
+ padding: 6px 8px;
+ align-items: flex-start;
+ min-height: 48px;
+ overflow: visible;
+}
+
+/* ── Proxy Cell Content ─────────────────────────────────────────────────────── */
+.proxy-cell-content {
+ display: flex;
+ flex-direction: column;
+ gap: 3px;
+ width: 100%;
+ min-width: 0;
+}
+
+.proxy-cell-row {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ min-width: 0;
+ overflow: hidden;
+}
+
+.proxy-scheme-badge {
+ display: inline-flex;
+ align-items: center;
+ font-size: 9px;
+ font-weight: 700;
+ letter-spacing: 0.04em;
+ padding: 1px 5px;
+ border-radius: 3px;
+ color: #fff;
+ flex-shrink: 0;
+ text-transform: uppercase;
+}
+
+.proxy-host-text {
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11px;
+ color: var(--text-2);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ flex: 1;
+ min-width: 0;
+ cursor: text;
+ border-bottom: 1px dashed transparent;
+ transition: border-color 0.13s, color 0.13s;
+}
+.proxy-host-text:hover {
+ color: var(--text-1);
+ border-bottom-color: var(--accent);
+}
+
+/* GoLogin-style inline input — replaces proxy-host-text in-place */
+.proxy-inline-input {
+ flex: 1;
+ min-width: 0;
+ background: var(--bg-input);
+ border: 1px solid var(--accent);
+ border-radius: 5px;
+ color: var(--text-1);
+ font-family: 'JetBrains Mono', monospace;
+ font-size: 11px;
+ padding: 2px 6px;
+ outline: none;
+ box-shadow: 0 0 0 2px var(--accent-dim);
+ transition: border-color 0.13s, box-shadow 0.13s;
+ height: 22px;
+}
+.proxy-inline-input:focus {
+ border-color: var(--accent);
+ box-shadow: 0 0 0 2px var(--accent-dim);
+}
+.proxy-inline-input::placeholder { color: var(--text-3); }
+
+.proxy-edit-hint {
+ flex-shrink: 0;
+ color: var(--text-3);
+ opacity: 0;
+ transition: opacity 0.13s;
+ display: flex;
+ align-items: center;
+}
+.proxy-edit-hint svg { width: 11px; height: 11px; }
+.proxy-cell-row:hover .proxy-edit-hint { opacity: 1; }
+
+/* No-proxy button */
+.proxy-none-btn {
+ font-size: 11px;
+ color: var(--text-3);
+ cursor: pointer;
+ padding: 2px 0;
+ border-bottom: 1px dashed var(--border-s);
+ transition: color 0.12s, border-color 0.12s;
+ display: inline-block;
+}
+.proxy-none-btn:hover { color: var(--accent); border-color: var(--accent); }
+
+/* IP result row */
+.proxy-ip-row {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ flex-wrap: nowrap;
+ overflow: hidden;
+}
+
+.proxy-ip-result {
+ display: inline-flex;
+ align-items: center;
+ gap: 3px;
+ font-size: 10.5px;
+ color: var(--text-2);
+ font-family: 'JetBrains Mono', monospace;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ max-width: 120px;
+}
+.proxy-ip-result.dc { color: var(--danger); }
+
+.proxy-country-code {
+ font-size: 10px;
+ font-weight: 700;
+ color: var(--text-3);
+ letter-spacing: 0.04em;
+ flex-shrink: 0;
+}
+
+.proxy-ip-checking {
+ font-size: 10.5px;
+ color: var(--text-3);
+ font-style: italic;
+}
+
+.proxy-check-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 10.5px;
+ font-weight: 500;
+ font-family: inherit;
+ color: var(--accent);
+ background: var(--accent-dim);
+ border: 1px solid var(--accent-border);
+ border-radius: 4px;
+ padding: 1px 7px;
+ cursor: pointer;
+ transition: background 0.12s;
+ white-space: nowrap;
+}
+.proxy-check-btn:hover { background: rgba(52,152,219,0.2); }
+.proxy-check-btn.loading { opacity: 0.5; pointer-events: none; }
+.proxy-check-btn svg { width: 10px; height: 10px; }
+
+.proxy-recheck-btn {
+ background: none;
+ border: none;
+ font-size: 12px;
+ color: var(--text-3);
+ cursor: pointer;
+ padding: 0 2px;
+ line-height: 1;
+ transition: color 0.12s;
+ flex-shrink: 0;
+}
+.proxy-recheck-btn:hover { color: var(--accent); }
+.proxy-recheck-btn.loading { animation: spin 0.8s linear infinite; pointer-events: none; }
+
+/* ── Inline Proxy Editor (PIE) ──────────────────────────────────────────────── */
+/* Legacy pie-* classes kept for safety — inline editor is now GoLogin-style (proxy-inline-input) */
+
+/* ── Update Banner ──────────────────────────────────────────────────────────── */
+.update-banner {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ padding: 7px 16px;
+ background: linear-gradient(90deg, rgba(52,152,219,0.12), rgba(52,152,219,0.06));
+ border-bottom: 1px solid var(--accent-border);
+ font-size: 12px;
+ color: var(--text-1);
+ flex-shrink: 0;
+ animation: slideDown 0.2s ease;
+}
+
+.update-banner-icon {
+ display: flex;
+ align-items: center;
+ color: var(--accent);
+ flex-shrink: 0;
+}
+.update-banner-icon svg { width: 14px; height: 14px; }
+
+.update-banner-text {
+ flex: 1;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ color: var(--text-2);
+ font-size: 12px;
+}
+
+/* ── Update pill in settings / kernel manager ───────────────────────────────── */
+.update-pill {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ font-size: 10px;
+ font-weight: 700;
+ padding: 2px 7px;
+ border-radius: 20px;
+ background: rgba(52,152,219,0.12);
+ color: var(--accent);
+ border: 1px solid var(--accent-border);
+ white-space: nowrap;
+}
+.update-pill svg { width: 10px; height: 10px; }
+
+/* ── Light mode overrides for new elements ──────────────────────────────────── */
+body.light .proxy-inline-input { background: var(--bg-input); color: var(--text-1); }
+body.light .update-banner { background: linear-gradient(90deg, rgba(41,128,185,0.08), rgba(41,128,185,0.03)); }
+
+/* ── Proxy Status Dot ───────────────────────────────────────────────────────── */
+.proxy-status-dot {
+ width: 7px;
+ height: 7px;
+ border-radius: 50%;
+ flex-shrink: 0;
+ display: inline-block;
+ margin-right: 2px;
+}
+.proxy-status-dot.ok { background: var(--success); box-shadow: 0 0 4px var(--success); }
+.proxy-status-dot.ok.dc { background: var(--warning); box-shadow: 0 0 4px var(--warning); }
+.proxy-status-dot.error { background: var(--danger); box-shadow: 0 0 4px var(--danger); }
+.proxy-status-dot.checking { background: var(--accent); animation: pulse 1s infinite; }
+.proxy-status-dot.unchecked { background: var(--text-3); }
+
+/* Proxy flag + country inline */
+.proxy-flag-country {
+ display: inline-flex;
+ align-items: center;
+ gap: 3px;
+ font-size: 10.5px;
+ color: var(--text-2);
+ font-family: 'JetBrains Mono', monospace;
+}
+
+/* ── Inline Proxy Editor status line ───────────────────────────────────────── */
+.pie-status {
+ font-size: 11px;
+ font-family: 'JetBrains Mono', monospace;
+ padding: 2px 6px;
+ border-radius: 4px;
+ flex: 1;
+}
+.pie-status.ok { color: var(--success); background: var(--success-dim); border: 1px solid var(--success-b); }
+.pie-status.error { color: var(--danger); background: var(--danger-dim); border: 1px solid var(--danger-b); }
+.pie-status.checking { color: var(--accent); background: var(--accent-dim); border: 1px solid var(--accent-border); }