| | #!/bin/bash |
| | set -e |
| |
|
| | echo "🤖 MoltBot AI - Starting..." |
| |
|
| | |
| | |
| | |
| | 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="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 |
| |
|
| | |
| | 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}" |
| |
|
| | |
| | |
| | |
| | echo "🔧 运行 doctor --fix..." |
| | openclaw doctor --fix || true |
| | |
| | rm -rf /root/.openclaw/extensions/feishu-openclaw |
| |
|
| | |
| | |
| | |
| | |
| | echo "📝 写入完整配置..." |
| |
|
| | |
| | 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 |
| |
|
| | |
| | |
| |
|
| | |
| | |
| | |
| | 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" |
| |
|
| | |
| | |
| | |
| | echo "📊 启动状态监控网页 (端口 7860)..." |
| | exec python3 /app/status_page.py |
| |
|
| |
|
| |
|
| |
|