From 460c3930066e652b2442f7a667b60a72b2875f28 Mon Sep 17 00:00:00 2001 From: weishu Date: Wed, 31 Dec 2025 20:41:02 +0800 Subject: [PATCH] docs: add documentation site with VitePress setup - Initialize VitePress documentation site with config, index, and guides - Add guides for quick-start, installation, PWA, how-it-works, FAQ, and why HAPI - Update .gitignore to exclude VitePress cache directory - Update logo.svg with actual icon from web/public/icon.svg - Simplify README.md with link to full installation guide - Remove redundant WHY_NOT_HAPPY.md (content migrated to why-hapi guide) --- .gitignore | 1 + README.md | 154 +++------------- docs/.vitepress/config.ts | 47 +++++ docs/WHY_NOT_HAPPY.md | 271 ---------------------------- docs/bun.lock | 353 +++++++++++++++++++++++++++++++++++++ docs/guide/faq.md | 177 +++++++++++++++++++ docs/guide/how-it-works.md | 166 +++++++++++++++++ docs/guide/installation.md | 219 +++++++++++++++++++++++ docs/guide/pwa.md | 187 ++++++++++++++++++++ docs/guide/quick-start.md | 55 ++++++ docs/guide/why-hapi.md | 192 ++++++++++++++++++++ docs/index.md | 14 ++ docs/package.json | 17 ++ docs/public/favicon.ico | Bin 0 -> 4085 bytes docs/public/logo.svg | 1 + 15 files changed, 1452 insertions(+), 402 deletions(-) create mode 100644 docs/.vitepress/config.ts delete mode 100644 docs/WHY_NOT_HAPPY.md create mode 100644 docs/bun.lock create mode 100644 docs/guide/faq.md create mode 100644 docs/guide/how-it-works.md create mode 100644 docs/guide/installation.md create mode 100644 docs/guide/pwa.md create mode 100644 docs/guide/quick-start.md create mode 100644 docs/guide/why-hapi.md create mode 100644 docs/index.md create mode 100644 docs/package.json create mode 100644 docs/public/favicon.ico create mode 100644 docs/public/logo.svg diff --git a/.gitignore b/.gitignore index 65edfe50..55a92a1d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ # Build output **/dist/ +**/.vitepress/cache/ **/dist-exe/ **/*.tsbuildinfo server/src/web/embeddedAssets.generated.ts diff --git a/README.md b/README.md index f4edd93c..19e6363b 100644 --- a/README.md +++ b/README.md @@ -17,152 +17,50 @@ Run Claude Code / Codex / Gemini sessions locally and control them remotely thro ## Installation -### Homebrew (macOS/Linux) +```bash +npm install -g @twsxtd/hapi +``` + +Or with Homebrew: ```bash brew install tiann/tap/hapi ``` -### npm/npx +Other options: [Installation guide](docs/guide/installation.md) + +## Quickstart + +1. Start the server: ```bash -npx @twsxtd/hapi +hapi server ``` -Or install globally: +2. Start a coding session: ```bash -npm install -g @twsxtd/hapi +hapi ``` -### Prebuilt binary +3. Open the UI at `http://localhost:3006` and log in with the token in `~/.hapi/settings.json`. -Download from [Releases](https://github.com/tiann/hapi/releases). - -**macOS users**: Remove the quarantine attribute before running: - -```bash -xattr -d com.apple.quarantine ./hapi -``` - -### Docker - -Pull the pre-built image from GitHub Container Registry: - -```bash -docker pull ghcr.io/tiann/hapi-server:latest -``` - -Run the server: +## Docker (server only) ```bash docker run -d --name hapi -p 3006:3006 -v hapi-data:/data ghcr.io/tiann/hapi-server:latest ``` -
-More options (Telegram + environment variables) +More setup options: [Installation guide](docs/guide/installation.md) -#### Environment Variables +## Docs -| Variable | Default | Description | -|----------|---------|-------------| -| `WEBAPP_PORT` | `3006` | Server listening port | -| `CLI_API_TOKEN` | (auto-generated) | Access token for CLI and web UI | -| `TELEGRAM_BOT_TOKEN` | - | Telegram bot token (optional) | -| `WEBAPP_URL` | - | Public URL for Telegram Mini App | - -#### With Telegram Support - -```bash -docker run -d \ - --name hapi \ - -p 3006:3006 \ - -v hapi-data:/data \ - -e WEBAPP_URL="https://your-domain.example" \ - -e TELEGRAM_BOT_TOKEN="your-bot-token" \ - ghcr.io/tiann/hapi-server:latest -``` - -#### Build from Source - -```bash -docker build -t hapi-server -f server/Dockerfile . -``` - -
- -## Quickstart - -1. Start the server on a machine you control: - -```bash -hapi server -# or: npx @twsxtd/hapi server -``` - -2. If the server has no public IP, expose it over HTTPS: - - Cloudflare Tunnel: https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/ - - Tailscale: https://tailscale.com/kb/ - -3. Run the CLI on the machine where you want sessions: - -```bash -# If the server is not on localhost:3006 -export HAPI_SERVER_URL="https://your-domain.example" - -hapi -# or: npx @twsxtd/hapi -``` - -4. Open the UI in a browser at the server URL and log in with `CLI_API_TOKEN`. - -### Finding Your Access Token - -On first run, an Access Token is automatically generated and saved to `~/.hapi/settings.json`. - -View your token: - -```bash -cat ~/.hapi/settings.json | grep cliApiToken -``` - -Or set your own via environment variable (takes priority over generated token): - -```bash -export CLI_API_TOKEN="your-secret-token" -``` - -## Telegram Mini App (optional) - -To use Telegram for notifications and the Mini App: - -1. Create a bot with @BotFather and get the token. - -2. Expose your server over HTTPS (Cloudflare Tunnel, Tailscale, etc.). - -3. Add environment variables: - -``` -WEBAPP_URL="https://your-domain.example" -TELEGRAM_BOT_TOKEN="..." -``` - -4. Start the server and send `/start` to the bot. - -5. Run `/app` in the bot chat to open the Mini App. - -6. If prompted, enter `CLI_API_TOKEN` to bind your Telegram account. After binding, you can open the Mini App without re-entering the token, and notifications only go to bound users. - -## Multi-agent support - -- `hapi` - Start a Claude Code session. -- `hapi codex` - Start an OpenAI Codex session. -- `hapi gemini` - Start a Google Gemini session. - -## CLI config file - -You can store the token in `~/.hapi/settings.json` instead of an env var. -Environment variables take priority over the file. +- [Quick Start](docs/guide/quick-start.md) +- [Installation](docs/guide/installation.md) +- [PWA](docs/guide/pwa.md) +- [How it Works](docs/guide/how-it-works.md) +- [Why HAPI](docs/guide/why-hapi.md) +- [FAQ](docs/guide/faq.md) ## Requirements @@ -175,9 +73,3 @@ Environment variables take priority over the file. bun install bun run build:single-exe ``` - -## Docs - -- `cli/README.md` - CLI usage and config -- `server/README.md` - Server setup and architecture -- `web/README.md` - Web app behavior and dev workflow diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts new file mode 100644 index 00000000..8929801c --- /dev/null +++ b/docs/.vitepress/config.ts @@ -0,0 +1,47 @@ +import { defineConfig } from 'vitepress' + +export default defineConfig({ + title: 'HAPI', + description: 'Control your AI agents from anywhere', + + head: [ + ['link', { rel: 'icon', href: '/favicon.ico' }], + ], + + themeConfig: { + logo: '/logo.svg', + + nav: [ + { text: 'Quick Start', link: '/guide/quick-start' }, + { text: 'App', link: 'https://app.hapi.run', target: '_blank' } + ], + + sidebar: [ + { text: 'Quick Start', link: '/guide/quick-start' }, + { text: 'Installation', link: '/guide/installation' }, + { text: 'PWA', link: '/guide/pwa' }, + { text: 'How it Works', link: '/guide/how-it-works' }, + { text: 'Why HAPI', link: '/guide/why-hapi' }, + { text: 'FAQ', link: '/guide/faq' } + ], + + socialLinks: [ + { icon: 'github', link: 'https://github.com/tiann/hapi' } + ], + + footer: { + message: 'Released under the LGPL-3.0 License.', + copyright: 'Copyright © 2024-present' + }, + + search: { + provider: 'local' + } + }, + + vite: { + server: { + allowedHosts: true + } + } +}) diff --git a/docs/WHY_NOT_HAPPY.md b/docs/WHY_NOT_HAPPY.md deleted file mode 100644 index 29aad4cd..00000000 --- a/docs/WHY_NOT_HAPPY.md +++ /dev/null @@ -1,271 +0,0 @@ -# Why Not Happy? - -[Happy](https://github.com/slopus/happy) is an excellent project. So why build HAPI? - -**The short answer**: Happy is designed for cloud hosting with multiple users. HAPI is designed for self-hosting with a single user. These different goals lead to fundamentally different architectures. - -Happy's cloud-first design requires: -- End-to-end encryption (because you don't trust the server) -- Distributed database + cache + storage (because you need to scale) -- Complex deployment (Docker, multiple services, config files) - -HAPI's local-first design simplifies everything: -- No E2EE needed (your data never leaves your machine) -- Single embedded database (no scaling required) -- One-command deployment (single binary, zero config) - -**TL;DR**: If you want to self-host for personal use, HAPI removes all the complexity that Happy needs for its cloud service. - ---- - -## 1. Architecture Overview - -### Happy: Cloud-First - -``` -┌─────────────────────────────────────────────────────────────────────────────────┐ -│ PUBLIC INTERNET │ -│ │ -│ │ -│ ┌─────────────┐ ┌─────────────────────────────────┐ │ -│ │ │ │ │ │ -│ │ Mobile App │◄─────── E2EE ───────►│ Cloud Server │ │ -│ │ │ │ │ │ -│ └─────────────┘ │ ┌───────────────────────────┐ │ │ -│ │ │ │ │ │ -│ │ │ Encrypted Database │ │ │ -│ │ │ (server cannot read) │ │ │ -│ │ │ │ │ │ -│ │ └───────────────────────────┘ │ │ -│ │ │ │ -│ └────────────────┬────────────────┘ │ -│ │ │ -│ │ E2EE │ -│ │ │ -└──────────────────────────────────────────────────────────────┼───────────────────┘ - │ -┌──────────────────────────────────────────────────────────────┼─────────────────────┐ -│ PRIVATE NETWORK │ │ -│ │ │ -│ ▼ │ -│ ┌──────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ CLI │ │ -│ │ │ │ -│ │ ┌────────────────────────────────────┐ │ │ -│ │ │ Encryption Keys │ │ │ -│ │ │ (only client holds the keys) │ │ │ -│ │ └────────────────────────────────────┘ │ │ -│ │ │ │ -│ └──────────────────────────────────────────────┘ │ -│ │ -└───────────────────────────────────────────────────────────────────────────────────┘ - - -Data Flow: -┌────────────────────────────────────────────────────────────────────────────────────┐ -│ │ -│ CLI ◄───────► Cloud Server ◄───────► App │ -│ │ │ │ │ -│ │ (encrypt) │ (ciphertext) │ (decrypt) │ -│ ▼ ▼ ▼ │ -│ Keys ──────► [Encrypted Data] ◄────── Keys │ -│ │ │ -│ ▼ │ -│ Server stores │ -│ encrypted blobs │ -│ (zero knowledge) │ -│ │ -└────────────────────────────────────────────────────────────────────────────────────┘ -``` - -### HAPI: Local-First - -``` -┌────────────────────────────────────────────────────────────────────────────────────┐ -│ PRIVATE NETWORK │ -│ │ -│ ┌──────────────────────────────────────────────────────────────────────────┐ │ -│ │ │ │ -│ │ Single Process / Binary │ │ -│ │ │ │ -│ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ -│ │ │ │ │ │ │ │ │ │ -│ │ │ CLI │◄──►│ Server │◄──►│ Web App │ │ │ -│ │ │ │ │ │ │ (embedded) │ │ │ -│ │ └──────────────┘ └──────┬───────┘ └──────────────┘ │ │ -│ │ │ │ │ -│ │ ▼ │ │ -│ │ ┌────────────────┐ │ │ -│ │ │ │ │ │ -│ │ │ Local Database│ │ │ -│ │ │ (plaintext) │ │ │ -│ │ │ │ │ │ -│ │ └────────────────┘ │ │ -│ │ │ │ -│ └──────────────────────────────────────────────────────────────────────────┘ │ -│ │ │ -│ │ localhost │ -└────────────────────────────────────────┼───────────────────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────────────────────────────────┐ -│ TUNNEL (Optional) │ -│ │ -│ localhost ────────► public URL (TLS) │ -│ │ -└────────────────────────────────────────┬──────────────────────────────────────────┘ - │ - ▼ -┌───────────────────────────────────────────────────────────────────────────────────┐ -│ PUBLIC INTERNET │ -│ │ -│ ┌───────────────────────────────┐ │ -│ │ │ │ -│ │ Remote Clients │ │ -│ │ (PWA / Mini App) │ │ -│ │ │ │ -│ └───────────────────────────────┘ │ -│ │ -└───────────────────────────────────────────────────────────────────────────────────┘ - - -Data Flow: -┌────────────────────────────────────────────────────────────────────────────────────┐ -│ │ -│ CLI ◄────────► Server ◄────────► App │ -│ │ │ -│ │ (same machine / process) │ -│ ▼ │ -│ Local Database │ -│ (data never leaves) │ -│ │ │ -│ ▼ │ -│ Tunnel provides │ -│ external access │ -│ (TLS encryption) │ -│ │ -└────────────────────────────────────────────────────────────────────────────────────┘ -``` - ---- - -## 2. Key Architectural Differences - -### 2.1 Data Location - -| Aspect | Happy | HAPI | -|--------|-------|------| -| **Where data lives** | Cloud server | Your local machine | -| **Who can access** | Server stores encrypted blobs (cannot read) | Only you (data never uploaded) | -| **Trust model** | Don't trust server → need E2EE | Trust local environment → TLS sufficient | - -### 2.2 Deployment Model - -``` -Happy: -┌───────────────────────────────────────────────────────────────────────┐ -│ │ -│ Distributed Services (4+ components) │ -│ │ -│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ -│ │ Database │ │ Cache │ │ Storage │ │ Server │ │ -│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ -│ │ -│ Requires: Container orchestration, multiple config files │ -│ │ -└───────────────────────────────────────────────────────────────────────┘ - -HAPI: -┌───────────────────────────────────────────────────────────────────────┐ -│ │ -│ Single Binary (everything bundled) │ -│ │ -│ ┌─────────────────────────────────────────────────────────────┐ │ -│ │ CLI + Server + Web App + Database (embedded) │ │ -│ └─────────────────────────────────────────────────────────────┘ │ -│ │ -│ Requires: One command to run │ -│ │ -└───────────────────────────────────────────────────────────────────────┘ -``` - -### 2.3 Security Approach - -| Aspect | Happy | HAPI | -|--------|-------|------| -| **Problem to solve** | Data on untrusted server | External access to local data | -| **Solution** | End-to-end encryption | Tunnel with TLS | -| **Complexity** | High (key management, crypto) | Low (tunnel setup) | -| **Data at rest** | Encrypted | Plaintext (protected by OS) | - ---- - -## 3. Why Different Architectures? - -### Happy's Constraints - -``` -Goal: Multi-user cloud platform - │ - ├──► Users don't trust your server - │ │ - │ └──► Must encrypt everything (E2EE) - │ - ├──► Many concurrent users - │ │ - │ └──► Must scale horizontally (distributed DB + cache) - │ - └──► Multiple devices per user - │ - └──► Must sync state across devices (complex sync logic) - -Result: Sophisticated but complex architecture -``` - -### HAPI's Simplifications - -``` -Goal: Single-user self-hosted tool - │ - ├──► Data stays on your machine - │ │ - │ └──► No E2EE needed (you trust yourself) - │ - ├──► Only one user - │ │ - │ └──► No scaling needed (simple embedded database) - │ - └──► One primary device - │ - └──► Minimal sync logic (tunnel for remote access) - -Result: Simple and portable architecture -``` - ---- - -## 4. Summary - -| Dimension | Happy | HAPI | -|-----------|-------|------| -| **Philosophy** | Cloud-first | Local-first | -| **Data location** | Server (encrypted) | Local (plaintext) | -| **Deployment** | Multiple services | Single binary | -| **Scaling** | Horizontal | None needed | -| **Encryption** | Application-layer E2EE | Transport-layer TLS | -| **Target user** | Teams, cloud users | Individuals, self-hosters | - ---- - -## 5. Conclusion - -The architectural differences stem from fundamentally different product goals: - -- **Happy**: Built for multi-user cloud scenarios. Solves the "untrusted server" problem with E2EE, at the cost of deployment complexity. - -- **HAPI**: Built for single-user self-hosted scenarios. Solves the "remote access" problem with tunneling, achieving one-command deployment. - -**Choose Happy if**: You need multi-user collaboration or team sharing. - -**Choose HAPI if**: You want personal use, data sovereignty, and minimal setup. diff --git a/docs/bun.lock b/docs/bun.lock new file mode 100644 index 00000000..57d541a6 --- /dev/null +++ b/docs/bun.lock @@ -0,0 +1,353 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "hapi-docs", + "devDependencies": { + "vitepress": "^1.6.4", + }, + }, + }, + "packages": { + "@algolia/abtesting": ["@algolia/abtesting@1.12.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-oWknd6wpfNrmRcH0vzed3UPX0i17o4kYLM5OMITyMVM2xLgaRbIafoxL0e8mcrNNb0iORCJA0evnNDKRYth5WQ=="], + + "@algolia/autocomplete-core": ["@algolia/autocomplete-core@1.17.7", "", { "dependencies": { "@algolia/autocomplete-plugin-algolia-insights": "1.17.7", "@algolia/autocomplete-shared": "1.17.7" } }, "sha512-BjiPOW6ks90UKl7TwMv7oNQMnzU+t/wk9mgIDi6b1tXpUek7MW0lbNOUHpvam9pe3lVCf4xPFT+lK7s+e+fs7Q=="], + + "@algolia/autocomplete-plugin-algolia-insights": ["@algolia/autocomplete-plugin-algolia-insights@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "search-insights": ">= 1 < 3" } }, "sha512-Jca5Ude6yUOuyzjnz57og7Et3aXjbwCSDf/8onLHSQgw1qW3ALl9mrMWaXb5FmPVkV3EtkD2F/+NkT6VHyPu9A=="], + + "@algolia/autocomplete-preset-algolia": ["@algolia/autocomplete-preset-algolia@1.17.7", "", { "dependencies": { "@algolia/autocomplete-shared": "1.17.7" }, "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-ggOQ950+nwbWROq2MOCIL71RE0DdQZsceqrg32UqnhDz8FlO9rL8ONHNsI2R1MH0tkgVIDKI/D0sMiUchsFdWA=="], + + "@algolia/autocomplete-shared": ["@algolia/autocomplete-shared@1.17.7", "", { "peerDependencies": { "@algolia/client-search": ">= 4.9.1 < 6", "algoliasearch": ">= 4.9.1 < 6" } }, "sha512-o/1Vurr42U/qskRSuhBH+VKxMvkkUVTLU6WZQr+L5lGZZLYWyhdzWjW0iGXY7EkwRTjBqvN2EsR81yCTGV/kmg=="], + + "@algolia/client-abtesting": ["@algolia/client-abtesting@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-oRSUHbylGIuxrlzdPA8FPJuwrLLRavOhAmFGgdAvMcX47XsyM+IOGa9tc7/K5SPvBqn4nhppOCEz7BrzOPWc4A=="], + + "@algolia/client-analytics": ["@algolia/client-analytics@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-EPBN2Oruw0maWOF4OgGPfioTvd+gmiNwx0HmD9IgmlS+l75DatcBkKOPNJN+0z3wBQWUO5oq602ATxIfmTQ8bA=="], + + "@algolia/client-common": ["@algolia/client-common@5.46.2", "", {}, "sha512-Hj8gswSJNKZ0oyd0wWissqyasm+wTz1oIsv5ZmLarzOZAp3vFEda8bpDQ8PUhO+DfkbiLyVnAxsPe4cGzWtqkg=="], + + "@algolia/client-insights": ["@algolia/client-insights@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-6dBZko2jt8FmQcHCbmNLB0kCV079Mx/DJcySTL3wirgDBUH7xhY1pOuUTLMiGkqM5D8moVZTvTdRKZUJRkrwBA=="], + + "@algolia/client-personalization": ["@algolia/client-personalization@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-1waE2Uqh/PHNeDXGn/PM/WrmYOBiUGSVxAWqiJIj73jqPqvfzZgzdakHscIVaDl6Cp+j5dwjsZ5LCgaUr6DtmA=="], + + "@algolia/client-query-suggestions": ["@algolia/client-query-suggestions@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-EgOzTZkyDcNL6DV0V/24+oBJ+hKo0wNgyrOX/mePBM9bc9huHxIY2352sXmoZ648JXXY2x//V1kropF/Spx83w=="], + + "@algolia/client-search": ["@algolia/client-search@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-ZsOJqu4HOG5BlvIFnMU0YKjQ9ZI6r3C31dg2jk5kMWPSdhJpYL9xa5hEe7aieE+707dXeMI4ej3diy6mXdZpgA=="], + + "@algolia/ingestion": ["@algolia/ingestion@1.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-1Uw2OslTWiOFDtt83y0bGiErJYy5MizadV0nHnOoHFWMoDqWW0kQoMFI65pXqRSkVvit5zjXSLik2xMiyQJDWQ=="], + + "@algolia/monitoring": ["@algolia/monitoring@1.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-xk9f+DPtNcddWN6E7n1hyNNsATBCHIqAvVGG2EAGHJc4AFYL18uM/kMTiOKXE/LKDPyy1JhIerrh9oYb7RBrgw=="], + + "@algolia/recommend": ["@algolia/recommend@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-NApbTPj9LxGzNw4dYnZmj2BoXiAc8NmbbH6qBNzQgXklGklt/xldTvu+FACN6ltFsTzoNU6j2mWNlHQTKGC5+Q=="], + + "@algolia/requester-browser-xhr": ["@algolia/requester-browser-xhr@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2" } }, "sha512-ekotpCwpSp033DIIrsTpYlGUCF6momkgupRV/FA3m62SreTSZUKjgK6VTNyG7TtYfq9YFm/pnh65bATP/ZWJEg=="], + + "@algolia/requester-fetch": ["@algolia/requester-fetch@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2" } }, "sha512-gKE+ZFi/6y7saTr34wS0SqYFDcjHW4Wminv8PDZEi0/mE99+hSrbKgJWxo2ztb5eqGirQTgIh1AMVacGGWM1iw=="], + + "@algolia/requester-node-http": ["@algolia/requester-node-http@5.46.2", "", { "dependencies": { "@algolia/client-common": "5.46.2" } }, "sha512-ciPihkletp7ttweJ8Zt+GukSVLp2ANJHU+9ttiSxsJZThXc4Y2yJ8HGVWesW5jN1zrsZsezN71KrMx/iZsOYpg=="], + + "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], + + "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.28.5", "", {}, "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q=="], + + "@babel/parser": ["@babel/parser@7.28.5", "", { "dependencies": { "@babel/types": "^7.28.5" }, "bin": "./bin/babel-parser.js" }, "sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ=="], + + "@babel/types": ["@babel/types@7.28.5", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" } }, "sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA=="], + + "@docsearch/css": ["@docsearch/css@3.8.2", "", {}, "sha512-y05ayQFyUmCXze79+56v/4HpycYF3uFqB78pLPrSV5ZKAlDuIAAJNhaRi8tTdRNXh05yxX/TyNnzD6LwSM89vQ=="], + + "@docsearch/js": ["@docsearch/js@3.8.2", "", { "dependencies": { "@docsearch/react": "3.8.2", "preact": "^10.0.0" } }, "sha512-Q5wY66qHn0SwA7Taa0aDbHiJvaFJLOJyHmooQ7y8hlwwQLQ/5WwCcoX0g7ii04Qi2DJlHsd0XXzJ8Ypw9+9YmQ=="], + + "@docsearch/react": ["@docsearch/react@3.8.2", "", { "dependencies": { "@algolia/autocomplete-core": "1.17.7", "@algolia/autocomplete-preset-algolia": "1.17.7", "@docsearch/css": "3.8.2", "algoliasearch": "^5.14.2" }, "peerDependencies": { "@types/react": ">= 16.8.0 < 19.0.0", "react": ">= 16.8.0 < 19.0.0", "react-dom": ">= 16.8.0 < 19.0.0", "search-insights": ">= 1 < 3" }, "optionalPeers": ["@types/react", "react", "react-dom", "search-insights"] }, "sha512-xCRrJQlTt8N9GU0DG4ptwHRkfnSnD/YpdeaXe02iKfqs97TkZJv60yE+1eq/tjPcVnTW8dP5qLP7itifFVV5eg=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.21.5", "", { "os": "aix", "cpu": "ppc64" }, "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.21.5", "", { "os": "android", "cpu": "arm" }, "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.21.5", "", { "os": "android", "cpu": "arm64" }, "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.21.5", "", { "os": "android", "cpu": "x64" }, "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.21.5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.21.5", "", { "os": "darwin", "cpu": "x64" }, "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.21.5", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.21.5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.21.5", "", { "os": "linux", "cpu": "arm" }, "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.21.5", "", { "os": "linux", "cpu": "arm64" }, "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.21.5", "", { "os": "linux", "cpu": "ia32" }, "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.21.5", "", { "os": "linux", "cpu": "ppc64" }, "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.21.5", "", { "os": "linux", "cpu": "none" }, "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.21.5", "", { "os": "linux", "cpu": "s390x" }, "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.21.5", "", { "os": "linux", "cpu": "x64" }, "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.21.5", "", { "os": "none", "cpu": "x64" }, "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.21.5", "", { "os": "openbsd", "cpu": "x64" }, "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.21.5", "", { "os": "sunos", "cpu": "x64" }, "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.21.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.21.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.21.5", "", { "os": "win32", "cpu": "x64" }, "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw=="], + + "@iconify-json/simple-icons": ["@iconify-json/simple-icons@1.2.64", "", { "dependencies": { "@iconify/types": "*" } }, "sha512-SMmm//tjZBvHnT0EAzZLnBTL6bukSkncM0pwkOXjr0FsAeCqjQtqoxBR0Mp+PazIJjXJKHm1Ju0YgnCIPOodJg=="], + + "@iconify/types": ["@iconify/types@2.0.0", "", {}, "sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.54.0", "", { "os": "android", "cpu": "arm" }, "sha512-OywsdRHrFvCdvsewAInDKCNyR3laPA2mc9bRYJ6LBp5IyvF3fvXbbNR0bSzHlZVFtn6E0xw2oZlyjg4rKCVcng=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.54.0", "", { "os": "android", "cpu": "arm64" }, "sha512-Skx39Uv+u7H224Af+bDgNinitlmHyQX1K/atIA32JP3JQw6hVODX5tkbi2zof/E69M1qH2UoN3Xdxgs90mmNYw=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.54.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-k43D4qta/+6Fq+nCDhhv9yP2HdeKeP56QrUUTW7E6PhZP1US6NDqpJj4MY0jBHlJivVJD5P8NxrjuobZBJTCRw=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.54.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-cOo7biqwkpawslEfox5Vs8/qj83M/aZCSSNIWpVzfU2CYHa2G3P1UN5WF01RdTHSgCkri7XOlTdtk17BezlV3A=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.54.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-miSvuFkmvFbgJ1BevMa4CPCFt5MPGw094knM64W9I0giUIMMmRYcGW/JWZDriaw/k1kOBtsWh1z6nIFV1vPNtA=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.54.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-KGXIs55+b/ZfZsq9aR026tmr/+7tq6VG6MsnrvF4H8VhwflTIuYh+LFUlIsRdQSgrgmtM3fVATzEAj4hBQlaqQ=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-EHMUcDwhtdRGlXZsGSIuXSYwD5kOT9NVnx9sqzYiwAc91wfYOE1g1djOEDseZJKKqtHAHGwnGPQu3kytmfaXLQ=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.54.0", "", { "os": "linux", "cpu": "arm" }, "sha512-+pBrqEjaakN2ySv5RVrj/qLytYhPKEUwk+e3SFU5jTLHIcAtqh2rLrd/OkbNuHJpsBgxsD8ccJt5ga/SeG0JmA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-NSqc7rE9wuUaRBsBp5ckQ5CVz5aIRKCwsoa6WMF7G01sX3/qHUw/z4pv+D+ahL1EIKy6Enpcnz1RY8pf7bjwng=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.54.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-gr5vDbg3Bakga5kbdpqx81m2n9IX8M6gIMlQQIXiLTNeQW6CucvuInJ91EuCJ/JYvc+rcLLsDFcfAD1K7fMofg=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-gsrtB1NA3ZYj2vq0Rzkylo9ylCtW/PhpLEivlgWe0bpgtX5+9j9EZa0wtZiCjgu6zmSeZWyI/e2YRX1URozpIw=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.54.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-y3qNOfTBStmFNq+t4s7Tmc9hW2ENtPg8FeUD/VShI7rKxNW7O4fFeaYbMsd3tpFlIg1Q8IapFgy7Q9i2BqeBvA=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-89sepv7h2lIVPsFma8iwmccN7Yjjtgz0Rj/Ou6fEqg3HDhpCa+Et+YSufy27i6b0Wav69Qv4WBNl3Rs6pwhebQ=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.54.0", "", { "os": "linux", "cpu": "none" }, "sha512-ZcU77ieh0M2Q8Ur7D5X7KvK+UxbXeDHwiOt/CPSBTI1fBmeDMivW0dPkdqkT4rOgDjrDDBUed9x4EgraIKoR2A=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.54.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-2AdWy5RdDF5+4YfG/YesGDDtbyJlC9LHmL6rZw6FurBJ5n4vFGupsOBGfwMRjBYH7qRQowT8D/U4LoSvVwOhSQ=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-WGt5J8Ij/rvyqpFexxk3ffKqqbLf9AqrTBbWDk7ApGUzaIs6V+s2s84kAxklFwmMF/vBNGrVdYgbblCOFFezMQ=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.54.0", "", { "os": "linux", "cpu": "x64" }, "sha512-JzQmb38ATzHjxlPHuTH6tE7ojnMKM2kYNzt44LO/jJi8BpceEC8QuXYA908n8r3CNuG/B3BV8VR3Hi1rYtmPiw=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.54.0", "", { "os": "none", "cpu": "arm64" }, "sha512-huT3fd0iC7jigGh7n3q/+lfPcXxBi+om/Rs3yiFxjvSxbSB6aohDFXbWvlspaqjeOh+hx7DDHS+5Es5qRkWkZg=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.54.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-c2V0W1bsKIKfbLMBu/WGBz6Yci8nJ/ZJdheE0EwB73N3MvHYKiKGs3mVilX4Gs70eGeDaMqEob25Tw2Gb9Nqyw=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.54.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-woEHgqQqDCkAzrDhvDipnSirm5vxUXtSKDYTVpZG3nUdW/VVB5VdCYA2iReSj/u3yCZzXID4kuKG7OynPnB3WQ=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-dzAc53LOuFvHwbCEOS0rPbXp6SIhAf2txMP5p6mGyOXXw5mWY8NGGbPMPrs4P1WItkfApDathBj/NzMLUZ9rtQ=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.54.0", "", { "os": "win32", "cpu": "x64" }, "sha512-hYT5d3YNdSh3mbCU1gwQyPgQd3T2ne0A3KG8KSBdav5TiBg6eInVmV+TeR5uHufiIgSFg0XsOWGW5/RhNcSvPg=="], + + "@shikijs/core": ["@shikijs/core@2.5.0", "", { "dependencies": { "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.4" } }, "sha512-uu/8RExTKtavlpH7XqnVYBrfBkUc20ngXiX9NSrBhOVZYv/7XQRKUyhtkeflY5QsxC0GbJThCerruZfsUaSldg=="], + + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^3.1.0" } }, "sha512-VjnOpnQf8WuCEZtNUdjjwGUbtAVKuZkVQ/5cHy/tojVVRIRtlWMYVjyWhxOmIq05AlSOv72z7hRNRGVBgQOl0w=="], + + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-pGd1wRATzbo/uatrCIILlAdFVKdxImWJGQ5rFiB5VZi2ve5xj3Ax9jny8QvkaV93btQEwR/rSz5ERFpC5mKNIw=="], + + "@shikijs/langs": ["@shikijs/langs@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-Qfrrt5OsNH5R+5tJ/3uYBBZv3SuGmnRPejV9IlIbFH3HTGLDlkqgHymAlzklVmKBjAaVmkPkyikAV/sQ1wSL+w=="], + + "@shikijs/themes": ["@shikijs/themes@2.5.0", "", { "dependencies": { "@shikijs/types": "2.5.0" } }, "sha512-wGrk+R8tJnO0VMzmUExHR+QdSaPUl/NKs+a4cQQRWyoc3YFbUzuLEi/KWK1hj+8BfHRKm2jNhhJck1dfstJpiw=="], + + "@shikijs/transformers": ["@shikijs/transformers@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/types": "2.5.0" } }, "sha512-SI494W5X60CaUwgi8u4q4m4s3YAFSxln3tzNjOSYqq54wlVgz0/NbbXEb3mdLbqMBztcmS7bVTaEd2w0qMmfeg=="], + + "@shikijs/types": ["@shikijs/types@2.5.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-ygl5yhxki9ZLNuNpPitBWvcy9fsSKKaRuO4BAlMyagszQidxcpLAr0qiW/q43DtSIDxO6hEbtYLiFZNXO/hdGw=="], + + "@shikijs/vscode-textmate": ["@shikijs/vscode-textmate@10.0.2", "", {}, "sha512-83yeghZ2xxin3Nj8z1NMd/NCuca+gsYXswywDy5bHvwlWL8tpTQmzGeUuHd9FC3E/SBEMvzJRwWEOz5gGes9Qg=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/hast": ["@types/hast@3.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ=="], + + "@types/linkify-it": ["@types/linkify-it@5.0.0", "", {}, "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q=="], + + "@types/markdown-it": ["@types/markdown-it@14.1.2", "", { "dependencies": { "@types/linkify-it": "^5", "@types/mdurl": "^2" } }, "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog=="], + + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], + + "@types/mdurl": ["@types/mdurl@2.0.0", "", {}, "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg=="], + + "@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "@types/web-bluetooth": ["@types/web-bluetooth@0.0.21", "", {}, "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA=="], + + "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + + "@vitejs/plugin-vue": ["@vitejs/plugin-vue@5.2.4", "", { "peerDependencies": { "vite": "^5.0.0 || ^6.0.0", "vue": "^3.2.25" } }, "sha512-7Yx/SXSOcQq5HiiV3orevHUFn+pmMB4cgbEkDYgnkUWb0WfeQ/wa2yFv6D5ICiCQOVpjA7vYDXrC7AGO8yjDHA=="], + + "@vue/compiler-core": ["@vue/compiler-core@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/shared": "3.5.26", "entities": "^7.0.0", "estree-walker": "^2.0.2", "source-map-js": "^1.2.1" } }, "sha512-vXyI5GMfuoBCnv5ucIT7jhHKl55Y477yxP6fc4eUswjP8FG3FFVFd41eNDArR+Uk3QKn2Z85NavjaxLxOC19/w=="], + + "@vue/compiler-dom": ["@vue/compiler-dom@3.5.26", "", { "dependencies": { "@vue/compiler-core": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-y1Tcd3eXs834QjswshSilCBnKGeQjQXB6PqFn/1nxcQw4pmG42G8lwz+FZPAZAby6gZeHSt/8LMPfZ4Rb+Bd/A=="], + + "@vue/compiler-sfc": ["@vue/compiler-sfc@3.5.26", "", { "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.26", "@vue/compiler-dom": "3.5.26", "@vue/compiler-ssr": "3.5.26", "@vue/shared": "3.5.26", "estree-walker": "^2.0.2", "magic-string": "^0.30.21", "postcss": "^8.5.6", "source-map-js": "^1.2.1" } }, "sha512-egp69qDTSEZcf4bGOSsprUr4xI73wfrY5oRs6GSgXFTiHrWj4Y3X5Ydtip9QMqiCMCPVwLglB9GBxXtTadJ3mA=="], + + "@vue/compiler-ssr": ["@vue/compiler-ssr@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-lZT9/Y0nSIRUPVvapFJEVDbEXruZh2IYHMk2zTtEgJSlP5gVOqeWXH54xDKAaFS4rTnDeDBQUYDtxKyoW9FwDw=="], + + "@vue/devtools-api": ["@vue/devtools-api@7.7.9", "", { "dependencies": { "@vue/devtools-kit": "^7.7.9" } }, "sha512-kIE8wvwlcZ6TJTbNeU2HQNtaxLx3a84aotTITUuL/4bzfPxzajGBOoqjMhwZJ8L9qFYDU/lAYMEEm11dnZOD6g=="], + + "@vue/devtools-kit": ["@vue/devtools-kit@7.7.9", "", { "dependencies": { "@vue/devtools-shared": "^7.7.9", "birpc": "^2.3.0", "hookable": "^5.5.3", "mitt": "^3.0.1", "perfect-debounce": "^1.0.0", "speakingurl": "^14.0.1", "superjson": "^2.2.2" } }, "sha512-PyQ6odHSgiDVd4hnTP+aDk2X4gl2HmLDfiyEnn3/oV+ckFDuswRs4IbBT7vacMuGdwY/XemxBoh302ctbsptuA=="], + + "@vue/devtools-shared": ["@vue/devtools-shared@7.7.9", "", { "dependencies": { "rfdc": "^1.4.1" } }, "sha512-iWAb0v2WYf0QWmxCGy0seZNDPdO3Sp5+u78ORnyeonS6MT4PC7VPrryX2BpMJrwlDeaZ6BD4vP4XKjK0SZqaeA=="], + + "@vue/reactivity": ["@vue/reactivity@3.5.26", "", { "dependencies": { "@vue/shared": "3.5.26" } }, "sha512-9EnYB1/DIiUYYnzlnUBgwU32NNvLp/nhxLXeWRhHUEeWNTn1ECxX8aGO7RTXeX6PPcxe3LLuNBFoJbV4QZ+CFQ=="], + + "@vue/runtime-core": ["@vue/runtime-core@3.5.26", "", { "dependencies": { "@vue/reactivity": "3.5.26", "@vue/shared": "3.5.26" } }, "sha512-xJWM9KH1kd201w5DvMDOwDHYhrdPTrAatn56oB/LRG4plEQeZRQLw0Bpwih9KYoqmzaxF0OKSn6swzYi84e1/Q=="], + + "@vue/runtime-dom": ["@vue/runtime-dom@3.5.26", "", { "dependencies": { "@vue/reactivity": "3.5.26", "@vue/runtime-core": "3.5.26", "@vue/shared": "3.5.26", "csstype": "^3.2.3" } }, "sha512-XLLd/+4sPC2ZkN/6+V4O4gjJu6kSDbHAChvsyWgm1oGbdSO3efvGYnm25yCjtFm/K7rrSDvSfPDgN1pHgS4VNQ=="], + + "@vue/server-renderer": ["@vue/server-renderer@3.5.26", "", { "dependencies": { "@vue/compiler-ssr": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "vue": "3.5.26" } }, "sha512-TYKLXmrwWKSodyVuO1WAubucd+1XlLg4set0YoV+Hu8Lo79mp/YMwWV5mC5FgtsDxX3qo1ONrxFaTP1OQgy1uA=="], + + "@vue/shared": ["@vue/shared@3.5.26", "", {}, "sha512-7Z6/y3uFI5PRoKeorTOSXKcDj0MSasfNNltcslbFrPpcw6aXRUALq4IfJlaTRspiWIUOEZbrpM+iQGmCOiWe4A=="], + + "@vueuse/core": ["@vueuse/core@12.8.2", "", { "dependencies": { "@types/web-bluetooth": "^0.0.21", "@vueuse/metadata": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" } }, "sha512-HbvCmZdzAu3VGi/pWYm5Ut+Kd9mn1ZHnn4L5G8kOQTPs/IwIAmJoBrmYk2ckLArgMXZj0AW3n5CAejLUO+PhdQ=="], + + "@vueuse/integrations": ["@vueuse/integrations@12.8.2", "", { "dependencies": { "@vueuse/core": "12.8.2", "@vueuse/shared": "12.8.2", "vue": "^3.5.13" }, "peerDependencies": { "async-validator": "^4", "axios": "^1", "change-case": "^5", "drauu": "^0.4", "focus-trap": "^7", "fuse.js": "^7", "idb-keyval": "^6", "jwt-decode": "^4", "nprogress": "^0.2", "qrcode": "^1.5", "sortablejs": "^1", "universal-cookie": "^7" }, "optionalPeers": ["async-validator", "axios", "change-case", "drauu", "focus-trap", "fuse.js", "idb-keyval", "jwt-decode", "nprogress", "qrcode", "sortablejs", "universal-cookie"] }, "sha512-fbGYivgK5uBTRt7p5F3zy6VrETlV9RtZjBqd1/HxGdjdckBgBM4ugP8LHpjolqTj14TXTxSK1ZfgPbHYyGuH7g=="], + + "@vueuse/metadata": ["@vueuse/metadata@12.8.2", "", {}, "sha512-rAyLGEuoBJ/Il5AmFHiziCPdQzRt88VxR+Y/A/QhJ1EWtWqPBBAxTAFaSkviwEuOEZNtW8pvkPgoCZQ+HxqW1A=="], + + "@vueuse/shared": ["@vueuse/shared@12.8.2", "", { "dependencies": { "vue": "^3.5.13" } }, "sha512-dznP38YzxZoNloI0qpEfpkms8knDtaoQ6Y/sfS0L7Yki4zh40LFHEhur0odJC6xTHG5dxWVPiUWBXn+wCG2s5w=="], + + "algoliasearch": ["algoliasearch@5.46.2", "", { "dependencies": { "@algolia/abtesting": "1.12.2", "@algolia/client-abtesting": "5.46.2", "@algolia/client-analytics": "5.46.2", "@algolia/client-common": "5.46.2", "@algolia/client-insights": "5.46.2", "@algolia/client-personalization": "5.46.2", "@algolia/client-query-suggestions": "5.46.2", "@algolia/client-search": "5.46.2", "@algolia/ingestion": "1.46.2", "@algolia/monitoring": "1.46.2", "@algolia/recommend": "5.46.2", "@algolia/requester-browser-xhr": "5.46.2", "@algolia/requester-fetch": "5.46.2", "@algolia/requester-node-http": "5.46.2" } }, "sha512-qqAXW9QvKf2tTyhpDA4qXv1IfBwD2eduSW6tUEBFIfCeE9gn9HQ9I5+MaKoenRuHrzk5sQoNh1/iof8mY7uD6Q=="], + + "birpc": ["birpc@2.9.0", "", {}, "sha512-KrayHS5pBi69Xi9JmvoqrIgYGDkD6mcSe/i6YKi3w5kekCLzrX4+nawcXqrj2tIp50Kw/mT/s3p+GVK0A0sKxw=="], + + "ccount": ["ccount@2.0.1", "", {}, "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg=="], + + "character-entities-html4": ["character-entities-html4@2.1.0", "", {}, "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA=="], + + "character-entities-legacy": ["character-entities-legacy@3.0.0", "", {}, "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ=="], + + "comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="], + + "copy-anything": ["copy-anything@4.0.5", "", { "dependencies": { "is-what": "^5.2.0" } }, "sha512-7Vv6asjS4gMOuILabD3l739tsaxFQmC+a7pLZm02zyvs8p977bL3zEgq3yDk5rn9B0PbYgIv++jmHcuUab4RhA=="], + + "csstype": ["csstype@3.2.3", "", {}, "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ=="], + + "dequal": ["dequal@2.0.3", "", {}, "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA=="], + + "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + + "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], + + "entities": ["entities@7.0.0", "", {}, "sha512-FDWG5cmEYf2Z00IkYRhbFrwIwvdFKH07uV8dvNy0omp/Qb1xcyCWp2UDtcwJF4QZZvk0sLudP6/hAu42TaqVhQ=="], + + "esbuild": ["esbuild@0.21.5", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.21.5", "@esbuild/android-arm": "0.21.5", "@esbuild/android-arm64": "0.21.5", "@esbuild/android-x64": "0.21.5", "@esbuild/darwin-arm64": "0.21.5", "@esbuild/darwin-x64": "0.21.5", "@esbuild/freebsd-arm64": "0.21.5", "@esbuild/freebsd-x64": "0.21.5", "@esbuild/linux-arm": "0.21.5", "@esbuild/linux-arm64": "0.21.5", "@esbuild/linux-ia32": "0.21.5", "@esbuild/linux-loong64": "0.21.5", "@esbuild/linux-mips64el": "0.21.5", "@esbuild/linux-ppc64": "0.21.5", "@esbuild/linux-riscv64": "0.21.5", "@esbuild/linux-s390x": "0.21.5", "@esbuild/linux-x64": "0.21.5", "@esbuild/netbsd-x64": "0.21.5", "@esbuild/openbsd-x64": "0.21.5", "@esbuild/sunos-x64": "0.21.5", "@esbuild/win32-arm64": "0.21.5", "@esbuild/win32-ia32": "0.21.5", "@esbuild/win32-x64": "0.21.5" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw=="], + + "estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="], + + "focus-trap": ["focus-trap@7.7.0", "", { "dependencies": { "tabbable": "^6.3.0" } }, "sha512-DJJDHpEgoSbP8ZE1MNeU2IzCpfFyFdNZZRilqmfH2XiQsPK6PtD8AfJqWzEBudUQB2yHwZc5iq54rjTaGQ+ljw=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], + + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], + + "hookable": ["hookable@5.5.3", "", {}, "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ=="], + + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], + + "is-what": ["is-what@5.5.0", "", {}, "sha512-oG7cgbmg5kLYae2N5IVd3jm2s+vldjxJzK1pcu9LfpGuQ93MQSzo0okvRna+7y5ifrD+20FE8FvjusyGaz14fw=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "mark.js": ["mark.js@8.11.1", "", {}, "sha512-1I+1qpDt4idfgLQG+BNWmrqku+7/2bi5nLf4YwF8y8zXvmfiTBY3PV3ZibfrjBueCByROpuBjLLFCajqkgYoLQ=="], + + "mdast-util-to-hast": ["mdast-util-to-hast@13.2.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "@ungap/structured-clone": "^1.0.0", "devlop": "^1.0.0", "micromark-util-sanitize-uri": "^2.0.0", "trim-lines": "^3.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0" } }, "sha512-cctsq2wp5vTsLIcaymblUriiTcZd0CwWtCbLvrOzYCDZoWyMNV8sZ7krj09FSnsiJi3WVsHLM4k6Dq/yaPyCXA=="], + + "micromark-util-character": ["micromark-util-character@2.1.1", "", { "dependencies": { "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q=="], + + "micromark-util-encode": ["micromark-util-encode@2.0.1", "", {}, "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw=="], + + "micromark-util-sanitize-uri": ["micromark-util-sanitize-uri@2.0.1", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-encode": "^2.0.0", "micromark-util-symbol": "^2.0.0" } }, "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ=="], + + "micromark-util-symbol": ["micromark-util-symbol@2.0.1", "", {}, "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q=="], + + "micromark-util-types": ["micromark-util-types@2.0.2", "", {}, "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA=="], + + "minisearch": ["minisearch@7.2.0", "", {}, "sha512-dqT2XBYUOZOiC5t2HRnwADjhNS2cecp9u+TJRiJ1Qp/f5qjkeT5APcGPjHw+bz89Ms8Jp+cG4AlE+QZ/QnDglg=="], + + "mitt": ["mitt@3.0.1", "", {}, "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw=="], + + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + + "oniguruma-to-es": ["oniguruma-to-es@3.1.1", "", { "dependencies": { "emoji-regex-xs": "^1.0.0", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-bUH8SDvPkH3ho3dvwJwfonjlQ4R80vjyvrU8YpxuROddv55vAEJrTuCuCVUhhsHbtlD9tGGbaNApGQckXhS8iQ=="], + + "perfect-debounce": ["perfect-debounce@1.0.0", "", {}, "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "postcss": ["postcss@8.5.6", "", { "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg=="], + + "preact": ["preact@10.28.1", "", {}, "sha512-u1/ixq/lVQI0CakKNvLDEcW5zfCjUQfZdK9qqWuIJtsezuyG6pk9TWj75GMuI/EzRSZB/VAE43sNWWZfiy8psw=="], + + "property-information": ["property-information@7.1.0", "", {}, "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ=="], + + "regex": ["regex@6.1.0", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-6VwtthbV4o/7+OaAF9I5L5V3llLEsoPyq9P1JVXkedTP33c7MfCG0/5NOPcSJn0TzXcG9YUrR0gQSWioew3LDg=="], + + "regex-recursion": ["regex-recursion@6.0.2", "", { "dependencies": { "regex-utilities": "^2.3.0" } }, "sha512-0YCaSCq2VRIebiaUviZNs0cBz1kg5kVS2UKUfNIx8YVs1cN3AV7NTctO5FOKBA+UT2BPJIWZauYHPqJODG50cg=="], + + "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + + "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], + + "rollup": ["rollup@4.54.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.54.0", "@rollup/rollup-android-arm64": "4.54.0", "@rollup/rollup-darwin-arm64": "4.54.0", "@rollup/rollup-darwin-x64": "4.54.0", "@rollup/rollup-freebsd-arm64": "4.54.0", "@rollup/rollup-freebsd-x64": "4.54.0", "@rollup/rollup-linux-arm-gnueabihf": "4.54.0", "@rollup/rollup-linux-arm-musleabihf": "4.54.0", "@rollup/rollup-linux-arm64-gnu": "4.54.0", "@rollup/rollup-linux-arm64-musl": "4.54.0", "@rollup/rollup-linux-loong64-gnu": "4.54.0", "@rollup/rollup-linux-ppc64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-gnu": "4.54.0", "@rollup/rollup-linux-riscv64-musl": "4.54.0", "@rollup/rollup-linux-s390x-gnu": "4.54.0", "@rollup/rollup-linux-x64-gnu": "4.54.0", "@rollup/rollup-linux-x64-musl": "4.54.0", "@rollup/rollup-openharmony-arm64": "4.54.0", "@rollup/rollup-win32-arm64-msvc": "4.54.0", "@rollup/rollup-win32-ia32-msvc": "4.54.0", "@rollup/rollup-win32-x64-gnu": "4.54.0", "@rollup/rollup-win32-x64-msvc": "4.54.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-3nk8Y3a9Ea8szgKhinMlGMhGMw89mqule3KWczxhIzqudyHdCIOHw8WJlj/r329fACjKLEh13ZSk7oE22kyeIw=="], + + "search-insights": ["search-insights@2.17.3", "", {}, "sha512-RQPdCYTa8A68uM2jwxoY842xDhvx3E5LFL1LxvxCNMev4o5mLuokczhzjAgGwUZBAmOKZknArSxLKmXtIi2AxQ=="], + + "shiki": ["shiki@2.5.0", "", { "dependencies": { "@shikijs/core": "2.5.0", "@shikijs/engine-javascript": "2.5.0", "@shikijs/engine-oniguruma": "2.5.0", "@shikijs/langs": "2.5.0", "@shikijs/themes": "2.5.0", "@shikijs/types": "2.5.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-mI//trrsaiCIPsja5CNfsyNOqgAZUb6VpJA+340toL42UpzQlXpwRV9nch69X6gaUxrr9kaOOa6e3y3uAkGFxQ=="], + + "source-map-js": ["source-map-js@1.2.1", "", {}, "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA=="], + + "space-separated-tokens": ["space-separated-tokens@2.0.2", "", {}, "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q=="], + + "speakingurl": ["speakingurl@14.0.1", "", {}, "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ=="], + + "stringify-entities": ["stringify-entities@4.0.4", "", { "dependencies": { "character-entities-html4": "^2.0.0", "character-entities-legacy": "^3.0.0" } }, "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg=="], + + "superjson": ["superjson@2.2.6", "", { "dependencies": { "copy-anything": "^4" } }, "sha512-H+ue8Zo4vJmV2nRjpx86P35lzwDT3nItnIsocgumgr0hHMQ+ZGq5vrERg9kJBo5AWGmxZDhzDo+WVIJqkB0cGA=="], + + "tabbable": ["tabbable@6.3.0", "", {}, "sha512-EIHvdY5bPLuWForiR/AN2Bxngzpuwn1is4asboytXtpTgsArc+WmSJKVLlhdh71u7jFcryDqB2A8lQvj78MkyQ=="], + + "trim-lines": ["trim-lines@3.0.1", "", {}, "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg=="], + + "unist-util-is": ["unist-util-is@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-LsiILbtBETkDz8I9p1dQ0uyRUWuaQzd/cuEeS1hoRSyW5E5XGmTzlwY1OrNzzakGowI9Dr/I8HVaw4hTtnxy8g=="], + + "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], + + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], + + "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], + + "unist-util-visit-parents": ["unist-util-visit-parents@6.0.2", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-goh1s1TBrqSqukSc8wrjwWhL0hiJxgA8m4kFxGlQ+8FYQ3C/m11FcTs4YYem7V664AhHVvgoQLk890Ssdsr2IQ=="], + + "vfile": ["vfile@6.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "vfile-message": "^4.0.0" } }, "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q=="], + + "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], + + "vite": ["vite@5.4.21", "", { "dependencies": { "esbuild": "^0.21.3", "postcss": "^8.4.43", "rollup": "^4.20.0" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || >=20.0.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.4.0" }, "optionalPeers": ["@types/node", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser"], "bin": { "vite": "bin/vite.js" } }, "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw=="], + + "vitepress": ["vitepress@1.6.4", "", { "dependencies": { "@docsearch/css": "3.8.2", "@docsearch/js": "3.8.2", "@iconify-json/simple-icons": "^1.2.21", "@shikijs/core": "^2.1.0", "@shikijs/transformers": "^2.1.0", "@shikijs/types": "^2.1.0", "@types/markdown-it": "^14.1.2", "@vitejs/plugin-vue": "^5.2.1", "@vue/devtools-api": "^7.7.0", "@vue/shared": "^3.5.13", "@vueuse/core": "^12.4.0", "@vueuse/integrations": "^12.4.0", "focus-trap": "^7.6.4", "mark.js": "8.11.1", "minisearch": "^7.1.1", "shiki": "^2.1.0", "vite": "^5.4.14", "vue": "^3.5.13" }, "peerDependencies": { "markdown-it-mathjax3": "^4", "postcss": "^8" }, "optionalPeers": ["markdown-it-mathjax3", "postcss"], "bin": { "vitepress": "bin/vitepress.js" } }, "sha512-+2ym1/+0VVrbhNyRoFFesVvBvHAVMZMK0rw60E3X/5349M1GuVdKeazuksqopEdvkKwKGs21Q729jX81/bkBJg=="], + + "vue": ["vue@3.5.26", "", { "dependencies": { "@vue/compiler-dom": "3.5.26", "@vue/compiler-sfc": "3.5.26", "@vue/runtime-dom": "3.5.26", "@vue/server-renderer": "3.5.26", "@vue/shared": "3.5.26" }, "peerDependencies": { "typescript": "*" }, "optionalPeers": ["typescript"] }, "sha512-SJ/NTccVyAoNUJmkM9KUqPcYlY+u8OVL1X5EW9RIs3ch5H2uERxyyIUI4MRxVCSOiEcupX9xNGde1tL9ZKpimA=="], + + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + } +} diff --git a/docs/guide/faq.md b/docs/guide/faq.md new file mode 100644 index 00000000..5c867d1f --- /dev/null +++ b/docs/guide/faq.md @@ -0,0 +1,177 @@ +# FAQ + +## General + +### What is HAPI? + +HAPI is a local-first, self-hosted platform for running and controlling AI coding agents (Claude Code, Codex, Gemini) remotely. It lets you start coding sessions on your computer and monitor/control them from your phone. + +### What does HAPI stand for? + +HAPI (哈皮) is a Chinese transliteration of "Happy", reflecting the project's goal of making AI coding assistance a happier experience by freeing you from the terminal. + +### Is HAPI free? + +Yes, HAPI is open source and free to use under the LGPL-3.0 license. + +### What AI agents does HAPI support? + +- **Claude Code** (recommended) +- **OpenAI Codex** +- **Google Gemini** + +## Setup & Installation + +### Do I need a server? + +HAPI includes an embedded server. Just run `hapi server` on your machine - no external server required. + +### How do I access HAPI from my phone? + +For local network access: +``` +http://:3006 +``` + +For internet access, set up a tunnel (Cloudflare Tunnel, Tailscale, or ngrok). + +### What's the access token for? + +The `CLI_API_TOKEN` is a shared secret that authenticates: +- CLI connections to the server +- Web app logins +- Telegram account binding + +It's auto-generated on first server start and saved to `~/.hapi/settings.json`. + +### Can I use HAPI without Telegram? + +Yes. Telegram is optional. You can use the web app directly in any browser or install it as a PWA. + +## Usage + +### How do I approve permissions remotely? + +1. When your AI agent requests permission (e.g., to edit a file), you'll see a notification +2. Open HAPI on your phone +3. Navigate to the active session +4. Approve or deny the pending permission + +### Can I start sessions remotely? + +Yes, with daemon mode: + +1. Run `hapi daemon start` on your computer +2. Your machine appears in the "Machines" list in the web app +3. Tap to spawn new sessions from anywhere + +### How do I see what files were changed? + +In the session view, tap the "Files" tab to: +- Browse project files +- View git status +- See diffs of changed files + +### Can I send messages to the AI from my phone? + +Yes. Open any session and use the chat interface to send messages directly to the AI agent. + +## Security + +### Is my data safe? + +Yes. HAPI is local-first: +- All data stays on your machine +- Nothing is uploaded to external servers +- The database is stored locally in `~/.hapi/` + +### How secure is the token authentication? + +The auto-generated token is 256-bit (cryptographically secure). For external access, always use HTTPS via a tunnel. + +### Can others access my HAPI instance? + +Only if they have your access token. For additional security: +- Use a strong, unique token +- Always use HTTPS for external access +- Consider Tailscale for private networking + +## Troubleshooting + +### "Connection refused" error + +- Ensure server is running: `hapi server` +- Check firewall allows port 3006 +- Verify `HAPI_SERVER_URL` is correct + +### "Invalid token" error + +- Re-run `hapi auth login` +- Check token matches in CLI and server +- Verify `~/.hapi/settings.json` has correct `cliApiToken` + +### Daemon won't start + +```bash +# Check status +hapi daemon status + +# Clear stale lock file +rm ~/.hapi/daemon.state.json.lock + +# Check logs +hapi daemon logs +``` + +### Claude Code not found + +Install Claude Code or set custom path: +```bash +npm install -g @anthropic-ai/claude-code +# or +export HAPI_CLAUDE_PATH=/path/to/claude +``` + +### How do I run diagnostics? + +```bash +hapi doctor +``` + +This checks server connectivity, token validity, agent availability, and more. + +## Comparison + +### HAPI vs Happy + +| Aspect | Happy | HAPI | +|--------|-------|------| +| Design | Cloud-first | Local-first | +| Users | Multi-user | Single user | +| Deployment | Multiple services | Single binary | +| Data | Encrypted on server | Never leaves your machine | + +See [Why HAPI](/guide/why-hapi) for detailed comparison. + +### HAPI vs running Claude Code directly + +| Feature | Claude Code | HAPI + Claude Code | +|---------|-------------|-------------------| +| Remote access | No | Yes | +| Mobile control | No | Yes | +| Permission approval | Terminal only | Phone/web | +| Session persistence | No | Yes | +| Multi-machine | Manual | Built-in | + +## Contributing + +### How can I contribute? + +Visit our [GitHub repository](https://github.com/tiann/hapi) to: +- Report issues +- Submit pull requests +- Suggest features + +### Where do I report bugs? + +Open an issue on [GitHub Issues](https://github.com/tiann/hapi/issues). diff --git a/docs/guide/how-it-works.md b/docs/guide/how-it-works.md new file mode 100644 index 00000000..233b59e2 --- /dev/null +++ b/docs/guide/how-it-works.md @@ -0,0 +1,166 @@ +# How it Works + +HAPI consists of three interconnected components that work together to provide remote AI agent control. + +## Architecture Overview + +``` +┌────────────────────────────────────────────────────────────────────────────┐ +│ Your Local Machine │ +│ │ +│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ +│ │ │ │ │ │ │ │ +│ │ HAPI CLI │◄───────►│ HAPI Server │◄───────►│ Web App │ │ +│ │ │ Socket │ │ SSE │ (embedded) │ │ +│ │ + AI Agent │ .IO │ + SQLite │ │ │ │ +│ │ │ │ + REST API │ │ │ │ +│ └──────────────┘ └──────┬───────┘ └──────────────┘ │ +│ │ │ +│ │ localhost:3006 │ +└───────────────────────────────────┼────────────────────────────────────────┘ + │ + ┌─────────▼─────────┐ + │ Tunnel (Optional)│ + │ Cloudflare/ngrok │ + └─────────┬─────────┘ + │ +┌───────────────────────────────────┼────────────────────────────────────────┐ +│ Public Internet │ +│ │ │ +│ ┌─────────────────────────┼─────────────────────────┐ │ +│ │ ▼ │ │ +│ │ ┌──────────────┐ ┌──────────────┐ │ │ +│ │ │ │ │ │ │ │ +│ │ │ Telegram │ │ PWA / │ │ │ +│ │ │ Mini App │ │ Browser │ │ │ +│ │ │ │ │ │ │ │ +│ │ └──────────────┘ └──────────────┘ │ │ +│ │ │ │ +│ └───────────────────────────────────────────────────┘ │ +│ Your Phone │ +└────────────────────────────────────────────────────────────────────────────┘ +``` + +## Components + +### HAPI CLI + +The CLI is a wrapper around AI coding agents (Claude Code, Codex, Gemini). It: + +- Starts and manages coding sessions +- Registers sessions with the HAPI server +- Relays messages and permission requests +- Provides MCP (Model Context Protocol) tools + +**Key Commands:** +```bash +hapi # Start Claude Code session +hapi codex # Start OpenAI Codex session +hapi gemini # Start Google Gemini session +hapi daemon start # Run background service for remote session spawning +``` + +### HAPI Server + +The server is the central hub that connects everything: + +- **HTTP API** - RESTful endpoints for sessions, messages, permissions +- **Socket.IO** - Real-time bidirectional communication with CLI +- **SSE (Server-Sent Events)** - Live updates pushed to web clients +- **SQLite Database** - Persistent storage for sessions and messages +- **Telegram Bot** - Notifications and Mini App integration + +### Web App + +A React-based PWA that provides the mobile interface: + +- **Session List** - View all active and past sessions +- **Chat Interface** - Send messages and view agent responses +- **Permission Management** - Approve or deny tool access +- **File Browser** - Browse project files and view git diffs +- **Remote Spawn** - Start new sessions on any connected machine + +## Data Flow + +### Starting a Session + +``` +1. User runs `hapi` in terminal + │ + ▼ +2. CLI starts Claude Code (or other agent) + │ + ▼ +3. CLI connects to server via Socket.IO + │ + ▼ +4. Server creates session in database + │ + ▼ +5. Web clients receive SSE update + │ + ▼ +6. Session appears in mobile app +``` + +### Permission Request Flow + +``` +1. AI agent requests tool permission (e.g., file edit) + │ + ▼ +2. CLI sends permission request to server + │ + ▼ +3. Server stores request and notifies via SSE + Telegram + │ + ▼ +4. User receives notification on phone + │ + ▼ +5. User approves/denies in web app or Telegram + │ + ▼ +6. Server relays decision to CLI via Socket.IO + │ + ▼ +7. CLI informs AI agent, execution continues +``` + +### Message Flow + +``` +User (Phone) Server CLI + │ │ │ + │──── Send message ──────►│ │ + │ │─── Socket.IO emit ───►│ + │ │ │ + │ │ ├── AI processes + │ │ │ + │ │◄── Stream response ───│ + │◄─────── SSE ────────────│ │ + │ │ │ +``` + +## Communication Protocols + +### CLI ↔ Server: Socket.IO + +Real-time bidirectional communication for: +- Session registration and heartbeat +- Message relay (user input → agent) +- Permission requests and responses +- Metadata and state updates +- RPC method invocation + +### Server ↔ Web: REST + SSE + +- **REST API** for actions (send message, approve permission) +- **SSE stream** for real-time updates (new messages, status changes) + +### External Access: Tunnel + +For remote access outside your local network: +- **Cloudflare Tunnel** (recommended) - Free, secure, reliable +- **Tailscale** - Mesh VPN for private networks +- **ngrok** - Quick setup for testing diff --git a/docs/guide/installation.md b/docs/guide/installation.md new file mode 100644 index 00000000..cef6b787 --- /dev/null +++ b/docs/guide/installation.md @@ -0,0 +1,219 @@ +# Installation + +Install the HAPI CLI and set up the server. + +## Prerequisites + +- Claude Code, OpenAI Codex CLI, or Google Gemini CLI installed + +## Install the CLI + +```bash +npm install -g @twsxtd/hapi +``` + +Or with Homebrew: + +```bash +brew install tiann/tap/hapi +``` + +## Other install options + +
+npx (no install) + +```bash +npx @twsxtd/hapi +``` +
+ +
+Prebuilt binary + +Download the latest release from [GitHub Releases](https://github.com/tiann/hapi/releases). + +```bash +xattr -d com.apple.quarantine ./hapi +chmod +x ./hapi +sudo mv ./hapi /usr/local/bin/ +``` +
+ +
+Docker (server only) + +```bash +docker pull ghcr.io/tiann/hapi-server:latest + +docker run -d \ + --name hapi-server \ + -p 3006:3006 \ + -v ~/.hapi:/root/.hapi \ + -e CLI_API_TOKEN=your-secret-token \ + ghcr.io/tiann/hapi-server:latest +``` +
+ +
+Build from source + +```bash +git clone https://github.com/tiann/hapi.git +cd hapi +bun install +bun build:single-exe + +./cli/dist/hapi +``` +
+ +## Server setup + +Start the server: + +```bash +hapi server +``` + +The server listens on `http://localhost:3006` by default. + +On first run, HAPI: + +1. Creates `~/.hapi/` +2. Generates a secure access token +3. Prints the token and saves it to `~/.hapi/settings.json` + +
+Config files + +``` +~/.hapi/ +├── settings.json # Main configuration +├── hapi.db # SQLite database (server) +├── daemon.state.json # Daemon process state +└── logs/ # Log files +``` +
+ +
+Environment variables + +| Variable | Default | Description | +|----------|---------|-------------| +| `CLI_API_TOKEN` | Auto-generated | Shared secret for authentication | +| `HAPI_SERVER_URL` | `http://localhost:3006` | Server URL for CLI | +| `WEBAPP_PORT` | `3006` | HTTP server port | +| `HAPI_HOME` | `~/.hapi` | Config directory path | +| `DB_PATH` | `~/.hapi/hapi.db` | Database file path | +| `CORS_ORIGINS` | - | Allowed CORS origins | +
+ +## CLI setup + +If the server is not on localhost, set these before running `hapi`: + +```bash +export HAPI_SERVER_URL="http://your-server:3006" +export CLI_API_TOKEN="your-token-here" +``` + +Or use interactive login: + +```bash +hapi auth login +``` + +Authentication commands: + +```bash +hapi auth status +hapi auth login +hapi auth logout +``` + +Each machine gets a unique ID stored in `~/.hapi/settings.json`. This allows: + +- Multiple machines to connect to one server +- Remote session spawning on specific machines +- Machine health monitoring + +## Operations + +### Remote access + +Cloudflare Tunnel (recommended): + +https://developers.cloudflare.com/cloudflare-one/connections/connect-networks/ + +```bash +export WEBAPP_URL="https://your-tunnel.trycloudflare.com" +``` + +Tailscale: + +https://tailscale.com/download + +```bash +sudo tailscale up +``` + +Access via your Tailscale IP: + +``` +http://100.x.x.x:3006 +``` + +ngrok: + +```bash +ngrok http 3006 +``` + +### Telegram setup + +Enable Telegram notifications and Mini App access: + +1. Message [@BotFather](https://t.me/BotFather) and create a bot +2. Set the bot token and public URL +3. Start the server and bind your account + +```bash +export TELEGRAM_BOT_TOKEN="your-bot-token" +export WEBAPP_URL="https://your-public-url" + +hapi server +``` + +Then message your bot with `/start`, open the app, and enter your `CLI_API_TOKEN`. + +### Daemon setup + +Run a background service for remote session spawning: + +```bash +hapi daemon start +hapi daemon status +hapi daemon logs +hapi daemon stop +``` + +With the daemon running: + +- Your machine appears in the "Machines" list +- You can spawn sessions remotely from the web app +- Sessions persist even when the terminal is closed + +### Security notes + +- Keep tokens secret and rotate if needed +- Use HTTPS for public access +- Restrict CORS origins in production + +
+Firewall example (ufw) + +```bash +ufw allow from 192.168.1.0/24 to any port 3006 +``` +
diff --git a/docs/guide/pwa.md b/docs/guide/pwa.md new file mode 100644 index 00000000..44478dd6 --- /dev/null +++ b/docs/guide/pwa.md @@ -0,0 +1,187 @@ +# Progressive Web App (PWA) + +HAPI's web interface is a fully-featured PWA that can be installed on your phone for a native app-like experience. + +## What is PWA? + +A Progressive Web App (PWA) is a web application that can be installed on your device and works like a native app: + +- **Home screen icon** - Launch HAPI like any other app +- **Full screen mode** - No browser chrome, immersive experience +- **Offline support** - Basic functionality works without internet +- **Auto-updates** - Always get the latest version + +## Installing HAPI PWA + +### Android (Chrome/Edge) + +1. Open HAPI in Chrome or Edge browser +2. Look for the **"Install HAPI"** banner at the bottom +3. Tap **"Install"** +4. HAPI appears on your home screen + +::: tip +If you don't see the install banner, tap the three-dot menu and select **"Add to Home screen"** or **"Install app"**. +::: + +### iOS (Safari) + +1. Open HAPI in Safari browser +2. Tap the **Share** button (square with arrow) +3. Scroll down and tap **"Add to Home Screen"** +4. Tap **"Add"** in the top right corner + +::: warning +iOS requires Safari for PWA installation. Chrome/Firefox on iOS don't support the "Add to Home Screen" feature. +::: + +### Desktop (Chrome/Edge) + +1. Open HAPI in your browser +2. Click the install icon in the address bar (⊕) +3. Or use the menu: **"Install HAPI..."** +4. HAPI opens as a standalone window + +## PWA Features + +### Offline Mode + +When offline, HAPI can: + +- Display cached session lists +- Show previously loaded messages +- Queue actions for when you're back online + +An offline indicator appears when you lose connection. + +### Auto-Update + +HAPI automatically checks for updates: + +- Updates are checked hourly in the background +- When a new version is available, you'll see a prompt +- Click "Reload" to get the latest version + +### Background Sync + +Actions taken offline are synced when reconnected: + +- Pending messages are sent +- Permission decisions are relayed +- Session state is refreshed + +## Caching Strategy + +HAPI uses intelligent caching: + +| Content | Strategy | Duration | +|---------|----------|----------| +| App shell | Cache first | Until update | +| Sessions API | Network first | 5 minutes | +| Machines API | Network first | 10 minutes | +| Static assets | Cache first | Forever | + +## Notifications + +::: warning Work in Progress +Push notifications are planned for a future release. Currently, notifications work through: +- Telegram bot (recommended) +- Keeping the app open in background +::: + +## Managing Your PWA + +### Check Install Status + +HAPI shows different UI based on install status: + +- **Not installed** - Shows install prompt +- **Installing** - Shows progress indicator +- **Installed** - No prompt shown + +### Uninstalling + +**Android:** +1. Long-press the HAPI icon +2. Drag to "Uninstall" or tap the X + +**iOS:** +1. Long-press the HAPI icon +2. Tap "Remove App" → "Delete App" + +**Desktop:** +1. Open HAPI +2. Click the three-dot menu +3. Select "Uninstall HAPI" + +### Clearing Cache + +If you experience issues: + +1. Open HAPI in browser (not installed version) +2. Open Developer Tools (F12) +3. Go to Application → Storage +4. Click "Clear site data" + +## Best Practices + +### Battery Optimization + +On Android, disable battery optimization for HAPI to ensure: +- Background sync works reliably +- Notifications arrive promptly + +Settings → Apps → HAPI → Battery → Unrestricted + +### Data Usage + +HAPI uses minimal data: + +- Initial load: ~500KB +- Cached after first load +- Only syncs changed data + +### Multiple Devices + +You can install HAPI on multiple devices: + +- All devices use the same server +- Sessions sync across devices +- Same access token works everywhere + +## Troubleshooting + +### Install Button Not Showing + +- Ensure you're using HTTPS (required for PWA) +- Try refreshing the page +- Check if already installed + +### App Not Updating + +1. Close the app completely +2. Reopen and wait for update prompt +3. If stuck, clear cache and reinstall + +### Offline Mode Not Working + +- Ensure you've loaded the app at least once online +- Check if ServiceWorker is registered (DevTools → Application) +- Clear cache and reload + +### iOS-Specific Issues + +- Must use Safari for installation +- No background sync on iOS +- Limited offline capabilities + +## Telegram Mini App Alternative + +If PWA doesn't suit your needs, consider the Telegram Mini App: + +- Works inside Telegram +- No separate installation +- Same features as PWA +- Integrated notifications + +See [Installation Guide](/guide/installation#telegram-setup) for Telegram setup. diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md new file mode 100644 index 00000000..b0275dfc --- /dev/null +++ b/docs/guide/quick-start.md @@ -0,0 +1,55 @@ +# Quick Start + +Get HAPI running in a few minutes. + +## Step 1: Install HAPI + +::: code-group + +```bash [npm] +npm install -g @twsxtd/hapi +``` + +```bash [Homebrew] +brew install tiann/tap/hapi +``` + +```bash [npx (one-off)] +npx @twsxtd/hapi +``` + +::: + +Other install options: [Installation](/guide/installation) + +## Step 2: Start the server + +```bash +hapi server +``` + +On first run, HAPI prints an access token and saves it to `~/.hapi/settings.json`. + +## Step 3: Start a coding session + +```bash +hapi +``` + +This starts Claude Code wrapped with HAPI. The session appears in the web UI. + +## Step 4: Open the UI + +Open your browser: + +``` +http://localhost:3006 +``` + +Or from another device on the same network: + +``` +http://:3006 +``` + +Enter your access token to log in. diff --git a/docs/guide/why-hapi.md b/docs/guide/why-hapi.md new file mode 100644 index 00000000..7d87f633 --- /dev/null +++ b/docs/guide/why-hapi.md @@ -0,0 +1,192 @@ +# Why HAPI? + +[Happy](https://github.com/slopus/happy) is an excellent project. So why build HAPI? + +**The short answer**: Happy is designed for cloud hosting with multiple users. HAPI is designed for self-hosting with a single user. These different goals lead to fundamentally different architectures. + +## TL;DR + +| Aspect | Happy | HAPI | +|--------|-------|------| +| **Design** | Cloud-first | Local-first | +| **Users** | Multi-user | Single user | +| **Data** | Encrypted on server | Never leaves your machine | +| **Deployment** | Multiple services | Single binary | +| **Complexity** | High (E2EE, scaling) | Low (one command) | + +**Choose HAPI if**: You want personal use, data sovereignty, and minimal setup. + +**Choose Happy if**: You need multi-user collaboration or team sharing. + +## Architecture Comparison + +### Happy: Cloud-First + +Happy's cloud design requires: + +- **End-to-end encryption** - Because you don't trust the server +- **Distributed database + cache** - Because you need to scale +- **Complex deployment** - Docker, multiple services, config files + +``` +┌─────────────────────────────────────────────────────────────────────────┐ +│ PUBLIC INTERNET │ +│ │ +│ ┌─────────────┐ ┌─────────────────────────────────┐│ +│ │ │ │ ││ +│ │ Mobile App │◄───── E2EE ───────►│ Cloud Server ││ +│ │ │ │ ││ +│ └─────────────┘ │ ┌─────────────────────────────┐││ +│ │ │ Encrypted Database │││ +│ │ │ (server cannot read) │││ +│ │ └─────────────────────────────┘││ +│ └────────────────┬────────────────┘│ +│ │ E2EE │ +└───────────────────────────────────────────────────────┼─────────────────┘ + ▼ + ┌───────────────────┐ + │ CLI │ + │ (holds the keys) │ + └───────────────────┘ +``` + +### HAPI: Local-First + +HAPI's local design simplifies everything: + +- **No E2EE needed** - Your data never leaves your machine +- **Single embedded database** - No scaling required +- **One-command deployment** - Single binary, zero config + +``` +┌────────────────────────────────────────────────────────────────────────┐ +│ PRIVATE NETWORK │ +│ │ +│ ┌────────────────────────────────────────────────────────────────┐ │ +│ │ Single Process / Binary │ │ +│ │ │ │ +│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ +│ │ │ CLI │◄──►│ Server │◄──►│ Web App │ │ │ +│ │ └──────────┘ └────┬─────┘ └──────────┘ │ │ +│ │ │ │ │ +│ │ ▼ │ │ +│ │ ┌────────────────┐ │ │ +│ │ │ Local Database │ │ │ +│ │ │ (plaintext) │ │ │ +│ │ └────────────────┘ │ │ +│ └────────────────────────────────────────────────────────────────┘ │ +│ │ │ +│ ▼ localhost │ +└────────────────────────────┼───────────────────────────────────────────┘ + │ + ┌────────▼────────┐ + │ Tunnel (Optional)│ + │ for remote access│ + └─────────────────┘ +``` + +## Key Differences + +### Data Location + +| Aspect | Happy | HAPI | +|--------|-------|------| +| **Where data lives** | Cloud server | Your local machine | +| **Who can access** | Server stores encrypted blobs | Only you | +| **Trust model** | Don't trust server → E2EE | Trust local → TLS sufficient | + +### Deployment Model + +**Happy** requires orchestrating multiple components: + +``` +┌───────────────────────────────────────────────────────────────────┐ +│ Distributed Services (4+ components) │ +│ │ +│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ +│ │ Database │ │ Cache │ │ Storage │ │ Server │ │ +│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ +│ │ +│ Requires: Container orchestration, multiple config files │ +└───────────────────────────────────────────────────────────────────┘ +``` + +**HAPI** bundles everything: + +``` +┌───────────────────────────────────────────────────────────────────┐ +│ Single Binary (everything bundled) │ +│ │ +│ ┌─────────────────────────────────────────────────────────────┐ │ +│ │ CLI + Server + Web App + Database (embedded) │ │ +│ └─────────────────────────────────────────────────────────────┘ │ +│ │ +│ Requires: One command to run │ +└───────────────────────────────────────────────────────────────────┘ +``` + +### Security Approach + +| Aspect | Happy | HAPI | +|--------|-------|------| +| **Problem** | Data on untrusted server | External access to local data | +| **Solution** | End-to-end encryption | Tunnel with TLS | +| **Complexity** | High (key management) | Low (tunnel setup) | +| **Data at rest** | Encrypted | Plaintext (protected by OS) | + +## Why Different Architectures? + +### Happy's Constraints + +``` +Goal: Multi-user cloud platform + │ + ├──► Users don't trust your server + │ └──► Must encrypt everything (E2EE) + │ + ├──► Many concurrent users + │ └──► Must scale horizontally + │ + └──► Multiple devices per user + └──► Must sync state across devices +``` + +**Result**: Sophisticated but complex architecture + +### HAPI's Simplifications + +``` +Goal: Single-user self-hosted tool + │ + ├──► Data stays on your machine + │ └──► No E2EE needed + │ + ├──► Only one user + │ └──► No scaling needed + │ + └──► One primary device + └──► Minimal sync logic +``` + +**Result**: Simple and portable architecture + +## Summary + +| Dimension | Happy | HAPI | +|-----------|-------|------| +| **Philosophy** | Cloud-first | Local-first | +| **Data location** | Server (encrypted) | Local (plaintext) | +| **Deployment** | Multiple services | Single binary | +| **Scaling** | Horizontal | None needed | +| **Encryption** | Application-layer E2EE | Transport-layer TLS | +| **Target user** | Teams, cloud users | Individuals, self-hosters | + +## Conclusion + +The architectural differences stem from fundamentally different goals: + +- **Happy**: Built for multi-user cloud scenarios. Solves the "untrusted server" problem with E2EE, at the cost of deployment complexity. + +- **HAPI**: Built for single-user self-hosted scenarios. Solves the "remote access" problem with tunneling, achieving one-command deployment. + +If you want to self-host for personal use, HAPI removes all the complexity that Happy needs for its cloud service. diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..0eca41b1 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,14 @@ +--- +layout: home +title: HAPI Documentation +--- + + diff --git a/docs/package.json b/docs/package.json new file mode 100644 index 00000000..8016aaa6 --- /dev/null +++ b/docs/package.json @@ -0,0 +1,17 @@ +{ + "name": "hapi-docs", + "version": "1.0.0", + "description": "HAPI Documentation", + "type": "module", + "scripts": { + "docs:dev": "vitepress dev", + "docs:build": "vitepress build", + "docs:preview": "vitepress preview" + }, + "keywords": ["hapi", "ai", "agent", "documentation"], + "author": "", + "license": "LGPL-3.0-or-later", + "devDependencies": { + "vitepress": "^1.6.4" + } +} diff --git a/docs/public/favicon.ico b/docs/public/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..11e41499d69b8b1f06a8d8b8765019753f604e58 GIT binary patch literal 4085 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 literal 0 HcmV?d00001 diff --git a/docs/public/logo.svg b/docs/public/logo.svg new file mode 100644 index 00000000..723b90f8 --- /dev/null +++ b/docs/public/logo.svg @@ -0,0 +1 @@ + \ No newline at end of file