Files
hapi/cli/CLAUDE.md
T
weishu 9e335fa305 refactor: rename configuration variables for clarity
Standardize naming across CLI and server components:
- CLI: serverUrl → apiUrl, HAPI_SERVER_URL → HAPI_API_URL
- Server: webapp* → listen*, miniAppUrl → publicUrl, WEBAPP_* → HAPI_LISTEN_*, WEBAPP_URL → HAPI_PUBLIC_URL
- Rename serverUrlInit.ts → apiUrlInit.ts with updated logic for backward compatibility
- Update all imports, function calls, and documentation accordingly
2026-01-19 12:57:37 +08:00

224 lines
8.1 KiB
Markdown

# HAPI CLI Codebase Overview
## 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:
1. **hapi** (this project) - CLI wrapper for Claude Code
2. **hapi-server** - Public server (Socket.IO + REST + SQLite) + Telegram Mini App
## Code Style Preferences
### TypeScript Conventions
- **Strict typing**: No untyped code ("I despise untyped code")
- **Clean function signatures**: Explicit parameter and return types
- **As little as possible classes**
- **Comprehensive JSDoc comments**: Each file includes header comments explaining responsibilities.
- **Import style**: Uses `@/` alias for src imports, e.g., `import { logger } from '@/ui/logger'`
- **File extensions**: Uses `.ts` for TypeScript files
- **Export style**: Named exports preferred, with occasional default exports for main functions
### DO NOT
- Create stupid small functions / getters / setters
- Excessive use of `if` statements - especially if you can avoid control flow changes with a better design
- **NEVER import modules mid-code** - ALL imports must be at the top of the file
### Error Handling
- Graceful error handling with proper error messages
- Use of `try-catch` blocks with specific error logging
- Abort controllers for cancellable operations
- Careful handling of process lifecycle and cleanup
### Testing
- Unit tests using Vitest
- No mocking - tests make real API calls
- Test files colocated with source files (`.test.ts`)
- Descriptive test names and proper async handling
### Logging
- All debugging through file logs to avoid disturbing Claude sessions
- Console output only for user-facing messages
- Special handling for large JSON objects with truncation
## Architecture & Key Components
### 1. API Module (`/src/api/`)
Handles bot communication (direct connect; no end-to-end encryption).
- **`api.ts`**: Main API client class for session management
- **`apiSession.ts`**: WebSocket-based real-time session client with RPC support
- **`auth.ts`**: Loads `CLI_API_TOKEN` from env
- **`encryption.ts`**: Base64 helpers (no encryption)
- **`types.ts`**: Zod schemas for type-safe API communication
**Key Features:**
- Socket.IO for real-time messaging
- Optimistic concurrency control for state updates
- RPC handler registration for remote procedure calls
### 2. Claude Integration (`/src/claude/`)
Core Claude Code integration layer.
- **`loop.ts`**: Main control loop managing interactive/remote modes
- **`types.ts`**: Claude message type definitions with parsers
- **`claudeSdk.ts`**: Direct SDK integration using `@anthropic-ai/claude-code`
- **`interactive.ts`**: **LIKELY WILL BE DEPRECATED in favor of running through SDK** PTY-based interactive Claude sessions
- **`watcher.ts`**: File system watcher for Claude session files (for interactive mode snooping)
- **`mcp/startPermissionServer.ts`**: MCP (Model Context Protocol) permission server
**Key Features:**
- Dual mode operation: interactive (terminal) and remote (mobile control)
- Session persistence and resumption
- Real-time message streaming
- Permission intercepting via MCP [Permission checking not implemented yet]
### 3. UI Module (`/src/ui/`)
User interface components.
- **`logger.ts`**: Centralized logging system with file output
- **`qrcode.ts`**: QR code generation for mobile authentication
- **`start.ts`**: Main application startup and orchestration
**Key Features:**
- Clean console UI with chalk styling
- QR code display for easy mobile connection
- Graceful mode switching between interactive and remote
### 4. Core Files
- **`index.ts`**: CLI entry point with argument parsing
- **`persistence.ts`**: Local storage for settings and keys
- **`utils/time.ts`**: Exponential backoff utilities
## Data Flow
1. **Authentication**:
- Use `CLI_API_TOKEN` to authenticate to `hapi-server` (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
4. **Permission Handling**:
- Claude requests permission → hapi CLI exposes RPC handlers → Mini App calls REST → hapi-server 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
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.
- Session isolation through unique session IDs.
## Dependencies
- Core: Node.js, TypeScript
- Claude: `@anthropic-ai/claude-code` SDK
- Networking: Socket.IO client, Axios
- Terminal: node-pty, chalk, qrcode-terminal
- Validation: Zod
- Testing: Vitest
# Running the Runner
## Starting the Runner
```bash
# From the hapi CLI directory:
hapi runner start
# With custom bot URL (for local development):
HAPI_API_URL=http://localhost:3006 CLI_API_TOKEN=your_token hapi runner start
# Stop the runner:
hapi runner stop
# Check runner status:
hapi runner status
```
## Runner Logs
- Runner logs are stored in `~/.hapi/logs/` (or `$HAPI_HOME/logs/`)
- Named with format: `YYYY-MM-DD-HH-MM-SS-runner.log`
# Session Forking `claude` and sdk behavior
## Commands Run
### Initial Session
```bash
claude --print --output-format stream-json --verbose 'list files in this directory'
```
- Original Session ID: `aada10c6-9299-4c45-abc4-91db9c0f935d`
- Created file: `~/.claude/projects/.../aada10c6-9299-4c45-abc4-91db9c0f935d.jsonl`
### Resume with --resume flag
```bash
claude --print --output-format stream-json --verbose --resume aada10c6-9299-4c45-abc4-91db9c0f935d 'what file did we just see?'
```
- New Session ID: `1433467f-ff14-4292-b5b2-2aac77a808f0`
- Created file: `~/.claude/projects/.../1433467f-ff14-4292-b5b2-2aac77a808f0.jsonl`
## Key Findings for --resume
### 1. Session File Behavior
- Creates a NEW session file with NEW session ID
- Original session file remains unchanged
- Two separate files exist after resumption
### 2. History Preservation
- The new session file contains the COMPLETE history from the original session
- History is prefixed at the beginning of the new file
- Includes a summary line at the very top
### 3. Session ID Rewriting
- **CRITICAL FINDING**: All historical messages have their sessionId field UPDATED to the new session ID
- Original messages from session `aada10c6-9299-4c45-abc4-91db9c0f935d` now show `sessionId: "1433467f-ff14-4292-b5b2-2aac77a808f0"`
- This creates a unified session history under the new ID
### 4. Message Structure in New File
```
Line 1: Summary of previous conversation
Lines 2-6: Complete history from original session (with updated session IDs)
Lines 7-8: New messages from current interaction
```
### 5. Context Preservation
- Claude successfully maintains full context
- Can answer questions about previous interactions
- Behaves as if it's a continuous conversation
## Technical Details
### Original Session File Structure
- Contains only messages from the original session
- All messages have original session ID
- Remains untouched after resume
### New Session File Structure After Resume
```json
{"type":"summary","summary":"Listing directory files in current location","leafUuid":"..."}
{"parentUuid":null,"sessionId":"1433467f-ff14-4292-b5b2-2aac77a808f0","message":{"role":"user","content":[{"type":"text","text":"list files in this directory"}]},...}
// ... all historical messages with NEW session ID ...
{"parentUuid":"...","sessionId":"1433467f-ff14-4292-b5b2-2aac77a808f0","message":{"role":"user","content":"what file did we just see?"},...}
```
## Implications for handy-cli
When using --resume:
1. Must handle new session ID in responses
2. Original session remains as historical record
3. All context preserved but under new session identity
4. Session ID in stream-json output will be the new one, not the resumed one