Windows 10 (Version 2004+ or higher) or Windows 11.
WSL 2 enabled (You currently have WSL 2 enabled with Docker Desktop).
Step 1: Install Ubuntu Distribution
Since you currently only have docker-desktop installed, it is highly recommended to install a full Linux distribution like Ubuntu for a stable OpenClaw environment.
Open PowerShell as Administrator.
Run the following command to install Ubuntu 22.04 LTS:
wsl --install -d Ubuntu-22.04
Restart your computer if prompted.
After installation, a new terminal window will open. You will be asked to create a username and password for your Linux system. Remember these credentials.
Step 2: Environment Setup
Once you are logged into your Ubuntu terminal (you can open it by searching for "Ubuntu" in the Start menu or running wsl -d Ubuntu-22.04 in PowerShell), update the system and install necessary tools.
After the OpenClaw service is running and configured, you need to pair your Feishu account with the bot.
Send a Message to Your Bot:
In Feishu, find your bot and send any message (e.g., "hello").
Get Pairing Code:
The bot will reply with an "Access not configured" message containing your User ID and a Pairing Code.
OpenClaw: access not configured.
Your Feishu user id: ou_xxxxxxxx
Pairing code: 123456
Approve Pairing:
Run the following command in your WSL terminal (replace <pairing_code> with the code from the bot):
openclaw pairing approve feishu <pairing_code>
Verification:
The bot should reply: "You have been approved as owner." You can now start using OpenClaw!
Note on Webhook Configuration (For Developers)
If you are deploying for public use or need event subscriptions, you would typically configure the "Request URL" in the Feishu Developer Console. However, for personal bot usage, the pairing method above is sufficient for initial setup.
Note: Since you are running on WSL, accessing the service from outside your local network (for Feishu webhooks) requires exposing your local port (usually 8080 or defined in config) to the internet. Tools like ngrok or cloudflared are recommended for development.
Troubleshooting
If you encounter issues like "Duplicate plugin id", "Service config looks out of date", or "RPC probe failed", follow these steps:
1. Fix Service Configuration
The service manager might need a repair to correctly locate the OpenClaw executable.
openclaw doctor --repair
2. Fix Duplicate Feishu Plugin
If you see warnings about duplicate plugin IDs (e.g., in ~/.openclaw/extensions/feishu/index.ts), it means the plugin is installed in two places (manually and via package manager). Remove the local extension folder:
rm -rf ~/.openclaw/extensions/feishu
Then reinstall the official plugin to be safe:
openclaw plugins install @openclaw/feishu
3. Expose Gateway for WSL
By default, OpenClaw listens on loopback (127.0.0.1). To allow access from Windows, change the bind mode to lan (which binds to 0.0.0.0/all interfaces). Additionally, you must set the gateway mode to local.
openclaw config set gateway.bind "lan"
openclaw config set gateway.mode "local"
(Note: Use lan mode instead of legacy IP addresses like 0.0.0.0)
4. Restart Service
After applying fixes, restart the gateway:
openclaw gateway restart
openclaw gateway status
5. Windows Host Proxy Issues (LLM Timeout & TLS Handshake)
If you use proxy software (e.g., Clash Verge) with TUN mode enabled, but WSL requests to LLM (e.g., Doubao) timeout or hang during TLS handshake:
Step 1: Clean Up Proxy Environment Variables
Remove any manual proxy settings to let TUN mode handle traffic correctly.
Edit ~/.bashrc and remove any export http_proxy=... lines. Run source ~/.bashrc.
Edit ~/.config/systemd/user/openclaw-gateway.service and remove any Environment=HTTP_PROXY=... lines. Run systemctl --user daemon-reload and openclaw gateway restart.
Step 2: Configure WSL to Disable Auto-Proxy
Prevent WSL from automatically inheriting Windows proxy settings.
Create/Edit %USERPROFILE%\.wslconfig in Windows.
Add the following configuration:
[wsl2]autoProxy=false# networkingMode=mirrored (Optional, if you use mirrored mode)
Restart WSL: wsl --shutdown.
Step 3: Configure Clash Verge (Crucial)
Enable TUN Mode: Ensure TUN mode is on.
Fix DNS/Fake IP: Go to Settings -> TUN Mode -> Fake IP Filter, add +.volces.com to the list. This ensures the domain resolves to a real IP instead of a fake one.
Fix TLS Handshake Hangs: Go to Settings -> TUN Mode, change Stack to gvisor (or system if gvisor fails). This fixes compatibility issues where packets are dropped during TLS handshake.
Restart Clash Core: You must restart the Clash kernel for changes to take effect.
Verification:
Run curl -I https://ark.cn-beijing.volces.com in WSL. It should return HTTP 200/404 immediately without hanging.
Appendix: Restarting WSL (If Service Hangs)
If restarting the service doesn't work, you may need to fully restart the WSL instance.
Shutdown WSL (Run in PowerShell as Admin):
wsl --shutdown
Wait a few seconds for the subsystem to stop completely.
Start WSL Again:
Open a new Ubuntu terminal or run:
wsl -d Ubuntu-22.04
Start OpenClaw:
Once inside WSL, start the service:
openclaw gateway start
openclaw gateway status
Check if the Dashboard URL is accessible.
Appendix: Configuration Script
Save the following content as configure_openclaw.py:
170 code lines
import subprocess
import json
import argparse
import sys
defrun_command(command):
try:
result = subprocess.run(command, shell=True, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
return result.stdout.strip()
except subprocess.CalledProcessError:
returnNonedefset_config_recursive(path_parts, full_config_dict):
"""
Starting from root, check each node. If missing, set the node's entire subtree as a JSON string.
"""for i inrange(1, len(path_parts) + 1):
current_path = ".".join(path_parts[:i])
# 1. Check if node exists
check_cmd = f"openclaw config get {current_path}"
output = run_command(check_cmd)
# 2. If node is missing or undefinedifnot output or output == "undefined":
print(f"Path '{current_path}' is missing. Setting its content...")
# Extract the subtree for this node from full_config_dict
subtree = full_config_dict
for part in path_parts[:i]:
subtree = subtree.get(part, {})
# Convert subtree to JSON string
json_value = json.dumps(subtree)
# Execute set command for this node
set_cmd = f"openclaw config set {current_path} '{json_value}'"if run_command(set_cmd) isNone:
print(f"Failed to set node: {current_path}")
else:
print(f"Successfully initialized node: {current_path}")
# Once we've set a node's entire subtree, all its children are implicitly created.breakelse:
print(f"Path '{current_path}' exists.")
defmain():
parser = argparse.ArgumentParser(description="Configure OpenClaw for Doubao or Anthropic")
parser.add_argument("--provider", choices=["doubao", "anthropic"], default="doubao", help="Model provider (doubao or anthropic)")
parser.add_argument("--api-key", required=True, help="API Key for the provider")
parser.add_argument("--endpoint-id", help="Endpoint ID (Required for Doubao, usually the model ID for Anthropic)")
parser.add_argument("--model-name", required=True, help="Model Name (e.g., Doubao-1.8 or claude-3-5-sonnet-20240620)")
parser.add_argument("--feishu-app-id", required=True, help="Feishu App ID")
parser.add_argument("--feishu-app-secret", required=True, help="Feishu App Secret")
args = parser.parse_args()
# Determine provider-specific settings
provider = args.provider
api_key = args.api_key
model_name = args.model_name
feishu_app_id = args.feishu_app_id
feishu_app_secret = args.feishu_app_secret
# For Doubao, endpoint_id is required. For Anthropic, we use model_name as ID if endpoint_id is missing.if provider == "doubao":
ifnot args.endpoint_id:
print("Error: --endpoint-id is required for Doubao provider.")
sys.exit(1)
endpoint_id = args.endpoint_id
base_url = "https://ark.cn-beijing.volces.com/api/v3"
api_type = "openai-completions"
agent_model_ref = f"doubao/{endpoint_id}"
models_config = [{"id": endpoint_id, "name": model_name}]
elif provider == "anthropic":
# Anthropic doesn't need endpoint_id in the same way, usually just model ID.# We'll use endpoint_id if provided, otherwise model_name.
endpoint_id = args.endpoint_id if args.endpoint_id else model_name
base_url = "https://api.anthropic.com"# Default Anthropic API URL
api_type = "anthropic-chat"# Assuming this is the OpenClaw key for Anthropic
agent_model_ref = f"anthropic/{endpoint_id}"
models_config = [{"id": endpoint_id, "name": model_name}]
# Build the configuration dictionary
full_config = {
"auth": {
"profiles": {
f"{provider}:default": {
"provider": provider,
"mode": "api_key"
}
}
},
"models": {
"providers": {
provider: {
"baseUrl": base_url,
"apiKey": api_key,
"api": api_type, # This might differ for Anthropic, assuming OpenClaw supports 'anthropic' or generic OpenAI"models": models_config
}
}
},
"agents": {
"defaults": {
"model": {
"primary": agent_model_ref
},
"models": {
agent_model_ref: provider
},
"workspace": "~/.openclaw/workspace",
"compaction": {
"mode": "safeguard"
},
"maxConcurrent": 4,
"subagents": {
"maxConcurrent": 8
}
}
},
"messages": {
"ackReactionScope": "group-mentions"
},
"commands": {
"native": "auto",
"nativeSkills": "auto",
"restart": True,
"ownerDisplay": "raw"
},
"session": {
"dmScope": "per-channel-peer"
},
"channels": {
"feishu": {
"appId": feishu_app_id,
"appSecret": feishu_app_secret,
"enabled": True
}
},
"plugins": {
"entries": {
"feishu": {
"enabled": True
}
}
}
}
print(f"Starting OpenClaw Hierarchical Configuration for {provider}...")
# Root paths to check
root_paths = [
f"auth.profiles.{provider}:default",
f"models.providers.{provider}",
"agents.defaults",
"messages",
"commands",
"session",
"channels.feishu",
"plugins.entries.feishu"
]
for path in root_paths:
path_parts = path.split('.')
set_config_recursive(path_parts, full_config)
print("\nConfiguration process completed.")
if __name__ == "__main__":
main()