diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..2d7aa60
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,16 @@
+{
+ "env": {
+ "browser": true,
+ "es6": true,
+ "node": true
+ },
+ "extends": [
+ "eslint:recommended",
+ "plugin:@typescript-eslint/eslint-recommended",
+ "plugin:@typescript-eslint/recommended",
+ "plugin:import/recommended",
+ "plugin:import/electron",
+ "plugin:import/typescript"
+ ],
+ "parser": "@typescript-eslint/parser"
+}
diff --git a/.gitignore b/.gitignore
index 0177d45..d7ff8c6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,13 +1,94 @@
+resources/netbird*
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
.DS_Store
-# Build outputs and dependency folders
-apps/vynte-connect/.build/
-apps/vynte-connect/dist/
-apps/vynte-connect-windows/node_modules/
-apps/vynte-connect-windows/dist/
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
-# Locally fetched NetBird runtime assets
-apps/vynte-connect/Resources/netbird
-apps/vynte-connect-windows/resources/netbird.exe
-apps/vynte-connect-windows/resources/wintun.dll
-.tmp-netbird-assets/
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# Webpack
+.webpack/
+
+# Vite
+.vite/
+
+# Electron-Forge
+out/
diff --git a/README.md b/README.md
index 2bf8f31..3b5e30d 100644
--- a/README.md
+++ b/README.md
@@ -1,54 +1,41 @@
-# Vynte Connect
+# Vynte Connect for Windows
-Vynte Connect is the branded client wrapper for the Vynte NetBird VPN at `https://vpn.vyntehome.com`.
+Vynte Connect for Windows is a tray wrapper around the bundled `netbird.exe` CLI for `https://vpn.vyntehome.com`.
-This source tree contains:
+It exposes the same basic flow as the macOS app:
-- `apps/vynte-connect`: macOS 14 SwiftUI menu bar app.
-- `apps/vynte-connect-windows`: Windows Electron tray app.
-- `docs/vynte-connect-design-spec.md`: product and UI behavior notes.
-- `scripts/fetch-netbird-assets.mjs`: fetches the official NetBird runtime assets needed for packaging.
+- Connect to `vpn.vyntehome.com`
+- Disconnect the tunnel
+- Log out so the next connect registers with NetBird again
+- Show gateway, internal IP, peer count, and connection state
-The apps do not implement VPN networking themselves. They bundle and drive the official NetBird CLI, install/start the local NetBird helper service when needed, then poll `netbird status --json` until the tunnel is ready.
+## Build
-## Prepare NetBird Assets
+Place the Windows NetBird runtime files in `resources/` before packaging:
-Downloaded binaries are intentionally ignored by git. Fetch them before packaging:
+- `resources/netbird.exe`
+- `resources/wintun.dll`
-```bash
-node scripts/fetch-netbird-assets.mjs
-```
-
-To pin a version:
-
-```bash
-NETBIRD_VERSION=v0.71.4 node scripts/fetch-netbird-assets.mjs
-```
-
-## macOS
-
-```bash
-cd apps/vynte-connect
-swift build -c release
-./Scripts/package-zip.sh
-```
-
-Output:
-
-- `apps/vynte-connect/dist/Vynte Connect.app`
-- `apps/vynte-connect/dist/Vynte-Connect-macOS.zip`
-
-## Windows
-
-```bash
-cd apps/vynte-connect-windows
+```powershell
npm install
npm run package:win
```
-Output:
+The packaged app is written to `dist/Vynte Connect-win32-x64/`, and a portable zip is written to `dist/Vynte-Connect-Windows-x64.zip`.
-- `apps/vynte-connect-windows/dist/Vynte Connect-win32-x64/`
-- `apps/vynte-connect-windows/dist/Vynte-Connect-Windows-x64.zip`
+This build intentionally creates a portable Windows app without stamping Windows executable metadata, so it can be built from macOS without Wine.
-The Windows package is a portable x64 app bundle. It is not code-signed and does not stamp Windows executable metadata, so it can be built from macOS without Wine.
+## Runtime behavior
+
+On first connect, the app uses Windows UAC to install/start the bundled Vynte network helper service:
+
+```powershell
+netbird.exe service install --management-url https://vpn.vyntehome.com --admin-url https://vpn.vyntehome.com
+netbird.exe service start
+```
+
+Then it runs:
+
+```powershell
+netbird.exe up --management-url https://vpn.vyntehome.com --admin-url https://vpn.vyntehome.com
+```
diff --git a/apps/vynte-connect-windows/README.md b/apps/vynte-connect-windows/README.md
deleted file mode 100644
index 3b5e30d..0000000
--- a/apps/vynte-connect-windows/README.md
+++ /dev/null
@@ -1,41 +0,0 @@
-# Vynte Connect for Windows
-
-Vynte Connect for Windows is a tray wrapper around the bundled `netbird.exe` CLI for `https://vpn.vyntehome.com`.
-
-It exposes the same basic flow as the macOS app:
-
-- Connect to `vpn.vyntehome.com`
-- Disconnect the tunnel
-- Log out so the next connect registers with NetBird again
-- Show gateway, internal IP, peer count, and connection state
-
-## Build
-
-Place the Windows NetBird runtime files in `resources/` before packaging:
-
-- `resources/netbird.exe`
-- `resources/wintun.dll`
-
-```powershell
-npm install
-npm run package:win
-```
-
-The packaged app is written to `dist/Vynte Connect-win32-x64/`, and a portable zip is written to `dist/Vynte-Connect-Windows-x64.zip`.
-
-This build intentionally creates a portable Windows app without stamping Windows executable metadata, so it can be built from macOS without Wine.
-
-## Runtime behavior
-
-On first connect, the app uses Windows UAC to install/start the bundled Vynte network helper service:
-
-```powershell
-netbird.exe service install --management-url https://vpn.vyntehome.com --admin-url https://vpn.vyntehome.com
-netbird.exe service start
-```
-
-Then it runs:
-
-```powershell
-netbird.exe up --management-url https://vpn.vyntehome.com --admin-url https://vpn.vyntehome.com
-```
diff --git a/apps/vynte-connect-windows/package-lock.json b/apps/vynte-connect-windows/package-lock.json
deleted file mode 100644
index f059c9b..0000000
--- a/apps/vynte-connect-windows/package-lock.json
+++ /dev/null
@@ -1,2149 +0,0 @@
-{
- "name": "vynte-connect-windows",
- "version": "0.1.0",
- "lockfileVersion": 3,
- "requires": true,
- "packages": {
- "": {
- "name": "vynte-connect-windows",
- "version": "0.1.0",
- "devDependencies": {
- "electron": "^36.3.1",
- "electron-packager": "^17.1.2"
- }
- },
- "node_modules/@electron/asar": {
- "version": "3.4.1",
- "resolved": "https://registry.npmjs.org/@electron/asar/-/asar-3.4.1.tgz",
- "integrity": "sha512-i4/rNPRS84t0vSRa2HorerGRXWyF4vThfHesw0dmcWHp+cspK743UanA0suA5Q5y8kzY2y6YKrvbIUn69BCAiA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "commander": "^5.0.0",
- "glob": "^7.1.6",
- "minimatch": "^3.0.4"
- },
- "bin": {
- "asar": "bin/asar.js"
- },
- "engines": {
- "node": ">=10.12.0"
- }
- },
- "node_modules/@electron/get": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz",
- "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.1.1",
- "env-paths": "^2.2.0",
- "fs-extra": "^8.1.0",
- "got": "^11.8.5",
- "progress": "^2.0.3",
- "semver": "^6.2.0",
- "sumchecker": "^3.0.1"
- },
- "engines": {
- "node": ">=12"
- },
- "optionalDependencies": {
- "global-agent": "^3.0.0"
- }
- },
- "node_modules/@electron/notarize": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@electron/notarize/-/notarize-1.2.4.tgz",
- "integrity": "sha512-W5GQhJEosFNafewnS28d3bpQ37/s91CDWqxVchHfmv2dQSTWpOzNlUVQwYzC1ay5bChRV/A9BTL68yj0Pa+TSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.1.1",
- "fs-extra": "^9.0.1"
- },
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/@electron/notarize/node_modules/fs-extra": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
- "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "at-least-node": "^1.0.0",
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@electron/notarize/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@electron/notarize/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/@electron/osx-sign": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/@electron/osx-sign/-/osx-sign-1.3.3.tgz",
- "integrity": "sha512-KZ8mhXvWv2rIEgMbWZ4y33bDHyUKMXnx4M0sTyPNK/vcB81ImdeY9Ggdqy0SWbMDgmbqyQ+phgejh6V3R2QuSg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "compare-version": "^0.1.2",
- "debug": "^4.3.4",
- "fs-extra": "^10.0.0",
- "isbinaryfile": "^4.0.8",
- "minimist": "^1.2.6",
- "plist": "^3.0.5"
- },
- "bin": {
- "electron-osx-flat": "bin/electron-osx-flat.js",
- "electron-osx-sign": "bin/electron-osx-sign.js"
- },
- "engines": {
- "node": ">=12.0.0"
- }
- },
- "node_modules/@electron/osx-sign/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/@electron/osx-sign/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@electron/osx-sign/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/@electron/universal": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/@electron/universal/-/universal-1.5.1.tgz",
- "integrity": "sha512-kbgXxyEauPJiQQUNG2VgUeyfQNFk6hBF11ISN2PNI6agUgPl55pv4eQmaqHzTAzchBvqZ2tQuRVaPStGf0mxGw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@electron/asar": "^3.2.1",
- "@malept/cross-spawn-promise": "^1.1.0",
- "debug": "^4.3.1",
- "dir-compare": "^3.0.0",
- "fs-extra": "^9.0.1",
- "minimatch": "^3.0.4",
- "plist": "^3.0.4"
- },
- "engines": {
- "node": ">=8.6"
- }
- },
- "node_modules/@electron/universal/node_modules/fs-extra": {
- "version": "9.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
- "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "at-least-node": "^1.0.0",
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@electron/universal/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/@electron/universal/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/@malept/cross-spawn-promise": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz",
- "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/malept"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund"
- }
- ],
- "license": "Apache-2.0",
- "dependencies": {
- "cross-spawn": "^7.0.1"
- },
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@sindresorhus/is": {
- "version": "4.6.0",
- "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz",
- "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/is?sponsor=1"
- }
- },
- "node_modules/@szmarczak/http-timer": {
- "version": "4.0.6",
- "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz",
- "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "defer-to-connect": "^2.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@types/cacheable-request": {
- "version": "6.0.3",
- "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz",
- "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/http-cache-semantics": "*",
- "@types/keyv": "^3.1.4",
- "@types/node": "*",
- "@types/responselike": "^1.0.0"
- }
- },
- "node_modules/@types/http-cache-semantics": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
- "integrity": "sha512-L3LgimLHXtGkWikKnsPg0/VFx9OGZaC+eN1u4r+OB1XRqH3meBIAVC2zr1WdMH+RHmnRkqliQAOHNJ/E0j/e0Q==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@types/keyv": {
- "version": "3.1.4",
- "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz",
- "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/node": {
- "version": "22.19.19",
- "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.19.tgz",
- "integrity": "sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "undici-types": "~6.21.0"
- }
- },
- "node_modules/@types/responselike": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz",
- "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@types/yauzl": {
- "version": "2.10.3",
- "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz",
- "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@types/node": "*"
- }
- },
- "node_modules/@xmldom/xmldom": {
- "version": "0.9.10",
- "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.10.tgz",
- "integrity": "sha512-A9gOqLdi6cV4ibazAjcQufGj0B1y/vDqYrcuP6d/6x8P27gRS8643Dj9o1dEKtB6O7fwxb2FgBmJS2mX7gpvdw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=14.6"
- }
- },
- "node_modules/at-least-node": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
- "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/author-regex": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/author-regex/-/author-regex-1.0.0.tgz",
- "integrity": "sha512-KbWgR8wOYRAPekEmMXrYYdc7BRyhn2Ftk7KWfMUnQ43hFdojWEFRxhhRUm3/OFEdPa1r0KAvTTg9YQK57xTe0g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8"
- }
- },
- "node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/base64-js": {
- "version": "1.5.1",
- "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
- "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/feross"
- },
- {
- "type": "patreon",
- "url": "https://www.patreon.com/feross"
- },
- {
- "type": "consulting",
- "url": "https://feross.org/support"
- }
- ],
- "license": "MIT"
- },
- "node_modules/bluebird": {
- "version": "3.7.2",
- "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz",
- "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/boolean": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/boolean/-/boolean-3.2.0.tgz",
- "integrity": "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw==",
- "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/brace-expansion": {
- "version": "1.1.15",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.15.tgz",
- "integrity": "sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/buffer-crc32": {
- "version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/buffer-equal": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/buffer-equal/-/buffer-equal-1.0.1.tgz",
- "integrity": "sha512-QoV3ptgEaQpvVwbXdSO39iqPQTCxSF7A5U99AxbHYqUdCizL/lH2Z0A2y6nbZucxMEOtNyZfG2s6gsVugGpKkg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/cacheable-lookup": {
- "version": "5.0.4",
- "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz",
- "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10.6.0"
- }
- },
- "node_modules/cacheable-request": {
- "version": "7.0.4",
- "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz",
- "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "clone-response": "^1.0.2",
- "get-stream": "^5.1.0",
- "http-cache-semantics": "^4.0.0",
- "keyv": "^4.0.0",
- "lowercase-keys": "^2.0.0",
- "normalize-url": "^6.0.1",
- "responselike": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/clone-response": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.3.tgz",
- "integrity": "sha512-ROoL94jJH2dUVML2Y/5PEDNaSHgeOdSDicUyS7izcF63G6sTc/FTjLub4b8Il9S8S0beOfYt0TaA5qvFK+w0wA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mimic-response": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/commander": {
- "version": "5.1.0",
- "resolved": "https://registry.npmjs.org/commander/-/commander-5.1.0.tgz",
- "integrity": "sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 6"
- }
- },
- "node_modules/compare-version": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz",
- "integrity": "sha512-pJDh5/4wrEnXX/VWRZvruAGHkzKdr46z11OlTPN+VrATlWWhSKewNCJ1futCO5C7eJB3nPMFZA1LeYtcFboZ2A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/concat-map": {
- "version": "0.0.1",
- "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
- "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/cross-spawn": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz",
- "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "path-key": "^3.1.0",
- "shebang-command": "^2.0.0",
- "which": "^2.0.1"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/cross-spawn-windows-exe": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz",
- "integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==",
- "dev": true,
- "funding": [
- {
- "type": "individual",
- "url": "https://github.com/sponsors/malept"
- },
- {
- "type": "tidelift",
- "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund"
- }
- ],
- "license": "Apache-2.0",
- "dependencies": {
- "@malept/cross-spawn-promise": "^1.1.0",
- "is-wsl": "^2.2.0",
- "which": "^2.0.2"
- },
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/debug": {
- "version": "4.4.3",
- "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
- "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "^2.1.3"
- },
- "engines": {
- "node": ">=6.0"
- },
- "peerDependenciesMeta": {
- "supports-color": {
- "optional": true
- }
- }
- },
- "node_modules/decompress-response": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
- "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "mimic-response": "^3.1.0"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/decompress-response/node_modules/mimic-response": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz",
- "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/defer-to-connect": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz",
- "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/define-data-property": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
- "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "es-define-property": "^1.0.0",
- "es-errors": "^1.3.0",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/define-properties": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
- "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "define-data-property": "^1.0.1",
- "has-property-descriptors": "^1.0.0",
- "object-keys": "^1.1.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/detect-node": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz",
- "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/dir-compare": {
- "version": "3.3.0",
- "resolved": "https://registry.npmjs.org/dir-compare/-/dir-compare-3.3.0.tgz",
- "integrity": "sha512-J7/et3WlGUCxjdnD3HAAzQ6nsnc0WL6DD7WcwJb7c39iH1+AWfg+9OqzJNaI6PkBwBvm1mhZNL9iY/nRiZXlPg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-equal": "^1.0.0",
- "minimatch": "^3.0.4"
- }
- },
- "node_modules/electron": {
- "version": "36.9.5",
- "resolved": "https://registry.npmjs.org/electron/-/electron-36.9.5.tgz",
- "integrity": "sha512-1UCss2IqxqujSzg/2jkRjuiT3G+EEXgd6UKB5kUekwQW1LJ6d4QCr8YItfC3Rr9VIGRDJ29eOERmnRNO1Eh+NA==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "dependencies": {
- "@electron/get": "^2.0.0",
- "@types/node": "^22.7.7",
- "extract-zip": "^2.0.1"
- },
- "bin": {
- "electron": "cli.js"
- },
- "engines": {
- "node": ">= 12.20.55"
- }
- },
- "node_modules/electron-packager": {
- "version": "17.1.2",
- "resolved": "https://registry.npmjs.org/electron-packager/-/electron-packager-17.1.2.tgz",
- "integrity": "sha512-XofXdikjYI7MVBcnXeoOvRR+yFFFHOLs3J7PF5KYQweigtgLshcH4W660PsvHr4lYZ03JBpLyEcUB8DzHZ+BNw==",
- "deprecated": "Please use @electron/packager moving forward. There is no API change, just a package name change",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "@electron/asar": "^3.2.1",
- "@electron/get": "^2.0.0",
- "@electron/notarize": "^1.2.3",
- "@electron/osx-sign": "^1.0.5",
- "@electron/universal": "^1.3.2",
- "cross-spawn-windows-exe": "^1.2.0",
- "debug": "^4.0.1",
- "extract-zip": "^2.0.0",
- "filenamify": "^4.1.0",
- "fs-extra": "^11.1.0",
- "galactus": "^1.0.0",
- "get-package-info": "^1.0.0",
- "junk": "^3.1.0",
- "parse-author": "^2.0.0",
- "plist": "^3.0.0",
- "rcedit": "^3.0.1",
- "resolve": "^1.1.6",
- "semver": "^7.1.3",
- "yargs-parser": "^21.1.1"
- },
- "bin": {
- "electron-packager": "bin/electron-packager.js"
- },
- "engines": {
- "node": ">= 14.17.5"
- },
- "funding": {
- "url": "https://github.com/electron/electron-packager?sponsor=1"
- }
- },
- "node_modules/electron-packager/node_modules/fs-extra": {
- "version": "11.3.5",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.5.tgz",
- "integrity": "sha512-eKpRKAovdpZtR1WopLHxlBWvAgPny3c4gX1G5Jhwmmw4XJj0ifSD5qB5TOo8hmA0wlRKDAOAhEE1yVPgs6Fgcg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=14.14"
- }
- },
- "node_modules/electron-packager/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/electron-packager/node_modules/semver": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
- "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/electron-packager/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/end-of-stream": {
- "version": "1.4.5",
- "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz",
- "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "once": "^1.4.0"
- }
- },
- "node_modules/env-paths": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
- "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/error-ex": {
- "version": "1.3.4",
- "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
- "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-arrayish": "^0.2.1"
- }
- },
- "node_modules/es-define-property": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es-errors": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/es6-error": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz",
- "integrity": "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/escape-string-regexp": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
- "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/extract-zip": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz",
- "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "debug": "^4.1.1",
- "get-stream": "^5.1.0",
- "yauzl": "^2.10.0"
- },
- "bin": {
- "extract-zip": "cli.js"
- },
- "engines": {
- "node": ">= 10.17.0"
- },
- "optionalDependencies": {
- "@types/yauzl": "^2.9.1"
- }
- },
- "node_modules/fd-slicer": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz",
- "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pend": "~1.2.0"
- }
- },
- "node_modules/filename-reserved-regex": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-2.0.0.tgz",
- "integrity": "sha512-lc1bnsSr4L4Bdif8Xb/qrtokGbq5zlsms/CYH8PP+WtCkGNF65DPiQY8vG3SakEdRn8Dlnm+gW/qWKKjS5sZzQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/filenamify": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-4.3.0.tgz",
- "integrity": "sha512-hcFKyUG57yWGAzu1CMt/dPzYZuv+jAJUT85bL8mrXvNe6hWj6yEHEc4EdcgiA6Z3oi1/9wXJdZPXF2dZNgwgOg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "filename-reserved-regex": "^2.0.0",
- "strip-outer": "^1.0.1",
- "trim-repeated": "^1.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/find-up": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
- "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "locate-path": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/flora-colossus": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/flora-colossus/-/flora-colossus-2.0.0.tgz",
- "integrity": "sha512-dz4HxH6pOvbUzZpZ/yXhafjbR2I8cenK5xL0KtBFb7U2ADsR+OwXifnxZjij/pZWF775uSCMzWVd+jDik2H2IA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.4",
- "fs-extra": "^10.1.0"
- },
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/flora-colossus/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/flora-colossus/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/flora-colossus/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/fs-extra": {
- "version": "8.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz",
- "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^4.0.0",
- "universalify": "^0.1.0"
- },
- "engines": {
- "node": ">=6 <7 || >=8"
- }
- },
- "node_modules/fs.realpath": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
- "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/function-bind": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
- "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/galactus": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/galactus/-/galactus-1.0.0.tgz",
- "integrity": "sha512-R1fam6D4CyKQGNlvJne4dkNF+PvUUl7TAJInvTGa9fti9qAv95quQz29GXapA4d8Ec266mJJxFVh82M4GIIGDQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "debug": "^4.3.4",
- "flora-colossus": "^2.0.0",
- "fs-extra": "^10.1.0"
- },
- "engines": {
- "node": ">= 12"
- }
- },
- "node_modules/galactus/node_modules/fs-extra": {
- "version": "10.1.0",
- "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
- "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.2.0",
- "jsonfile": "^6.0.1",
- "universalify": "^2.0.0"
- },
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/galactus/node_modules/jsonfile": {
- "version": "6.2.1",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
- "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "universalify": "^2.0.0"
- },
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/galactus/node_modules/universalify": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
- "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/get-package-info": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/get-package-info/-/get-package-info-1.0.0.tgz",
- "integrity": "sha512-SCbprXGAPdIhKAXiG+Mk6yeoFH61JlYunqdFQFHDtLjJlDjFf6x07dsS8acO+xWt52jpdVo49AlVDnUVK1sDNw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "bluebird": "^3.1.1",
- "debug": "^2.2.0",
- "lodash.get": "^4.0.0",
- "read-pkg-up": "^2.0.0"
- },
- "engines": {
- "node": ">= 4.0"
- }
- },
- "node_modules/get-package-info/node_modules/debug": {
- "version": "2.6.9",
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ms": "2.0.0"
- }
- },
- "node_modules/get-package-info/node_modules/ms": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/get-stream": {
- "version": "5.2.0",
- "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz",
- "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pump": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/glob": {
- "version": "7.2.3",
- "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
- "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
- "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "fs.realpath": "^1.0.0",
- "inflight": "^1.0.4",
- "inherits": "2",
- "minimatch": "^3.1.1",
- "once": "^1.3.0",
- "path-is-absolute": "^1.0.0"
- },
- "engines": {
- "node": "*"
- },
- "funding": {
- "url": "https://github.com/sponsors/isaacs"
- }
- },
- "node_modules/global-agent": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz",
- "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==",
- "dev": true,
- "license": "BSD-3-Clause",
- "optional": true,
- "dependencies": {
- "boolean": "^3.0.1",
- "es6-error": "^4.1.1",
- "matcher": "^3.0.0",
- "roarr": "^2.15.3",
- "semver": "^7.3.2",
- "serialize-error": "^7.0.1"
- },
- "engines": {
- "node": ">=10.0"
- }
- },
- "node_modules/global-agent/node_modules/semver": {
- "version": "7.8.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz",
- "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==",
- "dev": true,
- "license": "ISC",
- "optional": true,
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/globalthis": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz",
- "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "define-properties": "^1.2.1",
- "gopd": "^1.0.1"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/gopd": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/got": {
- "version": "11.8.6",
- "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz",
- "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@sindresorhus/is": "^4.0.0",
- "@szmarczak/http-timer": "^4.0.5",
- "@types/cacheable-request": "^6.0.1",
- "@types/responselike": "^1.0.0",
- "cacheable-lookup": "^5.0.3",
- "cacheable-request": "^7.0.2",
- "decompress-response": "^6.0.0",
- "http2-wrapper": "^1.0.0-beta.5.2",
- "lowercase-keys": "^2.0.0",
- "p-cancelable": "^2.0.0",
- "responselike": "^2.0.0"
- },
- "engines": {
- "node": ">=10.19.0"
- },
- "funding": {
- "url": "https://github.com/sindresorhus/got?sponsor=1"
- }
- },
- "node_modules/graceful-fs": {
- "version": "4.2.11",
- "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
- "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/has-property-descriptors": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz",
- "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "es-define-property": "^1.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/hasown": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz",
- "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "function-bind": "^1.1.2"
- },
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/hosted-git-info": {
- "version": "2.8.9",
- "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz",
- "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/http-cache-semantics": {
- "version": "4.2.0",
- "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz",
- "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==",
- "dev": true,
- "license": "BSD-2-Clause"
- },
- "node_modules/http2-wrapper": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
- "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "quick-lru": "^5.1.1",
- "resolve-alpn": "^1.0.0"
- },
- "engines": {
- "node": ">=10.19.0"
- }
- },
- "node_modules/inflight": {
- "version": "1.0.6",
- "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
- "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
- "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "once": "^1.3.0",
- "wrappy": "1"
- }
- },
- "node_modules/inherits": {
- "version": "2.0.4",
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/is-arrayish": {
- "version": "0.2.1",
- "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz",
- "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/is-core-module": {
- "version": "2.16.2",
- "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz",
- "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "hasown": "^2.0.3"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/is-docker": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "is-docker": "cli.js"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-docker": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/isbinaryfile": {
- "version": "4.0.10",
- "resolved": "https://registry.npmjs.org/isbinaryfile/-/isbinaryfile-4.0.10.tgz",
- "integrity": "sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 8.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/gjtorikian/"
- }
- },
- "node_modules/isexe": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
- "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/json-buffer": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
- "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/json-stringify-safe": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
- "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==",
- "dev": true,
- "license": "ISC",
- "optional": true
- },
- "node_modules/jsonfile": {
- "version": "4.0.0",
- "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz",
- "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==",
- "dev": true,
- "license": "MIT",
- "optionalDependencies": {
- "graceful-fs": "^4.1.6"
- }
- },
- "node_modules/junk": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/junk/-/junk-3.1.0.tgz",
- "integrity": "sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/keyv": {
- "version": "4.5.4",
- "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
- "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "json-buffer": "3.0.1"
- }
- },
- "node_modules/load-json-file": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
- "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "graceful-fs": "^4.1.2",
- "parse-json": "^2.2.0",
- "pify": "^2.0.0",
- "strip-bom": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/locate-path": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
- "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-locate": "^2.0.0",
- "path-exists": "^3.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/lodash.get": {
- "version": "4.4.2",
- "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
- "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==",
- "deprecated": "This package is deprecated. Use the optional chaining (?.) operator instead.",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/lowercase-keys": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz",
- "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/matcher": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz",
- "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "escape-string-regexp": "^4.0.0"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/mimic-response": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz",
- "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
- }
- },
- "node_modules/minimist": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
- "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
- "dev": true,
- "license": "MIT",
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/ms": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/normalize-package-data": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
- "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
- "dev": true,
- "license": "BSD-2-Clause",
- "dependencies": {
- "hosted-git-info": "^2.1.4",
- "resolve": "^1.10.0",
- "semver": "2 || 3 || 4 || 5",
- "validate-npm-package-license": "^3.0.1"
- }
- },
- "node_modules/normalize-package-data/node_modules/semver": {
- "version": "5.7.2",
- "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz",
- "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver"
- }
- },
- "node_modules/normalize-url": {
- "version": "6.1.0",
- "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz",
- "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/object-keys": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
- "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">= 0.4"
- }
- },
- "node_modules/once": {
- "version": "1.4.0",
- "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
- "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "wrappy": "1"
- }
- },
- "node_modules/p-cancelable": {
- "version": "2.1.1",
- "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz",
- "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/p-limit": {
- "version": "1.3.0",
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
- "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-try": "^1.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/p-locate": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
- "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "p-limit": "^1.1.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/p-try": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
- "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/parse-author": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/parse-author/-/parse-author-2.0.0.tgz",
- "integrity": "sha512-yx5DfvkN8JsHL2xk2Os9oTia467qnvRgey4ahSm2X8epehBLx/gWLcy5KI+Y36ful5DzGbCS6RazqZGgy1gHNw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "author-regex": "^1.0.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/parse-json": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
- "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "error-ex": "^1.2.0"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-exists": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
- "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/path-is-absolute": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
- "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/path-key": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
- "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/path-parse": {
- "version": "1.0.7",
- "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz",
- "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/path-type": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
- "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "pify": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/pend": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz",
- "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/pify": {
- "version": "2.3.0",
- "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
- "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/plist": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.1.tgz",
- "integrity": "sha512-ZIfcLJC+7E7FBFnDxm9MPmt7D+DidyQ26lewieO75AdhA2ayMtsJSES0iWzqJQbcVRSrTufQoy0DR94xHue0oA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@xmldom/xmldom": "^0.9.10",
- "base64-js": "^1.5.1",
- "xmlbuilder": "^15.1.1"
- },
- "engines": {
- "node": ">=10.4.0"
- }
- },
- "node_modules/progress": {
- "version": "2.0.3",
- "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
- "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.4.0"
- }
- },
- "node_modules/pump": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz",
- "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "end-of-stream": "^1.1.0",
- "once": "^1.3.1"
- }
- },
- "node_modules/quick-lru": {
- "version": "5.1.1",
- "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz",
- "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/rcedit": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-3.1.0.tgz",
- "integrity": "sha512-WRlRdY1qZbu1L11DklT07KuHfRk42l0NFFJdaExELEu4fEQ982bP5Z6OWGPj/wLLIuKRQDCxZJGAwoFsxhZhNA==",
- "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cross-spawn-windows-exe": "^1.1.0"
- },
- "engines": {
- "node": ">= 10.0.0"
- }
- },
- "node_modules/read-pkg": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
- "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "load-json-file": "^2.0.0",
- "normalize-package-data": "^2.3.2",
- "path-type": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/read-pkg-up": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
- "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "find-up": "^2.0.0",
- "read-pkg": "^2.0.0"
- },
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/resolve": {
- "version": "1.22.12",
- "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz",
- "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "es-errors": "^1.3.0",
- "is-core-module": "^2.16.1",
- "path-parse": "^1.0.7",
- "supports-preserve-symlinks-flag": "^1.0.0"
- },
- "bin": {
- "resolve": "bin/resolve"
- },
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/resolve-alpn": {
- "version": "1.2.1",
- "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz",
- "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/responselike": {
- "version": "2.0.1",
- "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz",
- "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "lowercase-keys": "^2.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/roarr": {
- "version": "2.15.4",
- "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz",
- "integrity": "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A==",
- "dev": true,
- "license": "BSD-3-Clause",
- "optional": true,
- "dependencies": {
- "boolean": "^3.0.1",
- "detect-node": "^2.0.4",
- "globalthis": "^1.0.1",
- "json-stringify-safe": "^5.0.1",
- "semver-compare": "^1.0.0",
- "sprintf-js": "^1.1.2"
- },
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/semver": {
- "version": "6.3.1",
- "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
- "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- }
- },
- "node_modules/semver-compare": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
- "integrity": "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow==",
- "dev": true,
- "license": "MIT",
- "optional": true
- },
- "node_modules/serialize-error": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz",
- "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "type-fest": "^0.13.1"
- },
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/shebang-command": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
- "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "shebang-regex": "^3.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/shebang-regex": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
- "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/spdx-correct": {
- "version": "3.2.0",
- "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz",
- "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-expression-parse": "^3.0.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-exceptions": {
- "version": "2.5.0",
- "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz",
- "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==",
- "dev": true,
- "license": "CC-BY-3.0"
- },
- "node_modules/spdx-expression-parse": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
- "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "spdx-exceptions": "^2.1.0",
- "spdx-license-ids": "^3.0.0"
- }
- },
- "node_modules/spdx-license-ids": {
- "version": "3.0.23",
- "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz",
- "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==",
- "dev": true,
- "license": "CC0-1.0"
- },
- "node_modules/sprintf-js": {
- "version": "1.1.3",
- "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz",
- "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==",
- "dev": true,
- "license": "BSD-3-Clause",
- "optional": true
- },
- "node_modules/strip-bom": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
- "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=4"
- }
- },
- "node_modules/strip-outer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/strip-outer/-/strip-outer-1.0.1.tgz",
- "integrity": "sha512-k55yxKHwaXnpYGsOzg4Vl8+tDrWylxDEpknGjhTiZB8dFRU5rTo9CAzeycivxV3s+zlTKwrs6WxMxR95n26kwg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "escape-string-regexp": "^1.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/strip-outer/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/sumchecker": {
- "version": "3.0.1",
- "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz",
- "integrity": "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "debug": "^4.1.0"
- },
- "engines": {
- "node": ">= 8.0"
- }
- },
- "node_modules/supports-preserve-symlinks-flag": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz",
- "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 0.4"
- },
- "funding": {
- "url": "https://github.com/sponsors/ljharb"
- }
- },
- "node_modules/trim-repeated": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/trim-repeated/-/trim-repeated-1.0.0.tgz",
- "integrity": "sha512-pkonvlKk8/ZuR0D5tLW8ljt5I8kmxp2XKymhepUeOdCEfKpZaktSArkLHZt76OB1ZvO9bssUsDty4SWhLvZpLg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "escape-string-regexp": "^1.0.2"
- },
- "engines": {
- "node": ">=0.10.0"
- }
- },
- "node_modules/trim-repeated/node_modules/escape-string-regexp": {
- "version": "1.0.5",
- "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
- "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=0.8.0"
- }
- },
- "node_modules/type-fest": {
- "version": "0.13.1",
- "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz",
- "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==",
- "dev": true,
- "license": "(MIT OR CC0-1.0)",
- "optional": true,
- "engines": {
- "node": ">=10"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/undici-types": {
- "version": "6.21.0",
- "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
- "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/universalify": {
- "version": "0.1.2",
- "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz",
- "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4.0.0"
- }
- },
- "node_modules/validate-npm-package-license": {
- "version": "3.0.4",
- "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
- "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "spdx-correct": "^3.0.0",
- "spdx-expression-parse": "^3.0.0"
- }
- },
- "node_modules/which": {
- "version": "2.0.2",
- "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
- "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "isexe": "^2.0.0"
- },
- "bin": {
- "node-which": "bin/node-which"
- },
- "engines": {
- "node": ">= 8"
- }
- },
- "node_modules/wrappy": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
- "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/xmlbuilder": {
- "version": "15.1.1",
- "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz",
- "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=8.0"
- }
- },
- "node_modules/yargs-parser": {
- "version": "21.1.1",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
- "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
- "dev": true,
- "license": "ISC",
- "engines": {
- "node": ">=12"
- }
- },
- "node_modules/yauzl": {
- "version": "2.10.0",
- "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz",
- "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "buffer-crc32": "~0.2.3",
- "fd-slicer": "~1.1.0"
- }
- }
- }
-}
diff --git a/apps/vynte-connect-windows/package.json b/apps/vynte-connect-windows/package.json
deleted file mode 100644
index 77895cd..0000000
--- a/apps/vynte-connect-windows/package.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "name": "vynte-connect-windows",
- "version": "0.1.0",
- "private": true,
- "description": "Vynte Connect Windows tray wrapper for vpn.vyntehome.com",
- "main": "src/main.js",
- "scripts": {
- "start": "electron .",
- "package:win": "node scripts/package-win-portable.mjs"
- },
- "devDependencies": {
- "electron": "^36.3.1",
- "electron-packager": "^17.1.2"
- }
-}
diff --git a/apps/vynte-connect-windows/resources/NETBIRD-LICENSE b/apps/vynte-connect-windows/resources/NETBIRD-LICENSE
deleted file mode 100644
index d922f15..0000000
--- a/apps/vynte-connect-windows/resources/NETBIRD-LICENSE
+++ /dev/null
@@ -1,16 +0,0 @@
-This BSD‑3‑Clause license applies to all parts of the repository except for the directories management/, signal/, relay/ and combined/.
-Those directories are licensed under the GNU Affero General Public License version 3.0 (AGPLv3). See the respective LICENSE files inside each directory.
-
-BSD 3-Clause License
-
-Copyright (c) 2022 NetBird GmbH & AUTHORS
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-
-2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-
-3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/apps/vynte-connect-windows/scripts/package-win-portable.mjs b/apps/vynte-connect-windows/scripts/package-win-portable.mjs
deleted file mode 100644
index cb85c58..0000000
--- a/apps/vynte-connect-windows/scripts/package-win-portable.mjs
+++ /dev/null
@@ -1,64 +0,0 @@
-import { existsSync, mkdirSync, rmSync, copyFileSync, cpSync, renameSync, writeFileSync } from "node:fs";
-import { homedir } from "node:os";
-import { basename, join } from "node:path";
-import { spawnSync } from "node:child_process";
-
-const root = join(import.meta.dirname, "..");
-const electronVersion = "36.9.5";
-const cacheRoot = join(homedir(), "Library", "Caches", "electron");
-const zipName = `electron-v${electronVersion}-win32-x64.zip`;
-const appDir = join(root, "dist", "Vynte Connect-win32-x64");
-const zipPath = join(root, "dist", "Vynte-Connect-Windows-x64.zip");
-
-function run(command, args, options = {}) {
- const result = spawnSync(command, args, { stdio: "inherit", ...options });
- if (result.status !== 0) {
- throw new Error(`${command} ${args.join(" ")} failed with exit code ${result.status}`);
- }
-}
-
-function findElectronZip(dir) {
- if (!existsSync(dir)) return null;
- const result = spawnSync("find", [dir, "-name", zipName, "-print", "-quit"], { encoding: "utf8" });
- return result.stdout.trim() || null;
-}
-
-function sha256(file) {
- const result = spawnSync("shasum", ["-a", "256", file], { encoding: "utf8" });
- if (result.status !== 0) return "unknown";
- return result.stdout.split(/\s+/)[0];
-}
-
-const electronZip = findElectronZip(cacheRoot);
-if (!electronZip) {
- throw new Error(`Missing ${zipName}. Run npm install once so Electron downloads its Windows runtime.`);
-}
-
-mkdirSync(join(root, "dist"), { recursive: true });
-rmSync(appDir, { recursive: true, force: true });
-rmSync(zipPath, { force: true });
-mkdirSync(appDir, { recursive: true });
-
-run("ditto", ["-x", "-k", electronZip, appDir]);
-renameSync(join(appDir, "electron.exe"), join(appDir, "Vynte Connect.exe"));
-
-const packagedAppDir = join(appDir, "resources", "app");
-mkdirSync(packagedAppDir, { recursive: true });
-copyFileSync(join(root, "package.json"), join(packagedAppDir, "package.json"));
-cpSync(join(root, "src"), join(packagedAppDir, "src"), { recursive: true });
-cpSync(join(root, "resources"), join(packagedAppDir, "resources"), { recursive: true });
-
-writeFileSync(
- join(appDir, "BUILD-INFO.txt"),
- [
- "Vynte Connect for Windows",
- `Electron runtime: ${basename(electronZip)}`,
- `Electron runtime sha256: ${sha256(electronZip)}`,
- "Gateway: https://vpn.vyntehome.com",
- "",
- ].join("\n"),
-);
-
-run("zip", ["-qry", zipPath, "."], { cwd: appDir });
-console.log(`Packaged ${appDir}`);
-console.log(`Created ${zipPath}`);
diff --git a/apps/vynte-connect-windows/src/config.js b/apps/vynte-connect-windows/src/config.js
deleted file mode 100644
index d203b32..0000000
--- a/apps/vynte-connect-windows/src/config.js
+++ /dev/null
@@ -1,19 +0,0 @@
-const MANAGEMENT_URL = 'https://vpn.vyntehome.com';
-const GATEWAY_HOST = new URL(MANAGEMENT_URL).host;
-
-module.exports = {
- APP_NAME: 'Vynte Connect',
- MANAGEMENT_URL,
- GATEWAY_HOST,
- POLL_INTERVAL_MS: 5000,
- DEFAULT_STATUS: {
- state: 'checking',
- title: `Checking ${GATEWAY_HOST}`,
- message: 'Preparing the Vynte access client.',
- ip: '',
- fqdn: '',
- peers: '0/0',
- connectedSince: null,
- gatewayHost: GATEWAY_HOST
- }
-};
diff --git a/apps/vynte-connect-windows/src/netbird.js b/apps/vynte-connect-windows/src/netbird.js
deleted file mode 100644
index f35e155..0000000
--- a/apps/vynte-connect-windows/src/netbird.js
+++ /dev/null
@@ -1,100 +0,0 @@
-const { execFile } = require('child_process');
-const { MANAGEMENT_URL } = require('./config');
-
-class NetBirdClient {
- constructor(executablePath) {
- this.executablePath = executablePath;
- }
-
- run(args, options = {}) {
- return new Promise((resolve) => {
- execFile(this.executablePath, args, {
- windowsHide: true,
- timeout: options.timeout || 30000
- }, (error, stdout, stderr) => {
- resolve({
- ok: !error,
- code: error && typeof error.code === 'number' ? error.code : 0,
- stdout: stdout || '',
- stderr: stderr || '',
- error
- });
- });
- });
- }
-
- runElevated(args) {
- const quotedExe = this.executablePath.replace(/'/g, "''");
- const quotedArgs = args.map((arg) => `'${String(arg).replace(/'/g, "''")}'`).join(',');
- const script = `Start-Process -FilePath '${quotedExe}' -ArgumentList @(${quotedArgs}) -Verb RunAs -Wait`;
-
- return new Promise((resolve) => {
- execFile('powershell.exe', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-Command', script], {
- windowsHide: true,
- timeout: 180000
- }, (error, stdout, stderr) => {
- resolve({
- ok: !error,
- stdout: stdout || '',
- stderr: stderr || '',
- error
- });
- });
- });
- }
-
- status(options = {}) {
- return this.run(['status', '--json'], { timeout: options.timeout || 12000 });
- }
-
- async ensureService() {
- const status = await this.status({ timeout: 10000 });
- if (status.ok) {
- return true;
- }
-
- await this.runElevated([
- 'service',
- 'install',
- '--management-url', MANAGEMENT_URL,
- '--admin-url', MANAGEMENT_URL,
- '--log-level', 'info'
- ]);
- await this.runElevated(['service', 'start']);
-
- for (let index = 0; index < 20; index += 1) {
- const check = await this.status({ timeout: 8000 });
- if (check.ok) {
- return true;
- }
- await new Promise((resolve) => setTimeout(resolve, 500));
- }
-
- return false;
- }
-
- connect() {
- return this.run([
- 'up',
- '--management-url', MANAGEMENT_URL,
- '--admin-url', MANAGEMENT_URL
- ], { timeout: 180000 });
- }
-
- disconnect() {
- return this.run(['down', '--management-url', MANAGEMENT_URL], { timeout: 45000 });
- }
-
- async logout() {
- await this.run(['down', '--management-url', MANAGEMENT_URL], { timeout: 30000 });
- return this.run([
- 'deregister',
- '--management-url', MANAGEMENT_URL,
- '--admin-url', MANAGEMENT_URL
- ], { timeout: 60000 });
- }
-}
-
-module.exports = {
- NetBirdClient
-};
diff --git a/apps/vynte-connect-windows/src/renderer.js b/apps/vynte-connect-windows/src/renderer.js
deleted file mode 100644
index c209dfc..0000000
--- a/apps/vynte-connect-windows/src/renderer.js
+++ /dev/null
@@ -1,46 +0,0 @@
-const body = document.body;
-const orb = document.getElementById('orb');
-const statusTitle = document.getElementById('status-title');
-const statusMessage = document.getElementById('status-message');
-const identity = document.getElementById('identity');
-const gatewayState = document.getElementById('gateway-state');
-const stats = document.getElementById('stats');
-const ip = document.getElementById('ip');
-const peers = document.getElementById('peers');
-const refresh = document.getElementById('refresh');
-const auto = document.getElementById('auto');
-const logout = document.getElementById('logout');
-const quit = document.getElementById('quit');
-
-let currentStatus = { state: 'checking' };
-
-function setStatus(status) {
- currentStatus = status;
- body.className = status.state || 'disconnected';
- statusTitle.textContent = status.title || 'Disconnected';
- statusMessage.textContent = status.message || '';
- identity.textContent = status.fqdn || 'Vynte access';
- document.querySelector('[data-gateway-host]').textContent = status.gatewayHost || 'vpn.vyntehome.com';
- gatewayState.textContent = status.state === 'connected' ? 'Live' : 'Offline';
- ip.textContent = status.ip || 'Assigned';
- peers.textContent = status.peers || '0/0';
- stats.classList.toggle('hidden', status.state !== 'connected');
- logout.classList.toggle('hidden', status.state !== 'connected');
- auto.classList.toggle('active', Boolean(status.autoConnect));
-}
-
-orb.addEventListener('click', async () => {
- if (currentStatus.state === 'connected') {
- await window.vynte.disconnect();
- } else if (!['connecting', 'disconnecting', 'loggingOut', 'checking'].includes(currentStatus.state)) {
- await window.vynte.connect();
- }
-});
-
-refresh.addEventListener('click', () => window.vynte.refresh());
-auto.addEventListener('click', () => window.vynte.toggleAuto());
-logout.addEventListener('click', () => window.vynte.logout());
-quit.addEventListener('click', () => window.vynte.quit());
-
-window.vynte.onStatus(setStatus);
-window.vynte.getStatus().then(setStatus);
diff --git a/apps/vynte-connect-windows/src/status.js b/apps/vynte-connect-windows/src/status.js
deleted file mode 100644
index 4febf23..0000000
--- a/apps/vynte-connect-windows/src/status.js
+++ /dev/null
@@ -1,57 +0,0 @@
-const { GATEWAY_HOST } = require('./config');
-
-function baseStatus(overrides) {
- return {
- ip: '',
- fqdn: '',
- peers: '0/0',
- connectedSince: null,
- gatewayHost: GATEWAY_HOST,
- ...overrides
- };
-}
-
-function normalizeStatus(raw, previousStatus = {}) {
- let parsed;
- try {
- parsed = JSON.parse(raw);
- } catch {
- return baseStatus({
- state: 'error',
- title: 'Connection failed',
- message: `${GATEWAY_HOST} status could not be parsed.`
- });
- }
-
- const daemonConnected = String(parsed.daemonStatus || '').toLowerCase().includes('connected');
- const managementConnected = parsed.management && parsed.management.connected === true;
- const signalConnected = parsed.signal && parsed.signal.connected === true;
- const hasIdentity = Boolean(parsed.netbirdIp || parsed.fqdn);
- const connected = daemonConnected && (managementConnected || signalConnected || hasIdentity);
- const peerCount = parsed.peers || {};
-
- if (connected) {
- return baseStatus({
- state: 'connected',
- title: 'Connected',
- message: "You're on the Vynte network.",
- ip: parsed.netbirdIp || 'Assigned',
- fqdn: parsed.fqdn || '',
- peers: `${peerCount.connected || 0}/${peerCount.total || 0}`,
- connectedSince: previousStatus.connectedSince || Date.now()
- });
- }
-
- return baseStatus({
- state: 'disconnected',
- title: 'Disconnected',
- message: 'Click the flame to connect with NetBird.',
- fqdn: parsed.fqdn || '',
- peers: `${peerCount.connected || 0}/${peerCount.total || 0}`
- });
-}
-
-module.exports = {
- baseStatus,
- normalizeStatus
-};
diff --git a/apps/vynte-connect/Package.swift b/apps/vynte-connect/Package.swift
deleted file mode 100644
index d233673..0000000
--- a/apps/vynte-connect/Package.swift
+++ /dev/null
@@ -1,22 +0,0 @@
-// swift-tools-version: 6.0
-
-import PackageDescription
-
-let package = Package(
- name: "VynteConnect",
- platforms: [
- .macOS(.v14)
- ],
- products: [
- .executable(name: "VynteConnect", targets: ["VynteConnect"])
- ],
- targets: [
- .executableTarget(
- name: "VynteConnect",
- path: "Sources/VynteConnect",
- resources: [
- .process("../../Resources")
- ]
- )
- ]
-)
diff --git a/apps/vynte-connect/README.md b/apps/vynte-connect/README.md
deleted file mode 100644
index c31086e..0000000
--- a/apps/vynte-connect/README.md
+++ /dev/null
@@ -1,77 +0,0 @@
-# Vynte Connect
-
-Vynte Connect is a small macOS menu bar wrapper for NetBird. It gives internal Vynte users a branded one-button connect/disconnect flow for the self-hosted VPN at `https://vpn.vyntehome.com`.
-
-The app does not implement VPN networking and does not store setup keys or NetBird secrets. It calls a bundled `netbird` CLI when one is packaged with the app, then falls back to an installed `netbird` CLI and polls the local NetBird daemon.
-
-The app bundles Vynte visual assets and Montserrat for the menu bar popover UI. The UI is intentionally rudimentary: one flame button, a fixed Vynte gateway, connected stats, auto-connect, and quit.
-
-## Requirements
-
-- macOS 14 or newer.
-- A bundled or installed NetBird CLI.
-- A working NetBird daemon/network extension on the Mac.
-- User belongs to the correct NetBird groups.
-
-## Run from source
-
-```bash
-cd apps/vynte-connect
-swift run
-```
-
-## Build the app bundle
-
-```bash
-cd apps/vynte-connect
-./Scripts/build-app.sh
-open "dist/Vynte Connect.app"
-```
-
-To force a specific NetBird binary into the app bundle:
-
-```bash
-cd apps/vynte-connect
-NETBIRD_BINARY_PATH=/path/to/netbird ./Scripts/build-app.sh
-```
-
-The build script copies that binary to `dist/Vynte Connect.app/Contents/Resources/netbird`. If `NETBIRD_BINARY_PATH` is not set, it tries `Resources/netbird`, then the first `netbird` on `PATH`.
-
-## Package an unsigned internal zip
-
-```bash
-cd apps/vynte-connect
-./Scripts/package-zip.sh
-```
-
-Unsigned builds require users to approve the app in macOS Gatekeeper.
-
-## Signed build
-
-```bash
-cd apps/vynte-connect
-export DEVELOPER_ID_APPLICATION="Developer ID Application: Your Name (TEAMID)"
-./Scripts/sign-app.sh
-```
-
-For notarization:
-
-```bash
-export APPLE_ID="you@example.com"
-export APPLE_TEAM_ID="TEAMID"
-export APPLE_APP_PASSWORD="app-specific-password"
-./Scripts/notarize-zip.sh
-```
-
-## NetBird commands used
-
-```bash
-netbird up --management-url https://vpn.vyntehome.com --admin-url https://vpn.vyntehome.com
-netbird down --management-url https://vpn.vyntehome.com
-netbird status --json
-netbird status --check ready
-```
-
-## Connection flow
-
-Clicking the flame runs `netbird up` against `https://vpn.vyntehome.com`. Vynte Connect polls NetBird status until the tunnel is ready.
diff --git a/apps/vynte-connect/Resources/Montserrat-VariableFont_wght.ttf b/apps/vynte-connect/Resources/Montserrat-VariableFont_wght.ttf
deleted file mode 100644
index 451e692..0000000
Binary files a/apps/vynte-connect/Resources/Montserrat-VariableFont_wght.ttf and /dev/null differ
diff --git a/apps/vynte-connect/Resources/VYNTE-bg.jpg b/apps/vynte-connect/Resources/VYNTE-bg.jpg
deleted file mode 100644
index 50a97d3..0000000
Binary files a/apps/vynte-connect/Resources/VYNTE-bg.jpg and /dev/null differ
diff --git a/apps/vynte-connect/Resources/VYNTE.png b/apps/vynte-connect/Resources/VYNTE.png
deleted file mode 100644
index 215b69e..0000000
Binary files a/apps/vynte-connect/Resources/VYNTE.png and /dev/null differ
diff --git a/apps/vynte-connect/Resources/VynteIcon.png b/apps/vynte-connect/Resources/VynteIcon.png
deleted file mode 100644
index 7f28e8b..0000000
Binary files a/apps/vynte-connect/Resources/VynteIcon.png and /dev/null differ
diff --git a/apps/vynte-connect/Resources/VynteMenuBarIcon.png b/apps/vynte-connect/Resources/VynteMenuBarIcon.png
deleted file mode 100644
index ed8430a..0000000
Binary files a/apps/vynte-connect/Resources/VynteMenuBarIcon.png and /dev/null differ
diff --git a/apps/vynte-connect/Scripts/build-app.sh b/apps/vynte-connect/Scripts/build-app.sh
deleted file mode 100755
index 985ee86..0000000
--- a/apps/vynte-connect/Scripts/build-app.sh
+++ /dev/null
@@ -1,72 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-BUILD_DIR="${ROOT_DIR}/.build/release"
-DIST_DIR="${ROOT_DIR}/dist"
-APP_NAME="Vynte Connect"
-APP_DIR="${DIST_DIR}/${APP_NAME}.app"
-EXECUTABLE_NAME="VynteConnect"
-
-cd "$ROOT_DIR"
-
-swift build -c release
-
-rm -rf "$APP_DIR"
-mkdir -p "$APP_DIR/Contents/MacOS" "$APP_DIR/Contents/Resources"
-
-cp "$BUILD_DIR/$EXECUTABLE_NAME" "$APP_DIR/Contents/MacOS/$EXECUTABLE_NAME"
-
-RESOURCE_BUNDLE="${BUILD_DIR}/VynteConnect_VynteConnect.bundle"
-if [[ -d "$RESOURCE_BUNDLE" ]]; then
- cp -R "$RESOURCE_BUNDLE" "$APP_DIR/Contents/Resources/"
-fi
-
-NETBIRD_SOURCE="${NETBIRD_BINARY_PATH:-}"
-if [[ -z "$NETBIRD_SOURCE" && -x "$ROOT_DIR/Resources/netbird" ]]; then
- NETBIRD_SOURCE="$ROOT_DIR/Resources/netbird"
-fi
-if [[ -z "$NETBIRD_SOURCE" ]]; then
- NETBIRD_SOURCE="$(command -v netbird || true)"
-fi
-if [[ -n "$NETBIRD_SOURCE" && -x "$NETBIRD_SOURCE" ]]; then
- cp "$NETBIRD_SOURCE" "$APP_DIR/Contents/Resources/netbird"
- chmod 755 "$APP_DIR/Contents/Resources/netbird"
-else
- echo "warning: no netbird binary found to bundle; set NETBIRD_BINARY_PATH=/path/to/netbird" >&2
-fi
-
-cat > "$APP_DIR/Contents/Info.plist" <<'PLIST'
-
-
-
-
- CFBundleDevelopmentRegion
- en
- CFBundleDisplayName
- Vynte Connect
- CFBundleExecutable
- VynteConnect
- CFBundleIdentifier
- com.vyntehome.connect
- CFBundleInfoDictionaryVersion
- 6.0
- CFBundleName
- Vynte Connect
- CFBundlePackageType
- APPL
- CFBundleShortVersionString
- 0.1.0
- CFBundleVersion
- 1
- LSMinimumSystemVersion
- 14.0
- LSUIElement
-
- NSHighResolutionCapable
-
-
-
-PLIST
-
-echo "Built ${APP_DIR}"
diff --git a/apps/vynte-connect/Scripts/notarize-zip.sh b/apps/vynte-connect/Scripts/notarize-zip.sh
deleted file mode 100755
index ca94549..0000000
--- a/apps/vynte-connect/Scripts/notarize-zip.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-: "${APPLE_ID:?Set APPLE_ID to the Apple ID used for notarization.}"
-: "${APPLE_TEAM_ID:?Set APPLE_TEAM_ID to your Apple team ID.}"
-: "${APPLE_APP_PASSWORD:?Set APPLE_APP_PASSWORD to an app-specific password or keychain profile password.}"
-
-ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-ZIP_PATH="${ROOT_DIR}/dist/Vynte-Connect-macOS.zip"
-
-"${ROOT_DIR}/Scripts/sign-app.sh"
-SKIP_BUILD=1 PRESERVE_SIGNATURE=1 "${ROOT_DIR}/Scripts/package-zip.sh"
-
-xcrun notarytool submit "$ZIP_PATH" \
- --apple-id "$APPLE_ID" \
- --team-id "$APPLE_TEAM_ID" \
- --password "$APPLE_APP_PASSWORD" \
- --wait
-
-echo "Notarized ${ZIP_PATH}"
diff --git a/apps/vynte-connect/Scripts/package-zip.sh b/apps/vynte-connect/Scripts/package-zip.sh
deleted file mode 100755
index 73bccfd..0000000
--- a/apps/vynte-connect/Scripts/package-zip.sh
+++ /dev/null
@@ -1,20 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-APP_DIR="${ROOT_DIR}/dist/Vynte Connect.app"
-ZIP_PATH="${ROOT_DIR}/dist/Vynte-Connect-macOS.zip"
-
-if [[ "${SKIP_BUILD:-0}" != "1" ]]; then
- "${ROOT_DIR}/Scripts/build-app.sh"
-fi
-
-if [[ "${PRESERVE_SIGNATURE:-0}" != "1" ]]; then
- codesign --force --deep --sign - "$APP_DIR"
-fi
-codesign --verify --deep --strict --verbose=2 "$APP_DIR"
-
-rm -f "$ZIP_PATH"
-ditto -c -k --keepParent "$APP_DIR" "$ZIP_PATH"
-
-echo "Packaged ${ZIP_PATH}"
diff --git a/apps/vynte-connect/Scripts/sign-app.sh b/apps/vynte-connect/Scripts/sign-app.sh
deleted file mode 100755
index ca64a08..0000000
--- a/apps/vynte-connect/Scripts/sign-app.sh
+++ /dev/null
@@ -1,32 +0,0 @@
-#!/usr/bin/env bash
-set -euo pipefail
-
-: "${DEVELOPER_ID_APPLICATION:?Set DEVELOPER_ID_APPLICATION to your Apple Developer ID Application signing identity.}"
-
-ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
-APP_DIR="${ROOT_DIR}/dist/Vynte Connect.app"
-
-if [[ ! -d "$APP_DIR" ]]; then
- "${ROOT_DIR}/Scripts/build-app.sh"
-fi
-
-if [[ -f "$APP_DIR/Contents/Resources/netbird" ]]; then
- codesign \
- --force \
- --options runtime \
- --timestamp \
- --sign "$DEVELOPER_ID_APPLICATION" \
- "$APP_DIR/Contents/Resources/netbird"
-fi
-
-codesign \
- --force \
- --deep \
- --options runtime \
- --timestamp \
- --sign "$DEVELOPER_ID_APPLICATION" \
- "$APP_DIR"
-
-codesign --verify --deep --strict --verbose=2 "$APP_DIR"
-
-echo "Signed ${APP_DIR}"
diff --git a/apps/vynte-connect/Sources/VynteConnect/AppConfig.swift b/apps/vynte-connect/Sources/VynteConnect/AppConfig.swift
deleted file mode 100644
index d3931ea..0000000
--- a/apps/vynte-connect/Sources/VynteConnect/AppConfig.swift
+++ /dev/null
@@ -1,9 +0,0 @@
-import Foundation
-
-enum AppConfig {
- static let appName = "Vynte Connect"
- static let managementURL = URL(string: "https://vpn.vyntehome.com")!
- static let gatewayHost = managementURL.host() ?? "vpn.vyntehome.com"
- static let installURL = URL(string: "https://docs.netbird.io/get-started/install")!
- static let supportEmail = "vynte@vyntehome.com"
-}
diff --git a/apps/vynte-connect/Sources/VynteConnect/Branding.swift b/apps/vynte-connect/Sources/VynteConnect/Branding.swift
deleted file mode 100644
index fcc2be2..0000000
--- a/apps/vynte-connect/Sources/VynteConnect/Branding.swift
+++ /dev/null
@@ -1,72 +0,0 @@
-import CoreText
-import AppKit
-import SwiftUI
-
-enum VynteBrand {
- static let background = Color(red: 0.078, green: 0.086, blue: 0.102)
- static let panel = Color(red: 0.078, green: 0.086, blue: 0.102)
- static let panelElevated = Color(red: 0.110, green: 0.118, blue: 0.138)
- static let text = Color(red: 0.961, green: 0.965, blue: 0.973)
- static let muted = Color(red: 0.565, green: 0.600, blue: 0.639)
- static let lowText = Color(red: 0.353, green: 0.365, blue: 0.400)
- static let hairline = Color.white.opacity(0.06)
- static let blue = Color(red: 0.118, green: 0.565, blue: 1.000)
- static let cyan = Color(red: 0.180, green: 0.820, blue: 0.940)
- static let orange = Color(red: 1.000, green: 0.478, blue: 0.102)
- static let orangeLight = Color(red: 1.000, green: 0.592, blue: 0.278)
- static let pink = Color(red: 0.902, green: 0.243, blue: 0.361)
- static let purple = Color(red: 0.435, green: 0.169, blue: 0.682)
- static let red = Color(red: 0.902, green: 0.243, blue: 0.361)
- static let errorText = Color(red: 1.000, green: 0.541, blue: 0.612)
-
- static let logoGradient = LinearGradient(
- colors: [orange, pink, purple, blue],
- startPoint: .topLeading,
- endPoint: .bottomTrailing
- )
-
- static let wallpaperGradient = LinearGradient(
- colors: [
- orange.opacity(0.55),
- pink.opacity(0.45),
- purple.opacity(0.40),
- blue.opacity(0.45)
- ],
- startPoint: .topLeading,
- endPoint: .bottomTrailing
- )
-
- static func font(size: CGFloat, weight: Font.Weight = .regular) -> Font {
- .custom("Montserrat", size: size).weight(weight)
- }
-}
-
-enum FontRegistrar {
- static func registerBundledFonts() {
- guard let url = Bundle.module.url(forResource: "Montserrat-VariableFont_wght", withExtension: "ttf") else {
- return
- }
-
- CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
- }
-}
-
-struct BundledImage: View {
- let name: String
- let fileExtension: String
-
- init(_ name: String, fileExtension: String = "png") {
- self.name = name
- self.fileExtension = fileExtension
- }
-
- var body: some View {
- if let url = Bundle.module.url(forResource: name, withExtension: fileExtension),
- let image = NSImage(contentsOf: url) {
- Image(nsImage: image)
- .resizable()
- } else {
- Color.clear
- }
- }
-}
diff --git a/apps/vynte-connect/Sources/VynteConnect/NetBirdService.swift b/apps/vynte-connect/Sources/VynteConnect/NetBirdService.swift
deleted file mode 100644
index b0e65a4..0000000
--- a/apps/vynte-connect/Sources/VynteConnect/NetBirdService.swift
+++ /dev/null
@@ -1,351 +0,0 @@
-@preconcurrency import Foundation
-
-struct CommandResult: Sendable {
- let exitCode: Int32
- let stdout: String
- let stderr: String
-
- var combinedOutput: String {
- [stdout, stderr].filter { !$0.isEmpty }.joined(separator: "\n")
- }
-}
-
-struct NetBirdStatus: Decodable {
- struct ServiceStatus: Decodable {
- let url: String?
- let connected: Bool?
- let error: String?
- }
-
- struct Peers: Decodable {
- let total: Int?
- let connected: Int?
- }
-
- let daemonStatus: String?
- let management: ServiceStatus?
- let signal: ServiceStatus?
- let netbirdIp: String?
- let fqdn: String?
- let peers: Peers?
-}
-
-enum NetBirdServiceError: LocalizedError, Sendable {
- case commandNotFound
- case commandFailed(String)
- case invalidStatus(String)
-
- var errorDescription: String? {
- switch self {
- case .commandNotFound:
- "vpn.vyntehome.com helper is not installed."
- case .commandFailed(let message):
- message
- case .invalidStatus(let message):
- message
- }
- }
-}
-
-struct NetBirdService {
- let executableURL: URL
-
- static func discover() -> NetBirdService? {
- let bundledCandidates = [
- Bundle.main.resourceURL?.appendingPathComponent("netbird").path,
- Bundle.module.url(forResource: "netbird", withExtension: nil)?.path
- ].compactMap { $0 }
-
- let candidates = bundledCandidates + [
- "/opt/homebrew/bin/netbird",
- "/usr/local/bin/netbird",
- "/usr/bin/netbird",
- "/Applications/NetBird.app/Contents/MacOS/netbird"
- ]
-
- let fileManager = FileManager.default
- if let match = candidates.first(where: { fileManager.isExecutableFile(atPath: $0) }) {
- return NetBirdService(executableURL: URL(fileURLWithPath: match))
- }
-
- if let pathMatch = findOnPath("netbird") {
- return NetBirdService(executableURL: URL(fileURLWithPath: pathMatch))
- }
-
- return nil
- }
-
- func status() async throws -> NetBirdStatus {
- let result = try await run(["status", "--json"], timeout: 15)
- guard result.exitCode == 0 else {
- throw NetBirdServiceError.commandFailed(cleanError(result))
- }
-
- guard let data = result.stdout.data(using: .utf8) else {
- throw NetBirdServiceError.invalidStatus("vpn.vyntehome.com returned an unreadable status response.")
- }
-
- do {
- return try JSONDecoder().decode(NetBirdStatus.self, from: data)
- } catch {
- throw NetBirdServiceError.invalidStatus("vpn.vyntehome.com status could not be parsed.")
- }
- }
-
- func readyCheck() async -> Bool {
- let result = try? await run(["status", "--check", "ready"], timeout: 8)
- return result?.exitCode == 0
- }
-
- func connect() async throws {
- try await installServiceIfNeeded()
-
- let result = try await run([
- "up",
- "--management-url", AppConfig.managementURL.absoluteString,
- "--admin-url", AppConfig.managementURL.absoluteString
- ], timeout: 180)
-
- guard result.exitCode == 0 else {
- let message = cleanError(result)
- if message.localizedCaseInsensitiveContains("already registered by a different User") ||
- message.localizedCaseInsensitiveContains("peer login has expired") {
- try await resetLocalState()
- throw NetBirdServiceError.commandFailed("vpn.vyntehome.com had stale registration state. I reset the local state; click Connect again to register with NetBird.")
- }
- throw NetBirdServiceError.commandFailed(message)
- }
- }
-
- func resetLocalState() async throws {
- _ = try? await run(["down", "--management-url", AppConfig.managementURL.absoluteString], timeout: 20)
- _ = try? await run(["deregister", "--management-url", AppConfig.managementURL.absoluteString, "--admin-url", AppConfig.managementURL.absoluteString], timeout: 45)
- try await runPrivilegedShell([
- shellQuoted(executableURL.path),
- "service",
- "stop"
- ].joined(separator: " "))
-
- _ = try? await run(["state", "clean", "--all"], timeout: 20)
-
- try await runPrivilegedShell("rm -rf /etc/netbird /var/root/Library/Application\\ Support/netbird /var/root/Library/Preferences/io.netbird.client.plist")
-
- try await runPrivilegedShell([
- shellQuoted(executableURL.path),
- "service",
- "start"
- ].joined(separator: " "))
- }
-
- func installServiceIfNeeded() async throws {
- if await serviceIsAvailable() {
- return
- }
-
- try await runPrivilegedShell([
- shellQuoted(executableURL.path),
- "service",
- "install",
- "--management-url", shellQuoted(AppConfig.managementURL.absoluteString),
- "--admin-url", shellQuoted(AppConfig.managementURL.absoluteString),
- "--log-level", "info"
- ].joined(separator: " "))
-
- try await runPrivilegedShell([
- shellQuoted(executableURL.path),
- "service",
- "start"
- ].joined(separator: " "))
-
- for _ in 0..<20 {
- if await serviceIsAvailable() {
- return
- }
- try? await Task.sleep(for: .milliseconds(500))
- }
-
- throw NetBirdServiceError.commandFailed("vpn.vyntehome.com helper was installed, but it did not become ready.")
- }
-
- func disconnect() async throws {
- let result = try await run([
- "down",
- "--management-url", AppConfig.managementURL.absoluteString
- ], timeout: 45)
-
- guard result.exitCode == 0 else {
- throw NetBirdServiceError.commandFailed(cleanError(result))
- }
- }
-
- func logout() async throws {
- _ = try? await run([
- "down",
- "--management-url", AppConfig.managementURL.absoluteString
- ], timeout: 30)
-
- let result = try await run([
- "deregister",
- "--management-url", AppConfig.managementURL.absoluteString,
- "--admin-url", AppConfig.managementURL.absoluteString
- ], timeout: 60)
-
- guard result.exitCode == 0 else {
- let message = cleanError(result)
- if message.localizedCaseInsensitiveContains("not registered") ||
- message.localizedCaseInsensitiveContains("no active profile") {
- return
- }
- throw NetBirdServiceError.commandFailed(message)
- }
- }
-
- private func cleanError(_ result: CommandResult) -> String {
- let output = result.combinedOutput.trimmingCharacters(in: .whitespacesAndNewlines)
- if output.isEmpty {
- return "vpn.vyntehome.com exited with code \(result.exitCode)."
- }
- return output
- }
-
- private func run(_ arguments: [String], timeout: TimeInterval) async throws -> CommandResult {
- try await CommandRunner.run(
- executableURL: executableURL,
- arguments: arguments,
- timeout: timeout
- )
- }
-
- private func serviceIsAvailable() async -> Bool {
- if FileManager.default.fileExists(atPath: "/var/run/netbird.sock") {
- return true
- }
-
- let statusResult = try? await run(["status", "--json"], timeout: 8)
- return statusResult?.exitCode == 0
- }
-
- private func runPrivilegedShell(_ command: String) async throws {
- let script = "do shell script \(appleScriptStringLiteral(command)) with administrator privileges"
- let result = try await CommandRunner.run(
- executableURL: URL(fileURLWithPath: "/usr/bin/osascript"),
- arguments: ["-e", script],
- timeout: 120
- )
-
- guard result.exitCode == 0 else {
- throw NetBirdServiceError.commandFailed(cleanError(result))
- }
- }
-
- private func shellQuoted(_ value: String) -> String {
- "'\(value.replacingOccurrences(of: "'", with: "'\\''"))'"
- }
-
- private func appleScriptStringLiteral(_ value: String) -> String {
- "\"\(value.replacingOccurrences(of: "\\", with: "\\\\").replacingOccurrences(of: "\"", with: "\\\""))\""
- }
-
- private static func findOnPath(_ executable: String) -> String? {
- let path = ProcessInfo.processInfo.environment["PATH"] ?? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
- for directory in path.split(separator: ":") {
- let candidate = "\(directory)/\(executable)"
- if FileManager.default.isExecutableFile(atPath: candidate) {
- return candidate
- }
- }
- return nil
- }
-}
-
-enum CommandRunner {
- static func run(
- executableURL: URL,
- arguments: [String],
- timeout: TimeInterval
- ) async throws -> CommandResult {
- try await withCheckedThrowingContinuation { continuation in
- let process = Process()
- process.executableURL = executableURL
- process.arguments = arguments
- process.environment = processEnvironment()
-
- let stdout = Pipe()
- let stderr = Pipe()
- process.standardOutput = stdout
- process.standardError = stderr
-
- let state = CommandState(continuation: continuation)
-
- process.terminationHandler = { process in
- let stdoutData = stdout.fileHandleForReading.readDataToEndOfFile()
- let stderrData = stderr.fileHandleForReading.readDataToEndOfFile()
- let result = CommandResult(
- exitCode: process.terminationStatus,
- stdout: String(data: stdoutData, encoding: .utf8) ?? "",
- stderr: String(data: stderrData, encoding: .utf8) ?? ""
- )
- state.finish(.success(result))
- }
-
- do {
- try process.run()
- } catch {
- state.finish(.failure(error))
- return
- }
-
- DispatchQueue.global().asyncAfter(deadline: .now() + timeout) {
- if state.shouldTerminate {
- process.terminate()
- DispatchQueue.global().asyncAfter(deadline: .now() + 1) {
- if process.isRunning {
- kill(process.processIdentifier, SIGKILL)
- }
- }
- state.finish(.failure(NetBirdServiceError.commandFailed("vpn.vyntehome.com command timed out.")))
- }
- }
- }
- }
-
- private static func processEnvironment() -> [String: String] {
- var environment = ProcessInfo.processInfo.environment
- environment["PATH"] = [
- "/opt/homebrew/bin",
- "/usr/local/bin",
- "/usr/bin",
- "/bin",
- "/usr/sbin",
- "/sbin"
- ].joined(separator: ":")
- return environment
- }
-}
-
-private final class CommandState: @unchecked Sendable {
- private let lock = NSLock()
- private var completed = false
- private let continuation: CheckedContinuation
-
- init(continuation: CheckedContinuation) {
- self.continuation = continuation
- }
-
- var shouldTerminate: Bool {
- lock.lock()
- defer { lock.unlock() }
- return !completed
- }
-
- func finish(_ result: Result) {
- lock.lock()
- guard !completed else {
- lock.unlock()
- return
- }
- completed = true
- lock.unlock()
- continuation.resume(with: result)
- }
-}
diff --git a/apps/vynte-connect/Sources/VynteConnect/StatusPopover.swift b/apps/vynte-connect/Sources/VynteConnect/StatusPopover.swift
deleted file mode 100644
index 678bb9c..0000000
--- a/apps/vynte-connect/Sources/VynteConnect/StatusPopover.swift
+++ /dev/null
@@ -1,487 +0,0 @@
-import SwiftUI
-
-struct StatusPopover: View {
- @ObservedObject var model: VynteConnectModel
-
- var body: some View {
- ZStack {
- VynteBackground()
-
- VStack(spacing: 0) {
- header
- hero
- gatewayStrip
-
- if model.isConnected {
- statsRow
- }
-
- footer
- }
- .background(.black.opacity(0.10))
- .clipShape(RoundedRectangle(cornerRadius: 14, style: .continuous))
- .overlay {
- RoundedRectangle(cornerRadius: 14, style: .continuous)
- .stroke(VynteBrand.hairline, lineWidth: 1)
- }
- .shadow(color: .black.opacity(0.46), radius: 28, y: 18)
- .padding(10)
- }
- .preferredColorScheme(.dark)
- }
-
- private var header: some View {
- HStack(spacing: 10) {
- BundledImage("VynteIcon")
- .scaledToFit()
- .frame(width: 22, height: 22)
- .clipShape(RoundedRectangle(cornerRadius: 4, style: .continuous))
-
- VStack(alignment: .leading, spacing: 1) {
- Text("Vynte Connect")
- .font(.system(size: 13, weight: .semibold))
- .foregroundStyle(VynteBrand.text)
-
- Text(model.identityLabel)
- .font(.system(size: 11, weight: .regular))
- .foregroundStyle(VynteBrand.muted)
- .lineLimit(1)
- .truncationMode(.middle)
- }
-
- Spacer()
-
- RefreshButton(isWorking: model.isWorking) {
- model.refreshNow()
- }
- }
- .padding(.horizontal, 14)
- .padding(.vertical, 12)
- .overlay(alignment: .bottom) {
- Rectangle()
- .fill(VynteBrand.hairline)
- .frame(height: 1)
- }
- }
-
- private var hero: some View {
- VStack(spacing: 12) {
- Button {
- model.primaryAction()
- } label: {
- ConnectOrb(state: model.state)
- }
- .buttonStyle(.plain)
- .disabled(model.isWorking && !model.isConnecting)
- .help(model.primaryButtonTitle)
-
- VStack(spacing: 4) {
- HStack(spacing: 8) {
- if model.isConnected {
- StatusDot(color: VynteBrand.orange, glow: true, size: 8)
- } else if model.isError {
- StatusDot(color: VynteBrand.red, glow: false, size: 8)
- }
-
- Text(model.statusTitle)
- .font(.system(size: 16, weight: .semibold))
- .foregroundStyle(model.isError ? VynteBrand.errorText : VynteBrand.text)
- }
-
- Text(model.statusMessage)
- .font(.system(size: 12, weight: .regular))
- .foregroundStyle(VynteBrand.muted)
- .multilineTextAlignment(.center)
- .lineLimit(2)
- .fixedSize(horizontal: false, vertical: true)
- }
- .padding(.horizontal, 18)
- }
- .padding(.top, 20)
- .padding(.bottom, 16)
- }
-
- private var gatewayStrip: some View {
- HStack(spacing: 10) {
- Image(systemName: "server.rack")
- .font(.system(size: 14, weight: .medium))
- .foregroundStyle(VynteBrand.muted)
- .frame(width: 28, height: 28)
- .background(.white.opacity(0.04), in: RoundedRectangle(cornerRadius: 6, style: .continuous))
-
- VStack(alignment: .leading, spacing: 2) {
- Text("GATEWAY")
- .font(.system(size: 10, weight: .medium))
- .tracking(0.6)
- .foregroundStyle(VynteBrand.lowText)
-
- Text(AppConfig.gatewayHost)
- .font(.system(size: 12.5, weight: .medium, design: .monospaced))
- .foregroundStyle(VynteBrand.text)
- .lineLimit(1)
- .truncationMode(.middle)
- }
-
- Spacer()
-
- Text(model.isConnected ? "LIVE" : "OFFLINE")
- .font(.system(size: 10, weight: .semibold))
- .tracking(0.4)
- .foregroundStyle(model.isConnected ? VynteBrand.orangeLight : VynteBrand.lowText)
- .padding(.horizontal, 7)
- .padding(.vertical, 3)
- .background(
- model.isConnected ? VynteBrand.orange.opacity(0.14) : .white.opacity(0.06),
- in: Capsule()
- )
- }
- .padding(10)
- .background(.white.opacity(0.04), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
- .overlay {
- RoundedRectangle(cornerRadius: 10, style: .continuous)
- .stroke(VynteBrand.hairline, lineWidth: 1)
- }
- .padding(.horizontal, 14)
- .padding(.bottom, 12)
- }
-
- private var statsRow: some View {
- HStack(spacing: 0) {
- StatCell(label: "Internal IP", value: model.netbirdIP ?? "Assigned")
- Rectangle()
- .fill(VynteBrand.hairline)
- .frame(width: 1)
- StatCell(label: "Uptime", value: model.uptimeLabel)
- }
- .background(.white.opacity(0.04), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
- .overlay {
- RoundedRectangle(cornerRadius: 10, style: .continuous)
- .stroke(VynteBrand.hairline, lineWidth: 1)
- }
- .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
- .padding(.horizontal, 14)
- .padding(.bottom, 12)
- }
-
- private var footer: some View {
- HStack(spacing: 4) {
- Button {
- model.toggleAutoConnect()
- } label: {
- HStack(spacing: 6) {
- Image(systemName: "bolt.fill")
- .font(.system(size: 11, weight: .semibold))
- Text("Auto-connect")
- .font(.system(size: 11, weight: .medium))
- TogglePill(isOn: model.autoConnectEnabled)
- }
- .padding(.horizontal, 10)
- .padding(.vertical, 6)
- }
- .buttonStyle(VynteFooterChipStyle(active: model.autoConnectEnabled))
- .help("Connect automatically when Vynte Connect starts")
-
- Spacer()
-
- if model.isConnected {
- Button {
- model.logout()
- } label: {
- Image(systemName: "person.crop.circle.badge.xmark")
- .font(.system(size: 13, weight: .semibold))
- .frame(width: 30, height: 26)
- }
- .buttonStyle(VynteIconButtonStyle())
- .help("Log out and register with NetBird again")
- }
-
- Button {
- model.quit()
- } label: {
- Image(systemName: "rectangle.portrait.and.arrow.right")
- .font(.system(size: 13, weight: .semibold))
- .frame(width: 30, height: 26)
- }
- .buttonStyle(VynteIconButtonStyle())
- .help("Quit Vynte Connect")
- }
- .padding(.horizontal, 6)
- .padding(.vertical, 8)
- .overlay(alignment: .top) {
- Rectangle()
- .fill(VynteBrand.hairline)
- .frame(height: 1)
- }
- }
-}
-
-struct VynteBackground: View {
- var body: some View {
- Color(red: 0.098, green: 0.106, blue: 0.124)
- .overlay(.ultraThinMaterial.opacity(0.34))
- .overlay {
- VynteBrand.wallpaperGradient
- .opacity(0.28)
- }
- }
-}
-
-struct RefreshButton: View {
- let isWorking: Bool
- let action: () -> Void
-
- var body: some View {
- TimelineView(.animation(minimumInterval: 1.0 / 30.0, paused: !isWorking)) { tl in
- let angle = isWorking
- ? tl.date.timeIntervalSinceReferenceDate.truncatingRemainder(dividingBy: 0.8) / 0.8 * 360
- : 0.0
- Button(action: action) {
- Image(systemName: "arrow.clockwise")
- .font(.system(size: 13, weight: .semibold))
- .frame(width: 26, height: 26)
- .rotationEffect(.degrees(angle))
- }
- .buttonStyle(VynteIconButtonStyle())
- .disabled(isWorking)
- .help("Refresh status")
- }
- }
-}
-
-struct ConnectOrb: View {
- let state: VPNConnectionState
-
- private var isConnected: Bool {
- if case .connected = state { return true }
- return false
- }
-
- private var isConnecting: Bool {
- if case .connecting = state { return true }
- if case .checking = state { return true }
- return false
- }
-
- private var isDisconnecting: Bool {
- if case .disconnecting = state { return true }
- if case .loggingOut = state { return true }
- return false
- }
-
- private var isTransitioning: Bool {
- isConnecting || isDisconnecting
- }
-
- private var isError: Bool {
- if case .error = state { return true }
- if case .netbirdMissing = state { return true }
- return false
- }
-
- var body: some View {
- TimelineView(.animation(minimumInterval: 1.0 / 60.0, paused: !isTransitioning)) { timeline in
- let elapsed = timeline.date.timeIntervalSinceReferenceDate
- let rotation = transitionRotation(elapsed)
-
- ZStack {
- Circle()
- .fill(orbRing)
- .frame(width: 116, height: 116)
- .opacity(isError ? 0.58 : 1)
- .shadow(color: activeGlow.opacity(glowOpacity), radius: 22)
- .shadow(color: VynteBrand.purple.opacity(isConnected ? 0.24 : 0), radius: 38)
-
- Circle()
- .fill(
- RadialGradient(
- colors: [
- Color(red: 0.122, green: 0.133, blue: 0.157),
- Color(red: 0.078, green: 0.086, blue: 0.102)
- ],
- center: UnitPoint(x: 0.30, y: 0.25),
- startRadius: 4,
- endRadius: 58
- )
- )
- .frame(width: 112, height: 112)
-
- if isTransitioning {
- Circle()
- .trim(from: transitionTrimStart, to: transitionTrimEnd)
- .stroke(
- AngularGradient(
- colors: transitionGradientColors,
- center: .center
- ),
- style: StrokeStyle(lineWidth: 4, lineCap: .round)
- )
- .frame(width: 124, height: 124)
- .rotationEffect(.degrees(rotation))
- }
-
- if isConnected {
- BundledImage("VynteIcon")
- .scaledToFit()
- .frame(width: 52, height: 52)
- .clipShape(RoundedRectangle(cornerRadius: 10, style: .continuous))
- } else {
- Image(systemName: stateIcon)
- .font(.system(size: 38, weight: .regular))
- .foregroundStyle(iconColor)
- }
- }
- .frame(width: 126, height: 126)
- }
- }
-
- private var orbRing: AnyShapeStyle {
- if isConnected || isTransitioning {
- return AnyShapeStyle(VynteBrand.logoGradient)
- }
- return AnyShapeStyle(
- AngularGradient(
- colors: [
- Color(red: 0.165, green: 0.176, blue: 0.200),
- Color(red: 0.235, green: 0.247, blue: 0.282),
- Color(red: 0.165, green: 0.176, blue: 0.200)
- ],
- center: .center,
- angle: .degrees(90)
- )
- )
- }
-
- private var stateIcon: String {
- switch state {
- case .netbirdMissing:
- "square.and.arrow.down"
- case .error:
- "exclamationmark"
- case .loggingOut:
- "person.crop.circle.badge.xmark"
- default:
- "power"
- }
- }
-
- private var iconColor: Color {
- if isError {
- return VynteBrand.pink
- }
- if isTransitioning {
- return VynteBrand.text
- }
- return Color(red: 0.478, green: 0.494, blue: 0.529)
- }
-
- private var activeGlow: Color {
- isDisconnecting ? VynteBrand.purple : VynteBrand.orange
- }
-
- private var glowOpacity: Double {
- isConnected ? 0.34 : 0
- }
-
- private var transitionTrimStart: CGFloat {
- isDisconnecting ? 0.18 : 0.04
- }
-
- private var transitionTrimEnd: CGFloat {
- isDisconnecting ? 0.52 : 0.74
- }
-
- private var transitionGradientColors: [Color] {
- if isDisconnecting {
- return [.clear, VynteBrand.blue.opacity(0.45), VynteBrand.purple, VynteBrand.pink.opacity(0.80)]
- }
- return [.clear, VynteBrand.orange, VynteBrand.pink, VynteBrand.blue]
- }
-
- private func transitionRotation(_ elapsed: TimeInterval) -> Double {
- let duration = isDisconnecting ? 1.35 : 1.05
- let progress = elapsed.truncatingRemainder(dividingBy: duration) / duration
- return (isDisconnecting ? -360 : 360) * progress
- }
-
-}
-
-struct StatusDot: View {
- let color: Color
- let glow: Bool
- let size: CGFloat
-
- var body: some View {
- Circle()
- .fill(color)
- .frame(width: size, height: size)
- .shadow(color: glow ? color.opacity(0.55) : .clear, radius: 8)
- .overlay {
- if glow {
- Circle()
- .stroke(color.opacity(0.18), lineWidth: 6)
- }
- }
- }
-}
-
-struct StatCell: View {
- let label: String
- let value: String
-
- var body: some View {
- VStack(alignment: .leading, spacing: 3) {
- Text(label.uppercased())
- .font(.system(size: 10, weight: .medium))
- .tracking(0.6)
- .foregroundStyle(VynteBrand.lowText)
-
- Text(value)
- .font(.system(size: 12.5, weight: .medium, design: .monospaced))
- .foregroundStyle(VynteBrand.text)
- .lineLimit(1)
- .truncationMode(.middle)
- }
- .frame(maxWidth: .infinity, alignment: .leading)
- .padding(.horizontal, 12)
- .padding(.vertical, 9)
- .background(.black.opacity(0.18))
- }
-}
-
-struct TogglePill: View {
- let isOn: Bool
-
- var body: some View {
- ZStack(alignment: isOn ? .trailing : .leading) {
- Capsule()
- .fill(isOn ? AnyShapeStyle(VynteBrand.logoGradient) : AnyShapeStyle(.white.opacity(0.10)))
- .frame(width: 22, height: 12)
-
- Circle()
- .fill(.white)
- .frame(width: 9, height: 9)
- .padding(.horizontal, 1.5)
- }
- }
-}
-
-struct VynteIconButtonStyle: ButtonStyle {
- func makeBody(configuration: Configuration) -> some View {
- configuration.label
- .foregroundStyle(configuration.isPressed ? VynteBrand.text : VynteBrand.muted)
- .background(configuration.isPressed ? .white.opacity(0.08) : .clear, in: RoundedRectangle(cornerRadius: 6))
- }
-}
-
-struct VynteFooterChipStyle: ButtonStyle {
- let active: Bool
-
- func makeBody(configuration: Configuration) -> some View {
- configuration.label
- .foregroundStyle(active ? VynteBrand.orangeLight : VynteBrand.muted)
- .background(
- active ? VynteBrand.orange.opacity(configuration.isPressed ? 0.16 : 0.10) : .white.opacity(configuration.isPressed ? 0.06 : 0),
- in: RoundedRectangle(cornerRadius: 6, style: .continuous)
- )
- }
-}
diff --git a/apps/vynte-connect/Sources/VynteConnect/VynteConnectApp.swift b/apps/vynte-connect/Sources/VynteConnect/VynteConnectApp.swift
deleted file mode 100644
index 77f11b1..0000000
--- a/apps/vynte-connect/Sources/VynteConnect/VynteConnectApp.swift
+++ /dev/null
@@ -1,47 +0,0 @@
-import AppKit
-import SwiftUI
-
-@main
-struct VynteConnectApp: App {
- @StateObject private var model = VynteConnectModel()
-
- init() {
- FontRegistrar.registerBundledFonts()
- }
-
- var body: some Scene {
- MenuBarExtra {
- StatusPopover(model: model)
- .frame(width: 384)
- } label: {
- VynteMenuBarIcon()
- .accessibilityLabel("Vynte Connect")
- }
- .menuBarExtraStyle(.window)
- }
-}
-
-struct VynteMenuBarIcon: View {
- var body: some View {
- if let image = Self.image {
- Image(nsImage: image)
- .resizable()
- .renderingMode(.original)
- .scaledToFit()
- .frame(width: 18, height: 18)
- } else {
- Image(systemName: "flame.fill")
- .font(.system(size: 14, weight: .semibold))
- }
- }
-
- private static var image: NSImage? {
- guard let url = Bundle.module.url(forResource: "VynteMenuBarIcon", withExtension: "png"),
- let image = NSImage(contentsOf: url) else {
- return nil
- }
- image.size = NSSize(width: 18, height: 18)
- image.isTemplate = false
- return image
- }
-}
diff --git a/apps/vynte-connect/Sources/VynteConnect/VynteConnectModel.swift b/apps/vynte-connect/Sources/VynteConnect/VynteConnectModel.swift
deleted file mode 100644
index 0f8ef43..0000000
--- a/apps/vynte-connect/Sources/VynteConnect/VynteConnectModel.swift
+++ /dev/null
@@ -1,480 +0,0 @@
-import AppKit
-import Foundation
-
-enum VPNConnectionState: Equatable {
- case checking
- case netbirdMissing
- case disconnected
- case connecting
- case disconnecting
- case connected
- case loggingOut
- case error(String)
-}
-
-@MainActor
-final class VynteConnectModel: ObservableObject {
- @Published private(set) var state: VPNConnectionState = .checking
- @Published private(set) var netbirdPath: String?
- @Published private(set) var netbirdIP: String?
- @Published private(set) var fqdn: String?
- @Published private(set) var connectedPeers: Int = 0
- @Published private(set) var totalPeers: Int = 0
- @Published private(set) var lastUpdated: Date?
- @Published private(set) var autoConnectEnabled: Bool
- @Published private(set) var connectedSince: Date?
- @Published private(set) var isCompletingRegistration = false
-
- private var service: NetBirdService?
- private var refreshTask: Task?
- private var connectTask: Task?
- private var logoutTask: Task?
- private var hasAttemptedStartupAutoConnect = false
- private let defaults = UserDefaults.standard
-
- private static let autoConnectDefaultsKey = "VynteConnect.autoConnectEnabled"
-
- init() {
- autoConnectEnabled = defaults.bool(forKey: Self.autoConnectDefaultsKey)
- refreshTask = Task { [weak self] in
- await self?.refreshLoop()
- }
- }
-
- deinit {
- refreshTask?.cancel()
- connectTask?.cancel()
- logoutTask?.cancel()
- }
-
- var menuBarSystemImage: String {
- switch state {
- case .checking:
- "circle.dotted"
- case .netbirdMissing:
- "exclamationmark.triangle"
- case .disconnected:
- "flame"
- case .connecting:
- "arrow.triangle.2.circlepath"
- case .disconnecting:
- "arrow.triangle.2.circlepath"
- case .connected:
- "flame.fill"
- case .loggingOut:
- "rectangle.portrait.and.arrow.right"
- case .error:
- "exclamationmark.octagon"
- }
- }
-
- var primaryButtonTitle: String {
- switch state {
- case .connected:
- "Disconnect"
- case .connecting:
- "Connecting..."
- case .disconnecting:
- "Disconnecting..."
- case .netbirdMissing:
- "Install helper"
- case .loggingOut:
- "Logging out..."
- default:
- "Connect"
- }
- }
-
- var statusTitle: String {
- switch state {
- case .checking:
- "Checking vpn.vyntehome.com"
- case .netbirdMissing:
- "vpn.vyntehome.com missing"
- case .disconnected:
- "Disconnected"
- case .connecting:
- "Connecting..."
- case .disconnecting:
- "Disconnecting..."
- case .connected:
- "Connected"
- case .loggingOut:
- "Logging out"
- case .error:
- "Connection failed"
- }
- }
-
- var statusMessage: String {
- switch state {
- case .checking:
- return "Preparing the Vynte access client."
- case .netbirdMissing:
- return "vpn.vyntehome.com helper is not bundled with this build."
- case .disconnected:
- return "Click the flame to connect with NetBird."
- case .connecting:
- if isCompletingRegistration {
- return "Finishing NetBird registration..."
- }
- return "Starting secure Vynte access..."
- case .disconnecting:
- return "Closing secure Vynte access..."
- case .connected:
- return "You're on the Vynte network."
- case .loggingOut:
- return "Removing this Mac's NetBird registration."
- case .error(let message):
- return message
- }
- }
-
- var isConnected: Bool {
- if case .connected = state {
- return true
- }
- return false
- }
-
- var isWorking: Bool {
- if case .checking = state {
- return true
- }
- if case .connecting = state {
- return true
- }
- if case .disconnecting = state {
- return true
- }
- if case .loggingOut = state {
- return true
- }
- return false
- }
-
- var isConnecting: Bool {
- if case .connecting = state {
- return true
- }
- return false
- }
-
- var isDisconnecting: Bool {
- if case .disconnecting = state {
- return true
- }
- return false
- }
-
- var isError: Bool {
- if case .error = state {
- return true
- }
- if case .netbirdMissing = state {
- return true
- }
- return false
- }
-
- var identityLabel: String {
- if let fqdn {
- return fqdn
- }
-
- let user = NSUserName()
- if user.isEmpty {
- return "Vynte access"
- }
- return "\(user)@vynte"
- }
-
- var uptimeLabel: String {
- guard let connectedSince else {
- return "00:00:00"
- }
-
- let elapsed = max(0, Int(Date().timeIntervalSince(connectedSince)))
- let hours = elapsed / 3600
- let minutes = (elapsed % 3600) / 60
- let seconds = elapsed % 60
- return String(format: "%02d:%02d:%02d", hours, minutes, seconds)
- }
-
- func primaryAction() {
- switch state {
- case .netbirdMissing:
- NSWorkspace.shared.open(AppConfig.installURL)
- case .connected:
- disconnect()
- case .connecting, .disconnecting, .checking, .loggingOut:
- break
- default:
- connect()
- }
- }
-
- func logout() {
- connectTask?.cancel()
- logoutTask?.cancel()
- isCompletingRegistration = false
- state = .loggingOut
-
- logoutTask = Task { [weak self] in
- guard let self else { return }
- await self.ensureService()
- guard let service = self.service else {
- await MainActor.run { self.state = .netbirdMissing }
- return
- }
-
- do {
- try await service.logout()
- await MainActor.run {
- self.clearStatusFields()
- self.state = .disconnected
- }
- } catch {
- await MainActor.run {
- self.state = .error(Self.userFacingError(error))
- }
- }
- }
- }
-
- func refreshNow() {
- Task {
- await refresh()
- }
- }
-
- func toggleAutoConnect() {
- autoConnectEnabled.toggle()
- defaults.set(autoConnectEnabled, forKey: Self.autoConnectDefaultsKey)
- }
-
- func quit() {
- NSApplication.shared.terminate(nil)
- }
-
- private func connect() {
- connectTask?.cancel()
- isCompletingRegistration = false
- state = .connecting
-
- connectTask = Task { [weak self] in
- guard let self else { return }
- await self.ensureService()
- guard let service = self.service else {
- await MainActor.run { self.state = .netbirdMissing }
- return
- }
-
- do {
- try await service.connect()
- await MainActor.run {
- self.isCompletingRegistration = false
- }
- await self.waitForReady()
- } catch {
- await MainActor.run {
- self.state = .error(Self.userFacingError(error))
- }
- }
- }
- }
-
- private func disconnect() {
- connectTask?.cancel()
- isCompletingRegistration = false
- state = .disconnecting
-
- Task { [weak self] in
- guard let self else { return }
- guard let service = self.service ?? NetBirdService.discover() else {
- await MainActor.run { self.state = .netbirdMissing }
- return
- }
-
- do {
- try await service.disconnect()
- await MainActor.run {
- self.connectedSince = nil
- }
- await self.refresh()
- } catch {
- await MainActor.run {
- self.state = .error(Self.userFacingError(error))
- }
- }
- }
- }
-
- private func refreshLoop() async {
- await refresh()
- await runStartupAutoConnectIfNeeded()
- while !Task.isCancelled {
- try? await Task.sleep(for: .seconds(12))
- await refresh()
- }
- }
-
- private func ensureService() async {
- if service == nil {
- service = NetBirdService.discover()
- netbirdPath = service?.executableURL.path
- }
- }
-
- private func refresh() async {
- await ensureService()
- guard let service else {
- state = .netbirdMissing
- return
- }
-
- do {
- let status = try await service.status()
- updateStatusFields(status)
- lastUpdated = Date()
-
- if connectionIsActive(status) {
- state = .connected
- isCompletingRegistration = false
- if connectedSince == nil {
- connectedSince = Date()
- }
- } else {
- state = .disconnected
- isCompletingRegistration = false
- connectedSince = nil
- }
- } catch {
- isCompletingRegistration = false
- connectedSince = nil
- let message = Self.userFacingError(error)
- if message.localizedCaseInsensitiveContains("daemon") || message.localizedCaseInsensitiveContains("service") {
- state = .error("Click the flame to install the Vynte network helper.")
- } else {
- state = .error(message)
- }
- }
- }
-
- private func waitForReady() async {
- guard let service else {
- state = .netbirdMissing
- return
- }
-
- for _ in 0..<60 {
- do {
- let status = try await service.status()
- updateStatusFields(status)
- lastUpdated = Date()
-
- if connectionIsActive(status) {
- state = .connected
- isCompletingRegistration = false
- if connectedSince == nil {
- connectedSince = Date()
- }
- return
- }
-
- if let staleRegistrationMessage = staleRegistrationMessage(status) {
- try? await service.resetLocalState()
- isCompletingRegistration = false
- state = .error(staleRegistrationMessage)
- return
- }
-
- isCompletingRegistration = registrationIsPending(status)
- } catch {
- // During registration, the daemon can briefly return partial status.
- }
-
- if await service.readyCheck() {
- await refresh()
- if isConnected {
- return
- }
- }
-
- try? await Task.sleep(for: .seconds(2))
- }
-
- isCompletingRegistration = false
- state = .error("NetBird registration did not finish. Try Connect again.")
- }
-
- private func runStartupAutoConnectIfNeeded() async {
- guard autoConnectEnabled, !hasAttemptedStartupAutoConnect else {
- return
- }
-
- hasAttemptedStartupAutoConnect = true
- if case .disconnected = state {
- connect()
- }
- }
-
- private func updateStatusFields(_ status: NetBirdStatus) {
- netbirdIP = status.netbirdIp
- fqdn = status.fqdn
- connectedPeers = status.peers?.connected ?? 0
- totalPeers = status.peers?.total ?? 0
- }
-
- private func clearStatusFields() {
- netbirdIP = nil
- fqdn = nil
- connectedPeers = 0
- totalPeers = 0
- connectedSince = nil
- lastUpdated = Date()
- isCompletingRegistration = false
- }
-
- private func connectionIsActive(_ status: NetBirdStatus) -> Bool {
- let managementConnected = status.management?.connected == true
- let signalConnected = status.signal?.connected == true
- let daemonConnected = status.daemonStatus?.localizedCaseInsensitiveContains("connected") == true
- let hasAssignedIdentity = (status.netbirdIp?.isEmpty == false) || (status.fqdn?.isEmpty == false)
- return daemonConnected && (managementConnected || signalConnected || hasAssignedIdentity)
- }
-
- private func staleRegistrationMessage(_ status: NetBirdStatus) -> String? {
- let errors = [
- status.management?.error,
- status.signal?.error
- ].compactMap { $0 }.joined(separator: "\n")
-
- if errors.localizedCaseInsensitiveContains("already registered by a different User") {
- return "vpn.vyntehome.com had stale registration state from a previous NetBird login. I reset the local state; click Connect again to register with NetBird."
- }
-
- return nil
- }
-
- private func registrationIsPending(_ status: NetBirdStatus) -> Bool {
- if status.daemonStatus?.localizedCaseInsensitiveContains("needslogin") == true {
- return true
- }
-
- let errors = [
- status.management?.error,
- status.signal?.error
- ].compactMap { $0 }.joined(separator: "\n")
-
- return errors.localizedCaseInsensitiveContains("please log in") ||
- errors.localizedCaseInsensitiveContains("no peer auth method provided")
- }
-
- private static func userFacingError(_ error: Error) -> String {
- if let localized = (error as? LocalizedError)?.errorDescription, !localized.isEmpty {
- return localized
- }
- return error.localizedDescription
- }
-}
diff --git a/docs/vynte-connect-design-spec.md b/docs/vynte-connect-design-spec.md
deleted file mode 100644
index 4ecca4d..0000000
--- a/docs/vynte-connect-design-spec.md
+++ /dev/null
@@ -1,84 +0,0 @@
-# Vynte Connect Design Spec
-
-## Purpose
-
-Vynte Connect is an internal macOS menu bar app for connecting to the VynteHome NetBird VPN. It should feel obvious to non-infrastructure users: open app, sign in, connect, then open internal apps.
-
-## Visual Direction
-
-Use the Vynte pitch deck style:
-
-- Dark base with soft blue, orange, and pink warmth.
-- Flame icon asset from `icon.png` as the app and menu bar logo.
-- Montserrat typography.
-- Minimal copy focused on comfort, home, and secure team access.
-
-## Frames
-
-### First Launch
-
-- Header: VYNTE mark and wordmark.
-- Title: `Private access for the team`
-- Body: `Connect to VynteHome to open internal apps securely.`
-- Primary action: `Connect to VynteHome`
-- Secondary note: `Authentication opens in your browser.`
-
-### NetBird Missing
-
-- Title: `NetBird required`
-- Body: `Vynte Connect uses the official NetBird client securely underneath. Install NetBird, then come back here.`
-- Primary action: `Install NetBird`
-- Secondary action: `Refresh status`
-
-### Disconnected
-
-- Status: gray dot.
-- Title: `Ready to connect`
-- Body: `Connect to open Vynte internal apps securely.`
-- Primary action: `Connect to VynteHome`
-- Help bullets:
- - `Authentication opens in your browser.`
- - `Internal apps unlock after the tunnel connects.`
- - `Vynte never stores your password.`
-
-### Connecting
-
-- Status: orange dot and spinner.
-- Title: `Authenticating`
-- Body: `Finishing NetBird registration...`
-- Primary action disabled: `Connecting...`
-
-### Connected
-
-- Status: green dot.
-- Title: `Connected`
-- Body: `Secure tunnel active as .`
-- Primary action: `Disconnect`
-- App links:
- - `Vynte Home` -> `https://vynte.internal.vyntehome.com`
- - `Plane` -> `https://plane.internal.zachsharma.us`
-- Show small reachability dots for internal links.
-
-### Error
-
-- Status: red dot.
-- Title: `Needs attention`
-- Body: concise error explanation.
-- Primary action changes based on error:
- - Missing NetBird: `Install NetBird`
- - Other errors: `Connect to VynteHome`
-- Footer always includes `Dashboard`, `Refresh`, and `Quit`.
-
-## Figma Notes
-
-Create a compact screen set, not a full design system. Suggested frame size: `384 x 560`, matching the app popover. Components to include:
-
-- Vynte mark and wordmark lockup.
-- Status card.
-- Primary button.
-- Internal app link row.
-- Error/help bullet row.
-
-Use the pitch deck background asset as a reference, but keep the actual app UI native SwiftUI so it remains lightweight.
-
-The browser-reviewable mockup sheet is in `docs/vynte-connect-mockups.html`.
diff --git a/forge.config.ts b/forge.config.ts
new file mode 100644
index 0000000..c59a670
--- /dev/null
+++ b/forge.config.ts
@@ -0,0 +1,59 @@
+import type { ForgeConfig } from '@electron-forge/shared-types';
+import { MakerSquirrel } from '@electron-forge/maker-squirrel';
+import { MakerZIP } from '@electron-forge/maker-zip';
+import { MakerDeb } from '@electron-forge/maker-deb';
+import { MakerRpm } from '@electron-forge/maker-rpm';
+import { VitePlugin } from '@electron-forge/plugin-vite';
+import { FusesPlugin } from '@electron-forge/plugin-fuses';
+import { FuseV1Options, FuseVersion } from '@electron/fuses';
+
+const config: ForgeConfig = {
+ packagerConfig: {
+ asar: true,
+ },
+ rebuildConfig: {},
+ makers: [
+ new MakerSquirrel({}),
+ new MakerZIP({}, ['darwin']),
+ new MakerRpm({}),
+ new MakerDeb({}),
+ ],
+ plugins: [
+ new VitePlugin({
+ // `build` can specify multiple entry builds, which can be Main process, Preload scripts, Worker process, etc.
+ // If you are familiar with Vite configuration, it will look really familiar.
+ build: [
+ {
+ // `entry` is just an alias for `build.lib.entry` in the corresponding file of `config`.
+ entry: 'src/main.ts',
+ config: 'vite.main.config.ts',
+ target: 'main',
+ },
+ {
+ entry: 'src/preload.ts',
+ config: 'vite.preload.config.ts',
+ target: 'preload',
+ },
+ ],
+ renderer: [
+ {
+ name: 'main_window',
+ config: 'vite.renderer.config.ts',
+ },
+ ],
+ }),
+ // Fuses are used to enable/disable various Electron functionality
+ // at package time, before code signing the application
+ new FusesPlugin({
+ version: FuseVersion.V1,
+ [FuseV1Options.RunAsNode]: false,
+ [FuseV1Options.EnableCookieEncryption]: true,
+ [FuseV1Options.EnableNodeOptionsEnvironmentVariable]: false,
+ [FuseV1Options.EnableNodeCliInspectArguments]: false,
+ [FuseV1Options.EnableEmbeddedAsarIntegrityValidation]: true,
+ [FuseV1Options.OnlyLoadAppFromAsar]: true,
+ }),
+ ],
+};
+
+export default config;
diff --git a/forge.env.d.ts b/forge.env.d.ts
new file mode 100644
index 0000000..9700e0a
--- /dev/null
+++ b/forge.env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/apps/vynte-connect-windows/src/index.html b/index.html
similarity index 96%
rename from apps/vynte-connect-windows/src/index.html
rename to index.html
index cb01677..ea9cf07 100644
--- a/apps/vynte-connect-windows/src/index.html
+++ b/index.html
@@ -5,7 +5,6 @@
Vynte Connect
-
@@ -65,6 +64,6 @@
-
+