From 9c691b2266cbf0e7416d5d65fd6397aecbaa829f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E3=82=A8=E3=82=A4=E3=82=AB=E3=82=AF?= <62183434+zouyonghe@users.noreply.github.com> Date: Thu, 19 Feb 2026 23:04:18 +0900 Subject: [PATCH] chore: remove Electron desktop pipeline and switch to tauri repo (#5226) * ci: remove Electron desktop build from release pipeline * chore: remove electron desktop and switch to tauri release trigger * ci: remove desktop workflow dispatch trigger * refactor: migrate data paths to astrbot_path helpers * fix: point desktop update prompt to AstrBot-desktop releases --- .github/workflows/release.yml | 165 -- .gitignore | 7 - README.md | 4 +- README_en.md | 4 +- astrbot/core/knowledge_base/kb_db_sqlite.py | 7 +- astrbot/core/knowledge_base/kb_mgr.py | 5 +- .../sources/sensevoice_selfhosted_source.py | 6 +- astrbot/core/utils/astrbot_path.py | 4 +- astrbot/core/utils/pip_installer.py | 8 +- astrbot/core/utils/runtime_env.py | 4 +- astrbot/dashboard/routes/plugin.py | 10 +- astrbot/dashboard/utils.py | 9 +- .../full/vertical-header/VerticalHeader.vue | 10 +- desktop/README.md | 131 - desktop/assets/icon-no-shadow.svg | 1 - desktop/assets/icon.png | Bin 59198 -> 0 bytes desktop/assets/tray.png | Bin 48530 -> 0 bytes desktop/lib/backend-manager.js | 821 ------ desktop/lib/buffered-rotating-logger.js | 162 -- desktop/lib/common.js | 115 - desktop/lib/dashboard-loader.js | 30 - desktop/lib/electron-logger.js | 53 - desktop/lib/locale-service.js | 174 -- desktop/lib/rotating-log-writer.js | 178 -- desktop/lib/startup-screen.js | 116 - desktop/main.js | 420 --- desktop/package.json | 97 - desktop/pnpm-lock.yaml | 2277 ----------------- desktop/preload.js | 22 - desktop/scripts/build-backend.mjs | 86 - desktop/scripts/prepare-webui.mjs | 20 - desktop/scripts/sync-version.mjs | 66 - main.py | 2 + 33 files changed, 45 insertions(+), 4969 deletions(-) delete mode 100644 desktop/README.md delete mode 100644 desktop/assets/icon-no-shadow.svg delete mode 100644 desktop/assets/icon.png delete mode 100644 desktop/assets/tray.png delete mode 100644 desktop/lib/backend-manager.js delete mode 100644 desktop/lib/buffered-rotating-logger.js delete mode 100644 desktop/lib/common.js delete mode 100644 desktop/lib/dashboard-loader.js delete mode 100644 desktop/lib/electron-logger.js delete mode 100644 desktop/lib/locale-service.js delete mode 100644 desktop/lib/rotating-log-writer.js delete mode 100644 desktop/lib/startup-screen.js delete mode 100644 desktop/main.js delete mode 100644 desktop/package.json delete mode 100644 desktop/pnpm-lock.yaml delete mode 100644 desktop/preload.js delete mode 100644 desktop/scripts/build-backend.mjs delete mode 100644 desktop/scripts/prepare-webui.mjs delete mode 100644 desktop/scripts/sync-version.mjs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 59c229b04..8d5791ba3 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -102,170 +102,11 @@ jobs: cp "dashboard/AstrBot-${VERSION_TAG}-dashboard.zip" "dashboard/astrbot-webui-${VERSION_TAG}.zip" rclone copy "dashboard/astrbot-webui-${VERSION_TAG}.zip" "r2:${R2_BUCKET_NAME}" --progress - build-desktop: - name: Build ${{ matrix.name }} - runs-on: ${{ matrix.runner }} - strategy: - fail-fast: false - matrix: - include: - - name: linux-x64 - runner: ubuntu-24.04 - os: linux - arch: amd64 - - name: linux-arm64 - runner: ubuntu-24.04-arm - os: linux - arch: arm64 - - name: windows-x64 - runner: windows-2022 - os: win - arch: amd64 - - name: windows-arm64 - runner: windows-11-arm - os: win - arch: arm64 - - name: macos-x64 - runner: macos-15-intel - os: mac - arch: amd64 - - name: macos-arm64 - runner: macos-15 - os: mac - arch: arm64 - env: - CSC_IDENTITY_AUTO_DISCOVERY: "false" - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - ref: ${{ inputs.ref || github.ref }} - - - name: Resolve tag - id: tag - shell: bash - run: | - if [ "${{ github.event_name }}" = "push" ]; then - tag="${GITHUB_REF_NAME}" - elif [ -n "${{ inputs.tag }}" ]; then - tag="${{ inputs.tag }}" - else - tag="$(git describe --tags --abbrev=0)" - fi - if [ -z "$tag" ]; then - echo "Failed to resolve tag." >&2 - exit 1 - fi - echo "tag=$tag" >> "$GITHUB_OUTPUT" - - - name: Setup uv - uses: astral-sh/setup-uv@v7 - - - name: Setup Python - uses: actions/setup-python@v6 - with: - python-version: "3.12" - - - name: Setup pnpm - uses: pnpm/action-setup@v4 - with: - version: 10.28.2 - - - name: Setup Node.js - uses: actions/setup-node@v6 - with: - node-version: '24.13.0' - cache: "pnpm" - cache-dependency-path: | - dashboard/pnpm-lock.yaml - desktop/pnpm-lock.yaml - - - name: Prepare OpenSSL for Windows ARM64 - if: ${{ matrix.os == 'win' && matrix.arch == 'arm64' }} - shell: pwsh - run: | - git clone https://github.com/microsoft/vcpkg.git C:\vcpkg - & C:\vcpkg\bootstrap-vcpkg.bat -disableMetrics - & C:\vcpkg\vcpkg.exe install openssl:arm64-windows - - "VCPKG_ROOT=C:\vcpkg" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "VCPKGRS_TRIPLET=arm64-windows" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_DIR=C:\vcpkg\installed\arm64-windows" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_ROOT_DIR=C:\vcpkg\installed\arm64-windows" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_LIB_DIR=C:\vcpkg\installed\arm64-windows\lib" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - "OPENSSL_INCLUDE_DIR=C:\vcpkg\installed\arm64-windows\include" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - - - name: Install dependencies - shell: bash - run: | - uv sync - pnpm --dir dashboard install --frozen-lockfile - pnpm --dir desktop install --frozen-lockfile - - - name: Build desktop package - shell: bash - run: | - pnpm --dir dashboard run build - pnpm --dir desktop run build:webui - pnpm --dir desktop run build:backend - pnpm --dir desktop run sync:version - pnpm --dir desktop exec electron-builder --publish never - - - name: Normalize artifact names - shell: bash - env: - NAME_PREFIX: AstrBot-${{ steps.tag.outputs.tag }}-${{ matrix.arch }}-${{ matrix.os }} - run: | - shopt -s nullglob - out_dir="desktop/dist/release" - mkdir -p "$out_dir" - files=( - desktop/dist/*.AppImage - desktop/dist/*.dmg - desktop/dist/*.zip - desktop/dist/*.exe - ) - if [ ${#files[@]} -eq 0 ]; then - echo "No desktop artifacts found to rename." >&2 - exit 1 - fi - for src in "${files[@]}"; do - file="$(basename "$src")" - case "$file" in - *.AppImage) - dest="$out_dir/${NAME_PREFIX}.AppImage" - ;; - *.dmg) - dest="$out_dir/${NAME_PREFIX}.dmg" - ;; - *.exe) - dest="$out_dir/${NAME_PREFIX}.exe" - ;; - *.zip) - dest="$out_dir/${NAME_PREFIX}.zip" - ;; - *) - continue - ;; - esac - cp "$src" "$dest" - done - ls -la "$out_dir" - - - name: Upload desktop artifacts - uses: actions/upload-artifact@v6 - with: - name: AstrBot-${{ steps.tag.outputs.tag }}-${{ matrix.arch }}-${{ matrix.os }} - if-no-files-found: error - path: desktop/dist/release/* - publish-release: name: Publish GitHub Release runs-on: ubuntu-24.04 needs: - build-dashboard - - build-desktop steps: - name: Checkout repository uses: actions/checkout@v6 @@ -296,12 +137,6 @@ jobs: name: Dashboard-${{ steps.tag.outputs.tag }} path: release-assets - - name: Download desktop artifacts - uses: actions/download-artifact@v7 - with: - pattern: AstrBot-${{ steps.tag.outputs.tag }}-* - path: release-assets - merge-multiple: true - name: Resolve release notes id: notes diff --git a/.gitignore b/.gitignore index e060b85a6..e3ffbd473 100644 --- a/.gitignore +++ b/.gitignore @@ -33,13 +33,6 @@ tests/astrbot_plugin_openai dashboard/node_modules/ dashboard/dist/ .pnpm-store/ -desktop/node_modules/ -desktop/dist/ -desktop/out/ -desktop/resources/backend/astrbot-backend* -desktop/resources/backend/*.exe -desktop/resources/webui/* -desktop/resources/.pyinstaller/ package-lock.json yarn.lock diff --git a/README.md b/README.md index 7fbf982fd..4a0bb5338 100644 --- a/README.md +++ b/README.md @@ -146,9 +146,9 @@ yay -S astrbot-git paru -S astrbot-git ``` -#### 桌面端 Electron 打包 +#### 桌面端(Tauri) -桌面端(Electron 打包,`pnpm` 工作流)构建流程请参阅:[`desktop/README.md`](desktop/README.md)。 +桌面端已迁移为独立仓库(Tauri):[https://github.com/AstrBotDevs/AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop)。 ## 支持的消息平台 diff --git a/README_en.md b/README_en.md index d6950c33b..b20e806c0 100644 --- a/README_en.md +++ b/README_en.md @@ -154,9 +154,9 @@ yay -S astrbot-git paru -S astrbot-git ``` -#### Desktop Electron Build +#### Desktop (Tauri) -For desktop build steps (Electron packaging, `pnpm` workflow), see [`desktop/README.md`](desktop/README.md). +Desktop packaging has moved to a standalone Tauri repository: [https://github.com/AstrBotDevs/AstrBot-desktop](https://github.com/AstrBotDevs/AstrBot-desktop). ## Supported Messaging Platforms diff --git a/astrbot/core/knowledge_base/kb_db_sqlite.py b/astrbot/core/knowledge_base/kb_db_sqlite.py index ba25ed7e5..39fc72ac8 100644 --- a/astrbot/core/knowledge_base/kb_db_sqlite.py +++ b/astrbot/core/knowledge_base/kb_db_sqlite.py @@ -13,16 +13,19 @@ from astrbot.core.knowledge_base.models import ( KBMedia, KnowledgeBase, ) +from astrbot.core.utils.astrbot_path import get_astrbot_knowledge_base_path class KBSQLiteDatabase: - def __init__(self, db_path: str = "data/knowledge_base/kb.db") -> None: + def __init__(self, db_path: str | None = None) -> None: """初始化知识库数据库 Args: - db_path: 数据库文件路径, 默认为 data/knowledge_base/kb.db + db_path: 数据库文件路径, 默认位于 AstrBot 数据目录下的 knowledge_base/kb.db """ + if db_path is None: + db_path = str(Path(get_astrbot_knowledge_base_path()) / "kb.db") self.db_path = db_path self.DATABASE_URL = f"sqlite+aiosqlite:///{db_path}" self.inited = False diff --git a/astrbot/core/knowledge_base/kb_mgr.py b/astrbot/core/knowledge_base/kb_mgr.py index ae5a1b9e7..f26409e56 100644 --- a/astrbot/core/knowledge_base/kb_mgr.py +++ b/astrbot/core/knowledge_base/kb_mgr.py @@ -3,6 +3,7 @@ from pathlib import Path from astrbot.core import logger from astrbot.core.provider.manager import ProviderManager +from astrbot.core.utils.astrbot_path import get_astrbot_knowledge_base_path # from .chunking.fixed_size import FixedSizeChunker from .chunking.recursive import RecursiveCharacterChunker @@ -13,7 +14,7 @@ from .retrieval.manager import RetrievalManager, RetrievalResult from .retrieval.rank_fusion import RankFusion from .retrieval.sparse_retriever import SparseRetriever -FILES_PATH = "data/knowledge_base" +FILES_PATH = get_astrbot_knowledge_base_path() DB_PATH = Path(FILES_PATH) / "kb.db" """Knowledge Base storage root directory""" CHUNKER = RecursiveCharacterChunker() @@ -27,7 +28,7 @@ class KnowledgeBaseManager: self, provider_manager: ProviderManager, ) -> None: - Path(DB_PATH).parent.mkdir(parents=True, exist_ok=True) + DB_PATH.parent.mkdir(parents=True, exist_ok=True) self.provider_manager = provider_manager self._session_deleted_callback_registered = False diff --git a/astrbot/core/provider/sources/sensevoice_selfhosted_source.py b/astrbot/core/provider/sources/sensevoice_selfhosted_source.py index 965b83a5a..af6c0f631 100644 --- a/astrbot/core/provider/sources/sensevoice_selfhosted_source.py +++ b/astrbot/core/provider/sources/sensevoice_selfhosted_source.py @@ -7,12 +7,14 @@ import asyncio import os import re from datetime import datetime +from pathlib import Path from typing import cast from funasr_onnx import SenseVoiceSmall from funasr_onnx.utils.postprocess_utils import rich_transcription_postprocess from astrbot.core import logger +from astrbot.core.utils.astrbot_path import get_astrbot_temp_path from astrbot.core.utils.io import download_file from astrbot.core.utils.tencent_record_helper import tencent_silk_to_wav @@ -50,7 +52,9 @@ class ProviderSenseVoiceSTTSelfHost(STTProvider): async def get_timestamped_path(self) -> str: timestamp = datetime.now().strftime("%Y%m%d_%H%M%S") - return os.path.join("data", "temp", f"{timestamp}") + temp_dir = Path(get_astrbot_temp_path()) + temp_dir.mkdir(parents=True, exist_ok=True) + return str(temp_dir / timestamp) async def _is_silk_file(self, file_path) -> bool: silk_header = b"SILK" diff --git a/astrbot/core/utils/astrbot_path.py b/astrbot/core/utils/astrbot_path.py index 063c8ddfc..987ce110a 100644 --- a/astrbot/core/utils/astrbot_path.py +++ b/astrbot/core/utils/astrbot_path.py @@ -15,7 +15,7 @@ Skills 目录路径:固定为数据目录下的 skills 目录 import os -from astrbot.core.utils.runtime_env import is_packaged_electron_runtime +from astrbot.core.utils.runtime_env import is_packaged_desktop_runtime def get_astrbot_path() -> str: @@ -29,7 +29,7 @@ def get_astrbot_root() -> str: """获取Astrbot根目录路径""" if path := os.environ.get("ASTRBOT_ROOT"): return os.path.realpath(path) - if is_packaged_electron_runtime(): + if is_packaged_desktop_runtime(): return os.path.realpath(os.path.join(os.path.expanduser("~"), ".astrbot")) return os.path.realpath(os.getcwd()) diff --git a/astrbot/core/utils/pip_installer.py b/astrbot/core/utils/pip_installer.py index 1c8da23c1..562a0ed30 100644 --- a/astrbot/core/utils/pip_installer.py +++ b/astrbot/core/utils/pip_installer.py @@ -12,7 +12,7 @@ import threading from collections import deque from astrbot.core.utils.astrbot_path import get_astrbot_site_packages_path -from astrbot.core.utils.runtime_env import is_packaged_electron_runtime +from astrbot.core.utils.runtime_env import is_packaged_desktop_runtime logger = logging.getLogger("astrbot") @@ -35,7 +35,7 @@ def _get_pip_main(): "pip module is unavailable " f"(sys.executable={sys.executable}, " f"frozen={getattr(sys, 'frozen', False)}, " - f"ASTRBOT_ELECTRON_CLIENT={os.environ.get('ASTRBOT_ELECTRON_CLIENT')})" + f"ASTRBOT_DESKTOP_CLIENT={os.environ.get('ASTRBOT_DESKTOP_CLIENT')})" ) from exc return pip_main @@ -556,7 +556,7 @@ class PipInstaller: args.extend(["--trusted-host", "mirrors.aliyun.com", "-i", index_url]) target_site_packages = None - if is_packaged_electron_runtime(): + if is_packaged_desktop_runtime(): target_site_packages = get_astrbot_site_packages_path() os.makedirs(target_site_packages, exist_ok=True) _prepend_sys_path(target_site_packages) @@ -582,7 +582,7 @@ class PipInstaller: def prefer_installed_dependencies(self, requirements_path: str) -> None: """优先使用已安装在插件 site-packages 中的依赖,不执行安装。""" - if not is_packaged_electron_runtime(): + if not is_packaged_desktop_runtime(): return target_site_packages = get_astrbot_site_packages_path() diff --git a/astrbot/core/utils/runtime_env.py b/astrbot/core/utils/runtime_env.py index 2eb1bc7e4..483f5bc0c 100644 --- a/astrbot/core/utils/runtime_env.py +++ b/astrbot/core/utils/runtime_env.py @@ -6,5 +6,5 @@ def is_frozen_runtime() -> bool: return bool(getattr(sys, "frozen", False)) -def is_packaged_electron_runtime() -> bool: - return is_frozen_runtime() and os.environ.get("ASTRBOT_ELECTRON_CLIENT") == "1" +def is_packaged_desktop_runtime() -> bool: + return is_frozen_runtime() and os.environ.get("ASTRBOT_DESKTOP_CLIENT") == "1" diff --git a/astrbot/dashboard/routes/plugin.py b/astrbot/dashboard/routes/plugin.py index bfa4dca39..25fed7d27 100644 --- a/astrbot/dashboard/routes/plugin.py +++ b/astrbot/dashboard/routes/plugin.py @@ -20,7 +20,10 @@ from astrbot.core.star.filter.permission import PermissionTypeFilter from astrbot.core.star.filter.regex import RegexFilter from astrbot.core.star.star_handler import EventType, star_handlers_registry from astrbot.core.star.star_manager import PluginManager -from astrbot.core.utils.astrbot_path import get_astrbot_temp_path +from astrbot.core.utils.astrbot_path import ( + get_astrbot_data_path, + get_astrbot_temp_path, +) from .route import Response, Route, RouteContext @@ -196,10 +199,11 @@ class PluginRoute(Route): def _build_registry_source(self, custom_url: str | None) -> RegistrySource: """构建注册表源信息""" + data_dir = get_astrbot_data_path() if custom_url: # 对自定义URL生成一个安全的文件名 url_hash = hashlib.md5(custom_url.encode()).hexdigest()[:8] - cache_file = f"data/plugins_custom_{url_hash}.json" + cache_file = os.path.join(data_dir, f"plugins_custom_{url_hash}.json") # 更安全的后缀处理方式 if custom_url.endswith(".json"): @@ -209,7 +213,7 @@ class PluginRoute(Route): urls = [custom_url] else: - cache_file = "data/plugins.json" + cache_file = os.path.join(data_dir, "plugins.json") md5_url = "https://api.soulter.top/astrbot/plugins-md5" urls = [ "https://api.soulter.top/astrbot/plugins", diff --git a/astrbot/dashboard/utils.py b/astrbot/dashboard/utils.py index b81faad06..3a0ee5bdc 100644 --- a/astrbot/dashboard/utils.py +++ b/astrbot/dashboard/utils.py @@ -1,5 +1,4 @@ import base64 -import os import traceback from io import BytesIO @@ -51,14 +50,14 @@ async def generate_tsne_visualization( return None kb = kb_helper.kb - index_path = f"data/knowledge_base/{kb.kb_id}/index.faiss" + index_path = kb_helper.kb_dir / "index.faiss" # 读取 FAISS 索引 - if not os.path.exists(index_path): - logger.warning(f"FAISS 索引不存在: {index_path}") + if not index_path.exists(): + logger.warning(f"FAISS 索引不存在: {index_path!s}") return None - index = faiss.read_index(index_path) + index = faiss.read_index(str(index_path)) if index.ntotal == 0: logger.warning("索引为空") diff --git a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue index 48cefd3cb..1bcd7f167 100644 --- a/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue +++ b/dashboard/src/layouts/full/vertical-header/VerticalHeader.vue @@ -51,7 +51,8 @@ const isElectronApp = ref( const redirectConfirmDialog = ref(false); const pendingRedirectUrl = ref(''); const resolvingReleaseTarget = ref(false); -const fallbackReleaseUrl = 'https://github.com/AstrBotDevs/AstrBot/releases/latest'; +const desktopReleaseBaseUrl = 'https://github.com/AstrBotDevs/AstrBot-desktop/releases'; +const fallbackReleaseUrl = desktopReleaseBaseUrl; const getSelectedGitHubProxy = () => { if (typeof window === "undefined" || !window.localStorage) return ""; @@ -128,12 +129,15 @@ function confirmExternalRedirect() { const getReleaseUrlForElectron = () => { const firstRelease = (releases.value as any[])?.[0]; - if (firstRelease?.html_url) return firstRelease.html_url as string; + if (firstRelease?.tag_name) { + const tag = firstRelease.tag_name as string; + return `${desktopReleaseBaseUrl}/tag/${tag}`; + } if (hasNewVersion.value) return fallbackReleaseUrl; const tag = botCurrVersion.value?.startsWith('v') ? botCurrVersion.value : 'latest'; return tag === 'latest' ? fallbackReleaseUrl - : `https://github.com/AstrBotDevs/AstrBot/releases/tag/${tag}`; + : `${desktopReleaseBaseUrl}/tag/${tag}`; }; function handleUpdateClick() { diff --git a/desktop/README.md b/desktop/README.md deleted file mode 100644 index 48dcb341a..000000000 --- a/desktop/README.md +++ /dev/null @@ -1,131 +0,0 @@ -# AstrBot Desktop (Electron) - -This document describes how to build the Electron desktop app from source. - -## What This Package Contains - -- Electron desktop shell (`desktop/main.js`) -- Bundled WebUI static files (`desktop/resources/webui`) -- App assets (`desktop/assets`) - -Current behavior: - -- Backend executable is bundled in the installer/package. -- App startup checks backend availability and auto-starts bundled backend when needed. -- Runtime data is stored under `~/.astrbot` by default, not as a full AstrBot source project. - -## Prerequisites - -- Python environment ready in repository root (`uv` available) -- Node.js available -- `pnpm` available - -Desktop dependency management uses `pnpm` with a lockfile: - -- `desktop/pnpm-lock.yaml` -- `pnpm --dir desktop install --frozen-lockfile` - -## Build From Scratch - -Run commands from repository root: - -```bash -uv sync -pnpm --dir dashboard install -pnpm --dir dashboard build -pnpm --dir desktop install --frozen-lockfile -pnpm --dir desktop run dist:full -``` - -Output files are generated under: - -- `desktop/dist/` - -## Local Run (Development) - -Start backend first: - -```bash -uv run main.py -``` - -Start Electron shell: - -```bash -pnpm --dir desktop run dev -``` - -## Notes - -- `dist:full` runs WebUI build + backend build + Electron packaging. -- In packaged app mode, backend data root defaults to `~/.astrbot` (can be overridden by `ASTRBOT_ROOT`). -- Backend build uses `uv run --with pyinstaller ...`, so no manual `PyInstaller` install is required. - -## Runtime Directory Layout - -By default (`ASTRBOT_ROOT` not set), packaged desktop app uses this layout: - -```text -~/.astrbot/ - data/ - config/ # Main configuration - plugins/ # Installed plugins - plugin_data/ # Plugin persistent data - site-packages/ # Plugin dependency installation target in packaged mode - temp/ # Runtime temp files - skills/ # Skill-related runtime data - knowledge_base/ # Knowledge base files - backups/ # Backup data -``` - -The app does not store a full AstrBot source tree in home directory. - -## Troubleshooting - -Startup behavior: - -- Packaged app shows a local startup page first, then switches to dashboard after backend is reachable. -- If startup page never switches, check logs and timeout settings below. - -Runtime logs: - -- Electron shell log: `~/.astrbot/logs/electron.log` -- Backend stdout/stderr log: `~/.astrbot/logs/backend.log` -- Both files rotate by size by default: `20MB` per file, keep `3` backups. -- Electron log rotation envs: - - `ASTRBOT_ELECTRON_LOG_MAX_MB` - - `ASTRBOT_ELECTRON_LOG_BACKUP_COUNT` -- Backend log rotation envs: - - `ASTRBOT_BACKEND_LOG_MAX_MB` - - `ASTRBOT_BACKEND_LOG_BACKUP_COUNT` -- Rotation debug logging: - - `ASTRBOT_LOG_ROTATION_DEBUG=1` (or `NODE_ENV=development`) to print filesystem errors from rotation operations. -- On backend startup failure, the app dialog also shows the backend reason and backend log path. - -Timeout and loading controls: - -- `ASTRBOT_BACKEND_TIMEOUT_MS` controls how long Electron waits for backend reachability. -- In packaged mode, default is `0` (auto mode with a 5-minute safety cap). -- In development mode, default is `20000`. -- If backend startup times out, app shows startup failure dialog and exits. -- `ASTRBOT_DASHBOARD_TIMEOUT_MS` controls dashboard page load wait time after backend is ready (default `20000`). -- If you see `Unable to load the AstrBot dashboard.`, increase `ASTRBOT_DASHBOARD_TIMEOUT_MS`. - -Startup page locale: - -- Startup page language follows cached dashboard locale in `~/.astrbot/data/desktop_state.json`. -- Supported startup locales are `zh-CN` and `en-US`. -- Remove that file to reset locale fallback behavior. - -Backend auto-start: - -- `ASTRBOT_BACKEND_AUTO_START=0` disables Electron-managed backend startup. -- When disabled, backend must already be running at `ASTRBOT_BACKEND_URL` before launching app. - -If Electron download times out on restricted networks, configure mirrors before install: - -```bash -export ELECTRON_MIRROR="https://npmmirror.com/mirrors/electron/" -export ELECTRON_BUILDER_BINARIES_MIRROR="https://npmmirror.com/mirrors/electron-builder-binaries/" -pnpm --dir desktop install --frozen-lockfile -``` diff --git a/desktop/assets/icon-no-shadow.svg b/desktop/assets/icon-no-shadow.svg deleted file mode 100644 index 4268e03e2..000000000 --- a/desktop/assets/icon-no-shadow.svg +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/desktop/assets/icon.png b/desktop/assets/icon.png deleted file mode 100644 index 512d1eaedf84835e5fff19eb8879da1ad6122ffd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59198 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 diff --git a/desktop/assets/tray.png b/desktop/assets/tray.png deleted file mode 100644 index 4fcc92ba63813e7801cd49b4130426e82ed3effe..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48530 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 e6@ap!yaYv)&hcOkpBmosW0*X diff --git a/desktop/lib/backend-manager.js b/desktop/lib/backend-manager.js deleted file mode 100644 index eb8958a4c..000000000 --- a/desktop/lib/backend-manager.js +++ /dev/null @@ -1,821 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const os = require('os'); -const path = require('path'); -const { spawn, spawnSync } = require('child_process'); -const { BufferedRotatingLogger } = require('./buffered-rotating-logger'); -const { - delay, - ensureDir, - formatLogTimestamp, - normalizeUrl, - parseLogBackupCount, - parseLogMaxBytes, - waitForProcessExit, -} = require('./common'); - -const PACKAGED_BACKEND_TIMEOUT_FALLBACK_MS = 5 * 60 * 1000; -const GRACEFUL_RESTART_WAIT_FALLBACK_MS = 20 * 1000; -const BACKEND_LOG_FLUSH_INTERVAL_MS = 120; -const BACKEND_LOG_MAX_BUFFER_BYTES = 128 * 1024; - -function parseBackendTimeoutMs(app) { - const defaultTimeoutMs = app.isPackaged ? 0 : 20000; - const parsed = Number.parseInt( - process.env.ASTRBOT_BACKEND_TIMEOUT_MS || `${defaultTimeoutMs}`, - 10, - ); - if (Number.isFinite(parsed) && parsed >= 0) { - return parsed; - } - return defaultTimeoutMs; -} - -class BackendManager { - constructor({ app, baseDir, log, shouldSkipStart }) { - this.app = app; - this.baseDir = baseDir; - this.log = typeof log === 'function' ? log : () => {}; - this.shouldSkipStart = - typeof shouldSkipStart === 'function' ? shouldSkipStart : () => false; - - this.backendUrl = normalizeUrl( - process.env.ASTRBOT_BACKEND_URL || 'http://127.0.0.1:6185/', - ); - this.backendAutoStart = process.env.ASTRBOT_BACKEND_AUTO_START !== '0'; - this.backendTimeoutMs = parseBackendTimeoutMs(app); - this.backendLogMaxBytes = parseLogMaxBytes( - process.env.ASTRBOT_BACKEND_LOG_MAX_MB, - ); - this.backendLogBackupCount = parseLogBackupCount( - process.env.ASTRBOT_BACKEND_LOG_BACKUP_COUNT, - ); - - this.backendProcess = null; - this.backendConfig = null; - this.backendLogger = new BufferedRotatingLogger({ - logPath: null, - maxBytes: this.backendLogMaxBytes, - backupCount: this.backendLogBackupCount, - flushIntervalMs: BACKEND_LOG_FLUSH_INTERVAL_MS, - maxBufferBytes: BACKEND_LOG_MAX_BUFFER_BYTES, - }); - this.backendLastExitReason = null; - this.backendStartupFailureReason = null; - this.backendSpawning = false; - this.backendRestarting = false; - } - - getBackendUrl() { - return this.backendUrl; - } - - getBackendTimeoutMs() { - return this.backendTimeoutMs; - } - - getRootDir() { - return ( - process.env.ASTRBOT_ROOT || - this.backendConfig?.rootDir || - this.resolveBackendRoot() - ); - } - - getBackendLogPath() { - const rootDir = this.getRootDir(); - if (!rootDir) { - return null; - } - return path.join(rootDir, 'logs', 'backend.log'); - } - - getStartupFailureReason() { - return this.backendStartupFailureReason; - } - - isSpawning() { - return this.backendSpawning; - } - - isRestarting() { - return this.backendRestarting; - } - - resolveBackendRoot() { - if (!this.app.isPackaged) { - return null; - } - return path.join(os.homedir(), '.astrbot'); - } - - resolveBackendCwd() { - if (!this.app.isPackaged) { - return path.resolve(this.baseDir, '..'); - } - return this.resolveBackendRoot(); - } - - resolveWebuiDir() { - if (process.env.ASTRBOT_WEBUI_DIR) { - return process.env.ASTRBOT_WEBUI_DIR; - } - if (!this.app.isPackaged) { - return null; - } - const candidate = path.join(process.resourcesPath, 'webui'); - const indexPath = path.join(candidate, 'index.html'); - return fs.existsSync(indexPath) ? candidate : null; - } - - getPackagedBackendPath() { - if (!this.app.isPackaged) { - return null; - } - const filename = - process.platform === 'win32' ? 'astrbot-backend.exe' : 'astrbot-backend'; - const candidate = path.join(process.resourcesPath, 'backend', filename); - return fs.existsSync(candidate) ? candidate : null; - } - - buildDefaultBackendLaunch(webuiDir) { - if (this.app.isPackaged) { - const packagedBackend = this.getPackagedBackendPath(); - if (!packagedBackend) { - return null; - } - const args = []; - if (webuiDir) { - args.push('--webui-dir', webuiDir); - } - return { - cmd: packagedBackend, - args, - shell: false, - }; - } - - const args = ['run', 'main.py']; - if (webuiDir) { - args.push('--webui-dir', webuiDir); - } - return { - cmd: 'uv', - args, - shell: process.platform === 'win32', - }; - } - - resolveBackendConfig() { - const webuiDir = this.resolveWebuiDir(); - const customCmd = process.env.ASTRBOT_BACKEND_CMD; - const launch = customCmd - ? { - cmd: customCmd, - args: [], - shell: true, - } - : this.buildDefaultBackendLaunch(webuiDir); - const cwd = process.env.ASTRBOT_BACKEND_CWD || this.resolveBackendCwd(); - const rootDir = process.env.ASTRBOT_ROOT || this.resolveBackendRoot(); - ensureDir(cwd); - if (rootDir) { - ensureDir(rootDir); - } - this.backendConfig = { - cmd: launch ? launch.cmd : null, - args: launch ? launch.args : [], - shell: launch ? launch.shell : true, - cwd, - webuiDir, - rootDir, - }; - return this.backendConfig; - } - - getBackendConfig() { - if (!this.backendConfig) { - return this.resolveBackendConfig(); - } - return this.backendConfig; - } - - getBackendPort() { - try { - const parsed = new URL(this.backendUrl); - if (parsed.port) { - const port = Number.parseInt(parsed.port, 10); - return Number.isFinite(port) ? port : null; - } - return parsed.protocol === 'https:' ? 443 : 80; - } catch { - return null; - } - } - - canManageBackend() { - return Boolean(this.getBackendConfig().cmd); - } - - async flushLogs() { - await this.backendLogger.flush(); - } - - async pingBackend(timeoutMs = 800) { - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), timeoutMs); - try { - await fetch(this.backendUrl, { - signal: controller.signal, - redirect: 'manual', - }); - return true; - } catch { - return false; - } finally { - clearTimeout(timeout); - } - } - - getEffectiveWaitMs(maxWaitMs = 0) { - if (maxWaitMs > 0) { - return maxWaitMs; - } - if (this.app.isPackaged) { - return PACKAGED_BACKEND_TIMEOUT_FALLBACK_MS; - } - return 0; - } - - async requestBackendJson(pathname, options = {}) { - const timeoutMs = options.timeoutMs || 2000; - const method = options.method || 'GET'; - const controller = new AbortController(); - const timeout = setTimeout(() => controller.abort(), timeoutMs); - const requestUrl = new URL(pathname, this.backendUrl); - requestUrl.searchParams.set('_ts', `${Date.now()}`); - - const authToken = - typeof options.authToken === 'string' && options.authToken - ? options.authToken - : null; - - try { - const response = await fetch(requestUrl.toString(), { - method, - signal: controller.signal, - redirect: 'manual', - headers: { - Accept: 'application/json', - ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}), - ...(options.headers || {}), - }, - }); - if (!response.ok) { - return { ok: false, data: null }; - } - const data = await response.json(); - return { ok: true, data }; - } catch { - return { ok: false, data: null }; - } finally { - clearTimeout(timeout); - } - } - - async getBackendStartTime() { - const result = await this.requestBackendJson('/api/stat/start-time', { - timeoutMs: 1800, - method: 'GET', - }); - if (!result.ok || !result.data) { - return null; - } - const rawStartTime = result.data?.data?.start_time; - const numericStartTime = Number(rawStartTime); - return Number.isFinite(numericStartTime) ? numericStartTime : null; - } - - async requestGracefulRestart(authToken = null) { - const result = await this.requestBackendJson('/api/stat/restart-core', { - timeoutMs: 2500, - method: 'POST', - authToken, - headers: { - 'Content-Type': 'application/json', - }, - }); - return result.ok; - } - - async waitForGracefulRestart(previousStartTime, maxWaitMs = 0) { - const effectiveMaxWaitMs = this.getEffectiveWaitMs(maxWaitMs); - const gracefulWaitMs = - effectiveMaxWaitMs > 0 - ? effectiveMaxWaitMs - : GRACEFUL_RESTART_WAIT_FALLBACK_MS; - const start = Date.now(); - let sawBackendDown = false; - - while (true) { - const reachable = await this.pingBackend(700); - if (!reachable) { - sawBackendDown = true; - } else { - const currentStartTime = await this.getBackendStartTime(); - if ( - previousStartTime !== null && - currentStartTime !== null && - currentStartTime !== previousStartTime - ) { - return { ok: true, reason: null }; - } - if (sawBackendDown && previousStartTime === null) { - return { ok: true, reason: null }; - } - } - - if (Date.now() - start >= gracefulWaitMs) { - return { - ok: false, - reason: `Timed out after ${gracefulWaitMs}ms waiting for graceful restart.`, - }; - } - - await delay(350); - } - } - - async waitForBackend(maxWaitMs = 0, failOnProcessExit = false) { - const effectiveMaxWaitMs = this.getEffectiveWaitMs(maxWaitMs); - const start = Date.now(); - while (true) { - if (await this.pingBackend()) { - return { ok: true, reason: null }; - } - if (failOnProcessExit && !this.backendProcess) { - return { - ok: false, - reason: - this.backendLastExitReason || - 'Backend process exited before becoming reachable.', - }; - } - if (effectiveMaxWaitMs > 0 && Date.now() - start >= effectiveMaxWaitMs) { - return { - ok: false, - reason: `Timed out after ${effectiveMaxWaitMs}ms waiting for backend startup.`, - }; - } - await delay(600); - } - } - - async startBackend() { - if (this.shouldSkipStart()) { - this.log('Skip backend start because app is quitting.'); - return; - } - if (this.backendProcess) { - return; - } - const backendConfig = this.getBackendConfig(); - if (!backendConfig.cmd) { - return; - } - - this.backendLastExitReason = null; - const env = { - ...process.env, - PYTHONUNBUFFERED: '1', - }; - if (this.app.isPackaged) { - env.ASTRBOT_ELECTRON_CLIENT = '1'; - const hasExplicitDashboardHost = Boolean( - process.env.DASHBOARD_HOST || process.env.ASTRBOT_DASHBOARD_HOST, - ); - const hasExplicitDashboardPort = Boolean( - process.env.DASHBOARD_PORT || process.env.ASTRBOT_DASHBOARD_PORT, - ); - if (!hasExplicitDashboardHost) { - env.DASHBOARD_HOST = '127.0.0.1'; - } - if (!hasExplicitDashboardPort) { - env.DASHBOARD_PORT = '6185'; - } - } - if (backendConfig.webuiDir) { - env.ASTRBOT_WEBUI_DIR = backendConfig.webuiDir; - } - let backendLogPath = null; - if (backendConfig.rootDir) { - env.ASTRBOT_ROOT = backendConfig.rootDir; - const logsDir = path.join(backendConfig.rootDir, 'logs'); - ensureDir(logsDir); - backendLogPath = path.join(logsDir, 'backend.log'); - } - await this.backendLogger.setLogPath(backendLogPath); - const usePipedLogging = Boolean(backendLogPath); - - this.backendProcess = spawn(backendConfig.cmd, backendConfig.args || [], { - cwd: backendConfig.cwd, - env, - shell: backendConfig.shell, - stdio: usePipedLogging ? ['ignore', 'pipe', 'pipe'] : 'ignore', - windowsHide: true, - }); - - if (usePipedLogging) { - if (this.backendProcess.stdout) { - this.backendProcess.stdout.on('data', (chunk) => { - this.backendLogger.log(chunk); - }); - } - if (this.backendProcess.stderr) { - this.backendProcess.stderr.on('data', (chunk) => { - this.backendLogger.log(chunk); - }); - } - } - - if (usePipedLogging) { - const launchLine = [backendConfig.cmd, ...(backendConfig.args || [])] - .map((item) => JSON.stringify(item)) - .join(' '); - this.backendLogger.log( - `[${formatLogTimestamp()}] [Electron] Start backend ${launchLine}\n`, - ); - } - - this.backendProcess.on('error', (error) => { - this.backendLastExitReason = - error instanceof Error ? error.message : String(error); - this.backendLogger.log( - `[${formatLogTimestamp()}] [Electron] Backend spawn error: ${ - error instanceof Error ? error.message : String(error) - }\n`, - ); - void this.backendLogger.flush(); - this.backendProcess = null; - }); - - this.backendProcess.on('exit', (code, signal) => { - this.backendLastExitReason = `Backend process exited (code=${code ?? 'null'}, signal=${signal ?? 'null'}).`; - void this.backendLogger.flush(); - this.backendProcess = null; - }); - } - - async startBackendAndWait(maxWaitMs = this.backendTimeoutMs) { - if (!this.canManageBackend()) { - return { - ok: false, - reason: 'Backend command is not configured.', - }; - } - this.backendSpawning = true; - try { - await this.startBackend(); - return await this.waitForBackend(maxWaitMs, true); - } finally { - this.backendSpawning = false; - } - } - - async stopManagedBackend() { - if (!this.backendProcess) { - return; - } - const processToStop = this.backendProcess; - const pid = processToStop.pid; - this.backendProcess = null; - this.log(`Stop backend requested pid=${pid ?? 'unknown'}`); - - if (process.platform === 'win32' && pid) { - try { - // Synchronous taskkill is acceptable here because stop/restart is - // already a control-path operation and not latency-sensitive. - const result = spawnSync('taskkill', ['/pid', `${pid}`, '/t', '/f'], { - stdio: 'ignore', - windowsHide: true, - }); - if (result.status !== 0) { - this.log( - `taskkill failed pid=${pid} status=${result.status} signal=${result.signal ?? 'null'}`, - ); - } else { - this.log(`taskkill completed pid=${pid}`); - } - } catch (error) { - this.log( - `taskkill threw for pid=${pid}: ${ - error instanceof Error ? error.message : String(error) - }`, - ); - } - await waitForProcessExit(processToStop, 5000); - } else { - if (!processToStop.killed) { - try { - processToStop.kill('SIGTERM'); - } catch (error) { - this.log( - `SIGTERM failed for pid=${pid ?? 'unknown'}: ${ - error instanceof Error ? error.message : String(error) - }`, - ); - } - } - const exitResult = await waitForProcessExit(processToStop, 5000); - if (exitResult === 'timeout' && !processToStop.killed) { - try { - processToStop.kill('SIGKILL'); - } catch {} - await waitForProcessExit(processToStop, 1500); - } - } - await this.backendLogger.flush(); - } - - findListeningPidsOnWindows(port) { - // Synchronous netstat parsing is acceptable here because this helper is - // used only during shutdown/restart cleanup paths. - const result = spawnSync('netstat', ['-ano', '-p', 'tcp'], { - stdio: ['ignore', 'pipe', 'ignore'], - encoding: 'utf8', - windowsHide: true, - }); - - if (result.status !== 0 || !result.stdout) { - return []; - } - - const pids = new Set(); - const lines = result.stdout.split(/\r?\n/); - - for (const line of lines) { - const trimmed = line.trim(); - if (!trimmed || !trimmed.toUpperCase().startsWith('TCP')) { - continue; - } - - const parts = trimmed.split(/\s+/); - if (parts.length < 5) { - continue; - } - - const localAddress = parts[1] || ''; - const state = (parts[3] || '').toUpperCase(); - const pid = parts[parts.length - 1]; - if (!/^\d+$/.test(pid)) { - continue; - } - - if (state !== 'LISTENING') { - continue; - } - - const cleanedLocalAddress = localAddress.replace(/\]$/, ''); - const segments = cleanedLocalAddress.split(':'); - const portStr = segments[segments.length - 1]; - const portNum = Number(portStr); - if (Number.isInteger(portNum) && portNum === Number(port)) { - pids.add(pid); - } - } - - return Array.from(pids); - } - - getWindowsProcessInfo(pid) { - const result = spawnSync( - 'tasklist', - ['/FI', `PID eq ${pid}`, '/FO', 'CSV', '/NH'], - { - stdio: ['ignore', 'pipe', 'ignore'], - encoding: 'utf8', - windowsHide: true, - }, - ); - if (result.status !== 0 || !result.stdout) { - return null; - } - - const firstLine = result.stdout - .split(/\r?\n/) - .map((line) => line.trim()) - .find((line) => line.length > 0); - if (!firstLine || firstLine.startsWith('INFO:')) { - return null; - } - - const fields = firstLine - .replace(/^"/, '') - .replace(/"$/, '') - .split('","'); - const imageName = fields[0] || ''; - const parsedPid = Number.parseInt(fields[1] || '', 10); - if (!imageName || !Number.isInteger(parsedPid) || parsedPid !== Number(pid)) { - return null; - } - return { imageName, pid: parsedPid }; - } - - async stopUnmanagedBackendByPort() { - if (!this.app.isPackaged || process.platform !== 'win32') { - return false; - } - - const port = this.getBackendPort(); - if (!port) { - return false; - } - - const pids = this.findListeningPidsOnWindows(port); - if (!pids.length) { - return false; - } - - this.log( - `Attempting unmanaged backend cleanup by port=${port} pids=${pids.join(',')}`, - ); - - const expectedImageName = ( - path.basename(this.getPackagedBackendPath() || '') || 'astrbot-backend.exe' - ).toLowerCase(); - - for (const pid of pids) { - const processInfo = this.getWindowsProcessInfo(pid); - if (!processInfo) { - this.log(`Skip unmanaged cleanup for pid=${pid}: unable to resolve process info.`); - continue; - } - - const actualImageName = processInfo.imageName.toLowerCase(); - if (actualImageName !== expectedImageName) { - this.log( - `Skip unmanaged cleanup for pid=${pid}: unexpected process image ${processInfo.imageName}.`, - ); - continue; - } - - try { - // Synchronous taskkill is acceptable here because unmanaged cleanup - // is performed only during shutdown/restart control flows. - spawnSync('taskkill', ['/pid', `${pid}`, '/t', '/f'], { - stdio: 'ignore', - windowsHide: true, - }); - } catch {} - } - - await delay(500); - return !(await this.pingBackend(1200)); - } - - async stopAnyBackend() { - if (this.backendProcess) { - await this.stopManagedBackend(); - const running = await this.pingBackend(); - if (!running) { - return { ok: true, reason: null }; - } - } else { - const running = await this.pingBackend(); - if (!running) { - return { ok: true, reason: null }; - } - } - - const cleaned = await this.stopUnmanagedBackendByPort(); - if (cleaned) { - return { ok: true, reason: null }; - } - - return { - ok: false, - reason: 'Backend is running but not managed by Electron.', - }; - } - - async ensureBackend() { - this.backendStartupFailureReason = null; - - const running = await this.pingBackend(); - if (running) { - return true; - } - if (!this.backendAutoStart || !this.canManageBackend()) { - this.backendStartupFailureReason = - 'Backend auto-start is disabled or backend command is not configured.'; - return false; - } - const waitResult = await this.startBackendAndWait(this.backendTimeoutMs); - if (!waitResult.ok) { - this.backendStartupFailureReason = waitResult.reason; - return false; - } - return true; - } - - async getState() { - return { - running: await this.pingBackend(), - spawning: this.backendSpawning, - restarting: this.backendRestarting, - canManage: this.canManageBackend(), - }; - } - - async restartBackend(authToken = null) { - if (!this.canManageBackend()) { - return { - ok: false, - reason: 'Backend command is not configured.', - }; - } - if (this.backendSpawning || this.backendRestarting) { - return { - ok: false, - reason: 'Backend action already in progress.', - }; - } - - this.backendRestarting = true; - try { - const backendRunning = await this.pingBackend(900); - if (backendRunning) { - const previousStartTime = await this.getBackendStartTime(); - const gracefulRequested = await this.requestGracefulRestart(authToken); - if (gracefulRequested) { - const gracefulResult = await this.waitForGracefulRestart( - previousStartTime, - this.backendTimeoutMs, - ); - if (gracefulResult.ok) { - return { - ok: true, - reason: null, - }; - } - this.log( - `Graceful restart did not complete: ${gracefulResult.reason || 'unknown reason'}`, - ); - } else { - this.log( - 'Graceful restart request failed; falling back to managed restart.', - ); - } - } - - await this.stopManagedBackend(); - const startResult = await this.startBackendAndWait(this.backendTimeoutMs); - if (!startResult.ok) { - return { - ok: false, - reason: startResult.reason || 'Failed to restart backend.', - }; - } - return { - ok: true, - reason: null, - }; - } catch (error) { - return { - ok: false, - reason: error instanceof Error ? error.message : String(error), - }; - } finally { - this.backendRestarting = false; - } - } - - async stopBackendForIpc() { - if (!this.canManageBackend()) { - return { - ok: false, - reason: 'Backend command is not configured.', - }; - } - if (this.backendSpawning || this.backendRestarting) { - return { - ok: false, - reason: 'Backend action already in progress.', - }; - } - - try { - return await this.stopAnyBackend(); - } catch (error) { - return { - ok: false, - reason: error instanceof Error ? error.message : String(error), - }; - } - } -} - -module.exports = { - BackendManager, -}; diff --git a/desktop/lib/buffered-rotating-logger.js b/desktop/lib/buffered-rotating-logger.js deleted file mode 100644 index 7a443a97d..000000000 --- a/desktop/lib/buffered-rotating-logger.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; - -const { RotatingLogWriter } = require('./rotating-log-writer'); -const { parseEnvInt } = require('./common'); - -const DEFAULT_FLUSH_INTERVAL_MS = 120; -const DEFAULT_MAX_BUFFER_BYTES = 128 * 1024; -const MIN_FLUSH_INTERVAL_MS = 10; -const MIN_MAX_BUFFER_BYTES = 4 * 1024; -const MAX_MAX_BUFFER_BYTES = 16 * 1024 * 1024; - -function clampIntOption(raw, { defaultValue, min, max }) { - const value = parseEnvInt(raw, defaultValue); - return Math.min(Math.max(value, min), max); -} - -class BufferedRotatingLogger { - constructor({ - logPath = null, - maxBytes, - backupCount, - flushIntervalMs, - maxBufferBytes, - label = 'buffered-log', - }) { - this.logPath = logPath || null; - this.flushIntervalMs = clampIntOption(flushIntervalMs, { - defaultValue: DEFAULT_FLUSH_INTERVAL_MS, - min: MIN_FLUSH_INTERVAL_MS, - max: 60 * 1000, - }); - this.maxBufferBytes = clampIntOption(maxBufferBytes, { - defaultValue: DEFAULT_MAX_BUFFER_BYTES, - min: MIN_MAX_BUFFER_BYTES, - max: MAX_MAX_BUFFER_BYTES, - }); - this.buffer = []; - this.bufferBytes = 0; - this.flushTimer = null; - this.pathSwitch = Promise.resolve(); - this.writer = new RotatingLogWriter({ - logPath: this.logPath, - maxBytes, - backupCount, - label, - }); - } - - setLogPath(logPath) { - const nextLogPath = logPath || null; - this.pathSwitch = this.pathSwitch.then(async () => { - if (nextLogPath === this.logPath) { - await this.flush(); - return; - } - - const previousLogPath = this.logPath; - if (previousLogPath) { - await this.flush(); - } - - this.logPath = null; - await this.writer.setLogPath(nextLogPath); - this.logPath = nextLogPath; - await this.flush(); - }); - return this.pathSwitch; - } - - log(payload) { - if (payload === undefined || payload === null) { - return; - } - const chunk = Buffer.isBuffer(payload) - ? payload - : Buffer.from(String(payload), 'utf8'); - if (!chunk.length) { - return; - } - - if (!this.logPath) { - const boundedChunk = this.clipChunkToBufferLimit(chunk); - this.dropOldestUntilWithinLimit(boundedChunk.length); - this.buffer.push(boundedChunk); - this.bufferBytes += boundedChunk.length; - return; - } - - this.buffer.push(chunk); - this.bufferBytes += chunk.length; - - if (this.bufferBytes >= this.maxBufferBytes) { - void this.flush(); - return; - } - this.scheduleFlush(); - } - - flush() { - this.clearFlushTimer(); - if (!this.buffer.length) { - return this.writer.flush(); - } - if (!this.logPath) { - // Path is switching or temporarily unavailable; keep buffered data. - this.dropOldestUntilWithinLimit(0); - return this.writer.flush(); - } - - const chunks = this.buffer; - this.buffer = []; - this.bufferBytes = 0; - const payload = chunks.length === 1 ? chunks[0] : Buffer.concat(chunks); - this.writer.append(payload); - return this.writer.flush(); - } - - dropOldestUntilWithinLimit(incomingBytes = 0) { - while ( - this.buffer.length && - this.bufferBytes + Math.max(0, incomingBytes) > this.maxBufferBytes - ) { - const removed = this.buffer.shift(); - if (removed) { - this.bufferBytes -= removed.length; - } - } - if (this.bufferBytes < 0) { - this.bufferBytes = 0; - } - } - - clipChunkToBufferLimit(chunk) { - if (chunk.length <= this.maxBufferBytes) { - return chunk; - } - return chunk.subarray(chunk.length - this.maxBufferBytes); - } - - scheduleFlush() { - if (this.flushTimer !== null) { - return; - } - this.flushTimer = setTimeout(() => { - this.flushTimer = null; - void this.flush(); - }, this.flushIntervalMs); - this.flushTimer.unref?.(); - } - - clearFlushTimer() { - if (this.flushTimer === null) { - return; - } - clearTimeout(this.flushTimer); - this.flushTimer = null; - } -} - -module.exports = { - BufferedRotatingLogger, -}; diff --git a/desktop/lib/common.js b/desktop/lib/common.js deleted file mode 100644 index 9f39358dc..000000000 --- a/desktop/lib/common.js +++ /dev/null @@ -1,115 +0,0 @@ -'use strict'; - -const fs = require('fs'); - -const LOG_ROTATION_DEFAULT_MAX_MB = 20; -const LOG_ROTATION_DEFAULT_BACKUP_COUNT = 3; - -function normalizeUrl(value) { - try { - const url = new URL(value); - if (!url.pathname.endsWith('/')) { - url.pathname += '/'; - } - return url.toString(); - } catch { - return 'http://127.0.0.1:6185/'; - } -} - -function ensureDir(value) { - if (!value) { - return; - } - if (fs.existsSync(value)) { - return; - } - fs.mkdirSync(value, { recursive: true }); -} - -function parseEnvInt(raw, defaultValue) { - const parsed = Number.parseInt(`${raw ?? ''}`, 10); - return Number.isFinite(parsed) ? parsed : defaultValue; -} - -function isLogRotationDebugEnabled() { - return ( - process.env.ASTRBOT_LOG_ROTATION_DEBUG === '1' || - process.env.NODE_ENV === 'development' - ); -} - -function parseLogMaxBytes(envValue) { - const mb = parseEnvInt(envValue, LOG_ROTATION_DEFAULT_MAX_MB); - const maxMb = mb > 0 ? mb : LOG_ROTATION_DEFAULT_MAX_MB; - return maxMb * 1024 * 1024; -} - -function parseLogBackupCount(envValue) { - const count = parseEnvInt(envValue, LOG_ROTATION_DEFAULT_BACKUP_COUNT); - return count >= 0 ? count : LOG_ROTATION_DEFAULT_BACKUP_COUNT; -} - -function isIgnorableFsError(error) { - return Boolean(error && typeof error === 'object' && error.code === 'ENOENT'); -} - -function delay(ms) { - return new Promise((resolve) => setTimeout(resolve, ms)); -} - -function waitForProcessExit(child, timeoutMs = 5000) { - if (!child) { - return Promise.resolve('missing'); - } - if (child.exitCode !== null || child.signalCode !== null) { - return Promise.resolve('exited'); - } - return new Promise((resolve) => { - let settled = false; - const finish = (reason) => { - if (settled) { - return; - } - settled = true; - clearTimeout(timeout); - resolve(reason); - }; - const timeout = setTimeout(() => finish('timeout'), timeoutMs); - child.once('exit', () => finish('exit')); - child.once('error', () => finish('error')); - }); -} - -function formatLogTimestamp(date = new Date()) { - const year = date.getFullYear(); - const month = `${date.getMonth() + 1}`.padStart(2, '0'); - const day = `${date.getDate()}`.padStart(2, '0'); - const hour = `${date.getHours()}`.padStart(2, '0'); - const minute = `${date.getMinutes()}`.padStart(2, '0'); - const second = `${date.getSeconds()}`.padStart(2, '0'); - const millisecond = `${date.getMilliseconds()}`.padStart(3, '0'); - - const offsetMinutes = -date.getTimezoneOffset(); - const offsetSign = offsetMinutes >= 0 ? '+' : '-'; - const absOffsetMinutes = Math.abs(offsetMinutes); - const offsetHour = `${Math.floor(absOffsetMinutes / 60)}`.padStart(2, '0'); - const offsetMinute = `${absOffsetMinutes % 60}`.padStart(2, '0'); - - return `${year}-${month}-${day} ${hour}:${minute}:${second}.${millisecond} ${offsetSign}${offsetHour}${offsetMinute}`; -} - -module.exports = { - LOG_ROTATION_DEFAULT_BACKUP_COUNT, - LOG_ROTATION_DEFAULT_MAX_MB, - delay, - ensureDir, - formatLogTimestamp, - isIgnorableFsError, - isLogRotationDebugEnabled, - normalizeUrl, - parseEnvInt, - parseLogBackupCount, - parseLogMaxBytes, - waitForProcessExit, -}; diff --git a/desktop/lib/dashboard-loader.js b/desktop/lib/dashboard-loader.js deleted file mode 100644 index 6e858843f..000000000 --- a/desktop/lib/dashboard-loader.js +++ /dev/null @@ -1,30 +0,0 @@ -'use strict'; - -const { delay } = require('./common'); - -async function loadDashboard(mainWindow, backendUrl, maxWaitMs = 20000) { - if (!mainWindow) { - return false; - } - const loadUrl = new URL(backendUrl); - loadUrl.searchParams.set('_electron_ts', `${Date.now()}`); - const start = Date.now(); - let lastError = null; - while (maxWaitMs <= 0 || Date.now() - start < maxWaitMs) { - try { - await mainWindow.loadURL(loadUrl.toString()); - return true; - } catch (error) { - lastError = error; - await delay(600); - } - } - if (lastError) { - throw lastError; - } - throw new Error(`Timed out loading ${backendUrl}`); -} - -module.exports = { - loadDashboard, -}; diff --git a/desktop/lib/electron-logger.js b/desktop/lib/electron-logger.js deleted file mode 100644 index 6a52d1c76..000000000 --- a/desktop/lib/electron-logger.js +++ /dev/null @@ -1,53 +0,0 @@ -'use strict'; - -const path = require('path'); -const { RotatingLogWriter } = require('./rotating-log-writer'); -const { - formatLogTimestamp, - parseLogBackupCount, - parseLogMaxBytes, -} = require('./common'); - -function createElectronLogger({ app, getRootDir }) { - const electronLogMaxBytes = parseLogMaxBytes( - process.env.ASTRBOT_ELECTRON_LOG_MAX_MB, - ); - const electronLogBackupCount = parseLogBackupCount( - process.env.ASTRBOT_ELECTRON_LOG_BACKUP_COUNT, - ); - const writer = new RotatingLogWriter({ - logPath: null, - maxBytes: electronLogMaxBytes, - backupCount: electronLogBackupCount, - label: 'electron-log', - }); - - function getElectronLogPath() { - const rootDir = - process.env.ASTRBOT_ROOT || - (typeof getRootDir === 'function' ? getRootDir() : null) || - app.getPath('userData'); - return path.join(rootDir, 'logs', 'electron.log'); - } - - function logElectron(message) { - const logPath = getElectronLogPath(); - const line = `[${formatLogTimestamp()}] ${message}\n`; - void writer.setLogPath(logPath); - void writer.append(line); - } - - async function flushElectron() { - await writer.flush(); - } - - return { - getElectronLogPath, - logElectron, - flushElectron, - }; -} - -module.exports = { - createElectronLogger, -}; diff --git a/desktop/lib/locale-service.js b/desktop/lib/locale-service.js deleted file mode 100644 index d68039e7d..000000000 --- a/desktop/lib/locale-service.js +++ /dev/null @@ -1,174 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { delay, ensureDir } = require('./common'); - -const LOCALE_STORAGE_KEY = 'astrbot-locale'; -const SUPPORTED_STARTUP_LOCALES = new Set(['zh-CN', 'en-US']); - -function normalizeLocale(value) { - if (!value) { - return null; - } - const raw = String(value).trim(); - if (!raw) { - return null; - } - if (SUPPORTED_STARTUP_LOCALES.has(raw)) { - return raw; - } - const lower = raw.toLowerCase(); - if (lower.startsWith('zh')) { - return 'zh-CN'; - } - if (lower.startsWith('en')) { - return 'en-US'; - } - return null; -} - -function getStartupTexts(locale) { - if (locale === 'zh-CN') { - return { - title: 'AstrBot 正在启动', - message: '界面很快就会加载完成。', - }; - } - return { - title: 'AstrBot is starting', - message: 'The dashboard will be ready in a moment.', - }; -} - -function getShellTexts(locale) { - if (locale === 'zh-CN') { - return { - trayHide: '隐藏 AstrBot', - trayShow: '显示 AstrBot', - trayReload: '重新加载', - trayRestartBackend: '重启后端', - trayQuit: '退出', - startupFailTitle: 'AstrBot 启动失败', - startupFailMessage: 'AstrBot 后端不可达。', - startupFailReasonPrefix: '原因', - startupFailAction: - '请先启动 http://127.0.0.1:6185 的后端服务,然后重新打开 AstrBot。', - startupFailLogPrefix: '后端日志', - dashboardFailTitle: 'AstrBot 加载失败', - dashboardFailMessage: '无法加载 AstrBot 控制台页面。', - }; - } - return { - trayHide: 'Hide AstrBot', - trayShow: 'Show AstrBot', - trayReload: 'Reload', - trayRestartBackend: 'Restart Backend', - trayQuit: 'Quit', - startupFailTitle: 'AstrBot startup failed', - startupFailMessage: 'AstrBot backend is not reachable.', - startupFailReasonPrefix: 'Reason', - startupFailAction: - 'Please start the backend at http://127.0.0.1:6185 and relaunch AstrBot.', - startupFailLogPrefix: 'Backend log', - dashboardFailTitle: 'Failed to load AstrBot', - dashboardFailMessage: 'Unable to load the AstrBot dashboard.', - }; -} - -function createLocaleService({ app, getRootDir }) { - function resolveStateRoot() { - const callbackRoot = (() => { - try { - return getRootDir ? getRootDir() : null; - } catch { - return null; - } - })(); - return process.env.ASTRBOT_ROOT || callbackRoot || app.getPath('userData'); - } - - function getDesktopStatePath() { - return path.join(resolveStateRoot(), 'data', 'desktop_state.json'); - } - - function readCachedLocale() { - const statePath = getDesktopStatePath(); - try { - const raw = fs.readFileSync(statePath, 'utf8'); - const parsed = JSON.parse(raw); - return normalizeLocale(parsed?.locale); - } catch { - return null; - } - } - - function writeCachedLocale(locale) { - const normalized = normalizeLocale(locale); - if (!normalized) { - return; - } - const statePath = getDesktopStatePath(); - ensureDir(path.dirname(statePath)); - try { - fs.writeFileSync( - statePath, - `${JSON.stringify({ locale: normalized }, null, 2)}\n`, - 'utf8', - ); - } catch {} - } - - function resolveStartupLocale() { - const cached = readCachedLocale(); - if (cached) { - return cached; - } - return normalizeLocale(app.getLocale()) || 'zh-CN'; - } - - async function persistLocaleFromDashboard( - mainWindow, - backendUrl, - timeoutMs = 1200, - ) { - if (!mainWindow || mainWindow.isDestroyed()) { - return; - } - const currentUrl = mainWindow.webContents.getURL(); - if (!currentUrl || !currentUrl.startsWith(backendUrl)) { - return; - } - try { - const localeRaw = await Promise.race([ - mainWindow.webContents.executeJavaScript( - `(() => { - try { - return window.localStorage.getItem('${LOCALE_STORAGE_KEY}') || ''; - } catch { - return ''; - } - })();`, - true, - ), - delay(timeoutMs).then(() => null), - ]); - const locale = normalizeLocale(localeRaw); - if (locale) { - writeCachedLocale(locale); - } - } catch {} - } - - return { - getShellTexts, - getStartupTexts, - persistLocaleFromDashboard, - resolveStartupLocale, - }; -} - -module.exports = { - createLocaleService, - normalizeLocale, -}; diff --git a/desktop/lib/rotating-log-writer.js b/desktop/lib/rotating-log-writer.js deleted file mode 100644 index c6c8f8fb1..000000000 --- a/desktop/lib/rotating-log-writer.js +++ /dev/null @@ -1,178 +0,0 @@ -'use strict'; - -const fs = require('fs/promises'); -const path = require('path'); -const { isIgnorableFsError, isLogRotationDebugEnabled } = require('./common'); - -class RotatingLogWriter { - constructor({ logPath = null, maxBytes = 0, backupCount = 0, label = 'log' }) { - this.logPath = logPath || null; - this.maxBytes = Number.isFinite(maxBytes) && maxBytes > 0 ? maxBytes : 0; - this.backupCount = Number.isFinite(backupCount) && backupCount >= 0 ? backupCount : 0; - this.label = label; - this.cachedSize = null; - this.dirReadyForPath = null; - this.queue = Promise.resolve(); - } - - setLogPath(logPath) { - const nextPath = logPath || null; - if (nextPath === this.logPath) { - return this.queue; - } - return this.enqueue(async () => { - this.logPath = nextPath; - this.cachedSize = null; - this.dirReadyForPath = null; - }); - } - - append(payload) { - if (payload === undefined || payload === null) { - return this.queue; - } - const content = Buffer.isBuffer(payload) - ? payload - : Buffer.from(String(payload), 'utf8'); - if (!content.length) { - return this.queue; - } - return this.enqueue(async () => { - if (!this.logPath) { - return; - } - await this.ensureDirReady(); - await this.ensureSizeLoaded(); - await this.rotateIfNeeded(content.length); - await fs.appendFile(this.logPath, content); - if (!Number.isFinite(this.cachedSize)) { - this.cachedSize = await this.readSize(); - } else { - this.cachedSize += content.length; - } - }); - } - - flush() { - return this.queue; - } - - enqueue(task) { - const run = async () => { - try { - await task(); - } catch (error) { - this.reportError('write', this.logPath || 'unknown', error); - } - }; - this.queue = this.queue.then(run, run); - return this.queue; - } - - async ensureSizeLoaded() { - if (Number.isFinite(this.cachedSize)) { - return; - } - this.cachedSize = await this.readSize(); - } - - async ensureDirReady() { - if (!this.logPath) { - return; - } - if (this.dirReadyForPath === this.logPath) { - return; - } - const dirPath = path.dirname(this.logPath); - try { - await fs.mkdir(dirPath, { recursive: true }); - this.dirReadyForPath = this.logPath; - } catch (error) { - this.reportError('mkdir', dirPath, error); - } - } - - async readSize() { - if (!this.logPath) { - return 0; - } - try { - const stat = await fs.stat(this.logPath); - return stat.size; - } catch (error) { - if (isIgnorableFsError(error)) { - return 0; - } - this.reportError('stat', this.logPath, error); - return 0; - } - } - - async rotateIfNeeded(incomingBytes) { - if (!this.logPath || this.maxBytes <= 0) { - return; - } - - const currentSize = Number.isFinite(this.cachedSize) ? this.cachedSize : 0; - if (currentSize + Math.max(0, incomingBytes) <= this.maxBytes) { - return; - } - - if (this.backupCount <= 0) { - try { - await fs.truncate(this.logPath, 0); - } catch (error) { - if (!isIgnorableFsError(error)) { - this.reportError('truncate', this.logPath, error); - } - } - this.cachedSize = await this.readSize(); - return; - } - - const oldestPath = `${this.logPath}.${this.backupCount}`; - try { - await fs.unlink(oldestPath); - } catch (error) { - if (!isIgnorableFsError(error)) { - this.reportError('unlink', oldestPath, error); - } - } - - for (let index = this.backupCount - 1; index >= 1; index -= 1) { - const sourcePath = `${this.logPath}.${index}`; - const targetPath = `${this.logPath}.${index + 1}`; - try { - await fs.rename(sourcePath, targetPath); - } catch (error) { - if (!isIgnorableFsError(error)) { - this.reportError('rename', `${sourcePath} -> ${targetPath}`, error); - } - } - } - - try { - await fs.rename(this.logPath, `${this.logPath}.1`); - } catch (error) { - if (!isIgnorableFsError(error)) { - this.reportError('rename', `${this.logPath} -> ${this.logPath}.1`, error); - } - } - - this.cachedSize = await this.readSize(); - } - - reportError(action, targetPath, error) { - if (!isLogRotationDebugEnabled()) { - return; - } - const details = error instanceof Error ? error.message : String(error); - console.error( - `[astrbot][${this.label}] ${action} failed for ${targetPath}: ${details}`, - ); - } -} - -module.exports = { - RotatingLogWriter, -}; diff --git a/desktop/lib/startup-screen.js b/desktop/lib/startup-screen.js deleted file mode 100644 index 93a342b81..000000000 --- a/desktop/lib/startup-screen.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; - -const fs = require('fs'); - -async function loadStartupScreen(mainWindow, { getAssetPath, startupTexts }) { - if (!mainWindow) { - return false; - } - let iconUrl = ''; - try { - const iconBuffer = fs.readFileSync(getAssetPath('icon-no-shadow.svg')); - iconUrl = `data:image/svg+xml;base64,${iconBuffer.toString('base64')}`; - } catch {} - - const html = ` - - - - - AstrBot - - - -

- -`; - const startupUrl = `data:text/html;charset=utf-8,${encodeURIComponent(html)}`; - await mainWindow.loadURL(startupUrl); - return true; -} - -module.exports = { - loadStartupScreen, -}; diff --git a/desktop/main.js b/desktop/main.js deleted file mode 100644 index 5adff38b3..000000000 --- a/desktop/main.js +++ /dev/null @@ -1,420 +0,0 @@ -'use strict'; - -const fs = require('fs'); -const path = require('path'); -const { - app, - BrowserWindow, - Menu, - Tray, - nativeImage, - shell, - dialog, - ipcMain, -} = require('electron'); - -const { BackendManager } = require('./lib/backend-manager'); -const { loadDashboard } = require('./lib/dashboard-loader'); -const { createElectronLogger } = require('./lib/electron-logger'); -const { createLocaleService } = require('./lib/locale-service'); -const { loadStartupScreen } = require('./lib/startup-screen'); - -const isMac = process.platform === 'darwin'; -const dashboardTimeoutMsParsed = Number.parseInt( - process.env.ASTRBOT_DASHBOARD_TIMEOUT_MS || '20000', - 10, -); -const dashboardTimeoutMs = Number.isFinite(dashboardTimeoutMsParsed) - ? dashboardTimeoutMsParsed - : 20000; - -let mainWindow = null; -let tray = null; -let isQuitting = false; -let quitInProgress = false; -let backendManager = null; - -app.commandLine.appendSwitch('disable-http-cache'); - -const { logElectron, flushElectron } = createElectronLogger({ - app, - getRootDir: () => (backendManager ? backendManager.getRootDir() : null), -}); - -backendManager = new BackendManager({ - app, - baseDir: __dirname, - log: logElectron, - shouldSkipStart: () => isQuitting || quitInProgress, -}); - -const localeService = createLocaleService({ - app, - getRootDir: () => backendManager.getRootDir(), -}); - -function getAssetPath(filename) { - if (app.isPackaged) { - const packaged = path.join(process.resourcesPath, 'assets', filename); - if (fs.existsSync(packaged)) { - return packaged; - } - } - return path.join(__dirname, 'assets', filename); -} - -function loadImageSafe(imagePath) { - try { - const image = nativeImage.createFromPath(imagePath); - if (!image.isEmpty()) { - return image; - } - } catch {} - return nativeImage.createEmpty(); -} - -function showWindow() { - if (!mainWindow) { - return; - } - mainWindow.show(); - mainWindow.focus(); - updateTrayMenu(); -} - -function toggleWindow() { - if (!mainWindow) { - return; - } - if (mainWindow.isVisible()) { - mainWindow.hide(); - } else { - mainWindow.show(); - mainWindow.focus(); - } - updateTrayMenu(); -} - -function updateTrayMenu() { - if (!tray || !mainWindow) { - return; - } - const shellTexts = localeService.getShellTexts( - localeService.resolveStartupLocale(), - ); - const isVisible = mainWindow.isVisible(); - const contextMenu = Menu.buildFromTemplate([ - { - label: isVisible ? shellTexts.trayHide : shellTexts.trayShow, - click: () => toggleWindow(), - }, - { - label: shellTexts.trayReload, - click: () => { - if (mainWindow) { - mainWindow.reload(); - } - }, - }, - { - label: shellTexts.trayRestartBackend, - click: async () => { - if (!backendManager) { - return; - } - if (mainWindow && !mainWindow.isDestroyed()) { - showWindow(); - const currentUrl = mainWindow.webContents.getURL(); - if (currentUrl.startsWith(backendManager.getBackendUrl())) { - mainWindow.webContents.send('astrbot-desktop:tray-restart-backend'); - return; - } - } - - const result = await backendManager.restartBackend(); - if (!result.ok) { - logElectron( - `Tray restart backend fallback failed: ${result.reason || 'unknown reason'}`, - ); - } - }, - }, - { type: 'separator' }, - { - label: shellTexts.trayQuit, - click: () => app.quit(), - }, - ]); - tray.setContextMenu(contextMenu); -} - -function createTray() { - const traySize = isMac ? 18 : 16; - const trayPath = getAssetPath('tray.png'); - let trayImage = loadImageSafe(trayPath); - if (trayImage.isEmpty()) { - trayImage = loadImageSafe(getAssetPath('icon.png')); - } - if (!trayImage.isEmpty()) { - trayImage = trayImage.resize({ width: traySize, height: traySize }); - if (isMac) { - trayImage.setTemplateImage(true); - } - tray = new Tray(trayImage); - } else { - tray = new Tray(nativeImage.createEmpty()); - } - tray.setToolTip('AstrBot'); - tray.on('click', () => toggleWindow()); - updateTrayMenu(); -} - -function createWindow() { - mainWindow = new BrowserWindow({ - width: 1280, - height: 800, - minWidth: 980, - minHeight: 680, - show: false, - backgroundColor: '#f9fafc', - autoHideMenuBar: !isMac, - icon: getAssetPath('icon.png'), - webPreferences: { - contextIsolation: true, - nodeIntegration: false, - sandbox: true, - preload: path.join(__dirname, 'preload.js'), - ...(isMac - ? { - defaultFontFamily: { - standard: 'PingFang SC', - sansSerif: 'PingFang SC', - serif: 'Songti SC', - monospace: 'SF Mono', - }, - } - : {}), - }, - }); - - mainWindow.on('close', (event) => { - if (isQuitting) { - return; - } - event.preventDefault(); - mainWindow.hide(); - }); - - mainWindow.on('minimize', (event) => { - event.preventDefault(); - mainWindow.hide(); - }); - - mainWindow.on('show', () => updateTrayMenu()); - mainWindow.on('hide', () => updateTrayMenu()); - - mainWindow.webContents.setWindowOpenHandler(({ url }) => { - shell.openExternal(url); - return { action: 'deny' }; - }); - - mainWindow.webContents.on( - 'did-fail-load', - (_event, errorCode, errorDescription, validatedURL, isMainFrame) => { - if (!isMainFrame) { - return; - } - logElectron( - `did-fail-load main-frame code=${errorCode} desc=${errorDescription} url=${validatedURL}`, - ); - }, - ); - - mainWindow.webContents.on('did-finish-load', () => { - const currentUrl = mainWindow.webContents.getURL(); - logElectron(`did-finish-load url=${currentUrl}`); - if (currentUrl.startsWith(backendManager.getBackendUrl())) { - void localeService.persistLocaleFromDashboard( - mainWindow, - backendManager.getBackendUrl(), - ); - } - }); - - mainWindow.webContents.on('render-process-gone', (_event, details) => { - logElectron( - `render-process-gone reason=${details.reason} exitCode=${details.exitCode}`, - ); - }); - - mainWindow.webContents.on( - 'console-message', - (_event, level, message, line, sourceId) => { - if (level >= 2) { - logElectron( - `renderer-console level=${level} source=${sourceId}:${line} message=${message}`, - ); - } - }, - ); - - return mainWindow; -} - -function registerIpcHandlers() { - ipcMain.handle('astrbot-desktop:is-electron-runtime', async () => true); - - ipcMain.handle('astrbot-desktop:get-backend-state', async () => { - return backendManager.getState(); - }); - - ipcMain.handle('astrbot-desktop:restart-backend', async (_event, authToken) => { - return backendManager.restartBackend(authToken); - }); - - ipcMain.handle('astrbot-desktop:stop-backend', async () => { - return backendManager.stopBackendForIpc(); - }); -} - -async function startDesktopFlow() { - createWindow(); - createTray(); - - try { - const startupTexts = localeService.getStartupTexts( - localeService.resolveStartupLocale(), - ); - await loadStartupScreen(mainWindow, { - getAssetPath, - startupTexts, - }); - } catch (error) { - logElectron( - `failed to load startup screen: ${ - error instanceof Error ? error.message : String(error) - }`, - ); - } - - showWindow(); - - const ready = await backendManager.ensureBackend(); - if (isQuitting) { - return; - } - - if (!ready) { - const shellTexts = localeService.getShellTexts( - localeService.resolveStartupLocale(), - ); - const backendLogPath = backendManager.getBackendLogPath(); - const detailLines = []; - const startupFailureReason = backendManager.getStartupFailureReason(); - if (startupFailureReason) { - detailLines.push( - `${shellTexts.startupFailReasonPrefix}: ${startupFailureReason}`, - ); - } - detailLines.push(shellTexts.startupFailAction); - if (backendLogPath) { - detailLines.push(`${shellTexts.startupFailLogPrefix}: ${backendLogPath}`); - } - - await dialog.showMessageBox({ - type: 'error', - title: shellTexts.startupFailTitle, - message: shellTexts.startupFailMessage, - detail: detailLines.join('\n'), - }); - isQuitting = true; - app.quit(); - return; - } - - try { - await loadDashboard( - mainWindow, - backendManager.getBackendUrl(), - dashboardTimeoutMs, - ); - showWindow(); - } catch (error) { - const shellTexts = localeService.getShellTexts( - localeService.resolveStartupLocale(), - ); - await dialog.showMessageBox({ - type: 'error', - title: shellTexts.dashboardFailTitle, - message: shellTexts.dashboardFailMessage, - detail: error instanceof Error ? error.message : String(error), - }); - isQuitting = true; - app.quit(); - } -} - -registerIpcHandlers(); - -app.setAppUserModelId('com.astrbot.desktop'); - -const gotLock = app.requestSingleInstanceLock(); -if (!gotLock) { - app.quit(); -} else { - app.on('second-instance', () => { - showWindow(); - }); -} - -app.on('before-quit', (event) => { - if (quitInProgress) { - event.preventDefault(); - return; - } - event.preventDefault(); - quitInProgress = true; - isQuitting = true; - logElectron('before-quit received, stopping backend.'); - - localeService - .persistLocaleFromDashboard(mainWindow, backendManager.getBackendUrl()) - .catch(() => {}) - .then(() => - backendManager.stopAnyBackend().then((result) => { - if (!result.ok) { - logElectron(`stopBackend failed: ${result.reason || 'unknown reason'}`); - } - }), - ) - .finally(async () => { - logElectron('Backend stop finished, exiting app.'); - await Promise.allSettled([ - flushElectron(), - backendManager ? backendManager.flushLogs() : Promise.resolve(), - ]); - app.exit(0); - }); -}); - -app.whenReady().then(async () => { - if (isMac && app.dock) { - const dockIcon = getAssetPath('icon.png'); - if (fs.existsSync(dockIcon)) { - app.dock.setIcon(dockIcon); - } - } - await startDesktopFlow(); -}); - -app.on('activate', () => { - if (mainWindow) { - showWindow(); - } -}); - -app.on('window-all-closed', () => { - if (!isMac) { - app.quit(); - } -}); diff --git a/desktop/package.json b/desktop/package.json deleted file mode 100644 index 2fb2349d3..000000000 --- a/desktop/package.json +++ /dev/null @@ -1,97 +0,0 @@ -{ - "name": "astrbot-desktop", - "version": "4.17.5", - "description": "AstrBot desktop wrapper", - "private": true, - "main": "main.js", - "author": "AstrBot", - "packageManager": "pnpm@10.28.2", - "pnpm": { - "onlyBuiltDependencies": [ - "electron" - ] - }, - "scripts": { - "dev": "electron .", - "start": "electron .", - "sync:version": "node scripts/sync-version.mjs", - "build:webui": "node scripts/prepare-webui.mjs", - "build:backend": "node scripts/build-backend.mjs", - "dist:full": "pnpm run build:webui && pnpm run build:backend && pnpm run dist", - "pack": "pnpm run sync:version && electron-builder --dir", - "dist": "pnpm run sync:version && electron-builder" - }, - "devDependencies": { - "electron": "^40.3.0", - "electron-builder": "^24.13.0" - }, - "build": { - "appId": "com.astrbot.desktop", - "productName": "AstrBot", - "icon": "assets/icon.png", - "extraResources": [ - { - "from": "resources/backend", - "to": "backend", - "filter": [ - "**/*", - "!**/*.map" - ] - }, - { - "from": "resources/webui", - "to": "webui", - "filter": [ - "**/*", - "!**/*.map" - ] - }, - { - "from": "assets", - "to": "assets", - "filter": [ - "**/*", - "!**/*.map" - ] - } - ], - "files": [ - "**/*", - "!**/*.map", - "!**/*.d.ts", - "!**/{test,__tests__,tests,powered-test,example,examples}/**" - ], - "compression": "maximum", - "electronLanguages": [ - "en-US", - "zh-CN" - ], - "asar": true, - "directories": { - "buildResources": "assets" - }, - "linux": { - "target": [ - "AppImage" - ], - "category": "Utility" - }, - "mac": { - "target": [ - "dmg", - "zip" - ], - "category": "public.app-category.productivity" - }, - "win": { - "target": [ - "nsis", - "zip" - ] - }, - "nsis": { - "oneClick": false, - "allowToChangeInstallationDirectory": true - } - } -} diff --git a/desktop/pnpm-lock.yaml b/desktop/pnpm-lock.yaml deleted file mode 100644 index 98411a90e..000000000 --- a/desktop/pnpm-lock.yaml +++ /dev/null @@ -1,2277 +0,0 @@ -lockfileVersion: '9.0' - -settings: - autoInstallPeers: true - excludeLinksFromLockfile: false - -importers: - - .: - devDependencies: - electron: - specifier: ^40.3.0 - version: 40.3.0 - electron-builder: - specifier: ^24.13.0 - version: 24.13.3(electron-builder-squirrel-windows@24.13.3) - -packages: - - 7zip-bin@5.2.0: - resolution: {integrity: sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==} - - '@develar/schema-utils@2.6.5': - resolution: {integrity: sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==} - engines: {node: '>= 8.9.0'} - - '@electron/asar@3.4.1': - resolution: {integrity: sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==} - engines: {node: '>=10.12.0'} - hasBin: true - - '@electron/get@2.0.3': - resolution: {integrity: sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==} - engines: {node: '>=12'} - - '@electron/notarize@2.2.1': - resolution: {integrity: sha512-aL+bFMIkpR0cmmj5Zgy0LMKEpgy43/hw5zadEArgmAMWWlKc5buwFvFT9G/o/YJkvXAJm5q3iuTuLaiaXW39sg==} - engines: {node: '>= 10.0.0'} - - '@electron/osx-sign@1.0.5': - resolution: {integrity: sha512-k9ZzUQtamSoweGQDV2jILiRIHUu7lYlJ3c6IEmjv1hC17rclE+eb9U+f6UFlOOETo0JzY1HNlXy4YOlCvl+Lww==} - engines: {node: '>=12.0.0'} - hasBin: true - - '@electron/universal@1.5.1': - resolution: {integrity: sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==} - engines: {node: '>=8.6'} - - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@malept/cross-spawn-promise@1.1.1': - resolution: {integrity: sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==} - engines: {node: '>= 10'} - - '@malept/flatpak-bundler@0.4.0': - resolution: {integrity: sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==} - engines: {node: '>= 10.0.0'} - - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} - - '@sindresorhus/is@4.6.0': - resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} - engines: {node: '>=10'} - - '@szmarczak/http-timer@4.0.6': - resolution: {integrity: sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==} - engines: {node: '>=10'} - - '@tootallnate/once@2.0.0': - resolution: {integrity: sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==} - engines: {node: '>= 10'} - - '@types/cacheable-request@6.0.3': - resolution: {integrity: sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==} - - '@types/debug@4.1.12': - resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} - - '@types/fs-extra@9.0.13': - resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==} - - '@types/http-cache-semantics@4.2.0': - resolution: {integrity: sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==} - - '@types/keyv@3.1.4': - resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==} - - '@types/ms@2.1.0': - resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} - - '@types/node@24.10.13': - resolution: {integrity: sha512-oH72nZRfDv9lADUBSo104Aq7gPHpQZc4BTx38r9xf9pg5LfP6EzSyH2n7qFmmxRQXh7YlUXODcYsg6PuTDSxGg==} - - '@types/node@25.2.2': - resolution: {integrity: sha512-BkmoP5/FhRYek5izySdkOneRyXYN35I860MFAGupTdebyE66uZaR+bXLHq8k4DirE5DwQi3NuhvRU1jqTVwUrQ==} - - '@types/plist@3.0.5': - resolution: {integrity: sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==} - - '@types/responselike@1.0.3': - resolution: {integrity: sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==} - - '@types/verror@1.10.11': - resolution: {integrity: sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==} - - '@types/yauzl@2.10.3': - resolution: {integrity: sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==} - - '@xmldom/xmldom@0.8.11': - resolution: {integrity: sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==} - engines: {node: '>=10.0.0'} - - agent-base@6.0.2: - resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} - engines: {node: '>= 6.0.0'} - - ajv-keywords@3.5.2: - resolution: {integrity: sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==} - peerDependencies: - ajv: ^6.9.1 - - ajv@6.12.6: - resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} - - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.2.2: - resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.3: - resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} - engines: {node: '>=12'} - - app-builder-bin@4.0.0: - resolution: {integrity: sha512-xwdG0FJPQMe0M0UA4Tz0zEB8rBJTRA5a476ZawAqiBkMv16GRK5xpXThOjMaEOFnZ6zabejjG4J3da0SXG63KA==} - - app-builder-lib@24.13.3: - resolution: {integrity: sha512-FAzX6IBit2POXYGnTCT8YHFO/lr5AapAII6zzhQO3Rw4cEDOgK+t1xhLc5tNcKlicTHlo9zxIwnYCX9X2DLkig==} - engines: {node: '>=14.0.0'} - peerDependencies: - dmg-builder: 24.13.3 - electron-builder-squirrel-windows: 24.13.3 - - archiver-utils@2.1.0: - resolution: {integrity: sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==} - engines: {node: '>= 6'} - - archiver-utils@3.0.4: - resolution: {integrity: sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==} - engines: {node: '>= 10'} - - archiver@5.3.2: - resolution: {integrity: sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==} - engines: {node: '>= 10'} - - argparse@2.0.1: - resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} - - assert-plus@1.0.0: - resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} - engines: {node: '>=0.8'} - - astral-regex@2.0.0: - resolution: {integrity: sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==} - engines: {node: '>=8'} - - async-exit-hook@2.0.1: - resolution: {integrity: sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==} - engines: {node: '>=0.12.0'} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} - - asynckit@0.4.0: - resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - - at-least-node@1.0.0: - resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==} - engines: {node: '>= 4.0.0'} - - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} - - base64-js@1.5.1: - resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - - bl@4.1.0: - resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - - bluebird-lst@1.0.9: - resolution: {integrity: sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==} - - bluebird@3.7.2: - resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} - - boolean@3.2.0: - resolution: {integrity: sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==} - deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info. - - brace-expansion@1.1.12: - resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} - - brace-expansion@2.0.2: - resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} - - buffer-crc32@0.2.13: - resolution: {integrity: sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==} - - buffer-equal@1.0.1: - resolution: {integrity: sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==} - engines: {node: '>=0.4'} - - buffer-from@1.1.2: - resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} - - buffer@5.7.1: - resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} - - builder-util-runtime@9.2.4: - resolution: {integrity: sha512-upp+biKpN/XZMLim7aguUyW8s0FUpDvOtK6sbanMFDAMBzpHDqdhgVYm6zc9HJ6nWo7u2Lxk60i2M6Jd3aiNrA==} - engines: {node: '>=12.0.0'} - - builder-util@24.13.1: - resolution: {integrity: sha512-NhbCSIntruNDTOVI9fdXz0dihaqX2YuE1D6zZMrwiErzH4ELZHE6mdiB40wEgZNprDia+FghRFgKoAqMZRRjSA==} - - cacheable-lookup@5.0.4: - resolution: {integrity: sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==} - engines: {node: '>=10.6.0'} - - cacheable-request@7.0.4: - resolution: {integrity: sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==} - engines: {node: '>=8'} - - call-bind-apply-helpers@1.0.2: - resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} - engines: {node: '>= 0.4'} - - chalk@4.1.2: - resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} - engines: {node: '>=10'} - - chownr@2.0.0: - resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==} - engines: {node: '>=10'} - - chromium-pickle-js@0.2.0: - resolution: {integrity: sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==} - - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - - cli-truncate@2.1.0: - resolution: {integrity: sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==} - engines: {node: '>=8'} - - cliui@8.0.1: - resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} - engines: {node: '>=12'} - - clone-response@1.0.3: - resolution: {integrity: sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==} - - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - combined-stream@1.0.8: - resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} - engines: {node: '>= 0.8'} - - commander@5.1.0: - resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} - engines: {node: '>= 6'} - - compare-version@0.1.2: - resolution: {integrity: sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==} - engines: {node: '>=0.10.0'} - - compress-commons@4.1.2: - resolution: {integrity: sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==} - engines: {node: '>= 10'} - - concat-map@0.0.1: - resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} - - config-file-ts@0.2.6: - resolution: {integrity: sha512-6boGVaglwblBgJqGyxm4+xCmEGcWgnWHSWHY5jad58awQhB6gftq0G8HbzU39YqCIYHMLAiL1yjwiZ36m/CL8w==} - - core-util-is@1.0.2: - resolution: {integrity: sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==} - - core-util-is@1.0.3: - resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} - - crc-32@1.2.2: - resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==} - engines: {node: '>=0.8'} - hasBin: true - - crc32-stream@4.0.3: - resolution: {integrity: sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==} - engines: {node: '>= 10'} - - crc@3.8.0: - resolution: {integrity: sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==} - - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - - debug@4.4.3: - resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} - engines: {node: '>=6.0'} - peerDependencies: - supports-color: '*' - peerDependenciesMeta: - supports-color: - optional: true - - decompress-response@6.0.0: - resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} - engines: {node: '>=10'} - - defer-to-connect@2.0.1: - resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} - engines: {node: '>=10'} - - define-data-property@1.1.4: - resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} - engines: {node: '>= 0.4'} - - define-properties@1.2.1: - resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} - engines: {node: '>= 0.4'} - - delayed-stream@1.0.0: - resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} - engines: {node: '>=0.4.0'} - - detect-node@2.1.0: - resolution: {integrity: sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==} - - dir-compare@3.3.0: - resolution: {integrity: sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==} - - dmg-builder@24.13.3: - resolution: {integrity: sha512-rcJUkMfnJpfCboZoOOPf4L29TRtEieHNOeAbYPWPxlaBw/Z1RKrRA86dOI9rwaI4tQSc/RD82zTNHprfUHXsoQ==} - - dmg-license@1.0.11: - resolution: {integrity: sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==} - engines: {node: '>=8'} - os: [darwin] - hasBin: true - - dotenv-expand@5.1.0: - resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} - - dotenv@9.0.2: - resolution: {integrity: sha512-I9OvvrHp4pIARv4+x9iuewrWycX6CcZtoAu1XrzPxc5UygMJXJZYmBsynku8IkrJwgypE5DGNjDPmPRhDCptUg==} - engines: {node: '>=10'} - - dunder-proto@1.0.1: - resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} - engines: {node: '>= 0.4'} - - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - electron-builder-squirrel-windows@24.13.3: - resolution: {integrity: sha512-oHkV0iogWfyK+ah9ZIvMDpei1m9ZRpdXcvde1wTpra2U8AFDNNpqJdnin5z+PM1GbQ5BoaKCWas2HSjtR0HwMg==} - - electron-builder@24.13.3: - resolution: {integrity: sha512-yZSgVHft5dNVlo31qmJAe4BVKQfFdwpRw7sFp1iQglDRCDD6r22zfRJuZlhtB5gp9FHUxCMEoWGq10SkCnMAIg==} - engines: {node: '>=14.0.0'} - hasBin: true - - electron-publish@24.13.1: - resolution: {integrity: sha512-2ZgdEqJ8e9D17Hwp5LEq5mLQPjqU3lv/IALvgp+4W8VeNhryfGhYEQC/PgDPMrnWUp+l60Ou5SJLsu+k4mhQ8A==} - - electron@40.3.0: - resolution: {integrity: sha512-ZaDkTZpNHr863tyZHieoqbaiLI0e3RVCXoEC5y1Ld70/Q5H1mPV9d5TK0h1dWtaSFVOW0w8iDvtdLwAXtasXpg==} - engines: {node: '>= 12.20.55'} - hasBin: true - - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - - end-of-stream@1.4.5: - resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - - env-paths@2.2.1: - resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} - engines: {node: '>=6'} - - err-code@2.0.3: - resolution: {integrity: sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==} - - es-define-property@1.0.1: - resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} - engines: {node: '>= 0.4'} - - es-errors@1.3.0: - resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} - engines: {node: '>= 0.4'} - - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} - engines: {node: '>= 0.4'} - - es-set-tostringtag@2.1.0: - resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} - engines: {node: '>= 0.4'} - - es6-error@4.1.1: - resolution: {integrity: sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==} - - escalade@3.2.0: - resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} - engines: {node: '>=6'} - - escape-string-regexp@4.0.0: - resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} - engines: {node: '>=10'} - - extract-zip@2.0.1: - resolution: {integrity: sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==} - engines: {node: '>= 10.17.0'} - hasBin: true - - extsprintf@1.4.1: - resolution: {integrity: sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==} - engines: {'0': node >=0.6.0} - - fast-deep-equal@3.1.3: - resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} - - fast-json-stable-stringify@2.1.0: - resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - - fd-slicer@1.1.0: - resolution: {integrity: sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==} - - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - - form-data@4.0.5: - resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} - engines: {node: '>= 6'} - - fs-constants@1.0.0: - resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} - - fs-extra@10.1.0: - resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} - engines: {node: '>=12'} - - fs-extra@8.1.0: - resolution: {integrity: sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==} - engines: {node: '>=6 <7 || >=8'} - - fs-extra@9.1.0: - resolution: {integrity: sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==} - engines: {node: '>=10'} - - fs-minipass@2.1.0: - resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==} - engines: {node: '>= 8'} - - fs.realpath@1.0.0: - resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} - - function-bind@1.1.2: - resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - - get-caller-file@2.0.5: - resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} - engines: {node: 6.* || 8.* || >= 10.*} - - get-intrinsic@1.3.0: - resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} - engines: {node: '>= 0.4'} - - get-proto@1.0.1: - resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} - engines: {node: '>= 0.4'} - - get-stream@5.2.0: - resolution: {integrity: sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==} - engines: {node: '>=8'} - - glob@10.5.0: - resolution: {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 - hasBin: true - - glob@7.2.3: - resolution: {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 - - global-agent@3.0.0: - resolution: {integrity: sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==} - engines: {node: '>=10.0'} - - globalthis@1.0.4: - resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} - engines: {node: '>= 0.4'} - - gopd@1.2.0: - resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} - engines: {node: '>= 0.4'} - - got@11.8.6: - resolution: {integrity: sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==} - engines: {node: '>=10.19.0'} - - graceful-fs@4.2.11: - resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - - has-flag@4.0.0: - resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} - engines: {node: '>=8'} - - has-property-descriptors@1.0.2: - resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} - - has-symbols@1.1.0: - resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} - engines: {node: '>= 0.4'} - - has-tostringtag@1.0.2: - resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} - engines: {node: '>= 0.4'} - - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} - engines: {node: '>= 0.4'} - - hosted-git-info@4.1.0: - resolution: {integrity: sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==} - engines: {node: '>=10'} - - http-cache-semantics@4.2.0: - resolution: {integrity: sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==} - - http-proxy-agent@5.0.0: - resolution: {integrity: sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==} - engines: {node: '>= 6'} - - http2-wrapper@1.0.3: - resolution: {integrity: sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==} - engines: {node: '>=10.19.0'} - - https-proxy-agent@5.0.1: - resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} - engines: {node: '>= 6'} - - iconv-corefoundation@1.1.7: - resolution: {integrity: sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==} - engines: {node: ^8.11.2 || >=10} - os: [darwin] - - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} - - ieee754@1.2.1: - resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} - - inflight@1.0.6: - resolution: {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. - - inherits@2.0.4: - resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} - - is-ci@3.0.1: - resolution: {integrity: sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==} - hasBin: true - - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isbinaryfile@4.0.10: - resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} - engines: {node: '>= 8.0.0'} - - isbinaryfile@5.0.7: - resolution: {integrity: sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==} - engines: {node: '>= 18.0.0'} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - - jake@10.9.4: - resolution: {integrity: sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==} - engines: {node: '>=10'} - hasBin: true - - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} - hasBin: true - - json-buffer@3.0.1: - resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - - json-schema-traverse@0.4.1: - resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} - - json-stringify-safe@5.0.1: - resolution: {integrity: sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==} - - json5@2.2.3: - resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} - engines: {node: '>=6'} - hasBin: true - - jsonfile@4.0.0: - resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} - - jsonfile@6.2.0: - resolution: {integrity: sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==} - - keyv@4.5.4: - resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} - - lazy-val@1.0.5: - resolution: {integrity: sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==} - - lazystream@1.0.1: - resolution: {integrity: sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==} - engines: {node: '>= 0.6.3'} - - lodash.defaults@4.2.0: - resolution: {integrity: sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==} - - lodash.difference@4.5.0: - resolution: {integrity: sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==} - - lodash.flatten@4.4.0: - resolution: {integrity: sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==} - - lodash.isplainobject@4.0.6: - resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} - - lodash.union@4.6.0: - resolution: {integrity: sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==} - - lodash@4.17.23: - resolution: {integrity: sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==} - - lowercase-keys@2.0.0: - resolution: {integrity: sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==} - engines: {node: '>=8'} - - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - - lru-cache@6.0.0: - resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} - engines: {node: '>=10'} - - matcher@3.0.0: - resolution: {integrity: sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==} - engines: {node: '>=10'} - - math-intrinsics@1.1.0: - resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} - engines: {node: '>= 0.4'} - - mime-db@1.52.0: - resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} - engines: {node: '>= 0.6'} - - mime-types@2.1.35: - resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} - engines: {node: '>= 0.6'} - - mime@2.6.0: - resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} - engines: {node: '>=4.0.0'} - hasBin: true - - mimic-response@1.0.1: - resolution: {integrity: sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==} - engines: {node: '>=4'} - - mimic-response@3.1.0: - resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} - engines: {node: '>=10'} - - minimatch@3.1.2: - resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} - - minimatch@5.1.6: - resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} - engines: {node: '>=10'} - - minimatch@9.0.5: - resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} - engines: {node: '>=16 || 14 >=14.17'} - - minimist@1.2.8: - resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - - minipass@3.3.6: - resolution: {integrity: sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==} - engines: {node: '>=8'} - - minipass@5.0.0: - resolution: {integrity: sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==} - engines: {node: '>=8'} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - - minizlib@2.1.2: - resolution: {integrity: sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==} - engines: {node: '>= 8'} - - mkdirp@1.0.4: - resolution: {integrity: sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==} - engines: {node: '>=10'} - hasBin: true - - ms@2.1.3: - resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} - - node-addon-api@1.7.2: - resolution: {integrity: sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==} - - normalize-path@3.0.0: - resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} - engines: {node: '>=0.10.0'} - - normalize-url@6.1.0: - resolution: {integrity: sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==} - engines: {node: '>=10'} - - object-keys@1.1.1: - resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} - engines: {node: '>= 0.4'} - - once@1.4.0: - resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} - - p-cancelable@2.1.1: - resolution: {integrity: sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==} - engines: {node: '>=8'} - - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - path-is-absolute@1.0.1: - resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} - engines: {node: '>=0.10.0'} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - - pend@1.2.0: - resolution: {integrity: sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==} - - picocolors@1.1.1: - resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - - plist@3.1.0: - resolution: {integrity: sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==} - engines: {node: '>=10.4.0'} - - process-nextick-args@2.0.1: - resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} - - progress@2.0.3: - resolution: {integrity: sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==} - engines: {node: '>=0.4.0'} - - promise-retry@2.0.1: - resolution: {integrity: sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==} - engines: {node: '>=10'} - - pump@3.0.3: - resolution: {integrity: sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==} - - punycode@2.3.1: - resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} - engines: {node: '>=6'} - - quick-lru@5.1.1: - resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} - engines: {node: '>=10'} - - read-config-file@6.3.2: - resolution: {integrity: sha512-M80lpCjnE6Wt6zb98DoW8WHR09nzMSpu8XHtPkiTHrJ5Az9CybfeQhTJ8D7saeBHpGhLPIVyA8lcL6ZmdKwY6Q==} - engines: {node: '>=12.0.0'} - - readable-stream@2.3.8: - resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} - - readable-stream@3.6.2: - resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} - engines: {node: '>= 6'} - - readdir-glob@1.1.3: - resolution: {integrity: sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==} - - require-directory@2.1.1: - resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} - engines: {node: '>=0.10.0'} - - resolve-alpn@1.2.1: - resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - - responselike@2.0.1: - resolution: {integrity: sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==} - - retry@0.12.0: - resolution: {integrity: sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==} - engines: {node: '>= 4'} - - roarr@2.15.4: - resolution: {integrity: sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==} - engines: {node: '>=8.0'} - - safe-buffer@5.1.2: - resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} - - safe-buffer@5.2.1: - resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} - - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - - sanitize-filename@1.6.3: - resolution: {integrity: sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==} - - sax@1.4.4: - resolution: {integrity: sha512-1n3r/tGXO6b6VXMdFT54SHzT9ytu9yr7TaELowdYpMqY/Ao7EnlQGmAQ1+RatX7Tkkdm6hONI2owqNx2aZj5Sw==} - engines: {node: '>=11.0.0'} - - semver-compare@1.0.0: - resolution: {integrity: sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==} - - semver@6.3.1: - resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} - hasBin: true - - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} - engines: {node: '>=10'} - hasBin: true - - serialize-error@7.0.1: - resolution: {integrity: sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==} - engines: {node: '>=10'} - - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - simple-update-notifier@2.0.0: - resolution: {integrity: sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==} - engines: {node: '>=10'} - - slice-ansi@3.0.0: - resolution: {integrity: sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==} - engines: {node: '>=8'} - - smart-buffer@4.2.0: - resolution: {integrity: sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==} - engines: {node: '>= 6.0.0', npm: '>= 3.0.0'} - - source-map-support@0.5.21: - resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} - - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - sprintf-js@1.1.3: - resolution: {integrity: sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==} - - stat-mode@1.0.0: - resolution: {integrity: sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==} - engines: {node: '>= 6'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - string_decoder@1.1.1: - resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} - - string_decoder@1.3.0: - resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.2: - resolution: {integrity: sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==} - engines: {node: '>=12'} - - sumchecker@3.0.1: - resolution: {integrity: sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==} - engines: {node: '>= 8.0'} - - supports-color@7.2.0: - resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} - engines: {node: '>=8'} - - tar-stream@2.2.0: - resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} - engines: {node: '>=6'} - - tar@6.2.1: - resolution: {integrity: sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==} - engines: {node: '>=10'} - 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 - - temp-file@3.4.0: - resolution: {integrity: sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==} - - tmp-promise@3.0.3: - resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} - - tmp@0.2.5: - resolution: {integrity: sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==} - engines: {node: '>=14.14'} - - truncate-utf8-bytes@1.0.2: - resolution: {integrity: sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==} - - type-fest@0.13.1: - resolution: {integrity: sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==} - engines: {node: '>=10'} - - typescript@5.9.3: - resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} - engines: {node: '>=14.17'} - hasBin: true - - undici-types@7.16.0: - resolution: {integrity: sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==} - - universalify@0.1.2: - resolution: {integrity: sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==} - engines: {node: '>= 4.0.0'} - - universalify@2.0.1: - resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} - engines: {node: '>= 10.0.0'} - - uri-js@4.4.1: - resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} - - utf8-byte-length@1.0.5: - resolution: {integrity: sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==} - - util-deprecate@1.0.2: - resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - - verror@1.10.1: - resolution: {integrity: sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==} - engines: {node: '>=0.6.0'} - - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - wrappy@1.0.2: - resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} - - xmlbuilder@15.1.1: - resolution: {integrity: sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==} - engines: {node: '>=8.0'} - - y18n@5.0.8: - resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} - engines: {node: '>=10'} - - yallist@4.0.0: - resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} - - yargs-parser@21.1.1: - resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} - engines: {node: '>=12'} - - yargs@17.7.2: - resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} - engines: {node: '>=12'} - - yauzl@2.10.0: - resolution: {integrity: sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==} - - zip-stream@4.1.1: - resolution: {integrity: sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==} - engines: {node: '>= 10'} - -snapshots: - - 7zip-bin@5.2.0: {} - - '@develar/schema-utils@2.6.5': - dependencies: - ajv: 6.12.6 - ajv-keywords: 3.5.2(ajv@6.12.6) - - '@electron/asar@3.4.1': - dependencies: - commander: 5.1.0 - glob: 7.2.3 - minimatch: 3.1.2 - - '@electron/get@2.0.3': - dependencies: - debug: 4.4.3 - env-paths: 2.2.1 - fs-extra: 8.1.0 - got: 11.8.6 - progress: 2.0.3 - semver: 6.3.1 - sumchecker: 3.0.1 - optionalDependencies: - global-agent: 3.0.0 - transitivePeerDependencies: - - supports-color - - '@electron/notarize@2.2.1': - dependencies: - debug: 4.4.3 - fs-extra: 9.1.0 - promise-retry: 2.0.1 - transitivePeerDependencies: - - supports-color - - '@electron/osx-sign@1.0.5': - dependencies: - compare-version: 0.1.2 - debug: 4.4.3 - fs-extra: 10.1.0 - isbinaryfile: 4.0.10 - minimist: 1.2.8 - plist: 3.1.0 - transitivePeerDependencies: - - supports-color - - '@electron/universal@1.5.1': - dependencies: - '@electron/asar': 3.4.1 - '@malept/cross-spawn-promise': 1.1.1 - debug: 4.4.3 - dir-compare: 3.3.0 - fs-extra: 9.1.0 - minimatch: 3.1.2 - plist: 3.1.0 - transitivePeerDependencies: - - supports-color - - '@isaacs/cliui@8.0.2': - dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.2 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@malept/cross-spawn-promise@1.1.1': - dependencies: - cross-spawn: 7.0.6 - - '@malept/flatpak-bundler@0.4.0': - dependencies: - debug: 4.4.3 - fs-extra: 9.1.0 - lodash: 4.17.23 - tmp-promise: 3.0.3 - transitivePeerDependencies: - - supports-color - - '@pkgjs/parseargs@0.11.0': - optional: true - - '@sindresorhus/is@4.6.0': {} - - '@szmarczak/http-timer@4.0.6': - dependencies: - defer-to-connect: 2.0.1 - - '@tootallnate/once@2.0.0': {} - - '@types/cacheable-request@6.0.3': - dependencies: - '@types/http-cache-semantics': 4.2.0 - '@types/keyv': 3.1.4 - '@types/node': 25.2.2 - '@types/responselike': 1.0.3 - - '@types/debug@4.1.12': - dependencies: - '@types/ms': 2.1.0 - - '@types/fs-extra@9.0.13': - dependencies: - '@types/node': 25.2.2 - - '@types/http-cache-semantics@4.2.0': {} - - '@types/keyv@3.1.4': - dependencies: - '@types/node': 25.2.2 - - '@types/ms@2.1.0': {} - - '@types/node@24.10.13': - dependencies: - undici-types: 7.16.0 - - '@types/node@25.2.2': - dependencies: - undici-types: 7.16.0 - - '@types/plist@3.0.5': - dependencies: - '@types/node': 25.2.2 - xmlbuilder: 15.1.1 - optional: true - - '@types/responselike@1.0.3': - dependencies: - '@types/node': 25.2.2 - - '@types/verror@1.10.11': - optional: true - - '@types/yauzl@2.10.3': - dependencies: - '@types/node': 25.2.2 - optional: true - - '@xmldom/xmldom@0.8.11': {} - - agent-base@6.0.2: - dependencies: - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - ajv-keywords@3.5.2(ajv@6.12.6): - dependencies: - ajv: 6.12.6 - - ajv@6.12.6: - dependencies: - fast-deep-equal: 3.1.3 - fast-json-stable-stringify: 2.1.0 - json-schema-traverse: 0.4.1 - uri-js: 4.4.1 - - ansi-regex@5.0.1: {} - - ansi-regex@6.2.2: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.3: {} - - app-builder-bin@4.0.0: {} - - app-builder-lib@24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3): - 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.4.3 - dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) - ejs: 3.1.10 - electron-builder-squirrel-windows: 24.13.3(dmg-builder@24.13.3) - electron-publish: 24.13.1 - form-data: 4.0.5 - fs-extra: 10.1.0 - hosted-git-info: 4.1.0 - is-ci: 3.0.1 - isbinaryfile: 5.0.7 - js-yaml: 4.1.1 - lazy-val: 1.0.5 - minimatch: 5.1.6 - read-config-file: 6.3.2 - sanitize-filename: 1.6.3 - semver: 7.7.4 - tar: 6.2.1 - temp-file: 3.4.0 - transitivePeerDependencies: - - supports-color - - archiver-utils@2.1.0: - dependencies: - glob: 7.2.3 - graceful-fs: 4.2.11 - lazystream: 1.0.1 - 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.3.8 - - archiver-utils@3.0.4: - dependencies: - glob: 7.2.3 - graceful-fs: 4.2.11 - lazystream: 1.0.1 - 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.2 - - archiver@5.3.2: - dependencies: - archiver-utils: 2.1.0 - async: 3.2.6 - buffer-crc32: 0.2.13 - readable-stream: 3.6.2 - readdir-glob: 1.1.3 - tar-stream: 2.2.0 - zip-stream: 4.1.1 - - argparse@2.0.1: {} - - assert-plus@1.0.0: - optional: true - - astral-regex@2.0.0: - optional: true - - async-exit-hook@2.0.1: {} - - async@3.2.6: {} - - asynckit@0.4.0: {} - - at-least-node@1.0.0: {} - - balanced-match@1.0.2: {} - - base64-js@1.5.1: {} - - bl@4.1.0: - dependencies: - buffer: 5.7.1 - inherits: 2.0.4 - readable-stream: 3.6.2 - - bluebird-lst@1.0.9: - dependencies: - bluebird: 3.7.2 - - bluebird@3.7.2: {} - - boolean@3.2.0: - optional: true - - brace-expansion@1.1.12: - dependencies: - balanced-match: 1.0.2 - concat-map: 0.0.1 - - brace-expansion@2.0.2: - dependencies: - balanced-match: 1.0.2 - - buffer-crc32@0.2.13: {} - - buffer-equal@1.0.1: {} - - buffer-from@1.1.2: {} - - buffer@5.7.1: - dependencies: - base64-js: 1.5.1 - ieee754: 1.2.1 - - builder-util-runtime@9.2.4: - dependencies: - debug: 4.4.3 - sax: 1.4.4 - transitivePeerDependencies: - - supports-color - - builder-util@24.13.1: - dependencies: - 7zip-bin: 5.2.0 - '@types/debug': 4.1.12 - 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.6 - debug: 4.4.3 - fs-extra: 10.1.0 - http-proxy-agent: 5.0.0 - https-proxy-agent: 5.0.1 - is-ci: 3.0.1 - js-yaml: 4.1.1 - source-map-support: 0.5.21 - stat-mode: 1.0.0 - temp-file: 3.4.0 - transitivePeerDependencies: - - supports-color - - cacheable-lookup@5.0.4: {} - - cacheable-request@7.0.4: - dependencies: - clone-response: 1.0.3 - get-stream: 5.2.0 - http-cache-semantics: 4.2.0 - keyv: 4.5.4 - lowercase-keys: 2.0.0 - normalize-url: 6.1.0 - responselike: 2.0.1 - - call-bind-apply-helpers@1.0.2: - dependencies: - es-errors: 1.3.0 - function-bind: 1.1.2 - - chalk@4.1.2: - dependencies: - ansi-styles: 4.3.0 - supports-color: 7.2.0 - - chownr@2.0.0: {} - - chromium-pickle-js@0.2.0: {} - - ci-info@3.9.0: {} - - cli-truncate@2.1.0: - dependencies: - slice-ansi: 3.0.0 - string-width: 4.2.3 - optional: true - - cliui@8.0.1: - dependencies: - string-width: 4.2.3 - strip-ansi: 6.0.1 - wrap-ansi: 7.0.0 - - clone-response@1.0.3: - dependencies: - mimic-response: 1.0.1 - - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - combined-stream@1.0.8: - dependencies: - delayed-stream: 1.0.0 - - commander@5.1.0: {} - - compare-version@0.1.2: {} - - compress-commons@4.1.2: - dependencies: - buffer-crc32: 0.2.13 - crc32-stream: 4.0.3 - normalize-path: 3.0.0 - readable-stream: 3.6.2 - - concat-map@0.0.1: {} - - config-file-ts@0.2.6: - dependencies: - glob: 10.5.0 - typescript: 5.9.3 - - core-util-is@1.0.2: - optional: true - - core-util-is@1.0.3: {} - - crc-32@1.2.2: {} - - crc32-stream@4.0.3: - dependencies: - crc-32: 1.2.2 - readable-stream: 3.6.2 - - crc@3.8.0: - dependencies: - buffer: 5.7.1 - optional: true - - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - - debug@4.4.3: - dependencies: - ms: 2.1.3 - - decompress-response@6.0.0: - dependencies: - mimic-response: 3.1.0 - - defer-to-connect@2.0.1: {} - - define-data-property@1.1.4: - dependencies: - es-define-property: 1.0.1 - es-errors: 1.3.0 - gopd: 1.2.0 - optional: true - - define-properties@1.2.1: - dependencies: - define-data-property: 1.1.4 - has-property-descriptors: 1.0.2 - object-keys: 1.1.1 - optional: true - - delayed-stream@1.0.0: {} - - detect-node@2.1.0: - optional: true - - dir-compare@3.3.0: - dependencies: - buffer-equal: 1.0.1 - minimatch: 3.1.2 - - dmg-builder@24.13.3(electron-builder-squirrel-windows@24.13.3): - dependencies: - app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) - builder-util: 24.13.1 - builder-util-runtime: 9.2.4 - fs-extra: 10.1.0 - iconv-lite: 0.6.3 - js-yaml: 4.1.1 - optionalDependencies: - dmg-license: 1.0.11 - transitivePeerDependencies: - - electron-builder-squirrel-windows - - supports-color - - dmg-license@1.0.11: - dependencies: - '@types/plist': 3.0.5 - '@types/verror': 1.10.11 - ajv: 6.12.6 - crc: 3.8.0 - iconv-corefoundation: 1.1.7 - plist: 3.1.0 - smart-buffer: 4.2.0 - verror: 1.10.1 - optional: true - - dotenv-expand@5.1.0: {} - - dotenv@9.0.2: {} - - dunder-proto@1.0.1: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-errors: 1.3.0 - gopd: 1.2.0 - - eastasianwidth@0.2.0: {} - - ejs@3.1.10: - dependencies: - jake: 10.9.4 - - electron-builder-squirrel-windows@24.13.3(dmg-builder@24.13.3): - dependencies: - app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) - archiver: 5.3.2 - builder-util: 24.13.1 - fs-extra: 10.1.0 - transitivePeerDependencies: - - dmg-builder - - supports-color - - electron-builder@24.13.3(electron-builder-squirrel-windows@24.13.3): - dependencies: - app-builder-lib: 24.13.3(dmg-builder@24.13.3)(electron-builder-squirrel-windows@24.13.3) - builder-util: 24.13.1 - builder-util-runtime: 9.2.4 - chalk: 4.1.2 - dmg-builder: 24.13.3(electron-builder-squirrel-windows@24.13.3) - fs-extra: 10.1.0 - is-ci: 3.0.1 - lazy-val: 1.0.5 - read-config-file: 6.3.2 - simple-update-notifier: 2.0.0 - yargs: 17.7.2 - transitivePeerDependencies: - - electron-builder-squirrel-windows - - supports-color - - electron-publish@24.13.1: - dependencies: - '@types/fs-extra': 9.0.13 - 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.6.0 - transitivePeerDependencies: - - supports-color - - electron@40.3.0: - dependencies: - '@electron/get': 2.0.3 - '@types/node': 24.10.13 - extract-zip: 2.0.1 - transitivePeerDependencies: - - supports-color - - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - - end-of-stream@1.4.5: - dependencies: - once: 1.4.0 - - env-paths@2.2.1: {} - - err-code@2.0.3: {} - - es-define-property@1.0.1: {} - - es-errors@1.3.0: {} - - es-object-atoms@1.1.1: - dependencies: - es-errors: 1.3.0 - - es-set-tostringtag@2.1.0: - dependencies: - es-errors: 1.3.0 - get-intrinsic: 1.3.0 - has-tostringtag: 1.0.2 - hasown: 2.0.2 - - es6-error@4.1.1: - optional: true - - escalade@3.2.0: {} - - escape-string-regexp@4.0.0: - optional: true - - extract-zip@2.0.1: - dependencies: - debug: 4.4.3 - get-stream: 5.2.0 - yauzl: 2.10.0 - optionalDependencies: - '@types/yauzl': 2.10.3 - transitivePeerDependencies: - - supports-color - - extsprintf@1.4.1: - optional: true - - fast-deep-equal@3.1.3: {} - - fast-json-stable-stringify@2.1.0: {} - - fd-slicer@1.1.0: - dependencies: - pend: 1.2.0 - - filelist@1.0.4: - dependencies: - minimatch: 5.1.6 - - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - - form-data@4.0.5: - dependencies: - asynckit: 0.4.0 - combined-stream: 1.0.8 - es-set-tostringtag: 2.1.0 - hasown: 2.0.2 - mime-types: 2.1.35 - - fs-constants@1.0.0: {} - - fs-extra@10.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - - fs-extra@8.1.0: - dependencies: - graceful-fs: 4.2.11 - jsonfile: 4.0.0 - universalify: 0.1.2 - - fs-extra@9.1.0: - dependencies: - at-least-node: 1.0.0 - graceful-fs: 4.2.11 - jsonfile: 6.2.0 - universalify: 2.0.1 - - fs-minipass@2.1.0: - dependencies: - minipass: 3.3.6 - - fs.realpath@1.0.0: {} - - function-bind@1.1.2: {} - - get-caller-file@2.0.5: {} - - get-intrinsic@1.3.0: - dependencies: - call-bind-apply-helpers: 1.0.2 - es-define-property: 1.0.1 - es-errors: 1.3.0 - es-object-atoms: 1.1.1 - function-bind: 1.1.2 - get-proto: 1.0.1 - gopd: 1.2.0 - has-symbols: 1.1.0 - hasown: 2.0.2 - math-intrinsics: 1.1.0 - - get-proto@1.0.1: - dependencies: - dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 - - get-stream@5.2.0: - dependencies: - pump: 3.0.3 - - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.5 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - - glob@7.2.3: - dependencies: - fs.realpath: 1.0.0 - inflight: 1.0.6 - inherits: 2.0.4 - minimatch: 3.1.2 - once: 1.4.0 - path-is-absolute: 1.0.1 - - global-agent@3.0.0: - dependencies: - boolean: 3.2.0 - es6-error: 4.1.1 - matcher: 3.0.0 - roarr: 2.15.4 - semver: 7.7.4 - serialize-error: 7.0.1 - optional: true - - globalthis@1.0.4: - dependencies: - define-properties: 1.2.1 - gopd: 1.2.0 - optional: true - - gopd@1.2.0: {} - - got@11.8.6: - dependencies: - '@sindresorhus/is': 4.6.0 - '@szmarczak/http-timer': 4.0.6 - '@types/cacheable-request': 6.0.3 - '@types/responselike': 1.0.3 - cacheable-lookup: 5.0.4 - cacheable-request: 7.0.4 - decompress-response: 6.0.0 - http2-wrapper: 1.0.3 - lowercase-keys: 2.0.0 - p-cancelable: 2.1.1 - responselike: 2.0.1 - - graceful-fs@4.2.11: {} - - has-flag@4.0.0: {} - - has-property-descriptors@1.0.2: - dependencies: - es-define-property: 1.0.1 - optional: true - - has-symbols@1.1.0: {} - - has-tostringtag@1.0.2: - dependencies: - has-symbols: 1.1.0 - - hasown@2.0.2: - dependencies: - function-bind: 1.1.2 - - hosted-git-info@4.1.0: - dependencies: - lru-cache: 6.0.0 - - http-cache-semantics@4.2.0: {} - - http-proxy-agent@5.0.0: - dependencies: - '@tootallnate/once': 2.0.0 - agent-base: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - http2-wrapper@1.0.3: - dependencies: - quick-lru: 5.1.1 - resolve-alpn: 1.2.1 - - https-proxy-agent@5.0.1: - dependencies: - agent-base: 6.0.2 - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - iconv-corefoundation@1.1.7: - dependencies: - cli-truncate: 2.1.0 - node-addon-api: 1.7.2 - optional: true - - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - - ieee754@1.2.1: {} - - inflight@1.0.6: - dependencies: - once: 1.4.0 - wrappy: 1.0.2 - - inherits@2.0.4: {} - - is-ci@3.0.1: - dependencies: - ci-info: 3.9.0 - - is-fullwidth-code-point@3.0.0: {} - - isarray@1.0.0: {} - - isbinaryfile@4.0.10: {} - - isbinaryfile@5.0.7: {} - - isexe@2.0.0: {} - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - - jake@10.9.4: - dependencies: - async: 3.2.6 - filelist: 1.0.4 - picocolors: 1.1.1 - - js-yaml@4.1.1: - dependencies: - argparse: 2.0.1 - - json-buffer@3.0.1: {} - - json-schema-traverse@0.4.1: {} - - json-stringify-safe@5.0.1: - optional: true - - json5@2.2.3: {} - - jsonfile@4.0.0: - optionalDependencies: - graceful-fs: 4.2.11 - - jsonfile@6.2.0: - dependencies: - universalify: 2.0.1 - optionalDependencies: - graceful-fs: 4.2.11 - - keyv@4.5.4: - dependencies: - json-buffer: 3.0.1 - - lazy-val@1.0.5: {} - - lazystream@1.0.1: - dependencies: - readable-stream: 2.3.8 - - 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: {} - - lodash@4.17.23: {} - - lowercase-keys@2.0.0: {} - - lru-cache@10.4.3: {} - - lru-cache@6.0.0: - dependencies: - yallist: 4.0.0 - - matcher@3.0.0: - dependencies: - escape-string-regexp: 4.0.0 - optional: true - - math-intrinsics@1.1.0: {} - - mime-db@1.52.0: {} - - mime-types@2.1.35: - dependencies: - mime-db: 1.52.0 - - mime@2.6.0: {} - - mimic-response@1.0.1: {} - - mimic-response@3.1.0: {} - - minimatch@3.1.2: - dependencies: - brace-expansion: 1.1.12 - - minimatch@5.1.6: - dependencies: - brace-expansion: 2.0.2 - - minimatch@9.0.5: - dependencies: - brace-expansion: 2.0.2 - - minimist@1.2.8: {} - - minipass@3.3.6: - dependencies: - yallist: 4.0.0 - - minipass@5.0.0: {} - - minipass@7.1.2: {} - - minizlib@2.1.2: - dependencies: - minipass: 3.3.6 - yallist: 4.0.0 - - mkdirp@1.0.4: {} - - ms@2.1.3: {} - - node-addon-api@1.7.2: - optional: true - - normalize-path@3.0.0: {} - - normalize-url@6.1.0: {} - - object-keys@1.1.1: - optional: true - - once@1.4.0: - dependencies: - wrappy: 1.0.2 - - p-cancelable@2.1.1: {} - - package-json-from-dist@1.0.1: {} - - path-is-absolute@1.0.1: {} - - path-key@3.1.1: {} - - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - - pend@1.2.0: {} - - picocolors@1.1.1: {} - - plist@3.1.0: - dependencies: - '@xmldom/xmldom': 0.8.11 - base64-js: 1.5.1 - xmlbuilder: 15.1.1 - - process-nextick-args@2.0.1: {} - - progress@2.0.3: {} - - promise-retry@2.0.1: - dependencies: - err-code: 2.0.3 - retry: 0.12.0 - - pump@3.0.3: - dependencies: - end-of-stream: 1.4.5 - once: 1.4.0 - - punycode@2.3.1: {} - - quick-lru@5.1.1: {} - - read-config-file@6.3.2: - dependencies: - config-file-ts: 0.2.6 - dotenv: 9.0.2 - dotenv-expand: 5.1.0 - js-yaml: 4.1.1 - json5: 2.2.3 - lazy-val: 1.0.5 - - readable-stream@2.3.8: - dependencies: - core-util-is: 1.0.3 - inherits: 2.0.4 - isarray: 1.0.0 - process-nextick-args: 2.0.1 - safe-buffer: 5.1.2 - string_decoder: 1.1.1 - util-deprecate: 1.0.2 - - readable-stream@3.6.2: - dependencies: - inherits: 2.0.4 - string_decoder: 1.3.0 - util-deprecate: 1.0.2 - - readdir-glob@1.1.3: - dependencies: - minimatch: 5.1.6 - - require-directory@2.1.1: {} - - resolve-alpn@1.2.1: {} - - responselike@2.0.1: - dependencies: - lowercase-keys: 2.0.0 - - retry@0.12.0: {} - - roarr@2.15.4: - dependencies: - boolean: 3.2.0 - detect-node: 2.1.0 - globalthis: 1.0.4 - json-stringify-safe: 5.0.1 - semver-compare: 1.0.0 - sprintf-js: 1.1.3 - optional: true - - safe-buffer@5.1.2: {} - - safe-buffer@5.2.1: {} - - safer-buffer@2.1.2: {} - - sanitize-filename@1.6.3: - dependencies: - truncate-utf8-bytes: 1.0.2 - - sax@1.4.4: {} - - semver-compare@1.0.0: - optional: true - - semver@6.3.1: {} - - semver@7.7.4: {} - - serialize-error@7.0.1: - dependencies: - type-fest: 0.13.1 - optional: true - - shebang-command@2.0.0: - dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - signal-exit@4.1.0: {} - - simple-update-notifier@2.0.0: - dependencies: - semver: 7.7.4 - - slice-ansi@3.0.0: - dependencies: - ansi-styles: 4.3.0 - astral-regex: 2.0.0 - is-fullwidth-code-point: 3.0.0 - optional: true - - smart-buffer@4.2.0: - optional: true - - source-map-support@0.5.21: - dependencies: - buffer-from: 1.1.2 - source-map: 0.6.1 - - source-map@0.6.1: {} - - sprintf-js@1.1.3: - optional: true - - stat-mode@1.0.0: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.2 - - string_decoder@1.1.1: - dependencies: - safe-buffer: 5.1.2 - - string_decoder@1.3.0: - dependencies: - safe-buffer: 5.2.1 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.2: - dependencies: - ansi-regex: 6.2.2 - - sumchecker@3.0.1: - dependencies: - debug: 4.4.3 - transitivePeerDependencies: - - supports-color - - supports-color@7.2.0: - dependencies: - has-flag: 4.0.0 - - tar-stream@2.2.0: - dependencies: - bl: 4.1.0 - end-of-stream: 1.4.5 - fs-constants: 1.0.0 - inherits: 2.0.4 - readable-stream: 3.6.2 - - tar@6.2.1: - dependencies: - chownr: 2.0.0 - fs-minipass: 2.1.0 - minipass: 5.0.0 - minizlib: 2.1.2 - mkdirp: 1.0.4 - yallist: 4.0.0 - - temp-file@3.4.0: - dependencies: - async-exit-hook: 2.0.1 - fs-extra: 10.1.0 - - tmp-promise@3.0.3: - dependencies: - tmp: 0.2.5 - - tmp@0.2.5: {} - - truncate-utf8-bytes@1.0.2: - dependencies: - utf8-byte-length: 1.0.5 - - type-fest@0.13.1: - optional: true - - typescript@5.9.3: {} - - undici-types@7.16.0: {} - - universalify@0.1.2: {} - - universalify@2.0.1: {} - - uri-js@4.4.1: - dependencies: - punycode: 2.3.1 - - utf8-byte-length@1.0.5: {} - - util-deprecate@1.0.2: {} - - verror@1.10.1: - dependencies: - assert-plus: 1.0.0 - core-util-is: 1.0.2 - extsprintf: 1.4.1 - optional: true - - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.3 - string-width: 5.1.2 - strip-ansi: 7.1.2 - - wrappy@1.0.2: {} - - xmlbuilder@15.1.1: {} - - y18n@5.0.8: {} - - yallist@4.0.0: {} - - yargs-parser@21.1.1: {} - - yargs@17.7.2: - dependencies: - cliui: 8.0.1 - escalade: 3.2.0 - get-caller-file: 2.0.5 - require-directory: 2.1.1 - string-width: 4.2.3 - y18n: 5.0.8 - yargs-parser: 21.1.1 - - yauzl@2.10.0: - dependencies: - buffer-crc32: 0.2.13 - fd-slicer: 1.1.0 - - zip-stream@4.1.1: - dependencies: - archiver-utils: 3.0.4 - compress-commons: 4.1.2 - readable-stream: 3.6.2 diff --git a/desktop/preload.js b/desktop/preload.js deleted file mode 100644 index 7d699a21f..000000000 --- a/desktop/preload.js +++ /dev/null @@ -1,22 +0,0 @@ -'use strict'; - -const { contextBridge, ipcRenderer } = require('electron'); - -contextBridge.exposeInMainWorld('astrbotDesktop', { - isElectron: true, - isElectronRuntime: () => ipcRenderer.invoke('astrbot-desktop:is-electron-runtime'), - getBackendState: () => ipcRenderer.invoke('astrbot-desktop:get-backend-state'), - restartBackend: (authToken) => - ipcRenderer.invoke('astrbot-desktop:restart-backend', authToken), - stopBackend: () => ipcRenderer.invoke('astrbot-desktop:stop-backend'), - onTrayRestartBackend: (callback) => { - const listener = () => { - if (typeof callback === 'function') { - callback(); - } - }; - ipcRenderer.on('astrbot-desktop:tray-restart-backend', listener); - return () => - ipcRenderer.removeListener('astrbot-desktop:tray-restart-backend', listener); - }, -}); diff --git a/desktop/scripts/build-backend.mjs b/desktop/scripts/build-backend.mjs deleted file mode 100644 index 921cf19cb..000000000 --- a/desktop/scripts/build-backend.mjs +++ /dev/null @@ -1,86 +0,0 @@ -import { spawnSync } from 'node:child_process'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const rootDir = path.resolve(__dirname, '..', '..'); -const outputDir = path.join(rootDir, 'desktop', 'resources', 'backend'); -const workDir = path.join(rootDir, 'desktop', 'resources', '.pyinstaller'); -const dataSeparator = process.platform === 'win32' ? ';' : ':'; -const kbStopwordsSrc = path.join( - rootDir, - 'astrbot', - 'core', - 'knowledge_base', - 'retrieval', - 'hit_stopwords.txt', -); -const kbStopwordsDest = 'astrbot/core/knowledge_base/retrieval'; -const builtinStarsSrc = path.join(rootDir, 'astrbot', 'builtin_stars'); -const builtinStarsDest = 'astrbot/builtin_stars'; - -const args = [ - 'run', - '--with', - 'pyinstaller', - 'python', - '-m', - 'PyInstaller', - '--noconfirm', - '--clean', - '--onefile', - '--name', - 'astrbot-backend', - '--collect-all', - 'aiosqlite', - '--collect-all', - 'pip', - '--collect-all', - 'bs4', - '--collect-all', - 'readability', - '--collect-all', - 'lxml', - '--collect-all', - 'lxml_html_clean', - '--collect-all', - 'rfc3987_syntax', - '--collect-submodules', - 'astrbot.api', - '--collect-submodules', - 'astrbot.builtin_stars', - '--collect-data', - 'certifi', - '--add-data', - `${builtinStarsSrc}${dataSeparator}${builtinStarsDest}`, - '--add-data', - `${kbStopwordsSrc}${dataSeparator}${kbStopwordsDest}`, - '--distpath', - outputDir, - '--workpath', - workDir, - '--specpath', - workDir, - path.join(rootDir, 'main.py'), -]; - -const result = spawnSync('uv', args, { - cwd: rootDir, - stdio: 'inherit', - shell: process.platform === 'win32', -}); - -if (result.error) { - console.error(`Failed to run 'uv': ${result.error.message}`); - process.exit(typeof result.status === 'number' ? result.status : 1); -} - -if (result.status !== 0) { - console.error( - `'uv' exited with status ${result.status} while running PyInstaller. ` + - 'Verify that uv and pyinstaller are installed and that arguments are valid.', - ); - process.exit(result.status ?? 1); -} - -process.exit(0); diff --git a/desktop/scripts/prepare-webui.mjs b/desktop/scripts/prepare-webui.mjs deleted file mode 100644 index 404ae7ef9..000000000 --- a/desktop/scripts/prepare-webui.mjs +++ /dev/null @@ -1,20 +0,0 @@ -import { cp, mkdir, rm } from 'node:fs/promises'; -import { existsSync } from 'node:fs'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const rootDir = path.resolve(__dirname, '..', '..'); -const distDir = path.join(rootDir, 'dashboard', 'dist'); -const targetDir = path.join(rootDir, 'desktop', 'resources', 'webui'); - -if (!existsSync(distDir)) { - console.error('dashboard/dist is missing. Run `pnpm --dir dashboard build` first.'); - process.exit(1); -} - -await rm(targetDir, { recursive: true, force: true }); -await mkdir(targetDir, { recursive: true }); -await cp(distDir, targetDir, { recursive: true }); - -console.log(`Copied WebUI to ${targetDir}`); diff --git a/desktop/scripts/sync-version.mjs b/desktop/scripts/sync-version.mjs deleted file mode 100644 index 08651d75a..000000000 --- a/desktop/scripts/sync-version.mjs +++ /dev/null @@ -1,66 +0,0 @@ -import { readFile, writeFile } from 'node:fs/promises'; -import { spawnSync } from 'node:child_process'; -import path from 'node:path'; -import { fileURLToPath } from 'node:url'; - -const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const rootDir = path.resolve(__dirname, '..', '..'); -const desktopPackagePath = path.join(rootDir, 'desktop', 'package.json'); -const pyprojectPath = path.join(rootDir, 'pyproject.toml'); - -function getGitTag() { - const result = spawnSync('git', ['describe', '--tags', '--abbrev=0'], { - cwd: rootDir, - encoding: 'utf8', - }); - if (result.status === 0) { - const tag = result.stdout.trim(); - return tag.length ? tag : null; - } - return null; -} - -function normalizeTag(tag) { - return tag.replace(/^v/i, ''); -} - -async function getPyprojectVersion() { - try { - const data = await readFile(pyprojectPath, 'utf8'); - const match = data.match(/^\s*version\s*=\s*"([^"]+)"/m); - return match ? match[1] : null; - } catch { - return null; - } -} - -const pkgRaw = await readFile(desktopPackagePath, 'utf8'); -const pkg = JSON.parse(pkgRaw); -const tag = getGitTag(); -const versionFromTag = tag ? normalizeTag(tag) : null; -const versionFromPyproject = await getPyprojectVersion(); -const version = versionFromPyproject || versionFromTag || pkg.version; - -if ( - versionFromPyproject && - versionFromTag && - versionFromPyproject !== versionFromTag -) { - console.log( - `Using pyproject version ${versionFromPyproject} (ignoring git tag ${versionFromTag}).`, - ); -} - -if (!version) { - console.warn('No version found to sync.'); - process.exit(0); -} - -if (pkg.version === version) { - console.log(`Desktop version already ${version}`); - process.exit(0); -} - -pkg.version = version; -await writeFile(desktopPackagePath, `${JSON.stringify(pkg, null, 2)}\n`, 'utf8'); -console.log(`Updated desktop version to ${version}`); diff --git a/main.py b/main.py index be188140c..36c46fca3 100644 --- a/main.py +++ b/main.py @@ -15,6 +15,7 @@ from astrbot.core.initial_loader import InitialLoader # noqa: E402 from astrbot.core.utils.astrbot_path import ( # noqa: E402 get_astrbot_config_path, get_astrbot_data_path, + get_astrbot_knowledge_base_path, get_astrbot_plugin_path, get_astrbot_root, get_astrbot_site_packages_path, @@ -55,6 +56,7 @@ def check_env() -> None: os.makedirs(get_astrbot_config_path(), exist_ok=True) os.makedirs(get_astrbot_plugin_path(), exist_ok=True) os.makedirs(get_astrbot_temp_path(), exist_ok=True) + os.makedirs(get_astrbot_knowledge_base_path(), exist_ok=True) os.makedirs(site_packages_path, exist_ok=True) # 针对问题 #181 的临时解决方案