create-openclaw-bot 5.0.5 → 5.0.7
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 +195 -11
- package/docs/install-native.md +1 -1
- package/docs/install-native.vi.md +1 -1
- package/package.json +1 -1
- package/setup.js +52 -37
- package/tests/smoke-cli-logic.mjs +41 -5
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.7-0EA5E9?style=for-the-badge" alt="Version 5.0.7" /></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.7
|
|
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.7 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.7-0EA5E9?style=for-the-badge" alt="Version 5.0.7" /></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.7
|
|
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.7 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
|
@@ -50,14 +50,104 @@ function isPm2Installed() {
|
|
|
50
50
|
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function is9RouterInstalled() {
|
|
54
|
+
try {
|
|
55
|
+
execSync('9router --help', { stdio: 'ignore' });
|
|
56
|
+
return true;
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getUserNpmPrefixInfo() {
|
|
63
|
+
if (process.platform === 'win32') {
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const prefixDir = path.join(os.homedir(), '.local');
|
|
68
|
+
return {
|
|
69
|
+
prefixDir,
|
|
70
|
+
binDir: path.join(prefixDir, 'bin')
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function ensureBinDirOnPath(binDir) {
|
|
75
|
+
const delimiter = path.delimiter;
|
|
76
|
+
const pathParts = String(process.env.PATH || '').split(delimiter).filter(Boolean);
|
|
77
|
+
if (!pathParts.includes(binDir)) {
|
|
78
|
+
process.env.PATH = [binDir, ...pathParts].join(delimiter);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function appendLineIfMissing(filePath, line) {
|
|
83
|
+
let content = '';
|
|
84
|
+
if (fs.existsSync(filePath)) {
|
|
85
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!content.includes(line)) {
|
|
89
|
+
const prefix = content && !content.endsWith('\n') ? '\n' : '';
|
|
90
|
+
fs.appendFileSync(filePath, `${prefix}${line}\n`);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function ensureUserWritableGlobalNpm({ isVi, osChoice }) {
|
|
95
|
+
if (process.platform === 'win32') {
|
|
96
|
+
return true;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const npmInfo = getUserNpmPrefixInfo();
|
|
100
|
+
if (!npmInfo) {
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
try {
|
|
105
|
+
fs.ensureDirSync(npmInfo.binDir);
|
|
106
|
+
process.env.npm_config_prefix = npmInfo.prefixDir;
|
|
107
|
+
ensureBinDirOnPath(npmInfo.binDir);
|
|
108
|
+
|
|
109
|
+
execSync(`npm config set prefix "${npmInfo.prefixDir.replace(/"/g, '\\"')}"`, {
|
|
110
|
+
stdio: 'ignore',
|
|
111
|
+
shell: true,
|
|
112
|
+
env: process.env
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
appendLineIfMissing(path.join(os.homedir(), '.profile'), 'export PATH="$HOME/.local/bin:$PATH"');
|
|
116
|
+
appendLineIfMissing(
|
|
117
|
+
path.join(os.homedir(), osChoice === 'macos' ? '.zshrc' : '.bashrc'),
|
|
118
|
+
'export PATH="$HOME/.local/bin:$PATH"'
|
|
119
|
+
);
|
|
120
|
+
return true;
|
|
121
|
+
} catch {
|
|
122
|
+
console.log(chalk.yellow(isVi
|
|
123
|
+
? '⚠️ Không thể cấu hình npm global prefix trong ~/.local. Tiếp tục thử cài đặt trực tiếp.'
|
|
124
|
+
: '⚠️ Could not configure npm global prefix in ~/.local. Falling back to direct install.'));
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const userNpmInfo = getUserNpmPrefixInfo();
|
|
130
|
+
if (userNpmInfo) {
|
|
131
|
+
ensureBinDirOnPath(userNpmInfo.binDir);
|
|
132
|
+
}
|
|
133
|
+
|
|
53
134
|
function installGlobalPackage(pkg, { isVi, osChoice, displayName }) {
|
|
54
|
-
const installCommands =
|
|
55
|
-
|
|
56
|
-
|
|
135
|
+
const installCommands = [];
|
|
136
|
+
|
|
137
|
+
if (osChoice === 'windows') {
|
|
138
|
+
installCommands.push(`npm install -g ${pkg}`);
|
|
139
|
+
} else {
|
|
140
|
+
ensureUserWritableGlobalNpm({ isVi, osChoice });
|
|
141
|
+
installCommands.push(`npm install -g ${pkg}`);
|
|
142
|
+
const npmInfo = getUserNpmPrefixInfo();
|
|
143
|
+
if (npmInfo) {
|
|
144
|
+
installCommands.push(`npm install -g --prefix "${npmInfo.prefixDir.replace(/"/g, '\\"')}" ${pkg}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
57
147
|
|
|
58
148
|
for (const cmd of installCommands) {
|
|
59
149
|
try {
|
|
60
|
-
execSync(cmd, { stdio: 'inherit', shell: true });
|
|
150
|
+
execSync(cmd, { stdio: 'inherit', shell: true, env: process.env });
|
|
61
151
|
return true;
|
|
62
152
|
} catch {
|
|
63
153
|
// try next candidate
|
|
@@ -65,11 +155,75 @@ function installGlobalPackage(pkg, { isVi, osChoice, displayName }) {
|
|
|
65
155
|
}
|
|
66
156
|
|
|
67
157
|
console.log(chalk.yellow(isVi
|
|
68
|
-
? `⚠️ Không thể tự cài ${displayName}. Chạy thủ công: ${osChoice === 'windows' ? `npm install -g ${pkg}` : `
|
|
69
|
-
: `⚠️ Could not auto-install ${displayName}. Run manually: ${osChoice === 'windows' ? `npm install -g ${pkg}` : `
|
|
158
|
+
? `⚠️ 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}`}`
|
|
159
|
+
: `⚠️ Could not auto-install ${displayName}. Run manually: ${osChoice === 'windows' ? `npm install -g ${pkg}` : `npm config set prefix ~/.local && npm install -g ${pkg}`}`));
|
|
70
160
|
return false;
|
|
71
161
|
}
|
|
72
162
|
|
|
163
|
+
function extractFirstHttpUrl(text) {
|
|
164
|
+
const match = String(text || '').match(/https?:\/\/[^\s"'`]+/);
|
|
165
|
+
return match ? match[0] : null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function getTokenizedDashboardUrl(projectDir) {
|
|
169
|
+
try {
|
|
170
|
+
const output = execSync('openclaw dashboard', {
|
|
171
|
+
cwd: projectDir,
|
|
172
|
+
env: process.env,
|
|
173
|
+
encoding: 'utf8',
|
|
174
|
+
shell: true,
|
|
175
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
176
|
+
timeout: 15000
|
|
177
|
+
});
|
|
178
|
+
return extractFirstHttpUrl(output);
|
|
179
|
+
} catch (error) {
|
|
180
|
+
const combined = `${error?.stdout || ''}\n${error?.stderr || ''}`;
|
|
181
|
+
return extractFirstHttpUrl(combined);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function printNativeDashboardAccessInfo({ isVi, providerKey, projectDir, gatewayPort = 18791 }) {
|
|
186
|
+
const dashboardUrl = `http://localhost:${gatewayPort}`;
|
|
187
|
+
const tokenizedUrl = getTokenizedDashboardUrl(projectDir);
|
|
188
|
+
|
|
189
|
+
console.log(chalk.yellow(`\n🧭 ${isVi ? 'Dashboard OpenClaw:' : 'OpenClaw Dashboard:'} ${dashboardUrl}`));
|
|
190
|
+
|
|
191
|
+
if (tokenizedUrl) {
|
|
192
|
+
console.log(chalk.green(isVi
|
|
193
|
+
? ` → Mở link đã kèm token: ${tokenizedUrl}`
|
|
194
|
+
: ` → Open the tokenized link directly: ${tokenizedUrl}`));
|
|
195
|
+
} else {
|
|
196
|
+
console.log(chalk.gray(isVi
|
|
197
|
+
? ' → Nếu dashboard đòi Gateway Token, chạy: openclaw dashboard'
|
|
198
|
+
: ' → If the dashboard asks for a Gateway Token, run: openclaw dashboard'));
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
if (providerKey === '9router') {
|
|
202
|
+
console.log(chalk.yellow(`\n🔀 ${isVi ? '9Router Dashboard:' : '9Router Dashboard:'} http://localhost:20128/dashboard`));
|
|
203
|
+
console.log(chalk.gray(isVi
|
|
204
|
+
? ' → Mở dashboard 9Router → đăng nhập OAuth → kết nối provider miễn phí'
|
|
205
|
+
: ' → Open the 9Router dashboard → complete OAuth login → connect a free provider'));
|
|
206
|
+
console.log(chalk.gray(isVi
|
|
207
|
+
? ' → Sau khi login 9Router xong, bot sẽ tự dùng model smart-route qua http://localhost:20128/v1'
|
|
208
|
+
: ' → Once 9Router is logged in, the bot will use smart-route through http://localhost:20128/v1'));
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function startNative9RouterPm2({ isVi, projectDir, appName }) {
|
|
213
|
+
const routerAppName = `${appName}-9router`;
|
|
214
|
+
execSync(
|
|
215
|
+
`pm2 start "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update" --name "${routerAppName}" --cwd "${projectDir.replace(/\\/g, '/')}" && pm2 save`,
|
|
216
|
+
{
|
|
217
|
+
cwd: projectDir,
|
|
218
|
+
stdio: 'inherit',
|
|
219
|
+
shell: true,
|
|
220
|
+
env: process.env
|
|
221
|
+
}
|
|
222
|
+
);
|
|
223
|
+
console.log(chalk.green(`\n✅ ${isVi ? '9Router da duoc khoi dong qua PM2.' : '9Router is running via PM2.'}`));
|
|
224
|
+
console.log(chalk.gray(isVi ? ` Xem log: pm2 logs ${routerAppName}` : ` View logs: pm2 logs ${routerAppName}`));
|
|
225
|
+
}
|
|
226
|
+
|
|
73
227
|
async function syncLocalConfigToHome(projectDir, isVi) {
|
|
74
228
|
const homedir = os.homedir();
|
|
75
229
|
const globalClawDir = path.join(homedir, '.openclaw');
|
|
@@ -1109,7 +1263,7 @@ ${hasBrowserDesktop ? ` extra_hosts:
|
|
|
1109
1263
|
mode: 'merge',
|
|
1110
1264
|
providers: {
|
|
1111
1265
|
'9router': {
|
|
1112
|
-
baseUrl: 'http://9router:20128/v1',
|
|
1266
|
+
baseUrl: deployMode === 'native' ? 'http://localhost:20128/v1' : 'http://9router:20128/v1',
|
|
1113
1267
|
apiKey: 'sk-no-key',
|
|
1114
1268
|
api: 'openai-completions',
|
|
1115
1269
|
models: [
|
|
@@ -1355,7 +1509,7 @@ ${hasBrowserDesktop ? ` extra_hosts:
|
|
|
1355
1509
|
mode: 'merge',
|
|
1356
1510
|
providers: {
|
|
1357
1511
|
'9router': {
|
|
1358
|
-
baseUrl: 'http://9router:20128/v1',
|
|
1512
|
+
baseUrl: deployMode === 'native' ? 'http://localhost:20128/v1' : 'http://9router:20128/v1',
|
|
1359
1513
|
apiKey: 'sk-no-key',
|
|
1360
1514
|
api: 'openai-completions',
|
|
1361
1515
|
models: [
|
|
@@ -1712,7 +1866,7 @@ fi
|
|
|
1712
1866
|
: `Could not auto-sync config. Run manually:\n cp -rn ${localClawDir}/. ${globalClawDir}/`}`));
|
|
1713
1867
|
}
|
|
1714
1868
|
|
|
1715
|
-
console.log(chalk.cyan(`\n👉 ${isVi ? 'Đã tạo xong file cấu hình
|
|
1869
|
+
console.log(chalk.cyan(`\n👉 ${isVi ? 'Đã tạo xong file cấu hình Docker.' : 'Docker config files are ready.'}`));
|
|
1716
1870
|
console.log(chalk.gray(isVi
|
|
1717
1871
|
? ` Cấu trúc config: ${isMultiBot && channelKey === 'telegram' ? '.openclaw/ dùng chung + agents/workspace-*' : (isMultiBot ? 'bot1/, bot2/, ...' : '.openclaw/')}`
|
|
1718
1872
|
: ` Config layout: ${isMultiBot && channelKey === 'telegram' ? 'shared .openclaw/ with agents/workspace-*' : (isMultiBot ? 'bot1/, bot2/, ...' : '.openclaw/')}`));
|
|
@@ -1736,12 +1890,22 @@ fi
|
|
|
1736
1890
|
console.log(chalk.cyan(isVi
|
|
1737
1891
|
? '\n📦 Dang cai openclaw binary (npm install -g openclaw)...'
|
|
1738
1892
|
: '\n📦 Installing openclaw binary (npm install -g openclaw)...'));
|
|
1739
|
-
if (!installGlobalPackage('openclaw', { isVi, osChoice, displayName: 'openclaw' })) {
|
|
1893
|
+
if (!installGlobalPackage('openclaw@latest', { isVi, osChoice, displayName: 'openclaw' })) {
|
|
1740
1894
|
process.exit(1);
|
|
1741
1895
|
}
|
|
1742
1896
|
console.log(chalk.green(isVi ? '✅ openclaw da cai xong!' : '✅ openclaw installed!'));
|
|
1743
1897
|
}
|
|
1744
1898
|
|
|
1899
|
+
if (providerKey === '9router' && !is9RouterInstalled()) {
|
|
1900
|
+
console.log(chalk.cyan(isVi
|
|
1901
|
+
? '\n📦 Dang cai 9Router binary (npm install -g 9router)...'
|
|
1902
|
+
: '\n📦 Installing 9Router binary (npm install -g 9router)...'));
|
|
1903
|
+
if (!installGlobalPackage('9router@latest', { isVi, osChoice, displayName: '9Router' })) {
|
|
1904
|
+
process.exit(1);
|
|
1905
|
+
}
|
|
1906
|
+
console.log(chalk.green(isVi ? '✅ 9Router da cai xong!' : '✅ 9Router installed!'));
|
|
1907
|
+
}
|
|
1908
|
+
|
|
1745
1909
|
await syncLocalConfigToHome(projectDir, isVi);
|
|
1746
1910
|
|
|
1747
1911
|
if (isMultiBot && channelKey === 'telegram') {
|
|
@@ -1751,12 +1915,15 @@ fi
|
|
|
1751
1915
|
if (osChoice === 'vps') {
|
|
1752
1916
|
if (!isPm2Installed()) {
|
|
1753
1917
|
console.log(chalk.cyan(isVi ? '\n📦 Dang cai PM2...' : '\n📦 Installing PM2...'));
|
|
1754
|
-
if (!installGlobalPackage('pm2', { isVi, osChoice, displayName: 'PM2' })) {
|
|
1918
|
+
if (!installGlobalPackage('pm2@latest', { isVi, osChoice, displayName: 'PM2' })) {
|
|
1755
1919
|
process.exit(1);
|
|
1756
1920
|
}
|
|
1757
1921
|
}
|
|
1758
1922
|
|
|
1759
1923
|
if (isMultiBot && channelKey === 'telegram') {
|
|
1924
|
+
if (providerKey === '9router') {
|
|
1925
|
+
startNative9RouterPm2({ isVi, projectDir, appName: botName || 'openclaw-multibot' });
|
|
1926
|
+
}
|
|
1760
1927
|
execSync('pm2 start ecosystem.config.js && pm2 save', {
|
|
1761
1928
|
cwd: projectDir,
|
|
1762
1929
|
stdio: 'inherit',
|
|
@@ -1764,8 +1931,12 @@ fi
|
|
|
1764
1931
|
});
|
|
1765
1932
|
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.'}`));
|
|
1766
1933
|
console.log(chalk.gray(isVi ? ` Xem log: pm2 logs ${botName || 'openclaw-multibot'}` : ` View logs: pm2 logs ${botName || 'openclaw-multibot'}`));
|
|
1934
|
+
printNativeDashboardAccessInfo({ isVi, providerKey, projectDir });
|
|
1767
1935
|
} else {
|
|
1768
1936
|
const appName = botName || 'openclaw';
|
|
1937
|
+
if (providerKey === '9router') {
|
|
1938
|
+
startNative9RouterPm2({ isVi, projectDir, appName });
|
|
1939
|
+
}
|
|
1769
1940
|
execSync(`pm2 start "openclaw gateway run" --name "${appName}" --cwd "${projectDir.replace(/\\/g, '/')}" && pm2 save`, {
|
|
1770
1941
|
cwd: projectDir,
|
|
1771
1942
|
stdio: 'inherit',
|
|
@@ -1773,8 +1944,21 @@ fi
|
|
|
1773
1944
|
});
|
|
1774
1945
|
console.log(chalk.green(`\n🎉 ${isVi ? 'Setup hoan tat! Bot native dang chay qua PM2.' : 'Setup complete! Native bot is running via PM2.'}`));
|
|
1775
1946
|
console.log(chalk.gray(isVi ? ` Xem log: pm2 logs ${appName}` : ` View logs: pm2 logs ${appName}`));
|
|
1947
|
+
printNativeDashboardAccessInfo({ isVi, providerKey, projectDir });
|
|
1776
1948
|
}
|
|
1777
1949
|
} else {
|
|
1950
|
+
if (providerKey === '9router') {
|
|
1951
|
+
console.log(chalk.yellow(`\n${isVi ? 'Khoi dong 9Router native (background)...' : 'Starting native 9Router (background)...'}`));
|
|
1952
|
+
spawn('9router', ['-n', '-t', '-l', '-H', '0.0.0.0', '-p', '20128', '--skip-update'], {
|
|
1953
|
+
cwd: projectDir,
|
|
1954
|
+
detached: true,
|
|
1955
|
+
stdio: 'ignore',
|
|
1956
|
+
shell: process.platform === 'win32'
|
|
1957
|
+
}).unref();
|
|
1958
|
+
console.log(chalk.gray(isVi
|
|
1959
|
+
? ' 9Router dashboard: http://localhost:20128/dashboard'
|
|
1960
|
+
: ' 9Router dashboard: http://localhost:20128/dashboard'));
|
|
1961
|
+
}
|
|
1778
1962
|
console.log(chalk.yellow(`\n${isVi ? 'Khoi dong native bot (foreground)...' : 'Starting native bot (foreground)...'}`));
|
|
1779
1963
|
const child = spawn('openclaw', ['gateway', 'run'], {
|
|
1780
1964
|
cwd: projectDir,
|
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.7+ 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.7+ 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/package.json
CHANGED
package/setup.js
CHANGED
|
@@ -2836,18 +2836,18 @@ I am **${botName}**. When asked my name, I answer: _"I'm ${botName}"_.`;
|
|
|
2836
2836
|
const pluginCmd = allPlugins.length > 0 ? ('npm exec openclaw plugins install ' + allPlugins.join(' ')) : '';
|
|
2837
2837
|
|
|
2838
2838
|
// ─── Shared initializer (provider install) ───────────────────────────────
|
|
2839
|
-
function providerLines(arr, shell) {
|
|
2840
|
-
if (is9Router) {
|
|
2841
|
-
if (shell === 'bat') {
|
|
2842
|
-
arr.push('npm install -g 9router');
|
|
2843
|
-
arr.push('start "9Router" cmd /k "9router"');
|
|
2844
|
-
arr.push('timeout /t 5 /nobreak >nul');
|
|
2845
|
-
} else {
|
|
2846
|
-
arr.push('npm install -g 9router');
|
|
2847
|
-
arr.push('9router &');
|
|
2848
|
-
arr.push('sleep 3');
|
|
2849
|
-
}
|
|
2850
|
-
} else if (isOllama) {
|
|
2839
|
+
function providerLines(arr, shell) {
|
|
2840
|
+
if (is9Router) {
|
|
2841
|
+
if (shell === 'bat') {
|
|
2842
|
+
arr.push('npm install -g 9router');
|
|
2843
|
+
arr.push('start "9Router" cmd /k "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2844
|
+
arr.push('timeout /t 5 /nobreak >nul');
|
|
2845
|
+
} else {
|
|
2846
|
+
arr.push('npm install -g 9router');
|
|
2847
|
+
arr.push('nohup 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update >/tmp/9router.log 2>&1 &');
|
|
2848
|
+
arr.push('sleep 3');
|
|
2849
|
+
}
|
|
2850
|
+
} else if (isOllama) {
|
|
2851
2851
|
if (shell === 'bat') {
|
|
2852
2852
|
arr.push('where ollama >nul 2>&1 || (powershell -Command "Invoke-WebRequest -Uri https://ollama.com/download/OllamaSetup.exe -OutFile OllamaSetup.exe" && OllamaSetup.exe && del OllamaSetup.exe)');
|
|
2853
2853
|
arr.push('ollama pull ' + selectedModel);
|
|
@@ -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
|
|
|
@@ -40,16 +40,28 @@ checks.push(() => expectMatch(
|
|
|
40
40
|
|
|
41
41
|
checks.push(() => expectMatch(
|
|
42
42
|
cli,
|
|
43
|
-
/if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\('openclaw', \{ isVi, osChoice, displayName: 'openclaw' \}\)/,
|
|
43
|
+
/if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\('openclaw@latest', \{ isVi, osChoice, displayName: 'openclaw' \}\)/,
|
|
44
44
|
'Native branch must auto-install openclaw'
|
|
45
45
|
));
|
|
46
46
|
|
|
47
47
|
checks.push(() => expectMatch(
|
|
48
48
|
cli,
|
|
49
|
-
/if \(osChoice === 'vps'\) \{[\s\S]*installGlobalPackage\('pm2', \{ isVi, osChoice, displayName: 'PM2' \}\)/,
|
|
49
|
+
/if \(osChoice === 'vps'\) \{[\s\S]*installGlobalPackage\('pm2@latest', \{ isVi, osChoice, displayName: 'PM2' \}\)/,
|
|
50
50
|
'VPS native branch must auto-install PM2'
|
|
51
51
|
));
|
|
52
52
|
|
|
53
|
+
checks.push(() => expectMatch(
|
|
54
|
+
cli,
|
|
55
|
+
/if \(providerKey === '9router' && !is9RouterInstalled\(\)\) \{[\s\S]*installGlobalPackage\('9router@latest', \{ isVi, osChoice, displayName: '9Router' \}\)/,
|
|
56
|
+
'Native 9Router flow must auto-install 9Router'
|
|
57
|
+
));
|
|
58
|
+
|
|
59
|
+
checks.push(() => expectMatch(
|
|
60
|
+
cli,
|
|
61
|
+
/function ensureUserWritableGlobalNpm\(\{ isVi, osChoice \}\) \{[\s\S]*process\.env\.npm_config_prefix = npmInfo\.prefixDir[\s\S]*npm config set prefix "\$\{npmInfo\.prefixDir\.replace/s,
|
|
62
|
+
'Native CLI must configure a user-writable npm global prefix for non-Windows installs'
|
|
63
|
+
));
|
|
64
|
+
|
|
53
65
|
checks.push(() => expectMatch(
|
|
54
66
|
cli,
|
|
55
67
|
/execSync\('pm2 start ecosystem\.config\.js && pm2 save'/,
|
|
@@ -62,6 +74,24 @@ checks.push(() => expectMatch(
|
|
|
62
74
|
'Native single-bot VPS must start gateway through PM2'
|
|
63
75
|
));
|
|
64
76
|
|
|
77
|
+
checks.push(() => expectMatch(
|
|
78
|
+
cli,
|
|
79
|
+
/function printNativeDashboardAccessInfo\(\{ isVi, providerKey, projectDir, gatewayPort = 18791 \}\) \{[\s\S]*openclaw dashboard[\s\S]*9Router Dashboard:[\s\S]*localhost:20128\/dashboard/s,
|
|
80
|
+
'Native PM2 flow must expose dashboard access info and the tokenized dashboard command'
|
|
81
|
+
));
|
|
82
|
+
|
|
83
|
+
checks.push(() => expectMatch(
|
|
84
|
+
cli,
|
|
85
|
+
/baseUrl: deployMode === 'native' \? 'http:\/\/localhost:20128\/v1' : 'http:\/\/9router:20128\/v1'/,
|
|
86
|
+
'Native 9Router config must target localhost instead of the Docker hostname'
|
|
87
|
+
));
|
|
88
|
+
|
|
89
|
+
checks.push(() => expectMatch(
|
|
90
|
+
cli,
|
|
91
|
+
/function startNative9RouterPm2\(\{ isVi, projectDir, appName \}\) \{[\s\S]*9router -n -t -l -H 0\.0\.0\.0 -p 20128 --skip-update[\s\S]*pm2 save/s,
|
|
92
|
+
'VPS native 9Router flow must start a standalone 9Router dashboard on port 20128 via PM2'
|
|
93
|
+
));
|
|
94
|
+
|
|
65
95
|
checks.push(() => expectMatch(
|
|
66
96
|
cli,
|
|
67
97
|
/const child = spawn\('openclaw', \['gateway', 'run'\], \{/,
|
|
@@ -107,16 +137,22 @@ checks.push(() => expectMatch(
|
|
|
107
137
|
|
|
108
138
|
checks.push(() => expectMatch(
|
|
109
139
|
setup,
|
|
110
|
-
/else if \(state\.nativeOs === 'linux'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-macos\.sh' : 'setup-openclaw-macos\.sh';[\s\S]*npm install -g openclaw@latest[\s\S]*openclaw gateway run/s,
|
|
140
|
+
/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,
|
|
111
141
|
'macOS script generation must use the correct file name and start command'
|
|
112
142
|
));
|
|
113
143
|
|
|
114
144
|
checks.push(() => expectMatch(
|
|
115
145
|
setup,
|
|
116
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*scriptName = 'setup-openclaw-vps\.sh';[\s\S]*npm install -g openclaw@latest pm2[\s\S]*pm2 save && pm2 startup/s,
|
|
146
|
+
/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,
|
|
117
147
|
'VPS native script generation must install openclaw+pm2 and persist PM2 startup'
|
|
118
148
|
));
|
|
119
149
|
|
|
150
|
+
checks.push(() => expectMatch(
|
|
151
|
+
setup,
|
|
152
|
+
/function providerLines\(arr, shell\) \{[\s\S]*npm install -g 9router[\s\S]*9router -n -t -l -H 0\.0\.0\.0 -p 20128 --skip-update/s,
|
|
153
|
+
'Native script generation must install and start a standalone 9Router dashboard on port 20128'
|
|
154
|
+
));
|
|
155
|
+
|
|
120
156
|
checks.push(() => expectMatch(
|
|
121
157
|
setup,
|
|
122
158
|
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw-multibot/s,
|
|
@@ -131,7 +167,7 @@ checks.push(() => expectMatch(
|
|
|
131
167
|
|
|
132
168
|
checks.push(() => expectMatch(
|
|
133
169
|
setup,
|
|
134
|
-
/else if \(state\.nativeOs === 'linux-desktop'\) \{[\s\S]*scriptName = 'setup-openclaw-linux\.sh';[\s\S]*npm install -g openclaw@latest[\s\S]*openclaw gateway run/s,
|
|
170
|
+
/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,
|
|
135
171
|
'Linux Desktop native script generation must install openclaw and run the gateway'
|
|
136
172
|
));
|
|
137
173
|
|