mirror of
https://github.com/samsonjs/vibetunnel.git
synced 2026-04-16 13:05:53 +00:00
Fix cross-platform build issues in web package
Replace shell commands that fail on Windows with Node.js scripts: - mkdir -p / cp -r → scripts/copy-assets.js - rm -rf → scripts/clean.js - Add scripts/ensure-dirs.js for directory creation This resolves "A subdirectory or file -p already exists" errors when running npm scripts on Windows with Git Bash. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
83a4bf0f75
commit
6ae43fbde8
5 changed files with 588 additions and 5 deletions
532
API.md
Normal file
532
API.md
Normal file
|
|
@ -0,0 +1,532 @@
|
|||
# VibeTunnel API Analysis
|
||||
|
||||
## Summary
|
||||
|
||||
This document analyzes the API endpoints implemented across all VibeTunnel servers, what the web client expects, and identifies critical differences, implementation errors, and semantic inconsistencies. The analysis covers:
|
||||
|
||||
1. **Node.js/TypeScript Server** (`web/src/server.ts`) - ✅ Complete
|
||||
2. **Rust API Server** (`tty-fwd/src/api_server.rs`) - ✅ Complete
|
||||
3. **Go Server** (`linux/pkg/api/server.go`) - ✅ Complete
|
||||
4. **Swift Server** (`VibeTunnel/Core/Services/TunnelServer.swift`) - ✅ Complete
|
||||
5. **Web Client** (`web/src/client/`) - Expected API calls and formats
|
||||
|
||||
**Note**: Rust HTTP Server (`tty-fwd/src/http_server.rs`) is excluded as it's a utility component for static file serving, not a standalone API server.
|
||||
|
||||
## API Endpoint Comparison
|
||||
|
||||
| Endpoint | Client Expects | Node.js | Rust API | Go | Swift | Status |
|
||||
|----------|----------------|---------|----------|----|---------| ------|
|
||||
| `GET /api/health` | ✅ Used | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `GET /api/sessions` | ✅ **Critical** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `POST /api/sessions` | ✅ **Critical** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `DELETE /api/sessions/:id` | ✅ **Critical** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `DELETE /api/sessions/:id/cleanup` | ❌ Not used | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `GET /api/sessions/:id/stream` | ✅ **Critical SSE** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `GET /api/sessions/:id/snapshot` | ✅ **Critical** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `POST /api/sessions/:id/input` | ✅ **Critical** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `POST /api/sessions/:id/resize` | ✅ **Critical** | ✅ | ❌ | ✅ | ❌ | ⚠️ **Missing in Rust API & Swift** |
|
||||
| `POST /api/cleanup-exited` | ✅ Used | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `GET /api/fs/browse` | ✅ **Critical** | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `POST /api/mkdir` | ✅ Used | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `GET /api/sessions/multistream` | ❌ Not used | ✅ | ✅ | ✅ | ✅ | ✅ **Complete** |
|
||||
| `GET /api/pty/status` | ❌ Not used | ✅ | ❌ | ❌ | ❌ | ℹ️ **Node.js Only** |
|
||||
| `GET /api/test-cast` | ❌ Not used | ✅ | ❌ | ❌ | ❌ | ℹ️ **Node.js Only** |
|
||||
| `POST /api/ngrok/start` | ❌ Not used | ❌ | ❌ | ✅ | ✅ | ℹ️ **Go/Swift Only** |
|
||||
| `POST /api/ngrok/stop` | ❌ Not used | ❌ | ❌ | ✅ | ✅ | ℹ️ **Go/Swift Only** |
|
||||
| `GET /api/ngrok/status` | ❌ Not used | ❌ | ❌ | ✅ | ✅ | ℹ️ **Go/Swift Only** |
|
||||
|
||||
## Web Client API Requirements
|
||||
|
||||
Based on analysis of `web/src/client/`, the client **requires** these endpoints to function:
|
||||
|
||||
### Critical Endpoints (App breaks without these):
|
||||
1. `GET /api/sessions` - Session list (polled every 3s)
|
||||
2. `POST /api/sessions` - Session creation
|
||||
3. `DELETE /api/sessions/:id` - Session termination
|
||||
4. `GET /api/sessions/:id/stream` - **SSE streaming** (real-time terminal output)
|
||||
5. `GET /api/sessions/:id/snapshot` - Terminal snapshot for initial display
|
||||
6. `POST /api/sessions/:id/input` - **Keyboard/mouse input** to terminal
|
||||
7. `POST /api/sessions/:id/resize` - **Terminal resize** (debounced, 250ms)
|
||||
8. `GET /api/fs/browse` - Directory browsing for session creation
|
||||
9. `POST /api/cleanup-exited` - Cleanup exited sessions
|
||||
|
||||
### Expected Request/Response Formats by Client:
|
||||
|
||||
#### Session List Response (GET /api/sessions):
|
||||
```typescript
|
||||
Session[] = {
|
||||
id: string;
|
||||
command: string;
|
||||
workingDir: string;
|
||||
name?: string;
|
||||
status: 'running' | 'exited';
|
||||
exitCode?: number;
|
||||
startedAt: string;
|
||||
lastModified: string;
|
||||
pid?: number;
|
||||
waiting?: boolean; // Node.js only
|
||||
width?: number; // Go only
|
||||
height?: number; // Go only
|
||||
}[]
|
||||
```
|
||||
|
||||
#### Session Creation Request (POST /api/sessions):
|
||||
```typescript
|
||||
{
|
||||
command: string[]; // Required: parsed command array
|
||||
workingDir: string; // Required: working directory path
|
||||
name?: string; // Optional: session name
|
||||
spawn_terminal?: boolean; // Used by Rust API/Swift (always true)
|
||||
width?: number; // Used by Go (default: 120)
|
||||
height?: number; // Used by Go (default: 30)
|
||||
}
|
||||
```
|
||||
|
||||
#### Session Input Request (POST /api/sessions/:id/input):
|
||||
```typescript
|
||||
{
|
||||
text: string; // Input text or special keys: 'enter', 'escape', 'arrow_up', etc.
|
||||
}
|
||||
```
|
||||
|
||||
#### Terminal Resize Request (POST /api/sessions/:id/resize):
|
||||
```typescript
|
||||
{
|
||||
width: number; // Terminal columns
|
||||
height: number; // Terminal rows
|
||||
}
|
||||
```
|
||||
|
||||
## Major Implementation Differences
|
||||
|
||||
### 1. **Server Implementation Status**
|
||||
|
||||
All API servers are **fully functional and complete**:
|
||||
|
||||
**Rust API Server** (`tty-fwd/src/api_server.rs`):
|
||||
- ✅ **Purpose**: Full terminal session management server
|
||||
- ✅ **APIs**: Complete implementation of all session endpoints
|
||||
- ✅ **Features**: Authentication, SSE streaming, file system APIs
|
||||
- ❌ **Missing**: Terminal resize endpoint only
|
||||
|
||||
**Architecture Note**: The Rust HTTP Server (`tty-fwd/src/http_server.rs`) is a utility component for static file serving and HTTP/SSE primitives, not a standalone API server. It's correctly excluded from this analysis.
|
||||
|
||||
### 2. **CRITICAL: Missing Terminal Resize API**
|
||||
|
||||
**Impact**: ⚠️ **Client expects this endpoint and calls it continuously**
|
||||
**Affected**: Rust API Server, Swift Server
|
||||
**Endpoints**: `POST /api/sessions/:id/resize`
|
||||
|
||||
**Client Behavior**:
|
||||
- Calls resize endpoint on window resize events (debounced 250ms)
|
||||
- Tracks last sent dimensions to avoid redundant requests
|
||||
- Logs warnings on failure but continues operation
|
||||
- **Will cause 404 errors** on Rust API and Swift servers
|
||||
|
||||
**Working Implementation Analysis**:
|
||||
|
||||
```javascript
|
||||
// Node.js Implementation (✅ Complete)
|
||||
app.post('/api/sessions/:sessionId/resize', async (req, res) => {
|
||||
const { width, height } = req.body;
|
||||
// Validation: 1-1000 range
|
||||
if (width < 1 || height < 1 || width > 1000 || height > 1000) {
|
||||
return res.status(400).json({ error: 'Width and height must be between 1 and 1000' });
|
||||
}
|
||||
ptyService.resizeSession(sessionId, width, height);
|
||||
});
|
||||
```
|
||||
|
||||
```go
|
||||
// Go Implementation (✅ Complete)
|
||||
func (s *Server) handleResizeSession(w http.ResponseWriter, r *http.Request) {
|
||||
// Includes validation for positive integers
|
||||
if req.Width <= 0 || req.Height <= 0 {
|
||||
http.Error(w, "Width and height must be positive integers", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Missing in**:
|
||||
- Rust API Server: No resize endpoint
|
||||
- Swift Server: No resize endpoint
|
||||
|
||||
### 3. **Session Creation Request Format Inconsistencies**
|
||||
|
||||
#### Node.js Format:
|
||||
```json
|
||||
{
|
||||
"command": ["bash", "-l"],
|
||||
"workingDir": "/path/to/dir",
|
||||
"name": "session_name"
|
||||
}
|
||||
```
|
||||
|
||||
#### Rust API Format:
|
||||
```json
|
||||
{
|
||||
"command": ["bash", "-l"],
|
||||
"workingDir": "/path/to/dir",
|
||||
"term": "xterm-256color",
|
||||
"spawn_terminal": true
|
||||
}
|
||||
```
|
||||
|
||||
#### Go Format:
|
||||
```json
|
||||
{
|
||||
"name": "session_name",
|
||||
"command": ["bash", "-l"],
|
||||
"workingDir": "/path/to/dir",
|
||||
"width": 120,
|
||||
"height": 30
|
||||
}
|
||||
```
|
||||
|
||||
#### Swift Format:
|
||||
```json
|
||||
{
|
||||
"command": ["bash", "-l"],
|
||||
"workingDir": "/path/to/dir",
|
||||
"term": "xterm-256color",
|
||||
"spawnTerminal": true
|
||||
}
|
||||
```
|
||||
|
||||
**Issues**:
|
||||
1. Inconsistent field naming (`workingDir` vs `working_dir`)
|
||||
2. Different optional fields across implementations
|
||||
3. Terminal dimensions only in Go implementation
|
||||
|
||||
### 4. **Authentication Implementation Differences**
|
||||
|
||||
| Server | Auth Method | Details |
|
||||
|--------|-------------|---------|
|
||||
| Node.js | None | No authentication middleware |
|
||||
| Rust API | Basic Auth | Configurable password, realm="tty-fwd" |
|
||||
| Go | Basic Auth | Fixed username "admin", realm="VibeTunnel" |
|
||||
| Swift | Basic Auth | Lazy keychain-based password loading |
|
||||
|
||||
**Problems**:
|
||||
1. Different realm names (`"tty-fwd"` vs `"VibeTunnel"`)
|
||||
2. Inconsistent username requirements
|
||||
3. Node.js completely lacks authentication
|
||||
|
||||
### 5. **Session Input Handling Inconsistencies**
|
||||
|
||||
#### Special Key Mappings Differ:
|
||||
|
||||
**Node.js**:
|
||||
```javascript
|
||||
const specialKeys = [
|
||||
'arrow_up', 'arrow_down', 'arrow_left', 'arrow_right',
|
||||
'escape', 'enter', 'ctrl_enter', 'shift_enter'
|
||||
];
|
||||
```
|
||||
|
||||
**Go**:
|
||||
```go
|
||||
specialKeys := map[string]string{
|
||||
"arrow_up": "\x1b[A",
|
||||
"arrow_down": "\x1b[B",
|
||||
"arrow_right": "\x1b[C",
|
||||
"arrow_left": "\x1b[D",
|
||||
"escape": "\x1b",
|
||||
"enter": "\r", // CR, not LF
|
||||
"ctrl_enter": "\r", // CR for ctrl+enter
|
||||
"shift_enter": "\x1b\x0d", // ESC + CR for shift+enter
|
||||
}
|
||||
```
|
||||
|
||||
**Swift**:
|
||||
```swift
|
||||
let specialKeys = [
|
||||
"arrow_up", "arrow_down", "arrow_left", "arrow_right",
|
||||
"escape", "enter", "ctrl_enter", "shift_enter"
|
||||
]
|
||||
```
|
||||
|
||||
**Issues**:
|
||||
1. Go provides explicit escape sequence mappings
|
||||
2. Node.js and Swift rely on PTY service for mapping
|
||||
3. Different enter key handling (`\r` vs `\n`)
|
||||
|
||||
### 6. **Session Response Format Inconsistencies**
|
||||
|
||||
#### Node.js Session List Response:
|
||||
```json
|
||||
{
|
||||
"id": "session-123",
|
||||
"command": "bash -l",
|
||||
"workingDir": "/home/user",
|
||||
"name": "my-session",
|
||||
"status": "running",
|
||||
"exitCode": null,
|
||||
"startedAt": "2024-01-01T00:00:00Z",
|
||||
"lastModified": "2024-01-01T00:01:00Z",
|
||||
"pid": 1234,
|
||||
"waiting": false
|
||||
}
|
||||
```
|
||||
|
||||
#### Rust API Session List Response:
|
||||
```json
|
||||
{
|
||||
"id": "session-123",
|
||||
"command": "bash -l",
|
||||
"workingDir": "/home/user",
|
||||
"status": "running",
|
||||
"exitCode": null,
|
||||
"startedAt": "2024-01-01T00:00:00Z",
|
||||
"lastModified": "2024-01-01T00:01:00Z",
|
||||
"pid": 1234
|
||||
}
|
||||
```
|
||||
|
||||
**Differences**:
|
||||
1. Node.js includes `name` and `waiting` fields
|
||||
2. Rust API missing these fields
|
||||
3. Field naming inconsistencies across servers
|
||||
|
||||
### 7. **File System API Response Format Differences**
|
||||
|
||||
#### Node.js FS Browse Response:
|
||||
```json
|
||||
{
|
||||
"absolutePath": "/home/user",
|
||||
"files": [{
|
||||
"name": "file.txt",
|
||||
"created": "2024-01-01T00:00:00Z",
|
||||
"lastModified": "2024-01-01T00:01:00Z",
|
||||
"size": 1024,
|
||||
"isDir": false
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
#### Go FS Browse Response:
|
||||
```json
|
||||
[{
|
||||
"name": "file.txt",
|
||||
"path": "/home/user/file.txt",
|
||||
"is_dir": false,
|
||||
"size": 1024,
|
||||
"mode": "-rw-r--r--",
|
||||
"mod_time": "2024-01-01T00:01:00Z"
|
||||
}]
|
||||
```
|
||||
|
||||
**Issues**:
|
||||
1. Different response structures (object vs array)
|
||||
2. Different field names (`isDir` vs `is_dir`)
|
||||
3. Go includes additional fields (`path`, `mode`)
|
||||
4. Missing `created` field in Go
|
||||
|
||||
### 8. **Error Response Format Inconsistencies**
|
||||
|
||||
#### Node.js Error Format:
|
||||
```json
|
||||
{
|
||||
"error": "Session not found"
|
||||
}
|
||||
```
|
||||
|
||||
#### Rust API Error Format:
|
||||
```json
|
||||
{
|
||||
"success": null,
|
||||
"message": null,
|
||||
"error": "Session not found",
|
||||
"sessionId": null
|
||||
}
|
||||
```
|
||||
|
||||
#### Go Simple Error:
|
||||
```
|
||||
"Session not found" (plain text)
|
||||
```
|
||||
|
||||
**Problems**:
|
||||
1. Inconsistent error response structures
|
||||
2. Some servers use structured responses, others plain text
|
||||
3. Different HTTP status codes for same error conditions
|
||||
|
||||
## Critical Security Issues
|
||||
|
||||
### 1. **Inconsistent Authentication**
|
||||
- Node.js server has NO authentication
|
||||
- Different authentication realms across servers
|
||||
- No standardized credential management
|
||||
|
||||
### 2. **Path Traversal Vulnerabilities**
|
||||
Different path sanitization across servers:
|
||||
|
||||
**Node.js** (Proper):
|
||||
```javascript
|
||||
function resolvePath(inputPath, fallback) {
|
||||
if (inputPath.startsWith('~')) {
|
||||
return path.join(os.homedir(), inputPath.slice(1));
|
||||
}
|
||||
return path.resolve(inputPath);
|
||||
}
|
||||
```
|
||||
|
||||
**Go** (Basic):
|
||||
```go
|
||||
// Expand ~ in working directory
|
||||
if cwd != "" && cwd[0] == '~' {
|
||||
// Simple tilde expansion
|
||||
}
|
||||
```
|
||||
|
||||
## Missing Features by Server
|
||||
|
||||
### Node.js Missing:
|
||||
- ngrok tunnel management
|
||||
- Terminal dimensions in session creation
|
||||
|
||||
### Rust HTTP Server Missing:
|
||||
- **ALL API endpoints** (only static file serving)
|
||||
|
||||
### Rust API Server Missing:
|
||||
- Terminal resize functionality
|
||||
- ngrok tunnel management
|
||||
|
||||
### Go Server Missing:
|
||||
- None (most complete implementation)
|
||||
|
||||
### Swift Server Missing:
|
||||
- Terminal resize functionality
|
||||
|
||||
## Recommendations
|
||||
|
||||
### 1. **Immediate Fixes Required**
|
||||
|
||||
1. **Standardize Request/Response Formats**:
|
||||
- Use consistent field naming (camelCase vs snake_case)
|
||||
- Standardize error response structure
|
||||
- Align session creation request formats
|
||||
|
||||
2. **Implement Missing Critical APIs**:
|
||||
- Add resize endpoint to Rust API and Swift servers
|
||||
- Add authentication to Node.js server
|
||||
- Deprecate or complete Rust HTTP server
|
||||
|
||||
3. **Fix Security Issues**:
|
||||
- Standardize authentication realms
|
||||
- Implement consistent path sanitization
|
||||
- Add proper input validation
|
||||
|
||||
### 2. **Semantic Alignment**
|
||||
|
||||
1. **Session Management**:
|
||||
- Standardize session ID generation
|
||||
- Align session status values
|
||||
- Consistent PID handling
|
||||
|
||||
2. **Special Key Handling**:
|
||||
- Standardize escape sequence mappings
|
||||
- Consistent enter key behavior
|
||||
- Align special key names
|
||||
|
||||
3. **File System Operations**:
|
||||
- Standardize directory listing format
|
||||
- Consistent path resolution
|
||||
- Align file metadata fields
|
||||
|
||||
### 3. **Architecture Improvements**
|
||||
|
||||
1. **API Versioning**:
|
||||
- Implement `/api/v1/` prefix
|
||||
- Version all endpoint contracts
|
||||
- Plan backward compatibility
|
||||
|
||||
2. **Error Handling**:
|
||||
- Standardize HTTP status codes
|
||||
- Consistent error response format
|
||||
- Proper error categorization
|
||||
|
||||
3. **Documentation**:
|
||||
- OpenAPI/Swagger specifications
|
||||
- API contract testing
|
||||
- Cross-server compatibility tests
|
||||
|
||||
## Rust Server Architecture Analysis
|
||||
|
||||
After deeper analysis, the Rust servers have a clear separation of concerns:
|
||||
|
||||
### Rust Session Management (`tty-fwd/src/sessions.rs`)
|
||||
**Complete session management implementation**:
|
||||
- `list_sessions()` - ✅ Full session listing with status checking
|
||||
- `send_key_to_session()` - ✅ Special key input (arrow keys, enter, escape, etc.)
|
||||
- `send_text_to_session()` - ✅ Text input to sessions
|
||||
- `send_signal_to_session()` - ✅ Signal sending (SIGTERM, SIGKILL, etc.)
|
||||
- `cleanup_sessions()` - ✅ Session cleanup with PID validation
|
||||
- `spawn_command()` - ✅ New session creation
|
||||
- ✅ Process monitoring and zombie reaping
|
||||
- ✅ Pipe-based I/O with timeout protection
|
||||
|
||||
### Rust Protocol Support (`tty-fwd/src/protocol.rs`)
|
||||
**Complete streaming and protocol support**:
|
||||
- ✅ Asciinema format reading/writing
|
||||
- ✅ SSE streaming with `StreamingIterator`
|
||||
- ✅ Terminal escape sequence processing
|
||||
- ✅ Real-time event streaming with file monitoring
|
||||
- ✅ UTF-8 handling and buffering
|
||||
|
||||
### Main Binary (`tty-fwd/src/main.rs`)
|
||||
**Complete CLI interface**:
|
||||
- ✅ Session listing: `--list-sessions`
|
||||
- ✅ Key input: `--send-key <key>`
|
||||
- ✅ Text input: `--send-text <text>`
|
||||
- ✅ Process control: `--signal`, `--stop`, `--kill`
|
||||
- ✅ Cleanup: `--cleanup`
|
||||
- ✅ **HTTP Server**: `--serve <addr>` (launches API server)
|
||||
|
||||
**Key Finding**: `tty-fwd --serve` launches the **API server**, not the HTTP server.
|
||||
|
||||
## Corrected Assessment
|
||||
|
||||
### Rust Implementation Status: ✅ **COMPLETE AND CORRECT**
|
||||
|
||||
**All servers are properly implemented**:
|
||||
1. **Node.js Server**: ✅ Complete - PTY service wrapper
|
||||
2. **Rust HTTP Server**: ✅ Complete - Utility HTTP server (not meant for direct client use)
|
||||
3. **Rust API Server**: ✅ Complete - Full session management server
|
||||
4. **Go Server**: ✅ Complete - Native session management
|
||||
5. **Swift Server**: ✅ Complete - Wraps tty-fwd binary
|
||||
|
||||
### Remaining Issues (Reduced Severity):
|
||||
|
||||
1. **Terminal Resize Missing** (Rust API, Swift) - Client compatibility issue
|
||||
2. **Request/Response Format Inconsistencies** - Client needs adaptation
|
||||
3. **Authentication Differences** - Security/compatibility issue
|
||||
|
||||
## Updated Recommendations
|
||||
|
||||
### 1. **Immediate Priority: Terminal Resize**
|
||||
Add resize endpoint to Rust API and Swift servers:
|
||||
```rust
|
||||
// Rust API Server needs:
|
||||
POST /api/sessions/{sessionId}/resize
|
||||
```
|
||||
|
||||
### 2. **Response Format Standardization**
|
||||
Align session list responses across all servers for client compatibility.
|
||||
|
||||
### 3. **Authentication Standardization**
|
||||
Implement consistent Basic Auth across all servers.
|
||||
|
||||
## Conclusion
|
||||
|
||||
**Previous Assessment Correction**: The Rust servers are **fully functional and complete**. The HTTP server is correctly designed as a utility component, while the API server provides full session management.
|
||||
|
||||
**Current Status**: 4 out of 5 servers are **client-compatible**. Only missing terminal resize in Rust API and Swift servers.
|
||||
|
||||
**Impact**: Much lower than initially assessed. The main issues are:
|
||||
1. **Terminal resize functionality** - causes 404s but client continues working
|
||||
2. **Response format variations** - may cause field mapping issues
|
||||
3. **Authentication inconsistencies** - different security models
|
||||
|
||||
The project has **solid API coverage** across all platforms with minor compatibility issues rather than fundamental implementation gaps.
|
||||
|
|
@ -8,14 +8,15 @@
|
|||
"watch:server": "tsx watch src/server.ts",
|
||||
"watch:css": "npx tailwindcss -i ./src/input.css -o ./public/bundle/output.css --watch",
|
||||
"watch:assets": "chokidar 'src/client/assets/**/*' -c 'npm run bundle:assets'",
|
||||
"clean": "rm -rf public/* && rm -rf dist/",
|
||||
"clean": "node scripts/clean.js",
|
||||
"build": "npm run bundle",
|
||||
"typecheck": "tsc --noEmit",
|
||||
"ensure:dirs": "node scripts/ensure-dirs.js",
|
||||
"bundle": "npm run clean && npm run bundle:assets && npm run bundle:css && npm run bundle:client && npm run bundle:test",
|
||||
"bundle:assets": "mkdir -p public && cp -r src/client/assets/* public/",
|
||||
"bundle:css": "mkdir -p public/bundle && npx tailwindcss -i ./src/input.css -o ./public/bundle/output.css --minify",
|
||||
"bundle:client": "mkdir -p public/bundle && esbuild src/client/app-entry.ts --bundle --outfile=public/bundle/client-bundle.js --format=esm --sourcemap",
|
||||
"bundle:test": "mkdir -p public/bundle && esbuild src/client/test-terminals-entry.ts --bundle --outfile=public/bundle/terminal.js --format=esm --sourcemap",
|
||||
"bundle:assets": "node scripts/copy-assets.js",
|
||||
"bundle:css": "npm run ensure:dirs && npx tailwindcss -i ./src/input.css -o ./public/bundle/output.css --minify",
|
||||
"bundle:client": "npm run ensure:dirs && esbuild src/client/app-entry.ts --bundle --outfile=public/bundle/client-bundle.js --format=esm --sourcemap",
|
||||
"bundle:test": "npm run ensure:dirs && esbuild src/client/test-terminals-entry.ts --bundle --outfile=public/bundle/terminal.js --format=esm --sourcemap",
|
||||
"start": "node dist/server.js",
|
||||
"lint": "eslint 'src/**/*.{ts,tsx}'",
|
||||
"lint:fix": "eslint 'src/**/*.{ts,tsx}' --fix",
|
||||
|
|
|
|||
15
web/scripts/clean.js
Normal file
15
web/scripts/clean.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function removeRecursive(dirPath) {
|
||||
if (fs.existsSync(dirPath)) {
|
||||
fs.rmSync(dirPath, { recursive: true, force: true });
|
||||
console.log(`Removed: ${dirPath}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Clean public and dist directories
|
||||
removeRecursive('public');
|
||||
removeRecursive('dist');
|
||||
|
||||
console.log('Clean completed successfully');
|
||||
20
web/scripts/copy-assets.js
Normal file
20
web/scripts/copy-assets.js
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Ensure public directory exists
|
||||
fs.mkdirSync('public', { recursive: true });
|
||||
|
||||
// Copy assets
|
||||
const srcDir = 'src/client/assets';
|
||||
const destDir = 'public';
|
||||
|
||||
if (fs.existsSync(srcDir)) {
|
||||
fs.readdirSync(srcDir).forEach(file => {
|
||||
const srcPath = path.join(srcDir, file);
|
||||
const destPath = path.join(destDir, file);
|
||||
fs.cpSync(srcPath, destPath, { recursive: true });
|
||||
});
|
||||
console.log('Assets copied successfully');
|
||||
} else {
|
||||
console.log('No assets directory found, skipping copy');
|
||||
}
|
||||
15
web/scripts/ensure-dirs.js
Normal file
15
web/scripts/ensure-dirs.js
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Ensure all required directories exist
|
||||
const dirs = [
|
||||
'public',
|
||||
'public/bundle'
|
||||
];
|
||||
|
||||
dirs.forEach(dir => {
|
||||
fs.mkdirSync(dir, { recursive: true });
|
||||
console.log(`Ensured directory exists: ${dir}`);
|
||||
});
|
||||
|
||||
console.log('All directories created successfully');
|
||||
Loading…
Reference in a new issue