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 +1 -1
- package/CHANGELOG.vi.md +1 -1
- package/README.md +3 -3
- package/README.vi.md +3 -3
- package/cli.js +209 -15
- package/docs/install-native.md +1 -1
- package/docs/install-native.vi.md +1 -1
- package/index.html +1 -1
- package/package.json +2 -2
- package/setup.js +44 -29
- package/tests/smoke-cli-logic.mjs +160 -0
package/CHANGELOG.md
CHANGED
package/CHANGELOG.vi.md
CHANGED
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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 =
|
|
1138
|
-
'
|
|
1139
|
-
`
|
|
1140
|
-
`
|
|
1141
|
-
`
|
|
1142
|
-
`
|
|
1143
|
-
`
|
|
1144
|
-
`
|
|
1145
|
-
`
|
|
1146
|
-
`
|
|
1147
|
-
'
|
|
1148
|
-
].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
|
|
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
|
-
|
package/docs/install-native.md
CHANGED
|
@@ -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.
|
|
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.
|
|
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
|
|
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.
|
|
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": "
|
|
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
|
|
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 và 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
|
-
'
|
|
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
|
-
'
|
|
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
|
-
'
|
|
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}`);
|