@licity/openclaw-connector 1.0.2 → 1.0.4
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/README.md +4 -4
- package/index.js +66 -45
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,15 +5,15 @@
|
|
|
5
5
|
## 特点
|
|
6
6
|
|
|
7
7
|
- **零配置启动**:首次运行自动创建会话,扫码后自动保存凭证
|
|
8
|
-
-
|
|
9
|
-
- **纯 OpenClaw**:不依赖 QClaw
|
|
8
|
+
- **当前终端直接显示二维码**:扫码流程在同一个命令行窗口内完成
|
|
9
|
+
- **纯 OpenClaw**:不依赖 QClaw,自动启动 OpenClaw Gateway + CLI 执行 AI 任务
|
|
10
10
|
- **凭证持久化**:扫码成功后凭证保存至 `~/.licity-connector/`,下次无需重新扫码
|
|
11
11
|
|
|
12
12
|
## 前置要求
|
|
13
13
|
|
|
14
14
|
1. **Node.js v18+**(推荐 v22+)
|
|
15
15
|
2. **OpenClaw** 已安装([下载 OpenClaw](https://openclaw.ai))
|
|
16
|
-
3. **里世界 APP**
|
|
16
|
+
3. **里世界 APP** 已登录
|
|
17
17
|
|
|
18
18
|
## 快速开始
|
|
19
19
|
|
|
@@ -23,7 +23,7 @@ npx @licity/openclaw-connector
|
|
|
23
23
|
|
|
24
24
|
**首次运行流程:**
|
|
25
25
|
1. 运行命令后,自动创建连接会话
|
|
26
|
-
2.
|
|
26
|
+
2. 二维码直接在当前终端中显示
|
|
27
27
|
3. 打开里世界 APP → 我的龙虾 → 扫一扫,扫描二维码
|
|
28
28
|
4. APP 批准后自动连接,开始监听任务
|
|
29
29
|
|
package/index.js
CHANGED
|
@@ -138,53 +138,13 @@ async function apiRequest(endpoint, options = {}, token = null) {
|
|
|
138
138
|
|
|
139
139
|
// ─── 显示二维码(Windows 弹 CMD 新窗口) ──────────────────────────────────────
|
|
140
140
|
function showQrCode(qrText) {
|
|
141
|
-
|
|
141
|
+
console.log('\n请在里世界 APP → 我的龙虾 → 扫一扫,扫描以下二维码:\n');
|
|
142
142
|
try {
|
|
143
143
|
require('qrcode-terminal').generate(qrText, { small: true });
|
|
144
144
|
} catch {}
|
|
145
145
|
console.log('\n二维码文本:');
|
|
146
146
|
console.log(qrText);
|
|
147
147
|
console.log('');
|
|
148
|
-
|
|
149
|
-
if (process.platform !== 'win32') return;
|
|
150
|
-
|
|
151
|
-
// Windows:额外弹出一个 CMD 新窗口,字符渲染更准确,方便扫码
|
|
152
|
-
try {
|
|
153
|
-
const qrcodeModPath = require.resolve('qrcode-terminal');
|
|
154
|
-
const scriptLines = [
|
|
155
|
-
`const q = require(${JSON.stringify(qrcodeModPath)});`,
|
|
156
|
-
`const text = ${JSON.stringify(qrText)};`,
|
|
157
|
-
`try { process.title = '里世界龙虾连接 - 打开APP扫码'; } catch {}`,
|
|
158
|
-
`console.clear && console.clear();`,
|
|
159
|
-
`console.log('');`,
|
|
160
|
-
`console.log('╔══════════════════════════════════════╗');`,
|
|
161
|
-
`console.log('║ 里世界 龙虾连接二维码 ║');`,
|
|
162
|
-
`console.log('╚══════════════════════════════════════╝');`,
|
|
163
|
-
`console.log('');`,
|
|
164
|
-
`q.generate(text, { small: false });`,
|
|
165
|
-
`console.log('');`,
|
|
166
|
-
`console.log('请打开里世界APP → 我的龙虾 → 扫一扫');`,
|
|
167
|
-
`console.log('扫码完成后可关闭此窗口(连接器继续在后台运行)');`,
|
|
168
|
-
`console.log('');`,
|
|
169
|
-
`console.log('二维码文本:');`,
|
|
170
|
-
`console.log(text);`,
|
|
171
|
-
`process.stdin.resume();`,
|
|
172
|
-
];
|
|
173
|
-
const tempScript = path.join(os.tmpdir(), `licity_qr_${Date.now()}.js`);
|
|
174
|
-
fs.writeFileSync(tempScript, scriptLines.join('\n'), 'utf8');
|
|
175
|
-
|
|
176
|
-
// 使用 cmd /c start 打开一个新的可见 CMD 窗口
|
|
177
|
-
const child = spawn('cmd.exe', [
|
|
178
|
-
'/c', 'start', '"里世界龙虾连接"', 'cmd', '/k',
|
|
179
|
-
`"${process.execPath}" "${tempScript}"`,
|
|
180
|
-
], { detached: true, windowsHide: false, shell: false });
|
|
181
|
-
child.unref();
|
|
182
|
-
|
|
183
|
-
console.log('[扫码] 已弹出新的 CMD 窗口,请在新窗口中扫码。');
|
|
184
|
-
} catch (e) {
|
|
185
|
-
// 弹窗失败: 当前终端中已有文字版二维码,用户可在此扫码
|
|
186
|
-
console.log('[扫码] 注:无法弹出新窗口,请在当前终端扫码。');
|
|
187
|
-
}
|
|
188
148
|
}
|
|
189
149
|
|
|
190
150
|
// ─── OpenClaw Agent 执行 ─────────────────────────────────────────────────────
|
|
@@ -256,12 +216,11 @@ async function runAgent({ cliPath, configPath, agentId, lobsterName, message })
|
|
|
256
216
|
cliPath,
|
|
257
217
|
'agent',
|
|
258
218
|
'--agent', agentId || 'main',
|
|
259
|
-
'--config', configPath,
|
|
260
219
|
'--message', prompt,
|
|
261
220
|
'--json',
|
|
262
221
|
'--timeout', String(Math.max(15, Math.ceil(AGENT_TIMEOUT_MS / 1000))),
|
|
263
222
|
], {
|
|
264
|
-
env: { ...process.env, OPENCLAW_CONFIG_PATH: configPath, NODE_OPTIONS: '--no-warnings' },
|
|
223
|
+
env: { ...process.env, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_STATE_DIR: path.dirname(configPath), NODE_OPTIONS: '--no-warnings' },
|
|
265
224
|
windowsHide: true,
|
|
266
225
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
267
226
|
});
|
|
@@ -448,11 +407,68 @@ async function handleTask(token, task, cliPath, configPath, lobsterName) {
|
|
|
448
407
|
}
|
|
449
408
|
}
|
|
450
409
|
|
|
410
|
+
// ─── OpenClaw Gateway 管理 ─────────────────────────────────────────────────
|
|
411
|
+
let _gatewayProc = null;
|
|
412
|
+
|
|
413
|
+
async function checkPort18789() {
|
|
414
|
+
return new Promise(res => {
|
|
415
|
+
const sock = new net.Socket();
|
|
416
|
+
sock.setTimeout(800);
|
|
417
|
+
sock.on('connect', () => { sock.destroy(); res(true); });
|
|
418
|
+
sock.on('error', () => res(false));
|
|
419
|
+
sock.on('timeout', () => { sock.destroy(); res(false); });
|
|
420
|
+
sock.connect(18789, '127.0.0.1');
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async function ensureGateway(cliPath, configPath) {
|
|
425
|
+
if (!cliPath || !configPath) return;
|
|
426
|
+
const running = await checkPort18789();
|
|
427
|
+
if (running) {
|
|
428
|
+
console.log(' OpenClaw Gateway: ✓ 已在运行 (18789)');
|
|
429
|
+
return;
|
|
430
|
+
}
|
|
431
|
+
console.log(' OpenClaw Gateway: 正在启动...');
|
|
432
|
+
_gatewayProc = spawn(process.execPath, [
|
|
433
|
+
cliPath, 'gateway', 'run', '--allow-unconfigured',
|
|
434
|
+
], {
|
|
435
|
+
env: { ...process.env, OPENCLAW_CONFIG_PATH: configPath, OPENCLAW_STATE_DIR: path.dirname(configPath), NODE_OPTIONS: '--no-warnings' },
|
|
436
|
+
windowsHide: true,
|
|
437
|
+
stdio: 'ignore',
|
|
438
|
+
detached: false,
|
|
439
|
+
});
|
|
440
|
+
_gatewayProc.on('error', err => console.error(`\n[Gateway] 启动错误: ${err.message}`));
|
|
441
|
+
_gatewayProc.on('exit', code => {
|
|
442
|
+
_gatewayProc = null;
|
|
443
|
+
if (state.shouldRun) console.log(`\n[Gateway] 进程已退出 (code: ${code}),可能影响 AI 任务执行`);
|
|
444
|
+
});
|
|
445
|
+
for (let i = 0; i < 20; i++) {
|
|
446
|
+
await sleep(500);
|
|
447
|
+
if (await checkPort18789()) {
|
|
448
|
+
console.log(' OpenClaw Gateway: ✓ 启动成功');
|
|
449
|
+
return;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
console.log(' OpenClaw Gateway: ⚠ 启动超时,AI 任务执行可能失败');
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
function stopGateway() {
|
|
456
|
+
if (!_gatewayProc) return;
|
|
457
|
+
try {
|
|
458
|
+
if (process.platform === 'win32') {
|
|
459
|
+
execFile('taskkill', ['/PID', String(_gatewayProc.pid), '/T', '/F'], { windowsHide: true, timeout: 5000 }, () => {});
|
|
460
|
+
} else {
|
|
461
|
+
_gatewayProc.kill('SIGTERM');
|
|
462
|
+
}
|
|
463
|
+
} catch {}
|
|
464
|
+
_gatewayProc = null;
|
|
465
|
+
}
|
|
466
|
+
|
|
451
467
|
// ─── 主循环 ───────────────────────────────────────────────────────────────────
|
|
452
468
|
const state = { shouldRun: true, reconnect: false };
|
|
453
469
|
|
|
454
|
-
process.on('SIGINT', () => { state.shouldRun = false; state.reconnect = true; });
|
|
455
|
-
process.on('SIGTERM', () => { state.shouldRun = false; state.reconnect = true; });
|
|
470
|
+
process.on('SIGINT', () => { state.shouldRun = false; state.reconnect = true; stopGateway(); });
|
|
471
|
+
process.on('SIGTERM', () => { state.shouldRun = false; state.reconnect = true; stopGateway(); });
|
|
456
472
|
|
|
457
473
|
async function sleep(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
458
474
|
|
|
@@ -473,6 +489,11 @@ async function connectAndRun(cfg) {
|
|
|
473
489
|
if (configPath && !cfg.openclawConfigPath) {
|
|
474
490
|
saveConfig({ openclawConfigPath: configPath });
|
|
475
491
|
}
|
|
492
|
+
|
|
493
|
+
// 启动并确保 OpenClaw Gateway 在运行(agent 执行任务时需要连接到它)
|
|
494
|
+
if (cliPath && configPath) {
|
|
495
|
+
await ensureGateway(cliPath, configPath);
|
|
496
|
+
}
|
|
476
497
|
console.log('');
|
|
477
498
|
|
|
478
499
|
// ── 若有已保存的 token,直接跳到心跳循环 ──────────────────────────────────
|