# XClawLink CLI — Agent Skill

## Metadata
- **name**: xclawlink-remote-exec
- **version**: 1.0.2
- **description**: Execute commands, transfer files, and manage tasks on remote devices via XClawLink CLI. All output is JSON-structured for machine parsing.
- **triggers**: `remote execute`, `run on server`, `upload file`, `download file`, `check device`, `system info`, `scheduled task`

---

## Architecture

```
AI Agent → CLI (xclawlink) → Relay → Proxy → Agent (remote device)
                                    ↑
                                 P2P tunnel (KCP) — preferred, auto-fallback to WebSocket
```

---

## Authentication

Three methods, priority order:

| Priority | Method | How |
|----------|--------|-----|
| 1 | Config file | `config.yaml` with `token` and `otp` fields |
| 2 | Environment | `CLI_TOKEN` + `CLI_OTP` |
| 3 | CLI flags | `--token <TOKEN>` + `--otp <OTP>` |

---

## Global Options

| Flag | Type | Default | Description |
|------|------|---------|-------------|
| `-d, --device-id` | string | required | CLI device identifier |
| `--token` | string | from config | JWT authentication token |
| `--otp` | string | from config | One-time password |
| `--tenant-id` | string | from config | Tenant identifier |
| `--output` | string | `json` | Output format: `json` (recommended), `table`, `text` |
| `-p, --persist` | bool | `false` | Keep daemon connection alive |

---

## Commands

### 1. `execute` — Run command on remote device

```
xclawlink execute <DEVICE_ID> "<COMMAND>" [flags]
```

**Response** (JSON):
```json
{
  "device_id": "AGT-000016",
  "command": "whoami",
  "exit_code": 0,
  "stdout": "root\n",
  "stderr": "",
  "error": null,
  "duration": "1.2s",
  "idempotent": false
}
```

**Exit codes**:
- `0` = success
- `1-255` = command failed
- `-1` = execution error (agent unreachable, timeout, etc.)

**Execution modes**:

| Flags | Behavior |
|-------|----------|
| *(none)* | Direct exec on host |
| `--shell` | Shell pipeline: `sh -c "<command>"` (for pipes, redirects) |
| `--shell --sandbox` | Shell + network namespace isolation |
| `--target "<host:port/proto>"` | Network whitelist within sandbox |

**Scheduling flags**:
- `--mode scheduled --at "2026-06-01 02:00:00"` — one-time scheduled
- `--mode interval --every "5m" --count 10` — recurring (every 5min, 10 times)

**Examples**:
```bash
# Basic command
xclawlink execute AGT-000016 "whoami"

# Shell pipeline
xclawlink execute AGT-000016 "ps -ef | grep nginx > /tmp/nginx.lst" --shell

# Network-restricted sandbox
xclawlink execute AGT-000016 "ping -c 1 1.1.1.1" --shell --sandbox
xclawlink execute AGT-000016 "curl http://192.168.1.100:8080/api" --shell --sandbox --target "192.168.1.100:8080/tcp"

# Scheduled
xclawlink execute AGT-000016 "date" --mode scheduled --at "2026-06-01 02:00:00"
xclawlink execute AGT-000016 "free -m" --mode interval --every "5m" --count 10
```

### 2. `devices` — List online devices

```
xclawlink devices
```

**Response**:
```json
{
  "devices": [
    {"device_id": "AGT-000016", "hostname": "web-01", "os": "linux", "arch": "amd64", "status": "online"}
  ],
  "idempotent": true
}
```

### 3. `info` — System information

```
xclawlink info <DEVICE_ID>
```

**Response**:
```json
{
  "system_info": {
    "device_id": "AGT-000016",
    "cpu_usage": 12.5,
    "memory_usage": 45.2,
    "disk_usage": 67.8,
    "uptime": "12 days, 3:15"
  }
}
```

### 4. `upload` — Upload file to remote device

```
xclawlink upload <DEVICE_ID> <LOCAL_FILE> <REMOTE_PATH> [--progress <N>]
```

`--progress`: `"bar"` for graphical, numeric N for JSON milestones every N%

**Response**:
```json
{
  "device_id": "AGT-000016",
  "operation": "upload",
  "file_path": "/tmp/upload.dat",
  "file_size": 1048576,
  "duration": "4.2s",
  "exit_code": 0
}
```

### 5. `download` — Download file from remote device

```
xclawlink download <DEVICE_ID> <REMOTE_FILE> <LOCAL_PATH> [--progress <N>]
```

Same response format as upload.

### 6. `schedules` — Manage scheduled tasks

```
xclawlink schedules list [<DEVICE_ID>]
xclawlink schedules status <DEVICE_ID> <TASK_ID>
xclawlink schedules cancel <DEVICE_ID> <TASK_ID>
```

### 7. `version` — Version info

```
xclawlink version
```

**Response**:
```json
{"version": "1.0.2", "commit": "abc1234", "build": "2026-06-13"}
```

### 8. `daemon status` — Daemon process status

```
xclawlink daemon status
```

---

## Error Handling

When `error` field is non-null or `exit_code` is `-1`, check and handle:

| Error Pattern | Cause | Action |
|---------------|-------|--------|
| `path not in allowed directories` | Agent directory restriction | Use `/tmp/` or `/var/tmp/` |
| `command not in allowed list` | Proxy command whitelist | Contact admin to whitelist command |
| `OTP validation failed` | OTP expired or invalid | Rotate OTP in Portal |
| `Network is unreachable` | Sandbox isolation | Add `--target <host:port/proto>` |
| `connection timeout` | Agent offline | Retry later or check device status |
| `permission denied` | Insufficient privileges | Use `sudo` or check permissions |

---

## Directory Restrictions

Agent only allows operations in `/tmp` and `/var/tmp` by default. Admin can configure per-device directory whitelists in the Proxy management page.

---

## Python Wrapper

```python
import subprocess, json

def xclawlink_execute(device: str, command: str, *, shell: bool = False, sandbox: bool = False, target: str = None) -> str:
    """Execute command on remote device via XClawLink CLI."""
    args = ["xclawlink", "execute", device, command, "--output", "json"]
    if shell: args.append("--shell")
    if sandbox: args.append("--sandbox")
    if target: args.extend(["--target", target])
    
    result = subprocess.run(args, capture_output=True, text=True, timeout=30)
    data = json.loads(result.stdout)
    
    if data.get("error"):
        raise RuntimeError(f"XClawLink error: {data['error']}")
    if data["exit_code"] == -1:
        raise RuntimeError(f"Execution failed")
    if data["exit_code"] != 0:
        raise RuntimeError(f"Exit {data['exit_code']}: {data['stderr']}")
    
    return data["stdout"].strip()


def xclawlink_devices() -> list[dict]:
    """List online devices."""
    result = subprocess.run(["xclawlink", "devices", "--output", "json"], capture_output=True, text=True)
    return json.loads(result.stdout)["devices"]


def xclawlink_upload(device: str, local: str, remote: str) -> dict:
    """Upload file to remote device."""
    result = subprocess.run(["xclawlink", "upload", device, local, remote, "--output", "json"], capture_output=True, text=True)
    return json.loads(result.stdout)


def xclawlink_download(device: str, remote: str, local: str) -> dict:
    """Download file from remote device."""
    result = subprocess.run(["xclawlink", "download", device, remote, local, "--output", "json"], capture_output=True, text=True)
    return json.loads(result.stdout)
```

---

## Quick Start

```bash
# 1. Check available devices
xclawlink devices

# 2. Get system info
xclawlink info AGT-000016

# 3. Execute a command
xclawlink execute AGT-000016 "systemctl status nginx"

# 4. Upload a file
xclawlink upload AGT-000016 ./config.json /tmp/config.json

# 5. Download a log
xclawlink download AGT-000016 /var/log/nginx/access.log ./access.log
```
