create-openclaw-bot 5.1.13 → 5.1.14
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/CHANGELOG.md +11 -1
- package/CHANGELOG.vi.md +11 -1
- package/README.md +11 -14
- package/README.vi.md +11 -14
- package/cli.js +30 -26
- package/docs/install-docker.md +3 -2
- package/docs/install-docker.vi.md +3 -2
- package/package.json +1 -1
- package/setup.js +51 -41
- package/tests/smoke-cli-logic.mjs +45 -7
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Changelog (English)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
## [5.1.14] — 2026-04-08
|
|
5
|
+
|
|
6
|
+
### OpenClaw stability and Docker fixes
|
|
7
|
+
|
|
8
|
+
- Pinned OpenClaw back to `openclaw@2026.4.5` because the update published on `April 8, 2026` is currently broken.
|
|
9
|
+
- Fixed Dockerfile generation for Windows Docker setups to avoid startup failures caused by bad command escaping and invalid `allowedOrigins`.
|
|
10
|
+
- Added guidance to use `Node.js 20` through `24`, and to avoid `Node.js 25` for now for better OpenClaw stability.
|
|
11
|
+
|
|
12
|
+
## [5.1.13] — 2026-04-08
|
|
4
13
|
|
|
5
14
|
### 🐛 macOS Install Fixes & Wizard Stability
|
|
6
15
|
|
|
@@ -403,3 +412,4 @@ All notable changes to this project will be documented in this file.
|
|
|
403
412
|
- Configured Telegram-only support.
|
|
404
413
|
- Configured Google Gemini single provider support.
|
|
405
414
|
- Included manual configuration file setup instructions.
|
|
415
|
+
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
# Changelog (Tiếng Việt)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
|
|
4
|
+
## [5.1.14] — 2026-04-08
|
|
5
|
+
|
|
6
|
+
### Sửa lỗi ổn định OpenClaw và Docker
|
|
7
|
+
|
|
8
|
+
- Pin lại OpenClaw về `openclaw@2026.4.5` vì bản cập nhật ngày `08/04/2026` đang lỗi.
|
|
9
|
+
- Sửa Dockerfile cho các case Docker trên Windows để tránh lỗi startup do escape command sai và lỗi `allowedOrigins`.
|
|
10
|
+
- Thêm ghi chú khuyên dùng `Node.js 20` đến `24`, tạm tránh `Node.js 25` để ổn định hơn với OpenClaw.
|
|
11
|
+
|
|
12
|
+
## [5.1.13] — 2026-04-08
|
|
4
13
|
|
|
5
14
|
### 🐛 Sửa lỗi cài macOS & ổn định Wizard
|
|
6
15
|
|
|
@@ -395,3 +404,4 @@ Tất cả những thay đổi nổi bật của dự án sẽ được ghi ché
|
|
|
395
404
|
- Telegram-only configuration
|
|
396
405
|
- Google Gemini single provider support
|
|
397
406
|
- Manual config file instructions
|
|
407
|
+
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🦞 OpenClaw Setup
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.
|
|
6
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.14-0EA5E9?style=for-the-badge" alt="Version 5.1.14" /></a>
|
|
7
7
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
9
9
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -24,16 +24,11 @@ An interactive **CLI tool** and **Setup Wizard** to deploy your own free AI Bot
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
## 🆕 What's new in v5.1.
|
|
27
|
+
## 🆕 What's new in v5.1.14
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- 🌐 **Native Install for Ubuntu/VPS** — No Docker needed. Both CLI and Web Wizard support running directly with PM2 — less RAM overhead, stable 24/7.
|
|
33
|
-
- 🤖 **Multi-Bot Deployment** — Deploy up to **5 independent Telegram bots** in one setup. Each bot runs in its own isolated workspace (`bot1/`, `bot2/`...) with its own token, name, slash command, and AI personality.
|
|
34
|
-
- 💬 **Department Room Model** — Add all bots to one Telegram group and they behave like a professional team. Bots stay silent by default, only responding when @mentioned or their slash command is used — no spam, no chaos.
|
|
35
|
-
- 🔗 **Group ID Helper** — "Get Group ID" button in Web Wizard + step-by-step guide in CLI. Opens @userinfobot directly — no documentation hunting needed.
|
|
36
|
-
- 🔐 **Zalo Personal QR Login** — Zalo Personal now uses direct QR login flow for both native and Docker. Setup prints exact QR path + copy/login commands — no need to go through `openclaw onboard`.
|
|
29
|
+
- 🔒 **Pinned OpenClaw version** — The OpenClaw update published on `April 8, 2026` is currently broken, so setup now stays on `openclaw@2026.4.5` for stability.
|
|
30
|
+
- 🐳 **Dockerfile fixes** — Fixed the Windows Docker flow so startup no longer breaks on bad command escaping or invalid generated `allowedOrigins`.
|
|
31
|
+
- 🟢 **Stable Node.js note** — Added guidance to use `Node.js 20` through `24`, and to avoid `Node.js 25` for now due to reported OpenClaw issues.
|
|
37
32
|
|
|
38
33
|
<details>
|
|
39
34
|
<summary><b>Previous: What's new in v5.0.0</b></summary>
|
|
@@ -102,7 +97,9 @@ npx create-openclaw-bot
|
|
|
102
97
|
|
|
103
98
|
Run in your terminal → follow the interactive prompts → startup script is generated automatically.
|
|
104
99
|
|
|
105
|
-
> Requires: **Node.js 20
|
|
100
|
+
> Requires: **Node.js 20/22/24**. Check: `node -v`
|
|
101
|
+
>
|
|
102
|
+
> Note: **avoid Node.js 25 for now**. There are reports of OpenClaw failing on Node 25.
|
|
106
103
|
|
|
107
104
|
<details>
|
|
108
105
|
<summary><b>3️⃣ Option C — AI Agent (Antigravity)</b></summary>
|
|
@@ -112,7 +109,7 @@ Run in your terminal → follow the interactive prompts → startup script is ge
|
|
|
112
109
|
2. Open this repo as your workspace
|
|
113
110
|
3. Paste into chat:
|
|
114
111
|
```
|
|
115
|
-
|
|
112
|
+
Read SETUP.md and set up OpenClaw v5.1.14 for me.
|
|
116
113
|
My bot token is X. Use 9Router (no API key).
|
|
117
114
|
My project folder: <YOUR_PATH>
|
|
118
115
|
```
|
|
@@ -127,7 +124,7 @@ Run in your terminal → follow the interactive prompts → startup script is ge
|
|
|
127
124
|
|
|
128
125
|
| Requirement | Notes |
|
|
129
126
|
| ----------------------- | ------------------------------------------------------------------------- |
|
|
130
|
-
| **Node.js 20
|
|
127
|
+
| **Node.js 20/22/24** | [Download](https://nodejs.org/) · Check: `node -v` · Avoid Node 25 for now |
|
|
131
128
|
| **An AI provider** | 9Router (free) or Gemini/Claude/GPT-4o |
|
|
132
129
|
| **Bot Token** | From Telegram BotFather or Zalo Developer |
|
|
133
130
|
| **Ollama** _(optional)_ | Only if you want to run Gemma 4 locally · [Download](https://ollama.com/) |
|
|
@@ -136,7 +133,7 @@ Run in your terminal → follow the interactive prompts → startup script is ge
|
|
|
136
133
|
|
|
137
134
|
| Requirement | Notes |
|
|
138
135
|
| ------------------------------- | --------------------------------------------------------------------------------------------- |
|
|
139
|
-
| **Node.js 20
|
|
136
|
+
| **Node.js 20/22/24** | [Download](https://nodejs.org/) · Check: `node -v` · Avoid Node 25 for now |
|
|
140
137
|
| **Docker Desktop + Compose V2** | [Download](https://www.docker.com/products/docker-desktop/) · Check: `docker compose version` |
|
|
141
138
|
| **An AI provider** | 9Router runs as a sidecar container — no separate install needed |
|
|
142
139
|
| **Bot Token** | From Telegram BotFather or Zalo Developer |
|
package/README.vi.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🦞 OpenClaw Setup
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.
|
|
6
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.14-0EA5E9?style=for-the-badge" alt="Version 5.1.14" /></a>
|
|
7
7
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
9
9
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -24,16 +24,11 @@ Công cụ **CLI tương tác** và **Setup Wizard** để tự triển khai Bot
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
## 🆕 Có gì mới trong v5.1.
|
|
27
|
+
## 🆕 Có gì mới trong v5.1.14
|
|
28
28
|
|
|
29
|
-
-
|
|
30
|
-
-
|
|
31
|
-
-
|
|
32
|
-
- 🌐 **Native Install cho Ubuntu/VPS** — Không cần Docker. CLI và Web Wizard đều hỗ trợ chạy trực tiếp với PM2 — tiết kiệm RAM, ổn định 24/7.
|
|
33
|
-
- 🤖 **Triển khai nhiều Bot** — Setup tới **5 Telegram bot độc lập** chỉ trong một lần cài. Mỗi bot sống trong thư mục riêng (`bot1/`, `bot2/`...) với token, tên, slash command và cá tính AI riêng biệt.
|
|
34
|
-
- 💬 **Chế độ Phòng Ban** — Thêm tất cả bot vào một Telegram group, chúng sẽ hoạt động như một đội nhân viên chuyên nghiệp. Bot im lặng theo mặc định — chỉ phản hồi khi được @tag hoặc gọi đúng lệnh slash, không spam, không ồn ào.
|
|
35
|
-
- 🔗 **Nút lấy Group ID tự động** — Nút "Lấy Group ID" ngay trong Web Wizard + hướng dẫn từng bước trong CLI. Mở @userinfobot thẳng luôn — không cần mò docs.
|
|
36
|
-
- 🔐 **Zalo Personal QR Login** — Zalo Personal giờ dùng flow login `zalouser` trực tiếp cho cả native lẫn Docker. Setup in sẵn lệnh login, lệnh copy QR và đường dẫn file QR, không cần vòng qua `openclaw onboard`.
|
|
29
|
+
- 🔒 **Pin lại OpenClaw** — Bản OpenClaw cập nhật ngày `08/04/2026` đang lỗi, nên setup được fix để giữ ở `openclaw@2026.4.5` cho ổn định.
|
|
30
|
+
- 🐳 **Fix Dockerfile** — Sửa luồng Docker cho Windows để không còn lỗi startup do command/escaping sai và tránh lỗi `allowedOrigins` bị sinh ra không hợp lệ.
|
|
31
|
+
- 🟢 **Ghi chú Node.js ổn định** — Thêm note khuyên dùng `Node.js 20` đến `24`, tạm tránh `Node.js 25` vì có report lỗi với OpenClaw.
|
|
37
32
|
|
|
38
33
|
<details>
|
|
39
34
|
<summary><b>Trước đó: Có gì mới ở v5.0.0</b></summary>
|
|
@@ -102,7 +97,9 @@ npx create-openclaw-bot
|
|
|
102
97
|
|
|
103
98
|
Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → script khởi động được tạo tự động.
|
|
104
99
|
|
|
105
|
-
> Yêu cầu: **Node.js 20
|
|
100
|
+
> Yêu cầu: **Node.js 20/22/24**. Kiểm tra: `node -v`
|
|
101
|
+
>
|
|
102
|
+
> Lưu ý: **tạm tránh Node.js 25**. Đã có báo cáo OpenClaw lỗi với Node 25.
|
|
106
103
|
|
|
107
104
|
<details>
|
|
108
105
|
<summary><b>3️⃣ Cách C — AI Agent (Antigravity)</b></summary>
|
|
@@ -112,7 +109,7 @@ Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → sc
|
|
|
112
109
|
2. Mở repo này làm workspace
|
|
113
110
|
3. Paste vào chat:
|
|
114
111
|
```
|
|
115
|
-
|
|
112
|
+
Read SETUP.md and set up OpenClaw v5.1.14 for me.
|
|
116
113
|
My bot token is X. Use 9Router (no API key).
|
|
117
114
|
My project folder: <THƯ_MỤC_CỦA_BẠN>
|
|
118
115
|
```
|
|
@@ -127,7 +124,7 @@ Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → sc
|
|
|
127
124
|
|
|
128
125
|
| Yêu cầu | Ghi chú |
|
|
129
126
|
| ----------------------- | ------------------------------------------------------------------- |
|
|
130
|
-
| **Node.js 20
|
|
127
|
+
| **Node.js 20/22/24** | [Tải về](https://nodejs.org/) · Kiểm tra: `node -v` · Tạm tránh Node 25 |
|
|
131
128
|
| **Một AI provider** | 9Router (miễn phí) hoặc Gemini/Claude/GPT-4o |
|
|
132
129
|
| **Bot Token** | Từ Telegram BotFather hoặc Zalo Developer |
|
|
133
130
|
| **Ollama** _(tuỳ chọn)_ | Chỉ cần nếu muốn chạy Gemma 4 local · [Tải về](https://ollama.com/) |
|
|
@@ -136,7 +133,7 @@ Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → sc
|
|
|
136
133
|
|
|
137
134
|
| Yêu cầu | Ghi chú |
|
|
138
135
|
| ------------------------------- | ---------------------------------------------------------------------------------------------- |
|
|
139
|
-
| **Node.js 20
|
|
136
|
+
| **Node.js 20/22/24** | [Tải về](https://nodejs.org/) · Kiểm tra: `node -v` · Tạm tránh Node 25 |
|
|
140
137
|
| **Docker Desktop + Compose V2** | [Tải về](https://www.docker.com/products/docker-desktop/) · Kiểm tra: `docker compose version` |
|
|
141
138
|
| **Một AI provider** | 9Router chạy như sidecar container — không cần cài riêng |
|
|
142
139
|
| **Bot Token** | Từ Telegram BotFather hoặc Zalo Developer |
|
package/cli.js
CHANGED
|
@@ -6,7 +6,8 @@ import path from 'path';
|
|
|
6
6
|
import os from 'os';
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import { spawn, execSync, execFileSync } from 'child_process';
|
|
9
|
-
const TELEGRAM_RELAY_PLUGIN_ID = 'openclaw-telegram-multibot-relay';
|
|
9
|
+
const TELEGRAM_RELAY_PLUGIN_ID = 'openclaw-telegram-multibot-relay';
|
|
10
|
+
const OPENCLAW_NPM_SPEC = 'openclaw@2026.4.5';
|
|
10
11
|
// Use plain npm package name — clawhub: protocol not supported in all OpenClaw versions
|
|
11
12
|
const TELEGRAM_RELAY_PLUGIN_SPEC = TELEGRAM_RELAY_PLUGIN_ID;
|
|
12
13
|
|
|
@@ -345,11 +346,11 @@ function installLatestOpenClaw({ isVi, osChoice }) {
|
|
|
345
346
|
return;
|
|
346
347
|
}
|
|
347
348
|
|
|
348
|
-
console.log(chalk.cyan(isVi
|
|
349
|
-
?
|
|
350
|
-
:
|
|
351
|
-
|
|
352
|
-
if (!installGlobalPackage(
|
|
349
|
+
console.log(chalk.cyan(isVi
|
|
350
|
+
? `\n📦 Dang cai/cap nhat ${OPENCLAW_NPM_SPEC}...`
|
|
351
|
+
: `\n📦 Installing/updating ${OPENCLAW_NPM_SPEC}...`));
|
|
352
|
+
|
|
353
|
+
if (!installGlobalPackage(OPENCLAW_NPM_SPEC, { isVi, osChoice, displayName: 'openclaw' })) {
|
|
353
354
|
process.exit(1);
|
|
354
355
|
}
|
|
355
356
|
|
|
@@ -1370,7 +1371,7 @@ async function main() {
|
|
|
1370
1371
|
}
|
|
1371
1372
|
|
|
1372
1373
|
|
|
1373
|
-
const patchScript = `const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add(
|
|
1374
|
+
const patchScript = `const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add('http://' + entry.address + ':18791');}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a).filter(Boolean)})});fs.writeFileSync(p,JSON.stringify(c,null,2));}`;
|
|
1374
1375
|
const b64Patch = Buffer.from(patchScript).toString('base64');
|
|
1375
1376
|
|
|
1376
1377
|
// Browser Playwright (both desktop & server modes need chromium)
|
|
@@ -1405,10 +1406,10 @@ async function main() {
|
|
|
1405
1406
|
|
|
1406
1407
|
];
|
|
1407
1408
|
if (browserDockerLines) dockerfileLines.push(browserDockerLines);
|
|
1408
|
-
dockerfileLines.push(
|
|
1409
|
+
dockerfileLines.push(
|
|
1409
1410
|
'',
|
|
1410
1411
|
`ARG CACHEBUST=${Date.now()}`,
|
|
1411
|
-
|
|
1412
|
+
`RUN npm install -g ${OPENCLAW_NPM_SPEC} grammy`,
|
|
1412
1413
|
'',
|
|
1413
1414
|
'# Fix chat.send dropping resolved agent timeout into reply pipeline.',
|
|
1414
1415
|
'# Without this, Telegram/WebChat paths fall back to an internal 300s default even when',
|
|
@@ -1420,10 +1421,12 @@ async function main() {
|
|
|
1420
1421
|
'EXPOSE 18791',
|
|
1421
1422
|
'',
|
|
1422
1423
|
`CMD sh -c "node -e \\"eval(Buffer.from('${b64Patch}','base64').toString())\\" && ${skillInstallCmd}${relayInstallCmd}${socatBridge}(while true; do sleep 5; openclaw devices approve --latest 2>/dev/null || true; done) & openclaw gateway run"`
|
|
1423
|
-
);
|
|
1424
|
-
const dockerfile = dockerfileLines.join('\n');
|
|
1425
|
-
|
|
1426
|
-
|
|
1424
|
+
);
|
|
1425
|
+
const dockerfile = dockerfileLines.join('\n');
|
|
1426
|
+
|
|
1427
|
+
const dockerDir = path.join(projectDir, 'docker', 'openclaw');
|
|
1428
|
+
await fs.ensureDir(dockerDir);
|
|
1429
|
+
await fs.writeFile(path.join(dockerDir, 'Dockerfile'), dockerfile);
|
|
1427
1430
|
|
|
1428
1431
|
// agentId no longer tightly coupled here, handled inside bot processes
|
|
1429
1432
|
const agentId = botName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-$/, '') || 'chat';
|
|
@@ -1658,7 +1661,8 @@ ${hasBrowserDesktop ? ` extra_hosts:
|
|
|
1658
1661
|
- ../../.openclaw:/root/.openclaw`;
|
|
1659
1662
|
}
|
|
1660
1663
|
|
|
1661
|
-
await fs.
|
|
1664
|
+
await fs.ensureDir(dockerDir);
|
|
1665
|
+
await fs.writeFile(path.join(dockerDir, 'docker-compose.yml'), compose);
|
|
1662
1666
|
|
|
1663
1667
|
let authProfilesJson = {};
|
|
1664
1668
|
if (provider.isLocal) {
|
|
@@ -2378,15 +2382,15 @@ fi
|
|
|
2378
2382
|
const isOpenClawInstalled = () => { try { execSync('openclaw --version', { stdio: 'ignore' }); return true; } catch { return false; } };
|
|
2379
2383
|
if (!isOpenClawInstalled()) {
|
|
2380
2384
|
console.log(chalk.cyan(isVi
|
|
2381
|
-
?
|
|
2382
|
-
:
|
|
2383
|
-
try {
|
|
2384
|
-
execSync(
|
|
2385
|
-
console.log(chalk.green(isVi ? '✅ openclaw đã cài xong!' : '✅ openclaw installed!'));
|
|
2386
|
-
} catch {
|
|
2387
|
-
console.log(chalk.yellow(isVi
|
|
2388
|
-
?
|
|
2389
|
-
:
|
|
2385
|
+
? `\n📦 Đang cài openclaw binary (npm install -g ${OPENCLAW_NPM_SPEC})...`
|
|
2386
|
+
: `\n📦 Installing openclaw binary (npm install -g ${OPENCLAW_NPM_SPEC})...`));
|
|
2387
|
+
try {
|
|
2388
|
+
execSync(`npm install -g ${OPENCLAW_NPM_SPEC}`, { stdio: 'inherit' });
|
|
2389
|
+
console.log(chalk.green(isVi ? '✅ openclaw đã cài xong!' : '✅ openclaw installed!'));
|
|
2390
|
+
} catch {
|
|
2391
|
+
console.log(chalk.yellow(isVi
|
|
2392
|
+
? `⚠️ Không tự cài được. Chạy thủ công: sudo npm install -g ${OPENCLAW_NPM_SPEC}`
|
|
2393
|
+
: `⚠️ Could not auto-install. Run manually: sudo npm install -g ${OPENCLAW_NPM_SPEC}`));
|
|
2390
2394
|
}
|
|
2391
2395
|
}
|
|
2392
2396
|
|
|
@@ -2413,9 +2417,9 @@ fi
|
|
|
2413
2417
|
} else {
|
|
2414
2418
|
if (!isOpenClawInstalled()) {
|
|
2415
2419
|
console.log(chalk.cyan(isVi
|
|
2416
|
-
?
|
|
2417
|
-
:
|
|
2418
|
-
if (!installGlobalPackage(
|
|
2420
|
+
? `\n📦 Dang cai openclaw binary (npm install -g ${OPENCLAW_NPM_SPEC})...`
|
|
2421
|
+
: `\n📦 Installing openclaw binary (npm install -g ${OPENCLAW_NPM_SPEC})...`));
|
|
2422
|
+
if (!installGlobalPackage(OPENCLAW_NPM_SPEC, { isVi, osChoice, displayName: 'openclaw' })) {
|
|
2419
2423
|
process.exit(1);
|
|
2420
2424
|
}
|
|
2421
2425
|
console.log(chalk.green(isVi ? '✅ openclaw da cai xong!' : '✅ openclaw installed!'));
|
package/docs/install-docker.md
CHANGED
|
@@ -22,8 +22,9 @@ Docker works well, but it has a learning curve. Review this table before proceed
|
|
|
22
22
|
|
|
23
23
|
Before installing Docker, ensure the following:
|
|
24
24
|
|
|
25
|
-
- **Node.js 20
|
|
26
|
-
- Node.js 20
|
|
25
|
+
- **Node.js 20/22/24**: [nodejs.org](https://nodejs.org/)
|
|
26
|
+
- Node.js 20 is the minimum recommended version. Node.js 22 and 24 are also fine.
|
|
27
|
+
- Avoid Node.js 25 for now; there are reports of OpenClaw failing on it.
|
|
27
28
|
- Verify: `node -v`
|
|
28
29
|
- **Docker Engine with Compose v2 plugin**: Instructions below.
|
|
29
30
|
- Verify Compose v2: `docker compose version` (Note: `docker compose`, not `docker-compose`)
|
|
@@ -22,8 +22,9 @@ Docker hiệu quả nhưng có yêu cầu kỹ thuật nhất định. Hãy xem
|
|
|
22
22
|
|
|
23
23
|
Trước khi cài Docker, hãy đảm bảo có đủ:
|
|
24
24
|
|
|
25
|
-
- **Node.js 20
|
|
26
|
-
- Node.js 20
|
|
25
|
+
- **Node.js 20/22/24**: [nodejs.org](https://nodejs.org/)
|
|
26
|
+
- Node.js 20 là mức tối thiểu khuyên dùng. Node.js 22 và 24 cũng ổn.
|
|
27
|
+
- Tạm tránh Node.js 25 vì đã có report OpenClaw lỗi trên bản này.
|
|
27
28
|
- Kiểm tra: `node -v`
|
|
28
29
|
- **Docker Engine kèm plugin Compose V2**: Hướng dẫn cài bên dưới.
|
|
29
30
|
- Kiểm tra Compose V2: `docker compose version` (Lưu ý: `docker compose`, không phải `docker-compose`)
|
package/package.json
CHANGED
package/setup.js
CHANGED
|
@@ -804,8 +804,8 @@
|
|
|
804
804
|
icon: '🐧',
|
|
805
805
|
titleVi: 'Ubuntu / VPS — Khuyên dùng Native (Không Docker)',
|
|
806
806
|
titleEn: 'Ubuntu / VPS — Recommended: Native (No Docker)',
|
|
807
|
-
descVi: 'Chạy thẳng trên máy, tiết kiệm RAM, khởi động nhanh. Script tự cài Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama và giữ bot chạy liên tục sau reboot.',
|
|
808
|
-
descEn: 'Run directly on machine — lower RAM, faster startup. Script auto-installs Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama and keeps bot running across reboots.',
|
|
807
|
+
descVi: 'Chạy thẳng trên máy, tiết kiệm RAM, khởi động nhanh. Script tự cài Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama và giữ bot chạy liên tục sau reboot. Tạm tránh Node 25.',
|
|
808
|
+
descEn: 'Run directly on machine — lower RAM, faster startup. Script auto-installs Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama and keeps bot running across reboots. Avoid Node 25 for now.',
|
|
809
809
|
deploy: 'native',
|
|
810
810
|
badgeVi: '💻 Native + PM2',
|
|
811
811
|
badgeEn: '💻 Native + PM2',
|
|
@@ -815,8 +815,8 @@
|
|
|
815
815
|
icon: '🖥️',
|
|
816
816
|
titleVi: 'Linux Desktop — Khuyên dùng Native',
|
|
817
817
|
titleEn: 'Linux Desktop — Recommended: Native',
|
|
818
|
-
descVi: 'Không cần Docker. Script tự cài Node.js 20 LTS nếu chưa có, cài OpenClaw CLI, rồi cài 9Router hoặc Ollama theo provider bạn chọn và khởi động bot ngay.',
|
|
819
|
-
descEn: 'No Docker needed. Script auto-installs Node.js 20 LTS if missing, installs OpenClaw CLI, then installs 9Router or Ollama based on your provider choice and starts the bot immediately.',
|
|
818
|
+
descVi: 'Không cần Docker. Script tự cài Node.js 20 LTS nếu chưa có, cài OpenClaw CLI, rồi cài 9Router hoặc Ollama theo provider bạn chọn và khởi động bot ngay. Tạm tránh Node 25.',
|
|
819
|
+
descEn: 'No Docker needed. Script auto-installs Node.js 20 LTS if missing, installs OpenClaw CLI, then installs 9Router or Ollama based on your provider choice and starts the bot immediately. Avoid Node 25 for now.',
|
|
820
820
|
deploy: 'native',
|
|
821
821
|
badgeVi: '💻 Native',
|
|
822
822
|
badgeEn: '💻 Native',
|
|
@@ -1940,7 +1940,7 @@ model:
|
|
|
1940
1940
|
? 'socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 & '
|
|
1941
1941
|
: '';
|
|
1942
1942
|
// Patch config on every startup to keep gateway settings stable
|
|
1943
|
-
const patchCmd = `node -e \\"const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add(
|
|
1943
|
+
const patchCmd = `node -e \\"const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add('http://' + entry.address + ':18791');}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a).filter(Boolean)})});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\" && `;
|
|
1944
1944
|
// Auto-approve device pairing after gateway starts (required since v2026.3.x)
|
|
1945
1945
|
const autoApproveCmd = '(while true; do sleep 5; openclaw devices approve --latest 2>/dev/null || true; done) & ';
|
|
1946
1946
|
const finalCmd = `CMD sh -c "${pluginInstallCmd}${patchCmd}${browserPrefix}${autoApproveCmd}${gatewayCmd}"`;
|
|
@@ -1951,7 +1951,7 @@ RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /v
|
|
|
1951
1951
|
|
|
1952
1952
|
|
|
1953
1953
|
ARG CACHEBUST=${Date.now()}
|
|
1954
|
-
RUN npm install -g openclaw@
|
|
1954
|
+
RUN npm install -g openclaw@2026.4.5 grammy${skillLines}${browserInstallLines}
|
|
1955
1955
|
RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const files=fs.readdirSync(dir).filter(n=>/\\.js$/.test(n));let patched=0;for(const file of files){const p=path.join(dir,file);let s='';try{s=fs.readFileSync(p,'utf8');}catch{continue;}if(s.includes(to)||!s.includes(from))continue;s=s.replace(from,to);fs.writeFileSync(p,s);patched++;}if(!patched){process.exit(0);}"
|
|
1956
1956
|
WORKDIR /root/.openclaw
|
|
1957
1957
|
|
|
@@ -2979,25 +2979,24 @@ I am **${botName}**. When asked my name, I answer: _"I'm ${botName}"_.`;
|
|
|
2979
2979
|
if (p) allPlugins.push(p.package);
|
|
2980
2980
|
});
|
|
2981
2981
|
if (isMultiBot && state.channel === 'telegram') allPlugins.push(relayPluginSpec);
|
|
2982
|
-
const pluginCmd = allPlugins.length > 0 ? ('npm exec openclaw plugins install ' + allPlugins.join(' ')) : '';
|
|
2983
|
-
|
|
2984
|
-
function native9RouterSyncScriptContent() {
|
|
2985
|
-
return `const fs=require('fs');
|
|
2986
|
-
const path=require('path');
|
|
2987
|
-
const INTERVAL=30000;
|
|
2988
|
-
const p=path.join(process.env.HOME||process.env.USERPROFILE||'.','.9router','db.json');
|
|
2989
|
-
const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],claude-code:['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],cline:['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],iflow:['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],qwen:['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],kiro:['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],ollama:['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],glm:['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],minimax:['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],kimi:['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],deepseek:['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],xai:['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],mistral:['mistral/mistral-large-latest','mistral/codestral-latest'],groq:['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],cerebras:['cerebras/gpt-oss-120b'],alicode:['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],openai:['openai/gpt-4o','openai/gpt-4.1'],anthropic:['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],gemini:['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
|
|
2990
|
-
const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catch{}if(!db.combos)db.combos=[];const removeSmartRoute=()=>{const next=db.combos.filter(x=>x.id!=='smart-route');if(next.length!==db.combos.length){db.combos=next;fs.writeFileSync(p,JSON.stringify(db,null,2));}};const a=(db.providerConnections||[]).filter(c=>c&&c.provider&&c.isActive!==false&&!c.disabled).map(c=>c.provider);if(!a.length){removeSmartRoute();return;}const PREF=['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];a.sort((x,y)=>(PREF.indexOf(x)===-1?99:PREF.indexOf(x))-(PREF.indexOf(y)===-1?99:PREF.indexOf(y)));const m=a.flatMap(provider=>PM[provider]||[]);if(!m.length){removeSmartRoute();return;}const c={id:'smart-route',name:'smart-route',alias:'smart-route',models:m};const i=db.combos.findIndex(x=>x.id==='smart-route');if(i>=0){if(JSON.stringify(db.combos[i].models)!==JSON.stringify(c.models)){db.combos[i]=c;fs.writeFileSync(p,JSON.stringify(db,null,2));}}else{db.combos.push(c);fs.writeFileSync(p,JSON.stringify(db,null,2));}}catch{}};sync();setInterval(sync,INTERVAL);`;
|
|
2991
|
-
}
|
|
2982
|
+
const pluginCmd = allPlugins.length > 0 ? ('call npm exec -- openclaw plugins install ' + allPlugins.join(' ') + ' || goto :fail') : '';
|
|
2983
|
+
|
|
2984
|
+
function native9RouterSyncScriptContent() {
|
|
2985
|
+
return `const fs=require('fs');
|
|
2986
|
+
const path=require('path');
|
|
2987
|
+
const INTERVAL=30000;
|
|
2988
|
+
const p=path.join(process.env.HOME||process.env.USERPROFILE||'.','.9router','db.json');
|
|
2989
|
+
const PM={'codex':['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],'claude-code':['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],'github':['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],'cursor':['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],'kilo':['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],'cline':['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],'iflow':['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],'qwen':['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],'kiro':['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],'ollama':['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],'glm':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'minimax':['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],'kimi':['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],'deepseek':['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],'xai':['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],'mistral':['mistral/mistral-large-latest','mistral/codestral-latest'],'groq':['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],'cerebras':['cerebras/gpt-oss-120b'],'alicode':['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],'openai':['openai/gpt-4o','openai/gpt-4.1'],'anthropic':['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],'gemini':['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
|
|
2990
|
+
const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catch{}if(!db.combos)db.combos=[];const removeSmartRoute=()=>{const next=db.combos.filter(x=>x.id!=='smart-route');if(next.length!==db.combos.length){db.combos=next;fs.writeFileSync(p,JSON.stringify(db,null,2));}};const a=(db.providerConnections||[]).filter(c=>c&&c.provider&&c.isActive!==false&&!c.disabled).map(c=>c.provider);if(!a.length){removeSmartRoute();return;}const PREF=['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];a.sort((x,y)=>(PREF.indexOf(x)===-1?99:PREF.indexOf(x))-(PREF.indexOf(y)===-1?99:PREF.indexOf(y)));const m=a.flatMap(provider=>PM[provider]||[]);if(!m.length){removeSmartRoute();return;}const c={id:'smart-route',name:'smart-route',alias:'smart-route',models:m};const i=db.combos.findIndex(x=>x.id==='smart-route');if(i>=0){if(JSON.stringify(db.combos[i].models)!==JSON.stringify(c.models)){db.combos[i]=c;fs.writeFileSync(p,JSON.stringify(db,null,2));}}else{db.combos.push(c);fs.writeFileSync(p,JSON.stringify(db,null,2));}}catch{}};sync();setInterval(sync,INTERVAL);`;
|
|
2991
|
+
}
|
|
2992
2992
|
|
|
2993
2993
|
// ─── Shared initializer (provider install) ───────────────────────────────
|
|
2994
2994
|
function providerLines(arr, shell) {
|
|
2995
2995
|
if (is9Router) {
|
|
2996
2996
|
if (shell === 'bat') {
|
|
2997
|
-
arr.push('npm install -g 9router');
|
|
2998
|
-
arr.push('start "9Router" cmd /k "9router -n -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2999
|
-
arr.push('
|
|
3000
|
-
arr.push('timeout /t 5 /nobreak >nul');
|
|
2997
|
+
arr.push('call npm install -g 9router || goto :fail');
|
|
2998
|
+
arr.push('start "9Router" cmd /k "9router -n -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2999
|
+
arr.push('timeout /t 5 /nobreak >nul');
|
|
3001
3000
|
} else {
|
|
3002
3001
|
arr.push('npm install -g 9router');
|
|
3003
3002
|
arr.push('nohup env PORT=20128 HOSTNAME=0.0.0.0 node "$(npm root -g)/9router/app/server.js" >/tmp/9router.log 2>&1 &');
|
|
@@ -3588,32 +3587,43 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3588
3587
|
const isDocker = state.deployMode === 'docker';
|
|
3589
3588
|
scriptName = isDocker ? 'setup-openclaw-docker-win.bat' : 'setup-openclaw-win.bat';
|
|
3590
3589
|
const lines = [
|
|
3591
|
-
'@echo off',
|
|
3592
|
-
'
|
|
3590
|
+
'@echo off',
|
|
3591
|
+
'setlocal EnableExtensions',
|
|
3592
|
+
'chcp 65001 >nul',
|
|
3593
3593
|
`echo === OpenClaw Setup — Windows${isDocker ? ' Docker' : ' Native'} ===`,
|
|
3594
3594
|
'echo.',
|
|
3595
3595
|
'echo [1/5] Kiem tra Node.js...',
|
|
3596
3596
|
'where node >nul 2>&1 || (echo ERROR: Node.js chua cai! Tai tai: https://nodejs.org && pause && exit /b 1)',
|
|
3597
3597
|
'echo [2/5] Cai OpenClaw CLI...',
|
|
3598
|
-
'npm install -g openclaw@
|
|
3598
|
+
'call npm install -g openclaw@2026.4.5 || goto :fail',
|
|
3599
3599
|
];
|
|
3600
3600
|
providerLines(lines, 'bat');
|
|
3601
3601
|
if (pluginCmd) { lines.push('echo Cai plugins...'); lines.push(pluginCmd); }
|
|
3602
3602
|
|
|
3603
|
-
if (isMultiBot) {
|
|
3604
|
-
lines.push('echo [4/5] Tao runtime multi-agent dung chung...');
|
|
3605
|
-
appendBatWriteCommands(lines, sharedNativeFileMap());
|
|
3606
|
-
lines.push('
|
|
3607
|
-
lines.push('
|
|
3608
|
-
|
|
3609
|
-
|
|
3610
|
-
|
|
3611
|
-
lines
|
|
3612
|
-
lines.push('
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
3616
|
-
|
|
3603
|
+
if (isMultiBot) {
|
|
3604
|
+
lines.push('echo [4/5] Tao runtime multi-agent dung chung...');
|
|
3605
|
+
appendBatWriteCommands(lines, sharedNativeFileMap());
|
|
3606
|
+
if (is9Router) lines.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
|
|
3607
|
+
lines.push('echo [5/5] Khoi dong gateway multi-bot...');
|
|
3608
|
+
lines.push('call openclaw gateway run');
|
|
3609
|
+
} else {
|
|
3610
|
+
lines.push('echo [4/5] Tao file cau hinh...');
|
|
3611
|
+
appendBatWriteCommands(lines, botFiles(0));
|
|
3612
|
+
if (is9Router) lines.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
|
|
3613
|
+
lines.push('echo [5/5] Khoi dong bot...');
|
|
3614
|
+
lines.push('call openclaw gateway run');
|
|
3615
|
+
}
|
|
3616
|
+
|
|
3617
|
+
lines.push('goto :end');
|
|
3618
|
+
lines.push(':fail');
|
|
3619
|
+
lines.push('echo.');
|
|
3620
|
+
lines.push('echo Cai dat that bai. Kiem tra dong loi ngay phia tren.');
|
|
3621
|
+
lines.push('pause');
|
|
3622
|
+
lines.push('exit /b 1');
|
|
3623
|
+
lines.push(':end');
|
|
3624
|
+
lines.push('pause');
|
|
3625
|
+
lines.push('endlocal');
|
|
3626
|
+
scriptContent = lines.filter(Boolean).join('\r\n');
|
|
3617
3627
|
|
|
3618
3628
|
// ─── macOS .SH ───────────────────────────────────────────────────────────
|
|
3619
3629
|
} else if (state.nativeOs === 'linux') {
|
|
@@ -3653,7 +3663,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3653
3663
|
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.zshrc"',
|
|
3654
3664
|
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3655
3665
|
'# Install openclaw (user-local first, sudo fallback)',
|
|
3656
|
-
'npm install -g openclaw@
|
|
3666
|
+
'npm install -g openclaw@2026.4.5 || sudo npm install -g openclaw@2026.4.5',
|
|
3657
3667
|
];
|
|
3658
3668
|
providerLines(sh, 'sh');
|
|
3659
3669
|
if (pluginCmd) sh.push(pluginCmd);
|
|
@@ -3685,7 +3695,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3685
3695
|
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3686
3696
|
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
|
|
3687
3697
|
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3688
|
-
'npm install -g openclaw@
|
|
3698
|
+
'npm install -g openclaw@2026.4.5 pm2@latest',
|
|
3689
3699
|
];
|
|
3690
3700
|
providerLines(vps, 'sh');
|
|
3691
3701
|
if (pluginCmd) vps.push(pluginCmd);
|
|
@@ -3732,7 +3742,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3732
3742
|
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3733
3743
|
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
|
|
3734
3744
|
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3735
|
-
'npm install -g openclaw@
|
|
3745
|
+
'npm install -g openclaw@2026.4.5',
|
|
3736
3746
|
];
|
|
3737
3747
|
providerLines(lnx, 'sh');
|
|
3738
3748
|
if (pluginCmd) lnx.push(pluginCmd);
|
|
@@ -3766,7 +3776,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3766
3776
|
if (stepsList) {
|
|
3767
3777
|
const steps = [];
|
|
3768
3778
|
steps.push(isVi ? '✅ Kiểm tra Node.js (cài tự động trên Ubuntu/VPS nếu chưa có)' : '✅ Check Node.js (auto-install on Ubuntu/VPS if missing)');
|
|
3769
|
-
steps.push(isVi ? '📦 Cài OpenClaw CLI (<code>npm install -g openclaw@
|
|
3779
|
+
steps.push(isVi ? '📦 Cài OpenClaw CLI (<code>npm install -g openclaw@2026.4.5</code>)' : '📦 Install OpenClaw CLI (<code>npm install -g openclaw@2026.4.5</code>)');
|
|
3770
3780
|
if (is9Router) {
|
|
3771
3781
|
steps.push(isVi ? '🔀 Cài 9Router (<code>npm install -g 9router</code>) và khởi động tự động' : '🔀 Install 9Router (<code>npm install -g 9router</code>) and start automatically');
|
|
3772
3782
|
} else if (isOllama) {
|
|
@@ -52,8 +52,8 @@ checks.push(() => expectMatch(
|
|
|
52
52
|
|
|
53
53
|
checks.push(() => expectMatch(
|
|
54
54
|
cli,
|
|
55
|
-
/function installLatestOpenClaw\(\{ isVi, osChoice \}\) \{[\s\S]*installGlobalPackage\(
|
|
56
|
-
'CLI must provide a shared helper that always installs or upgrades openclaw
|
|
55
|
+
/function installLatestOpenClaw\(\{ isVi, osChoice \}\) \{[\s\S]*installGlobalPackage\(OPENCLAW_NPM_SPEC, \{ isVi, osChoice, displayName: 'openclaw' \}\)[\s\S]*process\.exit\(1\)/,
|
|
56
|
+
'CLI must provide a shared helper that always installs or upgrades the pinned openclaw version'
|
|
57
57
|
));
|
|
58
58
|
|
|
59
59
|
checks.push(() => expectMatch(
|
|
@@ -70,7 +70,7 @@ checks.push(() => expectMatch(
|
|
|
70
70
|
|
|
71
71
|
checks.push(() => expectMatch(
|
|
72
72
|
cli,
|
|
73
|
-
/if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\(
|
|
73
|
+
/if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\(OPENCLAW_NPM_SPEC, \{ isVi, osChoice, displayName: 'openclaw' \}\)/,
|
|
74
74
|
'Native branch must auto-install openclaw'
|
|
75
75
|
));
|
|
76
76
|
|
|
@@ -216,6 +216,25 @@ checks.push(() => expectMatch(
|
|
|
216
216
|
'Native per-bot gateway config must seed control UI allowed origins for each port'
|
|
217
217
|
));
|
|
218
218
|
|
|
219
|
+
checks.push(() => expectMatch(
|
|
220
|
+
cli,
|
|
221
|
+
/const dockerDir = path\.join\(projectDir, 'docker', 'openclaw'\);\s*await fs\.ensureDir\(dockerDir\);\s*await fs\.writeFile\(path\.join\(dockerDir, 'Dockerfile'\), dockerfile\);[\s\S]*await fs\.ensureDir\(dockerDir\);\s*await fs\.writeFile\(path\.join\(dockerDir, 'docker-compose\.yml'\), compose\);/s,
|
|
222
|
+
'Docker CLI flow must ensure docker/openclaw exists immediately before writing Dockerfile and docker-compose.yml'
|
|
223
|
+
));
|
|
224
|
+
|
|
225
|
+
checks.push(() => expectMatch(
|
|
226
|
+
cli,
|
|
227
|
+
/RUN npm install -g \$\{OPENCLAW_NPM_SPEC\} grammy/,
|
|
228
|
+
'Docker CLI image must install grammy alongside openclaw so Telegram runtime dependencies resolve'
|
|
229
|
+
));
|
|
230
|
+
|
|
231
|
+
checks.push(() => expect(
|
|
232
|
+
cli.includes("a.add('http://' + entry.address + ':18791')")
|
|
233
|
+
&& cli.includes('allowedOrigins:Array.from(a).filter(Boolean)')
|
|
234
|
+
&& !cli.includes("a.add(`http://${entry.address}:18791`)"),
|
|
235
|
+
'Docker CLI patch script must avoid shell-expanding ${entry.address} and must filter null origins'
|
|
236
|
+
));
|
|
237
|
+
|
|
219
238
|
checks.push(() => expectMatch(
|
|
220
239
|
cli,
|
|
221
240
|
/channelKey === 'zalo-personal'\) \{\s*botConfig\.channels\['zalouser'\] = \{\s*enabled: true,\s*dmPolicy: 'open',\s*autoReply: true/s,
|
|
@@ -303,19 +322,32 @@ checks.push(() => expectMatch(
|
|
|
303
322
|
|
|
304
323
|
checks.push(() => expectMatch(
|
|
305
324
|
setup,
|
|
306
|
-
/if \(state\.nativeOs === 'win'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-win\.bat' : 'setup-openclaw-win\.bat';[\s\S]*npm install -g openclaw@
|
|
325
|
+
/if \(state\.nativeOs === 'win'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-win\.bat' : 'setup-openclaw-win\.bat';[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*openclaw gateway run/s,
|
|
307
326
|
'Windows native/docker script generation must use the correct file name and start command'
|
|
308
327
|
));
|
|
309
328
|
|
|
310
329
|
checks.push(() => expectMatch(
|
|
311
330
|
setup,
|
|
312
|
-
/else if \(state\.nativeOs === 'linux'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-macos\.sh' : 'setup-openclaw-macos\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@
|
|
331
|
+
/else if \(state\.nativeOs === 'linux'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-macos\.sh' : 'setup-openclaw-macos\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*openclaw gateway run/s,
|
|
313
332
|
'macOS script generation must use the correct file name and start command'
|
|
314
333
|
));
|
|
315
334
|
|
|
316
335
|
checks.push(() => expectMatch(
|
|
317
336
|
setup,
|
|
318
|
-
/
|
|
337
|
+
/RUN npm install -g openclaw@2026\.4\.5 grammy/,
|
|
338
|
+
'Wizard Dockerfile generation must install grammy alongside openclaw so Telegram runtime dependencies resolve'
|
|
339
|
+
));
|
|
340
|
+
|
|
341
|
+
checks.push(() => expect(
|
|
342
|
+
setup.includes("a.add('http://' + entry.address + ':18791')")
|
|
343
|
+
&& setup.includes('allowedOrigins:Array.from(a).filter(Boolean)')
|
|
344
|
+
&& !setup.includes("a.add(\\`http://\\${entry.address}:18791\\`)"),
|
|
345
|
+
'Wizard Docker patch command must avoid shell-expanding ${entry.address} and must filter null origins'
|
|
346
|
+
));
|
|
347
|
+
|
|
348
|
+
checks.push(() => expectMatch(
|
|
349
|
+
setup,
|
|
350
|
+
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*scriptName = 'setup-openclaw-vps\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@2026\.4\.5 pm2@latest[\s\S]*pm2 save && pm2 startup/s,
|
|
319
351
|
'VPS native script generation must install openclaw+pm2 and persist PM2 startup'
|
|
320
352
|
));
|
|
321
353
|
|
|
@@ -325,6 +357,12 @@ checks.push(() => expectMatch(
|
|
|
325
357
|
'Native script generation must install and start a standalone 9Router dashboard on port 20128'
|
|
326
358
|
));
|
|
327
359
|
|
|
360
|
+
checks.push(() => expectMatch(
|
|
361
|
+
setup,
|
|
362
|
+
/} else if \(is9Router\) \{[\s\S]*container_name: openclaw-bot[\s\S]*depends_on:[\s\S]*- 9router[\s\S]*container_name: 9router[\s\S]*PORT=20128[\s\S]*HOSTNAME=0\.0\.0\.0[\s\S]*9router-data:/s,
|
|
363
|
+
'Wizard single-bot Docker compose must include the 9Router sidecar service and named volume when provider is 9Router'
|
|
364
|
+
));
|
|
365
|
+
|
|
328
366
|
checks.push(() => expectMatch(
|
|
329
367
|
setup,
|
|
330
368
|
/function native9RouterSyncScriptContent\(\) \{[\s\S]*path\.join\(process\.env\.HOME\|\|process\.env\.USERPROFILE\|\|'\.'\,\'\.9router\'\,\'db\.json\'\)[\s\S]*providerConnections[\s\S]*smart-route/s,
|
|
@@ -381,7 +419,7 @@ checks.push(() => expectMatch(
|
|
|
381
419
|
|
|
382
420
|
checks.push(() => expectMatch(
|
|
383
421
|
setup,
|
|
384
|
-
/else if \(state\.nativeOs === 'linux-desktop'\) \{[\s\S]*scriptName = 'setup-openclaw-linux\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@
|
|
422
|
+
/else if \(state\.nativeOs === 'linux-desktop'\) \{[\s\S]*scriptName = 'setup-openclaw-linux\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*openclaw gateway run/s,
|
|
385
423
|
'Linux Desktop native script generation must install openclaw and run the gateway'
|
|
386
424
|
));
|
|
387
425
|
|