@syengup/friday-channel-next 0.0.38 → 0.0.40

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/install.js CHANGED
@@ -1,21 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
  import { execSync } from "node:child_process";
3
- import { cpSync, existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
4
4
  import { homedir, networkInterfaces } from "node:os";
5
- import { dirname, join, relative, resolve, sep } from "node:path";
6
- import { fileURLToPath } from "node:url";
7
-
8
- const __filename = fileURLToPath(import.meta.url);
9
- const __dirname = dirname(__filename);
5
+ import { join } from "node:path";
10
6
 
11
7
  const sudoUser = process.env.SUDO_USER;
12
8
 
13
9
  function realHome() {
14
10
  if (!sudoUser) return homedir();
15
- // Under sudo, homedir() may return /root. Check if HOME was preserved.
16
11
  const current = homedir();
17
12
  if (current !== "/root" && current !== "/var/root" && existsSync(current)) return current;
18
- // Resolve the real user's home
19
13
  try {
20
14
  const h = execSync(`sh -c 'echo ~${sudoUser}'`, { encoding: "utf8" }).trim();
21
15
  if (h && !h.startsWith("~") && existsSync(h)) return h;
@@ -27,30 +21,18 @@ function realHome() {
27
21
  }
28
22
 
29
23
  const USER_HOME = realHome();
30
- const PLUGIN_DIR = process.argv[2] || join(USER_HOME, ".openclaw", "extensions", "friday-channel-next");
31
24
  const OPENCLAW_CONFIG = join(USER_HOME, ".openclaw", "openclaw.json");
32
- const REPO_URL = process.env.FRIDAY_NEXT_REPO || "https://github.com/SyengUp/openclaw-fridaynext-channel.git";
33
25
 
34
26
  const G = (s) => `\x1b[32m${s}\x1b[0m`;
35
27
  const Y = (s) => `\x1b[33m${s}\x1b[0m`;
36
28
  const R = (s) => `\x1b[31m${s}\x1b[0m`;
37
- function log(msg) {
38
- console.log(` ${msg}`);
39
- }
40
- function warn(msg) {
41
- console.log(` ${Y("!")} ${msg}`);
42
- }
43
- function err(msg) {
44
- console.error(` ${R("X")} ${msg}`);
45
- }
29
+ function log(msg) { console.log(` ${msg}`); }
30
+ function warn(msg) { console.log(` ${Y("!")} ${msg}`); }
31
+ function err(msg) { console.error(` ${R("X")} ${msg}`); }
46
32
 
47
33
  function has(cmd) {
48
- try {
49
- execSync(`${cmd} --version`, { stdio: "ignore" });
50
- return true;
51
- } catch {
52
- return false;
53
- }
34
+ try { execSync(`${cmd} --version`, { stdio: "ignore" }); return true; }
35
+ catch { return false; }
54
36
  }
55
37
 
56
38
  let openclawCmd = "openclaw";
@@ -58,7 +40,6 @@ let openclawCmd = "openclaw";
58
40
  function hasOpenclaw() {
59
41
  if (has("openclaw")) return true;
60
42
  if (!sudoUser) return false;
61
- // Under sudo, openclaw isn't in root's PATH — run via the real user.
62
43
  try {
63
44
  execSync(`sudo -u "${sudoUser}" openclaw --version`, { stdio: "ignore" });
64
45
  openclawCmd = `sudo -u "${sudoUser}" openclaw`;
@@ -67,16 +48,6 @@ function hasOpenclaw() {
67
48
  return false;
68
49
  }
69
50
 
70
- // Running from an npm/npx package when we have the full source (index.ts + package.json)
71
- // and are NOT already inside the target plugin dir.
72
- function isRunningFromNpmPackage() {
73
- return (
74
- resolve(__dirname) !== resolve(PLUGIN_DIR) &&
75
- existsSync(join(__dirname, "package.json")) &&
76
- existsSync(join(__dirname, "index.ts"))
77
- );
78
- }
79
-
80
51
  // --------------- prerequisites ---------------
81
52
 
82
53
  if (sudoUser) {
@@ -84,15 +55,12 @@ if (sudoUser) {
84
55
  warn("If possible, run without sudo: npx -y @syengup/friday-channel-next");
85
56
  }
86
57
 
87
- const missing = [];
88
- if (!has("node")) missing.push("node");
89
- if (!hasOpenclaw()) missing.push("openclaw");
90
- if (missing.length) {
91
- missing.forEach((c) => err(`${c} is required but not found. Install it first.`));
92
- if (sudoUser && missing.includes("openclaw")) {
93
- err("Could not find openclaw even via the real user's PATH.");
94
- err(`Check that openclaw is installed under ${USER_HOME}.`);
95
- }
58
+ if (!has("node")) {
59
+ err("node is required but not found. Install it first.");
60
+ process.exit(1);
61
+ }
62
+ if (!hasOpenclaw()) {
63
+ err("openclaw is required but not found. Install OpenClaw first: https://docs.openclaw.ai");
96
64
  process.exit(1);
97
65
  }
98
66
 
@@ -113,219 +81,149 @@ if (missing.length) {
113
81
  err(`OpenClaw version ${m[0]} is too old.`);
114
82
  err(`Friday Next channel requires OpenClaw 2026.5.12 or above.`);
115
83
  err(`Please update: ${openclawCmd} update`);
116
- err(`请先升级 OpenClaw 至 2026.5.12 或以上版本:${openclawCmd} update`);
117
84
  process.exit(1);
118
85
  }
119
86
  }
120
87
  } catch {
121
- // If version check itself fails, don't block — continue with a warning
122
88
  warn("Could not determine OpenClaw version — continuing anyway.");
123
89
  }
124
90
  }
125
91
 
126
- const PKG = has("pnpm") ? "pnpm" : has("npm") ? "npm" : null;
127
- if (!PKG) {
128
- err("pnpm or npm is required but not found. Install one first.");
129
- process.exit(1);
130
- }
92
+ // --------------- install via openclaw plugins ---------------
93
+
94
+ log("Installing Friday Next channel via openclaw plugins...");
95
+ let installed = false;
131
96
 
132
- // Auto-detect best registry (measure latency; fall back to npmmirror if slow/unreachable)
133
- let registryFlag = "";
134
97
  try {
135
- const start = Date.now();
136
- execSync('curl -s -o /dev/null --connect-timeout 2 --max-time 4 https://registry.npmjs.org/', { stdio: "pipe", timeout: 6000 });
137
- if (Date.now() - start > 1500) {
138
- warn("Default registry slow, using https://registry.npmmirror.com");
139
- registryFlag = "--registry=https://registry.npmmirror.com";
140
- }
141
- } catch {
142
- warn("Default registry unreachable, using https://registry.npmmirror.com");
143
- registryFlag = "--registry=https://registry.npmmirror.com";
98
+ // --dangerously-force-unsafe-install needed for child_process in device-approve / nodes-approve
99
+ const out = execSync(
100
+ `${openclawCmd} plugins install --dangerously-force-unsafe-install @syengup/friday-channel-next@latest`,
101
+ { encoding: "utf8", stdio: "pipe", timeout: 120000 }
102
+ );
103
+ if (out.trim()) console.log(out.trim());
104
+ installed = true;
105
+ log("Plugin registered with install record — auto-upgrade enabled.");
106
+ } catch (e) {
107
+ const msg = (e.stderr || e.stdout || e.message || "").toString();
108
+ warn("openclaw plugins install failed: " + msg.trim().split("\n").pop());
109
+ warn("Falling back to manual install...");
144
110
  }
145
111
 
146
- if (!existsSync(OPENCLAW_CONFIG)) {
147
- err(`OpenClaw config not found at ${OPENCLAW_CONFIG}`);
148
- err("Make sure OpenClaw is installed and has been run at least once.");
149
- process.exit(1);
150
- }
112
+ // --------------- fallback: manual install ---------------
151
113
 
152
- // --------------- acquire source ---------------
153
-
154
- if (isRunningFromNpmPackage()) {
155
- log(`Copying plugin from npm package to ${PLUGIN_DIR} ...`);
156
- cpSync(__dirname, PLUGIN_DIR, {
157
- recursive: true,
158
- filter: (src) => {
159
- const rel = relative(__dirname, src);
160
- if (rel === "") return true; // root dir
161
- const top = rel.split(sep)[0];
162
- return ![".git", "node_modules", "dist", "attachments", ".claude"].includes(top);
163
- },
164
- });
165
- } else if (existsSync(PLUGIN_DIR)) {
166
- log(`Plugin directory found: ${PLUGIN_DIR}`);
167
- if (existsSync(join(PLUGIN_DIR, ".git"))) {
168
- try {
169
- log("Pulling latest changes...");
170
- execSync("git fetch origin && git checkout -f origin/main", { cwd: PLUGIN_DIR, stdio: "pipe" });
171
- log("Updated to latest version.");
172
- } catch {
173
- warn("Could not update from git — continuing with existing source.");
174
- }
175
- }
176
- } else {
177
- if (!has("git")) {
178
- err("git is required for installation from GitHub. Install git first or use npx @openclaw/friday-channel-next.");
114
+ if (!installed) {
115
+ const PKG = has("npm") ? "npm" : has("pnpm") ? "pnpm" : null;
116
+ if (!PKG) {
117
+ err("npm is required for manual install. Install Node.js first.");
179
118
  process.exit(1);
180
119
  }
181
- log(`Cloning plugin to ${PLUGIN_DIR} ...`);
182
- execSync(`git clone "${REPO_URL}" "${PLUGIN_DIR}"`, { stdio: "inherit" });
183
- }
184
-
185
- process.chdir(PLUGIN_DIR);
186
120
 
187
- // --------------- install + build ---------------
188
-
189
- log("Installing dependencies...");
190
- try {
191
- execSync(`${PKG} install ${registryFlag}`, { stdio: "inherit" });
192
- } catch {
193
- err("Dependency installation failed.");
194
- err("Check your network connection and try again.");
195
- if (sudoUser) err("If running under sudo, ensure the real user can access the package manager.");
196
- process.exit(1);
197
- }
198
-
199
- log("Building TypeScript...");
200
- try {
201
- execSync(`${PKG} run build`, { stdio: "inherit" });
202
- } catch {
203
- err("TypeScript build failed.");
204
- err("Check the compilation errors above and make sure the package is not corrupted.");
205
- process.exit(1);
206
- }
207
-
208
- // --------------- configure OpenClaw ---------------
209
-
210
- log("Configuring OpenClaw...");
121
+ if (!existsSync(OPENCLAW_CONFIG)) {
122
+ err(`OpenClaw config not found at ${OPENCLAW_CONFIG}`);
123
+ err("Run openclaw at least once before installing plugins.");
124
+ process.exit(1);
125
+ }
211
126
 
212
- let config;
213
- try {
214
- config = JSON.parse(readFileSync(OPENCLAW_CONFIG, "utf8"));
215
- } catch {
216
- err(`Failed to read ${OPENCLAW_CONFIG}.`);
217
- err("The file may be missing or corrupted. Verify OpenClaw is installed correctly.");
218
- process.exit(1);
219
- }
127
+ // Configure OpenClaw
128
+ log("Configuring OpenClaw...");
220
129
 
221
- if (!config.plugins) config.plugins = {};
222
- if (!Array.isArray(config.plugins.allow)) config.plugins.allow = [];
223
- if (!config.plugins.allow.includes("friday-next")) {
224
- config.plugins.allow.push("friday-next");
225
- console.log(" + Added friday-next to plugins.allow");
226
- }
130
+ let config;
131
+ try {
132
+ config = JSON.parse(readFileSync(OPENCLAW_CONFIG, "utf8"));
133
+ } catch {
134
+ err(`Failed to read ${OPENCLAW_CONFIG}.`);
135
+ process.exit(1);
136
+ }
227
137
 
228
- if (!config.plugins.entries) config.plugins.entries = {};
229
- if (!config.plugins.entries["friday-next"]) {
230
- config.plugins.entries["friday-next"] = { enabled: true };
231
- console.log(" + Added friday-next to plugins.entries (enabled)");
232
- } else if (!config.plugins.entries["friday-next"].enabled) {
233
- config.plugins.entries["friday-next"].enabled = true;
234
- console.log(" + Enabled friday-next in plugins.entries");
235
- }
138
+ if (!config.plugins) config.plugins = {};
139
+ if (!Array.isArray(config.plugins.allow)) config.plugins.allow = [];
140
+ if (!config.plugins.allow.includes("friday-next")) {
141
+ config.plugins.allow.push("friday-next");
142
+ console.log(" + Added friday-next to plugins.allow");
143
+ }
236
144
 
237
- if (!config.plugins.allow.includes("canvas")) {
238
- config.plugins.allow.push("canvas");
239
- console.log(" + Added canvas to plugins.allow");
240
- }
241
- if (!config.plugins.entries["canvas"]) {
242
- config.plugins.entries["canvas"] = { enabled: true };
243
- console.log(" + Added canvas to plugins.entries (enabled)");
244
- } else if (!config.plugins.entries["canvas"].enabled) {
245
- config.plugins.entries["canvas"].enabled = true;
246
- console.log(" + Enabled canvas in plugins.entries");
247
- }
145
+ if (!config.plugins.entries) config.plugins.entries = {};
146
+ if (!config.plugins.entries["friday-next"]) {
147
+ config.plugins.entries["friday-next"] = { enabled: true };
148
+ console.log(" + Added friday-next to plugins.entries (enabled)");
149
+ } else if (!config.plugins.entries["friday-next"].enabled) {
150
+ config.plugins.entries["friday-next"].enabled = true;
151
+ console.log(" + Enabled friday-next in plugins.entries");
152
+ }
248
153
 
249
- if (!config.channels) config.channels = {};
250
- if (!config.channels["friday-next"]) {
251
- config.channels["friday-next"] = { enabled: true, transport: "http+sse" };
252
- console.log(" + Added friday-next channel config (auth defaults to gateway token)");
253
- } else {
254
- if (!config.channels["friday-next"].enabled) {
255
- config.channels["friday-next"].enabled = true;
256
- console.log(" + Enabled friday-next channel");
154
+ if (!config.plugins.allow.includes("canvas")) {
155
+ config.plugins.allow.push("canvas");
156
+ console.log(" + Added canvas to plugins.allow");
257
157
  }
258
- if (!config.channels["friday-next"].transport) {
259
- config.channels["friday-next"].transport = "http+sse";
260
- console.log(" + Set friday-next transport to http+sse");
158
+ if (!config.plugins.entries["canvas"]) {
159
+ config.plugins.entries["canvas"] = { enabled: true };
160
+ console.log(" + Added canvas to plugins.entries (enabled)");
161
+ } else if (!config.plugins.entries["canvas"].enabled) {
162
+ config.plugins.entries["canvas"].enabled = true;
163
+ console.log(" + Enabled canvas in plugins.entries");
261
164
  }
262
- }
263
165
 
264
- if (!config.gateway) config.gateway = {};
265
- if (config.gateway.bind !== "lan") {
266
- config.gateway.bind = "lan";
267
- console.log(" + Set gateway.bind to lan");
268
- }
269
- if (!config.gateway.nodes) config.gateway.nodes = {};
270
- if (!Array.isArray(config.gateway.nodes.allowCommands)) config.gateway.nodes.allowCommands = [];
271
- for (const cmd of [
272
- "canvas.navigate",
273
- "canvas.present",
274
- "canvas.hide",
275
- "canvas.eval",
276
- "canvas.snapshot",
277
- "canvas.a2ui.push",
278
- "canvas.a2ui.reset",
279
- "canvas.a2ui.pushJSONL",
280
- ]) {
281
- if (!config.gateway.nodes.allowCommands.includes(cmd)) {
282
- config.gateway.nodes.allowCommands.push(cmd);
283
- console.log(" + Added " + cmd + " to gateway.nodes.allowCommands");
166
+ if (!config.channels) config.channels = {};
167
+ if (!config.channels["friday-next"]) {
168
+ config.channels["friday-next"] = { enabled: true, transport: "http+sse" };
169
+ console.log(" + Added friday-next channel config");
170
+ } else {
171
+ if (!config.channels["friday-next"].enabled) {
172
+ config.channels["friday-next"].enabled = true;
173
+ console.log(" + Enabled friday-next channel");
174
+ }
175
+ if (!config.channels["friday-next"].transport) {
176
+ config.channels["friday-next"].transport = "http+sse";
177
+ console.log(" + Set friday-next transport to http+sse");
178
+ }
284
179
  }
285
- }
286
180
 
287
- // Ensure canvas and nodes are in the main agent's tools.alsoAllow (not deny)
288
- if (!config.agents) config.agents = {};
289
- if (!Array.isArray(config.agents.list)) config.agents.list = [];
290
- let mainAgent = config.agents.list.find((a) => a.id === "main");
291
- if (!mainAgent) {
292
- mainAgent = { id: "main" };
293
- config.agents.list.push(mainAgent);
294
- }
295
- if (!mainAgent.tools) mainAgent.tools = {};
296
- if (!Array.isArray(mainAgent.tools.alsoAllow)) mainAgent.tools.alsoAllow = [];
297
- for (const tool of ["canvas", "nodes"]) {
298
- if (!mainAgent.tools.alsoAllow.includes(tool)) {
299
- mainAgent.tools.alsoAllow.push(tool);
300
- console.log(" + Added " + tool + " to agent 'main' tools.alsoAllow");
181
+ if (!config.gateway) config.gateway = {};
182
+ if (config.gateway.bind !== "lan") {
183
+ config.gateway.bind = "lan";
184
+ console.log(" + Set gateway.bind to lan");
301
185
  }
302
- }
303
- if (Array.isArray(mainAgent.tools.deny)) {
304
- for (const tool of ["canvas", "nodes"]) {
305
- const idx = mainAgent.tools.deny.indexOf(tool);
306
- if (idx !== -1) {
307
- mainAgent.tools.deny.splice(idx, 1);
308
- console.log(" - Removed " + tool + " from agent 'main' tools.deny");
186
+ if (!config.gateway.nodes) config.gateway.nodes = {};
187
+ if (!Array.isArray(config.gateway.nodes.allowCommands)) config.gateway.nodes.allowCommands = [];
188
+ for (const cmd of [
189
+ "canvas.navigate", "canvas.present", "canvas.hide", "canvas.eval",
190
+ "canvas.snapshot", "canvas.a2ui.push", "canvas.a2ui.reset", "canvas.a2ui.pushJSONL",
191
+ ]) {
192
+ if (!config.gateway.nodes.allowCommands.includes(cmd)) {
193
+ config.gateway.nodes.allowCommands.push(cmd);
194
+ console.log(" + Added " + cmd + " to gateway.nodes.allowCommands");
309
195
  }
310
196
  }
311
- }
312
197
 
313
- try {
314
- writeFileSync(OPENCLAW_CONFIG, JSON.stringify(config, null, 2) + "\n", "utf8");
315
- } catch {
316
- err(`Failed to write ${OPENCLAW_CONFIG}.`);
317
- err("Check disk space and file permissions.");
318
- process.exit(1);
319
- }
320
- console.log(" Config updated.");
198
+ if (!config.agents) config.agents = {};
199
+ if (!Array.isArray(config.agents.list)) config.agents.list = [];
200
+ let mainAgent = config.agents.list.find((a) => a.id === "main");
201
+ if (!mainAgent) { mainAgent = { id: "main" }; config.agents.list.push(mainAgent); }
202
+ if (!mainAgent.tools) mainAgent.tools = {};
203
+ if (!Array.isArray(mainAgent.tools.alsoAllow)) mainAgent.tools.alsoAllow = [];
204
+ for (const tool of ["canvas", "nodes"]) {
205
+ if (!mainAgent.tools.alsoAllow.includes(tool)) {
206
+ mainAgent.tools.alsoAllow.push(tool);
207
+ console.log(" + Added " + tool + " to agent 'main' tools.alsoAllow");
208
+ }
209
+ }
210
+ if (Array.isArray(mainAgent.tools.deny)) {
211
+ for (const tool of ["canvas", "nodes"]) {
212
+ const idx = mainAgent.tools.deny.indexOf(tool);
213
+ if (idx !== -1) { mainAgent.tools.deny.splice(idx, 1); console.log(" - Removed " + tool + " from agent 'main' tools.deny"); }
214
+ }
215
+ }
321
216
 
322
- if (sudoUser) {
323
217
  try {
324
- execSync(`chown -R "${sudoUser}" "${PLUGIN_DIR}" "${OPENCLAW_CONFIG}"`, { stdio: "ignore" });
325
- log("Fixed file ownership back to " + sudoUser);
218
+ writeFileSync(OPENCLAW_CONFIG, JSON.stringify(config, null, 2) + "\n", "utf8");
219
+ console.log(" Config updated.");
326
220
  } catch {
327
- warn("Could not fix file ownership — files in " + PLUGIN_DIR + " may be owned by root.");
221
+ err(`Failed to write ${OPENCLAW_CONFIG}.`);
222
+ process.exit(1);
328
223
  }
224
+
225
+ warn("Manual install complete, but auto-upgrade is not available.");
226
+ warn("To enable auto-upgrade, run: openclaw plugins install --dangerously-force-unsafe-install @syengup/friday-channel-next");
329
227
  }
330
228
 
331
229
  // --------------- restart gateway ---------------
@@ -337,8 +235,7 @@ try {
337
235
  } catch (e) {
338
236
  if (e.stdout?.trim()) console.log(e.stdout.trim());
339
237
  if (e.stderr?.trim()) console.error(e.stderr.trim());
340
- warn("Gateway restart failed or timed out. The plugin files are installed but the gateway may not have restarted.");
341
- warn("Check 'openclaw gateway status' and restart manually: openclaw gateway restart");
238
+ warn("Gateway restart failed. Restart manually: openclaw gateway restart");
342
239
  }
343
240
 
344
241
  // --------------- verify ---------------
@@ -353,6 +250,9 @@ function getLanIp() {
353
250
  return "127.0.0.1";
354
251
  }
355
252
 
253
+ let config;
254
+ try { config = JSON.parse(readFileSync(OPENCLAW_CONFIG, "utf8")); } catch { config = {}; }
255
+
356
256
  const gatewayPort = config.gateway?.port || 18789;
357
257
  const gatewayToken = config.gateway?.auth?.token || "(not set)";
358
258
  const bindMode = config.gateway?.bind || "localhost";
@@ -387,32 +287,18 @@ async function verifyGateway(url, token, retries = 6) {
387
287
  warn("Plugin responded but ok=false — " + JSON.stringify(data));
388
288
  return false;
389
289
  } catch {
390
- // body is not JSON (e.g. HTML control panel) plugin route not registered yet
391
- if (i < 3) {
392
- warn(`Plugin routes not registered yet, retrying (${i}/${retries})...`);
393
- } else if (i < retries) {
394
- warn(`Gateway is up but plugin routes missing — may need config reload, retrying (${i}/${retries})...`);
395
- } else {
396
- warn("Gateway is running but plugin routes were not loaded. Check plugin config in openclaw.json.");
397
- }
290
+ if (i < retries) warn(`Plugin routes not registered yet, retrying (${i}/${retries})...`);
398
291
  continue;
399
292
  }
400
293
  }
401
- if (res.status === 401) {
402
- warn("Auth token mismatchcheck gateway.auth.token in openclaw.json.");
403
- return false;
404
- }
405
- if (res.status === 404) {
406
- warn("Route /friday-next/status not found — plugin may not be loaded.");
407
- return false;
408
- }
294
+ if (res.status === 401) { warn("Auth token mismatch — check gateway.auth.token."); return false; }
295
+ if (res.status === 404) { warn("Route not foundplugin may not be loaded."); return false; }
409
296
  if (i < retries) warn(`Gateway responded ${res.status}, retrying (${i}/${retries})...`);
410
297
  } catch {
411
- // Connection refused / timeout — gateway not running yet
412
298
  if (i < retries) warn(`Gateway not reachable, retrying (${i}/${retries})...`);
413
299
  }
414
300
  }
415
- warn("Gateway verification timed out — check 'openclaw gateway status' manually.");
301
+ warn("Gateway verification timed out.");
416
302
  return false;
417
303
  }
418
304
 
@@ -428,9 +314,7 @@ if (verified) {
428
314
  log("Installation complete! Friday Next channel is now active.");
429
315
  } else {
430
316
  warn("Installation complete, but gateway verification failed.");
431
- warn("Check 'openclaw gateway status' and restart the gateway if needed.");
432
- warn("Also ensure OpenClaw is updated to 2026.5.7 or above: openclaw update");
433
- warn("同时请确认 OpenClaw 已升级至 2026.5.7 或以上版本:openclaw update");
317
+ warn("Check 'openclaw gateway status' and restart if needed.");
434
318
  }
435
319
  log("");
436
320
 
@@ -450,11 +334,7 @@ try {
450
334
  log("If QR scan doesn't work, enter manually:");
451
335
  log("若二维码无法使用,请手动输入:");
452
336
  qrShown = true;
453
- } catch {
454
- // qrcode-terminal not available, fall through to manual-only
455
- }
456
-
457
- // --------------- manual input ---------------
337
+ } catch {}
458
338
 
459
339
  if (!qrShown) {
460
340
  log(BOLD_YELLOW("Input the URL and Token below into your FridayNext app to connect."));
@@ -478,13 +358,8 @@ const ip = new URL(gatewayUrl).hostname;
478
358
  const ipType = classifyIp(ip);
479
359
  if (ipType === "tailscale") {
480
360
  log("This is a Tailscale network URL (" + ip + ").");
481
- log("Accessible from your Tailnet devices.");
482
361
  } else if (ipType === "private") {
483
362
  log("This is a LOCAL network URL (" + ip + ", bind=" + bindMode + ").");
484
- log("If you need public access, configure HTTPS, Tailscale, or a reverse proxy.");
485
- } else if (ipType === "loopback") {
486
- log("This is a LOOPBACK URL (" + ip + ").");
487
- log("Only accessible from this machine.");
488
363
  } else {
489
364
  log("This URL appears to be publicly accessible (" + ip + ").");
490
365
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@syengup/friday-channel-next",
3
- "version": "0.0.38",
3
+ "version": "0.0.40",
4
4
  "description": "OpenClaw Friday Next Apple channel plugin",
5
5
  "type": "module",
6
6
  "files": [
@@ -0,0 +1,29 @@
1
+ import { createRequire } from "node:module";
2
+ import { readdirSync } from "node:fs";
3
+ import { join } from "node:path";
4
+
5
+ const OPENCLAW_DIST = "/opt/homebrew/lib/node_modules/openclaw/dist";
6
+
7
+ let cache: { listNodePairing: Function; approveNodePairing: Function } | null = null;
8
+
9
+ export function loadNodePairingModule(): {
10
+ listNodePairing: Function;
11
+ approveNodePairing: Function;
12
+ } {
13
+ if (cache) return cache;
14
+ const file = readdirSync(OPENCLAW_DIST).find(
15
+ (f) => f.startsWith("node-pairing-") && f.endsWith(".js") && !f.includes("authz"),
16
+ );
17
+ if (!file) throw new Error("node-pairing module not found in OpenClaw dist");
18
+ const gatewayRequire = createRequire(join(OPENCLAW_DIST, "_"));
19
+ cache = gatewayRequire(`./${file.replace(/\.js$/, "")}`);
20
+ return cache!;
21
+ }
22
+
23
+ /** Vitest-only: inject mock pairing functions. */
24
+ export function __setMockNodePairingForTests(mock: {
25
+ listNodePairing: Function;
26
+ approveNodePairing: Function;
27
+ }): void {
28
+ cache = mock;
29
+ }