docker:
everclaw/everclaw:latest
Visit install page on userport, after installation you can visit the gptcp1 port as it has an index landing page.
This is just an initial start, openclaw is a pain with ports and stuff...
Dockerfile
Code: Select all
FROM everweb/test:latest
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && \
apt install -y tzdata && \
ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime && \
dpkg-reconfigure --frontend noninteractive tzdata && \
apt install -y curl ttyd && \
curl -qL https://www.npmjs.com/install.sh | bash && \
apt install -y openssh-server openssl sudo bash nano && \
apt-get install -y supervisor && \
mkdir -p /var/log/supervisor && \
rm -rf /var/lib/apt/lists/*
RUN useradd -m -s /bin/bash everclaw && \
echo "everclaw:default" | chpasswd && \
usermod -aG sudo everclaw && \
echo "everclaw ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
RUN mkdir /var/run/sshd && \
echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config
COPY start.sh /start.sh
COPY supervisord.conf /home/everclaw/supervisord.conf
COPY node_modules /home/everclaw/node_modules
COPY package.json /home/everclaw/package.json
COPY startscript.js /home/everclaw/startscript.js
COPY install_openclaw.sh /home/everclaw/install_openclaw.sh
COPY openclaw_wizard.sh /home/everclaw/openclaw_wizard.sh
COPY openclaw_finish.sh /home/everclaw/openclaw_finish.sh
COPY proxy.js /home/everclaw/proxy.js
COPY index.html /home/everclaw/index.html
RUN chmod +x /home/everclaw/openclaw_finish.sh
RUN chmod +x /home/everclaw/openclaw_wizard.sh
RUN chmod +x /home/everclaw/install_openclaw.sh
RUN chmod +x /start.sh
WORKDIR /home/everclaw
ENTRYPOINT ["/start.sh"]Code: Select all
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>OpenClaw Gateway</title>
<style>
:root{
--bg:#0b0f14; --panel:#0f1621; --border:#263244;
--text:#e6edf3; --muted:#9fb0c0; --link:#6ee7ff;
--good:#36d399;
}
*{box-sizing:border-box}
body{
margin:0; min-height:100vh;
background:radial-gradient(1000px 600px at 20% 0%, #132033 0%, var(--bg) 55%);
color:var(--text);
font-family: ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, Arial, sans-serif;
display:flex; align-items:center; justify-content:center;
padding:24px;
}
.wrap{width:100%; max-width:820px}
.card{
background:rgba(15,22,33,.92);
border:1px solid var(--border);
border-radius:16px;
padding:22px;
box-shadow: 0 18px 60px rgba(0,0,0,.45);
backdrop-filter: blur(6px);
}
h1{margin:0 0 6px; font-size:22px; letter-spacing:.2px}
p{margin:0 0 16px; color:var(--muted); line-height:1.5}
.grid{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
gap:12px;
margin-top:14px;
}
a.tile{
display:block;
text-decoration:none;
color:var(--text);
border:1px solid var(--border);
border-radius:14px;
padding:14px 14px;
background:linear-gradient(180deg, rgba(255,255,255,.04), rgba(255,255,255,.02));
transition: transform .12s ease, border-color .12s ease, background .12s ease;
}
a.tile:hover{
transform: translateY(-2px);
border-color:#3a536b;
background:linear-gradient(180deg, rgba(110,231,255,.10), rgba(255,255,255,.02));
}
.title{font-weight:650; margin:0 0 4px; display:flex; align-items:center; gap:8px}
.desc{margin:0; color:var(--muted); font-size:13px; line-height:1.45}
.badge{
font-size:12px; padding:2px 8px; border-radius:999px;
border:1px solid var(--border); color:var(--muted);
}
.badge.ok{color:var(--good); border-color:rgba(54,211,153,.35)}
.footer{
margin-top:14px; display:flex; flex-wrap:wrap; gap:10px;
align-items:center; justify-content:space-between;
color:var(--muted); font-size:12px;
}
code{
background:#0a1018;
border:1px solid var(--border);
padding:2px 6px;
border-radius:8px;
color:#d7e3ef;
}
</style>
</head>
<body>
<div class="wrap">
<div class="card">
<h1>OpenClaw Gateway</h1>
<p>Quick links for this node. Use the same domain/port you opened this page with.</p>
<div class="grid">
<a class="tile" href="/dashboard">
<div class="title">Dashboard <span class="badge">/dashboard</span></div>
<p class="desc">Main UI (proxied).</p>
</a>
<a class="tile" href="/install">
<div class="title">Install / Terminal <span class="badge">/install</span></div>
<p class="desc">Installer & ttyd terminal (WebSocket).</p>
</a>
<a class="tile" href="/status">
<div class="title">Gateway Status <span class="badge ok">/status</span></div>
<p class="desc">Health check endpoint.</p>
</a>
<a class="tile" href="/api" onclick="return false;">
<div class="title">API (WebSocket) <span class="badge">/api</span></div>
<p class="desc">WebSocket endpoint. Not a normal page.</p>
</a>
</div>
<div class="footer">
<div>Current origin: <code id="origin"></code></div>
<div>Tip: If a service is down, the gateway returns <code>503</code>.</div>
</div>
</div>
</div>
<script>
document.getElementById('origin').textContent = window.location.origin;
</script>
</body>
</html>Code: Select all
#!/bin/bash
set -euo pipefail
SUPERVISOR_CONF="/home/everclaw/supervisord.conf"
CFG_FILE="/contract/cfg/hp.cfg"
STATE_DIR="/home/everclaw/state"
GPTCP1="${1:?gptcp1 required}"
ENABLE_SSH="${2:-no}"
GPTCP2="${3:-$((GPTCP1+1))}"
SSHPASS="${4:-}"
ttydpassword="$(openssl rand -hex 9)" # 18 hex chars
_sed_ttydpwd="$(printf '%s' "$ttydpassword" | sed -e 's/[&|\\]/\\&/g')"
sed -i "s|__ttydpwd__|${_sed_ttydpwd}|g" "$SUPERVISOR_CONF"
mkdir -p "$STATE_DIR"
log() { echo "[$(date +'%F %T')] $*"; }
valid_port() {
[[ "${1:-}" =~ ^[0-9]+$ ]] && (( 1 <= 10#$1 && 10#$1 <= 65535 ))
}
need_cmd() {
command -v "$1" >/dev/null 2>&1 || { log "Missing required command: $1"; exit 1; }
}
set_program_kv() {
local program="$1" key="$2" value="$3" file="$4"
awk -v prog="$program" -v key="$key" -v val="$value" '
BEGIN { in_sec=0; wrote=0 }
/^[[:space:]]*\[/ {
if (in_sec && !wrote) { print key "=" val; wrote=1 }
if ($0 ~ "^[[:space:]]*\\[program:" prog "\\][[:space:]]*$") { in_sec=1; wrote=0 } else { in_sec=0 }
print; next
}
{
if (in_sec && $0 ~ "^[[:space:]]*" key "[[:space:]]*=") { print key "=" val; wrote=1; next }
print
}
END { if (in_sec && !wrote) print key "=" val }
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
}
reload_supervisor() {
supervisorctl reread
supervisorctl update
}
if ! valid_port "$GPTCP1"; then log "Bad gptcp1: $GPTCP1"; exit 1; fi
if [[ "$ENABLE_SSH" == "yes" ]]; then
if ! valid_port "$GPTCP2"; then log "Bad gptcp2: $GPTCP2"; exit 1; fi
if [[ -z "$SSHPASS" ]]; then
log "SSH enabled but no sshPass provided; refusing to use default."
exit 1
fi
fi
need_cmd curl
need_cmd bash
need_cmd supervisorctl
need_cmd awk
need_cmd sed
USERPORT="$(
awk '
$0 ~ /"user"/ { user_block=1 }
user_block && $0 ~ /"port"/ {
gsub(/[ ,]/, "", $2)
gsub(/[^0-9]/, "", $2)
print $2
exit
}
' "$CFG_FILE"
)"
if [[ -z "${USERPORT}" ]]; then
log "Could not extract user.port from $CFG_FILE"
exit 1
fi
log "Inputs: gptcp1=$GPTCP1 enableSsh=$ENABLE_SSH gptcp2=$GPTCP2 userport=$USERPORT"
sed -i "s/__PORTSSL__/${GPTCP1}/g" "/home/everclaw/proxy.js"
sed -i "s/autostart=0/autostart=1/g" "$SUPERVISOR_CONF"
if [[ "$ENABLE_SSH" == "yes" ]]; then
log "Enabling SSH on port $GPTCP2"
SSHD_CONFIG="/etc/ssh/sshd_config"
grep -vE '^[[:space:]]*Port[[:space:]]+[0-9]+' "$SSHD_CONFIG" > "${SSHD_CONFIG}.tmp"
echo "Port $GPTCP2" >> "${SSHD_CONFIG}.tmp"
mv "${SSHD_CONFIG}.tmp" "$SSHD_CONFIG"
need_cmd chpasswd
echo "everclaw:${SSHPASS}" | chpasswd
log "SSH password set for user everclaw."
set_program_kv "sshd" "autostart" "true" "$SUPERVISOR_CONF"
set_program_kv "sshd" "autorestart" "true" "$SUPERVISOR_CONF"
reload_supervisor
supervisorctl start sshd || true
fi
heartbeat_pid=""
start_heartbeat() {
(
while true; do
log "Installer still running..."
sleep 8
done
) &
heartbeat_pid="$!"
}
stop_heartbeat() {
if [[ -n "${heartbeat_pid}" ]] && kill -0 "${heartbeat_pid}" 2>/dev/null; then
kill "${heartbeat_pid}" 2>/dev/null || true
wait "${heartbeat_pid}" 2>/dev/null || true
fi
}
log "Installing OpenClaw..."
start_heartbeat
set +e
set +o pipefail
curl -fsSL https://openclaw.ai/install.sh | bash -s -- --no-onboard
rc=$?
set -o pipefail
set -e
stop_heartbeat
if [[ $rc -ne 0 ]]; then
log "OpenClaw installer failed with exit code $rc"
exit $rc
fi
log "OpenClaw install done."
log "Ensuring ttyd exists"
if ! command -v ttyd >/dev/null 2>&1; then
if command -v apt-get >/dev/null 2>&1; then
apt-get update
apt-get install -y ttyd
elif command -v apk >/dev/null 2>&1; then
apk add --no-cache ttyd
else
log "No package manager found to install ttyd. Install ttyd in the image."
exit 1
fi
fi
log "Almost Done."
curl -sk -m 2 -X POST "https://127.0.0.1:${USERPORT}/almost-done" || \
curl -s -m 2 -X POST "http://127.0.0.1:${USERPORT}/almost-done" || true
echo "Your install username is everclaw and your install password is $ttydpassword, do not forget it during installation. PS: this window needs to be closed so hotpocket can start, if you want to use it."
nohup bash -s -- "$USERPORT" "$SUPERVISOR_CONF" >"$STATE_DIR/switch_to_ttyd.log" 2>&1 <<'SWITCH'
set -euo pipefail
USERPORT="$1"
SUPERVISOR_CONF="$2"
echo "[handover] starting handover on port $USERPORT"
set_program_kv() {
local program="$1" key="$2" value="$3" file="$4"
awk -v prog="$program" -v key="$key" -v val="$value" '
BEGIN { in_sec=0; wrote=0 }
/^[[:space:]]*\[/ {
if (in_sec && !wrote) { print key "=" val; wrote=1 }
if ($0 ~ "^[[:space:]]*\\[program:" prog "\\][[:space:]]*$") { in_sec=1; wrote=0 } else { in_sec=0 }
print; next
}
{
if (in_sec && $0 ~ "^[[:space:]]*" key "[[:space:]]*=") { print key "=" val; wrote=1; next }
print
}
END { if (in_sec && !wrote) print key "=" val }
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
}
port_in_use() {
local p="$1"
if command -v ss >/dev/null 2>&1; then
ss -lnt | awk '{print $4}' | grep -q ":${p}\$"
elif command -v netstat >/dev/null 2>&1; then
netstat -lnt 2>/dev/null | awk '{print $4}' | grep -q ":${p}\$"
elif command -v lsof >/dev/null 2>&1; then
lsof -iTCP -sTCP:LISTEN -P 2>/dev/null | awk '{print $9}' | grep -q ":${p}$"
else
return 0
fi
}
set_program_kv "ttyd" "autostart" "true" "$SUPERVISOR_CONF"
set_program_kv "ttyd" "autorestart" "true" "$SUPERVISOR_CONF"
set_program_kv "startscript" "autostart" "false" "$SUPERVISOR_CONF"
set_program_kv "startscript" "autorestart" "false" "$SUPERVISOR_CONF"
supervisorctl reread
supervisorctl update
sleep 2
supervisorctl stop startscript || true
for i in $(seq 1 200); do
if port_in_use "$USERPORT"; then
sleep 0.1
else
break
fi
done
supervisorctl start ttyd
echo "[handover] ttyd started"
SWITCH
exit 0Code: Select all
#!/bin/bash
set -euo pipefail
SUPERVISOR_CONF="/home/everclaw/supervisord.conf"
log(){ echo "[$(date +'%F %T')] $*"; }
set_program_kv() {
local program="$1" key="$2" value="$3" file="$4"
awk -v prog="$program" -v key="$key" -v val="$value" '
BEGIN { in_sec=0; wrote=0 }
/^[[:space:]]*\[/ {
if (in_sec && !wrote) { print key "=" val; wrote=1 }
if ($0 ~ "^[[:space:]]*\\[program:" prog "\\][[:space:]]*$") { in_sec=1; wrote=0 } else { in_sec=0 }
print; next
}
{
if (in_sec && $0 ~ "^[[:space:]]*" key "[[:space:]]*=") { print key "=" val; wrote=1; next }
print
}
END { if (in_sec && !wrote) print key "=" val }
' "$file" > "${file}.tmp" && mv "${file}.tmp" "$file"
}
reload_supervisor() {
supervisorctl reread
supervisorctl update
}
log "Finish step: decide HotPocket + shut off ttyd"
read -r -p "Enable HotPocket autostart? (yes/no) [yes]: " ans
ans="${ans:-yes}"
if [[ "$ans" == "yes" ]]; then
set_program_kv "hotpocket" "autostart" "true" "$SUPERVISOR_CONF"
set_program_kv "hotpocket" "autorestart" "true" "$SUPERVISOR_CONF"
else
set_program_kv "hotpocket" "autostart" "false" "$SUPERVISOR_CONF"
set_program_kv "hotpocket" "autorestart" "false" "$SUPERVISOR_CONF"
fi
set_program_kv "ttyd" "autostart" "false" "$SUPERVISOR_CONF"
set_program_kv "ttyd" "autorestart" "false" "$SUPERVISOR_CONF"
reload_supervisor
if [[ "$ans" == "yes" ]]; then
supervisorctl start hotpocket || true
fi
supervisorctl stop ttyd || true
log "Done."
log "If you enabled hotpocket, it's started. ttyd has been stopped."Code: Select all
#!/bin/bash
set -e
echo
echo "=== OpenClaw Wizard ==="
echo "Running: openclaw setup --wizard"
echo
openclaw setup --wizard
echo
echo "Starting OpenClaw Gateway (Supervisor)..."
echo
supervisorctl reread
supervisorctl update
supervisorctl start openclaw-gateway
supervisorctl status openclaw-gateway || true
echo
echo "Wizard finished."
echo "Now it is your turn to finetune configurations."
echo "When you are finished, run the script below to remove this terminal and to decide if you want to enable hotpocket or not."
echo " /home/everclaw/openclaw_finish.sh"
echo
exec bashCode: Select all
{
"name": "eveclaw",
"version": "1.0.0",
"private": true,
"description": "everclaw",
"main": "startscript.js",
"type": "commonjs",
"scripts": {
"start": "node startscript.js"
},
"dependencies": {
"http-proxy-middleware": "^3.0.0",
"express": "^4.19.2"
}
}proxy.js
Code: Select all
'use strict';
const fs = require('fs');
const https = require('https');
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const path = require('path');
const CERT = '/contract/cfg/tlscert.pem';
const KEY = '/contract/cfg/tlskey.pem';
const DASHBOARD_TARGET = 'http://127.0.0.1:18791';
const INSTALL_TARGET = 'http://127.0.0.1:__USERPORT__';
const API_TARGET = 'ws://127.0.0.1:18789';
const app = express();
app.get('/', (req, res) => {
res.sendFile('/home/everclaw/index.html');
});
app.use(express.static('/home/everclaw'));
app.get('/status', (req, res) => res.status(200).send('ok'));
function proxyErrorHandler(routeName) {
return (err, req, res) => {
if (res && !res.headersSent) {
res.status(503).type('text/plain').send(
`${routeName} is temporarily unavailable (upstream unreachable).\n` +
`Error: ${err.code || err.message}\n`
);
}
};
}
function addForwardHeaders(proxyReq, req) {
proxyReq.setHeader('X-Forwarded-Proto', 'https');
proxyReq.setHeader('X-Forwarded-Host', req.headers.host);
}
const dashboardProxy = createProxyMiddleware({
target: DASHBOARD_TARGET,
changeOrigin: true,
logLevel: 'warn',
pathRewrite: (path) => path.replace(/^\/dashboard/, '') || '/',
proxyTimeout: 30_000,
timeout: 30_000,
onProxyReq: addForwardHeaders,
onError: proxyErrorHandler('dashboard'),
});
app.use('/dashboard', dashboardProxy);
const installProxy = createProxyMiddleware({
target: INSTALL_TARGET,
changeOrigin: true,
ws: true,
logLevel: 'warn',
pathRewrite: (path) => path.replace(/^\/install/, '') || '/',
proxyTimeout: 30_000,
timeout: 30_000,
onProxyReq: addForwardHeaders,
onError: proxyErrorHandler('install'),
});
app.use('/install', installProxy);
const installWsProxy = createProxyMiddleware({
target: INSTALL_TARGET,
changeOrigin: true,
ws: true,
logLevel: 'warn',
proxyTimeout: 30_000,
timeout: 30_000,
onProxyReq: addForwardHeaders,
onError: proxyErrorHandler('install-ws'),
});
app.use('/ws', installWsProxy);
const apiProxy = createProxyMiddleware({
target: API_TARGET,
changeOrigin: true,
ws: true,
logLevel: 'warn',
pathRewrite: (path) => path.replace(/^\/api/, '') || '/',
proxyTimeout: 30_000,
timeout: 30_000,
onProxyReq: addForwardHeaders,
onError: proxyErrorHandler('api'),
});
app.use('/api', apiProxy);
const server = https.createServer(
{
cert: fs.readFileSync(CERT),
key: fs.readFileSync(KEY),
},
app
);
server.on('upgrade', (req, socket, head) => {
const url = req.url || '';
if (url.startsWith('/api')) {
apiProxy.upgrade(req, socket, head);
} else if (url.startsWith('/install')) {
installProxy.upgrade(req, socket, head);
} else if (url.startsWith('/ws')) {
installWsProxy.upgrade(req, socket, head);
} else {
socket.destroy();
}
});
server.listen(__PORTSSL__, '0.0.0.0', () => {
console.log(`HTTPS proxy listening on :__PORTSSL__`);
console.log(`https://<host>/dashboard -> ${DASHBOARD_TARGET}`);
console.log(`https://<host>/install -> ${INSTALL_TARGET}`);
console.log(`wss://<host>/api -> ${API_TARGET}`);
});Code: Select all
#!/bin/bash
set -euo pipefail
SUPERVISOR_CONF="/home/everclaw/supervisord.conf"
CFG_FILE="/contract/cfg/hp.cfg"
STATE_DIR="/home/everclaw/state"
mkdir -p "$STATE_DIR"
if [ ! -f /etc/ssh/ssh_host_rsa_key ]; then
ssh-keygen -A
fi
userport="$(
awk '
$0 ~ /"user"/ { user_block=1 }
user_block && $0 ~ /"port"/ {
gsub(/[ ,]/, "", $2)
gsub(/[^0-9]/, "", $2)
print $2
exit
}
' "$CFG_FILE"
)"
if [[ -z "${userport}" ]]; then
echo "Could not extract user.port from $CFG_FILE"
exit 1
fi
TARGET="/home/everclaw/startscript.js"
sed -i -E "s/^const PORT = .*/const PORT = ${userport};/" "$TARGET"
sed -i "s/__USERPORT__/${userport}/g" "$SUPERVISOR_CONF"
sed -i "s/__USERPORT__/${userport}/g" "/home/everclaw/proxy.js"
check_service() {
local service_name="$1"
local start_command="$2"
if ! pgrep -f "$service_name" > /dev/null; then
echo "$service_name is not running. Restarting..."
nohup bash -c "$start_command" > /dev/null 2>&1 &
echo "$service_name restarted."
else
echo "$service_name is running." > /dev/null 2>&1 &
fi
}
while true; do
check_service "supervisord" "/usr/bin/supervisord -c /home/everclaw/supervisord.conf"
sleep 10
doneCode: Select all
'use strict';
const fs = require('fs');
const http = require('http');
const https = require('https');
const express = require('express');
const { spawn } = require('child_process');
const PORT = __USERPORT__;
const TLS_CERT = '/contract/cfg/tlscert.pem';
const TLS_KEY = '/contract/cfg/tlskey.pem';
const INSTALL_SH = '/home/everclaw/install_openclaw.sh';
const app = express();
app.use(express.urlencoded({ extended: false }));
app.set('trust proxy', true);
let logClients = [];
function broadcast(line) {
const msg = String(line).replace(/\r?\n/g, '');
for (const res of logClients) res.write(`data: ${msg}\n\n`);
}
function firstHeader(v) {
return (v || '').toString().split(',')[0].trim();
}
function externalProto(req) {
return firstHeader(req.headers['x-forwarded-proto']) || 'https';
}
function externalHost(req) {
return firstHeader(req.headers['x-forwarded-host']) || firstHeader(req.headers.host);
}
function hostWithPort(host, port) {
host = firstHeader(host);
const p = String(port || '').trim();
if (!host || !p) return '';
if (host.startsWith('[')) {
const end = host.indexOf(']');
if (end !== -1) return `${host.slice(0, end + 1)}:${p}`;
}
const hostname = host.split(':')[0];
return `${hostname}:${p}`;
}
let lastInstallUrl = null;
let currentProc = null;
app.get('/', (req, res) => {
res.type('html').send(`<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width,initial-scale=1" />
<title>OpenClaw Installer</title>
<style>
body { font-family: ui-sans-serif, system-ui; margin: 18px; background:#0b0f14; color:#e6edf3; display:flex;}
.wrapper {max-width:860px;margin:auto;width:100%;}
input, button { padding: 6px 8px; }
button { cursor:pointer; }
#out {
margin-top: 12px; height: 55vh; overflow:auto;
background:#000; color:#00ff6a; border:1px solid #263244; padding:12px;
white-space: pre-wrap; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
font-size: 13px;
}
.row { margin-top: 10px; }
.muted { color:#9fb0c0; font-size: 13px; }
</style>
</head>
<body><div class="wrapper">
<h2>OpenClaw Installer</h2>
<div class="muted">Tip: this page may be served under <code>/install</code> by the gateway.</div>
<form id="installForm">
<div class="row">Enter your GPTCP1 Port: <input name="gptcp1" type="number" required></div>
<div class="row">
<label>
<input id="enableSsh" type="checkbox" name="enableSsh" value="yes">
Enable SSH (Port: GPTCP2 will be used, Username: everclaw)
</label>
</div>
<div class="row" id="sshPassRow" style="display:none;">
SSH Password:
<input id="sshPass" name="sshPass" type="text" readonly style="width:260px;">
<button type="button" id="genBtn">Generate</button>
</div>
<!-- keep gptcp2 hidden; installer defaults to gptcp1+1 if empty -->
<input name="gptcp2" type="hidden" value="">
<div class="row"><button type="submit">Install</button></div>
</form>
<h3>Console</h3>
<div id="out">(waiting)</div>
<form class="row" id="sendForm" style="width:100%;text-align:center;justify-content:center;align-items:center;display:flex;">
<input id="cmd" type="text" style="width:100%;" placeholder="type input to send to installer stdin (no PTY)" />
<button type="submit" style="width:125px;">Send</button>
<button type="button" id="stopBtn" style="width:125px;">Stop</button>
</form>
<script>
const base = window.location.pathname.endsWith('/')
? window.location.pathname
: (window.location.pathname + '/');
function url(p) { return base + p; }
const outEl = document.getElementById('out');
const es = new EventSource(url('stream'));
es.onmessage = (e) => { outEl.textContent += e.data + "\\n"; outEl.scrollTop = outEl.scrollHeight; };
const enableSshEl = document.getElementById('enableSsh');
const sshPassRow = document.getElementById('sshPassRow');
const sshPassEl = document.getElementById('sshPass');
const genBtn = document.getElementById('genBtn');
function genSshPass(len = 18) {
const alphabet = 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789';
const buf = new Uint32Array(len);
crypto.getRandomValues(buf);
let pw = '';
for (let i = 0; i < len; i++) pw += alphabet[buf[i] % alphabet.length];
sshPassEl.value = pw;
return pw;
}
genBtn.addEventListener('click', () => genSshPass());
enableSshEl.addEventListener('change', () => {
sshPassRow.style.display = enableSshEl.checked ? '' : 'none';
if (enableSshEl.checked && !sshPassEl.value) genSshPass();
});
document.getElementById('installForm').addEventListener('submit', async (ev) => {
ev.preventDefault();
if (enableSshEl.checked && !sshPassEl.value) genSshPass();
const fd = new FormData(ev.target);
const body = new URLSearchParams(fd);
const r = await fetch(url('install'), {
method: 'POST',
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
body
});
if (!r.ok) {
const t = await r.text().catch(() => '');
outEl.textContent += "Install request failed: " + r.status + " " + t + "\\n";
outEl.scrollTop = outEl.scrollHeight;
}
});
document.getElementById('sendForm').addEventListener('submit', async (ev) => {
ev.preventDefault();
const v = document.getElementById('cmd').value;
document.getElementById('cmd').value = '';
await fetch(url('send'), {
method:'POST',
headers:{'Content-Type':'application/x-www-form-urlencoded'},
body: 'line=' + encodeURIComponent(v)
});
});
document.getElementById('stopBtn').addEventListener('click', async () => {
await fetch(url('stop'), { method:'POST' });
});
</script>
</div>
</body>
</html>`);
});
app.get('/stream', (req, res) => {
res.writeHead(200, {
'Content-Type': 'text/event-stream',
'Cache-Control': 'no-cache',
Connection: 'keep-alive',
});
logClients.push(res);
req.on('close', () => { logClients = logClients.filter(x => x !== res); });
});
app.post('/almost-done', (req, res) => {
broadcast('Almost Done. The page will disconnect when switching to ttyd.');
broadcast('Re-enter this page in ~10 seconds to complete the openclaw installation:');
broadcast(lastInstallUrl || '(install URL not available yet — start install from this page first)');
res.type('text/plain').send('ok');
});
app.post('/install', (req, res) => {
if (currentProc) return res.status(409).send('Install already running.');
if (!fs.existsSync(INSTALL_SH)) return res.status(500).send('Missing install_openclaw.sh');
const gptcp1 = (req.body.gptcp1 || '').toString().trim();
const enableSsh = (req.body.enableSsh || '') === 'yes' ? 'yes' : 'no';
const gptcp2 = (req.body.gptcp2 || '').toString().trim();
const sshPass = (req.body.sshPass || '').toString().trim();
const proto = externalProto(req);
const host = externalHost(req);
lastInstallUrl = `${proto}://${hostWithPort(host, gptcp1)}/install`;
broadcast('--- starting installer ---');
currentProc = spawn('/bin/bash', [INSTALL_SH, gptcp1, enableSsh, gptcp2, sshPass], {
stdio: ['pipe', 'pipe', 'pipe']
});
currentProc.stdout.on('data', d => broadcast(d.toString('utf8')));
currentProc.stderr.on('data', d => broadcast(d.toString('utf8')));
currentProc.on('exit', code => {
broadcast(`--- installer exit ${code} ---`);
currentProc = null;
});
res.type('text/plain').send('ok');
});
app.post('/send', (req, res) => {
const line = (req.body.line || '').toString();
if (!currentProc || !currentProc.stdin || currentProc.killed) {
return res.status(409).send('No installer running.');
}
currentProc.stdin.write(line + '\n');
res.send('ok');
});
app.post('/stop', (req, res) => {
if (currentProc && !currentProc.killed) {
currentProc.kill('SIGTERM');
broadcast('--- sent SIGTERM to installer ---');
}
res.send('ok');
});
const useTls = fs.existsSync(TLS_CERT) && fs.existsSync(TLS_KEY);
if (useTls) {
https.createServer({ cert: fs.readFileSync(TLS_CERT), key: fs.readFileSync(TLS_KEY) }, app)
.listen(PORT, '0.0.0.0', () => console.log(`HTTPS on ${PORT}`));
} else {
http.createServer(app)
.listen(PORT, '0.0.0.0', () => console.log(`HTTP on ${PORT}`));
}Code: Select all
[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid
[unix_http_server]
file=/var/run/supervisor.sock
chmod=0770
chown=everclaw:everclaw
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
serverurl=unix:///var/run/supervisor.sock
[program:sshd]
startretries=100
startsecs=15
command=/usr/sbin/sshd -D
autostart=false
autorestart=false
stderr_logfile=/var/log/supervisor/sshd.err.log
stdout_logfile=/var/log/supervisor/sshd.out.log
priority=10
[program:hotpocket]
startretries=100
startsecs=15
command=/usr/local/bin/hotpocket/hpcore run /contract
autostart=false
autorestart=false
stderr_logfile=/var/log/supervisor/hotpocket.err.log
stdout_logfile=/var/log/supervisor/hotpocket.out.log
priority=20
[program:ttyd]
command=/usr/bin/ttyd -p __USERPORT__ -i 0.0.0.0 -W -c everclaw:__ttydpwd__ bash -lc /home/everclaw/openclaw_wizard.sh
autostart=false
autorestart=true
stderr_logfile=/var/log/supervisor/ttyd.err.log
stdout_logfile=/var/log/supervisor/ttyd.out.log
priority=25
[program:startscript]
startretries=100
startsecs=15
command=/usr/bin/node /home/everclaw/startscript.js
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/startscript.err.log
stdout_logfile=/var/log/supervisor/startscript.out.log
priority=30
[program:proxy]
startretries=100
startsecs=15
command=/usr/bin/node /home/everclaw/proxy.js
autostart=0
autorestart=1
stderr_logfile=/var/log/supervisor/proxy.err.log
stdout_logfile=/var/log/supervisor/proxy.out.log
priority=30
[program:openclaw-gateway]
startretries=100
startsecs=5
command=/bin/bash -lc "openclaw gateway"
directory=/home/everclaw
user=everclaw
autostart=false
autorestart=true
stderr_logfile=/var/log/supervisor/openclaw-gateway.err.log
stdout_logfile=/var/log/supervisor/openclaw-gateway.out.log
priority=35