openclawsetup 2.1.1 → 2.1.3
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 +14 -4
- package/bin/cli.mjs +267 -30
- package/package.json +1 -1
- package//344/275/277/347/224/250/350/257/264/346/230/216.md +15 -6
package/README.md
CHANGED
|
@@ -72,6 +72,7 @@ npx openclawsetup@latest --auto
|
|
|
72
72
|
| `--with-channel` | 检测到渠道配置时暂停自动选择 |
|
|
73
73
|
| `--update` | 检查并更新已安装的 OpenClaw |
|
|
74
74
|
| `--reinstall` | 卸载后重新安装(清除配置) |
|
|
75
|
+
| `--uninstall` | 卸载 OpenClaw |
|
|
75
76
|
| `--help, -h` | 显示帮助信息 |
|
|
76
77
|
|
|
77
78
|
## 使用示例
|
|
@@ -100,6 +101,9 @@ npx openclawsetup@latest --update
|
|
|
100
101
|
|
|
101
102
|
# 卸载后重新安装(会清除配置)
|
|
102
103
|
npx openclawsetup@latest --reinstall
|
|
104
|
+
|
|
105
|
+
# 直接卸载
|
|
106
|
+
npx openclawsetup@latest --uninstall
|
|
103
107
|
```
|
|
104
108
|
|
|
105
109
|
## 安装后
|
|
@@ -114,6 +118,12 @@ npx openclawapi@latest preset-claude
|
|
|
114
118
|
npx openclawapi@latest
|
|
115
119
|
```
|
|
116
120
|
|
|
121
|
+
## Dashboard 访问
|
|
122
|
+
|
|
123
|
+
安装完成后会自动显示:
|
|
124
|
+
- 本机访问地址(含 token)
|
|
125
|
+
- 云服务器场景下的 SSH 隧道命令与说明
|
|
126
|
+
|
|
117
127
|
## 配置聊天渠道
|
|
118
128
|
|
|
119
129
|
```bash
|
|
@@ -143,11 +153,11 @@ openclaw doctor
|
|
|
143
153
|
## 工作原理
|
|
144
154
|
|
|
145
155
|
1. 安装 `openclaw` npm 包
|
|
146
|
-
2.
|
|
147
|
-
3.
|
|
148
|
-
4.
|
|
156
|
+
2. 优先检测官方 `openclaw onboard` 的非交互参数(可用时直接自动完成)
|
|
157
|
+
3. 如不支持,则使用 `node-pty` 创建伪终端进行自动应答(按任意键接管)
|
|
158
|
+
4. 若自动应答不可用,退回手动模式
|
|
149
159
|
|
|
150
|
-
##
|
|
160
|
+
## 自动应答规则(兜底)
|
|
151
161
|
|
|
152
162
|
| 提示类型 | 自动选择 |
|
|
153
163
|
|---------|---------|
|
package/bin/cli.mjs
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
import { execSync, spawnSync } from 'child_process';
|
|
14
|
-
import { existsSync, accessSync, constants as fsConstants, rmSync } from 'fs';
|
|
14
|
+
import { existsSync, accessSync, constants as fsConstants, rmSync, readFileSync } from 'fs';
|
|
15
15
|
import { homedir, platform } from 'os';
|
|
16
16
|
import { join } from 'path';
|
|
17
17
|
import { createInterface } from 'readline';
|
|
@@ -51,6 +51,7 @@ function parseArgs() {
|
|
|
51
51
|
return {
|
|
52
52
|
update: args.includes('--update'),
|
|
53
53
|
reinstall: args.includes('--reinstall'),
|
|
54
|
+
uninstall: args.includes('--uninstall'),
|
|
54
55
|
manual: args.includes('--manual'),
|
|
55
56
|
auto: args.includes('--auto'),
|
|
56
57
|
withModel: args.includes('--with-model'),
|
|
@@ -67,6 +68,7 @@ ${colors.cyan('用法:')}
|
|
|
67
68
|
npx openclawsetup 带中文指引的安装
|
|
68
69
|
npx openclawsetup --update 更新已安装的 OpenClaw
|
|
69
70
|
npx openclawsetup --reinstall 卸载后重新安装
|
|
71
|
+
npx openclawsetup --uninstall 卸载 OpenClaw
|
|
70
72
|
npx openclawsetup --manual 完全手动模式
|
|
71
73
|
npx openclawsetup --auto 强制自动模式
|
|
72
74
|
|
|
@@ -139,6 +141,65 @@ function detectExistingInstall() {
|
|
|
139
141
|
return { installed: false };
|
|
140
142
|
}
|
|
141
143
|
|
|
144
|
+
function getConfigInfo() {
|
|
145
|
+
const home = homedir();
|
|
146
|
+
const configs = [
|
|
147
|
+
{ dir: join(home, '.openclaw'), file: 'openclaw.json' },
|
|
148
|
+
{ dir: join(home, '.clawdbot'), file: 'clawdbot.json' },
|
|
149
|
+
];
|
|
150
|
+
|
|
151
|
+
for (const cfg of configs) {
|
|
152
|
+
const configPath = join(cfg.dir, cfg.file);
|
|
153
|
+
if (!existsSync(configPath)) continue;
|
|
154
|
+
try {
|
|
155
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
156
|
+
const json = JSON.parse(raw);
|
|
157
|
+
const token = json.token || json.gatewayToken || '';
|
|
158
|
+
const port = Number(json.port || json.gatewayPort || 18789);
|
|
159
|
+
const bind = json.bind || json.gatewayBind || '';
|
|
160
|
+
return { configDir: cfg.dir, configPath, token, port, bind, raw };
|
|
161
|
+
} catch {
|
|
162
|
+
try {
|
|
163
|
+
const raw = readFileSync(configPath, 'utf8');
|
|
164
|
+
const tokenMatch = raw.match(/"token"\\s*:\\s*"([^"]+)"/i);
|
|
165
|
+
const portMatch = raw.match(/"port"\\s*:\\s*(\\d+)/i);
|
|
166
|
+
const bindMatch = raw.match(/"bind"\\s*:\\s*"([^"]+)"/i);
|
|
167
|
+
return {
|
|
168
|
+
configDir: cfg.dir,
|
|
169
|
+
configPath,
|
|
170
|
+
token: tokenMatch ? tokenMatch[1] : '',
|
|
171
|
+
port: portMatch ? Number(portMatch[1]) : 18789,
|
|
172
|
+
bind: bindMatch ? bindMatch[1] : '',
|
|
173
|
+
raw,
|
|
174
|
+
};
|
|
175
|
+
} catch {
|
|
176
|
+
return { configDir: cfg.dir, configPath, token: '', port: 18789, bind: '', raw: '' };
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return { configDir: '', configPath: '', token: '', port: 18789, bind: '', raw: '' };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function detectVps() {
|
|
184
|
+
return existsSync('/etc/cloud') || existsSync('/var/lib/cloud') || existsSync('/sys/hypervisor');
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function getServerIp() {
|
|
188
|
+
const candidates = [
|
|
189
|
+
'curl -s --connect-timeout 2 ifconfig.me',
|
|
190
|
+
'curl -s --connect-timeout 2 icanhazip.com',
|
|
191
|
+
'curl -s --connect-timeout 2 ipinfo.io/ip',
|
|
192
|
+
"hostname -I 2>/dev/null | awk '{print $1}'",
|
|
193
|
+
];
|
|
194
|
+
for (const cmd of candidates) {
|
|
195
|
+
const result = safeExec(cmd);
|
|
196
|
+
if (result.ok && result.output) {
|
|
197
|
+
return result.output.trim();
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
return '';
|
|
201
|
+
}
|
|
202
|
+
|
|
142
203
|
// ============ 安装指引 ============
|
|
143
204
|
|
|
144
205
|
function showInstallGuide() {
|
|
@@ -184,11 +245,82 @@ function stripAnsi(input) {
|
|
|
184
245
|
.replace(/\r/g, '\n');
|
|
185
246
|
}
|
|
186
247
|
|
|
187
|
-
function
|
|
248
|
+
function getOnboardHelp(cliName) {
|
|
249
|
+
const candidates = [
|
|
250
|
+
`${cliName} onboard --help`,
|
|
251
|
+
`${cliName} onboard -h`,
|
|
252
|
+
`${cliName} help onboard`,
|
|
253
|
+
];
|
|
254
|
+
|
|
255
|
+
for (const cmd of candidates) {
|
|
256
|
+
const result = safeExec(cmd, { stdio: 'pipe' });
|
|
257
|
+
const output = result.ok ? result.output : '';
|
|
258
|
+
if (output && output.length > 20) {
|
|
259
|
+
return output;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
return '';
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function buildOnboardArgsFromHelp(helpText, options) {
|
|
266
|
+
const help = helpText.toLowerCase();
|
|
267
|
+
const args = [];
|
|
268
|
+
const enabled = [];
|
|
269
|
+
|
|
270
|
+
const pickFlag = (flags) => flags.find((flag) => help.includes(flag));
|
|
271
|
+
|
|
272
|
+
const yesFlag = pickFlag(['--yes', '--assume-yes', '--accept', '--agree']);
|
|
273
|
+
if (yesFlag) {
|
|
274
|
+
args.push(yesFlag);
|
|
275
|
+
enabled.push('yes');
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const installDaemonFlag = pickFlag(['--install-daemon', '--daemon']);
|
|
279
|
+
if (installDaemonFlag) {
|
|
280
|
+
args.push(installDaemonFlag);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
if (help.includes('--quickstart')) {
|
|
284
|
+
args.push('--quickstart');
|
|
285
|
+
enabled.push('quickstart');
|
|
286
|
+
} else if (help.includes('--mode') && (help.includes('quickstart') || help.includes('quick start'))) {
|
|
287
|
+
args.push('--mode', 'quickstart');
|
|
288
|
+
enabled.push('quickstart');
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
if (!options.withModel) {
|
|
292
|
+
const skipModelFlag = pickFlag(['--skip-model', '--no-model', '--skip-provider']);
|
|
293
|
+
if (skipModelFlag) {
|
|
294
|
+
args.push(skipModelFlag);
|
|
295
|
+
enabled.push('skip-model');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
if (!options.withChannel) {
|
|
300
|
+
const skipChannelFlag = pickFlag(['--skip-channel', '--no-channel', '--skip-channels']);
|
|
301
|
+
if (skipChannelFlag) {
|
|
302
|
+
args.push(skipChannelFlag);
|
|
303
|
+
enabled.push('skip-channel');
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (help.includes('--ui') && (help.includes('web') || help.includes('dashboard'))) {
|
|
308
|
+
args.push('--ui', 'web');
|
|
309
|
+
enabled.push('ui-web');
|
|
310
|
+
} else if (help.includes('--web')) {
|
|
311
|
+
args.push('--web');
|
|
312
|
+
enabled.push('ui-web');
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const autoCapable = enabled.length > 0 || Boolean(yesFlag);
|
|
316
|
+
return { args, enabled, autoCapable };
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
function getOnboardCommand(cliName, onboardArgs = ['onboard', '--install-daemon']) {
|
|
188
320
|
if (platform() === 'win32') {
|
|
189
|
-
return { file: 'cmd.exe', args: ['/c', cliName,
|
|
321
|
+
return { file: 'cmd.exe', args: ['/c', cliName, ...onboardArgs] };
|
|
190
322
|
}
|
|
191
|
-
return { file: cliName, args:
|
|
323
|
+
return { file: cliName, args: onboardArgs };
|
|
192
324
|
}
|
|
193
325
|
|
|
194
326
|
function waitForEnter(message) {
|
|
@@ -276,21 +408,31 @@ async function runOnboard(cliName) {
|
|
|
276
408
|
let usedAuto = false;
|
|
277
409
|
|
|
278
410
|
if (preferAuto) {
|
|
279
|
-
const
|
|
280
|
-
if (
|
|
411
|
+
const flagResult = runOnboardFlags(cliName, options);
|
|
412
|
+
if (flagResult.ran) {
|
|
281
413
|
usedAuto = true;
|
|
282
414
|
console.log(colors.gray('\n' + '-'.repeat(60)));
|
|
283
|
-
if (
|
|
284
|
-
log.warn(`onboard 退出码: ${
|
|
415
|
+
if (!flagResult.ok) {
|
|
416
|
+
log.warn(`onboard 退出码: ${flagResult.exitCode}`);
|
|
285
417
|
log.hint('如果配置未完成,可以手动运行: ' + cliName + ' onboard');
|
|
286
418
|
}
|
|
287
|
-
} else if (options.auto) {
|
|
288
|
-
log.error('自动模式不可用,已退出');
|
|
289
|
-
log.hint(autoResult.reason || '请尝试 --manual');
|
|
290
|
-
process.exit(1);
|
|
291
419
|
} else {
|
|
292
|
-
|
|
293
|
-
|
|
420
|
+
const autoResult = await runOnboardAuto(cliName, options);
|
|
421
|
+
if (autoResult.ok) {
|
|
422
|
+
usedAuto = true;
|
|
423
|
+
console.log(colors.gray('\n' + '-'.repeat(60)));
|
|
424
|
+
if (autoResult.exitCode !== 0) {
|
|
425
|
+
log.warn(`onboard 退出码: ${autoResult.exitCode}`);
|
|
426
|
+
log.hint('如果配置未完成,可以手动运行: ' + cliName + ' onboard');
|
|
427
|
+
}
|
|
428
|
+
} else if (options.auto) {
|
|
429
|
+
log.error('自动模式不可用,已退出');
|
|
430
|
+
log.hint(autoResult.reason || '请尝试 --manual');
|
|
431
|
+
process.exit(1);
|
|
432
|
+
} else {
|
|
433
|
+
log.warn('自动模式不可用,已切换为手动模式');
|
|
434
|
+
log.hint(autoResult.reason || '未能启用自动应答');
|
|
435
|
+
}
|
|
294
436
|
}
|
|
295
437
|
}
|
|
296
438
|
|
|
@@ -311,6 +453,30 @@ function runOnboardManual(cliName) {
|
|
|
311
453
|
});
|
|
312
454
|
}
|
|
313
455
|
|
|
456
|
+
function runOnboardFlags(cliName, options) {
|
|
457
|
+
const help = getOnboardHelp(cliName);
|
|
458
|
+
if (!help) {
|
|
459
|
+
return { ran: false, reason: '未检测到 onboard 帮助信息' };
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
const { args, enabled, autoCapable } = buildOnboardArgsFromHelp(help, options);
|
|
463
|
+
if (!autoCapable) {
|
|
464
|
+
return { ran: false, reason: '未检测到可用的非交互参数' };
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
log.info('检测到官方非交互参数,优先使用原生方式安装');
|
|
468
|
+
if (enabled.length) {
|
|
469
|
+
log.hint(`已启用: ${enabled.join(', ')}`);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
const { file, args: spawnArgs } = getOnboardCommand(cliName, ['onboard', ...args]);
|
|
473
|
+
const result = spawnSync(file, spawnArgs, {
|
|
474
|
+
stdio: 'inherit',
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
return { ran: true, ok: result.status === 0, exitCode: result.status ?? 1 };
|
|
478
|
+
}
|
|
479
|
+
|
|
314
480
|
function createAutoResponder(term, options) {
|
|
315
481
|
const lastSent = new Map();
|
|
316
482
|
let autoStopped = false;
|
|
@@ -344,7 +510,11 @@ function createAutoResponder(term, options) {
|
|
|
344
510
|
const tail = text.slice(-800);
|
|
345
511
|
|
|
346
512
|
if (tail.includes('do you want to continue') || tail.includes('continue?') || tail.includes('是否继续')) {
|
|
347
|
-
|
|
513
|
+
if (tail.includes('yes') && tail.includes('no')) {
|
|
514
|
+
send('confirm-select', '\x1b[A\x1b[D\r');
|
|
515
|
+
} else {
|
|
516
|
+
send('confirm', 'y\r');
|
|
517
|
+
}
|
|
348
518
|
return;
|
|
349
519
|
}
|
|
350
520
|
|
|
@@ -567,6 +737,8 @@ function showCompletionInfo(cliName) {
|
|
|
567
737
|
console.log(colors.cyan('\n下一步 - 配置 AI 模型(必须):'));
|
|
568
738
|
console.log(` ${colors.yellow('npx openclawapi@latest preset-claude')}`);
|
|
569
739
|
|
|
740
|
+
showDashboardAccessInfo();
|
|
741
|
+
|
|
570
742
|
console.log(colors.cyan('\n常用命令:'));
|
|
571
743
|
console.log(` 查看状态: ${colors.yellow(`${cliName} status`)}`);
|
|
572
744
|
console.log(` 查看日志: ${colors.yellow(`${cliName} gateway logs`)}`);
|
|
@@ -580,6 +752,78 @@ function showCompletionInfo(cliName) {
|
|
|
580
752
|
console.log('');
|
|
581
753
|
}
|
|
582
754
|
|
|
755
|
+
function showDashboardAccessInfo() {
|
|
756
|
+
const config = getConfigInfo();
|
|
757
|
+
const port = config.port || 18789;
|
|
758
|
+
const token = config.token || '<你的token>';
|
|
759
|
+
const dashboardUrl = `http://127.0.0.1:${port}/?token=${token}`;
|
|
760
|
+
|
|
761
|
+
if (detectVps()) {
|
|
762
|
+
const serverIp = getServerIp() || '<服务器IP>';
|
|
763
|
+
const user = process.env.USER || 'root';
|
|
764
|
+
|
|
765
|
+
console.log(colors.cyan('\n📡 Dashboard 访问(云服务器):'));
|
|
766
|
+
console.log(colors.gray(' Gateway 默认绑定 127.0.0.1,外部无法直接访问(安全设计)'));
|
|
767
|
+
console.log('');
|
|
768
|
+
console.log(colors.yellow(' 方式一:SSH 隧道(推荐,安全)'));
|
|
769
|
+
console.log(colors.gray(' 在本地电脑执行以下命令,保持终端窗口打开:'));
|
|
770
|
+
console.log(` ${colors.green(`ssh -N -L ${port}:127.0.0.1:${port} ${user}@${serverIp}`)}`);
|
|
771
|
+
console.log(colors.gray(' 然后在本地浏览器访问:'));
|
|
772
|
+
console.log(` ${colors.green(dashboardUrl)}`);
|
|
773
|
+
console.log('');
|
|
774
|
+
console.log(colors.yellow(' 方式二:直接暴露端口(不推荐,有安全风险)'));
|
|
775
|
+
console.log(colors.gray(' 1. 修改配置文件 ~/.openclaw/openclaw.json'));
|
|
776
|
+
console.log(colors.gray(' 将 "bind": "loopback" 改为 "bind": "all"'));
|
|
777
|
+
console.log(colors.gray(` 2. 在云服务器控制台开放端口 ${port}`));
|
|
778
|
+
console.log(colors.gray(' 3. 重启 Gateway:openclaw gateway restart'));
|
|
779
|
+
console.log(colors.gray(` 4. 访问:http://${serverIp}:${port}/?token=...`));
|
|
780
|
+
} else {
|
|
781
|
+
console.log(colors.cyan('\nDashboard 访问:'));
|
|
782
|
+
console.log(` ${colors.yellow(dashboardUrl)}`);
|
|
783
|
+
}
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
async function uninstallOpenClaw(existing) {
|
|
787
|
+
const cliName = existing?.name || 'openclaw';
|
|
788
|
+
const useSudo = needsSudo();
|
|
789
|
+
|
|
790
|
+
console.log(colors.cyan('\n开始卸载...'));
|
|
791
|
+
|
|
792
|
+
safeExec(`${cliName} gateway stop`);
|
|
793
|
+
|
|
794
|
+
if (useSudo) {
|
|
795
|
+
console.log(colors.yellow('\n请运行以下命令卸载:'));
|
|
796
|
+
console.log(colors.green(` sudo npm uninstall -g ${cliName}`));
|
|
797
|
+
console.log(colors.green(' rm -rf ~/.openclaw ~/.clawdbot\n'));
|
|
798
|
+
await waitForEnter('卸载完成后按回车继续...');
|
|
799
|
+
} else {
|
|
800
|
+
spawnSync('npm', ['uninstall', '-g', cliName], { stdio: 'inherit', shell: true });
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
const config = getConfigInfo();
|
|
804
|
+
if (config.configDir && existsSync(config.configDir)) {
|
|
805
|
+
rmSync(config.configDir, { recursive: true, force: true });
|
|
806
|
+
}
|
|
807
|
+
const otherDir = config.configDir?.endsWith('.openclaw') ? join(homedir(), '.clawdbot') : join(homedir(), '.openclaw');
|
|
808
|
+
if (existsSync(otherDir)) {
|
|
809
|
+
rmSync(otherDir, { recursive: true, force: true });
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (platform() === 'darwin') {
|
|
813
|
+
const plist = join(homedir(), 'Library/LaunchAgents/com.openclaw.gateway.plist');
|
|
814
|
+
safeExec(`launchctl unload "${plist}"`);
|
|
815
|
+
if (existsSync(plist)) rmSync(plist, { force: true });
|
|
816
|
+
} else if (platform() === 'linux') {
|
|
817
|
+
safeExec('systemctl --user stop openclaw');
|
|
818
|
+
safeExec('systemctl --user disable openclaw');
|
|
819
|
+
const service = join(homedir(), '.config/systemd/user/openclaw.service');
|
|
820
|
+
if (existsSync(service)) rmSync(service, { force: true });
|
|
821
|
+
safeExec('systemctl --user daemon-reload');
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
log.success('卸载完成');
|
|
825
|
+
}
|
|
826
|
+
|
|
583
827
|
// ============ 主函数 ============
|
|
584
828
|
|
|
585
829
|
async function main() {
|
|
@@ -607,27 +851,20 @@ async function main() {
|
|
|
607
851
|
process.exit(0);
|
|
608
852
|
}
|
|
609
853
|
|
|
854
|
+
if (options.uninstall) {
|
|
855
|
+
await uninstallOpenClaw(existing);
|
|
856
|
+
process.exit(0);
|
|
857
|
+
}
|
|
858
|
+
|
|
610
859
|
if (options.reinstall) {
|
|
611
|
-
|
|
612
|
-
safeExec(`${existing.name} gateway stop`);
|
|
613
|
-
|
|
614
|
-
if (needsSudo()) {
|
|
615
|
-
console.log(colors.yellow('\n请运行以下命令卸载:'));
|
|
616
|
-
console.log(colors.green(` sudo npm uninstall -g ${existing.name}`));
|
|
617
|
-
console.log(colors.green(` rm -rf ~/.openclaw ~/.clawdbot\n`));
|
|
618
|
-
await waitForEnter('卸载完成后按回车继续...');
|
|
619
|
-
} else {
|
|
620
|
-
spawnSync('npm', ['uninstall', '-g', existing.name], { stdio: 'inherit', shell: true });
|
|
621
|
-
if (existing.configDir && existsSync(existing.configDir)) {
|
|
622
|
-
rmSync(existing.configDir, { recursive: true, force: true });
|
|
623
|
-
}
|
|
624
|
-
log.success('卸载完成');
|
|
625
|
-
}
|
|
860
|
+
await uninstallOpenClaw(existing);
|
|
626
861
|
} else {
|
|
627
862
|
console.log(colors.cyan('\n已安装,可选操作:'));
|
|
628
863
|
console.log(` 更新: ${colors.yellow('npx openclawsetup --update')}`);
|
|
629
864
|
console.log(` 重装: ${colors.yellow('npx openclawsetup --reinstall')}`);
|
|
865
|
+
console.log(` 卸载: ${colors.yellow('npx openclawsetup --uninstall')}`);
|
|
630
866
|
console.log(` 配置模型: ${colors.yellow('npx openclawapi@latest preset-claude')}`);
|
|
867
|
+
showDashboardAccessInfo();
|
|
631
868
|
console.log('');
|
|
632
869
|
process.exit(0);
|
|
633
870
|
}
|
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ npx openclawsetup@latest
|
|
|
11
11
|
```
|
|
12
12
|
|
|
13
13
|
你会看到官方的 `openclaw onboard` 界面,但所有选项会自动选择推荐配置(按任意键可接管)。
|
|
14
|
+
默认优先使用官方提供的非交互参数,若版本不支持则使用自动应答兜底。
|
|
14
15
|
|
|
15
16
|
### 方式二:手动模式
|
|
16
17
|
|
|
@@ -73,6 +74,9 @@ npx openclawsetup@latest --update
|
|
|
73
74
|
|
|
74
75
|
# 卸载后重新安装(会清除配置)
|
|
75
76
|
npx openclawsetup@latest --reinstall
|
|
77
|
+
|
|
78
|
+
# 直接卸载
|
|
79
|
+
npx openclawsetup@latest --uninstall
|
|
76
80
|
```
|
|
77
81
|
|
|
78
82
|
## 安装完成后
|
|
@@ -83,10 +87,9 @@ npx openclawsetup@latest --reinstall
|
|
|
83
87
|
```
|
|
84
88
|
|
|
85
89
|
2. **访问 Dashboard**
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
Token 在安装完成时会显示。
|
|
90
|
+
安装完成后会自动显示:
|
|
91
|
+
- 本机访问地址(含 token)
|
|
92
|
+
- 云服务器场景下的 SSH 隧道命令与说明
|
|
90
93
|
|
|
91
94
|
3. **配置聊天渠道(可选)**
|
|
92
95
|
```bash
|
|
@@ -154,7 +157,7 @@ curl -fsSL https://unpkg.com/openclawsetup@latest/install.sh | bash
|
|
|
154
157
|
|
|
155
158
|
### 自动选择没有生效
|
|
156
159
|
**现象**:安装过程中需要手动输入
|
|
157
|
-
|
|
160
|
+
**原因**:可能是终端环境问题、无 TTY,或自动应答依赖不可用(node-pty 未安装成功)
|
|
158
161
|
**解决**:
|
|
159
162
|
1. 使用手动模式:
|
|
160
163
|
```bash
|
|
@@ -164,7 +167,13 @@ curl -fsSL https://unpkg.com/openclawsetup@latest/install.sh | bash
|
|
|
164
167
|
```bash
|
|
165
168
|
npx openclawsetup@latest --auto
|
|
166
169
|
```
|
|
167
|
-
3.
|
|
170
|
+
3. 在 Linux 服务器上补齐编译依赖后再试(Ubuntu 示例):
|
|
171
|
+
```bash
|
|
172
|
+
sudo apt-get update
|
|
173
|
+
sudo apt-get install -y build-essential python3 make g++ pkg-config
|
|
174
|
+
npx openclawsetup@latest --auto
|
|
175
|
+
```
|
|
176
|
+
4. 或直接运行官方命令:
|
|
168
177
|
```bash
|
|
169
178
|
npm install -g openclaw@latest
|
|
170
179
|
openclaw onboard --install-daemon
|