somratpro commited on
Commit
cebf290
Β·
1 Parent(s): 43f221d

refactor: condense code formatting and add OpenClaw scope-clearing patch to startup script

Browse files
Files changed (2) hide show
  1. start.sh +35 -0
  2. wa-guardian.js +35 -76
start.sh CHANGED
@@ -264,6 +264,41 @@ chmod 600 /home/node/.openclaw/openclaw.json
264
  # This preload script keeps iframe embedding working on HF Spaces.
265
  export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--require /home/node/app/iframe-fix.cjs"
266
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
267
  # ── Startup Summary ──
268
  echo ""
269
  echo " β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”"
 
264
  # This preload script keeps iframe embedding working on HF Spaces.
265
  export NODE_OPTIONS="${NODE_OPTIONS:+$NODE_OPTIONS }--require /home/node/app/iframe-fix.cjs"
266
 
267
+ # ── Patch OpenClaw scope-clearing bug for headless HF auth ──
268
+ # OpenClaw can clear requested operator scopes after allowing a token-auth
269
+ # connection without device identity, which breaks the WhatsApp guardian's
270
+ # web.login.wait / channels.status calls on Spaces.
271
+ patch_openclaw_scope_bug() {
272
+ local roots=(
273
+ "/home/node/.openclaw/openclaw-app"
274
+ "/usr/local/lib/node_modules/openclaw"
275
+ )
276
+ local target=""
277
+ local updated=0
278
+
279
+ for root in "${roots[@]}"; do
280
+ [ -d "$root/dist" ] || continue
281
+ target=$(find "$root/dist" -maxdepth 1 -type f -name 'gateway-cli-*.js' | head -n 1)
282
+ [ -n "$target" ] || continue
283
+
284
+ if grep -q 'return params.decision.kind !== "allow" || !params.controlUiAuthPolicy.allowBypass' "$target"; then
285
+ perl -0pi -e 's@return params\.decision\.kind !== "allow" \|\| !params\.controlUiAuthPolicy\.allowBypass && !params\.preserveInsecureLocalControlUiScopes && \(params\.authMethod === "token" \|\| params\.authMethod === "password" \|\| params\.authMethod === "trusted-proxy" \|\| params\.trustedProxyAuthOk === true\);@return params.decision.kind !== "allow";@g' "$target"
286
+
287
+ if grep -q 'return params.decision.kind !== "allow";' "$target"; then
288
+ echo "πŸ”§ Patched OpenClaw scope-clearing bug in $(basename "$target")"
289
+ updated=1
290
+ break
291
+ fi
292
+ fi
293
+ done
294
+
295
+ if [ "$updated" -eq 0 ]; then
296
+ echo "⚠️ OpenClaw scope patch not applied (bundle format may have changed)"
297
+ fi
298
+ }
299
+
300
+ patch_openclaw_scope_bug
301
+
302
  # ── Startup Summary ──
303
  echo ""
304
  echo " β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”"
wa-guardian.js CHANGED
@@ -9,10 +9,8 @@
9
 
10
  const fs = require("fs");
11
  const path = require("path");
12
- const {
13
- WebSocket,
14
- } = require("/home/node/.openclaw/openclaw-app/node_modules/ws");
15
- const { randomUUID } = require("node:crypto");
16
 
17
  const GATEWAY_URL = "ws://127.0.0.1:7860";
18
  const GATEWAY_TOKEN = process.env.GATEWAY_TOKEN || "huggingclaw";
@@ -33,8 +31,7 @@ let last515At = 0;
33
  function extractErrorMessage(msg) {
34
  if (!msg || typeof msg !== "object") return "Unknown error";
35
  if (typeof msg.error === "string") return msg.error;
36
- if (msg.error && typeof msg.error.message === "string")
37
- return msg.error.message;
38
  if (typeof msg.message === "string") return msg.message;
39
  return "Unknown error";
40
  }
@@ -43,13 +40,9 @@ function writeResetMarker() {
43
  try {
44
  fs.mkdirSync(path.dirname(RESET_MARKER_PATH), { recursive: true });
45
  fs.writeFileSync(RESET_MARKER_PATH, "reset\n");
46
- console.log(
47
- `[guardian] Created backup reset marker at ${RESET_MARKER_PATH}`,
48
- );
49
  } catch (error) {
50
- console.log(
51
- `[guardian] Failed to write backup reset marker: ${error.message}`,
52
- );
53
  }
54
  }
55
 
@@ -62,32 +55,25 @@ async function createConnection() {
62
  const msg = JSON.parse(data.toString());
63
 
64
  if (msg.type === "event" && msg.event === "connect.challenge") {
65
- ws.send(
66
- JSON.stringify({
67
- type: "req",
68
- id: randomUUID(),
69
- method: "connect",
70
- params: {
71
- minProtocol: 3,
72
- maxProtocol: 3,
73
- client: {
74
- id: "gateway-client",
75
- version: "1.0.0",
76
- platform: "linux",
77
- mode: "backend",
78
- },
79
- caps: [],
80
- auth: { token: GATEWAY_TOKEN },
81
- role: "operator",
82
- scopes: [
83
- "operator.read",
84
- "operator.write",
85
- "operator.admin",
86
- "operator.pairing",
87
- ],
88
  },
89
- }),
90
- );
 
 
 
 
91
  return;
92
  }
93
 
@@ -104,15 +90,8 @@ async function createConnection() {
104
  }
105
  });
106
 
107
- ws.on("error", (e) => {
108
- if (!resolved) reject(e);
109
- });
110
- setTimeout(() => {
111
- if (!resolved) {
112
- ws.close();
113
- reject(new Error("Timeout"));
114
- }
115
- }, 10000);
116
  });
117
  }
118
 
@@ -132,10 +111,7 @@ async function callRpc(ws, method, params) {
132
  };
133
  ws.on("message", handler);
134
  ws.send(JSON.stringify({ type: "req", id, method, params }));
135
- setTimeout(() => {
136
- ws.removeListener("message", handler);
137
- reject(new Error("RPC Timeout"));
138
- }, WAIT_TIMEOUT + 5000);
139
  });
140
  }
141
 
@@ -147,16 +123,12 @@ async function checkStatus() {
147
  ws = await createConnection();
148
  isWaiting = true;
149
  if (!hasShownWaitMessage) {
150
- console.log(
151
- "\n[guardian] πŸ“± WhatsApp pairing in progress. Please scan the QR code in the Control UI.",
152
- );
153
  hasShownWaitMessage = true;
154
  }
155
 
156
  console.log("[guardian] Waiting for pairing completion...");
157
- const waitRes = await callRpc(ws, "web.login.wait", {
158
- timeoutMs: WAIT_TIMEOUT,
159
- });
160
  const result = waitRes.payload || waitRes.result;
161
  const message = result?.message || "";
162
  const linkedAfter515 = !result?.connected && message.includes("515");
@@ -169,36 +141,27 @@ async function checkStatus() {
169
  hasShownWaitMessage = false;
170
 
171
  if (linkedAfter515) {
172
- console.log(
173
- "[guardian] 515 after scan: credentials saved, reloading config to start WhatsApp...",
174
- );
175
  } else {
176
  console.log("[guardian] βœ… Pairing completed! Reloading config...");
177
  }
178
 
179
  const getRes = await callRpc(ws, "config.get", {});
180
  if (getRes.payload?.raw && getRes.payload?.hash) {
181
- await callRpc(ws, "config.apply", {
182
- raw: getRes.payload.raw,
183
- baseHash: getRes.payload.hash,
184
- });
185
  console.log("[guardian] Configuration re-applied.");
186
  }
187
- } else if (
188
- !message.includes("No active") &&
189
- !message.includes("Still waiting")
190
- ) {
191
  console.log(`[guardian] Wait result: ${message}`);
192
  }
 
193
  } catch (e) {
194
  const message = e && e.message ? e.message : "";
195
  if (
196
  /401|unauthorized|logged out|440|conflict/i.test(message) &&
197
  Date.now() - last515At >= POST_515_NO_LOGOUT_MS
198
  ) {
199
- console.log(
200
- "[guardian] Clearing invalid WhatsApp session so a fresh QR can be used...",
201
- );
202
  try {
203
  if (ws) {
204
  await callRpc(ws, "channels.logout", { channel: "whatsapp" });
@@ -207,9 +170,7 @@ async function checkStatus() {
207
  console.log("[guardian] Logged out invalid WhatsApp session.");
208
  }
209
  } catch (error) {
210
- console.log(
211
- `[guardian] Failed to log out invalid session: ${error.message}`,
212
- );
213
  }
214
  }
215
  // Normal timeout or gateway starting up; retry on the next interval.
@@ -219,8 +180,6 @@ async function checkStatus() {
219
  }
220
  }
221
 
222
- console.log(
223
- "[guardian] βš”οΈ WhatsApp Guardian active. Monitoring pairing status...",
224
- );
225
  setInterval(checkStatus, CHECK_INTERVAL);
226
  setTimeout(checkStatus, 15000);
 
9
 
10
  const fs = require("fs");
11
  const path = require("path");
12
+ const { WebSocket } = require('/home/node/.openclaw/openclaw-app/node_modules/ws');
13
+ const { randomUUID } = require('node:crypto');
 
 
14
 
15
  const GATEWAY_URL = "ws://127.0.0.1:7860";
16
  const GATEWAY_TOKEN = process.env.GATEWAY_TOKEN || "huggingclaw";
 
31
  function extractErrorMessage(msg) {
32
  if (!msg || typeof msg !== "object") return "Unknown error";
33
  if (typeof msg.error === "string") return msg.error;
34
+ if (msg.error && typeof msg.error.message === "string") return msg.error.message;
 
35
  if (typeof msg.message === "string") return msg.message;
36
  return "Unknown error";
37
  }
 
40
  try {
41
  fs.mkdirSync(path.dirname(RESET_MARKER_PATH), { recursive: true });
42
  fs.writeFileSync(RESET_MARKER_PATH, "reset\n");
43
+ console.log(`[guardian] Created backup reset marker at ${RESET_MARKER_PATH}`);
 
 
44
  } catch (error) {
45
+ console.log(`[guardian] Failed to write backup reset marker: ${error.message}`);
 
 
46
  }
47
  }
48
 
 
55
  const msg = JSON.parse(data.toString());
56
 
57
  if (msg.type === "event" && msg.event === "connect.challenge") {
58
+ ws.send(JSON.stringify({
59
+ type: "req",
60
+ id: randomUUID(),
61
+ method: "connect",
62
+ params: {
63
+ minProtocol: 3,
64
+ maxProtocol: 3,
65
+ client: {
66
+ id: "gateway-client",
67
+ version: "1.0.0",
68
+ platform: "linux",
69
+ mode: "backend",
 
 
 
 
 
 
 
 
 
 
 
70
  },
71
+ caps: [],
72
+ auth: { token: GATEWAY_TOKEN },
73
+ role: "operator",
74
+ scopes: ["operator.read", "operator.write", "operator.admin", "operator.pairing"],
75
+ },
76
+ }));
77
  return;
78
  }
79
 
 
90
  }
91
  });
92
 
93
+ ws.on("error", (e) => { if (!resolved) reject(e); });
94
+ setTimeout(() => { if (!resolved) { ws.close(); reject(new Error("Timeout")); } }, 10000);
 
 
 
 
 
 
 
95
  });
96
  }
97
 
 
111
  };
112
  ws.on("message", handler);
113
  ws.send(JSON.stringify({ type: "req", id, method, params }));
114
+ setTimeout(() => { ws.removeListener("message", handler); reject(new Error("RPC Timeout")); }, WAIT_TIMEOUT + 5000);
 
 
 
115
  });
116
  }
117
 
 
123
  ws = await createConnection();
124
  isWaiting = true;
125
  if (!hasShownWaitMessage) {
126
+ console.log("\n[guardian] πŸ“± WhatsApp pairing in progress. Please scan the QR code in the Control UI.");
 
 
127
  hasShownWaitMessage = true;
128
  }
129
 
130
  console.log("[guardian] Waiting for pairing completion...");
131
+ const waitRes = await callRpc(ws, "web.login.wait", { timeoutMs: WAIT_TIMEOUT });
 
 
132
  const result = waitRes.payload || waitRes.result;
133
  const message = result?.message || "";
134
  const linkedAfter515 = !result?.connected && message.includes("515");
 
141
  hasShownWaitMessage = false;
142
 
143
  if (linkedAfter515) {
144
+ console.log("[guardian] 515 after scan: credentials saved, reloading config to start WhatsApp...");
 
 
145
  } else {
146
  console.log("[guardian] βœ… Pairing completed! Reloading config...");
147
  }
148
 
149
  const getRes = await callRpc(ws, "config.get", {});
150
  if (getRes.payload?.raw && getRes.payload?.hash) {
151
+ await callRpc(ws, "config.apply", { raw: getRes.payload.raw, baseHash: getRes.payload.hash });
 
 
 
152
  console.log("[guardian] Configuration re-applied.");
153
  }
154
+ } else if (!message.includes("No active") && !message.includes("Still waiting")) {
 
 
 
155
  console.log(`[guardian] Wait result: ${message}`);
156
  }
157
+
158
  } catch (e) {
159
  const message = e && e.message ? e.message : "";
160
  if (
161
  /401|unauthorized|logged out|440|conflict/i.test(message) &&
162
  Date.now() - last515At >= POST_515_NO_LOGOUT_MS
163
  ) {
164
+ console.log("[guardian] Clearing invalid WhatsApp session so a fresh QR can be used...");
 
 
165
  try {
166
  if (ws) {
167
  await callRpc(ws, "channels.logout", { channel: "whatsapp" });
 
170
  console.log("[guardian] Logged out invalid WhatsApp session.");
171
  }
172
  } catch (error) {
173
+ console.log(`[guardian] Failed to log out invalid session: ${error.message}`);
 
 
174
  }
175
  }
176
  // Normal timeout or gateway starting up; retry on the next interval.
 
180
  }
181
  }
182
 
183
+ console.log("[guardian] βš”οΈ WhatsApp Guardian active. Monitoring pairing status...");
 
 
184
  setInterval(checkStatus, CHECK_INTERVAL);
185
  setTimeout(checkStatus, 15000);