File size: 11,795 Bytes
d41fe21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cfe4b3a
 
 
d41fe21
 
 
 
 
 
 
 
 
 
4a18107
d41fe21
6275d3e
 
 
 
 
 
abf5aa0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6275d3e
d41fe21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
b0340c8
d41fe21
b0340c8
 
 
6275d3e
d41fe21
 
 
 
 
9cf2dd4
 
 
d41fe21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9cf2dd4
d41fe21
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
00c3396
 
27165bb
aeb1d95
27165bb
c917d32
d41fe21
 
8f0d1fd
 
 
 
 
 
 
 
 
 
d41fe21
 
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
#!/bin/bash
set -e

# ════════════════════════════════════════════════════════════════
# HuggingClaw β€” OpenClaw Gateway for HF Spaces
# ════════════════════════════════════════════════════════════════

# ── Startup Banner ──
OPENCLAW_VERSION="${OPENCLAW_VERSION:-latest}"
echo ""
echo "  ╔══════════════════════════════════════════╗"
echo "  β•‘          🦞 HuggingClaw Gateway          β•‘"
echo "  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•"
echo ""

# ── Validate required secrets ──
ERRORS=""
if [ -z "$LLM_API_KEY" ]; then
  ERRORS="${ERRORS}  ❌ LLM_API_KEY is not set\n"
fi
if [ -z "$LLM_MODEL" ]; then
  ERRORS="${ERRORS}  ❌ LLM_MODEL is not set (e.g. google/gemini-2.5-flash, anthropic/claude-sonnet-4-5, openai/gpt-4)\n"
fi
if [ -z "$GATEWAY_TOKEN" ]; then
  ERRORS="${ERRORS}  ❌ GATEWAY_TOKEN is not set (generate: openssl rand -hex 32)\n"
fi
if [ -n "$ERRORS" ]; then
  echo "Missing required secrets:"
  echo -e "$ERRORS"
  echo "Add them in HF Spaces β†’ Settings β†’ Secrets"
  exit 1
fi

# ── Set LLM env based on model name ──

# Auto-correct Gemini models to use google/ prefix if anthropic/ was mistakenly used
if [[ "$LLM_MODEL" == "anthropic/gemini"* ]]; then
  LLM_MODEL=$(echo "$LLM_MODEL" | sed 's/^anthropic\//google\//')
  echo "⚠️  Corrected model from anthropic/gemini* to google/gemini*"
fi

# Extract provider prefix from model name (e.g. "google/gemini-2.5-flash" β†’ "google")
LLM_PROVIDER=$(echo "$LLM_MODEL" | cut -d'/' -f1)

# Map provider prefix to the correct API key environment variable
# Based on actual OpenRouter model IDs: https://openrouter.ai/api/v1/models
case "$LLM_PROVIDER" in
  openrouter)       export OPENROUTER_API_KEY="$LLM_API_KEY" ;;
  anthropic)        export ANTHROPIC_API_KEY="$LLM_API_KEY" ;;
  openai)           export OPENAI_API_KEY="$LLM_API_KEY" ;;
  google)           export GOOGLE_API_KEY="$LLM_API_KEY" ;;
  deepseek)         export DEEPSEEK_API_KEY="$LLM_API_KEY" ;;
  mistralai)        export MISTRAL_API_KEY="$LLM_API_KEY" ;;
  qwen)             export QWEN_API_KEY="$LLM_API_KEY" ;;
  x-ai)             export XAI_API_KEY="$LLM_API_KEY" ;;
  meta-llama)       export META_API_KEY="$LLM_API_KEY" ;;
  minimax)          export MINIMAX_API_KEY="$LLM_API_KEY" ;;
  z-ai)             export ZAI_API_KEY="$LLM_API_KEY" ;;
  moonshotai)       export MOONSHOT_API_KEY="$LLM_API_KEY" ;;
  nvidia)           export NVIDIA_API_KEY="$LLM_API_KEY" ;;
  cohere)           export COHERE_API_KEY="$LLM_API_KEY" ;;
  perplexity)       export PERPLEXITY_API_KEY="$LLM_API_KEY" ;;
  bytedance-seed)   export BYTEDANCE_API_KEY="$LLM_API_KEY" ;;
  xiaomi)           export XIAOMI_API_KEY="$LLM_API_KEY" ;;
  amazon)           export AMAZON_API_KEY="$LLM_API_KEY" ;;
  reka|rekaai)      export REKA_API_KEY="$LLM_API_KEY" ;;
  inception)        export INCEPTION_API_KEY="$LLM_API_KEY" ;;
  kwaipilot)        export KWAIPILOT_API_KEY="$LLM_API_KEY" ;;
  ai21)             export AI21_API_KEY="$LLM_API_KEY" ;;
  baidu)            export BAIDU_API_KEY="$LLM_API_KEY" ;;
  tencent)          export TENCENT_API_KEY="$LLM_API_KEY" ;;
  stepfun)          export STEPFUN_API_KEY="$LLM_API_KEY" ;;
  inflection)       export INFLECTION_API_KEY="$LLM_API_KEY" ;;
  writer)           export WRITER_API_KEY="$LLM_API_KEY" ;;
  upstage)          export UPSTAGE_API_KEY="$LLM_API_KEY" ;;
  *)
    # Fallback: export as ANTHROPIC (default) and also as generic
    export ANTHROPIC_API_KEY="$LLM_API_KEY"
    ;;
esac

# ── Setup directories ──
mkdir -p /home/node/.openclaw/agents/main/sessions
mkdir -p /home/node/.openclaw/credentials
mkdir -p /home/node/.openclaw/workspace
chmod 700 /home/node/.openclaw

# ── Validate HF token (if provided) ──
if [ -n "$HF_TOKEN" ]; then
  echo "πŸ”‘ Validating HF token..."
  HF_AUTH_STATUS=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $HF_TOKEN" https://huggingface.co/api/repos/create --max-time 10 2>/dev/null || echo "000")
  if [ "$HF_AUTH_STATUS" = "401" ]; then
    echo "  ⚠️  HF token is invalid or expired! Workspace backup will not work."
    echo "  Get a new token: https://huggingface.co/settings/tokens"
  else
    echo "  βœ… HF token is valid"
  fi
fi

# ── Auto-create + Restore workspace from HF Dataset ──
if [ -n "$HF_USERNAME" ] && [ -n "$HF_TOKEN" ]; then
  BACKUP_DATASET="${BACKUP_DATASET_NAME:-huggingclaw-backup}"
  BACKUP_URL="https://${HF_USERNAME}:${HF_TOKEN}@huggingface.co/datasets/${HF_USERNAME}/${BACKUP_DATASET}"
  
  # Auto-create the dataset if it doesn't exist
  echo "πŸ“¦ Checking HF Dataset: ${HF_USERNAME}/${BACKUP_DATASET}..."
  DATASET_CHECK=$(curl -s -o /dev/null -w "%{http_code}" \
    -H "Authorization: Bearer $HF_TOKEN" \
    "https://huggingface.co/api/datasets/${HF_USERNAME}/${BACKUP_DATASET}" \
    --max-time 10 2>/dev/null || echo "000")
  
  if [ "$DATASET_CHECK" = "404" ]; then
    echo "  πŸ“ Dataset not found, creating ${HF_USERNAME}/${BACKUP_DATASET}..."
    CREATE_RESULT=$(curl -s -w "\n%{http_code}" \
      -X POST "https://huggingface.co/api/repos/create" \
      -H "Authorization: Bearer $HF_TOKEN" \
      -H "Content-Type: application/json" \
      -d "{\"type\":\"dataset\",\"name\":\"${BACKUP_DATASET}\",\"private\":true}" \
      --max-time 15 2>/dev/null || echo "error")
    CREATE_STATUS=$(echo "$CREATE_RESULT" | tail -1)
    if [ "$CREATE_STATUS" = "200" ] || [ "$CREATE_STATUS" = "201" ]; then
      echo "  βœ… Dataset created: ${HF_USERNAME}/${BACKUP_DATASET} (private)"
    else
      echo "  ⚠️  Could not create dataset (HTTP $CREATE_STATUS). Create it manually:"
      echo "     https://huggingface.co/datasets/create"
    fi
  elif [ "$DATASET_CHECK" = "200" ]; then
    echo "  βœ… Dataset exists"
  else
    echo "  ⚠️  Could not check dataset (HTTP $DATASET_CHECK)"
  fi
  
  # Restore workspace
  echo "πŸ“¦ Restoring workspace..."
  WORKSPACE="/home/node/.openclaw/workspace"
  GIT_USER_EMAIL="${WORKSPACE_GIT_USER:-openclaw@example.com}"
  GIT_USER_NAME="${WORKSPACE_GIT_NAME:-OpenClaw Bot}"
  
  cd "$WORKSPACE"
  if [ ! -d ".git" ]; then
    git init -q
    git remote add origin "$BACKUP_URL"
  else
    git remote set-url origin "$BACKUP_URL"
  fi
  
  git config user.email "$GIT_USER_EMAIL"
  git config user.name "$GIT_USER_NAME"
  
  if git fetch origin main 2>/dev/null; then
    git reset --hard origin/main 2>/dev/null && echo "  βœ… Workspace restored!"
  else
    echo "  ⚠️ No remote data yet, starting fresh."
  fi
  cd /
fi

# ── Build config ──
CONFIG_JSON=$(cat <<'CONFIGEOF'
{
  "gateway": {
    "mode": "local",
    "port": 7860,
    "bind": "lan",
    "auth": {
      "token": ""
    },
    "controlUi": {
      "allowInsecureAuth": true
    },
    "trustedProxies": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]
  },
  "channels": {},
  "plugins": {
    "entries": {}
  }
}
CONFIGEOF
)

# Gateway token
CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".gateway.auth.token = \"$GATEWAY_TOKEN\"")

# Model configuration at top level
CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".agents.defaults.model = \"$LLM_MODEL\"")

# Control UI origin (allow HF Space URL for web UI access)
if [ -n "$SPACE_HOST" ]; then
  CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".gateway.controlUi.allowedOrigins = [\"https://${SPACE_HOST}\"]")
fi

# Disable device auth (pairing) for headless Docker β€” token-only auth
CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".gateway.controlUi.dangerouslyDisableDeviceAuth = true")

# Telegram (supports multiple user IDs, comma-separated)
if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
  CONFIG_JSON=$(echo "$CONFIG_JSON" | jq '.plugins.entries.telegram = {"enabled": true}')
  export TELEGRAM_BOT_TOKEN="$TELEGRAM_BOT_TOKEN"
  
  if [ -n "$TELEGRAM_USER_IDS" ]; then
    # Convert comma-separated IDs to JSON array
    IDS_JSON=$(echo "$TELEGRAM_USER_IDS" | tr ',' '\n' | sed 's/^ *//;s/ *$//' | jq -R . | jq -s .)
    CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".channels.telegram = {\"dmPolicy\": \"allowlist\", \"allowFrom\": $IDS_JSON}")
  elif [ -n "$TELEGRAM_USER_ID" ]; then
    # Single user (backward compatible)
    CONFIG_JSON=$(echo "$CONFIG_JSON" | jq ".channels.telegram = {\"dmPolicy\": \"allowlist\", \"allowFrom\": [\"$TELEGRAM_USER_ID\"]}")
  fi
fi

# Write config
echo "$CONFIG_JSON" > "/home/node/.openclaw/openclaw.json"

# ── Startup Summary ──
echo ""
echo "  β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”"
echo "  β”‚  πŸ“‹ Configuration Summary                β”‚"
echo "  β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€"
printf "  β”‚  %-40s β”‚\n" "Model: $LLM_MODEL"
if [ -n "$TELEGRAM_BOT_TOKEN" ]; then
printf "  β”‚  %-40s β”‚\n" "Telegram: βœ… enabled"
else
printf "  β”‚  %-40s β”‚\n" "Telegram: ❌ not configured"
fi
if [ -n "$HF_USERNAME" ] && [ -n "$HF_TOKEN" ]; then
printf "  β”‚  %-40s β”‚\n" "Backup: βœ… ${HF_USERNAME}/${BACKUP_DATASET:-huggingclaw-backup}"
else
printf "  β”‚  %-40s β”‚\n" "Backup: ❌ not configured"
fi
if [ -n "$SPACE_HOST" ]; then
printf "  β”‚  %-40s β”‚\n" "Keep-alive: βœ… every ${KEEP_ALIVE_INTERVAL:-300}s"
printf "  β”‚  %-40s β”‚\n" "Control UI: https://${SPACE_HOST}"
else
printf "  β”‚  %-40s β”‚\n" "Keep-alive: ⏸️  local mode"
fi
SYNC_STATUS="❌ disabled"
if [ -n "$HF_USERNAME" ] && [ -n "$HF_TOKEN" ]; then
  SYNC_STATUS="βœ… every ${SYNC_INTERVAL:-600}s"
fi
printf "  β”‚  %-40s β”‚\n" "Auto-sync: $SYNC_STATUS"
echo "  β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜"
echo ""

# ── Trap SIGTERM for graceful shutdown ──
graceful_shutdown() {
  echo ""
  echo "πŸ›‘ Shutting down gracefully..."
  
  # Commit any unsaved workspace changes
  if [ -d "/home/node/.openclaw/workspace/.git" ]; then
    echo "πŸ’Ύ Saving workspace before exit..."
    cd /home/node/.openclaw/workspace
    git add -A 2>/dev/null
    if ! git diff --cached --quiet 2>/dev/null; then
      TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
      git commit -m "Shutdown sync ${TIMESTAMP}" 2>/dev/null
      git push origin main 2>/dev/null && echo "  βœ… Workspace saved!" || echo "  ⚠️ Push failed"
    else
      echo "  βœ… No unsaved changes"
    fi
  fi
  
  # Kill background processes
  kill $(jobs -p) 2>/dev/null
  echo "πŸ‘‹ Goodbye!"
  exit 0
}
trap graceful_shutdown SIGTERM SIGINT

# ── Start background services ──
node /home/node/app/health-server.js &
/home/node/app/keep-alive.sh &
/home/node/app/workspace-sync.sh &

# ── Launch gateway ──
echo "πŸš€ Launching OpenClaw gateway on port 7860..."
echo ""
# Set model via environment for the gateway
export LLM_MODEL="$LLM_MODEL"



openclaw gateway run --port 7860 --bind lan --verbose 2>&1 | tee -a /home/node/.openclaw/gateway.log &
GATEWAY_PID=$!

# Wait a moment for startup errors
sleep 3
if ! kill -0 $GATEWAY_PID 2>/dev/null; then
  echo ""
  echo "❌ Gateway failed to start. Last 30 lines of log:"
  echo "────────────────────────────────────────────"
  tail -30 /home/node/.openclaw/gateway.log
  exit 1
fi

# Wait for gateway (allows trap to fire)
wait $GATEWAY_PID