diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2794fac --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,36 @@ +# Run the same unit suite developers run locally on every PR and on main. +# Packaging stays in release.yml; these tests are plain Node and do not need +# Electron or a matrix of OS runners. + +name: CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + # PR code runs npm ci / npm test; do not leave GITHUB_TOKEN in git config. + persist-credentials: false + + - uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - run: npm ci + + # Production deps only: dev-only tools (Vite/esbuild) may still report + # advisories that do not affect the packaged app. + - run: npm audit --omit=dev --audit-level=high + + - run: npm test diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index bf7f9de..6947f53 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -128,9 +128,15 @@ jobs: - name: Package run: npx electron-builder ${{ matrix.args }} --publish never env: - # There is no signing certificate on a CI runner. Without this, - # electron-builder hunts for one and fails the macOS build. - CSC_IDENTITY_AUTO_DISCOVERY: false + # When WIN_CSC_LINK / CSC_LINK secrets are set, electron-builder signs + # the Windows installer and portable exe (or the macOS bundle). Without + # them, builds stay unsigned — see docs/security.md. + WIN_CSC_LINK: ${{ secrets.WIN_CSC_LINK }} + WIN_CSC_KEY_PASSWORD: ${{ secrets.WIN_CSC_KEY_PASSWORD }} + CSC_LINK: ${{ secrets.CSC_LINK }} + CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }} + # Only hunt the macOS keychain when a Developer ID cert is supplied. + CSC_IDENTITY_AUTO_DISCOVERY: ${{ secrets.CSC_LINK != '' && 'true' || 'false' }} # The agent SDK carries the Claude CLI as an optional platform package, # a 253MB binary that the app deliberately does not ship: the provider diff --git a/.gitignore b/.gitignore index 3510368..3a89e5e 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,6 @@ releases/ # Local working notes and agent config, not part of the project CLAUDE.md .claude/ + +# Local product planning notes; keep out of the shared tree +PRD.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..adc1cbc --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,81 @@ +# Changelog + +All notable changes to CloudTerm are documented here. The format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions align +with [GitHub Releases](https://github.com/BradPerbs/cloudterm/releases). + +## [1.3.1] - 2026-08-12 + +### Added + +- GitHub Actions CI (`.github/workflows/ci.yml`) runs `npm test` on every pull + request and on pushes to `main`. +- CI runs `npm audit --omit=dev` to block high-severity vulnerabilities in + production dependencies. +- Optional Authenticode / macOS code signing in release builds when + `WIN_CSC_LINK` / `CSC_LINK` repository secrets are configured (certificate + **pending procurement** — builds stay unsigned until secrets are added). See + [docs/security.md](docs/security.md). +- [Security documentation](docs/security.md): VirusTotal/SmartScreen, signing, + dependency audit, vulnerability reporting. +- First-run assistant warning explaining that tools act on live sessions and + what each approval mode means. +- Confirm dialog before switching the assistant to **Never ask**. +- Contributor docs: [assistant approval modes](docs/assistant-approvals.md), + [RDP credential lifetime](docs/rdp-credentials.md), and + [IME composition plan](docs/ime-composition.md). + +### Changed + +- npm production and dev dependencies updated (`npm audit fix`); esbuild + overridden to a patched release for the dev toolchain. +- Default assistant approval mode is **Ask before changes** (`writes`): reads + run freely; mutating tools stop for approval. +- New installs ship with `allowLocalTools: false`. +- RDP passwords are cleared from the renderer as soon as CredSSP completes; + reconnect always re-fetches credentials from the vault. See + [RDP credentials](docs/rdp-credentials.md). +- Activity log redacts `rdpPassword` and `bmcPassword` in change diffs. + +### Fixed + +- Session log transcript files use a unique exclusive path per session so two + tabs in the same second cannot interleave into one file. +- First-run approval warning only clears after **Got it** (not Escape / backdrop). +- Assistant approval acknowledgement accepts only real booleans when loading + `assistant.json`. + +## [1.3.0] - 2026-08-10 + +### Added + +- Host monitoring with desktop notifications when a saved host stops answering. +- Launch CloudTerm at sign-in (Windows). +- Check for updates on every launch. +- BMC/IPMI web UI opens in a tab with auto-login. +- App icon on every platform (`build/icon.png`). + +### Changed + +- Host cards fit every protocol kind on one row. +- Distro icons halved to match on-screen draw size. +- Renderer dependencies kept out of the packaged app bundle. +- Monitor events kept out of the notifications bell. + +## [1.2.1] - 2026-08-06 + +### Added + +- Quick connect: type an address in the host picker and connect immediately. +- Windows install and update via `winget install CloudBlast.CloudTerm`. See + [docs/winget.md](docs/winget.md). + +## [1.2.0] + +Earlier releases are listed on +[GitHub Releases](https://github.com/BradPerbs/cloudterm/releases). + +[Unreleased]: https://github.com/BradPerbs/cloudterm/compare/v1.3.0...HEAD +[1.3.0]: https://github.com/BradPerbs/cloudterm/releases/tag/v1.3.0 +[1.2.1]: https://github.com/BradPerbs/cloudterm/releases/tag/v1.2.1 +[1.2.0]: https://github.com/BradPerbs/cloudterm/releases/tag/v1.2.0 diff --git a/PRD.md b/PRD.md new file mode 100644 index 0000000..44182cb --- /dev/null +++ b/PRD.md @@ -0,0 +1,241 @@ +# CloudTerm — Product Requirements Document + +| Field | Value | +|-------|-------| +| **Product** | CloudTerm | +| **Owner** | CloudBlast | +| **Version covered** | 1.3.0 (as shipped) | +| **Status** | Living document — current product + next roadmap | +| **License** | Fair-code (free to use/modify/share; commercial resale needs a CloudBlast license) | +| **Repo** | https://github.com/BradPerbs/cloudterm | + +--- + +## 1. Vision + +CloudTerm is a **desktop multi-protocol terminal workspace**: one window for SSH, SFTP, Telnet, serial, RDP, VNC, and BMC/IPMI, with encrypted local vault storage, optional CloudBlast cloud sync, and a local AI agent that can act through live sessions (with approval). + +**North star:** Replace tool sprawl (PuTTY + WinSCP + RDP client + VNC + serial terminal + snippet managers) with a single, trustworthy, modern app that travels with the user’s setup across machines. + +--- + +## 2. Problem + +| Pain | Today without CloudTerm | +|------|-------------------------| +| Tool sprawl | Separate apps per protocol; repeated logins and context switching | +| Setup doesn’t travel | Hosts, keys, snippets, known hosts differ per laptop | +| AI is paste-driven | Assistants don’t see the live terminal; users copy/paste riskily | +| CloudBlast customers | Manually re-enter VPS details instead of seeing servers ready to connect | + +--- + +## 3. Goals & non-goals + +### Goals + +1. **One workspace** for every common remote-access protocol on Windows, macOS, and Linux. +2. **Secrets stay local** — vault-locked encrypted storage; cloud sync encrypts before upload. +3. **CloudBlast differentiation** — free encrypted sync for everyone; auto host list for CloudBlast VPS customers. +4. **Session-aware AI** — local CLIs (Claude Code / Codex / OpenCode) with approval gates; no credential tools. +5. **Trustworthy distribution** — signed installs, predictable updates, clear fair-code terms. + +### Non-goals (near term) + +- Becoming a full IDE or browser-based terminal SaaS +- Replacing enterprise PAM / jump-box products +- Storing plaintext credentials in the cloud +- Shipping AI that can extract or export vault secrets +- Guaranteeing recovery if the vault password is forgotten + +--- + +## 4. Personas + +| Persona | Needs | Success looks like | +|---------|--------|-------------------| +| **Sysadmin / SRE** | Many hosts, jump hosts, tunnels, monitoring | Folders/tags/search; reconnect; uptime checks; activity log | +| **Developer** | SSH + SFTP + occasional RDP/VNC | Fast connect, split panes, snippets, remote edit | +| **CloudBlast customer** | Servers appear without manual entry | Sign in → VPS hosts ready | +| **Multi-machine user** | Same setup on laptop + desktop | Encrypted cloud snapshot restore on sign-in | +| **AI-assisted operator** | Agent reads session and runs approved commands | Clear approval UX; safe defaults | + +--- + +## 5. Current product (v1.3) + +### 5.1 User-facing capabilities + +| Area | Capability | +|------|------------| +| **Sessions** | SSH, Telnet, serial; jump hosts; SOCKS/HTTP proxies; tabs & split panes (max 8); themes; find; broadcast; screenshots; session log | +| **Files** | SFTP (list/mkdir/delete/rename, transfers, resume, conflicts, drag-and-drop); remote edit → upload on save | +| **Networking** | Local / remote / dynamic port forwarding with traffic counters | +| **Desktops** | RDP (IronRDP WASM), VNC (noVNC, SSH-tunnelled), BMC/IPMI web UI (sandboxed webview + auto-login) | +| **Inventory** | Hosts, folders, tags, search, quick-connect; keychain (generate/import, Windows Hello/TPM); snippets with `{{placeholders}}` | +| **Import** | `~/.ssh/config`, PuTTY, MobaXterm | +| **Security** | App lock / vault (password or OS keystore); encrypted backup export/restore | +| **Cloud** | CloudBlast OAuth (PKCE, system browser); VPS server sync; encrypted cross-device snapshot | +| **Ops** | Host TCP monitoring + notifications; activity log; auto-update from GitHub Releases (winget on Windows) | +| **AI** | Claude Code / Codex / OpenCode; tools: hosts, sessions, terminal read/write, files, connect/disconnect — approval modes | +| **i18n** | In-app: en, zh, vi, pt, ru (README also offers Español — see gap below) | + +### 5.2 Architecture (summary) + +``` +Renderer (React + xterm / IronRDP / noVNC) + ↕ IPC + MessagePort (session bytes) +Main (Electron): store, vault, transport (ssh|telnet|serial), + sftp, tunnels, rdp, vnc, bmc, account, AI, monitor +``` + +- **Security defaults:** `contextIsolation`, no `nodeIntegration`, `sandbox`; secrets resolved in main; vault lock gates IPC. +- **Persistence:** `sessions.json`, `vault.json`, window state, cloud snapshot under Electron `userData`; UI prefs in `localStorage`. + +### 5.3 Platforms & distribution + +| Channel | Notes | +|---------|--------| +| GitHub Releases | NSIS + portable (Win), DMG/zip (macOS), AppImage (Linux) | +| winget | `CloudBlast.CloudTerm` — see `docs/winget.md` | +| Signing | **Unsigned** — Windows Authenticode cert **pending procurement**; CI ready when secrets are set — see [docs/security.md](docs/security.md) | + +--- + +## 6. Requirements + +### 6.1 Functional (must remain true) + +| ID | Requirement | +|----|-------------| +| F1 | User can open SSH / Telnet / serial sessions from a host record without leaving the app | +| F2 | Credentials and private keys never leave the main process (RDP CredSSP path is the documented exception) | +| F3 | Vault lock blocks sensitive IPC until unlocked | +| F4 | SFTP and tunnels can reuse an active SSH connection | +| F5 | Cloud snapshot is encrypted client-side before upload | +| F6 | AI cannot read vault secrets; destructive/session-changing tools require approval unless user opts into a looser mode | +| F7 | Auto-update can be disabled (`CLOUDBLAST_UPDATE_DISABLED`) for air-gapped use | + +### 6.2 Non-functional + +| ID | Requirement | Current gap | +|----|-------------|-------------| +| N1 | PR builds run automated tests | **Shipped (M1):** `.github/workflows/ci.yml` on PRs and `main` | +| N2 | Installers code-signed on Windows (and ideally macOS) | **Pending** — cert procurement in progress; release CI wired for signing | +| N3 | Core paths (SSH connect, SFTP, lock/unlock) covered by automated smoke | Unit tests only; no E2E | +| N4 | Cold start acceptable for SSH-only users | RDP/VNC/WASM always in bundle weight | +| N5 | CJK IME composition usable in terminal | Known limitation — documented in `docs/ime-composition.md`; fix tracked in #7 | +| N6 | Marketing locales match in-app locales | Spanish README without in-app `es` | + +--- + +## 7. Success metrics (suggested) + +| Metric | Target (directional) | +|--------|----------------------| +| Crash-free sessions / week | Track via optional anonymous telemetry later; start with GitHub issue rate | +| Time-to-first-connect (fresh install → SSH) | < 2 minutes with import or CloudBlast sync | +| Update adoption (7-day) | Majority on latest within a week of release (post-signing) | +| Test gate | `npm test` green on every PR before merge | +| AI incident rate | Near-zero reports of unexpected `run_command` without approval on default settings | + +--- + +## 8. Roadmap — bugs, enhancements, optimizations + +Priorities: **P0** ship-blocking / trust / safety · **P1** product quality · **P2** polish & scale. + +### 8.1 Bug fixes & reliability (P0–P1) + +| Priority | Item | Rationale | Notes | +|----------|------|-----------|-------| +| **P0** | Add CI workflow that runs `npm test` on PRs | Regressions can ship with release-only CI | **Done** — `.github/workflows/ci.yml` | +| **P0** | Harden AI approval defaults + first-run warning | “Never ask” + `run_command` is high risk | **Done** — default `writes`, warning, confirm for `never`; see `docs/assistant-approvals.md` | +| **P0** | Audit RDP password lifetime in renderer/WASM | Documented secret exception | **Done** — see `docs/rdp-credentials.md`; tests in `test/rdp.test.js` | +| **P0** | Fix or quarantine IME composition positioning | Blocks CJK users; zh locale is shipped | **Quarantined (M1)** — `docs/ime-composition.md`; #7 open | +| **P1** | Session restore reconnect policy | Tabs restore without live sockets | Explicit “reconnect all” or per-tab option | +| **P1** | Update download UX | `autoDownload = true` may surprise corporate users | Optional “ask before download” | +| **P1** | Align Spanish: add `es` locale **or** remove es README until ready | Docs/product mismatch | | + +### 8.2 Enhancements (P1–P2) + +| Priority | Item | Rationale | +|----------|------|-----------| +| **P1** | Code-sign Windows (and ideally macOS) builds | SmartScreen, winget, enterprise adoption | **In progress** — Windows cert pending; `release.yml` ready | +| **P1** | Split oversized modules (`ipc.js`, `store.js`, `App.jsx`, `TerminalView.jsx`) by domain | Safer reviews; fewer regressions | +| **P1** | E2E smoke: mock SSH → SFTP → lock/unlock | Catch IPC/UI wiring unit tests miss | +| **P1** | SSH/SFTP integration tests (container or mock) | Core path under-tested vs edge protocols | +| **P1** | True 1024² app icon (replace upscaled soft cloud) | Documented in `resources/README.md` | +| **P1** | Rename npm package / clarify branding (`cloudblast-ssh` → `cloudterm`) | Contributor & packaging clarity | +| **P2** | Accessibility pass on dialogs + terminal chrome | Enterprise procurement | +| **P2** | Portable winget package parity | Mentioned as future in `docs/winget.md` | +| **P2** | Document env vars & `userData` layout under `docs/` | Support / IT / contributors | +| **P2** | Gradual TypeScript on `store` / `vault` / `ai/tools` | Catch IPC/secret-field shape bugs | + +### 8.3 Optimizations (P2) + +| Priority | Item | Rationale | +|----------|------|-----------| +| **P2** | Lazy-load RDP / VNC / noVNC chunks | Faster cold start for SSH-only users | +| **P2** | Cap / paginate activity & AI event retention more aggressively | Long-lived processes; large event buffers | +| **P2** | Parallelize `npm test` (today sequential `&&`) | Faster local + CI feedback | +| **P2** | Profile WebGL terminals under multi-pane load | Cap is 8 panes; validate GPU cost | + +### 8.4 Explicitly out of scope for this PRD cycle + +- Vault password recovery without user-held secret +- Server-side decryption of cloud snapshots +- AI tools that return private keys or vault DEK material + +--- + +## 9. Security & threat notes (product constraints) + +| Topic | Stance | +|-------|--------| +| Vault | Protects against disk theft; forgotten password = data unrecoverable by design | +| Cloud snapshot | Protects DB theft in transit/at rest; **not** a compromised CloudBlast app server acting as a malicious client | +| AI | Local CLIs under user accounts; approval modes are the control plane | +| BMC webview | Sandboxed but still untrusted vendor JS on LAN — treat as higher risk surface | +| RDP | Password must reach WASM for CredSSP — minimize lifetime and logging | +| OAuth | System browser + PKCE; client id `cloudblast-desktop` | + +--- + +## 10. Milestones (suggested) + +| Milestone | Scope | Exit criteria | +|-----------|--------|---------------| +| **M1 — Trust & gates** | PR CI for tests; AI default hardening; RDP password audit; IME fix plan | **Shipped** — CI on PRs; safer AI defaults; RDP docs + tests; IME quarantined in `docs/ime-composition.md` | +| **M2 — Distribution** | Code signing; icon quality; optional update consent | Signed Win (+ mac if feasible); reduced SmartScreen friction — **cert pending** | +| **M3 — Quality depth** | Module splits; E2E smoke; SSH/SFTP integration tests | Reviewable IPC surfaces; smoke green in CI | +| **M4 — Polish** | Locale alignment; lazy-load remoting; docs for env/`userData`; a11y pass | Marketing ↔ app parity; measurable startup win | + +--- + +## 11. Open questions + +1. Should cloud sync remain forever-free for non-CloudBlast users, or become tiered later? +2. Code signing: **Windows certificate procurement in progress**; macOS Developer ID deferred until after first signed Windows release. +3. Should “never ask” AI mode remain available, or be removed / hidden behind an advanced unlock? +4. Telemetry: opt-in crash/usage metrics, or stay fully silent? +5. Rename `appId` / package name in a major version, or keep `com.cloudblast.ssh` for upgrade continuity? + +--- + +## 12. References + +| Doc | Path / URL | +|-----|------------| +| User README | `README.md` (+ zh-CN, es, ru) | +| Changelog | `CHANGELOG.md` | +| Contributor docs | `docs/README.md` | +| Winget publish | `docs/winget.md` | +| Resources / icon notes | `resources/README.md` | +| License | `LICENSE` | +| Product site | https://cloudblast.io | + +--- + +*This PRD describes the product as of v1.3.0 plus the M1 trust-and-gates +work on `feat/m1-trust-gates`. Update milestones when releases ship.* diff --git a/README.es.md b/README.es.md index c6cb83c..5ab225d 100644 --- a/README.es.md +++ b/README.es.md @@ -121,8 +121,10 @@ para leerlo y modificarlo. - **Trabaja en el terminal que ves**: los comandos se escriben en el panel y la salida se queda en tu historial, o se ejecutan en un canal oculto si lo prefieres -- **Pregunta antes de cambiar nada**, con una lista de comandos que solo miran - y un modo más estricto o más suelto cuando lo quieras +- **Los modos de aprobación controlan cuándo pregunta** — cada herramienta / + solo cambios / nunca (nunca auto-aprueba herramientas no bloqueadas), aviso + al primer uso y confirmación antes de **Nunca preguntar**. Ver + [docs/assistant-approvals.md](docs/assistant-approvals.md) (inglés) - **Apuntado donde tú digas**: la sesión que tienes delante, una que fijes, o todos los hosts que tengas guardados - **Herramientas en vez de suposiciones**: conectar un host guardado, leer y @@ -300,6 +302,14 @@ Compila un ejecutable portable en `dist/`: npm run build ``` +Antes de abrir un pull request, ejecuta las pruebas unitarias: + +```bash +npm test +``` + +Más documentación en [CHANGELOG.md](CHANGELOG.md) y [docs/README.md](docs/README.md) (inglés). + ### Atajos | | | | | diff --git a/README.md b/README.md index 94fd26a..bed48ac 100644 --- a/README.md +++ b/README.md @@ -75,6 +75,8 @@ everyone, and the whole source is here to read and change. - [Features](#features) - [Screenshots](#screenshots) - [Getting started](#getting-started) +- [Documentation](#documentation) +- [Contributing](#contributing) - [Community](#community) - [Contributors](#contributors) - [Tech stack](#tech-stack) @@ -135,8 +137,10 @@ Whichever you choose, the agent: one it answers, without you pasting anything - **Works in the terminal you can see**: commands are typed into the pane and the output stays in your scrollback, or run on a hidden channel if you prefer -- **Asks before it changes anything**, with an allow list for the commands that - only look, and a stricter or looser mode when you want one +- **Approval modes control when it asks** — every tool / changes only / never + (never auto-approves non-blocked tools), plus a first-run warning and a + confirm step before **Never ask**. See + [assistant approvals](docs/assistant-approvals.md). - **Pointed where you like**: the session in front, one you pin, or every host you have saved - **Tools instead of guesses**: connect a saved host, read and write files, @@ -297,6 +301,12 @@ winget install CloudBlast.CloudTerm Or browse [all GitHub releases](https://github.com/BradPerbs/cloudterm/releases). +> **Windows SmartScreen / VirusTotal:** Installers are **unsigned** while a +> code-signing certificate is being procured. This can trigger SmartScreen or +> heuristic scanner warnings (often 1–3 vendors on VirusTotal). See +> [docs/security.md](docs/security.md) for details and what to expect after +> signing is enabled. + ### Build from source ```bash @@ -316,6 +326,14 @@ Build a portable executable into `dist/`: npm run build ``` +Run the unit test suite before opening a pull request: + +```bash +npm test +``` + +The same suite runs on every pull request via GitHub Actions (`.github/workflows/ci.yml`). + ### Shortcuts | | | | | @@ -326,6 +344,28 @@ npm run build | `Ctrl+Shift+C` / `V` | Copy and paste | `Ctrl+Shift+W` | Close pane | | `Ctrl+Shift+A` | AI agent | `Alt+Arrows` | Move between panes | + +## Documentation + +| Doc | What it covers | +| --- | --- | +| [CHANGELOG.md](CHANGELOG.md) | Release history | +| [PRD.md](PRD.md) | Product scope and roadmap | +| [docs/assistant-approvals.md](docs/assistant-approvals.md) | AI approval modes and defaults | +| [docs/rdp-credentials.md](docs/rdp-credentials.md) | RDP password handling (CredSSP exception) | +| [docs/ime-composition.md](docs/ime-composition.md) | CJK IME positioning (known limitation) | +| [docs/winget.md](docs/winget.md) | Publishing to Windows Package Manager | +| [docs/security.md](docs/security.md) | Signing status, SmartScreen/VirusTotal, npm audit | + +Full index: [docs/README.md](docs/README.md). + + +## Contributing + +1. Fork and clone the repo, then `npm install` and `npm run dev`. +2. Run `npm test` — CI runs the same suite on every pull request. +3. Open a pull request against `main`. + ## Community diff --git a/README.ru.md b/README.ru.md index bf19726..4561cb8 100644 --- a/README.ru.md +++ b/README.ru.md @@ -121,8 +121,11 @@ CloudTerm собирает все способы подключения к се - **Работает в терминале, который вы видите**: команды печатаются в панели, а вывод остаётся в вашем буфере. Или выполняются в скрытом канале, если так удобнее -- **Спрашивает, прежде чем что-то менять**, со списком команд, которые только - смотрят, и режимом строже или свободнее, когда он нужен +- **Режим одобрения решает, когда спрашивать** — каждый инструмент / только + изменения / никогда (никогда само одобряет незаблокированные инструменты), + предупреждение при первом запуске и подтверждение перед **Никогда не + спрашивать**. См. [docs/assistant-approvals.md](docs/assistant-approvals.md) + (англ.) - **Направлен туда, куда скажете**: сеанс перед вами, закреплённый сеанс или все сохранённые хосты - **Инструменты вместо догадок**: подключиться к сохранённому хосту, читать и @@ -297,6 +300,14 @@ npm run dev npm run build ``` +Перед pull request запустите модульные тесты: + +```bash +npm test +``` + +Документация: [CHANGELOG.md](CHANGELOG.md) и [docs/README.md](docs/README.md) (англ.). + ### Горячие клавиши | | | | | diff --git a/README.zh-CN.md b/README.zh-CN.md index fbb0ee0..6c16520 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -105,7 +105,7 @@ Windows 主机,或者任意服务商上的服务器。CloudTerm 由 VPS 云主
- **使用本机已有的 Claude Code、Codex 或 OpenCode**,沿用你自己的账号和配置 -- **读取当前会话并操作远程服务器**,执行更改前会先征求你的同意 +- **读取当前会话并操作远程服务器**;是否征求同意由审批模式决定(每次 / 仅更改 / 从不——从不模式会自动批准未拦截的工具),详见 [docs/assistant-approvals.md](docs/assistant-approvals.md)(英文) - **每个对话可单独选择模型和推理强度**,并在运行时显示用量 ### 终端 @@ -268,6 +268,14 @@ npm run dev npm run build ``` +提交 pull request 前请运行单元测试: + +```bash +npm test +``` + +更多文档见 [CHANGELOG.md](CHANGELOG.md) 与 [docs/README.md](docs/README.md)(英文)。 + ### 快捷键 | | | | | diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 0000000..363f6a0 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,15 @@ +# CloudTerm documentation + +Operator and contributor notes live here. User-facing marketing and setup +instructions are in the root [README](../README.md). + +| Doc | Audience | Summary | +|-----|----------|---------| +| [Assistant approval modes](assistant-approvals.md) | Users, contributors | How `always` / `writes` / `never` approval works, defaults, and block list | +| [RDP credentials](rdp-credentials.md) | Contributors, security review | CredSSP exception: password lifetime, logging, residual risk | +| [IME composition](ime-composition.md) | Contributors | CJK IME candidate positioning — known limitation and fix plan | +| [Winget publishing](winget.md) | Maintainers | First-time and automated Windows package updates | +| [Security](security.md) | Users, maintainers, IT | Signing status (cert pending), SmartScreen/VirusTotal, npm audit, reporting | + +Product scope, roadmap, and milestones are in [PRD.md](../PRD.md). Release +history is in [CHANGELOG.md](../CHANGELOG.md). diff --git a/docs/assistant-approvals.md b/docs/assistant-approvals.md new file mode 100644 index 0000000..b98eb33 --- /dev/null +++ b/docs/assistant-approvals.md @@ -0,0 +1,29 @@ +# Assistant approval modes + +The local AI agent can read live terminals and run tools against saved hosts. +Approval modes decide when those tools stop for a person. + +| Mode | Value | Behaviour | +|------|-------|-----------| +| Ask every time | `always` | Every tool waits, including reads | +| Ask before changes | `writes` | **Default.** Reads run freely; mutating tools ask | +| Never ask | `never` | Nothing asks; blocked commands are still refused | + +## Defaults and hardening + +- New installs ship with `writes` and `allowLocalTools: false`. +- The first time the assistant panel opens, a short warning explains the modes + and that tools act on live sessions. Choosing **Got it** sets + `acknowledgedApprovalWarning` in `assistant.json` (Escape / backdrop do not). +- Switching to **Never ask** from the composer chip or Settings requires an + explicit confirm dialog. The block list (`blockedCommands`, seeded with + `rm -rf`) still refuses matching commands under every mode, including never. + +## Where it lives + +- Persistence: `userData/assistant.json` via `src/main/ai/settings.js` +- Policy: `src/main/ai/tools.js` (`isAutoApproved`, `blockedReason`) +- UI: composer `ApprovalMenu`, Settings → Assistant + +Settings for the assistant stay machine-local on purpose and do not sync with +cloud snapshots. diff --git a/docs/ime-composition.md b/docs/ime-composition.md new file mode 100644 index 0000000..ead9a63 --- /dev/null +++ b/docs/ime-composition.md @@ -0,0 +1,58 @@ +# IME composition positioning (CJK) + +CloudTerm ships Chinese (`zh`) and other locales, but CJK Input Method Editor +(IME) candidate windows can appear mis-positioned in the terminal. This is a +known limitation tracked as GitHub +[#7](https://github.com/BradPerbs/cloudterm/issues/7). + +## Symptom + +While composing CJK (or other IME) text in an SSH/Telnet/serial pane, the +composition / candidate UI may not sit on the caret. English and dead-key Latin +input are unaffected. + +## Current code + +- Terminal: `src/renderer/components/TerminalView.jsx` (`@xterm/xterm` ^5.4) +- Styles: vendored `src/renderer/xterm.css` (upstream still carries + `/* TODO: Composition position got messed up somewhere */` on + `.xterm .composition-view`) +- No app-level `compositionstart` / IME hooks — positioning is entirely xterm’s + `CompositionHelper` + CSS + +Likely aggravators to test (not yet proven): + +- Pane mount uses `p-3` + `overflow-hidden` on the open target +- WebGL addon vs DOM renderer +- Non-default `lineHeight` / `letterSpacing` +- Hardcoded black/white `.composition-view` vs themed terminal + +## Repro matrix (for a later fix) + +| Dimension | Values | +|-----------|--------| +| OS | Windows, macOS | +| IME | zh / ja / ko system IMEs | +| Renderer | WebGL on, WebGL off | +| Typography | default vs custom lineHeight / letterSpacing | + +Compare against a stock xterm.js demo on the same Electron version to separate +CloudTerm layout from upstream bugs. + +## Ranked options + +1. **CSS / layout-only** — ensure composition view is not clipped; theme + `.composition-view`; reduce padding/`overflow` interaction on the mount node. +2. **Bump `@xterm/xterm`** — evaluate 6.x for upstream IME fixes vs breakage in + addons / WebGL. +3. **App composition sync** — listen for composition events and reposition + (last resort; fights the library). +4. **Quarantine (this milestone)** — document the limitation, keep #7 open, + cross-link the CSS TODO. Full N5 fix is out of M1 exit criteria. + +## Quarantine stance (M1) + +Until a verified fix lands: treat CJK IME in the terminal as best-effort. +Prefer composing in an external editor for critical CJK input if positioning +blocks work. Do not close #7 until zh QA passes on Windows and macOS with WebGL +on and off. diff --git a/docs/rdp-credentials.md b/docs/rdp-credentials.md new file mode 100644 index 0000000..d2c4cef --- /dev/null +++ b/docs/rdp-credentials.md @@ -0,0 +1,41 @@ +# RDP credentials (CredSSP exception) + +Most secrets in CloudTerm never leave the main process. RDP is the documented +exception: Network Level Authentication (CredSSP) runs inside IronRDP’s +WebAssembly client in the renderer, so the decrypted password must reach that +client once when a session opens. + +## Lifetime + +1. **Vault / store** — `rdpPassword` is encrypted at rest like other host + secrets (`src/main/store.js`). +2. **Open** — `rdp.open` decrypts via `resolveDesktop`, returns + `{ username, domain, password, … }` once over IPC, and does **not** keep the + password on the live session object or in `snapshot()`. +3. **Renderer** — `RdpView` loads IronRDP **before** calling `open`, so the + secret does not sit on the IPC result through WASM compile. It passes the + password into `SessionBuilder`, then clears `result.password` in a `finally` + that covers every post-open exit (missing canvas, cancel, connect error). +4. **Reconnect** — always re-calls `open`; nothing retains the password for + reuse. + +## Logging and redaction + +- Host list IPC redacts `rdpPassword` (exposes `hasRdpPassword` only). +- Activity change diffs treat `rdpPassword` and `bmcPassword` as secrets + (`secret: true`, empty `from`/`to`). +- Error paths describe rejected credentials without echoing the value. + +## Residual risk + +JavaScript strings are immutable: clearing a property drops the reference; the +engine may retain the buffer until GC. IronRDP’s WASM heap is opaque to the +app — there is no upstream API to zeroize credentials after NLA. Treat the +renderer process as a higher-trust surface for the duration of CredSSP, then +minimize lifetime as above. + +## Contrast with VNC / BMC + +VNC authenticates in the main process; the viewer never receives the password. +BMC uses a sandboxed webview with its own auto-login path — also not this +CredSSP handoff. diff --git a/docs/security.md b/docs/security.md new file mode 100644 index 0000000..5bbc3e2 --- /dev/null +++ b/docs/security.md @@ -0,0 +1,105 @@ +# Security + +CloudTerm holds SSH keys, passwords, and live server sessions. This page +covers distribution trust (SmartScreen, VirusTotal), dependency hygiene, and +where to report issues. + +## Code signing status + +| Item | Status | +|------|--------| +| Windows Authenticode certificate | **Pending procurement** | +| macOS Developer ID certificate | **Not started** (Windows first) | +| CI signing wiring (`release.yml`) | **Ready** — signs automatically once secrets are set | +| Current release builds | **Unsigned** | + +A Windows code-signing certificate is being procured. Until `WIN_CSC_LINK` and +`WIN_CSC_KEY_PASSWORD` are added as repository secrets, tagged releases continue +to ship unsigned installers and portable executables. See [Enabling code signing in CI](#enabling-code-signing-in-ci) below for the steps once the certificate arrives. + +## VirusTotal and SmartScreen (unsigned builds) + +Release builds are **not code-signed today** (certificate pending). On Windows that means: + +- **SmartScreen** may warn on first download ("Windows protected your PC"). +- **VirusTotal** and some enterprise scanners may flag the portable exe + (`CloudTerm-x64.exe`) or the NSIS installer with heuristic detections + (often 1–3 vendors out of 60+). This is common for unsigned Electron apps + packaged with NSIS; it does not mean the published GitHub release binary + contains malware. + +What actually reduces those warnings: + +1. **Authenticode signing** — sign the app binary and the installer with an + Extended Validation or standard code-signing certificate. +2. **Reputation** — signed builds gain SmartScreen reputation over time as + more users install them. +3. **False-positive review** — submit the file hash to vendors that flagged it + for whitelisting (use the full VirusTotal report to see which engines). + +### Enabling code signing in CI + +**Prerequisite:** a code-signing certificate must be procured and converted to +the secrets below. Windows certificate procurement is **in progress**; macOS +Developer ID is planned after Windows. + +When repository secrets are configured, [release.yml](../.github/workflows/release.yml) +passes them to electron-builder: + +| Secret | Platform | Purpose | +|--------|----------|---------| +| `WIN_CSC_LINK` | Windows | Base64-encoded `.pfx` or path to cert | +| `WIN_CSC_KEY_PASSWORD` | Windows | PFX password | +| `CSC_LINK` | macOS | Developer ID Application cert (`.p12`) | +| `CSC_KEY_PASSWORD` | macOS | P12 password | + +Obtain certificates from a public CA (e.g. DigiCert, Sectigo). Store the PFX +as base64: + +```powershell +[Convert]::ToBase64String([IO.File]::ReadAllBytes('cloudterm.pfx')) | Set-Clipboard +``` + +Paste the result into `WIN_CSC_LINK`. After the next tagged release, installers +and the portable exe are signed automatically. + +Unsigned builds continue to work when these secrets are absent. + +## npm dependencies + +CI runs `npm audit --omit=dev --audit-level=high` on every pull request and +push to `main`. That checks **production** dependencies only — the libraries +shipped inside the packaged app. + +Dev-only tools (Vite, esbuild, electron-builder) may still show advisories +locally when you run `npm audit` without `--omit=dev`. Those affect the +development server, not end users. + +To refresh production deps after an advisory: + +```bash +npm audit fix +npm audit --omit=dev +npm test +``` + +## Application security model (summary) + +| Area | Behaviour | +|------|-----------| +| Renderer | Sandboxed; no Node integration; context isolation | +| Secrets | Passwords and keys stay in the main process; vault lock blocks sensitive IPC | +| RDP | Password cleared from renderer after CredSSP — see [rdp-credentials.md](rdp-credentials.md) | +| Assistant | Default approval mode asks before mutating tools — see [assistant-approvals.md](assistant-approvals.md) | +| External links | Only `http:` and `https:` via `open-external` IPC | +| Updates | Auto-install only when platform signature checks apply; unsigned mac builds use notify-only mode | +| BMC webview | Guest pages sandboxed; only `http(s)` in `bmc-*` partitions | + +## Reporting a vulnerability + +Open a [GitHub Security Advisory](https://github.com/BradPerbs/cloudterm/security/advisories/new) +or email the maintainers listed in the repository. Include steps to reproduce +and affected version. + +Do **not** file public issues for undisclosed credential theft or RCE paths +before a fix is available. diff --git a/docs/winget.md b/docs/winget.md index 5b8618a..8c71f8b 100644 --- a/docs/winget.md +++ b/docs/winget.md @@ -97,10 +97,12 @@ may come back with questions on the pull request itself. Two things that come up: -- The installer is not code signed. That is allowed, and plenty of packages in - winget are the same, but SmartScreen and occasionally the pipeline's own - scanner have opinions about unsigned installers and the review can stall on - it. Reply on the pull request if it does. +- The installer is not code signed (**Windows certificate pending procurement**). + CI is wired to sign automatically once `WIN_CSC_LINK` is configured — see + [security.md](security.md). That is allowed in winget, and plenty of packages + are the same, but SmartScreen and occasionally the pipeline's own scanner + have opinions about unsigned installers and the review can stall on it. Reply + on the pull request if it does. - The publisher name has to be one you can claim. `CloudBlast` is fine here. It is also permanent in practice: the identifier is the directory name, the thing people type, and what every already-installed copy is matched against, diff --git a/package-lock.json b/package-lock.json index 40b1082..477b9f7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "cloudblast-ssh", - "version": "1.0.0", + "version": "1.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "cloudblast-ssh", - "version": "1.0.0", + "version": "1.3.0", "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE", "dependencies": { @@ -43,7 +43,7 @@ "patch-package": "^8.0.1", "postcss": "^8.5.6", "tailwindcss": "^3.4.17", - "vite": "^5.0.12", + "vite": "^5.4.21", "wait-on": "^7.2.0" } }, @@ -201,13 +201,13 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", - "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.28.5", + "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, @@ -216,9 +216,9 @@ } }, "node_modules/@babel/compat-data": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", - "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", "dev": true, "license": "MIT", "engines": { @@ -226,21 +226,21 @@ } }, "node_modules/@babel/core": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", - "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-compilation-targets": "^7.28.6", - "@babel/helper-module-transforms": "^7.28.6", - "@babel/helpers": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", @@ -257,14 +257,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", - "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" @@ -274,14 +274,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", - "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.28.6", - "@babel/helper-validator-option": "^7.27.1", + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -308,9 +308,9 @@ "license": "ISC" }, "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", "dev": true, "license": "MIT", "engines": { @@ -318,29 +318,29 @@ } }, "node_modules/@babel/helper-module-imports": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", - "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", "dev": true, "license": "MIT", "dependencies": { - "@babel/traverse": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", - "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.28.6", - "@babel/helper-validator-identifier": "^7.28.5", - "@babel/traverse": "^7.28.6" + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -360,9 +360,9 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz", + "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==", "dev": true, "license": "MIT", "engines": { @@ -370,9 +370,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.28.5", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", - "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", "dev": true, "license": "MIT", "engines": { @@ -380,9 +380,9 @@ } }, "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", "dev": true, "license": "MIT", "engines": { @@ -390,27 +390,27 @@ } }, "node_modules/@babel/helpers": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", - "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz", + "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", - "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.8.tgz", + "integrity": "sha512-E8lTAYNB1KW+FH+VGJuZM1ioAx2E6oVlvQFRrf5P8ZZmsiJXYAD9vTFV7yyEURNzgh1dFqMZuO6tUwcARbqFCA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/types": "^7.28.6" + "@babel/types": "^7.29.8" }, "bin": { "parser": "bin/babel-parser.js" @@ -461,33 +461,33 @@ } }, "node_modules/@babel/template": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", - "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/parser": "^7.28.6", - "@babel/types": "^7.28.6" + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", - "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.8.tgz", + "integrity": "sha512-I5z7H3bf/41ktsNVLtpN0wAa336HkqIHQ5BuPLEhTkt1jVSyZpeNKIzTgEWmlxjdg81R0IgUCcaE+Ok3NvrfZg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.28.6", - "@babel/generator": "^7.28.6", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.6", - "@babel/template": "^7.28.6", - "@babel/types": "^7.28.6", + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.8", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", "debug": "^4.3.1" }, "engines": { @@ -495,14 +495,14 @@ } }, "node_modules/@babel/types": { - "version": "7.28.6", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", - "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.8.tgz", + "integrity": "sha512-Vj1jF3cPfxg7OAfoI7QnVKLoILlm2JF9pnVHrX8qx7AHMiYWT+NDAA7jChlNgRS4WTLc/fD1lXLmPixluj+3Gg==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.28.5" + "@babel/helper-string-parser": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7" }, "engines": { "node": ">=6.9.0" @@ -535,10 +535,11 @@ } }, "node_modules/@electron/asar/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -965,9 +966,9 @@ } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", - "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -978,13 +979,13 @@ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", - "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -995,13 +996,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", - "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -1012,13 +1013,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", - "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -1029,13 +1030,13 @@ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", - "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -1046,13 +1047,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", - "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -1063,13 +1064,13 @@ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", - "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -1080,13 +1081,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", - "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -1097,13 +1098,13 @@ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", - "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -1114,13 +1115,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", - "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -1131,13 +1132,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", - "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -1148,13 +1149,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", - "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -1165,13 +1166,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", - "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -1182,13 +1183,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", - "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -1199,13 +1200,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", - "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -1216,13 +1217,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", - "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -1233,13 +1234,13 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", - "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -1250,13 +1251,30 @@ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", - "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -1267,13 +1285,30 @@ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", - "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -1284,13 +1319,30 @@ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", - "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -1301,13 +1353,13 @@ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", - "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -1318,13 +1370,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", - "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -1335,13 +1387,13 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", - "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -1352,7 +1404,7 @@ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@fontsource/inter": { @@ -1576,6 +1628,26 @@ } } }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, "node_modules/@noble/hashes": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz", @@ -1822,9 +1894,9 @@ "license": "MIT" }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.56.0.tgz", - "integrity": "sha512-LNKIPA5k8PF1+jAFomGe3qN3bbIgJe/IlpDBwuVjrDKrJhVWywgnJvflMt/zkbVNLFtF1+94SljYQS6e99klnw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.4.tgz", + "integrity": "sha512-RrPokAb7dmbxFoeO3TloqHyOjgye8RkBhSqmp4aJMIex4c9r46ZstPnleDQOq1t46VOVjwIuwNogIqbodV1Vvg==", "cpu": [ "arm" ], @@ -1836,9 +1908,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.56.0.tgz", - "integrity": "sha512-lfbVUbelYqXlYiU/HApNMJzT1E87UPGvzveGg2h0ktUNlOCxKlWuJ9jtfvs1sKHdwU4fzY7Pl8sAl49/XaEk6Q==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.4.tgz", + "integrity": "sha512-JKuJc+pnpks2pjy7L/N3v/cAkZxYlnmuZoD840ldbMI5KDbC4iO9NKwPKYdjYFCMAIIlBzYSFHxIJVYzRo2/8A==", "cpu": [ "arm64" ], @@ -1850,9 +1922,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.56.0.tgz", - "integrity": "sha512-EgxD1ocWfhoD6xSOeEEwyE7tDvwTgZc8Bss7wCWe+uc7wO8G34HHCUH+Q6cHqJubxIAnQzAsyUsClt0yFLu06w==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.4.tgz", + "integrity": "sha512-krw5uS2STmvJ02x0uTXHbqQNuz+9eZ1iw+qXk9dmW2gvV4jV7O2hEoOnuhFrpOPiel1mBFtqbxYZZtC46hXLOw==", "cpu": [ "arm64" ], @@ -1864,9 +1936,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.56.0.tgz", - "integrity": "sha512-1vXe1vcMOssb/hOF8iv52A7feWW2xnu+c8BV4t1F//m9QVLTfNVpEdja5ia762j/UEJe2Z1jAmEqZAK42tVW3g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.4.tgz", + "integrity": "sha512-wsTxtgApb4PrOsNJIm0FZ1h3WvCC+k9uxLJ4ad75hgoS4NiRes2SoJFlDAyMwiUY8IssDqGcHbXuN0sx1tfF1A==", "cpu": [ "x64" ], @@ -1878,9 +1950,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.56.0.tgz", - "integrity": "sha512-bof7fbIlvqsyv/DtaXSck4VYQ9lPtoWNFCB/JY4snlFuJREXfZnm+Ej6yaCHfQvofJDXLDMTVxWscVSuQvVWUQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.4.tgz", + "integrity": "sha512-GUOnQlyZe3yAXhWOtOMsn5Qkrv5E5mZXa0thbARWi5Ei2szlVXJFQhddZ4HbAzh8q92w5twp+CQvs/eFanz9YQ==", "cpu": [ "arm64" ], @@ -1892,9 +1964,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.56.0.tgz", - "integrity": "sha512-KNa6lYHloW+7lTEkYGa37fpvPq+NKG/EHKM8+G/g9WDU7ls4sMqbVRV78J6LdNuVaeeK5WB9/9VAFbKxcbXKYg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.4.tgz", + "integrity": "sha512-/Y7f3QuxjzPKsjA/rfEDa3+0vXqyjmJ50Ln8dPpCmWkKTrUoWHG1cWhTqaAMLob2m2nESWuC7yGrREz019Ztqg==", "cpu": [ "x64" ], @@ -1906,13 +1978,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.56.0.tgz", - "integrity": "sha512-E8jKK87uOvLrrLN28jnAAAChNq5LeCd2mGgZF+fGF5D507WlG/Noct3lP/QzQ6MrqJ5BCKNwI9ipADB6jyiq2A==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.4.tgz", + "integrity": "sha512-81wiiX3v7aqy+T+bT61TJ78yJjRquqFFTTbAPt08imfQQzkPIW8t6aJbkTagtCCrXMNc9D66+geqlK7ydLPNqA==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1920,13 +1995,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.56.0.tgz", - "integrity": "sha512-jQosa5FMYF5Z6prEpTCCmzCXz6eKr/tCBssSmQGEeozA9tkRUty/5Vx06ibaOP9RCrW1Pvb8yp3gvZhHwTDsJw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.4.tgz", + "integrity": "sha512-9kmDIvNZqdoHOBZgNtpTBeLWYO/LVipM3H/j62P8848/l/VPEQL6N3uxU9pvP1oZAsXyC2MEnFP3ovRjo7WYNQ==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1934,13 +2012,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.56.0.tgz", - "integrity": "sha512-uQVoKkrC1KGEV6udrdVahASIsaF8h7iLG0U0W+Xn14ucFwi6uS539PsAr24IEF9/FoDtzMeeJXJIBo5RkbNWvQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.4.tgz", + "integrity": "sha512-CcnXHWnXg69g+DX5VWL3FHts3qMRN2uVEHX+BZvGLdd07/gXkn3ePjYtO1LDJvxkGKVHMclKBRa1QUTH+6toYQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1948,13 +2029,16 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.56.0.tgz", - "integrity": "sha512-vLZ1yJKLxhQLFKTs42RwTwa6zkGln+bnXc8ueFGMYmBTLfNu58sl5/eXyxRa2RarTkJbXl8TKPgfS6V5ijNqEA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.4.tgz", + "integrity": "sha512-iFOibiHnTRuhrWLlRsOQFdZJJIa7S8OwkneJr4ocALP16u5yk6lWLINFwhHaEqBFMsKDUZofLkGos7+CPzGB3g==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1962,13 +2046,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.56.0.tgz", - "integrity": "sha512-FWfHOCub564kSE3xJQLLIC/hbKqHSVxy8vY75/YHHzWvbJL7aYJkdgwD/xGfUlL5UV2SB7otapLrcCj2xnF1dg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.4.tgz", + "integrity": "sha512-XnWYMI7euHlb5a871xPja+Gm7DRCFU+FGRrtS2sMq9N8FvqtpagUy6gD4YOemC5MRk9xbh8+jYMEJbigFQwsgA==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -1976,13 +2063,16 @@ ] }, "node_modules/@rollup/rollup-linux-loong64-musl": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.56.0.tgz", - "integrity": "sha512-z1EkujxIh7nbrKL1lmIpqFTc/sr0u8Uk0zK/qIEFldbt6EDKWFk/pxFq3gYj4Bjn3aa9eEhYRlL3H8ZbPT1xvA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.4.tgz", + "integrity": "sha512-qGDAlO0U8xedCcsdRm9oaoQY8DAx/QT7uIxJWhCdx0ceIWX783UC9QSYkdpzAe29wNiVfp24+bZdQmn49o45SQ==", "cpu": [ "loong64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -1990,13 +2080,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.56.0.tgz", - "integrity": "sha512-iNFTluqgdoQC7AIE8Q34R3AuPrJGJirj5wMUErxj22deOcY7XwZRaqYmB6ZKFHoVGqRcRd0mqO+845jAibKCkw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.4.tgz", + "integrity": "sha512-ru4H6ezD7ysA5EiEK6qkkaEb4modH8CTej6kUy/gQi20u3kB3G7Zn8snXXkeJSCOFKG/rbPPtM/+9Wgas1961w==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2004,13 +2097,16 @@ ] }, "node_modules/@rollup/rollup-linux-ppc64-musl": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.56.0.tgz", - "integrity": "sha512-MtMeFVlD2LIKjp2sE2xM2slq3Zxf9zwVuw0jemsxvh1QOpHSsSzfNOTH9uYW9i1MXFxUSMmLpeVeUzoNOKBaWg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.4.tgz", + "integrity": "sha512-2W4MO5WQVJnbJaZdvDb9rhBDuFU1nKIepPFpJUBsTh2k1YY2g+ODViaWuyOAjQ5cOP7NvrvLzt3wvHOoiAvc7w==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2018,13 +2114,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.56.0.tgz", - "integrity": "sha512-in+v6wiHdzzVhYKXIk5U74dEZHdKN9KH0Q4ANHOTvyXPG41bajYRsy7a8TPKbYPl34hU7PP7hMVHRvv/5aCSew==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.4.tgz", + "integrity": "sha512-+fxjfuoAmVMCYV5QyjoIpu0cp5DOiOTeqYFk1AVaxGr+/ravWLX89XfQmptsoWcaVy/TGf2hexzbUOrCQIL1CQ==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2032,13 +2131,16 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.56.0.tgz", - "integrity": "sha512-yni2raKHB8m9NQpI9fPVwN754mn6dHQSbDTwxdr9SE0ks38DTjLMMBjrwvB5+mXrX+C0npX0CVeCUcvvvD8CNQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.4.tgz", + "integrity": "sha512-jTn8JfHGL4djjFxPuM06LmNUJDsst2jeVlsd9OmIH6zc5sC9K6rIuO4YajXatLUpBmBKl6b35ro1QZocLi+tcA==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2046,13 +2148,16 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.56.0.tgz", - "integrity": "sha512-zhLLJx9nQPu7wezbxt2ut+CI4YlXi68ndEve16tPc/iwoylWS9B3FxpLS2PkmfYgDQtosah07Mj9E0khc3Y+vQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.4.tgz", + "integrity": "sha512-oCJCJL4pXsoDcP2QZ+JVlPTIRc6266zsIaeJJsWImmF7HO0W8nb6HuSgZlMWxJwaPf8ehbSw8yo0EUw925hKsA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2060,13 +2165,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.56.0.tgz", - "integrity": "sha512-MVC6UDp16ZSH7x4rtuJPAEoE1RwS8N4oK9DLHy3FTEdFoUTCFVzMfJl/BVJ330C+hx8FfprA5Wqx4FhZXkj2Kw==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.4.tgz", + "integrity": "sha512-W69hukhZ3KKNRCaMIEzKvcFye42hh0FE1+YoYaf5+Ikacuftoco6yO/xouz0hc5d5W/s3yBro5jRiuEE/Q5vUw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "MIT", "optional": true, "os": [ @@ -2074,13 +2182,16 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.56.0.tgz", - "integrity": "sha512-ZhGH1eA4Qv0lxaV00azCIS1ChedK0V32952Md3FtnxSqZTBTd6tgil4nZT5cU8B+SIw3PFYkvyR4FKo2oyZIHA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.4.tgz", + "integrity": "sha512-qiXbGG2jkjXhzXpsFZSR2Xpb8DN/UaxYsbb/STbuR/6fpaDgRmmaq1B/LmtF2wQFOFOSsK2jdE0RZ3a0zHn4QA==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "MIT", "optional": true, "os": [ @@ -2088,9 +2199,9 @@ ] }, "node_modules/@rollup/rollup-openbsd-x64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.56.0.tgz", - "integrity": "sha512-O16XcmyDeFI9879pEcmtWvD/2nyxR9mF7Gs44lf1vGGx8Vg2DRNx11aVXBEqOQhWb92WN4z7fW/q4+2NYzCbBA==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.4.tgz", + "integrity": "sha512-nWeM//hxv8mIo6jD7Hu4o48DVmV9pbV6gsKaWU+4NFyqHoPKwrkRiZGLKUhOBk8qNmDmpwFtPKg80Bo/Tn4xiQ==", "cpu": [ "x64" ], @@ -2102,9 +2213,9 @@ ] }, "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.56.0.tgz", - "integrity": "sha512-LhN/Reh+7F3RCgQIRbgw8ZMwUwyqJM+8pXNT6IIJAqm2IdKkzpCh/V9EdgOMBKuebIrzswqy4ATlrDgiOwbRcQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.4.tgz", + "integrity": "sha512-s62SQ/vgsRSvMwDkOEfTqfgASF0f26ZNaQuTA6Aok5lrikf89yI2W0gFHvZb2Jpgc6N8JnOKZgCK2iciO3CsxQ==", "cpu": [ "arm64" ], @@ -2116,9 +2227,9 @@ ] }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.56.0.tgz", - "integrity": "sha512-kbFsOObXp3LBULg1d3JIUQMa9Kv4UitDmpS+k0tinPBz3watcUiV2/LUDMMucA6pZO3WGE27P7DsfaN54l9ing==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.4.tgz", + "integrity": "sha512-J6wGf8TVGbXJq+HH+ttTvrcfNKPbuZecV6KT1B8I18BC5IURUh5kl4Yl5OEP5eFIUoI5BWxCsyYMhFsDx8kekw==", "cpu": [ "arm64" ], @@ -2130,9 +2241,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.56.0.tgz", - "integrity": "sha512-vSSgny54D6P4vf2izbtFm/TcWYedw7f8eBrOiGGecyHyQB9q4Kqentjaj8hToe+995nob/Wv48pDqL5a62EWtg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.4.tgz", + "integrity": "sha512-zmfrQd/0wu6oJs8Vq8KwY/YtsKSsLtKe/HwAP4Wqy8LhWjeT55fHRAkOhYQ12wI3ayS4Tt12d5CDRD7N96SAYQ==", "cpu": [ "ia32" ], @@ -2144,9 +2255,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.56.0.tgz", - "integrity": "sha512-FeCnkPCTHQJFbiGG49KjV5YGW/8b9rrXAM2Mz2kiIoktq2qsJxRD5giEMEOD2lPdgs72upzefaUvS+nc8E3UzQ==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.4.tgz", + "integrity": "sha512-qPzHqdj9rfUD+w79dtE07zi/kFwKyCJqplp5K5ygeLTp7jLpAoc16OAH39HSmRC9UpozaecsleI8uAdEj6v2yw==", "cpu": [ "x64" ], @@ -2158,9 +2269,9 @@ ] }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.56.0.tgz", - "integrity": "sha512-H8AE9Ur/t0+1VXujj90w0HrSOuv0Nq9r1vSZF2t5km20NTfosQsGGUXDaKdQZzwuLts7IyL1fYT4hM95TI9c4g==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.4.tgz", + "integrity": "sha512-zD6NdeWEByGE9QF9vCrlJ5YQB4oq9q91kPZS37Jwj5hOkvR1lTBSpsKhKDw4IJtbQ35LsTS1HD9DZYGKIshU1Q==", "cpu": [ "x64" ], @@ -2526,9 +2637,9 @@ } }, "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", "dev": true, "license": "MIT" }, @@ -2694,6 +2805,19 @@ "url": "https://opencollective.com/express" } }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, "node_modules/ajv": { "version": "8.20.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", @@ -2882,10 +3006,11 @@ } }, "node_modules/app-builder-lib/node_modules/brace-expansion": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.8.tgz", - "integrity": "sha512-JZyDyq3D4AUifKTPOB7DELf6XsB3WdPuNxCtob1vFXPsSXhdAiHBWJ/tJ8HAc9aH84BK+5JFZLNkJKx3G9kzQg==", + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, @@ -3151,15 +3276,16 @@ "dev": true }, "node_modules/axios": { - "version": "1.13.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.3.tgz", - "integrity": "sha512-ERT8kdX7DZjtUm7IitEyV7InTHAF42iJuMArIiDIV5YtPanJkgw4hw5Dyg9fh0mihdWNn1GKaeIWErfe56UQ1g==", + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.19.0.tgz", + "integrity": "sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==", "dev": true, "license": "MIT", "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" + "follow-redirects": "^1.16.0", + "form-data": "^4.0.6", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, "node_modules/balanced-match": { @@ -3272,9 +3398,9 @@ "optional": true }, "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==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.4.tgz", + "integrity": "sha512-hGfVzPxthbf3+2yjg/RBs60cB0FhqBS/zvdV/4wn4/BmN0bNMMHPc4V/BbFieqf1TKAGGAHnY4eSjajCl0f2Xg==", "dev": true, "license": "MIT", "dependencies": { @@ -3701,15 +3827,15 @@ "license": "MIT" }, "node_modules/concurrently": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.1.tgz", - "integrity": "sha512-fsfrO0MxV64Znoy8/l1vVIjjHa29SZyyqPgQBwhiDcaW8wJc2W3XWVOGx4M3oJBnv/zdUZIIp1gDeS98GzP8Ng==", + "version": "9.2.4", + "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-9.2.4.tgz", + "integrity": "sha512-TZ0CEhyzvFjgtAvHTusDMgj7wNdihCh7LLLrzdUOXIhdlnL2JBBGA9eJxR24rtqgmdjh3OA3hrN1rCHj6HM8qA==", "dev": true, "license": "MIT", "dependencies": { "chalk": "4.1.2", "rxjs": "7.8.2", - "shell-quote": "1.8.3", + "shell-quote": "1.9.0", "supports-color": "8.1.1", "tree-kill": "1.2.2", "yargs": "17.7.2" @@ -3995,10 +4121,11 @@ } }, "node_modules/dir-compare/node_modules/brace-expansion": { - "version": "1.1.16", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz", - "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4407,9 +4534,9 @@ "optional": true }, "node_modules/esbuild": { - "version": "0.21.5", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", - "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -4417,32 +4544,35 @@ "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.21.5", - "@esbuild/android-arm": "0.21.5", - "@esbuild/android-arm64": "0.21.5", - "@esbuild/android-x64": "0.21.5", - "@esbuild/darwin-arm64": "0.21.5", - "@esbuild/darwin-x64": "0.21.5", - "@esbuild/freebsd-arm64": "0.21.5", - "@esbuild/freebsd-x64": "0.21.5", - "@esbuild/linux-arm": "0.21.5", - "@esbuild/linux-arm64": "0.21.5", - "@esbuild/linux-ia32": "0.21.5", - "@esbuild/linux-loong64": "0.21.5", - "@esbuild/linux-mips64el": "0.21.5", - "@esbuild/linux-ppc64": "0.21.5", - "@esbuild/linux-riscv64": "0.21.5", - "@esbuild/linux-s390x": "0.21.5", - "@esbuild/linux-x64": "0.21.5", - "@esbuild/netbsd-x64": "0.21.5", - "@esbuild/openbsd-x64": "0.21.5", - "@esbuild/sunos-x64": "0.21.5", - "@esbuild/win32-arm64": "0.21.5", - "@esbuild/win32-ia32": "0.21.5", - "@esbuild/win32-x64": "0.21.5" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escalade": { @@ -4632,9 +4762,9 @@ "peer": true }, "node_modules/fast-uri": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.4.tgz", - "integrity": "sha512-8JnbkQ4juDyvYs4mgFGQqg4yCYtFDtUtmp2QIQq11ZZe5CFQ5wcqm1rqDgAh/QdMySuBnPzMUiJUNZG5N/AiQw==", + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", "funding": [ { "type": "github", @@ -4644,7 +4774,8 @@ "type": "opencollective", "url": "https://opencollective.com/fastify" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.20.1", @@ -4708,9 +4839,9 @@ } }, "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.16.0.tgz", + "integrity": "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==", "dev": true, "funding": [ { @@ -4729,17 +4860,17 @@ } }, "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==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", "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" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -4928,9 +5059,9 @@ } }, "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==", + "version": "1.1.18", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.18.tgz", + "integrity": "sha512-Edep/X9fGqVNmzKBVsDYIOtD+z1tuezV70LBjdCst9Tqu76lsnvRiZ6oTic1n+/BIwX6QDGAO94PN4N2SADvtw==", "dev": true, "license": "MIT", "dependencies": { @@ -4939,9 +5070,9 @@ } }, "node_modules/glob/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "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": { @@ -5103,9 +5234,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -5115,9 +5246,10 @@ } }, "node_modules/hono": { - "version": "4.12.32", - "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.32.tgz", - "integrity": "sha512-XcuyW9qE2kJn07PkecMOBd5Vq/hMy7mmGw+idz1yblbg9N17ijJODrvPkn7/dwL3Kulj8LcRJ69DLOWf91dRUg==", + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.1.tgz", + "integrity": "sha512-kdJoFVv2xmayw6cY09H7AbMJMt8Jn5jdlEdXsP7AGBdF2DIptVlKlOLKXP41yPip4/a3yQPv9gVcJYI8YY04dw==", + "license": "MIT", "engines": { "node": ">=16.9.0" } @@ -5174,6 +5306,20 @@ "node": ">=10.19.0" } }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/hugeicons-react": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/hugeicons-react/-/hugeicons-react-0.4.0.tgz", @@ -5397,9 +5543,9 @@ } }, "node_modules/joi": { - "version": "17.13.3", - "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.3.tgz", - "integrity": "sha512-otDA4ldcIx+ZXsKHWmp0YizCweVRZG96J10b0FevjfuncLO1oX59THoAmHkNubYJ+9gWsYsp5k8v4ib6oDv1fA==", + "version": "17.13.4", + "resolved": "https://registry.npmjs.org/joi/-/joi-17.13.4.tgz", + "integrity": "sha512-1RuuER6kmt8K8I3nIWvPZKi5RQCb568ZPyY4Pwjlua+yo+63ZTmIwxLZH0heBmiKN4uxjvCiarDrjaeH84xicQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { @@ -5425,9 +5571,9 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz", - "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", "funding": [ { "type": "github", @@ -5438,6 +5584,7 @@ "url": "https://github.com/sponsors/nodeca" } ], + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -5598,9 +5745,9 @@ "license": "MIT" }, "node_modules/lodash": { - "version": "4.17.23", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", - "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", "dev": true, "license": "MIT" }, @@ -5764,9 +5911,9 @@ } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "dev": true, "license": "ISC", "dependencies": { @@ -5812,9 +5959,9 @@ "optional": true }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.18", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", "dev": true, "funding": [ { @@ -6110,9 +6257,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", "dev": true, "license": "MIT", "engines": { @@ -6194,9 +6341,9 @@ } }, "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "version": "8.5.26", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", "dev": true, "funding": [ { @@ -6214,7 +6361,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.17", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -6439,11 +6586,14 @@ } }, "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-2.1.0.tgz", + "integrity": "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==", "dev": true, - "license": "MIT" + "license": "MIT", + "engines": { + "node": ">=10" + } }, "node_modules/pump": { "version": "3.0.3", @@ -6752,13 +6902,13 @@ } }, "node_modules/rollup": { - "version": "4.56.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.56.0.tgz", - "integrity": "sha512-9FwVqlgUHzbXtDg9RCMgodF3Ua4Na6Gau+Sdt9vyCN4RhHfVKX2DCHy3BjMLTDd47ITDhYAnTwGulWTblJSDLg==", + "version": "4.62.4", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.4.tgz", + "integrity": "sha512-RXOqwaPsBGjMNMa4sQjDjHieHEZDFoj/Rdr46l2MU5DfEs16wHJPC2RPTPHWhNl+M3aI472LLqFkFKut4SblOg==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.8" + "@types/estree": "1.0.9" }, "bin": { "rollup": "dist/bin/rollup" @@ -6768,31 +6918,32 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.56.0", - "@rollup/rollup-android-arm64": "4.56.0", - "@rollup/rollup-darwin-arm64": "4.56.0", - "@rollup/rollup-darwin-x64": "4.56.0", - "@rollup/rollup-freebsd-arm64": "4.56.0", - "@rollup/rollup-freebsd-x64": "4.56.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.56.0", - "@rollup/rollup-linux-arm-musleabihf": "4.56.0", - "@rollup/rollup-linux-arm64-gnu": "4.56.0", - "@rollup/rollup-linux-arm64-musl": "4.56.0", - "@rollup/rollup-linux-loong64-gnu": "4.56.0", - "@rollup/rollup-linux-loong64-musl": "4.56.0", - "@rollup/rollup-linux-ppc64-gnu": "4.56.0", - "@rollup/rollup-linux-ppc64-musl": "4.56.0", - "@rollup/rollup-linux-riscv64-gnu": "4.56.0", - "@rollup/rollup-linux-riscv64-musl": "4.56.0", - "@rollup/rollup-linux-s390x-gnu": "4.56.0", - "@rollup/rollup-linux-x64-gnu": "4.56.0", - "@rollup/rollup-linux-x64-musl": "4.56.0", - "@rollup/rollup-openbsd-x64": "4.56.0", - "@rollup/rollup-openharmony-arm64": "4.56.0", - "@rollup/rollup-win32-arm64-msvc": "4.56.0", - "@rollup/rollup-win32-ia32-msvc": "4.56.0", - "@rollup/rollup-win32-x64-gnu": "4.56.0", - "@rollup/rollup-win32-x64-msvc": "4.56.0", + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.62.4", + "@rollup/rollup-android-arm64": "4.62.4", + "@rollup/rollup-darwin-arm64": "4.62.4", + "@rollup/rollup-darwin-x64": "4.62.4", + "@rollup/rollup-freebsd-arm64": "4.62.4", + "@rollup/rollup-freebsd-x64": "4.62.4", + "@rollup/rollup-linux-arm-gnueabihf": "4.62.4", + "@rollup/rollup-linux-arm-musleabihf": "4.62.4", + "@rollup/rollup-linux-arm64-gnu": "4.62.4", + "@rollup/rollup-linux-arm64-musl": "4.62.4", + "@rollup/rollup-linux-loong64-gnu": "4.62.4", + "@rollup/rollup-linux-loong64-musl": "4.62.4", + "@rollup/rollup-linux-ppc64-gnu": "4.62.4", + "@rollup/rollup-linux-ppc64-musl": "4.62.4", + "@rollup/rollup-linux-riscv64-gnu": "4.62.4", + "@rollup/rollup-linux-riscv64-musl": "4.62.4", + "@rollup/rollup-linux-s390x-gnu": "4.62.4", + "@rollup/rollup-linux-x64-gnu": "4.62.4", + "@rollup/rollup-linux-x64-musl": "4.62.4", + "@rollup/rollup-openbsd-x64": "4.62.4", + "@rollup/rollup-openharmony-arm64": "4.62.4", + "@rollup/rollup-win32-arm64-msvc": "4.62.4", + "@rollup/rollup-win32-ia32-msvc": "4.62.4", + "@rollup/rollup-win32-x64-gnu": "4.62.4", + "@rollup/rollup-win32-x64-msvc": "4.62.4", "fsevents": "~2.3.2" } }, @@ -7068,9 +7219,9 @@ } }, "node_modules/shell-quote": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", - "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.9.0.tgz", + "integrity": "sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==", "dev": true, "license": "MIT", "engines": { @@ -7539,9 +7690,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", "dev": true, "license": "MIT", "engines": { @@ -7991,9 +8142,9 @@ "license": "ISC" }, "node_modules/yaml": { - "version": "2.8.2", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", - "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", + "integrity": "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==", "dev": true, "license": "ISC", "bin": { diff --git a/package.json b/package.json index 69f8fa2..9f69b0d 100644 --- a/package.json +++ b/package.json @@ -172,7 +172,7 @@ "patch-package": "^8.0.1", "postcss": "^8.5.6", "tailwindcss": "^3.4.17", - "vite": "^5.0.12", + "vite": "^5.4.21", "wait-on": "^7.2.0" }, "dependencies": { @@ -199,5 +199,8 @@ "ssh2": "^1.15.0", "ws": "^8.21.1", "zod": "^4.0.0" + }, + "overrides": { + "esbuild": "^0.25.0" } } diff --git a/src/main/activity.js b/src/main/activity.js index 620a4f7..87daa9e 100644 --- a/src/main/activity.js +++ b/src/main/activity.js @@ -280,7 +280,9 @@ function clear() { * Change detection * ------------------------------------------------------------------ */ -const SECRET_FIELDS = new Set(['password', 'privateKey', 'passphrase', 'vncPassword']); +const SECRET_FIELDS = new Set([ + 'password', 'privateKey', 'passphrase', 'vncPassword', 'rdpPassword', 'bmcPassword', +]); /** * Bookkeeping the app writes on its own. `lastConnectedAt` alone is written on @@ -290,6 +292,7 @@ const SECRET_FIELDS = new Set(['password', 'privateKey', 'passphrase', 'vncPassw const NOISE_FIELDS = new Set([ 'id', 'lastConnectedAt', 'os', 'distro', 'hasPassword', 'hasPrivateKey', 'hasPassphrase', 'hasVncPassword', + 'hasRdpPassword', 'hasBmcPassword', ]); /** How many of a list of words are worth naming before it becomes a paragraph. */ diff --git a/src/main/ai/settings.js b/src/main/ai/settings.js index 45954d2..d299b42 100644 --- a/src/main/ai/settings.js +++ b/src/main/ai/settings.js @@ -95,6 +95,10 @@ const DEFAULTS = { // of things nobody here asked for is filler, and the useful ones are the // ones a particular person asks their particular fleet over and over. quickPrompts: [], + // Whether the first-run warning about approval modes has been dismissed. + // False until the person has seen that tools act on live sessions and that + // "never ask" runs mutating tools without prompts. + acknowledgedApprovalWarning: false, }; const stateFile = () => path.join(app.getPath('userData'), 'assistant.json'); @@ -148,6 +152,9 @@ function sanitize(raw) { .filter(Boolean) .slice(0, 12); } + if (typeof raw.acknowledgedApprovalWarning === 'boolean') { + next.acknowledgedApprovalWarning = raw.acknowledgedApprovalWarning; + } } return next; } diff --git a/src/main/session-log.js b/src/main/session-log.js index 79672a2..8a42a20 100644 --- a/src/main/session-log.js +++ b/src/main/session-log.js @@ -1,6 +1,7 @@ const { app } = require('electron'); const path = require('path'); const fs = require('fs'); +const crypto = require('crypto'); const activity = require('./activity'); /** @@ -286,14 +287,33 @@ function start(tabId, { hostName = '', address = '', hostId = '', protocol = '', const directory = current.directory || defaultDirectory(); const startedAt = new Date(); - const filePath = path.join(directory, `${slug(hostName || address)}_${stamp(startedAt)}.log`); + const baseName = `${slug(hostName || address)}_${stamp(startedAt)}`; try { fs.mkdirSync(directory, { recursive: true }); - // `a`, not `w`: two sessions to the same host within the same second - // would otherwise have the second silently truncate the first. - const stream = fs.createWriteStream(filePath, { flags: 'a', encoding: 'utf8' }); + // Open synchronously so a returned path already exists on disk. The + // stream open is otherwise async, and callers (and the retention + // tests) treat the path as ready the moment start() returns. + // Exclusive create (`wx`) plus a random suffix: two tabs that stamp + // the same second must not share or truncate one transcript. + let filePath; + let fd; + for (let attempt = 0; attempt < 8; attempt++) { + const suffix = crypto.randomBytes(attempt === 0 ? 3 : 4).toString('hex'); + filePath = path.join(directory, `${baseName}_${suffix}.log`); + try { + fd = fs.openSync(filePath, 'wx'); + break; + } catch (error) { + if (error.code !== 'EEXIST') throw error; + fd = undefined; + } + } + if (fd === undefined) { + throw new Error('Could not create a unique session log path'); + } + const stream = fs.createWriteStream(null, { fd, encoding: 'utf8' }); stream.on('error', (error) => { console.error(`Session log write failed for ${tabId}:`, error.message); // Drop the entry rather than keep handing bytes to a dead stream. diff --git a/src/renderer/components/RdpView.jsx b/src/renderer/components/RdpView.jsx index 10cddc7..ab6f3e6 100644 --- a/src/renderer/components/RdpView.jsx +++ b/src/renderer/components/RdpView.jsx @@ -260,22 +260,10 @@ function RdpView({ paneId, host, isActive, isFocused, isLive, toolbarHost = null // about what the last one was told this machine had copied. warnedRef.current = false; lastSentRef.current = null; - setStatus('opening'); - const result = await open(host?.id); - if (!current()) return; - - if (!result?.success) { - setStatus('error'); - setError(result?.message || 'Could not open the desktop'); - return; - } - - // From here the password is in hand. Everything between this line and - // the builder call is deliberately short and deliberately synchronous - // where it can be. + // Load WASM before asking main for the password, so the secret does not + // sit on the IPC result through a multi-megabyte compile. setStatus('loading'); - let ironrdp; try { ironrdp = await loadIronRdp(); @@ -283,27 +271,42 @@ function RdpView({ paneId, host, isActive, isFocused, isLive, toolbarHost = null if (!current()) return; setStatus('error'); setError(`The RDP module failed to load: ${loadError.message}`); - close(host?.id); return; } if (!current()) return; ironrdpRef.current = ironrdp; - const canvas = canvasRef.current; - const frame = frameRef.current; - if (!canvas || !frame) return; - - setScaling(SCALING[result.scaling] ? result.scaling : 'fit'); - setStatus('connecting'); + setStatus('opening'); + const result = await open(host?.id); + if (!current()) { + if (result) result.password = ''; + return; + } - const ratio = window.devicePixelRatio || 1; - const scaleFactor = scaleFactorFor(ratio); - const width = clampWidth((frame.clientWidth || MIN_WIDTH) * ratio); - const height = clampHeight((frame.clientHeight || MIN_HEIGHT) * ratio); + if (!result?.success) { + setStatus('error'); + setError(result?.message || 'Could not open the desktop'); + return; + } + // From here the password is in hand. Everything between this line and + // the builder call is deliberately short; the wipe in `finally` covers + // every exit, including cancel, missing canvas, and connect errors. let live; try { + const canvas = canvasRef.current; + const frame = frameRef.current; + if (!canvas || !frame) return; + + setScaling(SCALING[result.scaling] ? result.scaling : 'fit'); + setStatus('connecting'); + + const ratio = window.devicePixelRatio || 1; + const scaleFactor = scaleFactorFor(ratio); + const width = clampWidth((frame.clientWidth || MIN_WIDTH) * ratio); + const height = clampHeight((frame.clientHeight || MIN_HEIGHT) * ratio); + const builder = new ironrdp.SessionBuilder(); builder.username(result.username || ''); builder.password(result.password || ''); @@ -409,7 +412,8 @@ function RdpView({ paneId, host, isActive, isFocused, isLive, toolbarHost = null return; } finally { // The builder has copied what it needs. Nothing here should keep a - // reference to the credential after this point. + // reference to the credential after this point — including early + // exits above that never reached connect. result.password = ''; } @@ -428,8 +432,8 @@ function RdpView({ paneId, host, isActive, isFocused, isLive, toolbarHost = null setSize(agreedSize(live)); setStatus('connected'); - detachInputRef.current = attachInput(canvas, () => sessionRef.current, ironrdp); - if (isActive && isFocused) canvas.focus(); + detachInputRef.current = attachInput(canvasRef.current, () => sessionRef.current, ironrdp); + if (isActive && isFocused) canvasRef.current?.focus(); // `run` resolves when the session ends, however it ends. Anything other // than a shutdown we asked for is worth showing, because the pane diff --git a/src/renderer/components/assistant/ApprovalMenu.jsx b/src/renderer/components/assistant/ApprovalMenu.jsx index 1ced668..c0af085 100644 --- a/src/renderer/components/assistant/ApprovalMenu.jsx +++ b/src/renderer/components/assistant/ApprovalMenu.jsx @@ -1,5 +1,7 @@ +import { useState } from 'react'; import { ArrowDown01Icon, FlashIcon, SecurityCheckIcon, Shield01Icon } from 'hugeicons-react'; import PanelMenu from './PanelMenu'; +import ConfirmDialog from '../ui/ConfirmDialog'; import { useT } from '../../i18n'; /** @@ -15,6 +17,10 @@ import { useT } from '../../i18n'; * Icon only. It is a standing state rather than an action, and the composer row * has one label on it already; two competing bits of small text is what makes * that row look like a toolbar. + * + * Switching to "never ask" is gated behind a confirm. One click from a chip is + * otherwise how someone turns off every approval without reading what that + * means for deletes and restarts on live hosts. */ const APPROVALS = [ @@ -40,6 +46,7 @@ const APPROVALS = [ export default function ApprovalMenu({ settings, onChange }) { const t = useT(); + const [confirmNever, setConfirmNever] = useState(false); const options = APPROVALS.map(option => ({ ...option, @@ -54,43 +61,67 @@ export default function ApprovalMenu({ settings, onChange }) { // and the chip is the only place in the panel that can tell them. const loud = current.value === 'never'; + const pick = (value) => { + if (value === 'never' && settings.approval !== 'never') { + setConfirmNever(true); + return; + } + onChange({ approval: value }); + }; + return ( -