# NexusCLI API & Wrapper Contract Specification

**Version**: 0.1.0
**Created**: 2025-11-17
**Based on**: NexusChat wrapper architecture
**Purpose**: Official API contract for NexusCLI control plane and nexus-wrapper node agents

---

## 📋 Table of Contents

1. [Architecture Overview](#architecture-overview)
2. [Control Plane API (NexusCLI)](#control-plane-api-nexuscli)
3. [Node Agent API (nexus-wrapper)](#node-agent-api-nexus-wrapper)
4. [Data Structures](#data-structures)
5. [Event Streaming](#event-streaming)
6. [Error Handling](#error-handling)
7. [Pipeline Integration](#pipeline-integration)

---

## 🏗️ Architecture Overview

### Philosophy: Separation of Concerns

**NexusChat Pattern** (adapted from analysis):
```
User Request → LibreChat API → Wrapper → Claude CLI → PTY Output → Parser → SSE Events → Client
```

**NexusCLI Pattern** (generalized):
```
Control Plane → Job API → nexus-wrapper → Remote CLI → Stdout/Stderr → Parser → SSE Events → Dashboard
       ↓            ↓            ↓              ↓             ↓            ↓          ↓          ↓
   Orchestrator  REST API    HTTP Agent    Local Tools    Raw Output  Structured  Real-time  UI/Logs
```

### Key Principles (from NexusChat)

1. **Stateless Wrappers**: Each wrapper is a lightweight HTTP server that spawns CLI processes
2. **Session Persistence**: 2-layer (RAM cache + Disk storage) for crash recovery
3. **Event-Driven Streaming**: SSE (Server-Sent Events) for real-time progress
4. **Output Parsing**: Regex-based parsing of stdout/stderr into structured events
5. **Tool Abstraction**: Generic tool interface (Bash, Read, Write, etc.)

---

## 🎛️ Control Plane API (NexusCLI)

### Base URL
```
http://localhost:4000/api/v1
```

### Authentication
```
Authorization: Bearer <jwt_token>
```

---

### 1. Node Management

#### `POST /nodes/register`
Register a new node with the control plane.

**Request**:
```json
{
  "nodeId": "node-001",
  "hostname": "server-prod-1",
  "ipAddress": "192.168.1.10",
  "capabilities": {
    "tools": ["bash", "python", "git", "docker"],
    "maxConcurrentJobs": 5,
    "platform": "linux",
    "arch": "x86_64"
  },
  "wrapperVersion": "1.0.0",
  "wrapperEndpoint": "http://192.168.1.10:5000"
}
```

**Response** (200 OK):
```json
{
  "nodeId": "node-001",
  "status": "registered",
  "token": "<node_auth_token>",
  "heartbeatInterval": 30000
}
```

#### `GET /nodes`
List all registered nodes.

**Response** (200 OK):
```json
{
  "nodes": [
    {
      "nodeId": "node-001",
      "hostname": "server-prod-1",
      "status": "online",
      "lastHeartbeat": "2025-11-17T10:30:00Z",
      "activeJobs": 2,
      "capabilities": { "tools": ["bash", "python"] }
    }
  ]
}
```

#### `POST /nodes/:nodeId/heartbeat`
Update node status (sent by wrapper every N seconds).

**Request**:
```json
{
  "status": "online",
  "activeJobs": 2,
  "systemLoad": 0.45,
  "timestamp": "2025-11-17T10:30:00Z"
}
```

**Response** (200 OK):
```json
{
  "acknowledged": true
}
```

---

### 2. Job Management

#### `POST /jobs`
Create a new job for execution on a node.

**Request**:
```json
{
  "nodeId": "node-001",
  "tool": "bash",
  "command": "ls -la /var/www",
  "workingDir": "/tmp",
  "timeout": 30000,
  "metadata": {
    "userId": "user-123",
    "requestId": "req-456"
  }
}
```

**Response** (201 Created):
```json
{
  "jobId": "job-789",
  "nodeId": "node-001",
  "status": "queued",
  "createdAt": "2025-11-17T10:30:00Z",
  "streamEndpoint": "/jobs/job-789/stream"
}
```

#### `GET /jobs/:jobId`
Get job status and results.

**Response** (200 OK):
```json
{
  "jobId": "job-789",
  "nodeId": "node-001",
  "status": "completed",
  "tool": "bash",
  "command": "ls -la /var/www",
  "result": {
    "exitCode": 0,
    "stdout": "total 24\ndrwxr-xr-x 6 root root...",
    "stderr": "",
    "duration": 123
  },
  "createdAt": "2025-11-17T10:30:00Z",
  "completedAt": "2025-11-17T10:30:01Z"
}
```

#### `GET /jobs/:jobId/stream` (SSE)
Real-time event stream for job execution.

**SSE Events**:
```javascript
// Event: status
data: {"type":"status","category":"queued","message":"Job queued on node-001","timestamp":"..."}

// Event: status (execution started)
data: {"type":"status","category":"executing","message":"Running bash command...","icon":"🔧","timestamp":"..."}

// Event: output_chunk (incremental stdout)
data: {"type":"output_chunk","stream":"stdout","text":"total 24\n","isIncremental":true}

// Event: status (tool execution)
data: {"type":"status","category":"tool","message":"Bash: ls -la /var/www","icon":"🔧","toolOutput":"..."}

// Event: response_done
data: {"type":"response_done","exitCode":0,"duration":123}

// Event: done
data: {"type":"done","jobId":"job-789","status":"completed"}
```

#### `DELETE /jobs/:jobId`
Cancel a running job.

**Response** (200 OK):
```json
{
  "jobId": "job-789",
  "status": "cancelled"
}
```

---

### 3. Batch Operations

#### `POST /batch`
Execute a job on multiple nodes.

**Request**:
```json
{
  "nodeIds": ["node-001", "node-002", "node-003"],
  "tool": "bash",
  "command": "systemctl status nginx",
  "parallel": true
}
```

**Response** (201 Created):
```json
{
  "batchId": "batch-abc",
  "jobs": [
    {"jobId": "job-001", "nodeId": "node-001"},
    {"jobId": "job-002", "nodeId": "node-002"},
    {"jobId": "job-003", "nodeId": "node-003"}
  ]
}
```

---

## 🤖 Node Agent API (nexus-wrapper)

### Base URL (on each node)
```
http://<node-ip>:5000/api/v1
```

### Authentication
```
Authorization: Bearer <node_auth_token>
```

---

### 1. Health & Capabilities

#### `GET /health`
Health check endpoint.

**Response** (200 OK):
```json
{
  "status": "ok",
  "nodeId": "node-001",
  "wrapperVersion": "1.0.0",
  "uptime": 86400,
  "activeJobs": 2
}
```

#### `GET /capabilities`
List available tools and capabilities.

**Response** (200 OK):
```json
{
  "tools": [
    {
      "name": "bash",
      "version": "5.1.16",
      "available": true
    },
    {
      "name": "python",
      "version": "3.11.5",
      "available": true
    },
    {
      "name": "git",
      "version": "2.39.2",
      "available": true
    }
  ],
  "platform": "linux",
  "arch": "x86_64",
  "maxConcurrentJobs": 5
}
```

---

### 2. Job Execution

#### `POST /jobs`
Execute a job locally on this node.

**Request**:
```json
{
  "jobId": "job-789",
  "tool": "bash",
  "command": "ls -la /var/www",
  "workingDir": "/tmp",
  "timeout": 30000,
  "env": {
    "PATH": "/usr/local/bin:/usr/bin"
  }
}
```

**Response** (202 Accepted):
```json
{
  "jobId": "job-789",
  "status": "accepted",
  "streamEndpoint": "/jobs/job-789/stream"
}
```

#### `GET /jobs/:jobId/stream` (SSE)
Real-time output stream (like NexusChat claude-wrapper).

**SSE Events** (same format as Control Plane):
```javascript
// status events
data: {"type":"status","category":"executing","message":"Running command..."}

// stdout chunks
data: {"type":"output_chunk","stream":"stdout","text":"line 1\n"}

// stderr chunks
data: {"type":"output_chunk","stream":"stderr","text":"warning: ...\n"}

// completion
data: {"type":"response_done","exitCode":0,"duration":123}
data: {"type":"done"}
```

#### `GET /jobs/:jobId`
Get job result (after completion).

**Response** (200 OK):
```json
{
  "jobId": "job-789",
  "status": "completed",
  "exitCode": 0,
  "stdout": "total 24\ndrwxr-xr-x...",
  "stderr": "",
  "duration": 123,
  "startedAt": "2025-11-17T10:30:00Z",
  "completedAt": "2025-11-17T10:30:01Z"
}
```

#### `DELETE /jobs/:jobId`
Kill a running job.

**Response** (200 OK):
```json
{
  "jobId": "job-789",
  "status": "killed",
  "signal": "SIGTERM"
}
```

---

## 📦 Data Structures

### Node
```typescript
interface Node {
  nodeId: string;           // Unique node identifier
  hostname: string;         // Node hostname
  ipAddress: string;        // Node IP address
  status: 'online' | 'offline' | 'error';
  capabilities: {
    tools: string[];        // Available CLI tools
    maxConcurrentJobs: number;
    platform: string;       // 'linux' | 'darwin' | 'windows'
    arch: string;           // 'x86_64' | 'arm64'
  };
  wrapperVersion: string;
  wrapperEndpoint: string;
  lastHeartbeat: string;    // ISO 8601 timestamp
  activeJobs: number;
}
```

### Job
```typescript
interface Job {
  jobId: string;
  nodeId: string;
  status: 'queued' | 'executing' | 'completed' | 'failed' | 'cancelled' | 'killed';
  tool: string;             // 'bash' | 'python' | 'git' | etc.
  command: string;          // Command to execute
  workingDir?: string;      // Working directory (optional)
  timeout?: number;         // Timeout in milliseconds
  env?: Record<string, string>; // Environment variables
  result?: {
    exitCode: number;
    stdout: string;
    stderr: string;
    duration: number;       // Milliseconds
  };
  metadata?: Record<string, any>; // Custom metadata
  createdAt: string;
  startedAt?: string;
  completedAt?: string;
}
```

### Event (SSE)
```typescript
interface Event {
  type: 'status' | 'output_chunk' | 'response_done' | 'done' | 'error';

  // For type: 'status'
  category?: 'queued' | 'executing' | 'tool' | 'streaming' | 'complete' | 'warning';
  message?: string;
  icon?: string;            // Emoji icon

  // For type: 'output_chunk'
  stream?: 'stdout' | 'stderr';
  text?: string;
  isIncremental?: boolean;

  // For type: 'response_done'
  exitCode?: number;
  duration?: number;

  // For type: 'error'
  error?: string;
  details?: string;

  timestamp?: string;       // ISO 8601
}
```

---

## 🌊 Event Streaming

### SSE (Server-Sent Events)

**Inspired by NexusChat**: Real-time streaming via SSE for live job progress.

**Headers**:
```http
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive
X-Accel-Buffering: no
```

**Event Flow** (based on NexusChat output-parser.js):

1. **Queued**:
   ```javascript
   data: {"type":"status","category":"queued","message":"Job queued","icon":"⏱️"}
   ```

2. **Executing**:
   ```javascript
   data: {"type":"status","category":"executing","message":"Running bash command...","icon":"🔧"}
   ```

3. **Output Streaming** (incremental):
   ```javascript
   data: {"type":"output_chunk","stream":"stdout","text":"line 1\n","isIncremental":true}
   data: {"type":"output_chunk","stream":"stdout","text":"line 2\n","isIncremental":true}
   ```

4. **Tool Events** (if detected):
   ```javascript
   data: {"type":"status","category":"tool","message":"Git: clone repository","icon":"🔀"}
   ```

5. **Completion**:
   ```javascript
   data: {"type":"response_done","exitCode":0,"duration":123}
   data: {"type":"done","jobId":"job-789"}
   ```

6. **Error**:
   ```javascript
   data: {"type":"error","error":"Command failed","details":"...\n"}
   ```

---

## ⚠️ Error Handling

### Error Response Format

**HTTP Error Response**:
```json
{
  "error": "Error message",
  "code": "ERROR_CODE",
  "details": "Additional details...",
  "timestamp": "2025-11-17T10:30:00Z"
}
```

### Common Error Codes

| Code | HTTP Status | Description |
|------|-------------|-------------|
| `NODE_NOT_FOUND` | 404 | Node ID does not exist |
| `NODE_OFFLINE` | 503 | Node is offline or unreachable |
| `JOB_NOT_FOUND` | 404 | Job ID does not exist |
| `JOB_TIMEOUT` | 408 | Job exceeded timeout limit |
| `TOOL_NOT_AVAILABLE` | 400 | Requested tool not available on node |
| `AUTHENTICATION_FAILED` | 401 | Invalid or missing auth token |
| `RATE_LIMIT_EXCEEDED` | 429 | Too many requests |
| `INTERNAL_ERROR` | 500 | Internal server error |

### Retry Strategy (inspired by NexusChat wrapper)

- **Connection errors**: Retry up to 3 times with exponential backoff
- **Timeout errors**: No retry (user-initiated cancellation)
- **Tool errors**: No retry (fix command and resubmit)

---

## 🔄 Pipeline Integration

### Workflow: Analysis → Coordination → Execution → Test → Deploy

**Based on NexusChat CLAUDE.md pipeline**, adapted for distributed CLI:

```
┌─────────────────────────────────────────────────────────────────┐
│                        CONTROL PLANE                            │
│                                                                 │
│  1. ANALYSIS                                                    │
│     - User submits job request                                  │
│     - Validate command, tool, node availability                 │
│                                                                 │
│  2. COORDINATION                                                │
│     - Select target node(s)                                     │
│     - Queue job                                                 │
│     - Establish SSE stream                                      │
│                                                                 │
│  3. EXECUTION (delegated to nexus-wrapper)                      │
│     - POST /jobs to wrapper API                                 │
│     - Wrapper spawns PTY process                                │
│     - Output parser streams events                              │
│                                                                 │
│  4. TEST (optional - automated verification)                    │
│     - Check exit code                                           │
│     - Validate output format                                    │
│     - Assert expected results                                   │
│                                                                 │
│  5. DEPLOY (future - chained jobs)                              │
│     - Trigger dependent jobs                                    │
│     - Update deployment status                                  │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘

┌─────────────────────────────────────────────────────────────────┐
│                      NEXUS-WRAPPER (Node)                       │
│                                                                 │
│  - Receive job request                                          │
│  - Spawn PTY process (like NexusChat claude-wrapper)            │
│  - Parse stdout/stderr with OutputParser                        │
│  - Emit SSE events (status, output_chunk, done)                 │
│  - Store result in local job storage                            │
│                                                                 │
└─────────────────────────────────────────────────────────────────┘
```

### Session Persistence (NexusChat pattern)

**2-Layer Storage**:
1. **RAM Cache** (Map): Fast access for active jobs
2. **Disk Storage** (JSON files): Persistence across restarts

**File Structure** (per wrapper):
```
/var/lib/nexus-wrapper/jobs/
  ├── job-001.json
  ├── job-002.json
  └── job-003.json
```

**Job File Format**:
```json
{
  "jobId": "job-001",
  "status": "completed",
  "command": "ls -la",
  "result": {
    "exitCode": 0,
    "stdout": "...",
    "stderr": "",
    "duration": 123
  },
  "createdAt": "2025-11-17T10:30:00Z",
  "completedAt": "2025-11-17T10:30:01Z"
}
```

---

## 🎯 Implementation Notes

### From NexusChat Analysis

1. **PTY over Subprocess** (claude-wrapper.js:61)
   - Use `node-pty` for real PTY (fixes CLI spawn issues)
   - Handles ANSI escape codes correctly
   - Better signal handling (SIGTERM, SIGKILL)

2. **Output Parser State Machine** (output-parser.js:13)
   - States: `idle | tool_execution | tool_output | thinking | response`
   - Regex patterns for detecting tool markers
   - Line-by-line parsing for accuracy

3. **SSE Streaming** (server.js:71-76)
   - Set correct headers (`text/event-stream`, `no-cache`)
   - Disable nginx buffering (`X-Accel-Buffering: no`)
   - Emit events as `data: {JSON}\n\n`

4. **Session Manager** (session-manager.js)
   - Load from disk on cache miss (lazy loading)
   - Save to disk on every update (durability)
   - List() loads all from disk (ensure completeness)

5. **Graceful Shutdown** (server.js:248)
   - Handle SIGTERM for systemd compatibility
   - Clean up active PTY processes
   - Save in-flight jobs to disk

---

## 📚 References

- **NexusChat Source**: `/var/www/chat.mmmbuto.com/servers/claude-code-server/`
- **NexusChat CLAUDE.md**: Development workflow and pipeline
- **LibreChat API**: Upstream reference for agents/chat routes
- **Model**: Based on production wrapper serving 100+ daily requests

---

**Next Steps**:
1. Implement Control Plane API endpoints (Express.js)
2. Create nexus-wrapper reference implementation
3. Adapt OutputParser for generic CLI tools
4. Add WebSocket support (alternative to SSE)
5. Implement authentication & rate limiting

---

_Generated by Claude Code (Sonnet 4.5) - 2025-11-17_
