| #!/bin/bash |
| |
| |
| |
|
|
| set -e |
| cd /app |
|
|
| WORKSPACE="${AGENT_WORKSPACE:-/data/cow}" |
| CONFIG_PATH="${WORKSPACE}/config.json" |
|
|
| mkdir -p "${WORKSPACE}" |
|
|
| |
| python3 - <<PYEOF |
| import json, os |
| |
| workspace = os.environ.get("AGENT_WORKSPACE", "/data/cow") |
| cfg_path = os.path.join(workspace, "config.json") |
| |
| # Load existing config so user tweaks survive restarts |
| if os.path.exists(cfg_path): |
| with open(cfg_path) as f: |
| cfg = json.load(f) |
| else: |
| cfg = {} |
| |
| def env(key, default=""): |
| return os.environ.get(key, default) |
| |
| # ββ Model / API keys ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| cfg.setdefault("model", env("MODEL", "gpt-4o-mini")) |
| |
| if env("OPEN_AI_API_KEY"): |
| cfg["open_ai_api_key"] = env("OPEN_AI_API_KEY") |
| cfg["open_ai_api_base"] = env("OPEN_AI_API_BASE", "https://api.openai.com/v1") |
| |
| if env("CLAUDE_API_KEY"): |
| cfg["claude_api_key"] = env("CLAUDE_API_KEY") |
| cfg["claude_api_base"] = env("CLAUDE_API_BASE", "https://api.anthropic.com/v1") |
| |
| if env("DEEPSEEK_API_KEY"): |
| cfg["deepseek_api_key"] = env("DEEPSEEK_API_KEY") |
| cfg["deepseek_api_base"] = env("DEEPSEEK_API_BASE", "https://api.deepseek.com/v1") |
| |
| if env("GEMINI_API_KEY"): |
| cfg["gemini_api_key"] = env("GEMINI_API_KEY") |
| cfg["gemini_api_base"] = env("GEMINI_API_BASE", "https://generativelanguage.googleapis.com") |
| |
| if env("QWEN_API_KEY"): |
| cfg["dashscope_api_key"] = env("QWEN_API_KEY") |
| |
| if env("ZHIPU_API_KEY"): |
| cfg["zhipu_ai_api_key"] = env("ZHIPU_API_KEY") |
| |
| if env("LINKAI_API_KEY"): |
| cfg["use_linkai"] = True |
| cfg["linkai_api_key"] = env("LINKAI_API_KEY") |
| cfg["linkai_app_code"] = env("LINKAI_APP_CODE", "") |
| |
| # ββ Web console settings ββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| # HF Spaces requires port 7860; "0.0.0.0" makes it reachable externally |
| cfg["channel_type"] = "web" |
| cfg["web_host"] = "0.0.0.0" |
| cfg["web_port"] = 7860 |
| cfg["web_console"] = True |
| |
| if env("WEB_PASSWORD"): |
| cfg["web_password"] = env("WEB_PASSWORD") |
| |
| # ββ Agent settings ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ |
| cfg["agent"] = env("AGENT_ENABLED", "true").lower() not in ("false", "0", "no") |
| cfg["agent_workspace"] = workspace |
| cfg["agent_max_steps"] = int(env("AGENT_MAX_STEPS", "20")) |
| cfg.setdefault("knowledge", True) |
| cfg.setdefault("self_evolution_enabled", False) # off by default in cloud env |
| cfg.setdefault("cow_lang", env("COW_LANG", "auto")) |
| |
| with open(cfg_path, "w") as f: |
| json.dump(cfg, f, indent=2, ensure_ascii=False) |
| |
| print(f"[entrypoint] Config written to {cfg_path}") |
| print(f"[entrypoint] Model: {cfg.get('model')}") |
| print(f"[entrypoint] Web console: http://0.0.0.0:{cfg['web_port']}") |
| PYEOF |
|
|
| |
| ln -sf "${CONFIG_PATH}" /app/config.json |
|
|
| |
| CHAT_HTML="/app/channel/web/chat.html" |
| WEB_CHANNEL_PY="/app/channel/web/web_channel.py" |
| MOBILE_CSS_LINK='<link rel="stylesheet" href="assets/css/mobile-fix.css">' |
| if [ -f "${CHAT_HTML}" ]; then |
| cp -f /app/mobile-fix.css /app/channel/web/static/css/mobile-fix.css |
| if ! grep -q "mobile-fix.css" "${CHAT_HTML}"; then |
| |
| |
| sed -i "s|</head>| ${MOBILE_CSS_LINK}\n</head>|" "${CHAT_HTML}" |
| echo "[entrypoint] Mobile CSS patch link injected into chat.html" |
| else |
| echo "[entrypoint] Mobile CSS patch link already present, file refreshed" |
| fi |
| else |
| echo "[entrypoint] WARNING: ${CHAT_HTML} not found, mobile CSS patch skipped" |
| fi |
|
|
| |
| |
| |
| if [ -f "${WEB_CHANNEL_PY}" ] && ! grep -q "mobile-fix.css'," "${WEB_CHANNEL_PY}"; then |
| python3 - <<'PYEOF' |
| path = "/app/channel/web/web_channel.py" |
| with open(path, "r", encoding="utf-8") as f: |
| content = f.read() |
|
|
| marker = "html = html.replace('assets/css/console.css', f'assets/css/console.css?v={cache_bust}')" |
| addition = "\n html = html.replace('assets/css/mobile-fix.css', f'assets/css/mobile-fix.css?v={cache_bust}')" |
|
|
| if marker in content and "mobile-fix.css', f'assets/css/mobile-fix.css" not in content: |
| content = content.replace(marker, marker + addition, 1) |
| with open(path, "w", encoding="utf-8") as f: |
| f.write(content) |
| print("[entrypoint] Cache-busting patch applied to web_channel.py") |
| else: |
| print("[entrypoint] Cache-busting patch not applicable or already present") |
| PYEOF |
| fi |
|
|
| |
| echo "[entrypoint] Starting CowAgent..." |
| exec python3 /app/app.py |
|
|