Skill: danzig233/clawdos (version 2.1.1)

--- name: clawdos description: "Windows automation via Clawdos API: screen capture, mouse/keyboard input, window management, file-system operations, and shell command execution. Standalone CLI execution via Python script. Use when the user wants to control or inspect a Windows host remotely." license: "MIT" metadata: openclaw: version: "2.1.1" display_name: "Windows Execution Interface for OpenClaw" author: "DANZIG MOE" emoji: "🐾" python_requires: ">=3.10" dependencies: - "requests>=2.28.0" requires: env: - "CLAWDOS_BASE_URL" - "CLAWDOS_API_KEY" - "CLAWDOS_TIMEOUT" - "CLAWDOS_FS_ROOT_ID" primaryEnv: "CLAWDOS_API_KEY" config_schema: base_url: type: "string" default: "http://127.0.0.1:17171" description: "Clawdos Windows host service address" api_key: type: "string" required: true description: "Clawdos API key" timeout: type: "integer" default: 30 description: "Request timeout in seconds" fs_root_id: type: "string" description: "Sandbox root directory ID for file system operations" --- # Clawdos ## Overview This skill exposes a CLI wrapper around the Clawdos REST API, allowing you to operate a Windows machine securely from OpenClaw via shell commands. Instead of loading tools, use `exec` to call the standalone python script `scripts/clawdos.py`. ### ⚠️ SECURITY & SANDBOX MECHANISM **File System Sandbox Protection:** - All file system operations (`fs_list`, `fs_read`, `fs_write`, `fs_delete`, `fs_move`) are **restricted to a sandboxed root directory** on the Windows host. - The sandbox root is configured via the `CLAWDOS_FS_ROOT_ID` environment variable and enforced server-side. - **The Clawdos service prevents access to files outside the designated sandbox directory.** Path traversal attempts (e.g., `../../../`) are blocked. - This isolation ensures that skill operations cannot accidentally or intentionally access sensitive system files, user documents, or configuration outside the permitted scope. **Network Isolation:** - The Clawdos service only communicates with the configured `CLAWDOS_BASE_URL` and does not establish unauthorized external connections. - All API calls are authenticated via `CLAWDOS_API_KEY` and encrypted over HTTPS when applicable. ### ⚠️ AUTHORIZATION & CAPABILITY WARNINGS This skill grants access to **powerful Windows automation capabilities**. Users must explicitly understand and authorize the following operations: 1. **Shell Command Execution** (`shell_exec`) - Can execute arbitrary PowerShell or cmd commands on the Windows host. - Even within the sandbox, commands can potentially modify system state, install software, or alter configurations. - **Only use with trusted sources and explicit user approval.** 2. **File Deletion** (`fs_delete`) - Permanently removes files and directories within the sandbox. - No recovery mechanism exists once deleted. - **Exercise extreme caution; confirm deletion intent before execution.** 3. **File Upload/Download** (`--file`, `--out`) - The CLI script can read local files and upload them to the Windows host (within sandbox). - The script can download remote files from the Windows host to the agent system. - **Do not use with sensitive files or untrusted remote systems.** 4. **Persistent Screen/Window Monitoring** - Visual actions (`screen_capture`, `window_list`, `window_focus`) can observe active GUI content. - If sensitive information is visible on screen, it may be captured. ### ⚠️ Requirements **This skill requires a corresponding server running on your Windows host.** Ensure the Windows host is running `danzig233/clawdos`. The connection parameters (`CLAWDOS_BASE_URL` and `CLAWDOS_API_KEY`) must be configured via OpenClaw's skill configuration UI or environment variables, as specified in this file's metadata. ## Usage You interact with Clawdos by running the `scripts/clawdos.py` CLI using the `exec` tool. The script will automatically pick up the `CLAWDOS_BASE_URL` and `CLAWDOS_API_KEY` environment variables injected by OpenClaw. **Basic Syntax:** ```bash python3 ~/.nvm/versions/node/v22.22.1/lib/node_modules/openclaw/skills/clawdos/scripts/clawdos.py <action> --args '{"key":"value"}' ``` ### Available Actions #### 1. Visual Navigation & System Check - `health`: Check service status. - `get_env`: Get screen resolution, DPI scale, and active window. - `window_list`: List all open windows. - `window_focus`: Focus a window. Args: `{"titleContains": "..."}` or `{"processName": "..."}` - `screen_capture`: Take a screenshot. Use `--out path/to/save.png` to save binary. Args: `{"format": "png", "quality": 80}` #### 2. Precise Input (Mouse & Keyboard) *(Prioritize keyboard/shell when possible to avoid visual estimation errors)* - `click`: Click the mouse. Args: `{"x": 100, "y": 200, "button": "left"}` - `move`: Move cursor. Args: `{"x": 100, "y": 200}` - `drag`: Drag mouse. Args: `{"fromX": 100, "fromY": 200, "toX": 300, "toY": 400}` - `keys`: Press key combos. Args: `{"combo": ["ctrl", "c"]}` - `type_text`: Type text. Args: `{"text": "hello"}` - `scroll`: Scroll wheel. Args: `{"amount": -500}` - `batch`: Execute multiple input actions sequentially. Args: `{"actions": [...]}` #### 3. File & System Operations - `fs_list`: List directory contents. Args: `{"path": "/"}` - `fs_read`: Read a file (prints raw contents to stdout). Use `--out path/to/save.bin` to save binary files. Args: `{"path": "/hello.txt"}` - `fs_write`: Write to a file. Args: `{"path": "/hello.txt", "content": "hello world"}`. Or use `--file path/to/local.bin` to upload a local binary file. - `fs_mkdir`: Create a directory. Args: `{"path": "/newdir"}` - `fs_delete`: Delete a file or directory. Args: `{"path": "/newdir", "recursive": true}` - `fs_move`: Move or rename. Args: `{"from": "/src", "to": "/dst"}` - `shell_exec`: Run a shell command on the Windows host. Args: `{"command": "dir", "args": ["/w"], "workingDir": ""}` ### Operation Strategy ### Operational Best Practices - **Prefer Keyboard & Shell**: To minimize errors from visual coordinate estimation, prioritize using keyboard shortcuts (`key_combo`, `type_text`) or shell commands (`shell_exec`) over mouse operations whenever possible. - **Targeted Mouse Usage**: Reserve precise mouse operations (`mouse_click`, `mouse_move`, `mouse_drag`) strictly for necessary UI interactions (e.g., clicking a specific button on a web page, navigating a software interface, or focusing an input field). - **Scrolling**: Using `mouse_scroll` is safe and recommended for navigating long pages or documents. ### Security Best Practices - **Verify File Paths**: Always confirm the target path is within the intended sandbox directory. The server enforces isolation, but double-check paths in scripts. - **Audit Shell Commands**: Review `shell_exec` commands before execution. Avoid running commands from untrusted sources. - **File Transfer Restrictions**: Only upload/download files you trust. Do not use `--file` with sensitive credentials or system files. - **Minimize Screen Captures**: Avoid capturing screens if sensitive information (passwords, tokens, personal data) may be visible. - **Explicit Deletion Confirmation**: Review the target path carefully before executing `fs_delete`. Deleted files cannot be recovered. ## Examples **Focus MS Edge and type:** ```bash python3 scripts/clawdos.py window_focus --args '{"processName": "msedge"}' python3 scripts/clawdos.py type_text --args '{"text": "https://openclaw.ai\n"}' ``` **Take a screenshot and save it locally:** ```bash python3 scripts/clawdos.py screen_capture --out /tmp/windows_screen.png --args '{"format":"png"}' ``` **Read a file from Windows:** ```bash python3 scripts/clawdos.py fs_read --args '{"path": "logs/app.log"}' ``` **Execute PowerShell on Windows:** ```bash python3 scripts/clawdos.py shell_exec --args '{"command": "powershell", "args": ["-Command", "Get-Process"]}' ```

path: scripts/clawdos.py
content: #!/usr/bin/env python3
"""
Clawdos CLI - Standalone Windows Automation Client

Usage:
    python clawdos.py health
    python clawdos.py window_list
    python clawdos.py click --args '{"x": 100, "y": 200}'
    ...
"""

import os
import sys
import json
import base64
import argparse
import requests
from pathlib import Path

# Standard OpenClaw injected environment variables via SKILL.md metadata
BASE_URL = os.getenv("CLAWDOS_BASE_URL", "http://127.0.0.1:17171")
[REDACTED_SECRET]"CLAWDOS_API_KEY", "")
TIMEOUT = int(os.getenv("CLAWDOS_TIMEOUT", "30"))
FS_ROOT_ID = int(os.getenv("CLAWDOS_FS_ROOT_ID", "0"))

def get_headers(auth=True):
    if auth:
        if not [REDACTED_SECRET]"error": "CLAWDOS_API_KEY environment variable is not set. Please configure the skill in OpenClaw."}), file=sys.stderr)
            sys.exit(1)
        return {"X-Api-Key": API_KEY}
    return {}

def api_get(path, params=None, auth=True):
    url = f"{BASE_URL}{path}"
    resp = requests.get(url, params=params, headers=get_headers(auth), timeout=TIMEOUT)
    resp.raise_for_status()
    return resp

def api_post(path, payload):
    url = f"{BASE_URL}{path}"
    resp = requests.post(url, json=payload, headers=get_headers(True), timeout=TIMEOUT)
    resp.raise_for_status()
    return resp.json()

def main():
    parser = argparse.ArgumentParser(description="Clawdos REST API CLI")
    parser.add_argument("action", help="Action to perform (e.g., health, get_env, window_list, window_focus, screen_capture, click, move, type_text, fs_list, shell_exec, etc.)")
    parser.add_argument("--args", type=str, default="{}", help="JSON dictionary of arguments for the action")
    parser.add_argument("--out", type=str, help="Output file path (for saving images or binary data)")
    parser.add_argument("--file", type=str, help="Input file path (e.g. for reading local file to fs_write)")

    args = parser.parse_args()
    action = args.action
    
    try:
        kwargs = json.loads(args.args)
    except json.JSONDecodeError as e:
        print(json.dumps({"error": f"Invalid JSON in --args: {e}"}), file=sys.stderr)
        sys.exit(1)

    try:
        if action == "health":
            res = api_get("/v1/health", auth=False).json()
        elif action == "get_env":
            res = api_get("/v1/env").json()
        elif action == "window_list":
            res = api_get("/v1/window/list").json()
        elif action == "window_focus":
            res = api_post("/v1/window/focus", kwargs)
        elif action == "screen_capture":
            resp = api_get("/v1/screen/capture", params=kwargs)
            res_bytes = resp.content
            ct = resp.headers.get("Content-Type", "image/png")
            if args.out:
                with open(args.out, "wb") as f:
                    f.write(res_bytes)
                res = {"success": True, "saved_to": args.out, "content_type": ct}
            else:
                b64 = base64.b64encode(res_bytes).decode("ascii")
                # For stdout, return full base64 so agents can read it
                print(json.dumps({"success": True, "content_type": ct, "image_base64": b64}))
                return
        elif action == "click":
            res = api_post("/v1/input/click", kwargs)
        elif action == "move":
            res = api_post("/v1/input/move", kwargs)
        elif action == "drag":
            res = api_post("/v1/input/drag", kwargs)
        elif action == "keys":
            res = api_post("/v1/input/keys", kwargs)
        elif action == "type_text":
            res = api_post("/v1/input/type", kwargs)
        elif action == "scroll":
            res = api_post("/v1/input/scroll", kwargs)
        elif action == "batch":
            res = api_post("/v1/input/batch", kwargs)
        elif action == "fs_list":
            if "rootId" not in kwargs: kwargs["rootId"] = FS_ROOT_ID
            res = api_get("/v1/fs/list", params=kwargs).json()
        elif action == "fs_read":
            if "rootId" not in kwargs: kwargs["rootId"] = FS_ROOT_ID
            raw = api_get("/v1/fs/read", params=kwargs).json()
            b_data = base64.b64decode(raw["data"])
            if args.out:
                with open(args.out, "wb") as f:
                    f.write(b_data)
                res = {"success": True, "saved_to": args.out}
            else:
                try:
                    res = b_data.decode("utf-8")
                    print(res)
                    return
                except UnicodeDecodeError:
                    print(json.dumps({"error": "File is binary, use --out to save it"}), file=sys.stderr)
                    sys.exit(1)
        elif action == "fs_write":
            if "rootId" not in kwargs: kwargs["rootId"] = FS_ROOT_ID
            if args.file:
                with open(args.file, "rb") as f:
                    b_data = f.read()
                kwargs["data"] = base64.b64encode(b_data).decode("ascii")
                kwargs["encoding"] = "base64"
            elif "content" in kwargs:
                content = kwargs.pop("content")
                kwargs["data"] = base64.b64encode(content.encode("utf-8")).decode("ascii")
                kwargs["encoding"] = "base64"
            res = api_post("/v1/fs/write", kwargs)
        elif action == "fs_mkdir":
            if "rootId" not in kwargs: kwargs["rootId"] = FS_ROOT_ID
            res = api_post("/v1/fs/mkdir", kwargs)
        elif action == "fs_delete":
            if "rootId" not in kwargs: kwargs["rootId"] = FS_ROOT_ID
            res = api_post("/v1/fs/delete", kwargs)
        elif action == "fs_move":
            if "rootId" not in kwargs: kwargs["rootId"] = FS_ROOT_ID
            res = api_post("/v1/fs/move", kwargs)
        elif action == "shell_exec":
            res = api_post("/v1/shell/exec", kwargs)
        else:
            print(json.dumps({"error": f"Unknown action: {action}"}), file=sys.stderr)
            sys.exit(1)
            
        print(json.dumps(res, indent=2))
        
    except requests.exceptions.RequestException as e:
        details = str(e)
        if hasattr(e, "response") and e.response is not None:
            try:
                details += " | Response: " + e.response.text
            except Exception:
                pass
        print(json.dumps({"error": "Request failed", "details": details}), file=sys.stderr)
        sys.exit(1)
    except Exception as e:
        print(json.dumps({"error": str(e)}), file=sys.stderr)
        sys.exit(1)

if __name__ == "__main__":
    main()


Security scan verdicts — ClawScan: clean | static analysis: clean | VirusTotal: clean | SkillSpector: clean (LOW)