An open API service indexing awesome lists of open source software.

https://github.com/sanshao85/persistent-terminal-api

A standalone REST API service for persistent terminal session management, designed for AI assistants
https://github.com/sanshao85/persistent-terminal-api

ai-assistant api claude gpt node-pty rest-api terminal typescript

Last synced: 3 months ago
JSON representation

A standalone REST API service for persistent terminal session management, designed for AI assistants

Awesome Lists containing this project

README

          

# Persistent Terminal API

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Node.js Version](https://img.shields.io/badge/node-%3E%3D18.0.0-brightgreen.svg)](https://nodejs.org/)
[![TypeScript](https://img.shields.io/badge/TypeScript-5.9-blue.svg)](https://www.typescriptlang.org/)

**A standalone REST API service for persistent terminal session management**

*Designed to enable AI assistants (Claude, GPT, etc.) to execute long-running commands without blocking*

English | [简体中文](./README.zh-CN.md)

[Features](#features) • [Quick Start](#quick-start) • [API Docs](#api-documentation) • [Web UI](#web-ui) • [Contributing](#contributing)

---

## 🌟 Features

- 🖥️ **Persistent Terminal Sessions** - Terminals run in background even when clients disconnect
- 🔄 **Auto-Execute Commands** - Automatically adds newline to execute commands
- 📦 **Smart Output Buffering** - Circular buffer with configurable size and intelligent truncation
- 📊 **Statistics & Monitoring** - Track output size, line count, and token estimates
- ⏰ **Auto-Cleanup** - Automatically removes inactive sessions after timeout
- 🌐 **REST API** - Simple HTTP/JSON interface
- 🔒 **Graceful Shutdown** - Properly terminates all terminals on exit
- 📂 **Directory Awareness** - Required `cwd` parameter prevents path confusion
- 👥 **Multi-User Isolation** - Each user (AI assistant) can only access their own terminals
- 🆕 **ANSI Sequence Cleaning** - Optional `clean=true` parameter removes control sequences to save 30-90% tokens
- 🎨 **Web UI Included** - Beautiful web interface with xterm.js for visual management

## 📋 Table of Contents

- [Quick Start](#quick-start)
- [Installation](#installation)
- [Configuration](#configuration)
- [API Documentation](#api-documentation)
- [Web UI](#web-ui)
- [Usage Examples](#usage-examples)
- [Architecture](#architecture)
- [Performance](#performance)
- [Security](#security-considerations)
- [Troubleshooting](#troubleshooting)
- [Contributing](#contributing)
- [License](#license)

## 🚀 Quick Start

### Prerequisites

- Node.js >= 18.0.0
- npm or yarn

### Installation

```bash
# Clone the repository
git clone https://github.com/yourusername/persistent-terminal-api.git
cd persistent-terminal-api

# Install dependencies
npm install

# Copy environment file
cp .env.example .env
```

### Start Backend API

```bash
# Development mode (with auto-reload)
npm run dev

# Production mode
npm run build
npm start
```

API will be available at: `http://localhost:2556/api`

### Start Frontend Web UI

```bash
cd frontend
node server.js
```

Web UI will be available at: `http://localhost:2555`

### Quick Test

```bash
# Create a terminal
curl -X POST http://localhost:2556/api/terminals \
-H "Content-Type: application/json" \
-d '{"userId":"test-user","cwd":"/tmp"}'

# Get terminal ID from response, then send a command
curl -X POST http://localhost:2556/api/terminals/{TERMINAL_ID}/input \
-H "Content-Type: application/json" \
-d '{"userId":"test-user","input":"echo Hello World"}'

# Read output
curl "http://localhost:2556/api/terminals/{TERMINAL_ID}/output?userId=test-user&mode=tail&tailLines=10"
```

## ⚙️ Configuration

Default configuration in `.env`:

```env
# Backend API
PORT=2556
HOST=0.0.0.0

# Frontend Web UI
FRONTEND_PORT=2555

# Terminal Settings
MAX_BUFFER_SIZE=10000 # Maximum output buffer size
SESSION_TIMEOUT=86400000 # 24 hours in milliseconds

# Security
CORS_ORIGIN=* # Restrict in production!

# Logging
LOG_LEVEL=info
```

## 📚 API Documentation

### Base URL

```
http://localhost:2556/api
```

### Endpoints

| Method | Endpoint | Description |
|--------|----------|-------------|
| POST | `/terminals` | Create a new terminal session |
| POST | `/terminals/:id/input` | Send input to terminal |
| GET | `/terminals/:id/output` | Read terminal output |
| GET | `/terminals/:id/stats` | Get terminal statistics |
| GET | `/terminals` | List all terminals |
| DELETE | `/terminals/:id` | Terminate a terminal |
| GET | `/health` | Health check |

### 1. Create Terminal

**POST** `/terminals`

⚠️ **Both `userId` and `cwd` are required!**

**Request:**
```json
{
"userId": "claude-assistant-1",
"cwd": "/path/to/project",
"shell": "/bin/bash",
"env": { "NODE_ENV": "development" },
"cols": 80,
"rows": 24
}
```

**Response:**
```json
{
"success": true,
"data": {
"terminalId": "abc-123",
"userId": "claude-assistant-1",
"pid": 12345,
"shell": "/bin/bash",
"cwd": "/path/to/project",
"created": "2025-10-04T00:00:00.000Z",
"status": "active"
}
}
```

### 2. Send Input

**POST** `/terminals/:terminalId/input`

Automatically adds `\n` if not present.

**Request:**
```json
{
"userId": "claude-assistant-1",
"input": "npm start"
}
```

### 3. Read Output

**GET** `/terminals/:terminalId/output`

**Query Parameters:**
- `userId` (required) - User identifier
- `since` - Read from line number
- `mode` - `full`, `head`, `tail`, `head-tail`
- `tailLines` - Number of lines from end (default: 50)
- `headLines` - Number of lines from start (default: 50)
- `maxLines` - Max lines in full mode (default: 1000)
- `clean` - 🆕 Remove ANSI sequences (`true`/`false`)

**Examples:**
```bash
# Get last 30 lines (recommended)
GET /terminals/abc-123/output?userId=user-1&mode=tail&tailLines=30

# Get clean output (no ANSI, saves tokens)
GET /terminals/abc-123/output?userId=user-1&mode=tail&tailLines=30&clean=true

# Incremental reading
GET /terminals/abc-123/output?userId=user-1&since=100
```

**Response:**
```json
{
"success": true,
"data": {
"output": "...",
"totalLines": 150,
"nextReadFrom": 150,
"hasMore": false,
"truncated": false,
"stats": {
"totalBytes": 5000,
"estimatedTokens": 1250,
"linesShown": 30,
"linesOmitted": 120
}
}
}
```

### 4. Get Statistics

**GET** `/terminals/:terminalId/stats?userId=user-1`

### 5. List Terminals

**GET** `/terminals?userId=user-1`

### 6. Terminate Terminal

**DELETE** `/terminals/:terminalId`

```json
{
"userId": "user-1",
"signal": "SIGTERM"
}
```

### 7. Health Check

**GET** `/health`

## 🎨 Web UI

Access the web interface at `http://localhost:2555`

**Features:**
- 📊 View all terminal sessions
- 🎯 Filter terminals by user ID
- 🖥️ Interactive terminal with xterm.js
- ⌨️ Command input box
- 🎨 Dark/Light theme toggle
- 🛑 Bulk operations (stop all)

**Starting the Web UI:**
```bash
cd frontend
node server.js
```

Or configure auto-start in your process manager.

## 💡 Usage Examples

### Example 1: Start Development Server

```bash
USER_ID="claude-assistant-1"

# 1. Create terminal
TERMINAL_ID=$(curl -s -X POST http://localhost:2556/api/terminals \
-H "Content-Type: application/json" \
-d "{\"userId\":\"$USER_ID\",\"cwd\":\"/path/to/project\"}" \
| jq -r '.data.terminalId')

# 2. Start dev server
curl -X POST http://localhost:2556/api/terminals/$TERMINAL_ID/input \
-H "Content-Type: application/json" \
-d "{\"userId\":\"$USER_ID\",\"input\":\"npm run dev\"}"

# 3. Wait and check output
sleep 5
curl "http://localhost:2556/api/terminals/$TERMINAL_ID/output?userId=$USER_ID&mode=tail&tailLines=30&clean=true"
```

### Example 2: Monitor Long-Running Process

```bash
# Poll for new output every 5 seconds
LAST_LINE=0
while true; do
OUTPUT=$(curl -s "http://localhost:2556/api/terminals/$TERMINAL_ID/output?userId=$USER_ID&since=$LAST_LINE&clean=true")
echo "$OUTPUT" | jq -r '.data.output'
LAST_LINE=$(echo "$OUTPUT" | jq -r '.data.nextReadFrom')
sleep 5
done
```

### Example 3: AI Assistant Integration

```python
import requests

class TerminalClient:
def __init__(self, base_url, user_id):
self.base_url = base_url
self.user_id = user_id

def create_terminal(self, cwd):
response = requests.post(f"{self.base_url}/terminals", json={
"userId": self.user_id,
"cwd": cwd
})
return response.json()['data']['terminalId']

def execute_command(self, terminal_id, command):
requests.post(f"{self.base_url}/terminals/{terminal_id}/input", json={
"userId": self.user_id,
"input": command
})

def get_output(self, terminal_id, clean=True):
params = {
"userId": self.user_id,
"mode": "tail",
"tailLines": 50,
"clean": "true" if clean else "false"
}
response = requests.get(
f"{self.base_url}/terminals/{terminal_id}/output",
params=params
)
return response.json()['data']['output']

# Usage
client = TerminalClient("http://localhost:2556/api", "claude-assistant-1")
terminal_id = client.create_terminal("/path/to/project")
client.execute_command(terminal_id, "npm start")
time.sleep(5)
output = client.get_output(terminal_id, clean=True)
print(output)
```

## 🏗️ Architecture

```
src/
├── index.ts # Entry point & graceful shutdown
├── server.ts # Express server setup
├── routes/
│ └── terminals.ts # API routes
├── controllers/
│ └── terminalController.ts # Request handlers
├── services/
│ ├── terminalManager.ts # Terminal lifecycle & ANSI cleaning
│ └── outputBuffer.ts # Circular buffer with smart reading
├── middleware/
│ ├── errorHandler.ts # Error handling
│ └── logger.ts # Request logging
└── types/
└── index.ts # TypeScript definitions

frontend/
├── server.js # Static file server
├── index.html # Web UI
└── app.js # Frontend logic
```

### Key Design Decisions

1. **Auto-Execute Commands** - Automatically adds `\n` to execute commands
2. **Smart Buffering** - Circular buffer prevents memory overflow
3. **ANSI Cleaning** - Optional token optimization for AI assistants
4. **Directory Safety** - Required `cwd` prevents accidental operations
5. **Multi-User Isolation** - Permission checks on all operations

## 📊 Performance

- **Concurrent Sessions**: 50+ terminals
- **API Response Time**: < 100ms (excluding command execution)
- **Memory Usage**: < 500MB (50 sessions with 10K buffer each)
- **CPU Usage**: < 50% (normal load)
- **Token Savings**: 30-90% with `clean=true` parameter

## 🔒 Security Considerations

⚠️ **Important**: This API has **no authentication** by default!

**For production use:**

1. ✅ Add authentication (API keys, JWT, OAuth)
2. ✅ Implement rate limiting
3. ✅ Restrict CORS origins (change `CORS_ORIGIN` in `.env`)
4. ✅ Limit max terminals per user
5. ✅ Validate and sanitize all inputs
6. ✅ Use HTTPS in production
7. ✅ Run in isolated container/sandbox
8. ✅ Monitor for command injection attempts

## 🐛 Troubleshooting

### Port Already in Use

```bash
# Check what's using the port
lsof -i :2556
lsof -i :2555

# Kill processes
lsof -ti :2556 | xargs kill -9
lsof -ti :2555 | xargs kill -9
```

### Terminal Not Executing Commands

Ensure the working directory (`cwd`) exists and has proper permissions.

### Buffer Overflow

Increase `MAX_BUFFER_SIZE` in `.env` or use incremental reading with `since` parameter.

### Sessions Timing Out

Increase `SESSION_TIMEOUT` in `.env` (value in milliseconds).

## 🤝 Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details.

1. Fork the repository
2. Create your feature branch (`git checkout -b feature/amazing-feature`)
3. Commit your changes (`git commit -m 'Add amazing feature'`)
4. Push to the branch (`git push origin feature/amazing-feature`)
5. Open a Pull Request

## 📝 License

This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.

## 🙏 Acknowledgments

- [node-pty](https://github.com/microsoft/node-pty) - PTY bindings for Node.js
- [xterm.js](https://xtermjs.org/) - Terminal emulator for the web
- [Express](https://expressjs.com/) - Web framework

## 📧 Support

- 📖 [Documentation](./PROMPT.md) - Full AI assistant guide (中文)
- 🐛 [Issue Tracker](https://github.com/yourusername/persistent-terminal-api/issues)
- 💬 [Discussions](https://github.com/yourusername/persistent-terminal-api/discussions)

---

**Made with ❤️ for AI Assistants**

[⬆ Back to Top](#persistent-terminal-api)