From 37e10a831bd05e06d0c78f79d2bb459309a21e42 Mon Sep 17 00:00:00 2001 From: weishu Date: Tue, 27 Jan 2026 19:51:21 +0800 Subject: [PATCH] feat: rename server package to hub Rename the `server/` directory to `hub/` and update all references across CLI, docs, web, and workspace configuration. --- .gitignore | 6 +- AGENTS.md | 6 +- README.md | 4 +- bun.lock | 8 +- cli/CLAUDE.md | 14 +-- cli/README.md | 17 +-- cli/scripts/build-executable.ts | 8 +- cli/scripts/write-embedded-assets-stub.ts | 2 +- cli/src/api/apiMachine.ts | 2 +- cli/src/codex/codexLocalLauncher.ts | 2 +- cli/src/commands/claude.ts | 15 +-- cli/src/commands/{server.ts => hub.ts} | 10 +- cli/src/commands/notify.ts | 2 +- cli/src/commands/registry.ts | 5 +- cli/src/runner/README.md | 12 +-- cli/src/runner/runner.integration.test.ts | 6 +- cli/src/runtime/assets.ts | 4 +- cli/src/runtime/embeddedAssets.bun.ts | 12 +-- cli/src/utils/autoStartServer.ts | 40 +++---- cli/tsconfig.json | 4 +- docs/guide/faq.md | 20 ++-- docs/guide/how-it-works.md | 28 ++--- docs/guide/installation.md | 102 +++++++++--------- docs/guide/namespace.md | 8 +- docs/guide/quick-start.md | 6 +- docs/guide/voice-assistant.md | 14 +-- {server => hub}/README.md | 32 +++--- {server => hub}/package.json | 2 +- {server => hub}/scripts/cleanup-sessions.ts | 28 ++--- {server => hub}/scripts/download-tunwg.ts | 2 +- .../scripts/generate-embedded-web-assets.ts | 4 +- {server => hub}/src/config/cliApiToken.ts | 0 {server => hub}/src/config/generators.ts | 0 {server => hub}/src/config/jwtSecret.ts | 0 {server => hub}/src/config/ownerId.ts | 0 {server => hub}/src/config/serverSettings.ts | 6 +- {server => hub}/src/config/settings.ts | 0 {server => hub}/src/config/vapidKeys.ts | 0 {server => hub}/src/configuration.ts | 14 +-- {server => hub}/src/index.ts | 36 +++---- .../src/notifications/eventParsing.test.ts | 0 .../src/notifications/eventParsing.ts | 0 .../src/notifications/notificationHub.test.ts | 0 .../src/notifications/notificationHub.ts | 0 .../src/notifications/notificationTypes.ts | 0 .../src/notifications/sessionInfo.ts | 0 .../src/push/pushNotificationChannel.ts | 0 {server => hub}/src/push/pushService.ts | 0 .../src/socket/handlers/cli/index.ts | 0 .../socket/handlers/cli/machineHandlers.ts | 0 .../src/socket/handlers/cli/rpcHandlers.ts | 0 .../socket/handlers/cli/sessionHandlers.ts | 0 .../socket/handlers/cli/terminalHandlers.ts | 0 .../src/socket/handlers/terminal.test.ts | 0 .../src/socket/handlers/terminal.ts | 0 {server => hub}/src/socket/rpcRegistry.ts | 0 {server => hub}/src/socket/server.ts | 0 {server => hub}/src/socket/socketTypes.ts | 0 .../src/socket/terminalRegistry.ts | 0 {server => hub}/src/sse/sseManager.test.ts | 0 {server => hub}/src/sse/sseManager.ts | 0 {server => hub}/src/store/index.ts | 0 {server => hub}/src/store/json.ts | 0 {server => hub}/src/store/machineStore.ts | 0 {server => hub}/src/store/machines.ts | 0 {server => hub}/src/store/messageStore.ts | 0 {server => hub}/src/store/messages.ts | 0 {server => hub}/src/store/namespace.test.ts | 0 {server => hub}/src/store/pushStore.ts | 0 .../src/store/pushSubscriptions.ts | 0 {server => hub}/src/store/sessionStore.ts | 0 {server => hub}/src/store/sessions.ts | 0 {server => hub}/src/store/types.ts | 0 {server => hub}/src/store/userStore.ts | 0 {server => hub}/src/store/users.ts | 0 {server => hub}/src/store/versionedUpdates.ts | 0 {server => hub}/src/sync/aliveTime.ts | 0 {server => hub}/src/sync/eventPublisher.ts | 0 {server => hub}/src/sync/machineCache.ts | 0 {server => hub}/src/sync/messageService.ts | 0 {server => hub}/src/sync/rpcGateway.ts | 0 {server => hub}/src/sync/sessionCache.ts | 0 {server => hub}/src/sync/syncEngine.ts | 4 +- {server => hub}/src/sync/todos.ts | 0 {server => hub}/src/telegram/bot.ts | 0 {server => hub}/src/telegram/callbacks.ts | 0 {server => hub}/src/telegram/renderer.ts | 0 {server => hub}/src/telegram/sessionView.ts | 0 {server => hub}/src/tunnel/index.ts | 0 {server => hub}/src/tunnel/tlsGate.ts | 0 {server => hub}/src/tunnel/tunnelManager.ts | 2 +- .../src/types/webAssetImports.d.ts | 0 {server => hub}/src/utils/accessToken.test.ts | 0 {server => hub}/src/utils/accessToken.ts | 0 {server => hub}/src/utils/bunCompiled.ts | 0 {server => hub}/src/utils/crypto.ts | 0 .../src/visibility/visibilityTracker.ts | 0 .../src/web/embeddedAssets.generated.d.ts | 0 {server => hub}/src/web/embeddedAssets.ts | 0 {server => hub}/src/web/middleware/auth.ts | 0 {server => hub}/src/web/routes/auth.ts | 0 {server => hub}/src/web/routes/bind.ts | 0 {server => hub}/src/web/routes/cli.ts | 0 {server => hub}/src/web/routes/events.ts | 0 {server => hub}/src/web/routes/git.ts | 0 {server => hub}/src/web/routes/guards.ts | 0 {server => hub}/src/web/routes/machines.ts | 0 {server => hub}/src/web/routes/messages.ts | 0 {server => hub}/src/web/routes/permissions.ts | 0 {server => hub}/src/web/routes/push.ts | 0 {server => hub}/src/web/routes/sessions.ts | 0 {server => hub}/src/web/routes/voice.ts | 0 {server => hub}/src/web/server.ts | 8 +- {server => hub}/src/web/telegramInitData.ts | 0 {server => hub}/tools/.gitignore | 0 {server => hub}/tools/tunwg/LICENSE | 0 {server => hub}/tsconfig.json | 0 package.json | 26 ++--- web/README.md | 14 +-- web/src/App.tsx | 3 +- web/src/api/voice.ts | 12 +-- web/src/components/LoginPrompt.tsx | 2 +- web/src/hooks/useServerUrl.ts | 20 ++-- web/src/lib/locales/en.ts | 8 +- website/src/pages/Home.tsx | 6 +- 125 files changed, 301 insertions(+), 285 deletions(-) rename cli/src/commands/{server.ts => hub.ts} (81%) rename {server => hub}/README.md (86%) rename {server => hub}/package.json (97%) rename {server => hub}/scripts/cleanup-sessions.ts (93%) rename {server => hub}/scripts/download-tunwg.ts (98%) rename {server => hub}/scripts/generate-embedded-web-assets.ts (95%) rename {server => hub}/src/config/cliApiToken.ts (100%) rename {server => hub}/src/config/generators.ts (100%) rename {server => hub}/src/config/jwtSecret.ts (100%) rename {server => hub}/src/config/ownerId.ts (100%) rename {server => hub}/src/config/serverSettings.ts (97%) rename {server => hub}/src/config/settings.ts (100%) rename {server => hub}/src/config/vapidKeys.ts (100%) rename {server => hub}/src/configuration.ts (94%) rename {server => hub}/src/index.ts (87%) rename {server => hub}/src/notifications/eventParsing.test.ts (100%) rename {server => hub}/src/notifications/eventParsing.ts (100%) rename {server => hub}/src/notifications/notificationHub.test.ts (100%) rename {server => hub}/src/notifications/notificationHub.ts (100%) rename {server => hub}/src/notifications/notificationTypes.ts (100%) rename {server => hub}/src/notifications/sessionInfo.ts (100%) rename {server => hub}/src/push/pushNotificationChannel.ts (100%) rename {server => hub}/src/push/pushService.ts (100%) rename {server => hub}/src/socket/handlers/cli/index.ts (100%) rename {server => hub}/src/socket/handlers/cli/machineHandlers.ts (100%) rename {server => hub}/src/socket/handlers/cli/rpcHandlers.ts (100%) rename {server => hub}/src/socket/handlers/cli/sessionHandlers.ts (100%) rename {server => hub}/src/socket/handlers/cli/terminalHandlers.ts (100%) rename {server => hub}/src/socket/handlers/terminal.test.ts (100%) rename {server => hub}/src/socket/handlers/terminal.ts (100%) rename {server => hub}/src/socket/rpcRegistry.ts (100%) rename {server => hub}/src/socket/server.ts (100%) rename {server => hub}/src/socket/socketTypes.ts (100%) rename {server => hub}/src/socket/terminalRegistry.ts (100%) rename {server => hub}/src/sse/sseManager.test.ts (100%) rename {server => hub}/src/sse/sseManager.ts (100%) rename {server => hub}/src/store/index.ts (100%) rename {server => hub}/src/store/json.ts (100%) rename {server => hub}/src/store/machineStore.ts (100%) rename {server => hub}/src/store/machines.ts (100%) rename {server => hub}/src/store/messageStore.ts (100%) rename {server => hub}/src/store/messages.ts (100%) rename {server => hub}/src/store/namespace.test.ts (100%) rename {server => hub}/src/store/pushStore.ts (100%) rename {server => hub}/src/store/pushSubscriptions.ts (100%) rename {server => hub}/src/store/sessionStore.ts (100%) rename {server => hub}/src/store/sessions.ts (100%) rename {server => hub}/src/store/types.ts (100%) rename {server => hub}/src/store/userStore.ts (100%) rename {server => hub}/src/store/users.ts (100%) rename {server => hub}/src/store/versionedUpdates.ts (100%) rename {server => hub}/src/sync/aliveTime.ts (100%) rename {server => hub}/src/sync/eventPublisher.ts (100%) rename {server => hub}/src/sync/machineCache.ts (100%) rename {server => hub}/src/sync/messageService.ts (100%) rename {server => hub}/src/sync/rpcGateway.ts (100%) rename {server => hub}/src/sync/sessionCache.ts (100%) rename {server => hub}/src/sync/syncEngine.ts (99%) rename {server => hub}/src/sync/todos.ts (100%) rename {server => hub}/src/telegram/bot.ts (100%) rename {server => hub}/src/telegram/callbacks.ts (100%) rename {server => hub}/src/telegram/renderer.ts (100%) rename {server => hub}/src/telegram/sessionView.ts (100%) rename {server => hub}/src/tunnel/index.ts (100%) rename {server => hub}/src/tunnel/tlsGate.ts (100%) rename {server => hub}/src/tunnel/tunnelManager.ts (99%) rename {server => hub}/src/types/webAssetImports.d.ts (100%) rename {server => hub}/src/utils/accessToken.test.ts (100%) rename {server => hub}/src/utils/accessToken.ts (100%) rename {server => hub}/src/utils/bunCompiled.ts (100%) rename {server => hub}/src/utils/crypto.ts (100%) rename {server => hub}/src/visibility/visibilityTracker.ts (100%) rename {server => hub}/src/web/embeddedAssets.generated.d.ts (100%) rename {server => hub}/src/web/embeddedAssets.ts (100%) rename {server => hub}/src/web/middleware/auth.ts (100%) rename {server => hub}/src/web/routes/auth.ts (100%) rename {server => hub}/src/web/routes/bind.ts (100%) rename {server => hub}/src/web/routes/cli.ts (100%) rename {server => hub}/src/web/routes/events.ts (100%) rename {server => hub}/src/web/routes/git.ts (100%) rename {server => hub}/src/web/routes/guards.ts (100%) rename {server => hub}/src/web/routes/machines.ts (100%) rename {server => hub}/src/web/routes/messages.ts (100%) rename {server => hub}/src/web/routes/permissions.ts (100%) rename {server => hub}/src/web/routes/push.ts (100%) rename {server => hub}/src/web/routes/sessions.ts (100%) rename {server => hub}/src/web/routes/voice.ts (100%) rename {server => hub}/src/web/server.ts (97%) rename {server => hub}/src/web/telegramInitData.ts (100%) rename {server => hub}/tools/.gitignore (100%) rename {server => hub}/tools/tunwg/LICENSE (100%) rename {server => hub}/tsconfig.json (100%) diff --git a/.gitignore b/.gitignore index 96c9be9e..40f16eed 100644 --- a/.gitignore +++ b/.gitignore @@ -6,11 +6,11 @@ **/.vitepress/cache/ **/dist-exe/ **/*.tsbuildinfo -server/src/web/embeddedAssets.generated.ts +hub/src/web/embeddedAssets.generated.ts # Downloaded tools (fetched at build time) -server/tools/tunwg/tunwg-* -server/tools/tunwg/*.exe +hub/tools/tunwg/tunwg-* +hub/tools/tunwg/*.exe # Env files (can contain secrets) **/.env diff --git a/AGENTS.md b/AGENTS.md index bc145e87..0975a023 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,13 +6,13 @@ Short guide for AI agents in this repo. Prefer progressive loading: start with t ## Repo layout - `cli/` - hapi CLI, runner, Codex/MCP tooling -- `server/` - Telegram bot + HTTP API + Socket.IO + SSE +- `hub/` - Telegram bot + HTTP API + Socket.IO + SSE - `web/` - React Mini App / PWA ## Reference docs - `README.md` (user overview) - `cli/README.md` (CLI behavior and config) -- `server/README.md` (server setup and architecture) +- `hub/README.md` (hub setup and architecture) - `web/README.md` (web app behavior and dev workflow) - `localdocs/` (optional deep dives) @@ -30,7 +30,7 @@ Short guide for AI agents in this repo. Prefer progressive loading: start with t ## Key source dirs - `cli/src/api/`, `cli/src/claude/`, `cli/src/commands/`, `cli/src/codex/` -- `server/src/web/`, `server/src/socket/`, `server/src/telegram/`, `server/src/sync/` +- `hub/src/web/`, `hub/src/socket/`, `hub/src/telegram/`, `hub/src/sync/` - `web/src/components/`, `web/src/api/`, `web/src/hooks/` ## Critical Thinking diff --git a/README.md b/README.md index 121755be..6acf304b 100644 --- a/README.md +++ b/README.md @@ -20,10 +20,12 @@ https://github.com/user-attachments/assets/38230353-94c6-4dbe-9c29-b2a2cc457546 ## Getting Started ```bash -npx @twsxtd/hapi server --relay # start server with E2E encrypted relay +npx @twsxtd/hapi hub --relay # start hub with E2E encrypted relay npx @twsxtd/hapi # run claude code ``` +`hapi server` remains supported as an alias. + The terminal will display a URL and QR code. Scan the QR code with your phone or open the URL to access. > The relay uses WireGuard + TLS for end-to-end encryption. Your data is encrypted from your device to your machine. diff --git a/bun.lock b/bun.lock index a4124eda..7b7414f4 100644 --- a/bun.lock +++ b/bun.lock @@ -57,8 +57,8 @@ "vitepress": "^1.6.4", }, }, - "server": { - "name": "hapi-server", + "hub": { + "name": "hapi-hub", "version": "0.1.0", "dependencies": { "@hapi/protocol": "workspace:*", @@ -858,6 +858,8 @@ "@twsxtd/hapi-linux-x64": ["@twsxtd/hapi-linux-x64@0.13.0", "", { "os": "linux", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-09RiknUv0CKcjDFMk7RhsImYGRiufJ+/0zPifsVuVMd9e67hlO/2ClJgjF+Ez5Gg348WZX1loi6FJZf0CLIxuw=="], + "@twsxtd/hapi-win32-x64": ["@twsxtd/hapi-win32-x64@0.13.0", "", { "os": "win32", "cpu": "x64", "bin": { "hapi": "bin/hapi.exe" } }, "sha512-d0Dk/VVTi6clLXvHa/rpqR8z2WJqSDOHF384N2n1a9iL8J9amLP/ZckfBm07iz2+xMZcB4r/JgPAedo2psxfGg=="], + "@types/babel__core": ["@types/babel__core@7.20.5", "", { "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA=="], "@types/babel__generator": ["@types/babel__generator@7.27.0", "", { "dependencies": { "@babel/types": "^7.0.0" } }, "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg=="], @@ -1556,7 +1558,7 @@ "hapi-docs": ["hapi-docs@workspace:docs"], - "hapi-server": ["hapi-server@workspace:server"], + "hapi-hub": ["hapi-hub@workspace:hub"], "hapi-web": ["hapi-web@workspace:web"], diff --git a/cli/CLAUDE.md b/cli/CLAUDE.md index 8d3a4d02..40079358 100644 --- a/cli/CLAUDE.md +++ b/cli/CLAUDE.md @@ -2,10 +2,10 @@ ## Project Overview -HAPI CLI (`hapi`) is a command-line tool that wraps Claude Code to enable remote control and session sharing via `hapi-server` (Telegram Bot + Mini App). It's part of a two-component system: +HAPI CLI (`hapi`) is a command-line tool that wraps Claude Code to enable remote control and session sharing via `hapi-hub` (Telegram Bot + Mini App). It's part of a two-component system: 1. **hapi** (this project) - CLI wrapper for Claude Code -2. **hapi-server** - Public server (Socket.IO + REST + SQLite) + Telegram Mini App +2. **hapi-hub** - Hub service (Socket.IO + REST + SQLite) + Telegram Mini App ## Code Style Preferences @@ -96,29 +96,29 @@ User interface components. ## Data Flow 1. **Authentication**: - - Use `CLI_API_TOKEN` to authenticate to `hapi-server` (REST + Socket.IO) + - Use `CLI_API_TOKEN` to authenticate to `hapi-hub` (REST + Socket.IO) 2. **Session Creation**: - Create/load session via `POST /cli/sessions` → Establish Socket.IO `/cli` connection 3. **Message Flow**: - - Local mode: terminal/SDK → hapi CLI → hapi-server → Telegram Mini App + - Local mode: terminal/SDK → hapi CLI → hapi-hub → Telegram Mini App 4. **Permission Handling**: - - Claude requests permission → hapi CLI exposes RPC handlers → Mini App calls REST → hapi-server relays RPC to hapi CLI + - Claude requests permission → hapi CLI exposes RPC handlers → Mini App calls REST → hapi-hub relays RPC to hapi CLI ## Key Design Decisions 1. **File-based logging**: Prevents interference with Claude's terminal UI 2. **Dual Claude integration**: Process spawning for interactive, SDK for remote -3. **No E2E encryption**: Use HTTPS/TLS for `hapi-server` deployments +3. **No E2E encryption**: Use HTTPS/TLS for `hapi-hub` deployments 4. **Session persistence**: Allows resuming sessions across restarts 5. **Optimistic concurrency**: Handles distributed state updates gracefully ## Security Considerations - `CLI_API_TOKEN` is a shared secret; treat it like a password. -- No end-to-end encryption: use HTTPS/TLS for `hapi-server` deployments. +- No end-to-end encryption: use HTTPS/TLS for `hapi-hub` deployments. - Session isolation through unique session IDs. ## Dependencies diff --git a/cli/README.md b/cli/README.md index a58afdce..dfd86966 100644 --- a/cli/README.md +++ b/cli/README.md @@ -1,10 +1,10 @@ # hapi CLI -Run Claude Code, Codex, or Gemini sessions from your terminal and control them remotely through the hapi server. +Run Claude Code, Codex, or Gemini sessions from your terminal and control them remotely through the hapi hub. ## What it does -- Starts Claude Code sessions and registers them with hapi-server. +- Starts Claude Code sessions and registers them with hapi-hub. - Starts Codex mode for OpenAI-based sessions. - Starts Gemini mode via ACP (Anthropic Code Plugins). - Provides an MCP stdio bridge for external tools. @@ -13,7 +13,7 @@ Run Claude Code, Codex, or Gemini sessions from your terminal and control them r ## Typical flow -1. Start the server and set env vars (see ../server/README.md). +1. Start the hub and set env vars (see ../hub/README.md). 2. Set the same CLI_API_TOKEN on this machine or run `hapi auth login`. 3. Run `hapi` to start a session. 4. Use the web app or Telegram Mini App to monitor and control. @@ -25,7 +25,7 @@ Run Claude Code, Codex, or Gemini sessions from your terminal and control them r - `hapi` - Start a Claude Code session (passes through Claude CLI flags). See `src/index.ts`. - `hapi codex` - Start Codex mode. See `src/codex/runCodex.ts`. - `hapi gemini` - Start Gemini mode via ACP. See `src/agent/runners/runAgentSession.ts`. - Note: Gemini runs in remote mode only; it waits for messages from the server UI/Telegram. + Note: Gemini runs in remote mode only; it waits for messages from the hub UI/Telegram. ### Authentication @@ -58,7 +58,8 @@ See `src/ui/doctor.ts`. ### Other - `hapi mcp` - Start MCP stdio bridge. See `src/codex/happyMcpStdioBridge.ts`. -- `hapi server` - Start the bundled server (single binary workflow). +- `hapi hub` - Start the bundled hub (single binary workflow). +- `hapi server` - Alias for `hapi hub`. ## Configuration @@ -66,8 +67,8 @@ See `src/configuration.ts` for all options. ### Required -- `CLI_API_TOKEN` - Shared secret; must match the server. Can be set via env or `~/.hapi/settings.json` (env wins). -- `HAPI_API_URL` - Server base URL (default: http://localhost:3006). +- `CLI_API_TOKEN` - Shared secret; must match the hub. Can be set via env or `~/.hapi/settings.json` (env wins). +- `HAPI_API_URL` - Hub base URL (default: http://localhost:3006). ### Optional @@ -123,5 +124,5 @@ bun run build:single-exe ## Related docs -- `../server/README.md` +- `../hub/README.md` - `../web/README.md` diff --git a/cli/scripts/build-executable.ts b/cli/scripts/build-executable.ts index fe4e04e1..7c3a9635 100644 --- a/cli/scripts/build-executable.ts +++ b/cli/scripts/build-executable.ts @@ -126,7 +126,7 @@ function resolveOutdir(projectRoot: string, outdir: string): string { } function writeStubEmbeddedAssets(workspaceRoot: string): void { - const outputPath = join(workspaceRoot, 'server', 'src', 'web', 'embeddedAssets.generated.ts'); + const outputPath = join(workspaceRoot, 'hub', 'src', 'web', 'embeddedAssets.generated.ts'); const contents = [ '// This file is generated by cli/scripts/build-executable.ts when --with-web-assets is not used.', '// It intentionally contains no embedded assets.', @@ -153,16 +153,16 @@ function isStubEmbeddedAssets(manifestPath: string): boolean { } function ensureEmbeddedAssetsManifest(workspaceRoot: string, includeWebAssets: boolean): void { - const manifestPath = join(workspaceRoot, 'server', 'src', 'web', 'embeddedAssets.generated.ts'); + const manifestPath = join(workspaceRoot, 'hub', 'src', 'web', 'embeddedAssets.generated.ts'); if (includeWebAssets) { if (!existsSync(manifestPath)) { throw new Error( - 'Missing embedded web asset manifest. Run `bun run build:web` and `cd server && bun run generate:embedded-web-assets`, or `bun run build:single-exe` from the repo root.' + 'Missing embedded web asset manifest. Run `bun run build:web` and `cd hub && bun run generate:embedded-web-assets`, or `bun run build:single-exe` from the repo root.' ); } if (isStubEmbeddedAssets(manifestPath)) { throw new Error( - 'Embedded web asset manifest is a stub. Run `bun run build:web` and `cd server && bun run generate:embedded-web-assets`, or `bun run build:single-exe` from the repo root.' + 'Embedded web asset manifest is a stub. Run `bun run build:web` and `cd hub && bun run generate:embedded-web-assets`, or `bun run build:single-exe` from the repo root.' ); } return; diff --git a/cli/scripts/write-embedded-assets-stub.ts b/cli/scripts/write-embedded-assets-stub.ts index 44800d99..7942f413 100644 --- a/cli/scripts/write-embedded-assets-stub.ts +++ b/cli/scripts/write-embedded-assets-stub.ts @@ -5,7 +5,7 @@ import { fileURLToPath } from 'node:url'; const scriptDir = dirname(fileURLToPath(import.meta.url)); const cliRoot = join(scriptDir, '..'); const workspaceRoot = join(cliRoot, '..'); -const outputPath = join(workspaceRoot, 'server', 'src', 'web', 'embeddedAssets.generated.ts'); +const outputPath = join(workspaceRoot, 'hub', 'src', 'web', 'embeddedAssets.generated.ts'); const contents = [ '// This file is generated by cli/scripts/build-executable.ts when --with-web-assets is not used.', '// It intentionally contains no embedded assets.', diff --git a/cli/src/api/apiMachine.ts b/cli/src/api/apiMachine.ts index cc02f9d4..e38d0889 100644 --- a/cli/src/api/apiMachine.ts +++ b/cli/src/api/apiMachine.ts @@ -1,5 +1,5 @@ /** - * WebSocket client for machine/runner communication with hapi-server + * WebSocket client for machine/runner communication with hapi-hub */ import { io, type Socket } from 'socket.io-client' diff --git a/cli/src/codex/codexLocalLauncher.ts b/cli/src/codex/codexLocalLauncher.ts index 28e92563..69f8bc74 100644 --- a/cli/src/codex/codexLocalLauncher.ts +++ b/cli/src/codex/codexLocalLauncher.ts @@ -13,7 +13,7 @@ export async function codexLocalLauncher(session: CodexSession): Promise<'switch const exitFuture = new Future(); const resumeSessionId = session.sessionId; - // Start hapi server for MCP bridge (same as remote mode) + // Start hapi hub for MCP bridge (same as remote mode) const { server: happyServer, mcpServers } = await buildHapiMcpBridge(session.client); logger.debug(`[codex-local]: Started hapi MCP bridge server at ${happyServer.url}`); diff --git a/cli/src/commands/claude.ts b/cli/src/commands/claude.ts index 4b38f2b3..67b2499a 100644 --- a/cli/src/commands/claude.ts +++ b/cli/src/commands/claude.ts @@ -75,8 +75,9 @@ ${chalk.bold('Usage:')} hapi mcp Start MCP stdio bridge hapi connect (not available in direct-connect mode) hapi notify (not available in direct-connect mode) - hapi server Start the API + web server - hapi server --relay Start with public relay + hapi hub Start the API + web hub + hapi hub --relay Start with public relay + hapi server Alias for hapi hub hapi runner Manage background service that allows to spawn new sessions away from your computer hapi doctor System diagnostics & troubleshooting @@ -146,9 +147,9 @@ ${chalk.bold.cyan('Claude Code Options (from `claude --help`):')} messageLower.includes('enotfound') || messageLower.includes('network error') ) { - console.error(chalk.yellow('Unable to connect to HAPI server')) - console.error(chalk.gray(` Server URL: ${configuration.apiUrl}`)) - console.error(chalk.gray(' Please check your network connection or server status')) + console.error(chalk.yellow('Unable to connect to HAPI hub')) + console.error(chalk.gray(` Hub URL: ${configuration.apiUrl}`)) + console.error(chalk.gray(' Please check your network connection or hub status')) } else if (httpStatus === 403 && responseErrorText === 'Machine access denied') { console.error(chalk.red('Machine access denied.')) console.error(chalk.gray(' This machineId is already registered under a different namespace.')) @@ -171,9 +172,9 @@ ${chalk.bold.cyan('Claude Code Options (from `claude --help`):')} if (serverProtocolVersion !== undefined && serverProtocolVersion !== PROTOCOL_VERSION) { if (serverProtocolVersion < PROTOCOL_VERSION) { - console.error(chalk.yellow(` Hint: server protocol version (${serverProtocolVersion}) is behind CLI (${PROTOCOL_VERSION}). Please update the server.`)) + console.error(chalk.yellow(` Hint: hub protocol version (${serverProtocolVersion}) is behind CLI (${PROTOCOL_VERSION}). Please update the hub.`)) } else { - console.error(chalk.yellow(` Hint: CLI protocol version (${PROTOCOL_VERSION}) is behind server (${serverProtocolVersion}). Please update the CLI.`)) + console.error(chalk.yellow(` Hint: CLI protocol version (${PROTOCOL_VERSION}) is behind hub (${serverProtocolVersion}). Please update the CLI.`)) } } diff --git a/cli/src/commands/server.ts b/cli/src/commands/hub.ts similarity index 81% rename from cli/src/commands/server.ts rename to cli/src/commands/hub.ts index 0fa73b85..bcebef4b 100644 --- a/cli/src/commands/server.ts +++ b/cli/src/commands/hub.ts @@ -1,7 +1,7 @@ import chalk from 'chalk' import type { CommandDefinition, CommandContext } from './types' -function parseServerArgs(args: string[]): { host?: string; port?: string } { +function parseHubArgs(args: string[]): { host?: string; port?: string } { const result: { host?: string; port?: string } = {} for (let i = 0; i < args.length; i++) { @@ -20,12 +20,12 @@ function parseServerArgs(args: string[]): { host?: string; port?: string } { return result } -export const serverCommand: CommandDefinition = { - name: 'server', +export const hubCommand: CommandDefinition = { + name: 'hub', requiresRuntimeAssets: true, run: async (context: CommandContext) => { try { - const { host, port } = parseServerArgs(context.commandArgs) + const { host, port } = parseHubArgs(context.commandArgs) if (host) { process.env.WEBAPP_HOST = host @@ -33,7 +33,7 @@ export const serverCommand: CommandDefinition = { if (port) { process.env.WEBAPP_PORT = port } - await import('../../../server/src/index') + await import('../../../hub/src/index') } catch (error) { console.error(chalk.red('Error:'), error instanceof Error ? error.message : 'Unknown error') if (process.env.DEBUG) { diff --git a/cli/src/commands/notify.ts b/cli/src/commands/notify.ts index ee2480e7..ddadfbfd 100644 --- a/cli/src/commands/notify.ts +++ b/cli/src/commands/notify.ts @@ -6,7 +6,7 @@ export const notifyCommand: CommandDefinition = { requiresRuntimeAssets: true, run: async () => { console.error(chalk.red('The `hapi notify` command is not available in direct-connect mode.')) - console.error(chalk.gray('Use Telegram notifications from hapi-server instead.')) + console.error(chalk.gray('Use Telegram notifications from hapi-hub instead.')) process.exit(1) } } diff --git a/cli/src/commands/registry.ts b/cli/src/commands/registry.ts index 2a2a1e87..268562a3 100644 --- a/cli/src/commands/registry.ts +++ b/cli/src/commands/registry.ts @@ -8,7 +8,7 @@ import { geminiCommand } from './gemini' import { hookForwarderCommand } from './hookForwarder' import { mcpCommand } from './mcp' import { notifyCommand } from './notify' -import { serverCommand } from './server' +import { hubCommand } from './hub' import type { CommandContext, CommandDefinition } from './types' const COMMANDS: CommandDefinition[] = [ @@ -17,7 +17,8 @@ const COMMANDS: CommandDefinition[] = [ codexCommand, geminiCommand, mcpCommand, - serverCommand, + hubCommand, + { ...hubCommand, name: 'server' }, hookForwarderCommand, doctorCommand, runnerCommand, diff --git a/cli/src/runner/README.md b/cli/src/runner/README.md index f4d1e4e6..0cba44ce 100644 --- a/cli/src/runner/README.md +++ b/cli/src/runner/README.md @@ -280,7 +280,7 @@ All data is plain JSON over TLS; authentication is `CLI_API_TOKEN` (no end-to-en ### Test Environment - Requires `.env.integration-test` -- Uses local hapi-server (http://localhost:3006) +- Uses local hapi-hub (http://localhost:3006) - Separate `~/.hapi-dev-test` home directory ### Key Test Scenarios @@ -296,7 +296,7 @@ All data is plain JSON over TLS; authentication is `CLI_API_TOKEN` (no end-to-en # Machine Sync Architecture - Separated Metadata & Runner State -> Direct-connect note: the "server" is `hapi-server`, payloads are plain JSON (no base64/encryption), +> Direct-connect note: the "hub" is `hapi-hub`, payloads are plain JSON (no base64/encryption), > and authentication uses `CLI_API_TOKEN` (REST `Authorization: Bearer ...` + Socket.IO `handshake.auth.token`). ## Data Structure (Similar to Session's metadata + agentState) @@ -327,7 +327,7 @@ interface RunnerState { Checks if machine ID exists in settings: - If not: creates ID locally only (so sessions can reference it) -- Does NOT create machine on server - that's runner's job +- Does NOT create machine on hub - that's runner's job - CLI doesn't manage machine details - all API & schema live in runner subpackage ## 2. Runner Startup - Initial Registration @@ -444,10 +444,10 @@ socket.emit('machine-update-metadata', { }, callback) ``` -## 5. Mini App RPC Calls (via hapi-server) +## 5. Mini App RPC Calls (via hapi-hub) -The Telegram Mini App calls REST endpoints on `hapi-server` (for example `POST /api/machines/:id/spawn`). -`hapi-server` then relays those requests to the runner via Socket.IO `rpc-request` on the `/cli` namespace. +The Telegram Mini App calls REST endpoints on `hapi-hub` (for example `POST /api/machines/:id/spawn`). +`hapi-hub` then relays those requests to the runner via Socket.IO `rpc-request` on the `/cli` namespace. RPC method naming (machine-scoped) uses a `${machineId}:` prefix, for example: - `${machineId}:spawn-happy-session` diff --git a/cli/src/runner/runner.integration.test.ts b/cli/src/runner/runner.integration.test.ts index 207056fe..94c3f48a 100644 --- a/cli/src/runner/runner.integration.test.ts +++ b/cli/src/runner/runner.integration.test.ts @@ -11,8 +11,8 @@ * * The integration test environment uses .env.integration-test which sets: * - HAPI_HOME=~/.hapi-dev-test (DIFFERENT from dev's ~/.hapi-dev!) - * - HAPI_API_URL=http://localhost:3006 (local hapi-server) - * - CLI_API_TOKEN=... (must match the server) + * - HAPI_API_URL=http://localhost:3006 (local hapi-hub) + * - CLI_API_TOKEN=... (must match the hub) */ import { describe, it, expect, beforeEach, afterEach } from 'vitest'; @@ -48,7 +48,7 @@ async function waitFor( throw new Error('Timeout waiting for condition'); } -// Check if dev server is running and properly configured +// Check if dev hub is running and properly configured async function isServerHealthy(): Promise { try { if (!configuration.cliApiToken) { diff --git a/cli/src/runtime/assets.ts b/cli/src/runtime/assets.ts index 09bfb367..5f22b8ef 100644 --- a/cli/src/runtime/assets.ts +++ b/cli/src/runtime/assets.ts @@ -172,8 +172,8 @@ export function getTunwgPath(): string { return join(runtimePath(), 'tools', 'tunwg', tunwgBinary); } - // Development mode: use downloaded binary from server/tools/tunwg + // Development mode: use downloaded binary from hub/tools/tunwg const platformDir = getPlatformDir(); const devBinaryName = isWin ? `tunwg-${platformDir}.exe` : `tunwg-${platformDir}`; - return join(__dirname, '..', '..', '..', 'server', 'tools', 'tunwg', devBinaryName); + return join(__dirname, '..', '..', '..', 'hub', 'tools', 'tunwg', devBinaryName); } diff --git a/cli/src/runtime/embeddedAssets.bun.ts b/cli/src/runtime/embeddedAssets.bun.ts index 0df87800..e5d8572b 100644 --- a/cli/src/runtime/embeddedAssets.bun.ts +++ b/cli/src/runtime/embeddedAssets.bun.ts @@ -4,7 +4,7 @@ import difftasticArchiveLicense from '../../tools/archives/difftastic-LICENSE' a import ripgrepArchiveLicense from '../../tools/archives/ripgrep-LICENSE' assert { type: 'file' }; import difftasticLicense from '../../tools/licenses/difftastic-LICENSE' assert { type: 'file' }; import ripgrepLicense from '../../tools/licenses/ripgrep-LICENSE' assert { type: 'file' }; -import tunwgLicense from '../../../server/tools/tunwg/LICENSE' assert { type: 'file' }; +import tunwgLicense from '../../../hub/tools/tunwg/LICENSE' assert { type: 'file' }; export interface EmbeddedAsset { relativePath: string; @@ -35,7 +35,7 @@ async function selectEmbeddedAssets(): Promise { ] = await Promise.all([ import('../../tools/archives/difftastic-arm64-darwin.tar.gz', { assert: { type: 'file' } }), import('../../tools/archives/ripgrep-arm64-darwin.tar.gz', { assert: { type: 'file' } }), - import('../../../server/tools/tunwg/tunwg-arm64-darwin', { assert: { type: 'file' } }) + import('../../../hub/tools/tunwg/tunwg-arm64-darwin', { assert: { type: 'file' } }) ]); return [ ...COMMON_ASSETS, @@ -53,7 +53,7 @@ async function selectEmbeddedAssets(): Promise { ] = await Promise.all([ import('../../tools/archives/difftastic-x64-darwin.tar.gz', { assert: { type: 'file' } }), import('../../tools/archives/ripgrep-x64-darwin.tar.gz', { assert: { type: 'file' } }), - import('../../../server/tools/tunwg/tunwg-x64-darwin', { assert: { type: 'file' } }) + import('../../../hub/tools/tunwg/tunwg-x64-darwin', { assert: { type: 'file' } }) ]); return [ ...COMMON_ASSETS, @@ -71,7 +71,7 @@ async function selectEmbeddedAssets(): Promise { ] = await Promise.all([ import('../../tools/archives/difftastic-arm64-linux.tar.gz', { assert: { type: 'file' } }), import('../../tools/archives/ripgrep-arm64-linux.tar.gz', { assert: { type: 'file' } }), - import('../../../server/tools/tunwg/tunwg-arm64-linux', { assert: { type: 'file' } }) + import('../../../hub/tools/tunwg/tunwg-arm64-linux', { assert: { type: 'file' } }) ]); return [ ...COMMON_ASSETS, @@ -89,7 +89,7 @@ async function selectEmbeddedAssets(): Promise { ] = await Promise.all([ import('../../tools/archives/difftastic-x64-linux.tar.gz', { assert: { type: 'file' } }), import('../../tools/archives/ripgrep-x64-linux.tar.gz', { assert: { type: 'file' } }), - import('../../../server/tools/tunwg/tunwg-x64-linux', { assert: { type: 'file' } }) + import('../../../hub/tools/tunwg/tunwg-x64-linux', { assert: { type: 'file' } }) ]); return [ ...COMMON_ASSETS, @@ -107,7 +107,7 @@ async function selectEmbeddedAssets(): Promise { ] = await Promise.all([ import('../../tools/archives/difftastic-x64-win32.tar.gz', { assert: { type: 'file' } }), import('../../tools/archives/ripgrep-x64-win32.tar.gz', { assert: { type: 'file' } }), - import('../../../server/tools/tunwg/tunwg-x64-win32.exe', { assert: { type: 'file' } }) + import('../../../hub/tools/tunwg/tunwg-x64-win32.exe', { assert: { type: 'file' } }) ]); return [ ...COMMON_ASSETS, diff --git a/cli/src/utils/autoStartServer.ts b/cli/src/utils/autoStartServer.ts index 27441885..c565d962 100644 --- a/cli/src/utils/autoStartServer.ts +++ b/cli/src/utils/autoStartServer.ts @@ -1,10 +1,10 @@ /** - * Auto-start server module + * Auto-start hub module * - * Automatically starts the HAPI server when CLI is launched + * Automatically starts the HAPI hub when CLI is launched * if specific conditions are met: * 1. HAPI_API_URL is not set (using default localhost:3006) - * 2. cliApiToken exists in settings.json (server was previously started) + * 2. cliApiToken exists in settings.json (hub was previously started) * 3. Port 3006 is not currently listening */ @@ -52,7 +52,7 @@ async function checkPortListening(port: number, host: string = '127.0.0.1'): Pro } /** - * Check if server is ready via health endpoint + * Check if hub is ready via health endpoint */ async function checkServerHealth(url: string): Promise { try { @@ -66,7 +66,7 @@ async function checkServerHealth(url: string): Promise { } /** - * Wait for server to become ready + * Wait for hub to become ready */ async function waitForServerReady( url: string, @@ -87,7 +87,7 @@ async function waitForServerReady( } /** - * Determine if server should be auto-started + * Determine if hub should be auto-started */ async function shouldAutoStartServer(): Promise { // Condition 1: HAPI_API_URL not set (using default localhost:3006) @@ -99,13 +99,13 @@ async function shouldAutoStartServer(): Promise { // Condition 2: Check settings.json const settings = await readSettings() - // 2a: apiUrl is set in settings.json (user configured a specific server) + // 2a: apiUrl is set in settings.json (user configured a specific hub) if (settings.apiUrl || settings.serverUrl) { logger.debug('[AUTO-START] apiUrl is set in settings.json, skipping auto-start') return false } - // 2b: cliApiToken exists in settings.json (server was previously started) + // 2b: cliApiToken exists in settings.json (hub was previously started) if (!settings.cliApiToken) { logger.debug('[AUTO-START] No cliApiToken in settings, skipping auto-start') return false @@ -122,25 +122,25 @@ async function shouldAutoStartServer(): Promise { } /** - * Start server as a child process (will exit when CLI exits) + * Start hub as a child process (will exit when CLI exits) */ function startServerAsChild(): void { - const serverProcess = spawnHappyCLI(['server'], { + const serverProcess = spawnHappyCLI(['hub'], { detached: false, stdio: 'ignore', env: process.env }) - logger.debug(`[AUTO-START] Server process spawned with PID ${serverProcess.pid}`) + logger.debug(`[AUTO-START] Hub process spawned with PID ${serverProcess.pid}`) - // Ensure server is killed when CLI exits + // Ensure hub is killed when CLI exits process.on('exit', () => { serverProcess.kill() }) } /** - * Main entry point: auto-start server if conditions are met + * Main entry point: auto-start hub if conditions are met */ export async function maybeAutoStartServer(): Promise { try { @@ -149,23 +149,23 @@ export async function maybeAutoStartServer(): Promise { return } - logger.debug('[AUTO-START] Starting server automatically...') - console.log(chalk.gray('Starting HAPI server in background...')) + logger.debug('[AUTO-START] Starting hub automatically...') + console.log(chalk.gray('Starting HAPI hub in background...')) startServerAsChild() const isReady = await waitForServerReady(configuration.apiUrl) if (!isReady) { - console.log(chalk.yellow('Warning: Server did not start within expected time')) - console.log(chalk.gray(' Try running `hapi server` manually to see errors')) + console.log(chalk.yellow('Warning: Hub did not start within expected time')) + console.log(chalk.gray(' Try running `hapi hub` manually to see errors')) return } - console.log(chalk.green('HAPI server started')) + console.log(chalk.green('HAPI hub started')) } catch (error) { - logger.debug('[AUTO-START] Error during server auto-start', error) - console.log(chalk.yellow('Warning: Failed to auto-start server')) + logger.debug('[AUTO-START] Error during hub auto-start', error) + console.log(chalk.yellow('Warning: Failed to auto-start hub')) if (error instanceof Error) { console.log(chalk.gray(` Error: ${error.message}`)) } diff --git a/cli/tsconfig.json b/cli/tsconfig.json index 4e447cde..2b42bacf 100644 --- a/cli/tsconfig.json +++ b/cli/tsconfig.json @@ -27,7 +27,7 @@ "src/**/*.ts", "src/**/*.tsx", "src/**/*.d.ts", - "../server/src/**/*.ts", - "../server/src/**/*.d.ts" + "../hub/src/**/*.ts", + "../hub/src/**/*.d.ts" ] } diff --git a/docs/guide/faq.md b/docs/guide/faq.md index 62d57340..47ede8c9 100644 --- a/docs/guide/faq.md +++ b/docs/guide/faq.md @@ -22,9 +22,11 @@ Yes, HAPI is open source and free to use under the AGPL-3.0-only license. ## Setup & Installation -### Do I need a server? +### Do I need a hub? -HAPI includes an embedded server. Just run `hapi server` on your machine - no external server required. +HAPI includes an embedded hub. Just run `hapi hub` on your machine - no external hub required. + +`hapi server` remains supported as an alias. ### How do I access HAPI from my phone? @@ -34,21 +36,21 @@ http://:3006 ``` For internet access: -- If the server has a public IP, access it directly (use HTTPS via reverse proxy for production) +- If the hub has a public IP, access it directly (use HTTPS via reverse proxy for production) - If behind NAT, 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 +- CLI connections to the hub - Web app logins - Telegram account binding -It's auto-generated on first server start and saved to `~/.hapi/settings.json`. +It's auto-generated on first hub start and saved to `~/.hapi/settings.json`. ### Do you support multiple accounts? -Yes. We support lightweight multi-account access via namespaces for shared team servers. See [Namespace (Advanced)](./namespace.md). +Yes. We support lightweight multi-account access via namespaces for shared team hubs. See [Namespace (Advanced)](./namespace.md). ### Can I use HAPI without Telegram? @@ -117,14 +119,14 @@ Only if they have your access token. For additional security: ### "Connection refused" error -- Ensure server is running: `hapi server` +- Ensure hub is running: `hapi hub` - Check firewall allows port 3006 - Verify `HAPI_API_URL` is correct ### "Invalid token" error - Re-run `hapi auth login` -- Check token matches in CLI and server +- Check token matches in CLI and hub - Verify `~/.hapi/settings.json` has correct `cliApiToken` ### Runner won't start @@ -155,7 +157,7 @@ export HAPI_CLAUDE_PATH=/path/to/claude hapi doctor ``` -This checks server connectivity, token validity, agent availability, and more. +This checks hub connectivity, token validity, agent availability, and more. ## Comparison diff --git a/docs/guide/how-it-works.md b/docs/guide/how-it-works.md index fe99d89a..c26d9d0f 100644 --- a/docs/guide/how-it-works.md +++ b/docs/guide/how-it-works.md @@ -6,11 +6,11 @@ HAPI consists of three interconnected components that work together to provide r ``` ┌────────────────────────────────────────────────────────────────────────────┐ -│ Your Machine (Local or Server) │ +│ Your Machine (Local or Hub Host) │ │ │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │ │ │ │ │ │ │ │ │ -│ │ HAPI CLI │◄───────►│ HAPI Server │◄───────►│ Web App │ │ +│ │ HAPI CLI │◄───────►│ HAPI Hub │◄───────►│ Web App │ │ │ │ │ Socket │ │ SSE │ (embedded) │ │ │ │ + AI Agent │ .IO │ + SQLite │ │ │ │ │ │ │ │ + REST API │ │ │ │ @@ -41,7 +41,7 @@ HAPI consists of three interconnected components that work together to provide r └────────────────────────────────────────────────────────────────────────────┘ ``` -> **Note:** The server can run on your local desktop or a remote server (VPS, cloud, etc.). If deployed on a server with a public IP, tunneling is not required. +> **Note:** The hub can run on your local desktop or a remote host (VPS, cloud, etc.). If deployed on a host with a public IP, tunneling is not required. ## Components @@ -50,7 +50,7 @@ HAPI consists of three interconnected components that work together to provide r 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 +- Registers sessions with the HAPI hub - Relays messages and permission requests - Provides MCP (Model Context Protocol) tools @@ -62,9 +62,9 @@ hapi gemini # Start Google Gemini session hapi runner start # Run background service for remote session spawning ``` -### HAPI Server +### HAPI Hub -The server is the central hub that connects everything: +The hub is the central service that connects everything: - **HTTP API** - RESTful endpoints for sessions, messages, permissions - **Socket.IO** - Real-time bidirectional communication with CLI @@ -93,10 +93,10 @@ A React-based PWA that provides the mobile interface: 2. CLI starts Claude Code (or other agent) │ ▼ -3. CLI connects to server via Socket.IO +3. CLI connects to hub via Socket.IO │ ▼ -4. Server creates session in database +4. Hub creates session in database │ ▼ 5. Web clients receive SSE update @@ -111,10 +111,10 @@ A React-based PWA that provides the mobile interface: 1. AI agent requests tool permission (e.g., file edit) │ ▼ -2. CLI sends permission request to server +2. CLI sends permission request to hub │ ▼ -3. Server stores request and notifies via SSE + Telegram +3. Hub stores request and notifies via SSE + Telegram │ ▼ 4. User receives notification on phone @@ -123,7 +123,7 @@ A React-based PWA that provides the mobile interface: 5. User approves/denies in web app or Telegram │ ▼ -6. Server relays decision to CLI via Socket.IO +6. Hub relays decision to CLI via Socket.IO │ ▼ 7. CLI informs AI agent, execution continues @@ -132,7 +132,7 @@ A React-based PWA that provides the mobile interface: ### Message Flow ``` -User (Phone) Server CLI +User (Phone) Hub CLI │ │ │ │──── Send message ──────►│ │ │ │─── Socket.IO emit ───►│ @@ -146,7 +146,7 @@ User (Phone) Server CLI ## Communication Protocols -### CLI ↔ Server: Socket.IO +### CLI ↔ Hub: Socket.IO Real-time bidirectional communication for: - Session registration and heartbeat @@ -155,7 +155,7 @@ Real-time bidirectional communication for: - Metadata and state updates - RPC method invocation -### Server ↔ Web: REST + SSE +### Hub ↔ Web: REST + SSE - **REST API** for actions (send message, approve permission) - **SSE stream** for real-time updates (new messages, status changes) diff --git a/docs/guide/installation.md b/docs/guide/installation.md index 6e9091e6..ca0f5766 100644 --- a/docs/guide/installation.md +++ b/docs/guide/installation.md @@ -1,6 +1,6 @@ # Installation -Install the HAPI CLI and set up the server. +Install the HAPI CLI and set up the hub. ## Prerequisites @@ -26,7 +26,7 @@ HAPI has three components: | Component | Role | Required | |-----------|------|----------| | **CLI** | Wraps AI agents (Claude/Codex/Gemini), runs sessions | Yes | -| **Server** | Central hub: persistence, real-time sync, remote access | Yes | +| **Hub** | Central coordinator: persistence, real-time sync, remote access | Yes | | **Runner** | Background service for remote session spawning | Optional | ### How they work together @@ -36,7 +36,7 @@ HAPI has three components: │ Your Machine │ │ │ │ ┌─────────┐ Socket.IO ┌─────────────┐ │ -│ │ CLI │◄───────────────►│ Server │ │ +│ │ CLI │◄───────────────►│ Hub │ │ │ │+ Agent │ │ + SQLite │ │ │ └─────────┘ └──────┬──────┘ │ │ ▲ │ SSE │ @@ -54,15 +54,15 @@ HAPI has three components: └───────────┘ ``` -- **CLI**: Start a session with `hapi`. The CLI wraps your AI agent and syncs with the server. -- **Server**: Run `hapi server`. Stores sessions, handles permissions, enables remote access. +- **CLI**: Start a session with `hapi`. The CLI wraps your AI agent and syncs with the hub. +- **Hub**: Run `hapi hub`. Stores sessions, handles permissions, enables remote access. - **Runner**: Run `hapi runner start`. Lets you spawn sessions from phone/web without keeping a terminal open. ### Typical workflows -**Local only**: `hapi server` → `hapi` → work in terminal +**Local only**: `hapi hub` → `hapi` → work in terminal -**Remote access**: `hapi server --relay` → `hapi runner start` → control from phone/web +**Remote access**: `hapi hub --relay` → `hapi runner start` → control from phone/web ## Install the CLI @@ -111,21 +111,23 @@ bun build:single-exe ``` -## Server setup +## Hub setup -The server can be deployed on: +The hub can be deployed on: - **Local desktop** (default) - Run on your development machine -- **Remote server** - Deploy on a VPS, cloud server, or any machine with network access +- **Remote host** - Deploy the hub on a VPS, cloud host, or any machine with network access ### Default: Public Relay (recommended) ```bash -hapi server --relay +hapi hub --relay ``` The terminal displays a URL and QR code. Scan to access from anywhere. +`hapi server` remains supported as an alias. + - **End-to-end encrypted** with WireGuard + TLS - No configuration needed - Works behind NAT, firewalls, and any network @@ -133,12 +135,12 @@ The terminal displays a URL and QR code. Scan to access from anywhere. ### Local Only ```bash -hapi server +hapi hub # or -hapi server --no-relay +hapi hub --no-relay ``` -The server listens on `http://localhost:3006` by default. +The hub listens on `http://localhost:3006` by default. On first run, HAPI: @@ -152,7 +154,7 @@ On first run, HAPI: ``` ~/.hapi/ ├── settings.json # Main configuration -├── hapi.db # SQLite database (server) +├── hapi.db # SQLite database (hub) ├── runner.state.json # Runner process state └── logs/ # Log files ``` @@ -164,9 +166,9 @@ On first run, HAPI: | Variable | Default | Description | |----------|---------|-------------| | `CLI_API_TOKEN` | Auto-generated | Shared secret for authentication | -| `HAPI_API_URL` | `http://localhost:3006` | Server URL for CLI | -| `HAPI_LISTEN_HOST` | `127.0.0.1` | HTTP server bind address | -| `HAPI_LISTEN_PORT` | `3006` | HTTP server port | +| `HAPI_API_URL` | `http://localhost:3006` | Hub URL for CLI | +| `HAPI_LISTEN_HOST` | `127.0.0.1` | HTTP service bind address | +| `HAPI_LISTEN_PORT` | `3006` | HTTP service port | | `HAPI_PUBLIC_URL` | - | Public URL for external access | | `HAPI_HOME` | `~/.hapi` | Config directory path | | `DB_PATH` | `~/.hapi/hapi.db` | Database file path | @@ -177,10 +179,10 @@ On first run, HAPI: ## CLI setup -If the server is not on localhost, set these before running `hapi`: +If the hub is not on localhost, set these before running `hapi`: ```bash -export HAPI_API_URL="http://your-server:3006" +export HAPI_API_URL="http://your-hub:3006" export CLI_API_TOKEN="your-token-here" ``` @@ -200,7 +202,7 @@ hapi auth logout Each machine gets a unique ID stored in `~/.hapi/settings.json`. This allows: -- Multiple machines to connect to one server +- Multiple machines to connect to one hub - Remote session spawning on specific machines - Machine health monitoring @@ -226,7 +228,7 @@ Copy the generated URL and set it: ```bash export HAPI_PUBLIC_URL="https://your-tunnel.trycloudflare.com" -hapi server +hapi hub ``` **Named tunnel** (persistent URL): @@ -251,7 +253,7 @@ https://tailscale.com/download ```bash sudo tailscale up -hapi server +hapi hub ``` Access via your Tailscale IP: @@ -264,7 +266,7 @@ http://100.x.x.x:3006
Public IP / Reverse Proxy -If the server has a public IP, access directly via `http://your-server-ip:3006`. +If the hub has a public IP, access directly via `http://your-hub-ip:3006`. Use HTTPS (via Nginx, Caddy, etc.) for production.
@@ -275,13 +277,13 @@ 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 +3. Start the hub and bind your account ```bash export TELEGRAM_BOT_TOKEN="your-bot-token" export HAPI_PUBLIC_URL="https://your-public-url" -hapi server +hapi hub ``` Then message your bot with `/start`, open the app, and enter your `CLI_API_TOKEN`. @@ -329,8 +331,8 @@ Keep HAPI running persistently so it survives terminal closes, system restarts, Simple one-liner for quick background runs: ```bash -# Server -nohup hapi server --relay > ~/.hapi/logs/server.log 2>&1 & +# Hub +nohup hapi hub --relay > ~/.hapi/logs/hub.log 2>&1 & # Runner nohup hapi runner start --foreground > ~/.hapi/logs/runner.log 2>&1 & @@ -339,14 +341,14 @@ nohup hapi runner start --foreground > ~/.hapi/logs/runner.log 2>&1 & View logs: ```bash -tail -f ~/.hapi/logs/server.log +tail -f ~/.hapi/logs/hub.log tail -f ~/.hapi/logs/runner.log ``` Stop processes: ```bash -pkill -f "hapi server" +pkill -f "hapi hub" pkill -f "hapi runner" ``` @@ -360,13 +362,13 @@ pm2 provides process management with auto-restart on crashes and system reboot. # Install pm2 npm install -g pm2 -# Start server and runner -pm2 start "hapi server --relay" --name hapi-server +# Start hub and runner +pm2 start "hapi hub --relay" --name hapi-hub pm2 start "hapi runner start --foreground" --name hapi-runner # View status and logs pm2 status -pm2 logs hapi-server +pm2 logs hapi-hub pm2 logs hapi-runner # Auto-restart on system reboot @@ -380,7 +382,7 @@ pm2 save # Save current process list Create plist files for automatic startup on macOS. -**Server** (`~/Library/LaunchAgents/com.hapi.server.plist`): +**Hub** (`~/Library/LaunchAgents/com.hapi.hub.plist`): ```xml @@ -388,11 +390,11 @@ Create plist files for automatic startup on macOS. Label - com.hapi.server + com.hapi.hub ProgramArguments /usr/local/bin/hapi - server + hub --relay RunAtLoad @@ -400,9 +402,9 @@ Create plist files for automatic startup on macOS. KeepAlive StandardOutPath - /Users/YOUR_USERNAME/.hapi/logs/server.log + /Users/YOUR_USERNAME/.hapi/logs/hub.log StandardErrorPath - /Users/YOUR_USERNAME/.hapi/logs/server.log + /Users/YOUR_USERNAME/.hapi/logs/hub.log ``` @@ -439,17 +441,17 @@ Load/unload services: ```bash # Load (start) -launchctl load ~/Library/LaunchAgents/com.hapi.server.plist +launchctl load ~/Library/LaunchAgents/com.hapi.hub.plist launchctl load ~/Library/LaunchAgents/com.hapi.runner.plist # Unload (stop) -launchctl unload ~/Library/LaunchAgents/com.hapi.server.plist +launchctl unload ~/Library/LaunchAgents/com.hapi.hub.plist launchctl unload ~/Library/LaunchAgents/com.hapi.runner.plist ``` > **macOS sleep note:** macOS may suspend background processes when the display sleeps. Use `caffeinate` to prevent this: > ```bash -> caffeinate -dimsu hapi server --relay +> caffeinate -dimsu hapi hub --relay > ``` > Or run `caffeinate -dimsu` in a separate terminal while HAPI is running. @@ -459,16 +461,16 @@ launchctl unload ~/Library/LaunchAgents/com.hapi.runner.plist Create user-level systemd services for automatic startup. -**Server** (`~/.config/systemd/user/hapi-server.service`): +**Hub** (`~/.config/systemd/user/hapi-hub.service`): ```ini [Unit] -Description=HAPI Server +Description=HAPI Hub After=network.target [Service] Type=simple -ExecStart=/usr/local/bin/hapi server --relay +ExecStart=/usr/local/bin/hapi hub --relay Restart=always RestartSec=5 @@ -481,7 +483,7 @@ WantedBy=default.target ```ini [Unit] Description=HAPI Runner -After=network.target hapi-server.service +After=network.target hapi-hub.service [Service] Type=simple @@ -500,16 +502,16 @@ Enable and start: systemctl --user daemon-reload # Enable (auto-start on login) -systemctl --user enable hapi-server +systemctl --user enable hapi-hub systemctl --user enable hapi-runner # Start now -systemctl --user start hapi-server +systemctl --user start hapi-hub systemctl --user start hapi-runner # View status/logs -systemctl --user status hapi-server -journalctl --user -u hapi-server -f +systemctl --user status hapi-hub +journalctl --user -u hapi-hub -f ``` > **Persist after logout:** To keep services running even when not logged in: @@ -527,7 +529,7 @@ Enable voice control: ```bash export ELEVENLABS_API_KEY="your-api-key" -hapi server --relay +hapi hub --relay ``` See [Voice Assistant](./voice-assistant.md) for usage details. diff --git a/docs/guide/namespace.md b/docs/guide/namespace.md index 00edc9e1..d2ac4a64 100644 --- a/docs/guide/namespace.md +++ b/docs/guide/namespace.md @@ -1,17 +1,17 @@ # Namespace (Advanced) -Namespaces are intended for small teams sharing a single public HAPI server. Each team member uses a different namespace to isolate their sessions and machines without running separate servers. +Namespaces are intended for small teams sharing a single public HAPI hub. Each team member uses a different namespace to isolate their sessions and machines without running separate hubs. This is not a default setup path for most users. ## How it works -- The server uses a single base `CLI_API_TOKEN`. +- The hub uses a single base `CLI_API_TOKEN`. - Clients append `:` to the token for isolation. ## Setup -1. On the server, configure only the base token: +1. On the hub, configure only the base token: ``` CLI_API_TOKEN="your-base-token" @@ -27,7 +27,7 @@ CLI_API_TOKEN="your-base-token:alice" ## Limitations and gotchas -- Server-side `CLI_API_TOKEN` must not include `:`. If it does, the server will strip the suffix and log a warning. +- Hub-side `CLI_API_TOKEN` must not include `:`. If it does, the hub will strip the suffix and log a warning. - Namespaces are isolated: sessions, machines, and users are not visible across namespaces. - One machine ID cannot be reused across namespaces. - To run multiple namespaces on one machine, use a separate `HAPI_HOME` per namespace, or clear the machine ID with `hapi auth logout` before switching. diff --git a/docs/guide/quick-start.md b/docs/guide/quick-start.md index ee9ec0c5..40930b39 100644 --- a/docs/guide/quick-start.md +++ b/docs/guide/quick-start.md @@ -22,14 +22,16 @@ npx @twsxtd/hapi Other install options: [Installation](./installation.md) -## Start the server +## Start the hub ```bash -hapi server --relay +hapi hub --relay ``` On first run, HAPI prints an access token and saves it to `~/.hapi/settings.json`. +`hapi server` remains supported as an alias. + The terminal will display a URL and QR code for remote access. > End-to-end encrypted with WireGuard + TLS. diff --git a/docs/guide/voice-assistant.md b/docs/guide/voice-assistant.md index 694e8d48..7787d773 100644 --- a/docs/guide/voice-assistant.md +++ b/docs/guide/voice-assistant.md @@ -24,16 +24,16 @@ An [ElevenLabs](https://elevenlabs.io) account with API access 2. Go to [API Keys](https://elevenlabs.io/app/settings/api-keys) in your account settings 3. Create a new API key and copy it -### 2. Configure the Server +### 2. Configure the Hub -Set the environment variable before starting the server: +Set the environment variable before starting the hub: ```bash export ELEVENLABS_API_KEY="your-api-key" -hapi server --relay +hapi hub --relay ``` -The server automatically creates a "Hapi Voice Assistant" agent in your ElevenLabs account on first use. +The hub automatically creates a "Hapi Voice Assistant" agent in your ElevenLabs account on first use. ### 3. (Optional) Custom Agent @@ -85,10 +85,10 @@ The voice assistant has two tools to interact with your coding agent: ### Architecture ``` -Browser → WebRTC → ElevenLabs ConvAI → Voice Assistant → HAPI Server → Coding Agent +Browser → WebRTC → ElevenLabs ConvAI → Voice Assistant → HAPI Hub → Coding Agent ``` -The voice connection uses WebRTC for low-latency audio streaming. The HAPI server provides conversation tokens and handles authentication. +The voice connection uses WebRTC for low-latency audio streaming. The HAPI hub provides conversation tokens and handles authentication. ## Tips @@ -101,7 +101,7 @@ The voice connection uses WebRTC for low-latency audio streaming. The HAPI serve ### "ElevenLabs API key not configured" -Set `ELEVENLABS_API_KEY` in your environment and restart the server. +Set `ELEVENLABS_API_KEY` in your environment and restart the hub. ### "Failed to get microphone permission" diff --git a/server/README.md b/hub/README.md similarity index 86% rename from server/README.md rename to hub/README.md index c25bfbc3..cb138aa8 100644 --- a/server/README.md +++ b/hub/README.md @@ -1,6 +1,6 @@ -# hapi-server +# hapi-hub -Telegram bot + HTTP API + realtime updates for hapi. +Telegram bot + HTTP API + realtime updates for hapi hub. ## What it does @@ -46,18 +46,20 @@ export TELEGRAM_BOT_TOKEN="..." export CLI_API_TOKEN="shared-secret" export HAPI_PUBLIC_URL="https://your-domain.example" -hapi server +hapi hub ``` +`hapi server` remains supported as an alias. + If you only need web + CLI, you can omit TELEGRAM_BOT_TOKEN. -To enable Telegram, set TELEGRAM_BOT_TOKEN and HAPI_PUBLIC_URL, start the server, open `/app` +To enable Telegram, set TELEGRAM_BOT_TOKEN and HAPI_PUBLIC_URL, start the hub, open `/app` in the bot chat, and bind the Mini App with `CLI_API_TOKEN:` when prompted. From source: ```bash bun install -bun run dev:server +bun run dev:hub ``` ## HTTP API @@ -122,7 +124,7 @@ See `src/socket/handlers/cli.ts` for event handlers. Namespace: `/cli` -### Client events (CLI to server) +### Client events (CLI to hub) - `message` - Send message to session. - `update-metadata` - Update session metadata. @@ -133,7 +135,7 @@ Namespace: `/cli` - `rpc-register` - Register RPC handler. - `rpc-unregister` - Unregister RPC handler. -### Server events (server to clients) +### Hub events (hub to clients) - `update` - Broadcast session/message updates. - `rpc-request` - Incoming RPC call. @@ -181,7 +183,7 @@ See `src/store/index.ts` for SQLite persistence: ## Source structure -- `src/web/` - HTTP server and routes. +- `src/web/` - HTTP service and routes. - `src/socket/` - Socket.IO setup and handlers. - `src/telegram/` - Telegram bot. - `src/sync/` - Core session/message logic. @@ -194,30 +196,30 @@ Access is controlled by: - Telegram initData verification plus bound Telegram users (bound via `CLI_API_TOKEN:`). - `CLI_API_TOKEN` base secret for CLI and browser access (namespace is appended by clients). -Transport security depends on HTTPS in front of the server. +Transport security depends on HTTPS in front of the hub. ## Build for deployment From the repo root: ```bash -bun run build:server +bun run build:hub bun run build:web ``` -The server build output is `server/dist/index.js`, and the web assets are in `web/dist`. +The hub build output is `hub/dist/index.js`, and the web assets are in `web/dist`. ## Networking notes -- Telegram Mini Apps require HTTPS and a public URL. If the server has no public IP, use Cloudflare Tunnel or Tailscale and set `HAPI_PUBLIC_URL` to the HTTPS endpoint. +- Telegram Mini Apps require HTTPS and a public URL. If the hub has no public IP, use Cloudflare Tunnel or Tailscale and set `HAPI_PUBLIC_URL` to the HTTPS endpoint. - If the web app is hosted on a different origin, set `CORS_ORIGINS` (or `HAPI_PUBLIC_URL`) to include that static host origin. ## Standalone web hosting -The web UI can be hosted separately from the server (for example on GitHub Pages or Cloudflare Pages): +The web UI can be hosted separately from the hub (for example on GitHub Pages or Cloudflare Pages): 1. Build and deploy `web/dist` from the repo root. 2. Set `CORS_ORIGINS` (or `HAPI_PUBLIC_URL`) to the static host origin. -3. Open the static site, click the Server button on the login screen, and enter the hapi server origin. +3. Open the static site, click the Hub button on the login screen, and enter the hapi hub origin. -Leaving the server override empty preserves the default same-origin behavior when the server serves the web assets directly. +Leaving the hub override empty preserves the default same-origin behavior when the hub serves the web assets directly. diff --git a/server/package.json b/hub/package.json similarity index 97% rename from server/package.json rename to hub/package.json index d5cffa69..aa7c78fa 100644 --- a/server/package.json +++ b/hub/package.json @@ -1,5 +1,5 @@ { - "name": "hapi-server", + "name": "hapi-hub", "private": true, "version": "0.1.0", "description": "Telegram Bot client for HAPI - control Claude Code sessions", diff --git a/server/scripts/cleanup-sessions.ts b/hub/scripts/cleanup-sessions.ts similarity index 93% rename from server/scripts/cleanup-sessions.ts rename to hub/scripts/cleanup-sessions.ts index 0523b4f4..93ec3d03 100755 --- a/server/scripts/cleanup-sessions.ts +++ b/hub/scripts/cleanup-sessions.ts @@ -9,7 +9,7 @@ * - Orphaned: Delete sessions whose path no longer exists * * Usage: - * bun run server/scripts/cleanup-sessions.ts [options] + * bun run hub/scripts/cleanup-sessions.ts [options] * * Options: * --min-messages=N Delete sessions with fewer than N messages (default: 5) @@ -20,12 +20,12 @@ * --help Show this help message * * Examples: - * bun run server/scripts/cleanup-sessions.ts - * bun run server/scripts/cleanup-sessions.ts --min-messages=3 - * bun run server/scripts/cleanup-sessions.ts --path="/tmp/*" - * bun run server/scripts/cleanup-sessions.ts --message="hello" - * bun run server/scripts/cleanup-sessions.ts --orphaned - * bun run server/scripts/cleanup-sessions.ts --orphaned --min-messages=5 --force + * bun run hub/scripts/cleanup-sessions.ts + * bun run hub/scripts/cleanup-sessions.ts --min-messages=3 + * bun run hub/scripts/cleanup-sessions.ts --path="/tmp/*" + * bun run hub/scripts/cleanup-sessions.ts --message="hello" + * bun run hub/scripts/cleanup-sessions.ts --orphaned + * bun run hub/scripts/cleanup-sessions.ts --orphaned --min-messages=5 --force */ import { Database } from 'bun:sqlite' @@ -322,7 +322,7 @@ async function main(): Promise { if (help) { console.log(` -Usage: bun run server/scripts/cleanup-sessions.ts [options] +Usage: bun run hub/scripts/cleanup-sessions.ts [options] Options: --min-messages=N Delete sessions with fewer than N messages (default: 5) @@ -340,12 +340,12 @@ Filtering logic: - Multiple filters: Delete sessions matching ALL conditions (AND) Examples: - bun run server/scripts/cleanup-sessions.ts - bun run server/scripts/cleanup-sessions.ts --min-messages=3 - bun run server/scripts/cleanup-sessions.ts --path="/tmp/*" - bun run server/scripts/cleanup-sessions.ts --message="hello" - bun run server/scripts/cleanup-sessions.ts --orphaned - bun run server/scripts/cleanup-sessions.ts --orphaned --min-messages=5 --force + bun run hub/scripts/cleanup-sessions.ts + bun run hub/scripts/cleanup-sessions.ts --min-messages=3 + bun run hub/scripts/cleanup-sessions.ts --path="/tmp/*" + bun run hub/scripts/cleanup-sessions.ts --message="hello" + bun run hub/scripts/cleanup-sessions.ts --orphaned + bun run hub/scripts/cleanup-sessions.ts --orphaned --min-messages=5 --force `) process.exit(0) } diff --git a/server/scripts/download-tunwg.ts b/hub/scripts/download-tunwg.ts similarity index 98% rename from server/scripts/download-tunwg.ts rename to hub/scripts/download-tunwg.ts index df9ee4a0..520f6a2e 100644 --- a/server/scripts/download-tunwg.ts +++ b/hub/scripts/download-tunwg.ts @@ -2,7 +2,7 @@ * Download tunwg binaries for all platforms * * Downloads pre-built tunwg binaries from GitHub releases. - * Output directory: server/tools/tunwg/ + * Output directory: hub/tools/tunwg/ */ import { existsSync, mkdirSync, writeFileSync, chmodSync } from 'node:fs'; diff --git a/server/scripts/generate-embedded-web-assets.ts b/hub/scripts/generate-embedded-web-assets.ts similarity index 95% rename from server/scripts/generate-embedded-web-assets.ts rename to hub/scripts/generate-embedded-web-assets.ts index 8578e397..34d8d023 100644 --- a/server/scripts/generate-embedded-web-assets.ts +++ b/hub/scripts/generate-embedded-web-assets.ts @@ -57,7 +57,7 @@ function main(): void { const scriptDir = dirname(fileURLToPath(import.meta.url)); const workspaceRoot = join(scriptDir, '..', '..'); const webDistDir = join(workspaceRoot, 'web', 'dist'); - const outputPath = join(workspaceRoot, 'server', 'src', 'web', 'embeddedAssets.generated.ts'); + const outputPath = join(workspaceRoot, 'hub', 'src', 'web', 'embeddedAssets.generated.ts'); const outputDir = dirname(outputPath); if (!existsSync(webDistDir)) { @@ -89,7 +89,7 @@ function main(): void { }); const output = [ - '// This file is generated by server/scripts/generate-embedded-web-assets.ts.', + '// This file is generated by hub/scripts/generate-embedded-web-assets.ts.', '// Do not edit by hand.', '', ...imports, diff --git a/server/src/config/cliApiToken.ts b/hub/src/config/cliApiToken.ts similarity index 100% rename from server/src/config/cliApiToken.ts rename to hub/src/config/cliApiToken.ts diff --git a/server/src/config/generators.ts b/hub/src/config/generators.ts similarity index 100% rename from server/src/config/generators.ts rename to hub/src/config/generators.ts diff --git a/server/src/config/jwtSecret.ts b/hub/src/config/jwtSecret.ts similarity index 100% rename from server/src/config/jwtSecret.ts rename to hub/src/config/jwtSecret.ts diff --git a/server/src/config/ownerId.ts b/hub/src/config/ownerId.ts similarity index 100% rename from server/src/config/ownerId.ts rename to hub/src/config/ownerId.ts diff --git a/server/src/config/serverSettings.ts b/hub/src/config/serverSettings.ts similarity index 97% rename from server/src/config/serverSettings.ts rename to hub/src/config/serverSettings.ts index ab03fa8b..095fc2db 100644 --- a/server/src/config/serverSettings.ts +++ b/hub/src/config/serverSettings.ts @@ -1,7 +1,7 @@ /** - * Server Settings Management + * Hub Settings Management * - * Handles loading and persistence of server configuration. + * Handles loading and persistence of hub configuration. * Priority: environment variable > settings.json > default value * * When a value is loaded from environment variable and not present in settings.json, @@ -69,7 +69,7 @@ function deriveCorsOrigins(publicUrl: string): string[] { } /** - * Load server settings with priority: env > file > default + * Load hub settings with priority: env > file > default * Saves new env values to file when not already present */ export async function loadServerSettings(dataDir: string): Promise { diff --git a/server/src/config/settings.ts b/hub/src/config/settings.ts similarity index 100% rename from server/src/config/settings.ts rename to hub/src/config/settings.ts diff --git a/server/src/config/vapidKeys.ts b/hub/src/config/vapidKeys.ts similarity index 100% rename from server/src/config/vapidKeys.ts rename to hub/src/config/vapidKeys.ts diff --git a/server/src/configuration.ts b/hub/src/configuration.ts similarity index 94% rename from server/src/configuration.ts rename to hub/src/configuration.ts index 3a341fe6..4b22db1d 100644 --- a/server/src/configuration.ts +++ b/hub/src/configuration.ts @@ -1,5 +1,5 @@ /** - * Configuration for hapi-server (Direct Connect) + * Configuration for hapi-hub (Direct Connect) * * Configuration is loaded with priority: environment variable > settings.json > default * When values are read from environment variables and not present in settings.json, @@ -9,8 +9,8 @@ * - CLI_API_TOKEN: Shared secret for hapi CLI authentication (auto-generated if not set) * - TELEGRAM_BOT_TOKEN: Telegram Bot API token from @BotFather * - TELEGRAM_NOTIFICATION: Enable/disable Telegram notifications (default: true) - * - HAPI_LISTEN_HOST: Host/IP to bind the HTTP server (default: 127.0.0.1) - * - HAPI_LISTEN_PORT: Port for HTTP server (default: 3006) + * - HAPI_LISTEN_HOST: Host/IP to bind the HTTP service (default: 127.0.0.1) + * - HAPI_LISTEN_PORT: Port for HTTP service (default: 3006) * - HAPI_PUBLIC_URL: Public URL for external access (e.g., Telegram Mini App) * - CORS_ORIGINS: Comma-separated CORS origins * - HAPI_RELAY_API: Relay API domain for tunwg (default: relay.hapi.run) @@ -68,10 +68,10 @@ class Configuration { /** SQLite DB path */ public readonly dbPath: string - /** Port for the HTTP server */ + /** Port for the HTTP service */ public readonly listenPort: number - /** Host/IP to bind the HTTP server to */ + /** Host/IP to bind the HTTP service to */ public readonly listenHost: string /** Public URL for external access (e.g., Telegram Mini App) */ @@ -136,11 +136,11 @@ class Configuration { ? process.env.DB_PATH.replace(/^~/, homedir()) : join(dataDir, 'hapi.db') - // 3. Load server settings (with persistence) + // 3. Load hub settings (with persistence) const settingsResult = await loadServerSettings(dataDir) if (settingsResult.savedToFile) { - console.log(`[Server] Configuration saved to ${getSettingsFile(dataDir)}`) + console.log(`[Hub] Configuration saved to ${getSettingsFile(dataDir)}`) } // 4. Create configuration instance diff --git a/server/src/index.ts b/hub/src/index.ts similarity index 87% rename from server/src/index.ts rename to hub/src/index.ts index 96981565..a53ae330 100644 --- a/server/src/index.ts +++ b/hub/src/index.ts @@ -1,5 +1,5 @@ /** - * HAPI Server - Main Entry Point + * HAPI Hub - Main Entry Point * * Provides: * - Web app + HTTP API @@ -106,7 +106,7 @@ let notificationHub: NotificationHub | null = null let tunnelManager: TunnelManager | null = null async function main() { - console.log('HAPI Server starting...') + console.log('HAPI Hub starting...') // Load configuration (async - loads from env/file with persistence) const relayApiDomain = process.env.HAPI_RELAY_API || 'relay.hapi.run' @@ -133,28 +133,28 @@ async function main() { console.log('='.repeat(70)) console.log('') } else { - console.log(`[Server] CLI_API_TOKEN: loaded from ${formatSource(config.sources.cliApiToken)}`) + console.log(`[Hub] CLI_API_TOKEN: loaded from ${formatSource(config.sources.cliApiToken)}`) } // Display other configuration sources - console.log(`[Server] HAPI_LISTEN_HOST: ${config.listenHost} (${formatSource(config.sources.listenHost)})`) - console.log(`[Server] HAPI_LISTEN_PORT: ${config.listenPort} (${formatSource(config.sources.listenPort)})`) - console.log(`[Server] HAPI_PUBLIC_URL: ${config.publicUrl} (${formatSource(config.sources.publicUrl)})`) + console.log(`[Hub] HAPI_LISTEN_HOST: ${config.listenHost} (${formatSource(config.sources.listenHost)})`) + console.log(`[Hub] HAPI_LISTEN_PORT: ${config.listenPort} (${formatSource(config.sources.listenPort)})`) + console.log(`[Hub] HAPI_PUBLIC_URL: ${config.publicUrl} (${formatSource(config.sources.publicUrl)})`) if (!config.telegramEnabled) { - console.log('[Server] Telegram: disabled (no TELEGRAM_BOT_TOKEN)') + console.log('[Hub] Telegram: disabled (no TELEGRAM_BOT_TOKEN)') } else { const tokenSource = formatSource(config.sources.telegramBotToken) - console.log(`[Server] Telegram: enabled (${tokenSource})`) + console.log(`[Hub] Telegram: enabled (${tokenSource})`) const notificationSource = formatSource(config.sources.telegramNotification) - console.log(`[Server] Telegram notifications: ${config.telegramNotification ? 'enabled' : 'disabled'} (${notificationSource})`) + console.log(`[Hub] Telegram notifications: ${config.telegramNotification ? 'enabled' : 'disabled'} (${notificationSource})`) } // Display tunnel status if (relayFlag.enabled) { - console.log(`[Server] Tunnel: enabled (${relayFlag.source}), API: ${relayApiDomain}`) + console.log(`[Hub] Tunnel: enabled (${relayFlag.source}), API: ${relayApiDomain}`) } else { - console.log(`[Server] Tunnel: disabled (${relayFlag.source})`) + console.log(`[Hub] Tunnel: disabled (${relayFlag.source})`) } const store = new Store(config.dbPath) @@ -204,7 +204,7 @@ async function main() { notificationHub = new NotificationHub(syncEngine, notificationChannels) - // Start HTTP server first (before tunnel, so tunnel has something to forward to) + // Start HTTP service first (before tunnel, so tunnel has something to forward to) webServer = await startWebServer({ getSyncEngine: () => syncEngine, getSseManager: () => sseManager, @@ -224,10 +224,10 @@ async function main() { } console.log('') - console.log('[Web] Server listening on :' + config.listenPort) + console.log('[Web] Hub listening on :' + config.listenPort) console.log('[Web] Local: http://localhost:' + config.listenPort) - // Initialize tunnel AFTER web server is ready + // Initialize tunnel AFTER web service is ready let tunnelUrl: string | null = null if (relayFlag.enabled) { tunnelManager = new TunnelManager({ @@ -242,7 +242,7 @@ async function main() { tunnelUrl = await tunnelManager.start() } catch (error) { console.error('[Tunnel] Failed to start:', error instanceof Error ? error.message : error) - console.log('[Tunnel] Server continuing without tunnel. Restart without --relay to disable.') + console.log('[Tunnel] Hub continuing without tunnel. Restart without --relay to disable.') } } @@ -257,9 +257,9 @@ async function main() { console.log('[Web] Public: ' + tunnelUrl) - // Generate direct access link with server and token + // Generate direct access link with hub and token const params = new URLSearchParams({ - server: tunnelUrl, + hub: tunnelUrl, token: config.cliApiToken }) const directAccessUrl = `${officialWebUrl}/?${params.toString()}` @@ -288,7 +288,7 @@ async function main() { void announceTunnelAccess() } console.log('') - console.log('HAPI Server is ready!') + console.log('HAPI Hub is ready!') // Handle shutdown const shutdown = async () => { diff --git a/server/src/notifications/eventParsing.test.ts b/hub/src/notifications/eventParsing.test.ts similarity index 100% rename from server/src/notifications/eventParsing.test.ts rename to hub/src/notifications/eventParsing.test.ts diff --git a/server/src/notifications/eventParsing.ts b/hub/src/notifications/eventParsing.ts similarity index 100% rename from server/src/notifications/eventParsing.ts rename to hub/src/notifications/eventParsing.ts diff --git a/server/src/notifications/notificationHub.test.ts b/hub/src/notifications/notificationHub.test.ts similarity index 100% rename from server/src/notifications/notificationHub.test.ts rename to hub/src/notifications/notificationHub.test.ts diff --git a/server/src/notifications/notificationHub.ts b/hub/src/notifications/notificationHub.ts similarity index 100% rename from server/src/notifications/notificationHub.ts rename to hub/src/notifications/notificationHub.ts diff --git a/server/src/notifications/notificationTypes.ts b/hub/src/notifications/notificationTypes.ts similarity index 100% rename from server/src/notifications/notificationTypes.ts rename to hub/src/notifications/notificationTypes.ts diff --git a/server/src/notifications/sessionInfo.ts b/hub/src/notifications/sessionInfo.ts similarity index 100% rename from server/src/notifications/sessionInfo.ts rename to hub/src/notifications/sessionInfo.ts diff --git a/server/src/push/pushNotificationChannel.ts b/hub/src/push/pushNotificationChannel.ts similarity index 100% rename from server/src/push/pushNotificationChannel.ts rename to hub/src/push/pushNotificationChannel.ts diff --git a/server/src/push/pushService.ts b/hub/src/push/pushService.ts similarity index 100% rename from server/src/push/pushService.ts rename to hub/src/push/pushService.ts diff --git a/server/src/socket/handlers/cli/index.ts b/hub/src/socket/handlers/cli/index.ts similarity index 100% rename from server/src/socket/handlers/cli/index.ts rename to hub/src/socket/handlers/cli/index.ts diff --git a/server/src/socket/handlers/cli/machineHandlers.ts b/hub/src/socket/handlers/cli/machineHandlers.ts similarity index 100% rename from server/src/socket/handlers/cli/machineHandlers.ts rename to hub/src/socket/handlers/cli/machineHandlers.ts diff --git a/server/src/socket/handlers/cli/rpcHandlers.ts b/hub/src/socket/handlers/cli/rpcHandlers.ts similarity index 100% rename from server/src/socket/handlers/cli/rpcHandlers.ts rename to hub/src/socket/handlers/cli/rpcHandlers.ts diff --git a/server/src/socket/handlers/cli/sessionHandlers.ts b/hub/src/socket/handlers/cli/sessionHandlers.ts similarity index 100% rename from server/src/socket/handlers/cli/sessionHandlers.ts rename to hub/src/socket/handlers/cli/sessionHandlers.ts diff --git a/server/src/socket/handlers/cli/terminalHandlers.ts b/hub/src/socket/handlers/cli/terminalHandlers.ts similarity index 100% rename from server/src/socket/handlers/cli/terminalHandlers.ts rename to hub/src/socket/handlers/cli/terminalHandlers.ts diff --git a/server/src/socket/handlers/terminal.test.ts b/hub/src/socket/handlers/terminal.test.ts similarity index 100% rename from server/src/socket/handlers/terminal.test.ts rename to hub/src/socket/handlers/terminal.test.ts diff --git a/server/src/socket/handlers/terminal.ts b/hub/src/socket/handlers/terminal.ts similarity index 100% rename from server/src/socket/handlers/terminal.ts rename to hub/src/socket/handlers/terminal.ts diff --git a/server/src/socket/rpcRegistry.ts b/hub/src/socket/rpcRegistry.ts similarity index 100% rename from server/src/socket/rpcRegistry.ts rename to hub/src/socket/rpcRegistry.ts diff --git a/server/src/socket/server.ts b/hub/src/socket/server.ts similarity index 100% rename from server/src/socket/server.ts rename to hub/src/socket/server.ts diff --git a/server/src/socket/socketTypes.ts b/hub/src/socket/socketTypes.ts similarity index 100% rename from server/src/socket/socketTypes.ts rename to hub/src/socket/socketTypes.ts diff --git a/server/src/socket/terminalRegistry.ts b/hub/src/socket/terminalRegistry.ts similarity index 100% rename from server/src/socket/terminalRegistry.ts rename to hub/src/socket/terminalRegistry.ts diff --git a/server/src/sse/sseManager.test.ts b/hub/src/sse/sseManager.test.ts similarity index 100% rename from server/src/sse/sseManager.test.ts rename to hub/src/sse/sseManager.test.ts diff --git a/server/src/sse/sseManager.ts b/hub/src/sse/sseManager.ts similarity index 100% rename from server/src/sse/sseManager.ts rename to hub/src/sse/sseManager.ts diff --git a/server/src/store/index.ts b/hub/src/store/index.ts similarity index 100% rename from server/src/store/index.ts rename to hub/src/store/index.ts diff --git a/server/src/store/json.ts b/hub/src/store/json.ts similarity index 100% rename from server/src/store/json.ts rename to hub/src/store/json.ts diff --git a/server/src/store/machineStore.ts b/hub/src/store/machineStore.ts similarity index 100% rename from server/src/store/machineStore.ts rename to hub/src/store/machineStore.ts diff --git a/server/src/store/machines.ts b/hub/src/store/machines.ts similarity index 100% rename from server/src/store/machines.ts rename to hub/src/store/machines.ts diff --git a/server/src/store/messageStore.ts b/hub/src/store/messageStore.ts similarity index 100% rename from server/src/store/messageStore.ts rename to hub/src/store/messageStore.ts diff --git a/server/src/store/messages.ts b/hub/src/store/messages.ts similarity index 100% rename from server/src/store/messages.ts rename to hub/src/store/messages.ts diff --git a/server/src/store/namespace.test.ts b/hub/src/store/namespace.test.ts similarity index 100% rename from server/src/store/namespace.test.ts rename to hub/src/store/namespace.test.ts diff --git a/server/src/store/pushStore.ts b/hub/src/store/pushStore.ts similarity index 100% rename from server/src/store/pushStore.ts rename to hub/src/store/pushStore.ts diff --git a/server/src/store/pushSubscriptions.ts b/hub/src/store/pushSubscriptions.ts similarity index 100% rename from server/src/store/pushSubscriptions.ts rename to hub/src/store/pushSubscriptions.ts diff --git a/server/src/store/sessionStore.ts b/hub/src/store/sessionStore.ts similarity index 100% rename from server/src/store/sessionStore.ts rename to hub/src/store/sessionStore.ts diff --git a/server/src/store/sessions.ts b/hub/src/store/sessions.ts similarity index 100% rename from server/src/store/sessions.ts rename to hub/src/store/sessions.ts diff --git a/server/src/store/types.ts b/hub/src/store/types.ts similarity index 100% rename from server/src/store/types.ts rename to hub/src/store/types.ts diff --git a/server/src/store/userStore.ts b/hub/src/store/userStore.ts similarity index 100% rename from server/src/store/userStore.ts rename to hub/src/store/userStore.ts diff --git a/server/src/store/users.ts b/hub/src/store/users.ts similarity index 100% rename from server/src/store/users.ts rename to hub/src/store/users.ts diff --git a/server/src/store/versionedUpdates.ts b/hub/src/store/versionedUpdates.ts similarity index 100% rename from server/src/store/versionedUpdates.ts rename to hub/src/store/versionedUpdates.ts diff --git a/server/src/sync/aliveTime.ts b/hub/src/sync/aliveTime.ts similarity index 100% rename from server/src/sync/aliveTime.ts rename to hub/src/sync/aliveTime.ts diff --git a/server/src/sync/eventPublisher.ts b/hub/src/sync/eventPublisher.ts similarity index 100% rename from server/src/sync/eventPublisher.ts rename to hub/src/sync/eventPublisher.ts diff --git a/server/src/sync/machineCache.ts b/hub/src/sync/machineCache.ts similarity index 100% rename from server/src/sync/machineCache.ts rename to hub/src/sync/machineCache.ts diff --git a/server/src/sync/messageService.ts b/hub/src/sync/messageService.ts similarity index 100% rename from server/src/sync/messageService.ts rename to hub/src/sync/messageService.ts diff --git a/server/src/sync/rpcGateway.ts b/hub/src/sync/rpcGateway.ts similarity index 100% rename from server/src/sync/rpcGateway.ts rename to hub/src/sync/rpcGateway.ts diff --git a/server/src/sync/sessionCache.ts b/hub/src/sync/sessionCache.ts similarity index 100% rename from server/src/sync/sessionCache.ts rename to hub/src/sync/sessionCache.ts diff --git a/server/src/sync/syncEngine.ts b/hub/src/sync/syncEngine.ts similarity index 99% rename from server/src/sync/syncEngine.ts rename to hub/src/sync/syncEngine.ts index 8cc520e3..e81cd68f 100644 --- a/server/src/sync/syncEngine.ts +++ b/hub/src/sync/syncEngine.ts @@ -2,8 +2,8 @@ * Sync Engine for HAPI Telegram Bot (Direct Connect) * * In the direct-connect architecture: - * - hapi-server is the server (Socket.IO + REST) - * - hapi CLI connects directly to the server (no relay) + * - hapi-hub is the hub (Socket.IO + REST) + * - hapi CLI connects directly to the hub (no relay) * - No E2E encryption; data is stored as JSON in SQLite */ diff --git a/server/src/sync/todos.ts b/hub/src/sync/todos.ts similarity index 100% rename from server/src/sync/todos.ts rename to hub/src/sync/todos.ts diff --git a/server/src/telegram/bot.ts b/hub/src/telegram/bot.ts similarity index 100% rename from server/src/telegram/bot.ts rename to hub/src/telegram/bot.ts diff --git a/server/src/telegram/callbacks.ts b/hub/src/telegram/callbacks.ts similarity index 100% rename from server/src/telegram/callbacks.ts rename to hub/src/telegram/callbacks.ts diff --git a/server/src/telegram/renderer.ts b/hub/src/telegram/renderer.ts similarity index 100% rename from server/src/telegram/renderer.ts rename to hub/src/telegram/renderer.ts diff --git a/server/src/telegram/sessionView.ts b/hub/src/telegram/sessionView.ts similarity index 100% rename from server/src/telegram/sessionView.ts rename to hub/src/telegram/sessionView.ts diff --git a/server/src/tunnel/index.ts b/hub/src/tunnel/index.ts similarity index 100% rename from server/src/tunnel/index.ts rename to hub/src/tunnel/index.ts diff --git a/server/src/tunnel/tlsGate.ts b/hub/src/tunnel/tlsGate.ts similarity index 100% rename from server/src/tunnel/tlsGate.ts rename to hub/src/tunnel/tlsGate.ts diff --git a/server/src/tunnel/tunnelManager.ts b/hub/src/tunnel/tunnelManager.ts similarity index 99% rename from server/src/tunnel/tunnelManager.ts rename to hub/src/tunnel/tunnelManager.ts index 35cf4360..47a1ea87 100644 --- a/server/src/tunnel/tunnelManager.ts +++ b/hub/src/tunnel/tunnelManager.ts @@ -48,7 +48,7 @@ function getTunwgPath(): string { return join(runtimePath, 'tools', 'tunwg', tunwgBinary) } - // Development mode: use downloaded binary from server/tools/tunwg + // Development mode: use downloaded binary from hub/tools/tunwg const platformDir = getPlatformDir() const devBinaryName = isWin ? `tunwg-${platformDir}.exe` : `tunwg-${platformDir}` return join(__dirname, '..', '..', 'tools', 'tunwg', devBinaryName) diff --git a/server/src/types/webAssetImports.d.ts b/hub/src/types/webAssetImports.d.ts similarity index 100% rename from server/src/types/webAssetImports.d.ts rename to hub/src/types/webAssetImports.d.ts diff --git a/server/src/utils/accessToken.test.ts b/hub/src/utils/accessToken.test.ts similarity index 100% rename from server/src/utils/accessToken.test.ts rename to hub/src/utils/accessToken.test.ts diff --git a/server/src/utils/accessToken.ts b/hub/src/utils/accessToken.ts similarity index 100% rename from server/src/utils/accessToken.ts rename to hub/src/utils/accessToken.ts diff --git a/server/src/utils/bunCompiled.ts b/hub/src/utils/bunCompiled.ts similarity index 100% rename from server/src/utils/bunCompiled.ts rename to hub/src/utils/bunCompiled.ts diff --git a/server/src/utils/crypto.ts b/hub/src/utils/crypto.ts similarity index 100% rename from server/src/utils/crypto.ts rename to hub/src/utils/crypto.ts diff --git a/server/src/visibility/visibilityTracker.ts b/hub/src/visibility/visibilityTracker.ts similarity index 100% rename from server/src/visibility/visibilityTracker.ts rename to hub/src/visibility/visibilityTracker.ts diff --git a/server/src/web/embeddedAssets.generated.d.ts b/hub/src/web/embeddedAssets.generated.d.ts similarity index 100% rename from server/src/web/embeddedAssets.generated.d.ts rename to hub/src/web/embeddedAssets.generated.d.ts diff --git a/server/src/web/embeddedAssets.ts b/hub/src/web/embeddedAssets.ts similarity index 100% rename from server/src/web/embeddedAssets.ts rename to hub/src/web/embeddedAssets.ts diff --git a/server/src/web/middleware/auth.ts b/hub/src/web/middleware/auth.ts similarity index 100% rename from server/src/web/middleware/auth.ts rename to hub/src/web/middleware/auth.ts diff --git a/server/src/web/routes/auth.ts b/hub/src/web/routes/auth.ts similarity index 100% rename from server/src/web/routes/auth.ts rename to hub/src/web/routes/auth.ts diff --git a/server/src/web/routes/bind.ts b/hub/src/web/routes/bind.ts similarity index 100% rename from server/src/web/routes/bind.ts rename to hub/src/web/routes/bind.ts diff --git a/server/src/web/routes/cli.ts b/hub/src/web/routes/cli.ts similarity index 100% rename from server/src/web/routes/cli.ts rename to hub/src/web/routes/cli.ts diff --git a/server/src/web/routes/events.ts b/hub/src/web/routes/events.ts similarity index 100% rename from server/src/web/routes/events.ts rename to hub/src/web/routes/events.ts diff --git a/server/src/web/routes/git.ts b/hub/src/web/routes/git.ts similarity index 100% rename from server/src/web/routes/git.ts rename to hub/src/web/routes/git.ts diff --git a/server/src/web/routes/guards.ts b/hub/src/web/routes/guards.ts similarity index 100% rename from server/src/web/routes/guards.ts rename to hub/src/web/routes/guards.ts diff --git a/server/src/web/routes/machines.ts b/hub/src/web/routes/machines.ts similarity index 100% rename from server/src/web/routes/machines.ts rename to hub/src/web/routes/machines.ts diff --git a/server/src/web/routes/messages.ts b/hub/src/web/routes/messages.ts similarity index 100% rename from server/src/web/routes/messages.ts rename to hub/src/web/routes/messages.ts diff --git a/server/src/web/routes/permissions.ts b/hub/src/web/routes/permissions.ts similarity index 100% rename from server/src/web/routes/permissions.ts rename to hub/src/web/routes/permissions.ts diff --git a/server/src/web/routes/push.ts b/hub/src/web/routes/push.ts similarity index 100% rename from server/src/web/routes/push.ts rename to hub/src/web/routes/push.ts diff --git a/server/src/web/routes/sessions.ts b/hub/src/web/routes/sessions.ts similarity index 100% rename from server/src/web/routes/sessions.ts rename to hub/src/web/routes/sessions.ts diff --git a/server/src/web/routes/voice.ts b/hub/src/web/routes/voice.ts similarity index 100% rename from server/src/web/routes/voice.ts rename to hub/src/web/routes/voice.ts diff --git a/server/src/web/server.ts b/hub/src/web/server.ts similarity index 97% rename from server/src/web/server.ts rename to hub/src/web/server.ts index 605409e8..08800fc7 100644 --- a/server/src/web/server.ts +++ b/hub/src/web/server.ts @@ -104,10 +104,10 @@ function createWebApp(options: { app.get('/', (c) => { return c.html(` -HAPI Server +HAPI Hub -

HAPI Server

-

This server is running in relay mode. Please use the official web app:

+

HAPI Hub

+

This hub is running in relay mode. Please use the official web app:

${officialUrl}

Why am I seeing this? @@ -245,7 +245,7 @@ export async function startWebServer(options: { } }) - console.log(`[Web] server listening on ${configuration.listenHost}:${configuration.listenPort}`) + console.log(`[Web] hub listening on ${configuration.listenHost}:${configuration.listenPort}`) console.log(`[Web] public URL: ${configuration.publicUrl}`) return server diff --git a/server/src/web/telegramInitData.ts b/hub/src/web/telegramInitData.ts similarity index 100% rename from server/src/web/telegramInitData.ts rename to hub/src/web/telegramInitData.ts diff --git a/server/tools/.gitignore b/hub/tools/.gitignore similarity index 100% rename from server/tools/.gitignore rename to hub/tools/.gitignore diff --git a/server/tools/tunwg/LICENSE b/hub/tools/tunwg/LICENSE similarity index 100% rename from server/tools/tunwg/LICENSE rename to hub/tools/tunwg/LICENSE diff --git a/server/tsconfig.json b/hub/tsconfig.json similarity index 100% rename from server/tsconfig.json rename to hub/tsconfig.json diff --git a/package.json b/package.json index 5a213490..6d677bae 100644 --- a/package.json +++ b/package.json @@ -1,27 +1,27 @@ { "name": "hapi", "private": true, - "workspaces": ["cli", "shared", "server", "web", "website", "docs"], + "workspaces": ["cli", "shared", "hub", "web", "website", "docs"], "scripts": { "build:site": "bun run --cwd website build && bun run --cwd docs docs:build && cp -r docs/.vitepress/dist website/dist/public/docs", - "dev": "concurrently \"bun run dev:server\" \"bun run dev:web\" --kill-others-on-exit", - "build": "bun run build:cli && bun run build:server && bun run build:web", + "dev": "concurrently \"bun run dev:hub\" \"bun run dev:web\" --kill-others-on-exit", + "build": "bun run build:cli && bun run build:hub && bun run build:web", "build:cli": "cd cli && bun run build", - "build:single-exe": "bun run download:tunwg && bun run build:web && (cd server && bun run generate:embedded-web-assets) && (cd cli && bun run build:exe:allinone)", - "build:single-exe:all": "bun run download:tunwg && bun run build:web && (cd server && bun run generate:embedded-web-assets) && (cd cli && bun run build:exe:allinone:all)", - "download:tunwg": "bun run server/scripts/download-tunwg.ts", - "build:server": "cd server && bun run build", + "build:single-exe": "bun run download:tunwg && bun run build:web && (cd hub && bun run generate:embedded-web-assets) && (cd cli && bun run build:exe:allinone)", + "build:single-exe:all": "bun run download:tunwg && bun run build:web && (cd hub && bun run generate:embedded-web-assets) && (cd cli && bun run build:exe:allinone:all)", + "download:tunwg": "bun run hub/scripts/download-tunwg.ts", + "build:hub": "cd hub && bun run build", "build:web": "cd web && bun run build", - "dev:server": "cd server && bun run dev", + "dev:hub": "cd hub && bun run dev", "dev:web": "cd web && bun run dev", - "typecheck": "bun run typecheck:cli && bun run typecheck:web && bun run typecheck:server", + "typecheck": "bun run typecheck:cli && bun run typecheck:web && bun run typecheck:hub", "typecheck:cli": "cd cli && bun run typecheck", - "typecheck:server": "cd server && bun run typecheck", + "typecheck:hub": "cd hub && bun run typecheck", "typecheck:web": "cd web && bun run typecheck", - "test": "bun run test:cli && bun run test:server", + "test": "bun run test:cli && bun run test:hub", "test:cli": "cd cli && bun run test", - "test:server": "cd server && bun run test", - "clean-session": "bun run server/scripts/cleanup-sessions.ts", + "test:hub": "cd hub && bun run test", + "clean-session": "bun run hub/scripts/cleanup-sessions.ts", "release-all": "cd cli && bun run release-all" }, "devDependencies": { diff --git a/web/README.md b/web/README.md index 40fbec6d..23197489 100644 --- a/web/README.md +++ b/web/README.md @@ -16,8 +16,8 @@ React Mini App / PWA for monitoring and controlling hapi sessions. - When opened inside Telegram, auth uses Telegram WebApp init data. - When opened in a normal browser, you can log in with `CLI_API_TOKEN:` (or `CLI_API_TOKEN` for the default namespace). -- The login screen includes a top-right server picker; if unset, the app uses the same origin it was loaded from. -- Live updates come from the server via SSE. +- The login screen includes a top-right hub picker; if unset, the app uses the same origin it was loaded from. +- Live updates come from the hub via SSE. ## Routes @@ -122,11 +122,11 @@ If testing in Telegram, set: bun run build:web ``` -The built assets land in `web/dist` and are served by hapi-server. The single executable can embed these assets. +The built assets land in `web/dist` and are served by hapi-hub. The single executable can embed these assets. ## Standalone hosting -You can host `web/dist` on a static host (GitHub Pages, Cloudflare Pages) and point it at any hapi server: +You can host `web/dist` on a static host (GitHub Pages, Cloudflare Pages) and point it at any hapi hub: 1. Build the web app. If your static host uses a subpath, set the Vite base: @@ -135,7 +135,7 @@ bun run build:web -- --base // ``` 2. Deploy `web/dist` to your static host. -3. Set server CORS to allow the static origin (`HAPI_PUBLIC_URL` or `CORS_ORIGINS`). -4. Open the static site, click the top-right Server button on the login screen, and enter the hapi server origin. +3. Set hub CORS to allow the static origin (`HAPI_PUBLIC_URL` or `CORS_ORIGINS`). +4. Open the static site, click the top-right Hub button on the login screen, and enter the hapi hub origin. -Clear the server override in the same dialog to return to same-origin behavior. +Clear the hub override in the same dialog to return to same-origin behavior. diff --git a/web/src/App.tsx b/web/src/App.tsx index 5f0bc2e7..30c37b42 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -133,10 +133,11 @@ function AppInner() { if (!token || !api) return const { pathname, search, hash, state } = router.history.location const searchParams = new URLSearchParams(search) - if (!searchParams.has('server') && !searchParams.has('token')) { + if (!searchParams.has('server') && !searchParams.has('hub') && !searchParams.has('token')) { return } searchParams.delete('server') + searchParams.delete('hub') searchParams.delete('token') const nextSearch = searchParams.toString() const nextHref = `${pathname}${nextSearch ? `?${nextSearch}` : ''}${hash}` diff --git a/web/src/api/voice.ts b/web/src/api/voice.ts index 5a6da937..66cee443 100644 --- a/web/src/api/voice.ts +++ b/web/src/api/voice.ts @@ -1,11 +1,11 @@ /** * API functions for voice assistant integration. * - * Fetches conversation tokens from the server for ElevenLabs integration. - * The server handles authentication with ElevenLabs API, keeping credentials secure. + * Fetches conversation tokens from the hub for ElevenLabs integration. + * The hub handles authentication with ElevenLabs API, keeping credentials secure. * * Supports two modes: - * 1. Default: Server uses its own ElevenLabs credentials (production) + * 1. Default: Hub uses its own ElevenLabs credentials (production) * 2. Custom: Client provides their own ElevenLabs agent ID and API key */ @@ -29,11 +29,11 @@ export interface VoiceTokenRequest { } /** - * Fetch a conversation token from the server for ElevenLabs voice sessions. + * Fetch a conversation token from the hub for ElevenLabs voice sessions. * * This uses the private agent flow where: - * 1. Server holds the ELEVENLABS_API_KEY and ELEVENLABS_AGENT_ID (or uses user-provided ones) - * 2. Server fetches a short-lived conversation token from ElevenLabs + * 1. Hub holds the ELEVENLABS_API_KEY and ELEVENLABS_AGENT_ID (or uses user-provided ones) + * 2. Hub fetches a short-lived conversation token from ElevenLabs * 3. Client uses this token to establish WebRTC connection */ export async function fetchVoiceToken( diff --git a/web/src/components/LoginPrompt.tsx b/web/src/components/LoginPrompt.tsx index b1f3bc7c..20356001 100644 --- a/web/src/components/LoginPrompt.tsx +++ b/web/src/components/LoginPrompt.tsx @@ -161,7 +161,7 @@ export function LoginPrompt(props: LoginPromptProps) { diff --git a/web/src/hooks/useServerUrl.ts b/web/src/hooks/useServerUrl.ts index 69e739e1..0a6aec09 100644 --- a/web/src/hooks/useServerUrl.ts +++ b/web/src/hooks/useServerUrl.ts @@ -1,6 +1,6 @@ import { useCallback, useMemo, useState } from 'react' -const SERVER_URL_KEY = 'hapi_server_url' +const HUB_URL_KEY = 'hapi_hub_url' export type ServerUrlResult = | { ok: true; value: string } @@ -9,7 +9,7 @@ export type ServerUrlResult = export function normalizeServerUrl(input: string): ServerUrlResult { const trimmed = input.trim() if (!trimmed) { - return { ok: false, error: 'Enter a server URL like https://example.com' } + return { ok: false, error: 'Enter a hub URL like https://example.com' } } let parsed: URL @@ -20,7 +20,7 @@ export function normalizeServerUrl(input: string): ServerUrlResult { } if (parsed.protocol !== 'http:' && parsed.protocol !== 'https:') { - return { ok: false, error: 'Server URL must start with http:// or https://' } + return { ok: false, error: 'Hub URL must start with http:// or https://' } } return { ok: true, value: parsed.origin } @@ -29,9 +29,9 @@ export function normalizeServerUrl(input: string): ServerUrlResult { function getServerFromUrlParams(): string | null { if (typeof window === 'undefined') return null const query = new URLSearchParams(window.location.search) - const server = query.get('server') - if (server) { - const normalized = normalizeServerUrl(server) + const hub = query.get('hub') + if (hub) { + const normalized = normalizeServerUrl(hub) return normalized.ok ? normalized.value : null } return null @@ -39,13 +39,13 @@ function getServerFromUrlParams(): string | null { function readStoredServerUrl(): string | null { try { - const stored = localStorage.getItem(SERVER_URL_KEY) + const stored = localStorage.getItem(HUB_URL_KEY) if (!stored) { return null } const normalized = normalizeServerUrl(stored) if (!normalized.ok) { - localStorage.removeItem(SERVER_URL_KEY) + localStorage.removeItem(HUB_URL_KEY) return null } return normalized.value @@ -56,7 +56,7 @@ function readStoredServerUrl(): string | null { function writeStoredServerUrl(value: string): void { try { - localStorage.setItem(SERVER_URL_KEY, value) + localStorage.setItem(HUB_URL_KEY, value) } catch { // Ignore storage errors } @@ -64,7 +64,7 @@ function writeStoredServerUrl(value: string): void { function clearStoredServerUrl(): void { try { - localStorage.removeItem(SERVER_URL_KEY) + localStorage.removeItem(HUB_URL_KEY) } catch { // Ignore storage errors } diff --git a/web/src/lib/locales/en.ts b/web/src/lib/locales/en.ts index 7a157486..98bc0bf9 100644 --- a/web/src/lib/locales/en.ts +++ b/web/src/lib/locales/en.ts @@ -25,14 +25,14 @@ export default { 'login.help': 'Needs help?', 'login.server.default': '(Default)', 'login.server.custom': '(Custom)', - 'login.server.title': 'Server URL', - 'login.server.description': 'Set hapi server origin for API and live updates.', + 'login.server.title': 'Hub URL', + 'login.server.description': 'Set hapi hub origin for API and live updates.', 'login.server.current': 'Current:', - 'login.server.origin': 'Server origin', + 'login.server.origin': 'Hub origin', 'login.server.placeholder': 'https://hapi.example.com', 'login.server.hint': 'Use http(s) only. Any path is ignored.', 'login.server.useSameOrigin': 'Use same origin', - 'login.server.save': 'Save server', + 'login.server.save': 'Save hub', 'login.footer': 'Designed with', 'login.footer.for': 'for Vibe Coding', 'login.footer.copyright': '©', diff --git a/website/src/pages/Home.tsx b/website/src/pages/Home.tsx index ec3e6e5c..4a105298 100644 --- a/website/src/pages/Home.tsx +++ b/website/src/pages/Home.tsx @@ -274,9 +274,9 @@ export default function Home() {

{t('installation.npm.step1')}

- npx @twsxtd/hapi server --relay -