mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-03-25 09:25:50 +00:00
docs: Add Linux setup instructions and authentication documentation (#344)
Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
parent
2fc28b687f
commit
68e6456aef
20 changed files with 3661 additions and 133 deletions
265
README.md
265
README.md
|
|
@ -16,11 +16,27 @@
|
|||
|
||||
Ever wanted to check on your AI agents while you're away? Need to monitor that long-running build from your phone? Want to share a terminal session with a colleague without complex SSH setups? VibeTunnel makes it happen with zero friction.
|
||||
|
||||
## Installation Options
|
||||
|
||||
### macOS App (Recommended for Mac users)
|
||||
The native macOS app provides the best experience with menu bar integration and automatic updates.
|
||||
|
||||
### npm Package (Linux & Headless Systems)
|
||||
For Linux servers, Docker containers, or headless macOS systems, install via npm:
|
||||
|
||||
```bash
|
||||
npm install -g vibetunnel
|
||||
```
|
||||
|
||||
This gives you the full VibeTunnel server with web UI, just without the macOS menu bar app. See the [npm Package section](#npm-package) for detailed usage.
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Requirements
|
||||
|
||||
**VibeTunnel requires an Apple Silicon Mac (M1+).** Intel Macs are not supported.
|
||||
**macOS App**: Requires an Apple Silicon Mac (M1+). Intel Macs are not supported for the native app.
|
||||
|
||||
**npm Package**: Works on any system with Node.js 20+, including Intel Macs and Linux. Windows is not yet supported ([#252](https://github.com/amantus-ai/vibetunnel/issues/252)).
|
||||
|
||||
### 1. Download & Install
|
||||
|
||||
|
|
@ -38,6 +54,24 @@ VibeTunnel lives in your menu bar. Click the icon to start the server.
|
|||
|
||||
### 3. Use the `vt` Command
|
||||
|
||||
The `vt` command is a smart wrapper that forwards your terminal sessions through VibeTunnel:
|
||||
|
||||
**How it works**:
|
||||
- `vt` is a bash script that internally calls `vibetunnel fwd` to forward terminal output
|
||||
- It provides additional features like shell alias resolution and session title management
|
||||
- Available from both the Mac app and npm package installations
|
||||
|
||||
**Installation sources**:
|
||||
- **macOS App**: Creates `/usr/local/bin/vt` symlink during installation
|
||||
- **npm Package**: Installs `vt` globally, with intelligent Mac app detection
|
||||
|
||||
**Smart detection**:
|
||||
When you run `vt` from the npm package, it:
|
||||
1. Checks if the Mac app is installed at `/Applications/VibeTunnel.app`
|
||||
2. If found, forwards to the Mac app's `vt` for the best experience
|
||||
3. If not found, uses the npm-installed `vibetunnel fwd`
|
||||
4. This ensures `vt` always uses the best available implementation
|
||||
|
||||
```bash
|
||||
# Run any command in the browser
|
||||
vt pnpm run dev
|
||||
|
|
@ -67,11 +101,11 @@ Visit [http://localhost:4020](http://localhost:4020) to see all your terminal se
|
|||
- **🚀 Zero Configuration** - No SSH keys, no port forwarding, no complexity
|
||||
- **🤖 AI Agent Friendly** - Perfect for monitoring Claude Code, ChatGPT, or any terminal-based AI tools
|
||||
- **📊 Dynamic Terminal Titles** - Real-time activity tracking shows what's happening in each session
|
||||
- **🔒 Secure by Design** - Password protection, localhost-only mode, or secure tunneling via Tailscale/ngrok
|
||||
- **🔒 Secure by Design** - Multiple authentication modes, localhost-only mode, or secure tunneling via Tailscale/ngrok
|
||||
- **📱 Mobile Ready** - Native iOS app and responsive web interface for phones and tablets
|
||||
- **🎬 Session Recording** - All sessions recorded in asciinema format for later playback
|
||||
- **⚡ High Performance** - Powered by Bun runtime for blazing-fast JavaScript execution
|
||||
- **🍎 Apple Silicon Native** - Optimized for M1/M2/M3 Macs with ARM64-only binaries
|
||||
- **⚡ High Performance** - Optimized Node.js server with minimal resource usage
|
||||
- **🍎 Apple Silicon Native** - Optimized for Apple Silicon (M1+) Macs with ARM64-only binaries
|
||||
- **🐚 Shell Alias Support** - Your custom aliases and shell functions work automatically
|
||||
|
||||
> **Note**: The iOS app and Tauri-based components are still work in progress and not recommended for production use yet.
|
||||
|
|
@ -81,10 +115,10 @@ Visit [http://localhost:4020](http://localhost:4020) to see all your terminal se
|
|||
VibeTunnel consists of three main components:
|
||||
|
||||
1. **macOS Menu Bar App** - Native Swift application that manages the server lifecycle
|
||||
2. **Node.js/Bun Server** - High-performance TypeScript server handling terminal sessions
|
||||
2. **Node.js Server** - High-performance TypeScript server handling terminal sessions
|
||||
3. **Web Frontend** - Modern web interface using Lit components and xterm.js
|
||||
|
||||
The server runs as a standalone Bun executable with embedded Node.js modules, providing excellent performance and minimal resource usage.
|
||||
The server runs as a standalone Node.js executable with embedded modules, providing excellent performance and minimal resource usage.
|
||||
|
||||
## Remote Access Options
|
||||
|
||||
|
|
@ -132,7 +166,7 @@ The server runs as a standalone Bun executable with embedded Node.js modules, pr
|
|||
**Note**: Free ngrok URLs change each time you restart the tunnel. You can claim one free static domain per user, or upgrade to a paid plan for multiple domains.
|
||||
|
||||
### Option 3: Local Network
|
||||
1. Set a dashboard password in settings
|
||||
1. Configure authentication (see Authentication section)
|
||||
2. Switch to "Network" mode
|
||||
3. Access via `http://[your-mac-ip]:4020`
|
||||
|
||||
|
|
@ -165,13 +199,224 @@ Dynamic mode includes real-time activity detection:
|
|||
- Shows `•` when there's terminal output within 5 seconds
|
||||
- Claude commands show specific status (Crafting, Transitioning, etc.)
|
||||
- Extensible system for future app-specific detectors
|
||||
|
||||
## Authentication
|
||||
|
||||
VibeTunnel provides multiple authentication modes to secure your terminal sessions:
|
||||
|
||||
### Authentication Modes
|
||||
|
||||
#### 1. System Authentication (Default)
|
||||
Uses your operating system's native authentication:
|
||||
- **macOS**: Authenticates against local user accounts
|
||||
- **Linux**: Uses PAM (Pluggable Authentication Modules)
|
||||
- Login with your system username and password
|
||||
|
||||
#### 2. Environment Variable Authentication
|
||||
Simple authentication for deployments:
|
||||
```bash
|
||||
export VIBETUNNEL_USERNAME=admin
|
||||
export VIBETUNNEL_PASSWORD=your-secure-password
|
||||
npm run start
|
||||
```
|
||||
|
||||
#### 3. SSH Key Authentication
|
||||
Use Ed25519 SSH keys from `~/.ssh/authorized_keys`:
|
||||
```bash
|
||||
# Enable SSH key authentication
|
||||
npm run start -- --enable-ssh-keys
|
||||
|
||||
# Make SSH keys mandatory (disable password auth)
|
||||
npm run start -- --enable-ssh-keys --disallow-user-password
|
||||
```
|
||||
|
||||
#### 4. No Authentication
|
||||
For trusted environments only:
|
||||
```bash
|
||||
npm run start -- --no-auth
|
||||
```
|
||||
|
||||
#### 5. Local Bypass (Development Only)
|
||||
Allow localhost connections to bypass authentication:
|
||||
```bash
|
||||
# Basic local bypass (DEVELOPMENT ONLY - NOT FOR PRODUCTION)
|
||||
npm run start -- --allow-local-bypass
|
||||
|
||||
# With token for additional security (minimum for production)
|
||||
npm run start -- --allow-local-bypass --local-auth-token mytoken
|
||||
```
|
||||
|
||||
**Security Note**: Local bypass uses `req.socket.remoteAddress` which cannot be spoofed remotely due to TCP's three-way handshake. The implementation also rejects requests with proxy headers (`X-Forwarded-For`, etc.) to prevent header injection attacks. However:
|
||||
- **Development only**: Basic bypass without token should never be used in production
|
||||
- **Local processes**: Any process on the same machine can access the API
|
||||
- **Always use tokens**: In production, always require `--local-auth-token`
|
||||
- **Consider alternatives**: For production, use proper authentication instead of local bypass
|
||||
|
||||
### macOS App Authentication
|
||||
|
||||
The macOS menu bar app supports these authentication modes:
|
||||
- **No Authentication**: For trusted environments only
|
||||
- **System Authentication**: Uses your macOS user account credentials
|
||||
- **SSH Key Authentication**: Uses Ed25519 SSH keys from `~/.ssh/authorized_keys`
|
||||
- Configure via Settings → Security when in "Network" mode
|
||||
|
||||
### Security Best Practices
|
||||
|
||||
1. **Always use authentication** when binding to network interfaces (`--bind 0.0.0.0`)
|
||||
2. **Use HTTPS** in production with a reverse proxy (nginx, Caddy)
|
||||
3. **Rotate credentials** regularly
|
||||
4. **Consider SSH keys** for stronger security
|
||||
5. **Never use local bypass without tokens** in production environments
|
||||
6. **Monitor access logs** for suspicious authentication patterns
|
||||
7. **Default to secure** - explicitly enable less secure options only when needed
|
||||
|
||||
|
||||
## npm Package
|
||||
|
||||
The VibeTunnel npm package provides the full server functionality for Linux, Docker, CI/CD environments, and headless macOS systems.
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
# Install globally via npm
|
||||
npm install -g vibetunnel
|
||||
|
||||
# Or with yarn
|
||||
yarn global add vibetunnel
|
||||
|
||||
# Or with pnpm
|
||||
pnpm add -g vibetunnel
|
||||
```
|
||||
|
||||
**Requirements**: Node.js 20.0.0 or higher
|
||||
|
||||
### Running the VibeTunnel Server
|
||||
|
||||
#### Basic Usage
|
||||
|
||||
```bash
|
||||
# Start with default settings (localhost:4020)
|
||||
vibetunnel
|
||||
|
||||
# Bind to all network interfaces
|
||||
vibetunnel --bind 0.0.0.0
|
||||
|
||||
# Use a custom port
|
||||
vibetunnel --port 8080
|
||||
|
||||
# With authentication
|
||||
VIBETUNNEL_USERNAME=admin VIBETUNNEL_PASSWORD=secure vibetunnel --bind 0.0.0.0
|
||||
|
||||
# Enable debug logging
|
||||
VIBETUNNEL_DEBUG=1 vibetunnel
|
||||
|
||||
# Run without authentication (trusted networks only!)
|
||||
vibetunnel --no-auth
|
||||
```
|
||||
|
||||
#### Using the `vt` Command
|
||||
|
||||
The `vt` command wrapper makes it easy to forward terminal sessions:
|
||||
|
||||
```bash
|
||||
# Monitor AI agents with automatic activity tracking
|
||||
vt claude
|
||||
vt claude --dangerously-skip-permissions
|
||||
vt --title-mode dynamic claude # See real-time Claude status
|
||||
|
||||
# Run any command and see it in the browser
|
||||
vt npm test
|
||||
vt python script.py
|
||||
vt cargo build --release
|
||||
|
||||
# Open an interactive shell
|
||||
vt --shell
|
||||
vt -i # short form
|
||||
|
||||
# Control terminal titles
|
||||
vt --title-mode static npm run dev # Shows path and command
|
||||
vt --title-mode dynamic python app.py # Shows path, command, and activity
|
||||
vt --title-mode filter vim # Blocks vim from changing title
|
||||
|
||||
# Shell aliases work automatically!
|
||||
vt claude-danger # Your custom alias for claude --dangerously-skip-permissions
|
||||
|
||||
# Update session title (inside a VibeTunnel session)
|
||||
vt title "My Project - Testing"
|
||||
```
|
||||
|
||||
### Mac App Interoperability
|
||||
|
||||
The npm package is designed to work seamlessly alongside the Mac app:
|
||||
|
||||
#### Smart Command Routing
|
||||
- The `vt` command automatically detects if the Mac app is installed
|
||||
- If found at `/Applications/VibeTunnel.app`, it defers to the Mac app
|
||||
- If not found, it uses the npm-installed server
|
||||
- This ensures you always get the best available implementation
|
||||
|
||||
#### Installation Behavior
|
||||
- If `/usr/local/bin/vt` already exists (from another tool), npm won't overwrite it
|
||||
- You'll see a helpful warning with alternatives: `vibetunnel` or `npx vt`
|
||||
- The installation always succeeds, even if the `vt` symlink can't be created
|
||||
|
||||
#### When to Use Each Version
|
||||
- **Mac app only**: Best for macOS users who want menu bar integration
|
||||
- **npm only**: Perfect for Linux, Docker, CI/CD, or headless servers
|
||||
- **Both installed**: Mac app takes precedence, npm serves as fallback
|
||||
- **Development**: npm package useful for testing without affecting Mac app
|
||||
|
||||
### Package Contents
|
||||
|
||||
The npm package includes:
|
||||
- Full VibeTunnel server with web UI
|
||||
- CLI tools (`vibetunnel` and `vt` commands)
|
||||
- Native PTY support via node-pty
|
||||
- Pre-built binaries for common platforms
|
||||
- Complete feature parity with macOS app (minus menu bar)
|
||||
|
||||
### Building the npm Package
|
||||
|
||||
For maintainers who need to build the npm package:
|
||||
|
||||
#### Unified Build (Multi-Platform by Default)
|
||||
```bash
|
||||
# Build with prebuilt binaries for all platforms
|
||||
# Requires Docker for Linux cross-compilation
|
||||
npm run build:npm
|
||||
```
|
||||
|
||||
This creates prebuilt binaries for:
|
||||
- macOS (x64, arm64) - Node.js 20, 22, 23, 24
|
||||
- Linux (x64, arm64) - Node.js 20, 22, 23, 24
|
||||
|
||||
#### Build Options
|
||||
```bash
|
||||
# Current platform only (faster for development)
|
||||
node scripts/build-npm.js --current-only
|
||||
|
||||
# Specific platform/architecture
|
||||
node scripts/build-npm.js --platform darwin --arch arm64
|
||||
|
||||
# Skip Docker builds
|
||||
node scripts/build-npm.js --no-docker
|
||||
```
|
||||
|
||||
#### Publishing
|
||||
```bash
|
||||
# Test the package locally
|
||||
npm pack
|
||||
|
||||
# Publish to npm
|
||||
npm publish
|
||||
```
|
||||
|
||||
## Building from Source
|
||||
|
||||
### Prerequisites
|
||||
- macOS 14.0+ (Sonoma) on Apple Silicon (M1/M2/M3)
|
||||
- macOS 14.0+ (Sonoma) on Apple Silicon (M1+)
|
||||
- Xcode 16.0+
|
||||
- Node.js 20+
|
||||
- Bun runtime
|
||||
- Node.js 20+ (minimum supported version)
|
||||
|
||||
### Build Steps
|
||||
|
||||
|
|
|
|||
|
|
@ -282,7 +282,17 @@ else
|
|||
echo "Warning: authenticate_pam.node not found. PAM authentication may not work."
|
||||
fi
|
||||
|
||||
echo "✓ Native executable and modules copied successfully"
|
||||
# Copy unified vt script
|
||||
if [ -f "${WEB_DIR}/bin/vt" ]; then
|
||||
echo "Copying unified vt script..."
|
||||
cp "${WEB_DIR}/bin/vt" "${APP_RESOURCES}/"
|
||||
chmod +x "${APP_RESOURCES}/vt"
|
||||
else
|
||||
echo "error: Unified vt script not found at ${WEB_DIR}/bin/vt"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✓ Native executable, modules, and vt script copied successfully"
|
||||
|
||||
# Sanity check: Verify all required binaries are present in the app bundle
|
||||
echo "Performing final sanity check..."
|
||||
|
|
@ -314,6 +324,16 @@ if [ -f "${APP_RESOURCES}/spawn-helper" ] && [ ! -x "${APP_RESOURCES}/spawn-help
|
|||
MISSING_FILES+=("spawn-helper is not executable")
|
||||
fi
|
||||
|
||||
# Check for vt script
|
||||
if [ ! -f "${APP_RESOURCES}/vt" ]; then
|
||||
MISSING_FILES+=("vt script")
|
||||
fi
|
||||
|
||||
# Check if vt script is executable
|
||||
if [ -f "${APP_RESOURCES}/vt" ] && [ ! -x "${APP_RESOURCES}/vt" ]; then
|
||||
MISSING_FILES+=("vt script is not executable")
|
||||
fi
|
||||
|
||||
# If any files are missing, fail the build
|
||||
if [ ${#MISSING_FILES[@]} -gt 0 ]; then
|
||||
echo "error: Build sanity check failed! Missing required files:"
|
||||
|
|
@ -323,7 +343,7 @@ if [ ${#MISSING_FILES[@]} -gt 0 ]; then
|
|||
echo "Build artifacts in ${NATIVE_DIR}:"
|
||||
ls -la "${NATIVE_DIR}" || echo " Directory does not exist"
|
||||
echo "App resources in ${APP_RESOURCES}:"
|
||||
ls -la "${APP_RESOURCES}/vibetunnel" "${APP_RESOURCES}/pty.node" "${APP_RESOURCES}/spawn-helper" 2>/dev/null || true
|
||||
ls -la "${APP_RESOURCES}/vibetunnel" "${APP_RESOURCES}/pty.node" "${APP_RESOURCES}/spawn-helper" "${APP_RESOURCES}/vt" 2>/dev/null || true
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
|
|
|||
18
web/.gitignore
vendored
18
web/.gitignore
vendored
|
|
@ -121,6 +121,10 @@ native/
|
|||
# Custom Node.js builds
|
||||
.node-builds/
|
||||
|
||||
# Prebuild binaries (generated)
|
||||
prebuilds/
|
||||
prebuilds-linux/
|
||||
|
||||
# Bun lockfile (generated during native build)
|
||||
bun.lock
|
||||
|
||||
|
|
@ -137,3 +141,17 @@ coverage-summary.json
|
|||
# Playwright traces and test data
|
||||
data/
|
||||
trace/
|
||||
|
||||
# Compiled binaries (should not be in git)
|
||||
bin-macos/
|
||||
*.dylib
|
||||
*.so
|
||||
*.exe
|
||||
spawn-helper*
|
||||
|
||||
# Test recordings and artifacts
|
||||
*.cast
|
||||
test.cast
|
||||
|
||||
# Temporary build directories
|
||||
temp-spawn-helper/
|
||||
|
|
|
|||
1
web/.npm-lock-notice
Normal file
1
web/.npm-lock-notice
Normal file
|
|
@ -0,0 +1 @@
|
|||
This package was built for npm distribution. Some dev files are excluded.
|
||||
65
web/.npmignore
Normal file
65
web/.npmignore
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
# Development and build files
|
||||
src/
|
||||
scripts/
|
||||
docs/
|
||||
test/
|
||||
playwright/
|
||||
coverage/
|
||||
.husky/
|
||||
|
||||
# Config files not needed in production
|
||||
.gitignore
|
||||
.prettierignore
|
||||
.prettierrc
|
||||
biome.json
|
||||
vitest.config.ts
|
||||
playwright.config.ts
|
||||
playwright.config.skip-failing.ts
|
||||
tsconfig.*.json
|
||||
tsconfig.json
|
||||
postcss.config.js
|
||||
tailwind.config.js
|
||||
|
||||
# Build artifacts and temp files
|
||||
build/
|
||||
*.log
|
||||
*.tmp
|
||||
.DS_Store
|
||||
node_modules/
|
||||
pnpm-lock.yaml
|
||||
|
||||
# Native build scripts - not needed for npm package
|
||||
build-native.js
|
||||
build-native-clean.sh
|
||||
build-custom-node.js
|
||||
custom-node.md
|
||||
native/
|
||||
|
||||
# Development files
|
||||
CLAUDE.md
|
||||
DEVELOPMENT.md
|
||||
LOGGING_STYLE_GUIDE.md
|
||||
SECURITY.md
|
||||
fwd-test.ts
|
||||
spec.md
|
||||
|
||||
# Only include the built node-pty, not source
|
||||
node-pty/src/
|
||||
node-pty/tsconfig.json
|
||||
node-pty/*.ts
|
||||
node-pty/node_modules/
|
||||
|
||||
# Test files
|
||||
public/bundle/test.js
|
||||
public/bundle/screencap.js
|
||||
public/test/
|
||||
public/test.cast
|
||||
|
||||
# Keep:
|
||||
# - dist/ (compiled server code)
|
||||
# - public/ (web interface)
|
||||
# - bin/ (CLI entry points)
|
||||
# - node-pty/build/ (native module)
|
||||
# - node-pty/lib/ (JS files)
|
||||
# - package.json
|
||||
# - README.md
|
||||
|
|
@ -3,6 +3,4 @@
|
|||
# - enable-pre-post-scripts (pre/post scripts are enabled by default in npm 7+)
|
||||
# - auto-install-peers (use --legacy-peer-deps if needed)
|
||||
# - unsafe-perm (no longer needed in npm 7+)
|
||||
|
||||
# Approve builds for vendored packages
|
||||
side-effects-cache-unsafe=@vibetunnel/vendored-pty
|
||||
# - side-effects-cache-unsafe (deprecated in npm 9+)
|
||||
35
web/.prebuildrc
Normal file
35
web/.prebuildrc
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
{
|
||||
"targets": [
|
||||
{
|
||||
"runtime": "node",
|
||||
"target": "20.0.0"
|
||||
},
|
||||
{
|
||||
"runtime": "node",
|
||||
"target": "22.0.0"
|
||||
},
|
||||
{
|
||||
"runtime": "node",
|
||||
"target": "23.0.0"
|
||||
},
|
||||
{
|
||||
"runtime": "node",
|
||||
"target": "24.0.0"
|
||||
}
|
||||
],
|
||||
"include": [
|
||||
"node-pty/build/Release/pty.node"
|
||||
],
|
||||
"prebuild": [
|
||||
{
|
||||
"name": "node-pty",
|
||||
"binary": {
|
||||
"module_name": "pty",
|
||||
"module_path": "./node-pty/build/Release/",
|
||||
"remote_path": "{version}",
|
||||
"package_name": "{module_name}-v{version}-{node_abi}-{platform}-{arch}.tar.gz",
|
||||
"host": "https://github.com/amantus-ai/vibetunnel/releases/download/"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
130
web/README.md
130
web/README.md
|
|
@ -1,81 +1,99 @@
|
|||
# VibeTunnel Web
|
||||
# VibeTunnel CLI
|
||||
|
||||
Web terminal interface and server for VibeTunnel.
|
||||
Full-featured terminal sharing server with web interface for macOS and Linux. Windows not yet supported.
|
||||
|
||||
## Quick Start
|
||||
|
||||
Production users: Use the pre-built VibeTunnel executable from the main app.
|
||||
|
||||
## Development
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm run dev # Watch mode: server + client
|
||||
pnpm run dev:client # Watch mode: client only (for debugging server)
|
||||
npm install -g vibetunnel
|
||||
```
|
||||
|
||||
Open http://localhost:3000
|
||||
## Requirements
|
||||
|
||||
### Pre-commit Hooks
|
||||
- Node.js >= 20.0.0
|
||||
- macOS or Linux (Windows not yet supported)
|
||||
- Build tools for native modules (Xcode on macOS, build-essential on Linux)
|
||||
|
||||
This project uses husky and lint-staged to enforce code quality standards. After running `pnpm install`, pre-commit hooks will automatically:
|
||||
## Usage
|
||||
|
||||
- Format code with Biome
|
||||
- Check for linting errors
|
||||
- Run TypeScript type checking for all configs
|
||||
|
||||
If your commit fails due to linting or type errors, fix the issues and try again. Many formatting issues will be auto-fixed.
|
||||
|
||||
### Build Commands
|
||||
### Start the server
|
||||
|
||||
```bash
|
||||
pnpm run clean # Remove build artifacts
|
||||
pnpm run build # Build everything (including native executable)
|
||||
pnpm run lint # Check code style
|
||||
pnpm run lint:fix # Fix code style
|
||||
pnpm run typecheck # Type checking
|
||||
pnpm run test # Run all tests (unit + e2e)
|
||||
pnpm run format # Format code
|
||||
# Start with default settings (port 4020)
|
||||
vibetunnel
|
||||
|
||||
# Start with custom port
|
||||
vibetunnel --port 8080
|
||||
|
||||
# Start without authentication
|
||||
vibetunnel --no-auth
|
||||
```
|
||||
|
||||
## Production Build
|
||||
Then open http://localhost:4020 in your browser to access the web interface.
|
||||
|
||||
### Use the vt command wrapper
|
||||
|
||||
The `vt` command allows you to run commands with TTY forwarding:
|
||||
|
||||
```bash
|
||||
pnpm run build # Creates Node.js SEA executable
|
||||
./native/vibetunnel # Run standalone executable (no Node.js required)
|
||||
# Monitor AI agents with automatic activity tracking
|
||||
vt claude
|
||||
vt claude --dangerously-skip-permissions
|
||||
|
||||
# Run commands with output visible in VibeTunnel
|
||||
vt npm test
|
||||
vt python script.py
|
||||
vt top
|
||||
|
||||
# Launch interactive shell
|
||||
vt --shell
|
||||
vt -i
|
||||
|
||||
# Update session title (inside a session)
|
||||
vt title "My Project"
|
||||
```
|
||||
|
||||
## Architecture
|
||||
### Forward commands to a session
|
||||
|
||||
See [spec.md](./spec.md) for detailed architecture documentation.
|
||||
```bash
|
||||
# Basic usage
|
||||
vibetunnel fwd <session-id> <command> [args...]
|
||||
|
||||
## Key Features
|
||||
# Examples
|
||||
vibetunnel fwd --session-id abc123 ls -la
|
||||
vibetunnel fwd --session-id abc123 npm test
|
||||
vibetunnel fwd --session-id abc123 python script.py
|
||||
```
|
||||
|
||||
- Terminal sessions via node-pty
|
||||
- Real-time streaming (SSE + WebSocket)
|
||||
- Binary-optimized buffer updates
|
||||
- Multi-session support
|
||||
- File browser integration
|
||||
## Features
|
||||
|
||||
## Terminal Resizing Behavior
|
||||
- **Web-based terminal interface** - Access terminals from any browser
|
||||
- **Multiple concurrent sessions** - Run multiple terminals simultaneously
|
||||
- **Real-time synchronization** - See output in real-time
|
||||
- **TTY forwarding** - Full terminal emulation support
|
||||
- **Session management** - Create, list, and manage sessions
|
||||
- **Cross-platform** - Works on macOS and Linux
|
||||
- **No dependencies** - Just Node.js required
|
||||
|
||||
VibeTunnel intelligently handles terminal width based on how the session was created:
|
||||
## Package Contents
|
||||
|
||||
### Tunneled Sessions (via `vt` command)
|
||||
- Sessions created by running `vt` in a native terminal window
|
||||
- Terminal width is automatically limited to the native terminal's width to prevent text overflow
|
||||
- Prevents flickering and display issues in the native terminal
|
||||
- Shows "≤120" (or actual width) in the width selector when limited
|
||||
- Users can manually override this limit using the width selector
|
||||
This npm package includes:
|
||||
- Full VibeTunnel server with web UI
|
||||
- Command-line tools (vibetunnel, vt)
|
||||
- Native PTY support for terminal emulation
|
||||
- Web interface with xterm.js
|
||||
- Session management and forwarding
|
||||
|
||||
### Frontend-Created Sessions
|
||||
- Sessions created directly from the web interface (using the "New Session" button)
|
||||
- No width restrictions by default - uses full browser width
|
||||
- Perfect for web-only workflows where no native terminal is involved
|
||||
- Shows "∞" in the width selector for unlimited width
|
||||
## Platform Support
|
||||
|
||||
### Manual Width Control
|
||||
- Click the width indicator in the session header to open the width selector
|
||||
- Choose from common terminal widths (80, 120, 132, etc.) or unlimited
|
||||
- Width preferences are saved per session and persist across reloads
|
||||
- Selecting any width manually overrides automatic limitations
|
||||
- macOS (Intel and Apple Silicon)
|
||||
- Linux (x64 and ARM64)
|
||||
- Windows: Not yet supported ([#252](https://github.com/amantus-ai/vibetunnel/issues/252))
|
||||
|
||||
## Documentation
|
||||
|
||||
See the main repository for complete documentation: https://github.com/amantus-ai/vibetunnel
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
|
|
|
|||
4
web/bin/vibetunnel
Executable file
4
web/bin/vibetunnel
Executable file
|
|
@ -0,0 +1,4 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
// Start the CLI - it handles all command routing including 'fwd'
|
||||
require('../dist/cli.js');
|
||||
|
|
@ -1,47 +1,107 @@
|
|||
#!/bin/bash
|
||||
# VibeTunnel CLI wrapper
|
||||
# Unified VibeTunnel CLI wrapper - compatible with both Mac app and npm installations
|
||||
|
||||
# First, find the VibeTunnel app location
|
||||
# Try standard locations first, but verify the binary exists
|
||||
APP_PATH=""
|
||||
for TRY_PATH in "/Applications/VibeTunnel.app" "$HOME/Applications/VibeTunnel.app"; do
|
||||
if [ -d "$TRY_PATH" ] && [ -f "$TRY_PATH/Contents/Resources/vibetunnel" ]; then
|
||||
APP_PATH="$TRY_PATH"
|
||||
break
|
||||
fi
|
||||
done
|
||||
# Only check for Mac app on macOS
|
||||
if [[ "$OSTYPE" == "darwin"* ]]; then
|
||||
# macOS symlink resolution function using BSD readlink
|
||||
resolve_symlink_macos() {
|
||||
local target="$1"
|
||||
local current="$target"
|
||||
while [ -L "$current" ]; do
|
||||
current="$(readlink "$current")"
|
||||
# Handle relative symlinks
|
||||
if [[ "$current" != /* ]]; then
|
||||
current="$(dirname "$target")/$current"
|
||||
fi
|
||||
done
|
||||
echo "$current"
|
||||
}
|
||||
|
||||
# If not found in standard locations with valid binary, search for it
|
||||
if [ -z "$APP_PATH" ]; then
|
||||
# First try DerivedData (for development)
|
||||
for CANDIDATE in $(find ~/Library/Developer/Xcode/DerivedData -name "VibeTunnel.app" -type d 2>/dev/null | grep -v "\.dSYM" | grep -v "Index\.noindex"); do
|
||||
if [ -f "$CANDIDATE/Contents/Resources/vibetunnel" ]; then
|
||||
APP_PATH="$CANDIDATE"
|
||||
# Get the real path of this script to avoid infinite recursion
|
||||
SCRIPT_REAL_PATH="$(resolve_symlink_macos "${BASH_SOURCE[0]}")"
|
||||
|
||||
# Comprehensive Mac app search - try standard locations first, then development locations
|
||||
APP_PATH=""
|
||||
|
||||
# First try standard locations with valid binary check
|
||||
for TRY_PATH in "/Applications/VibeTunnel.app" "$HOME/Applications/VibeTunnel.app"; do
|
||||
if [ -d "$TRY_PATH" ] && [ -f "$TRY_PATH/Contents/Resources/vibetunnel" ]; then
|
||||
VT_SCRIPT="$TRY_PATH/Contents/Resources/vt"
|
||||
if [ -f "$VT_SCRIPT" ] && [ -x "$VT_SCRIPT" ]; then
|
||||
# Avoid infinite recursion by checking if this is the same script
|
||||
VT_REAL_PATH="$(resolve_symlink_macos "$VT_SCRIPT")"
|
||||
if [ "$SCRIPT_REAL_PATH" != "$VT_REAL_PATH" ]; then
|
||||
exec "$VT_SCRIPT" "$@"
|
||||
fi
|
||||
fi
|
||||
APP_PATH="$TRY_PATH"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If still not found, use mdfind as last resort
|
||||
# If not found in standard locations, search for development builds
|
||||
if [ -z "$APP_PATH" ]; then
|
||||
for CANDIDATE in $(mdfind -name "VibeTunnel.app" 2>/dev/null | grep -v "\.dSYM"); do
|
||||
# First try DerivedData (for development)
|
||||
for CANDIDATE in $(find ~/Library/Developer/Xcode/DerivedData -name "VibeTunnel.app" -type d 2>/dev/null | grep -v "\.dSYM" | grep -v "Index\.noindex"); do
|
||||
if [ -f "$CANDIDATE/Contents/Resources/vibetunnel" ]; then
|
||||
VT_SCRIPT="$CANDIDATE/Contents/Resources/vt"
|
||||
if [ -f "$VT_SCRIPT" ] && [ -x "$VT_SCRIPT" ]; then
|
||||
VT_REAL_PATH="$(resolve_symlink_macos "$VT_SCRIPT")"
|
||||
if [ "$SCRIPT_REAL_PATH" != "$VT_REAL_PATH" ]; then
|
||||
exec "$VT_SCRIPT" "$@"
|
||||
fi
|
||||
fi
|
||||
APP_PATH="$CANDIDATE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If still not found, use mdfind as last resort
|
||||
if [ -z "$APP_PATH" ]; then
|
||||
for CANDIDATE in $(mdfind -name "VibeTunnel.app" 2>/dev/null | grep -v "\.dSYM"); do
|
||||
if [ -f "$CANDIDATE/Contents/Resources/vibetunnel" ]; then
|
||||
VT_SCRIPT="$CANDIDATE/Contents/Resources/vt"
|
||||
if [ -f "$VT_SCRIPT" ] && [ -x "$VT_SCRIPT" ]; then
|
||||
VT_REAL_PATH="$(resolve_symlink_macos "$VT_SCRIPT")"
|
||||
if [ "$SCRIPT_REAL_PATH" != "$VT_REAL_PATH" ]; then
|
||||
exec "$VT_SCRIPT" "$@"
|
||||
fi
|
||||
fi
|
||||
APP_PATH="$CANDIDATE"
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$APP_PATH" ]; then
|
||||
echo "Error: VibeTunnel.app with vibetunnel binary not found anywhere on the system" >&2
|
||||
exit 1
|
||||
# If we found a Mac app but couldn't use its vt script, use its binary directly
|
||||
if [ -n "$APP_PATH" ]; then
|
||||
VIBETUNNEL_BIN="$APP_PATH/Contents/Resources/vibetunnel"
|
||||
if [ -f "$VIBETUNNEL_BIN" ]; then
|
||||
# Found Mac app bundle - will use this binary
|
||||
echo "# Using VibeTunnel from Mac app bundle" >&2
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
# Execute vibetunnel from app bundle
|
||||
VIBETUNNEL_BIN="$APP_PATH/Contents/Resources/vibetunnel"
|
||||
if [ ! -f "$VIBETUNNEL_BIN" ]; then
|
||||
echo "Error: vibetunnel binary not found in app bundle at $VIBETUNNEL_BIN" >&2
|
||||
exit 1
|
||||
# If we get here without a Mac app, use the npm-installed vibetunnel
|
||||
if [ -z "$VIBETUNNEL_BIN" ]; then
|
||||
# First, try to find vibetunnel in the same directory as this script
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
if [ -f "$SCRIPT_DIR/vibetunnel" ]; then
|
||||
VIBETUNNEL_BIN="$SCRIPT_DIR/vibetunnel"
|
||||
else
|
||||
# Try to find vibetunnel in PATH
|
||||
if command -v vibetunnel >/dev/null 2>&1; then
|
||||
VIBETUNNEL_BIN="$(command -v vibetunnel)"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -z "$VIBETUNNEL_BIN" ] || [ ! -f "$VIBETUNNEL_BIN" ]; then
|
||||
echo "Error: vibetunnel binary not found. Please ensure vibetunnel is installed." >&2
|
||||
echo "Install with: npm install -g vibetunnel" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Check if we're already inside a VibeTunnel session
|
||||
|
|
@ -115,8 +175,9 @@ TITLE MODES:
|
|||
dynamic Show directory, command, and live activity status (default for web UI)
|
||||
|
||||
NOTE:
|
||||
This script automatically uses the vibetunnel executable bundled with
|
||||
VibeTunnel from the app bundle.
|
||||
This script automatically detects and uses the best available VibeTunnel installation:
|
||||
- Mac app bundle (preferred on macOS)
|
||||
- npm package installation (fallback)
|
||||
EOF
|
||||
|
||||
# Show path and version info
|
||||
|
|
@ -124,20 +185,39 @@ EOF
|
|||
echo "VIBETUNNEL BINARY:"
|
||||
echo " Path: $VIBETUNNEL_BIN"
|
||||
if [ -f "$VIBETUNNEL_BIN" ]; then
|
||||
# Extract version from the vibetunnel output
|
||||
# Try to get version from binary output first (works for both Mac app and npm)
|
||||
VERSION_INFO=$("$VIBETUNNEL_BIN" --version 2>&1 | grep "^VibeTunnel Server" | head -n 1)
|
||||
BUILD_INFO=$("$VIBETUNNEL_BIN" --version 2>&1 | grep "^Built:" | head -n 1)
|
||||
PLATFORM_INFO=$("$VIBETUNNEL_BIN" --version 2>&1 | grep "^Platform:" | head -n 1)
|
||||
|
||||
if [ -n "$VERSION_INFO" ]; then
|
||||
echo " Version: ${VERSION_INFO#VibeTunnel Server }"
|
||||
else
|
||||
# Fallback to package.json for npm installations
|
||||
PACKAGE_JSON="$(dirname "$(dirname "$VIBETUNNEL_BIN")")/package.json"
|
||||
if [ -f "$PACKAGE_JSON" ]; then
|
||||
VERSION=$(grep '"version"' "$PACKAGE_JSON" | head -1 | sed 's/.*"version".*:.*"\(.*\)".*/\1/')
|
||||
echo " Version: $VERSION"
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ -n "$BUILD_INFO" ]; then
|
||||
echo " ${BUILD_INFO}"
|
||||
fi
|
||||
if [ -n "$PLATFORM_INFO" ]; then
|
||||
echo " ${PLATFORM_INFO}"
|
||||
fi
|
||||
|
||||
# Determine installation type
|
||||
if [[ "$VIBETUNNEL_BIN" == */Applications/VibeTunnel.app/* ]]; then
|
||||
echo " Status: Mac app bundle"
|
||||
elif [[ "$VIBETUNNEL_BIN" == */DerivedData/* ]]; then
|
||||
echo " Status: Development build"
|
||||
elif [[ "$VIBETUNNEL_BIN" == *npm* ]] || [[ "$VIBETUNNEL_BIN" == */bin/vibetunnel ]]; then
|
||||
echo " Status: Installed via npm"
|
||||
else
|
||||
echo " Status: Unknown installation"
|
||||
fi
|
||||
else
|
||||
echo " Status: Not found"
|
||||
fi
|
||||
|
|
@ -156,15 +236,15 @@ resolve_command() {
|
|||
case "$shell_name" in
|
||||
zsh)
|
||||
# For zsh, we need interactive mode to get aliases
|
||||
exec "$VIBETUNNEL_BIN" fwd $TITLE_MODE_ARGS "$user_shell" -i -c "$(printf '%q ' "$cmd" "$@")"
|
||||
exec "$VIBETUNNEL_BIN" fwd ${TITLE_MODE_ARGS:+"$TITLE_MODE_ARGS"} "$user_shell" -i -c "$(printf '%q ' "$cmd" "$@")"
|
||||
;;
|
||||
bash)
|
||||
# For bash, expand aliases in non-interactive mode
|
||||
exec "$VIBETUNNEL_BIN" fwd $TITLE_MODE_ARGS "$user_shell" -c "shopt -s expand_aliases; source ~/.bashrc 2>/dev/null || source ~/.bash_profile 2>/dev/null || true; $(printf '%q ' "$cmd" "$@")"
|
||||
exec "$VIBETUNNEL_BIN" fwd ${TITLE_MODE_ARGS:+"$TITLE_MODE_ARGS"} "$user_shell" -c "shopt -s expand_aliases; source ~/.bashrc 2>/dev/null || source ~/.bash_profile 2>/dev/null || true; $(printf '%q ' "$cmd" "$@")"
|
||||
;;
|
||||
*)
|
||||
# Generic shell handling
|
||||
exec "$VIBETUNNEL_BIN" fwd $TITLE_MODE_ARGS "$user_shell" -c "$(printf '%q ' "$cmd" "$@")"
|
||||
exec "$VIBETUNNEL_BIN" fwd ${TITLE_MODE_ARGS:+"$TITLE_MODE_ARGS"} "$user_shell" -c "$(printf '%q ' "$cmd" "$@")"
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
|
@ -207,12 +287,12 @@ fi
|
|||
if [ $# -gt 0 ] && [[ "$1" != -* ]]; then
|
||||
if [[ "$NO_SHELL_WRAP" == "true" ]]; then
|
||||
# Execute directly without shell wrapper
|
||||
exec "$VIBETUNNEL_BIN" fwd $TITLE_MODE_ARGS "$@"
|
||||
exec "$VIBETUNNEL_BIN" fwd ${TITLE_MODE_ARGS:+"$TITLE_MODE_ARGS"} "$@"
|
||||
else
|
||||
# Check if the first argument is a real binary
|
||||
if which "$1" >/dev/null 2>&1; then
|
||||
# It's a real binary, execute directly
|
||||
exec "$VIBETUNNEL_BIN" fwd $TITLE_MODE_ARGS "$@"
|
||||
exec "$VIBETUNNEL_BIN" fwd ${TITLE_MODE_ARGS:+"$TITLE_MODE_ARGS"} "$@"
|
||||
else
|
||||
# Not a real binary, try alias resolution
|
||||
resolve_command "$@"
|
||||
|
|
@ -220,5 +300,5 @@ if [ $# -gt 0 ] && [[ "$1" != -* ]]; then
|
|||
fi
|
||||
else
|
||||
# Run with fwd command (original behavior for options)
|
||||
exec "$VIBETUNNEL_BIN" fwd $TITLE_MODE_ARGS "$@"
|
||||
exec "$VIBETUNNEL_BIN" fwd ${TITLE_MODE_ARGS:+"$TITLE_MODE_ARGS"} "$@"
|
||||
fi
|
||||
406
web/docs/npm.md
Normal file
406
web/docs/npm.md
Normal file
|
|
@ -0,0 +1,406 @@
|
|||
# VibeTunnel NPM Package Distribution
|
||||
|
||||
This document explains the npm package build process, native module handling, and prebuild system for VibeTunnel.
|
||||
|
||||
## Overview
|
||||
|
||||
VibeTunnel is distributed as an npm package that includes:
|
||||
- Full web server with terminal sharing capabilities
|
||||
- Native modules for terminal (PTY) and authentication (PAM) support
|
||||
- Cross-platform prebuilt binaries to avoid requiring build tools
|
||||
- Command-line tools (`vibetunnel` and `vt`)
|
||||
|
||||
## Package Structure
|
||||
|
||||
```
|
||||
vibetunnel/
|
||||
├── dist/ # Compiled server code
|
||||
├── public/ # Web interface assets
|
||||
├── bin/ # CLI entry points
|
||||
│ ├── vibetunnel # Main server executable
|
||||
│ └── vt # Terminal wrapper command
|
||||
├── node-pty/ # Vendored PTY implementation
|
||||
│ ├── lib/ # TypeScript compiled code
|
||||
│ └── package.json # PTY package configuration
|
||||
├── prebuilds/ # Native module prebuilt binaries
|
||||
│ ├── node-pty-* # PTY binaries for all platforms/Node versions
|
||||
│ └── authenticate-pam-* # PAM binaries for all platforms/Node versions
|
||||
└── README.md # Package documentation
|
||||
```
|
||||
|
||||
## Native Modules
|
||||
|
||||
VibeTunnel requires two native modules:
|
||||
|
||||
### 1. node-pty (Terminal Support)
|
||||
- **Purpose**: Provides pseudo-terminal (PTY) functionality
|
||||
- **Components**:
|
||||
- `pty.node`: Main Node.js addon for terminal operations
|
||||
- `spawn-helper`: macOS-only C helper binary for process spawning
|
||||
- **Platforms**: All (macOS, Linux)
|
||||
- **Dependencies**: None (vendored implementation)
|
||||
|
||||
### 2. authenticate-pam (Authentication)
|
||||
- **Purpose**: PAM (Pluggable Authentication Modules) integration
|
||||
- **Components**:
|
||||
- `authenticate_pam.node`: Node.js addon for system authentication
|
||||
- **Platforms**: Linux primarily, macOS for compatibility
|
||||
- **Dependencies**: System PAM libraries
|
||||
|
||||
## Prebuild System
|
||||
|
||||
### Overview
|
||||
We use `prebuild` and `prebuild-install` to provide precompiled native modules, eliminating the need for users to have build tools installed.
|
||||
|
||||
### Coverage
|
||||
- **Node.js versions**: 20, 22, 23, 24
|
||||
- **Platforms**: macOS (x64, arm64), Linux (x64, arm64)
|
||||
- **Total prebuilds**: 32 binaries (16 per native module)
|
||||
|
||||
### Prebuild Files
|
||||
```
|
||||
prebuilds/
|
||||
├── node-pty-v1.0.0-node-v115-darwin-arm64.tar.gz
|
||||
├── node-pty-v1.0.0-node-v115-darwin-x64.tar.gz
|
||||
├── node-pty-v1.0.0-node-v115-linux-arm64.tar.gz
|
||||
├── node-pty-v1.0.0-node-v115-linux-x64.tar.gz
|
||||
├── authenticate-pam-v1.0.5-node-v115-darwin-arm64.tar.gz
|
||||
├── authenticate-pam-v1.0.5-node-v115-darwin-x64.tar.gz
|
||||
├── authenticate-pam-v1.0.5-node-v115-linux-arm64.tar.gz
|
||||
├── authenticate-pam-v1.0.5-node-v115-linux-x64.tar.gz
|
||||
└── ... (similar for node versions 22, 23, 24)
|
||||
```
|
||||
|
||||
Note: Node version numbers map to internal versions (v115=Node 20, v127=Node 22, v131=Node 23, v134=Node 24)
|
||||
|
||||
## Build Process
|
||||
|
||||
### Unified Build (Multi-Platform by Default)
|
||||
```bash
|
||||
npm run build:npm
|
||||
```
|
||||
- Compiles TypeScript and bundles client code
|
||||
- Builds native modules for all supported platforms (macOS x64/arm64, Linux x64/arm64)
|
||||
- Creates comprehensive prebuilds for zero-dependency installation
|
||||
- Generates npm README optimized for package distribution
|
||||
|
||||
### Build Options
|
||||
The unified build script supports flexible targeting:
|
||||
|
||||
```bash
|
||||
# Default: All platforms
|
||||
npm run build:npm
|
||||
|
||||
# Current platform only (faster for development)
|
||||
node scripts/build-npm.js --current-only
|
||||
|
||||
# Specific platform/architecture
|
||||
node scripts/build-npm.js --platform darwin --arch arm64
|
||||
node scripts/build-npm.js --platform linux
|
||||
|
||||
# Skip Docker (Linux builds will be skipped)
|
||||
node scripts/build-npm.js --no-docker
|
||||
```
|
||||
|
||||
### Docker Requirements
|
||||
For Linux builds, Docker is required:
|
||||
- **Recommended**: [OrbStack](https://orbstack.dev/)
|
||||
- **Alternative**: [Docker Desktop](https://www.docker.com/products/docker-desktop/)
|
||||
|
||||
The build will fail with helpful error messages if Docker is not available.
|
||||
|
||||
## Installation Process
|
||||
|
||||
### For End Users
|
||||
1. **Install package**: `npm install -g vibetunnel`
|
||||
2. **Prebuild-install runs**: Attempts to download prebuilt binaries
|
||||
3. **Fallback compilation**: If prebuilds fail, compiles from source
|
||||
4. **Result**: Working VibeTunnel installation
|
||||
|
||||
### Installation Scripts
|
||||
The package uses a multi-stage installation approach:
|
||||
|
||||
```json
|
||||
{
|
||||
"scripts": {
|
||||
"install": "prebuild-install || node scripts/postinstall-npm.js"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Stage 1: prebuild-install
|
||||
- Downloads appropriate prebuilt binary for current platform/Node version
|
||||
- Installs to standard locations
|
||||
- **Success**: Installation complete, no compilation needed
|
||||
- **Failure**: Proceeds to Stage 2
|
||||
|
||||
#### Stage 2: postinstall-npm.js
|
||||
- **node-pty**: Essential module, installation fails if build fails
|
||||
- **authenticate-pam**: Optional module, warns if build fails
|
||||
- Provides helpful error messages about required build tools
|
||||
|
||||
## Platform-Specific Details
|
||||
|
||||
### macOS
|
||||
- **spawn-helper**: Additional C binary needed for proper PTY operations
|
||||
- **Built during install**: spawn-helper compiles via node-gyp when needed
|
||||
- **Architecture**: Supports both Intel (x64) and Apple Silicon (arm64)
|
||||
- **Build tools**: Requires Xcode Command Line Tools for source compilation
|
||||
|
||||
### Linux
|
||||
- **PAM libraries**: Requires `libpam0g-dev` for authenticate-pam compilation
|
||||
- **spawn-helper**: Not used on Linux (macOS-only)
|
||||
- **Build tools**: Requires `build-essential` package for source compilation
|
||||
|
||||
### Docker Build Environment
|
||||
Linux prebuilds are created using Docker with:
|
||||
- **Base image**: `node:22-bookworm`
|
||||
- **Dependencies**: `python3 make g++ git libpam0g-dev`
|
||||
- **Package manager**: pnpm (more reliable than npm in Docker)
|
||||
- **Environment**: `CI=true` to avoid interactive prompts
|
||||
|
||||
## spawn-helper Binary
|
||||
|
||||
### What is spawn-helper?
|
||||
`spawn-helper` is a small C helper binary used by node-pty for proper terminal process spawning on macOS.
|
||||
|
||||
### Key Facts
|
||||
- **Size**: ~70KB pure C binary
|
||||
- **Platform**: macOS only (Linux doesn't use it)
|
||||
- **Purpose**: Handles terminal device attachment for spawned processes
|
||||
- **Dependencies**: None (pure C, no Node.js dependencies)
|
||||
- **Architecture**: Platform-specific (x64 vs arm64)
|
||||
|
||||
### Source Code
|
||||
```c
|
||||
// Simplified version of spawn-helper functionality
|
||||
int main (int argc, char** argv) {
|
||||
char *slave_path = ttyname(STDIN_FILENO);
|
||||
close(open(slave_path, O_RDWR)); // Attach to terminal
|
||||
|
||||
char *cwd = argv[1];
|
||||
char *file = argv[2];
|
||||
argv = &argv[2];
|
||||
|
||||
if (strlen(cwd) && chdir(cwd) == -1) {
|
||||
_exit(1);
|
||||
}
|
||||
|
||||
execvp(file, argv); // Execute the target command
|
||||
return 1;
|
||||
}
|
||||
```
|
||||
|
||||
### Installation Handling
|
||||
- **Current approach**: Universal spawn-helper binary included in prebuilds (macOS only)
|
||||
- **Benefits**: No compilation needed, faster installation, works without build tools
|
||||
- **Fallback path**: If prebuild fails, compilation happens automatically via node-gyp
|
||||
- **Error handling**: Non-fatal if missing (warns but continues)
|
||||
|
||||
### Universal Binary Implementation
|
||||
spawn-helper is now shipped as a prebuilt universal binary in all macOS prebuilds:
|
||||
|
||||
**Implementation**:
|
||||
- Built for both x64 and arm64 architectures using clang++
|
||||
- Combined into universal binary with `lipo`
|
||||
- Included in every macOS node-pty prebuild automatically
|
||||
|
||||
**Benefits**:
|
||||
- ✅ Faster installation (no compilation needed)
|
||||
- ✅ Works without build tools (Xcode Command Line Tools)
|
||||
- ✅ Universal compatibility across Intel and Apple Silicon Macs
|
||||
- ✅ Smaller download than compiling during install
|
||||
|
||||
**Build process**:
|
||||
```bash
|
||||
# Build for both architectures
|
||||
clang++ -arch x86_64 -o spawn-helper-x64 spawn-helper.cc
|
||||
clang++ -arch arm64 -o spawn-helper-arm64 spawn-helper.cc
|
||||
|
||||
# Create universal binary
|
||||
lipo -create spawn-helper-x64 spawn-helper-arm64 -output spawn-helper-universal
|
||||
|
||||
# Include in all macOS prebuilds
|
||||
```
|
||||
|
||||
## Package Optimization
|
||||
|
||||
### File Exclusions
|
||||
Development artifacts are excluded from the final package:
|
||||
- Test files (`public/bundle/test.js`, `public/test/` directory)
|
||||
- Recording files (`*.cast` prevented by .gitignore)
|
||||
- Build artifacts (`dist/` selectively included via package.json `files` field)
|
||||
|
||||
**Note**: `screencap.js` is kept as it provides screen capture functionality for the web interface.
|
||||
|
||||
### Size Optimization
|
||||
- **Final size**: ~8.5 MB
|
||||
- **File count**: ~275 files
|
||||
- **Prebuilds**: Included for zero-build installation experience
|
||||
- **Source code**: Minimal, compiled assets only
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Local Development
|
||||
```bash
|
||||
# Multi-platform build with prebuilds (default)
|
||||
npm run build:npm
|
||||
|
||||
# Single-platform build for local testing
|
||||
node scripts/build-npm.js --current-only
|
||||
|
||||
# Test package locally
|
||||
npm pack
|
||||
|
||||
# Verify package contents
|
||||
tar -tzf vibetunnel-*.tgz | head -20
|
||||
```
|
||||
|
||||
### Quality Checks
|
||||
Always run before publishing:
|
||||
```bash
|
||||
pnpm run lint # Check code style
|
||||
pnpm run typecheck # Verify TypeScript
|
||||
```
|
||||
|
||||
## Publishing
|
||||
|
||||
### Prerequisites
|
||||
1. Update version in `package.json`
|
||||
2. Run multi-platform build
|
||||
3. Test package locally
|
||||
4. Verify all prebuilds are included
|
||||
|
||||
### Publish Command
|
||||
```bash
|
||||
npm publish
|
||||
```
|
||||
|
||||
## Usage After Installation
|
||||
|
||||
### Installation
|
||||
```bash
|
||||
# Install globally
|
||||
npm install -g vibetunnel
|
||||
```
|
||||
|
||||
### Starting the Server
|
||||
```bash
|
||||
# Start with default settings (port 4020)
|
||||
vibetunnel
|
||||
|
||||
# Start with custom port
|
||||
vibetunnel --port 8080
|
||||
|
||||
# Start without authentication
|
||||
vibetunnel --no-auth
|
||||
```
|
||||
|
||||
Then open http://localhost:4020 in your browser to access the web interface.
|
||||
|
||||
### Using the vt Command
|
||||
```bash
|
||||
# Monitor AI agents with automatic activity tracking
|
||||
vt claude
|
||||
vt claude --dangerously-skip-permissions
|
||||
|
||||
# Run commands with output visible in VibeTunnel
|
||||
vt npm test
|
||||
vt python script.py
|
||||
vt top
|
||||
|
||||
# Launch interactive shell
|
||||
vt --shell
|
||||
vt -i
|
||||
|
||||
# Update session title (inside a session)
|
||||
vt title "My Project"
|
||||
```
|
||||
|
||||
### Command Forwarding
|
||||
```bash
|
||||
# Basic usage
|
||||
vibetunnel fwd <session-id> <command> [args...]
|
||||
|
||||
# Examples
|
||||
vibetunnel fwd --session-id abc123 ls -la
|
||||
vibetunnel fwd --session-id abc123 npm test
|
||||
vibetunnel fwd --session-id abc123 python script.py
|
||||
```
|
||||
|
||||
## Coexistence with Mac App
|
||||
|
||||
The npm package works seamlessly alongside the Mac app:
|
||||
|
||||
### Command Routing
|
||||
- The `vt` command from npm automatically detects if the Mac app is installed
|
||||
- If Mac app found at `/Applications/VibeTunnel.app`, npm `vt` defers to it
|
||||
- Ensures you always get the best available implementation
|
||||
|
||||
### Installation Behavior
|
||||
- Won't overwrite existing `/usr/local/bin/vt` from other tools
|
||||
- Provides helpful warnings if conflicts exist
|
||||
- Installation always succeeds, even if `vt` symlink can't be created
|
||||
- Use `vibetunnel` or `npx vt` as alternatives
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
#### Missing Build Tools
|
||||
**Error**: `gyp ERR! stack Error: not found: make`
|
||||
**Solution**: Install build tools:
|
||||
- **macOS**: `xcode-select --install`
|
||||
- **Linux**: `apt-get install build-essential`
|
||||
|
||||
#### Missing PAM Development Libraries
|
||||
**Error**: `fatal error: security/pam_appl.h: No such file or directory`
|
||||
**Solution**: Install PAM development libraries:
|
||||
- **Linux**: `apt-get install libpam0g-dev`
|
||||
- **macOS**: Usually available by default
|
||||
|
||||
#### Docker Not Available
|
||||
**Error**: `Docker is required for multi-platform builds`
|
||||
**Solution**: Install Docker using OrbStack or Docker Desktop
|
||||
|
||||
#### Prebuild Download Failures
|
||||
**Error**: `prebuild-install warn install No prebuilt binaries found`
|
||||
**Cause**: Network issues or unsupported platform/Node version
|
||||
**Result**: Automatic fallback to source compilation
|
||||
|
||||
### Debugging Installation
|
||||
```bash
|
||||
# Verbose npm install
|
||||
npm install -g vibetunnel --verbose
|
||||
|
||||
# Check prebuild availability
|
||||
npx prebuild-install --list
|
||||
|
||||
# Force source compilation
|
||||
npm install -g vibetunnel --build-from-source
|
||||
```
|
||||
|
||||
## Architecture Decisions
|
||||
|
||||
### Why Prebuilds?
|
||||
- **User experience**: No build tools required for most users
|
||||
- **Installation speed**: Pre-compiled binaries install much faster
|
||||
- **Reliability**: Eliminates compilation errors in user environments
|
||||
- **Cross-platform**: Supports all target platforms without user setup
|
||||
|
||||
### Why Docker for Linux Builds?
|
||||
- **Cross-compilation**: Build Linux binaries from macOS development machine
|
||||
- **Consistency**: Reproducible build environment
|
||||
- **Dependencies**: Proper PAM library versions for Linux
|
||||
|
||||
### Why Vendored node-pty?
|
||||
- **Control**: Custom modifications for VibeTunnel's needs
|
||||
- **Reliability**: Avoid external dependency issues
|
||||
- **Optimization**: Minimal implementation without unnecessary features
|
||||
|
||||
## Related Files
|
||||
|
||||
- `scripts/build-npm.js` - Unified npm build process with multi-platform support
|
||||
- `scripts/postinstall-npm.js` - Fallback compilation logic
|
||||
- `.prebuildrc` - Prebuild configuration for target platforms
|
||||
- `package.json` - Package configuration and file inclusions
|
||||
|
|
@ -15,6 +15,7 @@
|
|||
"devDependencies": {
|
||||
"@types/node": "^24.0.3",
|
||||
"node-gyp": "^11.0.0",
|
||||
"prebuild": "^13.0.1",
|
||||
"rimraf": "^5.0.5",
|
||||
"typescript": "^5.8.3"
|
||||
},
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,10 +1,35 @@
|
|||
{
|
||||
"name": "@vibetunnel/vibetunnel-cli",
|
||||
"name": "vibetunnel",
|
||||
"version": "1.0.0-beta.10",
|
||||
"description": "Web frontend for terminal multiplexer",
|
||||
"main": "dist/server.js",
|
||||
"description": "Terminal sharing server with web interface - supports macOS, Linux, and headless environments",
|
||||
"main": "dist/server/server.js",
|
||||
"bin": {
|
||||
"vibetunnel": "./dist/vibetunnel-cli"
|
||||
"vibetunnel": "./bin/vibetunnel"
|
||||
},
|
||||
"files": [
|
||||
"dist/",
|
||||
"public/",
|
||||
"bin/",
|
||||
"node-pty/lib/",
|
||||
"node-pty/package.json",
|
||||
"prebuilds/",
|
||||
"README.md"
|
||||
],
|
||||
"os": [
|
||||
"darwin",
|
||||
"linux"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=20.0.0"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/amantus-ai/vibetunnel.git",
|
||||
"directory": "web"
|
||||
},
|
||||
"homepage": "https://vibetunnel.sh",
|
||||
"bugs": {
|
||||
"url": "https://github.com/amantus-ai/vibetunnel/issues"
|
||||
},
|
||||
"scripts": {
|
||||
"clean": "node scripts/clean.js",
|
||||
|
|
@ -13,7 +38,11 @@
|
|||
"dev:client": "node scripts/dev.js --client-only",
|
||||
"build": "node scripts/build.js",
|
||||
"build:ci": "node scripts/build-ci.js",
|
||||
"build:npm": "node scripts/build-npm.js",
|
||||
"prepublishOnly": "npm run build:npm",
|
||||
"postinstall": "node scripts/ensure-native-modules.js",
|
||||
"prebuild": "echo 'Skipping prebuild - handled by build-npm.js'",
|
||||
"prebuild:upload": "echo 'Skipping prebuild:upload - not used'",
|
||||
"lint": "concurrently -n biome,tsc-server,tsc-client,tsc-sw \"biome check src\" \"tsc --noEmit --project tsconfig.server.json\" \"tsc --noEmit --project tsconfig.client.json\" \"tsc --noEmit --project tsconfig.sw.json\"",
|
||||
"lint:fix": "biome check src --write",
|
||||
"lint:biome": "biome check src",
|
||||
|
|
|
|||
601
web/scripts/build-npm.js
Executable file
601
web/scripts/build-npm.js
Executable file
|
|
@ -0,0 +1,601 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Unified npm build script for VibeTunnel
|
||||
* Builds for all platforms by default with complete prebuild support
|
||||
*
|
||||
* Options:
|
||||
* --current-only Build for current platform/arch only (legacy mode)
|
||||
* --no-docker Skip Docker builds (Linux builds will be skipped)
|
||||
* --platform <os> Build for specific platform (darwin, linux)
|
||||
* --arch <arch> Build for specific architecture (x64, arm64)
|
||||
*/
|
||||
|
||||
const { execSync, spawn } = require('child_process');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const NODE_VERSIONS = ['20', '22', '23', '24'];
|
||||
const ALL_PLATFORMS = {
|
||||
darwin: ['x64', 'arm64'],
|
||||
linux: ['x64', 'arm64']
|
||||
};
|
||||
|
||||
// Map Node.js versions to ABI versions
|
||||
function getNodeAbi(nodeVersion) {
|
||||
const abiMap = {
|
||||
'20': '115',
|
||||
'22': '127',
|
||||
'23': '131',
|
||||
'24': '134'
|
||||
};
|
||||
return abiMap[nodeVersion];
|
||||
}
|
||||
|
||||
// Parse command line arguments
|
||||
const args = process.argv.slice(2);
|
||||
const currentOnly = args.includes('--current-only');
|
||||
const noDocker = args.includes('--no-docker');
|
||||
const platformFilter = args.find(arg => arg.startsWith('--platform'))?.split('=')[1] ||
|
||||
(args.includes('--platform') ? args[args.indexOf('--platform') + 1] : null);
|
||||
const archFilter = args.find(arg => arg.startsWith('--arch'))?.split('=')[1] ||
|
||||
(args.includes('--arch') ? args[args.indexOf('--arch') + 1] : null);
|
||||
|
||||
let PLATFORMS = ALL_PLATFORMS;
|
||||
|
||||
if (currentOnly) {
|
||||
// Legacy mode: current platform/arch only
|
||||
PLATFORMS = { [process.platform]: [process.arch] };
|
||||
} else {
|
||||
// Apply filters
|
||||
if (platformFilter) {
|
||||
PLATFORMS = { [platformFilter]: ALL_PLATFORMS[platformFilter] || [] };
|
||||
}
|
||||
if (archFilter) {
|
||||
PLATFORMS = Object.fromEntries(
|
||||
Object.entries(PLATFORMS).map(([platform, archs]) => [
|
||||
platform,
|
||||
archs.filter(arch => arch === archFilter)
|
||||
])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('🚀 Building VibeTunnel for npm distribution...\n');
|
||||
|
||||
if (currentOnly) {
|
||||
console.log(`📦 Legacy mode: Building for ${process.platform}/${process.arch} only\n`);
|
||||
} else {
|
||||
console.log('🌐 Multi-platform mode: Building for all supported platforms\n');
|
||||
console.log('Target platforms:', Object.entries(PLATFORMS)
|
||||
.map(([platform, archs]) => `${platform}(${archs.join(',')})`)
|
||||
.join(', '));
|
||||
console.log('');
|
||||
}
|
||||
|
||||
// Check if Docker is available for Linux builds
|
||||
function checkDocker() {
|
||||
try {
|
||||
execSync('docker --version', { stdio: 'pipe' });
|
||||
return true;
|
||||
} catch (e) {
|
||||
if (PLATFORMS.linux && !noDocker) {
|
||||
console.error('❌ Docker is required for Linux builds but is not installed.');
|
||||
console.error('Please install Docker using one of these options:');
|
||||
console.error(' - OrbStack (recommended): https://orbstack.dev/');
|
||||
console.error(' - Docker Desktop: https://www.docker.com/products/docker-desktop/');
|
||||
console.error(' - Use --no-docker to skip Linux builds');
|
||||
process.exit(1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Build for macOS locally
|
||||
function buildMacOS() {
|
||||
console.log('🍎 Building macOS binaries locally...\n');
|
||||
|
||||
// First ensure prebuild is available
|
||||
try {
|
||||
execSync('npx prebuild --version', { stdio: 'pipe' });
|
||||
} catch (e) {
|
||||
console.log(' Installing prebuild dependencies...');
|
||||
execSync('npm install', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
// Build node-pty
|
||||
console.log(' Building node-pty...');
|
||||
const nodePtyDir = path.join(__dirname, '..', 'node-pty');
|
||||
|
||||
for (const nodeVersion of NODE_VERSIONS) {
|
||||
for (const arch of PLATFORMS.darwin || []) {
|
||||
console.log(` → node-pty for Node.js ${nodeVersion} ${arch}`);
|
||||
try {
|
||||
execSync(`npx prebuild --runtime node --target ${nodeVersion}.0.0 --arch ${arch}`, {
|
||||
cwd: nodePtyDir,
|
||||
stdio: 'pipe'
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(` ❌ Failed to build node-pty for Node.js ${nodeVersion} ${arch}`);
|
||||
console.error(` Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Build universal spawn-helper binaries for macOS
|
||||
console.log(' Building universal spawn-helper binaries...');
|
||||
const spawnHelperSrc = path.join(nodePtyDir, 'src', 'unix', 'spawn-helper.cc');
|
||||
const tempDir = path.join(__dirname, '..', 'temp-spawn-helper');
|
||||
|
||||
// Ensure temp directory exists
|
||||
if (!fs.existsSync(tempDir)) {
|
||||
fs.mkdirSync(tempDir, { recursive: true });
|
||||
}
|
||||
|
||||
try {
|
||||
// Build for x64
|
||||
console.log(` → spawn-helper for x64`);
|
||||
execSync(`clang++ -arch x86_64 -o ${tempDir}/spawn-helper-x64 ${spawnHelperSrc}`, {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
// Build for arm64
|
||||
console.log(` → spawn-helper for arm64`);
|
||||
execSync(`clang++ -arch arm64 -o ${tempDir}/spawn-helper-arm64 ${spawnHelperSrc}`, {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
// Create universal binary
|
||||
console.log(` → Creating universal spawn-helper binary`);
|
||||
execSync(`lipo -create ${tempDir}/spawn-helper-x64 ${tempDir}/spawn-helper-arm64 -output ${tempDir}/spawn-helper-universal`, {
|
||||
stdio: 'pipe'
|
||||
});
|
||||
|
||||
// Add universal spawn-helper to each macOS prebuild
|
||||
for (const nodeVersion of NODE_VERSIONS) {
|
||||
for (const arch of PLATFORMS.darwin || []) {
|
||||
const prebuildFile = path.join(nodePtyDir, 'prebuilds', `node-pty-v1.0.0-node-v${getNodeAbi(nodeVersion)}-darwin-${arch}.tar.gz`);
|
||||
if (fs.existsSync(prebuildFile)) {
|
||||
console.log(` → Adding spawn-helper to ${path.basename(prebuildFile)}`);
|
||||
|
||||
// Extract existing prebuild
|
||||
const extractDir = path.join(tempDir, `extract-${nodeVersion}-${arch}`);
|
||||
if (fs.existsSync(extractDir)) {
|
||||
fs.rmSync(extractDir, { recursive: true, force: true });
|
||||
}
|
||||
fs.mkdirSync(extractDir, { recursive: true });
|
||||
|
||||
execSync(`tar -xzf ${prebuildFile} -C ${extractDir}`, { stdio: 'pipe' });
|
||||
|
||||
// Copy universal spawn-helper
|
||||
fs.copyFileSync(`${tempDir}/spawn-helper-universal`, `${extractDir}/build/Release/spawn-helper`);
|
||||
fs.chmodSync(`${extractDir}/build/Release/spawn-helper`, '755');
|
||||
|
||||
// Repackage prebuild
|
||||
execSync(`tar -czf ${prebuildFile} -C ${extractDir} .`, { stdio: 'pipe' });
|
||||
|
||||
// Clean up extract directory
|
||||
fs.rmSync(extractDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clean up temp directory
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
console.log(' ✅ Universal spawn-helper binaries created and added to prebuilds');
|
||||
|
||||
} catch (error) {
|
||||
console.error(` ❌ Failed to build universal spawn-helper: ${error.message}`);
|
||||
// Clean up on error
|
||||
if (fs.existsSync(tempDir)) {
|
||||
fs.rmSync(tempDir, { recursive: true, force: true });
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Build authenticate-pam
|
||||
console.log(' Building authenticate-pam...');
|
||||
const authenticatePamDir = path.join(__dirname, '..', 'node_modules', '.pnpm', 'authenticate-pam@1.0.5', 'node_modules', 'authenticate-pam');
|
||||
|
||||
for (const nodeVersion of NODE_VERSIONS) {
|
||||
for (const arch of PLATFORMS.darwin || []) {
|
||||
console.log(` → authenticate-pam for Node.js ${nodeVersion} ${arch}`);
|
||||
try {
|
||||
execSync(`npx prebuild --runtime node --target ${nodeVersion}.0.0 --arch ${arch} --tag-prefix authenticate-pam-v`, {
|
||||
cwd: authenticatePamDir,
|
||||
stdio: 'pipe',
|
||||
env: { ...process.env, npm_config_target_platform: 'darwin', npm_config_target_arch: arch }
|
||||
});
|
||||
} catch (error) {
|
||||
console.error(` ❌ Failed to build authenticate-pam for Node.js ${nodeVersion} ${arch}`);
|
||||
console.error(` Error: ${error.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✅ macOS builds completed\n');
|
||||
}
|
||||
|
||||
// Build for Linux using Docker
|
||||
function buildLinux() {
|
||||
console.log('🐧 Building Linux binaries using Docker...\n');
|
||||
|
||||
const dockerScript = `
|
||||
set -e
|
||||
export CI=true
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
|
||||
# Install dependencies including cross-compilation tools
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq python3 make g++ git libpam0g-dev gcc-aarch64-linux-gnu g++-aarch64-linux-gnu
|
||||
|
||||
# Add ARM64 architecture for cross-compilation
|
||||
dpkg --add-architecture arm64
|
||||
apt-get update -qq
|
||||
apt-get install -y -qq libpam0g-dev:arm64
|
||||
|
||||
# Install pnpm
|
||||
npm install -g pnpm --force --no-frozen-lockfile
|
||||
|
||||
# Install dependencies
|
||||
cd /workspace
|
||||
pnpm install --force --no-frozen-lockfile
|
||||
|
||||
# Build node-pty for Linux
|
||||
cd /workspace/node-pty
|
||||
for node_version in ${NODE_VERSIONS.join(' ')}; do
|
||||
for arch in ${(PLATFORMS.linux || []).join(' ')}; do
|
||||
echo "Building node-pty for Node.js \$node_version \$arch"
|
||||
if [ "\$arch" = "arm64" ]; then
|
||||
export CC=aarch64-linux-gnu-gcc
|
||||
export CXX=aarch64-linux-gnu-g++
|
||||
export AR=aarch64-linux-gnu-ar
|
||||
export STRIP=aarch64-linux-gnu-strip
|
||||
export LINK=aarch64-linux-gnu-g++
|
||||
else
|
||||
unset CC CXX AR STRIP LINK
|
||||
fi
|
||||
npm_config_target_platform=linux npm_config_target_arch=\$arch \\
|
||||
npx prebuild --runtime node --target \$node_version.0.0 --arch \$arch || exit 1
|
||||
done
|
||||
done
|
||||
|
||||
# Build authenticate-pam for Linux
|
||||
cd /workspace/node_modules/.pnpm/authenticate-pam@1.0.5/node_modules/authenticate-pam
|
||||
for node_version in ${NODE_VERSIONS.join(' ')}; do
|
||||
for arch in ${(PLATFORMS.linux || []).join(' ')}; do
|
||||
echo "Building authenticate-pam for Node.js \$node_version \$arch"
|
||||
if [ "\$arch" = "arm64" ]; then
|
||||
export CC=aarch64-linux-gnu-gcc
|
||||
export CXX=aarch64-linux-gnu-g++
|
||||
export AR=aarch64-linux-gnu-ar
|
||||
export STRIP=aarch64-linux-gnu-strip
|
||||
export LINK=aarch64-linux-gnu-g++
|
||||
else
|
||||
unset CC CXX AR STRIP LINK
|
||||
fi
|
||||
npm_config_target_platform=linux npm_config_target_arch=\$arch \\
|
||||
npx prebuild --runtime node --target \$node_version.0.0 --arch \$arch --tag-prefix authenticate-pam-v || exit 1
|
||||
done
|
||||
done
|
||||
|
||||
echo "Linux builds completed successfully"
|
||||
`;
|
||||
|
||||
try {
|
||||
execSync(`docker run --rm --platform linux/amd64 -v "\${PWD}:/workspace" -w /workspace node:22-bookworm bash -c '${dockerScript}'`, {
|
||||
stdio: 'inherit',
|
||||
cwd: path.join(__dirname, '..')
|
||||
});
|
||||
console.log('✅ Linux builds completed\n');
|
||||
} catch (error) {
|
||||
console.error('❌ Linux build failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// Copy and merge all prebuilds
|
||||
function mergePrebuilds() {
|
||||
console.log('📦 Merging prebuilds...\n');
|
||||
|
||||
const rootPrebuildsDir = path.join(__dirname, '..', 'prebuilds');
|
||||
const nodePtyPrebuildsDir = path.join(__dirname, '..', 'node-pty', 'prebuilds');
|
||||
|
||||
// Ensure root prebuilds directory exists
|
||||
if (!fs.existsSync(rootPrebuildsDir)) {
|
||||
fs.mkdirSync(rootPrebuildsDir, { recursive: true });
|
||||
}
|
||||
|
||||
// Copy node-pty prebuilds
|
||||
if (fs.existsSync(nodePtyPrebuildsDir)) {
|
||||
console.log(' Copying node-pty prebuilds...');
|
||||
const nodePtyFiles = fs.readdirSync(nodePtyPrebuildsDir);
|
||||
for (const file of nodePtyFiles) {
|
||||
const srcPath = path.join(nodePtyPrebuildsDir, file);
|
||||
const destPath = path.join(rootPrebuildsDir, file);
|
||||
if (fs.statSync(srcPath).isFile()) {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
console.log(` → ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy authenticate-pam prebuilds
|
||||
const authenticatePamPrebuildsDir = path.join(__dirname, '..', 'node_modules', '.pnpm', 'authenticate-pam@1.0.5', 'node_modules', 'authenticate-pam', 'prebuilds');
|
||||
if (fs.existsSync(authenticatePamPrebuildsDir)) {
|
||||
console.log(' Copying authenticate-pam prebuilds...');
|
||||
const pamFiles = fs.readdirSync(authenticatePamPrebuildsDir);
|
||||
for (const file of pamFiles) {
|
||||
const srcPath = path.join(authenticatePamPrebuildsDir, file);
|
||||
const destPath = path.join(rootPrebuildsDir, file);
|
||||
if (fs.statSync(srcPath).isFile()) {
|
||||
fs.copyFileSync(srcPath, destPath);
|
||||
console.log(` → ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Count total prebuilds
|
||||
const allPrebuilds = fs.readdirSync(rootPrebuildsDir).filter(f => f.endsWith('.tar.gz'));
|
||||
const nodePtyCount = allPrebuilds.filter(f => f.startsWith('node-pty')).length;
|
||||
const pamCount = allPrebuilds.filter(f => f.startsWith('authenticate-pam')).length;
|
||||
|
||||
console.log(`✅ Merged prebuilds: ${nodePtyCount} node-pty + ${pamCount} authenticate-pam = ${allPrebuilds.length} total\n`);
|
||||
}
|
||||
|
||||
// Main build process
|
||||
async function main() {
|
||||
// Step 0: Temporarily modify package.json for npm packaging
|
||||
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
||||
const originalPackageJson = fs.readFileSync(packageJsonPath, 'utf8');
|
||||
const packageJson = JSON.parse(originalPackageJson);
|
||||
|
||||
// Store original postinstall
|
||||
const originalPostinstall = packageJson.scripts.postinstall;
|
||||
|
||||
// Set install script for npm package
|
||||
packageJson.scripts.install = 'prebuild-install || node scripts/postinstall-npm.js';
|
||||
delete packageJson.scripts.postinstall;
|
||||
|
||||
// Add prebuild dependencies for npm package only
|
||||
packageJson.dependencies['prebuild-install'] = '^7.1.3';
|
||||
|
||||
// Write modified package.json
|
||||
fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
|
||||
|
||||
// Restore original package.json on exit
|
||||
const restorePackageJson = () => {
|
||||
fs.writeFileSync(packageJsonPath, originalPackageJson);
|
||||
};
|
||||
process.on('exit', restorePackageJson);
|
||||
process.on('SIGINT', () => { restorePackageJson(); process.exit(1); });
|
||||
process.on('SIGTERM', () => { restorePackageJson(); process.exit(1); });
|
||||
|
||||
// Step 1: Standard build process (includes spawn-helper)
|
||||
console.log('1️⃣ Running standard build process...\n');
|
||||
try {
|
||||
execSync('node scripts/build.js', { stdio: 'inherit' });
|
||||
console.log('✅ Standard build completed\n');
|
||||
} catch (error) {
|
||||
console.error('❌ Standard build failed:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Step 2: Multi-platform native module builds (unless current-only)
|
||||
if (!currentOnly) {
|
||||
// Check Docker availability for Linux builds
|
||||
const hasDocker = checkDocker();
|
||||
|
||||
// Build for macOS if included in targets
|
||||
if (PLATFORMS.darwin && process.platform === 'darwin') {
|
||||
buildMacOS();
|
||||
} else if (PLATFORMS.darwin && process.platform !== 'darwin') {
|
||||
console.log('⚠️ Skipping macOS builds (not running on macOS)\n');
|
||||
}
|
||||
|
||||
// Build for Linux if included in targets
|
||||
if (PLATFORMS.linux && hasDocker && !noDocker) {
|
||||
buildLinux();
|
||||
} else if (PLATFORMS.linux) {
|
||||
console.log('⚠️ Skipping Linux builds (Docker not available or --no-docker specified)\n');
|
||||
}
|
||||
|
||||
// Merge all prebuilds
|
||||
mergePrebuilds();
|
||||
}
|
||||
|
||||
// Step 3: Ensure node-pty is built for current platform
|
||||
console.log('3️⃣ Ensuring node-pty is built for current platform...\n');
|
||||
const nodePtyBuild = path.join(__dirname, '..', 'node-pty', 'build', 'Release', 'pty.node');
|
||||
if (!fs.existsSync(nodePtyBuild)) {
|
||||
console.log(' Building node-pty for current platform...');
|
||||
const nodePtyDir = path.join(__dirname, '..', 'node-pty');
|
||||
try {
|
||||
execSync('npm run install', { cwd: nodePtyDir, stdio: 'inherit' });
|
||||
console.log('✅ node-pty built successfully');
|
||||
} catch (error) {
|
||||
console.error('❌ Failed to build node-pty:', error.message);
|
||||
process.exit(1);
|
||||
}
|
||||
} else {
|
||||
console.log('✅ node-pty already built');
|
||||
}
|
||||
|
||||
// Step 4: Create package-specific README
|
||||
console.log('\n4️⃣ Creating npm package README...\n');
|
||||
const readmeContent = `# VibeTunnel CLI
|
||||
|
||||
Full-featured terminal sharing server with web interface for macOS and Linux. Windows not yet supported.
|
||||
|
||||
## Installation
|
||||
|
||||
\`\`\`bash
|
||||
npm install -g vibetunnel
|
||||
\`\`\`
|
||||
|
||||
## Requirements
|
||||
|
||||
- Node.js >= 20.0.0
|
||||
- macOS or Linux (Windows not yet supported)
|
||||
- Build tools for native modules (Xcode on macOS, build-essential on Linux)
|
||||
|
||||
## Usage
|
||||
|
||||
### Start the server
|
||||
|
||||
\`\`\`bash
|
||||
# Start with default settings (port 4020)
|
||||
vibetunnel
|
||||
|
||||
# Start with custom port
|
||||
vibetunnel --port 8080
|
||||
|
||||
# Start without authentication
|
||||
vibetunnel --no-auth
|
||||
\`\`\`
|
||||
|
||||
Then open http://localhost:4020 in your browser to access the web interface.
|
||||
|
||||
### Use the vt command wrapper
|
||||
|
||||
The \`vt\` command allows you to run commands with TTY forwarding:
|
||||
|
||||
\`\`\`bash
|
||||
# Monitor AI agents with automatic activity tracking
|
||||
vt claude
|
||||
vt claude --dangerously-skip-permissions
|
||||
|
||||
# Run commands with output visible in VibeTunnel
|
||||
vt npm test
|
||||
vt python script.py
|
||||
vt top
|
||||
|
||||
# Launch interactive shell
|
||||
vt --shell
|
||||
vt -i
|
||||
|
||||
# Update session title (inside a session)
|
||||
vt title "My Project"
|
||||
\`\`\`
|
||||
|
||||
### Forward commands to a session
|
||||
|
||||
\`\`\`bash
|
||||
# Basic usage
|
||||
vibetunnel fwd <session-id> <command> [args...]
|
||||
|
||||
# Examples
|
||||
vibetunnel fwd --session-id abc123 ls -la
|
||||
vibetunnel fwd --session-id abc123 npm test
|
||||
vibetunnel fwd --session-id abc123 python script.py
|
||||
\`\`\`
|
||||
|
||||
## Features
|
||||
|
||||
- **Web-based terminal interface** - Access terminals from any browser
|
||||
- **Multiple concurrent sessions** - Run multiple terminals simultaneously
|
||||
- **Real-time synchronization** - See output in real-time
|
||||
- **TTY forwarding** - Full terminal emulation support
|
||||
- **Session management** - Create, list, and manage sessions
|
||||
- **Cross-platform** - Works on macOS and Linux
|
||||
- **No dependencies** - Just Node.js required
|
||||
|
||||
## Package Contents
|
||||
|
||||
This npm package includes:
|
||||
- Full VibeTunnel server with web UI
|
||||
- Command-line tools (vibetunnel, vt)
|
||||
- Native PTY support for terminal emulation
|
||||
- Web interface with xterm.js
|
||||
- Session management and forwarding
|
||||
|
||||
## Platform Support
|
||||
|
||||
- macOS (Intel and Apple Silicon)
|
||||
- Linux (x64 and ARM64)
|
||||
- Windows: Not yet supported ([#252](https://github.com/amantus-ai/vibetunnel/issues/252))
|
||||
|
||||
## Documentation
|
||||
|
||||
See the main repository for complete documentation: https://github.com/amantus-ai/vibetunnel
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
`;
|
||||
|
||||
const readmePath = path.join(__dirname, '..', 'README.md');
|
||||
fs.writeFileSync(readmePath, readmeContent);
|
||||
console.log('✅ npm README created');
|
||||
|
||||
// Step 5: Clean up test files (keep screencap.js - it's needed)
|
||||
console.log('\n5️⃣ Cleaning up test files...\n');
|
||||
const testFiles = [
|
||||
'public/bundle/test.js',
|
||||
'public/test' // Remove entire test directory
|
||||
];
|
||||
|
||||
for (const file of testFiles) {
|
||||
const filePath = path.join(__dirname, '..', file);
|
||||
if (fs.existsSync(filePath)) {
|
||||
if (fs.statSync(filePath).isDirectory()) {
|
||||
fs.rmSync(filePath, { recursive: true, force: true });
|
||||
console.log(` Removed directory: ${file}`);
|
||||
} else {
|
||||
fs.unlinkSync(filePath);
|
||||
console.log(` Removed file: ${file}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Step 6: Show final package info
|
||||
console.log('\n6️⃣ Package summary...\n');
|
||||
|
||||
// Calculate total size
|
||||
function getDirectorySize(dirPath) {
|
||||
let totalSize = 0;
|
||||
const items = fs.readdirSync(dirPath);
|
||||
|
||||
for (const item of items) {
|
||||
const itemPath = path.join(dirPath, item);
|
||||
const stats = fs.statSync(itemPath);
|
||||
|
||||
if (stats.isFile()) {
|
||||
totalSize += stats.size;
|
||||
} else if (stats.isDirectory()) {
|
||||
totalSize += getDirectorySize(itemPath);
|
||||
}
|
||||
}
|
||||
|
||||
return totalSize;
|
||||
}
|
||||
|
||||
const packageRoot = path.join(__dirname, '..');
|
||||
const totalSize = getDirectorySize(packageRoot);
|
||||
const sizeMB = (totalSize / 1024 / 1024).toFixed(1);
|
||||
|
||||
console.log(`📦 Package size: ${sizeMB} MB`);
|
||||
|
||||
if (!currentOnly) {
|
||||
const prebuildsDir = path.join(__dirname, '..', 'prebuilds');
|
||||
if (fs.existsSync(prebuildsDir)) {
|
||||
const prebuildFiles = fs.readdirSync(prebuildsDir).filter(f => f.endsWith('.tar.gz'));
|
||||
console.log(`🔧 Prebuilds: ${prebuildFiles.length} binaries included`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n🎉 npm package build completed successfully!');
|
||||
console.log('\nNext steps:');
|
||||
console.log(' - Test locally: npm pack');
|
||||
console.log(' - Publish: npm publish');
|
||||
|
||||
// Restore original package.json
|
||||
restorePackageJson();
|
||||
}
|
||||
|
||||
main().catch(error => {
|
||||
console.error('❌ Build failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -64,7 +64,7 @@ async function build() {
|
|||
|
||||
// Build server TypeScript
|
||||
console.log('Building server...');
|
||||
execSync('tsc', { stdio: 'inherit' });
|
||||
execSync('npx tsc', { stdio: 'inherit' });
|
||||
|
||||
// Bundle CLI
|
||||
console.log('Bundling CLI...');
|
||||
|
|
|
|||
34
web/scripts/docker-build-test.sh
Executable file
34
web/scripts/docker-build-test.sh
Executable file
|
|
@ -0,0 +1,34 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Installing build dependencies..."
|
||||
apt-get update && apt-get install -y python3 make g++ git
|
||||
|
||||
echo "Setting up project..."
|
||||
cd /workspace
|
||||
|
||||
# Fix npm permissions issue in Docker
|
||||
mkdir -p ~/.npm
|
||||
chown -R $(id -u):$(id -g) ~/.npm
|
||||
|
||||
# Install pnpm using corepack (more reliable)
|
||||
corepack enable
|
||||
corepack prepare pnpm@latest --activate
|
||||
|
||||
# Install dependencies
|
||||
cd /workspace
|
||||
pnpm install --ignore-scripts --no-frozen-lockfile
|
||||
|
||||
# Go to node-pty directory
|
||||
cd node-pty
|
||||
|
||||
# Install prebuild locally in node-pty
|
||||
pnpm add -D prebuild
|
||||
|
||||
# Build for Node.js 20
|
||||
echo "Building for Node.js 20..."
|
||||
./node_modules/.bin/prebuild --runtime node --target 20.0.0
|
||||
|
||||
# List results
|
||||
echo "Build complete. Prebuilds:"
|
||||
ls -la prebuilds/
|
||||
144
web/scripts/postinstall-npm.js
Executable file
144
web/scripts/postinstall-npm.js
Executable file
|
|
@ -0,0 +1,144 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
/**
|
||||
* Postinstall script for npm package
|
||||
* Fallback build script when prebuild-install fails
|
||||
*/
|
||||
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
console.log('Setting up native modules for VibeTunnel...');
|
||||
|
||||
// Check if we're in development (has src directory) or npm install
|
||||
const isDevelopment = fs.existsSync(path.join(__dirname, '..', 'src'));
|
||||
|
||||
if (isDevelopment) {
|
||||
// In development, run the existing ensure-native-modules script
|
||||
require('./ensure-native-modules.js');
|
||||
return;
|
||||
}
|
||||
|
||||
// Try prebuild-install first for each module
|
||||
const tryPrebuildInstall = (name, dir) => {
|
||||
console.log(`Trying prebuild-install for ${name}...`);
|
||||
try {
|
||||
execSync('prebuild-install', {
|
||||
cwd: dir,
|
||||
stdio: 'inherit',
|
||||
env: { ...process.env, npm_config_cache: path.join(require('os').homedir(), '.npm') }
|
||||
});
|
||||
console.log(`✓ ${name} prebuilt binary installed`);
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log(` No prebuilt binary available for ${name}, will compile from source`);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Handle both native modules with prebuild-install fallback
|
||||
const modules = [
|
||||
{
|
||||
name: 'node-pty',
|
||||
dir: path.join(__dirname, '..', 'node-pty'),
|
||||
build: path.join(__dirname, '..', 'node-pty', 'build', 'Release', 'pty.node'),
|
||||
essential: true
|
||||
},
|
||||
{
|
||||
name: 'authenticate-pam',
|
||||
dir: path.join(__dirname, '..', 'node_modules', 'authenticate-pam'),
|
||||
build: path.join(__dirname, '..', 'node_modules', 'authenticate-pam', 'build', 'Release', 'authenticate_pam.node'),
|
||||
essential: false
|
||||
}
|
||||
];
|
||||
|
||||
let hasErrors = false;
|
||||
|
||||
for (const module of modules) {
|
||||
if (!fs.existsSync(module.build)) {
|
||||
// First try prebuild-install
|
||||
const prebuildSuccess = tryPrebuildInstall(module.name, module.dir);
|
||||
|
||||
if (!prebuildSuccess) {
|
||||
// Fall back to compilation
|
||||
console.log(`Building ${module.name} from source...`);
|
||||
try {
|
||||
execSync('node-gyp rebuild', {
|
||||
cwd: module.dir,
|
||||
stdio: 'inherit'
|
||||
});
|
||||
console.log(`✓ ${module.name} built successfully`);
|
||||
} catch (error) {
|
||||
console.error(`Failed to build ${module.name}:`, error.message);
|
||||
if (module.essential) {
|
||||
console.error(`${module.name} is required for VibeTunnel to function.`);
|
||||
console.error('You may need to install build tools for your platform:');
|
||||
console.error('- macOS: Install Xcode Command Line Tools');
|
||||
console.error('- Linux: Install build-essential package');
|
||||
hasErrors = true;
|
||||
} else {
|
||||
console.warn(`Warning: ${module.name} build failed. Some features may be limited.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
console.log(`✓ ${module.name} already available`);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasErrors) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// Conditionally install vt symlink
|
||||
if (!isDevelopment) {
|
||||
try {
|
||||
// Find npm's global bin directory
|
||||
const npmBinDir = execSync('npm bin -g', { encoding: 'utf8' }).trim();
|
||||
const vtTarget = path.join(npmBinDir, 'vt');
|
||||
const vtSource = path.join(__dirname, '..', 'bin', 'vt');
|
||||
|
||||
// Check if vt already exists
|
||||
if (fs.existsSync(vtTarget)) {
|
||||
// Check if it's already our symlink
|
||||
try {
|
||||
const stats = fs.lstatSync(vtTarget);
|
||||
if (stats.isSymbolicLink()) {
|
||||
const linkTarget = fs.readlinkSync(vtTarget);
|
||||
if (linkTarget.includes('vibetunnel')) {
|
||||
console.log('✓ vt command already installed (VibeTunnel)');
|
||||
} else {
|
||||
console.log('⚠️ vt command already exists (different tool)');
|
||||
console.log(' Use "vibetunnel" command or "npx vt" instead');
|
||||
}
|
||||
} else {
|
||||
console.log('⚠️ vt command already exists (not a symlink)');
|
||||
console.log(' Use "vibetunnel" command instead');
|
||||
}
|
||||
} catch (e) {
|
||||
// Ignore errors checking the existing file
|
||||
console.log('⚠️ vt command already exists');
|
||||
console.log(' Use "vibetunnel" command instead');
|
||||
}
|
||||
} else {
|
||||
// Create the symlink
|
||||
try {
|
||||
fs.symlinkSync(vtSource, vtTarget);
|
||||
// Make it executable
|
||||
fs.chmodSync(vtTarget, '755');
|
||||
console.log('✓ vt command installed successfully');
|
||||
} catch (error) {
|
||||
console.warn('⚠️ Could not install vt command:', error.message);
|
||||
console.log(' Use "vibetunnel" command instead');
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
// If we can't determine npm bin dir or create symlink, just warn
|
||||
console.warn('⚠️ Could not install vt command:', error.message);
|
||||
console.log(' Use "vibetunnel" command instead');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('✓ VibeTunnel is ready to use');
|
||||
console.log('Run "vibetunnel --help" for usage information');
|
||||
55
web/scripts/test-docker-build.sh
Executable file
55
web/scripts/test-docker-build.sh
Executable file
|
|
@ -0,0 +1,55 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Testing Docker build for Linux x64..."
|
||||
|
||||
# Create the build script
|
||||
cat > docker-build-test.sh << 'EOF'
|
||||
#!/bin/bash
|
||||
set -e
|
||||
|
||||
echo "Installing build dependencies..."
|
||||
apt-get update && apt-get install -y python3 make g++ git
|
||||
|
||||
echo "Setting up project..."
|
||||
cd /workspace
|
||||
|
||||
# Fix npm permissions issue in Docker
|
||||
mkdir -p ~/.npm
|
||||
chown -R $(id -u):$(id -g) ~/.npm
|
||||
|
||||
# Install pnpm using corepack (more reliable)
|
||||
corepack enable
|
||||
corepack prepare pnpm@latest --activate
|
||||
|
||||
# Install dependencies
|
||||
cd /workspace
|
||||
pnpm install --ignore-scripts --no-frozen-lockfile
|
||||
|
||||
# Go to node-pty directory
|
||||
cd node-pty
|
||||
|
||||
# Install prebuild locally in node-pty
|
||||
pnpm add -D prebuild
|
||||
|
||||
# Build for Node.js 20
|
||||
echo "Building for Node.js 20..."
|
||||
./node_modules/.bin/prebuild --runtime node --target 20.0.0
|
||||
|
||||
# List results
|
||||
echo "Build complete. Prebuilds:"
|
||||
ls -la prebuilds/
|
||||
EOF
|
||||
|
||||
chmod +x docker-build-test.sh
|
||||
|
||||
# Run the test
|
||||
docker run --rm \
|
||||
-v "$(pwd)":/workspace \
|
||||
-w /workspace \
|
||||
--platform linux/amd64 \
|
||||
node:22-bookworm \
|
||||
/workspace/docker-build-test.sh
|
||||
|
||||
# Clean up
|
||||
rm docker-build-test.sh
|
||||
|
|
@ -1,20 +0,0 @@
|
|||
{"version":2,"width":80,"height":24}
|
||||
[0.353805917,"o","\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r\u001b]2;badlogic@Marios-MacBook-Pro:~\u0007"]
|
||||
[0.353852709,"o","\u001b]1;~\u0007"]
|
||||
[0.367397334,"o","\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[01;32m➜ \u001b[36m~\u001b[00m \u001b[K"]
|
||||
[0.367477375,"o","\u001b[?1h\u001b="]
|
||||
[0.367502292,"o","\u001b[?2004h"]
|
||||
[3.3307017500000002,"o","l"]
|
||||
[3.640558792,"o","\bls"]
|
||||
[3.970247959,"o","\u001b[?1l\u001b>"]
|
||||
[3.970351,"o","\u001b[?2004l\r\r\n"]
|
||||
[3.971092792,"o","\u001b]2;ls -G\u0007\u001b]1;ls\u0007"]
|
||||
[3.9785605840000002,"o","192.168.1.9\r\n\u001b[1m\u001b[36mApplications\u001b[39;49m\u001b[0m\r\n"]
|
||||
[3.978659209,"o","\u001b[1m\u001b[36mDataGripProjects\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mDesktop\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mDocuments\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mDownloads\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mLibrary\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mMovies\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mMusic\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mPictures\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mPublic\u001b[39;49m\u001b[0m\r\nRankers_Toreview.7z\r\n\u001b[1m\u001b[36mSupport\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mTemplates\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mUnrealEngine\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mVM.bundle\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mVulkanSDK\u001b[39;49m\u001b[0m\r\nandroid.webm\r\ncookies.txt\r\n\u001b[1m\u001b[36mdotTraceSnapshots\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mesp\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mfacebook-political-ads\u001b[39;49m\u001b[0m\r\nfirebase-service-credentials.json\r\n\u001b[1m\u001b[36mgo\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mgradle\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mjan\u001b[39;49m\u001b[0m\r\njcef_51747.log\r\njcef_77101.log\r\njcef_81453.log\r\n\u001b[1m\u001b[36mlol\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mnltk_data\u001b[39;49m\u001b[0m\r\noaJsApi.class.js.html\r\nout.mp4\r\noutput.html\r\npage.html\r\n"]
|
||||
[3.978693917,"o","proxy.js\r\nscene.html\r\nsearch?query=babler&fd=2023-01-01&td=2024-05-30&s=date&p=1\r\nsegment_ctaudio_ridp0aa0br191998_cinit_mpd.m4s\r\nsegment_ctaudio_ridp0aa0br191998_cs0_mpd.m4s\r\nsegment_ctvideo_ridp0va0br801408_cinit_mpd.m4s\r\nsegment_ctvideo_ridp0va0br801408_cs0_mpd.m4s\r\nsynology\r\ntest.json\r\ntest.ts\r\ntest.txt\r\n\u001b[1m\u001b[36mtools\u001b[39;49m\u001b[0m\r\n\u001b[1m\u001b[36mtty-fwd-control\u001b[39;49m\u001b[0m\r\ntwitteria-001-2024-03-05.wav\r\n\u001b[1m\u001b[36mworkspaces\u001b[39;49m\u001b[0m\r\n\u001b[31mx.sh\u001b[39;49m\u001b[0m\r\n"]
|
||||
[3.979061084,"o","\u001b[1m\u001b[7m%\u001b[27m\u001b[1m\u001b[0m \r \r"]
|
||||
[3.979122292,"o","\u001b]2;badlogic@Marios-MacBook-Pro:~\u0007"]
|
||||
[3.979145417,"o","\u001b]1;~\u0007"]
|
||||
[3.996851709,"o","\r\u001b[0m\u001b[27m\u001b[24m\u001b[J\u001b[01;32m➜ \u001b[36m~\u001b[00m \u001b[K"]
|
||||
[3.996903959,"o","\u001b[?1h\u001b="]
|
||||
[3.996924625,"o","\u001b[?2004h"]
|
||||
Loading…
Reference in a new issue