Back to skills
SkillHub ClubRun DevOpsFull StackDevOps

openclaw-remote-install

One-click remote OpenClaw deployment via SSH. Auto-detects OS and selects best method (Docker/Podman/npm). Use when: (1) Installing on VPS/cloud servers, (2) Automating multi-machine deployment, (3) Configuring models/channels/gateway post-install.

Packaged view

This page reorganizes the original catalog entry around fit, installability, and workflow context first. The original raw source lives below.

Stars
3,131
Hot score
99
Updated
March 20, 2026
Overall rating
C4.0
Composite score
4.0
Best-practice grade
B73.6

Install command

npx @skill-hub/cli install openclaw-skills-openclaw-remote-install

Repository

openclaw/skills

Skill path: skills/codeblackhole1024/openclaw-remote-install

One-click remote OpenClaw deployment via SSH. Auto-detects OS and selects best method (Docker/Podman/npm). Use when: (1) Installing on VPS/cloud servers, (2) Automating multi-machine deployment, (3) Configuring models/channels/gateway post-install.

Open repository

Best for

Primary workflow: Run DevOps.

Technical facets: Full Stack, DevOps.

Target audience: everyone.

License: Unknown.

Original source

Catalog source: SkillHub Club.

Repository owner: openclaw.

This is still a mirrored public skill entry. Review the repository before installing into production workflows.

What it helps with

  • Install openclaw-remote-install into Claude Code, Codex CLI, Gemini CLI, or OpenCode workflows
  • Review https://github.com/openclaw/skills before adding openclaw-remote-install to shared team environments
  • Use openclaw-remote-install for development workflows

Works across

Claude CodeCodex CLIGemini CLIOpenCode

Favorites: 0.

Sub-skills: 0.

Aggregator: No.

Original source / Raw SKILL.md

---
name: openclaw-remote-install
version: "1.0.0"
description: |
  One-click remote OpenClaw deployment via SSH. Auto-detects OS and selects best method (Docker/Podman/npm). Use when: (1) Installing on VPS/cloud servers, (2) Automating multi-machine deployment, (3) Configuring models/channels/gateway post-install.
---

# OpenClaw Remote Install Skill

This skill handles remote installation and configuration of OpenClaw on remote servers via SSH with intelligent method selection and async execution support.

## Log Directory

All installation logs are automatically saved to:
```
~/.openclaw/remote-install-logs/<host>_<timestamp>/
```

Each installation creates:
- `install.log` - Main installation log with timestamps
- `install_output.log` - Raw command output
- `install.pid` - Background process PID (async mode)
- `install.status` - Installation status: running/success/failed/timeout

A symlink `latest` points to the most recent log directory.

## Supported Installation Methods

| Method | Description | Best For |
|--------|-------------|----------|
| `auto` (default) | Auto-detect best method based on OS | Most cases |
| `installer` | Official install.sh script | Standard Linux/macOS |
| `cli` | install-cli.sh (local prefix) | No system Node dependency |
| `npm` | npm install -g openclaw | Node 22+ already installed |
| `pnpm` | pnpm add -g openclaw | pnpm users |
| `docker` | Docker container | Containerized deployments |
| `podman` | Podman rootless container | Rootless environments |

## Usage

### Quick Start (Auto-detect)

```bash
./scripts/install_openclaw_remote.sh <host> <user> <key_path>
```

### Async Installation (Recommended for long-running installs)

```bash
# Run installation in background with progress monitoring
./scripts/install_openclaw_remote.sh <host> <user> <key_path> --async

# Monitor in real-time
tail -f ~/.openclaw/remote-install-logs/latest/install_output.log

# Check status
cat ~/.openclaw/remote-install-logs/latest/install_status
```

### With Password

```bash
./scripts/install_openclaw_remote.sh <host> <user> <password> --password-based
```

### Force Specific Method

```bash
# Docker installation
./scripts/install_openclaw_remote.sh <host> <user> <key_path> --docker

# Podman installation
./scripts/install_openclaw_remote.sh <host> <user> <key_path> --podman

# npm method (if Node 22+ available)
./scripts/install_openclaw_remote.sh <host> <user> <key_path> --method npm
```

### Non-interactive (Automation)

```bash
./scripts/install_openclaw_remote.sh <host> <user> <key_path> \
  --non-interactive \
  --configure
```

### Custom Log Directory

```bash
./scripts/install_openclaw_remote.sh <host> <user> <key_path> \
  --log-dir /path/to/custom/logs
```

## Auto-Detection Logic

The installer automatically selects the best method:

1. **If `--docker` or `--podman` flag**: Use container method (if available)
2. **If Node 22+ installed**: Use `pnpm` or `npm` method
3. **Otherwise**: Use official `install.sh` script

## Supported Operating Systems

- **Ubuntu/Debian** (apt)
- **Fedora/RHEL/CentOS** (dnf/yum)
- **Alpine** (apk)
- **Arch Linux** (pacman)
- **macOS** (Homebrew)
- **Windows** (WSL2) - via installer script

## Post-Installation

```bash
# SSH into remote server
ssh user@host

# Check status
openclaw status

# Run diagnostics
openclaw doctor

# Configure (models, channels, etc.)
openclaw configure

# Or use Python script for non-interactive config
python3 scripts/configure_openclaw_remote.py <host> <user> \
  --auth <key> --key-based --configure \
  --auth-choice openai-api-key --api-key "your-key"
```

## Configuration Options

### Auth Providers (via Python script)

- `openai-api-key` - OpenAI API
- `anthropic-api-key` - Anthropic API
- `custom-api-key` - Custom OpenAI-compatible endpoint
- `azure-openai` - Azure OpenAI
- `google-ai` - Google AI (Gemini)
- `mistral-api-key` - Mistral AI
- `zai-api-key` - Z.AI endpoints

### Secret Modes

- `plaintext` - Store directly in config (not recommended)
- `ref` - Environment variable reference (recommended)

### Gateway Modes

```bash
--gateway-mode local    # Local gateway (default)
--gateway-mode remote   # Remote gateway
--gateway-port 18789
```

## Environment Variables

For secure non-interactive configuration:

```bash
export OPENAI_API_KEY="sk-..."
export ANTHROPIC_API_KEY="sk-ant-..."
export CUSTOM_API_KEY="your-key"
```

Then use `--secret-mode ref` to reference them securely.

## Troubleshooting

### SSH Issues

```bash
# Check key permissions
chmod 600 ~/.ssh/id_rsa

# Add to ssh-agent
ssh-add ~/.ssh/id_rsa
```

### Installation Issues

- Ensure curl is installed
- Check Node.js 22+ requirement (for non-Docker methods)
- Review logs: `~/.openclaw/logs/`

### Docker Issues

```bash
# Check Docker status
docker ps

# View logs
docker logs openclaw

# Restart container
docker restart openclaw
```


---

## Referenced Files

> The following files are referenced in this skill and included for context.

### scripts/install_openclaw_remote.sh

```bash
#!/bin/bash
# Remote OpenClaw Installation Script
# Automatically selects best installation method based on OS and environment
# Supports async execution with log monitoring

set -e

# Default variables
HOST=""
USER=""
AUTH=""
SSH_PORT=22
AUTH_TYPE="key"  # key or password
METHOD=""  # auto-detect if empty
VERSION="latest"
CONFIGURE=false
NON_INTERACTIVE=false
DOCKER=false
PODMAN=false
ASYNC_MODE=false
LOG_DIR=""

# Color codes
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m'

log_info() { 
    echo -e "${GREEN}[INFO]${NC} $1"
    [[ -n "$LOG_FILE" ]] && echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] [INFO] $1" >> "$LOG_FILE"
}
log_warn() { 
    echo -e "${YELLOW}[WARN]${NC} $1"
    [[ -n "$LOG_FILE" ]] && echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] [WARN] $1" >> "$LOG_FILE"
}
log_error() { 
    echo -e "${RED}[ERROR]${NC} $1"
    [[ -n "$LOG_FILE" ]] && echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] [ERROR] $1" >> "$LOG_FILE"
}
log_debug() {
    echo -e "${BLUE}[DEBUG]${NC} $1"
    [[ -n "$LOG_FILE" ]] && echo -e "[$(date '+%Y-%m-%d %H:%M:%S')] [DEBUG] $1" >> "$LOG_FILE"
}

# Setup log directory
setup_log_dir() {
    local timestamp=$(date '+%Y%m%d_%H%M%S')
    local host_safe=$(echo "$HOST" | tr '.' '_' | tr '@' '_')
    
    if [[ -z "$LOG_DIR" ]]; then
        LOG_DIR="$HOME/.openclaw/remote-install-logs/${host_safe}_${timestamp}"
    fi
    
    mkdir -p "$LOG_DIR"
    LOG_FILE="$LOG_DIR/install.log"
    
    # Create symlink to latest
    local latest_link="$HOME/.openclaw/remote-install-logs/latest"
    rm -f "$latest_link"
    ln -sf "$LOG_DIR" "$latest_link"
    
    log_info "Log directory: $LOG_DIR"
    log_info "Log file: $LOG_FILE"
}

show_help() {
    cat << EOF
OpenClaw Remote Installer
Usage: $0 <host> <user> <auth> [options]

Arguments:
  host              Remote server hostname/IP
  user              SSH username
  auth              SSH password or key path

Options:
  --port <port>         SSH port (default: 22)
  --method <method>     Installation method: auto, npm, git, docker, podman, cli
                        auto = auto-detect based on OS (default)
  --version <ver>       OpenClaw version (default: latest)
  --key-based           Use SSH key authentication
  --password-based      Use password authentication
  --docker              Use Docker installation
  --podman              Use Podman installation
  --configure           Run configuration after installation
  --non-interactive     Run in non-interactive mode
  --async               Run installation asynchronously with progress monitoring
  --log-dir <path>      Custom log directory (default: ~/.openclaw/remote-install-logs/)
  --help                Show this help

Examples:
  # Auto-detect best method
  $0 [email protected] user ~/.ssh/id_rsa

  # Async installation with monitoring
  $0 [email protected] user ~/.ssh/id_rsa --async

  # Force Docker installation
  $0 [email protected] user ~/.ssh/id_rsa --docker

  # Non-interactive with custom auth
  $0 [email protected] pass "mypassword" --password-based --non-interactive

EOF
    exit 0
}

# Parse arguments
if [[ $# -lt 3 ]]; then
    show_help
fi

HOST="$1"
USER="$2"
AUTH="$3"
shift 3

while [[ $# -gt 0 ]]; do
    case "$1" in
        --port) SSH_PORT="$2"; shift 2 ;;
        --method) METHOD="$2"; shift 2 ;;
        --version) VERSION="$2"; shift 2 ;;
        --key-based) AUTH_TYPE="key"; shift ;;
        --password-based) AUTH_TYPE="password"; shift ;;
        --docker) DOCKER=true; shift ;;
        --podman) PODMAN=true; shift ;;
        --configure) CONFIGURE=true; shift ;;
        --non-interactive) NON_INTERACTIVE=true; shift ;;
        --async) ASYNC_MODE=true; shift ;;
        --log-dir) LOG_DIR="$2"; shift 2 ;;
        --help) show_help ;;
        *) shift ;;
    esac
done

# Build SSH command
build_ssh_cmd() {
    local cmd_type="$1"
    if [[ "$AUTH_TYPE" == "password" ]]; then
        if ! command -v sshpass &> /dev/null; then
            log_error "sshpass required for password authentication"
            log_info "Install: apt-get install sshpass (Debian/Ubuntu) or brew install sshpass (macOS)"
            exit 1
        fi
        if [[ "$cmd_type" == "ssh" ]]; then
            echo "sshpass -p '$AUTH' ssh -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $SSH_PORT $USER@$HOST"
        else
            echo "sshpass -p '$AUTH' scp -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P $SSH_PORT"
        fi
    else
        if [[ "$cmd_type" == "ssh" ]]; then
            echo "ssh -i '$AUTH' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -p $SSH_PORT $USER@$HOST"
        else
            echo "scp -i '$AUTH' -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -P $SSH_PORT"
        fi
    fi
}

SSH_CMD=$(build_ssh_cmd "ssh")
SCP_CMD=$(build_ssh_cmd "scp")

# Setup logging
setup_log_dir

echo "=========================================="
echo "  OpenClaw Remote Installer"
echo "=========================================="
log_info "Host: $USER@$HOST:$SSH_PORT"
log_info "Method: ${METHOD:-auto-detect}"
log_info "Async Mode: $ASYNC_MODE"
echo "=========================================="

# Check connectivity
log_info "Checking SSH connectivity..."
if ! $SSH_CMD "echo 'SSH OK'" &>/dev/null; then
    log_error "Failed to connect to $HOST"
    exit 1
fi
log_info "SSH connection successful"

# Detect OS
log_info "Detecting remote OS..."
OS_INFO=$($SSH_CMD "cat /etc/os-release 2>/dev/null")
OS_ID=$(echo "$OS_INFO" | grep "^ID=" | cut -d= -f2 | tr -d '"')
OS_VERSION=$(echo "$OS_INFO" | grep "^VERSION_ID=" | cut -d= -f2 | tr -d '"')
OS_NAME=$(echo "$OS_INFO" | grep "^NAME=" | cut -d= -f2 | tr -d '"')

# Detect package manager
if $SSH_CMD "command -v apt-get &> /dev/null" &>/dev/null; then
    PKG_MGR="apt"
elif $SSH_CMD "command -v dnf &> /dev/null" &>/dev/null; then
    PKG_MGR="dnf"
elif $SSH_CMD "command -v yum &> /dev/null" &>/dev/null; then
    PKG_MGR="yum"
elif $SSH_CMD "command -v apk &> /dev/null" &>/dev/null; then
    PKG_MGR="apk"
elif $SSH_CMD "command -v pacman &> /dev/null" &>/dev/null; then
    PKG_MGR="pacman"
else
    PKG_MGR="unknown"
fi

log_info "OS: $OS_NAME (ID: $OS_ID, Version: $OS_VERSION)"
log_info "Package Manager: $PKG_MGR"

# Check if OpenClaw already installed
if $SSH_CMD "command -v openclaw &> /dev/null" 2>/dev/null; then
    EXISTING_VERSION=$($SSH_CMD "openclaw --version 2>/dev/null || echo 'unknown'")
    log_warn "OpenClaw already installed: $EXISTING_VERSION"
    if [[ "$ASYNC_MODE" != "true" ]]; then
        read -p "Update? [y/N] " -n 1 -r
        echo
        if [[ ! $REPLY =~ ^[Yy]$ ]]; then
            log_info "Installation cancelled"
            exit 0
        fi
    fi
fi

# Auto-detect best installation method
detect_install_method() {
    local detected=""
    
    # Docker option
    if [[ "$DOCKER" == "true" ]]; then
        if $SSH_CMD "command -v docker &> /dev/null" 2>/dev/null; then
            echo "docker"
            return
        else
            log_warn "Docker not installed, falling back to installer script"
        fi
    fi
    
    # Podman option
    if [[ "$PODMAN" == "true" ]]; then
        if $SSH_CMD "command -v podman &> /dev/null" 2>/dev/null; then
            echo "podman"
            return
        else
            log_warn "Podman not installed, falling back to installer script"
        fi
    fi
    
    # Check if Node.js 22+ is available
    if $SSH_CMD "command -v node &> /dev/null" 2>/dev/null; then
        NODE_VERSION=$($SSH_CMD "node -v" | sed 's/v//')
        NODE_MAJOR=$(echo "$NODE_VERSION" | cut -d. -f1)
        if [[ "$NODE_MAJOR" -ge 22 ]]; then
            if $SSH_CMD "command -v pnpm &> /dev/null" 2>/dev/null; then
                detected="pnpm"
            elif $SSH_CMD "command -v npm &> /dev/null" 2>/dev/null; then
                detected="npm"
            fi
        fi
    fi
    
    # Default: installer script
    if [[ -z "$detected" ]]; then
        detected="installer"
    fi
    
    echo "$detected"
}

if [[ -z "$METHOD" ]] || [[ "$METHOD" == "auto" ]]; then
    METHOD=$(detect_install_method)
fi

log_info "Selected installation method: $METHOD"

# Prepare installation command
prepare_install_cmd() {
    local install_cmd=""
    
    case "$METHOD" in
        docker)
            log_info "Preparing Docker installation..."
            # Check if Docker is available
            if ! $SSH_CMD "command -v docker &> /dev/null" 2>/dev/null; then
                log_info "Installing Docker first..."
                install_cmd="curl -fsSL https://get.docker.com | sh"
            fi
            # Run OpenClaw Docker
            install_cmd="docker run -d --name openclaw \
                -v ~/.openclaw:/home/node/.openclaw \
                -p 18789:18789 \
                openclawai/openclaw:latest"
            ;;
            
        podman)
            log_info "Preparing Podman installation..."
            if ! $SSH_CMD "command -v podman &> /dev/null" 2>/dev/null; then
                log_info "Installing Podman first..."
                case "$PKG_MGR" in
                    apt) install_cmd="apt-get update && apt-get install -y podman" ;;
                    dnf|yum) install_cmd="$PKG_MGR install -y podman" ;;
                    apk) install_cmd="apk add podman" ;;
                esac
            fi
            install_cmd="podman run -d --name openclaw \
                -v ~/.openclaw:/home/node/.openclaw \
                -p 18789:18789 \
                openclawai/openclaw:latest"
            ;;
            
        pnpm)
            log_info "Preparing pnpm installation..."
            if [[ "$NON_INTERACTIVE" == "true" ]]; then
                install_cmd="pnpm add -g openclaw@$VERSION && pnpm approve-builds -g openclaw"
            else
                install_cmd="pnpm add -g openclaw@$VERSION"
            fi
            ;;
            
        npm)
            log_info "Preparing npm installation..."
            install_cmd="npm install -g openclaw@$VERSION"
            ;;
            
        cli|install-cli)
            log_info "Preparing install-cli.sh installation..."
            install_cmd="curl -fsSL https://openclaw.ai/install-cli.sh | bash -s -- --version $VERSION"
            if [[ "$NON_INTERACTIVE" == "true" ]]; then
                install_cmd="$install_cmd --no-onboard"
            fi
            ;;
            
        installer|*)
            log_info "Preparing installer script installation..."
            install_cmd="curl -fsSL https://openclaw.ai/install.sh | bash -s --"
            if [[ "$VERSION" != "latest" ]]; then
                install_cmd="$install_cmd --version $VERSION"
            fi
            if [[ "$NON_INTERACTIVE" == "true" ]]; then
                install_cmd="$install_cmd --no-prompt --no-onboard"
            fi
            ;;
    esac
    
    echo "$install_cmd"
}

# Async installation with progress monitoring
run_async_install() {
    local install_cmd="$1"
    local pid_file="$LOG_DIR/install.pid"
    local status_file="$LOG_DIR/install.status"
    local output_file="$LOG_DIR/install_output.log"
    
    echo "running" > "$status_file"
    
    log_info "Starting async installation..."
    log_info "Monitor with: tail -f $LOG_DIR/install_output.log"
    log_info "Check status: cat $LOG_DIR/install_status"
    
    # Run installation in background, capture output
    (
        set -e
        {
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting installation..."
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] Command: $install_cmd"
            echo "---"
            $SSH_CMD "$install_cmd" 2>&1
            exit_code=$?
            echo "---"
            echo "[$(date '+%Y-%m-%d %H:%M:%S')] Installation finished with exit code: $exit_code"
            exit $exit_code
        } | tee "$output_file"
    ) &
    
    local bg_pid=$!
    echo $bg_pid > "$pid_file"
    
    log_info "Installation running in background (PID: $bg_pid)"
    log_info "Log file: $output_file"
    
    # Monitor progress
    monitor_installation "$pid_file" "$status_file" "$output_file"
}

# Monitor installation progress
monitor_installation() {
    local pid_file="$1"
    local status_file="$2"
    local output_file="$3"
    local check_interval=5
    local max_wait=3600  # 1 hour max
    local elapsed=0
    local spinner=('|' '/' '-' '\\')
    local spin_idx=0
    
    echo ""
    log_info "Monitoring installation progress..."
    echo "=========================================="
    
    while [[ $elapsed -lt $max_wait ]]; do
        # Check if process is still running
        if [[ -f "$pid_file" ]]; then
            local pid=$(cat "$pid_file" 2>/dev/null)
            if ! kill -0 "$pid" 2>/dev/null; then
                # Process finished
                wait "$pid"
                local exit_code=$?
                
                if [[ $exit_code -eq 0 ]]; then
                    echo "success" > "$status_file"
                    echo ""
                    echo "=========================================="
                    log_info "Installation completed successfully!"
                    echo "=========================================="
                else
                    echo "failed" > "$status_file"
                    echo ""
                    echo "=========================================="
                    log_error "Installation failed with exit code: $exit_code"
                    echo "=========================================="
                fi
                
                log_info "Final output saved to: $output_file"
                return $exit_code
            fi
        fi
        
        # Show last few lines of output
        if [[ -f "$output_file" ]]; then
            local last_line=$(tail -1 "$output_file" 2>/dev/null)
            printf "\r${CYAN}[%s]${NC} Checking... Last: %s" \
                "${spinner[$((spin_idx % 4))]}" \
                "${last_line:0:50}"
        fi
        
        sleep $check_interval
        elapsed=$((elapsed + check_interval))
        spin_idx=$((spin_idx + 1))
    done
    
    echo ""
    log_warn "Installation timeout reached ($max_wait seconds)"
    echo "timeout" > "$status_file"
    return 1
}

# Sync installation (original behavior)
run_sync_install() {
    local install_cmd="$1"
    local output_file="$LOG_DIR/install_output.log"
    
    log_info "Running installation (synchronous)..."
    {
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Starting installation..."
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Command: $install_cmd"
        echo "---"
        $SSH_CMD "$install_cmd" 2>&1
        exit_code=$?
        echo "---"
        echo "[$(date '+%Y-%m-%d %H:%M:%S')] Installation finished with exit code: $exit_code"
    } | tee "$output_file"
    
    return $exit_code
}

# Execute installation
INSTALL_CMD=$(prepare_install_cmd)

if [[ "$ASYNC_MODE" == "true" ]]; then
    run_async_install "$INSTALL_CMD"
    EXIT_CODE=$?
else
    run_sync_install "$INSTALL_CMD"
    EXIT_CODE=$?
fi

if [[ $EXIT_CODE -ne 0 ]]; then
    log_error "Installation failed with exit code: $EXIT_CODE"
    log_info "Check logs: $LOG_DIR/install_output.log"
    exit $EXIT_CODE
fi

# Verify installation
log_info "Verifying installation..."
sleep 2

if [[ "$METHOD" == "docker" ]] || [[ "$METHOD" == "podman" ]]; then
    if $SSH_CMD "docker ps | grep -q openclaw" 2>/dev/null; then
        log_info "OpenClaw container is running!"
        log_info "Container logs: $SSH_CMD docker logs openclaw"
    else
        log_error "Container failed to start"
        exit 1
    fi
else
    if $SSH_CMD "command -v openclaw &> /dev/null" 2>/dev/null; then
        VERSION_INSTALLED=$($SSH_CMD "openclaw --version")
        log_info "OpenClaw installed successfully: $VERSION_INSTALLED"
    else
        log_error "Installation verification failed"
        exit 1
    fi
fi

# Configuration (optional)
if [[ "$CONFIGURE" == "true" ]]; then
    log_info "Configuration requested..."
    if [[ "$NON_INTERACTIVE" == "true" ]]; then
        log_warn "Non-interactive configuration requires additional setup"
        log_info "Run 'openclaw configure' manually on remote host"
    else
        log_info "Starting configuration wizard..."
        $SSH_CMD "openclaw configure"
    fi
fi

echo "=========================================="
echo "  Installation Complete!"
echo "=========================================="
log_info "Connect to remote: ssh $USER@$HOST"
log_info "Check status: $SSH_CMD openclaw status"
log_info "Run dashboard: $SSH_CMD openclaw dashboard"
log_info "Log directory: $LOG_DIR"
echo "=========================================="

```

### scripts/configure_openclaw_remote.py

```python
#!/usr/bin/env python3
"""
OpenClaw Remote Configuration Script
Handles non-interactive configuration of OpenClaw after installation.
"""

import argparse
import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Optional, Dict, Any


def run_ssh_command(ssh_cmd: str, command: str) -> tuple[str, str, int]:
    """Execute command via SSH and return stdout, stderr, returncode."""
    full_cmd = f"{ssh_cmd} '{command}'"
    result = subprocess.run(
        full_cmd, shell=True, capture_output=True, text=True
    )
    return result.stdout, result.stderr, result.returncode


def check_openclaw_installed(ssh_cmd: str) -> bool:
    """Check if OpenClaw is installed on remote host."""
    _, _, code = run_ssh_command(ssh_cmd, "command -v openclaw")
    return code == 0


def get_remote_config_path(ssh_cmd: str) -> str:
    """Get the config file path from remote host."""
    stdout, _, _ = run_ssh_command(
        ssh_cmd, "echo $OPENCLAW_CONFIG_PATH"
    )
    path = stdout.strip()
    if not path:
        # Default location
        return "~/.openclaw/config.yaml"
    return path


def configure_non_interactive(
    ssh_cmd: str,
    auth_choice: str,
    api_key: Optional[str] = None,
    base_url: Optional[str] = None,
    model_id: Optional[str] = None,
    secret_mode: str = "plaintext",
) -> bool:
    """
    Configure OpenClaw non-interactively.
    
    Args:
        ssh_cmd: SSH command prefix
        auth_choice: Auth provider choice (e.g., 'openai-api-key', 'custom-api-key')
        api_key: API key for the provider
        base_url: Base URL for custom provider
        model_id: Model ID for custom provider
        secret_mode: 'plaintext' or 'ref' for secret references
    """
    cmd_parts = [
        "openclaw onboard --non-interactive",
        f"--auth-choice {auth_choice}",
    ]
    
    if api_key:
        if secret_mode == "ref":
            # Use environment variable reference
            env_var = f"{auth_choice.upper().replace('-', '_')}_KEY"
            cmd_parts.append(f"--secret-input-mode ref")
            # Note: The API key should be set as environment variable before calling
            print(f"Note: Set {env_var} environment variable before running")
        else:
            # Inline API key (less secure)
            cmd_parts.append(f"--{auth_choice}-api-key '{api_key}'")
    
    if base_url:
        cmd_parts.append(f"--custom-base-url '{base_url}'")
    
    if model_id:
        cmd_parts.append(f"--custom-model-id '{model_id}'")
    
    cmd_parts.append("--accept-risk")
    
    full_cmd = " ".join(cmd_parts)
    print(f"Running: {full_cmd}")
    
    stdout, stderr, code = run_ssh_command(ssh_cmd, full_cmd)
    
    if code != 0:
        print(f"Configuration failed: {stderr}", file=sys.stderr)
        return False
    
    print("Configuration completed successfully!")
    return True


def update_config_file(
    ssh_cmd: str,
    config_updates: Dict[str, Any],
) -> bool:
    """
    Update OpenClaw config file directly.
    
    Args:
        ssh_cmd: SSH command prefix
        config_updates: Dictionary of config updates
    """
    config_path = get_remote_config_path(ssh_cmd)
    
    # Read current config or create new
    read_cmd = f"cat {config_path} 2>/dev/null || echo ''"
    current_config, _, _ = run_ssh_command(ssh_cmd, read_cmd)
    
    # Merge updates (simplified - just print what would be updated)
    print(f"Current config at {config_path}:")
    print(current_config[:500] if current_config else "(empty)")
    
    print("\nRequested updates:")
    print(json.dumps(config_updates, indent=2))
    
    # For actual update, we'd need to parse YAML and merge
    # This is a placeholder for the full implementation
    print("\nNote: Direct config file update requires YAML parsing")
    return True


def setup_gateway(
    ssh_cmd: str,
    mode: str = "local",
    port: int = 18789,
    bind: str = "127.0.0.1",
) -> bool:
    """
    Configure the Gateway mode.
    
    Args:
        ssh_cmd: SSH command prefix
        mode: 'local' or 'remote'
        port: Gateway port
        bind: Bind address
    """
    cmd = f"openclaw config set gateway.mode {mode}"
    if mode == "remote":
        cmd += f" && openclaw config set gateway.port {port} && openclaw config set gateway.bind {bind}"
    
    stdout, stderr, code = run_ssh_command(ssh_cmd, cmd)
    
    if code != 0:
        print(f"Gateway setup failed: {stderr}", file=sys.stderr)
        return False
    
    print(f"Gateway configured: mode={mode}, port={port}, bind={bind}")
    return True


def add_channel(
    ssh_cmd: str,
    channel_type: str,
    **channel_config,
) -> bool:
    """
    Add a messaging channel.
    
    Args:
        ssh_cmd: SSH command prefix
        channel_type: Type of channel (telegram, discord, slack, etc.)
        **channel_config: Channel-specific configuration
    """
    # Build the channel add command
    cmd = f"openclaw channels add {channel_type}"
    
    for key, value in channel_config.items():
        cmd += f" --{key} '{value}'"
    
    stdout, stderr, code = run_ssh_command(ssh_cmd, cmd)
    
    if code != 0:
        print(f"Channel add failed: {stderr}", file=sys.stderr)
        return False
    
    print(f"Channel '{channel_type}' added successfully!")
    return True


def main():
    parser = argparse.ArgumentParser(
        description="Configure OpenClaw on remote host"
    )
    
    parser.add_argument("host", help="Remote host")
    parser.add_argument("user", help="SSH user")
    parser.add_argument(
        "--auth", "-a", 
        help="SSH password or key path"
    )
    parser.add_argument(
        "--port", "-p",
        type=int,
        default=22,
        help="SSH port (default: 22)"
    )
    parser.add_argument(
        "--key-based",
        action="store_true",
        help="Use SSH key authentication"
    )
    parser.add_argument(
        "--password-based",
        action="store_true",
        help="Use password authentication"
    )
    
    # Configuration options
    parser.add_argument(
        "--configure",
        action="store_true",
        help="Run configuration wizard"
    )
    parser.add_argument(
        "--auth-choice",
        choices=[
            "openai-api-key",
            "anthropic-api-key",
            "custom-api-key",
            "azure-openai",
            "google-ai",
            "mistral-api-key",
            "zai-api-key",
        ],
        help="Auth provider choice"
    )
    parser.add_argument("--api-key", help="API key for the provider")
    parser.add_argument("--base-url", help="Base URL for custom provider")
    parser.add_argument("--model-id", help="Model ID for custom provider")
    parser.add_argument(
        "--secret-mode",
        choices=["plaintext", "ref"],
        default="plaintext",
        help="Secret storage mode"
    )
    
    # Gateway options
    parser.add_argument(
        "--gateway-mode",
        choices=["local", "remote"],
        help="Gateway mode"
    )
    parser.add_argument(
        "--gateway-port",
        type=int,
        default=18789,
        help="Gateway port"
    )
    parser.add_argument(
        "--gateway-bind",
        default="127.0.0.1",
        help="Gateway bind address"
    )
    
    # Channel options
    parser.add_argument(
        "--add-channel",
        help="Add a channel (telegram, discord, slack, etc.)"
    )
    
    args = parser.parse_args()
    
    # Build SSH command
    if args.password_based or not args.key_based:
        if not args.auth:
            print("Error: Auth required (password or key path)")
            sys.exit(1)
        import shlex
        ssh_cmd = f"sshpass -p {shlex.quote(args.auth)} ssh -o StrictHostKeyChecking=no -p {args.port} {args.user}@{args.host}"
    else:
        ssh_cmd = f"ssh -i '{args.auth}' -o StrictHostKeyChecking=no -p {args.port} {args.user}@{args.host}"
    
    # Check if OpenClaw is installed
    if not check_openclaw_installed(ssh_cmd):
        print("Error: OpenClaw is not installed on remote host")
        print("Run install_openclaw_remote.sh first")
        sys.exit(1)
    
    # Run configuration
    if args.configure and args.auth_choice:
        success = configure_non_interactive(
            ssh_cmd,
            args.auth_choice,
            args.api_key,
            args.base_url,
            args.model_id,
            args.secret_mode,
        )
        sys.exit(0 if success else 1)
    
    # Setup gateway
    if args.gateway_mode:
        success = setup_gateway(
            ssh_cmd,
            args.gateway_mode,
            args.gateway_port,
            args.gateway_bind,
        )
        sys.exit(0 if success else 1)
    
    # Add channel
    if args.add_channel:
        # Parse channel config from remaining args
        print(f"Adding channel: {args.add_channel}")
        print("Note: Channel configuration requires additional parameters")
    
    print("No action specified. Use --help for options")


if __name__ == "__main__":
    main()

```



---

## Skill Companion Files

> Additional files collected from the skill directory layout.

### _meta.json

```json
{
  "owner": "codeblackhole1024",
  "slug": "openclaw-remote-install",
  "displayName": "Openclaw Remote Install",
  "latest": {
    "version": "1.0.1",
    "publishedAt": 1772870054924,
    "commit": "https://github.com/openclaw/skills/commit/7d5866bb49994f636110b7a82951b8e851983b40"
  },
  "history": [
    {
      "version": "1.1.0",
      "publishedAt": 1772764158437,
      "commit": "https://github.com/openclaw/skills/commit/ff2a74695a12ad7d4733dbd2c61561cd7b31dde8"
    }
  ]
}

```

openclaw-remote-install | SkillHub