File size: 4,193 Bytes
39e315a
 
 
 
95b8512
 
 
39e315a
95b8512
39e315a
95b8512
 
39e315a
 
 
 
95b8512
39e315a
 
 
95b8512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39e315a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
95b8512
 
 
39e315a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
#!/usr/bin/env bash
# ─────────────────────────────────────────────────────────────────────
# Rhodawk runtime bootstrap.
#
# 1. Launch the camofox-browser anti-detection browser server on
#    127.0.0.1:9377 (used by the orchestrator via camofox_client.py).
# 2. Launch the OpenClaude headless gRPC daemon for the DigitalOcean
#    Inference provider on :50051 (PRIMARY).
# 3. Launch the OpenClaude headless gRPC daemon for OpenRouter on
#    :50052 (FALLBACK) β€” only if OPENROUTER_API_KEY is present.
# 4. Wait briefly for everything to bind, then hand control to app.py
#    which talks to them over gRPC + HTTP.
# ─────────────────────────────────────────────────────────────────────
set -eo pipefail

OC_DIR=/opt/openclaude
CAMOFOX_DIR=/opt/camofox
LOG_DIR="${LOG_DIR:-/tmp}"
mkdir -p "${LOG_DIR}"

# ─── camofox-browser ─────────────────────────────────────────────────
# Anti-detection Firefox-fork browser server.  Lazily downloads the
# Camoufox engine (~300MB) on first launch into the user's home dir,
# so the first start may take a minute.  Subsequent starts are fast.
start_camofox() {
    local entry="${CAMOFOX_DIR}/node_modules/@askjo/camofox-browser/server.js"
    [[ -f "${entry}" ]] || entry="${CAMOFOX_DIR}/node_modules/camofox-browser/server.js"
    if [[ ! -f "${entry}" ]]; then
        echo "[entrypoint] camofox-browser not installed β€” skipping"
        return 0
    fi
    echo "[entrypoint] starting camofox-browser on ${CAMOFOX_HOST:-127.0.0.1}:${CAMOFOX_PORT:-9377}"
    (
        cd "${CAMOFOX_DIR}"
        PORT="${CAMOFOX_PORT:-9377}" \
        HOST="${CAMOFOX_HOST:-127.0.0.1}" \
        CAMOFOX_HEADLESS="${CAMOFOX_HEADLESS:-virtual}" \
        CAMOFOX_PROFILE_DIR="${CAMOFOX_PROFILE_DIR:-/data/camofox/profiles}" \
        CAMOFOX_COOKIES_DIR="${CAMOFOX_COOKIES_DIR:-/data/camofox/cookies}" \
        CAMOFOX_API_KEY="${CAMOFOX_API_KEY:-}" \
        PROXY_HOST="${PROXY_HOST:-}" \
        PROXY_PORT="${PROXY_PORT:-}" \
        PROXY_USERNAME="${PROXY_USERNAME:-}" \
        PROXY_PASSWORD="${PROXY_PASSWORD:-}" \
            node "${entry}" \
                > "${LOG_DIR}/camofox.log" 2>&1 &
        echo $! > "${LOG_DIR}/camofox.pid"
    )
}

start_daemon() {
    local label=$1 port=$2 base_url=$3 api_key=$4 model=$5
    if [[ -z "${api_key}" ]]; then
        echo "[entrypoint] skipping ${label} daemon β€” no API key"
        return 0
    fi
    echo "[entrypoint] starting OpenClaude ${label} daemon on :${port}"
    (
        cd "${OC_DIR}"
        CLAUDE_CODE_USE_OPENAI=1 \
        OPENAI_API_KEY="${api_key}" \
        OPENAI_BASE_URL="${base_url}" \
        OPENAI_MODEL="${model}" \
        GRPC_PORT="${port}" \
        GRPC_HOST=0.0.0.0 \
        OPENCLAUDE_AUTO_APPROVE=1 \
        MCP_RUNTIME_CONFIG="${MCP_RUNTIME_CONFIG:-/tmp/mcp_runtime.json}" \
            bun run scripts/start-grpc.ts \
                > "${LOG_DIR}/openclaude-${label}.log" 2>&1 &
        echo $! > "${LOG_DIR}/openclaude-${label}.pid"
    )
}

# camofox first β€” slowest to bind because of the lazy engine download.
start_camofox

# DigitalOcean Inference (PRIMARY)
DO_BASE="${DO_INFERENCE_BASE_URL:-https://inference.do-ai.run/v1}"
DO_MODEL="${DO_INFERENCE_MODEL:-llama3.3-70b-instruct}"
start_daemon "do" 50051 "${DO_BASE}" \
    "${DO_INFERENCE_API_KEY:-${DIGITALOCEAN_INFERENCE_KEY:-}}" \
    "${DO_MODEL}"

# OpenRouter (FALLBACK)
OR_BASE="${OPENROUTER_BASE_URL:-https://openrouter.ai/api/v1}"
OR_MODEL="${OPENROUTER_MODEL:-qwen/qwen-2.5-coder-32b-instruct:free}"
start_daemon "or" 50052 "${OR_BASE}" "${OPENROUTER_API_KEY:-}" "${OR_MODEL}"

# Brief settle window so the first healing call doesn't race the binder.
# The Python client also has wait_ready() so this is just a friendly nudge.
sleep 2
echo "[entrypoint] launching Rhodawk orchestrator…"
exec python -u app.py