create-openclaw-bot 5.0.4 → 5.0.6

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog (English)
2
2
 
3
- ## [5.0.1] — 2026-04-04
3
+ ## [5.0.5] — 2026-04-06
4
4
 
5
5
  ### 🚀 Native Install Mode — No Docker Required
6
6
 
package/CHANGELOG.vi.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Changelog (Tiếng Việt)
2
2
 
3
- ## [5.0.1] — 2026-04-04
3
+ ## [5.0.5] — 2026-04-06
4
4
 
5
5
  ### 🚀 Chế độ Native Install — Không cần Docker
6
6
 
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.0.1-0EA5E9?style=for-the-badge" alt="Version 5.0.1" /></a>
6
+ <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.0.5-0EA5E9?style=for-the-badge" alt="Version 5.0.5" /></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,7 +24,7 @@ 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.0.1
27
+ ## 🆕 What's new in v5.0.5
28
28
 
29
29
  - 💻 **OS-First Setup** — Step 1 is now choosing your OS (Windows, macOS, Ubuntu, VPS). All scripts, configs, and instructions are generated to match.
30
30
  - 🧠 **Gemma 4 — 4 sizes** — `gemma4:e2b` (~4 GB), `gemma4:e4b` (~8 GB), `gemma4:26b` (~18 GB), `gemma4:31b` (~24 GB). Auto-pulled on first launch.
@@ -111,7 +111,7 @@ Run in your terminal → follow the interactive prompts → startup script is ge
111
111
  2. Open this repo as your workspace
112
112
  3. Paste into chat:
113
113
  ```
114
- Read SETUP.md and set up OpenClaw v5.0.1 for me.
114
+ Read SETUP.md and set up OpenClaw v5.0.5 for me.
115
115
  My bot token is X. Use 9Router (no API key).
116
116
  My project folder: <YOUR_PATH>
117
117
  ```
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.0.1-0EA5E9?style=for-the-badge" alt="Version 5.0.1" /></a>
6
+ <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.0.5-0EA5E9?style=for-the-badge" alt="Version 5.0.5" /></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,7 +24,7 @@ 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.0.1
27
+ ## 🆕 Có gì mới trong v5.0.5
28
28
 
29
29
  - 💻 **OS-First Setup** — Bước đầu tiên bây giờ là chọn hệ điều hành của bạn (Windows, macOS, Ubuntu, VPS). Toàn bộ script, cấu hình và hướng dẫn được tạo ra phù hợp với lựa chọn đó.
30
30
  - 🧠 **Gemma 4 — 4 kích thước** — `gemma4:e2b` (~4 GB), `gemma4:e4b` (~8 GB), `gemma4:26b` (~18 GB), `gemma4:31b` (~24 GB). Tự pull về khi bot khởi động lần đầu.
@@ -111,7 +111,7 @@ Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → sc
111
111
  2. Mở repo này làm workspace
112
112
  3. Paste vào chat:
113
113
  ```
114
- Read SETUP.md and set up OpenClaw v5.0.1 for me.
114
+ Read SETUP.md and set up OpenClaw v5.0.5 for me.
115
115
  My bot token is X. Use 9Router (no API key).
116
116
  My project folder: <THƯ_MỤC_CỦA_BẠN>
117
117
  ```
package/cli.js CHANGED
@@ -32,6 +32,144 @@ function installRelayPluginForProject(projectDir, isVi) {
32
32
  return false;
33
33
  }
34
34
 
35
+ function isOpenClawInstalled() {
36
+ try {
37
+ execSync('openclaw --version', { stdio: 'ignore' });
38
+ return true;
39
+ } catch {
40
+ return false;
41
+ }
42
+ }
43
+
44
+ function isPm2Installed() {
45
+ try {
46
+ execSync('pm2 --version', { stdio: 'ignore' });
47
+ return true;
48
+ } catch {
49
+ return false;
50
+ }
51
+ }
52
+
53
+ function getUserNpmPrefixInfo() {
54
+ if (process.platform === 'win32') {
55
+ return null;
56
+ }
57
+
58
+ const prefixDir = path.join(os.homedir(), '.local');
59
+ return {
60
+ prefixDir,
61
+ binDir: path.join(prefixDir, 'bin')
62
+ };
63
+ }
64
+
65
+ function ensureBinDirOnPath(binDir) {
66
+ const delimiter = path.delimiter;
67
+ const pathParts = String(process.env.PATH || '').split(delimiter).filter(Boolean);
68
+ if (!pathParts.includes(binDir)) {
69
+ process.env.PATH = [binDir, ...pathParts].join(delimiter);
70
+ }
71
+ }
72
+
73
+ function appendLineIfMissing(filePath, line) {
74
+ let content = '';
75
+ if (fs.existsSync(filePath)) {
76
+ content = fs.readFileSync(filePath, 'utf8');
77
+ }
78
+
79
+ if (!content.includes(line)) {
80
+ const prefix = content && !content.endsWith('\n') ? '\n' : '';
81
+ fs.appendFileSync(filePath, `${prefix}${line}\n`);
82
+ }
83
+ }
84
+
85
+ function ensureUserWritableGlobalNpm({ isVi, osChoice }) {
86
+ if (process.platform === 'win32') {
87
+ return true;
88
+ }
89
+
90
+ const npmInfo = getUserNpmPrefixInfo();
91
+ if (!npmInfo) {
92
+ return true;
93
+ }
94
+
95
+ try {
96
+ fs.ensureDirSync(npmInfo.binDir);
97
+ process.env.npm_config_prefix = npmInfo.prefixDir;
98
+ ensureBinDirOnPath(npmInfo.binDir);
99
+
100
+ execSync(`npm config set prefix "${npmInfo.prefixDir.replace(/"/g, '\\"')}"`, {
101
+ stdio: 'ignore',
102
+ shell: true,
103
+ env: process.env
104
+ });
105
+
106
+ appendLineIfMissing(path.join(os.homedir(), '.profile'), 'export PATH="$HOME/.local/bin:$PATH"');
107
+ appendLineIfMissing(
108
+ path.join(os.homedir(), osChoice === 'macos' ? '.zshrc' : '.bashrc'),
109
+ 'export PATH="$HOME/.local/bin:$PATH"'
110
+ );
111
+ return true;
112
+ } catch {
113
+ console.log(chalk.yellow(isVi
114
+ ? '⚠️ Không thể cấu hình npm global prefix trong ~/.local. Tiếp tục thử cài đặt trực tiếp.'
115
+ : '⚠️ Could not configure npm global prefix in ~/.local. Falling back to direct install.'));
116
+ return false;
117
+ }
118
+ }
119
+
120
+ const userNpmInfo = getUserNpmPrefixInfo();
121
+ if (userNpmInfo) {
122
+ ensureBinDirOnPath(userNpmInfo.binDir);
123
+ }
124
+
125
+ function installGlobalPackage(pkg, { isVi, osChoice, displayName }) {
126
+ const installCommands = [];
127
+
128
+ if (osChoice === 'windows') {
129
+ installCommands.push(`npm install -g ${pkg}`);
130
+ } else {
131
+ ensureUserWritableGlobalNpm({ isVi, osChoice });
132
+ installCommands.push(`npm install -g ${pkg}`);
133
+ const npmInfo = getUserNpmPrefixInfo();
134
+ if (npmInfo) {
135
+ installCommands.push(`npm install -g --prefix "${npmInfo.prefixDir.replace(/"/g, '\\"')}" ${pkg}`);
136
+ }
137
+ }
138
+
139
+ for (const cmd of installCommands) {
140
+ try {
141
+ execSync(cmd, { stdio: 'inherit', shell: true, env: process.env });
142
+ return true;
143
+ } catch {
144
+ // try next candidate
145
+ }
146
+ }
147
+
148
+ console.log(chalk.yellow(isVi
149
+ ? `⚠️ Không thể tự cài ${displayName}. Chạy thủ công: ${osChoice === 'windows' ? `npm install -g ${pkg}` : `npm config set prefix ~/.local && npm install -g ${pkg}`}`
150
+ : `⚠️ Could not auto-install ${displayName}. Run manually: ${osChoice === 'windows' ? `npm install -g ${pkg}` : `npm config set prefix ~/.local && npm install -g ${pkg}`}`));
151
+ return false;
152
+ }
153
+
154
+ async function syncLocalConfigToHome(projectDir, isVi) {
155
+ const homedir = os.homedir();
156
+ const globalClawDir = path.join(homedir, '.openclaw');
157
+ const localClawDir = path.join(projectDir, '.openclaw');
158
+ try {
159
+ await fs.ensureDir(globalClawDir);
160
+ await fs.copy(localClawDir, globalClawDir, { overwrite: true });
161
+ console.log(chalk.green(`\n✅ ${isVi
162
+ ? 'Config đã được sync vào ~/.openclaw/ — openclaw sẵn sàng!'
163
+ : 'Config synced to ~/.openclaw/ — openclaw is ready!'}`));
164
+ return true;
165
+ } catch {
166
+ console.log(chalk.yellow(`\n⚠️ ${isVi
167
+ ? `Không thể tự sync config. Chạy thủ công:\n cp -rn ${localClawDir}/. ${globalClawDir}/`
168
+ : `Could not auto-sync config. Run manually:\n cp -rn ${localClawDir}/. ${globalClawDir}/`}`));
169
+ return false;
170
+ }
171
+ }
172
+
35
173
  function buildTelegramPostInstallChecklist({ isVi, bots, groupId }) {
36
174
  const botList = bots.map((bot, idx) => `- **${bot?.name || `Bot ${idx + 1}`}** — token: ${String(bot?.token || '').slice(0, 10)}...`).join('\n');
37
175
 
@@ -1134,18 +1272,18 @@ ${hasBrowserDesktop ? ` extra_hosts:
1134
1272
  );
1135
1273
  // Generate ecosystem.config.js for PM2 native multi-bot
1136
1274
  if (deployMode === 'native') {
1137
- const pm2Apps = agentMetas.map((meta) => [
1138
- ' {',
1139
- ` name: '${meta.agentId}',`,
1140
- ` script: 'openclaw',`,
1141
- ` args: '--agent ${meta.agentId}',`,
1142
- ` cwd: '${projectDir.replace(/\\/g, '/')}',`,
1143
- ` interpreter: 'none',`,
1144
- ` autorestart: true,`,
1145
- ` watch: false,`,
1146
- ` env: { NODE_ENV: 'production' }`,
1147
- ' }',
1148
- ].join('\n')).join(',\n');
1275
+ const pm2Apps = [
1276
+ ' {',
1277
+ ` name: '${botName || 'openclaw-multibot'}',`,
1278
+ ` script: 'openclaw',`,
1279
+ ` args: 'gateway run',`,
1280
+ ` cwd: '${projectDir.replace(/\\/g, '/')}',`,
1281
+ ` interpreter: 'none',`,
1282
+ ` autorestart: true,`,
1283
+ ` watch: false,`,
1284
+ ` env: { NODE_ENV: 'production' }`,
1285
+ ' }',
1286
+ ].join('\n');
1149
1287
  const ecosystemContent = [
1150
1288
  '// PM2 ecosystem — run: pm2 start ecosystem.config.js',
1151
1289
  'module.exports = {',
@@ -1157,7 +1295,6 @@ ${hasBrowserDesktop ? ` extra_hosts:
1157
1295
  ].join('\n');
1158
1296
  await fs.writeFile(path.join(projectDir, 'ecosystem.config.js'), ecosystemContent);
1159
1297
  }
1160
- installRelayPluginForProject(projectDir, isVi);
1161
1298
  if (Object.keys(authProfilesJson).length > 0) {
1162
1299
  await fs.writeJson(path.join(rootClawDir, 'auth-profiles.json'), authProfilesJson, { spaces: 2 });
1163
1300
  }
@@ -1656,7 +1793,7 @@ fi
1656
1793
  : `Could not auto-sync config. Run manually:\n cp -rn ${localClawDir}/. ${globalClawDir}/`}`));
1657
1794
  }
1658
1795
 
1659
- console.log(chalk.cyan(`\n👉 ${isVi ? 'Đã tạo xong file cấu hình native.' : 'Native config files are ready.'}`));
1796
+ console.log(chalk.cyan(`\n👉 ${isVi ? 'Đã tạo xong file cấu hình Docker.' : 'Docker config files are ready.'}`));
1660
1797
  console.log(chalk.gray(isVi
1661
1798
  ? ` Cấu trúc config: ${isMultiBot && channelKey === 'telegram' ? '.openclaw/ dùng chung + agents/workspace-*' : (isMultiBot ? 'bot1/, bot2/, ...' : '.openclaw/')}`
1662
1799
  : ` Config layout: ${isMultiBot && channelKey === 'telegram' ? 'shared .openclaw/ with agents/workspace-*' : (isMultiBot ? 'bot1/, bot2/, ...' : '.openclaw/')}`));
@@ -1675,6 +1812,64 @@ fi
1675
1812
  if (isMultiBot && channelKey === 'telegram') {
1676
1813
  console.log(chalk.yellow(`\n${isVi ? '📋 Xem hướng dẫn sau cài:' : '📋 Read post-install guide:'} ${path.join(projectDir, 'TELEGRAM-POST-INSTALL.md')}`));
1677
1814
  }
1815
+ } else {
1816
+ if (!isOpenClawInstalled()) {
1817
+ console.log(chalk.cyan(isVi
1818
+ ? '\n📦 Dang cai openclaw binary (npm install -g openclaw)...'
1819
+ : '\n📦 Installing openclaw binary (npm install -g openclaw)...'));
1820
+ if (!installGlobalPackage('openclaw@latest', { isVi, osChoice, displayName: 'openclaw' })) {
1821
+ process.exit(1);
1822
+ }
1823
+ console.log(chalk.green(isVi ? '✅ openclaw da cai xong!' : '✅ openclaw installed!'));
1824
+ }
1825
+
1826
+ await syncLocalConfigToHome(projectDir, isVi);
1827
+
1828
+ if (isMultiBot && channelKey === 'telegram') {
1829
+ installRelayPluginForProject(projectDir, isVi);
1830
+ }
1831
+
1832
+ if (osChoice === 'vps') {
1833
+ if (!isPm2Installed()) {
1834
+ console.log(chalk.cyan(isVi ? '\n📦 Dang cai PM2...' : '\n📦 Installing PM2...'));
1835
+ if (!installGlobalPackage('pm2@latest', { isVi, osChoice, displayName: 'PM2' })) {
1836
+ process.exit(1);
1837
+ }
1838
+ }
1839
+
1840
+ if (isMultiBot && channelKey === 'telegram') {
1841
+ execSync('pm2 start ecosystem.config.js && pm2 save', {
1842
+ cwd: projectDir,
1843
+ stdio: 'inherit',
1844
+ shell: true
1845
+ });
1846
+ console.log(chalk.green(`\n🎉 ${isVi ? 'Setup hoan tat! Multi-bot native dang chay qua PM2.' : 'Setup complete! Native multi-bot is running via PM2.'}`));
1847
+ console.log(chalk.gray(isVi ? ` Xem log: pm2 logs ${botName || 'openclaw-multibot'}` : ` View logs: pm2 logs ${botName || 'openclaw-multibot'}`));
1848
+ } else {
1849
+ const appName = botName || 'openclaw';
1850
+ execSync(`pm2 start "openclaw gateway run" --name "${appName}" --cwd "${projectDir.replace(/\\/g, '/')}" && pm2 save`, {
1851
+ cwd: projectDir,
1852
+ stdio: 'inherit',
1853
+ shell: true
1854
+ });
1855
+ console.log(chalk.green(`\n🎉 ${isVi ? 'Setup hoan tat! Bot native dang chay qua PM2.' : 'Setup complete! Native bot is running via PM2.'}`));
1856
+ console.log(chalk.gray(isVi ? ` Xem log: pm2 logs ${appName}` : ` View logs: pm2 logs ${appName}`));
1857
+ }
1858
+ } else {
1859
+ console.log(chalk.yellow(`\n${isVi ? 'Khoi dong native bot (foreground)...' : 'Starting native bot (foreground)...'}`));
1860
+ const child = spawn('openclaw', ['gateway', 'run'], {
1861
+ cwd: projectDir,
1862
+ stdio: 'inherit',
1863
+ shell: process.platform === 'win32'
1864
+ });
1865
+ child.on('close', (code) => process.exit(code ?? 0));
1866
+ return;
1867
+ }
1868
+
1869
+ console.log(chalk.cyan(`\n👉 ${isVi ? 'Native runtime da duoc cai san va khoi dong.' : 'Native runtime is installed and started.'}`));
1870
+ if (isMultiBot && channelKey === 'telegram') {
1871
+ console.log(chalk.yellow(`\n📋 ${isVi ? 'Xem huong dan sau cai:' : 'Read post-install guide:'} ${path.join(projectDir, 'TELEGRAM-POST-INSTALL.md')}`));
1872
+ }
1678
1873
  }
1679
1874
  }
1680
1875
 
@@ -1682,4 +1877,3 @@ main().catch(err => {
1682
1877
  console.error(chalk.red('Error:'), err);
1683
1878
  process.exit(1);
1684
1879
  });
1685
-
@@ -2,7 +2,7 @@
2
2
 
3
3
  Native installation is designed for users who cannot or prefer not to use Docker. This includes deployments on Shared Hosting (cPanel), low-tier VPS environments, or Windows desktops for direct access.
4
4
 
5
- OpenClaw v5.0.1+ natively supports deployment script generation for Windows, Linux, VPS, and Hosting environments.
5
+ OpenClaw v5.0.5+ natively supports deployment script generation for Windows, Linux, VPS, and Hosting environments.
6
6
 
7
7
  ---
8
8
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  Chế độ Native được thiết kế dành cho những ai không thể hoặc không muốn cài Docker. Chế độ này thường tối ưu cho Shared Hosting (cPanel), các gói VPS cấu hình rất thấp, hoặc cài trực tiếp trên máy Window để chạy cá nhân.
4
4
 
5
- OpenClaw v5.0.1+ tự động sinh sẵn các script cài đặt dành riêng cho Windows, Linux, VPS và Hosting.
5
+ OpenClaw v5.0.5+ tự động sinh sẵn các script cài đặt dành riêng cho Windows, Linux, VPS và Hosting.
6
6
 
7
7
  ---
8
8
 
package/index.html CHANGED
@@ -482,7 +482,7 @@
482
482
  <span style="font-size: 28px;">💻</span>
483
483
  <div>
484
484
  <h3 class="output-section__title" style="margin: 0;" data-vi="Download Script Cài đặt" data-en="Download Setup Script">Download Script Cài đặt</h3>
485
- <p style="font-size: 13px; color: var(--text-muted); margin: 4px 0 0;" id="native-instructions" data-vi="Tải file → chạy để cài bot trực tiếp trên máy (tự động cài 9Router/Ollama nếu đã chọn)" data-en="Download → run to install bot directly on machine (auto-installs 9Router/Ollama if selected)">Tải file → chạy để cài bot trực tiếp trên máy (tự động cài 9Router/Ollama nếu đã chọn)</p>
485
+ <p style="font-size: 13px; color: var(--text-muted); margin: 4px 0 0;" id="native-instructions" data-vi="Tải file → chạy để cài OpenClaw trực tiếp trên máy (tự động cài 9Router/Ollama nếu đã chọn)" data-en="Download → run to install OpenClaw directly on your machine (auto-installs 9Router/Ollama if selected)">Tải file → chạy để cài OpenClaw trực tiếp trên máy (tự động cài 9Router/Ollama nếu đã chọn)</p>
486
486
  </div>
487
487
  </div>
488
488
  <div style="display: flex; justify-content: center; margin: 20px 0 8px;">
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.0.4",
3
+ "version": "5.0.6",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "cli.js",
6
6
  "bin": {
7
7
  "create-openclaw-bot": "./cli.js"
8
8
  },
9
9
  "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1",
10
+ "test": "node tests/smoke-cli-logic.mjs",
11
11
  "bump": "node bump-version.mjs"
12
12
  },
13
13
  "keywords": [
package/setup.js CHANGED
@@ -728,8 +728,8 @@
728
728
  icon: '🐧',
729
729
  titleVi: 'Ubuntu / VPS — Khuyên dùng Native (Không Docker)',
730
730
  titleEn: 'Ubuntu / VPS — Recommended: Native (No Docker)',
731
- 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, PM2, 9Router/Ollama và giữ bot chạy liên tục sau reboot.',
732
- descEn: 'Run directly on machine — lower RAM, faster startup. Script auto-installs Node.js 20 LTS, PM2, 9Router/Ollama and keeps bot running across reboots.',
731
+ 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.',
732
+ 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.',
733
733
  deploy: 'native',
734
734
  badgeVi: '💻 Native + PM2',
735
735
  badgeEn: '💻 Native + PM2',
@@ -739,8 +739,8 @@
739
739
  icon: '🖥️',
740
740
  titleVi: 'Linux Desktop — Khuyên dùng Native',
741
741
  titleEn: 'Linux Desktop — Recommended: Native',
742
- descVi: 'Không cần Docker. Script tự cài Node.js 20 LTS nếu chưa có, rồi cài 9Router hoặc Ollama theo lựa chọn provider của bạn.',
743
- descEn: 'No Docker needed. Script auto-installs Node.js 20 LTS if missing, then installs 9Router or Ollama based on your provider choice.',
742
+ 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 khởi động bot ngay.',
743
+ 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.',
744
744
  deploy: 'native',
745
745
  badgeVi: '💻 Native',
746
746
  badgeEn: '💻 Native',
@@ -3452,12 +3452,17 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3452
3452
  } else if (state.nativeOs === 'linux') {
3453
3453
  const isDocker = state.deployMode === 'docker';
3454
3454
  scriptName = isDocker ? 'setup-openclaw-docker-macos.sh' : 'setup-openclaw-macos.sh';
3455
- const sh = [
3456
- '#!/usr/bin/env bash', 'set -e',
3457
- `echo "=== OpenClaw Setup — macOS${isDocker ? ' Docker' : ' Native'} ==="`,
3458
- 'command -v node > /dev/null 2>&1 || { echo "ERROR: Node.js chua cai! https://nodejs.org"; exit 1; }',
3459
- 'npm install -g openclaw@latest',
3460
- ];
3455
+ const sh = [
3456
+ '#!/usr/bin/env bash', 'set -e',
3457
+ `echo "=== OpenClaw Setup — macOS${isDocker ? ' Docker' : ' Native'} ==="`,
3458
+ 'command -v node > /dev/null 2>&1 || { echo "ERROR: Node.js chua cai! https://nodejs.org"; exit 1; }',
3459
+ 'mkdir -p "$HOME/.local/bin"',
3460
+ 'npm config set prefix "$HOME/.local"',
3461
+ 'export PATH="$HOME/.local/bin:$PATH"',
3462
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.zshrc"',
3463
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
3464
+ 'npm install -g openclaw@latest',
3465
+ ];
3461
3466
  providerLines(sh, 'sh');
3462
3467
  if (pluginCmd) sh.push(pluginCmd);
3463
3468
 
@@ -3474,16 +3479,21 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3474
3479
  // ─── VPS/Ubuntu PM2 .SH ──────────────────────────────────────────────────
3475
3480
  } else if (state.nativeOs === 'vps') {
3476
3481
  scriptName = 'setup-openclaw-vps.sh';
3477
- const vps = [
3478
- '#!/usr/bin/env bash', 'set -e',
3479
- `echo "=== OpenClaw Setup — Ubuntu/VPS${isMultiBot ? ` Multi-Bot (${state.botCount} bots)` : ''} ==="`,
3480
- '# Auto-install Node.js 20 LTS if missing',
3481
- 'if ! command -v node > /dev/null 2>&1; then',
3482
- ' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
3483
- ' sudo apt-get install -y nodejs',
3484
- 'fi',
3485
- 'npm install -g openclaw@latest pm2',
3486
- ];
3482
+ const vps = [
3483
+ '#!/usr/bin/env bash', 'set -e',
3484
+ `echo "=== OpenClaw Setup — Ubuntu/VPS${isMultiBot ? ` Multi-Bot (${state.botCount} bots)` : ''} ==="`,
3485
+ '# Auto-install Node.js 20 LTS if missing',
3486
+ 'if ! command -v node > /dev/null 2>&1; then',
3487
+ ' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
3488
+ ' sudo apt-get install -y nodejs',
3489
+ 'fi',
3490
+ 'mkdir -p "$HOME/.local/bin"',
3491
+ 'npm config set prefix "$HOME/.local"',
3492
+ 'export PATH="$HOME/.local/bin:$PATH"',
3493
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
3494
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
3495
+ 'npm install -g openclaw@latest pm2@latest',
3496
+ ];
3487
3497
  providerLines(vps, 'sh');
3488
3498
  if (pluginCmd) vps.push(pluginCmd);
3489
3499
 
@@ -3509,15 +3519,20 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3509
3519
  // ─── Linux Desktop .SH ───────────────────────────────────────────────────
3510
3520
  } else if (state.nativeOs === 'linux-desktop') {
3511
3521
  scriptName = 'setup-openclaw-linux.sh';
3512
- const lnx = [
3513
- '#!/usr/bin/env bash', 'set -e',
3514
- `echo "=== OpenClaw Setup — Linux Desktop${isMultiBot ? ' Multi-Bot' : ''} ==="`,
3515
- 'if ! command -v node > /dev/null 2>&1; then',
3516
- ' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
3517
- ' sudo apt-get install -y nodejs',
3518
- 'fi',
3519
- 'npm install -g openclaw@latest',
3520
- ];
3522
+ const lnx = [
3523
+ '#!/usr/bin/env bash', 'set -e',
3524
+ `echo "=== OpenClaw Setup — Linux Desktop${isMultiBot ? ' Multi-Bot' : ''} ==="`,
3525
+ 'if ! command -v node > /dev/null 2>&1; then',
3526
+ ' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
3527
+ ' sudo apt-get install -y nodejs',
3528
+ 'fi',
3529
+ 'mkdir -p "$HOME/.local/bin"',
3530
+ 'npm config set prefix "$HOME/.local"',
3531
+ 'export PATH="$HOME/.local/bin:$PATH"',
3532
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
3533
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
3534
+ 'npm install -g openclaw@latest',
3535
+ ];
3521
3536
  providerLines(lnx, 'sh');
3522
3537
  if (pluginCmd) lnx.push(pluginCmd);
3523
3538
 
@@ -0,0 +1,160 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+
4
+ const root = process.cwd();
5
+ const cli = fs.readFileSync(path.join(root, 'cli.js'), 'utf8');
6
+ const setup = fs.readFileSync(path.join(root, 'setup.js'), 'utf8');
7
+ const indexHtml = fs.readFileSync(path.join(root, 'index.html'), 'utf8');
8
+
9
+ function expect(condition, message) {
10
+ if (!condition) {
11
+ throw new Error(message);
12
+ }
13
+ }
14
+
15
+ function expectMatch(source, regex, message) {
16
+ expect(regex.test(source), message);
17
+ }
18
+
19
+ function expectOrder(source, before, after, message) {
20
+ const beforeIndex = source.indexOf(before);
21
+ const afterIndex = source.indexOf(after);
22
+ expect(beforeIndex !== -1, `${message} (missing first marker)`);
23
+ expect(afterIndex !== -1, `${message} (missing second marker)`);
24
+ expect(beforeIndex < afterIndex, message);
25
+ }
26
+
27
+ const checks = [];
28
+
29
+ checks.push(() => expectMatch(
30
+ cli,
31
+ /const deployModeDefault = \(osChoice === 'ubuntu' \|\| osChoice === 'vps'\) \? 'native' : 'docker';/,
32
+ 'Ubuntu/VPS must default to native deploy mode'
33
+ ));
34
+
35
+ checks.push(() => expectMatch(
36
+ cli,
37
+ /if \(deployMode === 'docker' && !isDockerInstalled\(\)\)/,
38
+ 'Docker branch must still auto-install Docker when missing'
39
+ ));
40
+
41
+ checks.push(() => expectMatch(
42
+ cli,
43
+ /if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\('openclaw@latest', \{ isVi, osChoice, displayName: 'openclaw' \}\)/,
44
+ 'Native branch must auto-install openclaw'
45
+ ));
46
+
47
+ checks.push(() => expectMatch(
48
+ cli,
49
+ /if \(osChoice === 'vps'\) \{[\s\S]*installGlobalPackage\('pm2@latest', \{ isVi, osChoice, displayName: 'PM2' \}\)/,
50
+ 'VPS native branch must auto-install PM2'
51
+ ));
52
+
53
+ checks.push(() => expectMatch(
54
+ cli,
55
+ /function ensureUserWritableGlobalNpm\(\{ isVi, osChoice \}\) \{[\s\S]*process\.env\.npm_config_prefix = npmInfo\.prefixDir[\s\S]*npm config set prefix "\$\{npmInfo\.prefixDir\.replace/s,
56
+ 'Native CLI must configure a user-writable npm global prefix for non-Windows installs'
57
+ ));
58
+
59
+ checks.push(() => expectMatch(
60
+ cli,
61
+ /execSync\('pm2 start ecosystem\.config\.js && pm2 save'/,
62
+ 'Native Telegram multi-bot must start through PM2 ecosystem'
63
+ ));
64
+
65
+ checks.push(() => expectMatch(
66
+ cli,
67
+ /execSync\(`pm2 start "openclaw gateway run" --name "\$\{appName\}" --cwd "\$\{projectDir\.replace\(\/\\\\\/g, '\/'\)\}" && pm2 save`/,
68
+ 'Native single-bot VPS must start gateway through PM2'
69
+ ));
70
+
71
+ checks.push(() => expectMatch(
72
+ cli,
73
+ /const child = spawn\('openclaw', \['gateway', 'run'\], \{/,
74
+ 'Native desktop flows must start openclaw in foreground'
75
+ ));
76
+
77
+ checks.push(() => expectOrder(
78
+ cli,
79
+ "await syncLocalConfigToHome(projectDir, isVi);",
80
+ "installRelayPluginForProject(projectDir, isVi);",
81
+ 'Relay plugin install must happen after config sync in native flow'
82
+ ));
83
+
84
+ checks.push(() => expectMatch(
85
+ cli,
86
+ /const pm2Apps = \[[\s\S]*args: 'gateway run'/,
87
+ 'Native multi-bot ecosystem must run one gateway process'
88
+ ));
89
+
90
+ checks.push(() => expectMatch(
91
+ setup,
92
+ /Linux Desktop[^]*Script tự cài Node\.js 20 LTS nếu chưa có, cài OpenClaw CLI[^]*khởi động bot ngay/s,
93
+ 'Web wizard Linux Desktop advisory must mention auto-install OpenClaw and immediate start'
94
+ ));
95
+
96
+ checks.push(() => expectMatch(
97
+ setup,
98
+ /Ubuntu \/ VPS[^]*Script tự cài Node\.js 20 LTS, OpenClaw CLI, PM2[^]*giữ bot chạy liên tục sau reboot/s,
99
+ 'Web wizard VPS advisory must mention OpenClaw CLI and PM2'
100
+ ));
101
+
102
+ checks.push(() => expectMatch(
103
+ indexHtml,
104
+ /Tải file → chạy để cài OpenClaw trực tiếp trên máy/,
105
+ 'Native download card copy must mention OpenClaw direct install'
106
+ ));
107
+
108
+ checks.push(() => expectMatch(
109
+ setup,
110
+ /if \(state\.nativeOs === 'win'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-win\.bat' : 'setup-openclaw-win\.bat';[\s\S]*npm install -g openclaw@latest[\s\S]*openclaw gateway run/s,
111
+ 'Windows native/docker script generation must use the correct file name and start command'
112
+ ));
113
+
114
+ checks.push(() => expectMatch(
115
+ setup,
116
+ /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@latest[\s\S]*openclaw gateway run/s,
117
+ 'macOS script generation must use the correct file name and start command'
118
+ ));
119
+
120
+ checks.push(() => expectMatch(
121
+ setup,
122
+ /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@latest pm2@latest[\s\S]*pm2 save && pm2 startup/s,
123
+ 'VPS native script generation must install openclaw+pm2 and persist PM2 startup'
124
+ ));
125
+
126
+ checks.push(() => expectMatch(
127
+ setup,
128
+ /else if \(state\.nativeOs === 'vps'\) \{[\s\S]*pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw-multibot/s,
129
+ 'VPS multi-bot native script must start the shared gateway via PM2'
130
+ ));
131
+
132
+ checks.push(() => expectMatch(
133
+ setup,
134
+ /else if \(state\.nativeOs === 'vps'\) \{[\s\S]*pm2 start --name openclaw -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw/s,
135
+ 'VPS single-bot native script must start one bot via PM2'
136
+ ));
137
+
138
+ checks.push(() => expectMatch(
139
+ setup,
140
+ /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@latest[\s\S]*openclaw gateway run/s,
141
+ 'Linux Desktop native script generation must install openclaw and run the gateway'
142
+ ));
143
+
144
+ checks.push(() => expectMatch(
145
+ setup,
146
+ /instrEl\.innerHTML = state\.nativeOs === 'win'[\s\S]*double-click[\s\S]*chmod \+x \$\{scriptName\} && \.\/\$\{scriptName\}/s,
147
+ 'Native instructions must show double-click for Windows and chmod for shell scripts'
148
+ ));
149
+
150
+ checks.push(() => expectMatch(
151
+ setup,
152
+ /steps\.push\(isVi \? '.*Cài OpenClaw CLI.*' : '.*Install OpenClaw CLI.*'\);/s,
153
+ 'Auto-steps summary must mention OpenClaw CLI installation'
154
+ ));
155
+
156
+ for (const check of checks) {
157
+ check();
158
+ }
159
+
160
+ console.log(`Smoke checks passed: ${checks.length}`);