openclawsetup 2.2.0 → 2.3.0
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 +163 -45
- package/package.json +2 -2
package/bin/cli.mjs
CHANGED
|
@@ -28,9 +28,12 @@ const ERROR_CODES = {
|
|
|
28
28
|
PERMISSION_DENIED: { code: 7, message: '权限不足' },
|
|
29
29
|
NETWORK_ERROR: { code: 8, message: '网络错误' },
|
|
30
30
|
PTY_UNAVAILABLE: { code: 9, message: '自动模式不可用' },
|
|
31
|
+
PORT_CONFLICT: { code: 10, message: '端口冲突' },
|
|
32
|
+
CONFIG_ERROR: { code: 11, message: '配置文件错误' },
|
|
33
|
+
GATEWAY_NOT_READY: { code: 12, message: 'Gateway 未就绪' },
|
|
31
34
|
};
|
|
32
35
|
|
|
33
|
-
function exitWithError(errorType, details = '') {
|
|
36
|
+
function exitWithError(errorType, details = '', autoFix = null) {
|
|
34
37
|
const err = ERROR_CODES[errorType] || { code: 99, message: '未知错误' };
|
|
35
38
|
console.log('\n' + '='.repeat(40));
|
|
36
39
|
console.log(`\x1b[31m❌ 错误: ${err.message}\x1b[0m`);
|
|
@@ -38,6 +41,10 @@ function exitWithError(errorType, details = '') {
|
|
|
38
41
|
if (details) {
|
|
39
42
|
console.log(details);
|
|
40
43
|
}
|
|
44
|
+
if (autoFix) {
|
|
45
|
+
console.log(`\n\x1b[33m💡 自动修复命令:\x1b[0m`);
|
|
46
|
+
console.log(` ${autoFix}`);
|
|
47
|
+
}
|
|
41
48
|
console.log(`\n错误码: ${err.code}`);
|
|
42
49
|
process.exit(err.code);
|
|
43
50
|
}
|
|
@@ -79,37 +86,45 @@ function parseArgs() {
|
|
|
79
86
|
reinstall: args.includes('--reinstall'),
|
|
80
87
|
uninstall: args.includes('--uninstall'),
|
|
81
88
|
check: args.includes('--check'),
|
|
89
|
+
fix: args.includes('--fix'),
|
|
82
90
|
manual: args.includes('--manual'),
|
|
83
91
|
auto: args.includes('--auto'),
|
|
84
92
|
withModel: args.includes('--with-model'),
|
|
85
93
|
withChannel: args.includes('--with-channel'),
|
|
94
|
+
quiet: args.includes('--quiet') || args.includes('-q'),
|
|
86
95
|
help: args.includes('--help') || args.includes('-h'),
|
|
87
96
|
};
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
function showHelp() {
|
|
91
100
|
console.log(`
|
|
92
|
-
${colors.bold('OpenClaw 安装向导')}
|
|
101
|
+
${colors.bold('OpenClaw 安装向导 v2.3')}
|
|
93
102
|
|
|
94
|
-
${colors.cyan('
|
|
103
|
+
${colors.cyan('基础用法:')}
|
|
95
104
|
npx openclawsetup 交互式菜单(已安装)/ 安装向导(未安装)
|
|
96
105
|
npx openclawsetup --check 检查配置和服务状态
|
|
106
|
+
npx openclawsetup --fix 检查并自动修复常见问题
|
|
97
107
|
npx openclawsetup --update 更新已安装的 OpenClaw
|
|
98
108
|
npx openclawsetup --reinstall 卸载后重新安装
|
|
99
109
|
npx openclawsetup --uninstall 卸载 OpenClaw
|
|
100
|
-
npx openclawsetup --manual 完全手动模式
|
|
101
|
-
npx openclawsetup --auto 强制自动模式
|
|
102
110
|
|
|
103
|
-
${colors.cyan('
|
|
104
|
-
|
|
105
|
-
|
|
111
|
+
${colors.cyan('安装模式:')}
|
|
112
|
+
npx openclawsetup --manual 完全手动模式(自己选择每个选项)
|
|
113
|
+
npx openclawsetup --auto 强制自动模式(脚本化场景)
|
|
114
|
+
npx openclawsetup -q 静默模式(减少输出)
|
|
106
115
|
|
|
107
116
|
${colors.cyan('高级选项:')}
|
|
108
117
|
--with-model 检测到模型配置时暂停自动选择
|
|
109
118
|
--with-channel 检测到渠道配置时暂停自动选择
|
|
110
119
|
|
|
111
120
|
${colors.cyan('安装后配置模型:')}
|
|
112
|
-
npx openclawapi@latest preset-claude
|
|
121
|
+
npx openclawapi@latest preset-claude # 一键配置 Claude
|
|
122
|
+
npx openclawapi@latest # 交互式配置
|
|
123
|
+
|
|
124
|
+
${colors.cyan('常见问题快速修复:')}
|
|
125
|
+
Gateway 未启动: openclaw gateway start
|
|
126
|
+
端口被占用: openclaw config set gateway.port 18790
|
|
127
|
+
配置文件损坏: rm ~/.openclaw/openclaw.json && openclaw onboard
|
|
113
128
|
`);
|
|
114
129
|
}
|
|
115
130
|
|
|
@@ -870,10 +885,11 @@ async function uninstallOpenClaw(existing) {
|
|
|
870
885
|
|
|
871
886
|
// ============ 健康检查 ============
|
|
872
887
|
|
|
873
|
-
async function runHealthCheck(cliName) {
|
|
888
|
+
async function runHealthCheck(cliName, autoFix = false) {
|
|
874
889
|
console.log(colors.bold(colors.cyan('\n🔍 OpenClaw 健康检查\n')));
|
|
875
890
|
|
|
876
891
|
const issues = [];
|
|
892
|
+
const fixed = [];
|
|
877
893
|
const config = getConfigInfo();
|
|
878
894
|
|
|
879
895
|
// 1. 检查配置文件
|
|
@@ -884,6 +900,7 @@ async function runHealthCheck(cliName) {
|
|
|
884
900
|
title: '配置文件不存在',
|
|
885
901
|
detail: '未找到 openclaw.json 配置文件',
|
|
886
902
|
solution: '运行 openclaw onboard 重新配置',
|
|
903
|
+
fixCmd: `${cliName} onboard`,
|
|
887
904
|
});
|
|
888
905
|
} else {
|
|
889
906
|
try {
|
|
@@ -891,12 +908,27 @@ async function runHealthCheck(cliName) {
|
|
|
891
908
|
JSON.parse(raw);
|
|
892
909
|
log.success('配置文件格式正确');
|
|
893
910
|
} catch (e) {
|
|
894
|
-
|
|
911
|
+
const issue = {
|
|
895
912
|
level: 'error',
|
|
896
913
|
title: '配置文件 JSON 格式错误',
|
|
897
914
|
detail: `解析失败: ${e.message}`,
|
|
898
|
-
solution: '
|
|
899
|
-
|
|
915
|
+
solution: '备份并重新生成配置文件',
|
|
916
|
+
fixCmd: `mv ${config.configPath} ${config.configPath}.bak && ${cliName} onboard`,
|
|
917
|
+
};
|
|
918
|
+
if (autoFix) {
|
|
919
|
+
console.log(colors.yellow(' 尝试修复配置文件...'));
|
|
920
|
+
const backupPath = `${config.configPath}.bak.${Date.now()}`;
|
|
921
|
+
try {
|
|
922
|
+
const { renameSync } = await import('fs');
|
|
923
|
+
renameSync(config.configPath, backupPath);
|
|
924
|
+
log.success(`已备份损坏的配置到 ${backupPath}`);
|
|
925
|
+
fixed.push('配置文件已备份,请重新运行 onboard');
|
|
926
|
+
} catch {
|
|
927
|
+
issues.push(issue);
|
|
928
|
+
}
|
|
929
|
+
} else {
|
|
930
|
+
issues.push(issue);
|
|
931
|
+
}
|
|
900
932
|
}
|
|
901
933
|
}
|
|
902
934
|
|
|
@@ -908,12 +940,25 @@ async function runHealthCheck(cliName) {
|
|
|
908
940
|
if (output.includes('running') || output.includes('active')) {
|
|
909
941
|
log.success('Gateway 进程正在运行');
|
|
910
942
|
} else if (output.includes('stopped') || output.includes('inactive') || output.includes('not running')) {
|
|
911
|
-
|
|
943
|
+
const issue = {
|
|
912
944
|
level: 'error',
|
|
913
945
|
title: 'Gateway 未运行',
|
|
914
946
|
detail: 'Gateway 服务已停止',
|
|
915
947
|
solution: `运行 ${cliName} gateway start 启动服务`,
|
|
916
|
-
|
|
948
|
+
fixCmd: `${cliName} gateway start`,
|
|
949
|
+
};
|
|
950
|
+
if (autoFix) {
|
|
951
|
+
console.log(colors.yellow(' 尝试启动 Gateway...'));
|
|
952
|
+
const startResult = safeExec(`${cliName} gateway start`);
|
|
953
|
+
if (startResult.ok) {
|
|
954
|
+
log.success('Gateway 已启动');
|
|
955
|
+
fixed.push('Gateway 已自动启动');
|
|
956
|
+
} else {
|
|
957
|
+
issues.push(issue);
|
|
958
|
+
}
|
|
959
|
+
} else {
|
|
960
|
+
issues.push(issue);
|
|
961
|
+
}
|
|
917
962
|
}
|
|
918
963
|
} else {
|
|
919
964
|
issues.push({
|
|
@@ -935,12 +980,25 @@ async function runHealthCheck(cliName) {
|
|
|
935
980
|
if (portResult.ok && portResult.output) {
|
|
936
981
|
log.success(`端口 ${port} 正在监听`);
|
|
937
982
|
} else {
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
983
|
+
// 检查是否有其他进程占用端口
|
|
984
|
+
const conflictCheck = safeExec(`lsof -i :${port} 2>/dev/null | head -5`);
|
|
985
|
+
if (conflictCheck.ok && conflictCheck.output && !conflictCheck.output.includes('openclaw') && !conflictCheck.output.includes('node')) {
|
|
986
|
+
issues.push({
|
|
987
|
+
level: 'error',
|
|
988
|
+
title: `端口 ${port} 被其他程序占用`,
|
|
989
|
+
detail: conflictCheck.output.slice(0, 100),
|
|
990
|
+
solution: `更换端口: ${cliName} config set gateway.port 18790`,
|
|
991
|
+
fixCmd: `${cliName} config set gateway.port 18790 && ${cliName} gateway restart`,
|
|
992
|
+
});
|
|
993
|
+
} else {
|
|
994
|
+
issues.push({
|
|
995
|
+
level: 'error',
|
|
996
|
+
title: `端口 ${port} 未监听`,
|
|
997
|
+
detail: 'Gateway 端口未开放,服务可能未正常启动',
|
|
998
|
+
solution: `运行 ${cliName} gateway restart`,
|
|
999
|
+
fixCmd: `${cliName} gateway restart`,
|
|
1000
|
+
});
|
|
1001
|
+
}
|
|
944
1002
|
}
|
|
945
1003
|
|
|
946
1004
|
// 4. 检查 API 健康
|
|
@@ -971,7 +1029,8 @@ async function runHealthCheck(cliName) {
|
|
|
971
1029
|
level: 'error',
|
|
972
1030
|
title: 'API 无响应',
|
|
973
1031
|
detail: '无法连接到 Gateway API',
|
|
974
|
-
solution:
|
|
1032
|
+
solution: `重启服务: ${cliName} gateway restart`,
|
|
1033
|
+
fixCmd: `${cliName} gateway restart`,
|
|
975
1034
|
});
|
|
976
1035
|
}
|
|
977
1036
|
|
|
@@ -986,6 +1045,7 @@ async function runHealthCheck(cliName) {
|
|
|
986
1045
|
title: '未配置 AI 模型',
|
|
987
1046
|
detail: '配置文件中未找到模型或 API Key 配置',
|
|
988
1047
|
solution: '运行 npx openclawapi@latest 配置模型',
|
|
1048
|
+
fixCmd: 'npx openclawapi@latest',
|
|
989
1049
|
});
|
|
990
1050
|
} else {
|
|
991
1051
|
log.success('已配置模型');
|
|
@@ -998,12 +1058,24 @@ async function runHealthCheck(cliName) {
|
|
|
998
1058
|
if (doctorResult.ok) {
|
|
999
1059
|
const output = doctorResult.output.toLowerCase();
|
|
1000
1060
|
if (output.includes('error') || output.includes('fail') || output.includes('问题')) {
|
|
1001
|
-
|
|
1061
|
+
const issue = {
|
|
1002
1062
|
level: 'warning',
|
|
1003
1063
|
title: '官方诊断发现问题',
|
|
1004
1064
|
detail: doctorResult.output.slice(0, 200),
|
|
1005
|
-
solution: `运行 ${cliName} doctor
|
|
1006
|
-
|
|
1065
|
+
solution: `运行 ${cliName} doctor --fix 尝试自动修复`,
|
|
1066
|
+
fixCmd: `${cliName} doctor --fix`,
|
|
1067
|
+
};
|
|
1068
|
+
if (autoFix) {
|
|
1069
|
+
console.log(colors.yellow(' 运行官方自动修复...'));
|
|
1070
|
+
const fixResult = safeExec(`${cliName} doctor --fix`);
|
|
1071
|
+
if (fixResult.ok) {
|
|
1072
|
+
fixed.push('官方诊断问题已尝试修复');
|
|
1073
|
+
} else {
|
|
1074
|
+
issues.push(issue);
|
|
1075
|
+
}
|
|
1076
|
+
} else {
|
|
1077
|
+
issues.push(issue);
|
|
1078
|
+
}
|
|
1007
1079
|
} else {
|
|
1008
1080
|
log.success('官方诊断通过');
|
|
1009
1081
|
}
|
|
@@ -1011,6 +1083,13 @@ async function runHealthCheck(cliName) {
|
|
|
1011
1083
|
|
|
1012
1084
|
// 输出结果
|
|
1013
1085
|
console.log('\n' + '='.repeat(50));
|
|
1086
|
+
|
|
1087
|
+
if (fixed.length > 0) {
|
|
1088
|
+
console.log(colors.bold(colors.green(`🔧 已自动修复 ${fixed.length} 个问题:`)));
|
|
1089
|
+
fixed.forEach((f, i) => console.log(colors.green(` ${i + 1}. ${f}`)));
|
|
1090
|
+
console.log('');
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1014
1093
|
if (issues.length === 0) {
|
|
1015
1094
|
console.log(colors.bold(colors.green('✅ 所有检查通过,OpenClaw 运行正常!')));
|
|
1016
1095
|
} else {
|
|
@@ -1022,9 +1101,18 @@ async function runHealthCheck(cliName) {
|
|
|
1022
1101
|
console.log(`\n${icon} ${colors.bold(`问题 ${i + 1}: ${issue.title}`)}`);
|
|
1023
1102
|
console.log(colors.gray(` ${issue.detail}`));
|
|
1024
1103
|
console.log(colors.cyan(` 解决方案: ${issue.solution}`));
|
|
1104
|
+
if (issue.fixCmd) {
|
|
1105
|
+
console.log(colors.yellow(` 快速修复: ${issue.fixCmd}`));
|
|
1106
|
+
}
|
|
1025
1107
|
});
|
|
1108
|
+
|
|
1109
|
+
if (!autoFix && issues.some(i => i.fixCmd)) {
|
|
1110
|
+
console.log(colors.cyan('\n💡 提示: 运行 npx openclawsetup --fix 尝试自动修复'));
|
|
1111
|
+
}
|
|
1026
1112
|
}
|
|
1027
1113
|
console.log('');
|
|
1114
|
+
|
|
1115
|
+
return { issues, fixed };
|
|
1028
1116
|
}
|
|
1029
1117
|
|
|
1030
1118
|
// ============ 交互式菜单 ============
|
|
@@ -1034,31 +1122,64 @@ async function showInteractiveMenu(existing) {
|
|
|
1034
1122
|
|
|
1035
1123
|
while (true) {
|
|
1036
1124
|
console.log(colors.bold(colors.cyan('\n' + '='.repeat(50))));
|
|
1037
|
-
console.log(colors.bold(colors.cyan(' OpenClaw 管理菜单')));
|
|
1125
|
+
console.log(colors.bold(colors.cyan(' 🦞 OpenClaw 管理菜单')));
|
|
1038
1126
|
console.log(colors.bold(colors.cyan('='.repeat(50))));
|
|
1039
1127
|
|
|
1040
1128
|
showDashboardAccessInfo();
|
|
1041
1129
|
|
|
1042
1130
|
console.log(colors.cyan('\n请选择操作:'));
|
|
1043
|
-
console.log(` ${colors.yellow('1')}. 检查
|
|
1044
|
-
console.log(` ${colors.yellow('2')}.
|
|
1045
|
-
console.log(` ${colors.yellow('3')}.
|
|
1046
|
-
console.log(` ${colors.yellow('4')}.
|
|
1047
|
-
console.log(` ${colors.yellow('5')}.
|
|
1131
|
+
console.log(` ${colors.yellow('1')}. 检查 - 诊断配置和服务状态`);
|
|
1132
|
+
console.log(` ${colors.yellow('2')}. 修复 - 自动修复常见问题`);
|
|
1133
|
+
console.log(` ${colors.yellow('3')}. 更新 - 更新到最新版本`);
|
|
1134
|
+
console.log(` ${colors.yellow('4')}. 重启 - 重启 Gateway 服务`);
|
|
1135
|
+
console.log(` ${colors.yellow('5')}. 日志 - 查看 Gateway 日志`);
|
|
1136
|
+
console.log(` ${colors.yellow('6')}. 配置模型 - 配置 AI 模型`);
|
|
1137
|
+
console.log(` ${colors.yellow('7')}. 重新安装 - 完全重新安装`);
|
|
1138
|
+
console.log(` ${colors.yellow('8')}. 卸载 - 完全卸载 OpenClaw`);
|
|
1048
1139
|
console.log(` ${colors.yellow('0')}. 退出`);
|
|
1049
1140
|
|
|
1050
|
-
const choice = await askQuestion('\n请输入选项 (0-
|
|
1141
|
+
const choice = await askQuestion('\n请输入选项 (0-8): ');
|
|
1051
1142
|
|
|
1052
1143
|
switch (choice.trim()) {
|
|
1053
1144
|
case '1':
|
|
1054
|
-
await runHealthCheck(cliName);
|
|
1145
|
+
await runHealthCheck(cliName, false);
|
|
1055
1146
|
await waitForEnter('\n按回车返回菜单...');
|
|
1056
1147
|
break;
|
|
1057
1148
|
case '2':
|
|
1058
|
-
await
|
|
1149
|
+
await runHealthCheck(cliName, true);
|
|
1059
1150
|
await waitForEnter('\n按回车返回菜单...');
|
|
1060
1151
|
break;
|
|
1061
1152
|
case '3':
|
|
1153
|
+
await updateOpenClaw(cliName);
|
|
1154
|
+
await waitForEnter('\n按回车返回菜单...');
|
|
1155
|
+
break;
|
|
1156
|
+
case '4':
|
|
1157
|
+
console.log(colors.cyan('\n重启 Gateway...'));
|
|
1158
|
+
const restartResult = safeExec(`${cliName} gateway restart`);
|
|
1159
|
+
if (restartResult.ok) {
|
|
1160
|
+
log.success('Gateway 已重启');
|
|
1161
|
+
} else {
|
|
1162
|
+
log.error('重启失败: ' + (restartResult.error || ''));
|
|
1163
|
+
}
|
|
1164
|
+
await waitForEnter('\n按回车返回菜单...');
|
|
1165
|
+
break;
|
|
1166
|
+
case '5':
|
|
1167
|
+
console.log(colors.cyan('\n显示最近日志(按 Ctrl+C 退出)...\n'));
|
|
1168
|
+
spawnSync(cliName, ['gateway', 'logs', '--tail', '50'], {
|
|
1169
|
+
stdio: 'inherit',
|
|
1170
|
+
shell: true,
|
|
1171
|
+
});
|
|
1172
|
+
await waitForEnter('\n按回车返回菜单...');
|
|
1173
|
+
break;
|
|
1174
|
+
case '6':
|
|
1175
|
+
console.log(colors.cyan('\n启动模型配置...'));
|
|
1176
|
+
spawnSync('npx', ['openclawapi@latest'], {
|
|
1177
|
+
stdio: 'inherit',
|
|
1178
|
+
shell: true,
|
|
1179
|
+
});
|
|
1180
|
+
await waitForEnter('\n按回车返回菜单...');
|
|
1181
|
+
break;
|
|
1182
|
+
case '7':
|
|
1062
1183
|
console.log(colors.yellow('\n即将重新安装 OpenClaw...'));
|
|
1063
1184
|
const confirmReinstall = await askQuestion('确认重新安装?(y/N): ');
|
|
1064
1185
|
if (confirmReinstall.toLowerCase() === 'y') {
|
|
@@ -1068,7 +1189,7 @@ async function showInteractiveMenu(existing) {
|
|
|
1068
1189
|
showCompletionInfo(newCliName);
|
|
1069
1190
|
}
|
|
1070
1191
|
break;
|
|
1071
|
-
case '
|
|
1192
|
+
case '8':
|
|
1072
1193
|
console.log(colors.red('\n⚠ 警告:卸载将删除所有配置!'));
|
|
1073
1194
|
const confirmUninstall = await askQuestion('确认卸载?(y/N): ');
|
|
1074
1195
|
if (confirmUninstall.toLowerCase() === 'y') {
|
|
@@ -1077,20 +1198,12 @@ async function showInteractiveMenu(existing) {
|
|
|
1077
1198
|
process.exit(0);
|
|
1078
1199
|
}
|
|
1079
1200
|
break;
|
|
1080
|
-
case '5':
|
|
1081
|
-
console.log(colors.cyan('\n启动模型配置...'));
|
|
1082
|
-
spawnSync('npx', ['openclawapi@latest'], {
|
|
1083
|
-
stdio: 'inherit',
|
|
1084
|
-
shell: true,
|
|
1085
|
-
});
|
|
1086
|
-
await waitForEnter('\n按回车返回菜单...');
|
|
1087
|
-
break;
|
|
1088
1201
|
case '0':
|
|
1089
1202
|
case '':
|
|
1090
1203
|
console.log(colors.gray('\n再见!'));
|
|
1091
1204
|
process.exit(0);
|
|
1092
1205
|
default:
|
|
1093
|
-
log.warn('无效选项,请输入 0-
|
|
1206
|
+
log.warn('无效选项,请输入 0-8');
|
|
1094
1207
|
}
|
|
1095
1208
|
}
|
|
1096
1209
|
}
|
|
@@ -1128,7 +1241,12 @@ async function main() {
|
|
|
1128
1241
|
console.log(colors.green(`\n✓ 检测到已安装: ${existing.name}`));
|
|
1129
1242
|
|
|
1130
1243
|
if (options.check) {
|
|
1131
|
-
await runHealthCheck(existing.name);
|
|
1244
|
+
await runHealthCheck(existing.name, false);
|
|
1245
|
+
process.exit(0);
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
if (options.fix) {
|
|
1249
|
+
await runHealthCheck(existing.name, true);
|
|
1132
1250
|
process.exit(0);
|
|
1133
1251
|
}
|
|
1134
1252
|
|