Cowagent / entrypoint.sh
Nerdur's picture
Upload 2 files
ce5974d verified
Raw
History Blame Contribute Delete
5.84 kB
#!/bin/bash
# ── CowAgent entrypoint for Hugging Face Spaces ───────────────────────────────
# Reads env vars (set as Secrets in HF Space settings) and writes config.json,
# then launches the app on port 7860 (required by HF Spaces).
set -e
cd /app
WORKSPACE="${AGENT_WORKSPACE:-/data/cow}"
CONFIG_PATH="${WORKSPACE}/config.json"
mkdir -p "${WORKSPACE}"
# ── Build config.json from environment variables ──────────────────────────────
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
# ── Symlink config to app root (CowAgent looks here) ─────────────────────────
ln -sf "${CONFIG_PATH}" /app/config.json
# ── Apply mobile responsiveness CSS patch (idempotent) ────────────────────────
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
# Insert the stylesheet link right before </head>, after console.css
# so our !important rules load last and win on cascade order too.
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
# Make sure mobile-fix.css also gets the same cache-busting query param as
# console.css/console.js, so browsers never serve a stale cached copy after
# we update the file on a redeploy.
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
# ── Start CowAgent ────────────────────────────────────────────────────────────
echo "[entrypoint] Starting CowAgent..."
exec python3 /app/app.py