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.
Install command
npx @skill-hub/cli install openclaw-skills-openclaw-remote-install
Repository
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 repositoryBest 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
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"
}
]
}
```