@ssdavidai/zoclaw 1.3.0-next.5 → 1.3.0-next.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scripts/bootstrap.sh +52 -42
package/package.json
CHANGED
package/scripts/bootstrap.sh
CHANGED
|
@@ -5,6 +5,17 @@
|
|
|
5
5
|
# via Tailscale Serve (both TUI and Control UI in browser).
|
|
6
6
|
# Also migrates secrets to Zo secrets and registers gateway as a
|
|
7
7
|
# Zo user service (supervisord).
|
|
8
|
+
#
|
|
9
|
+
# IMPORTANT: trustedProxies must be added AFTER the local device has
|
|
10
|
+
# auto-paired. When trustedProxies includes 127.0.0.1/32, the gateway
|
|
11
|
+
# treats direct loopback connections as reverse-proxy traffic and looks
|
|
12
|
+
# for x-forwarded-for headers. Direct CLI connections don't send those
|
|
13
|
+
# headers, so the gateway can't resolve their IP → isLocalClient=false
|
|
14
|
+
# → the built-in local auto-pair never fires → deadlock.
|
|
15
|
+
#
|
|
16
|
+
# The fix: two-phase config patching.
|
|
17
|
+
# Phase 1: Everything except trustedProxies → start gateway → auto-pair
|
|
18
|
+
# Phase 2: Add trustedProxies → restart gateway
|
|
8
19
|
set -euo pipefail
|
|
9
20
|
|
|
10
21
|
CONFIG="${HOME}/.openclaw/openclaw.json"
|
|
@@ -16,9 +27,9 @@ if [ ! -f "$CONFIG" ]; then
|
|
|
16
27
|
exit 1
|
|
17
28
|
fi
|
|
18
29
|
|
|
19
|
-
# ─── 1. Patch
|
|
30
|
+
# ─── 1. Patch config (Phase 1: without trustedProxies) ────────────────
|
|
20
31
|
|
|
21
|
-
echo "Patching openclaw config
|
|
32
|
+
echo "Patching openclaw config (phase 1)..."
|
|
22
33
|
|
|
23
34
|
node -e "
|
|
24
35
|
const fs = require('fs');
|
|
@@ -26,18 +37,13 @@ node -e "
|
|
|
26
37
|
const cfg = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
|
|
27
38
|
const gw = cfg.gateway ??= {};
|
|
28
39
|
|
|
29
|
-
// Bind to loopback only (required for tailscale serve mode)
|
|
30
40
|
gw.bind = 'loopback';
|
|
31
|
-
|
|
32
|
-
// Enable OpenClaw's native Tailscale Serve integration.
|
|
33
41
|
gw.tailscale = { mode: 'serve' };
|
|
34
42
|
|
|
35
|
-
//
|
|
36
|
-
|
|
43
|
+
// Do NOT set trustedProxies yet — local auto-pair must happen first.
|
|
44
|
+
// If a previous run left it in, remove it.
|
|
45
|
+
delete gw.trustedProxies;
|
|
37
46
|
|
|
38
|
-
// Ensure token auth is configured. The gateway token is how CLI
|
|
39
|
-
// tools (tui, devices list, etc.) authenticate over WebSocket.
|
|
40
|
-
// Without this, the gateway rejects all connections as 'pairing required'.
|
|
41
47
|
gw.auth ??= {};
|
|
42
48
|
gw.auth.mode = 'token';
|
|
43
49
|
if (!gw.auth.token) {
|
|
@@ -46,24 +52,17 @@ node -e "
|
|
|
46
52
|
} else {
|
|
47
53
|
console.log(' gateway.auth.token -> preserved');
|
|
48
54
|
}
|
|
49
|
-
|
|
50
|
-
// Trust Tailscale identity headers for browser access via Serve.
|
|
51
55
|
gw.auth.allowTailscale = true;
|
|
52
56
|
|
|
53
|
-
// Enable the browser Control UI
|
|
54
57
|
gw.controlUi ??= {};
|
|
55
58
|
gw.controlUi.enabled = true;
|
|
56
59
|
|
|
57
|
-
// Remove invalid denyCommands (default config generates names
|
|
58
|
-
// that don't match real command IDs, triggering audit warnings)
|
|
59
60
|
if (gw.nodes?.denyCommands) delete gw.nodes.denyCommands;
|
|
60
61
|
|
|
61
|
-
// Set workspace to /home/workspace/ (Zo standard workspace)
|
|
62
62
|
cfg.agents ??= {};
|
|
63
63
|
cfg.agents.defaults ??= {};
|
|
64
64
|
cfg.agents.defaults.workspace = '/home/workspace/';
|
|
65
65
|
|
|
66
|
-
// Fix credentials dir permissions (create if missing)
|
|
67
66
|
const credDir = process.env.HOME + '/.openclaw/credentials';
|
|
68
67
|
if (!fs.existsSync(credDir)) fs.mkdirSync(credDir, { recursive: true, mode: 0o700 });
|
|
69
68
|
else fs.chmodSync(credDir, 0o700);
|
|
@@ -73,7 +72,7 @@ node -e "
|
|
|
73
72
|
|
|
74
73
|
echo " gateway.bind = loopback"
|
|
75
74
|
echo " gateway.tailscale.mode = serve"
|
|
76
|
-
echo " gateway.trustedProxies =
|
|
75
|
+
echo " gateway.trustedProxies = (deferred to phase 2)"
|
|
77
76
|
echo " gateway.auth.mode = token"
|
|
78
77
|
echo " gateway.auth.allowTailscale = true"
|
|
79
78
|
echo " gateway.controlUi.enabled = true"
|
|
@@ -84,10 +83,8 @@ echo " agents.defaults.workspace = /home/workspace/"
|
|
|
84
83
|
echo ""
|
|
85
84
|
echo "Migrating secrets to Zo secrets..."
|
|
86
85
|
|
|
87
|
-
# Extract gateway token from (now-patched) openclaw config
|
|
88
86
|
GW_TOKEN=$(node -pe "JSON.parse(require('fs').readFileSync('${CONFIG}','utf8')).gateway?.auth?.token ?? ''" 2>/dev/null || true)
|
|
89
87
|
|
|
90
|
-
# Extract OpenRouter API key from agent auth profiles
|
|
91
88
|
AGENT_AUTH="${HOME}/.openclaw/agents/main/agent/auth-profiles.json"
|
|
92
89
|
OR_KEY=""
|
|
93
90
|
if [ -f "$AGENT_AUTH" ]; then
|
|
@@ -98,7 +95,6 @@ if [ -f "$AGENT_AUTH" ]; then
|
|
|
98
95
|
" 2>/dev/null || true)
|
|
99
96
|
fi
|
|
100
97
|
|
|
101
|
-
# Helper: add or update a key in zo_secrets
|
|
102
98
|
upsert_secret() {
|
|
103
99
|
local key="$1" val="$2"
|
|
104
100
|
if [ -z "$val" ]; then return; fi
|
|
@@ -124,8 +120,6 @@ else
|
|
|
124
120
|
fi
|
|
125
121
|
|
|
126
122
|
# Ensure future shell sessions source zo_secrets (for OPENCLAW_GATEWAY_TOKEN).
|
|
127
|
-
# The CLI tools (tui, devices list, etc.) need this env var to authenticate
|
|
128
|
-
# to the gateway — without it they get "pairing required".
|
|
129
123
|
for rc in "${HOME}/.bashrc" "${HOME}/.zshrc"; do
|
|
130
124
|
if [ -f "$rc" ] && ! grep -q 'source.*\.zo_secrets' "$rc" 2>/dev/null; then
|
|
131
125
|
echo "" >> "$rc"
|
|
@@ -135,7 +129,6 @@ for rc in "${HOME}/.bashrc" "${HOME}/.zshrc"; do
|
|
|
135
129
|
fi
|
|
136
130
|
done
|
|
137
131
|
|
|
138
|
-
# Source now so the rest of this script has the env vars
|
|
139
132
|
source "$SECRETS_FILE" 2>/dev/null || true
|
|
140
133
|
|
|
141
134
|
# ─── 3. Register gateway as Zo user service ───────────────────────────
|
|
@@ -143,20 +136,12 @@ source "$SECRETS_FILE" 2>/dev/null || true
|
|
|
143
136
|
echo ""
|
|
144
137
|
echo "Registering gateway as Zo user service..."
|
|
145
138
|
|
|
146
|
-
# Remove any openclaw daemon (from --install-daemon during onboarding)
|
|
147
|
-
# that would conflict with our supervisor-managed gateway.
|
|
148
139
|
openclaw daemon uninstall 2>/dev/null || true
|
|
149
140
|
|
|
150
|
-
# Kill any existing background gateway process
|
|
151
141
|
pkill -f "openclaw gateway run" 2>/dev/null || true
|
|
152
142
|
pkill -f "openclaw-gateway" 2>/dev/null || true
|
|
153
143
|
sleep 1
|
|
154
144
|
|
|
155
|
-
# Add [program:openclaw-gateway] to user supervisor config if not present.
|
|
156
|
-
# The gateway reads its config (including auth token) from ~/.openclaw/openclaw.json.
|
|
157
|
-
# We do NOT pass OPENCLAW_GATEWAY_TOKEN via env — that would override
|
|
158
|
-
# the config file token and is only needed for the gateway startup, not
|
|
159
|
-
# for CLI tools that read the token from the same config file.
|
|
160
145
|
if ! grep -q "\[program:openclaw-gateway\]" "$USER_SUPERVISOR" 2>/dev/null; then
|
|
161
146
|
cat >> "$USER_SUPERVISOR" << 'SUPERVISOR'
|
|
162
147
|
[program:openclaw-gateway]
|
|
@@ -181,17 +166,47 @@ else
|
|
|
181
166
|
echo " [program:openclaw-gateway] already in user supervisor"
|
|
182
167
|
fi
|
|
183
168
|
|
|
184
|
-
#
|
|
169
|
+
# ─── 4. Phase 1: Start gateway, auto-pair local device ───────────────
|
|
170
|
+
|
|
171
|
+
echo ""
|
|
172
|
+
echo "Starting gateway (phase 1: local device pairing)..."
|
|
173
|
+
|
|
185
174
|
supervisorctl -c "$USER_SUPERVISOR" reread > /dev/null 2>&1 || true
|
|
186
175
|
supervisorctl -c "$USER_SUPERVISOR" update > /dev/null 2>&1 || true
|
|
187
176
|
sleep 2
|
|
188
|
-
|
|
189
|
-
# Restart to pick up any config changes
|
|
190
177
|
supervisorctl -c "$USER_SUPERVISOR" restart openclaw-gateway > /dev/null 2>&1 || \
|
|
191
178
|
supervisorctl -c "$USER_SUPERVISOR" start openclaw-gateway > /dev/null 2>&1 || true
|
|
192
179
|
sleep 5
|
|
193
180
|
|
|
194
|
-
#
|
|
181
|
+
# Trigger a local CLI connection to auto-pair the local device.
|
|
182
|
+
# Without trustedProxies, the gateway sees 127.0.0.1 as a direct
|
|
183
|
+
# local client → isLocalClient=true → silent pairing → auto-approved.
|
|
184
|
+
echo "Pairing local device..."
|
|
185
|
+
if openclaw gateway health > /dev/null 2>&1; then
|
|
186
|
+
echo " Local device paired."
|
|
187
|
+
else
|
|
188
|
+
echo " Warning: local device pairing may have failed."
|
|
189
|
+
echo " If the CLI doesn't work, run: openclaw gateway health"
|
|
190
|
+
fi
|
|
191
|
+
|
|
192
|
+
# ─── 5. Phase 2: Add trustedProxies, restart ─────────────────────────
|
|
193
|
+
|
|
194
|
+
echo ""
|
|
195
|
+
echo "Adding trustedProxies for Tailscale Serve (phase 2)..."
|
|
196
|
+
|
|
197
|
+
node -e "
|
|
198
|
+
const fs = require('fs');
|
|
199
|
+
const cfg = JSON.parse(fs.readFileSync(process.argv[1], 'utf8'));
|
|
200
|
+
cfg.gateway.trustedProxies = ['127.0.0.1/32'];
|
|
201
|
+
fs.writeFileSync(process.argv[1], JSON.stringify(cfg, null, 2) + '\n');
|
|
202
|
+
" "$CONFIG"
|
|
203
|
+
|
|
204
|
+
echo " gateway.trustedProxies = [127.0.0.1/32]"
|
|
205
|
+
|
|
206
|
+
echo "Restarting gateway (phase 2: Tailscale proxy support)..."
|
|
207
|
+
supervisorctl -c "$USER_SUPERVISOR" restart openclaw-gateway > /dev/null 2>&1 || true
|
|
208
|
+
sleep 5
|
|
209
|
+
|
|
195
210
|
if supervisorctl -c "$USER_SUPERVISOR" status openclaw-gateway 2>/dev/null | grep -q RUNNING; then
|
|
196
211
|
echo " Gateway running (supervised)"
|
|
197
212
|
else
|
|
@@ -200,12 +215,7 @@ else
|
|
|
200
215
|
echo " Logs: tail /dev/shm/openclaw-gateway.log /dev/shm/openclaw-gateway_err.log"
|
|
201
216
|
fi
|
|
202
217
|
|
|
203
|
-
#
|
|
204
|
-
echo ""
|
|
205
|
-
echo "Gateway health:"
|
|
206
|
-
openclaw gateway health 2>&1 | head -5 || echo " (health check unavailable)"
|
|
207
|
-
|
|
208
|
-
# ─── 4. Print access info ─────────────────────────────────────────────
|
|
218
|
+
# ─── 6. Print access info ─────────────────────────────────────────────
|
|
209
219
|
|
|
210
220
|
TS_HOSTNAME=$(tailscale status --json 2>/dev/null | node -pe "
|
|
211
221
|
const s = JSON.parse(require('fs').readFileSync('/dev/stdin','utf8'));
|