openclawsetup 2.0.2 → 2.1.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/README.md +2 -2
- package/bin/cli.mjs +205 -318
- package/install.sh +1 -1
- package/package.json +2 -2
- package//344/275/277/347/224/250/350/257/264/346/230/216.md +3 -3
package/README.md
CHANGED
|
@@ -41,7 +41,7 @@ npx openclawsetup@latest
|
|
|
41
41
|
自动完成以下选择:
|
|
42
42
|
- ✓ 选择 QuickStart 模式
|
|
43
43
|
- ✓ 跳过模型配置(后续用 `npx openclawapi` 配置)
|
|
44
|
-
- ✓ 跳过渠道配置(后续用 `npx openclawdc
|
|
44
|
+
- ✓ 跳过渠道配置(后续用 `npx openclawdc` 或 `npx openclaw-chat-cn@latest feishu` 配置)
|
|
45
45
|
- ✓ 安装后台服务(开机自启)
|
|
46
46
|
- ✓ 选择 Web Dashboard
|
|
47
47
|
|
|
@@ -110,7 +110,7 @@ npx openclawapi@latest
|
|
|
110
110
|
npx openclawdc
|
|
111
111
|
|
|
112
112
|
# 飞书
|
|
113
|
-
npx
|
|
113
|
+
npx openclaw-chat-cn@latest feishu
|
|
114
114
|
```
|
|
115
115
|
|
|
116
116
|
## 常用命令
|
package/bin/cli.mjs
CHANGED
|
@@ -1,58 +1,21 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* OpenClaw
|
|
3
|
+
* OpenClaw 安装向导
|
|
4
4
|
*
|
|
5
|
-
* 调用官方 openclaw onboard
|
|
6
|
-
*
|
|
5
|
+
* 调用官方 openclaw onboard,在关键步骤前提供中文指引
|
|
6
|
+
* 用户看到完整的原版安装过程,我们只提供选择建议
|
|
7
7
|
*
|
|
8
8
|
* 用法:
|
|
9
|
-
* npx openclawsetup #
|
|
10
|
-
* npx openclawsetup --manual # 手动模式(完全交互)
|
|
9
|
+
* npx openclawsetup # 带中文指引的安装
|
|
11
10
|
* npx openclawsetup --update # 更新已安装的 OpenClaw
|
|
12
11
|
*/
|
|
13
12
|
|
|
14
|
-
import { execSync,
|
|
15
|
-
import { existsSync,
|
|
13
|
+
import { execSync, spawnSync } from 'child_process';
|
|
14
|
+
import { existsSync, accessSync, constants as fsConstants, rmSync } from 'fs';
|
|
16
15
|
import { homedir, platform } from 'os';
|
|
17
16
|
import { join } from 'path';
|
|
18
17
|
import { createInterface } from 'readline';
|
|
19
18
|
|
|
20
|
-
// ============ 配置 ============
|
|
21
|
-
|
|
22
|
-
// 自动应答规则:识别关键词 -> 自动输入
|
|
23
|
-
// 顺序很重要:先匹配的先执行
|
|
24
|
-
const AUTO_RESPONSES = [
|
|
25
|
-
// 安全确认
|
|
26
|
-
{ match: /continue\?|accept|agree|proceed|\(y\/n\)|\[y\/N\]|\[Y\/n\]/i, response: 'y', delay: 300, desc: '安全确认' },
|
|
27
|
-
|
|
28
|
-
// Setup 模式选择 - QuickStart
|
|
29
|
-
{ match: /quick\s*start|setup\s*mode|choose.*mode/i, response: '1', delay: 500, desc: '选择 QuickStart' },
|
|
30
|
-
|
|
31
|
-
// Model Provider - 跳过(用户后续用 openclawapi 配置)
|
|
32
|
-
{ match: /provider|anthropic|openai|select.*model|choose.*model/i, response: 's', delay: 500, desc: '跳过模型配置' },
|
|
33
|
-
|
|
34
|
-
// API Key - 跳过
|
|
35
|
-
{ match: /api\s*key|enter.*key|paste.*key|setup.*token/i, response: '', delay: 300, desc: '跳过 API Key', skip: true },
|
|
36
|
-
|
|
37
|
-
// Channel 配置 - 跳过
|
|
38
|
-
{ match: /channel|telegram|discord|whatsapp|slack|skip.*channel/i, response: 's', delay: 500, desc: '跳过渠道配置' },
|
|
39
|
-
|
|
40
|
-
// Skills - 跳过
|
|
41
|
-
{ match: /skill|install.*skill|skip.*skill/i, response: 's', delay: 500, desc: '跳过 Skills' },
|
|
42
|
-
|
|
43
|
-
// Daemon/Service - 安装
|
|
44
|
-
{ match: /daemon|service|background|auto.*start|launchd|systemd/i, response: 'y', delay: 300, desc: '安装后台服务' },
|
|
45
|
-
|
|
46
|
-
// UI 选择 - Web Dashboard
|
|
47
|
-
{ match: /interface|dashboard|tui|web.*ui|control.*ui/i, response: '1', delay: 500, desc: '选择 Web Dashboard' },
|
|
48
|
-
|
|
49
|
-
// 名称/称呼 - 使用默认
|
|
50
|
-
{ match: /name|call\s*you|address/i, response: '', delay: 300, desc: '使用默认名称' },
|
|
51
|
-
|
|
52
|
-
// 通用确认
|
|
53
|
-
{ match: /press\s*enter|continue|next|\[enter\]/i, response: '', delay: 200, desc: '继续' },
|
|
54
|
-
];
|
|
55
|
-
|
|
56
19
|
// ============ 工具函数 ============
|
|
57
20
|
|
|
58
21
|
const colors = {
|
|
@@ -62,7 +25,7 @@ const colors = {
|
|
|
62
25
|
cyan: (s) => `\x1b[36m${s}\x1b[0m`,
|
|
63
26
|
bold: (s) => `\x1b[1m${s}\x1b[0m`,
|
|
64
27
|
gray: (s) => `\x1b[90m${s}\x1b[0m`,
|
|
65
|
-
|
|
28
|
+
bgYellow: (s) => `\x1b[43m\x1b[30m${s}\x1b[0m`,
|
|
66
29
|
};
|
|
67
30
|
|
|
68
31
|
const log = {
|
|
@@ -71,30 +34,9 @@ const log = {
|
|
|
71
34
|
warn: (msg) => console.log(colors.yellow(`⚠ ${msg}`)),
|
|
72
35
|
error: (msg) => console.log(colors.red(`✗ ${msg}`)),
|
|
73
36
|
hint: (msg) => console.log(colors.gray(` 提示: ${msg}`)),
|
|
74
|
-
|
|
75
|
-
};
|
|
76
|
-
|
|
77
|
-
const ERROR_CODES = {
|
|
78
|
-
NODE_VERSION: { code: 1, message: 'Node.js 版本过低' },
|
|
79
|
-
NPM_INSTALL_FAILED: { code: 3, message: 'npm 安装失败' },
|
|
80
|
-
ONBOARD_FAILED: { code: 4, message: 'onboard 执行失败' },
|
|
81
|
-
NOT_TTY: { code: 5, message: '需要交互式终端' },
|
|
37
|
+
guide: (msg) => console.log(colors.bgYellow(` 📖 ${msg} `)),
|
|
82
38
|
};
|
|
83
39
|
|
|
84
|
-
function exitWithError(errorType, details = '', solutions = []) {
|
|
85
|
-
const err = ERROR_CODES[errorType] || { code: 99, message: '未知错误' };
|
|
86
|
-
console.log(colors.bold(colors.red('\n========================================')));
|
|
87
|
-
console.log(colors.bold(colors.red(`❌ 错误: ${err.message}`)));
|
|
88
|
-
console.log(colors.bold(colors.red('========================================')));
|
|
89
|
-
if (details) console.log(colors.gray(` ${details}`));
|
|
90
|
-
if (solutions.length > 0) {
|
|
91
|
-
console.log(colors.cyan('\n解决方案:'));
|
|
92
|
-
solutions.forEach((s, i) => console.log(` ${i + 1}. ${s}`));
|
|
93
|
-
}
|
|
94
|
-
console.log(colors.gray(`\n错误码: ${err.code}`));
|
|
95
|
-
process.exit(err.code);
|
|
96
|
-
}
|
|
97
|
-
|
|
98
40
|
function safeExec(cmd, options = {}) {
|
|
99
41
|
try {
|
|
100
42
|
const output = execSync(cmd, { encoding: 'utf8', stdio: 'pipe', ...options });
|
|
@@ -104,96 +46,52 @@ function safeExec(cmd, options = {}) {
|
|
|
104
46
|
}
|
|
105
47
|
}
|
|
106
48
|
|
|
107
|
-
function checkNodeVersion() {
|
|
108
|
-
const version = process.version;
|
|
109
|
-
const major = parseInt(version.slice(1).split('.')[0], 10);
|
|
110
|
-
if (major < 18) {
|
|
111
|
-
exitWithError('NODE_VERSION', `当前版本: ${version},需要 Node.js 18+`, [
|
|
112
|
-
'升级 Node.js: https://nodejs.org/',
|
|
113
|
-
'使用 nvm: nvm install 22',
|
|
114
|
-
]);
|
|
115
|
-
}
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
49
|
function parseArgs() {
|
|
120
50
|
const args = process.argv.slice(2);
|
|
121
51
|
return {
|
|
122
|
-
manual: args.includes('--manual'),
|
|
123
52
|
update: args.includes('--update'),
|
|
124
53
|
reinstall: args.includes('--reinstall'),
|
|
125
54
|
help: args.includes('--help') || args.includes('-h'),
|
|
126
|
-
skipModel: !args.includes('--with-model'), // 默认跳过模型配置
|
|
127
55
|
};
|
|
128
56
|
}
|
|
129
57
|
|
|
130
58
|
function showHelp() {
|
|
131
59
|
console.log(`
|
|
132
|
-
${colors.bold('OpenClaw
|
|
60
|
+
${colors.bold('OpenClaw 安装向导')}
|
|
133
61
|
|
|
134
62
|
${colors.cyan('用法:')}
|
|
135
|
-
npx openclawsetup
|
|
136
|
-
npx openclawsetup --manual 手动模式(完全交互,自己选择)
|
|
63
|
+
npx openclawsetup 带中文指引的安装
|
|
137
64
|
npx openclawsetup --update 更新已安装的 OpenClaw
|
|
138
65
|
npx openclawsetup --reinstall 卸载后重新安装
|
|
139
66
|
|
|
140
|
-
${colors.cyan('
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
--update 检查并更新
|
|
144
|
-
--reinstall 重新安装(清除配置)
|
|
145
|
-
--help, -h 显示帮助
|
|
146
|
-
|
|
147
|
-
${colors.cyan('智能安装会自动:')}
|
|
148
|
-
✓ 选择 QuickStart 模式
|
|
149
|
-
✓ 跳过模型配置(后续用 npx openclawapi 配置)
|
|
150
|
-
✓ 跳过渠道配置(后续用 npx openclawdc/openclawfs 配置)
|
|
151
|
-
✓ 安装后台服务(开机自启)
|
|
152
|
-
✓ 选择 Web Dashboard
|
|
67
|
+
${colors.cyan('说明:')}
|
|
68
|
+
本工具会调用官方 openclaw onboard 命令
|
|
69
|
+
在关键步骤前提供中文指引,告诉你该如何选择
|
|
153
70
|
|
|
154
71
|
${colors.cyan('安装后配置模型:')}
|
|
155
72
|
npx openclawapi@latest preset-claude
|
|
156
73
|
`);
|
|
157
74
|
}
|
|
158
75
|
|
|
159
|
-
// ============
|
|
160
|
-
|
|
161
|
-
function detectExistingInstall() {
|
|
162
|
-
const home = homedir();
|
|
163
|
-
const openclawDir = join(home, '.openclaw');
|
|
164
|
-
const clawdbotDir = join(home, '.clawdbot');
|
|
165
|
-
|
|
166
|
-
let result = { installed: false };
|
|
76
|
+
// ============ 环境检测 ============
|
|
167
77
|
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
result = { installed: true, name: 'openclaw', version: openclawResult.output };
|
|
178
|
-
} else {
|
|
179
|
-
const clawdbotResult = safeExec('clawdbot --version');
|
|
180
|
-
if (clawdbotResult.ok) {
|
|
181
|
-
result = { installed: true, name: 'clawdbot', version: clawdbotResult.output };
|
|
182
|
-
}
|
|
183
|
-
}
|
|
78
|
+
function checkNodeVersion() {
|
|
79
|
+
const version = process.version;
|
|
80
|
+
const major = parseInt(version.slice(1).split('.')[0], 10);
|
|
81
|
+
if (major < 18) {
|
|
82
|
+
log.error(`Node.js 版本过低: ${version},需要 18+`);
|
|
83
|
+
console.log(colors.cyan('\n解决方案:'));
|
|
84
|
+
console.log(' 1. 访问 https://nodejs.org/ 下载最新版本');
|
|
85
|
+
console.log(' 2. 或使用 nvm: nvm install 22');
|
|
86
|
+
process.exit(1);
|
|
184
87
|
}
|
|
185
|
-
|
|
186
|
-
return result;
|
|
88
|
+
return true;
|
|
187
89
|
}
|
|
188
90
|
|
|
189
|
-
// ============ 安装 OpenClaw ============
|
|
190
|
-
|
|
191
|
-
// 检测是否需要 sudo
|
|
192
91
|
function needsSudo() {
|
|
193
92
|
const os = platform();
|
|
194
93
|
if (os === 'win32' || os === 'darwin') return false;
|
|
195
94
|
|
|
196
|
-
// Linux: 检查 /usr/lib/node_modules 是否可写
|
|
197
95
|
try {
|
|
198
96
|
const testDir = '/usr/lib/node_modules';
|
|
199
97
|
if (existsSync(testDir)) {
|
|
@@ -206,219 +104,208 @@ function needsSudo() {
|
|
|
206
104
|
return true;
|
|
207
105
|
}
|
|
208
106
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
107
|
+
function detectExistingInstall() {
|
|
108
|
+
const home = homedir();
|
|
109
|
+
const openclawDir = join(home, '.openclaw');
|
|
110
|
+
const clawdbotDir = join(home, '.clawdbot');
|
|
111
|
+
|
|
112
|
+
if (existsSync(openclawDir)) {
|
|
113
|
+
return { installed: true, configDir: openclawDir, name: 'openclaw' };
|
|
114
|
+
}
|
|
115
|
+
if (existsSync(clawdbotDir)) {
|
|
116
|
+
return { installed: true, configDir: clawdbotDir, name: 'clawdbot' };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const openclawResult = safeExec('openclaw --version');
|
|
120
|
+
if (openclawResult.ok) {
|
|
121
|
+
return { installed: true, name: 'openclaw', version: openclawResult.output };
|
|
214
122
|
}
|
|
215
|
-
|
|
123
|
+
|
|
124
|
+
const clawdbotResult = safeExec('clawdbot --version');
|
|
125
|
+
if (clawdbotResult.ok) {
|
|
126
|
+
return { installed: true, name: 'clawdbot', version: clawdbotResult.output };
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return { installed: false };
|
|
216
130
|
}
|
|
217
131
|
|
|
218
|
-
|
|
219
|
-
|
|
132
|
+
// ============ 安装指引 ============
|
|
133
|
+
|
|
134
|
+
function showInstallGuide() {
|
|
135
|
+
console.log(colors.bold(colors.cyan('\n' + '='.repeat(60))));
|
|
136
|
+
console.log(colors.bold(colors.cyan(' 📖 OpenClaw 安装指引')));
|
|
137
|
+
console.log(colors.bold(colors.cyan('='.repeat(60))));
|
|
138
|
+
|
|
139
|
+
console.log(colors.yellow('\n接下来会运行官方 openclaw onboard 命令'));
|
|
140
|
+
console.log(colors.yellow('请按照以下指引选择:\n'));
|
|
141
|
+
|
|
142
|
+
console.log(colors.cyan('┌─────────────────────────────────────────────────────────┐'));
|
|
143
|
+
console.log(colors.cyan('│') + colors.bold(' 步骤 1: 安全确认') + colors.cyan(' │'));
|
|
144
|
+
console.log(colors.cyan('│') + ' 看到 "Do you want to continue?" 时 ' + colors.cyan('│'));
|
|
145
|
+
console.log(colors.cyan('│') + colors.green(' → 输入 y 然后回车') + ' ' + colors.cyan('│'));
|
|
146
|
+
console.log(colors.cyan('├─────────────────────────────────────────────────────────┤'));
|
|
147
|
+
console.log(colors.cyan('│') + colors.bold(' 步骤 2: Setup 模式') + colors.cyan(' │'));
|
|
148
|
+
console.log(colors.cyan('│') + ' 看到 "Quick Start" 和 "Advanced" 选项时 ' + colors.cyan('│'));
|
|
149
|
+
console.log(colors.cyan('│') + colors.green(' → 选择 Quick Start(通常是第 1 个)') + ' ' + colors.cyan('│'));
|
|
150
|
+
console.log(colors.cyan('├─────────────────────────────────────────────────────────┤'));
|
|
151
|
+
console.log(colors.cyan('│') + colors.bold(' 步骤 3: Model Provider') + colors.cyan(' │'));
|
|
152
|
+
console.log(colors.cyan('│') + ' 看到选择 AI 模型提供商时 ' + colors.cyan('│'));
|
|
153
|
+
console.log(colors.cyan('│') + colors.green(' → 选择 Skip 或按 s 跳过') + ' ' + colors.cyan('│'));
|
|
154
|
+
console.log(colors.cyan('│') + colors.gray(' (后续用 npx openclawapi 单独配置更方便)') + ' ' + colors.cyan('│'));
|
|
155
|
+
console.log(colors.cyan('├─────────────────────────────────────────────────────────┤'));
|
|
156
|
+
console.log(colors.cyan('│') + colors.bold(' 步骤 4: Channel 配置') + colors.cyan(' │'));
|
|
157
|
+
console.log(colors.cyan('│') + ' 看到选择聊天渠道(Telegram/Discord 等)时 ' + colors.cyan('│'));
|
|
158
|
+
console.log(colors.cyan('│') + colors.green(' → 选择 Skip 或按 s 跳过') + ' ' + colors.cyan('│'));
|
|
159
|
+
console.log(colors.cyan('│') + colors.gray(' (后续用 npx openclawdc 等单独配置)') + ' ' + colors.cyan('│'));
|
|
160
|
+
console.log(colors.cyan('├─────────────────────────────────────────────────────────┤'));
|
|
161
|
+
console.log(colors.cyan('│') + colors.bold(' 步骤 5: Daemon/Service') + colors.cyan(' │'));
|
|
162
|
+
console.log(colors.cyan('│') + ' 看到是否安装后台服务时 ' + colors.cyan('│'));
|
|
163
|
+
console.log(colors.cyan('│') + colors.green(' → 输入 y 确认安装(开机自启)') + ' ' + colors.cyan('│'));
|
|
164
|
+
console.log(colors.cyan('└─────────────────────────────────────────────────────────┘'));
|
|
165
|
+
|
|
166
|
+
console.log(colors.gray('\n其他选项可以直接回车使用默认值'));
|
|
167
|
+
console.log(colors.gray('如果不确定,选择 Skip 或直接回车通常是安全的\n'));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
function waitForEnter(message) {
|
|
171
|
+
return new Promise((resolve) => {
|
|
172
|
+
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
173
|
+
rl.question(colors.yellow(message), () => {
|
|
174
|
+
rl.close();
|
|
175
|
+
resolve();
|
|
176
|
+
});
|
|
177
|
+
});
|
|
178
|
+
}
|
|
220
179
|
|
|
180
|
+
// ============ 安装 OpenClaw ============
|
|
181
|
+
|
|
182
|
+
async function installOpenClaw() {
|
|
221
183
|
const useSudo = needsSudo();
|
|
184
|
+
|
|
185
|
+
console.log(colors.bold(colors.cyan('\n[1/2] 安装 OpenClaw CLI\n')));
|
|
186
|
+
|
|
222
187
|
if (useSudo) {
|
|
223
|
-
log.hint('
|
|
188
|
+
log.hint('Linux 系统需要 sudo 权限安装全局包');
|
|
189
|
+
console.log(colors.yellow('\n请运行以下命令:'));
|
|
190
|
+
console.log(colors.green(' sudo npm install -g openclaw@latest\n'));
|
|
191
|
+
|
|
192
|
+
await waitForEnter('安装完成后按回车继续...');
|
|
193
|
+
|
|
194
|
+
// 验证安装
|
|
195
|
+
const check = safeExec('openclaw --version');
|
|
196
|
+
if (!check.ok) {
|
|
197
|
+
log.error('未检测到 openclaw 命令,请确认安装成功');
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
log.success(`OpenClaw 已安装: ${check.output}`);
|
|
201
|
+
return 'openclaw';
|
|
224
202
|
}
|
|
225
203
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
const child = spawn(cmd, args, {
|
|
229
|
-
stdio: 'inherit',
|
|
230
|
-
shell: true,
|
|
231
|
-
});
|
|
204
|
+
// macOS 或有权限的 Linux:直接安装
|
|
205
|
+
console.log(colors.gray('正在安装 openclaw...\n'));
|
|
232
206
|
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
resolve('openclaw');
|
|
237
|
-
} else {
|
|
238
|
-
// 尝试 clawdbot
|
|
239
|
-
log.warn('openclaw 安装失败,尝试 clawdbot...');
|
|
240
|
-
const { cmd: cmd2, args: args2 } = getNpmInstallCmd('clawdbot@latest');
|
|
241
|
-
const fallback = spawn(cmd2, args2, {
|
|
242
|
-
stdio: 'inherit',
|
|
243
|
-
shell: true,
|
|
244
|
-
});
|
|
245
|
-
fallback.on('close', (code2) => {
|
|
246
|
-
if (code2 === 0) {
|
|
247
|
-
log.success('clawdbot 安装完成');
|
|
248
|
-
resolve('clawdbot');
|
|
249
|
-
} else {
|
|
250
|
-
reject(new Error('npm 安装失败'));
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
}
|
|
254
|
-
});
|
|
207
|
+
const result = spawnSync('npm', ['install', '-g', 'openclaw@latest'], {
|
|
208
|
+
stdio: 'inherit',
|
|
209
|
+
shell: true,
|
|
255
210
|
});
|
|
256
|
-
}
|
|
257
211
|
|
|
258
|
-
|
|
212
|
+
if (result.status === 0) {
|
|
213
|
+
log.success('OpenClaw CLI 安装完成');
|
|
214
|
+
return 'openclaw';
|
|
215
|
+
}
|
|
259
216
|
|
|
260
|
-
|
|
261
|
-
log.
|
|
217
|
+
// 尝试 clawdbot
|
|
218
|
+
log.warn('openclaw 安装失败,尝试 clawdbot...');
|
|
219
|
+
const fallback = spawnSync('npm', ['install', '-g', 'clawdbot@latest'], {
|
|
220
|
+
stdio: 'inherit',
|
|
221
|
+
shell: true,
|
|
222
|
+
});
|
|
262
223
|
|
|
263
|
-
if (
|
|
264
|
-
log.
|
|
265
|
-
|
|
266
|
-
console.log(colors.dim(' 智能模式:自动选择推荐配置,您可以观看安装过程\n'));
|
|
224
|
+
if (fallback.status === 0) {
|
|
225
|
+
log.success('clawdbot 安装完成');
|
|
226
|
+
return 'clawdbot';
|
|
267
227
|
}
|
|
268
228
|
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
// Windows: 直接 spawn,可能没有完整的交互体验
|
|
276
|
-
child = spawn(cliName, ['onboard', '--install-daemon'], {
|
|
277
|
-
stdio: options.manual ? 'inherit' : ['pipe', 'pipe', 'pipe'],
|
|
278
|
-
shell: true,
|
|
279
|
-
env: { ...process.env, FORCE_COLOR: '1', TERM: 'xterm-256color' },
|
|
280
|
-
});
|
|
281
|
-
} else if (os === 'darwin') {
|
|
282
|
-
// macOS: script -q /dev/null command args...
|
|
283
|
-
child = spawn('script', ['-q', '/dev/null', cliName, 'onboard', '--install-daemon'], {
|
|
284
|
-
stdio: options.manual ? 'inherit' : ['pipe', 'pipe', 'pipe'],
|
|
285
|
-
env: { ...process.env, FORCE_COLOR: '1', TERM: 'xterm-256color' },
|
|
286
|
-
});
|
|
287
|
-
} else {
|
|
288
|
-
// Linux: script -q -c "command args..." /dev/null
|
|
289
|
-
child = spawn('script', ['-q', '-c', `${cliName} onboard --install-daemon`, '/dev/null'], {
|
|
290
|
-
stdio: options.manual ? 'inherit' : ['pipe', 'pipe', 'pipe'],
|
|
291
|
-
env: { ...process.env, FORCE_COLOR: '1', TERM: 'xterm-256color' },
|
|
292
|
-
});
|
|
293
|
-
}
|
|
229
|
+
log.error('安装失败');
|
|
230
|
+
console.log(colors.cyan('\n解决方案:'));
|
|
231
|
+
console.log(' 1. 检查网络连接');
|
|
232
|
+
console.log(' 2. 手动安装: npm install -g openclaw@latest');
|
|
233
|
+
process.exit(1);
|
|
234
|
+
}
|
|
294
235
|
|
|
295
|
-
|
|
296
|
-
// 手动模式:完全交互
|
|
297
|
-
child.on('close', (code) => {
|
|
298
|
-
if (code === 0) {
|
|
299
|
-
resolve();
|
|
300
|
-
} else {
|
|
301
|
-
reject(new Error(`onboard 退出码: ${code}`));
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
return;
|
|
305
|
-
}
|
|
236
|
+
// ============ 运行 Onboard ============
|
|
306
237
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
let lastResponseTime = 0;
|
|
310
|
-
const RESPONSE_COOLDOWN = 1000; // 防止重复响应
|
|
311
|
-
|
|
312
|
-
const processOutput = (data) => {
|
|
313
|
-
const text = data.toString();
|
|
314
|
-
process.stdout.write(text); // 显示原版输出
|
|
315
|
-
buffer += text;
|
|
316
|
-
|
|
317
|
-
// 检查是否需要自动应答
|
|
318
|
-
const now = Date.now();
|
|
319
|
-
if (now - lastResponseTime < RESPONSE_COOLDOWN) return;
|
|
320
|
-
|
|
321
|
-
for (const rule of AUTO_RESPONSES) {
|
|
322
|
-
if (rule.match.test(buffer)) {
|
|
323
|
-
if (rule.skip) {
|
|
324
|
-
// 跳过这个提示,不发送任何内容
|
|
325
|
-
buffer = '';
|
|
326
|
-
return;
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
lastResponseTime = now;
|
|
330
|
-
setTimeout(() => {
|
|
331
|
-
if (child.stdin && !child.stdin.destroyed) {
|
|
332
|
-
log.auto(rule.desc);
|
|
333
|
-
child.stdin.write(rule.response + '\n');
|
|
334
|
-
}
|
|
335
|
-
}, rule.delay);
|
|
336
|
-
|
|
337
|
-
buffer = ''; // 清空缓冲区,避免重复匹配
|
|
338
|
-
break;
|
|
339
|
-
}
|
|
340
|
-
}
|
|
238
|
+
async function runOnboard(cliName) {
|
|
239
|
+
console.log(colors.bold(colors.cyan('\n[2/2] 运行配置向导\n')));
|
|
341
240
|
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
buffer = buffer.slice(-1000);
|
|
345
|
-
}
|
|
346
|
-
};
|
|
241
|
+
// 显示指引
|
|
242
|
+
showInstallGuide();
|
|
347
243
|
|
|
348
|
-
|
|
349
|
-
child.stdout.on('data', processOutput);
|
|
350
|
-
}
|
|
351
|
-
if (child.stderr) {
|
|
352
|
-
child.stderr.on('data', processOutput);
|
|
353
|
-
}
|
|
244
|
+
await waitForEnter('准备好了吗?按回车开始配置...');
|
|
354
245
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
resolve();
|
|
359
|
-
} else {
|
|
360
|
-
// 非零退出码不一定是错误,可能是用户中断
|
|
361
|
-
log.warn(`onboard 退出码: ${code}`);
|
|
362
|
-
resolve();
|
|
363
|
-
}
|
|
364
|
-
});
|
|
246
|
+
console.log(colors.gray('\n' + '-'.repeat(60)));
|
|
247
|
+
console.log(colors.gray('以下是官方 openclaw onboard 界面:'));
|
|
248
|
+
console.log(colors.gray('-'.repeat(60) + '\n'));
|
|
365
249
|
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
250
|
+
// 直接运行官方 onboard,完全交互
|
|
251
|
+
const result = spawnSync(cliName, ['onboard', '--install-daemon'], {
|
|
252
|
+
stdio: 'inherit',
|
|
253
|
+
shell: true,
|
|
369
254
|
});
|
|
255
|
+
|
|
256
|
+
console.log(colors.gray('\n' + '-'.repeat(60)));
|
|
257
|
+
|
|
258
|
+
if (result.status !== 0) {
|
|
259
|
+
log.warn(`onboard 退出码: ${result.status}`);
|
|
260
|
+
log.hint('如果配置未完成,可以手动运行: ' + cliName + ' onboard');
|
|
261
|
+
}
|
|
370
262
|
}
|
|
371
263
|
|
|
372
264
|
// ============ 更新 ============
|
|
373
265
|
|
|
374
266
|
async function updateOpenClaw(cliName) {
|
|
375
|
-
log.
|
|
267
|
+
console.log(colors.bold(colors.cyan('\n检查更新...\n')));
|
|
376
268
|
|
|
377
269
|
const currentResult = safeExec(`${cliName} --version`);
|
|
378
270
|
const currentVersion = currentResult.ok ? currentResult.output : '未知';
|
|
379
|
-
console.log(
|
|
271
|
+
console.log(`当前版本: ${colors.yellow(currentVersion)}`);
|
|
380
272
|
|
|
381
|
-
|
|
382
|
-
|
|
273
|
+
const useSudo = needsSudo();
|
|
274
|
+
|
|
275
|
+
if (useSudo) {
|
|
276
|
+
console.log(colors.yellow('\n请运行以下命令更新:'));
|
|
277
|
+
console.log(colors.green(` sudo npm install -g ${cliName}@latest`));
|
|
278
|
+
console.log(colors.green(` ${cliName} gateway restart\n`));
|
|
279
|
+
} else {
|
|
280
|
+
console.log(colors.gray('\n正在更新...\n'));
|
|
281
|
+
spawnSync('npm', ['install', '-g', `${cliName}@latest`], {
|
|
383
282
|
stdio: 'inherit',
|
|
384
283
|
shell: true,
|
|
385
284
|
});
|
|
386
285
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
const newResult = safeExec(`${cliName} --version`);
|
|
390
|
-
const newVersion = newResult.ok ? newResult.output : '未知';
|
|
391
|
-
if (newVersion !== currentVersion) {
|
|
392
|
-
log.success(`更新完成: ${currentVersion} → ${newVersion}`);
|
|
393
|
-
} else {
|
|
394
|
-
log.success(`已是最新版本: ${newVersion}`);
|
|
395
|
-
}
|
|
286
|
+
const newResult = safeExec(`${cliName} --version`);
|
|
287
|
+
const newVersion = newResult.ok ? newResult.output : '未知';
|
|
396
288
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
log.hint(`手动更新: npm install -g ${cliName}@latest`);
|
|
408
|
-
}
|
|
409
|
-
resolve();
|
|
410
|
-
});
|
|
411
|
-
});
|
|
289
|
+
if (newVersion !== currentVersion) {
|
|
290
|
+
log.success(`更新完成: ${currentVersion} → ${newVersion}`);
|
|
291
|
+
} else {
|
|
292
|
+
log.success(`已是最新版本: ${newVersion}`);
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
console.log(colors.gray('\n重启 Gateway...'));
|
|
296
|
+
safeExec(`${cliName} gateway restart`);
|
|
297
|
+
log.success('Gateway 已重启');
|
|
298
|
+
}
|
|
412
299
|
}
|
|
413
300
|
|
|
414
301
|
// ============ 完成信息 ============
|
|
415
302
|
|
|
416
303
|
function showCompletionInfo(cliName) {
|
|
417
|
-
console.log(colors.bold(colors.green('\n
|
|
418
|
-
console.log(colors.bold(colors.green('✅ OpenClaw 安装完成!')));
|
|
419
|
-
console.log(colors.bold(colors.green('
|
|
304
|
+
console.log(colors.bold(colors.green('\n' + '='.repeat(60))));
|
|
305
|
+
console.log(colors.bold(colors.green(' ✅ OpenClaw 安装完成!')));
|
|
306
|
+
console.log(colors.bold(colors.green('='.repeat(60))));
|
|
420
307
|
|
|
421
|
-
console.log(colors.cyan('\n下一步 - 配置 AI
|
|
308
|
+
console.log(colors.cyan('\n下一步 - 配置 AI 模型(必须):'));
|
|
422
309
|
console.log(` ${colors.yellow('npx openclawapi@latest preset-claude')}`);
|
|
423
310
|
|
|
424
311
|
console.log(colors.cyan('\n常用命令:'));
|
|
@@ -427,9 +314,9 @@ function showCompletionInfo(cliName) {
|
|
|
427
314
|
console.log(` 重启服务: ${colors.yellow(`${cliName} gateway restart`)}`);
|
|
428
315
|
console.log(` 诊断问题: ${colors.yellow(`${cliName} doctor`)}`);
|
|
429
316
|
|
|
430
|
-
console.log(colors.cyan('\n
|
|
317
|
+
console.log(colors.cyan('\n配置聊天渠道(可选):'));
|
|
431
318
|
console.log(` Discord: ${colors.yellow('npx openclawdc')}`);
|
|
432
|
-
console.log(` 飞书: ${colors.yellow('npx
|
|
319
|
+
console.log(` 飞书: ${colors.yellow('npx openclaw-chat-cn@latest feishu')}`);
|
|
433
320
|
|
|
434
321
|
console.log('');
|
|
435
322
|
}
|
|
@@ -444,7 +331,7 @@ async function main() {
|
|
|
444
331
|
process.exit(0);
|
|
445
332
|
}
|
|
446
333
|
|
|
447
|
-
console.log(colors.bold(colors.cyan('\n🦞 OpenClaw
|
|
334
|
+
console.log(colors.bold(colors.cyan('\n🦞 OpenClaw 安装向导\n')));
|
|
448
335
|
|
|
449
336
|
// 检查 Node 版本
|
|
450
337
|
checkNodeVersion();
|
|
@@ -464,10 +351,18 @@ async function main() {
|
|
|
464
351
|
if (options.reinstall) {
|
|
465
352
|
log.info('\n卸载现有安装...');
|
|
466
353
|
safeExec(`${existing.name} gateway stop`);
|
|
467
|
-
|
|
468
|
-
if (
|
|
469
|
-
|
|
470
|
-
log.
|
|
354
|
+
|
|
355
|
+
if (needsSudo()) {
|
|
356
|
+
console.log(colors.yellow('\n请运行以下命令卸载:'));
|
|
357
|
+
console.log(colors.green(` sudo npm uninstall -g ${existing.name}`));
|
|
358
|
+
console.log(colors.green(` rm -rf ~/.openclaw ~/.clawdbot\n`));
|
|
359
|
+
await waitForEnter('卸载完成后按回车继续...');
|
|
360
|
+
} else {
|
|
361
|
+
spawnSync('npm', ['uninstall', '-g', existing.name], { stdio: 'inherit', shell: true });
|
|
362
|
+
if (existing.configDir && existsSync(existing.configDir)) {
|
|
363
|
+
rmSync(existing.configDir, { recursive: true, force: true });
|
|
364
|
+
}
|
|
365
|
+
log.success('卸载完成');
|
|
471
366
|
}
|
|
472
367
|
} else {
|
|
473
368
|
console.log(colors.cyan('\n已安装,可选操作:'));
|
|
@@ -479,22 +374,14 @@ async function main() {
|
|
|
479
374
|
}
|
|
480
375
|
}
|
|
481
376
|
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
const cliName = await installOpenClaw();
|
|
485
|
-
|
|
486
|
-
// 运行 onboard
|
|
487
|
-
await runOnboardWithAutoResponse(cliName, options);
|
|
377
|
+
// 安装 CLI
|
|
378
|
+
const cliName = await installOpenClaw();
|
|
488
379
|
|
|
489
|
-
|
|
490
|
-
|
|
380
|
+
// 运行 onboard
|
|
381
|
+
await runOnboard(cliName);
|
|
491
382
|
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
'检查网络连接',
|
|
495
|
-
'手动安装: npm install -g openclaw && openclaw onboard --install-daemon',
|
|
496
|
-
]);
|
|
497
|
-
}
|
|
383
|
+
// 显示完成信息
|
|
384
|
+
showCompletionInfo(cliName);
|
|
498
385
|
}
|
|
499
386
|
|
|
500
387
|
main().catch((e) => {
|
package/install.sh
CHANGED
package/package.json
CHANGED
|
@@ -51,7 +51,7 @@ irm https://unpkg.com/openclawsetup@latest/install.ps1 | iex
|
|
|
51
51
|
|
|
52
52
|
**为什么跳过模型和渠道?**
|
|
53
53
|
- 模型配置:后续用 `npx openclawapi` 单独配置,更灵活
|
|
54
|
-
- 渠道配置:后续用 `npx openclawdc`(Discord)或 `npx
|
|
54
|
+
- 渠道配置:后续用 `npx openclawdc`(Discord)或 `npx openclaw-chat-cn@latest feishu`(飞书)配置
|
|
55
55
|
|
|
56
56
|
## 已安装用户:更新或重装
|
|
57
57
|
|
|
@@ -78,8 +78,8 @@ npx openclawsetup@latest --reinstall
|
|
|
78
78
|
|
|
79
79
|
3. **配置聊天渠道(可选)**
|
|
80
80
|
```bash
|
|
81
|
-
npx openclawdc
|
|
82
|
-
npx
|
|
81
|
+
npx openclawdc # Discord
|
|
82
|
+
npx openclaw-chat-cn@latest feishu # 飞书
|
|
83
83
|
```
|
|
84
84
|
|
|
85
85
|
## 常见问题
|