File size: 10,949 Bytes
cdf9dc5
 
 
 
 
edfe3a2
cdf9dc5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
edfe3a2
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
cdf9dc5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
edfe3a2
 
cdf9dc5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
edfe3a2
 
 
cdf9dc5
 
 
 
 
edfe3a2
cdf9dc5
 
 
edfe3a2
 
cdf9dc5
 
edfe3a2
cdf9dc5
 
 
edfe3a2
cdf9dc5
 
 
edfe3a2
cdf9dc5
 
 
 
 
edfe3a2
 
 
 
 
 
 
 
cdf9dc5
edfe3a2
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
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
#!/usr/bin/env bash
set -euo pipefail

export PROXY_PORT="${PROXY_PORT:-7860}"
export OPENCLAW_PORT="${OPENCLAW_PORT:-8080}"
export CODE_PORT="${CODE_PORT:-8888}"
export PORT="${OPENCLAW_PORT}"
export HOST="0.0.0.0"
export OPENCLAW_HOST="0.0.0.0"
export OPENCLAW_PORT
export OPENCLAW_GATEWAY_PASSWORD="${OPENCLAW_GATEWAY_PASSWORD:-773322}"
export SYNC_INTERVAL="${SYNC_INTERVAL:-300}"
export PLAYWRIGHT_BROWSERS_PATH="${PLAYWRIGHT_BROWSERS_PATH:-/ms-playwright}"
export TELEGRAM_API_ROOT="${TELEGRAM_API_ROOT:-https://tg-relay.markdevil11.workers.dev}"
export OPENCLAW_TELEGRAM_API_ROOT="${OPENCLAW_TELEGRAM_API_ROOT:-${TELEGRAM_API_ROOT}}"
export SPACE_URL="${SPACE_URL:-https://elysiadev11-openclaw.hf.space}"
export TELEGRAM_BOT_TOKEN="${TELEGRAM_BOT_TOKEN:-}"
export TELEGRAM_ALLOW_ID="${TELEGRAM_ALLOW_ID:-0}"

configure_dns() {
  if [ -w /etc/resolv.conf ]; then
    cat > /etc/resolv.conf <<'EOF'
nameserver 1.1.1.1
nameserver 1.0.0.1
nameserver 8.8.8.8
nameserver 8.8.4.4
options timeout:2 attempts:3 rotate
EOF
    echo "DNS configured with Cloudflare and Google resolvers."
  else
    echo "WARN: /etc/resolv.conf is not writable; keeping existing DNS."
  fi
}

mkdir -p /root/workspace
configure_dns

/app/sync-root-data.sh restore
mkdir -p /root/.openclaw

generate_openclaw_config() {
  local clean_base
  clean_base="$(printf '%s' "${OPENAI_API_BASE:-}" | sed 's|/chat/completions||g' | sed 's|/v1/|/v1|g' | sed 's|/v1$|/v1|g')"
  local allow_id
  allow_id="${TELEGRAM_ALLOW_ID:-0}"
  local primary_model="kilo_gateway/kilo-auto/free"
  local nvidia_provider=""

  if [ -n "$clean_base" ] && [ -n "${OPENAI_API_KEY:-}" ]; then
    primary_model="nvidia/${MODEL:-gpt-5.5}"
    nvidia_provider=',
      "nvidia": {
        "baseUrl": "'"${clean_base}"'",
        "apiKey": "'"${OPENAI_API_KEY:-}"'",
        "api": "openai-completions",
        "models": [
          { "id": "'"${MODEL:-gpt-5.5}"'", "name": "'"${MODEL:-gpt-5.5}"'", "contextWindow": 128000 },
          { "id": "'"${VISION_MODEL:-${MODEL:-gpt-5.5}}"'", "name": "Nvidia Vision", "contextWindow": 128000, "input": ["text", "image"] }
        ]
      }'
  fi

  cat > /root/.openclaw/openclaw.json <<JSONEOF
{
  "models": {
    "providers": {
      "kilo_gateway": {
        "baseUrl": "https://api.kilo.ai/api/gateway",
        "apiKey": "anonymous",
        "api": "openai-completions",
        "models": [
          { "id": "kilo-auto/free", "name": "Kilo Auto Free", "contextWindow": 128000, "maxTokens": 16000 }
        ]
      }${nvidia_provider}
    }
  },
  "agents": {
    "defaults": {
      "model": {
        "primary": "${primary_model}",
        "fallbacks": ["kilo_gateway/kilo-auto/free"]
      },
      "imageModel": {
        "primary": "${primary_model}"
      }
    }
  },
  "commands": { "restart": true },
  "browser": {
    "enabled": true,
    "headless": true,
    "noSandbox": true,
    "defaultProfile": "openclaw",
    "ssrfPolicy": { "dangerouslyAllowPrivateNetwork": true },
    "profiles": {
      "openclaw": {
        "cdpPort": 18800,
        "color": "0088FF"
      }
    }
  },
  "channels": {
    "telegram": {
      "enabled": true,
      "botToken": "${TELEGRAM_BOT_TOKEN:-}",
      "dmPolicy": "allowlist",
      "allowFrom": [${allow_id}],
      "apiRoot": "${TELEGRAM_API_ROOT}",
      "webhookUrl": "${SPACE_URL}/tg-webhook",
      "webhookSecret": "${OPENCLAW_GATEWAY_PASSWORD:-}",
      "webhookPath": "/tg-webhook",
      "webhookHost": "0.0.0.0",
      "webhookPort": 8787,
      "streaming": {
        "mode": "partial"
      }
    }
  },
  "gateway": {
    "mode": "local",
    "bind": "lan",
    "port": ${OPENCLAW_PORT},
    "trustedProxies": ["0.0.0.0/0"],
    "auth": { "mode": "token", "token": "${OPENCLAW_GATEWAY_PASSWORD:-}" },
    "http": {
      "endpoints": {
        "chatCompletions": { "enabled": true }
      }
    },
    "controlUi": {
      "enabled": true,
      "allowedOrigins": ["${SPACE_URL}"],
      "allowInsecureAuth": true,
      "dangerouslyDisableDeviceAuth": true,
      "dangerouslyAllowHostHeaderOriginFallback": true
    }
  }
}
JSONEOF
}

config_is_complete() {
  python3 - <<'PY'
import json
from pathlib import Path

path = Path('/root/.openclaw/openclaw.json')
if not path.exists():
    raise SystemExit(1)

try:
    data = json.loads(path.read_text())
except Exception:
    raise SystemExit(1)

required_paths = [
    ('models', 'providers'),
    ('models', 'providers', 'kilo_gateway'),
    ('agents', 'defaults'),
    ('browser',),
    ('gateway',),
    ('gateway', 'controlUi'),
    ('channels', 'telegram'),
]

for parts in required_paths:
    cur = data
    for part in parts:
        if not isinstance(cur, dict) or part not in cur:
            raise SystemExit(1)
        cur = cur[part]

raise SystemExit(0)
PY
}

if ! config_is_complete; then
  echo "Generating full /root/.openclaw/openclaw.json before OpenClaw startup..."
  generate_openclaw_config
else
  echo "Complete /root/.openclaw/openclaw.json found; keeping it."
fi

python3 - <<'PY'
import json
import os
from pathlib import Path

path = Path('/root/.openclaw/openclaw.json')
api_root = os.environ.get('TELEGRAM_API_ROOT', 'https://tg-relay.markdevil11.workers.dev')
space_url = os.environ.get('SPACE_URL', 'https://elysiadev11-openclaw.hf.space')
if not path.exists():
  raise SystemExit(0)

try:
  data = json.loads(path.read_text())
except Exception as exc:
  print(f'WARN: cannot update Telegram apiRoot in openclaw.json: {exc}')
  raise SystemExit(0)

channels = data.setdefault('channels', {})
telegram = channels.setdefault('telegram', {})
old = telegram.get('apiRoot')
providers = data.setdefault('models', {}).setdefault('providers', {})
nvidia = providers.get('nvidia')
if isinstance(nvidia, dict) and not nvidia.get('baseUrl'):
  providers.pop('nvidia', None)
agents = data.setdefault('agents', {})
defaults = agents.setdefault('defaults', {})
model = defaults.setdefault('model', {})
if model.get('primary', '').startswith('nvidia/') and 'nvidia' not in providers:
  model['primary'] = 'kilo_gateway/kilo-auto/free'
fallbacks = model.setdefault('fallbacks', [])
if 'kilo_gateway/kilo-auto/free' not in fallbacks:
  fallbacks.append('kilo_gateway/kilo-auto/free')
image_model = defaults.setdefault('imageModel', {})
if image_model.get('primary', '').startswith('nvidia/') and 'nvidia' not in providers:
  image_model['primary'] = model['primary']
telegram['webhookUrl'] = f'{space_url}/tg-webhook'
telegram['webhookPath'] = '/tg-webhook'
telegram['webhookHost'] = '0.0.0.0'
telegram['webhookPort'] = 8787
browser = data.get('browser')
if isinstance(browser, dict):
  browser.pop('requirePairing', None)
if isinstance(defaults, dict):
  defaults.pop('llm', None)
gateway = data.setdefault('gateway', {})
auth = gateway.setdefault('auth', {})
auth['mode'] = 'token'
auth['token'] = os.environ.get('OPENCLAW_GATEWAY_PASSWORD', '773322')
control_ui = gateway.setdefault('controlUi', {})
control_ui['enabled'] = True
control_ui['allowInsecureAuth'] = True
control_ui['dangerouslyDisableDeviceAuth'] = True
control_ui['dangerouslyAllowHostHeaderOriginFallback'] = True
origins = control_ui.setdefault('allowedOrigins', [])
if space_url not in origins:
  origins.append(space_url)
if old != api_root:
  telegram['apiRoot'] = api_root
  print(f'Updated Telegram apiRoot: {old!r} -> {api_root!r}')
else:
  print(f'Telegram apiRoot already set to {api_root}')
print(f'Telegram webhookUrl set to {telegram.get("webhookUrl")}')
path.write_text(json.dumps(data, indent=2) + '\n')
PY

/app/sync-root-data.sh reconcile

/app/sync-root-data.sh loop &

# ── code-server (VS Code) ─────────────────────────────────────────────────────
run_code_server() {
  mkdir -p /root/.config/code-server
  cat > /root/.config/code-server/config.yaml <<YAML
bind-addr: 127.0.0.1:${CODE_PORT}
auth: password
password: ${OPENCLAW_GATEWAY_PASSWORD}
cert: false
YAML
  while true; do
    echo "Starting code-server on 127.0.0.1:${CODE_PORT}..."
    code-server --config /root/.config/code-server/config.yaml /root/workspace
    echo "code-server exited; restarting in 2 seconds..."
    sleep 2
  done
}
# ─────────────────────────────────────────────────────────────────────────────

run_openclaw() {
  while true; do
    echo "Starting OpenClaw on 127.0.0.1:${OPENCLAW_PORT}..."
    echo "OpenClaw config:"
    cat /root/.openclaw/openclaw.json
    if [ -n "${OPENCLAW_CMD:-}" ]; then
      sh -lc "$OPENCLAW_CMD"
    else
      openclaw gateway --port "${OPENCLAW_PORT}" --allow-unconfigured &
      OPENCLAW_PID=$!
      echo "OpenClaw started with PID: $OPENCLAW_PID"
      wait $OPENCLAW_PID
    fi
    echo "OpenClaw exited; restarting in 2 seconds..."
    sleep 2
  done
}

run_nginx() {
  while true; do
    envsubst '${PROXY_PORT} ${OPENCLAW_PORT} ${OPENCLAW_GATEWAY_PASSWORD} ${CODE_PORT}' \
      < /app/nginx.conf.template > /tmp/nginx.conf
    nginx -c /tmp/nginx.conf -g 'daemon off;'
    echo "Nginx exited; restarting in 2 seconds..."
    sleep 2
  done
}

run_simple_webhook() {
  while true; do
    echo "Starting simple webhook server on port 8787..."
    python3 /app/webhook_server.py &
    WEBHOOK_PID=$!
    echo "Simple webhook server started with PID: $WEBHOOK_PID"
    wait $WEBHOOK_PID
    echo "Webhook server exited; restarting in 2 seconds..."
    sleep 2
  done
}

run_openclaw &
run_code_server &
run_nginx &

# Wait for services to start
echo "Waiting for services to start..."
sleep 10

# Check webhook port
echo "Checking webhook port 8787..."
if command -v ss &> /dev/null && ss -tuln 2>/dev/null | grep -q ":8787"; then
  echo "Webhook server is listening on port 8787"
  curl -s -o /dev/null -w "Webhook test status: %{http_code}\n" http://localhost:8787/tg-webhook || true
elif curl -s http://0.0.0.0:8787/tg-webhook &>/dev/null; then
  echo "Webhook server is accessible on port 8787"
else
  echo "WARNING: Webhook server is NOT listening on port 8787 — starting fallback..."
  run_simple_webhook &
fi

# Check OpenClaw port
echo "Checking OpenClaw port ${OPENCLAW_PORT}..."
if command -v ss &> /dev/null && ss -tuln 2>/dev/null | grep -q ":${OPENCLAW_PORT}"; then
  echo "OpenClaw is listening on port ${OPENCLAW_PORT}"
elif curl -s http://127.0.0.1:${OPENCLAW_PORT}/health &>/dev/null; then
  echo "OpenClaw is accessible on port ${OPENCLAW_PORT}"
else
  echo "WARNING: OpenClaw is NOT listening on port ${OPENCLAW_PORT}"
fi

# Check code-server port
echo "Checking code-server port ${CODE_PORT}..."
if command -v ss &> /dev/null && ss -tuln 2>/dev/null | grep -q ":${CODE_PORT}"; then
  echo "code-server is listening on port ${CODE_PORT}"
else
  echo "WARNING: code-server is NOT listening on port ${CODE_PORT}"
fi

wait -n
exit 1