openclawsetup 1.0.3 → 1.0.5
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 +117 -8
- package/package.json +1 -1
package/bin/cli.mjs
CHANGED
|
@@ -195,27 +195,95 @@ function detectExistingInstall() {
|
|
|
195
195
|
return { installed: false };
|
|
196
196
|
}
|
|
197
197
|
|
|
198
|
+
// 带进度动画的命令执行
|
|
199
|
+
function execWithProgress(cmd, message, options = {}) {
|
|
200
|
+
return new Promise((resolve) => {
|
|
201
|
+
const frames = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
202
|
+
let frameIndex = 0;
|
|
203
|
+
let seconds = 0;
|
|
204
|
+
|
|
205
|
+
// 显示进度动画
|
|
206
|
+
const spinner = setInterval(() => {
|
|
207
|
+
process.stdout.write(`\r${colors.cyan(frames[frameIndex])} ${message} ${colors.gray(`(${seconds}s)`)}`);
|
|
208
|
+
frameIndex = (frameIndex + 1) % frames.length;
|
|
209
|
+
}, 100);
|
|
210
|
+
|
|
211
|
+
// 计时器
|
|
212
|
+
const timer = setInterval(() => {
|
|
213
|
+
seconds++;
|
|
214
|
+
}, 1000);
|
|
215
|
+
|
|
216
|
+
// 执行命令
|
|
217
|
+
const child = spawn('sh', ['-c', cmd], {
|
|
218
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
219
|
+
...options,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
let stdout = '';
|
|
223
|
+
let stderr = '';
|
|
224
|
+
|
|
225
|
+
child.stdout?.on('data', (data) => {
|
|
226
|
+
stdout += data.toString();
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
child.stderr?.on('data', (data) => {
|
|
230
|
+
stderr += data.toString();
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
child.on('close', (code) => {
|
|
234
|
+
clearInterval(spinner);
|
|
235
|
+
clearInterval(timer);
|
|
236
|
+
// 清除当前行
|
|
237
|
+
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
238
|
+
|
|
239
|
+
if (code === 0) {
|
|
240
|
+
resolve({ ok: true, output: stdout.trim(), seconds });
|
|
241
|
+
} else {
|
|
242
|
+
resolve({ ok: false, error: stderr || stdout, stderr, stdout, seconds });
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
child.on('error', (err) => {
|
|
247
|
+
clearInterval(spinner);
|
|
248
|
+
clearInterval(timer);
|
|
249
|
+
process.stdout.write('\r' + ' '.repeat(60) + '\r');
|
|
250
|
+
resolve({ ok: false, error: err.message, seconds });
|
|
251
|
+
});
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
198
255
|
// 安装 OpenClaw CLI
|
|
199
|
-
function installOpenClaw() {
|
|
256
|
+
async function installOpenClaw() {
|
|
200
257
|
log.step(1, '安装 OpenClaw CLI...');
|
|
258
|
+
log.detail('正在从 npm 下载,请稍候...');
|
|
259
|
+
|
|
260
|
+
const result = await execWithProgress(
|
|
261
|
+
'npm install -g openclaw@latest',
|
|
262
|
+
'正在安装 openclaw...',
|
|
263
|
+
{ timeout: 300000 }
|
|
264
|
+
);
|
|
201
265
|
|
|
202
|
-
const result = safeExec('npm install -g openclaw@latest', { timeout: 300000 });
|
|
203
266
|
if (!result.ok) {
|
|
204
267
|
// 尝试 clawdbot 作为备选
|
|
205
268
|
log.warn('openclaw 安装失败,尝试 clawdbot...');
|
|
206
|
-
const fallback =
|
|
269
|
+
const fallback = await execWithProgress(
|
|
270
|
+
'npm install -g clawdbot@latest',
|
|
271
|
+
'正在安装 clawdbot...',
|
|
272
|
+
{ timeout: 300000 }
|
|
273
|
+
);
|
|
207
274
|
if (!fallback.ok) {
|
|
208
|
-
exitWithError('NPM_INSTALL_FAILED', result.
|
|
275
|
+
exitWithError('NPM_INSTALL_FAILED', result.error || result.stderr, [
|
|
209
276
|
'检查网络连接',
|
|
210
277
|
'尝试使用代理: npm config set proxy http://proxy:port',
|
|
211
278
|
'手动安装: npm install -g openclaw@latest',
|
|
212
279
|
'检查 npm 权限,可能需要 sudo (Linux/macOS)',
|
|
213
280
|
]);
|
|
214
281
|
}
|
|
282
|
+
log.success(`clawdbot 安装完成 (${fallback.seconds}s)`);
|
|
215
283
|
return 'clawdbot';
|
|
216
284
|
}
|
|
217
285
|
|
|
218
|
-
log.success(
|
|
286
|
+
log.success(`OpenClaw CLI 安装完成 (${result.seconds}s)`);
|
|
219
287
|
return 'openclaw';
|
|
220
288
|
}
|
|
221
289
|
|
|
@@ -451,6 +519,23 @@ function startService(cliName) {
|
|
|
451
519
|
return false;
|
|
452
520
|
}
|
|
453
521
|
|
|
522
|
+
// 检测是否在云服务器/VPS 上运行
|
|
523
|
+
function detectVPS() {
|
|
524
|
+
// 检查常见的云服务器特征
|
|
525
|
+
const indicators = [
|
|
526
|
+
existsSync('/etc/cloud'), // cloud-init
|
|
527
|
+
existsSync('/var/lib/cloud'), // cloud-init data
|
|
528
|
+
existsSync('/sys/hypervisor'), // 虚拟化
|
|
529
|
+
process.env.CLOUD_SHELL === 'true', // Google Cloud Shell
|
|
530
|
+
];
|
|
531
|
+
|
|
532
|
+
// 检查是否有公网 IP(简单检测)
|
|
533
|
+
const result = safeExec('hostname -I 2>/dev/null || hostname -i 2>/dev/null');
|
|
534
|
+
const hasPublicIP = result.ok && result.output && !result.output.startsWith('127.');
|
|
535
|
+
|
|
536
|
+
return indicators.some(Boolean) || hasPublicIP;
|
|
537
|
+
}
|
|
538
|
+
|
|
454
539
|
// 主函数
|
|
455
540
|
async function main() {
|
|
456
541
|
const options = parseArgs();
|
|
@@ -480,7 +565,7 @@ async function main() {
|
|
|
480
565
|
}
|
|
481
566
|
|
|
482
567
|
// 安装 CLI
|
|
483
|
-
const cliName = installOpenClaw();
|
|
568
|
+
const cliName = await installOpenClaw();
|
|
484
569
|
|
|
485
570
|
// 创建配置
|
|
486
571
|
const config = createConfig(options);
|
|
@@ -499,6 +584,9 @@ async function main() {
|
|
|
499
584
|
log.info('跳过服务启动 (--skip-start)');
|
|
500
585
|
}
|
|
501
586
|
|
|
587
|
+
// 检测是否在 VPS 上
|
|
588
|
+
const isVPS = detectVPS();
|
|
589
|
+
|
|
502
590
|
// 完成
|
|
503
591
|
console.log(colors.bold(colors.green('\n========================================')));
|
|
504
592
|
console.log(colors.bold(colors.green('✅ OpenClaw 安装完成!')));
|
|
@@ -509,8 +597,29 @@ async function main() {
|
|
|
509
597
|
console.log(` Gateway 端口: ${colors.yellow(config.port)}`);
|
|
510
598
|
console.log(` Gateway Token: ${colors.yellow(config.token)}`);
|
|
511
599
|
|
|
512
|
-
|
|
513
|
-
|
|
600
|
+
// 根据环境显示不同的访问说明
|
|
601
|
+
if (isVPS) {
|
|
602
|
+
console.log(colors.cyan('\n📡 Dashboard 访问 (云服务器):'));
|
|
603
|
+
console.log(colors.gray(' Gateway 默认绑定 127.0.0.1,外部无法直接访问(安全设计)'));
|
|
604
|
+
console.log('');
|
|
605
|
+
console.log(colors.yellow(' 方式一:SSH 隧道(推荐,安全)'));
|
|
606
|
+
console.log(colors.gray(' 在本地电脑执行以下命令,保持终端窗口打开:'));
|
|
607
|
+
console.log(` ssh -N -L ${config.port}:127.0.0.1:${config.port} root@<服务器IP>`);
|
|
608
|
+
console.log(colors.gray(' 然后在本地浏览器访问:'));
|
|
609
|
+
console.log(` ${colors.green(`http://127.0.0.1:${config.port}/?token=${config.token}`)}`);
|
|
610
|
+
console.log('');
|
|
611
|
+
console.log(colors.yellow(' 方式二:直接暴露端口(不推荐,有安全风险)'));
|
|
612
|
+
console.log(colors.gray(' 1. 修改配置文件 ~/.openclaw/openclaw.json'));
|
|
613
|
+
console.log(colors.gray(' 将 "bind": "loopback" 改为 "bind": "all"'));
|
|
614
|
+
console.log(colors.gray(' 2. 在云服务器控制台开放端口 ' + config.port));
|
|
615
|
+
console.log(colors.gray(' - 腾讯云:安全组 → 入站规则 → 添加 TCP ' + config.port));
|
|
616
|
+
console.log(colors.gray(' - 阿里云:安全组 → 配置规则 → 添加 TCP ' + config.port));
|
|
617
|
+
console.log(colors.gray(' 3. 重启 Gateway:' + cliName + ' gateway restart'));
|
|
618
|
+
console.log(colors.gray(' 4. 访问:http://<服务器IP>:' + config.port + '/?token=...'));
|
|
619
|
+
} else {
|
|
620
|
+
console.log(colors.cyan('\nDashboard 访问:'));
|
|
621
|
+
console.log(` ${colors.yellow(`http://127.0.0.1:${config.port}/?token=${config.token}`)}`);
|
|
622
|
+
}
|
|
514
623
|
|
|
515
624
|
console.log(colors.cyan('\n下一步 - 配置 AI 模型:'));
|
|
516
625
|
console.log(` ${colors.yellow('npx openclawapi@latest preset-claude')}`);
|