create-openclaw-bot 5.1.3 → 5.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,6 +1,22 @@
1
1
  # Changelog (English)
2
2
 
3
3
 
4
+ ## [5.1.5] — 2026-04-06
5
+
6
+ ### 🐞 Fix Native PM2 9Router Startup
7
+
8
+ - **Fix**: Replaced shell string execution (`execSync`) with strict array arguments (`execFileSync`) when starting 9Router and its background sync script via PM2 on native systems. This guarantees reliable process spawning across both Linux (VPS) and Windows environments without PM2 shell-parsing errors on quotes or path spaces.
9
+ - **Improved**: PM2 now explicitly runs the global `9router` binary via `--interpreter none` and the sync script via the current NodeJS runtime using `--interpreter process.execPath`.
10
+
11
+
12
+ ## [5.1.4] — 2026-04-06
13
+
14
+ ### 🐞 Fix CLI Startup BOM Error & Improve Docker Timeout Patch
15
+
16
+ - **Fix CLI BOM**: Removed the unexpected byte order mark (BOM) `\uFEFF` at the beginning of `cli.js` which could cause the shebang `#!/usr/bin/env node` to fail resolving or cause SyntaxErrors in certain environments
17
+ - **Improve Docker Timeout Patching**: The backend timeout override injection (`300s`) during Docker build now defensively scans all `.js` files in the `openclaw/dist` directory rather than trying to fuzzy-find a specific `gateway-cli-*` hash. This ensures the patch succeeds across different OpenClaw backend builds without noisy console warnings
18
+
19
+
4
20
  ## [5.1.3] — 2026-04-06
5
21
 
6
22
  ### 🐜 Fix Docker Compose Variable Interpolation Leak
package/CHANGELOG.vi.md CHANGED
@@ -1,6 +1,22 @@
1
1
  # Changelog (Tiếng Việt)
2
2
 
3
3
 
4
+ ## [5.1.5] — 2026-04-06
5
+
6
+ ### 🐞 Sửa lỗi PM2 khởi động 9Router trên Native
7
+
8
+ - **Fix**: Chuyển từ việc chạy chuỗi bash (`execSync`) sang truyền mảng tham số rõ ràng (`execFileSync`) khi khởi động 9Router và script đồng bộ (sync) qua PM2. Đảm bảo PM2 luôn chạy được ứng dụng ổn định trên cả Linux (VPS) và Windows mà không bị vướng lỗi phân tích cú pháp dấu ngoặc kép hay khoảng trắng trong đường dẫn.
9
+ - **Tối ưu**: PM2 giờ đây sẽ phân tách rạch ròi bằng cách gọi file thực thi `9router` với tham số `--interpreter none`, và luôn chạy sync script bằng đúng phiên bản NodeJS nội tại thông qua `--interpreter process.execPath`.
10
+
11
+
12
+ ## [5.1.4] — 2026-04-06
13
+
14
+ ### 🐞 Sửa lỗi BOM khởi động CLI & Tối ưu luồng vá Timeout trên Docker
15
+
16
+ - **Sửa file CLI (BOM)**: Xóa tự động chèn BOM (`\uFEFF`) ở đầu file `cli.js`. Ký tự thừa này vốn làm hỏng shebang `#!/usr/bin/env node` và gây `SyntaxError: Unexpected token` trong nhiều môi trường khi chạy npx
17
+ - **Cải thiện Docker Timeout Patch**: Quá trình can thiệp timeout (`300s`) trong lúc build Docker giờ chuyển sang scan quét toàn bộ các file `.js` trong thư mục `openclaw/dist` thay vì cố tìm file trùng hash `gateway-cli-*`. Giúp bản vá luôn áp dụng thành công trên các phiên bản backend khác biệt mà không in ra warning rác trên console
18
+
19
+
4
20
  ## [5.1.3] — 2026-04-06
5
21
 
6
22
  ### 🐜 Lỗi lọt biến nội suy vào giao diện Docker Compose
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  # 🦞 OpenClaw Setup
4
4
 
5
5
  <p align="center">
6
- <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.3-0EA5E9?style=for-the-badge" alt="Version 5.1.3" /></a>
6
+ <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.5-0EA5E9?style=for-the-badge" alt="Version 5.1.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.1.3
27
+ ## 🆕 What's new in v5.1.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.
@@ -112,7 +112,7 @@ Run in your terminal → follow the interactive prompts → startup script is ge
112
112
  2. Open this repo as your workspace
113
113
  3. Paste into chat:
114
114
  ```
115
- Read SETUP.md and set up OpenClaw v5.1.3 for me.
115
+ Read SETUP.md and set up OpenClaw v5.1.5 for me.
116
116
  My bot token is X. Use 9Router (no API key).
117
117
  My project folder: <YOUR_PATH>
118
118
  ```
package/README.vi.md CHANGED
@@ -3,7 +3,7 @@
3
3
  # 🦞 OpenClaw Setup
4
4
 
5
5
  <p align="center">
6
- <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.3-0EA5E9?style=for-the-badge" alt="Version 5.1.3" /></a>
6
+ <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.5-0EA5E9?style=for-the-badge" alt="Version 5.1.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.1.3
27
+ ## 🆕 Có gì mới trong v5.1.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.
@@ -112,7 +112,7 @@ Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → sc
112
112
  2. Mở repo này làm workspace
113
113
  3. Paste vào chat:
114
114
  ```
115
- Read SETUP.md and set up OpenClaw v5.1.3 for me.
115
+ Read SETUP.md and set up OpenClaw v5.1.5 for me.
116
116
  My bot token is X. Use 9Router (no API key).
117
117
  My project folder: <THƯ_MỤC_CỦA_BẠN>
118
118
  ```
package/cli.js CHANGED
@@ -1,11 +1,11 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
3
  import { input, select, checkbox, confirm } from '@inquirer/prompts';
4
4
  import fs from 'fs-extra';
5
5
  import path from 'path';
6
6
  import os from 'os';
7
7
  import chalk from 'chalk';
8
- import { spawn, execSync } from 'child_process';
8
+ import { spawn, execSync, execFileSync } from 'child_process';
9
9
  const TELEGRAM_RELAY_PLUGIN_ID = 'openclaw-telegram-multibot-relay';
10
10
  // Use plain npm package name — clawhub: protocol not supported in all OpenClaw versions
11
11
  const TELEGRAM_RELAY_PLUGIN_SPEC = TELEGRAM_RELAY_PLUGIN_ID;
@@ -95,7 +95,7 @@ function quotePowerShellSingle(value) {
95
95
  return `'${String(value).replace(/'/g, "''")}'`;
96
96
  }
97
97
 
98
- function resolveWindowsCommand(command) {
98
+ function resolveWindowsCommand(command) {
99
99
  try {
100
100
  const output = execSync(`where.exe ${command}`, {
101
101
  stdio: ['ignore', 'pipe', 'ignore'],
@@ -363,6 +363,23 @@ setTimeout(sync, 5000);
363
363
  setInterval(sync, INTERVAL);`;
364
364
  }
365
365
 
366
+ function resolveCommandOnPath(command) {
367
+ if (process.platform === 'win32') {
368
+ return resolveWindowsCommand(command);
369
+ }
370
+
371
+ try {
372
+ return execSync(`command -v ${command}`, {
373
+ stdio: ['ignore', 'pipe', 'ignore'],
374
+ encoding: 'utf8',
375
+ shell: true,
376
+ env: process.env
377
+ }).trim() || command;
378
+ } catch {
379
+ return command;
380
+ }
381
+ }
382
+
366
383
  function indentBlock(text, spaces) {
367
384
  const prefix = ' '.repeat(spaces);
368
385
  return String(text)
@@ -606,30 +623,50 @@ function runPm2Save({ projectDir, isVi }) {
606
623
  }
607
624
  }
608
625
 
609
- function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
610
- const routerAppName = `${appName}-9router`;
611
- execSync(
612
- `pm2 start "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update" --name "${routerAppName}" --cwd "${projectDir.replace(/\\/g, '/')}"`,
613
- {
614
- cwd: projectDir,
615
- stdio: 'inherit',
616
- shell: true,
617
- env: process.env
618
- }
619
- );
620
- if (syncScriptPath) {
621
- const syncAppName = `${appName}-9router-sync`;
622
- execSync(
623
- `pm2 start "node ${syncScriptPath.replace(/\\/g, '/')}" --name "${syncAppName}" --cwd "${projectDir.replace(/\\/g, '/')}"`,
624
- {
625
- cwd: projectDir,
626
- stdio: 'inherit',
627
- shell: true,
628
- env: process.env
629
- }
630
- );
631
- }
632
- runPm2Save({ projectDir, isVi });
626
+ function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
627
+ const routerAppName = `${appName}-9router`;
628
+ const routerCommand = resolveCommandOnPath('9router');
629
+ execFileSync('pm2', [
630
+ 'start',
631
+ routerCommand,
632
+ '--name',
633
+ routerAppName,
634
+ '--cwd',
635
+ projectDir.replace(/\\/g, '/'),
636
+ '--interpreter',
637
+ 'none',
638
+ '--',
639
+ '-n',
640
+ '-t',
641
+ '-l',
642
+ '-H',
643
+ '0.0.0.0',
644
+ '-p',
645
+ '20128',
646
+ '--skip-update'
647
+ ], {
648
+ cwd: projectDir,
649
+ stdio: 'inherit',
650
+ env: process.env
651
+ });
652
+ if (syncScriptPath) {
653
+ const syncAppName = `${appName}-9router-sync`;
654
+ execFileSync('pm2', [
655
+ 'start',
656
+ syncScriptPath.replace(/\\/g, '/'),
657
+ '--name',
658
+ syncAppName,
659
+ '--cwd',
660
+ projectDir.replace(/\\/g, '/'),
661
+ '--interpreter',
662
+ process.execPath
663
+ ], {
664
+ cwd: projectDir,
665
+ stdio: 'inherit',
666
+ env: process.env
667
+ });
668
+ }
669
+ runPm2Save({ projectDir, isVi });
633
670
  console.log(chalk.green(`\n✅ ${isVi ? '9Router da duoc khoi dong qua PM2.' : '9Router is running via PM2.'}`));
634
671
  console.log(chalk.gray(isVi ? ` Xem log: pm2 logs ${routerAppName}` : ` View logs: pm2 logs ${routerAppName}`));
635
672
  }
@@ -1261,7 +1298,7 @@ async function main() {
1261
1298
  '# Fix chat.send dropping resolved agent timeout into reply pipeline.',
1262
1299
  '# Without this, Telegram/WebChat paths fall back to an internal 300s default even when',
1263
1300
  '# agents.defaults.timeoutSeconds is higher in config.',
1264
- `RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const file=(fs.readdirSync(dir).find(n=>/^gateway-cli-.*\\.js$/.test(n))||'');if(!file){console.warn('gateway cli dist file not found; skipping timeout patch');process.exit(0);}const p=path.join(dir,file);let s=fs.readFileSync(p,'utf8');const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';if(s.includes(to)){process.exit(0);}if(!s.includes(from)){console.warn('chat.send patch anchor not found; skipping timeout patch');process.exit(0);}s=s.replace(from,to);fs.writeFileSync(p,s);"`,
1301
+ `RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const files=fs.readdirSync(dir).filter(n=>/\\.js$/.test(n));let patched=0;for(const file of files){const p=path.join(dir,file);let s='';try{s=fs.readFileSync(p,'utf8');}catch{continue;}if(s.includes(to)||!s.includes(from))continue;s=s.replace(from,to);fs.writeFileSync(p,s);patched++;}if(!patched){process.exit(0);}"`,
1265
1302
  '',
1266
1303
  'WORKDIR /root/.openclaw',
1267
1304
  '',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.1.3",
3
+ "version": "5.1.5",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/setup.js CHANGED
@@ -1827,7 +1827,7 @@ RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /v
1827
1827
 
1828
1828
  ARG CACHEBUST=${Date.now()}
1829
1829
  RUN npm install -g openclaw@latest${skillLines}${browserInstallLines}
1830
- RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const file=(fs.readdirSync(dir).find(n=>/^gateway-cli-.*\\.js$/.test(n))||'');if(!file){console.warn('gateway cli dist file not found; skipping timeout patch');process.exit(0);}const p=path.join(dir,file);let s=fs.readFileSync(p,'utf8');const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';if(s.includes(to)){process.exit(0);}if(!s.includes(from)){console.warn('chat.send patch anchor not found; skipping timeout patch');process.exit(0);}s=s.replace(from,to);fs.writeFileSync(p,s);}"
1830
+ RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const files=fs.readdirSync(dir).filter(n=>/\\.js$/.test(n));let patched=0;for(const file of files){const p=path.join(dir,file);let s='';try{s=fs.readFileSync(p,'utf8');}catch{continue;}if(s.includes(to)||!s.includes(from))continue;s=s.replace(from,to);fs.writeFileSync(p,s);patched++;}if(!patched){process.exit(0);}"
1831
1831
  WORKDIR /root/.openclaw
1832
1832
 
1833
1833
  EXPOSE 18791
@@ -5,6 +5,7 @@ const root = process.cwd();
5
5
  const cli = fs.readFileSync(path.join(root, 'cli.js'), 'utf8');
6
6
  const setup = fs.readFileSync(path.join(root, 'setup.js'), 'utf8');
7
7
  const indexHtml = fs.readFileSync(path.join(root, 'index.html'), 'utf8');
8
+ const cliBytes = fs.readFileSync(path.join(root, 'cli.js'));
8
9
 
9
10
  function expect(condition, message) {
10
11
  if (!condition) {
@@ -32,6 +33,11 @@ checks.push(() => expectMatch(
32
33
  'Ubuntu/VPS must default to native deploy mode'
33
34
  ));
34
35
 
36
+ checks.push(() => expect(
37
+ !(cliBytes[0] === 0xef && cliBytes[1] === 0xbb && cliBytes[2] === 0xbf),
38
+ 'CLI entrypoint must not include a UTF-8 BOM before the shebang'
39
+ ));
40
+
35
41
  checks.push(() => expectMatch(
36
42
  cli,
37
43
  /if \(deployMode === 'docker' && !isDockerInstalled\(\)\)/,
@@ -189,7 +195,7 @@ checks.push(() => expectMatch(
189
195
 
190
196
  checks.push(() => expectMatch(
191
197
  cli,
192
- /function startNative9RouterPm2\(\{ isVi, projectDir, appName, syncScriptPath \}\) \{[\s\S]*9router -n -t -l -H 0\.0\.0\.0 -p 20128 --skip-update[\s\S]*9router-sync[\s\S]*runPm2Save\(\{ projectDir, isVi \}\)/s,
198
+ /function startNative9RouterPm2\(\{ isVi, projectDir, appName, syncScriptPath \}\) \{[\s\S]*resolveCommandOnPath\('9router'\)[\s\S]*execFileSync\('pm2'[\s\S]*--interpreter'?,?[\s\S]*none[\s\S]*--skip-update[\s\S]*syncScriptPath\.replace\(\/\\\\\/g, '\/'\)[\s\S]*process\.execPath[\s\S]*runPm2Save\(\{ projectDir, isVi \}\)/s,
193
199
  'VPS native 9Router flow must start a standalone 9Router dashboard on port 20128 via PM2'
194
200
  ));
195
201
 
@@ -312,8 +318,8 @@ checks.push(() => expectMatch(
312
318
 
313
319
  checks.push(() => expectMatch(
314
320
  cli,
315
- /readdirSync\(dir\)\.find\(n=>\/\^gateway-cli-.*\\\\\.js\$\/\.test\(n\)\)[\s\S]*skipping timeout patch/,
316
- 'Dockerfile patching in CLI must resolve gateway-cli dist files dynamically instead of hardcoding one hash'
321
+ /const files=fs\.readdirSync\(dir\)\.filter\(n=>\/\\\\\.js\$\/\.test\(n\)\)[\s\S]*let patched=0[\s\S]*if\(!patched\)\{process\.exit\(0\);\}/,
322
+ 'Dockerfile patching in CLI must scan all OpenClaw dist JS files and silently skip when no timeout patch anchor exists'
317
323
  ));
318
324
 
319
325
  checks.push(() => expect(
@@ -323,8 +329,8 @@ checks.push(() => expect(
323
329
 
324
330
  checks.push(() => expectMatch(
325
331
  setup,
326
- /readdirSync\(dir\)\.find\(n=>\/\^gateway-cli-.*\\\\\.js\$\/\.test\(n\)\)[\s\S]*skipping timeout patch/,
327
- 'Dockerfile patching in setup.js must resolve gateway-cli dist files dynamically instead of hardcoding one hash'
332
+ /const files=fs\.readdirSync\(dir\)\.filter\(n=>\/\\\\\.js\$\/\.test\(n\)\)[\s\S]*let patched=0[\s\S]*if\(!patched\)\{process\.exit\(0\);\}/,
333
+ 'Dockerfile patching in setup.js must scan all OpenClaw dist JS files and silently skip when no timeout patch anchor exists'
328
334
  ));
329
335
 
330
336
  checks.push(() => expect(
package/tmp_diff.patch ADDED
@@ -0,0 +1,114 @@
1
+ diff --git a/cli.js b/cli.js
2
+ index e0bbb01..4d1b591 100644
3
+ --- a/cli.js
4
+ +++ b/cli.js
5
+ @@ -5,7 +5,7 @@ import fs from 'fs-extra';
6
+ import path from 'path';
7
+ import os from 'os';
8
+ import chalk from 'chalk';
9
+ -import { spawn, execSync } from 'child_process';
10
+ +import { spawn, execSync, execFileSync } from 'child_process';
11
+ const TELEGRAM_RELAY_PLUGIN_ID = 'openclaw-telegram-multibot-relay';
12
+ // Use plain npm package name ΓÇö clawhub: protocol not supported in all OpenClaw versions
13
+ const TELEGRAM_RELAY_PLUGIN_SPEC = TELEGRAM_RELAY_PLUGIN_ID;
14
+ @@ -363,6 +363,23 @@ setTimeout(sync, 5000);
15
+ setInterval(sync, INTERVAL);`;
16
+ }
17
+
18
+ +function resolveCommandOnPath(command) {
19
+ + if (process.platform === 'win32') {
20
+ + return resolveWindowsCommand(command);
21
+ + }
22
+ +
23
+ + try {
24
+ + return execSync(`command -v ${command}`, {
25
+ + stdio: ['ignore', 'pipe', 'ignore'],
26
+ + encoding: 'utf8',
27
+ + shell: true,
28
+ + env: process.env
29
+ + }).trim() || command;
30
+ + } catch {
31
+ + return command;
32
+ + }
33
+ +}
34
+ +
35
+ function indentBlock(text, spaces) {
36
+ const prefix = ' '.repeat(spaces);
37
+ return String(text)
38
+ @@ -608,26 +625,46 @@ function runPm2Save({ projectDir, isVi }) {
39
+
40
+ function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
41
+ const routerAppName = `${appName}-9router`;
42
+ - execSync(
43
+ - `pm2 start "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update" --name "${routerAppName}" --cwd "${projectDir.replace(/\\/g, '/')}"`,
44
+ - {
45
+ + const routerCommand = resolveCommandOnPath('9router');
46
+ + execFileSync('pm2', [
47
+ + 'start',
48
+ + routerCommand,
49
+ + '--name',
50
+ + routerAppName,
51
+ + '--cwd',
52
+ + projectDir.replace(/\\/g, '/'),
53
+ + '--interpreter',
54
+ + 'none',
55
+ + '--',
56
+ + '-n',
57
+ + '-t',
58
+ + '-l',
59
+ + '-H',
60
+ + '0.0.0.0',
61
+ + '-p',
62
+ + '20128',
63
+ + '--skip-update'
64
+ + ], {
65
+ + cwd: projectDir,
66
+ + stdio: 'inherit',
67
+ + env: process.env
68
+ + });
69
+ + if (syncScriptPath) {
70
+ + const syncAppName = `${appName}-9router-sync`;
71
+ + execFileSync('pm2', [
72
+ + 'start',
73
+ + syncScriptPath.replace(/\\/g, '/'),
74
+ + '--name',
75
+ + syncAppName,
76
+ + '--cwd',
77
+ + projectDir.replace(/\\/g, '/'),
78
+ + '--interpreter',
79
+ + process.execPath
80
+ + ], {
81
+ cwd: projectDir,
82
+ stdio: 'inherit',
83
+ - shell: true,
84
+ env: process.env
85
+ - }
86
+ - );
87
+ - if (syncScriptPath) {
88
+ - const syncAppName = `${appName}-9router-sync`;
89
+ - execSync(
90
+ - `pm2 start "node ${syncScriptPath.replace(/\\/g, '/')}" --name "${syncAppName}" --cwd "${projectDir.replace(/\\/g, '/')}"`,
91
+ - {
92
+ - cwd: projectDir,
93
+ - stdio: 'inherit',
94
+ - shell: true,
95
+ - env: process.env
96
+ - }
97
+ - );
98
+ + });
99
+ }
100
+ runPm2Save({ projectDir, isVi });
101
+ console.log(chalk.green(`\n✅ ${isVi ? '9Router da duoc khoi dong qua PM2.' : '9Router is running via PM2.'}`));
102
+ diff --git a/tests/smoke-cli-logic.mjs b/tests/smoke-cli-logic.mjs
103
+ index 3ab996c..7fd80f8 100644
104
+ --- a/tests/smoke-cli-logic.mjs
105
+ +++ b/tests/smoke-cli-logic.mjs
106
+ @@ -195,7 +195,7 @@ checks.push(() => expectMatch(
107
+
108
+ checks.push(() => expectMatch(
109
+ cli,
110
+ - /function startNative9RouterPm2\(\{ isVi, projectDir, appName, syncScriptPath \}\) \{[\s\S]*9router -n -t -l -H 0\.0\.0\.0 -p 20128 --skip-update[\s\S]*9router-sync[\s\S]*runPm2Save\(\{ projectDir, isVi \}\)/s,
111
+ + /function startNative9RouterPm2\(\{ isVi, projectDir, appName, syncScriptPath \}\) \{[\s\S]*resolveCommandOnPath\('9router'\)[\s\S]*execFileSync\('pm2'[\s\S]*--interpreter'?,?[\s\S]*none[\s\S]*--skip-update[\s\S]*syncScriptPath\.replace\(\/\\\\\/g, '\/'\)[\s\S]*process\.execPath[\s\S]*runPm2Save\(\{ projectDir, isVi \}\)/s,
112
+ 'VPS native 9Router flow must start a standalone 9Router dashboard on port 20128 via PM2'
113
+ ));
114
+