MoltBotXY / entrypoint.sh
asemxin
fix: 彻底移除 entrypoint.sh 中的飞书配置及插件文件
229d644
#!/bin/bash
set -e
echo "🤖 MoltBot AI - Starting..."
# ============================================
# 从环境变量生成 OpenClaw 配置
# ============================================
FEISHU_APP_ID="${FEISHU_APP_ID:-}"
FEISHU_APP_SECRET="${FEISHU_APP_SECRET:-}"
API_BASE_URL="${API_BASE_URL:-https://asem12345-cliproxyapi.hf.space/v1}"
API_KEY="${API_KEY:-}"
MODEL_NAME="${MODEL_NAME:-gemini-3-flash}"
BRAVE_API_KEY="${BRAVE_API_KEY:-}"
if [ -z "$FEISHU_APP_ID" ] || [ -z "$FEISHU_APP_SECRET" ]; then
echo "❌ 错误: 请设置 FEISHU_APP_ID 和 FEISHU_APP_SECRET 环境变量"
echo " 在 HF Space Settings → Secrets 中添加"
exit 1
fi
echo "📝 生成 OpenClaw 配置..."
# 生成 provider ID:从 URL 提取域名部分,加 custom- 前缀
# 例如 https://asem12345-cliproxyapi.hf.space/v1 → custom-asem12345-cliproxyapi-hf-space
PROVIDER_ID="custom-$(echo "$API_BASE_URL" | sed 's|https\?://||' | sed 's|/.*||' | sed 's|[^a-zA-Z0-9]|-|g' | sed 's|-*$||')"
OPENCLAW_DIR="$HOME/.openclaw"
# 创建必要目录
mkdir -p "$OPENCLAW_DIR/agents/main/sessions"
mkdir -p "$OPENCLAW_DIR/workspace"
chmod 700 "$OPENCLAW_DIR" 2>/dev/null || true
# 先写一个最小配置让 doctor 能跑
cat > "$OPENCLAW_DIR/openclaw.json" << JSONEOF
{
"gateway": {
"port": 18789,
"bind": "loopback",
"mode": "local"
},
"channels": {
"feishu": {
"enabled": false
}
}
}
JSONEOF
echo "✅ 最小配置已生成"
echo " 飞书 App ID: ${FEISHU_APP_ID}"
# ============================================
# 运行 doctor --fix(自动安装飞书插件等)
# ============================================
echo "🔧 运行 doctor --fix..."
openclaw doctor --fix || true
# 强力删除飞书插件,防止自动启用
rm -rf /root/.openclaw/extensions/feishu-openclaw
# ============================================
# doctor 完成后,写入完整配置(包含自定义模型)
# doctor 有时会覆盖我们的配置,所以放在 doctor 之后
# ============================================
echo "📝 写入完整配置..."
# 用 python 合并配置(保留 doctor 添加的字段如 meta, wizard, plugins 等)
python3 << PYEOF
import json, os
config_path = os.path.expanduser("~/.openclaw/openclaw.json")
# 读取 doctor 生成的配置
try:
with open(config_path) as f:
config = json.load(f)
except:
config = {}
# 设置 gateway
config.setdefault("gateway", {})
config["gateway"]["port"] = 18789
config["gateway"]["bind"] = "loopback"
config["gateway"]["mode"] = "local"
# 设置自定义 provider
config.setdefault("models", {})
config["models"]["mode"] = "merge"
config["models"].setdefault("providers", {})
config["models"]["providers"]["${PROVIDER_ID}"] = {
"baseUrl": "${API_BASE_URL}",
"apiKey": "${API_KEY}",
"api": "openai-completions",
"models": [
{
"id": "${MODEL_NAME}",
"name": "${MODEL_NAME} (Custom Provider)",
"reasoning": False,
"input": ["text"],
"cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
"contextWindow": 131072,
"maxTokens": 8192
}
]
}
# 设置 agent defaults
config.setdefault("agents", {}).setdefault("defaults", {})
config["agents"]["defaults"]["model"] = {
"primary": "${PROVIDER_ID}/${MODEL_NAME}"
}
# 配置 imageModel(Gemini 原生支持多模态,复用主模型)
config["agents"]["defaults"]["imageModel"] = {
"primary": "${PROVIDER_ID}/${MODEL_NAME}"
}
config["agents"]["defaults"].setdefault("models", {})
config["agents"]["defaults"]["models"]["${PROVIDER_ID}/${MODEL_NAME}"] = {}
config["agents"]["defaults"].setdefault("workspace", os.path.expanduser("~/.openclaw/workspace"))
config["agents"]["defaults"].setdefault("compaction", {"mode": "safeguard"})
config["agents"]["defaults"].setdefault("maxConcurrent", 4)
# 配置 memory(记忆功能)
config["agents"]["defaults"]["memorySearch"] = {
"enabled": True,
"provider": "local"
}
print(f"✅ 图片分析(imageModel)已启用")
print(f"✅ 记忆功能(memory)已启用")
# 删除飞书 channel 配置 (防止 OpenClaw 自动启用)
config.setdefault("channels", {})
if "feishu" in config["channels"]:
del config["channels"]["feishu"]
# 配置 Brave Search(上网搜索)
brave_key = os.environ.get("BRAVE_API_KEY", "${BRAVE_API_KEY}")
if brave_key:
config.setdefault("tools", {}).setdefault("web", {})
config["tools"]["web"]["search"] = {
"enabled": True,
"provider": "brave",
"maxResults": 10
}
print(f"✅ Brave Search 已启用")
with open(config_path, "w") as f:
json.dump(config, f, indent=2)
print(f"✅ 完整配置已写入 {config_path}")
print(f" 模型: ${PROVIDER_ID}/${MODEL_NAME}")
# 同时写入 agent 级别的 models.json(防止 fallback 到 anthropic)
agent_dir = os.path.expanduser("~/.openclaw/agents/main/agent")
os.makedirs(agent_dir, exist_ok=True)
agent_models = {
"providers": {
"github-copilot": {
"baseUrl": "https://api.individual.githubcopilot.com",
"models": []
},
"${PROVIDER_ID}": {
"baseUrl": "${API_BASE_URL}",
"apiKey": "${API_KEY}",
"api": "openai-completions",
"models": [
{
"id": "${MODEL_NAME}",
"name": "${MODEL_NAME} (Custom Provider)",
"reasoning": False,
"input": ["text"],
"cost": {"input": 0, "output": 0, "cacheRead": 0, "cacheWrite": 0},
"contextWindow": 131072,
"maxTokens": 8192
}
]
}
}
}
models_path = os.path.join(agent_dir, "models.json")
with open(models_path, "w") as f:
json.dump(agent_models, f, indent=2)
# 确保 auth-profiles.json 存在(OpenClaw 查找的是这个文件)
auth_path = os.path.join(agent_dir, "auth-profiles.json")
with open(auth_path, "w") as f:
json.dump({}, f)
# 也写一份 auth.json 以防万一
auth2_path = os.path.join(agent_dir, "auth.json")
if not os.path.exists(auth2_path):
with open(auth2_path, "w") as f:
json.dump({}, f)
print(f"✅ Agent 配置已写入 {agent_dir}")
# 递归扫描所有 json 文件,替换 anthropic 引用
import glob
replaced = []
provider_id = "${PROVIDER_ID}"
model_id = "${MODEL_NAME}"
full_model = f"{provider_id}/{model_id}"
for fpath in glob.glob(os.path.expanduser("~/.openclaw/**/*.json"), recursive=True):
try:
with open(fpath) as f:
content = f.read()
if "anthropic" in content:
original = content
# 替换 model references
content = content.replace('"anthropic/claude-sonnet-4-20250514"', f'"{full_model}"')
content = content.replace('"anthropic/claude-3-5-sonnet"', f'"{full_model}"')
content = content.replace('"anthropic/claude-3-5-haiku"', f'"{full_model}"')
content = content.replace('"anthropic/claude-3-haiku"', f'"{full_model}"')
# 通用 anthropic provider 引用
content = content.replace('"anthropic"', f'"{provider_id}"')
if content != original:
with open(fpath, "w") as f:
f.write(content)
replaced.append(fpath)
except:
pass
if replaced:
print(f"⚠️ 替换了 {len(replaced)} 个文件中的 anthropic 引用:")
for r in replaced:
print(f" - {r}")
else:
print("✅ 未发现 anthropic 引用")
# 打印调试信息
print("\n🔍 调试 - openclaw.json:")
try:
with open(config_path) as f:
print(f.read()[:2000])
except:
print(" 无法读取")
print("\n🔍 调试 - agent 目录内容:")
for fpath in glob.glob(os.path.join(agent_dir, "*")):
print(f" {fpath}")
try:
with open(fpath) as f:
c = f.read()[:500]
print(f" {c}")
except:
pass
PYEOF
# 注意: 不再使用 image_proxy 和 sed patch(会破坏 WebSocket 连接)
# 图片处理完全由 image_daemon.py 负责
# ============================================
# 启动 OpenClaw Gateway(后台)
# ============================================
echo "🚀 启动 OpenClaw Gateway..."
openclaw gateway --force &
GATEWAY_PID=$!
echo " Gateway PID: $GATEWAY_PID"
# 等待网关启动
sleep 5
# ============================================
# 启动图片预处理守护进程(后台,带自动重启守护)
# ============================================
echo "🖼️ 启动图片预处理守护进程(带自动重启)..."
(
RESTART_COUNT=0
while true; do
RESTART_COUNT=$((RESTART_COUNT + 1))
echo "[image_daemon_guard] 🚀 启动 image_daemon (第 ${RESTART_COUNT} 次)"
python3 /app/image_daemon.py
EXIT_CODE=$?
echo "[image_daemon_guard] ⚠️ image_daemon 退出 (code=${EXIT_CODE}), 3 秒后重启..."
sleep 3
done
) &
IMAGE_DAEMON_PID=$!
echo " Image Daemon Guard PID: $IMAGE_DAEMON_PID"
# ============================================
# 启动状态监控网页(前台,端口 7860)
# ============================================
echo "📊 启动状态监控网页 (端口 7860)..."
exec python3 /app/status_page.py