openclawsetup 1.0.6 → 1.0.7
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/bin/cli.mjs +213 -56
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -18,10 +18,11 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { execSync, spawn } from 'child_process';
|
|
21
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync } from 'fs';
|
|
21
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'fs';
|
|
22
22
|
import { homedir, platform } from 'os';
|
|
23
23
|
import { join } from 'path';
|
|
24
24
|
import { randomBytes } from 'crypto';
|
|
25
|
+
import { createInterface } from 'readline';
|
|
25
26
|
|
|
26
27
|
// 解析命令行参数
|
|
27
28
|
function parseArgs() {
|
|
@@ -167,6 +168,145 @@ function checkNodeVersion() {
|
|
|
167
168
|
return true;
|
|
168
169
|
}
|
|
169
170
|
|
|
171
|
+
// 交互式提示用户选择
|
|
172
|
+
function promptChoice(question, choices) {
|
|
173
|
+
return new Promise((resolve) => {
|
|
174
|
+
const rl = createInterface({
|
|
175
|
+
input: process.stdin,
|
|
176
|
+
output: process.stdout,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
console.log(colors.cyan(`\n${question}`));
|
|
180
|
+
choices.forEach((choice, i) => {
|
|
181
|
+
console.log(` ${colors.yellow(i + 1)}. ${choice.label}`);
|
|
182
|
+
});
|
|
183
|
+
console.log(` ${colors.yellow(0)}. 退出`);
|
|
184
|
+
|
|
185
|
+
rl.question(colors.cyan('\n请输入选项编号: '), (answer) => {
|
|
186
|
+
rl.close();
|
|
187
|
+
const num = parseInt(answer, 10);
|
|
188
|
+
if (num === 0 || isNaN(num) || num > choices.length) {
|
|
189
|
+
resolve(null);
|
|
190
|
+
} else {
|
|
191
|
+
resolve(choices[num - 1].value);
|
|
192
|
+
}
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// 检查并更新 OpenClaw
|
|
198
|
+
async function checkAndUpdate(cliName) {
|
|
199
|
+
console.log(colors.cyan('\n检查更新中...'));
|
|
200
|
+
|
|
201
|
+
// 获取当前版本
|
|
202
|
+
const currentResult = safeExec(`${cliName} --version`);
|
|
203
|
+
const currentVersion = currentResult.ok ? currentResult.output : '未知';
|
|
204
|
+
console.log(` 当前版本: ${colors.yellow(currentVersion)}`);
|
|
205
|
+
|
|
206
|
+
// 更新到最新版本
|
|
207
|
+
log.info('正在更新到最新版本...');
|
|
208
|
+
const updateResult = await execWithProgress(
|
|
209
|
+
`npm install -g ${cliName}@latest`,
|
|
210
|
+
`正在更新 ${cliName}...`,
|
|
211
|
+
{ timeout: 300000 }
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
if (updateResult.ok) {
|
|
215
|
+
const newResult = safeExec(`${cliName} --version`);
|
|
216
|
+
const newVersion = newResult.ok ? newResult.output : '未知';
|
|
217
|
+
if (newVersion !== currentVersion) {
|
|
218
|
+
log.success(`更新完成: ${currentVersion} → ${newVersion}`);
|
|
219
|
+
} else {
|
|
220
|
+
log.success(`已是最新版本: ${newVersion}`);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 重启 Gateway
|
|
224
|
+
console.log(colors.cyan('\n重启 Gateway 服务...'));
|
|
225
|
+
const restartResult = safeExec(`${cliName} gateway restart`);
|
|
226
|
+
if (restartResult.ok) {
|
|
227
|
+
log.success('Gateway 已重启');
|
|
228
|
+
} else {
|
|
229
|
+
log.warn('Gateway 重启失败,请手动重启: ' + cliName + ' gateway restart');
|
|
230
|
+
}
|
|
231
|
+
} else {
|
|
232
|
+
log.error('更新失败: ' + (updateResult.error || '未知错误'));
|
|
233
|
+
log.hint('可以手动更新: npm install -g ' + cliName + '@latest');
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// 卸载并重新安装
|
|
238
|
+
async function uninstallAndReinstall(existing, options) {
|
|
239
|
+
console.log(colors.cyan('\n开始卸载...'));
|
|
240
|
+
|
|
241
|
+
// 停止服务
|
|
242
|
+
log.info('停止 Gateway 服务...');
|
|
243
|
+
safeExec(`${existing.name} gateway stop`);
|
|
244
|
+
|
|
245
|
+
// 卸载 npm 包
|
|
246
|
+
log.info('卸载 npm 包...');
|
|
247
|
+
const uninstallResult = await execWithProgress(
|
|
248
|
+
`npm uninstall -g ${existing.name}`,
|
|
249
|
+
`正在卸载 ${existing.name}...`,
|
|
250
|
+
{ timeout: 60000 }
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
if (uninstallResult.ok) {
|
|
254
|
+
log.success('npm 包已卸载');
|
|
255
|
+
} else {
|
|
256
|
+
log.warn('npm 卸载可能未完成: ' + (uninstallResult.error || ''));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// 删除配置目录
|
|
260
|
+
if (existing.configDir && existsSync(existing.configDir)) {
|
|
261
|
+
log.info('删除配置目录...');
|
|
262
|
+
try {
|
|
263
|
+
rmSync(existing.configDir, { recursive: true, force: true });
|
|
264
|
+
log.success('配置目录已删除');
|
|
265
|
+
} catch (e) {
|
|
266
|
+
log.warn('配置目录删除失败: ' + e.message);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// 删除服务持久化配置
|
|
271
|
+
const os = platform();
|
|
272
|
+
if (os === 'darwin') {
|
|
273
|
+
const plistPath = join(homedir(), 'Library', 'LaunchAgents', 'com.openclaw.gateway.plist');
|
|
274
|
+
if (existsSync(plistPath)) {
|
|
275
|
+
safeExec('launchctl unload ' + plistPath);
|
|
276
|
+
try {
|
|
277
|
+
rmSync(plistPath);
|
|
278
|
+
log.success('macOS 服务配置已删除');
|
|
279
|
+
} catch (e) {}
|
|
280
|
+
}
|
|
281
|
+
} else if (os === 'linux') {
|
|
282
|
+
safeExec('systemctl --user stop openclaw');
|
|
283
|
+
safeExec('systemctl --user disable openclaw');
|
|
284
|
+
const servicePath = join(homedir(), '.config', 'systemd', 'user', 'openclaw.service');
|
|
285
|
+
if (existsSync(servicePath)) {
|
|
286
|
+
try {
|
|
287
|
+
rmSync(servicePath);
|
|
288
|
+
log.success('Linux 服务配置已删除');
|
|
289
|
+
} catch (e) {}
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
console.log(colors.green('\n✓ 卸载完成,开始重新安装...\n'));
|
|
294
|
+
|
|
295
|
+
// 重新安装
|
|
296
|
+
const cliName = await installOpenClaw();
|
|
297
|
+
const config = createConfig(options);
|
|
298
|
+
|
|
299
|
+
if (!options.skipDaemon) {
|
|
300
|
+
setupDaemon(cliName);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (!options.skipStart) {
|
|
304
|
+
startService(cliName);
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
return { cliName, config };
|
|
308
|
+
}
|
|
309
|
+
|
|
170
310
|
// 检测现有安装
|
|
171
311
|
function detectExistingInstall() {
|
|
172
312
|
const home = homedir();
|
|
@@ -535,6 +675,57 @@ function startService(cliName) {
|
|
|
535
675
|
return false;
|
|
536
676
|
}
|
|
537
677
|
|
|
678
|
+
// 显示安装完成信息
|
|
679
|
+
function showCompletionInfo(cliName, config, isVPS) {
|
|
680
|
+
console.log(colors.bold(colors.green('\n========================================')));
|
|
681
|
+
console.log(colors.bold(colors.green('✅ OpenClaw 安装完成!')));
|
|
682
|
+
console.log(colors.bold(colors.green('========================================')));
|
|
683
|
+
|
|
684
|
+
console.log(colors.cyan('\n配置信息:'));
|
|
685
|
+
console.log(` 配置目录: ${colors.yellow(config.configDir)}`);
|
|
686
|
+
console.log(` Gateway 端口: ${colors.yellow(config.port)}`);
|
|
687
|
+
console.log(` Gateway Token: ${colors.yellow(config.token)}`);
|
|
688
|
+
|
|
689
|
+
// 根据环境显示不同的访问说明
|
|
690
|
+
if (isVPS) {
|
|
691
|
+
console.log(colors.cyan('\n📡 Dashboard 访问 (云服务器):'));
|
|
692
|
+
console.log(colors.gray(' Gateway 默认绑定 127.0.0.1,外部无法直接访问(安全设计)'));
|
|
693
|
+
console.log('');
|
|
694
|
+
console.log(colors.yellow(' 方式一:SSH 隧道(推荐,安全)'));
|
|
695
|
+
console.log(colors.gray(' 在本地电脑执行以下命令,保持终端窗口打开:'));
|
|
696
|
+
console.log(` ssh -N -L ${config.port}:127.0.0.1:${config.port} root@<服务器IP>`);
|
|
697
|
+
console.log(colors.gray(' 然后在本地浏览器访问:'));
|
|
698
|
+
console.log(` ${colors.green(`http://127.0.0.1:${config.port}/?token=${config.token}`)}`);
|
|
699
|
+
console.log('');
|
|
700
|
+
console.log(colors.yellow(' 方式二:直接暴露端口(不推荐,有安全风险)'));
|
|
701
|
+
console.log(colors.gray(' 1. 修改配置文件 ~/.openclaw/openclaw.json'));
|
|
702
|
+
console.log(colors.gray(' 将 "bind": "loopback" 改为 "bind": "all"'));
|
|
703
|
+
console.log(colors.gray(' 2. 在云服务器控制台开放端口 ' + config.port));
|
|
704
|
+
console.log(colors.gray(' - 腾讯云:安全组 → 入站规则 → 添加 TCP ' + config.port));
|
|
705
|
+
console.log(colors.gray(' - 阿里云:安全组 → 配置规则 → 添加 TCP ' + config.port));
|
|
706
|
+
console.log(colors.gray(' 3. 重启 Gateway:' + cliName + ' gateway restart'));
|
|
707
|
+
console.log(colors.gray(' 4. 访问:http://<服务器IP>:' + config.port + '/?token=...'));
|
|
708
|
+
} else {
|
|
709
|
+
console.log(colors.cyan('\nDashboard 访问:'));
|
|
710
|
+
console.log(` ${colors.yellow(`http://127.0.0.1:${config.port}/?token=${config.token}`)}`);
|
|
711
|
+
}
|
|
712
|
+
|
|
713
|
+
console.log(colors.cyan('\n下一步 - 配置 AI 模型:'));
|
|
714
|
+
console.log(` ${colors.yellow('npx openclawapi@latest preset-claude')}`);
|
|
715
|
+
|
|
716
|
+
console.log(colors.cyan('\n常用命令:'));
|
|
717
|
+
console.log(` 查看状态: ${colors.yellow(`${cliName} status`)}`);
|
|
718
|
+
console.log(` 查看日志: ${colors.yellow(`${cliName} gateway logs`)}`);
|
|
719
|
+
console.log(` 重启服务: ${colors.yellow(`${cliName} gateway restart`)}`);
|
|
720
|
+
console.log(` 诊断问题: ${colors.yellow(`${cliName} doctor`)}`);
|
|
721
|
+
|
|
722
|
+
console.log(colors.cyan('\n配置聊天渠道:'));
|
|
723
|
+
console.log(` Discord: ${colors.yellow('npx openclawdc')}`);
|
|
724
|
+
console.log(` 飞书: ${colors.yellow('npx openclawfs')}`);
|
|
725
|
+
|
|
726
|
+
console.log('');
|
|
727
|
+
}
|
|
728
|
+
|
|
538
729
|
// 检测是否在云服务器/VPS 上运行
|
|
539
730
|
function detectVPS() {
|
|
540
731
|
// 检查常见的云服务器特征
|
|
@@ -626,11 +817,25 @@ async function main() {
|
|
|
626
817
|
console.log(` Discord: ${colors.yellow('npx openclawdc')}`);
|
|
627
818
|
console.log(` 飞书: ${colors.yellow('npx openclawfs')}`);
|
|
628
819
|
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
820
|
+
// 显示选项
|
|
821
|
+
const choice = await promptChoice('请选择操作:', [
|
|
822
|
+
{ label: '检查并更新 OpenClaw', value: 'update' },
|
|
823
|
+
{ label: '卸载后重新安装(会清除配置)', value: 'reinstall' },
|
|
824
|
+
]);
|
|
825
|
+
|
|
826
|
+
if (choice === 'update') {
|
|
827
|
+
await checkAndUpdate(existing.name);
|
|
828
|
+
console.log('');
|
|
829
|
+
process.exit(0);
|
|
830
|
+
} else if (choice === 'reinstall') {
|
|
831
|
+
const result = await uninstallAndReinstall(existing, options);
|
|
832
|
+
// 显示安装完成信息
|
|
833
|
+
showCompletionInfo(result.cliName, result.config, isVPS);
|
|
834
|
+
process.exit(0);
|
|
835
|
+
} else {
|
|
836
|
+
console.log(colors.gray('\n已退出'));
|
|
837
|
+
process.exit(0);
|
|
838
|
+
}
|
|
634
839
|
}
|
|
635
840
|
|
|
636
841
|
// 安装 CLI
|
|
@@ -653,57 +858,9 @@ async function main() {
|
|
|
653
858
|
log.info('跳过服务启动 (--skip-start)');
|
|
654
859
|
}
|
|
655
860
|
|
|
656
|
-
// 检测是否在 VPS
|
|
861
|
+
// 检测是否在 VPS 上并显示完成信息
|
|
657
862
|
const isVPS = detectVPS();
|
|
658
|
-
|
|
659
|
-
// 完成
|
|
660
|
-
console.log(colors.bold(colors.green('\n========================================')));
|
|
661
|
-
console.log(colors.bold(colors.green('✅ OpenClaw 安装完成!')));
|
|
662
|
-
console.log(colors.bold(colors.green('========================================')));
|
|
663
|
-
|
|
664
|
-
console.log(colors.cyan('\n配置信息:'));
|
|
665
|
-
console.log(` 配置目录: ${colors.yellow(config.configDir)}`);
|
|
666
|
-
console.log(` Gateway 端口: ${colors.yellow(config.port)}`);
|
|
667
|
-
console.log(` Gateway Token: ${colors.yellow(config.token)}`);
|
|
668
|
-
|
|
669
|
-
// 根据环境显示不同的访问说明
|
|
670
|
-
if (isVPS) {
|
|
671
|
-
console.log(colors.cyan('\n📡 Dashboard 访问 (云服务器):'));
|
|
672
|
-
console.log(colors.gray(' Gateway 默认绑定 127.0.0.1,外部无法直接访问(安全设计)'));
|
|
673
|
-
console.log('');
|
|
674
|
-
console.log(colors.yellow(' 方式一:SSH 隧道(推荐,安全)'));
|
|
675
|
-
console.log(colors.gray(' 在本地电脑执行以下命令,保持终端窗口打开:'));
|
|
676
|
-
console.log(` ssh -N -L ${config.port}:127.0.0.1:${config.port} root@<服务器IP>`);
|
|
677
|
-
console.log(colors.gray(' 然后在本地浏览器访问:'));
|
|
678
|
-
console.log(` ${colors.green(`http://127.0.0.1:${config.port}/?token=${config.token}`)}`);
|
|
679
|
-
console.log('');
|
|
680
|
-
console.log(colors.yellow(' 方式二:直接暴露端口(不推荐,有安全风险)'));
|
|
681
|
-
console.log(colors.gray(' 1. 修改配置文件 ~/.openclaw/openclaw.json'));
|
|
682
|
-
console.log(colors.gray(' 将 "bind": "loopback" 改为 "bind": "all"'));
|
|
683
|
-
console.log(colors.gray(' 2. 在云服务器控制台开放端口 ' + config.port));
|
|
684
|
-
console.log(colors.gray(' - 腾讯云:安全组 → 入站规则 → 添加 TCP ' + config.port));
|
|
685
|
-
console.log(colors.gray(' - 阿里云:安全组 → 配置规则 → 添加 TCP ' + config.port));
|
|
686
|
-
console.log(colors.gray(' 3. 重启 Gateway:' + cliName + ' gateway restart'));
|
|
687
|
-
console.log(colors.gray(' 4. 访问:http://<服务器IP>:' + config.port + '/?token=...'));
|
|
688
|
-
} else {
|
|
689
|
-
console.log(colors.cyan('\nDashboard 访问:'));
|
|
690
|
-
console.log(` ${colors.yellow(`http://127.0.0.1:${config.port}/?token=${config.token}`)}`);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
console.log(colors.cyan('\n下一步 - 配置 AI 模型:'));
|
|
694
|
-
console.log(` ${colors.yellow('npx openclawapi@latest preset-claude')}`);
|
|
695
|
-
|
|
696
|
-
console.log(colors.cyan('\n常用命令:'));
|
|
697
|
-
console.log(` 查看状态: ${colors.yellow(`${cliName} status`)}`);
|
|
698
|
-
console.log(` 查看日志: ${colors.yellow(`${cliName} gateway logs`)}`);
|
|
699
|
-
console.log(` 重启服务: ${colors.yellow(`${cliName} gateway restart`)}`);
|
|
700
|
-
console.log(` 诊断问题: ${colors.yellow(`${cliName} doctor`)}`);
|
|
701
|
-
|
|
702
|
-
console.log(colors.cyan('\n配置聊天渠道:'));
|
|
703
|
-
console.log(` Discord: ${colors.yellow('npx openclawdc')}`);
|
|
704
|
-
console.log(` 飞书: ${colors.yellow('npx openclawfs')}`);
|
|
705
|
-
|
|
706
|
-
console.log('');
|
|
863
|
+
showCompletionInfo(cliName, config, isVPS);
|
|
707
864
|
}
|
|
708
865
|
|
|
709
866
|
main().catch((e) => {
|