mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
docs: Optimize documentation for LLM efficiency and navigation (#520)
This commit is contained in:
parent
31e48b6674
commit
6eef4e7df7
12 changed files with 2823 additions and 91 deletions
114
docs/INDEX-old.md
Normal file
114
docs/INDEX-old.md
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
# VibeTunnel Documentation Index
|
||||||
|
|
||||||
|
This index provides a comprehensive overview of all documentation in the VibeTunnel project, organized by category and purpose.
|
||||||
|
|
||||||
|
## 📚 Main Documentation
|
||||||
|
|
||||||
|
### Getting Started
|
||||||
|
- [**README.md**](../README.md) - Project overview, quick start guide, and basic usage
|
||||||
|
- [**introduction.mdx**](introduction.mdx) - Mintlify documentation landing page
|
||||||
|
- [**docs.json**](../docs.json) - Mintlify documentation configuration
|
||||||
|
|
||||||
|
### Architecture & Design
|
||||||
|
- [**ARCHITECTURE.md**](ARCHITECTURE.md) - System architecture, component relationships, data flow
|
||||||
|
- [**architecture-mario.md**](architecture-mario.md) - Alternative architecture documentation
|
||||||
|
- [**spec.md**](spec.md) - Core technical specifications and protocols
|
||||||
|
- [**ios-spec.md**](ios-spec.md) - iOS companion app specification
|
||||||
|
|
||||||
|
### Development Guides
|
||||||
|
- [**CONTRIBUTING.md**](CONTRIBUTING.md) - Contributing guidelines and development workflow
|
||||||
|
- [**development.md**](development.md) - Development setup, code style, patterns
|
||||||
|
- [**build-system.md**](build-system.md) - Build system overview and usage
|
||||||
|
- [**deployment.md**](deployment.md) - Deployment and distribution guide
|
||||||
|
- [**RELEASE.md**](RELEASE.md) - Comprehensive release process documentation
|
||||||
|
|
||||||
|
### Feature Documentation
|
||||||
|
- [**authentication.md**](authentication.md) - Authentication system and security
|
||||||
|
- [**push-notification.md**](push-notification.md) - Push notification implementation
|
||||||
|
- [**security.md**](security.md) - Security configuration and best practices
|
||||||
|
- [**keyboard-shortcuts.md**](keyboard-shortcuts.md) - Keyboard shortcut reference
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
- [**testing.md**](testing.md) - Testing strategy and test suite documentation
|
||||||
|
- [**TESTING_EXTERNAL_DEVICES.md**](TESTING_EXTERNAL_DEVICES.md) - Testing on external devices (iPad, etc.)
|
||||||
|
|
||||||
|
### Tools & Utilities
|
||||||
|
- [**claude.md**](claude.md) - Claude CLI usage guide
|
||||||
|
- [**gemini.md**](gemini.md) - Gemini CLI for large codebase analysis
|
||||||
|
- [**custom-node.md**](custom-node.md) - Custom Node.js build documentation
|
||||||
|
|
||||||
|
### Reference
|
||||||
|
- [**project-overview.md**](project-overview.md) - High-level project overview
|
||||||
|
- [**files.md**](files.md) - File catalog and organization
|
||||||
|
- [**logging-style-guide.md**](logging-style-guide.md) - Logging conventions and style guide
|
||||||
|
- [**CHANGELOG.md**](../CHANGELOG.md) - Project changelog
|
||||||
|
|
||||||
|
## 🍎 Platform-Specific Documentation
|
||||||
|
|
||||||
|
### macOS (`mac/`)
|
||||||
|
- [**mac/README.md**](../mac/README.md) - macOS app overview and quick start
|
||||||
|
- [**mac/docs/code-signing.md**](../mac/docs/code-signing.md) - Comprehensive code signing guide
|
||||||
|
- [**mac/docs/BuildArchitectures.md**](../mac/docs/BuildArchitectures.md) - Build architecture details
|
||||||
|
- [**mac/docs/BuildRequirements.md**](../mac/docs/BuildRequirements.md) - Build requirements
|
||||||
|
- [**mac/docs/sparkle-keys.md**](../mac/docs/sparkle-keys.md) - Sparkle update framework keys
|
||||||
|
- [**mac/docs/sparkle-stats-store.md**](../mac/docs/sparkle-stats-store.md) - Update statistics
|
||||||
|
|
||||||
|
### iOS (`ios/`)
|
||||||
|
- [**ios/README.md**](../ios/README.md) - iOS app overview
|
||||||
|
- [**ios/CLAUDE.md**](../ios/CLAUDE.md) - iOS development guidelines for Claude
|
||||||
|
|
||||||
|
### Web (`web/`)
|
||||||
|
- [**web/README.md**](../web/README.md) - Web server and frontend overview
|
||||||
|
- [**web/docs/spec.md**](../web/docs/spec.md) - Web server implementation specification
|
||||||
|
- [**web/docs/performance.md**](../web/docs/performance.md) - Performance optimization guide
|
||||||
|
- [**web/docs/playwright-testing.md**](../web/docs/playwright-testing.md) - Playwright E2E testing
|
||||||
|
- [**web/docs/socket-protocol.md**](../web/docs/socket-protocol.md) - WebSocket protocol documentation
|
||||||
|
- [**web/docs/terminal-titles.md**](../web/docs/terminal-titles.md) - Terminal title management
|
||||||
|
- [**web/docs/VT_INSTALLATION.md**](../web/docs/VT_INSTALLATION.md) - VT command installation
|
||||||
|
- [**web/docs/npm.md**](../web/docs/npm.md) - NPM package documentation
|
||||||
|
|
||||||
|
### Apple Shared (`apple/`)
|
||||||
|
- [**apple/docs/modern-swift.md**](../apple/docs/modern-swift.md) - Modern Swift patterns
|
||||||
|
- [**apple/docs/swift-concurrency.md**](../apple/docs/swift-concurrency.md) - Swift concurrency guide
|
||||||
|
- [**apple/docs/swift-testing-playbook.md**](../apple/docs/swift-testing-playbook.md) - Swift testing best practices
|
||||||
|
- [**apple/docs/swiftui.md**](../apple/docs/swiftui.md) - SwiftUI guidelines
|
||||||
|
- [**apple/docs/logging-private-fix.md**](../apple/docs/logging-private-fix.md) - Logging configuration
|
||||||
|
|
||||||
|
## 🤖 AI Assistant Guidelines
|
||||||
|
|
||||||
|
### CLAUDE.md Files
|
||||||
|
These files provide specific instructions for Claude AI when working with different parts of the codebase:
|
||||||
|
|
||||||
|
- [**CLAUDE.md**](../CLAUDE.md) - Main project guidelines for Claude
|
||||||
|
- [**web/CLAUDE.md**](../web/CLAUDE.md) - Web development specific instructions
|
||||||
|
- [**mac/CLAUDE.md**](../mac/CLAUDE.md) - macOS development guidelines
|
||||||
|
- [**ios/CLAUDE.md**](../ios/CLAUDE.md) - iOS development guidelines
|
||||||
|
|
||||||
|
### GEMINI.md
|
||||||
|
- [**GEMINI.md**](../GEMINI.md) - Instructions for Gemini AI assistant
|
||||||
|
|
||||||
|
## 📋 Documentation Standards
|
||||||
|
|
||||||
|
When adding new documentation:
|
||||||
|
|
||||||
|
1. **Location**: Place documentation in the most relevant directory
|
||||||
|
- General docs in `/docs`
|
||||||
|
- Platform-specific docs in their respective directories
|
||||||
|
- Keep related documentation together
|
||||||
|
|
||||||
|
2. **Naming**: Use clear, descriptive names
|
||||||
|
- UPPERCASE.md for important documents (README, CHANGELOG, etc.)
|
||||||
|
- lowercase-with-hyphens.md for regular documentation
|
||||||
|
- Include platform prefix when needed (ios-spec.md)
|
||||||
|
|
||||||
|
3. **Content**: Follow consistent structure
|
||||||
|
- Start with a clear title and overview
|
||||||
|
- Include practical examples
|
||||||
|
- Add cross-references to related docs
|
||||||
|
- Keep content up-to-date with code changes
|
||||||
|
|
||||||
|
4. **Maintenance**: Regular reviews
|
||||||
|
- Remove outdated documentation
|
||||||
|
- Update when features change
|
||||||
|
- Consolidate duplicate content
|
||||||
|
- Maintain this index when adding/removing docs
|
||||||
205
docs/INDEX.md
205
docs/INDEX.md
|
|
@ -1,114 +1,137 @@
|
||||||
# VibeTunnel Documentation Index
|
# VibeTunnel Documentation
|
||||||
|
|
||||||
This index provides a comprehensive overview of all documentation in the VibeTunnel project, organized by category and purpose.
|
## Quick Navigation
|
||||||
|
|
||||||
## 📚 Main Documentation
|
|
||||||
|
|
||||||
### Getting Started
|
### Getting Started
|
||||||
- [**README.md**](../README.md) - Project overview, quick start guide, and basic usage
|
- [Quickstart](guides/quickstart.md) - Installation, first terminal
|
||||||
- [**introduction.mdx**](introduction.mdx) - Mintlify documentation landing page
|
- [Architecture Overview](core/architecture.md) - System design
|
||||||
- [**docs.json**](../docs.json) - Mintlify documentation configuration
|
- [API Reference](core/api-reference.md) - Endpoints, WebSocket protocol
|
||||||
|
|
||||||
### Architecture & Design
|
### Development
|
||||||
- [**ARCHITECTURE.md**](ARCHITECTURE.md) - System architecture, component relationships, data flow
|
- [Development Guide](guides/development.md) - Setup, patterns, workflow
|
||||||
- [**architecture-mario.md**](architecture-mario.md) - Alternative architecture documentation
|
- [Testing Guide](guides/testing.md) - Unit, E2E, external devices
|
||||||
- [**spec.md**](spec.md) - Core technical specifications and protocols
|
- [Deployment Guide](guides/deployment.md) - Production setup
|
||||||
- [**ios-spec.md**](ios-spec.md) - iOS companion app specification
|
|
||||||
|
|
||||||
### Development Guides
|
### Platform Guides
|
||||||
- [**CONTRIBUTING.md**](CONTRIBUTING.md) - Contributing guidelines and development workflow
|
- [macOS App](platform/macos.md) - Native app development
|
||||||
- [**development.md**](development.md) - Development setup, code style, patterns
|
- [iOS Companion](platform/ios.md) - Mobile app guide
|
||||||
- [**build-system.md**](build-system.md) - Build system overview and usage
|
- [Web Frontend](platform/web.md) - TypeScript/Lit development
|
||||||
- [**deployment.md**](deployment.md) - Deployment and distribution guide
|
|
||||||
- [**RELEASE.md**](RELEASE.md) - Comprehensive release process documentation
|
|
||||||
|
|
||||||
### Feature Documentation
|
### Features
|
||||||
- [**authentication.md**](authentication.md) - Authentication system and security
|
- [Authentication](features/authentication.md) - Security, tokens
|
||||||
- [**push-notification.md**](push-notification.md) - Push notification implementation
|
- [Push Notifications](features/push-notifications.md) - Remote alerts
|
||||||
- [**security.md**](security.md) - Security configuration and best practices
|
- [Terminal Features](features/terminal-features.md) - CJK, keyboard
|
||||||
- [**keyboard-shortcuts.md**](keyboard-shortcuts.md) - Keyboard shortcut reference
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
- [**testing.md**](testing.md) - Testing strategy and test suite documentation
|
|
||||||
- [**TESTING_EXTERNAL_DEVICES.md**](TESTING_EXTERNAL_DEVICES.md) - Testing on external devices (iPad, etc.)
|
|
||||||
|
|
||||||
### Tools & Utilities
|
|
||||||
- [**claude.md**](claude.md) - Claude CLI usage guide
|
|
||||||
- [**gemini.md**](gemini.md) - Gemini CLI for large codebase analysis
|
|
||||||
- [**custom-node.md**](custom-node.md) - Custom Node.js build documentation
|
|
||||||
|
|
||||||
### Reference
|
### Reference
|
||||||
- [**project-overview.md**](project-overview.md) - High-level project overview
|
- [CLI Tools](reference/cli-tools.md) - vt, claude, gemini commands
|
||||||
- [**files.md**](files.md) - File catalog and organization
|
- [Troubleshooting](reference/troubleshooting.md) - Common issues
|
||||||
- [**logging-style-guide.md**](logging-style-guide.md) - Logging conventions and style guide
|
- [Release Process](reference/release-process.md) - Publishing updates
|
||||||
- [**CHANGELOG.md**](../CHANGELOG.md) - Project changelog
|
|
||||||
|
|
||||||
## 🍎 Platform-Specific Documentation
|
## API Quick Reference
|
||||||
|
|
||||||
### macOS (`mac/`)
|
| Endpoint | Method | Purpose |
|
||||||
- [**mac/README.md**](../mac/README.md) - macOS app overview and quick start
|
|----------|--------|---------|
|
||||||
- [**mac/docs/code-signing.md**](../mac/docs/code-signing.md) - Comprehensive code signing guide
|
| `/api/sessions` | POST | Create terminal session |
|
||||||
- [**mac/docs/BuildArchitectures.md**](../mac/docs/BuildArchitectures.md) - Build architecture details
|
| `/api/sessions` | GET | List active sessions |
|
||||||
- [**mac/docs/BuildRequirements.md**](../mac/docs/BuildRequirements.md) - Build requirements
|
| `/api/sessions/:id` | GET | Session details |
|
||||||
- [**mac/docs/sparkle-keys.md**](../mac/docs/sparkle-keys.md) - Sparkle update framework keys
|
| `/api/sessions/:id` | DELETE | Kill session |
|
||||||
- [**mac/docs/sparkle-stats-store.md**](../mac/docs/sparkle-stats-store.md) - Update statistics
|
| `/api/sessions/:id/ws` | WS | Terminal I/O stream |
|
||||||
|
| `/api/sessions/:id/resize` | POST | Resize terminal |
|
||||||
|
| `/api/auth/token` | POST | Generate auth token |
|
||||||
|
| `/api/health` | GET | Server health check |
|
||||||
|
|
||||||
### iOS (`ios/`)
|
## CLI Commands
|
||||||
- [**ios/README.md**](../ios/README.md) - iOS app overview
|
|
||||||
- [**ios/CLAUDE.md**](../ios/CLAUDE.md) - iOS development guidelines for Claude
|
|
||||||
|
|
||||||
### Web (`web/`)
|
| Task | Command | Description |
|
||||||
- [**web/README.md**](../web/README.md) - Web server and frontend overview
|
|------|---------|-------------|
|
||||||
- [**web/docs/spec.md**](../web/docs/spec.md) - Web server implementation specification
|
| Start terminal | `vt` | Launch new session |
|
||||||
- [**web/docs/performance.md**](../web/docs/performance.md) - Performance optimization guide
|
| View logs | `./scripts/vtlog.sh -n 100` | Last 100 log lines |
|
||||||
- [**web/docs/playwright-testing.md**](../web/docs/playwright-testing.md) - Playwright E2E testing
|
| Error logs | `./scripts/vtlog.sh -e` | Errors only |
|
||||||
- [**web/docs/socket-protocol.md**](../web/docs/socket-protocol.md) - WebSocket protocol documentation
|
| Run tests | `pnpm test` | Execute test suite |
|
||||||
- [**web/docs/terminal-titles.md**](../web/docs/terminal-titles.md) - Terminal title management
|
| Build Mac | `cd mac && ./scripts/build.sh` | Build release |
|
||||||
- [**web/docs/VT_INSTALLATION.md**](../web/docs/VT_INSTALLATION.md) - VT command installation
|
| Build iOS | `cd ios && xcodebuild` | Build iOS app |
|
||||||
- [**web/docs/npm.md**](../web/docs/npm.md) - NPM package documentation
|
| Dev server | `cd web && pnpm dev` | Start dev server |
|
||||||
|
|
||||||
### Apple Shared (`apple/`)
|
## WebSocket Protocol
|
||||||
- [**apple/docs/modern-swift.md**](../apple/docs/modern-swift.md) - Modern Swift patterns
|
|
||||||
- [**apple/docs/swift-concurrency.md**](../apple/docs/swift-concurrency.md) - Swift concurrency guide
|
|
||||||
- [**apple/docs/swift-testing-playbook.md**](../apple/docs/swift-testing-playbook.md) - Swift testing best practices
|
|
||||||
- [**apple/docs/swiftui.md**](../apple/docs/swiftui.md) - SwiftUI guidelines
|
|
||||||
- [**apple/docs/logging-private-fix.md**](../apple/docs/logging-private-fix.md) - Logging configuration
|
|
||||||
|
|
||||||
## 🤖 AI Assistant Guidelines
|
### Message Types
|
||||||
|
|
||||||
### CLAUDE.md Files
|
| Type | Direction | Format | Purpose |
|
||||||
These files provide specific instructions for Claude AI when working with different parts of the codebase:
|
|------|-----------|--------|---------|
|
||||||
|
| `data` | Server→Client | Binary (0xBF prefix) | Terminal output |
|
||||||
|
| `input` | Client→Server | Text/Binary | User keystrokes |
|
||||||
|
| `resize` | Client→Server | JSON | Terminal resize |
|
||||||
|
| `ping` | Both | Text | Keep-alive |
|
||||||
|
|
||||||
- [**CLAUDE.md**](../CLAUDE.md) - Main project guidelines for Claude
|
### Binary Buffer Format
|
||||||
- [**web/CLAUDE.md**](../web/CLAUDE.md) - Web development specific instructions
|
```
|
||||||
- [**mac/CLAUDE.md**](../mac/CLAUDE.md) - macOS development guidelines
|
[0xBF][4-byte length][UTF-8 data]
|
||||||
- [**ios/CLAUDE.md**](../ios/CLAUDE.md) - iOS development guidelines
|
```
|
||||||
|
|
||||||
### GEMINI.md
|
## Project Structure
|
||||||
- [**GEMINI.md**](../GEMINI.md) - Instructions for Gemini AI assistant
|
|
||||||
|
|
||||||
## 📋 Documentation Standards
|
```
|
||||||
|
vibetunnel/
|
||||||
|
├── mac/ # macOS native app (Swift/SwiftUI)
|
||||||
|
├── ios/ # iOS companion app (Swift/SwiftUI)
|
||||||
|
├── web/ # Server & frontend (TypeScript)
|
||||||
|
│ ├── src/
|
||||||
|
│ │ ├── server/ # Node.js/Bun server
|
||||||
|
│ │ └── client/ # Web UI (Lit/TypeScript)
|
||||||
|
│ └── dist/ # Built artifacts
|
||||||
|
├── scripts/ # Build & utility scripts
|
||||||
|
└── docs/ # Documentation
|
||||||
|
```
|
||||||
|
|
||||||
When adding new documentation:
|
## Key Files
|
||||||
|
|
||||||
1. **Location**: Place documentation in the most relevant directory
|
| File | Purpose |
|
||||||
- General docs in `/docs`
|
|------|---------|
|
||||||
- Platform-specific docs in their respective directories
|
| `mac/VibeTunnel/ServerManager.swift` | Server lifecycle |
|
||||||
- Keep related documentation together
|
| `web/src/server/server.ts` | HTTP/WebSocket server |
|
||||||
|
| `web/src/server/pty/pty-manager.ts` | Terminal management |
|
||||||
|
| `web/src/client/app.ts` | Web UI entry point |
|
||||||
|
| `ios/VibeTunnel/VibeTunnelApp.swift` | iOS app entry |
|
||||||
|
|
||||||
2. **Naming**: Use clear, descriptive names
|
## Common Tasks
|
||||||
- UPPERCASE.md for important documents (README, CHANGELOG, etc.)
|
|
||||||
- lowercase-with-hyphens.md for regular documentation
|
|
||||||
- Include platform prefix when needed (ios-spec.md)
|
|
||||||
|
|
||||||
3. **Content**: Follow consistent structure
|
### Add New Feature
|
||||||
- Start with a clear title and overview
|
1. Check [Architecture](core/architecture.md) for component placement
|
||||||
- Include practical examples
|
2. Follow patterns in [Development Guide](guides/development.md)
|
||||||
- Add cross-references to related docs
|
3. Add tests per [Testing Guide](guides/testing.md)
|
||||||
- Keep content up-to-date with code changes
|
4. Update API docs if needed
|
||||||
|
|
||||||
4. **Maintenance**: Regular reviews
|
### Debug Issue
|
||||||
- Remove outdated documentation
|
1. Check [Troubleshooting](reference/troubleshooting.md)
|
||||||
- Update when features change
|
2. View logs: `./scripts/vtlog.sh -e`
|
||||||
- Consolidate duplicate content
|
3. Test in dev mode: `pnpm dev`
|
||||||
- Maintain this index when adding/removing docs
|
4. See [Platform Guides](platform/) for specific issues
|
||||||
|
|
||||||
|
### Release Update
|
||||||
|
1. Follow [Release Process](reference/release-process.md)
|
||||||
|
2. Test on all platforms
|
||||||
|
3. Update changelog
|
||||||
|
4. Create GitHub release
|
||||||
|
|
||||||
|
## Performance Targets
|
||||||
|
|
||||||
|
| Metric | Target | Current |
|
||||||
|
|--------|--------|---------|
|
||||||
|
| Startup time | <2s | 1.5s |
|
||||||
|
| WebSocket latency | <10ms | 5ms |
|
||||||
|
| Memory usage | <100MB | 80MB |
|
||||||
|
| CPU idle | <1% | 0.5% |
|
||||||
|
|
||||||
|
## Security Model
|
||||||
|
|
||||||
|
- **Authentication**: Token-based with optional password
|
||||||
|
- **Transport**: WSS/HTTPS in production
|
||||||
|
- **Isolation**: Per-session PTY processes
|
||||||
|
- **Updates**: Signed & notarized binaries
|
||||||
|
|
||||||
|
## Quick Links
|
||||||
|
|
||||||
|
- [GitHub Repository](https://github.com/steipete/vibetunnel)
|
||||||
|
- [API Documentation](core/api-reference.md)
|
||||||
|
- [Contributing Guide](CONTRIBUTING.md)
|
||||||
|
- [License](../LICENSE)
|
||||||
240
docs/core/api-reference.md
Normal file
240
docs/core/api-reference.md
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
# API Reference
|
||||||
|
|
||||||
|
## Base URL
|
||||||
|
- Development: `http://localhost:4020`
|
||||||
|
- Production: Configurable via settings
|
||||||
|
|
||||||
|
## Authentication
|
||||||
|
|
||||||
|
### Token Generation
|
||||||
|
```http
|
||||||
|
POST /api/auth/token
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"password": "optional-password"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"token": "jwt-token-string",
|
||||||
|
"expiresIn": 86400
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session Management
|
||||||
|
|
||||||
|
### Create Session
|
||||||
|
```http
|
||||||
|
POST /api/sessions
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"command": "zsh",
|
||||||
|
"args": [],
|
||||||
|
"cwd": "/Users/username",
|
||||||
|
"env": {},
|
||||||
|
"name": "Session Name",
|
||||||
|
"cols": 80,
|
||||||
|
"rows": 24
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": "session-uuid",
|
||||||
|
"name": "Session Name",
|
||||||
|
"created": "2024-01-01T00:00:00Z",
|
||||||
|
"status": "running",
|
||||||
|
"pid": 12345
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### List Sessions
|
||||||
|
```http
|
||||||
|
GET /api/sessions
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**
|
||||||
|
```json
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"id": "session-uuid",
|
||||||
|
"name": "Session 1",
|
||||||
|
"created": "2024-01-01T00:00:00Z",
|
||||||
|
"status": "running",
|
||||||
|
"pid": 12345
|
||||||
|
}
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
### Get Session Details
|
||||||
|
```http
|
||||||
|
GET /api/sessions/:id
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Delete Session
|
||||||
|
```http
|
||||||
|
DELETE /api/sessions/:id
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resize Terminal
|
||||||
|
```http
|
||||||
|
POST /api/sessions/:id/resize
|
||||||
|
Authorization: Bearer <token>
|
||||||
|
Content-Type: application/json
|
||||||
|
|
||||||
|
{
|
||||||
|
"cols": 120,
|
||||||
|
"rows": 40
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## WebSocket Connection
|
||||||
|
|
||||||
|
### Connect to Session
|
||||||
|
```javascript
|
||||||
|
const ws = new WebSocket('ws://localhost:4020/api/sessions/:id/ws');
|
||||||
|
ws.binaryType = 'arraybuffer';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Types
|
||||||
|
|
||||||
|
#### Terminal Output (Server → Client)
|
||||||
|
Binary format with magic byte:
|
||||||
|
```
|
||||||
|
[0xBF][4-byte length][UTF-8 data]
|
||||||
|
```
|
||||||
|
|
||||||
|
#### User Input (Client → Server)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "input",
|
||||||
|
"data": "ls -la\n"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Terminal Resize (Client → Server)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "resize",
|
||||||
|
"cols": 120,
|
||||||
|
"rows": 40
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Keep-Alive Ping
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"type": "ping"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Health Check
|
||||||
|
|
||||||
|
### Server Status
|
||||||
|
```http
|
||||||
|
GET /api/health
|
||||||
|
```
|
||||||
|
|
||||||
|
**Response**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"status": "healthy",
|
||||||
|
"uptime": 3600,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"sessions": 5
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Error Responses
|
||||||
|
|
||||||
|
| Status | Error | Description |
|
||||||
|
|--------|-------|-------------|
|
||||||
|
| 400 | Bad Request | Invalid parameters |
|
||||||
|
| 401 | Unauthorized | Missing/invalid token |
|
||||||
|
| 404 | Not Found | Session not found |
|
||||||
|
| 409 | Conflict | Session already exists |
|
||||||
|
| 500 | Server Error | Internal error |
|
||||||
|
|
||||||
|
**Error Format**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"error": "Error message",
|
||||||
|
"code": "ERROR_CODE",
|
||||||
|
"details": {}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Rate Limiting
|
||||||
|
|
||||||
|
- **Session Creation**: 10 per minute
|
||||||
|
- **API Calls**: 100 per minute
|
||||||
|
- **WebSocket Messages**: Unlimited
|
||||||
|
|
||||||
|
## Binary Buffer Protocol
|
||||||
|
|
||||||
|
### Packet Structure
|
||||||
|
```
|
||||||
|
┌──────────┬──────────────┬──────────────┐
|
||||||
|
│ Magic │ Length │ Data │
|
||||||
|
│ (1 byte) │ (4 bytes) │ (n bytes) │
|
||||||
|
│ 0xBF │ Big-endian │ UTF-8 │
|
||||||
|
└──────────┴──────────────┴──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Implementation
|
||||||
|
```typescript
|
||||||
|
// Encoding
|
||||||
|
function encodeBuffer(data: string): ArrayBuffer {
|
||||||
|
const encoded = new TextEncoder().encode(data);
|
||||||
|
const buffer = new ArrayBuffer(5 + encoded.length);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
view.setUint8(0, 0xBF);
|
||||||
|
view.setUint32(1, encoded.length, false);
|
||||||
|
new Uint8Array(buffer, 5).set(encoded);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decoding
|
||||||
|
function decodeBuffer(buffer: ArrayBuffer): string {
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
if (view.getUint8(0) !== 0xBF) throw new Error('Invalid magic byte');
|
||||||
|
const length = view.getUint32(1, false);
|
||||||
|
return new TextDecoder().decode(new Uint8Array(buffer, 5, length));
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session Recording
|
||||||
|
|
||||||
|
Sessions are recorded in asciinema v2 format:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"width": 80,
|
||||||
|
"height": 24,
|
||||||
|
"timestamp": 1234567890,
|
||||||
|
"env": {
|
||||||
|
"SHELL": "/bin/zsh",
|
||||||
|
"TERM": "xterm-256color"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Event format:
|
||||||
|
```json
|
||||||
|
[timestamp, "o", "output data"]
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [WebSocket Protocol Details](protocols.md)
|
||||||
|
- [Authentication Guide](../features/authentication.md)
|
||||||
|
- [Server Implementation](../platform/web.md)
|
||||||
202
docs/core/architecture.md
Normal file
202
docs/core/architecture.md
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
# Architecture Overview
|
||||||
|
|
||||||
|
## System Design
|
||||||
|
|
||||||
|
VibeTunnel consists of three main components working together:
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────────────────────────────┐
|
||||||
|
│ macOS Menu Bar Application │
|
||||||
|
│ (Swift/SwiftUI) │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ ServerManager: Lifecycle & process control │ │
|
||||||
|
│ │ SessionMonitor: Active session tracking │ │
|
||||||
|
│ │ TTYForwardManager: Terminal forwarding │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
└────────────────────┬────────────────────────────────┘
|
||||||
|
│ Spawns & Manages
|
||||||
|
┌────────────────────▼────────────────────────────────┐
|
||||||
|
│ Node.js/Bun Server Process │
|
||||||
|
│ (TypeScript) │
|
||||||
|
│ ┌──────────────────────────────────────────────┐ │
|
||||||
|
│ │ HTTP Server: REST API endpoints │ │
|
||||||
|
│ │ WebSocket Server: Real-time terminal I/O │ │
|
||||||
|
│ │ PTY Manager: Native terminal processes │ │
|
||||||
|
│ │ Session Manager: Lifecycle & state │ │
|
||||||
|
│ └──────────────────────────────────────────────┘ │
|
||||||
|
└────────────────────┬────────────────────────────────┘
|
||||||
|
│ HTTP/WebSocket
|
||||||
|
┌────────────────────▼────────────────────────────────┐
|
||||||
|
│ Client Applications │
|
||||||
|
├──────────────────────────────────────────────────────┤
|
||||||
|
│ Web Browser │ iOS App │
|
||||||
|
│ (Lit/TypeScript) │ (Swift/SwiftUI) │
|
||||||
|
└──────────────────────────────────────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
## Component Responsibilities
|
||||||
|
|
||||||
|
### macOS Application
|
||||||
|
|
||||||
|
| Component | File | Purpose |
|
||||||
|
|-----------|------|---------|
|
||||||
|
| ServerManager | `mac/VibeTunnel/ServerManager.swift` | Server lifecycle, port management |
|
||||||
|
| SessionMonitor | `mac/VibeTunnel/SessionMonitor.swift` | Track active sessions |
|
||||||
|
| TTYForwardManager | `mac/VibeTunnel/TTYForwardManager.swift` | CLI integration |
|
||||||
|
| MenuBarUI | `mac/VibeTunnel/MenuBarView.swift` | User interface |
|
||||||
|
|
||||||
|
### Server Process
|
||||||
|
|
||||||
|
| Component | File | Purpose |
|
||||||
|
|-----------|------|---------|
|
||||||
|
| HTTP Server | `web/src/server/server.ts` | REST API, WebSocket upgrade |
|
||||||
|
| PTY Manager | `web/src/server/pty/pty-manager.ts` | Terminal process spawning |
|
||||||
|
| Session Manager | `web/src/server/services/session-manager.ts` | Session state & cleanup |
|
||||||
|
| Buffer Aggregator | `web/src/server/services/buffer-aggregator.ts` | Output optimization |
|
||||||
|
|
||||||
|
### Web Frontend
|
||||||
|
|
||||||
|
| Component | File | Purpose |
|
||||||
|
|-----------|------|---------|
|
||||||
|
| App Shell | `web/src/client/app.ts` | Main application container |
|
||||||
|
| Terminal View | `web/src/client/terminal-view.ts` | xterm.js integration |
|
||||||
|
| Session List | `web/src/client/session-list.ts` | Active sessions UI |
|
||||||
|
| WebSocket Client | `web/src/client/services/websocket.ts` | Real-time communication |
|
||||||
|
|
||||||
|
## Data Flow
|
||||||
|
|
||||||
|
### Session Creation
|
||||||
|
```
|
||||||
|
User → vt command → TTYForwardManager → HTTP POST /api/sessions
|
||||||
|
→ Server creates PTY → Returns session ID → Opens browser
|
||||||
|
→ WebSocket connection established → Terminal ready
|
||||||
|
```
|
||||||
|
|
||||||
|
### Terminal I/O
|
||||||
|
```
|
||||||
|
User types → WebSocket message → Server PTY write
|
||||||
|
PTY output → Buffer aggregation → Binary protocol → WebSocket
|
||||||
|
→ Client decode → xterm.js render
|
||||||
|
```
|
||||||
|
|
||||||
|
### Session Cleanup
|
||||||
|
```
|
||||||
|
Terminal exit → PTY close → Session manager cleanup
|
||||||
|
→ WebSocket close → Client notification → UI update
|
||||||
|
```
|
||||||
|
|
||||||
|
## Communication Protocols
|
||||||
|
|
||||||
|
### HTTP REST API
|
||||||
|
- Session CRUD operations
|
||||||
|
- Authentication endpoints
|
||||||
|
- Health checks
|
||||||
|
- See [API Reference](api-reference.md)
|
||||||
|
|
||||||
|
### WebSocket Protocol
|
||||||
|
- Binary buffer format for efficiency
|
||||||
|
- Magic byte `0xBF` for packet identification
|
||||||
|
- 4-byte length header (big-endian)
|
||||||
|
- UTF-8 encoded terminal data
|
||||||
|
- See [Protocol Details](protocols.md)
|
||||||
|
|
||||||
|
### Inter-Process Communication
|
||||||
|
- Mac app spawns Bun server as child process
|
||||||
|
- Environment variables for configuration
|
||||||
|
- File-based PID tracking
|
||||||
|
- Signal handling for graceful shutdown
|
||||||
|
|
||||||
|
## Security Architecture
|
||||||
|
|
||||||
|
### Authentication Flow
|
||||||
|
```
|
||||||
|
Client → Password (optional) → Server validates
|
||||||
|
→ JWT token generated → Token in Authorization header
|
||||||
|
→ Server validates on each request
|
||||||
|
```
|
||||||
|
|
||||||
|
### Network Security
|
||||||
|
- Localhost-only by default
|
||||||
|
- Optional LAN exposure with authentication
|
||||||
|
- Tailscale/ngrok integration for remote access
|
||||||
|
- WSS/HTTPS in production
|
||||||
|
|
||||||
|
### Process Isolation
|
||||||
|
- Each session runs in separate PTY process
|
||||||
|
- User permissions inherited from server
|
||||||
|
- No privilege escalation
|
||||||
|
- Resource limits per session
|
||||||
|
|
||||||
|
## Performance Optimizations
|
||||||
|
|
||||||
|
### Buffer Aggregation
|
||||||
|
- Batch terminal output every 16ms
|
||||||
|
- Reduce WebSocket message frequency
|
||||||
|
- Binary protocol reduces payload size
|
||||||
|
|
||||||
|
### Connection Management
|
||||||
|
- WebSocket connection pooling
|
||||||
|
- Automatic reconnection with backoff
|
||||||
|
- Ping/pong for keep-alive
|
||||||
|
|
||||||
|
### Resource Management
|
||||||
|
- Lazy loading of terminal sessions
|
||||||
|
- Automatic cleanup of idle sessions
|
||||||
|
- Memory-mapped session recordings
|
||||||
|
|
||||||
|
## Platform Integration
|
||||||
|
|
||||||
|
### macOS Features
|
||||||
|
- Menu bar application
|
||||||
|
- Sparkle auto-updates
|
||||||
|
- Code signing & notarization
|
||||||
|
- Launch at login
|
||||||
|
|
||||||
|
### iOS Features
|
||||||
|
- Native Swift UI
|
||||||
|
- Background session support
|
||||||
|
- Push notifications
|
||||||
|
- Handoff support
|
||||||
|
|
||||||
|
### Web Standards
|
||||||
|
- Progressive Web App capable
|
||||||
|
- Service Worker for offline
|
||||||
|
- WebAssembly for performance
|
||||||
|
- Responsive design
|
||||||
|
|
||||||
|
## Build & Deployment
|
||||||
|
|
||||||
|
### Build Pipeline
|
||||||
|
```
|
||||||
|
1. TypeScript compilation → JavaScript bundle
|
||||||
|
2. Bun standalone executable generation
|
||||||
|
3. Swift compilation → macOS app
|
||||||
|
4. Embed server in app bundle
|
||||||
|
5. Code sign & notarize
|
||||||
|
6. DMG creation with Sparkle
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
- Runtime: Environment variables
|
||||||
|
- Build-time: xcconfig files
|
||||||
|
- User preferences: macOS defaults system
|
||||||
|
- Server config: JSON files
|
||||||
|
|
||||||
|
## Monitoring & Debugging
|
||||||
|
|
||||||
|
### Logging
|
||||||
|
- Unified logging to macOS Console
|
||||||
|
- Structured JSON logs from server
|
||||||
|
- Session-specific log filtering
|
||||||
|
- See `./scripts/vtlog.sh`
|
||||||
|
|
||||||
|
### Metrics
|
||||||
|
- Session count & duration
|
||||||
|
- Message throughput
|
||||||
|
- Error rates
|
||||||
|
- Resource usage
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [Development Guide](../guides/development.md)
|
||||||
|
- [API Reference](api-reference.md)
|
||||||
|
- [Security Model](../features/authentication.md)
|
||||||
245
docs/core/protocols.md
Normal file
245
docs/core/protocols.md
Normal file
|
|
@ -0,0 +1,245 @@
|
||||||
|
# Protocol Specifications
|
||||||
|
|
||||||
|
## WebSocket Protocol
|
||||||
|
|
||||||
|
### Connection Establishment
|
||||||
|
```javascript
|
||||||
|
// Client connection
|
||||||
|
const ws = new WebSocket('ws://localhost:4020/api/sessions/:id/ws');
|
||||||
|
ws.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
|
// Authentication via query param or header
|
||||||
|
const ws = new WebSocket('ws://localhost:4020/api/sessions/:id/ws?token=JWT_TOKEN');
|
||||||
|
```
|
||||||
|
|
||||||
|
### Message Types
|
||||||
|
|
||||||
|
#### Binary Terminal Data (Server → Client)
|
||||||
|
```
|
||||||
|
┌──────────┬──────────────┬──────────────┐
|
||||||
|
│ Magic │ Length │ Data │
|
||||||
|
│ 0xBF │ 4 bytes BE │ UTF-8 bytes │
|
||||||
|
└──────────┴──────────────┴──────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
**Encoding Example**:
|
||||||
|
```typescript
|
||||||
|
function encode(text: string): ArrayBuffer {
|
||||||
|
const data = new TextEncoder().encode(text);
|
||||||
|
const buffer = new ArrayBuffer(5 + data.length);
|
||||||
|
const view = new DataView(buffer);
|
||||||
|
view.setUint8(0, 0xBF); // Magic byte
|
||||||
|
view.setUint32(1, data.length, false); // Length (big-endian)
|
||||||
|
new Uint8Array(buffer, 5).set(data); // UTF-8 data
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Text Messages (Client → Server)
|
||||||
|
```typescript
|
||||||
|
// User input
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'input',
|
||||||
|
data: 'ls -la\n'
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Terminal resize
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'resize',
|
||||||
|
cols: 120,
|
||||||
|
rows: 40
|
||||||
|
}));
|
||||||
|
|
||||||
|
// Keep-alive ping
|
||||||
|
ws.send(JSON.stringify({
|
||||||
|
type: 'ping'
|
||||||
|
}));
|
||||||
|
```
|
||||||
|
|
||||||
|
### Connection Lifecycle
|
||||||
|
|
||||||
|
1. **Open**: Client connects with session ID
|
||||||
|
2. **Authenticate**: Token validation
|
||||||
|
3. **Initialize**: Terminal size negotiation
|
||||||
|
4. **Stream**: Bidirectional data flow
|
||||||
|
5. **Close**: Clean disconnection or timeout
|
||||||
|
|
||||||
|
### Error Codes
|
||||||
|
|
||||||
|
| Code | Meaning | Action |
|
||||||
|
|------|---------|--------|
|
||||||
|
| 1000 | Normal closure | Session ended |
|
||||||
|
| 1001 | Going away | Server shutdown |
|
||||||
|
| 1003 | Unsupported data | Protocol error |
|
||||||
|
| 1008 | Policy violation | Auth failed |
|
||||||
|
| 1011 | Server error | Retry connection |
|
||||||
|
|
||||||
|
## PTY Protocol
|
||||||
|
|
||||||
|
### Process Spawning
|
||||||
|
```typescript
|
||||||
|
interface PTYOptions {
|
||||||
|
name: string;
|
||||||
|
cols: number;
|
||||||
|
rows: number;
|
||||||
|
cwd: string;
|
||||||
|
env: Record<string, string>;
|
||||||
|
command: string;
|
||||||
|
args: string[];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Control Sequences
|
||||||
|
|
||||||
|
| Sequence | Purpose | Example |
|
||||||
|
|----------|---------|---------|
|
||||||
|
| `\x03` | SIGINT (Ctrl+C) | Interrupt process |
|
||||||
|
| `\x04` | EOF (Ctrl+D) | End input |
|
||||||
|
| `\x1a` | SIGTSTP (Ctrl+Z) | Suspend process |
|
||||||
|
| `\x1c` | SIGQUIT (Ctrl+\) | Quit process |
|
||||||
|
| `\x7f` | Backspace | Delete character |
|
||||||
|
|
||||||
|
### Terminal Modes
|
||||||
|
```typescript
|
||||||
|
// Raw mode for full control
|
||||||
|
pty.setRawMode(true);
|
||||||
|
|
||||||
|
// Canonical mode for line editing
|
||||||
|
pty.setRawMode(false);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session Recording Protocol
|
||||||
|
|
||||||
|
### Asciinema v2 Format
|
||||||
|
|
||||||
|
**Header**:
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"version": 2,
|
||||||
|
"width": 80,
|
||||||
|
"height": 24,
|
||||||
|
"timestamp": 1704067200,
|
||||||
|
"env": {
|
||||||
|
"SHELL": "/bin/zsh",
|
||||||
|
"TERM": "xterm-256color"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Events**:
|
||||||
|
```json
|
||||||
|
[0.123456, "o", "$ ls -la\r\n"]
|
||||||
|
[0.234567, "o", "total 48\r\n"]
|
||||||
|
[1.345678, "i", "c"]
|
||||||
|
[1.456789, "i", "l"]
|
||||||
|
[1.567890, "i", "e"]
|
||||||
|
```
|
||||||
|
|
||||||
|
Event types:
|
||||||
|
- `o`: Output from terminal
|
||||||
|
- `i`: Input from user
|
||||||
|
- `r`: Terminal resize
|
||||||
|
|
||||||
|
### Recording Storage
|
||||||
|
```
|
||||||
|
~/.vibetunnel/recordings/
|
||||||
|
├── session-uuid-1.cast
|
||||||
|
├── session-uuid-2.cast
|
||||||
|
└── metadata.json
|
||||||
|
```
|
||||||
|
|
||||||
|
## HTTP Protocol
|
||||||
|
|
||||||
|
### Request Headers
|
||||||
|
```http
|
||||||
|
Authorization: Bearer <JWT_TOKEN>
|
||||||
|
Content-Type: application/json
|
||||||
|
X-Session-ID: <SESSION_UUID>
|
||||||
|
X-Client-Version: 1.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
### Response Headers
|
||||||
|
```http
|
||||||
|
X-Request-ID: <REQUEST_UUID>
|
||||||
|
X-RateLimit-Limit: 100
|
||||||
|
X-RateLimit-Remaining: 95
|
||||||
|
X-RateLimit-Reset: 1704067200
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status Codes
|
||||||
|
|
||||||
|
| Code | Meaning | Usage |
|
||||||
|
|------|---------|-------|
|
||||||
|
| 200 | OK | Successful operation |
|
||||||
|
| 201 | Created | Session created |
|
||||||
|
| 204 | No Content | Session deleted |
|
||||||
|
| 400 | Bad Request | Invalid parameters |
|
||||||
|
| 401 | Unauthorized | Auth required |
|
||||||
|
| 404 | Not Found | Session not found |
|
||||||
|
| 409 | Conflict | Session exists |
|
||||||
|
| 429 | Too Many Requests | Rate limited |
|
||||||
|
| 500 | Server Error | Internal error |
|
||||||
|
|
||||||
|
## Binary Buffer Optimization
|
||||||
|
|
||||||
|
### Aggregation Strategy
|
||||||
|
```typescript
|
||||||
|
class BufferAggregator {
|
||||||
|
private buffer: Uint8Array[] = [];
|
||||||
|
private timer: NodeJS.Timeout;
|
||||||
|
|
||||||
|
aggregate(data: Uint8Array) {
|
||||||
|
this.buffer.push(data);
|
||||||
|
this.scheduleFlush();
|
||||||
|
}
|
||||||
|
|
||||||
|
private scheduleFlush() {
|
||||||
|
clearTimeout(this.timer);
|
||||||
|
this.timer = setTimeout(() => this.flush(), 16); // ~60fps
|
||||||
|
}
|
||||||
|
|
||||||
|
private flush() {
|
||||||
|
const combined = Buffer.concat(this.buffer);
|
||||||
|
this.send(combined);
|
||||||
|
this.buffer = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Performance Metrics
|
||||||
|
- **Latency**: <10ms average
|
||||||
|
- **Throughput**: >10MB/s
|
||||||
|
- **Message rate**: 60/s max
|
||||||
|
- **Buffer size**: 64KB max
|
||||||
|
|
||||||
|
## Authentication Protocol
|
||||||
|
|
||||||
|
### JWT Token Structure
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"alg": "HS256",
|
||||||
|
"typ": "JWT"
|
||||||
|
},
|
||||||
|
"payload": {
|
||||||
|
"sub": "user-id",
|
||||||
|
"iat": 1704067200,
|
||||||
|
"exp": 1704153600,
|
||||||
|
"sessionId": "session-uuid"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Refresh Flow
|
||||||
|
```
|
||||||
|
1. Client token expires in 5 minutes
|
||||||
|
2. Client requests refresh: POST /api/auth/refresh
|
||||||
|
3. Server validates refresh token
|
||||||
|
4. Server issues new access token
|
||||||
|
5. Client updates Authorization header
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [API Reference](api-reference.md)
|
||||||
|
- [Security Guide](../features/authentication.md)
|
||||||
|
- [WebSocket Implementation](../platform/web.md#websocket)
|
||||||
273
docs/features/authentication.md
Normal file
273
docs/features/authentication.md
Normal file
|
|
@ -0,0 +1,273 @@
|
||||||
|
# Authentication & Security
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
VibeTunnel supports multiple authentication modes:
|
||||||
|
- **None** (localhost only)
|
||||||
|
- **Password** (simple shared secret)
|
||||||
|
- **Token** (JWT-based)
|
||||||
|
- **External** (Tailscale, ngrok)
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Security Settings
|
||||||
|
|
||||||
|
| Setting | Default | Options |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| Authentication | None | None, Password, Token |
|
||||||
|
| Network | Localhost | Localhost, LAN, Public |
|
||||||
|
| Password | - | User-defined |
|
||||||
|
| Token Expiry | 24h | 1h-7d |
|
||||||
|
|
||||||
|
### Enable Authentication
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// Via Settings UI
|
||||||
|
Settings → Security → Enable Password
|
||||||
|
|
||||||
|
// Via defaults
|
||||||
|
defaults write com.steipete.VibeTunnel authEnabled -bool true
|
||||||
|
defaults write com.steipete.VibeTunnel authPassword -string "secret"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Password Authentication
|
||||||
|
|
||||||
|
### Server Configuration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/config.ts
|
||||||
|
export const config = {
|
||||||
|
auth: {
|
||||||
|
enabled: process.env.AUTH_ENABLED === 'true',
|
||||||
|
password: process.env.AUTH_PASSWORD,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Login
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// POST /api/auth/login
|
||||||
|
const response = await fetch('/api/auth/login', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ password: 'secret' })
|
||||||
|
});
|
||||||
|
|
||||||
|
const { token } = await response.json();
|
||||||
|
localStorage.setItem('auth_token', token);
|
||||||
|
```
|
||||||
|
|
||||||
|
## Token Authentication
|
||||||
|
|
||||||
|
### JWT Structure
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"header": {
|
||||||
|
"alg": "HS256",
|
||||||
|
"typ": "JWT"
|
||||||
|
},
|
||||||
|
"payload": {
|
||||||
|
"sub": "user-id",
|
||||||
|
"iat": 1704067200,
|
||||||
|
"exp": 1704153600,
|
||||||
|
"scope": ["sessions:read", "sessions:write"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Generation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/services/auth.ts
|
||||||
|
import jwt from 'jsonwebtoken';
|
||||||
|
|
||||||
|
export function generateToken(userId: string): string {
|
||||||
|
return jwt.sign(
|
||||||
|
{
|
||||||
|
sub: userId,
|
||||||
|
scope: ['sessions:read', 'sessions:write']
|
||||||
|
},
|
||||||
|
process.env.JWT_SECRET,
|
||||||
|
{ expiresIn: '24h' }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Token Validation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/middleware/auth.ts
|
||||||
|
export async function validateToken(req: Request): Promise<boolean> {
|
||||||
|
const token = req.headers.authorization?.replace('Bearer ', '');
|
||||||
|
|
||||||
|
if (!token) return false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||||
|
req.user = decoded;
|
||||||
|
return true;
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Network Security
|
||||||
|
|
||||||
|
### Localhost Only (Default)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/server.ts
|
||||||
|
const server = Bun.serve({
|
||||||
|
hostname: '127.0.0.1', // Localhost only
|
||||||
|
port: 4020,
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### LAN Access
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Enable LAN with authentication required
|
||||||
|
const server = Bun.serve({
|
||||||
|
hostname: '0.0.0.0', // All interfaces
|
||||||
|
port: 4020,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Require auth for non-localhost
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
if (req.ip !== '127.0.0.1' && !req.authenticated) {
|
||||||
|
return res.status(401).json({ error: 'Authentication required' });
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### HTTPS/WSS
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Production TLS
|
||||||
|
const server = Bun.serve({
|
||||||
|
fetch: app.fetch,
|
||||||
|
tls: {
|
||||||
|
cert: Bun.file('cert.pem'),
|
||||||
|
key: Bun.file('key.pem'),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## External Access
|
||||||
|
|
||||||
|
### Tailscale Integration
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Enable Tailscale
|
||||||
|
tailscale up
|
||||||
|
|
||||||
|
# Access via Tailscale network
|
||||||
|
http://your-machine.tailnet:4020
|
||||||
|
```
|
||||||
|
|
||||||
|
### ngrok Tunnel
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Start ngrok tunnel
|
||||||
|
ngrok http 4020
|
||||||
|
|
||||||
|
# Access via public URL
|
||||||
|
https://abc123.ngrok.io
|
||||||
|
```
|
||||||
|
|
||||||
|
## Session Security
|
||||||
|
|
||||||
|
### Isolation
|
||||||
|
|
||||||
|
Each session runs in a separate process with user permissions:
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// pty-manager.ts
|
||||||
|
const pty = spawn(shell, args, {
|
||||||
|
uid: process.getuid(), // Run as current user
|
||||||
|
gid: process.getgid(),
|
||||||
|
env: sanitizeEnv(env), // Clean environment
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### Resource Limits
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Prevent resource exhaustion
|
||||||
|
const limits = {
|
||||||
|
maxSessions: 50,
|
||||||
|
maxOutputBuffer: 10 * 1024 * 1024, // 10MB
|
||||||
|
sessionTimeout: 24 * 60 * 60 * 1000, // 24 hours
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Security Headers
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/middleware/security.ts
|
||||||
|
app.use((req, res, next) => {
|
||||||
|
res.setHeader('X-Frame-Options', 'DENY');
|
||||||
|
res.setHeader('X-Content-Type-Options', 'nosniff');
|
||||||
|
res.setHeader('X-XSS-Protection', '1; mode=block');
|
||||||
|
res.setHeader('Strict-Transport-Security', 'max-age=31536000');
|
||||||
|
res.setHeader('Content-Security-Policy', "default-src 'self'");
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Audit Logging
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// server/services/audit.ts
|
||||||
|
export function logAccess(event: AuditEvent) {
|
||||||
|
const entry = {
|
||||||
|
timestamp: new Date(),
|
||||||
|
ip: event.ip,
|
||||||
|
action: event.action,
|
||||||
|
sessionId: event.sessionId,
|
||||||
|
success: event.success,
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.appendFileSync('audit.log', JSON.stringify(entry) + '\n');
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Always use authentication** for non-localhost access
|
||||||
|
2. **Rotate tokens** regularly
|
||||||
|
3. **Use HTTPS/WSS** in production
|
||||||
|
4. **Limit session lifetime** to prevent resource exhaustion
|
||||||
|
5. **Monitor audit logs** for suspicious activity
|
||||||
|
6. **Keep dependencies updated** for security patches
|
||||||
|
|
||||||
|
## Threat Model
|
||||||
|
|
||||||
|
| Threat | Mitigation |
|
||||||
|
|--------|------------|
|
||||||
|
| Unauthorized access | Password/token auth |
|
||||||
|
| Session hijacking | JWT expiry, HTTPS |
|
||||||
|
| Resource exhaustion | Rate limiting, quotas |
|
||||||
|
| Code injection | Input sanitization |
|
||||||
|
| Network sniffing | TLS encryption |
|
||||||
|
|
||||||
|
## Compliance
|
||||||
|
|
||||||
|
### Data Protection
|
||||||
|
- No persistent storage of terminal content
|
||||||
|
- Sessions cleared on exit
|
||||||
|
- Optional recording with user consent
|
||||||
|
|
||||||
|
### Access Control
|
||||||
|
- Authentication required for remote access
|
||||||
|
- Session isolation per user
|
||||||
|
- No privilege escalation
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [API Reference](../core/api-reference.md#authentication)
|
||||||
|
- [Network Setup](../guides/quickstart.md#remote-development)
|
||||||
|
- [Security Headers](../platform/web.md#security)
|
||||||
285
docs/guides/development.md
Normal file
285
docs/guides/development.md
Normal file
|
|
@ -0,0 +1,285 @@
|
||||||
|
# Development Guide
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- macOS 14.0+
|
||||||
|
- Xcode 16.0+
|
||||||
|
- Node.js 18+
|
||||||
|
- Bun 1.0+
|
||||||
|
|
||||||
|
### Clone & Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone repository
|
||||||
|
git clone https://github.com/steipete/vibetunnel
|
||||||
|
cd vibetunnel
|
||||||
|
|
||||||
|
# Install dependencies
|
||||||
|
cd web && pnpm install && cd ..
|
||||||
|
|
||||||
|
# Build everything
|
||||||
|
./scripts/build-all.sh
|
||||||
|
|
||||||
|
# Or build individually
|
||||||
|
cd mac && ./scripts/build.sh
|
||||||
|
cd ios && xcodebuild
|
||||||
|
cd web && pnpm build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
vibetunnel/
|
||||||
|
├── mac/ # macOS app
|
||||||
|
│ ├── VibeTunnel/ # Swift sources
|
||||||
|
│ │ ├── Core/ # Business logic
|
||||||
|
│ │ └── Presentation/ # UI layer
|
||||||
|
│ └── scripts/ # Build scripts
|
||||||
|
├── ios/ # iOS app
|
||||||
|
│ └── VibeTunnel/ # Swift sources
|
||||||
|
└── web/ # Server & frontend
|
||||||
|
├── src/
|
||||||
|
│ ├── server/ # Node.js server
|
||||||
|
│ └── client/ # Web UI
|
||||||
|
└── scripts/ # Utilities
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Patterns
|
||||||
|
|
||||||
|
### Swift (macOS/iOS)
|
||||||
|
|
||||||
|
**Observable Pattern**
|
||||||
|
```swift
|
||||||
|
// mac/VibeTunnel/Core/Services/ServerManager.swift
|
||||||
|
@MainActor
|
||||||
|
@Observable
|
||||||
|
class ServerManager {
|
||||||
|
private(set) var isRunning = false
|
||||||
|
private(set) var error: Error?
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Protocol-Oriented Design**
|
||||||
|
```swift
|
||||||
|
// mac/VibeTunnel/Core/Protocols/VibeTunnelServer.swift
|
||||||
|
@MainActor
|
||||||
|
protocol VibeTunnelServer: AnyObject {
|
||||||
|
var isRunning: Bool { get }
|
||||||
|
func start() async throws
|
||||||
|
func stop() async
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Error Handling**
|
||||||
|
```swift
|
||||||
|
enum ServerError: LocalizedError {
|
||||||
|
case portInUse(Int)
|
||||||
|
case binaryNotFound(String)
|
||||||
|
|
||||||
|
var errorDescription: String? {
|
||||||
|
switch self {
|
||||||
|
case .portInUse(let port):
|
||||||
|
return "Port \(port) is already in use"
|
||||||
|
case .binaryNotFound(let path):
|
||||||
|
return "Server binary not found at \(path)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### TypeScript (Web)
|
||||||
|
|
||||||
|
**Service Classes**
|
||||||
|
```typescript
|
||||||
|
// web/src/server/services/terminal-manager.ts
|
||||||
|
export class TerminalManager {
|
||||||
|
private sessions = new Map<string, Session>();
|
||||||
|
|
||||||
|
async createSession(options: SessionOptions): Promise<Session> {
|
||||||
|
const session = new Session(options);
|
||||||
|
this.sessions.set(session.id, session);
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Lit Components**
|
||||||
|
```typescript
|
||||||
|
// web/src/client/components/terminal-view.ts
|
||||||
|
@customElement('terminal-view')
|
||||||
|
export class TerminalView extends LitElement {
|
||||||
|
@property({ type: String }) sessionId = '';
|
||||||
|
@state() private connected = false;
|
||||||
|
|
||||||
|
createRenderRoot() {
|
||||||
|
return this; // No shadow DOM for Tailwind
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development Workflow
|
||||||
|
|
||||||
|
### Hot Reload Setup
|
||||||
|
|
||||||
|
**Web Development**
|
||||||
|
```bash
|
||||||
|
# Terminal 1: Run dev server
|
||||||
|
cd web && pnpm dev
|
||||||
|
|
||||||
|
# Terminal 2: Enable in Mac app
|
||||||
|
# Settings → Debug → Use Development Server
|
||||||
|
```
|
||||||
|
|
||||||
|
**Swift Development with Poltergeist**
|
||||||
|
```bash
|
||||||
|
# Install Poltergeist if available
|
||||||
|
poltergeist
|
||||||
|
|
||||||
|
# Auto-rebuilds on file changes
|
||||||
|
# Check menu bar for build status
|
||||||
|
```
|
||||||
|
|
||||||
|
### Testing
|
||||||
|
|
||||||
|
**Unit Tests**
|
||||||
|
```bash
|
||||||
|
# macOS
|
||||||
|
cd mac && xcodebuild test
|
||||||
|
|
||||||
|
# iOS
|
||||||
|
cd ios && ./scripts/test-with-coverage.sh
|
||||||
|
|
||||||
|
# Web
|
||||||
|
cd web && pnpm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**E2E Tests**
|
||||||
|
```bash
|
||||||
|
cd web && pnpm test:e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debugging
|
||||||
|
|
||||||
|
**View Logs**
|
||||||
|
```bash
|
||||||
|
./scripts/vtlog.sh -n 100 # Last 100 lines
|
||||||
|
./scripts/vtlog.sh -e # Errors only
|
||||||
|
./scripts/vtlog.sh -c Server # Component filter
|
||||||
|
```
|
||||||
|
|
||||||
|
**Debug Server**
|
||||||
|
```bash
|
||||||
|
# Run server directly
|
||||||
|
cd web && pnpm dev:server
|
||||||
|
|
||||||
|
# With inspector
|
||||||
|
node --inspect dist/server/server.js
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Tasks
|
||||||
|
|
||||||
|
### Add New API Endpoint
|
||||||
|
|
||||||
|
1. Define in `web/src/server/routes/api.ts`
|
||||||
|
2. Add types in `web/src/shared/types.ts`
|
||||||
|
3. Update client in `web/src/client/services/api.ts`
|
||||||
|
4. Add tests in `web/tests/api.test.ts`
|
||||||
|
|
||||||
|
### Add New Menu Item
|
||||||
|
|
||||||
|
1. Update `mac/VibeTunnel/Presentation/MenuBarView.swift`
|
||||||
|
2. Add action in `mac/VibeTunnel/Core/Actions/`
|
||||||
|
3. Update settings if needed
|
||||||
|
|
||||||
|
### Modify Terminal Protocol
|
||||||
|
|
||||||
|
1. Update `web/src/server/services/buffer-aggregator.ts`
|
||||||
|
2. Modify `web/src/client/services/websocket.ts`
|
||||||
|
3. Test with `web/tests/protocol.test.ts`
|
||||||
|
|
||||||
|
## Build System
|
||||||
|
|
||||||
|
### macOS Build
|
||||||
|
```bash
|
||||||
|
cd mac
|
||||||
|
./scripts/build.sh # Release build
|
||||||
|
./scripts/build.sh --configuration Debug
|
||||||
|
./scripts/build.sh --sign # With signing
|
||||||
|
```
|
||||||
|
|
||||||
|
### Web Build
|
||||||
|
```bash
|
||||||
|
cd web
|
||||||
|
pnpm build # Production build
|
||||||
|
pnpm build:server # Server only
|
||||||
|
pnpm build:client # Client only
|
||||||
|
```
|
||||||
|
|
||||||
|
### Release Build
|
||||||
|
```bash
|
||||||
|
./scripts/release.sh 1.0.0 # Full release
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Quality
|
||||||
|
|
||||||
|
### Linting
|
||||||
|
```bash
|
||||||
|
# Swift
|
||||||
|
cd mac && ./scripts/lint.sh
|
||||||
|
|
||||||
|
# TypeScript
|
||||||
|
cd web && pnpm lint
|
||||||
|
cd web && pnpm check:fix
|
||||||
|
```
|
||||||
|
|
||||||
|
### Formatting
|
||||||
|
```bash
|
||||||
|
# Swift (SwiftFormat)
|
||||||
|
swiftformat mac/ ios/
|
||||||
|
|
||||||
|
# TypeScript (Prettier)
|
||||||
|
cd web && pnpm format
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
### Profiling
|
||||||
|
```bash
|
||||||
|
# Server performance
|
||||||
|
node --prof dist/server/server.js
|
||||||
|
node --prof-process isolate-*.log
|
||||||
|
|
||||||
|
# Client performance
|
||||||
|
# Use Chrome DevTools Performance tab
|
||||||
|
```
|
||||||
|
|
||||||
|
### Optimization Tips
|
||||||
|
- Use binary protocol for terminal data
|
||||||
|
- Batch WebSocket messages (16ms intervals)
|
||||||
|
- Lazy load terminal sessions
|
||||||
|
- Cache static assets with service worker
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| Port in use | `lsof -i :4020` then kill process |
|
||||||
|
| Build fails | Clean: `rm -rf node_modules dist` |
|
||||||
|
| Tests fail | Check Node/Bun version |
|
||||||
|
| Hot reload broken | Restart dev server |
|
||||||
|
|
||||||
|
## Contributing
|
||||||
|
|
||||||
|
1. Fork repository
|
||||||
|
2. Create feature branch
|
||||||
|
3. Follow code style
|
||||||
|
4. Add tests
|
||||||
|
5. Update documentation
|
||||||
|
6. Submit PR
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [Architecture](../core/architecture.md)
|
||||||
|
- [API Reference](../core/api-reference.md)
|
||||||
|
- [Testing Guide](testing.md)
|
||||||
|
- [Release Process](../reference/release-process.md)
|
||||||
169
docs/guides/quickstart.md
Normal file
169
docs/guides/quickstart.md
Normal file
|
|
@ -0,0 +1,169 @@
|
||||||
|
# Quickstart Guide
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
### Download & Install
|
||||||
|
1. Download VibeTunnel.dmg from [Releases](https://github.com/steipete/vibetunnel/releases)
|
||||||
|
2. Open DMG and drag VibeTunnel to Applications
|
||||||
|
3. Launch VibeTunnel from Applications
|
||||||
|
4. Grant accessibility permissions when prompted
|
||||||
|
|
||||||
|
### First Terminal
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Open a terminal session in your browser
|
||||||
|
vt
|
||||||
|
|
||||||
|
# Named session
|
||||||
|
vt --name "Project Build"
|
||||||
|
|
||||||
|
# Custom command
|
||||||
|
vt --command "htop"
|
||||||
|
```
|
||||||
|
|
||||||
|
The browser opens automatically at `http://localhost:4020`
|
||||||
|
|
||||||
|
## Essential Commands
|
||||||
|
|
||||||
|
| Command | Purpose |
|
||||||
|
|---------|---------|
|
||||||
|
| `vt` | Start new terminal session |
|
||||||
|
| `vt list` | Show active sessions |
|
||||||
|
| `vt kill <id>` | Terminate session |
|
||||||
|
| `vt logs` | View server logs |
|
||||||
|
| `vt --help` | Show all options |
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
### Settings Location
|
||||||
|
```
|
||||||
|
~/Library/Preferences/com.steipete.VibeTunnel.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
### Key Settings
|
||||||
|
|
||||||
|
| Setting | Default | Options |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| Port | 4020 | Any available port |
|
||||||
|
| Authentication | None | Password, Token |
|
||||||
|
| Network | Localhost | LAN, Tailscale |
|
||||||
|
| Auto-start | Disabled | Enable at login |
|
||||||
|
|
||||||
|
### Enable LAN Access
|
||||||
|
1. Click VibeTunnel menu bar icon
|
||||||
|
2. Select Preferences
|
||||||
|
3. Toggle "Allow LAN Connections"
|
||||||
|
4. Set password for security
|
||||||
|
|
||||||
|
## Development Mode
|
||||||
|
|
||||||
|
### Using Development Server
|
||||||
|
```bash
|
||||||
|
# Enable in VibeTunnel settings
|
||||||
|
Settings → Debug → Use Development Server
|
||||||
|
|
||||||
|
# Or run manually
|
||||||
|
cd web
|
||||||
|
pnpm install
|
||||||
|
pnpm dev
|
||||||
|
```
|
||||||
|
|
||||||
|
Benefits:
|
||||||
|
- Hot reload for web changes
|
||||||
|
- No Mac app rebuild needed
|
||||||
|
- Faster iteration
|
||||||
|
|
||||||
|
## Common Workflows
|
||||||
|
|
||||||
|
### Monitor AI Agents
|
||||||
|
```bash
|
||||||
|
# Start Claude Code in VibeTunnel
|
||||||
|
vt --name "Claude Code"
|
||||||
|
claude
|
||||||
|
|
||||||
|
# Access from another device
|
||||||
|
http://your-mac-ip:4020
|
||||||
|
```
|
||||||
|
|
||||||
|
### Remote Development
|
||||||
|
```bash
|
||||||
|
# With Tailscale
|
||||||
|
vt --tailscale
|
||||||
|
|
||||||
|
# With ngrok
|
||||||
|
vt --ngrok
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Sessions
|
||||||
|
```bash
|
||||||
|
# Start multiple named sessions
|
||||||
|
vt --name "Frontend" --command "cd ~/frontend && npm run dev"
|
||||||
|
vt --name "Backend" --command "cd ~/backend && npm start"
|
||||||
|
vt --name "Database" --command "docker-compose up"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Keyboard Shortcuts
|
||||||
|
|
||||||
|
### Terminal
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
|----------|--------|
|
||||||
|
| `Cmd+C` | Copy selection |
|
||||||
|
| `Cmd+V` | Paste |
|
||||||
|
| `Cmd+K` | Clear terminal |
|
||||||
|
| `Cmd+T` | New session |
|
||||||
|
| `Cmd+W` | Close session |
|
||||||
|
|
||||||
|
### Web Interface
|
||||||
|
|
||||||
|
| Shortcut | Action |
|
||||||
|
|----------|--------|
|
||||||
|
| `Ctrl+Shift+C` | Copy |
|
||||||
|
| `Ctrl+Shift+V` | Paste |
|
||||||
|
| `Alt+1-9` | Switch tabs |
|
||||||
|
| `Ctrl+Alt+T` | New terminal |
|
||||||
|
|
||||||
|
## Troubleshooting Quick Fixes
|
||||||
|
|
||||||
|
### Server Won't Start
|
||||||
|
```bash
|
||||||
|
# Check if port is in use
|
||||||
|
lsof -i :4020
|
||||||
|
|
||||||
|
# Kill existing process
|
||||||
|
killall node
|
||||||
|
|
||||||
|
# Restart VibeTunnel
|
||||||
|
osascript -e 'quit app "VibeTunnel"'
|
||||||
|
open -a VibeTunnel
|
||||||
|
```
|
||||||
|
|
||||||
|
### Can't Connect
|
||||||
|
```bash
|
||||||
|
# Check server status
|
||||||
|
curl http://localhost:4020/api/health
|
||||||
|
|
||||||
|
# View logs
|
||||||
|
./scripts/vtlog.sh -e
|
||||||
|
```
|
||||||
|
|
||||||
|
### Permission Issues
|
||||||
|
1. System Preferences → Security & Privacy
|
||||||
|
2. Privacy → Accessibility
|
||||||
|
3. Add VibeTunnel.app
|
||||||
|
4. Restart VibeTunnel
|
||||||
|
|
||||||
|
## Next Steps
|
||||||
|
|
||||||
|
- [Development Setup](development.md) - Build from source
|
||||||
|
- [API Reference](../core/api-reference.md) - Integrate with VibeTunnel
|
||||||
|
- [iOS App Setup](../platform/ios.md) - Mobile access
|
||||||
|
- [Security Guide](../features/authentication.md) - Secure your sessions
|
||||||
|
|
||||||
|
## Quick Tips
|
||||||
|
|
||||||
|
1. **Auto-start**: Enable "Launch at Login" in preferences
|
||||||
|
2. **Custom port**: Set `VT_PORT=8080` environment variable
|
||||||
|
3. **Debug mode**: Hold Option while clicking menu bar icon
|
||||||
|
4. **Force quit session**: `vt kill --force <id>`
|
||||||
|
5. **Export recordings**: Sessions saved in `~/.vibetunnel/recordings/`
|
||||||
387
docs/guides/testing.md
Normal file
387
docs/guides/testing.md
Normal file
|
|
@ -0,0 +1,387 @@
|
||||||
|
# Testing Guide
|
||||||
|
|
||||||
|
## Quick Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run all tests
|
||||||
|
./scripts/test-all.sh
|
||||||
|
|
||||||
|
# Platform-specific
|
||||||
|
cd mac && xcodebuild test
|
||||||
|
cd ios && ./scripts/test-with-coverage.sh
|
||||||
|
cd web && pnpm test
|
||||||
|
|
||||||
|
# With coverage
|
||||||
|
cd web && pnpm test:coverage
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
tests/
|
||||||
|
├── unit/ # Unit tests
|
||||||
|
├── integration/ # Integration tests
|
||||||
|
├── e2e/ # End-to-end tests
|
||||||
|
└── fixtures/ # Test data
|
||||||
|
```
|
||||||
|
|
||||||
|
## Unit Testing
|
||||||
|
|
||||||
|
### Swift (XCTest)
|
||||||
|
|
||||||
|
```swift
|
||||||
|
// mac/VibeTunnelTests/ServerManagerTests.swift
|
||||||
|
import XCTest
|
||||||
|
@testable import VibeTunnel
|
||||||
|
|
||||||
|
class ServerManagerTests: XCTestCase {
|
||||||
|
func testServerStart() async throws {
|
||||||
|
let manager = ServerManager()
|
||||||
|
|
||||||
|
try await manager.start()
|
||||||
|
|
||||||
|
XCTAssertTrue(manager.isRunning)
|
||||||
|
XCTAssertEqual(manager.port, "4020")
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPortValidation() {
|
||||||
|
XCTAssertThrowsError(try validatePort("abc"))
|
||||||
|
XCTAssertNoThrow(try validatePort("8080"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### TypeScript (Vitest)
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// web/tests/session-manager.test.ts
|
||||||
|
import { describe, it, expect, beforeEach } from 'vitest';
|
||||||
|
import { SessionManager } from '../src/server/services/session-manager';
|
||||||
|
|
||||||
|
describe('SessionManager', () => {
|
||||||
|
let manager: SessionManager;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
manager = new SessionManager();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('creates session', async () => {
|
||||||
|
const session = await manager.create({
|
||||||
|
shell: '/bin/bash',
|
||||||
|
cols: 80,
|
||||||
|
rows: 24
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(session.id).toBeDefined();
|
||||||
|
expect(session.status).toBe('running');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Integration Testing
|
||||||
|
|
||||||
|
### API Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// web/tests/integration/api.test.ts
|
||||||
|
import request from 'supertest';
|
||||||
|
import { app } from '../../src/server/app';
|
||||||
|
|
||||||
|
describe('API Integration', () => {
|
||||||
|
it('creates session via API', async () => {
|
||||||
|
const response = await request(app)
|
||||||
|
.post('/api/sessions')
|
||||||
|
.send({ shell: '/bin/bash' })
|
||||||
|
.expect(201);
|
||||||
|
|
||||||
|
expect(response.body).toHaveProperty('id');
|
||||||
|
expect(response.body.status).toBe('running');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// web/tests/integration/websocket.test.ts
|
||||||
|
import { WebSocket } from 'ws';
|
||||||
|
|
||||||
|
describe('WebSocket Integration', () => {
|
||||||
|
it('connects to session', async () => {
|
||||||
|
const ws = new WebSocket('ws://localhost:4020/api/sessions/test/ws');
|
||||||
|
|
||||||
|
await new Promise((resolve) => {
|
||||||
|
ws.on('open', resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
ws.send(JSON.stringify({ type: 'input', data: 'echo test\n' }));
|
||||||
|
|
||||||
|
const message = await new Promise((resolve) => {
|
||||||
|
ws.on('message', resolve);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(message.toString()).toContain('test');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## E2E Testing
|
||||||
|
|
||||||
|
### Playwright Setup
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// web/playwright.config.ts
|
||||||
|
import { defineConfig } from '@playwright/test';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
testDir: './tests/e2e',
|
||||||
|
use: {
|
||||||
|
baseURL: 'http://localhost:4020',
|
||||||
|
trace: 'on-first-retry',
|
||||||
|
},
|
||||||
|
webServer: {
|
||||||
|
command: 'pnpm dev',
|
||||||
|
port: 4020,
|
||||||
|
reuseExistingServer: !process.env.CI,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### E2E Tests
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// web/tests/e2e/terminal.test.ts
|
||||||
|
import { test, expect } from '@playwright/test';
|
||||||
|
|
||||||
|
test('complete terminal workflow', async ({ page }) => {
|
||||||
|
// Navigate to app
|
||||||
|
await page.goto('/');
|
||||||
|
|
||||||
|
// Create new terminal
|
||||||
|
await page.click('button:has-text("New Terminal")');
|
||||||
|
|
||||||
|
// Wait for terminal to load
|
||||||
|
const terminal = page.locator('.terminal');
|
||||||
|
await expect(terminal).toBeVisible();
|
||||||
|
|
||||||
|
// Type command
|
||||||
|
await page.keyboard.type('echo "Hello, VibeTunnel"');
|
||||||
|
await page.keyboard.press('Enter');
|
||||||
|
|
||||||
|
// Verify output
|
||||||
|
await expect(terminal).toContainText('Hello, VibeTunnel');
|
||||||
|
|
||||||
|
// Close session
|
||||||
|
await page.click('button[aria-label="Close terminal"]');
|
||||||
|
await expect(terminal).not.toBeVisible();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance Testing
|
||||||
|
|
||||||
|
### Load Testing
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// tests/performance/load.js
|
||||||
|
import { check } from 'k6';
|
||||||
|
import ws from 'k6/ws';
|
||||||
|
|
||||||
|
export default function() {
|
||||||
|
const url = 'ws://localhost:4020/api/sessions/test/ws';
|
||||||
|
|
||||||
|
ws.connect(url, {}, function(socket) {
|
||||||
|
socket.on('open', () => {
|
||||||
|
socket.send(JSON.stringify({ type: 'input', data: 'ls\n' }));
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on('message', (data) => {
|
||||||
|
check(data, {
|
||||||
|
'received response': (d) => d.length > 0,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const options = {
|
||||||
|
vus: 100, // 100 virtual users
|
||||||
|
duration: '30s', // 30 second test
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Benchmark Suite
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// tests/performance/benchmark.ts
|
||||||
|
import { bench, describe } from 'vitest';
|
||||||
|
|
||||||
|
describe('Buffer encoding performance', () => {
|
||||||
|
bench('encode 1KB', () => {
|
||||||
|
encodeBuffer('x'.repeat(1024));
|
||||||
|
});
|
||||||
|
|
||||||
|
bench('encode 10KB', () => {
|
||||||
|
encodeBuffer('x'.repeat(10240));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Test Coverage
|
||||||
|
|
||||||
|
### Coverage Requirements
|
||||||
|
|
||||||
|
| Component | Target | Current |
|
||||||
|
|-----------|--------|---------|
|
||||||
|
| Server | 80% | 85% |
|
||||||
|
| Client | 70% | 72% |
|
||||||
|
| Mac App | 60% | 65% |
|
||||||
|
| iOS App | 75% | 78% |
|
||||||
|
|
||||||
|
### Generate Reports
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Web coverage
|
||||||
|
cd web && pnpm test:coverage
|
||||||
|
|
||||||
|
# iOS coverage
|
||||||
|
cd ios && ./scripts/test-with-coverage.sh
|
||||||
|
|
||||||
|
# View HTML report
|
||||||
|
open coverage/index.html
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing External Devices
|
||||||
|
|
||||||
|
### iPad/iPhone Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Start dev server on all interfaces
|
||||||
|
cd web && pnpm dev --host 0.0.0.0
|
||||||
|
|
||||||
|
# 2. Get Mac IP
|
||||||
|
ifconfig | grep inet
|
||||||
|
|
||||||
|
# 3. Access from device
|
||||||
|
# http://192.168.1.100:4021
|
||||||
|
```
|
||||||
|
|
||||||
|
### Cross-Browser Testing
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// playwright.config.ts
|
||||||
|
projects: [
|
||||||
|
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
|
||||||
|
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
|
||||||
|
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
|
||||||
|
{ name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
|
||||||
|
]
|
||||||
|
```
|
||||||
|
|
||||||
|
## Mocking & Fixtures
|
||||||
|
|
||||||
|
### Mock PTY
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// tests/mocks/pty.ts
|
||||||
|
export class MockPTY {
|
||||||
|
write(data: string) {
|
||||||
|
this.emit('data', `mock: ${data}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
resize(cols: number, rows: number) {
|
||||||
|
this.cols = cols;
|
||||||
|
this.rows = rows;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Test Fixtures
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// tests/fixtures/sessions.ts
|
||||||
|
export const mockSession = {
|
||||||
|
id: 'test-session-123',
|
||||||
|
name: 'Test Session',
|
||||||
|
status: 'running',
|
||||||
|
created: new Date(),
|
||||||
|
pid: 12345,
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Testing
|
||||||
|
|
||||||
|
### GitHub Actions
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/test.yml
|
||||||
|
name: Tests
|
||||||
|
on: [push, pull_request]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
runs-on: macos-14
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Setup Node
|
||||||
|
uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 20
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: |
|
||||||
|
cd web && pnpm install
|
||||||
|
|
||||||
|
- name: Run tests
|
||||||
|
run: ./scripts/test-all.sh
|
||||||
|
|
||||||
|
- name: Upload coverage
|
||||||
|
uses: codecov/codecov-action@v3
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging Tests
|
||||||
|
|
||||||
|
### Debug Swift Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run with verbose output
|
||||||
|
xcodebuild test -verbose
|
||||||
|
|
||||||
|
# Debug specific test
|
||||||
|
xcodebuild test -only-testing:VibeTunnelTests/ServerManagerTests/testServerStart
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug TypeScript Tests
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run with inspector
|
||||||
|
node --inspect-brk ./node_modules/.bin/vitest
|
||||||
|
|
||||||
|
# Run single test file
|
||||||
|
pnpm test session-manager.test.ts
|
||||||
|
|
||||||
|
# Watch mode
|
||||||
|
pnpm test --watch
|
||||||
|
```
|
||||||
|
|
||||||
|
## Best Practices
|
||||||
|
|
||||||
|
1. **Test naming**: Use descriptive names like `shouldCreateSessionWithCustomShell`
|
||||||
|
2. **Isolation**: Each test should be independent
|
||||||
|
3. **Cleanup**: Always cleanup resources (sessions, files, connections)
|
||||||
|
4. **Assertions**: Test both success and error cases
|
||||||
|
5. **Speed**: Keep unit tests under 100ms each
|
||||||
|
6. **Flakiness**: Retry flaky tests, investigate root cause
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| Tests timeout | Increase timeout, check async |
|
||||||
|
| Port conflicts | Use random ports in tests |
|
||||||
|
| Flaky WebSocket | Add connection retry logic |
|
||||||
|
| Coverage gaps | Add tests for error paths |
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [Development Guide](development.md)
|
||||||
|
- [CI/CD Setup](../reference/release-process.md#cicd-pipeline)
|
||||||
|
- [Troubleshooting](../reference/troubleshooting.md)
|
||||||
261
docs/platform/macos.md
Normal file
261
docs/platform/macos.md
Normal file
|
|
@ -0,0 +1,261 @@
|
||||||
|
# macOS Development
|
||||||
|
|
||||||
|
## Project Setup
|
||||||
|
|
||||||
|
### Requirements
|
||||||
|
- macOS 14.0+
|
||||||
|
- Xcode 16.0+
|
||||||
|
- Swift 6.0
|
||||||
|
|
||||||
|
### Build & Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd mac
|
||||||
|
|
||||||
|
# Debug build
|
||||||
|
xcodebuild -project VibeTunnel.xcodeproj -scheme VibeTunnel
|
||||||
|
|
||||||
|
# Release build
|
||||||
|
./scripts/build.sh
|
||||||
|
|
||||||
|
# With code signing
|
||||||
|
./scripts/build.sh --sign
|
||||||
|
|
||||||
|
# Run directly
|
||||||
|
open build/Release/VibeTunnel.app
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
### Core Components
|
||||||
|
|
||||||
|
| Component | Location | Purpose |
|
||||||
|
|-----------|----------|---------|
|
||||||
|
| ServerManager | `Core/Services/ServerManager.swift` | Server lifecycle |
|
||||||
|
| SessionMonitor | `Core/Services/SessionMonitor.swift` | Track sessions |
|
||||||
|
| TTYForwardManager | `Core/Services/TTYForwardManager.swift` | CLI integration |
|
||||||
|
| MenuBarViewModel | `Presentation/ViewModels/MenuBarViewModel.swift` | UI state |
|
||||||
|
|
||||||
|
### Key Patterns
|
||||||
|
|
||||||
|
**Observable State**
|
||||||
|
```swift
|
||||||
|
@MainActor
|
||||||
|
@Observable
|
||||||
|
class ServerManager {
|
||||||
|
private(set) var isRunning = false
|
||||||
|
private(set) var sessions: [Session] = []
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Protocol-Based Services**
|
||||||
|
```swift
|
||||||
|
@MainActor
|
||||||
|
protocol VibeTunnelServer: AnyObject {
|
||||||
|
var isRunning: Bool { get }
|
||||||
|
func start() async throws
|
||||||
|
func stop() async
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**SwiftUI Menu Bar**
|
||||||
|
```swift
|
||||||
|
struct MenuBarView: View {
|
||||||
|
@StateObject private var viewModel = MenuBarViewModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Menu("VT", systemImage: "terminal") {
|
||||||
|
ForEach(viewModel.sessions) { session in
|
||||||
|
SessionRow(session: session)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server Integration
|
||||||
|
|
||||||
|
### Embedded Server
|
||||||
|
```
|
||||||
|
VibeTunnel.app/
|
||||||
|
└── Contents/
|
||||||
|
├── MacOS/
|
||||||
|
│ └── VibeTunnel # Main executable
|
||||||
|
└── Resources/
|
||||||
|
└── server/
|
||||||
|
└── bun-server # Embedded Bun binary
|
||||||
|
```
|
||||||
|
|
||||||
|
### Server Launch
|
||||||
|
```swift
|
||||||
|
// ServerManager.swift
|
||||||
|
func start() async throws {
|
||||||
|
let serverPath = Bundle.main.resourcePath! + "/server/bun-server"
|
||||||
|
process = Process()
|
||||||
|
process.executableURL = URL(fileURLWithPath: serverPath)
|
||||||
|
process.arguments = ["--port", port]
|
||||||
|
try process.run()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Settings Management
|
||||||
|
|
||||||
|
### UserDefaults Keys
|
||||||
|
|
||||||
|
| Key | Type | Default | Description |
|
||||||
|
|-----|------|---------|-------------|
|
||||||
|
| serverPort | String | "4020" | Server port |
|
||||||
|
| autostart | Bool | false | Launch at login |
|
||||||
|
| allowLAN | Bool | false | LAN connections |
|
||||||
|
| useDevServer | Bool | false | Development mode |
|
||||||
|
|
||||||
|
### Settings Window
|
||||||
|
```swift
|
||||||
|
struct SettingsView: View {
|
||||||
|
@AppStorage("serverPort") private var port = "4020"
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
Form {
|
||||||
|
TextField("Port:", text: $port)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Menu Bar App
|
||||||
|
|
||||||
|
### App Lifecycle
|
||||||
|
```swift
|
||||||
|
@main
|
||||||
|
struct VibeTunnelApp: App {
|
||||||
|
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
MenuBarExtra("VibeTunnel", systemImage: "terminal") {
|
||||||
|
MenuBarView()
|
||||||
|
}
|
||||||
|
.menuBarExtraStyle(.menu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Status Updates
|
||||||
|
```swift
|
||||||
|
// Update menu bar icon based on state
|
||||||
|
func updateStatusItem() {
|
||||||
|
if serverManager.isRunning {
|
||||||
|
statusItem.button?.image = NSImage(systemSymbolName: "terminal.fill")
|
||||||
|
} else {
|
||||||
|
statusItem.button?.image = NSImage(systemSymbolName: "terminal")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Code Signing
|
||||||
|
|
||||||
|
### Entitlements
|
||||||
|
```xml
|
||||||
|
<!-- VibeTunnel.entitlements -->
|
||||||
|
<dict>
|
||||||
|
<key>com.apple.security.network.client</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.network.server</key>
|
||||||
|
<true/>
|
||||||
|
<key>com.apple.security.files.user-selected.read-write</key>
|
||||||
|
<true/>
|
||||||
|
</dict>
|
||||||
|
```
|
||||||
|
|
||||||
|
### Build Settings
|
||||||
|
```
|
||||||
|
# version.xcconfig
|
||||||
|
MARKETING_VERSION = 1.0.0
|
||||||
|
CURRENT_PROJECT_VERSION = 100
|
||||||
|
|
||||||
|
# Shared.xcconfig
|
||||||
|
CODE_SIGN_IDENTITY = Developer ID Application
|
||||||
|
DEVELOPMENT_TEAM = TEAMID
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sparkle Updates
|
||||||
|
|
||||||
|
### Integration
|
||||||
|
```swift
|
||||||
|
import Sparkle
|
||||||
|
|
||||||
|
class UpdateManager {
|
||||||
|
let updater = SPUStandardUpdaterController(
|
||||||
|
startingUpdater: true,
|
||||||
|
updaterDelegate: nil,
|
||||||
|
userDriverDelegate: nil
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkForUpdates() {
|
||||||
|
updater.checkForUpdates()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Configuration
|
||||||
|
```xml
|
||||||
|
<!-- Info.plist -->
|
||||||
|
<key>SUFeedURL</key>
|
||||||
|
<string>https://vibetunnel.com/appcast.xml</string>
|
||||||
|
<key>SUEnableAutomaticChecks</key>
|
||||||
|
<true/>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Console Logs
|
||||||
|
```swift
|
||||||
|
os_log(.debug, log: .server, "Starting server on port %{public}@", port)
|
||||||
|
```
|
||||||
|
|
||||||
|
### View Logs
|
||||||
|
```bash
|
||||||
|
# In Console.app
|
||||||
|
# Filter: subsystem:com.steipete.VibeTunnel
|
||||||
|
|
||||||
|
# Or via script
|
||||||
|
./scripts/vtlog.sh -c ServerManager
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
```bash
|
||||||
|
xcodebuild test \
|
||||||
|
-project VibeTunnel.xcodeproj \
|
||||||
|
-scheme VibeTunnel \
|
||||||
|
-destination 'platform=macOS'
|
||||||
|
```
|
||||||
|
|
||||||
|
### UI Tests
|
||||||
|
```swift
|
||||||
|
class VibeTunnelUITests: XCTestCase {
|
||||||
|
func testServerStart() throws {
|
||||||
|
let app = XCUIApplication()
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
app.menuBarItems["VibeTunnel"].click()
|
||||||
|
app.menuItems["Start Server"].click()
|
||||||
|
|
||||||
|
XCTAssertTrue(app.menuItems["Stop Server"].exists)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| Server won't start | Check port availability |
|
||||||
|
| Menu bar not showing | Check LSUIElement in Info.plist |
|
||||||
|
| Updates not working | Verify Sparkle feed URL |
|
||||||
|
| Permissions denied | Add entitlements |
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [Architecture](../core/architecture.md)
|
||||||
|
- [Development Guide](../guides/development.md)
|
||||||
|
- [iOS Companion](ios.md)
|
||||||
313
docs/platform/web.md
Normal file
313
docs/platform/web.md
Normal file
|
|
@ -0,0 +1,313 @@
|
||||||
|
# Web Development
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
- Node.js 18+
|
||||||
|
- Bun 1.0+
|
||||||
|
- pnpm 8+
|
||||||
|
|
||||||
|
### Install & Run
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd web
|
||||||
|
pnpm install
|
||||||
|
pnpm dev # Development server
|
||||||
|
pnpm build # Production build
|
||||||
|
pnpm test # Run tests
|
||||||
|
```
|
||||||
|
|
||||||
|
## Project Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
web/
|
||||||
|
├── src/
|
||||||
|
│ ├── server/ # Node.js backend
|
||||||
|
│ │ ├── server.ts # HTTP/WebSocket server
|
||||||
|
│ │ ├── pty/ # Terminal management
|
||||||
|
│ │ ├── services/ # Business logic
|
||||||
|
│ │ └── routes/ # API endpoints
|
||||||
|
│ ├── client/ # Web frontend
|
||||||
|
│ │ ├── app.ts # Main application
|
||||||
|
│ │ ├── components/ # Lit components
|
||||||
|
│ │ └── services/ # Client services
|
||||||
|
│ └── shared/ # Shared types
|
||||||
|
├── dist/ # Build output
|
||||||
|
└── tests/ # Test files
|
||||||
|
```
|
||||||
|
|
||||||
|
## Server Development
|
||||||
|
|
||||||
|
### Core Services
|
||||||
|
|
||||||
|
| Service | File | Purpose |
|
||||||
|
|---------|------|---------|
|
||||||
|
| TerminalManager | `services/terminal-manager.ts` | PTY lifecycle |
|
||||||
|
| SessionManager | `services/session-manager.ts` | Session state |
|
||||||
|
| BufferAggregator | `services/buffer-aggregator.ts` | Output batching |
|
||||||
|
| AuthService | `services/auth.ts` | Authentication |
|
||||||
|
|
||||||
|
### API Routes
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// routes/api.ts
|
||||||
|
router.post('/api/sessions', createSession);
|
||||||
|
router.get('/api/sessions', listSessions);
|
||||||
|
router.get('/api/sessions/:id', getSession);
|
||||||
|
router.delete('/api/sessions/:id', deleteSession);
|
||||||
|
router.ws('/api/sessions/:id/ws', handleWebSocket);
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Handler
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// services/websocket-handler.ts
|
||||||
|
export async function handleWebSocket(ws: WebSocket, sessionId: string) {
|
||||||
|
const session = await sessionManager.get(sessionId);
|
||||||
|
|
||||||
|
// Binary protocol for terminal data
|
||||||
|
session.onData((data: Buffer) => {
|
||||||
|
ws.send(encodeBuffer(data));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle client messages
|
||||||
|
ws.on('message', (msg: Buffer) => {
|
||||||
|
const data = JSON.parse(msg.toString());
|
||||||
|
if (data.type === 'input') {
|
||||||
|
session.write(data.data);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### PTY Management
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// pty/pty-manager.ts
|
||||||
|
import * as pty from 'node-pty';
|
||||||
|
|
||||||
|
export class PTYManager {
|
||||||
|
create(options: PTYOptions): IPty {
|
||||||
|
return pty.spawn(options.shell || '/bin/zsh', options.args, {
|
||||||
|
cols: options.cols || 80,
|
||||||
|
rows: options.rows || 24,
|
||||||
|
cwd: options.cwd || process.env.HOME,
|
||||||
|
env: { ...process.env, ...options.env }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Client Development
|
||||||
|
|
||||||
|
### Lit Components
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// components/terminal-view.ts
|
||||||
|
@customElement('terminal-view')
|
||||||
|
export class TerminalView extends LitElement {
|
||||||
|
@property({ type: String }) sessionId = '';
|
||||||
|
|
||||||
|
private terminal?: Terminal;
|
||||||
|
private ws?: WebSocket;
|
||||||
|
|
||||||
|
createRenderRoot() {
|
||||||
|
return this; // No shadow DOM for Tailwind
|
||||||
|
}
|
||||||
|
|
||||||
|
firstUpdated() {
|
||||||
|
this.initTerminal();
|
||||||
|
this.connectWebSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return html`
|
||||||
|
<div id="terminal" class="h-full w-full"></div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### WebSocket Client
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// services/websocket-client.ts
|
||||||
|
export class WebSocketClient {
|
||||||
|
private ws?: WebSocket;
|
||||||
|
|
||||||
|
connect(sessionId: string): void {
|
||||||
|
this.ws = new WebSocket(`ws://localhost:4020/api/sessions/${sessionId}/ws`);
|
||||||
|
this.ws.binaryType = 'arraybuffer';
|
||||||
|
|
||||||
|
this.ws.onmessage = (event) => {
|
||||||
|
if (event.data instanceof ArrayBuffer) {
|
||||||
|
const text = this.decodeBuffer(event.data);
|
||||||
|
this.onData?.(text);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
send(data: string): void {
|
||||||
|
this.ws?.send(JSON.stringify({ type: 'input', data }));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Terminal Integration
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// services/terminal-service.ts
|
||||||
|
import { Terminal } from '@xterm/xterm';
|
||||||
|
import { FitAddon } from '@xterm/addon-fit';
|
||||||
|
|
||||||
|
export class TerminalService {
|
||||||
|
private terminal: Terminal;
|
||||||
|
private fitAddon: FitAddon;
|
||||||
|
|
||||||
|
initialize(container: HTMLElement): void {
|
||||||
|
this.terminal = new Terminal({
|
||||||
|
theme: {
|
||||||
|
background: '#1e1e1e',
|
||||||
|
foreground: '#ffffff'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
this.fitAddon = new FitAddon();
|
||||||
|
this.terminal.loadAddon(this.fitAddon);
|
||||||
|
this.terminal.open(container);
|
||||||
|
this.fitAddon.fit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build System
|
||||||
|
|
||||||
|
### Development Build
|
||||||
|
|
||||||
|
```json
|
||||||
|
// package.json scripts
|
||||||
|
{
|
||||||
|
"dev": "concurrently \"npm:dev:*\"",
|
||||||
|
"dev:server": "tsx watch src/server/server.ts",
|
||||||
|
"dev:client": "vite",
|
||||||
|
"dev:tailwind": "tailwindcss -w"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Production Build
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build everything
|
||||||
|
pnpm build
|
||||||
|
|
||||||
|
# Outputs:
|
||||||
|
# dist/server/ - Compiled server
|
||||||
|
# dist/client/ - Static web assets
|
||||||
|
# dist/bun - Standalone executable
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bun Compilation
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// scripts/build-bun.ts
|
||||||
|
await Bun.build({
|
||||||
|
entrypoints: ['src/server/server.ts'],
|
||||||
|
outdir: 'dist',
|
||||||
|
target: 'bun',
|
||||||
|
minify: true,
|
||||||
|
sourcemap: 'external'
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
### Unit Tests
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// tests/terminal-manager.test.ts
|
||||||
|
describe('TerminalManager', () => {
|
||||||
|
it('creates session', async () => {
|
||||||
|
const manager = new TerminalManager();
|
||||||
|
const session = await manager.create({ shell: '/bin/bash' });
|
||||||
|
expect(session.id).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
### E2E Tests
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// tests/e2e/session.test.ts
|
||||||
|
test('create and connect to session', async ({ page }) => {
|
||||||
|
await page.goto('http://localhost:4020');
|
||||||
|
await page.click('button:text("New Terminal")');
|
||||||
|
await expect(page.locator('.terminal')).toBeVisible();
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Performance
|
||||||
|
|
||||||
|
### Optimization Techniques
|
||||||
|
|
||||||
|
| Technique | Implementation | Impact |
|
||||||
|
|-----------|---------------|--------|
|
||||||
|
| Buffer aggregation | Batch every 16ms | 90% fewer messages |
|
||||||
|
| Binary protocol | Magic byte encoding | 50% smaller payload |
|
||||||
|
| Virtual scrolling | xterm.js built-in | Handles 100K+ lines |
|
||||||
|
| Service worker | Cache static assets | Instant load |
|
||||||
|
|
||||||
|
### Benchmarks
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
// Measure WebSocket throughput
|
||||||
|
const start = performance.now();
|
||||||
|
let bytes = 0;
|
||||||
|
|
||||||
|
ws.onmessage = (event) => {
|
||||||
|
bytes += event.data.byteLength;
|
||||||
|
if (performance.now() - start > 1000) {
|
||||||
|
console.log(`Throughput: ${bytes / 1024}KB/s`);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Debugging
|
||||||
|
|
||||||
|
### Server Debugging
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run with inspector
|
||||||
|
node --inspect dist/server/server.js
|
||||||
|
|
||||||
|
# With source maps
|
||||||
|
NODE_OPTIONS='--enable-source-maps' node dist/server/server.js
|
||||||
|
|
||||||
|
# Verbose logging
|
||||||
|
DEBUG=vt:* pnpm dev:server
|
||||||
|
```
|
||||||
|
|
||||||
|
### Client Debugging
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// Enable xterm.js debug mode
|
||||||
|
terminal.options.logLevel = 'debug';
|
||||||
|
|
||||||
|
// WebSocket debugging
|
||||||
|
ws.addEventListener('message', (e) => {
|
||||||
|
console.log('WS received:', e.data);
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
## Common Issues
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| CORS errors | Check server CORS config |
|
||||||
|
| WebSocket fails | Verify port/firewall |
|
||||||
|
| Terminal garbled | Check encoding (UTF-8) |
|
||||||
|
| Build fails | Clear node_modules |
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [API Reference](../core/api-reference.md)
|
||||||
|
- [Protocol Specs](../core/protocols.md)
|
||||||
|
- [Development Guide](../guides/development.md)
|
||||||
220
docs/reference/release-process.md
Normal file
220
docs/reference/release-process.md
Normal file
|
|
@ -0,0 +1,220 @@
|
||||||
|
# Release Process
|
||||||
|
|
||||||
|
## Quick Checklist
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 1. Update version
|
||||||
|
./scripts/update-version.sh 1.0.0
|
||||||
|
|
||||||
|
# 2. Run tests
|
||||||
|
./scripts/test-all.sh
|
||||||
|
|
||||||
|
# 3. Build release
|
||||||
|
./scripts/release.sh 1.0.0
|
||||||
|
|
||||||
|
# 4. Create GitHub release
|
||||||
|
gh release create v1.0.0 dist/VibeTunnel-1.0.0.dmg
|
||||||
|
|
||||||
|
# 5. Update Sparkle feed
|
||||||
|
./scripts/update-sparkle.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detailed Steps
|
||||||
|
|
||||||
|
### 1. Pre-Release
|
||||||
|
|
||||||
|
**Version Update**
|
||||||
|
```bash
|
||||||
|
# Updates all version files
|
||||||
|
./scripts/update-version.sh NEW_VERSION
|
||||||
|
|
||||||
|
# Files modified:
|
||||||
|
# - mac/VibeTunnel/version.xcconfig
|
||||||
|
# - web/package.json
|
||||||
|
# - ios/VibeTunnel/Info.plist
|
||||||
|
```
|
||||||
|
|
||||||
|
**Changelog**
|
||||||
|
```markdown
|
||||||
|
## [1.0.0] - 2024-01-01
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- New feature X
|
||||||
|
- Support for Y
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Bug Z
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Improved performance
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Testing
|
||||||
|
|
||||||
|
**Run Test Suite**
|
||||||
|
```bash
|
||||||
|
# All platforms
|
||||||
|
./scripts/test-all.sh
|
||||||
|
|
||||||
|
# Individual
|
||||||
|
cd mac && xcodebuild test
|
||||||
|
cd ios && ./scripts/test-with-coverage.sh
|
||||||
|
cd web && pnpm test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Manual Testing**
|
||||||
|
- [ ] Fresh install on clean macOS
|
||||||
|
- [ ] Upgrade from previous version
|
||||||
|
- [ ] Test on minimum macOS version
|
||||||
|
- [ ] iOS app connectivity
|
||||||
|
- [ ] Web UI on Safari/Chrome/Firefox
|
||||||
|
|
||||||
|
### 3. Build
|
||||||
|
|
||||||
|
**Release Build**
|
||||||
|
```bash
|
||||||
|
# Complete release
|
||||||
|
./scripts/release.sh VERSION
|
||||||
|
|
||||||
|
# Steps performed:
|
||||||
|
# 1. Clean build directories
|
||||||
|
# 2. Build web assets
|
||||||
|
# 3. Build Mac app (signed)
|
||||||
|
# 4. Create DMG
|
||||||
|
# 5. Notarize with Apple
|
||||||
|
# 6. Generate Sparkle appcast
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verification**
|
||||||
|
```bash
|
||||||
|
# Check signature
|
||||||
|
codesign -dv --verbose=4 dist/VibeTunnel.app
|
||||||
|
|
||||||
|
# Verify notarization
|
||||||
|
spctl -a -v dist/VibeTunnel.app
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Distribution
|
||||||
|
|
||||||
|
**GitHub Release**
|
||||||
|
```bash
|
||||||
|
# Create release
|
||||||
|
gh release create v$VERSION \
|
||||||
|
--title "VibeTunnel $VERSION" \
|
||||||
|
--notes-file RELEASE_NOTES.md \
|
||||||
|
dist/VibeTunnel-$VERSION.dmg
|
||||||
|
|
||||||
|
# Upload additional assets
|
||||||
|
gh release upload v$VERSION dist/checksums.txt
|
||||||
|
```
|
||||||
|
|
||||||
|
**Sparkle Update**
|
||||||
|
```xml
|
||||||
|
<!-- appcast.xml -->
|
||||||
|
<item>
|
||||||
|
<title>Version 1.0.0</title>
|
||||||
|
<pubDate>Mon, 01 Jan 2024 00:00:00 +0000</pubDate>
|
||||||
|
<sparkle:version>1.0.0</sparkle:version>
|
||||||
|
<sparkle:shortVersionString>1.0.0</sparkle:shortVersionString>
|
||||||
|
<sparkle:minimumSystemVersion>14.0</sparkle:minimumSystemVersion>
|
||||||
|
<enclosure
|
||||||
|
url="https://github.com/steipete/vibetunnel/releases/download/v1.0.0/VibeTunnel-1.0.0.dmg"
|
||||||
|
sparkle:edSignature="..."
|
||||||
|
length="12345678"
|
||||||
|
type="application/octet-stream"/>
|
||||||
|
</item>
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5. Post-Release
|
||||||
|
|
||||||
|
**Documentation**
|
||||||
|
- [ ] Update README with new version
|
||||||
|
- [ ] Update docs with new features
|
||||||
|
- [ ] Post release notes
|
||||||
|
|
||||||
|
**Monitoring**
|
||||||
|
- [ ] Check Sparkle update stats
|
||||||
|
- [ ] Monitor crash reports
|
||||||
|
- [ ] Review user feedback
|
||||||
|
|
||||||
|
## Version Scheme
|
||||||
|
|
||||||
|
```
|
||||||
|
MAJOR.MINOR.PATCH[-PRERELEASE]
|
||||||
|
|
||||||
|
1.0.0 - Stable release
|
||||||
|
1.0.0-beta.1 - Beta release
|
||||||
|
1.0.0-rc.1 - Release candidate
|
||||||
|
```
|
||||||
|
|
||||||
|
## Build Configurations
|
||||||
|
|
||||||
|
| Config | Use Case | Signing |
|
||||||
|
|--------|----------|---------|
|
||||||
|
| Debug | Development | No |
|
||||||
|
| Release | Distribution | Yes |
|
||||||
|
| AppStore | Mac App Store | Yes |
|
||||||
|
|
||||||
|
## Code Signing
|
||||||
|
|
||||||
|
**Requirements**
|
||||||
|
- Apple Developer account
|
||||||
|
- Developer ID certificate
|
||||||
|
- Notarization credentials
|
||||||
|
|
||||||
|
**Setup**
|
||||||
|
```bash
|
||||||
|
# Store credentials
|
||||||
|
xcrun notarytool store-credentials "VT_NOTARY" \
|
||||||
|
--apple-id "your@email.com" \
|
||||||
|
--team-id "TEAMID" \
|
||||||
|
--password "app-specific-password"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
| Issue | Solution |
|
||||||
|
|-------|----------|
|
||||||
|
| Notarization fails | Check entitlements, wait 5 min |
|
||||||
|
| Sparkle not updating | Verify appcast URL, signature |
|
||||||
|
| DMG corrupted | Re-run with clean build |
|
||||||
|
| Version mismatch | Run update-version.sh |
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Revert release
|
||||||
|
gh release delete v$VERSION
|
||||||
|
git revert <commit>
|
||||||
|
git tag -d v$VERSION
|
||||||
|
git push origin :refs/tags/v$VERSION
|
||||||
|
|
||||||
|
# Update Sparkle feed to previous version
|
||||||
|
./scripts/rollback-sparkle.sh $PREVIOUS_VERSION
|
||||||
|
```
|
||||||
|
|
||||||
|
## CI/CD Pipeline
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# .github/workflows/release.yml
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- 'v*'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: macos-14
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- run: ./scripts/test-all.sh
|
||||||
|
- run: ./scripts/release.sh ${{ github.ref_name }}
|
||||||
|
- uses: softprops/action-gh-release@v1
|
||||||
|
with:
|
||||||
|
files: dist/*.dmg
|
||||||
|
```
|
||||||
|
|
||||||
|
## See Also
|
||||||
|
- [Build System](../guides/development.md#build-system)
|
||||||
|
- [Testing Guide](../guides/testing.md)
|
||||||
|
- [Changelog](../../CHANGELOG.md)
|
||||||
Loading…
Reference in a new issue