diff --git a/.gitignore b/.gitignore index 4da81b08..47256d70 100644 --- a/.gitignore +++ b/.gitignore @@ -3,4 +3,12 @@ .env backend/data/** !backend/data/outputs/ -!backend/data/outputs/*.json \ No newline at end of file +!backend/data/outputs/*.json + +# Electron build artifacts +electron/dist/ +electron/python-env/ +electron/node_modules/ + +# Frontend build output +frontend/dist/ \ No newline at end of file diff --git a/LICENSE b/LICENSE index 6eb33b2d..fde97d56 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2026 ClusterLabs +Copyright (c) 2026 Haik Decie Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 0904df32..2bac56bb 100644 --- a/README.md +++ b/README.md @@ -1,104 +1,242 @@ -# Open Swarm — Agent Orchestrator +

+ Open Swarm +

-A locally-running React + FastAPI application for managing multiple Claude Code instances in parallel. Designed for power users who run multiple agents simultaneously and need a unified interface to monitor, control, and coordinate them. +

Open Swarm

+ +

+ An Army of AI Agents at Your Fingertips +
+ A locally-running orchestrator for managing multiple Claude Code instances in parallel. +
+ Launch, monitor, and coordinate entire swarms of coding agents from a single interface. +

+ +

+ License + Platform + GitHub Stars + PRs Welcome +

+ +
+ +

+ Open Swarm Dashboard +

+ +
+ +## Why Open Swarm? + +Running Claude Code in a terminal works fine for one task. But when you're juggling five agents across different branches, approving tool calls in separate windows, and losing track of who's doing what — it falls apart fast. + +- **Parallel agents, one screen** — Launch as many Claude Code instances as you need, arranged on a spatial canvas you can pan and zoom freely +- **Unified approval workflow** — Every tool-use request from every agent surfaces in one place. Approve or deny with a click or a keyboard shortcut. +- **Full conversation control** — Edit prior messages to fork conversations, navigate between branches, resume closed sessions +- **100% local** — Everything runs on your machine. No cloud relay, no telemetry, no third-party backend. + +
## Features -- **Multi-agent management** — Launch and monitor multiple Claude Code instances side by side -- **Git worktree isolation** — Each agent works on its own git worktree/branch to avoid conflicts -- **Real-time streaming** — WebSocket-based streaming of agent messages and status updates -- **HITL approvals** — Approve or deny tool usage requests from the dashboard or within each chat -- **Message branching** — Edit prior messages to fork conversations, navigate between branches -- **Prompt template library** — Reusable prompt templates with structured input fields, invoked via `/` commands -- **Skills library** — Manage skills synced to the native `~/.claude/skills/` directory -- **Tools library** — Define custom tool configurations (bash, MCP, Python) -- **Keyboard shortcuts** — Navigate between agents and approve/deny requests without a mouse -- **Diff viewer** — View uncommitted changes in each agent's worktree +**Spatial Dashboard** — Infinite canvas with drag-and-drop agent cards, view cards, and embedded browser cards. Create multiple dashboards for different workspaces. + +**Agent Chat** — Full streaming chat interface powered by WebSockets. Real-time token output, cost tracking per session, and persistent history that survives restarts. + +**Human-in-the-Loop Approvals** — Agents request permission before executing tools. Approve or deny individually, or batch-approve from the dashboard. Configurable per-tool permissions (always allow, ask, deny). + +**Message Branching** — Edit any prior message to fork the conversation. Navigate freely between branches without losing context. + +**Prompt Templates** — Build reusable templates with structured input fields. Invoke them inline via `/` slash commands. + +**Skills Library** — Manage skills that sync directly to `~/.claude/skills/`. Browse and install from the official Anthropic skills marketplace. + +**Tools Library** — Configure MCP tool servers (stdio, HTTP, SSE) with automatic tool discovery. Browse the MCP registry and Google's catalog with GitHub star counts. Includes Google Workspace OAuth integration. + +**Agent Modes** — Five built-in modes (Agent, Ask, Plan, View Builder, Skill Builder) plus custom user-defined modes with configurable system prompts and tool restrictions. + +**Views & Outputs** — Create interactive HTML/JS/CSS artifacts rendered in iframes. Supports vibe coding (LLM-generates the view), backend Python execution, auto-run with LLM-generated data, and agent-driven data gathering. + +**Git Worktree Isolation** — Each agent operates in its own git worktree and branch, preventing conflicts between parallel workstreams. + +**Diff Viewer** — Inspect uncommitted changes in any agent's worktree without leaving the app. + +**Cost Tracking** — Real-time USD spend tracking per agent session. + +**Dark & Light Themes** — Full theme support with Claude-inspired design tokens. + +**Keyboard Shortcuts** — Navigate between agents, approve/deny requests, and switch pages without touching a mouse. + +
+ +## Quick Start + +### Desktop App + +Download the latest release for macOS from [GitHub Releases](https://github.com/openswarm-ai/openswarm/releases). + +> Windows and Linux builds are planned but not yet available. + +### Development Setup + +**Prerequisites:** Python 3.11+, Node.js 18+, Git + +```bash +git clone https://github.com/openswarm-ai/openswarm.git +cd openswarm +bash run.sh +``` + +This starts the backend (port 8324), frontend (port 3000), and Electron shell together. Once running, set your Anthropic API key in the in-app Settings page. + +To run services individually: + +```bash +bash backend/run.sh # API at http://localhost:8324 — docs at /docs +bash frontend/run.sh # App at http://localhost:3000 +``` + +
## Architecture ``` -Frontend (React/TypeScript :3000) Backend (FastAPI/Python :8324) -┌─────────────────────────────┐ ┌──────────────────────────────┐ -│ Dashboard │◄────►│ REST API (/api/*) │ -│ Agent Chat (per session) │ │ WebSocket (/ws/*) │ -│ Templates / Skills / Tools │ │ Agent Manager │ -│ Slash Command Picker │ │ └─ Claude Agent SDK │ -│ Keyboard Shortcuts │ │ Worktree Manager │ -│ Diff Viewer │ │ Library Storage (JSON/files)│ -└─────────────────────────────┘ └──────────────────────────────┘ +Electron Shell (desktop wrapper, auto-updater) +├─────────────────────────────────────────────────────────────────────┐ +│ │ +│ Frontend (React/TypeScript :3000) Backend (FastAPI :8324) │ +│ ┌───────────────────────────────┐ ┌───────────────────────┐ │ +│ │ Spatial Dashboard Canvas │◄────►│ REST API (/api/*) │ │ +│ │ Agent Chat (streaming) │ │ WebSocket (/ws/*) │ │ +│ │ Templates / Skills / Tools │ WS │ Agent Manager │ │ +│ │ Modes / Views / Commands │◄────►│ └─ claude-agent-sdk│ │ +│ │ Settings │ │ MCP Tool Discovery │ │ +│ │ Redux Toolkit (state) │ │ JSON File Storage │ │ +│ └───────────────────────────────┘ └───────────────────────┘ │ +│ │ +└─────────────────────────────────────────────────────────────────────┘ ``` -## Quick Start +
-### Prerequisites +## Configuration -- Python 3.11+ -- Node.js 18+ -- Git -- An Anthropic API key (for real agent usage) — `export ANTHROPIC_API_KEY=...` +The Anthropic API key is configured in-app via the **Settings** page — no environment variable needed for normal usage. -### Backend +For advanced configuration, copy `backend/.env.example` to `backend/.env`: -```bash -bash backend/run/dev.sh -# API runs at http://localhost:8324 -# Docs at http://localhost:8324/docs -``` +| Variable | Purpose | +|----------|---------| +| `BACKEND_PORT` | Backend server port (default: `8324`) | +| `GOOGLE_OAUTH_CLIENT_ID` | Google Workspace integration (Gmail, Calendar, Drive) | +| `GOOGLE_OAUTH_CLIENT_SECRET` | Google Workspace integration | +| `APPLE_ID` | macOS code signing & notarization (release builds only) | +| `APPLE_APP_SPECIFIC_PASSWORD` | macOS notarization (release builds only) | +| `APPLE_TEAM_ID` | macOS code signing (release builds only) | +| `GH_TOKEN` | GitHub Releases publishing (release builds only) | -### Frontend - -```bash -bash frontend/run/dev.sh -# App runs at http://localhost:3000 -``` - -### Mock Mode - -If `claude-agent-sdk` is not installed, the backend runs in **mock mode** — agents simulate tool calls and responses so you can develop and test the UI without an API key. +
## Keyboard Shortcuts | Key | Action | |-----|--------| -| `d` | Go to Dashboard | -| `t` | Go to Templates | -| `1`–`9` | Open agent by position | +| `D` | Go to Dashboard | +| `T` | Go to Templates | +| `1` – `9` | Open agent by position | | `Shift+A` | Approve all pending requests | | `Shift+D` | Deny all pending requests | | `?` | Show shortcuts help | -## Slash Commands +Type `/` in the chat input to invoke prompt templates and skills as slash commands. -In the chat input, type `/` to invoke templates and skills: -- `/template-name` — Opens the template's input modal -- `/skill-name` — Inserts the skill content into the message +
## Project Structure ``` backend/ apps/ - agents/ — Agent lifecycle, WebSocket, worktree management - templates/ — Prompt template CRUD (JSON file storage) - skills/ — Skills CRUD (synced to ~/.claude/skills/) - tools_lib/ — Tool definitions CRUD (JSON file storage) - health/ — Health check endpoint - config/ — FastAPI app configuration - data/ — Persistent JSON file storage (sessions, dashboards, settings, templates, tools, etc.) + agents/ Agent lifecycle, streaming, worktree management + dashboards/ Dashboard CRUD and layout persistence + dashboard_layout/ Card positions and spatial canvas state + templates/ Prompt template CRUD + skills/ Skills CRUD (synced to ~/.claude/skills/) + tools_lib/ MCP tool configuration and discovery + modes/ Agent mode definitions + outputs/ Views/outputs, vibe coding, Python executor + settings/ App settings and file browser + health/ Health check endpoint + mcp_registry/ MCP server registry proxy + skill_registry/ Anthropic skills marketplace proxy + config/ FastAPI app configuration + data/ Persistent JSON file storage frontend/ src/ app/ - components/ — AppShell, NewAgentModal, SlashCommandPicker, KeyboardShortcutsHelp + components/ AppShell, Layout, shared UI pages/ - Dashboard/ — Agent overview grid with live status - AgentChat/ — Full chat UI with streaming, HITL, branching, diff viewer - Templates/ — Template library with editor - Skills/ — Skills library with editor - Tools/ — Tools library with editor + Dashboard/ Spatial canvas with agent/view/browser cards + AgentChat/ Streaming chat, HITL approvals, branching, diff viewer + Templates/ Template library with structured input fields + Skills/ Skills library, skill builder, registry browser + Tools/ Tool config, MCP discovery, OAuth, registry browser + Modes/ Mode definitions with system prompts + Views/ Output artifacts, code editor, vibe coding + Commands/ Keyboard shortcuts reference + Settings/ App configuration shared/ - state/ — Redux slices (agents, templates, skills, tools) - ws/ — WebSocket manager - hooks/ — Custom hooks (keyboard shortcuts) + state/ Redux slices (agents, dashboards, templates, skills, tools, modes, etc.) + ws/ WebSocket manager + hooks/ Custom hooks + styles/ Theme tokens, global styles + +electron/ + main.js Electron main process, auto-updater, Python env management + scripts/ Build and notarization scripts + +scripts/ + build-app.sh Desktop app packaging (electron-builder) + build-python-env.sh Standalone Python 3.13 environment bundler ``` + +
+ +## Tech Stack + +**Frontend** — React 18, TypeScript, Redux Toolkit, Material UI v7, CodeMirror 6, Framer Motion, React Router v7, Webpack 5 + +**Backend** — FastAPI, Python 3.11+, Pydantic v2, claude-agent-sdk, Anthropic SDK, WebSockets, httpx + +**Desktop** — Electron 33, electron-builder, electron-updater (auto-updates via GitHub Releases) + +**Bundled Runtime** — Standalone Python 3.13 (via python-build-standalone) so end users don't need Python installed + +
+ +## Contributing + +Contributions are welcome. To get started: + +1. Fork the repository +2. Create a feature branch (`git checkout -b feature/your-feature`) +3. Make your changes +4. Submit a pull request + +Please open an issue first for larger changes so we can discuss the approach. + +
+ +## Community + +- [Twitter / X](https://twitter.com/openswarm_ai) +- [Discord](https://discord.gg/openswarm) +- [Website](https://openswarm.ai) + +
+ +## License + +MIT — see [LICENSE](LICENSE) for details. diff --git a/assets/icon.png b/assets/icon.png new file mode 100644 index 00000000..8b9f8611 Binary files /dev/null and b/assets/icon.png differ diff --git a/assets/screenshot.png b/assets/screenshot.png new file mode 100644 index 00000000..4448cbe7 Binary files /dev/null and b/assets/screenshot.png differ diff --git a/backend/.env.example b/backend/.env.example index 4d293e35..ded015af 100644 --- a/backend/.env.example +++ b/backend/.env.example @@ -3,6 +3,17 @@ # ============================================================================= BACKEND_PORT=8324 - GOOGLE_OAUTH_CLIENT_ID=your-google-oauth-client-id.apps.googleusercontent.com GOOGLE_OAUTH_CLIENT_SECRET=your-google-oauth-client-secret + +# ============================================================================= +# macOS Code Signing & Notarization (required for ./scripts/build-app.sh --publish) +# ============================================================================= +APPLE_ID=your-apple-id@example.com +APPLE_APP_SPECIFIC_PASSWORD=xxxx-xxxx-xxxx-xxxx +APPLE_TEAM_ID=ABCDE12345 + +# ============================================================================= +# GitHub Releases (required for --publish) +# ============================================================================= +GH_TOKEN=ghp_your-github-personal-access-token diff --git a/backend/ENV_README.md b/backend/ENV_README.md new file mode 100644 index 00000000..ec04f287 --- /dev/null +++ b/backend/ENV_README.md @@ -0,0 +1,227 @@ +# Environment Variables Setup Guide + +Copy `.env.example` to `.env` and fill in the values below. This guide walks you through getting every single one. + +```bash +cp .env.example .env +``` + +--- + +## `BACKEND_PORT` + +The port the backend server runs on. The default is fine — only change it if something else is already using port 8324. + +``` +BACKEND_PORT=8324 +``` + +--- + +## `GOOGLE_OAUTH_CLIENT_ID` & `GOOGLE_OAUTH_CLIENT_SECRET` + +These let users sign in with their Google account. + +### Step 1 — Go to Google Cloud Console + +1. Open https://console.cloud.google.com/ +2. Sign in with your Google account (or create one). + +### Step 2 — Create a project + +1. Click the project dropdown at the very top of the page (it says "Select a project" or shows your current project name). +2. Click **New Project** in the top-right of the popup. +3. Name it something like `OpenSwarm`. +4. Click **Create**. +5. Wait a few seconds, then click the project dropdown again and select your new `OpenSwarm` project. + +### Step 3 — Enable the Google+ API (required for OAuth) + +1. In the left sidebar, click **APIs & Services** > **Library**. +2. Search for `Google+ API` (or `Google Identity`). +3. Click on it, then click **Enable**. + +### Step 4 — Configure the OAuth consent screen + +1. In the left sidebar, click **APIs & Services** > **OAuth consent screen**. +2. Select **External** (unless you're inside a Google Workspace org and only want internal users). +3. Click **Create**. +4. Fill in the required fields: + - **App name**: `OpenSwarm` + - **User support email**: your email + - **Developer contact email**: your email +5. Click **Save and Continue**. +6. On the **Scopes** page, click **Add or Remove Scopes**. + - Check `email` and `profile` (the `openid` scope is added automatically). + - Click **Update**, then **Save and Continue**. +7. On the **Test users** page, click **Add Users**, enter your own email, click **Add**, then **Save and Continue**. +8. Click **Back to Dashboard**. + +### Step 5 — Create OAuth credentials + +1. In the left sidebar, click **APIs & Services** > **Credentials**. +2. Click **+ Create Credentials** at the top. +3. Select **OAuth client ID**. +4. For **Application type**, select **Web application**. +5. **Name**: `OpenSwarm` (or anything you want). +6. Under **Authorized redirect URIs**, click **+ Add URI** and add: + ``` + http://localhost:8324/api/auth/google/callback + ``` + (Replace `8324` with your `BACKEND_PORT` if you changed it.) +7. Click **Create**. + +### Step 6 — Copy the values + +A popup appears with your credentials: + +- **Client ID** — copy this into `GOOGLE_OAUTH_CLIENT_ID` +- **Client Secret** — copy this into `GOOGLE_OAUTH_CLIENT_SECRET` + +``` +GOOGLE_OAUTH_CLIENT_ID=123456789-xxxxxxxxx.apps.googleusercontent.com +GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxx +``` + +--- + +## `APPLE_ID` + +This is the email address you use to sign in to your Apple Developer account. + +1. If you don't have one, go to https://developer.apple.com/programs/ and click **Enroll**. It costs $99/year. +2. Once enrolled, your `APPLE_ID` is just the email you signed up with. + +``` +APPLE_ID=you@example.com +``` + +--- + +## `APPLE_TEAM_ID` + +Your 10-character Apple Developer team identifier. + +1. Go to https://developer.apple.com/account +2. Sign in. +3. Look at the top-right — your name is shown. Click it, or scroll down. +4. Under **Membership Details** (or at https://developer.apple.com/account#MembershipDetailsCard), you'll see **Team ID**. +5. It looks like `ABCDE12345`. Copy it. + +``` +APPLE_TEAM_ID=ABCDE12345 +``` + +--- + +## `APPLE_APP_SPECIFIC_PASSWORD` + +Apple doesn't let you use your regular password for automated tools. You need to generate a special one-time password. + +### Step 1 — Turn on two-factor authentication (if you haven't already) + +1. On your Mac, go to **System Settings** > **[your name]** > **Sign-In & Security** > **Two-Factor Authentication**. +2. Turn it on and follow the prompts. + +### Step 2 — Generate the app-specific password + +1. Go to https://account.apple.com/ +2. Sign in with your Apple ID. +3. In the **Sign-In and Security** section, click **App-Specific Passwords**. +4. Click **Generate an app-specific password** (or the **+** button). +5. Enter a label like `OpenSwarm Notarization`. +6. Click **Create**. +7. Apple shows you a password in the format `xxxx-xxxx-xxxx-xxxx`. **Copy it now** — you can't see it again. + +``` +APPLE_APP_SPECIFIC_PASSWORD=abcd-efgh-ijkl-mnop +``` + +--- + +## macOS Signing Certificate (no env var, but required) + +Before you can sign and notarize, you need a **Developer ID Application** certificate installed in your macOS Keychain. This is what Apple uses to verify that the app was built by you. + +### Step 1 — Open Xcode + +1. Open **Xcode** on your Mac (install it from the Mac App Store if you don't have it). +2. Go to **Xcode** menu > **Settings** (or **Preferences** on older versions). +3. Click the **Accounts** tab. +4. Click **+** in the bottom-left and sign in with your Apple ID. + +### Step 2 — Create the certificate + +1. Select your account in the list, then click **Manage Certificates...** in the bottom-right. +2. Click the **+** in the bottom-left of the popup. +3. Select **Developer ID Application**. +4. Click **Create**. + +That's it — the certificate is now in your macOS Keychain. `electron-builder` will auto-discover it during builds. You don't need to set any env var for this. + +### Alternative — manual method (without Xcode) + +1. Go to https://developer.apple.com/account/resources/certificates/list +2. Click the **+** button. +3. Select **Developer ID Application**, click **Continue**. +4. You'll be asked to upload a **Certificate Signing Request (CSR)**: + - Open **Keychain Access** on your Mac. + - In the menu bar: **Keychain Access** > **Certificate Assistant** > **Request a Certificate From a Certificate Authority**. + - Enter your email, leave CA Email blank, select **Saved to disk**, click **Continue**. + - Save the `.certSigningRequest` file. +5. Upload that file on the Apple Developer page, click **Continue**. +6. Download the `.cer` file. +7. Double-click it — it installs into your Keychain. + +--- + +## `GH_TOKEN` + +A GitHub Personal Access Token that lets the build script upload release artifacts to GitHub Releases. + +### Step 1 — Go to GitHub token settings + +1. Go to https://github.com/settings/tokens +2. Sign in if needed. + +### Step 2 — Create a token + +1. Click **Generate new token** > **Generate new token (classic)**. +2. **Note**: `OpenSwarm Releases` (or whatever you want). +3. **Expiration**: pick a duration (90 days, or "No expiration" if you don't want to rotate it). +4. **Scopes**: check the **`repo`** checkbox (this gives full access to your repositories, which is needed to create releases and upload assets). +5. Click **Generate token** at the bottom. +6. **Copy the token now** — it starts with `ghp_` and you won't be able to see it again. + +``` +GH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +--- + +## Final `.env` example + +``` +BACKEND_PORT=8324 + +GOOGLE_OAUTH_CLIENT_ID=123456789-xxxxxxxxx.apps.googleusercontent.com +GOOGLE_OAUTH_CLIENT_SECRET=GOCSPX-xxxxxxxxxxxxxxxxxx + +APPLE_ID=you@example.com +APPLE_APP_SPECIFIC_PASSWORD=abcd-efgh-ijkl-mnop +APPLE_TEAM_ID=ABCDE12345 + +GH_TOKEN=ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +``` + +--- + +## Running a production build + +Once your `.env` is filled in, just run: + +```bash +./scripts/build-app.sh --publish +``` + +The build script automatically loads `backend/.env`, so you don't need to source it yourself. This will build the app, sign it with your certificate, notarize it with Apple, and upload the `.dmg` and `.zip` to a GitHub Release. diff --git a/backend/apps/agents/agent_manager.py b/backend/apps/agents/agent_manager.py index 388db807..e6f06c51 100644 --- a/backend/apps/agents/agent_manager.py +++ b/backend/apps/agents/agent_manager.py @@ -2,6 +2,7 @@ import asyncio import json import logging import os +import sys import time from datetime import datetime from uuid import uuid4 @@ -10,7 +11,6 @@ from typing import Optional from backend.apps.agents.models import ( AgentConfig, AgentSession, Message, MessageBranch, ApprovalRequest, ToolGroupMeta, ) -from backend.apps.agents.worktree_manager import WorktreeManager from backend.apps.agents.ws_manager import ws_manager from backend.apps.modes.modes import load_mode from backend.apps.outputs.outputs import _load_all as load_all_outputs @@ -22,16 +22,12 @@ from backend.apps.tools_lib.tools_lib import ( load_builtin_permissions, refresh_google_token, ) +from backend.config.paths import SESSIONS_DIR logger = logging.getLogger(__name__) os.environ.setdefault("CLAUDE_CODE_STREAM_CLOSE_TIMEOUT", "3600000") -SESSIONS_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "sessions", -) - def _save_session(session_id: str, doc_data: dict): os.makedirs(SESSIONS_DIR, exist_ok=True) @@ -72,9 +68,6 @@ FULL_TOOLS = [ "RenderOutput", ] -REPO_ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))) - - def _get_denied_tool_names(tool) -> set[str]: """Return the set of MCP sub-tool names whose permission is 'deny'.""" return { @@ -122,7 +115,6 @@ class AgentManager: def __init__(self): self.sessions: dict[str, AgentSession] = {} self.tasks: dict[str, asyncio.Task] = {} - self.worktree_mgr = WorktreeManager(REPO_ROOT) def _resolve_mode(self, mode_id: str) -> tuple[list[str], str | None, str | None]: """Return (tools, system_prompt, default_folder) resolved from the mode store.""" @@ -246,10 +238,7 @@ class AgentManager: async def launch_agent(self, config: AgentConfig) -> AgentSession: session_id = uuid4().hex - branch_name = f"agent-{session_id[:8]}" - - worktree_path = await self.worktree_mgr.create_worktree(branch_name) - + mode_tools, _, mode_folder = self._resolve_mode(config.mode) tools = mode_tools @@ -258,7 +247,7 @@ class AgentManager: config.target_directory or mode_folder or global_settings.default_folder - or str(REPO_ROOT) + or os.path.expanduser("~") ) if config.mode in ("view-builder", "skill-builder") and not config.target_directory: @@ -271,8 +260,6 @@ class AgentManager: name=config.name, model=config.model, mode=config.mode, - worktree_path=worktree_path, - branch_name=branch_name, system_prompt=config.system_prompt, allowed_tools=tools, max_turns=config.max_turns, @@ -561,6 +548,17 @@ class AgentManager: mcp_servers = await self._build_mcp_servers(session.allowed_tools) + browser_server_path = os.path.join( + os.path.dirname(__file__), "browser_mcp_server.py" + ) + backend_port = os.environ.get("OPENSWARM_PORT", "8324") + mcp_servers["openswarm-browser"] = { + "command": sys.executable, + "args": [browser_server_path], + "env": {"OPENSWARM_PORT": backend_port}, + "type": "stdio", + } + effective_allowed = [ t for t in session.allowed_tools if _builtin_perms.get(t, "always_allow") == "always_allow" @@ -583,6 +581,8 @@ class AgentManager: else: effective_allowed.append(f"mcp__{name}__*") + effective_allowed.append("mcp__openswarm-browser__*") + options_kwargs = { "model": session.model, "can_use_tool": can_use_tool, @@ -593,8 +593,9 @@ class AgentManager: "allowed_tools": effective_allowed, "include_partial_messages": True, } - if global_settings.anthropic_api_key: - options_kwargs["env"] = {"ANTHROPIC_API_KEY": global_settings.anthropic_api_key} + if not global_settings.anthropic_api_key: + raise ValueError("Anthropic API key not configured. Set it in Settings.") + options_kwargs["env"] = {"ANTHROPIC_API_KEY": global_settings.anthropic_api_key} if mcp_servers: options_kwargs["mcp_servers"] = mcp_servers if composed_prompt: @@ -867,7 +868,7 @@ class AgentManager: f"I've processed your request: \"{prompt}\"\n\n" "This is a mock response because `claude-agent-sdk` is not installed. " "Install it with `pip install claude-agent-sdk` to use real Claude Code instances.\n\n" - f"The agent was configured with:\n- Model: {session.model}\n- Mode: {session.mode}\n- Worktree: {session.branch_name}" + f"The agent was configured with:\n- Model: {session.model}\n- Mode: {session.mode}" ) asst_msg_id = uuid4().hex await self._stream_text(session_id, asst_msg_id, asst_text) @@ -946,7 +947,7 @@ class AgentManager: task = asyncio.create_task(self._run_agent_loop(session_id, prompt, images=images, context_paths=context_paths, forced_tools=forced_tools, attached_skills=attached_skills)) self.tasks[session_id] = task - async def stop_agent(self, session_id: str, remove_worktree: bool = False): + async def stop_agent(self, session_id: str): """Stop a running agent.""" task = self.tasks.get(session_id) if task and not task.done(): @@ -964,9 +965,6 @@ class AgentManager: "status": "stopped", "session": session.model_dump(mode="json"), }) - - if remove_worktree and session and session.branch_name: - await self.worktree_mgr.remove_worktree(session.branch_name) def handle_approval(self, request_id: str, decision: dict): """Resolve a pending HITL approval.""" @@ -1039,10 +1037,9 @@ class AgentManager: try: import anthropic global_settings = load_settings() - client_kwargs = {} - if global_settings.anthropic_api_key: - client_kwargs["api_key"] = global_settings.anthropic_api_key - client = anthropic.AsyncAnthropic(**client_kwargs) + if not global_settings.anthropic_api_key: + raise ValueError("API key not configured") + client = anthropic.AsyncAnthropic(api_key=global_settings.anthropic_api_key) resp = await client.messages.create( model="claude-sonnet-4-20250514", max_tokens=30, @@ -1084,10 +1081,9 @@ class AgentManager: try: import anthropic, json as _json global_settings = load_settings() - client_kwargs = {} - if global_settings.anthropic_api_key: - client_kwargs["api_key"] = global_settings.anthropic_api_key - client = anthropic.AsyncAnthropic(**client_kwargs) + if not global_settings.anthropic_api_key: + raise ValueError("API key not configured") + client = anthropic.AsyncAnthropic(api_key=global_settings.anthropic_api_key) tool_desc = "\n".join( f"- {tc.get('tool', '?')}: {tc.get('input_summary', '')}" for tc in tool_calls @@ -1171,7 +1167,7 @@ class AgentManager: async def close_session(self, session_id: str) -> None: """Close a session: pause the agent if running, persist to JSON file, - and remove from in-memory state. Worktree is kept on disk for resume.""" + and remove from in-memory state.""" task = self.tasks.get(session_id) if task and not task.done(): task.cancel() @@ -1211,7 +1207,7 @@ class AgentManager: logger.info(f"Session {session_id} closed and persisted") async def delete_session(self, session_id: str) -> None: - """Permanently delete a session: remove from memory, JSON file, worktree, and branch.""" + """Permanently delete a session: remove from memory and JSON file.""" task = self.tasks.get(session_id) if task and not task.done(): task.cancel() @@ -1220,21 +1216,9 @@ class AgentManager: except asyncio.CancelledError: pass - session = self.sessions.pop(session_id, None) + self.sessions.pop(session_id, None) self.tasks.pop(session_id, None) - branch_name: str | None = None - if session: - branch_name = session.branch_name - else: - data = _load_session_data(session_id) - if data: - branch_name = data.get("branch_name") - - if branch_name: - await self.worktree_mgr.remove_worktree(branch_name) - await self.worktree_mgr.delete_branch(branch_name) - _delete_session_file(session_id) logger.info(f"Session {session_id} permanently deleted") @@ -1249,17 +1233,6 @@ class AgentManager: session = AgentSession(**data) - if session.branch_name: - worktree_path = os.path.join(self.worktree_mgr.worktrees_dir, session.branch_name) - if not os.path.exists(worktree_path): - try: - worktree_path = await self.worktree_mgr.create_worktree(session.branch_name) - except RuntimeError: - new_branch = f"agent-{session_id[:8]}" - worktree_path = await self.worktree_mgr.create_worktree(new_branch) - session.branch_name = new_branch - session.worktree_path = worktree_path - session.closed_at = None self.sessions[session_id] = session @@ -1349,21 +1322,6 @@ class AgentManager: session.status = "stopped" session.closed_at = None session.pending_approvals = [] - if session.branch_name: - worktree_path = os.path.join( - self.worktree_mgr.worktrees_dir, session.branch_name - ) - if not os.path.exists(worktree_path): - try: - worktree_path = await self.worktree_mgr.create_worktree( - session.branch_name - ) - except RuntimeError: - logger.warning( - f"Could not restore worktree for session {session.id}, skipping" - ) - continue - session.worktree_path = worktree_path self.sessions[session.id] = session _delete_session_file(sid) logger.info(f"Restored session {session.id}") diff --git a/backend/apps/agents/agents.py b/backend/apps/agents/agents.py index 890ab856..462b8fde 100644 --- a/backend/apps/agents/agents.py +++ b/backend/apps/agents/agents.py @@ -13,7 +13,6 @@ logger = logging.getLogger(__name__) @asynccontextmanager async def agents_lifespan(): logger.info("Agents sub-app starting") - await agent_manager.worktree_mgr.cleanup_all_worktrees() await agent_manager.reconcile_on_startup() await agent_manager.restore_all_sessions() yield @@ -61,9 +60,8 @@ async def send_message(session_id: str, body: dict): return {"ok": True} @agents.router.post("/sessions/{session_id}/stop") -async def stop_agent(session_id: str, body: dict = {}): - remove_worktree = body.get("remove_worktree", False) - await agent_manager.stop_agent(session_id, remove_worktree=remove_worktree) +async def stop_agent(session_id: str): + await agent_manager.stop_agent(session_id) return {"ok": True} @agents.router.post("/approval") @@ -161,17 +159,3 @@ async def resume_session(session_id: str): raise HTTPException(status_code=404, detail=str(e)) return {"session": session.model_dump(mode="json")} -@agents.router.get("/worktrees") -async def list_worktrees(): - worktrees = await agent_manager.worktree_mgr.list_worktrees() - return {"worktrees": worktrees} - -@agents.router.get("/sessions/{session_id}/diff") -async def get_session_diff(session_id: str): - session = agent_manager.get_session(session_id) - if not session: - raise HTTPException(status_code=404, detail="Session not found") - if not session.branch_name: - return {"diff": ""} - diff = await agent_manager.worktree_mgr.get_worktree_diff(session.branch_name) - return {"diff": diff} diff --git a/backend/apps/agents/browser_mcp_server.py b/backend/apps/agents/browser_mcp_server.py new file mode 100644 index 00000000..382178aa --- /dev/null +++ b/backend/apps/agents/browser_mcp_server.py @@ -0,0 +1,303 @@ +#!/usr/bin/env python3 +""" +Minimal stdio MCP server that exposes browser interaction tools. + +Launched as a subprocess by the Claude Agent SDK. Proxies tool calls +to the OpenSwarm backend via HTTP, which bridges them to the Electron +frontend via WebSocket where the actual webview lives. +""" + +import base64 +import json +import sys +import os +import urllib.request +import urllib.error +from io import BytesIO + +try: + from PIL import Image + HAS_PIL = True +except ImportError: + HAS_PIL = False + +BACKEND_PORT = os.environ.get("OPENSWARM_PORT", "8324") +BACKEND_URL = f"http://127.0.0.1:{BACKEND_PORT}/api/browser/command" + +TOOLS = [ + { + "name": "BrowserScreenshot", + "description": ( + "Capture a screenshot of the browser page. Returns the screenshot as a " + "base64-encoded PNG image. Use this to see what is currently displayed." + ), + "inputSchema": { + "type": "object", + "properties": { + "browser_id": { + "type": "string", + "description": "The browser card ID to capture. Use the ID from the selected browser card context.", + }, + }, + "required": ["browser_id"], + }, + }, + { + "name": "BrowserGetText", + "description": ( + "Get the visible text content of the browser page. Returns the page's " + "innerText (up to 15000 characters)." + ), + "inputSchema": { + "type": "object", + "properties": { + "browser_id": { + "type": "string", + "description": "The browser card ID.", + }, + }, + "required": ["browser_id"], + }, + }, + { + "name": "BrowserNavigate", + "description": "Navigate the browser to a URL.", + "inputSchema": { + "type": "object", + "properties": { + "browser_id": { + "type": "string", + "description": "The browser card ID.", + }, + "url": { + "type": "string", + "description": "The URL to navigate to.", + }, + }, + "required": ["browser_id", "url"], + }, + }, + { + "name": "BrowserClick", + "description": ( + "Click an element in the browser page identified by a CSS selector." + ), + "inputSchema": { + "type": "object", + "properties": { + "browser_id": { + "type": "string", + "description": "The browser card ID.", + }, + "selector": { + "type": "string", + "description": "CSS selector of the element to click.", + }, + }, + "required": ["browser_id", "selector"], + }, + }, + { + "name": "BrowserType", + "description": ( + "Type text into an input element in the browser page. Clears the " + "existing value first, then types the new text." + ), + "inputSchema": { + "type": "object", + "properties": { + "browser_id": { + "type": "string", + "description": "The browser card ID.", + }, + "selector": { + "type": "string", + "description": "CSS selector of the input element.", + }, + "text": { + "type": "string", + "description": "The text to type.", + }, + }, + "required": ["browser_id", "selector", "text"], + }, + }, + { + "name": "BrowserEvaluate", + "description": ( + "Evaluate a JavaScript expression in the browser page and return the result. " + "The expression is run via executeJavaScript on the webview." + ), + "inputSchema": { + "type": "object", + "properties": { + "browser_id": { + "type": "string", + "description": "The browser card ID.", + }, + "expression": { + "type": "string", + "description": "JavaScript expression to evaluate.", + }, + }, + "required": ["browser_id", "expression"], + }, + }, +] + + +def send_response(id_, result=None, error=None): + msg = {"jsonrpc": "2.0", "id": id_} + if error is not None: + msg["error"] = error + else: + msg["result"] = result + sys.stdout.write(json.dumps(msg) + "\n") + sys.stdout.flush() + + +def send_notification(method, params=None): + msg = {"jsonrpc": "2.0", "method": method} + if params is not None: + msg["params"] = params + sys.stdout.write(json.dumps(msg) + "\n") + sys.stdout.flush() + + +def call_backend(action: str, browser_id: str, params: dict | None = None) -> dict: + payload = json.dumps({ + "action": action, + "browser_id": browser_id, + "params": params or {}, + }).encode() + req = urllib.request.Request( + BACKEND_URL, + data=payload, + headers={"Content-Type": "application/json"}, + method="POST", + ) + try: + with urllib.request.urlopen(req, timeout=30) as resp: + return json.loads(resp.read().decode()) + except urllib.error.HTTPError as e: + body = e.read().decode() if e.fp else str(e) + return {"error": f"HTTP {e.code}: {body}"} + except Exception as e: + return {"error": str(e)} + + +MAX_IMAGE_B64_BYTES = 700_000 + + +def compress_screenshot(b64_png: str) -> tuple[str, str] | None: + """Resize and re-encode as JPEG to stay under the stdio buffer limit.""" + if not HAS_PIL: + return None + try: + raw = base64.b64decode(b64_png) + img = Image.open(BytesIO(raw)) + max_width = 1280 + if img.width > max_width: + ratio = max_width / img.width + img = img.resize((max_width, int(img.height * ratio)), Image.LANCZOS) + buf = BytesIO() + img.convert("RGB").save(buf, format="JPEG", quality=55) + return base64.b64encode(buf.getvalue()).decode(), "image/jpeg" + except Exception: + return None + + +def handle_tool_call(tool_name: str, arguments: dict) -> dict: + browser_id = arguments.get("browser_id", "") + if not browser_id: + return {"content": [{"type": "text", "text": "Error: browser_id is required"}], "isError": True} + + action_map = { + "BrowserScreenshot": "screenshot", + "BrowserGetText": "get_text", + "BrowserNavigate": "navigate", + "BrowserClick": "click", + "BrowserType": "type", + "BrowserEvaluate": "evaluate", + } + action = action_map.get(tool_name) + if not action: + return {"content": [{"type": "text", "text": f"Unknown tool: {tool_name}"}], "isError": True} + + params = {k: v for k, v in arguments.items() if k != "browser_id"} + result = call_backend(action, browser_id, params) + + if "error" in result: + return {"content": [{"type": "text", "text": f"Error: {result['error']}"}], "isError": True} + + if action == "screenshot" and result.get("image"): + image_data = result["image"] + mime_type = "image/png" + + if len(image_data) > MAX_IMAGE_B64_BYTES: + compressed = compress_screenshot(image_data) + if compressed: + image_data, mime_type = compressed + + if len(image_data) > MAX_IMAGE_B64_BYTES: + return { + "content": [ + {"type": "text", "text": ( + f"Screenshot too large to return ({len(image_data)} bytes base64). " + f"URL: {result.get('url', 'unknown')}. " + "Use BrowserGetText to read the page content instead." + )}, + ], + } + + return { + "content": [ + {"type": "image", "data": image_data, "mimeType": mime_type}, + {"type": "text", "text": f"Screenshot captured. URL: {result.get('url', 'unknown')}"}, + ], + } + + text = result.get("text", result.get("data", json.dumps(result))) + return {"content": [{"type": "text", "text": str(text)}]} + + +def main(): + for line in sys.stdin: + line = line.strip() + if not line: + continue + try: + msg = json.loads(line) + except json.JSONDecodeError: + continue + + method = msg.get("method") + id_ = msg.get("id") + params = msg.get("params", {}) + + if method == "initialize": + send_response(id_, { + "protocolVersion": "2024-11-05", + "capabilities": {"tools": {}}, + "serverInfo": { + "name": "openswarm-browser", + "version": "1.0.0", + }, + }) + elif method == "notifications/initialized": + pass + elif method == "tools/list": + send_response(id_, {"tools": TOOLS}) + elif method == "tools/call": + tool_name = params.get("name", "") + arguments = params.get("arguments", {}) + result = handle_tool_call(tool_name, arguments) + send_response(id_, result) + elif method == "ping": + send_response(id_, {}) + elif id_ is not None: + send_response(id_, error={"code": -32601, "message": f"Method not found: {method}"}) + + +if __name__ == "__main__": + main() diff --git a/backend/apps/agents/models.py b/backend/apps/agents/models.py index 06805ac0..d17828a5 100644 --- a/backend/apps/agents/models.py +++ b/backend/apps/agents/models.py @@ -56,8 +56,6 @@ class AgentSession(BaseModel): status: Literal["running", "waiting_approval", "completed", "error", "stopped"] = "running" model: str = "sonnet" mode: str = "agent" - worktree_path: Optional[str] = None - branch_name: Optional[str] = None sdk_session_id: Optional[str] = None system_prompt: Optional[str] = None allowed_tools: list[str] = Field(default_factory=list) diff --git a/backend/apps/agents/worktree_manager.py b/backend/apps/agents/worktree_manager.py deleted file mode 100644 index ebddb7e1..00000000 --- a/backend/apps/agents/worktree_manager.py +++ /dev/null @@ -1,130 +0,0 @@ -import asyncio -import os -import shutil -import logging - -logger = logging.getLogger(__name__) - -class WorktreeManager: - def __init__(self, repo_root: str): - self.repo_root = repo_root - self.worktrees_dir = os.path.join(repo_root, ".worktrees") - os.makedirs(self.worktrees_dir, exist_ok=True) - - async def create_worktree(self, branch_name: str) -> str: - """Create a new git worktree and return its path.""" - worktree_path = os.path.join(self.worktrees_dir, branch_name) - if os.path.exists(worktree_path): - return worktree_path - - proc = await asyncio.create_subprocess_exec( - "git", "worktree", "add", worktree_path, "-b", branch_name, - cwd=self.repo_root, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await proc.communicate() - if proc.returncode != 0: - proc = await asyncio.create_subprocess_exec( - "git", "worktree", "add", worktree_path, branch_name, - cwd=self.repo_root, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await proc.communicate() - if proc.returncode != 0: - raise RuntimeError(f"Failed to create worktree: {stderr.decode()}") - - logger.info(f"Created worktree at {worktree_path} on branch {branch_name}") - return worktree_path - - async def remove_worktree(self, branch_name: str) -> None: - """Remove a git worktree.""" - worktree_path = os.path.join(self.worktrees_dir, branch_name) - if not os.path.exists(worktree_path): - return - - proc = await asyncio.create_subprocess_exec( - "git", "worktree", "remove", worktree_path, "--force", - cwd=self.repo_root, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await proc.communicate() - logger.info(f"Removed worktree at {worktree_path}") - - async def list_worktrees(self) -> list[dict]: - """List all worktrees with their branch and path info.""" - proc = await asyncio.create_subprocess_exec( - "git", "worktree", "list", "--porcelain", - cwd=self.repo_root, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, _ = await proc.communicate() - - worktrees = [] - current = {} - for line in stdout.decode().strip().split("\n"): - if line.startswith("worktree "): - if current: - worktrees.append(current) - current = {"path": line.split(" ", 1)[1]} - elif line.startswith("HEAD "): - current["head"] = line.split(" ", 1)[1] - elif line.startswith("branch "): - current["branch"] = line.split(" ", 1)[1].replace("refs/heads/", "") - elif line == "": - if current: - worktrees.append(current) - current = {} - if current: - worktrees.append(current) - return worktrees - - async def delete_branch(self, branch_name: str) -> None: - """Delete a local git branch.""" - proc = await asyncio.create_subprocess_exec( - "git", "branch", "-D", branch_name, - cwd=self.repo_root, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, stderr = await proc.communicate() - if proc.returncode == 0: - logger.info(f"Deleted branch {branch_name}") - else: - logger.warning(f"Could not delete branch {branch_name}: {stderr.decode().strip()}") - - async def cleanup_all_worktrees(self) -> None: - """Remove all worktree directories and prune stale git worktree refs.""" - if os.path.exists(self.worktrees_dir): - for entry in os.listdir(self.worktrees_dir): - entry_path = os.path.join(self.worktrees_dir, entry) - if os.path.isdir(entry_path): - shutil.rmtree(entry_path, ignore_errors=True) - logger.info("Removed all worktree directories") - - proc = await asyncio.create_subprocess_exec( - "git", "worktree", "prune", - cwd=self.repo_root, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - await proc.communicate() - logger.info("Pruned stale git worktree refs") - - async def get_worktree_diff(self, branch_name: str) -> str: - """Get the diff of uncommitted changes in a worktree.""" - worktree_path = os.path.join(self.worktrees_dir, branch_name) - if not os.path.exists(worktree_path): - return "" - - proc = await asyncio.create_subprocess_exec( - "git", "diff", "HEAD", - cwd=worktree_path, - stdout=asyncio.subprocess.PIPE, - stderr=asyncio.subprocess.PIPE, - ) - stdout, _ = await proc.communicate() - return stdout.decode() diff --git a/backend/apps/agents/ws_manager.py b/backend/apps/agents/ws_manager.py index 3d17b326..22651d8f 100644 --- a/backend/apps/agents/ws_manager.py +++ b/backend/apps/agents/ws_manager.py @@ -12,6 +12,7 @@ class ConnectionManager: self.connections: dict[str, list[WebSocket]] = {} self.global_connections: list[WebSocket] = [] self.pending_futures: dict[str, asyncio.Future] = {} + self.browser_futures: dict[str, asyncio.Future] = {} async def connect_session(self, session_id: str, websocket: WebSocket): await websocket.accept() @@ -85,4 +86,32 @@ class ConnectionManager: if future and not future.done(): future.set_result(decision) + async def send_browser_command( + self, request_id: str, action: str, browser_id: str, params: dict + ) -> dict: + """Send a browser command to the frontend and wait for the result.""" + future = asyncio.get_event_loop().create_future() + self.browser_futures[request_id] = future + + await self.broadcast_global("browser:command", { + "request_id": request_id, + "action": action, + "browser_id": browser_id, + "params": params, + }) + + try: + result = await asyncio.wait_for(future, timeout=30.0) + return result + except asyncio.TimeoutError: + return {"error": "Browser command timed out"} + finally: + self.browser_futures.pop(request_id, None) + + def resolve_browser_command(self, request_id: str, result: dict): + """Resolve a pending browser command Future with the frontend's result.""" + future = self.browser_futures.get(request_id) + if future and not future.done(): + future.set_result(result) + ws_manager = ConnectionManager() diff --git a/backend/apps/dashboard_layout/dashboard_layout.py b/backend/apps/dashboard_layout/dashboard_layout.py index c3f23355..5d16b81b 100644 --- a/backend/apps/dashboard_layout/dashboard_layout.py +++ b/backend/apps/dashboard_layout/dashboard_layout.py @@ -7,7 +7,8 @@ from backend.apps.dashboard_layout.models import DashboardLayout, DashboardLayou logger = logging.getLogger(__name__) -DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data", "dashboard_layout") +from backend.config.paths import DASHBOARD_LAYOUT_DIR as DATA_DIR + LAYOUT_FILE = os.path.join(DATA_DIR, "layout.json") diff --git a/backend/apps/dashboards/dashboards.py b/backend/apps/dashboards/dashboards.py index 1c2d2a45..9e137e25 100644 --- a/backend/apps/dashboards/dashboards.py +++ b/backend/apps/dashboards/dashboards.py @@ -13,16 +13,14 @@ from backend.apps.dashboards.models import ( DashboardLayout, CardPosition, ViewCardPosition, + BrowserCardPosition, ) from fastapi import HTTPException logger = logging.getLogger(__name__) -BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -DATA_DIR = os.path.join(BACKEND_DIR, "data", "dashboards") -SESSIONS_DIR = os.path.join(BACKEND_DIR, "data", "sessions") +from backend.config.paths import DASHBOARDS_DIR as DATA_DIR, SESSIONS_DIR, DASHBOARD_LAYOUT_DIR as OLD_LAYOUT_DIR -OLD_LAYOUT_DIR = os.path.join(BACKEND_DIR, "data", "dashboard_layout") OLD_LAYOUT_FILE = os.path.join(OLD_LAYOUT_DIR, "layout.json") @@ -115,6 +113,7 @@ async def list_dashboards(): items.append({ "id": dumped["id"], "name": dumped.get("name", "Untitled"), + "auto_named": dumped.get("auto_named", False), "created_at": dumped.get("created_at"), "updated_at": dumped.get("updated_at"), }) @@ -128,6 +127,69 @@ async def create_dashboard(body: DashboardCreate): return dashboard.model_dump(mode="json") +@dashboards.router.post("/{dashboard_id}/generate-name") +async def generate_name(dashboard_id: str): + dashboard = _load(dashboard_id) + + if not dashboard.auto_named and dashboard.name != "Untitled Dashboard": + return {"name": dashboard.name, "auto_named": dashboard.auto_named} + + from backend.apps.agents.agent_manager import agent_manager + + prompts = [] + for session in agent_manager.sessions.values(): + if getattr(session, "dashboard_id", None) != dashboard_id: + continue + for msg in session.messages: + if msg.role == "user" and isinstance(msg.content, str) and msg.content.strip(): + prompts.append(msg.content.strip()[:200]) + break + + if not prompts: + return {"name": dashboard.name, "auto_named": dashboard.auto_named} + + fallback = prompts[0][:40] + try: + import anthropic + from backend.apps.settings.settings import load_settings + global_settings = load_settings() + if not global_settings.anthropic_api_key: + raise ValueError("API key not configured") + + client = anthropic.AsyncAnthropic(api_key=global_settings.anthropic_api_key) + + if len(prompts) == 1: + system = ( + "Generate a concise 2-5 word workspace name for a project based on this task. " + "Return only the name, nothing else." + ) + user_content = prompts[0] + else: + system = ( + "Generate a concise 2-5 word workspace name that captures the overall theme of these tasks. " + "Return only the name, nothing else." + ) + user_content = "\n".join(f"- {p}" for p in prompts) + + resp = await client.messages.create( + model="claude-haiku-4-20250414", + max_tokens=30, + system=system, + messages=[{"role": "user", "content": user_content}], + ) + generated = resp.content[0].text.strip().strip('"\'') + if generated: + fallback = generated + except Exception as e: + logger.warning(f"Dashboard name generation failed, using fallback: {e}") + + dashboard.name = fallback + dashboard.auto_named = True + dashboard.updated_at = datetime.now() + _save(dashboard) + return {"name": dashboard.name, "auto_named": True} + + @dashboards.router.get("/{dashboard_id}") async def get_dashboard(dashboard_id: str): dashboard = _load(dashboard_id) @@ -139,6 +201,7 @@ async def update_dashboard(dashboard_id: str, body: DashboardUpdate): dashboard = _load(dashboard_id) if body.name is not None: dashboard.name = body.name + dashboard.auto_named = False if body.layout is not None: dashboard.layout = body.layout dashboard.updated_at = datetime.now() @@ -191,7 +254,11 @@ async def duplicate_dashboard(dashboard_id: str): "name": f"{source_data.get('name', 'Untitled')} (copy)", "created_at": now, "updated_at": now, - "layout": {"cards": {}, "view_cards": source_data.get("layout", {}).get("view_cards", {})}, + "layout": { + "cards": {}, + "view_cards": source_data.get("layout", {}).get("view_cards", {}), + "browser_cards": source_data.get("layout", {}).get("browser_cards", {}), + }, } with open(os.path.join(DATA_DIR, f"{new_id}.json"), "w") as f: json.dump(new_dashboard, f, indent=2) diff --git a/backend/apps/dashboards/models.py b/backend/apps/dashboards/models.py index 149f2069..2d5f6d00 100644 --- a/backend/apps/dashboards/models.py +++ b/backend/apps/dashboards/models.py @@ -20,14 +20,26 @@ class ViewCardPosition(BaseModel): height: float = 360 +class BrowserCardPosition(BaseModel): + browser_id: str + url: str = "" + x: float = 0 + y: float = 0 + width: float = 640 + height: float = 480 + + class DashboardLayout(BaseModel): cards: dict[str, CardPosition] = Field(default_factory=dict) view_cards: dict[str, ViewCardPosition] = Field(default_factory=dict) + browser_cards: dict[str, BrowserCardPosition] = Field(default_factory=dict) + expanded_session_ids: list[str] = Field(default_factory=list) class Dashboard(BaseModel): id: str = Field(default_factory=lambda: uuid4().hex) name: str = "Untitled Dashboard" + auto_named: bool = False created_at: datetime = Field(default_factory=datetime.now) updated_at: datetime = Field(default_factory=datetime.now) layout: DashboardLayout = Field(default_factory=DashboardLayout) diff --git a/backend/apps/modes/models.py b/backend/apps/modes/models.py index 3a884199..03c201fb 100644 --- a/backend/apps/modes/models.py +++ b/backend/apps/modes/models.py @@ -1,17 +1,8 @@ -import os from pydantic import BaseModel, Field from typing import Optional from uuid import uuid4 -OUTPUTS_WORKSPACE = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "outputs_workspace", -) - -SKILLS_WORKSPACE = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "skills_workspace", -) +from backend.config.paths import OUTPUTS_WORKSPACE_DIR as OUTPUTS_WORKSPACE, SKILLS_WORKSPACE_DIR as SKILLS_WORKSPACE class Mode(BaseModel): diff --git a/backend/apps/modes/modes.py b/backend/apps/modes/modes.py index a9c134f7..dc7b2b81 100644 --- a/backend/apps/modes/modes.py +++ b/backend/apps/modes/modes.py @@ -8,7 +8,7 @@ from backend.apps.modes.models import Mode, ModeCreate, ModeUpdate, BUILTIN_MODE logger = logging.getLogger(__name__) -DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data", "modes") +from backend.config.paths import MODES_DIR as DATA_DIR @asynccontextmanager diff --git a/backend/apps/outputs/outputs.py b/backend/apps/outputs/outputs.py index 2e22eac4..206e29e8 100644 --- a/backend/apps/outputs/outputs.py +++ b/backend/apps/outputs/outputs.py @@ -31,18 +31,13 @@ def _resolve_model(short_name: str) -> str: def _get_anthropic_client(): - """Create an AsyncAnthropic client, pulling the API key from app settings if - the ANTHROPIC_API_KEY env var isn't set.""" + """Create an AsyncAnthropic client using the API key from app settings.""" import anthropic - if os.environ.get("ANTHROPIC_API_KEY"): - return anthropic.AsyncAnthropic() - settings = load_settings() - if settings.anthropic_api_key: - return anthropic.AsyncAnthropic(api_key=settings.anthropic_api_key) - - return anthropic.AsyncAnthropic() + if not settings.anthropic_api_key: + raise ValueError("Anthropic API key not configured. Set it in Settings.") + return anthropic.AsyncAnthropic(api_key=settings.anthropic_api_key) def _validate_against_schema(data: dict, schema: dict) -> str | None: @@ -54,15 +49,7 @@ def _validate_against_schema(data: dict, schema: dict) -> str | None: path = " -> ".join(str(p) for p in exc.absolute_path) if exc.absolute_path else "(root)" return f"Schema validation failed at {path}: {exc.message}" -DATA_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "outputs", -) - -WORKSPACE_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "outputs_workspace", -) +from backend.config.paths import OUTPUTS_DIR as DATA_DIR, OUTPUTS_WORKSPACE_DIR as WORKSPACE_DIR def _build_data_injection(input_json: str, result_json: str) -> str: @@ -586,7 +573,7 @@ async def auto_run_agent(body: AutoRunAgentRequest): @outputs.router.delete("/auto-run-agent/{session_id}") async def cleanup_auto_run_agent(session_id: str): - """Delete a temporary auto-run agent session and its worktree.""" + """Delete a temporary auto-run agent session.""" from backend.apps.agents.agent_manager import agent_manager try: diff --git a/backend/apps/settings/models.py b/backend/apps/settings/models.py index 7efe35cb..640c2aa4 100644 --- a/backend/apps/settings/models.py +++ b/backend/apps/settings/models.py @@ -12,3 +12,4 @@ class AppSettings(BaseModel): theme: str = "dark" new_agent_shortcut: str = "Meta+l" anthropic_api_key: Optional[str] = None + browser_homepage: str = "https://www.google.com" diff --git a/backend/apps/settings/settings.py b/backend/apps/settings/settings.py index 60dafc55..a5908aad 100644 --- a/backend/apps/settings/settings.py +++ b/backend/apps/settings/settings.py @@ -13,10 +13,8 @@ from backend.apps.settings.models import AppSettings logger = logging.getLogger(__name__) -DATA_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "settings", -) +from backend.config.paths import SETTINGS_DIR as DATA_DIR + SETTINGS_FILE = os.path.join(DATA_DIR, "settings.json") diff --git a/backend/apps/skills/skills.py b/backend/apps/skills/skills.py index ad21cd52..83d868a6 100644 --- a/backend/apps/skills/skills.py +++ b/backend/apps/skills/skills.py @@ -13,10 +13,7 @@ logger = logging.getLogger(__name__) SKILLS_DIR = os.path.expanduser("~/.claude/skills") INDEX_PATH = os.path.join(SKILLS_DIR, ".skills_index.json") -SKILLS_WORKSPACE_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "data", "skills_workspace", -) +from backend.config.paths import SKILLS_WORKSPACE_DIR @asynccontextmanager diff --git a/backend/apps/templates/templates.py b/backend/apps/templates/templates.py index 65e6cb0c..36f0adf3 100644 --- a/backend/apps/templates/templates.py +++ b/backend/apps/templates/templates.py @@ -8,7 +8,7 @@ from backend.apps.templates.models import PromptTemplate, PromptTemplateCreate, logger = logging.getLogger(__name__) -DATA_DIR = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), "data", "templates") +from backend.config.paths import TEMPLATES_DIR as DATA_DIR @asynccontextmanager async def templates_lifespan(): diff --git a/backend/apps/tools_lib/tools_lib.py b/backend/apps/tools_lib/tools_lib.py index c05de0cc..4dec259e 100644 --- a/backend/apps/tools_lib/tools_lib.py +++ b/backend/apps/tools_lib/tools_lib.py @@ -18,11 +18,9 @@ from backend.apps.tools_lib.models import ToolDefinition, ToolCreate, ToolUpdate logger = logging.getLogger(__name__) -BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) -load_dotenv(os.path.join(BACKEND_DIR, ".env")) +from backend.config.paths import BACKEND_DIR, TOOLS_DIR as DATA_DIR, BUILTIN_PERMISSIONS_PATH as BUILTIN_PERMS_PATH -DATA_DIR = os.path.join(BACKEND_DIR, "data", "tools") -BUILTIN_PERMS_PATH = os.path.join(BACKEND_DIR, "data", "builtin_permissions.json") +load_dotenv(os.path.join(BACKEND_DIR, ".env")) @asynccontextmanager @@ -121,7 +119,8 @@ async def oauth_callback(code: str = Query(...), state: str = Query("")): tool = _load(tool_id) client_id = os.environ.get("GOOGLE_OAUTH_CLIENT_ID", "") client_secret = os.environ.get("GOOGLE_OAUTH_CLIENT_SECRET", "") - redirect_uri = "http://localhost:8324/api/tools/oauth/callback" + _port = os.environ.get("OPENSWARM_PORT", "8324") + redirect_uri = f"http://localhost:{_port}/api/tools/oauth/callback" async with httpx.AsyncClient(timeout=15.0) as client: resp = await client.post(GOOGLE_TOKEN_URL, data={ @@ -660,7 +659,8 @@ async def oauth_start(tool_id: str): if not client_id: raise HTTPException(status_code=400, detail="GOOGLE_OAUTH_CLIENT_ID not set in backend .env") - redirect_uri = "http://localhost:8324/api/tools/oauth/callback" + _port = os.environ.get("OPENSWARM_PORT", "8324") + redirect_uri = f"http://localhost:{_port}/api/tools/oauth/callback" state = tool_id _pending_oauth[state] = tool_id diff --git a/backend/config/Apps.py b/backend/config/Apps.py index ce8ffb3a..d8f0608c 100644 --- a/backend/config/Apps.py +++ b/backend/config/Apps.py @@ -1,3 +1,5 @@ +import os + from fastapi import FastAPI, APIRouter import debug from uuid import uuid4 @@ -30,7 +32,8 @@ class MainApp: for sub_app in sub_apps: debug(sub_app.name) await stack.enter_async_context(sub_app.lifespan()) - print("\nCheck out the API docs at: http://127.0.0.1:8324/docs\n") + _port = os.environ.get("OPENSWARM_PORT", "8324") + print(f"\nCheck out the API docs at: http://127.0.0.1:{_port}/docs\n") yield self.app = FastAPI(lifespan=lifespan) diff --git a/backend/config/paths.py b/backend/config/paths.py new file mode 100644 index 00000000..083ebdfb --- /dev/null +++ b/backend/config/paths.py @@ -0,0 +1,39 @@ +"""Centralised path definitions for the OpenSwarm backend. + +In dev mode (default) data lives under ``backend/data/``. +When packaged as a desktop app, Electron sets ``OPENSWARM_PACKAGED=1`` and +data is stored in a platform-appropriate location +(``~/Library/Application Support/OpenSwarm/data/`` on macOS). +""" + +import os +import sys + +_BACKEND_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +_is_packaged = os.environ.get("OPENSWARM_PACKAGED") == "1" + +if _is_packaged: + if sys.platform == "darwin": + _app_support = os.path.join(os.path.expanduser("~"), "Library", "Application Support", "OpenSwarm") + elif sys.platform == "win32": + _app_support = os.path.join(os.environ.get("APPDATA", os.path.expanduser("~")), "OpenSwarm") + else: + _app_support = os.path.join(os.environ.get("XDG_DATA_HOME", os.path.join(os.path.expanduser("~"), ".local", "share")), "OpenSwarm") + DATA_ROOT = os.path.join(_app_support, "data") +else: + DATA_ROOT = os.path.join(_BACKEND_DIR, "data") + +SESSIONS_DIR = os.path.join(DATA_ROOT, "sessions") +TOOLS_DIR = os.path.join(DATA_ROOT, "tools") +SETTINGS_DIR = os.path.join(DATA_ROOT, "settings") +MODES_DIR = os.path.join(DATA_ROOT, "modes") +TEMPLATES_DIR = os.path.join(DATA_ROOT, "templates") +DASHBOARDS_DIR = os.path.join(DATA_ROOT, "dashboards") +OUTPUTS_DIR = os.path.join(DATA_ROOT, "outputs") +OUTPUTS_WORKSPACE_DIR = os.path.join(DATA_ROOT, "outputs_workspace") +SKILLS_WORKSPACE_DIR = os.path.join(DATA_ROOT, "skills_workspace") +DASHBOARD_LAYOUT_DIR = os.path.join(DATA_ROOT, "dashboard_layout") +BUILTIN_PERMISSIONS_PATH = os.path.join(DATA_ROOT, "builtin_permissions.json") + +BACKEND_DIR = _BACKEND_DIR diff --git a/backend/main.py b/backend/main.py index d3708a9f..451e4490 100644 --- a/backend/main.py +++ b/backend/main.py @@ -1,4 +1,8 @@ +import os +from uuid import uuid4 + from fastapi.responses import JSONResponse +from fastapi import Request from backend.config.Apps import MainApp from backend.apps.health.health import health from backend.apps.agents.agents import agents @@ -83,9 +87,56 @@ async def websocket_dashboard(websocket: WebSocket): "message": payload.get("message"), "updated_input": payload.get("updated_input"), }) + elif event == "browser:result": + ws_manager.resolve_browser_command( + payload.get("request_id", ""), + payload, + ) except WebSocketDisconnect: ws_manager.disconnect_global(websocket) + +@app.post("/api/browser/command") +async def browser_command(request: Request): + """HTTP endpoint called by the browser MCP server subprocess. + Proxies commands to the frontend via WebSocket and waits for results.""" + body = await request.json() + action = body.get("action", "") + browser_id = body.get("browser_id", "") + params = body.get("params", {}) + + if not action or not browser_id: + return JSONResponse({"error": "action and browser_id are required"}, status_code=400) + + request_id = uuid4().hex + result = await ws_manager.send_browser_command(request_id, action, browser_id, params) + return JSONResponse(result) + + if __name__ == "__main__": + import argparse import uvicorn - uvicorn.run("backend.main:app", host="0.0.0.0", port=8324, reload=True) + + parser = argparse.ArgumentParser(description="OpenSwarm backend server") + parser.add_argument("--port", type=int, default=int(os.environ.get("OPENSWARM_PORT", "8324"))) + parser.add_argument("--host", default=os.environ.get("OPENSWARM_HOST", "127.0.0.1")) + parser.add_argument("--reload", action="store_true", default=False) + args = parser.parse_args() + + os.environ["OPENSWARM_PORT"] = str(args.port) + + import uvicorn.config + + class _ReadyServer(uvicorn.Server): + """Subclass that prints a machine-readable READY line on startup.""" + async def startup(self, sockets=None): + await super().startup(sockets) + print(f"READY:PORT={args.port}", flush=True) + + if args.reload: + uvicorn.run("backend.main:app", host=args.host, port=args.port, reload=True) + else: + config = uvicorn.Config("backend.main:app", host=args.host, port=args.port) + server = _ReadyServer(config) + import asyncio + asyncio.run(server.serve()) diff --git a/backend/requirements.txt b/backend/requirements.txt index 813faf0e..8ef474e6 100644 --- a/backend/requirements.txt +++ b/backend/requirements.txt @@ -8,4 +8,5 @@ langchain-openai==0.3.12 pytest==8.3.4 pytest-asyncio==0.25.2 typeguard==4.4.2 -python-dotenv==1.1.1 \ No newline at end of file +python-dotenv==1.1.1 +Pillow \ No newline at end of file diff --git a/backend/run/dev.sh b/backend/run.sh similarity index 67% rename from backend/run/dev.sh rename to backend/run.sh index 92eede71..689ecc1f 100755 --- a/backend/run/dev.sh +++ b/backend/run.sh @@ -13,14 +13,13 @@ else # echo "NOT in macOS server START" fi chmod +x "$DEV_ABSPATH" -source "$(dirname "$DEV_ABSPATH")/_utils.sh" - -PROJECT_ROOT_ABSPATH="$(dirname "$BACKEND_DIR_ABSPATH")" +PROJECT_ROOT_ABSPATH="$(dirname "$(dirname "$DEV_ABSPATH")")" +BACKEND_DIR_ABSPATH="$PROJECT_ROOT_ABSPATH/backend" # Cleanup function on exit cleanup() { - formatted_echo --yellow "Shutting down..." + echo "Shutting down..." cd - > /dev/null 2>&1 } trap cleanup EXIT INT TERM @@ -28,34 +27,34 @@ trap cleanup EXIT INT TERM # --- Create virtual environment if it doesn't exist --- VENV_DIR="$BACKEND_DIR_ABSPATH/.venv" if [[ ! -d "$VENV_DIR" ]]; then - formatted_echo --green "Creating virtual environment..." + echo "Creating virtual environment..." python -m venv "$VENV_DIR" fi source "$VENV_DIR/bin/activate" # --- Install custom debugger module if not already installed --- DEBUGGER_DIR_ABSPATH="$PROJECT_ROOT_ABSPATH/debugger" -if ! pip show debug > /dev/null 2>&1; then - formatted_echo --green "Installing debugger module..." +if ! pip3 show debug > /dev/null 2>&1; then + echo "Installing debugger module..." cd "$DEBUGGER_DIR_ABSPATH" - pip install -e . + pip3 install -e . if [[ $? -ne 0 ]]; then - formatted_error "Failed to install debugger module." + echo "Failed to install debugger module." exit 1 fi fi # --- Install Python dependencies --- -formatted_echo --green "Installing dependencies..." +echo "Installing dependencies..." cd "$BACKEND_DIR_ABSPATH" -pip install -r requirements.txt +pip3 install -r requirements.txt if [[ $? -ne 0 ]]; then - formatted_error "Failed to install Python dependencies." + echo "Failed to install Python dependencies." exit 1 fi # --- Start the backend server --- -formatted_echo --green "Starting backend server on http://0.0.0.0:8324 ..." +echo "Starting backend server on http://0.0.0.0:8324 ..." cd "$PROJECT_ROOT_ABSPATH" python -m uvicorn backend.main:app --host 0.0.0.0 --port 8324 --reload \ --reload-dir "$BACKEND_DIR_ABSPATH" \ diff --git a/backend/run/_utils.sh b/backend/run/_utils.sh deleted file mode 100755 index ea052bb5..00000000 --- a/backend/run/_utils.sh +++ /dev/null @@ -1,202 +0,0 @@ -#!/bin/bash -# Flag processing function with namespacing and global variable declaration -UTILS_FILE_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" -if [[ "$OSTYPE" == "darwin"* ]]; then - # echo "In macOS utils sed START" - # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" - sed -i '' 's/\r//g' "$UTILS_FILE_ABSPATH" - # echo "In macOS utils sed END" -else - # echo "NOT in macOS utils START" - # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" - sed -i 's/\r//g' "$UTILS_FILE_ABSPATH" - # echo "NOT in macOS utils END" -fi -chmod +x "$UTILS_FILE_ABSPATH" - -RUN_DIR_ABSPATH="$(dirname "$UTILS_FILE_ABSPATH")" -BACKEND_DIR_ABSPATH="$(dirname "$RUN_DIR_ABSPATH")" - -formatted_error() { - # Arguments: error message and array of conflicting flags - local initial_message="$1" - shift - local conflicting_flags=("$@") - - # Red color for the error message box - local COLOR_CODE='\033[0;31m' - local NC='\033[0m' # No Color - - # Start the error message with the initial message - local error_message="$initial_message" - - # Add each conflicting flag on a new line with indentation - for conflict in "${conflicting_flags[@]}"; do - error_message+="\n $conflict" # Replacing `\t` with four spaces - done - - # Prepare for printing by finding max length of each line in the message - local lines=() - local max_length=0 - - # Use printf to interpret new lines and calculate max length with spaces instead of tabs - while IFS= read -r line; do - # Substitute tabs with spaces for consistent width measurement - local line_with_spaces="${line//$'\t'/ }" - lines+=("$line_with_spaces") - if (( ${#line_with_spaces} > max_length )); then - max_length=${#line_with_spaces} - fi - done <<< "$(printf "$error_message")" - - # Create the top and bottom borders based on the maximum line length - local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') - - # Print the formatted error message with a red box - printf "\n${COLOR_CODE}%s${NC}\n" "$border" - for line in "${lines[@]}"; do - printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" - done - printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "" - printf "${COLOR_CODE}%s${NC}\n" "$border" -} - - -function process_flags() { - local -n flags_to_commands="$1" # Reference to the dictionary of flags and commands - local -n exclusives="$2" # Reference to the list of exclusive flag groups - local namespace="$3" # Unique prefix for variables - local calling_script_name="$(basename "$(readlink -f "${BASH_SOURCE[1]}")")" - local caller_id="${FUNCNAME[1]}" - local should_exit=false - if [ "$caller_id" == "main" ]; then - caller_id="$calling_script_name" - fi - # echo "caller_id: $caller_id" - # echo "Initializing flags with namespace: $namespace" - - # Initialize flag variables with namespacing in the sourcing script context - for flag in "${!flags_to_commands[@]}"; do - # Convert flag to uppercase variable name and apply namespace, e.g., MYAPP_FLAG1 - local flag_var="${namespace}_${flag^^}" # Prefix and uppercase - flag_var="${flag_var//-}" # Remove dashes - eval "declare -g $flag_var=false" # Initialize as global false - - # Diagnostic output for each initialized variable - # echo "Initialized $flag_var as false" - done - - # echo "Parsing command line arguments: $@" - - # Parse command line arguments and set flags - local unsupported_flags=() - # local potential_typos=() - for arg in "$@"; do - # echo "Processing argument: $arg" - if [[ -n "${flags_to_commands[$arg]}" ]]; then - # echo "Flag found: $arg" - local flag_var="${namespace}_${arg^^}" # Prefix and uppercase - flag_var="${flag_var//-}" # Remove dashes - eval "declare -g $flag_var=true" # Set as global true - # echo "Set $flag_var to true" - # echo "Executing command for $arg: ${flags_to_commands[$arg]}" - eval "${flags_to_commands[$arg]}" - else - if [[ "$arg" == "-"* ]]; then - # echo "Flag not found: $arg" - unsupported_flags+=("$arg") - if [[ -n "${flags_to_commands[-$arg]}" ]]; then - # echo "Potential typo: $arg" - unsupported_flags+=("\t*Note: Potential typo detected.") - unsupported_flags+=("\tDid you mean: -$arg") - fi - fi - fi - done - # Check exclusive flag groups for conflicts - # echo "Checking exclusive flag groups" - for group in "${exclusives[@]}"; do - local count=0 - local conflicting_flags=() - for flag in $group; do - local flag_var="${namespace}_${flag^^}" - flag_var="${flag_var//-}" - if [[ "$(eval echo "\$$flag_var")" == "true" ]]; then - conflicting_flags+=("$flag") - # count=$((count + 1)) - # ec "$flag_var is true in exclusive group" - fi - done - if (( ${#conflicting_flags[@]} > 1 )); then - # echo "found conflicting flags" - formatted_error "Error: $caller_id\n-----------------------------------------\nIncompatible flags:\nThe flags below cannot be used together\n-----------------------------------------\n" "${conflicting_flags[@]}" - should_exit=true - fi - done - # echo "should_exit: $should_exit" - # echo "unsupported_flags: ${#unsupported_flags[@]}" - if (( ${#unsupported_flags[@]} > 0 )); then - # echo "found unsupported flags 2" - formatted_error "Error: $caller_id\n-------------------------------------------------\nUnsupported flags:\nThe flags below are not supported by this script\n-------------------------------------------------\n" "${unsupported_flags[@]}" - should_exit=true - fi - if [[ $should_exit == true ]]; then - exit 1 - fi -} - - - -formatted_echo() { - local COLOR_CODE='\033[0m' # No Color - local NC='\033[0m' # No Color variable - # local message="$2" - # If the second argument is empty, then theres no color specified, so we use the first argument as the message - if [[ -z "$2" ]]; then - message="$1" - else - message="$2" - fi - - declare -A format_flags - format_flags=( - [--red]="COLOR_CODE='\033[0;31m'" - [--green]="COLOR_CODE='\033[0;32m'" - [--yellow]="COLOR_CODE='\033[0;33m'" - [--blue]="COLOR_CODE='\033[0;34m'" - [--purple]="COLOR_CODE='\033[0;35m'" - [--cyan]="COLOR_CODE='\033[0;36m'" - ) - exclusive_format_flags=( - "--red --green --yellow --blue --purple --cyan" - ) - - # Pass the arguments with the namespace "FORMAT" - process_flags format_flags exclusive_format_flags "FORMAT" "$@" - - # Process the message - local text - text=$(printf "%b" "$message") - - # Expand any escaped characters in the input (e.g., \n) - local lines=() - local max_length=0 - - # Read the text line by line and find the maximum length - while IFS= read -r line; do - lines+=("$line") - if (( ${#line} > max_length )); then - max_length=${#line} - fi - done <<< "$text" - - # Create the top and bottom borders based on the maximum line length - local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') - - # Print the formatted box with selected color - printf "\n${COLOR_CODE}%s${NC}\n" "$border" - for line in "${lines[@]}"; do - printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" - done - printf "${COLOR_CODE}%s${NC}\n" "$border" -} \ No newline at end of file diff --git a/electron/build/entitlements.mac.plist b/electron/build/entitlements.mac.plist new file mode 100644 index 00000000..7ace8487 --- /dev/null +++ b/electron/build/entitlements.mac.plist @@ -0,0 +1,18 @@ + + + + + com.apple.security.cs.allow-unsigned-executable-memory + + com.apple.security.cs.allow-jit + + com.apple.security.network.client + + com.apple.security.network.server + + com.apple.security.files.user-selected.read-write + + com.apple.security.inherit + + + diff --git a/electron/build/icon.icns b/electron/build/icon.icns new file mode 100644 index 00000000..28214fcf Binary files /dev/null and b/electron/build/icon.icns differ diff --git a/electron/build/icon.png b/electron/build/icon.png new file mode 100644 index 00000000..8b9f8611 Binary files /dev/null and b/electron/build/icon.png differ diff --git a/electron/main.js b/electron/main.js new file mode 100644 index 00000000..7d4168a1 --- /dev/null +++ b/electron/main.js @@ -0,0 +1,226 @@ +const { app, BrowserWindow, ipcMain } = require('electron'); +const { autoUpdater } = require('electron-updater'); +const path = require('path'); +const { spawn } = require('child_process'); +const getPort = require('get-port'); +const http = require('http'); + +let mainWindow = null; +let backendProcess = null; +let backendPort = null; + +const isPackaged = app.isPackaged; +const isDev = process.env.ELECTRON_DEV === '1'; +const iconPath = path.join(__dirname, 'build', 'icon.png'); + +function getResourcePath(...segments) { + if (isPackaged) { + return path.join(process.resourcesPath, ...segments); + } + return path.join(__dirname, '..', ...segments); +} + +function getPythonPath() { + if (isPackaged) { + const envPath = path.join(process.resourcesPath, 'python-env'); + return path.join(envPath, 'bin', 'python3'); + } + const venvPython = path.join(__dirname, '..', 'backend', '.venv', 'bin', 'python3'); + return venvPython; +} + +function waitForBackend(port, timeoutMs = 60000) { + const start = Date.now(); + return new Promise((resolve, reject) => { + function check() { + if (Date.now() - start > timeoutMs) { + return reject(new Error('Backend startup timed out')); + } + const req = http.get(`http://127.0.0.1:${port}/api/health/check`, (res) => { + if (res.statusCode === 200) { + resolve(); + } else { + setTimeout(check, 500); + } + }); + req.on('error', () => setTimeout(check, 500)); + req.setTimeout(2000, () => { + req.destroy(); + setTimeout(check, 500); + }); + } + check(); + }); +} + +async function startBackend() { + backendPort = await getPort({ port: getPort.makeRange(8324, 8424) }); + + const pythonPath = getPythonPath(); + const backendDir = getResourcePath('backend'); + const projectRoot = isPackaged ? process.resourcesPath : path.join(__dirname, '..'); + + const env = { + ...process.env, + OPENSWARM_PACKAGED: isPackaged ? '1' : '0', + OPENSWARM_PORT: String(backendPort), + PYTHONDONTWRITEBYTECODE: '1', + }; + + if (isPackaged) { + const pythonEnvSitePackages = path.join( + process.resourcesPath, 'python-env', 'lib', + 'python3.13', 'site-packages' + ); + const debuggerDir = getResourcePath('debugger'); + env.PYTHONPATH = [projectRoot, debuggerDir, pythonEnvSitePackages].join(':'); + } + + console.log(`Starting backend: ${pythonPath} on port ${backendPort}`); + console.log(`Project root: ${projectRoot}`); + + backendProcess = spawn( + pythonPath, + ['-m', 'uvicorn', 'backend.main:app', '--host', '127.0.0.1', '--port', String(backendPort)], + { + cwd: projectRoot, + env, + stdio: ['pipe', 'pipe', 'pipe'], + } + ); + + backendProcess.stdout.on('data', (data) => { + const text = data.toString(); + process.stdout.write(`[backend] ${text}`); + }); + + backendProcess.stderr.on('data', (data) => { + const text = data.toString(); + process.stderr.write(`[backend] ${text}`); + }); + + backendProcess.on('exit', (code) => { + console.log(`Backend exited with code ${code}`); + if (code !== 0 && code !== null && mainWindow) { + mainWindow.webContents.executeJavaScript( + `document.title = "OpenSwarm (backend crashed)";` + ); + } + }); + + await waitForBackend(backendPort); + console.log(`Backend ready on port ${backendPort}`); +} + +function createWindow() { + mainWindow = new BrowserWindow({ + width: 1400, + height: 900, + minWidth: 800, + minHeight: 600, + title: 'OpenSwarm', + icon: iconPath, + titleBarStyle: 'hiddenInset', + webPreferences: { + preload: path.join(__dirname, 'preload.js'), + nodeIntegration: false, + contextIsolation: true, + webviewTag: true, + }, + }); + + if (isDev) { + mainWindow.loadURL(`http://localhost:3000`); + } else { + const frontendPath = getResourcePath('frontend', 'index.html'); + mainWindow.loadFile(frontendPath); + } + + mainWindow.on('closed', () => { + mainWindow = null; + }); +} + +function setupAutoUpdater() { + autoUpdater.autoDownload = false; + autoUpdater.autoInstallOnAppQuit = true; + + autoUpdater.on('update-available', (info) => { + console.log(`Update available: ${info.version}`); + if (mainWindow) { + mainWindow.webContents.executeJavaScript( + `window.__OPENSWARM_UPDATE_AVAILABLE__ = ${JSON.stringify(info)};` + ); + } + autoUpdater.downloadUpdate(); + }); + + autoUpdater.on('update-downloaded', (info) => { + console.log(`Update downloaded: ${info.version}`); + if (mainWindow) { + mainWindow.webContents.executeJavaScript( + `window.__OPENSWARM_UPDATE_DOWNLOADED__ = ${JSON.stringify(info)};` + ); + } + }); + + autoUpdater.on('error', (err) => { + console.error('Auto-update error:', err); + }); + + autoUpdater.checkForUpdates().catch((err) => { + console.log('Update check skipped:', err.message); + }); +} + +function killBackend() { + if (backendProcess) { + console.log('Killing backend process...'); + backendProcess.kill('SIGTERM'); + setTimeout(() => { + if (backendProcess && !backendProcess.killed) { + backendProcess.kill('SIGKILL'); + } + }, 3000); + backendProcess = null; + } +} + +app.whenReady().then(async () => { + if (process.platform === 'darwin' && !isPackaged) { + try { app.dock.setIcon(iconPath); } catch (_) {} + } + + try { + if (isDev) { + backendPort = parseInt(process.env.OPENSWARM_PORT || '8324', 10); + console.log(`Dev mode: using existing backend on port ${backendPort}`); + } else { + await startBackend(); + } + createWindow(); + if (!isDev) { + setupAutoUpdater(); + } + } catch (err) { + console.error('Failed to start:', err); + app.quit(); + } +}); + +app.on('window-all-closed', () => { + if (!isDev) killBackend(); + app.quit(); +}); + +app.on('will-quit', () => { + if (!isDev) killBackend(); +}); + +app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0 && backendPort) { + createWindow(); + } +}); + +ipcMain.handle('get-backend-port', () => backendPort); diff --git a/electron/package-lock.json b/electron/package-lock.json new file mode 100644 index 00000000..5ec26fd8 --- /dev/null +++ b/electron/package-lock.json @@ -0,0 +1,5373 @@ +{ + "name": "openswarm", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "openswarm", + "version": "1.0.0", + "dependencies": { + "electron-updater": "^6.3.0", + "get-port": "^5.1.1" + }, + "devDependencies": { + "@electron/notarize": "^3.1.1", + "electron": "^33.0.0", + "electron-builder": "^25.1.0" + } + }, + "node_modules/@develar/schema-utils": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/@develar/schema-utils/-/schema-utils-2.6.5.tgz", + "integrity": "sha512-0cp4PsWQ/9avqTVMCtZ+GirikIA36ikvjtHweU4/j8yLtgObI0+JUPhYFScgwlteveGB1rt3Cm8UhN04XayDig==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.0", + "ajv-keywords": "^3.4.1" + }, + "engines": { + "node": ">= 8.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/@electron/asar": { + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz", + "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "commander": "^5.0.0", + "glob": "^7.1.6", + "minimatch": "^3.0.4" + }, + "bin": { + "asar": "bin/asar.js" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/@electron/asar/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/asar/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@electron/asar/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@electron/get": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", + "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^2.2.0", + "fs-extra": "^8.1.0", + "got": "^11.8.5", + "progress": "^2.0.3", + "semver": "^6.2.0", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@electron/notarize": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-3.1.1.tgz", + "integrity": "sha512-uQQSlOiJnqRkTL1wlEBAxe90nVN/Fc/hEmk0bqpKk8nKjV1if/tXLHKUPePtv9Xsx90PtZU8aidx5lAiOpjkQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 22.12.0" + } + }, + "node_modules/@electron/osx-sign": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.1.tgz", + "integrity": "sha512-BAfviURMHpmb1Yb50YbCxnOY0wfwaLXH5KJ4+80zS0gUkzDX3ec23naTlEqKsN+PwYn+a1cCzM7BJ4Wcd3sGzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "compare-version": "^0.1.2", + "debug": "^4.3.4", + "fs-extra": "^10.0.0", + "isbinaryfile": "^4.0.8", + "minimist": "^1.2.6", + "plist": "^3.0.5" + }, + "bin": { + "electron-osx-flat": "bin/electron-osx-flat.js", + "electron-osx-sign": "bin/electron-osx-sign.js" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@electron/osx-sign/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/osx-sign/node_modules/isbinaryfile": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz", + "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/@electron/osx-sign/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/osx-sign/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/rebuild": { + "version": "3.6.1", + "resolved": "https://registry.npmjs.org/@electron/rebuild/-/rebuild-3.6.1.tgz", + "integrity": "sha512-f6596ZHpEq/YskUd8emYvOUne89ij8mQgjYFA5ru25QwbrRO+t1SImofdDv7kKOuWCmVOuU5tvfkbgGxIl3E/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@malept/cross-spawn-promise": "^2.0.0", + "chalk": "^4.0.0", + "debug": "^4.1.1", + "detect-libc": "^2.0.1", + "fs-extra": "^10.0.0", + "got": "^11.7.0", + "node-abi": "^3.45.0", + "node-api-version": "^0.2.0", + "node-gyp": "^9.0.0", + "ora": "^5.1.0", + "read-binary-file-arch": "^1.0.6", + "semver": "^7.3.5", + "tar": "^6.0.5", + "yargs": "^17.0.1" + }, + "bin": { + "electron-rebuild": "lib/cli.js" + }, + "engines": { + "node": ">=12.13.0" + } + }, + "node_modules/@electron/rebuild/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@electron/rebuild/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/rebuild/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@electron/rebuild/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@electron/universal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-2.0.1.tgz", + "integrity": "sha512-fKpv9kg4SPmt+hY7SVBnIYULE9QJl8L3sCfcBsnqbJwwBwAeTLokJ9TRt9y7bK0JAzIW2y78TVVjvnQEms/yyA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/asar": "^3.2.7", + "@malept/cross-spawn-promise": "^2.0.0", + "debug": "^4.3.1", + "dir-compare": "^4.2.0", + "fs-extra": "^11.1.1", + "minimatch": "^9.0.3", + "plist": "^3.1.0" + }, + "engines": { + "node": ">=16.4" + } + }, + "node_modules/@electron/universal/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@electron/universal/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@electron/universal/node_modules/fs-extra": { + "version": "11.3.4", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", + "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/@electron/universal/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@electron/universal/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@electron/universal/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@gar/promisify": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.3.tgz", + "integrity": "sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", + "integrity": "sha512-1DpKU0Z5ThltBwjNySMC14g0CkbyhCaz9FkhxqNsZI6uAPJXFS8cMXlBKo26FJ8ZuW6S9GCMcR9IO5k2X5/9Fg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/@malept/flatpak-bundler": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@malept/flatpak-bundler/-/flatpak-bundler-0.4.0.tgz", + "integrity": "sha512-9QOtNffcOF/c1seMCDnjckb3R9WHcG34tky+FHpNKKCW0wc/scYLwMtO+ptyGUfMW0/b/n4qRiALlaFHc9Oj7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.0", + "lodash": "^4.17.15", + "tmp-promise": "^3.0.2" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@malept/flatpak-bundler/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/@npmcli/fs": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-2.1.2.tgz", + "integrity": "sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@gar/promisify": "^1.1.3", + "semver": "^7.3.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@npmcli/fs/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@npmcli/move-file": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@npmcli/move-file/-/move-file-2.0.1.tgz", + "integrity": "sha512-mJd2Z5TjYWq/ttPLLGqArdtnC74J6bOzg4rMDnN+p1xTacZ2yPRCk2y0oSWQtygLR9YVQXgOcONrwtnk3JupxQ==", + "deprecated": "This functionality has been moved to @npmcli/fs", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4", + "rimraf": "^3.0.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sindresorhus/is": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", + "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@szmarczak/http-timer": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", + "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "dev": true, + "license": "MIT", + "dependencies": { + "defer-to-connect": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@tootallnate/once": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", + "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@types/cacheable-request": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", + "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "*", + "@types/keyv": "^3.1.4", + "@types/node": "*", + "@types/responselike": "^1.0.0" + } + }, + "node_modules/@types/debug": { + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ms": "*" + } + }, + "node_modules/@types/fs-extra": { + "version": "9.0.13", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.13.tgz", + "integrity": "sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/keyv": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", + "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ms": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", + "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.37", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", + "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/plist": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@types/plist/-/plist-3.0.5.tgz", + "integrity": "sha512-E6OCaRmAe4WDmWNsL/9RMqdkkzDCY1etutkflWk4c+AcjDU07Pcz1fQwTX0TQz+Pxqn9i4L1TU3UFpjnrcDgxA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*", + "xmlbuilder": ">=11.0.1" + } + }, + "node_modules/@types/responselike": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", + "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/verror": { + "version": "1.10.11", + "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.11.tgz", + "integrity": "sha512-RlDm9K7+o5stv0Co8i8ZRGxDbrTxhJtgjqjFyVh/tXQyl/rYtTKlnTvZ88oSTeYREWurwx20Js4kTuKCsFkUtg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/@types/yauzl": { + "version": "2.10.3", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", + "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.8.11", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", + "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/7zip-bin": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/7zip-bin/-/7zip-bin-5.2.0.tgz", + "integrity": "sha512-ukTPVhqG4jNzMro2qA9HSCSSVJN3aN7tlb+hfqYCt3ER0yWroeA2VR38MNrOHLQ/cVj+DaIMad0kFCtWWowh/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/agentkeepalive": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.6.0.tgz", + "integrity": "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "humanize-ms": "^1.2.1" + }, + "engines": { + "node": ">= 8.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-keywords": { + "version": "3.5.2", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "ajv": "^6.9.1" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/app-builder-bin": { + "version": "5.0.0-alpha.10", + "resolved": "https://registry.npmjs.org/app-builder-bin/-/app-builder-bin-5.0.0-alpha.10.tgz", + "integrity": "sha512-Ev4jj3D7Bo+O0GPD2NMvJl+PGiBAfS7pUGawntBNpCbxtpncfUixqFj9z9Jme7V7s3LBGqsWZZP54fxBX3JKJw==", + "dev": true, + "license": "MIT" + }, + "node_modules/app-builder-lib": { + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/app-builder-lib/-/app-builder-lib-25.1.8.tgz", + "integrity": "sha512-pCqe7dfsQFBABC1jeKZXQWhGcCPF3rPCXDdfqVKjIeWBcXzyC1iOWZdfFhGl+S9MyE/k//DFmC6FzuGAUudNDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@develar/schema-utils": "~2.6.5", + "@electron/notarize": "2.5.0", + "@electron/osx-sign": "1.3.1", + "@electron/rebuild": "3.6.1", + "@electron/universal": "2.0.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": "25.1.7", + "builder-util-runtime": "9.2.10", + "chromium-pickle-js": "^0.2.0", + "config-file-ts": "0.2.8-rc1", + "debug": "^4.3.4", + "dotenv": "^16.4.5", + "dotenv-expand": "^11.0.6", + "ejs": "^3.1.8", + "electron-publish": "25.1.7", + "form-data": "^4.0.0", + "fs-extra": "^10.1.0", + "hosted-git-info": "^4.1.0", + "is-ci": "^3.0.0", + "isbinaryfile": "^5.0.0", + "js-yaml": "^4.1.0", + "json5": "^2.2.3", + "lazy-val": "^1.0.5", + "minimatch": "^10.0.0", + "resedit": "^1.7.0", + "sanitize-filename": "^1.6.3", + "semver": "^7.3.8", + "tar": "^6.1.12", + "temp-file": "^3.4.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "dmg-builder": "25.1.8", + "electron-builder-squirrel-windows": "25.1.8" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/notarize": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-2.5.0.tgz", + "integrity": "sha512-jNT8nwH1f9X5GEITXaQ8IF/KdskvIkOFfB2CvwumsveVidzpSc+mvhhTMdAGSYF3O+Nq49lJ7y+ssODRXu06+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "fs-extra": "^9.0.1", + "promise-retry": "^2.0.1" + }, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/app-builder-lib/node_modules/@electron/notarize/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/app-builder-lib/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/app-builder-lib/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/app-builder-lib/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/aproba": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.1.0.tgz", + "integrity": "sha512-tLIEcj5GuR2RSTnxNKdkK0dJ/GrC7P38sUkiDmDuHfsHmbagTFAxDVIBltoklXEVIQ/f14IL8IMJ5pn9Hez1Ew==", + "dev": true, + "license": "ISC" + }, + "node_modules/archiver": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-5.3.2.tgz", + "integrity": "sha512-+25nxyyznAXF7Nef3y0EbBeqmGZgeN/BxHX29Rs39djAfaFalmQ89SE6CWyDCHzGL0yt/ycBtNOmGTW0FyGWNw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^2.1.0", + "async": "^3.2.4", + "buffer-crc32": "^0.2.1", + "readable-stream": "^3.6.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^2.2.0", + "zip-stream": "^4.1.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/archiver-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-2.1.0.tgz", + "integrity": "sha512-bEL/yUb/fNNiNTuUz979Z0Yg5L+LzLxGJz8x79lYmR54fmTIb6ob/hNQgkQnIUDWIFjZVQwl9Xs356I6BAMHfw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.1.4", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^2.0.0" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/archiver-utils/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/archiver-utils/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/archiver-utils/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/are-we-there-yet": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-3.0.1.tgz", + "integrity": "sha512-QZW4EDmGwlYur0Yyf/b2uGucHQMa8aFUP7eu9ddR73vvhFyt4V0Vl3QHPcTNJ8l6qYOBdxgXdnBXQrHilfRQBg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "license": "Python-2.0" + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-exit-hook": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/async-exit-hook/-/async-exit-hook-2.0.1.tgz", + "integrity": "sha512-NW2cX8m1Q7KPA7a5M2ULQeZ2wR5qI5PAbw5L0UOMxdioVk9PMZ0h1TmyZEkPYrCvYjDlFICusOu1dlEKAAeXBw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/bluebird-lst": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/bluebird-lst/-/bluebird-lst-1.0.9.tgz", + "integrity": "sha512-7B1Rtx82hjnSD4PGLAjVWeYH3tHAcVUmChh85a3lltKQm6FresXh9ErQo6oAv6CqxttczC3/kEg8SY5NluPuUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bluebird": "^3.5.5" + } + }, + "node_modules/boolean": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz", + "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/brace-expansion": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", + "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/builder-util": { + "version": "25.1.7", + "resolved": "https://registry.npmjs.org/builder-util/-/builder-util-25.1.7.tgz", + "integrity": "sha512-7jPjzBwEGRbwNcep0gGNpLXG9P94VA3CPAZQCzxkFXiV2GMQKlziMbY//rXPI7WKfhsvGgFXjTcXdBEwgXw9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/debug": "^4.1.6", + "7zip-bin": "~5.2.0", + "app-builder-bin": "5.0.0-alpha.10", + "bluebird-lst": "^1.0.9", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "cross-spawn": "^7.0.3", + "debug": "^4.3.4", + "fs-extra": "^10.1.0", + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "is-ci": "^3.0.0", + "js-yaml": "^4.1.0", + "source-map-support": "^0.5.19", + "stat-mode": "^1.0.0", + "temp-file": "^3.4.0" + } + }, + "node_modules/builder-util-runtime": { + "version": "9.2.10", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.2.10.tgz", + "integrity": "sha512-6p/gfG1RJSQeIbz8TK5aPNkoztgY1q5TgmGFMAXcY8itsGW6Y2ld1ALsZ5UJn8rog7hKF3zHx5iQbNQ8uLcRlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/builder-util/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/builder-util/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/builder-util/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/cacache": { + "version": "16.1.3", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-16.1.3.tgz", + "integrity": "sha512-/+Emcj9DAXxX4cwlLmRI9c166RuL3w30zp4R7Joiv2cQTtTtA+jeuCAjH3ZlGnYS3tKENSrKhAzVVP9GVyzeYQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "@npmcli/fs": "^2.1.0", + "@npmcli/move-file": "^2.0.0", + "chownr": "^2.0.0", + "fs-minipass": "^2.1.0", + "glob": "^8.0.1", + "infer-owner": "^1.0.4", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "mkdirp": "^1.0.4", + "p-map": "^4.0.0", + "promise-inflight": "^1.0.1", + "rimraf": "^3.0.2", + "ssri": "^9.0.0", + "tar": "^6.1.11", + "unique-filename": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/cacache/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/cacache/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/cacache/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/cacache/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/cacache/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cacheable-lookup": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", + "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.6.0" + } + }, + "node_modules/cacheable-request": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", + "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^4.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^6.0.1", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/chromium-pickle-js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/chromium-pickle-js/-/chromium-pickle-js-0.2.0.tgz", + "integrity": "sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ci-info": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/sibiraj-s" + } + ], + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", + "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^3.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-truncate": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz", + "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "slice-ansi": "^3.0.0", + "string-width": "^4.2.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clone": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", + "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/clone-response": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz", + "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "license": "ISC", + "bin": { + "color-support": "bin.js" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz", + "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/compare-version": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", + "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/compress-commons": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-4.1.2.tgz", + "integrity": "sha512-D3uMHtGc/fcO1Gt1/L7i1e33VOvD4A9hfQLP+6ewd+BvG/gQ84Yh4oftEhAdjSMgBgwGL+jsppT7JYNpo6MHHg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "buffer-crc32": "^0.2.13", + "crc32-stream": "^4.0.2", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-file-ts": { + "version": "0.2.8-rc1", + "resolved": "https://registry.npmjs.org/config-file-ts/-/config-file-ts-0.2.8-rc1.tgz", + "integrity": "sha512-GtNECbVI82bT4RiDIzBSVuTKoSHufnU7Ce7/42bkWZJZFLjmDF2WBpVsvRkhKCfKBnTBb3qZrBwPpFBU/Myvhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.3.12", + "typescript": "^5.4.3" + } + }, + "node_modules/config-file-ts/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/config-file-ts/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/config-file-ts/node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/config-file-ts/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/crc": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/crc/-/crc-3.8.0.tgz", + "integrity": "sha512-iX3mfgcTMIq3ZKLIsVFAbv7+Mc10kxabAGQb8HvjA1o3T1PIYprbakQ65d3I+2HGHt6nSKkM9PYjgoJO2KcFBQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.1.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-4.0.3.tgz", + "integrity": "sha512-NT7w2JVU7DFroFdYkeq8cywxrgjPHWkdX1wjpRQXPX5Asews3tA+Ght6lddQO5Mkumffp3X7GEqku3epj2toIw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^3.4.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decompress-response/node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defaults": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", + "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/defer-to-connect": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", + "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/detect-node": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", + "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/dir-compare": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-4.2.0.tgz", + "integrity": "sha512-2xMCmOoMrdQIPHdsTawECdNPwlVFB9zGcz3kuhmBO6U3oU+UQjsue0i8ayLKpgBcm+hcXPMVSGUN9d+pvJ6+VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "^3.0.5", + "p-limit": "^3.1.0 " + } + }, + "node_modules/dir-compare/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/dir-compare/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/dir-compare/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dmg-builder": { + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/dmg-builder/-/dmg-builder-25.1.8.tgz", + "integrity": "sha512-NoXo6Liy2heSklTI5OIZbCgXC1RzrDQsZkeEwXhdOro3FT1VBOvbubvscdPnjVuQ4AMwwv61oaH96AbiYg9EnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "25.1.8", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "fs-extra": "^10.1.0", + "iconv-lite": "^0.6.2", + "js-yaml": "^4.1.0" + }, + "optionalDependencies": { + "dmg-license": "^1.0.11" + } + }, + "node_modules/dmg-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/dmg-builder/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/dmg-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/dmg-license": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/dmg-license/-/dmg-license-1.0.11.tgz", + "integrity": "sha512-ZdzmqwKmECOWJpqefloC5OJy1+WZBBse5+MR88z9g9Zn4VY+WYUkAyojmhzJckH5YbbZGcYIuGAkY5/Ys5OM2Q==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "@types/plist": "^3.0.1", + "@types/verror": "^1.10.3", + "ajv": "^6.10.0", + "crc": "^3.8.0", + "iconv-corefoundation": "^1.1.7", + "plist": "^3.0.4", + "smart-buffer": "^4.0.2", + "verror": "^1.10.0" + }, + "bin": { + "dmg-license": "bin/dmg-license.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dotenv-expand": { + "version": "11.0.7", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-11.0.7.tgz", + "integrity": "sha512-zIHwmZPRshsCdpMDyVsqGmgyP0yT8GAgXUnkdAoJisxvf33k7yO6OuoKmcTGuXPWSsm8Oh88nZicRLA9Y0rUeA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dotenv": "^16.4.5" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/electron": { + "version": "33.4.11", + "resolved": "https://registry.npmjs.org/electron/-/electron-33.4.11.tgz", + "integrity": "sha512-xmdAs5QWRkInC7TpXGNvzo/7exojubk+72jn1oJL7keNeIlw7xNglf8TGtJtkR4rWC5FJq0oXiIXPS9BcK2Irg==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^2.0.0", + "@types/node": "^20.9.0", + "extract-zip": "^2.0.1" + }, + "bin": { + "electron": "cli.js" + }, + "engines": { + "node": ">= 12.20.55" + } + }, + "node_modules/electron-builder": { + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/electron-builder/-/electron-builder-25.1.8.tgz", + "integrity": "sha512-poRgAtUHHOnlzZnc9PK4nzG53xh74wj2Jy7jkTrqZ0MWPoHGh1M2+C//hGeYdA+4K8w4yiVCNYoLXF7ySj2Wig==", + "dev": true, + "license": "MIT", + "dependencies": { + "app-builder-lib": "25.1.8", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "dmg-builder": "25.1.8", + "fs-extra": "^10.1.0", + "is-ci": "^3.0.0", + "lazy-val": "^1.0.5", + "simple-update-notifier": "2.0.0", + "yargs": "^17.6.2" + }, + "bin": { + "electron-builder": "cli.js", + "install-app-deps": "install-app-deps.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/electron-builder-squirrel-windows": { + "version": "25.1.8", + "resolved": "https://registry.npmjs.org/electron-builder-squirrel-windows/-/electron-builder-squirrel-windows-25.1.8.tgz", + "integrity": "sha512-2ntkJ+9+0GFP6nAISiMabKt6eqBB0kX1QqHNWFWAXgi0VULKGisM46luRFpIBiU3u/TDmhZMM8tzvo2Abn3ayg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "app-builder-lib": "25.1.8", + "archiver": "^5.3.1", + "builder-util": "25.1.7", + "fs-extra": "^10.1.0" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder-squirrel-windows/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-builder/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-builder/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-builder/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-publish": { + "version": "25.1.7", + "resolved": "https://registry.npmjs.org/electron-publish/-/electron-publish-25.1.7.tgz", + "integrity": "sha512-+jbTkR9m39eDBMP4gfbqglDd6UvBC7RLh5Y0MhFSsc6UkGHj9Vj9TWobxevHYMMqmoujL11ZLjfPpMX+Pt6YEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/fs-extra": "^9.0.11", + "builder-util": "25.1.7", + "builder-util-runtime": "9.2.10", + "chalk": "^4.1.2", + "fs-extra": "^10.1.0", + "lazy-val": "^1.0.5", + "mime": "^2.5.2" + } + }, + "node_modules/electron-publish/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-publish/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-publish/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/electron-updater": { + "version": "6.8.3", + "resolved": "https://registry.npmjs.org/electron-updater/-/electron-updater-6.8.3.tgz", + "integrity": "sha512-Z6sgw3jgbikWKXei1ENdqFOxBP0WlXg3TtKfz0rgw2vIZFJUyI4pD7ZN7jrkm7EoMK+tcm/qTnPUdqfZukBlBQ==", + "license": "MIT", + "dependencies": { + "builder-util-runtime": "9.5.1", + "fs-extra": "^10.1.0", + "js-yaml": "^4.1.0", + "lazy-val": "^1.0.5", + "lodash.escaperegexp": "^4.1.2", + "lodash.isequal": "^4.5.0", + "semver": "~7.7.3", + "tiny-typed-emitter": "^2.1.0" + } + }, + "node_modules/electron-updater/node_modules/builder-util-runtime": { + "version": "9.5.1", + "resolved": "https://registry.npmjs.org/builder-util-runtime/-/builder-util-runtime-9.5.1.tgz", + "integrity": "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "sax": "^1.2.4" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/electron-updater/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/electron-updater/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/electron-updater/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/electron-updater/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "iconv-lite": "^0.6.2" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/err-code": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/err-code/-/err-code-2.0.3.tgz", + "integrity": "sha512-2bmlRpNKBxT/CRmPOlyISQpNj+qSeYvcym/uT0Jx2bMOlKLtSy1ZmLuVxSEKKyor/N5yhvp/ZiG1oE3DEYMSFA==", + "dev": true, + "license": "MIT" + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es6-error": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", + "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/exponential-backoff": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/exponential-backoff/-/exponential-backoff-3.1.3.tgz", + "integrity": "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/extract-zip": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", + "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "debug": "^4.1.1", + "get-stream": "^5.1.0", + "yauzl": "^2.10.0" + }, + "bin": { + "extract-zip": "cli.js" + }, + "engines": { + "node": ">= 10.17.0" + }, + "optionalDependencies": { + "@types/yauzl": "^2.9.1" + } + }, + "node_modules/extsprintf": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.4.1.tgz", + "integrity": "sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA==", + "dev": true, + "engines": [ + "node >=0.6.0" + ], + "license": "MIT", + "optional": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/filelist": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.6.tgz", + "integrity": "sha512-5giy2PkLYY1cP39p17Ech+2xlpTRL9HLspOfEgm0L6CwBXBTgsK5ou0JtzYuepxkaQ/tvhCFIJ5uXo0OrM2DxA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/foreground-child/node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gauge": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-4.0.4.tgz", + "integrity": "sha512-f9m+BEN5jkg6a0fZjleidjN51VE1X+mPFQ2DJ0uv1V39oCLCbsGe6yjbBnp7eK7z/+GAon99a3nHuqbuuthyPg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.3", + "console-control-strings": "^1.1.0", + "has-unicode": "^2.0.1", + "signal-exit": "^3.0.7", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.5" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz", + "integrity": "sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==", + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/global-agent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" + }, + "engines": { + "node": ">=10.0" + } + }, + "node_modules/global-agent/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/got": { + "version": "11.8.6", + "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", + "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^4.0.0", + "@szmarczak/http-timer": "^4.0.5", + "@types/cacheable-request": "^6.0.1", + "@types/responselike": "^1.0.0", + "cacheable-lookup": "^5.0.3", + "cacheable-request": "^7.0.2", + "decompress-response": "^6.0.0", + "http2-wrapper": "^1.0.0-beta.5.2", + "lowercase-keys": "^2.0.0", + "p-cancelable": "^2.0.0", + "responselike": "^2.0.0" + }, + "engines": { + "node": ">=10.19.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/http-cache-semantics": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/http2-wrapper": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", + "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.0.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/humanize-ms": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz", + "integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.0.0" + } + }, + "node_modules/iconv-corefoundation": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", + "integrity": "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ==", + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "dependencies": { + "cli-truncate": "^2.1.0", + "node-addon-api": "^1.6.3" + }, + "engines": { + "node": "^8.11.2 || >=10" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/infer-owner": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/infer-owner/-/infer-owner-1.0.4.tgz", + "integrity": "sha512-IClj+Xz94+d7irH5qRyfJonOdfTzuDaifE6ZPWfx0N0+/ATZCbuTPq2prFl526urkQd90WyUKIh1DfBQ2hMz9A==", + "dev": true, + "license": "ISC" + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, + "node_modules/is-ci": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-3.0.1.tgz", + "integrity": "sha512-ZYvCgrefwqoQ6yTyYUbQu64HsITZ3NfKX1lzaEYdkTDcfKzzCI/wthRRYKkdjHKFVgNiXKAKm65Zo1pk2as/QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ci-info": "^3.2.0" + }, + "bin": { + "is-ci": "bin.js" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-interactive": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", + "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-lambda": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", + "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/isbinaryfile": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-5.0.7.tgz", + "integrity": "sha512-gnWD14Jh3FzS3CPhF0AxNOJ8CxqeblPTADzI38r0wt8ZyQl5edpy75myt08EG2oKvpyiqSqsx+Wkz9vtkbTqYQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/gjtorikian/" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "license": "MIT", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/lazy-val": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/lazy-val/-/lazy-val-1.0.5.tgz", + "integrity": "sha512-0/BnGCCfyUMkBpeDgWihanIAF9JmZhHBgUhEqzvf+adhNGLoP6TaiI5oF8oyb3I45P+PcnrqihSf01M0l0G5+Q==", + "license": "MIT" + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.defaults": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/lodash.defaults/-/lodash.defaults-4.2.0.tgz", + "integrity": "sha512-qjxPLHd3r5DnsdGacqOMU6pb/avJzdh9tFX2ymgoZE27BmjXrNy/y4LoaiTeAb+O3gL8AfpJGtqfX/ae2leYYQ==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.difference": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.difference/-/lodash.difference-4.5.0.tgz", + "integrity": "sha512-dS2j+W26TQ7taQBGN8Lbbq04ssV3emRw4NY58WErlTO29pIqS0HmoT5aJ9+TUQ1N3G+JOZSji4eugsWwGp9yPA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.escaperegexp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz", + "integrity": "sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==", + "license": "MIT" + }, + "node_modules/lodash.flatten": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", + "integrity": "sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "deprecated": "This package is deprecated. Use require('node:util').isDeepStrictEqual instead.", + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/lodash.union": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/lodash.union/-/lodash.union-4.6.0.tgz", + "integrity": "sha512-c4pB2CdGrGdjMKYLA+XiRDO7Y0PRQbm/Gzg8qMj+QH+pFVAoTp5sBpO0odL3FjoPCGjK96p6qsP+yQoiLoOBcw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/make-fetch-happen": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-10.2.1.tgz", + "integrity": "sha512-NgOPbRiaQM10DYXvN3/hhGVI2M5MtITFryzBGxHM5p4wnFxsVCbxkrBrDsk+EZ5OB4jEOT7AjDxtdF+KVEFT7w==", + "dev": true, + "license": "ISC", + "dependencies": { + "agentkeepalive": "^4.2.1", + "cacache": "^16.1.0", + "http-cache-semantics": "^4.1.0", + "http-proxy-agent": "^5.0.0", + "https-proxy-agent": "^5.0.0", + "is-lambda": "^1.0.1", + "lru-cache": "^7.7.1", + "minipass": "^3.1.6", + "minipass-collect": "^1.0.2", + "minipass-fetch": "^2.0.3", + "minipass-flush": "^1.0.5", + "minipass-pipeline": "^1.2.4", + "negotiator": "^0.6.3", + "promise-retry": "^2.0.1", + "socks-proxy-agent": "^7.0.0", + "ssri": "^9.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/make-fetch-happen/node_modules/http-proxy-agent": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", + "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tootallnate/once": "2", + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/make-fetch-happen/node_modules/lru-cache": { + "version": "7.18.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", + "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/matcher": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "escape-string-regexp": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mime": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-2.6.0.tgz", + "integrity": "sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-fn": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", + "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "10.2.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", + "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-collect": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/minipass-collect/-/minipass-collect-1.0.2.tgz", + "integrity": "sha512-6T6lH0H8OG9kITm/Jm6tdooIbogG9e0tLgpY6mphXSm/A9u8Nq1ryBG+Qspiub9LjWlBPsPS3tWQ/Botq4FdxA==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-2.1.2.tgz", + "integrity": "sha512-LT49Zi2/WMROHYoqGgdlQIZh8mLPZmOrN2NdJjMXxYe4nkN6FUyuPuOAOedNJDrx0IRGg9+4guZewtp8hE6TxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.1.6", + "minipass-sized": "^1.0.3", + "minizlib": "^2.1.2" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + }, + "optionalDependencies": { + "encoding": "^0.1.13" + } + }, + "node_modules/minipass-flush": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/minipass-flush/-/minipass-flush-1.0.5.tgz", + "integrity": "sha512-JmQSYYpPUqX5Jyn1mXaRwOda1uQ8HP5KAT/oDSLCzt1BYRhQU0/hDtsB1ufZfEEzMZ9aAVmsBw8+FWsIXlClWw==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/minipass-pipeline": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", + "integrity": "sha512-xuIq7cIOt09RPRJ19gdi4b+RiNvDFYe5JH+ggNvBqGqpQXcru3PcRmOZuHBKWK1Txf9+cQ+HMVN4d6z46LZP7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minipass-sized": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", + "integrity": "sha512-MbkQQ2CTiBMlA2Dm/5cY+9SWFEN8pzzOXi6rlM5Xxq0Yqbda5ZQy9sU75a673FE9ZK0Zsbr6Y5iP6u9nktfg2g==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.4", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.4.tgz", + "integrity": "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.88.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.88.0.tgz", + "integrity": "sha512-At6b4UqIEVudaqPsXjmUO1r/N5BUr4yhDGs5PkBE8/oG5+TfLPhFechiskFsnT6Ql0VfUXbalUUCbfXxtj7K+w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-abi/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-1.7.2.tgz", + "integrity": "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-api-version": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/node-api-version/-/node-api-version-0.2.1.tgz", + "integrity": "sha512-2xP/IGGMmmSQpI1+O/k72jF/ykvZ89JeuKX3TLJAYPDVLUalrshrLHkeVcCCZqG/eEa635cr8IBYzgnDvM2O8Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + } + }, + "node_modules/node-api-version/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-gyp": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-9.4.1.tgz", + "integrity": "sha512-OQkWKbjQKbGkMf/xqI1jjy3oCTgMKJac58G2+bjZb3fza6gW2YrCSdMQYaoTb70crvE//Gngr4f0AgVHmqHvBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "env-paths": "^2.2.0", + "exponential-backoff": "^3.1.1", + "glob": "^7.1.4", + "graceful-fs": "^4.2.6", + "make-fetch-happen": "^10.0.3", + "nopt": "^6.0.0", + "npmlog": "^6.0.0", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.2", + "which": "^2.0.2" + }, + "bin": { + "node-gyp": "bin/node-gyp.js" + }, + "engines": { + "node": "^12.13 || ^14.13 || >=16" + } + }, + "node_modules/node-gyp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/nopt": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-6.0.0.tgz", + "integrity": "sha512-ZwLpbTgdhuZUnZzjd7nb1ZV+4DoiC6/sfiVKok72ym/4Tlf+DFdlHYmT2JPmcNNWV6Pi3SDf1kT+A4r9RTuT9g==", + "dev": true, + "license": "ISC", + "dependencies": { + "abbrev": "^1.0.0" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/normalize-url": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", + "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npmlog": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-6.0.2.tgz", + "integrity": "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg==", + "deprecated": "This package is no longer supported.", + "dev": true, + "license": "ISC", + "dependencies": { + "are-we-there-yet": "^3.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^4.0.3", + "set-blocking": "^2.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", + "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-fn": "^2.1.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", + "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.1.0", + "chalk": "^4.1.0", + "cli-cursor": "^3.1.0", + "cli-spinners": "^2.5.0", + "is-interactive": "^1.0.0", + "is-unicode-supported": "^0.1.0", + "log-symbols": "^4.1.0", + "strip-ansi": "^6.0.0", + "wcwidth": "^1.0.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-cancelable": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", + "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/path-scurry/node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/pe-library": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/pe-library/-/pe-library-0.4.1.tgz", + "integrity": "sha512-eRWB5LBz7PpDu4PUlwT0PhnQfTQJlDDdPa35urV4Osrm0t0AqQFGn+UIkU3klZvwJ8KPO3VbBFsXquA6p6kqZw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/plist": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", + "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@xmldom/xmldom": "^0.8.8", + "base64-js": "^1.5.1", + "xmlbuilder": "^15.1.1" + }, + "engines": { + "node": ">=10.4.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/promise-inflight": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", + "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/promise-retry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/promise-retry/-/promise-retry-2.0.1.tgz", + "integrity": "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "err-code": "^2.0.2", + "retry": "^0.12.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/quick-lru": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", + "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-binary-file-arch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/read-binary-file-arch/-/read-binary-file-arch-1.0.6.tgz", + "integrity": "sha512-BNg9EN3DD3GsDXX7Aa8O4p92sryjkmzYYgmgTAc6CA4uGLEDzFfxOxugu21akOxpcXHiEgsYkC6nPsQvLLLmEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4" + }, + "bin": { + "read-binary-file-arch": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "peer": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/readdir-glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "peer": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resedit": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/resedit/-/resedit-1.7.2.tgz", + "integrity": "sha512-vHjcY2MlAITJhC0eRD/Vv8Vlgmu9Sd3LX9zZvtGzU5ZImdTN3+d6e/4mnTyV8vEbyf1sgNIrWxhWlrys52OkEA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pe-library": "^0.4.1" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/jet2jet" + } + }, + "node_modules/resolve-alpn": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", + "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/responselike": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", + "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/restore-cursor": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", + "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^5.1.0", + "signal-exit": "^3.0.2" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/rimraf": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", + "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/roarr": { + "version": "2.15.4", + "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", + "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true, + "dependencies": { + "boolean": "^3.0.1", + "detect-node": "^2.0.4", + "globalthis": "^1.0.1", + "json-stringify-safe": "^5.0.1", + "semver-compare": "^1.0.0", + "sprintf-js": "^1.1.2" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sanitize-filename": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", + "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", + "dev": true, + "license": "WTFPL OR ISC", + "dependencies": { + "truncate-utf8-bytes": "^1.0.0" + } + }, + "node_modules/sax": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", + "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/semver-compare": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz", + "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/serialize-error": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "type-fest": "^0.13.1" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "license": "ISC" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/simple-update-notifier": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz", + "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/simple-update-notifier/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/slice-ansi": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz", + "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks": { + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ip-address": "^10.0.1", + "smart-buffer": "^4.2.0" + }, + "engines": { + "node": ">= 10.0.0", + "npm": ">= 3.0.0" + } + }, + "node_modules/socks-proxy-agent": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-7.0.0.tgz", + "integrity": "sha512-Fgl0YPZ902wEsAyiQ+idGd1A7rSFx/ayC1CQVMw5P+EQx2V0SgpGtf6OKFhVjPflPUl9YMmEOnmfjCdMUsygww==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^6.0.2", + "debug": "^4.3.3", + "socks": "^2.6.2" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/socks-proxy-agent/node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", + "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", + "dev": true, + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ssri": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-9.0.1.tgz", + "integrity": "sha512-o57Wcn66jMQvfHG1FlYbWeZWW/dHZhJXjpIcTfXldXEk5nz5lStPo3mK0OJQfGR3RbZUlbISexbljkJzuEj/8Q==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^3.1.1" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/stat-mode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/stat-mode/-/stat-mode-1.0.0.tgz", + "integrity": "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/sumchecker": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", + "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", + "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "deprecated": "Old versions of tar are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^5.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/tar/node_modules/minipass": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", + "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" + } + }, + "node_modules/temp-file": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/temp-file/-/temp-file-3.4.0.tgz", + "integrity": "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-exit-hook": "^2.0.1", + "fs-extra": "^10.0.0" + } + }, + "node_modules/temp-file/node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/temp-file/node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/temp-file/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/tiny-typed-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-typed-emitter/-/tiny-typed-emitter-2.1.0.tgz", + "integrity": "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA==", + "license": "MIT" + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tmp-promise": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/tmp-promise/-/tmp-promise-3.0.3.tgz", + "integrity": "sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tmp": "^0.2.0" + } + }, + "node_modules/truncate-utf8-bytes": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", + "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "utf8-byte-length": "^1.0.1" + } + }, + "node_modules/type-fest": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/unique-filename": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-2.0.1.tgz", + "integrity": "sha512-ODWHtkkdx3IAR+veKxFV+VBkUMcN+FaqzUUd7IZzt+0zhDZFPFxhlqwPF3YQvMHx1TD0tdgYl+kuPnJ8E6ql7A==", + "dev": true, + "license": "ISC", + "dependencies": { + "unique-slug": "^3.0.0" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/unique-slug": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-3.0.0.tgz", + "integrity": "sha512-8EyMynh679x/0gqE9fT9oilG+qEt+ibFyqjuVTsZn1+CMxH+XLlpvr2UZx4nVcCwTpx81nICr2JQFkM+HPLq4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "imurmurhash": "^0.1.4" + }, + "engines": { + "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + } + }, + "node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8-byte-length": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", + "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", + "dev": true, + "license": "(WTFPL OR MIT)" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/verror": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.1.tgz", + "integrity": "sha512-veufcmxri4e3XSrT0xwfUR7kguIkaxBeosDg00yDWhk49wdwkSUrvvsm7nc75e1PUyvIeZj6nS8VQRYz2/S4Xg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/wcwidth": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", + "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", + "dev": true, + "license": "MIT", + "dependencies": { + "defaults": "^1.0.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/xmlbuilder": { + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", + "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zip-stream": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-4.1.1.tgz", + "integrity": "sha512-9qv4rlDiopXg4E69k+vMHjNN63YFMe9sZMrdlvKnCjlCRWeCBswPPMPUfx+ipsAWq1LXHe70RcbaHdJJpS6hyQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "archiver-utils": "^3.0.4", + "compress-commons": "^4.1.2", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + }, + "node_modules/zip-stream/node_modules/archiver-utils": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-3.0.4.tgz", + "integrity": "sha512-KVgf4XQVrTjhyWmx6cte4RxonPLR9onExufI1jhvw/MQ4BB6IsZD5gT8Lq+u/+pRkWna/6JoHpiQioaqFP5Rzw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "glob": "^7.2.3", + "graceful-fs": "^4.2.0", + "lazystream": "^1.0.0", + "lodash.defaults": "^4.2.0", + "lodash.difference": "^4.5.0", + "lodash.flatten": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.union": "^4.6.0", + "normalize-path": "^3.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">= 10" + } + } + } +} diff --git a/electron/package.json b/electron/package.json new file mode 100644 index 00000000..3c31d9a1 --- /dev/null +++ b/electron/package.json @@ -0,0 +1,114 @@ +{ + "name": "openswarm", + "version": "1.0.0", + "description": "OpenSwarm — AI Agent Orchestrator", + "main": "main.js", + "scripts": { + "start": "electron .", + "dev": "ELECTRON_DEV=1 electron .", + "dist": "electron-builder --mac --publish never", + "dist:publish": "electron-builder --mac --publish always", + "dist:all": "electron-builder --mac --win --linux" + }, + "dependencies": { + "electron-updater": "^6.3.0", + "get-port": "^5.1.1" + }, + "devDependencies": { + "@electron/notarize": "^3.1.1", + "electron": "^33.0.0", + "electron-builder": "^25.1.0" + }, + "build": { + "appId": "com.clusterlabs.openswarm", + "productName": "OpenSwarm", + "directories": { + "output": "dist" + }, + "icon": "build/icon.png", + "mac": { + "icon": "build/icon.icns", + "target": [ + { + "target": "dmg", + "arch": [ + "arm64", + "x64" + ] + }, + { + "target": "zip", + "arch": [ + "arm64", + "x64" + ] + } + ], + "category": "public.app-category.developer-tools", + "hardenedRuntime": true, + "entitlements": "build/entitlements.mac.plist", + "entitlementsInherit": "build/entitlements.mac.plist" + }, + "dmg": { + "title": "OpenSwarm", + "contents": [ + { + "x": 130, + "y": 220 + }, + { + "x": 410, + "y": 220, + "type": "link", + "path": "/Applications" + } + ] + }, + "extraResources": [ + { + "from": "../frontend/dist", + "to": "frontend", + "filter": [ + "**/*" + ] + }, + { + "from": "../backend", + "to": "backend", + "filter": [ + "**/*", + "!__pycache__/**", + "!**/__pycache__/**", + "!.venv/**", + "!*.pyc" + ] + }, + { + "from": "../debugger", + "to": "debugger", + "filter": [ + "**/*", + "!__pycache__/**", + "!**/__pycache__/**", + "!*.pyc", + "!.venv/**", + "!**/.venv/**", + "!**/node_modules/**" + ] + }, + { + "from": "python-env", + "to": "python-env", + "filter": [ + "**/*" + ] + } + ], + "publish": { + "provider": "github", + "owner": "openswarm-ai", + "repo": "production" + }, + "afterSign": "scripts/notarize.js" + } +} diff --git a/electron/preload.js b/electron/preload.js new file mode 100644 index 00000000..e523d844 --- /dev/null +++ b/electron/preload.js @@ -0,0 +1,11 @@ +const { contextBridge, ipcRenderer } = require('electron'); + +(async () => { + const port = await ipcRenderer.invoke('get-backend-port'); + + contextBridge.exposeInMainWorld('__OPENSWARM_PORT__', port); + + contextBridge.exposeInMainWorld('openswarm', { + getBackendPort: () => port, + }); +})(); diff --git a/electron/scripts/notarize.js b/electron/scripts/notarize.js new file mode 100644 index 00000000..76bf1c05 --- /dev/null +++ b/electron/scripts/notarize.js @@ -0,0 +1,31 @@ +const { notarize } = require('@electron/notarize'); + +exports.default = async function notarizing(context) { + const { electronPlatformName, appOutDir } = context; + if (electronPlatformName !== 'darwin') return; + + if (process.env.CSC_IDENTITY_AUTO_DISCOVERY === 'false') { + console.log('Skipping notarization (CSC_IDENTITY_AUTO_DISCOVERY=false)'); + return; + } + + if (!process.env.APPLE_ID || !process.env.APPLE_TEAM_ID) { + console.log('Skipping notarization (APPLE_ID or APPLE_TEAM_ID not set)'); + return; + } + + const appName = context.packager.appInfo.productFilename; + const appPath = `${appOutDir}/${appName}.app`; + + console.log(`Notarizing ${appPath}...`); + + await notarize({ + appBundleId: 'com.clusterlabs.openswarm', + appPath, + appleId: process.env.APPLE_ID, + appleIdPassword: process.env.APPLE_APP_SPECIFIC_PASSWORD, + teamId: process.env.APPLE_TEAM_ID, + }); + + console.log('Notarization complete.'); +}; diff --git a/frontend/package.json b/frontend/package.json index 52f0e3dc..a13fa95c 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -41,6 +41,7 @@ "@types/react-dom": "^18.2.0", "@types/react-redux": "^7.1.34", "babel-loader": "^9.2.1", + "copy-webpack-plugin": "^14.0.0", "css-loader": "^6.8.0", "css-modules-types-loader": "^0.6.10", "html-webpack-plugin": "^5.5.0", diff --git a/frontend/public/index.html b/frontend/public/index.html index 1c7c3101..5294c8ad 100644 --- a/frontend/public/index.html +++ b/frontend/public/index.html @@ -4,8 +4,8 @@ Open Swarm - - + + diff --git a/frontend/run/dev.sh b/frontend/run.sh similarity index 78% rename from frontend/run/dev.sh rename to frontend/run.sh index b81fe212..0fa200ab 100755 --- a/frontend/run/dev.sh +++ b/frontend/run.sh @@ -13,14 +13,14 @@ else # echo "NOT in macOS server START" fi chmod +x "$DEV_ABSPATH" -source "$(dirname "$DEV_ABSPATH")/_utils.sh" +FRONTEND_DIR_ABSPATH="$(dirname "$DEV_ABSPATH")" -formatted_echo --green "Installing dependencies..." +echo "Installing dependencies..." cd "$FRONTEND_DIR_ABSPATH" npm install -formatted_echo --green "Building with development mode..." +echo "Building with development mode..." npm run dev # exit back to the dir that we were in before diff --git a/frontend/run/_utils.sh b/frontend/run/_utils.sh deleted file mode 100755 index 2d24af62..00000000 --- a/frontend/run/_utils.sh +++ /dev/null @@ -1,202 +0,0 @@ -#!/bin/bash -# Flag processing function with namespacing and global variable declaration -UTILS_FILE_ABSPATH="$(readlink -f "${BASH_SOURCE[0]}")" -if [[ "$OSTYPE" == "darwin"* ]]; then - # echo "In macOS utils sed START" - # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" - sed -i '' 's/\r//g' "$UTILS_FILE_ABSPATH" - # echo "In macOS utils sed END" -else - # echo "NOT in macOS utils START" - # echo "UTILS_ABSPATH: $UTILS_FILE_ABSPATH" - sed -i 's/\r//g' "$UTILS_FILE_ABSPATH" - # echo "NOT in macOS utils END" -fi -chmod +x "$UTILS_FILE_ABSPATH" - -RUN_DIR_ABSPATH="$(dirname "$UTILS_FILE_ABSPATH")" -FRONTEND_DIR_ABSPATH="$(dirname "$RUN_DIR_ABSPATH")" - -formatted_error() { - # Arguments: error message and array of conflicting flags - local initial_message="$1" - shift - local conflicting_flags=("$@") - - # Red color for the error message box - local COLOR_CODE='\033[0;31m' - local NC='\033[0m' # No Color - - # Start the error message with the initial message - local error_message="$initial_message" - - # Add each conflicting flag on a new line with indentation - for conflict in "${conflicting_flags[@]}"; do - error_message+="\n $conflict" # Replacing `\t` with four spaces - done - - # Prepare for printing by finding max length of each line in the message - local lines=() - local max_length=0 - - # Use printf to interpret new lines and calculate max length with spaces instead of tabs - while IFS= read -r line; do - # Substitute tabs with spaces for consistent width measurement - local line_with_spaces="${line//$'\t'/ }" - lines+=("$line_with_spaces") - if (( ${#line_with_spaces} > max_length )); then - max_length=${#line_with_spaces} - fi - done <<< "$(printf "$error_message")" - - # Create the top and bottom borders based on the maximum line length - local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') - - # Print the formatted error message with a red box - printf "\n${COLOR_CODE}%s${NC}\n" "$border" - for line in "${lines[@]}"; do - printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" - done - printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "" - printf "${COLOR_CODE}%s${NC}\n" "$border" -} - - -function process_flags() { - local -n flags_to_commands="$1" # Reference to the dictionary of flags and commands - local -n exclusives="$2" # Reference to the list of exclusive flag groups - local namespace="$3" # Unique prefix for variables - local calling_script_name="$(basename "$(readlink -f "${BASH_SOURCE[1]}")")" - local caller_id="${FUNCNAME[1]}" - local should_exit=false - if [ "$caller_id" == "main" ]; then - caller_id="$calling_script_name" - fi - # echo "caller_id: $caller_id" - # echo "Initializing flags with namespace: $namespace" - - # Initialize flag variables with namespacing in the sourcing script context - for flag in "${!flags_to_commands[@]}"; do - # Convert flag to uppercase variable name and apply namespace, e.g., MYAPP_FLAG1 - local flag_var="${namespace}_${flag^^}" # Prefix and uppercase - flag_var="${flag_var//-}" # Remove dashes - eval "declare -g $flag_var=false" # Initialize as global false - - # Diagnostic output for each initialized variable - # echo "Initialized $flag_var as false" - done - - # echo "Parsing command line arguments: $@" - - # Parse command line arguments and set flags - local unsupported_flags=() - # local potential_typos=() - for arg in "$@"; do - # echo "Processing argument: $arg" - if [[ -n "${flags_to_commands[$arg]}" ]]; then - # echo "Flag found: $arg" - local flag_var="${namespace}_${arg^^}" # Prefix and uppercase - flag_var="${flag_var//-}" # Remove dashes - eval "declare -g $flag_var=true" # Set as global true - # echo "Set $flag_var to true" - # echo "Executing command for $arg: ${flags_to_commands[$arg]}" - eval "${flags_to_commands[$arg]}" - else - if [[ "$arg" == "-"* ]]; then - # echo "Flag not found: $arg" - unsupported_flags+=("$arg") - if [[ -n "${flags_to_commands[-$arg]}" ]]; then - # echo "Potential typo: $arg" - unsupported_flags+=("\t*Note: Potential typo detected.") - unsupported_flags+=("\tDid you mean: -$arg") - fi - fi - fi - done - # Check exclusive flag groups for conflicts - # echo "Checking exclusive flag groups" - for group in "${exclusives[@]}"; do - local count=0 - local conflicting_flags=() - for flag in $group; do - local flag_var="${namespace}_${flag^^}" - flag_var="${flag_var//-}" - if [[ "$(eval echo "\$$flag_var")" == "true" ]]; then - conflicting_flags+=("$flag") - # count=$((count + 1)) - # ec "$flag_var is true in exclusive group" - fi - done - if (( ${#conflicting_flags[@]} > 1 )); then - # echo "found conflicting flags" - formatted_error "Error: $caller_id\n-----------------------------------------\nIncompatible flags:\nThe flags below cannot be used together\n-----------------------------------------\n" "${conflicting_flags[@]}" - should_exit=true - fi - done - # echo "should_exit: $should_exit" - # echo "unsupported_flags: ${#unsupported_flags[@]}" - if (( ${#unsupported_flags[@]} > 0 )); then - # echo "found unsupported flags 2" - formatted_error "Error: $caller_id\n-------------------------------------------------\nUnsupported flags:\nThe flags below are not supported by this script\n-------------------------------------------------\n" "${unsupported_flags[@]}" - should_exit=true - fi - if [[ $should_exit == true ]]; then - exit 1 - fi -} - - - -formatted_echo() { - local COLOR_CODE='\033[0m' # No Color - local NC='\033[0m' # No Color variable - # local message="$2" - # If the second argument is empty, then theres no color specified, so we use the first argument as the message - if [[ -z "$2" ]]; then - message="$1" - else - message="$2" - fi - - declare -A format_flags - format_flags=( - [--red]="COLOR_CODE='\033[0;31m'" - [--green]="COLOR_CODE='\033[0;32m'" - [--yellow]="COLOR_CODE='\033[0;33m'" - [--blue]="COLOR_CODE='\033[0;34m'" - [--purple]="COLOR_CODE='\033[0;35m'" - [--cyan]="COLOR_CODE='\033[0;36m'" - ) - exclusive_format_flags=( - "--red --green --yellow --blue --purple --cyan" - ) - - # Pass the arguments with the namespace "FORMAT" - process_flags format_flags exclusive_format_flags "FORMAT" "$@" - - # Process the message - local text - text=$(printf "%b" "$message") - - # Expand any escaped characters in the input (e.g., \n) - local lines=() - local max_length=0 - - # Read the text line by line and find the maximum length - while IFS= read -r line; do - lines+=("$line") - if (( ${#line} > max_length )); then - max_length=${#line} - fi - done <<< "$text" - - # Create the top and bottom borders based on the maximum line length - local border=$(printf '%*s' "$((max_length + 4))" '' | tr ' ' '-') - - # Print the formatted box with selected color - printf "\n${COLOR_CODE}%s${NC}\n" "$border" - for line in "${lines[@]}"; do - printf "${COLOR_CODE}| %-*s |${NC}\n" "$max_length" "$line" - done - printf "${COLOR_CODE}%s${NC}\n" "$border" -} \ No newline at end of file diff --git a/frontend/src/app/Main.tsx b/frontend/src/app/Main.tsx index b6161048..7c213e95 100644 --- a/frontend/src/app/Main.tsx +++ b/frontend/src/app/Main.tsx @@ -1,6 +1,6 @@ import React, { useMemo, useEffect } from 'react'; import { Provider } from 'react-redux'; -import { BrowserRouter, Routes, Route } from 'react-router-dom'; +import { HashRouter, Routes, Route } from 'react-router-dom'; import { ThemeProvider as MuiThemeProvider, createTheme, CssBaseline } from '@mui/material'; import { store } from '../shared/state/store'; import { useAppDispatch } from '@/shared/hooks'; @@ -138,7 +138,7 @@ const ThemedApp: React.FC = () => { return ( - + @@ -155,7 +155,7 @@ const ThemedApp: React.FC = () => { - + ); }; diff --git a/frontend/src/app/components/DirectoryBrowser.tsx b/frontend/src/app/components/DirectoryBrowser.tsx index 768832d0..00294dec 100644 --- a/frontend/src/app/components/DirectoryBrowser.tsx +++ b/frontend/src/app/components/DirectoryBrowser.tsx @@ -20,8 +20,9 @@ import InsertDriveFileOutlinedIcon from '@mui/icons-material/InsertDriveFileOutl import ArrowUpwardIcon from '@mui/icons-material/ArrowUpward'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; import { BrowseResult } from '@/shared/state/settingsSlice'; +import { API_BASE } from '@/shared/config'; -const API_BASE = `http://${window.location.hostname}:8324/api/settings`; +const SETTINGS_API = `${API_BASE}/settings`; export interface ContextPath { path: string; @@ -48,7 +49,7 @@ const DirectoryBrowser: React.FC = ({ open, onClose, onSe setError(null); setSelected(null); try { - const res = await fetch(`${API_BASE}/browse-directories?path=${encodeURIComponent(path)}`); + const res = await fetch(`${SETTINGS_API}/browse-directories?path=${encodeURIComponent(path)}`); if (!res.ok) { const data = await res.json(); throw new Error(data.detail || 'Failed to browse'); diff --git a/frontend/src/app/components/ElementSelectionContext.tsx b/frontend/src/app/components/ElementSelectionContext.tsx index cfe88433..d42e5d1f 100644 --- a/frontend/src/app/components/ElementSelectionContext.tsx +++ b/frontend/src/app/components/ElementSelectionContext.tsx @@ -9,7 +9,7 @@ export interface SelectedElement { computedStyles: Record; screenshot?: string; boundingRect: { x: number; y: number; width: number; height: number }; - semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'dom-element'; + semanticType?: 'agent-card' | 'message' | 'tool-call' | 'tool-group' | 'view-card' | 'browser-card' | 'dom-element'; semanticLabel?: string; semanticData?: Record; } @@ -18,6 +18,8 @@ interface ElementSelectionContextValue { selectMode: boolean; toggleSelectMode: () => void; setSelectMode: (active: boolean) => void; + excludeSelectId: string | null; + setExcludeSelectId: (id: string | null) => void; selectedElements: SelectedElement[]; addSelectedElement: (el: SelectedElement) => void; updateSelectedElement: (id: string, patch: Partial) => void; @@ -34,11 +36,15 @@ export function useElementSelection() { export const ElementSelectionProvider: React.FC<{ children: React.ReactNode }> = ({ children }) => { const [selectMode, setSelectMode] = useState(false); + const [excludeSelectId, setExcludeSelectId] = useState(null); const [selectedElements, setSelectedElements] = useState([]); const iframeRef = useRef(null); const toggleSelectMode = useCallback(() => { - setSelectMode((prev) => !prev); + setSelectMode((prev) => { + if (prev) setExcludeSelectId(null); + return !prev; + }); }, []); const addSelectedElement = useCallback((el: SelectedElement) => { @@ -66,6 +72,8 @@ export const ElementSelectionProvider: React.FC<{ children: React.ReactNode }> = selectMode, toggleSelectMode, setSelectMode, + excludeSelectId, + setExcludeSelectId, selectedElements, addSelectedElement, updateSelectedElement, diff --git a/frontend/src/app/components/KeyboardShortcutsHelp.tsx b/frontend/src/app/components/KeyboardShortcutsHelp.tsx index 117fc2ac..c634aaf2 100644 --- a/frontend/src/app/components/KeyboardShortcutsHelp.tsx +++ b/frontend/src/app/components/KeyboardShortcutsHelp.tsx @@ -10,6 +10,8 @@ const shortcuts = [ { key: 'd', description: 'Go to Dashboard' }, { key: 't', description: 'Go to Templates' }, { key: '1-9', description: 'Open agent by position' }, + { key: '⌘M', description: 'Add View' }, + { key: '⌘O', description: 'History' }, { key: 'Shift+A', description: 'Approve all pending' }, { key: 'Shift+D', description: 'Deny all pending' }, { key: '?', description: 'Show this help' }, diff --git a/frontend/src/app/components/Layout/AppShell.tsx b/frontend/src/app/components/Layout/AppShell.tsx index dddc98ba..8319ed75 100644 --- a/frontend/src/app/components/Layout/AppShell.tsx +++ b/frontend/src/app/components/Layout/AppShell.tsx @@ -20,6 +20,9 @@ import ViewQuiltIcon from '@mui/icons-material/ViewQuilt'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import AddIcon from '@mui/icons-material/Add'; import SettingsIcon from '@mui/icons-material/Settings'; +import ViewSidebarOutlinedIcon from '@mui/icons-material/ViewSidebarOutlined'; +import ArrowBackOutlinedIcon from '@mui/icons-material/ArrowBackOutlined'; +import ArrowForwardOutlinedIcon from '@mui/icons-material/ArrowForwardOutlined'; import Settings from '@/app/pages/Settings/Settings'; import GlobalApprovalOverlay from '@/app/components/GlobalApprovalOverlay'; import { useAppDispatch, useAppSelector } from '@/shared/hooks'; @@ -42,6 +45,7 @@ const AppShell: React.FC = () => { const navigate = useNavigate(); const location = useLocation(); const [dashboardsExpanded, setDashboardsExpanded] = useState(false); + const [sidebarCollapsed, setSidebarCollapsed] = useState(false); const dashboardItems = useAppSelector((state) => state.dashboards.items); const dashboardList = Object.values(dashboardItems).sort( @@ -79,7 +83,101 @@ const AppShell: React.FC = () => { }; return ( - + + {/* Draggable title bar */} + + + setSidebarCollapsed((prev) => !prev)} + sx={{ + WebkitAppRegion: 'no-drag', + color: c.text.tertiary, + p: 0.5, + borderRadius: 1, + '&:hover': { color: c.text.secondary, bgcolor: `${c.text.tertiary}14` }, + }} + > + + + + + navigate(-1)} + sx={{ + WebkitAppRegion: 'no-drag', + color: c.text.tertiary, + p: 0.5, + borderRadius: 1, + '&:hover': { color: c.text.secondary, bgcolor: `${c.text.tertiary}14` }, + }} + > + + + + + navigate(1)} + sx={{ + WebkitAppRegion: 'no-drag', + color: c.text.tertiary, + p: 0.5, + borderRadius: 1, + '&:hover': { color: c.text.secondary, bgcolor: `${c.text.tertiary}14` }, + }} + > + + + + + + + + + + OpenSwarm + + + + + + {!sidebarCollapsed && ( { flexDirection: 'column', }} > - - - - - Open Swarm - - - Agent Orchestrator - - - { + )} + diff --git a/frontend/src/app/components/useDomElementSelector.ts b/frontend/src/app/components/useDomElementSelector.ts index fb2f4068..79031c0d 100644 --- a/frontend/src/app/components/useDomElementSelector.ts +++ b/frontend/src/app/components/useDomElementSelector.ts @@ -31,12 +31,16 @@ const SEMANTIC_LABELS: Record = { 'tool-call': 'Tool Call', 'tool-group': 'Tool Group', 'view-card': 'View', + 'browser-card': 'Browser', }; -function findSelectableAncestor(target: Element): Element | null { +function findSelectableAncestor(target: Element, excludeId?: string | null): Element | null { let current: Element | null = target; while (current) { - if (current.hasAttribute(SELECT_ATTR)) return current; + if (current.hasAttribute(SELECT_ATTR)) { + if (excludeId && current.getAttribute(SELECT_ID_ATTR) === excludeId) return null; + return current; + } current = current.parentElement; } return null; @@ -114,6 +118,11 @@ export function useDomElementSelector(): DomSelectorState { const isDraggingRef = useRef(false); const dragBoundsRef = useRef<{ left: number; top: number; right: number; bottom: number } | null>(null); + const excludeIdRef = useRef(null); + useEffect(() => { + excludeIdRef.current = ctx?.excludeSelectId ?? null; + }, [ctx?.excludeSelectId]); + const selectedIdsRef = useRef(new Map()); useEffect(() => { const map = new Map(); @@ -161,10 +170,12 @@ export function useDomElementSelector(): DomSelectorState { const allSelectables = document.querySelectorAll(`[${SELECT_ATTR}]`); const preview: DragPreviewElement[] = []; const seen = new Set(); + const excId = excludeIdRef.current; allSelectables.forEach((el) => { + const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; + if (excId && selectId === excId) return; const rect = el.getBoundingClientRect(); if (rectsIntersect(b, { left: rect.left, top: rect.top, right: rect.right, bottom: rect.bottom })) { - const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; if (seen.has(selectId)) return; seen.add(selectId); const type = el.getAttribute(SELECT_ATTR) || ''; @@ -200,7 +211,7 @@ export function useDomElementSelector(): DomSelectorState { return; } - const selectable = findSelectableAncestor(target); + const selectable = findSelectableAncestor(target, excludeIdRef.current); if (!selectable) { setOverlay(EMPTY_OVERLAY); hoveredRef.current = null; @@ -231,7 +242,7 @@ export function useDomElementSelector(): DomSelectorState { if (e.button !== 0) return; const target = e.target as Element; // Only start drag on "empty" canvas areas (not on selectable elements) - if (target && findSelectableAncestor(target)) return; + if (target && findSelectableAncestor(target, excludeIdRef.current)) return; dragOriginRef.current = { x: e.clientX, y: e.clientY }; isDraggingRef.current = false; }, []); @@ -250,7 +261,10 @@ export function useDomElementSelector(): DomSelectorState { const allSelectables = document.querySelectorAll(`[${SELECT_ATTR}]`); const processed = new Set(); + const excId = excludeIdRef.current; allSelectables.forEach((el) => { + const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; + if (excId && selectId === excId) return; const rect = el.getBoundingClientRect(); const elRect = { left: rect.left, @@ -260,7 +274,6 @@ export function useDomElementSelector(): DomSelectorState { }; if (rectsIntersect(dr, elRect)) { - const selectId = el.getAttribute(SELECT_ID_ATTR) || ''; if (processed.has(selectId)) return; processed.add(selectId); diff --git a/frontend/src/app/pages/AgentChat/AgentChat.tsx b/frontend/src/app/pages/AgentChat/AgentChat.tsx index a341c848..580fc187 100644 --- a/frontend/src/app/pages/AgentChat/AgentChat.tsx +++ b/frontend/src/app/pages/AgentChat/AgentChat.tsx @@ -629,6 +629,7 @@ const AgentChat: React.FC = ({ sessionId: sessionIdProp, onClose isRunning={!isDraft && (session.status === 'running' || session.status === 'waiting_approval')} onStop={handleStop} contextEstimate={contextEstimate} + sessionId={id} /> diff --git a/frontend/src/app/pages/AgentChat/ChatInput.tsx b/frontend/src/app/pages/AgentChat/ChatInput.tsx index 451bc4b6..6369464a 100644 --- a/frontend/src/app/pages/AgentChat/ChatInput.tsx +++ b/frontend/src/app/pages/AgentChat/ChatInput.tsx @@ -26,6 +26,8 @@ import AttachFileIcon from '@mui/icons-material/AttachFile'; import AdsClickIcon from '@mui/icons-material/AdsClick'; import CommandPicker, { CommandPickerItem, getToolGroupIcon } from '@/app/components/CommandPicker'; import { useElementSelection, SelectedElement } from '@/app/components/ElementSelectionContext'; +import { getWebview } from '@/shared/browserRegistry'; +import { API_BASE } from '@/shared/config'; import { ContextPath } from '@/app/components/DirectoryBrowser'; import { SKILL_PILL_ATTR, @@ -70,6 +72,7 @@ interface Props { contextEstimate?: { used: number; limit: number }; embedded?: boolean; autoFocus?: boolean; + sessionId?: string; } export interface ChatInputHandle { @@ -127,7 +130,7 @@ const ContextRing: React.FC<{ used: number; limit: number; accentColor: string; ); }; -const ChatInput = forwardRef(({ onSend, disabled, mode, onModeChange, model, onModelChange, isRunning, onStop, autoRunMode, contextEstimate, embedded, autoFocus }, ref) => { +const ChatInput = forwardRef(({ onSend, disabled, mode, onModeChange, model, onModelChange, isRunning, onStop, autoRunMode, contextEstimate, embedded, autoFocus, sessionId }, ref) => { const c = useClaudeTokens(); const editorRef = useRef(null); const containerRef = useRef(null); @@ -253,7 +256,7 @@ const ChatInput = forwardRef(({ onSend, disabled, mode, try { const formData = new FormData(); files.forEach((f) => formData.append('files', f)); - const resp = await fetch(`http://${window.location.hostname}:8324/api/settings/upload-files`, { + const resp = await fetch(`${API_BASE}/settings/upload-files`, { method: 'POST', body: formData, }); @@ -271,7 +274,7 @@ const ChatInput = forwardRef(({ onSend, disabled, mode, } }, []); - const handleSend = useCallback(() => { + const handleSend = useCallback(async () => { const editor = editorRef.current; if (!editor || disabled) return; const serialized = serializeEditorContent(editor, attachedSkillsRef.current); @@ -285,14 +288,47 @@ const ChatInput = forwardRef(({ onSend, disabled, mode, if (selectedEls.length > 0) { const lines: string[] = ['\n\n---\nSelected UI Elements:\n']; - selectedEls.forEach((el, i) => { - if (el.semanticType && el.semanticData) { + for (let i = 0; i < selectedEls.length; i++) { + const el = selectedEls[i]; + + if (el.semanticType === 'browser-card' && el.semanticData?.selectId) { + const wv = getWebview(el.semanticData.selectId as string); + if (wv) { + const url = el.semanticData.url || wv.getURL(); + const title = el.semanticData.name || wv.getTitle(); + lines.push(`${i + 1}. [Browser Card] ${title}`); + lines.push(` ID: ${el.semanticData.selectId}`); + lines.push(` URL: ${url}`); + + try { + const nativeImage = await wv.capturePage(); + const dataUrl = nativeImage.toDataURL(); + const base64 = dataUrl.replace(/^data:image\/\w+;base64,/, ''); + allImages.push({ data: base64, media_type: 'image/png' }); + lines.push(` [Screenshot captured and attached as image]`); + } catch { /* screenshot unavailable */ } + + try { + const pageText: string = await wv.executeJavaScript( + 'document.body.innerText.substring(0, 15000)' + ); + if (pageText?.trim()) { + lines.push(` Page text content:\n ---\n${pageText.trim().split('\n').map(l => ' ' + l).join('\n')}\n ---`); + } + } catch { /* text extraction unavailable */ } + } else { + lines.push(`${i + 1}. [Browser Card] ${el.semanticLabel || ''}`); + if (el.semanticData.selectId) lines.push(` ID: ${el.semanticData.selectId}`); + if (el.semanticData.url) lines.push(` URL: ${el.semanticData.url}`); + } + } else if (el.semanticType && el.semanticData) { const typeLabel = { 'agent-card': 'Agent Card', 'message': 'Message', 'tool-call': 'Tool Call', 'tool-group': 'Tool Group', 'view-card': 'View Card', + 'browser-card': 'Browser Card', 'dom-element': 'Element', }[el.semanticType] || el.semanticType; lines.push(`${i + 1}. [${typeLabel}] ${el.semanticLabel || ''}`); @@ -318,7 +354,7 @@ const ChatInput = forwardRef(({ onSend, disabled, mode, const base64 = el.screenshot.replace(/^data:image\/\w+;base64,/, ''); allImages.push({ data: base64, media_type: 'image/png' }); } - }); + } trimmed += lines.join('\n'); } @@ -963,7 +999,12 @@ const ChatInput = forwardRef(({ onSend, disabled, mode, { + if (!elementSelection.selectMode && sessionId) { + elementSelection.setExcludeSelectId(sessionId); + } + elementSelection.toggleSelectMode(); + }} sx={{ p: 0.5, ...(elementSelection.selectMode diff --git a/frontend/src/app/pages/AgentChat/DiffViewer.tsx b/frontend/src/app/pages/AgentChat/DiffViewer.tsx index 5957f23f..30d8b821 100644 --- a/frontend/src/app/pages/AgentChat/DiffViewer.tsx +++ b/frontend/src/app/pages/AgentChat/DiffViewer.tsx @@ -6,8 +6,9 @@ import Tooltip from '@mui/material/Tooltip'; import RefreshIcon from '@mui/icons-material/Refresh'; import DifferenceIcon from '@mui/icons-material/Difference'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; +import { API_BASE } from '@/shared/config'; -const API_BASE = `http://${window.location.hostname}:8324/api/agents`; +const AGENTS_API = `${API_BASE}/agents`; interface Props { sessionId: string; @@ -22,7 +23,7 @@ const DiffViewer: React.FC = ({ sessionId }) => { const fetchDiff = async () => { setLoading(true); try { - const res = await fetch(`${API_BASE}/sessions/${sessionId}/diff`); + const res = await fetch(`${AGENTS_API}/sessions/${sessionId}/diff`); const data = await res.json(); setDiff(data.diff || ''); } catch { diff --git a/frontend/src/app/pages/AgentChat/ViewBubble.tsx b/frontend/src/app/pages/AgentChat/ViewBubble.tsx index 9ae3af4b..08b06544 100644 --- a/frontend/src/app/pages/AgentChat/ViewBubble.tsx +++ b/frontend/src/app/pages/AgentChat/ViewBubble.tsx @@ -10,6 +10,7 @@ import OpenInFullIcon from '@mui/icons-material/OpenInFull'; import CloseIcon from '@mui/icons-material/Close'; import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import { useAppSelector } from '@/shared/hooks'; +import { SERVE_BASE } from '@/shared/state/outputsSlice'; import { useClaudeTokens } from '@/shared/styles/ThemeContext'; import ViewPreview from '../Views/ViewPreview'; @@ -41,7 +42,7 @@ const ViewBubble: React.FC = ({ toolInput, toolResult, isStreaming }) => const outputColor = c.accent.primary; const outputIcon = output?.icon || 'view_quilt'; const hasPreview = !!frontendCode.trim(); - const serveUrl = outputId ? `/api/outputs/${outputId}/serve/index.html` : undefined; + const serveUrl = outputId ? `${SERVE_BASE}/${outputId}/serve/index.html` : undefined; const inputEntries = Object.entries(inputData); if (isStreaming && !hasPreview) { diff --git a/frontend/src/app/pages/Dashboard/AgentCard.tsx b/frontend/src/app/pages/Dashboard/AgentCard.tsx index b1e6ddce..e15cd1c7 100644 --- a/frontend/src/app/pages/Dashboard/AgentCard.tsx +++ b/frontend/src/app/pages/Dashboard/AgentCard.tsx @@ -169,6 +169,12 @@ interface Props { cardHeight: number; zoom?: number; spawnFrom?: { x: number; y: number }; + isSelected?: boolean; + multiDragDelta?: { dx: number; dy: number } | null; + onCardSelect?: (id: string, type: 'agent' | 'view', shiftKey: boolean) => void; + onDragStart?: (id: string, type: 'agent' | 'view') => void; + onDragMove?: (dx: number, dy: number) => void; + onDragEnd?: (dx: number, dy: number, didDrag: boolean) => void; } const MIN_W = 480; @@ -177,7 +183,10 @@ const EXPANDED_OVERLAY_H = 620; const SPAWN_SPRING = { type: 'spring' as const, stiffness: 400, damping: 28, mass: 0.6 }; -const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth, cardHeight, zoom = 1, spawnFrom }) => { +const AgentCard: React.FC = ({ + session, expanded, cardX, cardY, cardWidth, cardHeight, zoom = 1, spawnFrom, + isSelected = false, multiDragDelta, onCardSelect, onDragStart, onDragMove, onDragEnd, +}) => { const c = useClaudeTokens(); const dispatch = useAppDispatch(); @@ -199,6 +208,7 @@ const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth const [isDragging, setIsDragging] = useState(false); const [localDragPos, setLocalDragPos] = useState<{ x: number; y: number } | null>(null); const didDrag = useRef(false); + const justDraggedRef = useRef(false); const handleDragPointerDown = useCallback((e: React.PointerEvent) => { e.preventDefault(); @@ -207,7 +217,8 @@ const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth didDrag.current = false; setIsDragging(true); (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); - }, [cardX, cardY]); + onDragStart?.(session.id, 'agent'); + }, [cardX, cardY, onDragStart, session.id]); const handleDragPointerMove = useCallback((e: React.PointerEvent) => { if (!dragState.current) return; @@ -215,31 +226,35 @@ const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth const rawDy = e.clientY - dragState.current.startY; if (!didDrag.current && Math.sqrt(rawDx * rawDx + rawDy * rawDy) < DRAG_THRESHOLD) return; didDrag.current = true; + const dx = rawDx / zoom; + const dy = rawDy / zoom; setLocalDragPos({ - x: dragState.current.origX + rawDx / zoom, - y: dragState.current.origY + rawDy / zoom, + x: dragState.current.origX + dx, + y: dragState.current.origY + dy, }); - }, [zoom]); + onDragMove?.(dx, dy); + }, [zoom, onDragMove]); const handleDragPointerUp = useCallback((e: React.PointerEvent) => { if (!dragState.current) return; + const dx = (e.clientX - dragState.current.startX) / zoom; + const dy = (e.clientY - dragState.current.startY) / zoom; if (didDrag.current) { - const dx = (e.clientX - dragState.current.startX) / zoom; - const dy = (e.clientY - dragState.current.startY) / zoom; dispatch(setCardPosition({ sessionId: session.id, x: dragState.current.origX + dx, y: dragState.current.origY + dy, })); - } else if (expanded) { - dispatch(toggleExpandSession(session.id)); + justDraggedRef.current = true; + requestAnimationFrame(() => { justDraggedRef.current = false; }); } + onDragEnd?.(dx, dy, didDrag.current); dragState.current = null; didDrag.current = false; setLocalDragPos(null); setIsDragging(false); (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); - }, [zoom, dispatch, session.id, expanded]); + }, [zoom, dispatch, session.id, onDragEnd]); // ---- Unified edge / corner resize ---- const resizeRef = useRef<{ @@ -351,10 +366,12 @@ const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth const pendingReq = session.pending_approvals[0]; const statusStyle = STATUS_COLORS[session.status] || { color: c.text.tertiary, bg: c.bg.secondary }; - const noTransition = isDragging || isResizing; + const noTransition = isDragging || isResizing || (isSelected && !!multiDragDelta); - const activeX = localResize?.x ?? localDragPos?.x ?? cardX; - const activeY = localResize?.y ?? localDragPos?.y ?? cardY; + const mdDx = (!isDragging && isSelected && multiDragDelta) ? multiDragDelta.dx : 0; + const mdDy = (!isDragging && isSelected && multiDragDelta) ? multiDragDelta.dy : 0; + const activeX = localResize?.x ?? localDragPos?.x ?? (cardX + mdDx); + const activeY = localResize?.y ?? localDragPos?.y ?? (cardY + mdDy); const activeW = localResize?.w ?? cardWidth; const activeH = localResize?.h ?? cardHeight; @@ -380,26 +397,38 @@ const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth data-select-type="agent-card" data-select-id={session.id} data-select-meta={JSON.stringify({ name: session.name || session.id, status: session.status, model: session.model, mode: session.mode })} - onClick={expanded ? undefined : () => dispatch(toggleExpandSession(session.id))} + onClick={(e: React.MouseEvent) => { + if (justDraggedRef.current) return; + onCardSelect?.(session.id, 'agent', e.shiftKey); + }} + onDoubleClick={() => dispatch(toggleExpandSession(session.id))} sx={{ position: 'relative', width: localResize ? activeW : Math.max(cardWidth, MIN_W), height: localResize ? activeH : (expanded ? Math.max(EXPANDED_OVERLAY_H, cardHeight) : 'auto'), bgcolor: c.bg.surface, - border: hasPending && !expanded - ? `1px solid ${c.status.warning}` - : expanded - ? `1px solid ${c.border.strong}` - : `1px solid ${c.border.subtle}`, + border: isSelected + ? '2px solid #3b82f6' + : hasPending && !expanded + ? `1px solid ${c.status.warning}` + : expanded + ? `1px solid ${c.border.strong}` + : `1px solid ${c.border.subtle}`, borderRadius: 3, p: 2, cursor: expanded ? 'default' : 'pointer', transition: noTransition ? 'none' : c.transition, - boxShadow: isDragging ? c.shadow.lg : expanded ? c.shadow.md : c.shadow.sm, + boxShadow: isDragging + ? c.shadow.lg + : isSelected + ? `0 0 0 1px #3b82f6, ${c.shadow.md}` + : expanded + ? c.shadow.md + : c.shadow.sm, display: 'flex', flexDirection: 'column', overflow: 'hidden', - ...(!expanded && !isDragging && { + ...(!expanded && !isDragging && !isSelected && { '&:hover': { boxShadow: c.shadow.md, borderColor: hasPending ? c.status.warning : c.border.strong, @@ -426,120 +455,131 @@ const AgentCard: React.FC = ({ session, expanded, cardX, cardY, cardWidth /> ))} - {/* Header: always visible – entire bar is draggable */} + {/* Drag zone: header + metadata – entire region above separator is draggable */} - - - - - {session.name} - - + > + + + + + {session.name} + + + + e.stopPropagation()} + sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0, ml: 0.5 }} + > + {expanded ? ( + + e.stopPropagation()} + sx={{ + color: c.text.ghost, + p: 0.5, + '&:hover': { color: c.text.secondary, bgcolor: c.bg.secondary }, + }} + > + + + + ) : ( + + e.stopPropagation()} + sx={{ + color: c.text.ghost, + p: 0.5, + '&:hover': { color: c.status.error, bgcolor: `${c.status.errorBg}` }, + }} + > + + + + )} + - e.stopPropagation()} - sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0, ml: 0.5 }} - > - {expanded ? ( - - e.stopPropagation()} - sx={{ - color: c.text.ghost, - p: 0.5, - '&:hover': { color: c.text.secondary, bgcolor: c.bg.secondary }, - }} - > - - - - ) : ( - - e.stopPropagation()} - sx={{ - color: c.text.ghost, - p: 0.5, - '&:hover': { color: c.status.error, bgcolor: `${c.status.errorBg}` }, - }} - > - - - + + {/* Metadata row */} + + + {session.model} + + + {session.mode} + + + {formatDuration(session.created_at)} + + {session.cost_usd > 0 && ( + + ${session.cost_usd.toFixed(4)} + )} - {/* Metadata row */} - - - {session.model} - - - {session.mode} - - - {formatDuration(session.created_at)} - - {session.cost_usd > 0 && ( - - ${session.cost_usd.toFixed(4)} - - )} - - {/* Expanded: inline chat fills remaining space */} {expanded && ( = { + n: 'ns-resize', s: 'ns-resize', e: 'ew-resize', w: 'ew-resize', + nw: 'nwse-resize', se: 'nwse-resize', ne: 'nesw-resize', sw: 'nesw-resize', +}; + +const HANDLE_DEFS: { dir: ResizeDir; sx: Record }[] = [ + { dir: 'n', sx: { top: -EDGE_THICKNESS / 2, left: CORNER_SIZE, right: CORNER_SIZE, height: EDGE_THICKNESS } }, + { dir: 's', sx: { bottom: -EDGE_THICKNESS / 2, left: CORNER_SIZE, right: CORNER_SIZE, height: EDGE_THICKNESS } }, + { dir: 'w', sx: { left: -EDGE_THICKNESS / 2, top: CORNER_SIZE, bottom: CORNER_SIZE, width: EDGE_THICKNESS } }, + { dir: 'e', sx: { right: -EDGE_THICKNESS / 2, top: CORNER_SIZE, bottom: CORNER_SIZE, width: EDGE_THICKNESS } }, + { dir: 'nw', sx: { top: -EDGE_THICKNESS / 2, left: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'ne', sx: { top: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'sw', sx: { bottom: -EDGE_THICKNESS / 2, left: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, + { dir: 'se', sx: { bottom: -EDGE_THICKNESS / 2, right: -EDGE_THICKNESS / 2, width: CORNER_SIZE, height: CORNER_SIZE } }, +]; + +const isElectron = navigator.userAgent.includes('Electron'); + +type WebviewElement = BrowserWebview; + +interface Props { + browserId: string; + url: string; + cardX: number; + cardY: number; + cardWidth: number; + cardHeight: number; + zoom?: number; + isSelected?: boolean; + multiDragDelta?: { dx: number; dy: number } | null; + onCardSelect?: (id: string, type: 'agent' | 'view' | 'browser', shiftKey: boolean) => void; + onDragStart?: (id: string, type: 'agent' | 'view' | 'browser') => void; + onDragMove?: (dx: number, dy: number) => void; + onDragEnd?: (dx: number, dy: number, didDrag: boolean) => void; +} + +function ensureProtocol(input: string): string { + const trimmed = input.trim(); + if (/^https?:\/\//i.test(trimmed)) return trimmed; + if (/^[a-zA-Z0-9-]+\.[a-zA-Z]{2,}/.test(trimmed)) return `https://${trimmed}`; + return trimmed; +} + +const BrowserCard: React.FC = ({ + browserId, url, cardX, cardY, cardWidth, cardHeight, zoom = 1, + isSelected = false, multiDragDelta, onCardSelect, onDragStart, onDragMove, onDragEnd, +}) => { + const c = useClaudeTokens(); + const dispatch = useAppDispatch(); + const webviewRef = useRef(null); + const activity = useBrowserActivity(browserId); + const agentActive = activity.active; + const agentAction = activity.action; + const lastAction = activity.lastAction; + + const [currentUrl, setCurrentUrl] = useState(url); + const [urlBarValue, setUrlBarValue] = useState(url); + const [pageTitle, setPageTitle] = useState(''); + const [loading, setLoading] = useState(false); + const [canGoBack, setCanGoBack] = useState(false); + const [canGoForward, setCanGoForward] = useState(false); + + // ---- Webview event wiring ---- + useEffect(() => { + if (!isElectron) return; + const wv = webviewRef.current; + if (!wv) return; + + const onNavigate = () => { + const newUrl = wv.getURL(); + setCurrentUrl(newUrl); + setUrlBarValue(newUrl); + setCanGoBack(wv.canGoBack()); + setCanGoForward(wv.canGoForward()); + dispatch(updateBrowserCardUrl({ browserId, url: newUrl })); + }; + + const onTitleUpdate = () => { + setPageTitle(wv.getTitle()); + }; + + const onLoadStart = () => setLoading(true); + const onLoadStop = () => { + setLoading(false); + onNavigate(); + onTitleUpdate(); + }; + + const onNewWindow = (e: any) => { + if (e.url) wv.loadURL(e.url); + }; + + wv.addEventListener('did-navigate', onNavigate); + wv.addEventListener('did-navigate-in-page', onNavigate); + wv.addEventListener('page-title-updated', onTitleUpdate); + wv.addEventListener('did-start-loading', onLoadStart); + wv.addEventListener('did-stop-loading', onLoadStop); + wv.addEventListener('new-window', onNewWindow); + + return () => { + wv.removeEventListener('did-navigate', onNavigate); + wv.removeEventListener('did-navigate-in-page', onNavigate); + wv.removeEventListener('page-title-updated', onTitleUpdate); + wv.removeEventListener('did-start-loading', onLoadStart); + wv.removeEventListener('did-stop-loading', onLoadStop); + wv.removeEventListener('new-window', onNewWindow); + }; + }, [browserId, dispatch]); + + useEffect(() => { + if (!isElectron) return; + const wv = webviewRef.current; + if (!wv) return; + registerWebview(browserId, wv); + return () => { unregisterWebview(browserId); }; + }, [browserId]); + + const navigate = useCallback((targetUrl: string) => { + const finalUrl = ensureProtocol(targetUrl); + setUrlBarValue(finalUrl); + if (isElectron && webviewRef.current) { + webviewRef.current.loadURL(finalUrl); + } + setCurrentUrl(finalUrl); + dispatch(updateBrowserCardUrl({ browserId, url: finalUrl })); + }, [browserId, dispatch]); + + const handleUrlKeyDown = useCallback((e: React.KeyboardEvent) => { + if (e.key === 'Enter') { + e.preventDefault(); + navigate(urlBarValue); + } + }, [navigate, urlBarValue]); + + const handleBack = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + webviewRef.current?.goBack(); + }, []); + + const handleForward = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + webviewRef.current?.goForward(); + }, []); + + const handleRefresh = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + webviewRef.current?.reload(); + }, []); + + const handleRemove = useCallback((e: React.MouseEvent) => { + e.stopPropagation(); + dispatch(removeBrowserCard(browserId)); + }, [dispatch, browserId]); + + // ---- Drag via header ---- + const DRAG_THRESHOLD = 3; + const dragState = useRef<{ startX: number; startY: number; origX: number; origY: number } | null>(null); + const [isDragging, setIsDragging] = useState(false); + const [localDragPos, setLocalDragPos] = useState<{ x: number; y: number } | null>(null); + const didDrag = useRef(false); + const justDraggedRef = useRef(false); + + const handleDragPointerDown = useCallback((e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + dragState.current = { startX: e.clientX, startY: e.clientY, origX: cardX, origY: cardY }; + didDrag.current = false; + setIsDragging(true); + (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId); + onDragStart?.(browserId, 'browser'); + }, [cardX, cardY, onDragStart, browserId]); + + const handleDragPointerMove = useCallback((e: React.PointerEvent) => { + if (!dragState.current) return; + const rawDx = e.clientX - dragState.current.startX; + const rawDy = e.clientY - dragState.current.startY; + if (!didDrag.current && Math.sqrt(rawDx * rawDx + rawDy * rawDy) < DRAG_THRESHOLD) return; + didDrag.current = true; + const dx = rawDx / zoom; + const dy = rawDy / zoom; + setLocalDragPos({ + x: dragState.current.origX + dx, + y: dragState.current.origY + dy, + }); + onDragMove?.(dx, dy); + }, [zoom, onDragMove]); + + const handleDragPointerUp = useCallback((e: React.PointerEvent) => { + if (!dragState.current) return; + const dx = (e.clientX - dragState.current.startX) / zoom; + const dy = (e.clientY - dragState.current.startY) / zoom; + if (didDrag.current) { + dispatch(setBrowserCardPosition({ + browserId, + x: dragState.current.origX + dx, + y: dragState.current.origY + dy, + })); + justDraggedRef.current = true; + requestAnimationFrame(() => { justDraggedRef.current = false; }); + } + onDragEnd?.(dx, dy, didDrag.current); + dragState.current = null; + didDrag.current = false; + setLocalDragPos(null); + setIsDragging(false); + (e.currentTarget as HTMLElement).releasePointerCapture(e.pointerId); + }, [zoom, dispatch, browserId, onDragEnd]); + + // ---- Resize ---- + const resizeRef = useRef<{ + dir: ResizeDir; startX: number; startY: number; + origX: number; origY: number; origW: number; origH: number; + } | null>(null); + const [isResizing, setIsResizing] = useState(false); + const [localResize, setLocalResize] = useState<{ x: number; y: number; w: number; h: number } | null>(null); + + const handleResizeDown = useCallback( + (dir: ResizeDir) => (e: React.PointerEvent) => { + e.preventDefault(); + e.stopPropagation(); + resizeRef.current = { + dir, startX: e.clientX, startY: e.clientY, + origX: cardX, origY: cardY, origW: cardWidth, origH: cardHeight, + }; + setIsResizing(true); + (e.target as HTMLElement).setPointerCapture(e.pointerId); + }, + [cardX, cardY, cardWidth, cardHeight], + ); + + const computeResize = useCallback( + (e: React.PointerEvent) => { + if (!resizeRef.current) return null; + const { dir, startX, startY, origX, origY, origW, origH } = resizeRef.current; + const dx = (e.clientX - startX) / zoom; + const dy = (e.clientY - startY) / zoom; + let newX = origX, newY = origY, newW = origW, newH = origH; + if (dir.includes('e')) newW = origW + dx; + if (dir.includes('w')) { newW = origW - dx; newX = origX + dx; } + if (dir.includes('s')) newH = origH + dy; + if (dir.includes('n')) { newH = origH - dy; newY = origY + dy; } + if (newW < MIN_W) { if (dir.includes('w')) newX = origX + origW - MIN_W; newW = MIN_W; } + if (newH < MIN_H) { if (dir.includes('n')) newY = origY + origH - MIN_H; newH = MIN_H; } + return { x: newX, y: newY, w: newW, h: newH }; + }, + [zoom], + ); + + const handleResizeMove = useCallback( + (e: React.PointerEvent) => { + const result = computeResize(e); + if (result) setLocalResize(result); + }, + [computeResize], + ); + + const handleResizeUp = useCallback((e: React.PointerEvent) => { + if (!resizeRef.current) return; + const result = computeResize(e); + if (result) { + dispatch(setBrowserCardPosition({ browserId, x: result.x, y: result.y })); + dispatch(setBrowserCardSize({ browserId, width: result.w, height: result.h })); + } + resizeRef.current = null; + setLocalResize(null); + setIsResizing(false); + (e.target as HTMLElement).releasePointerCapture(e.pointerId); + }, [computeResize, dispatch, browserId]); + + const mdDx = (!isDragging && isSelected && multiDragDelta) ? multiDragDelta.dx : 0; + const mdDy = (!isDragging && isSelected && multiDragDelta) ? multiDragDelta.dy : 0; + const displayX = localResize?.x ?? localDragPos?.x ?? (cardX + mdDx); + const displayY = localResize?.y ?? localDragPos?.y ?? (cardY + mdDy); + const displayW = localResize?.w ?? cardWidth; + const displayH = localResize?.h ?? cardHeight; + const noTransition = isDragging || isResizing || (isSelected && !!multiDragDelta); + + const isSecure = currentUrl.startsWith('https://'); + + const accentColor = c.accent.primary; + const accentHover = c.accent.hover; + + const agentBorder = agentActive + ? `2px solid ${accentColor}` + : isSelected ? '2px solid #3b82f6' : `1px solid ${c.border.medium}`; + + const agentShadow = agentActive + ? `0 0 0 2px ${accentColor}40, 0 0 18px ${accentColor}30, 0 0 40px ${accentColor}15` + : isDragging || isResizing + ? c.shadow.lg + : isSelected + ? `0 0 0 1px #3b82f6, ${c.shadow.md}` + : c.shadow.md; + + return ( + { + if (justDraggedRef.current) return; + onCardSelect?.(browserId, 'browser', e.shiftKey); + }} + sx={{ + position: 'absolute', + left: displayX, + top: displayY, + width: displayW, + height: displayH, + borderRadius: `${c.radius.lg}px`, + border: agentBorder, + bgcolor: c.bg.surface, + boxShadow: agentShadow, + overflow: 'hidden', + display: 'flex', + flexDirection: 'column', + zIndex: (isDragging || isResizing) ? 100 : agentActive ? 50 : 1, + transition: noTransition ? 'none' : 'box-shadow 0.4s ease, border 0.3s ease', + '&:hover .resize-handle': { opacity: 1 }, + ...(agentActive && { + animation: 'agent-glow-pulse 2s ease-in-out infinite', + '@keyframes agent-glow-pulse': { + '0%, 100%': { + boxShadow: `0 0 0 2px ${accentColor}40, 0 0 18px ${accentColor}30, 0 0 40px ${accentColor}15`, + }, + '50%': { + boxShadow: `0 0 0 3px ${accentColor}60, 0 0 28px ${accentColor}45, 0 0 56px ${accentColor}25`, + }, + }, + }), + }} + > + {/* Animated border glow (top edge overlay) */} + {agentActive && ( + + )} + + {/* Header / drag handle */} + + + + {pageTitle || 'Browser'} + + + {/* Agent activity badge */} + {agentActive && ( + + + + AI + + + )} + + + + e.stopPropagation()} + disabled={!canGoBack} + sx={{ color: c.text.muted, p: 0.4, '&:hover': { color: c.text.primary } }} + > + + + + + + + + e.stopPropagation()} + disabled={!canGoForward} + sx={{ color: c.text.muted, p: 0.4, '&:hover': { color: c.text.primary } }} + > + + + + + + + e.stopPropagation()} + sx={{ color: c.text.muted, p: 0.4, '&:hover': { color: c.text.primary } }} + > + + + + + + e.stopPropagation()} + sx={{ color: c.text.ghost, p: 0.4, '&:hover': { color: c.status.error } }} + > + + + + + + {/* URL bar */} + + {isSecure && ( + + )} + setUrlBarValue(e.target.value)} + onKeyDown={handleUrlKeyDown} + onPointerDown={(e) => e.stopPropagation()} + onFocus={(e) => (e.target as HTMLInputElement).select()} + placeholder="Enter URL..." + sx={{ + flex: 1, + fontSize: '0.76rem', + fontFamily: c.font.mono, + color: c.text.secondary, + py: 0, + '& input': { py: '3px' }, + '& input::placeholder': { color: c.text.ghost, opacity: 1 }, + }} + /> + + + {/* Loading indicator — accent-colored when agent is navigating */} + {(loading || (agentActive && agentAction === 'navigate')) && ( + + )} + + {/* Browser body */} + + {isElectron ? ( + + ) : ( + +