openclawsetup 2.4.2 → 2.4.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/bin/cli.mjs +145 -21
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -895,13 +895,20 @@ async function runHealthCheck(cliName, autoFix = false) {
|
|
|
895
895
|
// 1. 检查配置文件
|
|
896
896
|
console.log(colors.cyan('检查配置文件...'));
|
|
897
897
|
if (!config.configPath || !existsSync(config.configPath)) {
|
|
898
|
-
|
|
898
|
+
const issue = {
|
|
899
899
|
level: 'error',
|
|
900
900
|
title: '配置文件不存在',
|
|
901
901
|
detail: '未找到 openclaw.json 配置文件',
|
|
902
902
|
solution: '运行 openclaw onboard 重新配置',
|
|
903
903
|
fixCmd: `${cliName} onboard`,
|
|
904
|
-
}
|
|
904
|
+
};
|
|
905
|
+
if (autoFix) {
|
|
906
|
+
console.log(colors.yellow(' 尝试运行 onboard 生成配置...'));
|
|
907
|
+
spawnSync(cliName, ['onboard'], { stdio: 'inherit', shell: true });
|
|
908
|
+
fixed.push('已运行 onboard 生成配置');
|
|
909
|
+
} else {
|
|
910
|
+
issues.push(issue);
|
|
911
|
+
}
|
|
905
912
|
} else {
|
|
906
913
|
try {
|
|
907
914
|
const raw = readFileSync(config.configPath, 'utf8');
|
|
@@ -983,13 +990,26 @@ async function runHealthCheck(cliName, autoFix = false) {
|
|
|
983
990
|
// 检查是否有其他进程占用端口
|
|
984
991
|
const conflictCheck = safeExec(`lsof -i :${port} 2>/dev/null | head -5`);
|
|
985
992
|
if (conflictCheck.ok && conflictCheck.output && !conflictCheck.output.includes('openclaw') && !conflictCheck.output.includes('node')) {
|
|
986
|
-
|
|
993
|
+
const issue = {
|
|
987
994
|
level: 'error',
|
|
988
995
|
title: `端口 ${port} 被其他程序占用`,
|
|
989
996
|
detail: conflictCheck.output.slice(0, 100),
|
|
990
997
|
solution: `更换端口: ${cliName} config set gateway.port 18790`,
|
|
991
998
|
fixCmd: `${cliName} config set gateway.port 18790 && ${cliName} gateway restart`,
|
|
992
|
-
}
|
|
999
|
+
};
|
|
1000
|
+
if (autoFix) {
|
|
1001
|
+
console.log(colors.yellow(' 尝试更换端口到 18790...'));
|
|
1002
|
+
const portResult = safeExec(`${cliName} config set gateway.port 18790`);
|
|
1003
|
+
const restartResult = safeExec(`${cliName} gateway restart`);
|
|
1004
|
+
if (portResult.ok && restartResult.ok) {
|
|
1005
|
+
log.success('已更换端口到 18790 并重启 Gateway');
|
|
1006
|
+
fixed.push('端口冲突已自动解决(更换到 18790)');
|
|
1007
|
+
} else {
|
|
1008
|
+
issues.push(issue);
|
|
1009
|
+
}
|
|
1010
|
+
} else {
|
|
1011
|
+
issues.push(issue);
|
|
1012
|
+
}
|
|
993
1013
|
} else {
|
|
994
1014
|
const issue = {
|
|
995
1015
|
level: 'error',
|
|
@@ -1064,13 +1084,20 @@ async function runHealthCheck(cliName, autoFix = false) {
|
|
|
1064
1084
|
const hasModels = config.raw.includes('"models"') || config.raw.includes('"providers"');
|
|
1065
1085
|
const hasApiKey = config.raw.includes('"apiKey"') || config.raw.includes('"api_key"');
|
|
1066
1086
|
if (!hasModels && !hasApiKey) {
|
|
1067
|
-
|
|
1087
|
+
const issue = {
|
|
1068
1088
|
level: 'warning',
|
|
1069
1089
|
title: '未配置 AI 模型',
|
|
1070
1090
|
detail: '配置文件中未找到模型或 API Key 配置',
|
|
1071
1091
|
solution: '运行 npx openclawapi@latest 配置模型',
|
|
1072
1092
|
fixCmd: 'npx openclawapi@latest',
|
|
1073
|
-
}
|
|
1093
|
+
};
|
|
1094
|
+
if (autoFix) {
|
|
1095
|
+
console.log(colors.yellow(' 启动模型配置...'));
|
|
1096
|
+
spawnSync('npx', ['openclawapi@latest'], { stdio: 'inherit', shell: true });
|
|
1097
|
+
fixed.push('已启动模型配置向导');
|
|
1098
|
+
} else {
|
|
1099
|
+
issues.push(issue);
|
|
1100
|
+
}
|
|
1074
1101
|
} else {
|
|
1075
1102
|
log.success('已配置模型');
|
|
1076
1103
|
}
|
|
@@ -1141,6 +1168,98 @@ async function runHealthCheck(cliName, autoFix = false) {
|
|
|
1141
1168
|
|
|
1142
1169
|
// ============ 交互式菜单 ============
|
|
1143
1170
|
|
|
1171
|
+
async function showStatusInfo(cliName) {
|
|
1172
|
+
const config = getConfigInfo();
|
|
1173
|
+
const port = config.port || 18789;
|
|
1174
|
+
const token = config.token || '<未配置>';
|
|
1175
|
+
const dashboardUrl = `http://127.0.0.1:${port}/?token=${token}`;
|
|
1176
|
+
|
|
1177
|
+
console.log(colors.bold(colors.cyan('\n📊 OpenClaw 状态信息\n')));
|
|
1178
|
+
|
|
1179
|
+
// 服务状态
|
|
1180
|
+
const statusResult = safeExec(`${cliName} status`);
|
|
1181
|
+
const output = statusResult.ok ? statusResult.output : '';
|
|
1182
|
+
const isRunning = output.toLowerCase().includes('running') || output.toLowerCase().includes('active');
|
|
1183
|
+
|
|
1184
|
+
if (isRunning) {
|
|
1185
|
+
console.log(colors.green(' ✓ Gateway 服务正在运行'));
|
|
1186
|
+
} else {
|
|
1187
|
+
console.log(colors.red(' ✗ Gateway 服务未运行'));
|
|
1188
|
+
console.log(colors.yellow(' → 请先选择「检查修复」自动修复此问题\n'));
|
|
1189
|
+
return;
|
|
1190
|
+
}
|
|
1191
|
+
|
|
1192
|
+
// 端口检查
|
|
1193
|
+
const portCmd = platform() === 'win32'
|
|
1194
|
+
? `netstat -an | findstr :${port}`
|
|
1195
|
+
: `lsof -i :${port} 2>/dev/null || netstat -tlnp 2>/dev/null | grep :${port}`;
|
|
1196
|
+
const portResult = safeExec(portCmd);
|
|
1197
|
+
if (portResult.ok && portResult.output) {
|
|
1198
|
+
console.log(colors.green(` ✓ 端口 ${port} 正在监听`));
|
|
1199
|
+
} else {
|
|
1200
|
+
console.log(colors.red(` ✗ 端口 ${port} 未监听`));
|
|
1201
|
+
console.log(colors.yellow(' → 请先选择「检查修复」自动修复此问题\n'));
|
|
1202
|
+
return;
|
|
1203
|
+
}
|
|
1204
|
+
|
|
1205
|
+
// 模型配置
|
|
1206
|
+
if (config.raw) {
|
|
1207
|
+
const hasProviders = config.raw.includes('"providers"');
|
|
1208
|
+
if (hasProviders) {
|
|
1209
|
+
console.log(colors.green(' ✓ 已配置 AI 模型'));
|
|
1210
|
+
} else {
|
|
1211
|
+
console.log(colors.yellow(' ⚠ 未配置模型,请先选择「配置模型」'));
|
|
1212
|
+
}
|
|
1213
|
+
}
|
|
1214
|
+
|
|
1215
|
+
// Dashboard 访问指南
|
|
1216
|
+
console.log(colors.bold(colors.cyan('\n' + '─'.repeat(46))));
|
|
1217
|
+
console.log(colors.bold(colors.cyan(' 🌐 Web Dashboard 访问方法')));
|
|
1218
|
+
console.log(colors.bold(colors.cyan('─'.repeat(46))));
|
|
1219
|
+
|
|
1220
|
+
if (detectVps()) {
|
|
1221
|
+
const serverIp = getServerIp() || '<服务器IP>';
|
|
1222
|
+
const user = process.env.USER || 'root';
|
|
1223
|
+
|
|
1224
|
+
console.log(colors.gray('\n Gateway 绑定在 127.0.0.1(安全设计),需要通过'));
|
|
1225
|
+
console.log(colors.gray(' SSH 隧道从本地电脑访问。\n'));
|
|
1226
|
+
|
|
1227
|
+
console.log(colors.bold(' 第 1 步:在本地电脑打开终端,执行:\n'));
|
|
1228
|
+
|
|
1229
|
+
console.log(colors.yellow(' macOS / Linux:'));
|
|
1230
|
+
console.log(colors.green(` ssh -N -L ${port}:127.0.0.1:${port} ${user}@${serverIp}`));
|
|
1231
|
+
console.log('');
|
|
1232
|
+
console.log(colors.yellow(' Windows PowerShell:'));
|
|
1233
|
+
console.log(colors.green(` ssh -N -L ${port}:127.0.0.1:${port} ${user}@${serverIp}`));
|
|
1234
|
+
console.log('');
|
|
1235
|
+
console.log(colors.gray(' 保持这个终端窗口不要关闭。'));
|
|
1236
|
+
|
|
1237
|
+
console.log(colors.bold('\n 第 2 步:在本地浏览器打开:\n'));
|
|
1238
|
+
console.log(colors.green(` ${dashboardUrl}`));
|
|
1239
|
+
|
|
1240
|
+
console.log(colors.bold('\n 注意事项:'));
|
|
1241
|
+
console.log(colors.gray(' • SSH 窗口关闭后 Dashboard 无法访问,需重新执行第 1 步'));
|
|
1242
|
+
console.log(colors.gray(' • 如果连接断开,重新执行 SSH 命令即可'));
|
|
1243
|
+
console.log(colors.gray(' • 不建议将端口直接暴露到公网'));
|
|
1244
|
+
} else if (platform() === 'win32') {
|
|
1245
|
+
console.log(colors.gray('\n OpenClaw 运行在本机,直接用浏览器访问即可。\n'));
|
|
1246
|
+
console.log(colors.bold(' 在浏览器打开:\n'));
|
|
1247
|
+
console.log(colors.green(` ${dashboardUrl}`));
|
|
1248
|
+
console.log(colors.bold('\n 注意事项:'));
|
|
1249
|
+
console.log(colors.gray(' • 确保 Gateway 服务正在运行'));
|
|
1250
|
+
console.log(colors.gray(' • 如果无法访问,选择「检查修复」排查问题'));
|
|
1251
|
+
} else {
|
|
1252
|
+
console.log(colors.gray('\n OpenClaw 运行在本机,直接用浏览器访问即可。\n'));
|
|
1253
|
+
console.log(colors.bold(' 在浏览器打开:\n'));
|
|
1254
|
+
console.log(colors.green(` ${dashboardUrl}`));
|
|
1255
|
+
console.log(colors.bold('\n 注意事项:'));
|
|
1256
|
+
console.log(colors.gray(' • 确保 Gateway 服务正在运行'));
|
|
1257
|
+
console.log(colors.gray(' • 如果无法访问,选择「检查修复」排查问题'));
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
console.log('');
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1144
1263
|
async function showInteractiveMenu(existing) {
|
|
1145
1264
|
const cliName = existing.name || 'openclaw';
|
|
1146
1265
|
|
|
@@ -1152,27 +1271,32 @@ async function showInteractiveMenu(existing) {
|
|
|
1152
1271
|
showDashboardAccessInfo();
|
|
1153
1272
|
|
|
1154
1273
|
console.log(colors.cyan('\n请选择操作:'));
|
|
1155
|
-
console.log(` ${colors.yellow('1')}.
|
|
1156
|
-
console.log(` ${colors.yellow('2')}.
|
|
1157
|
-
console.log(` ${colors.yellow('3')}.
|
|
1158
|
-
console.log(` ${colors.yellow('4')}.
|
|
1159
|
-
console.log(` ${colors.yellow('5')}.
|
|
1160
|
-
console.log(` ${colors.yellow('6')}.
|
|
1161
|
-
console.log(` ${colors.yellow('7')}.
|
|
1274
|
+
console.log(` ${colors.yellow('1')}. 状态信息`);
|
|
1275
|
+
console.log(` ${colors.yellow('2')}. 检查修复`);
|
|
1276
|
+
console.log(` ${colors.yellow('3')}. 检查更新`);
|
|
1277
|
+
console.log(` ${colors.yellow('4')}. 配置模型`);
|
|
1278
|
+
console.log(` ${colors.yellow('5')}. 配置 Chat`);
|
|
1279
|
+
console.log(` ${colors.yellow('6')}. 配置技能`);
|
|
1280
|
+
console.log(` ${colors.yellow('7')}. 重新安装`);
|
|
1281
|
+
console.log(` ${colors.yellow('8')}. 完全卸载`);
|
|
1162
1282
|
console.log(` ${colors.yellow('0')}. 退出`);
|
|
1163
1283
|
|
|
1164
|
-
const choice = await askQuestion('\n请输入选项 (0-
|
|
1284
|
+
const choice = await askQuestion('\n请输入选项 (0-8): ');
|
|
1165
1285
|
|
|
1166
1286
|
switch (choice.trim()) {
|
|
1167
1287
|
case '1':
|
|
1168
|
-
await
|
|
1288
|
+
await showStatusInfo(cliName);
|
|
1169
1289
|
await waitForEnter('\n按回车返回菜单...');
|
|
1170
1290
|
break;
|
|
1171
1291
|
case '2':
|
|
1172
|
-
await
|
|
1292
|
+
await runHealthCheck(cliName, true);
|
|
1173
1293
|
await waitForEnter('\n按回车返回菜单...');
|
|
1174
1294
|
break;
|
|
1175
1295
|
case '3':
|
|
1296
|
+
await updateOpenClaw(cliName);
|
|
1297
|
+
await waitForEnter('\n按回车返回菜单...');
|
|
1298
|
+
break;
|
|
1299
|
+
case '4':
|
|
1176
1300
|
console.log(colors.cyan('\n启动模型配置...'));
|
|
1177
1301
|
spawnSync('npx', ['openclawapi@latest'], {
|
|
1178
1302
|
stdio: 'inherit',
|
|
@@ -1180,7 +1304,7 @@ async function showInteractiveMenu(existing) {
|
|
|
1180
1304
|
});
|
|
1181
1305
|
await waitForEnter('\n按回车返回菜单...');
|
|
1182
1306
|
break;
|
|
1183
|
-
case '
|
|
1307
|
+
case '5':
|
|
1184
1308
|
console.log(colors.cyan('\n选择聊天渠道:'));
|
|
1185
1309
|
console.log(` ${colors.yellow('a')}. Discord`);
|
|
1186
1310
|
console.log(` ${colors.yellow('b')}. 飞书`);
|
|
@@ -1200,13 +1324,13 @@ async function showInteractiveMenu(existing) {
|
|
|
1200
1324
|
}
|
|
1201
1325
|
await waitForEnter('\n按回车返回菜单...');
|
|
1202
1326
|
break;
|
|
1203
|
-
case '
|
|
1327
|
+
case '6':
|
|
1204
1328
|
console.log(colors.cyan('\n配置技能(即将支持)...'));
|
|
1205
1329
|
// TODO: 等待技能地址提供后实现
|
|
1206
1330
|
log.warn('技能配置功能即将上线,请稍后再试');
|
|
1207
1331
|
await waitForEnter('\n按回车返回菜单...');
|
|
1208
1332
|
break;
|
|
1209
|
-
case '
|
|
1333
|
+
case '7':
|
|
1210
1334
|
console.log(colors.yellow('\n即将重新安装 OpenClaw...'));
|
|
1211
1335
|
const confirmReinstall = await askQuestion('确认重新安装?(y/N): ');
|
|
1212
1336
|
if (confirmReinstall.toLowerCase() === 'y') {
|
|
@@ -1216,7 +1340,7 @@ async function showInteractiveMenu(existing) {
|
|
|
1216
1340
|
showCompletionInfo(newCliName);
|
|
1217
1341
|
}
|
|
1218
1342
|
break;
|
|
1219
|
-
case '
|
|
1343
|
+
case '8':
|
|
1220
1344
|
console.log(colors.red('\n⚠ 警告:卸载将删除所有配置!'));
|
|
1221
1345
|
const confirmUninstall = await askQuestion('确认卸载?(y/N): ');
|
|
1222
1346
|
if (confirmUninstall.toLowerCase() === 'y') {
|
|
@@ -1230,7 +1354,7 @@ async function showInteractiveMenu(existing) {
|
|
|
1230
1354
|
console.log(colors.gray('\n再见!'));
|
|
1231
1355
|
process.exit(0);
|
|
1232
1356
|
default:
|
|
1233
|
-
log.warn('无效选项,请输入 0-
|
|
1357
|
+
log.warn('无效选项,请输入 0-8');
|
|
1234
1358
|
}
|
|
1235
1359
|
}
|
|
1236
1360
|
}
|