mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
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)
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
|
||||
# Build output
|
||||
**/dist/
|
||||
**/.vitepress/cache/
|
||||
**/dist-exe/
|
||||
**/*.tsbuildinfo
|
||||
server/src/web/embeddedAssets.generated.ts
|
||||
|
||||
@@ -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
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>More options (Telegram + environment variables)</summary>
|
||||
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 .
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
## 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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -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.
|
||||
+353
@@ -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=="],
|
||||
}
|
||||
}
|
||||
@@ -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://<your-computer-ip>: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).
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
<details>
|
||||
<summary>npx (no install)</summary>
|
||||
|
||||
```bash
|
||||
npx @twsxtd/hapi
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Prebuilt binary</summary>
|
||||
|
||||
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/
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Docker (server only)</summary>
|
||||
|
||||
```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
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Build from source</summary>
|
||||
|
||||
```bash
|
||||
git clone https://github.com/tiann/hapi.git
|
||||
cd hapi
|
||||
bun install
|
||||
bun build:single-exe
|
||||
|
||||
./cli/dist/hapi
|
||||
```
|
||||
</details>
|
||||
|
||||
## 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`
|
||||
|
||||
<details>
|
||||
<summary>Config files</summary>
|
||||
|
||||
```
|
||||
~/.hapi/
|
||||
├── settings.json # Main configuration
|
||||
├── hapi.db # SQLite database (server)
|
||||
├── daemon.state.json # Daemon process state
|
||||
└── logs/ # Log files
|
||||
```
|
||||
</details>
|
||||
|
||||
<details>
|
||||
<summary>Environment variables</summary>
|
||||
|
||||
| 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 |
|
||||
</details>
|
||||
|
||||
## 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
|
||||
|
||||
<details>
|
||||
<summary>Firewall example (ufw)</summary>
|
||||
|
||||
```bash
|
||||
ufw allow from 192.168.1.0/24 to any port 3006
|
||||
```
|
||||
</details>
|
||||
@@ -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.
|
||||
@@ -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://<your-computer-ip>:3006
|
||||
```
|
||||
|
||||
Enter your access token to log in.
|
||||
@@ -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.
|
||||
@@ -0,0 +1,14 @@
|
||||
---
|
||||
layout: home
|
||||
title: HAPI Documentation
|
||||
---
|
||||
|
||||
<script setup>
|
||||
import { onMounted } from 'vue'
|
||||
import { useRouter } from 'vitepress'
|
||||
|
||||
onMounted(() => {
|
||||
const router = useRouter()
|
||||
router.go('/guide/quick-start')
|
||||
})
|
||||
</script>
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 447 KiB |
Reference in New Issue
Block a user