@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssdavidai/zoclaw",
3
- "version": "1.3.0-next.5",
3
+ "version": "1.3.0-next.6",
4
4
  "description": "Set up OpenClaw on Zo with Tailscale access in one command",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -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 openclaw config ─────────────────────────────────────────
30
+ # ─── 1. Patch config (Phase 1: without trustedProxies) ────────────────
20
31
 
21
- echo "Patching openclaw config for Tailscale Serve..."
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
- // Trust localhost as reverse proxy (Tailscale Serve loopback).
36
- gw.trustedProxies = ['127.0.0.1/32'];
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 = [127.0.0.1/32]"
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
- # Reload supervisor config and start/restart the gateway
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
- # Verify gateway is running
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
- # Quick gateway health check
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'));