diff --git a/.github/workflows/build-windows-agent.yml b/.github/workflows/build-windows-agent.yml new file mode 100644 index 0000000..825c3f9 --- /dev/null +++ b/.github/workflows/build-windows-agent.yml @@ -0,0 +1,65 @@ +name: Build Windows Agent EXE & Publish GitHub Release + +on: + workflow_dispatch: + push: + tags: + - 'v*' + paths: + - 'agent/**' + - 'scripts/**' + - '.github/workflows/build-windows-agent.yml' + +permissions: + contents: write + +jobs: + build-win64-agent: + runs-on: windows-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Python 3.12 + uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install PyInstaller & WebRTC P2P dependencies + run: | + python -m pip install --upgrade pip + pip install pyinstaller psutil websockets aiortc mss Pillow + + - name: Compile standalone GothwadRemoteAgent-win64.exe + run: | + pyinstaller --onefile --collect-all av --collect-all aiortc --name GothwadRemoteAgent-win64 agent/gothwad_agent.py + + - name: Bundle Setup Zip (Agent EXE + Hyper-V & Installer Scripts) + shell: pwsh + run: | + New-Item -ItemType Directory -Force -Path release-bundle | Out-Null + Copy-Item dist/GothwadRemoteAgent-win64.exe release-bundle/ + Copy-Item scripts/host-setup-hyperv.ps1 release-bundle/ + Copy-Item scripts/install-gothwad-agent.ps1 release-bundle/ + Compress-Archive -Path release-bundle/* -DestinationPath dist/GothwadRemoteSetup-win64.zip -Force + + - name: Upload Build Artifacts + uses: actions/upload-artifact@v4 + with: + name: gothwad-remote-agent-win64 + path: | + dist/GothwadRemoteAgent-win64.exe + dist/GothwadRemoteSetup-win64.zip + + - name: Publish to GitHub Releases (on version tag v*) + if: startsWith(github.ref, 'refs/tags/v') + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + shell: pwsh + run: | + $tag = "${{ github.ref_name }}" + gh release create $tag ` + dist/GothwadRemoteAgent-win64.exe ` + dist/GothwadRemoteSetup-win64.zip ` + --title "Gothwad Remote Desktop Agent $tag" ` + --generate-notes diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..757bce4 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI — File Size, Typecheck, Tests & Build + +on: + push: + branches: [main, 'arena/**'] + pull_request: + branches: [main] + +jobs: + verify: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Run GothwadTech File Size Guard, Typecheck & Worker Tests + run: npm test + + - name: Build Production Web Portal (Cloudflare Pages dist) + run: npm run build diff --git a/.github/workflows/deploy-cloudflare.yml b/.github/workflows/deploy-cloudflare.yml new file mode 100644 index 0000000..62810cb --- /dev/null +++ b/.github/workflows/deploy-cloudflare.yml @@ -0,0 +1,33 @@ +name: Deploy Cloudflare Pages (remotedesktop.gothwadtech.com) + +on: + workflow_dispatch: + push: + branches: [main] + +jobs: + deploy-pages: + runs-on: ubuntu-latest + # Runs if CLOUDFLARE_API_TOKEN is set; otherwise Cloudflare's native Git Pages integration deploys automatically. + if: ${{ secrets.CLOUDFLARE_API_TOKEN != '' }} + env: + CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }} + CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Node.js 22 + uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - name: Install dependencies, test & build static bundle + run: | + npm ci + npm test + npm run build + + - name: Deploy dist/ to Cloudflare Pages (Zero D1, Zero Worker!) + run: npx wrangler@latest pages deploy dist --project-name=remotedesktop --branch=main diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..65ea011 --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +node_modules/ +dist/ +dev-dist/ +coverage/ +.wrangler/ +.dev.vars +worker/.dev.vars +.env +.env.* +!.env.example +__pycache__/ +*.pyc +build/ +*.exe +*.zip +.DS_Store diff --git a/README.md b/README.md index 039a1cd..0afd933 100644 --- a/README.md +++ b/README.md @@ -1 +1,61 @@ -# vm \ No newline at end of file +# Gothwad Remote Desktop (`remotedesktop.gothwadtech.com`) + +**100% Zero-Database, Zero-Worker Cloudflare Pages Portal — Main Windows 11 OS Control + Headless Isolated Hyper-V Background VM, Secured by Gothwad Accounts (`@gothwadtech.com`) & WebRTC P2P!** + +Yeh repository (`GothwadTech/remotedesktop`) bilkul **`GothwadTech/meet`** ki tarah **Zero-D1 / Zero-Backend** architecture par bani hai — Cloudflare par **koi D1 database nahi banana, koi Worker deploy nahi karna, koi secret set nahi karna!** Bas repo ko Cloudflare Pages se connect karo aur `remotedesktop.gothwadtech.com` live! + +--- + +## ⚠️ Important Consent & Transparency Rules + +1. **Explicit Owner Permission Mandatory:** Kisi bhi Windows PC par one-time setup sirf unki **clear permission aur जानकारी** se karein (Agent pairing bina explicit consent confirm kiye reject ho jaati hai). +2. **Dual-Mode Transparency:** + - **🖥️ Mode 1 (Main Windows 11 Host OS):** Jab aap main OS connect karte ho, toh bilkul *Chrome Remote Desktop* ki tarah host screen par `"Your desktop is shared with username@gothwadtech.com [Stop Sharing]"` banner dikhta hai. + - **📦 Mode 2 (Background Hyper-V VM):** Jab dost apna kaam kar raha ho, toh aap isolated Hyper-V VM me connect hote ho — dost ki screen, mouse, ya personal files bilkul disturb nahi hoti! +3. **Laptop Battery Safety:** Kabhi bhi **"On Battery"** sleep disable mat karna taaki bag me band laptop overheat na ho. + +--- + +## 🧠 Bina Cloudflare D1 Database ke Kaise Kaam Karta Hai? (Zero-D1 Magic!) + +1. **Direct Gothwad Accounts SSO (`api.gothwadtech.com`):** + - `GothwadTech/accounts` pehle se har `*.gothwadtech.com` subdomain ke liye CORS + `.gothwadtech.com` `HttpOnly` multi-account cookies (`u/0..5`) allow karta hai! + - Isliye `remotedesktop.gothwadtech.com` seedha browser se `https://api.gothwadtech.com/api/auth/me` call karke aapka `@gothwadtech.com` user verify kar leta hai — kisi alag Worker ya D1 ki zaroorat hi nahi! +2. **Deterministic WebRTC P2P Slots (`PeerJS` + STUN/TURN, `GothwadTech/meet` Stack):** + - Jab aapka Windows PC start hota hai, `GothwadRemoteAgent.exe` aapke Gothwad username ke deterministic P2P slot (`grd--slot-1`..`6`) par online aa jaata hai. + - Jab aap duniya me kahin se bhi `remotedesktop.gothwadtech.com` kholte ho, browser aapke slots ko P2P probe karke turant **Online**, **RAM/CPU**, aur **Hyper-V VM State** dikha deta hai! +3. **Zero-Knowledge HMAC-SHA256 PIN + Host-Local ACL (`agent-config.json`):** + - Aapka **6-digit PIN** aur allowed `@gothwadtech.com` users (`vm_only` vs `host_and_vm`) seedha Windows Host ke `C:\ProgramData\GothwadRemote\agent-config.json` me rehte hain. + - Connect karte waqt raw PIN kabhi network par nahi jaata — fresh `nonce` ke saath `HMAC-SHA256(pin, nonce + ":" + viewer + ":" + mode)` verify hota hai (5 galat tries par 5-min lockout!). + +--- + +## ✨ Website ke Andar Built-in Windows Setup Wizard (`/setup`) + +Bilkul `remotedesktop.google.com/headless` ki tarah `https://remotedesktop.gothwadtech.com` par top bar me **`Windows Setup & Commands`** tab hai jahan user ko saare steps aur copy-paste PowerShell commands website ke andar hi mil jaate hain: +- **Mode Picker:** `Main OS + Background VM`, `Main OS Only` (2-min setup), ya `Background VM Only`. +- **Interactive Hyper-V VM Builder:** RAM, Disk, Linux/Windows ISO badalte hi live PowerShell command banti hai. +- **1-Line Gothwad P2P Installer Command** + Direct `.EXE` / `.ZIP` GitHub Release download buttons. +- **Safe Charger Power Command** (`standby-timeout-ac 0`, battery sleep protected). + +--- + +## 🚀 60-Second Cloudflare Pages Deployment (Zero D1, Zero Secrets!) + +1. **Cloudflare Dashboard -> Workers & Pages -> Create -> Pages -> Connect to Git (`GothwadTech/remotedesktop`)** +2. **Build command:** `npm run build` +3. **Build output directory:** `dist` +4. **Custom domain:** `remotedesktop.gothwadtech.com` +5. **Done!** 🎉 + +--- + +## 📚 Documentation Index (`docs/`) + +1. **[`docs/07-gothwad-ecosystem-setup.md`](docs/07-gothwad-ecosystem-setup.md)** — ⭐ **START HERE:** Zero-D1 Architecture, Cloudflare Pages 1-Step Deploy & GitHub Actions EXE Release Guide. +2. **[`docs/01-architecture.md`](docs/01-architecture.md)** — Hyper-V Type-1 Service, `Default Switch` Wi-Fi NAT & CGNAT Bypass Deep Dive. +3. **[`docs/02-host-setup-windows11.md`](docs/02-host-setup-windows11.md)** — Windows 11 Pro (16GB/512GB) Safe RAM/CPU Limits & Auto-Boot VM Guide. +4. **[`docs/03-option-a-crd-vm.md`](docs/03-option-a-crd-vm.md)** — Alternative A: Chrome Remote Desktop Inside Guest VM. +5. **[`docs/04-option-b-kasm-cloudflare.md`](docs/04-option-b-kasm-cloudflare.md)** — Alternative B: KasmVNC (`Webtop`) + Cloudflare Tunnel. +6. **[`docs/05-option-c-guacamole-rdp.md`](docs/05-option-c-guacamole-rdp.md)** — Alternative C: DWService / Apache Guacamole HTML5 Windows RDP. +7. **[`docs/06-troubleshooting-faq.md`](docs/06-troubleshooting-faq.md)** — 1080p Resolution Fix, Wi-Fi Drop Watchdog, Gaming Pause/Resume Shortcut & SSD Compacting. diff --git a/agent/gothwad_agent.py b/agent/gothwad_agent.py new file mode 100644 index 0000000..04ff42c --- /dev/null +++ b/agent/gothwad_agent.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python3 +""" +Gothwad Remote Desktop — Zero-D1 WebRTC P2P Host & Hyper-V Agent +================================================================ +- Zero cloud database required! Registers on WebRTC P2P slot `grd--slot-`. +- Requires explicit PC owner consent during one-time pairing. +- Verifies viewer Gothwad username ACL + constant-time HMAC-SHA256 6-digit PIN proof. +- Displays a visible Chrome-Remote-Desktop-style "Desktop Shared [Stop Sharing]" + banner whenever Main Windows OS screen sharing is active. +""" + +from __future__ import annotations + +import argparse +import asyncio +import base64 +import hashlib +import hmac +import io +import json +import os +import platform +import secrets +import subprocess +import sys +import threading +import time +from pathlib import Path +from typing import Any, Dict + +import psutil + +CONFIG_DIR = Path(os.environ.get("PROGRAMDATA", "C:\\ProgramData")) / "GothwadRemote" +CONFIG_FILE = CONFIG_DIR / "agent-config.json" +PEERJS_WS = "wss://0.peerjs.com/peerjs?key=peerjs&id={peer_id}&token={token}&version=1.5.5" + + +def sanitize_username(raw: str) -> str: + clean = raw.strip().lower().split("@")[0] + return "".join(c for c in clean if c.isalnum() or c in "._-") + + +def build_peer_id(username: str, slot: int) -> str: + safe_user = sanitize_username(username).replace(".", "-").replace("_", "-") + safe_slot = max(1, min(6, int(slot))) + return f"grd-{safe_user}-slot-{safe_slot}" + + +def compute_expected_proof(pin: str, nonce: str, viewer: str, mode: str) -> str: + msg = f"{nonce}:{sanitize_username(viewer)}:{mode}".encode("utf-8") + return hmac.new(pin.encode("utf-8"), msg, hashlib.sha256).hexdigest() + + +def query_hyperv_state(vm_name: str) -> str: + if platform.system() != "Windows": + return "Running" + try: + res = subprocess.run( + ["powershell", "-NoProfile", "-Command", f"(Get-VM -Name '{vm_name}' -ErrorAction SilentlyContinue).State"], + capture_output=True, + text=True, + timeout=5, + ) + st = res.stdout.strip() + return st if st in ("Running", "Saved", "Paused", "Off") else "Off" + except Exception: + return "Off" + + +def run_hyperv_action(vm_name: str, action: str) -> None: + if platform.system() != "Windows": + return + mapping = { + "start_vm": f"Start-VM -Name '{vm_name}'", + "save_vm": f"Save-VM -Name '{vm_name}'", + "restart_vm": f"Restart-VM -Name '{vm_name}' -Force", + } + cmd = mapping.get(action) + if cmd: + subprocess.run(["powershell", "-NoProfile", "-Command", cmd], capture_output=True, timeout=30) + + +def show_host_sharing_banner(viewer_username: str, stop_event: threading.Event) -> None: + try: + import tkinter as tk + + root = tk.Tk() + root.title("Gothwad Remote Desktop") + root.attributes("-topmost", True) + root.overrideredirect(True) + root.configure(bg="#0f172a") + + frame = tk.Frame(root, bg="#0f172a", padx=14, pady=8, highlightthickness=1, highlightbackground="#38bdf8") + frame.pack() + tk.Label( + frame, + text=f"Your desktop is shared with {viewer_username}@gothwadtech.com", + fg="#f8fafc", + bg="#0f172a", + font=("Segoe UI", 10, "bold"), + ).pack(side=tk.LEFT, padx=(0, 12)) + + def on_stop() -> None: + stop_event.set() + root.destroy() + + tk.Button(frame, text="Stop Sharing", command=on_stop, bg="#0284c7", fg="white", relief=tk.FLAT, padx=10).pack( + side=tk.LEFT + ) + root.update_idletasks() + root.geometry(f"+{(root.winfo_screenwidth() - root.winfo_width()) // 2}+18") + + def check_stop() -> None: + if stop_event.is_set(): + root.destroy() + else: + root.after(500, check_stop) + + root.after(500, check_stop) + root.mainloop() + except Exception: + pass + + +def capture_screen_jpeg_b64() -> str | None: + try: + import mss + from PIL import Image + + with mss.mss() as sct: + mon = sct.monitors[1] + shot = sct.grab(mon) + img = Image.frombytes("RGB", shot.size, shot.bgra, "raw", "BGRX") + img.thumbnail((1280, 720)) + buf = io.BytesIO() + img.save(buf, format="JPEG", quality=62) + return base64.b64encode(buf.getvalue()).decode("ascii") + except Exception: + return None + + +def pair_local(user: str, slot: int, name: str, vm_name: str, pin: str, consent: bool) -> None: + if not consent: + print("ERROR: Explicit PC owner consent (--confirm-owner-consent) is mandatory.") + sys.exit(1) + if not pin.isdigit() or not (6 <= len(pin) <= 12): + print("ERROR: PIN must be 6 to 12 digits.") + sys.exit(1) + + owner = sanitize_username(user) + peer_id = build_peer_id(owner, slot) + CONFIG_DIR.mkdir(parents=True, exist_ok=True) + cfg = { + "ownerUsername": owner, + "slot": slot, + "peerId": peer_id, + "deviceName": name or platform.node() or "Windows-11-PC", + "vmName": vm_name, + "pin": pin, + "sharedUsers": {}, + } + CONFIG_FILE.write_text(json.dumps(cfg, indent=2), encoding="utf-8") + print(f"Paired! Peer ID '{peer_id}' ready for {owner}@gothwadtech.com (Zero-D1 P2P).") + + +def grant_share(username: str, scope: str) -> None: + if not CONFIG_FILE.exists(): + sys.exit("Run --pair first.") + cfg = json.loads(CONFIG_FILE.read_text(encoding="utf-8")) + target = sanitize_username(username) + cfg.setdefault("sharedUsers", {})[target] = "host_and_vm" if scope == "host_and_vm" else "vm_only" + CONFIG_FILE.write_text(json.dumps(cfg, indent=2), encoding="utf-8") + print(f"Granted {target}@gothwadtech.com -> {cfg['sharedUsers'][target]}") + + +async def p2p_loop(cfg: Dict[str, Any]) -> None: + import websockets + from aiortc import RTCConfiguration, RTCIceServer, RTCPeerConnection, RTCSessionDescription + + peer_id = cfg["peerId"] + failed_attempts = 0 + lockout_until = 0.0 + + while True: + token = secrets.token_hex(8) + ws_url = PEERJS_WS.format(peer_id=peer_id, token=token) + try: + async with websockets.connect(ws_url, ping_interval=15) as ws: + print(f"[Online] Listening on P2P slot: {peer_id}") + + async def heartbeat_sender() -> None: + while True: + await asyncio.sleep(5) + await ws.send(json.dumps({"type": "HEARTBEAT"})) + + hb_task = asyncio.create_task(heartbeat_sender()) + try: + async for raw in ws: + msg = json.loads(raw) + if msg.get("type") != "OFFER": + continue + src = msg["src"] + payload = msg["payload"] + conn_id = payload.get("connectionId") + pc = RTCPeerConnection( + RTCConfiguration([RTCIceServer(urls=["stun:stun.l.google.com:19302"])]) + ) + nonce = secrets.token_hex(16) + stop_banner = threading.Event() + authed_mode: list[str] = [] + + @pc.on("datachannel") + def on_dc(channel: Any) -> None: + mem = psutil.virtual_memory() + hello = { + "type": "HELLO", + "nonce": nonce, + "device": { + "slot": cfg["slot"], + "name": cfg["deviceName"], + "ownerUsername": cfg["ownerUsername"], + "sharedUsers": cfg.get("sharedUsers", {}), + "osInfo": f"{platform.system()} {platform.release()} · {round(mem.total / (1024**3))}GB RAM", + "vmName": cfg["vmName"], + "vmState": query_hyperv_state(cfg["vmName"]), + "hostRamTotalMb": round(mem.total / (1024 * 1024)), + "hostRamUsedMb": round(mem.used / (1024 * 1024)), + "hostCpuPercent": round(psutil.cpu_percent()), + }, + } + channel.send(json.dumps(hello)) + + @channel.on("message") + def on_message(pkt_raw: Any) -> None: + nonlocal failed_attempts, lockout_until + pkt = json.loads(pkt_raw) if isinstance(pkt_raw, str) else pkt_raw + if pkt.get("type") == "AUTH": + if time.time() < lockout_until: + channel.send(json.dumps({"type": "AUTH_ERR", "error": "Too many wrong PIN attempts. Wait 5 min."})) + return + viewer = sanitize_username(str(pkt.get("viewerUsername", ""))) + mode = str(pkt.get("targetMode", "hyperv_vm")) + proof = str(pkt.get("proof", "")) + is_owner = viewer == cfg["ownerUsername"] + shared_scope = cfg.get("sharedUsers", {}).get(viewer) + if not is_owner and not shared_scope: + channel.send(json.dumps({"type": "AUTH_ERR", "error": "Gothwad Account not authorized"})) + return + if mode == "host_os" and not is_owner and shared_scope != "host_and_vm": + channel.send(json.dumps({"type": "AUTH_ERR", "error": "Restricted to Background VM only"})) + return + expected = compute_expected_proof(cfg["pin"], nonce, viewer, mode) + if not hmac.compare_digest(expected, proof): + failed_attempts += 1 + if failed_attempts >= 5: + lockout_until = time.time() + 300 + channel.send(json.dumps({"type": "AUTH_ERR", "error": "Incorrect 6-digit PIN"})) + return + failed_attempts = 0 + authed_mode.append(mode) + channel.send(json.dumps({"type": "AUTH_OK", "targetMode": mode})) + if mode == "host_os": + threading.Thread(target=show_host_sharing_banner, args=(viewer, stop_banner), daemon=True).start() + + async def stream_frames() -> None: + while not stop_banner.is_set() and channel.readyState == "open": + b64 = capture_screen_jpeg_b64() + if b64: + channel.send(json.dumps({"type": "FRAME", "jpegBase64": b64})) + await asyncio.sleep(0.12) + + asyncio.create_task(stream_frames()) + elif authed_mode and pkt.get("type") == "POWER": + run_hyperv_action(cfg["vmName"], str(pkt.get("action", ""))) + + await pc.setRemoteDescription(RTCSessionDescription(sdp=payload["sdp"]["sdp"], type="offer")) + ans = await pc.createAnswer() + await pc.setLocalDescription(ans) + await ws.send( + json.dumps( + { + "type": "ANSWER", + "dst": src, + "payload": { + "sdp": {"type": "answer", "sdp": pc.localDescription.sdp}, + "type": payload.get("type", "data"), + "connectionId": conn_id, + }, + } + ) + ) + finally: + hb_task.cancel() + except Exception as exc: + print(f"[Reconnecting P2P signaling in 5s] {exc}") + await asyncio.sleep(5) + + +def main() -> None: + p = argparse.ArgumentParser(description="Gothwad Remote Desktop Zero-D1 P2P Agent") + p.add_argument("--pair", action="store_true") + p.add_argument("--run", action="store_true") + p.add_argument("--grant-user", default="") + p.add_argument("--scope", default="vm_only") + p.add_argument("--gothwad-user", default="") + p.add_argument("--slot", type=int, default=1) + p.add_argument("--device-name", default="") + p.add_argument("--vm-name", default="RemoteWorkVM") + p.add_argument("--pin", default="") + p.add_argument("--confirm-owner-consent", action="store_true") + args = p.parse_args() + + if args.pair: + pair_local(args.gothwad_user, args.slot, args.device_name, args.vm_name, args.pin, args.confirm_owner_consent) + elif args.grant_user: + grant_share(args.grant_user, args.scope) + elif args.run: + if not CONFIG_FILE.exists(): + sys.exit("Config missing. Run --pair first.") + asyncio.run(p2p_loop(json.loads(CONFIG_FILE.read_text(encoding="utf-8")))) + else: + p.print_help() + + +if __name__ == "__main__": + main() diff --git a/agent/requirements.txt b/agent/requirements.txt new file mode 100644 index 0000000..cee1c8f --- /dev/null +++ b/agent/requirements.txt @@ -0,0 +1,7 @@ +psutil>=6.1.0 +websockets>=13.1 +aiortc>=1.9.0 +mss>=9.0.2 +Pillow>=11.0.0 +pyautogui>=0.9.54 +pyperclip>=1.9.0 diff --git a/docs/01-architecture.md b/docs/01-architecture.md new file mode 100644 index 0000000..ffa4f41 --- /dev/null +++ b/docs/01-architecture.md @@ -0,0 +1,65 @@ +# 01 — System Architecture & How It Works + +Yeh document explain karta hai ki **1 km (ya kisi bhi distance) door rakhe Wi-Fi laptop** par bina router chhede aur bina host desktop disturb kiye browser-based VM kaise kaam karta hai. + +--- + +## 1. Windows 11 Pro ka Sabse Bada Plus Point: Native Hyper-V + +Aapke dost ke laptop me **Windows 11 Pro** hai — yeh sabse bada advantage hai kyunki isme **Microsoft Hyper-V** pehle se built-in aata hai (free, official Microsoft feature). + +### VirtualBox / VMware Workstation vs Hyper-V: +| Feature | VirtualBox / VMware Player | **Microsoft Hyper-V (Win 11 Pro)** | +| :--- | :--- | :--- | +| **Type** | Type-2 (App ki tarah chalta hai) | **Type-1 Bare-Metal Hypervisor** | +| **Auto-Start on Boot** | Mushkil (User login aur taskbar window chahiye) | **Native Windows Service (`vmms`) — Boot hote hi start!** | +| **Headless Mode** | Aksar taskbar me icon/window aati hai | **100% Headless Service (Zero UI windows on desktop)** | +| **Dynamic Memory** | Fixed RAM block kar leta hai | **Dynamic RAM (Jitni zaroorat utni hi RAM use karega)** | +| **Auto Save on Shutdown**| VM crash ho sakta hai agar PC band ho | **Graceful State Save — Wahin se resume hoga!** | + +--- + +## 2. Wi-Fi + CGNAT Problem aur Uska Solution + +Jab dost ka laptop ghar ke Wi-Fi ya mobile hotspot se connect hota hai, toh 2 challenges aate hain: +1. **CGNAT / Dynamic IP:** India me JioFiber, Airtel, aur mobile networks par aapko public IP nahi milti, isliye router me **Port Forwarding kaam nahi karti**. +2. **Wi-Fi Bridging Issue:** Laptop ke Wi-Fi card par Hyper-V ka "External Switch" (Bridge) banane se dost ka Wi-Fi bar-bar disconnect ho sakta hai. + +### Solution: `Default Switch` (WinNAT) + Outbound Tunnel Inside VM +1. **Hyper-V `Default Switch`:** Windows 11 Hyper-V me ek built-in virtual switch hota hai jiska naam hai **`Default Switch`**. Dost chahe Ghar ka Wi-Fi lagaye, Hostel Wi-Fi lagaye, ya Phone Hotspot — Windows automatic apne internet ko NAT ke through VM ke andar bhej deta hai! +2. **Outbound Connection:** VM ke andar chalne wala agent (`chromoting` / `cloudflared` / `tailscaled`) andar se bahar (`Outbound Port 443 HTTPS/WebRTC`) connect karta hai. Isliye: + - Router login ki zaroorat **NAHI** hai. + - Static IP ki zaroorat **NAHI** hai. + - Host Windows Firewall me inbound ports kholne ki zaroorat **NAHI** hai. + +--- + +## 3. Boot-to-Browser Lifecycle (Jab Dost Laptop On Karega Toh Kya Hoga?) + +```text +[00:00 sec] Dost ne Laptop ka Power Button dabaya + | +[00:12 sec] Windows 11 Pro Boot hua -> Dost apna kaam shuru kar sakta hai + | (30-second Auto-Start Delay rakha hai taaki boot time pe SSD slow na ho) + | +[00:42 sec] Hyper-V Service (`vmms`) background me Guest VM ko Resume/Boot karti hai + | (Dost ke desktop par koi window nahi aati) + | +[00:55 sec] Dost ka Laptop Wi-Fi se connect hota hai -> `Default Switch` VM ko internet deta hai + | +[01:05 sec] Guest VM ke andar ka Tunnel Service (CRD / Cloudflared / Tailscale) + | auto-start hota hai aur Cloud Relay se jud jaata hai + | +[01:10 sec] AAP APNE GHAR PE BROWSER KHOLTE HO -> VM "ONLINE" DIKHTA HAI -> CLICK & USE! +``` + +Aur jab dost apna laptop **Shut Down** karega: +- Hyper-V automatically VM ki current state (khule huye apps ke saath!) disk par **Save** kar dega (`AutomaticStopAction = Save`). +- Agli baar laptop on hone par VM wahin se wapas chalu ho jayega! + +--- + +## 4. Security & Isolation Guarantee + +- **Host -> Guest Isolation:** Aap VM ke andar kuch bhi install karo, browser chalao, code compile karo — woh sab ek single `.vhdx` virtual hard disk file ke andar band rehta hai. Dost ke `C:\Users\...` personal folders VM ko bilkul accessible nahi hote (jab tak koi manually share na kare). +- **Encrypted Traffic:** Browser se VM tak sara display/keyboard stream **TLS 1.3 / WebRTC DTLS-SRTP** encrypted hota hai. diff --git a/docs/02-host-setup-windows11.md b/docs/02-host-setup-windows11.md new file mode 100644 index 0000000..eb8a13c --- /dev/null +++ b/docs/02-host-setup-windows11.md @@ -0,0 +1,102 @@ +# 02 — Host PC Setup (Dost ke Windows 11 Pro Laptop Par One-Time Setup) + +> **Consent Reminder:** Yeh one-time setup apne dost ke saamne aur unki permission se hi karein. Isme total ~15–20 minute lagenge. + +--- + +## Step 1: Check Virtualization Status + +1. Dost ke laptop par `Ctrl + Shift + Esc` daba kar **Task Manager** kholein. +2. **Performance** tab -> **CPU** par click karein. +3. Neeche right side me check karein: **`Virtualization: Enabled`** likha hona chahiye (Windows 11 laptops me yeh 99% pehle se On hota hai). + +--- + +## Step 2: Enable Hyper-V on Windows 11 Pro + +1. Start Menu par right-click karein -> **Terminal (Admin)** ya **PowerShell (Run as Administrator)** kholein. +2. Yeh command paste karke Enter dabayein: + +```powershell +Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All +``` + +3. Jab pooche `Do you want to restart the computer now? (Y/N)`, toh **`Y`** daba kar laptop ek baar restart kar lein. + +--- + +## Step 3: Download Guest OS ISO + +Faisla karein ki aapko VM ke andar kaunsa OS chahiye: +- **Recommended (Fast & Light):** **Ubuntu 24.04 LTS Desktop** ya **Linux Mint XFCE** (~2.5 GB RAM me makkhan chalta hai). +- **Windows Inside VM:** **Windows 10 Pro / IoT LTSC** ya **Windows 11 Pro** (~4 GB RAM chahiye hoga). + +ISO file download karke ek clean folder me rakh lein, jaise `C:\HyperV\ISO\`. + +--- + +## Step 4: Create Resource-Capped Auto-Start VM (Script ya GUI) + +### Method 1: Automated PowerShell Script (Recommended — Zero Mistake) +Is repo me diya gaya [`scripts/host-setup-hyperv.ps1`](../scripts/host-setup-hyperv.ps1) Admin PowerShell me run karein: + +```powershell +# Agar Guest OS Ubuntu/Linux hai: +.\scripts\host-setup-hyperv.ps1 -VMName "RemoteWorkVM" -GuestType "Linux" -IsoPath "C:\HyperV\ISO\ubuntu-24.04-desktop-amd64.iso" + +# Agar Guest OS Windows 10/11 hai: +.\scripts\host-setup-hyperv.ps1 -VMName "RemoteWorkVM" -GuestType "Windows" -IsoPath "C:\HyperV\ISO\Win11.iso" +``` + +### Method 2: Manual Setup via Hyper-V Manager (GUI) +Agar aap manually banana chahte hain: +1. Start Menu se **Hyper-V Manager** kholein -> Right side me **New -> Virtual Machine** click karein. +2. **Name:** `RemoteWorkVM` | Location: `C:\HyperV\VMs\` +3. **Generation:** **Generation 2** select karein. +4. **Assign Memory:** `3072 MB` (Linux ke liye) ya `4096 MB` (Windows ke liye) aur **"Use Dynamic Memory"** pe tick lagayein. +5. **Configure Networking:** Connection dropdown me **`Default Switch`** select karein *(Yeh bahut zaroori hai Wi-Fi NAT ke liye!)*. +6. **Connect Virtual Hard Disk:** Size `50 GB` rakhein (yeh Dynamically Expanding hota hai, shuru me sirf 8–12 GB space lega). +7. **Installation Options:** Apni downloaded `.iso` file select karein aur **Finish** click karein. + +#### ⚠️ Important VM Settings (Finish ke baad VM par Right-Click -> Settings me jayein): +1. **Security (Bahut Zaroori!):** + - Agar **Ubuntu / Linux** install kar rahe ho: **Secure Boot** template ko badal kar **`Microsoft UEFI Certificate Authority`** kar dein (warna Linux boot nahi hoga!). + - Agar **Windows 11** install kar rahe ho: `Enable Trusted Platform Module (TPM)` par tick laga dein. +2. **Memory:** Minimum RAM `1536 MB`, Maximum RAM **`4096 MB`** set karein (taaki dost ke 16GB me se 12GB hamesha free rahe). +3. **Processor:** `2` Virtual Processors dein aur `+` expand karke **Virtual machine limit (percentage)** ko **`60`** kar dein (isse laptop fan kabhi shor nahi karega). +4. **Automatic Start Action (Management tab ke neeche):** + - Select karein: **`Always start this virtual machine automatically`** + - **Startup delay:** `30` seconds daalein (taaki dost ka Windows pehle bina lag ke start ho jaye). +5. **Automatic Stop Action:** + - Select karein: **`Save the virtual machine state`** (Laptop band hone par VM wahin freeze hokar save ho jayega, kuch data loss nahi hoga). + +--- + +## Step 5: Pehli Baar OS Install Karein + +1. Hyper-V Manager me `RemoteWorkVM` par double-click karke **Start** dabayein. +2. Normal tarike se Ubuntu ya Windows install complete karein. +3. OS install hone ke baad Hyper-V Settings -> DVD Drive me se `.iso` hata dein ("None" select karein). +4. Ab **Hyper-V Manager ki window band kar dein!** VM background me chalta rahega — Hyper-V window khuli rakhne ki kabhi zaroorat nahi hoti. + +--- + +## Step 6: Laptop Power & Sleep Settings (Dost ki Permission Se) + +Laptop ki ek natural limitation hoti hai: agar laptop **Sleep** mode me chala gaya, toh Wi-Fi aur Hyper-V dono pause ho jayenge. + +**Safe Configuration (`Settings -> System -> Power & battery -> Screen and sleep`):** +- **When plugged in (Charger laga ho tab):** + - *Turn off my screen after:* `5 or 10 minutes` (Dost ki screen normal band ho sakti hai, VM chalta rahega). + - *Put my device to sleep after:* **`Never`** (Ya `2 hours` — jaisa dost comfortable ho). +- **On battery power (⚠️ SAFETY RULE):** + - Battery par sleep ko **`15 or 30 minutes`** hi rehne dein! **Kabhi bhi Battery par Sleep "Never" mat karna**, kyunki agar dost ne laptop bag me rakh diya toh andar garmi se laptop overheat ho jayega! + +--- + +## Next Step: Browser Access Setup Karein! + +Ab aapka background VM ready hai. Ab inme se koi ek guide follow karke VM ke andar Browser Remote Access lagayein: +- 👉 **[Option A: Chrome Remote Desktop Inside VM (Sabse Simple)](03-option-a-crd-vm.md)** +- 👉 **[Option B: KasmVNC + Cloudflare Tunnel (Custom URL + Ultra Fast)](04-option-b-kasm-cloudflare.md)** +- 👉 **[Option C: Apache Guacamole + Tailscale Funnel (HTML5 RDP)](05-option-c-guacamole-rdp.md)** diff --git a/docs/03-option-a-crd-vm.md b/docs/03-option-a-crd-vm.md new file mode 100644 index 0000000..4512d12 --- /dev/null +++ b/docs/03-option-a-crd-vm.md @@ -0,0 +1,81 @@ +# 03 — Option A: Hyper-V VM + Chrome Remote Desktop (Inside Guest VM) + +> ⭐ **Sabse Aasaan Zero-Config Method:** Koi domain kharidne ki zaroorat nahi, koi router port forward nahi, koi IP yaad rakhne ki zaroorat nahi! + +Aapne bilkul sahi pakda tha ki *Chrome Remote Desktop* agar dost ke main Windows par daalo toh woh dost ka system control karta hai. Lekin jab aap **Hyper-V Guest VM ke andar** apna Google account login karke Chrome Remote Desktop set karte ho (aur dost ke main Windows par kuch nahi daalte), toh `https://remoting.google.com/access` kholne par aapko **sirf apne isolated VM ka access** milta hai! + +--- + +## Case 1: Agar VM ke Andar **Windows 10 / 11** Hai + +### Step 1: Basic Session Mode Use Karein (One-Time Tip) +Jab aap pehli baar Hyper-V Manager se VM kholte ho: +1. Agar "Connect to RemoteWorkVM" resolution popup aaye, toh usse **Close (X)** kar dein taaki VM **Basic Session** me khule (View menu me *Enhanced Session* untick hona chahiye). + - *Kyun?* Enhanced Session asal me RDP hota hai; Basic Session virtual monitor (`Microsoft Hyper-V Video`) use karta hai jiske saath Chrome Remote Desktop makkhan ki tarah kaam karta hai. + +### Step 2: VM ke Andar Chrome Remote Desktop Install Karein +1. **VM ke andar** Edge kholkar **Google Chrome** download aur install karein. +2. Chrome me **APNI Google ID** (dost ki nahi!) login karein. +3. VM ke Chrome me yeh URL kholein: + ```text + https://remoting.google.com/access + ``` +4. **"Set up remote access"** ke neeche Download button dabayein -> `.msi` installer run karein. +5. Wapas browser me VM ka naam rakhein (jaise `My-Cloud-VM`) -> **Next** -> ek strong **6+ digit PIN** set karein. + +### Step 3: Verify Auto-Start Service Inside VM +VM ke andar `Win + R` dabayein -> `services.msc` likhein aur check karein: +- **`Chrome Remote Desktop Service`** -> Status: `Running` | Startup Type: **`Automatic`**. +- Iska fayda: Agar VM kabhi pura restart bhi ho jaye aur Windows Lock Screen par ruka ho, tab bhi yeh service boot hote hi chalu ho jaati hai! Aap browser se connect karke Lock Screen unlock kar sakte ho. + +### Step 4: Done! Window Band Kar Dein +1. Dost ke laptop par Hyper-V VM ki window (`vmconnect`) band kar dein (Shut down mat karna, bas `X` daba kar window close kar dein — VM background me chalta rahega!). +2. Ab apne phone ya apne ghar ke PC ke kisi bhi browser me jayein: + 👉 **`https://remoting.google.com/access`** +3. Apni Google ID se login karein -> `My-Cloud-VM` **Online** dikhega -> Click karein -> PIN daalein -> Full Windows VM aapke browser me! 🎉 + +--- + +## Case 2: Agar VM ke Andar **Ubuntu 24.04 LTS (Linux)** Hai *(Fastest & Lightest!)* + +Linux VM sirf **2GB–3GB RAM** me super fast chalta hai. Ubuntu me default GNOME Wayland ki jagah lightweight **XFCE Desktop** ke saath Chrome Remote Desktop lagana sabse reliable tarika hai: + +### Step 1: VM ke Terminal me XFCE + Chrome Remote Desktop Install Karein +Ubuntu VM kholkar Terminal me yeh commands copy-paste karein: + +```bash +sudo apt update && sudo apt upgrade -y + +# Lightweight XFCE desktop aur browser install karein +sudo apt install -y xfce4 xfce4-goodies dbus-x11 wget curl + +# Google Chrome + Chrome Remote Desktop package download & install karein +wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb +sudo apt install -y ./google-chrome-stable_current_amd64.deb + +wget https://dl.google.com/linux/direct/chrome-remote-desktop_current_amd64.deb +sudo apt install -y ./chrome-remote-desktop_current_amd64.deb + +# CRD ko XFCE session use karne ke liye set karein +echo "exec /etc/X11/Xsession /usr/bin/xfce4-session" > ~/.chrome-remote-desktop-session +``` + +### Step 2: Headless Auth Command se Link Karein +1. **Apne khud ke laptop/PC** (ya VM ke Chrome) me apni Google ID se yeh page kholein: + 👉 **`https://remoting.google.com/headless`** +2. **Begin** -> **Next** -> **Authorize** par click karein. +3. **"Debian Linux"** wala command copy karein (woh kuch aisa dikhega: `DISPLAY= /opt/google/chrome-remote-desktop/start-host --code="4/0A..." ...`). +4. Woh command **Ubuntu VM ke terminal** me paste karke Enter dabayein aur apna **6-digit PIN** set karein. + +### Step 3: Auto-Start at Boot Enable Karein +```bash +sudo systemctl enable chrome-remote-desktop@$USER.service +sudo loginctl enable-linger $USER +``` + +Ab Hyper-V window band kar dein! Jab bhi dost laptop on karega aur Wi-Fi judega, 1 minute ke andar `https://remoting.google.com/access` par aapka Linux Desktop **Online** aa jayega! + +--- + +## 💡 Bonus: Wi-Fi Drop Auto-Recovery Lagayein +Kabhi-kabhi agar dost ka Wi-Fi 10 minute band hokar wapas aata hai, toh instant reconnect ke liye [`scripts/guest-watchdog.sh`](../scripts/guest-watchdog.sh) ko VM ke andar enable kar lein (Guide [`docs/06-troubleshooting-faq.md`](06-troubleshooting-faq.md) me diya hai). diff --git a/docs/04-option-b-kasm-cloudflare.md b/docs/04-option-b-kasm-cloudflare.md new file mode 100644 index 0000000..8325ec5 --- /dev/null +++ b/docs/04-option-b-kasm-cloudflare.md @@ -0,0 +1,115 @@ +# 04 — Option B: Custom Website URL (`https://vm.yourdomain.com`) via KasmVNC + Cloudflare Tunnel + +> 🔥 **Sabse "Tagda" Pro Developer Setup:** Apni khud ki website (`https://vm.yourdomain.com`) kholo -> Email OTP daalo -> Seedha Browser Tab ke andar High-Speed Desktop (Audio + Clipboard + File Transfer ke saath) chalao! + +Yeh setup **Ubuntu Server 24.04 LTS** Guest VM use karta hai jo sirf **~1.5 GB se 2.5 GB RAM** leta hai — dost ke laptop ko bilkul pata bhi nahi chalega ki koi heavy workload chal raha hai (zero lag!). + +--- + +## Architecture Kaise Kaam Karta Hai? + +```text +Aapka Browser (https://vm.yourdomain.com) + | + v +[ Cloudflare Access Zero Trust ] <-- Sirf AAPKE Email par OTP aayega (100% Free) + | + v (Outbound Encrypted Cloudflare Tunnel — No Port Forwarding!) +[ Hyper-V Ubuntu Server VM ] + | + +--> Docker (`restart: always`) + └── LinuxServer Webtop (KasmVNC Desktop on localhost:3000) +``` + +### Prerequisites: +1. Dost ke Windows 11 Pro par [`docs/02-host-setup-windows11.md`](02-host-setup-windows11.md) follow karke **Ubuntu Server 24.04 LTS** ka VM bana lein (`3GB` Dynamic RAM kaafi hai). +2. Ek sasta domain (jaise `.xyz`, `.tech`, `.in` — ₹99–₹499/year) jo **Cloudflare Free Account** se connected ho. *(Agar domain nahi hai toh Option A ya Option C ka free Tailscale URL use karein).* + +--- + +## Step 1: Ubuntu VM ke Andar Webtop (KasmVNC) Setup Karein + +Aap hamara automated script [`scripts/guest-linux-kasm-cloudflare.sh`](../scripts/guest-linux-kasm-cloudflare.sh) chala sakte hain, ya neeche diye steps manually run karein: + +```bash +# 1. Docker & Docker Compose install karein +curl -fsSL https://get.docker.com | sudo sh +sudo usermod -aG docker $USER +newgrp docker + +# 2. Webtop folder banayein +mkdir -p ~/webtop && cd ~/webtop +``` + +Ab `~/webtop/docker-compose.yml` banayein (`nano docker-compose.yml`): + +```yaml +services: + webtop: + image: lscr.io/linuxserver/webtop:ubuntu-xfce + container_name: remote-webtop + security_opt: + - seccomp:unconfined + environment: + - PUID=1000 + - PGID=1000 + - TZ=Asia/Kolkata + - CUSTOM_USER=admin + - PASSWORD=ChangeThisStrongPassword123! + - SUBFOLDER=/ + - TITLE=My Remote Cloud PC + volumes: + - ./data:/config + ports: + - "127.0.0.1:3000:3000" # Sirf localhost par bind hai (bahar se bina tunnel koi access nahi kar sakta) + shm_size: "1gb" # Zaroori: Iske bina container ke andar Chrome crash hota hai! + restart: always # VM start hote hi automatic chalu hoga +``` + +Container start karein: +```bash +docker compose up -d +``` + +--- + +## Step 2: Cloudflare Zero Trust Tunnel Connect Karein (Free) + +1. Apne browser me **[Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/)** kholein. +2. Left menu me **Networks -> Tunnels** -> **Create a tunnel** -> **Cloudflared** select karein. +3. Tunnel ka naam rakhein: `friend-laptop-vm` -> **Save tunnel**. +4. **Debian (64-bit)** select karein — wahan aapko ek `curl ... && sudo cloudflared service install ` command milega. +5. Woh command **Ubuntu VM ke terminal** me paste karke run kar dein! + - `cloudflared` ek Linux system service ban jayega jo har reboot par **automatically start** hoga! + +### Public Hostname Set Karein: +Cloudflare Tunnel ke **Public Hostname** tab me: +- **Subdomain:** `vm` +- **Domain:** `yourdomain.com` +- **Service Type:** `HTTP` +- **URL:** `localhost:3000` +- **Save hostname** par click karein! + +--- + +## Step 3: Security Lock Lagayein (Cloudflare Access OTP — MUST DO!) + +Kabhi bhi remote desktop ko sirf password ke bharose public internet par mat chhodna. Cloudflare Access free me **Google Login / Email OTP wall** laga deta hai: + +1. Cloudflare Zero Trust Dashboard -> **Access -> Applications** -> **Add an application** -> **Self-hosted**. +2. **Application name:** `My VM` | **Subdomain:** `vm` | **Domain:** `yourdomain.com`. +3. **Policy Name:** `Only Me` | **Action:** `Allow`. +4. **Configure rules -> Include:** Selector me **`Emails`** chunein aur **sirf apna personal Gmail address** daalein! +5. Save kar dein! + +--- + +## 🚀 Kaise Use Karein? + +1. Dost ke laptop par Hyper-V window close kar dein. +2. Jab bhi dost laptop on karega aur Wi-Fi se connect hoga: + - Hyper-V Ubuntu VM ko background me start karega. + - Docker `remote-webtop` start karega aur `cloudflared` tunnel jod dega. +3. Aap 1 km door apne ghar pe laptop/tablet me browser kholkar **`https://vm.yourdomain.com`** type karoge: + - Cloudflare aapse Email maangega -> Gmail pe OTP aayega -> OTP daalte hi **Pura Ubuntu XFCE Desktop aapke browser tab me khul jayega!** + - Left side me chhota arrow tab hota hai jisse aap **Clipboard Sync, Audio Enable, Fullscreen, aur File Upload/Download** kar sakte ho. diff --git a/docs/05-option-c-guacamole-rdp.md b/docs/05-option-c-guacamole-rdp.md new file mode 100644 index 0000000..400e02d --- /dev/null +++ b/docs/05-option-c-guacamole-rdp.md @@ -0,0 +1,83 @@ +# 05 — Option C: Full Windows VM in Browser (RDP / Self-Hosted Gateway Alternatives) + +Agar aapko **Guest VM ke andar Windows 10/11 Pro** hi chalana hai aur aap Google ke Chrome Remote Desktop (Option A) ke alawa **pure Browser URL** alternatives dekh rahe hain, toh yeh 2 tarike sabse best aur lightweight hain: + +--- + +## Method C1: DWService Inside Windows VM (Zero Domain, Ultra-Light ~20MB RAM) + +**[DWService](https://www.dwservice.net/)** ek lightweight open-source agent hai jo seedha browser ke andar **Screen Control + File Explorer (Upload/Download) + Command Prompt** teeno de deta hai: + +1. [`docs/02-host-setup-windows11.md`](02-host-setup-windows11.md) ke hisaab se Hyper-V me **Windows 10/11 Guest VM** banayein. +2. Apne browser me `https://www.dwservice.net` par free account banayein (**2FA / Two-Factor Auth** zaroor on karein) aur **Agents -> New Agent** click karke **Install Code** lein. +3. **Windows Guest VM ke andar** `dwagent.exe` download karke **Install** select karein aur apna Install Code daal dein. +4. `DWAgent` Windows Service ki tarah install ho jaata hai (boot hote hi Lock Screen se pehle chalu ho jaata hai aur sirf ~20MB RAM leta hai!). +5. Ab kahin se bhi browser me `dwservice.net` login karein -> **Screen** click karein -> Pura Windows VM browser me control karein! + +--- + +## Method C2: Apache Guacamole + Tailscale HTTPS (100% Private Self-Hosted HTML5 RDP) + +Agar aap kisi third-party remote screen relay ki jagah **apna khud ka HTML5 RDP Gateway** chalana chahte hain (bina domain kharide!), toh **Apache Guacamole + Tailscale** best combination hai. + +### Architecture: +- **Guest VM 1 (`WinVM` — 3.5GB RAM):** Windows 10/11 Pro jisme built-in **Remote Desktop (RDP Port 3389)** enabled hai. +- **Guest VM 2 (`GatewayVM` — 512MB RAM Alpine/Ubuntu Server):** Isme **Apache Guacamole** (Docker) aur **Tailscale** chalta hai. Dono VMs Hyper-V ke same `Default Switch` par hone ki wajah se aapas me fast local network par baat karte hain! + +### Setup Steps (`GatewayVM` Ubuntu/Debian Mini Server par): + +#### 1. Windows Guest VM (`WinVM`) me RDP Enable Karein: +Windows Guest VM ke andar PowerShell (Admin) kholkar run karein: +```powershell +# Windows VM ke andar RDP enable karein +Set-ItemProperty -Path 'HKLM:\System\CurrentControlSet\Control\Terminal Server' -name "fDenyTSConnections" -value 0 +Enable-NetFirewallRule -DisplayGroup "Remote Desktop" + +# VM ka internal hostname check karein (jaise WinVM.mshome.net) +hostname +``` + +#### 2. `GatewayVM` me Docker + Guacamole Chalu Karein: +```bash +curl -fsSL https://get.docker.com | sudo sh +sudo usermod -aG docker $USER && newgrp docker + +mkdir -p ~/guac && cd ~/guac +cat << 'EOF' > docker-compose.yml +services: + guacamole: + image: abesnier/guacamole:latest + container_name: guacamole + ports: + - "127.0.0.1:8080:8080" + volumes: + - ./config:/config + restart: always +EOF + +docker compose up -d +``` + +#### 3. Tailscale HTTPS URL Enable Karein (Free `https://...ts.net` URL, Zero Public Exposure!): +```bash +# Tailscale install karein +curl -fsSL https://tailscale.com/install.sh | sh +sudo tailscale up + +# Port 8080 ko private HTTPS URL par background me serve karein +sudo tailscale serve --bg 8080 +``` + +Tailscale aapko ek private URL dega jaise: +👉 `https://gatewayvm.tail12345.ts.net/` + +#### 4. Guacamole Configure Karein: +1. Apne laptop/phone par **Tailscale** on rakhein aur browser me `https://gatewayvm.tail12345.ts.net/` kholein. +2. Default login (`guacadmin` / `guacadmin`) se ghuste hi **sabse pehle Settings -> Users me jaakar `guacadmin` ka password strong password se badlein aur TOTP 2FA on karein!** +3. **Settings -> Connections -> New Connection:** + - **Protocol:** `RDP` + - **Hostname:** `WinVM.mshome.net` *(Hyper-V `Default Switch` automatic `.mshome.net` local DNS deta hai — IP change hone ka darr bhi khatam!)* + - **Port:** `3389` + - **Username / Password:** Apne Windows VM ka username aur password + - **Security mode:** `NLA` + Tick `Ignore server certificate` +4. Save karein! Ab jab bhi aap browser me apna `https://gatewayvm.tail12345.ts.net` kholenge, click karte hi **Full-Screen 60FPS Windows RDP seedha Chrome/Edge ke andar khulega!** diff --git a/docs/06-troubleshooting-faq.md b/docs/06-troubleshooting-faq.md new file mode 100644 index 0000000..ab5098c --- /dev/null +++ b/docs/06-troubleshooting-faq.md @@ -0,0 +1,74 @@ +# 06 — Real-World Troubleshooting, Auto-Recovery & Best Practices + +Jab server ek dost ka daily-use Wi-Fi laptop ho, toh real life me kuch practical situations aati hain. Un sabka ready-made solution yahan hai: + +--- + +## 1. Screen Resolution Fix (`1024x768` se `1920x1080` Full HD Kaise Karein?) + +Kabhi-kabhi headless Hyper-V VM me default resolution `1024x768` lock dikhta hai. Isse 1080p Full HD karne ke liye **Dost ke Windows 11 Host PowerShell (Admin)** me ek baar yeh command chala dein: + +```powershell +Set-VMVideo -VMName "RemoteWorkVM" -HorizontalResolution 1920 -VerticalResolution 1080 -ResolutionType Single +``` +*(KasmVNC / Option B aur Chrome Remote Desktop Resize-to-Fit me browser window ke hisaab se resolution apne aap dynamic adjust ho jaata hai!)* + +--- + +## 2. Wi-Fi Disconnect / Hotspot Switch Auto-Recovery + +Agar dost ka router light jaane ki wajah se 10 minute band rahe aur phir wapas on ho, toh 99% time `cloudflared`, `tailscaled`, aur `chrome-remote-desktop` khud reconnect kar lete hain. + +Lekin **100% Bulletproof Guarantee** ke liye Ubuntu Guest VM ke andar hamara watchdog script lagayein: + +```bash +chmod +x scripts/guest-watchdog.sh +sudo cp scripts/guest-watchdog.sh /usr/local/bin/vm-watchdog.sh + +# Har 2 minute me internet + tunnel health check karne ke liye cron job lagayein: +(crontab -l 2>/dev/null; echo "*/2 * * * * /usr/local/bin/vm-watchdog.sh >> /var/log/vm-watchdog.log 2>&1") | crontab - +``` + +--- + +## 3. Dost Heavy Gaming Kare Toh Kya? (Give Friend a Pause/Resume Button!) + +Dost ke paas 16GB RAM hai. Normal coding, YouTube, MS Office, Chrome me `4GB` VM se unhe **zero farq** padega. Lekin agar dost kabhi heavy AAA game khele aur unhe poora CPU/RAM chahiye ho, toh unke Desktop par 2 chhote shortcuts bana kar de dein (isse dost ka bharosa bhi rahega aur unhe kabhi dikkat nahi aayegi): + +- **Pause VM (Instant Freeze, Frees CPU):** + ```powershell + powershell -Command "Start-Process powershell -Verb runAs -ArgumentList 'Suspend-VM -Name RemoteWorkVM'" + ``` +- **Resume VM:** + ```powershell + powershell -Command "Start-Process powershell -Verb runAs -ArgumentList 'Resume-VM -Name RemoteWorkVM'" + ``` + +--- + +## 4. Laptop Lid Close (Screen Band Karne Par Kya Hoga?) + +Default Windows 11 laptop me jab koi **Lid band (screen fold)** karta hai, toh laptop **Sleep** me chala jaata hai aur Wi-Fi band ho jaata hai. +- **Safe Rule:** Agar dost table par charger laga kar lid band karke rakhte hain, toh sirf **`Plugged in -> Closing the lid -> Do nothing`** set kar sakte hain (Control Panel -> Power Options -> Choose what closing the lid does). +- **⚠️ Kabhi Mat Karna:** **`On battery -> Closing the lid`** ko hamesha **`Sleep`** par hi rakhein! Warna bag ke andar laptop garm hokar kharab ho sakta hai. + +--- + +## 5. 512GB SSD Space Bachane ka Tarika + +Hyper-V ka **Dynamically Expanding `.vhdx`** jab VM ke andar files banti hain toh badhta hai, par files delete karne par apne-aap chhota nahi hota. Mahine me ek baar (jab VM shutdown ho) aap space wapas shrink kar sakte ho: + +```powershell +# Host Admin PowerShell me (VM band hone par): +Optimize-VHD -Path "C:\HyperV\VMs\RemoteWorkVM\Virtual Hard Disks\RemoteWorkVM.vhdx" -Mode Full +``` + +--- + +## 6. Security Checklist ✅ + +- [ ] Dost ki explicit permission li gayi hai. +- [ ] Guest VM ke andar strong password laga hai. +- [ ] Browser gateway par **2FA / OTP** on hai (Google Account 2FA for CRD / Cloudflare Access Email OTP / Tailscale Auth). +- [ ] Host PC par koi bhi inbound router port open **nahi** kiya gaya hai. +- [ ] VM ki Maximum RAM `4096 MB` aur CPU limit `60%` par capped hai. diff --git a/docs/07-gothwad-ecosystem-setup.md b/docs/07-gothwad-ecosystem-setup.md new file mode 100644 index 0000000..190eda4 --- /dev/null +++ b/docs/07-gothwad-ecosystem-setup.md @@ -0,0 +1,43 @@ +# 07 — Zero-D1 Cloudflare Pages Deployment (`remotedesktop.gothwadtech.com`) + +Yeh guide explain karta hai ki **bina koi alag Cloudflare D1 database ya Worker banaye** (bilkul `GothwadTech/meet` ki tarah!) **`https://remotedesktop.gothwadtech.com`** sirf **60 seconds me Cloudflare Pages** par kaise live hoga! + +--- + +## 1. Hume Alag D1 Database Kyun Nahi Chahiye? + +Pehle traditional remote tools device list aur signaling ke liye SQL database use karte the, jisme `wrangler d1 create`, UUIDs, aur secrets maintain karne ka jhanjhat rehta tha. Is project me humne **Zero-D1 P2P Architecture** lagaya hai: + +| Kaam | Zero-D1 Solution (Koi Database Nahi!) | +| :--- | :--- | +| **Gothwad Login & Multi-Account (`u/0..5`)** | Browser seedha `https://api.gothwadtech.com/api/auth/me` (`credentials: 'include'`) call karta hai! `GothwadTech/accounts` pehle se har `*.gothwadtech.com` subdomain ko allow karta hai. | +| **Auto-Finding Your Windows PCs** | Har PC aapke Gothwad username ke deterministic WebRTC slot (`grd--slot-1`..`6`) par listen karta hai. Login hote hi browser slots ko P2P probe karke Online PCs dikha deta hai! | +| **PIN & Sharing Permissions (`vm_only` / `host_and_vm`)** | Aapka 6-digit PIN aur allowed `@gothwadtech.com` users seedha Windows Host ke `C:\ProgramData\GothwadRemote\agent-config.json` me rehte hain — aur `HMAC-SHA256(pin, nonce:viewer:mode)` challenge-response se verify hote hain (raw PIN kabhi network par nahi jaata!). | +| **60 FPS Screen + Mouse/Keyboard Stream** | `PeerJS` + Google STUN + OpenRelay TURN (`GothwadTech/meet` wala proven CGNAT-bypass WebRTC stack) se seedha Browser ↔ Windows PC ke beech encrypted DTLS tunnel banta hai! | + +--- + +## 2. Cloudflare Pages Deployment (Sirf 1 Minute — Zero CLI, Zero Secrets!) + +1. **Cloudflare Dashboard** kholein -> **Workers & Pages** -> **Create** -> **Pages** tab -> **Connect to Git**. +2. Apni repo **`GothwadTech/remotedesktop`** select karein. +3. Build settings bharein: + - **Framework preset:** `Vite` + - **Build command:** `npm run build` + - **Build output directory:** `dist` +4. **Save and Deploy** click karein! +5. Deploy hone ke baad Pages project ke **Custom domains** tab me jaakar **`remotedesktop.gothwadtech.com`** add kar dein. + +**Bas khatam!** Koi `wrangler.toml`, koi D1 database, koi Worker route, ya koi secret key dalne ki zaroorat nahi hai! + +*(Optional: Agar kabhi future me non-`.gothwadtech.com` domain ya mobile app se OAuth consent screen chahiye ho, tabhi sirf `accounts` D1 ke `ecosystem_apps` me `gothwad-remotedesktop` add karna hota hai — web `remotedesktop.gothwadtech.com` ke liye uski bhi zaroorat nahi hai kyunki `.gothwadtech.com` cookie SSO direct kaam karta hai!)* + +--- + +## 3. GitHub Actions EXE Release (`GothwadTech/remotedesktop`) + +Dost ya apne Windows PC par Python install na karna pade, iske liye repo me [`.github/workflows/build-windows-agent.yml`](../.github/workflows/build-windows-agent.yml) laga hua hai: + +1. GitHub par **Actions -> Build Windows Agent EXE & Publish GitHub Release -> Run workflow** dabayein (ya `git tag v1.0.0 && git push origin v1.0.0` karein). +2. GitHub ka `windows-latest` server apne-aap **`GothwadRemoteAgent-win64.exe`** aur **`GothwadRemoteSetup-win64.zip`** bana kar **Releases** me daal dega! +3. Uske baad user bas **`https://remotedesktop.gothwadtech.com/setup`** kholega -> wahan diye gaye 1-line PowerShell command ko copy-paste karega -> Owner Consent (`YES`) aur **6-digit PIN** set karega -> PC turant dashboard par **Online** aa jayega! diff --git a/index.html b/index.html new file mode 100644 index 0000000..545ca8c --- /dev/null +++ b/index.html @@ -0,0 +1,18 @@ + + + + + + + + Gothwad Remote Desktop — remotedesktop.gothwadtech.com + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..179c6f0 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2892 @@ +{ + "name": "gothwad-remotedesktop", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gothwad-remotedesktop", + "version": "1.0.0", + "dependencies": { + "@tailwindcss/vite": "^4.1.4", + "@vitejs/plugin-react": "^4.3.4", + "lucide-react": "^0.487.0", + "peerjs": "^1.5.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwindcss": "^4.1.4" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@types/react": "^19.0.12", + "@types/react-dom": "^19.0.4", + "tsx": "^4.19.3", + "typescript": "^5.8.3", + "vite": "^6.3.2" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz", + "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz", + "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.7", + "@babel/helper-compilation-targets": "^7.29.7", + "@babel/helper-module-transforms": "^7.29.7", + "@babel/helpers": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/template": "^7.29.7", + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.8", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.8.tgz", + "integrity": "sha512-gZbepsdh3WDtgZKWL+vTPh71LSBrm/Y4/QDZBVCcYfmeTEEuoOYwlSy+G1StfJg+/Zy550u/3TATbm7qDbbMtg==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.8", + "@babel/types": "^7.29.8", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz", + "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==", + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.29.7", + "@babel/helper-validator-option": "^7.29.7", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz", + "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz", + "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz", + "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.29.7", + "@babel/helper-validator-identifier": "^7.29.7", + "@babel/traverse": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz", + "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.11.tgz", + "integrity": "sha512-snZbO7MEDOof+hvNa97EjEY7qD7h2kwvQr5AJC3ZhQudKW5dnej6k3kP9NQV88hJwaS8vDqRLFo9rivLEhle2g==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz", + "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.10.tgz", + "integrity": "sha512-EMCx3AGZ3WfAl6ht7AyQoPm2Lh+BGYEMdc4Q6Hf7uSCDOZFuLq44qNwL+iaim8VlyYhRAo4X43+weadK8ZB4Yg==", + "license": "MIT", + "dependencies": { + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.11.tgz", + "integrity": "sha512-OZ6+oaK7GMyg3IfCpj8JOmn8n+w2rJbDT0LLfVDTLb1ZFeKDh3AiNELgCMUw175IJHSkLl74hG0M0VLsvx+gag==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.11" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz", + "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz", + "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==", + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz", + "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.10.tgz", + "integrity": "sha512-B3lYAs2nkKskbgQ1nfy8Ma9ao9Q1rntTAQHrzzyDuj+TSO+X3le6PfWwqDvodsb+OumE10eMFfuMCTQSQ5iuIQ==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.7", + "@babel/generator": "^7.29.8", + "@babel/helper-globals": "^7.29.7", + "@babel/parser": "^7.29.9", + "@babel/template": "^7.29.7", + "@babel/types": "^7.29.8", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.11", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.11.tgz", + "integrity": "sha512-WCsemW40Cpc6gouC1SrY6PBaZ+oLRlEVY9XBvZBI7il/HyUrb2nJ505wE1MHEFyDZNnrWDEuy4jL50WVHfTTSQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.29.11", + "@babel/helper-validator-identifier": "^7.29.7" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@msgpack/msgpack": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@msgpack/msgpack/-/msgpack-2.8.0.tgz", + "integrity": "sha512-h9u4u/jiIRKbq25PM+zymTyW6bhTzELvOoUd+AvYriWOAKpLGnIamaET3pnHYoI5iYphAHBI4ayx0MehR+VVPQ==", + "license": "ISC", + "engines": { + "node": ">= 10" + } + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.65.0.tgz", + "integrity": "sha512-t2AnVba25baFdq7gczzS13JyVcdubLVbFX1X04mxrggDrTQPUXNv3XM0k9H1hr5tqvg7AVvtQ720QHAj0iYqhg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.65.0.tgz", + "integrity": "sha512-dNONUaEwPGhl/nuO2KV8VABWlcjYyU6KtZGN8dQv5OS5JU7JSCxPijVNKXbRJd0iuTzTiJ4TjPt/4bbvDRNiMQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.65.0.tgz", + "integrity": "sha512-zWQqUH+9zavr2Bxuzfpuv2SIF3X31cr58TvSQC9o1GhpOvkqRwr60GkZdQcudyT/BEgdpdSafjgd2I13MbTL7g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.65.0.tgz", + "integrity": "sha512-RQYreBFWtO4//hIuOaZkYUnF/lPU4lz35dveDimDshWCJvVisVtJ/shmT9bkXK8wFJY+FJF89X6F3Cq1qI3LCw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.65.0.tgz", + "integrity": "sha512-A0NfMj6Cuh/9BE5k3awJKv6HoxFVXIcLAb6SZqAMoxUHPA1ZNNK4RsGuUPD/vL7nEAm9LG+ZzXWWTrnC0XtkpA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.65.0.tgz", + "integrity": "sha512-BI6/H7q3aP0I7S7GOrjyxTjgEJlSnovXE38+bdxBpuKN6z+d//uF+T+VQ3Q7ltJL/71p17ufartARX9Ja5gOGQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.65.0.tgz", + "integrity": "sha512-W1vUprf09RWaVWnsFP1vlUkEWJzTZlpObOEAYZXt9Fq0UVAOYEkeGr7wBX7McKqqIQskXdBnpiOMgwPujordgA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.65.0.tgz", + "integrity": "sha512-s8QTcZWgrl9haFzEGIqyE5BWyo89pFDGLMBFZ752WYxk8DJwNU/O1X6pZgPw829d9/sm6CCHyrMP3aurt76p1Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.65.0.tgz", + "integrity": "sha512-ke1f6dLRzkqveyjjF2fkEwQYZvxHtNNwjegY8nJrwcO0Bdqr8CzFO/pKE0Bns13cx0qz2Ba2Y0Hs3dSwMdnHxA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.65.0.tgz", + "integrity": "sha512-926SMeUcy4y18Rz/w2aJiUKOnyAnm6czg72bXkQEaeemsll+Wr0hnsjb2BGRpHvWgwAMYy5ACJCotbvfLCfj0A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.65.0.tgz", + "integrity": "sha512-6T7JNRP1hc4hqF2dUsEoVLOt9K8tmODoCLldtQRYnrddGGQY8Gqe2pM/qMMUo7EMowpP7oa2N95k0/8sYK/ipw==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.65.0.tgz", + "integrity": "sha512-SImGFl1Je5kpyAgOCPONVT7h6ASV8mAUwGRjY7eZvVjPg//PfHOXgI+P241RF/+HqsA4PP3qD527ysWFKNQyhQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.65.0.tgz", + "integrity": "sha512-XeDTbmPF1Yu5asfXGPrMkWoxTDhgryQbmw5Q1+R582O51VNCk23J6f2pDrsAp5YkneBVBwtExx6ZGOb9D2aQ6A==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.65.0.tgz", + "integrity": "sha512-VS55JUI/1nsljmV/jfqNkLZXFw8uMO8nNlfoHmSevNf6fO3neuhyDvx9dv3c/Ytt8r/431SL8Qax1dpYvOMZTg==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.65.0.tgz", + "integrity": "sha512-Vblw84TbX7RS4la1Y4+yQ7aore/oSyaNtFbppuBr5Vs/4LfBeXKq9dIfoExw6zOvRERqd2rRAjnZRaMhH/0x5w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.65.0.tgz", + "integrity": "sha512-7+gHjtONvKgwR9InTKFaLq53f6YzBJkgeMRBENdWTsQr+spahGqQm8HF5aXpq8i4JaRrOqYyv6Zn1WltDTxaZQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.65.0.tgz", + "integrity": "sha512-KvNDC0J6s4vVHhtJ6KrDXU0Er0au/hmRIBq/+1maCV8TseGupeKizq0bWcrbp2HE1T8xoknvN0Q/KZ/YF0qX3w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.65.0.tgz", + "integrity": "sha512-GExdD15WPvu/x/drELjVam7vX8EsPgvTMOa/iJL/p/4m/oOqzbb7JXZg8fXR4hlXtg/sF2zyTZ3bR0AEtEMlhQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.65.0.tgz", + "integrity": "sha512-8sT9PemIZfIMED4Vd4KGoOXVDj5CMSWNcGdbheRUx9TYS03yOWdT2Q0lXPyEyNBB9YTilwewIQKNIBk6Nxi0Kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.65.0.tgz", + "integrity": "sha512-pO6Ehi0jo5yE0R6PltKhJhv9mlIiyzBJk5N5NUF2OF6xtVKI+1bdXxj/rb0jGXy3hck1ELVT0QPY3tzkAm360w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.65.0.tgz", + "integrity": "sha512-vo7N3/+cjjPDYr8vtxC+qSNgWa9sDvJ7g8N8R5SWqFY4WiNH1ULcAU70SKVyHeiIZOYYwNP9pTipmhN5sl1NPw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.65.0.tgz", + "integrity": "sha512-ArwvgFAbjKcDM7qqotZ4OVYf+UMT9xH2YzkCzK4+AuZSDihMlz1Ekoe4INwBVXzn2mnJzuIYOcSx07iTBijGRQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.65.0.tgz", + "integrity": "sha512-QGMmK4/cxKE/36FoLMSHQBCyymv/eFTnsCvvsBF0rgmOvVbvX78VWZRerL3dHa77f4pM6Xcc2iGDhJ10w/svKQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.65.0.tgz", + "integrity": "sha512-XfKYFWHIPs4HPODIMsGF1IKlS3H1e3WokUZIgpmn4+uNnZBlnRDDHEylNyazbQXwhMKZre68OjtRYbKqb5+j8w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.65.0.tgz", + "integrity": "sha512-wPWi++K4Bs1VR4XihOhfGi4IGX6Cub7FlerTf8X2v+2BtDHddgSqWCXwGD+h2yAe4/niIhNDwAltioOC+wEogQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", + "integrity": "sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.24.1", + "jiti": "^2.7.0", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.3.3.tgz", + "integrity": "sha512-krXjAikiaFSPaK/FkAQT5UTx3VormQaiZ5hBFlJZ9UFQGB/rwg1MZIhHAG9smMQRTdyJxP6Qt5MwMtdyU5FWrA==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-arm64": "4.3.3", + "@tailwindcss/oxide-darwin-x64": "4.3.3", + "@tailwindcss/oxide-freebsd-x64": "4.3.3", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.3.3", + "@tailwindcss/oxide-linux-arm64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-arm64-musl": "4.3.3", + "@tailwindcss/oxide-linux-x64-gnu": "4.3.3", + "@tailwindcss/oxide-linux-x64-musl": "4.3.3", + "@tailwindcss/oxide-wasm32-wasi": "4.3.3", + "@tailwindcss/oxide-win32-arm64-msvc": "4.3.3", + "@tailwindcss/oxide-win32-x64-msvc": "4.3.3" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.3.3.tgz", + "integrity": "sha512-Y85A2gmPSkl5Ve5qR86GL4HT509cFqQh1aes9p3sSkyTPwt0Pppf3GkwGe4JPACcRYjgJIEhQgM6dBClnr0NYw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.3.3.tgz", + "integrity": "sha512-BiaWatpBcERQFDlOjRDpIVXuFK5PJez5SA4JMg6VYZdBYU+qKfV/vqjcIs+IYmtitf1xYQZTwXvU/8y4lfZUGw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.3.3.tgz", + "integrity": "sha512-fAeUqfV5ndhxRwai8cXGzdLvul9utWOmeTkv69unv4ZXixjn61Z+p9lCWdwOwA3TYboG3BwdVuN/RDjhBRl0mw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.3.3.tgz", + "integrity": "sha512-iyf5bV6+wnAlflVeEy7R25dupxTNECZN5QMI0qNT6eT+EgaGdZcKhGkr5SdoaWiLJ3spLqIY9VCeSGrwmtg4kw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.3.3.tgz", + "integrity": "sha512-aAYUprJAJQWWbRrPvtjdroZ56Md+JM8pMiopS6xGEwDfLhqj+2ver2p4nU4Mb3CRqcMmNBjo8KkUgcxhkzVQGQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.3.3.tgz", + "integrity": "sha512-nDxldcEENOxZRzC2uu9jrutZdAAQtb+8WWDCSnWL1zvBk1+FN+x6MtDViPB5AJMfttVCUhehGWus3XBPgatM/w==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.3.3.tgz", + "integrity": "sha512-Md44bD6veX/PC5iyF8cDVnw4HBIANZepRZZ7a8DQOvkfo5WUBwcp6iAuCUz23u+4SUkhJlD3eL7hNdW8ezd/kA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.3.3.tgz", + "integrity": "sha512-tx7us1muwOKAKWao2v/GaafFeQboE6aj88vC6ziN2NCGcRm8gWUhwjzg+YdVB1e4boAtdtma4L43onunI6NS4w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.3.3.tgz", + "integrity": "sha512-SJxX60smvHgasZoBy11dX6YRjXJFovwWBoedhbQPOBzgFWBHGB+TVPWB9BxzR7TTxU8FQZAI2AyiNCMzFm8Img==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.3.3.tgz", + "integrity": "sha512-jx1+rPhY/5Ympkktd656HBWEBLxP7dH06losBLjjf5vgCODXvi9KhtftWcMIwTFIDqBr7cRnQkdLnAG+IOlGvQ==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.11.1", + "@emnapi/runtime": "^1.11.1", + "@emnapi/wasi-threads": "^1.2.2", + "@napi-rs/wasm-runtime": "^1.1.4", + "@tybys/wasm-util": "^0.10.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.3.3.tgz", + "integrity": "sha512-3rc292Ca2ceK6Ulcc/bAVnTs/3nDtoPhyEKlgPv+yQJQi/JS/AMJlqzxvlDacL1nekbrcf6bTqp/jV4qgnPxNQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.3.3.tgz", + "integrity": "sha512-yJ0pwIVc/nYeGoV02WtsN8KYyLQv7kyI2wDnkezyJlGGjkd4QLwDGAwl47YpPJeuI0M0ObaXGSPjvWDPeTPggw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.3.3.tgz", + "integrity": "sha512-yYU8cogLeSh/ms2jh8Fj7jaba/EWa7Ja6GoUqYZaraEuCI5YS6ms6ObZgjjedm+jm6XZjdNRWBpPP6Z86oOxcw==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.3.3", + "@tailwindcss/oxide": "4.3.3", + "tailwindcss": "4.3.3" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.20.5", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.20.5.tgz", + "integrity": "sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/react": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.3.0.tgz", + "integrity": "sha512-N0rFCuH9YoxG9/m61l9MfpJKfmLOVU0em7ipIz6TRgSSkvReLB9vL85GB+yr8Bs5leqpvg96JSwF4ZS1s4viQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.3.0.tgz", + "integrity": "sha512-ZI7bU42mZXXKHn/qNLEw2IrbiINU7X5+vfgdixBHkCNpYWXjKgfQ/P+uyGb5CjOLB9UcnTeg3rylQtV2hym44Q==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.3.0" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.17.0" + }, + "engines": { + "node": "^14.18.0 || >=16.0.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.11.32", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.32.tgz", + "integrity": "sha512-UvuxFU9P3hM29LrQdwu0cD6w6/+SFr+UlB9s/knI7+O2rK9xFxKUwSDWxGdbbqQ9/V7BJ5s0NI5nQAE4uJp3gQ==", + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.29.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.29.4.tgz", + "integrity": "sha512-2TcbbiwXEq+N0kD36tykpCfyyQigUXmuwNQbnPp4Xk4PX9JOqVIKjJlIDjpH7uY6tmRGoAd+apeT1p60T8wsdA==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.11.29", + "caniuse-lite": "^1.0.30001816", + "electron-to-chromium": "^1.5.459", + "node-releases": "^2.0.59", + "update-browserslist-db": "^1.3.4" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001819", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001819.tgz", + "integrity": "sha512-LFUqIIAwpzsIuevnKnsR9qnStBcgzPShTX61lBwN9uFkkf8o8zLiebEjbW8AYtZiF+1YKDWfdt/pWDtMuG8Xwg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.464", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.464.tgz", + "integrity": "sha512-nRa65aFge40viPAKxMKULIY8tcXKwKboJWNn4RoKwyWXJ6SP7BnrDGKUTzc64818jIciH0PK7LSF8QzZes19GA==", + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.26.0.tgz", + "integrity": "sha512-9vhedylFonb2YGogzUKX6+Ja72gOJbN1QHAqdrvqLwhdl/QWbKopzoUC9EbQNVsAns/bx/4uyqalrQAoy1IByw==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "devOptional": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/eventemitter3": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", + "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", + "license": "MIT" + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "0.487.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.487.0.tgz", + "integrity": "sha512-aKqhOQ+YmFnwq8dWgGjOuLc8V1R9/c/yOd+zDY4+ohsR2Jo05lSGc3WsstYPIzcTpeosN7LoCkLReUUITvaIvw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.60", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.60.tgz", + "integrity": "sha512-OmO8+GM1JUZqXET720VrJCwkjUCXmCMQm0Z3UDDRbfNLHS4NEAvY60j5jiFv0tkKw2PNNlqOLhahVmwJIOLDIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/peerjs": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/peerjs/-/peerjs-1.5.5.tgz", + "integrity": "sha512-viMUCPDL6CSfOu0ZqVcFqbWRXNHIbv2lPqNbrBIjbFYrflebOjItJ4hPfhjnuUCstqciHVu9vVJ7jFqqKi/EuQ==", + "license": "MIT", + "dependencies": { + "@msgpack/msgpack": "^2.8.0", + "eventemitter3": "^4.0.7", + "peerjs-js-binarypack": "^2.1.0", + "webrtc-adapter": "^9.0.0" + }, + "engines": { + "node": ">= 14" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/peer" + } + }, + "node_modules/peerjs-js-binarypack": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/peerjs-js-binarypack/-/peerjs-js-binarypack-2.1.0.tgz", + "integrity": "sha512-YIwCC+pTzp3Bi8jPI9UFKO0t0SLo6xALnHkiNt/iUFmUUZG0fEEmEyFKvjsDKweiFitzHRyhuh6NvyJZ4nNxMg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/peer" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/react": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-19.3.1.tgz", + "integrity": "sha512-QNhWdt7nrgVWWQG58QuGsAR3jv3Hbe4mb4vz7w2csnHavciZJgda1CPNaldF6eVxv90ud3jUXpKlHL69gO38yw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.3.1.tgz", + "integrity": "sha512-Y0bARz53lhEC1NMCASs0/j+faB/zqt3R8hpvg1e8+61ZVJ4UL0UdC4cBbXfZMcIuhrfgeILTLe+ZeBCavQPtKQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.28.0" + }, + "peerDependencies": { + "react": "^19.3.1" + } + }, + "node_modules/react-refresh": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.65.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.65.0.tgz", + "integrity": "sha512-QVCvQUTGDN6JRwi3jVGCY3H4aTHvi9rX0LyoBMkGUvdh/Z6O51CzcLz85BWZaTCDGhl6ySGqYZs+xI1jmPqGcw==", + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.65.0", + "@rollup/rollup-android-arm64": "4.65.0", + "@rollup/rollup-darwin-arm64": "4.65.0", + "@rollup/rollup-darwin-x64": "4.65.0", + "@rollup/rollup-freebsd-arm64": "4.65.0", + "@rollup/rollup-freebsd-x64": "4.65.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.65.0", + "@rollup/rollup-linux-arm-musleabihf": "4.65.0", + "@rollup/rollup-linux-arm64-gnu": "4.65.0", + "@rollup/rollup-linux-arm64-musl": "4.65.0", + "@rollup/rollup-linux-loong64-gnu": "4.65.0", + "@rollup/rollup-linux-loong64-musl": "4.65.0", + "@rollup/rollup-linux-ppc64-gnu": "4.65.0", + "@rollup/rollup-linux-ppc64-musl": "4.65.0", + "@rollup/rollup-linux-riscv64-gnu": "4.65.0", + "@rollup/rollup-linux-riscv64-musl": "4.65.0", + "@rollup/rollup-linux-s390x-gnu": "4.65.0", + "@rollup/rollup-linux-x64-gnu": "4.65.0", + "@rollup/rollup-linux-x64-musl": "4.65.0", + "@rollup/rollup-openbsd-x64": "4.65.0", + "@rollup/rollup-openharmony-arm64": "4.65.0", + "@rollup/rollup-win32-arm64-msvc": "4.65.0", + "@rollup/rollup-win32-ia32-msvc": "4.65.0", + "@rollup/rollup-win32-x64-gnu": "4.65.0", + "@rollup/rollup-win32-x64-msvc": "4.65.0", + "fsevents": "~2.3.2" + } + }, + "node_modules/scheduler": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.28.0.tgz", + "integrity": "sha512-juorfCmIkIw8tT+p5BXSm6PJjQF/ycEYmKyzURCIt/RaZIhL+PulbQ9Yu2z1HdOJDdqDTlxA1+xKBmHXJsczAw==", + "license": "MIT" + }, + "node_modules/sdp": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/sdp/-/sdp-3.2.2.tgz", + "integrity": "sha512-xZocWwfyp4hkbN4hLWxMjmv2Q8aNa9MhmOZ7L9aCZPT+dZsgRr6wZRrSYE3HTdyk/2pZKPSgqI7ns7Een1xMSA==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.3.tgz", + "integrity": "sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz", + "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "node_modules/tsx": { + "version": "4.23.15", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.23.15.tgz", + "integrity": "sha512-Yiex1Ovn8z2xPpOWckIiysV1SSyRMY9BkLF++q0yKiDxCqRhosKfMg3janKkiLBwZ5c/YryloKwGZcrEmtwxKw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.4.tgz", + "integrity": "sha512-t8wonIOdQyOjtPd2VktlBu6QjTVTks6v8bSi7kd0r2Vll8TK1JYDZtRRdbmCHOAMphkZgrzK+L+vXK4pyZ8t4A==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "6.4.5", + "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.5.tgz", + "integrity": "sha512-4enwjFfi4IwtEh0w+jPzpLVOFQiPHmmzPxcHXQOyEur47o6u0jE573DmGpjA+7irXoMLYRZD5ZaFUEneS3abPg==", + "license": "MIT", + "dependencies": { + "esbuild": "^0.25.0", + "fdir": "^6.4.4", + "picomatch": "^4.0.2", + "postcss": "^8.5.3", + "rollup": "^4.34.9", + "tinyglobby": "^0.2.13" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || ^20.0.0 || >=22.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", + "jiti": ">=1.21.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" + } + }, + "node_modules/webrtc-adapter": { + "version": "9.0.6", + "resolved": "https://registry.npmjs.org/webrtc-adapter/-/webrtc-adapter-9.0.6.tgz", + "integrity": "sha512-CHbl2ZQbxx164IgWRgzJno4hWtM4tFbRam1QfI3Yxhs3w/DvqluVxVWeXs3oL5/fbGkSNLKo0Ty5MgUWceNhog==", + "license": "BSD-3-Clause", + "dependencies": { + "sdp": "^3.2.0" + }, + "engines": { + "node": ">=6.0.0", + "npm": ">=3.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "license": "ISC" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..148beb4 --- /dev/null +++ b/package.json @@ -0,0 +1,33 @@ +{ + "name": "gothwad-remotedesktop", + "private": true, + "version": "1.0.0", + "type": "module", + "description": "Gothwad Remote Desktop (remotedesktop.gothwadtech.com) — Zero-D1 Cloudflare Pages Portal with Gothwad Accounts SSO & WebRTC P2P", + "scripts": { + "dev": "vite --port=3000 --host=0.0.0.0", + "build": "vite build", + "preview": "vite preview --port=3000 --host=0.0.0.0", + "lint": "tsc --noEmit", + "check:sizes": "node tools/check-file-sizes.mjs", + "test:unit": "node --import tsx --test tests/*.test.ts", + "test": "npm run check:sizes && npm run lint && npm run test:unit" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.4", + "@vitejs/plugin-react": "^4.3.4", + "lucide-react": "^0.487.0", + "peerjs": "^1.5.5", + "react": "^19.0.0", + "react-dom": "^19.0.0", + "tailwindcss": "^4.1.4" + }, + "devDependencies": { + "@types/node": "^22.14.0", + "@types/react": "^19.0.12", + "@types/react-dom": "^19.0.4", + "tsx": "^4.19.3", + "typescript": "^5.8.3", + "vite": "^6.3.2" + } +} diff --git a/public/_headers b/public/_headers new file mode 100644 index 0000000..d9b577a --- /dev/null +++ b/public/_headers @@ -0,0 +1,2 @@ +/index.html + Cache-Control: public, max-age=0, must-revalidate diff --git a/public/_redirects b/public/_redirects new file mode 100644 index 0000000..7797f7c --- /dev/null +++ b/public/_redirects @@ -0,0 +1 @@ +/* /index.html 200 diff --git a/public/manifest.json b/public/manifest.json new file mode 100644 index 0000000..79c1556 --- /dev/null +++ b/public/manifest.json @@ -0,0 +1,9 @@ +{ + "name": "Gothwad Remote Desktop", + "short_name": "Remote Desktop", + "description": "Control your Windows Host OS and headless Hyper-V background VMs in any browser via Gothwad Accounts.", + "start_url": "/", + "display": "standalone", + "background_color": "#020617", + "theme_color": "#4f46e5" +} diff --git a/scripts/guest-linux-kasm-cloudflare.sh b/scripts/guest-linux-kasm-cloudflare.sh new file mode 100755 index 0000000..86f6a12 --- /dev/null +++ b/scripts/guest-linux-kasm-cloudflare.sh @@ -0,0 +1,84 @@ +#!/usr/bin/env bash +# ============================================================================== +# Ubuntu Guest VM Automated Browser Desktop Setup (KasmVNC Webtop + Cloudflared) +# Run this script INSIDE the isolated Ubuntu 24.04 Guest VM (NOT on Host). +# ============================================================================== + +set -euo pipefail + +WEBTOP_USER="${WEBTOP_USER:-admin}" +WEBTOP_PASS="${WEBTOP_PASS:-}" +CF_TUNNEL_TOKEN="${CF_TUNNEL_TOKEN:-}" + +echo "============================================================" +echo " Guest VM Browser Desktop Installer (Docker + KasmVNC) " +echo "============================================================" + +if [[ -z "$WEBTOP_PASS" ]]; then + read -rsp "Set a strong password for Webtop user '${WEBTOP_USER}': " WEBTOP_PASS + echo "" + if [[ ${#WEBTOP_PASS} -lt 8 ]]; then + echo "Error: Password kam se kam 8 characters ka hona chahiye." + exit 1 + fi +fi + +echo "[1/4] Updating packages & installing Docker..." +sudo apt-get update -y +sudo apt-get install -y ca-certificates curl gnupg lsb-release + +if ! command -v docker >/dev/null 2>&1; then + curl -fsSL https://get.docker.com | sudo sh + sudo systemctl enable --now docker +fi + +echo "[2/4] Creating Webtop (Ubuntu XFCE + KasmVNC) Docker Compose stack..." +WORKDIR="$HOME/webtop" +mkdir -p "$WORKDIR/data" + +cat > "$WORKDIR/docker-compose.yml" </dev/null 2>&1; then + curl -fsSL https://github.com/cloudflare/cloudflared/releases/latest/download/cloudflared-linux-amd64.deb -o /tmp/cloudflared.deb + sudo dpkg -i /tmp/cloudflared.deb + rm -f /tmp/cloudflared.deb + fi + sudo cloudflared service install "$CF_TUNNEL_TOKEN" + sudo systemctl enable --now cloudflared + echo "[+] Cloudflared tunnel service active!" +else + echo "[4/4] Skipping cloudflared install (CF_TUNNEL_TOKEN pass nahi kiya gaya)." + echo " Cloudflare Dashboard -> Tunnels me jaakar Debian service command run karein," + echo " aur Public Hostname ko 'http://localhost:3000' par point karein." +fi + +echo "" +echo "✅ Setup Complete! Container 'remote-webtop' har boot par auto-start hoga." diff --git a/scripts/guest-watchdog.sh b/scripts/guest-watchdog.sh new file mode 100755 index 0000000..e93bbe3 --- /dev/null +++ b/scripts/guest-watchdog.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +# ============================================================================== +# Guest VM Wi-Fi Reconnection & Tunnel Health Watchdog +# Checks internet availability and restarts stalled remote-access services +# inside the Guest VM if they dropped during a host Wi-Fi change. +# ============================================================================== + +set -u + +TIMESTAMP="$(date '+%Y-%m-%d %H:%M:%S')" + +# 1. Check if the host Wi-Fi currently has internet access +if ! curl -fsS --max-time 8 https://clients3.google.com/generate_204 >/dev/null 2>&1; then + echo "[$TIMESTAMP] Internet abhi offline hai (Host Wi-Fi disconnected). Waiting for next cycle..." + exit 0 +fi + +# 2. Internet is UP — ensure installed remote-access services are active + +# Check Cloudflare Tunnel (Option B) +if systemctl list-unit-files | grep -q '^cloudflared\.service'; then + if ! systemctl is-active --quiet cloudflared; then + echo "[$TIMESTAMP] Restarting cloudflared.service..." + sudo systemctl restart cloudflared + fi +fi + +# Check Tailscale (Option C) +if systemctl list-unit-files | grep -q '^tailscaled\.service'; then + if ! systemctl is-active --quiet tailscaled; then + echo "[$TIMESTAMP] Restarting tailscaled.service..." + sudo systemctl restart tailscaled + fi +fi + +# Check Chrome Remote Desktop units (Option A on Linux) +for unit in $(systemctl list-units --type=service --all --no-legend 'chrome-remote-desktop@*' | awk '{print $1}'); do + if ! systemctl is-active --quiet "$unit"; then + echo "[$TIMESTAMP] Restarting $unit..." + sudo systemctl restart "$unit" + fi +done + +# Check Docker Webtop container if present (Option B) +if command -v docker >/dev/null 2>&1; then + if docker ps -a --format '{{.Names}}' | grep -q '^remote-webtop$'; then + if ! docker ps --format '{{.Names}}' | grep -q '^remote-webtop$'; then + echo "[$TIMESTAMP] Starting stopped remote-webtop container..." + docker start remote-webtop + fi + fi +fi diff --git a/scripts/host-setup-hyperv.ps1 b/scripts/host-setup-hyperv.ps1 new file mode 100644 index 0000000..ccd1291 --- /dev/null +++ b/scripts/host-setup-hyperv.ps1 @@ -0,0 +1,133 @@ +<# +.SYNOPSIS + Transparent Hyper-V Headless VM Provisioning Script for Windows 11 Pro (16GB RAM / 512GB SSD Host). + +.DESCRIPTION + Run ONLY with the explicit permission of the PC owner. + - Enables Microsoft Hyper-V if not yet enabled. + - Creates an isolated Generation 2 VM connected to 'Default Switch' (Wi-Fi NAT friendly). + - Caps RAM (Dynamic 1.5GB - 4GB) and CPU (Max 60%) so the host PC stays fast and cool. + - Configures Automatic Start on Boot (30s delay) and Automatic State Save on Shutdown. + +.EXAMPLE + .\host-setup-hyperv.ps1 -VMName "RemoteWorkVM" -GuestType "Linux" -IsoPath "C:\HyperV\ISO\ubuntu-24.04.iso" +#> + +param( + [Parameter(Mandatory = $false)] + [string]$VMName = "RemoteWorkVM", + + [Parameter(Mandatory = $false)] + [ValidateSet("Linux", "Windows")] + [string]$GuestType = "Linux", + + [Parameter(Mandatory = $true)] + [string]$IsoPath, + + [Parameter(Mandatory = $false)] + [string]$BaseFolder = "C:\HyperV", + + [Parameter(Mandatory = $false)] + [int]$DiskSizeGB = 50, + + [Parameter(Mandatory = $false)] + [int]$MaxRamMB = 4096 +) + +$ErrorActionPreference = "Stop" + +Write-Host "============================================================" -ForegroundColor Cyan +Write-Host " Hyper-V Headless Background VM Setup (Transparent Mode) " -ForegroundColor Cyan +Write-Host " IMPORTANT: Run ONLY with the laptop owner's full consent. " -ForegroundColor Yellow +Write-Host "============================================================" -ForegroundColor Cyan + +# 1. Verify Administrator privileges +$currentPrincipal = New-Object Security.Principal.WindowsPrincipal([Security.Principal.WindowsIdentity]::GetCurrent()) +if (-not $currentPrincipal.IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) { + Write-Error "Please run PowerShell as Administrator (Right-click -> Run as Administrator)." + exit 1 +} + +# 2. Check if Hyper-V is enabled +$hypervFeature = Get-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V +if ($hypervFeature.State -ne "Enabled") { + Write-Host "[!] Hyper-V abhi enabled nahi hai. Enabling Microsoft-Hyper-V-All..." -ForegroundColor Yellow + Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Hyper-V -All + Write-Host "[+] Hyper-V enable ho gaya hai! Please laptop ek baar RESTART karein aur yeh script dobara run karein." -ForegroundColor Green + exit 0 +} + +# 3. Validate ISO file path +if (-not (Test-Path -Path $IsoPath)) { + Write-Error "ISO file nahi mili: $IsoPath. Please sahi -IsoPath pass karein." + exit 1 +} + +# 4. Prepare directories +$vmPath = Join-Path $BaseFolder "VMs" +$vhdPath = Join-Path $vmPath "$VMName\$VMName.vhdx" +New-Item -ItemType Directory -Force -Path $vmPath | Out-Null + +if (Get-VM -Name $VMName -ErrorAction SilentlyContinue) { + Write-Error "VM '$VMName' pehle se exist karta hai! Dusra -VMName dein ya purana VM check karein." + exit 1 +} + +Write-Host "[1/6] Creating Generation 2 VM '$VMName' on 'Default Switch'..." -ForegroundColor Green +$startupBytes = if ($GuestType -eq "Linux") { 2560MB } else { 3584MB } +$minBytes = 1536MB +$maxBytes = ($MaxRamMB * 1MB) +$diskBytes = ([int64]$DiskSizeGB * 1GB) + +New-VM -Name $VMName ` + -MemoryStartupBytes $startupBytes ` + -Generation 2 ` + -NewVHDPath $vhdPath ` + -NewVHDSizeBytes $diskBytes ` + -Path $vmPath ` + -SwitchName "Default Switch" | Out-Null + +Write-Host "[2/6] Setting Dynamic Memory Limits (Min: 1.5GB | Max: $($MaxRamMB)MB)..." -ForegroundColor Green +Set-VMMemory -VMName $VMName ` + -DynamicMemoryEnabled $true ` + -MinimumBytes $minBytes ` + -StartupBytes $startupBytes ` + -MaximumBytes $maxBytes + +Write-Host "[3/6] Setting CPU Cap (2 vCPUs, Max 60% Host Load so laptop stays cool)..." -ForegroundColor Green +Set-VMProcessor -VMName $VMName -Count 2 -Maximum 60 + +Write-Host "[4/6] Attaching Installer ISO & Configuring Secure Boot for $GuestType..." -ForegroundColor Green +Add-VMDvdDrive -VMName $VMName -Path $IsoPath | Out-Null +$dvd = Get-VMDvdDrive -VMName $VMName + +if ($GuestType -eq "Linux") { + Set-VMFirmware -VMName $VMName ` + -EnableSecureBoot On ` + -SecureBootTemplate "MicrosoftUEFICertificateAuthority" ` + -FirstBootDevice $dvd +} else { + Set-VMFirmware -VMName $VMName ` + -EnableSecureBoot On ` + -SecureBootTemplate "MicrosoftWindows" ` + -FirstBootDevice $dvd + # Enable vTPM for Windows 11 compatibility + Set-VMKeyProtector -VMName $VMName -NewLocalKeyProtector + Enable-VMTPM -VMName $VMName +} + +Write-Host "[5/6] Setting 1080p Virtual Display & Headless Auto-Start/Save Lifecycle..." -ForegroundColor Green +Set-VMVideo -VMName $VMName -HorizontalResolution 1920 -VerticalResolution 1080 -ResolutionType Single + +Set-VM -Name $VMName ` + -AutomaticStartAction Start ` + -AutomaticStartDelay 30 ` + -AutomaticStopAction Save ` + -CheckpointType Disabled + +Write-Host "[6/6] VM '$VMName' successfully created!" -ForegroundColor Cyan +Write-Host "" +Write-Host "Next Steps:" -ForegroundColor Yellow +Write-Host " 1. Open 'Hyper-V Manager' -> Double-click '$VMName' -> Click 'Start' to install $GuestType." +Write-Host " 2. Inside the VM, setup Browser Access (Option A: Chrome Remote Desktop OR Option B: KasmVNC + Cloudflare)." +Write-Host " 3. Close the Hyper-V window — '$VMName' will keep running headlessly in the background and auto-start on every boot!" diff --git a/scripts/install-gothwad-agent.ps1 b/scripts/install-gothwad-agent.ps1 new file mode 100644 index 0000000..e50900b --- /dev/null +++ b/scripts/install-gothwad-agent.ps1 @@ -0,0 +1,79 @@ +<# +.SYNOPSIS + One-Time Transparent Installer for Gothwad Remote Desktop Agent (Zero-D1 WebRTC P2P). + +.DESCRIPTION + - Requires explicit confirmation from the PC owner. + - Downloads GothwadRemoteAgent-win64.exe from GothwadTech/remotedesktop GitHub Releases. + - Links this PC (Main Windows OS + Hyper-V VM) to your @gothwadtech.com P2P slot. + - Registers a transparent startup task ('GothwadRemoteAgent') so it reconnects on boot. +#> + +param( + [Parameter(Mandatory = $true)] + [string]$GothwadUser, + + [Parameter(Mandatory = $false)] + [int]$Slot = 1, + + [Parameter(Mandatory = $false)] + [string]$DeviceName = $env:COMPUTERNAME, + + [Parameter(Mandatory = $false)] + [string]$VMName = "RemoteWorkVM", + + [Parameter(Mandatory = $false)] + [string]$ReleaseExeUrl = "https://github.com/GothwadTech/remotedesktop/releases/latest/download/GothwadRemoteAgent-win64.exe" +) + +$ErrorActionPreference = "Stop" + +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host " Gothwad Remote Desktop — Zero-D1 WebRTC P2P Setup " -ForegroundColor Cyan +Write-Host " Portal: https://remotedesktop.gothwadtech.com " -ForegroundColor Cyan +Write-Host "================================================================" -ForegroundColor Cyan +Write-Host "IMPORTANT: Run ONLY with the explicit permission of this PC's owner." -ForegroundColor Yellow + +$consentInput = Read-Host "Does the PC owner consent to linking this PC with Gothwad Remote Desktop? Type YES to continue" +if ($consentInput -ne "YES") { + Write-Warning "Setup cancelled (Owner consent not confirmed)." + exit 1 +} + +$pinPlain = Read-Host "Create a 6 to 12 digit Remote Access PIN (numeric)" +if ($pinPlain -notmatch '^\d{6,12}$') { + Write-Error "PIN must be 6 to 12 digits." + exit 1 +} + +$installDir = "C:\Program Files\GothwadRemote" +New-Item -ItemType Directory -Force -Path $installDir | Out-Null +$exePath = Join-Path $installDir "GothwadRemoteAgent.exe" + +Write-Host "[1/3] Downloading GothwadRemoteAgent from GitHub Releases..." -ForegroundColor Green +try { + Invoke-WebRequest -Uri $ReleaseExeUrl -OutFile $exePath +} catch { + $fallbackUrl = "https://github.com/GothwadTech/vm/releases/latest/download/GothwadRemoteAgent-win64.exe" + Invoke-WebRequest -Uri $fallbackUrl -OutFile $exePath +} + +Write-Host "[2/3] Configuring Zero-D1 P2P Slot $Slot for '$GothwadUser@gothwadtech.com'..." -ForegroundColor Green +& $exePath --pair --gothwad-user $GothwadUser --slot $Slot --device-name $DeviceName --vm-name $VMName --pin $pinPlain --confirm-owner-consent + +Write-Host "[3/3] Registering startup Scheduled Task 'GothwadRemoteAgent'..." -ForegroundColor Green +$action = New-ScheduledTaskAction -Execute $exePath -Argument "--run" +$trigger = New-ScheduledTaskTrigger -AtStartup +$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -RestartCount 5 -RestartInterval (New-TimeSpan -Minutes 2) + +Register-ScheduledTask -TaskName "GothwadRemoteAgent" ` + -Description "Gothwad Remote Desktop & Hyper-V P2P Agent (https://remotedesktop.gothwadtech.com)" ` + -Action $action ` + -Trigger $trigger ` + -Settings $settings ` + -RunLevel Highest ` + -Force | Out-Null + +Start-ScheduledTask -TaskName "GothwadRemoteAgent" +Write-Host "" +Write-Host "Done! Open https://remotedesktop.gothwadtech.com in your browser to control Main OS or Hyper-V VM '$VMName'." -ForegroundColor Cyan diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..ab78166 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,382 @@ +import { useCallback, useEffect, useState } from 'react'; +import { + AlertTriangle, + ArrowRight, + Laptop, + LogIn, + Plus, + Server, + ShieldCheck, + Terminal, +} from 'lucide-react'; +import type { ActiveConnection, RemoteDevice, SessionPayload, TargetMode } from './types'; +import { AuthRequiredError, resolveGothwadSession } from './lib/gothwadAuth'; +import { startGothwadOAuthPkce } from './lib/oauthPkce'; +import { + connectP2PDevice, + discoverDevicesForUser, + removeBookmarkedPeer, + triggerVmPowerOnHost, +} from './lib/peerRegistry'; +import { Header, type NavTab } from './components/Header'; +import { DeviceCard } from './components/DeviceCard'; +import { PairDeviceModal } from './components/PairDeviceModal'; +import { ShareAccessModal } from './components/ShareAccessModal'; +import { PinPromptModal } from './components/PinPromptModal'; +import { RemoteViewer } from './components/RemoteViewer'; +import { SetupWizard } from './components/SetupWizard'; + +function readInitialAuthuser(): number { + const pathMatch = window.location.pathname.match(/^\/u\/([0-5])(?:\b|\/)/); + if (pathMatch) return Number(pathMatch[1]); + const q = new URLSearchParams(window.location.search).get('authuser'); + const n = Number(q); + return Number.isInteger(n) && n >= 0 && n <= 5 ? n : 0; +} + +function readInitialTab(): NavTab { + return window.location.pathname.endsWith('/setup') ? 'setup' : 'access'; +} + +export function App() { + const [authuser, setAuthuser] = useState(readInitialAuthuser); + const [activeTab, setActiveTab] = useState(readInitialTab); + const [session, setSession] = useState(null); + const [devices, setDevices] = useState([]); + const [loading, setLoading] = useState(true); + const [unauthSigninUrl, setUnauthSigninUrl] = useState(null); + const [serviceError, setServiceError] = useState(null); + + const [showPairModal, setShowPairModal] = useState(false); + const [shareDevice, setShareDevice] = useState(null); + const [pinTarget, setPinTarget] = useState<{ device: RemoteDevice; mode: TargetMode } | null>( + null, + ); + const [pinError, setPinError] = useState(null); + const [connecting, setConnecting] = useState(false); + const [lastVerifiedPin, setLastVerifiedPin] = useState(''); + const [activeConnection, setActiveConnection] = useState(null); + const [busyAction, setBusyAction] = useState(null); + const [toast, setToast] = useState(null); + + const showToast = (msg: string) => { + setToast(msg); + setTimeout(() => setToast(null), 3500); + }; + + const handleChangeTab = (tab: NavTab) => { + setActiveTab(tab); + const url = new URL(window.location.href); + url.pathname = tab === 'setup' ? '/setup' : '/'; + window.history.replaceState({}, '', url.toString()); + }; + + const loadAll = useCallback(async (slotIndex: number) => { + setLoading(true); + setServiceError(null); + setUnauthSigninUrl(null); + try { + const sess = await resolveGothwadSession(slotIndex); + setSession(sess); + const list = await discoverDevicesForUser(sess.user.username); + setDevices(list); + } catch (err) { + if (err instanceof AuthRequiredError) { + setSession(null); + setUnauthSigninUrl(err.signinUrl); + } else { + setServiceError(err instanceof Error ? err.message : 'Service temporarily unavailable'); + } + } finally { + setLoading(false); + } + }, []); + + useEffect(() => { + void loadAll(authuser); + }, [authuser, loadAll]); + + const handleSwitchAccount = (nextIndex: number) => { + const url = new URL(window.location.href); + url.searchParams.set('authuser', String(nextIndex)); + window.history.replaceState({}, '', url.toString()); + setActiveConnection(null); + setAuthuser(nextIndex); + }; + + const handlePinSubmit = async (pin: string) => { + if (!pinTarget || !session) return; + setConnecting(true); + setPinError(null); + try { + const conn = await connectP2PDevice( + pinTarget.device, + pinTarget.mode, + pin, + session.user.username, + ); + setLastVerifiedPin(pin); + setPinTarget(null); + setActiveConnection(conn); + } catch (err) { + setPinError(err instanceof Error ? err.message : 'Failed to connect'); + } finally { + setConnecting(false); + } + }; + + const handleHotSwitchMode = async (nextMode: TargetMode) => { + if (!activeConnection || !session || activeConnection.targetMode === nextMode) return; + try { + activeConnection.p2pHandle?.close(); + const conn = await connectP2PDevice( + activeConnection.device, + nextMode, + lastVerifiedPin, + session.user.username, + ); + setActiveConnection(conn); + } catch (err) { + showToast(err instanceof Error ? err.message : 'Cannot switch target mode'); + } + }; + + const handleVmPower = async ( + device: RemoteDevice, + action: 'start_vm' | 'save_vm' | 'restart_vm', + ) => { + setBusyAction(`${device.id}:${action}`); + try { + const vmState = await triggerVmPowerOnHost(device, action); + showToast(`Sent '${action}' to Hyper-V (${device.vmName}) -> ${vmState}`); + await loadAll(authuser); + } catch (err) { + showToast(err instanceof Error ? err.message : 'Power action failed'); + } finally { + setBusyAction(null); + } + }; + + const handleUnlink = (device: RemoteDevice) => { + if (device.isDemo) { + showToast('Preview Demo PC resets automatically; real P2P slots unlink via Host CLI.'); + return; + } + if (!window.confirm(`Remove '${device.name}' bookmark from this browser?`)) return; + removeBookmarkedPeer(device.peerId); + void loadAll(authuser); + }; + + return ( +
+
void loadAll(authuser)} + onOpenPairModal={() => setShowPairModal(true)} + onSwitchAccount={handleSwitchAccount} + /> + +
+ {unauthSigninUrl && ( +
+
+ +
+

Sign in with Gothwad Account

+

+ 100% Zero-Database Cloudflare Pages Portal. Uses your{' '} + @gothwadtech.com SSO cookie + WebRTC P2P HMAC-SHA256 PIN verification. +

+
+ + + Continue with Gothwad SSO (.gothwadtech.com) + + +
+
+ )} + + {serviceError && ( +
+ +

Temporary Connection Issue

+

{serviceError}

+ +
+ )} + + {session && !serviceError && activeTab === 'setup' && ( + { + handleChangeTab('access'); + void loadAll(authuser); + }} + /> + )} + + {session && !serviceError && activeTab === 'access' && ( + <> +
+
+ +
+
+ Option 1: Main Windows 11 OS Control +
+

+ Chrome Remote Desktop jaisa full system access. Host screen par transparent + sharing banner aur Stop button rehta hai. +

+
+
+
+ +
+
+ Option 2: Headless Hyper-V Background VM +
+

+ Dost apna laptop normal use karta rahe — aap background isolated VM me kaam + karo, dost ki screen/mouse bilkul touch nahi hogi! +

+
+
+
+ + {devices.length === 0 ? ( +
+ +

+ No Online Windows PCs Detected Yet +

+

+ Open the Windows Setup Wizard for step-by-step PowerShell commands to link your + first PC via WebRTC P2P (Zero D1 Database!). +

+ +
+ ) : ( +
+ {devices.map((dev) => ( + { + setPinError(null); + setPinTarget({ device: d, mode }); + }} + onVmPower={handleVmPower} + onOpenShares={(d) => setShareDevice(d)} + onUnlink={handleUnlink} + /> + ))} +
+ )} + +
+
+
+ +
+
+

+ Set up another Windows computer via PowerShell +

+

+ Step-by-step Hyper-V VM builder, Gothwad Account P2P pairing command, aur{' '} + .exe installer seedha yahin se copy karein. +

+
+
+ +
+ + )} +
+ + {showPairModal && session && ( + { + setShowPairModal(false); + handleChangeTab('setup'); + }} + onClose={() => { + setShowPairModal(false); + void loadAll(authuser); + }} + /> + )} + + {shareDevice && ( + setShareDevice(null)} /> + )} + + {pinTarget && ( + setPinTarget(null)} + /> + )} + + {activeConnection && session && ( + { + activeConnection.p2pHandle?.close(); + setActiveConnection(null); + }} + /> + )} + + {toast && ( +
+ {toast} +
+ )} +
+ ); +} diff --git a/src/components/CopyCodeBlock.tsx b/src/components/CopyCodeBlock.tsx new file mode 100644 index 0000000..bc60f24 --- /dev/null +++ b/src/components/CopyCodeBlock.tsx @@ -0,0 +1,39 @@ +import { useState } from 'react'; +import { Check, Copy, Terminal } from 'lucide-react'; + +interface CopyCodeBlockProps { + label: string; + command: string; +} + +export function CopyCodeBlock({ label, command }: CopyCodeBlockProps) { + const [copied, setCopied] = useState(false); + + const handleCopy = async () => { + await navigator.clipboard.writeText(command); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + return ( +
+
+ + + {label} + + +
+
+        {command}
+      
+
+ ); +} diff --git a/src/components/DeviceCard.tsx b/src/components/DeviceCard.tsx new file mode 100644 index 0000000..d91486d --- /dev/null +++ b/src/components/DeviceCard.tsx @@ -0,0 +1,239 @@ +import { + Cpu, + HardDrive, + Laptop, + Lock, + Play, + RotateCcw, + Save, + Server, + Share2, + Trash2, +} from 'lucide-react'; +import type { RemoteDevice, TargetMode } from '../types'; + +interface DeviceCardProps { + device: RemoteDevice; + busyAction: string | null; + onConnect: (device: RemoteDevice, mode: TargetMode) => void; + onVmPower: (device: RemoteDevice, action: 'start_vm' | 'save_vm' | 'restart_vm') => void; + onOpenShares: (device: RemoteDevice) => void; + onUnlink: (device: RemoteDevice) => void; +} + +export function DeviceCard({ + device, + busyAction, + onConnect, + onVmPower, + onOpenShares, + onUnlink, +}: DeviceCardProps) { + const ramPct = Math.min( + 100, + Math.round((device.hostRamUsedMb / Math.max(1024, device.hostRamTotalMb)) * 100), + ); + const ramUsedGb = (device.hostRamUsedMb / 1024).toFixed(1); + const ramTotalGb = (device.hostRamTotalMb / 1024).toFixed(0); + + return ( +
+
+
+
+ +
+
+
+

{device.name}

+ + + {device.online ? 'Online via Wi-Fi' : 'Offline'} + + + {device.role === 'owner' + ? `Owner: ${device.ownerUsername}` + : `Shared (${device.accessScope === 'host_and_vm' ? 'Host + VM' : 'VM Only'})`} + +
+

{device.osInfo}

+
+
+ + {device.role === 'owner' && ( +
+ + +
+ )} +
+ + {/* Host Telemetry Bar */} +
+
+ +
+
+ Host RAM + + {ramUsedGb} / {ramTotalGb} GB ({ramPct}%) + +
+
+
+
+
+
+ +
+ +
+ Host CPU: + {device.hostCpuPercent}% + (VM cap 60%) +
+
+ +
+ Hyper-V `{device.vmName}`: + + {device.vmState} + +
+
+ + {/* Dual-Mode Remote Launch Cards */} +
+ {/* Mode 1: Main Windows 11 Host OS */} +
+
+
+ + + Mode 1: Main Windows 11 OS + + + Full System + +
+

+ Chrome Remote Desktop jaisa direct Host screen, mouse & files control (Host par + transparent sharing banner dikhega). +

+
+ +
+ {device.canControlHostOs ? ( + + ) : ( +
+ + Restricted to Owner (`vm_only` share) +
+ )} +
+
+ + {/* Mode 2: Isolated Background Hyper-V VM */} +
+
+
+ + + Mode 2: Background Hyper-V VM + + + Headless Isolated + +
+

+ Dost ki screen/mouse bilkul disturb nahi hogi. 100% isolated background VM (`Max 4GB` + RAM, auto-boot). +

+
+ +
+ + +
+ + + +
+
+
+
+
+ ); +} diff --git a/src/components/Header.tsx b/src/components/Header.tsx new file mode 100644 index 0000000..0506577 --- /dev/null +++ b/src/components/Header.tsx @@ -0,0 +1,207 @@ +import { useState } from 'react'; +import { + ChevronDown, + ExternalLink, + Monitor, + Plus, + RefreshCw, + ShieldCheck, + Terminal, +} from 'lucide-react'; +import type { SessionPayload } from '../types'; + +export type NavTab = 'access' | 'setup'; + +interface HeaderProps { + session: SessionPayload | null; + authuser: number; + activeTab: NavTab; + loading: boolean; + onChangeTab: (tab: NavTab) => void; + onRefresh: () => void; + onOpenPairModal: () => void; + onSwitchAccount: (index: number) => void; +} + +export function Header({ + session, + authuser, + activeTab, + loading, + onChangeTab, + onRefresh, + onOpenPairModal, + onSwitchAccount, +}: HeaderProps) { + const [menuOpen, setMenuOpen] = useState(false); + + const initials = (session?.user.name || session?.user.username || 'G') + .split(/\s+/) + .map((p) => p[0]?.toUpperCase() || '') + .slice(0, 2) + .join(''); + + return ( +
+
+
+ + + {/* Google-Remote-Desktop Style Navigation Tabs */} + {session && ( + + )} +
+ +
+ + + + + {session && ( +
+ + + {menuOpen && ( +
+
+
+ {session.user.name || session.user.username} +
+
{session.user.email}
+ {session.source === 'dev' && ( + + Preview / Local Dev Session + + )} +
+ + {session.accounts.length > 0 && ( +
+
+ Switch Gothwad Account (u/0..5) +
+ {session.accounts.map((acc) => ( + + ))} +
+ )} + + +
+ )} +
+ )} +
+
+
+ ); +} diff --git a/src/components/HeadlessPairStep.tsx b/src/components/HeadlessPairStep.tsx new file mode 100644 index 0000000..aa52f4b --- /dev/null +++ b/src/components/HeadlessPairStep.tsx @@ -0,0 +1,148 @@ +import { useState } from 'react'; +import { Download, KeyRound, ShieldAlert } from 'lucide-react'; +import { buildHostPeerId } from '../lib/protocol'; +import { CopyCodeBlock } from './CopyCodeBlock'; + +interface HeadlessPairStepProps { + username: string; + userEmail: string; + vmName: string; + stepNumber: number; +} + +const RELEASE_EXE_URL = + 'https://github.com/GothwadTech/remotedesktop/releases/latest/download/GothwadRemoteAgent-win64.exe'; +const RELEASE_ZIP_URL = + 'https://github.com/GothwadTech/remotedesktop/releases/latest/download/GothwadRemoteSetup-win64.zip'; + +export function HeadlessPairStep({ + username, + userEmail, + vmName, + stepNumber, +}: HeadlessPairStepProps) { + const [slot, setSlot] = useState(1); + const [deviceLabel, setDeviceLabel] = useState('Dost-Win11Pro-Laptop'); + + const peerId = buildHostPeerId(username, slot); + + const oneLinePs = `irm https://raw.githubusercontent.com/GothwadTech/remotedesktop/main/scripts/install-gothwad-agent.ps1 -OutFile setup.ps1; .\\setup.ps1 -GothwadUser "${username}" -Slot ${slot} -DeviceName "${deviceLabel}" -VMName "${vmName}"`; + + const manualExeCmd = `.\\GothwadRemoteAgent-win64.exe --pair --gothwad-user "${username}" --slot ${slot} --device-name "${deviceLabel}" --vm-name "${vmName}" --pin 123456 --confirm-owner-consent`; + + return ( +
+
+
+ + {stepNumber} + +

+ Link Windows PC to Your Gothwad Account (Zero-Database WebRTC P2P) +

+
+ + Peer ID: {peerId} + +
+ +

+ Koi cloud database ki zaroorat nahi! Windows Agent seedha aapke Gothwad Account ( + {userEmail}) ke P2P slot ({peerId}) par encrypted WebRTC se + connect hota hai aur HMAC-SHA256 6-digit PIN verify karta hai. +

+ +
+ + + Consent Rule: Yeh command sirf PC owner ki permission se Admin PowerShell + me run karein. Script aapse YES consent aur aapka 6-digit PIN{' '} + set karne ko kahega (PIN sirf us PC par encrypted rehta hai). + +
+ +
+
+ + setDeviceLabel(e.target.value)} + placeholder="e.g. Dost-Win11Pro-Laptop" + className="mt-1 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2 text-xs text-white focus:border-indigo-500 focus:outline-none" + /> +
+ +
+ + +
+
+ +
+
+
+ + Method A (Recommended): 1-Command Auto Download + Install + Startup Service +
+

+ Windows par Win + X → Terminal (Admin) kholkar yeh + single command paste karein: +

+ +
+ +
+
+
+
+ Method B: Manual EXE / ZIP Download (GitHub Releases) +
+

+ Standalone .exe download karke manually pair karne ke liye: +

+
+ +
+ +
+
+
+ ); +} diff --git a/src/components/PairDeviceModal.tsx b/src/components/PairDeviceModal.tsx new file mode 100644 index 0000000..b239f77 --- /dev/null +++ b/src/components/PairDeviceModal.tsx @@ -0,0 +1,132 @@ +import { useState } from 'react'; +import { ArrowRight, KeyRound, ShieldAlert, Users, X } from 'lucide-react'; +import { addBookmarkedPeer } from '../lib/peerRegistry'; +import { CopyCodeBlock } from './CopyCodeBlock'; + +interface PairDeviceModalProps { + username: string; + onOpenFullGuide: () => void; + onClose: () => void; +} + +export function PairDeviceModal({ username, onOpenFullGuide, onClose }: PairDeviceModalProps) { + const [slot, setSlot] = useState(1); + const [friendUsername, setFriendUsername] = useState(''); + const [friendSlot, setFriendSlot] = useState(1); + const [addedPeer, setAddedPeer] = useState(null); + + const psCommand = `irm https://raw.githubusercontent.com/GothwadTech/remotedesktop/main/scripts/install-gothwad-agent.ps1 -OutFile setup.ps1; .\\setup.ps1 -GothwadUser "${username}" -Slot ${slot}`; + + const handleBookmarkFriend = () => { + if (!friendUsername.trim()) return; + const peerId = addBookmarkedPeer(friendUsername, friendSlot); + setAddedPeer(peerId); + setFriendUsername(''); + }; + + return ( +
+
+
+
+ +

+ Add / Connect Windows PC (Zero-D1 P2P) +

+
+ +
+ +
+ + + Explicit Owner Consent Required: Kisi bhi PC par setup sirf owner ki + permission ke saath Admin PowerShell me karein. + +
+ +
+
+
+ + +
+ +
+ +
+
+ + 2. Ya kisi dost ka Shared PC connect karein: +
+

+ Agar kisi dusre Gothwad user ne aapko apne PC/VM par allow kiya hai, toh unka username + daalein: +

+
+ setFriendUsername(e.target.value)} + placeholder="Friend's Gothwad username" + className="flex-1 rounded-lg border border-slate-700 bg-slate-950 px-3 py-1.5 text-xs text-white" + /> + + +
+ {addedPeer && ( +

+ Added {addedPeer} to discovery list! +

+ )} +
+
+ +
+ +
+
+
+ ); +} diff --git a/src/components/PinPromptModal.tsx b/src/components/PinPromptModal.tsx new file mode 100644 index 0000000..70f0f42 --- /dev/null +++ b/src/components/PinPromptModal.tsx @@ -0,0 +1,105 @@ +import { useState, type FormEvent } from 'react'; +import { KeyRound, Laptop, Server, X } from 'lucide-react'; +import type { RemoteDevice, TargetMode } from '../types'; + +interface PinPromptModalProps { + device: RemoteDevice; + targetMode: TargetMode; + error: string | null; + connecting: boolean; + onSubmit: (pin: string) => void; + onClose: () => void; +} + +export function PinPromptModal({ + device, + targetMode, + error, + connecting, + onSubmit, + onClose, +}: PinPromptModalProps) { + const [pin, setPin] = useState(device.id === 'dev_demo_win11' ? '123456' : ''); + + const handleSubmit = (e: FormEvent) => { + e.preventDefault(); + if (pin.trim()) onSubmit(pin.trim()); + }; + + return ( +
+
+
+
+ {targetMode === 'host_os' ? ( + + ) : ( + + )} +

+ {targetMode === 'host_os' + ? 'Connect to Main Windows 11 OS' + : `Connect to Hyper-V VM (${device.vmName})`} +

+
+ +
+ +

+ {targetMode === 'host_os' + ? 'Direct Host OS mode: Host screen par Chrome Remote Desktop jaisa "Shared with your Gothwad Account" banner dikhega.' + : 'Headless Background VM mode: Dost ka desktop/mouse bilkul untouched rahega; aap seedha isolated Hyper-V VM me connect hoge.'} +

+ +
+ + setPin(e.target.value)} + placeholder="••••••" + className="mt-1.5 w-full rounded-lg border border-slate-700 bg-slate-950 px-3 py-2.5 text-center font-mono text-lg tracking-[0.4em] text-white focus:border-indigo-500 focus:outline-none" + /> +
+ + {error &&

{error}

} + +
+ + +
+
+
+ ); +} diff --git a/src/components/RemoteViewer.tsx b/src/components/RemoteViewer.tsx new file mode 100644 index 0000000..3ab442b --- /dev/null +++ b/src/components/RemoteViewer.tsx @@ -0,0 +1,264 @@ +import { useEffect, useRef, useState, type MouseEvent } from 'react'; +import { + Clipboard, + Command, + Laptop, + Maximize2, + PhoneOff, + Server, + ShieldCheck, + Wifi, +} from 'lucide-react'; +import type { ActiveConnection, TargetMode } from '../types'; + +interface RemoteViewerProps { + connection: ActiveConnection; + viewerEmail: string; + onSwitchMode: (mode: TargetMode) => void; + onDisconnect: () => void; +} + +export function RemoteViewer({ + connection, + viewerEmail, + onSwitchMode, + onDisconnect, +}: RemoteViewerProps) { + const containerRef = useRef(null); + const [liveFrameUrl, setLiveFrameUrl] = useState(null); + const [cursorPos, setCursorPos] = useState({ x: 48, y: 52 }); + const [clipboardText, setClipboardText] = useState(''); + const [showClipboard, setShowClipboard] = useState(false); + const [lastEventLog, setLastEventLog] = useState('Zero-D1 WebRTC P2P DTLS DataChannel ready'); + + useEffect(() => { + setLiveFrameUrl(null); + if (!connection.p2pHandle) return; + connection.p2pHandle.onFrame((jpegDataUrl) => { + setLiveFrameUrl(jpegDataUrl); + }); + return () => { + connection.p2pHandle?.close(); + }; + }, [connection]); + + const sendInputPacket = (payload: Record) => { + connection.p2pHandle?.sendInput(payload); + }; + + const handleCanvasMove = (e: MouseEvent) => { + const rect = e.currentTarget.getBoundingClientRect(); + const xPct = Math.round(((e.clientX - rect.left) / rect.width) * 100); + const yPct = Math.round(((e.clientY - rect.top) / rect.height) * 100); + setCursorPos({ x: xPct, y: yPct }); + sendInputPacket({ type: 'MOUSE_MOVE', xPct, yPct }); + }; + + const handleSendCtrlAltDel = () => { + sendInputPacket({ type: 'HOTKEY', combo: 'ctrl+alt+del' }); + setLastEventLog('Sent Ctrl + Alt + Del over WebRTC P2P'); + }; + + const handlePushClipboard = () => { + sendInputPacket({ type: 'CLIPBOARD', text: clipboardText }); + setLastEventLog(`Synced clipboard (${clipboardText.length} chars)`); + setShowClipboard(false); + }; + + const toggleFullscreen = () => { + if (!document.fullscreenElement) { + void containerRef.current?.requestFullscreen(); + } else { + void document.exitFullscreen(); + } + }; + + const isHostMode = connection.targetMode === 'host_os'; + + return ( +
+
+
+ + + {connection.device.name} + + +
+ + +
+
+ +
+ + + + +
+
+ + {showClipboard && ( +
+
Sync Clipboard to Remote
+