openclaw-vchat-plugin 0.0.1 → 0.0.3

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.
Files changed (2) hide show
  1. package/bin/openclaw-vchat.js +168 -21
  2. package/package.json +1 -1
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
- const { spawnSync } = require('child_process');
4
+ const { spawnSync, spawn } = require('child_process');
5
5
 
6
6
  const packageRoot = path.resolve(__dirname, '..');
7
7
  const distEntry = path.join(packageRoot, 'dist', 'index.js');
@@ -12,7 +12,8 @@ function printUsage() {
12
12
 
13
13
  Usage:
14
14
  openclaw-vchat start
15
- openclaw-vchat install [--pm2]
15
+ openclaw-vchat install [--pm2] [--startup]
16
+ openclaw-vchat pair
16
17
  openclaw-vchat doctor
17
18
  openclaw-vchat version
18
19
  `);
@@ -23,6 +24,85 @@ function hasCommand(cmd) {
23
24
  return res.status === 0;
24
25
  }
25
26
 
27
+ function pm2ProcessExists(name) {
28
+ const res = spawnSync('pm2', ['jlist'], {
29
+ encoding: 'utf8',
30
+ env: process.env,
31
+ cwd: packageRoot,
32
+ });
33
+ if (res.status !== 0) {
34
+ return false;
35
+ }
36
+ try {
37
+ const list = JSON.parse(res.stdout || '[]');
38
+ return Array.isArray(list) && list.some((item) => item && item.name === name);
39
+ } catch (_) {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ function getOpenClawHome() {
45
+ const home = process.env.HOME || process.env.USERPROFILE || '';
46
+ return process.env.OPENCLAW_HOME || path.join(home, '.openclaw');
47
+ }
48
+
49
+ function getDeviceConfigPath() {
50
+ return path.join(getOpenClawHome(), 'wechat-device.json');
51
+ }
52
+
53
+ function hasDeviceConfig() {
54
+ return fs.existsSync(getDeviceConfigPath());
55
+ }
56
+
57
+ function sleep(ms) {
58
+ Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
59
+ }
60
+
61
+ function waitForPairingViaPm2Logs(name, timeoutMs) {
62
+ return new Promise((resolve, reject) => {
63
+ const child = spawn('pm2', ['logs', name, '--lines', '80'], {
64
+ stdio: 'inherit',
65
+ env: process.env,
66
+ cwd: packageRoot,
67
+ });
68
+
69
+ let settled = false;
70
+ const finish = (err) => {
71
+ if (settled) return;
72
+ settled = true;
73
+ clearInterval(checkTimer);
74
+ clearTimeout(timeoutTimer);
75
+ if (!child.killed) {
76
+ try {
77
+ child.kill('SIGINT');
78
+ } catch (_) {}
79
+ }
80
+ if (err) reject(err);
81
+ else resolve();
82
+ };
83
+
84
+ const checkTimer = setInterval(() => {
85
+ if (hasDeviceConfig()) {
86
+ finish();
87
+ }
88
+ }, 1000);
89
+
90
+ const timeoutTimer = setTimeout(() => {
91
+ finish(new Error('等待二维码配对超时'));
92
+ }, timeoutMs);
93
+
94
+ child.on('error', (err) => finish(err));
95
+ child.on('exit', (code) => {
96
+ if (settled) return;
97
+ if (hasDeviceConfig()) {
98
+ finish();
99
+ return;
100
+ }
101
+ finish(new Error(`pm2 logs 退出,exit=${code || 0}`));
102
+ });
103
+ });
104
+ }
105
+
26
106
  function runNodeEntry() {
27
107
  if (!fs.existsSync(distEntry)) {
28
108
  console.error('dist/index.js 不存在,请先执行 npm run build');
@@ -33,12 +113,17 @@ function runNodeEntry() {
33
113
  }
34
114
 
35
115
  function doctor() {
36
- const home = process.env.HOME || process.env.USERPROFILE || '';
37
- const openclawHome = process.env.OPENCLAW_HOME || path.join(home, '.openclaw');
38
- const deviceConfig = path.join(openclawHome, 'wechat-device.json');
116
+ const openclawHome = getOpenClawHome();
117
+ const deviceConfig = getDeviceConfigPath();
39
118
  const issues = [];
40
119
 
41
120
  if (!hasCommand('openclaw')) issues.push('未找到 openclaw CLI');
121
+ else {
122
+ const versionRes = spawnSync('openclaw', ['--version'], { encoding: 'utf8', env: process.env });
123
+ if (versionRes.status === 0) {
124
+ console.log(`openclaw version: ${versionRes.stdout.trim()}`);
125
+ }
126
+ }
42
127
  if (!fs.existsSync(deviceConfig)) issues.push('未找到 ~/.openclaw/wechat-device.json,设备尚未配对');
43
128
  if (!process.env.WECHAT_BACKEND_URL) issues.push('未设置 WECHAT_BACKEND_URL,将使用内置默认值');
44
129
  if (!process.env.PLUGIN_SECRET) issues.push('未设置 PLUGIN_SECRET,内部通信将依赖空密钥');
@@ -59,9 +144,17 @@ function doctor() {
59
144
  console.log('\nDoctor OK');
60
145
  }
61
146
 
62
- function install() {
147
+ function pair() {
148
+ console.log('启动前台配对模式...');
149
+ console.log('配对成功后会生成 ~/.openclaw/wechat-device.json');
150
+ runNodeEntry();
151
+ }
152
+
153
+ async function install() {
63
154
  const args = new Set(process.argv.slice(3));
64
155
  const usePm2 = args.has('--pm2');
156
+ const enableStartup = args.has('--startup');
157
+ const waitPair = args.has('--wait-pair') || !hasDeviceConfig();
65
158
 
66
159
  if (!fs.existsSync(distEntry)) {
67
160
  console.error('dist/index.js 不存在,请先执行 npm run build,或使用发布后的 npm 包。');
@@ -77,17 +170,59 @@ function install() {
77
170
  process.exit(1);
78
171
  }
79
172
  const name = 'openclaw-vchat';
80
- const start = spawnSync('pm2', ['start', process.execPath, '--name', name, '--', distEntry], {
81
- stdio: 'inherit',
82
- env: process.env,
83
- cwd: packageRoot,
84
- });
173
+ let start;
174
+ if (pm2ProcessExists(name)) {
175
+ console.log(`检测到现有 pm2 进程 ${name},改为重启并刷新配置`);
176
+ start = spawnSync('pm2', ['restart', name, '--update-env'], {
177
+ stdio: 'inherit',
178
+ env: process.env,
179
+ cwd: packageRoot,
180
+ });
181
+ } else {
182
+ start = spawnSync('pm2', ['start', process.execPath, '--name', name, '--', distEntry], {
183
+ stdio: 'inherit',
184
+ env: process.env,
185
+ cwd: packageRoot,
186
+ });
187
+ }
85
188
  if (start.status !== 0) {
86
189
  console.error('pm2 启动失败');
87
190
  process.exit(start.status || 1);
88
191
  }
89
192
  spawnSync('pm2', ['save'], { stdio: 'inherit', env: process.env, cwd: packageRoot });
193
+ if (enableStartup) {
194
+ const startup = spawnSync('pm2', ['startup'], {
195
+ stdio: 'inherit',
196
+ env: process.env,
197
+ cwd: packageRoot,
198
+ });
199
+ if (startup.status !== 0) {
200
+ console.error('\npm2 startup 执行失败,通常是需要按上面提示再手动执行一次 sudo 的 launchd 命令。');
201
+ console.error('安装不会中断,先继续二维码配对;开机自启可稍后补做。');
202
+ } else {
203
+ spawnSync('pm2', ['save'], { stdio: 'inherit', env: process.env, cwd: packageRoot });
204
+ console.log('\nStartup OK: 已尝试启用开机自启');
205
+ }
206
+ }
90
207
  console.log('\nInstall OK: pm2 进程已创建');
208
+ if (!enableStartup) {
209
+ console.log('如需开机自启: openclaw-vchat install --pm2 --startup');
210
+ }
211
+ if (waitPair) {
212
+ console.log('\n检测到当前设备尚未配对,自动进入二维码配对环节...');
213
+ console.log(`配对文件: ${getDeviceConfigPath()}`);
214
+ console.log(`将实时输出二维码日志,配对成功后自动结束。`);
215
+ sleep(1500);
216
+ try {
217
+ await waitForPairingViaPm2Logs(name, 15 * 60 * 1000);
218
+ } catch (error) {
219
+ console.error(`\n${error.message || '等待配对失败'}`);
220
+ console.error(`可手动查看日志: pm2 logs ${name}`);
221
+ process.exit(1);
222
+ }
223
+ console.log('\n配对完成:已检测到 wechat-device.json');
224
+ return;
225
+ }
91
226
  console.log(`查看日志: pm2 logs ${name}`);
92
227
  return;
93
228
  }
@@ -95,16 +230,28 @@ function install() {
95
230
  console.log('\nInstall OK');
96
231
  console.log('直接启动: openclaw-vchat start');
97
232
  console.log('若需守护进程: openclaw-vchat install --pm2');
233
+ console.log('若需守护进程 + 开机自启: openclaw-vchat install --pm2 --startup');
234
+ if (waitPair) {
235
+ console.log('当前设备尚未配对,建议直接执行: openclaw-vchat pair');
236
+ }
98
237
  }
99
238
 
100
- const cmd = process.argv[2] || 'start';
101
- if (cmd === 'start') runNodeEntry();
102
- else if (cmd === 'install') install();
103
- else if (cmd === 'doctor') doctor();
104
- else if (cmd === 'version' || cmd === '--version' || cmd === '-v') console.log(pkg.version);
105
- else if (cmd === 'help' || cmd === '--help' || cmd === '-h') printUsage();
106
- else {
107
- console.error(`未知命令: ${cmd}`);
108
- printUsage();
109
- process.exit(1);
239
+ async function main() {
240
+ const cmd = process.argv[2] || 'start';
241
+ if (cmd === 'start') runNodeEntry();
242
+ else if (cmd === 'install') await install();
243
+ else if (cmd === 'pair') pair();
244
+ else if (cmd === 'doctor') doctor();
245
+ else if (cmd === 'version' || cmd === '--version' || cmd === '-v') console.log(pkg.version);
246
+ else if (cmd === 'help' || cmd === '--help' || cmd === '-h') printUsage();
247
+ else {
248
+ console.error(`未知命令: ${cmd}`);
249
+ printUsage();
250
+ process.exit(1);
251
+ }
110
252
  }
253
+
254
+ main().catch((error) => {
255
+ console.error(error.message || String(error));
256
+ process.exit(1);
257
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "openclaw-vchat-plugin",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "OpenClaw plugin for VChat mini program - relay server and message channel",
5
5
  "main": "dist/index.js",
6
6
  "scripts": {