create-openclaw-bot 5.0.9 → 5.1.1

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.
@@ -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.9+ natively supports deployment script generation for Windows, Linux, VPS, and Hosting environments.
5
+ OpenClaw v5.1.0+ 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.9+ tự động sinh sẵn các script cài đặt dành riêng cho Windows, Linux, VPS và Hosting.
5
+ OpenClaw v5.1.0+ 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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.0.9",
3
+ "version": "5.1.1",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/setup.js CHANGED
@@ -1,4 +1,4 @@
1
- /* ============================================
1
+ /* ============================================
2
2
  OpenClaw Setup Wizard — Logic v2
3
3
  Multi-model, Multi-plugin, Multi-channel
4
4
  ============================================ */
@@ -277,7 +277,7 @@
277
277
  envExtra: '',
278
278
  credSteps: [
279
279
  { textVi: '⚠️ Zalo Personal dùng <strong>unofficial API (zca-js)</strong> — chỉ nên dùng tài khoản phụ', textEn: '⚠️ Zalo Personal uses <strong>unofficial API (zca-js)</strong> — use an alternate account' },
280
- { textVi: 'Sau khi Docker chạy, chạy <code>docker exec -it openclaw-bot openclaw onboard</code> để <strong>quét QR code</strong> login Zalo.', textEn: 'After Docker starts, run <code>docker exec -it openclaw-bot openclaw onboard</code> to <strong>scan QR code</strong> and login Zalo. 1-time setup.' },
280
+ { textVi: 'Native setup sẽ tự chạy login copy QR về thư mục project. Nếu cần chạy lại thủ công, dùng <code>openclaw channels login --channel zalouser --verbose</code>.', textEn: 'Native setup now auto-runs the login flow and copies the QR into the project folder. If needed, rerun it manually with <code>openclaw channels login --channel zalouser --verbose</code>.' },
281
281
  ],
282
282
  channelConfig: {
283
283
  zalouser: {
@@ -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, 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.',
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ó, 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.',
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',
@@ -1520,7 +1520,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
1520
1520
  // Generate native script if native mode
1521
1521
  if (isNativeMode) generateNativeScript();
1522
1522
 
1523
- // Show/hide Zalo Personal onboard notice
1523
+ // Show/hide Zalo Personal login notice
1524
1524
  const zaloNotice = document.getElementById('zalo-onboard-notice');
1525
1525
  const isZaloPersonal = state.channel === 'zalo-personal';
1526
1526
  if (zaloNotice) {
@@ -1802,7 +1802,7 @@ model:
1802
1802
  const browserPrefix = hasBrowser
1803
1803
  ? 'socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 & '
1804
1804
  : '';
1805
- // Patch config on every startup to survive openclaw onboard overwrites
1805
+ // Patch config on every startup to keep gateway settings stable
1806
1806
  const patchCmd = `node -e \\"const fs=require('fs'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0'});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\" && `;
1807
1807
  // Auto-approve device pairing after gateway starts (required since v2026.3.x)
1808
1808
  const autoApproveCmd = '(while true; do sleep 5; openclaw devices approve --latest 2>/dev/null || true; done) & ';
@@ -1815,7 +1815,7 @@ RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /v
1815
1815
 
1816
1816
  ARG CACHEBUST=${Date.now()}
1817
1817
  RUN npm install -g openclaw@latest${skillLines}${browserInstallLines}
1818
- 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);}"
1818
+ 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);}"
1819
1819
  WORKDIR /root/.openclaw
1820
1820
 
1821
1821
  EXPOSE 18791
@@ -1841,17 +1841,34 @@ const sync = async () => {
1841
1841
  try {
1842
1842
  let db = {};
1843
1843
  try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
1844
- const a = (db.providerConnections || [])
1844
+ if (!db.combos) db.combos = [];
1845
+ const removeSmartRoute = () => {
1846
+ const next = db.combos.filter(x => x.id !== 'smart-route');
1847
+ if (next.length !== db.combos.length) {
1848
+ db.combos = next;
1849
+ fs.writeFileSync(p, JSON.stringify(db, null, 2));
1850
+ console.log('[sync-combo] Removed smart-route (no active providers)');
1851
+ }
1852
+ };
1853
+ const res = await fetch('http://localhost:20128/api/providers');
1854
+ if (!res.ok) { console.log('[sync-combo] API not ready, retrying...'); return; }
1855
+ const d = await res.json();
1856
+ const a = (d.connections || [])
1845
1857
  .filter(c => c && c.provider && c.isActive !== false && !c.disabled)
1846
1858
  .map(c => c.provider);
1847
- if (!a.length) return;
1859
+ if (!a.length) {
1860
+ removeSmartRoute();
1861
+ return;
1862
+ }
1848
1863
 
1849
1864
  const PREF = ['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];
1850
1865
  a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
1851
1866
 
1852
- const m = a.flatMap(p => PM[p] || []);
1853
- if (!m.length) return;
1854
- if (!db.combos) db.combos = [];
1867
+ const m = a.flatMap(pv => PM[pv] || []);
1868
+ if (!m.length) {
1869
+ removeSmartRoute();
1870
+ return;
1871
+ }
1855
1872
 
1856
1873
  const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
1857
1874
  const i = db.combos.findIndex(x => x.id === 'smart-route');
@@ -1868,7 +1885,7 @@ const sync = async () => {
1868
1885
  }
1869
1886
  } catch (e) { }
1870
1887
  };
1871
- sync();
1888
+ setTimeout(sync, 5000);
1872
1889
  setInterval(sync, INTERVAL);`;
1873
1890
 
1874
1891
  let compose;
@@ -1903,9 +1920,7 @@ ${dependsOn}${extraHosts} volumes:
1903
1920
  - -c
1904
1921
  - |
1905
1922
  npm install -g 9router
1906
- cat << 'CLAWEOF' > /tmp/sync.js
1907
- ${syncScript.replace(/\$/g, '$$$$').replace(/\n/g, '\n ')}
1908
- CLAWEOF
1923
+ node -e "require('fs').writeFileSync('/tmp/sync.js',${JSON.stringify(syncScript)})"
1909
1924
  node /tmp/sync.js > /tmp/sync.log 2>&1 &
1910
1925
  exec 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update
1911
1926
  environment:
@@ -2001,9 +2016,7 @@ ${extraHostsBlock}
2001
2016
  - -c
2002
2017
  - |
2003
2018
  npm install -g 9router
2004
- cat << 'CLAWEOF' > /tmp/sync.js
2005
- ${syncScript.replace(/\$/g, '$$$$').replace(/\n/g, '\n ')}
2006
- CLAWEOF
2019
+ node -e "require('fs').writeFileSync('/tmp/sync.js',${JSON.stringify(syncScript)})"
2007
2020
  node /tmp/sync.js > /tmp/sync.log 2>&1 &
2008
2021
  exec 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update
2009
2022
  environment:
@@ -2835,19 +2848,30 @@ I am **${botName}**. When asked my name, I answer: _"I'm ${botName}"_.`;
2835
2848
  if (isMultiBot && state.channel === 'telegram') allPlugins.push(relayPluginSpec);
2836
2849
  const pluginCmd = allPlugins.length > 0 ? ('npm exec openclaw plugins install ' + allPlugins.join(' ')) : '';
2837
2850
 
2851
+ function native9RouterSyncScriptContent() {
2852
+ return `const fs=require('fs');
2853
+ const path=require('path');
2854
+ const INTERVAL=30000;
2855
+ const p=path.join(process.env.HOME||process.env.USERPROFILE||'.','.9router','db.json');
2856
+ const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],claude-code:['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],cline:['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],iflow:['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],qwen:['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],kiro:['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],ollama:['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],glm:['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],minimax:['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],kimi:['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],deepseek:['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],xai:['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],mistral:['mistral/mistral-large-latest','mistral/codestral-latest'],groq:['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],cerebras:['cerebras/gpt-oss-120b'],alicode:['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],openai:['openai/gpt-4o','openai/gpt-4.1'],anthropic:['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],gemini:['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
2857
+ const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catch{}if(!db.combos)db.combos=[];const removeSmartRoute=()=>{const next=db.combos.filter(x=>x.id!=='smart-route');if(next.length!==db.combos.length){db.combos=next;fs.writeFileSync(p,JSON.stringify(db,null,2));}};const a=(db.providerConnections||[]).filter(c=>c&&c.provider&&c.isActive!==false&&!c.disabled).map(c=>c.provider);if(!a.length){removeSmartRoute();return;}const PREF=['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];a.sort((x,y)=>(PREF.indexOf(x)===-1?99:PREF.indexOf(x))-(PREF.indexOf(y)===-1?99:PREF.indexOf(y)));const m=a.flatMap(provider=>PM[provider]||[]);if(!m.length){removeSmartRoute();return;}const c={id:'smart-route',name:'smart-route',alias:'smart-route',models:m};const i=db.combos.findIndex(x=>x.id==='smart-route');if(i>=0){if(JSON.stringify(db.combos[i].models)!==JSON.stringify(c.models)){db.combos[i]=c;fs.writeFileSync(p,JSON.stringify(db,null,2));}}else{db.combos.push(c);fs.writeFileSync(p,JSON.stringify(db,null,2));}}catch{}};sync();setInterval(sync,INTERVAL);`;
2858
+ }
2859
+
2838
2860
  // ─── 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 -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) {
2861
+ function providerLines(arr, shell) {
2862
+ if (is9Router) {
2863
+ if (shell === 'bat') {
2864
+ arr.push('npm install -g 9router');
2865
+ arr.push('start "9Router" cmd /k "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
2866
+ arr.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
2867
+ arr.push('timeout /t 5 /nobreak >nul');
2868
+ } else {
2869
+ arr.push('npm install -g 9router');
2870
+ arr.push('nohup 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update >/tmp/9router.log 2>&1 &');
2871
+ arr.push('nohup node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &');
2872
+ arr.push('sleep 3');
2873
+ }
2874
+ } else if (isOllama) {
2851
2875
  if (shell === 'bat') {
2852
2876
  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
2877
  arr.push('ollama pull ' + selectedModel);
@@ -3022,6 +3046,7 @@ I am **${botName}**. When asked my name, I answer: _"I'm ${botName}"_.`;
3022
3046
  '.openclaw/auth-profiles.json': sharedNativeAuthProfilesContent(),
3023
3047
  'TELEGRAM-POST-INSTALL.md': buildTelegramPostInstallChecklist(),
3024
3048
  };
3049
+ if (is9Router) files['.openclaw/9router-smart-route-sync.js'] = native9RouterSyncScriptContent();
3025
3050
  const teamMd = isVi
3026
3051
  ? `# Doi Bot\n\n${multiBotAgentMetas.map((meta) => `## ${meta.name}\n- Vai tro: ${meta.desc}\n- Agent ID: \`${meta.agentId}\`\n- Telegram accountId: \`${meta.accountId}\`\n- Slash command: ${meta.slashCmd || '_(chua co)_'}\n- Tinh cach: ${meta.persona || '_(khong ghi ro)_'}`).join('\n\n')}\n\n## Quy uoc phoi hop\n- Tat ca bot trong doi biet ro vai tro cua nhau.\n- Neu user bao ban hoi mot bot khac, hay dung agent-to-agent noi bo thay vi doi Telegram chuyen tin cua bot.\n- Bot mo loi chi noi 1 cau ngan, sau do chuyen turn noi bo cho bot dich.\n- Bot dich phai tra loi cong khai bang chinh Telegram account cua minh trong cung chat/thread hien tai.\n- Neu can fallback, chi bot mo loi moi duoc phep tom tat thay.`
3027
3052
  : `# Bot Team\n\n${multiBotAgentMetas.map((meta) => `## ${meta.name}\n- Role: ${meta.desc}\n- Agent ID: \`${meta.agentId}\`\n- Telegram accountId: \`${meta.accountId}\`\n- Slash command: ${meta.slashCmd || '_(not set)_'}\n- Persona: ${meta.persona || '_(not specified)_'}`).join('\n\n')}\n\n## Coordination Rules\n- Every bot knows the full roster.\n- If the user asks you to consult another bot, use internal agent-to-agent handoff instead of waiting for Telegram bot-to-bot delivery.\n- The caller bot only sends one short opener, then hands off internally.\n- The target bot must publish the real answer with its own Telegram account in the same chat/thread.\n- If a fallback is needed, only the caller bot may summarize on behalf of the target.`;
@@ -3372,6 +3397,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3372
3397
  files[`${base}/.openclaw/openclaw.json`] = botConfigContent(botIndex);
3373
3398
  files[`${base}/.openclaw/exec-approvals.json`] = botExecApprovalsContent(botIndex);
3374
3399
  files[`${base}/.openclaw/auth-profiles.json`] = botAuthProfilesContent(botIndex);
3400
+ if (is9Router) files[`${base}/.openclaw/9router-smart-route-sync.js`] = native9RouterSyncScriptContent();
3375
3401
  files[`${base}/.openclaw/agents/${agentId}.yaml`] = botAgentYamlContent(botIndex);
3376
3402
  files[`${base}/.openclaw/agents/${agentId}/agent/auth-profiles.json`] = botAuthProfilesContent(botIndex);
3377
3403
  Object.entries(botWorkspaceFiles(botIndex)).forEach(([name, content]) => {
@@ -3452,17 +3478,17 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3452
3478
  } else if (state.nativeOs === 'linux') {
3453
3479
  const isDocker = state.deployMode === 'docker';
3454
3480
  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
- '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
- ];
3481
+ const sh = [
3482
+ '#!/usr/bin/env bash', 'set -e',
3483
+ `echo "=== OpenClaw Setup — macOS${isDocker ? ' Docker' : ' Native'} ==="`,
3484
+ 'command -v node > /dev/null 2>&1 || { echo "ERROR: Node.js chua cai! https://nodejs.org"; exit 1; }',
3485
+ 'mkdir -p "$HOME/.local/bin"',
3486
+ 'npm config set prefix "$HOME/.local"',
3487
+ 'export PATH="$HOME/.local/bin:$PATH"',
3488
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.zshrc"',
3489
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
3490
+ 'npm install -g openclaw@latest',
3491
+ ];
3466
3492
  providerLines(sh, 'sh');
3467
3493
  if (pluginCmd) sh.push(pluginCmd);
3468
3494
 
@@ -3479,21 +3505,21 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3479
3505
  // ─── VPS/Ubuntu PM2 .SH ──────────────────────────────────────────────────
3480
3506
  } else if (state.nativeOs === 'vps') {
3481
3507
  scriptName = 'setup-openclaw-vps.sh';
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
- ];
3508
+ const vps = [
3509
+ '#!/usr/bin/env bash', 'set -e',
3510
+ `echo "=== OpenClaw Setup — Ubuntu/VPS${isMultiBot ? ` Multi-Bot (${state.botCount} bots)` : ''} ==="`,
3511
+ '# Auto-install Node.js 20 LTS if missing',
3512
+ 'if ! command -v node > /dev/null 2>&1; then',
3513
+ ' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
3514
+ ' sudo apt-get install -y nodejs',
3515
+ 'fi',
3516
+ 'mkdir -p "$HOME/.local/bin"',
3517
+ 'npm config set prefix "$HOME/.local"',
3518
+ 'export PATH="$HOME/.local/bin:$PATH"',
3519
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
3520
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
3521
+ 'npm install -g openclaw@latest pm2@latest',
3522
+ ];
3497
3523
  providerLines(vps, 'sh');
3498
3524
  if (pluginCmd) vps.push(pluginCmd);
3499
3525
 
@@ -3501,6 +3527,10 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3501
3527
  vps.push('echo "--- Creating shared multi-agent runtime ---"');
3502
3528
  appendShWriteCommands(vps, sharedNativeFileMap());
3503
3529
  vps.push('echo "--- Starting shared gateway via PM2 ---"');
3530
+ if (is9Router) {
3531
+ vps.push('pm2 start --name openclaw-multibot-9router -- sh -c "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
3532
+ vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
3533
+ }
3504
3534
  vps.push('pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"');
3505
3535
  vps.push('pm2 save && pm2 startup');
3506
3536
  vps.push(`echo ""`);
@@ -3510,6 +3540,10 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3510
3540
  vps.push(`echo " pm2 logs openclaw-multibot"`);
3511
3541
  } else {
3512
3542
  appendShWriteCommands(vps, botFiles(0));
3543
+ if (is9Router) {
3544
+ vps.push('pm2 start --name openclaw-9router -- sh -c "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
3545
+ vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
3546
+ }
3513
3547
  vps.push('pm2 start --name openclaw -- sh -c "openclaw gateway run"');
3514
3548
  vps.push('pm2 save && pm2 startup');
3515
3549
  vps.push('echo "Bot dang chay! Xem log: pm2 logs openclaw"');
@@ -3519,20 +3553,20 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3519
3553
  // ─── Linux Desktop .SH ───────────────────────────────────────────────────
3520
3554
  } else if (state.nativeOs === 'linux-desktop') {
3521
3555
  scriptName = 'setup-openclaw-linux.sh';
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
- ];
3556
+ const lnx = [
3557
+ '#!/usr/bin/env bash', 'set -e',
3558
+ `echo "=== OpenClaw Setup — Linux Desktop${isMultiBot ? ' Multi-Bot' : ''} ==="`,
3559
+ 'if ! command -v node > /dev/null 2>&1; then',
3560
+ ' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
3561
+ ' sudo apt-get install -y nodejs',
3562
+ 'fi',
3563
+ 'mkdir -p "$HOME/.local/bin"',
3564
+ 'npm config set prefix "$HOME/.local"',
3565
+ 'export PATH="$HOME/.local/bin:$PATH"',
3566
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
3567
+ 'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
3568
+ 'npm install -g openclaw@latest',
3569
+ ];
3536
3570
  providerLines(lnx, 'sh');
3537
3571
  if (pluginCmd) lnx.push(pluginCmd);
3538
3572
 
@@ -3684,7 +3718,9 @@ Write-Host " 🎉 ${isVi ? 'Setup hoàn tất!' : 'Setup complete!'}" -Foregrou
3684
3718
  ps += `Write-Host " ${isVi ? 'Mở http://localhost:30128/dashboard để login OAuth' : 'Open http://localhost:30128/dashboard to login OAuth'}" -ForegroundColor White\n`;
3685
3719
  }
3686
3720
  if (state.channel === 'zalo-personal') {
3687
- ps += `Write-Host " ${isVi ? 'Chạy: docker exec -it openclaw-bot openclaw onboard (quét QR)' : 'Run: docker exec -it openclaw-bot openclaw onboard (scan QR)'}" -ForegroundColor White\n`;
3721
+ ps += `Write-Host " ${isVi ? 'Chạy: docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose' : 'Run: docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose'}" -ForegroundColor White\n`;
3722
+ ps += `Write-Host " ${isVi ? 'QR sẽ nằm tại /tmp/openclaw/openclaw-zalouser-qr-default.png' : 'QR will be written to /tmp/openclaw/openclaw-zalouser-qr-default.png'}" -ForegroundColor DarkGray\n`;
3723
+ ps += `Write-Host " ${isVi ? 'Copy QR ra ngoài: docker compose cp ai-bot:/tmp/openclaw/openclaw-zalouser-qr-default.png ./zalo-login-qr.png' : 'Copy the QR out: docker compose cp ai-bot:/tmp/openclaw/openclaw-zalouser-qr-default.png ./zalo-login-qr.png'}" -ForegroundColor DarkGray\n`;
3688
3724
  }
3689
3725
 
3690
3726
  ps += `Write-Host ""
@@ -3858,69 +3894,37 @@ echo ""
3858
3894
  }
3859
3895
 
3860
3896
 
3861
- // ========== Zalo Personal Onboard Guide (post-Docker-setup) ==========
3897
+ // ========== Zalo Personal Login Guide (post-setup) ==========
3862
3898
 
3863
3899
  function generateZaloOnboardGuide() {
3864
3900
  const lang = document.getElementById('cfg-language')?.value || 'vi';
3865
- setOutput('out-zalo-onboard-cmd', `docker exec -it openclaw-bot openclaw onboard`);
3901
+ setOutput('out-zalo-onboard-cmd', `docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose`);
3866
3902
 
3867
3903
  if (lang === 'vi') {
3868
3904
  setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
3869
- OpenClaw sẽ hỏi lần lượt chọn như sau:
3870
- ├──────────────────────┬──────────────────────────────┤
3871
- Câu hỏi Chọn │
3872
- ├──────────────────────┼──────────────────────────────┤
3873
- Security warning │ ✅ Yes
3874
- Setup mode │ ✅ QuickStart
3875
- Config handling │ ✅ Use existing values
3876
- Model/auth provider Chọn tuỳ ý (VD: Google) │
3877
- API key │ Nhập key (hoặc Enter nếu
3878
- đã trong .env)
3879
- │ Select channel │ ✅ Zalo (Personal Account) │
3880
- │ Login via QR? │ ✅ Yes │
3881
- │ ─── QR LOGIN ─── │ 📱 Mở file QR → Quét Zalo │
3882
- │ Did you scan QR? │ ✅ Yes │
3883
- │ DM policy │ ✅ Pairing (recommended) │
3884
- │ Configure groups? │ ✅ No │
3885
- │ Configure skills? │ ✅ No │
3886
- │ Enable hooks? │ ✅ Enter (chọn mặc định) │
3887
- │ Hatch your bot? │ ✅ Do this later │
3888
- ├──────────────────────┴──────────────────────────────┤
3889
- │ 💡 Bước QR Login: │
3890
- │ Khi bước QR hiện ra, test_openclaw sẽ lưu file QR │
3891
- │ vào thư mục /tmp trong container. │
3892
- │ Dùng lệnh: docker cp openclaw-bot:/tmp/qr.png . │
3893
- │ Mở file ảnh → quét bằng Zalo điện thoại → │
3894
- │ xác nhận kết nối → quay lại chọn Yes. │
3905
+ Chạy lệnh bên trái để OpenClaw tạo QR đăng nhập.
3906
+ ├─────────────────────────────────────────────────────┤
3907
+ 1. Đảm bảo container/gateway đã chạy xong.
3908
+ │ 2. Chạy lệnh login để tạo QR cho zalouser. │
3909
+ 3. OpenClaw sẽ in ra đường dẫn file QR trong /tmp.
3910
+ 4. Copy file QR ra ngoài nếu cần:
3911
+ docker compose cp ai-bot:/tmp/openclaw/
3912
+ openclaw-zalouser-qr-default.png .
3913
+ 5. Mở ảnh QR quét bằng app Zalo → xác nhận.
3914
+ 6. Sau khi login xong, restart bot nếu cần.
3895
3915
  └─────────────────────────────────────────────────────┘`);
3896
3916
  } else {
3897
3917
  setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
3898
- OpenClaw will prompt you choose as follows:
3899
- ├──────────────────────┬──────────────────────────────┤
3900
- Prompt │ Choice
3901
- ├──────────────────────┼──────────────────────────────┤
3902
- Security warning │ ✅ Yes
3903
- Setup mode │ ✅ QuickStart
3904
- Config handling │ ✅ Use existing values
3905
- Model/auth provider │ Choose any (e.g. Google)
3906
- API key │ Enter key (or press Enter
3907
- if already in .env)
3908
- │ Select channel │ ✅ Zalo (Personal Account) │
3909
- │ Login via QR? │ ✅ Yes │
3910
- │ ─── QR LOGIN ─── │ 📱 Open QR file → Scan Zalo │
3911
- │ Did you scan QR? │ ✅ Yes │
3912
- │ DM policy │ ✅ Pairing (recommended) │
3913
- │ Configure groups? │ ✅ No │
3914
- │ Configure skills? │ ✅ No │
3915
- │ Enable hooks? │ ✅ Enter (default) │
3916
- │ Hatch your bot? │ ✅ Do this later │
3917
- ├──────────────────────┴──────────────────────────────┤
3918
- │ 💡 QR Login Step: │
3919
- │ When prompted, OpenClaw saves the QR code to │
3920
- │ /tmp inside the container. │
3921
- │ Run: docker cp openclaw-bot:/tmp/qr.png . │
3922
- │ Open image → scan with Zalo mobile app → │
3923
- │ confirm login → go back & select Yes. │
3918
+ Run the command on the left to generate a Zalo QR.
3919
+ ├─────────────────────────────────────────────────────┤
3920
+ 1. Make sure the container/gateway is already up.
3921
+ │ 2. Run the login command for zalouser. │
3922
+ 3. OpenClaw prints the QR image path under /tmp.
3923
+ 4. Copy the QR out if needed:
3924
+ docker compose cp ai-bot:/tmp/openclaw/
3925
+ openclaw-zalouser-qr-default.png .
3926
+ 5. Open the image scan with Zalo mobile app.
3927
+ 6. Restart the bot afterwards if needed.
3924
3928
  └─────────────────────────────────────────────────────┘`);
3925
3929
  }
3926
3930
  }