#!/bin/bash
TEMPLATES_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../templates" && pwd)"
render_template() {
local tpl_file="$1"
local out_file="$2"
local var_list="${3:-}"
if [ -f "$out_file" ]; then
log_warn "跳过: $out_file (已存在)"
return 0
fi
mkdir -p "$(dirname "$out_file")"
if [ -n "$var_list" ]; then
envsubst "$var_list" < "$tpl_file" > "$out_file"
else
local python_script="/tmp/render_template_$$.py"
cat > "$python_script" << 'PYEOF'
import os
import re
tpl_file = os.environ.get('TPL_FILE', '')
out_file = os.environ.get('OUT_FILE', '')
with open(tpl_file, 'r') as f:
content = f.read()
def replacer(match):
var_expr = match.group(1)
if ':-' in var_expr:
var_name, default = var_expr.split(':-', 1)
return os.environ.get(var_name.strip(), default.strip())
else:
return os.environ.get(var_expr.strip(), match.group(0))
result = re.sub(r'\$\{([^}]+)\}', replacer, content)
with open(out_file, 'w') as f:
f.write(result)
PYEOF
TPL_FILE="$tpl_file" OUT_FILE="$out_file" python3 "$python_script"
rm -f "$python_script"
fi
}
copy_template() {
local tpl_file="$1"
local out_file="$2"
if [ -f "$out_file" ]; then
log_warn "跳过: $out_file (已存在)"
return 0
fi
mkdir -p "$(dirname "$out_file")"
cp "$tpl_file" "$out_file"
}
generate_instance_config() {
local i=$1
local config_dir="$CONFIG_BASE/instance-$i"
export GW_PORT=$((BASE_PORT + (i - 1) * 4))
export SFTP_PORT=$((GW_PORT + 1))
export MDNS_PORT_HOST=$((GW_PORT + 2))
local sftp_password
sftp_password=$(generate_random_password)
if [ "$OPENCLAW_TOKEN_PER_INSTANCE" = "true" ]; then
if [ -f "$config_dir/.gateway_token" ]; then
OPENCLAW_TOKEN=$(cat "$config_dir/.gateway_token")
log_info "实例 $i Gateway Token: (保留现有)"
else
OPENCLAW_TOKEN=$(openssl rand -hex 16)
log_info "实例 $i Gateway Token: $OPENCLAW_TOKEN"
fi
fi
export TIMESTAMP
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%S.000Z 2>/dev/null || date -u +%Y-%m-%dT%H:%M:%SZ)
export OPENCLAW_TOKEN MODEL_NAME MODEL_PROVIDER INFER_URL MDNS_PORT API_KEY
export SUBAGENT_COORDINATOR_DIR SANDBOX_ENABLED
if [ "$MODEL_PROVIDER" != "local" ]; then
export ANTHROPIC_SUFFIX="/anthropic"
else
export ANTHROPIC_SUFFIX=""
fi
if [ "$SANDBOX_ENABLED" = "true" ]; then
export SANDBOX_MODE="all"
else
export SANDBOX_MODE="off"
fi
mkdir -p "$config_dir/agents/main/agent"
mkdir -p "$config_dir/data"
mkdir -p "$config_dir/ssh"
render_template \
"$TEMPLATES_DIR/openclaw.json.tpl" \
"$config_dir/openclaw.json"
render_template \
"$TEMPLATES_DIR/models.json.tpl" \
"$config_dir/agents/main/agent/models.json" \
'${INFER_URL} ${MODEL_NAME} ${MODEL_PROVIDER}'
render_template \
"$TEMPLATES_DIR/ssh/sshd_config.tpl" \
"$config_dir/ssh/sshd_config" \
'${SFTP_PORT}'
mkdir -p "$config_dir/.claude"
render_template "$TEMPLATES_DIR/claude-settings.json.tpl" "$config_dir/.claude/settings.json" '${INFER_URL} ${MODEL_NAME} ${API_KEY} ${ANTHROPIC_SUFFIX}'
mkdir -p "$config_dir/.hermes"
render_template \
"$TEMPLATES_DIR/hermes-config.yaml.tpl" \
"$config_dir/.hermes/config.yaml" \
'${INFER_URL} ${MODEL_NAME} ${API_KEY}'
if [ -n "$API_KEY" ]; then
echo "OPENAI_API_KEY=${API_KEY}" > "$config_dir/.hermes/.env"
echo "ANTHROPIC_API_KEY=${API_KEY}" >> "$config_dir/.hermes/.env"
fi
copy_template "$TEMPLATES_DIR/ssh/passwd.tpl" "$config_dir/ssh/passwd"
copy_template "$TEMPLATES_DIR/ssh/start_sshd.sh.tpl" "$config_dir/ssh/start_sshd.sh"
copy_template "$TEMPLATES_DIR/health_monitor.sh.tpl" "$config_dir/health_monitor.sh"
sed -i 's/\r$//' "$config_dir/ssh/start_sshd.sh" 2>/dev/null || true
sed -i 's/\r$//' "$config_dir/health_monitor.sh" 2>/dev/null || true
chmod +x "$config_dir/ssh/start_sshd.sh" 2>/dev/null || true
chmod +x "$config_dir/health_monitor.sh" 2>/dev/null || true
if [ ! -f "$config_dir/ssh/sftp_password" ]; then
echo "$sftp_password" > "$config_dir/ssh/sftp_password"
else
log_warn "跳过: $config_dir/ssh/sftp_password (已存在)"
fi
echo "$OPENCLAW_TOKEN" > "$config_dir/.gateway_token"
chown 1000:1000 -R "$config_dir" 2>/dev/null || true
local display_pass
display_pass=$(cat "$config_dir/ssh/sftp_password" 2>/dev/null)
log_ok "已配置实例 $i: Gateway $GW_PORT, SFTP $SFTP_PORT, SFTP密码: $display_pass"
}
generate_all_configs() {
ensure_envsubst
mkdir -p "$CONFIG_BASE"
log_info "准备生成 $COUNT 个实例的配置..."
log_info "基础端口: $BASE_PORT (Gateway: port, SFTP: port+1)"
for i in $(seq "$START_INDEX" "$((START_INDEX + COUNT - 1))"); do
generate_instance_config "$i"
done
log_ok "全部 $COUNT 个实例配置生成完成(编号: ${START_INDEX}-$((START_INDEX + COUNT - 1)))"
}