create-openclaw-bot 5.3.3 → 5.3.4

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,18 @@
1
1
  # Changelog (English)
2
2
 
3
3
 
4
+ ## [5.3.4] — 2026-04-12
5
+
6
+ ### 🐛 Windows Native — Gateway & Workspace Stability
7
+
8
+ - **Fix: Gateway terminal auto-closing** — `call openclaw gateway run` was blocking the setup terminal indefinitely; closing the window killed the gateway process. The gateway is now launched in a **dedicated, visible CMD window** via a PS1 background launcher (same pattern as 9Router), so the setup terminal closes cleanly while the gateway keeps running.
9
+ - **Fix: `call` missing from `openclaw gateway stop`** — The gateway stop command before relaunch was missing the `call` keyword, which could prevent the bat script from returning control after the stop completes. Added `call openclaw gateway stop 2>nul` in all affected paths.
10
+ - **Fix: Workspace naming** — Single-bot deployments previously used a generic `.openclaw/workspace` path. All agents (single-bot and multi-bot) now use a named directory matching their agent ID: `.openclaw/workspace-{agentId}` (e.g. `workspace-williams`, `workspace-luna`). This prevents workspace collisions and aligns the folder structure with the `agents/{agentId}` directory convention.
11
+ - **Improve: TOOLS.md enriched for all bots** — The generated `TOOLS.md` now includes both the custom skills & conventions section AND a "Local Notes" template section adapted from the OpenClaw workspace default, giving users a clear starting point for documenting environment-specific config.
12
+ - **Improve: Zalo bot AGENTS.md now includes security rules** — The generated `AGENTS.md` for Zalo Personal bots (Luna-pattern) now appends the same `🔐 Security Rules — MANDATORY` block that Telegram bots receive (file-system boundaries, credential hygiene, crypto wallet protection, Docker mount rules).
13
+ - **Improve: Zalo bot TOOLS.md added** — Zalo bot workspaces now receive a `TOOLS.md` file with the same skills list and conventions structure as Telegram bots.
14
+
15
+
4
16
  ## [5.3.3] — 2026-04-11
5
17
 
6
18
  ### 🧹 Automated Uninstall Scripts
package/CHANGELOG.vi.md CHANGED
@@ -1,6 +1,18 @@
1
1
  # Changelog (Tiếng Việt)
2
2
 
3
3
 
4
+ ## [5.3.4] — 2026-04-12
5
+
6
+ ### 🐛 Windows Native — Ổn Định Gateway & Workspace
7
+
8
+ - **Sửa: Terminal tự đóng sau khi khởi động gateway** — `call openclaw gateway run` chặn terminal vô thời hạn; đóng cửa sổ khiến gateway chết theo. Gateway giờ được mở trong **cửa sổ CMD riêng biệt** qua PS1 launcher (giống 9Router), setup terminal đóng gọn mà gateway vẫn chạy độc lập.
9
+ - **Sửa: Thiếu `call` trước `openclaw gateway stop`** — Lệnh dừng gateway trước khi khởi động lại thiếu từ khóa `call`, có thể khiến bat script không trả quyền điều hành sau khi stop. Đã thêm `call openclaw gateway stop 2>nul` cho tất cả luồng.
10
+ - **Sửa: Tên workspace** — Single-bot deployment trước đây dùng chung `.openclaw/workspace`. Giờ tất cả agent đều có thư mục riêng theo tên agent ID: `.openclaw/workspace-{agentId}` (ví dụ `workspace-williams`, `workspace-luna`). Tránh xung đột và đồng bộ với cấu trúc `agents/{agentId}`.
11
+ - **Cải tiến: TOOLS.md đầy đủ hơn cho mọi bot** — File `TOOLS.md` được tạo ra giờ bao gồm cả mục danh sách skills + quy ước VÀ section "Ghi chú thiết lập của bạn" theo chuẩn OpenClaw, giúp người dùng có điểm xuất phát rõ ràng để ghi lại cấu hình môi trường riêng.
12
+ - **Cải tiến: AGENTS.md cho Zalo bot bổ sung quy tắc bảo mật** — `AGENTS.md` sinh ra cho bot Zalo Personal (kiểu Luna) giờ có thêm block `🔐 Quy Tắc Bảo Mật — BẮT BUỘC` giống hệt bot Telegram (giới hạn file-system, bảo vệ credentials, ví crypto, mount Docker).
13
+ - **Cải tiến: Thêm TOOLS.md cho Zalo bot** — Workspace của bot Zalo giờ cũng có file `TOOLS.md` cùng cấu trúc với bot Telegram.
14
+
15
+
4
16
  ## [5.3.3] — 2026-04-11
5
17
 
6
18
  ### 🧹 Tự Động Tạo Script Gỡ Cài Đặt
package/cli.js CHANGED
@@ -451,31 +451,57 @@ const sync = async () => {
451
451
  let db = {};
452
452
  try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e) {}
453
453
  if (!db.combos) db.combos = [];
454
- const removeSmartRoute = () => {
454
+ const resCombo = await fetch(ROUTER + '/api/combos').catch(() => null);
455
+ let memoryCombos = [];
456
+ if (resCombo && resCombo.ok) {
457
+ const cData = await resCombo.json();
458
+ memoryCombos = cData.combos || [];
459
+ }
460
+ const removeSmartRoute = async () => {
455
461
  const next = db.combos.filter(x => x.id !== 'smart-route');
456
462
  if (next.length !== db.combos.length) {
457
463
  db.combos = next;
458
464
  fs.writeFileSync(p, JSON.stringify(db, null, 2));
459
465
  console.log('[sync-combo] Removed smart-route (no active providers)');
460
466
  }
467
+ if (memoryCombos.find(x => x.id === 'smart-route')) {
468
+ await fetch(ROUTER + '/api/combos/smart-route', { method: 'DELETE' }).catch(()=>{});
469
+ console.log('[sync-combo] Removed smart-route from 9Router memory');
470
+ }
461
471
  };
462
- if (!a.length) { removeSmartRoute(); return; }
472
+ if (!a.length) { await removeSmartRoute(); return; }
463
473
  const PREF = ['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];
464
474
  a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
465
475
  const m = a.flatMap(pv => PM[pv] || []);
466
- if (!m.length) { removeSmartRoute(); return; }
476
+ if (!m.length) { await removeSmartRoute(); return; }
467
477
  const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
468
478
  const i = db.combos.findIndex(x => x.id === 'smart-route');
479
+ let dbUpdated = false;
469
480
  if (i >= 0) {
470
481
  if (JSON.stringify(db.combos[i].models) !== JSON.stringify(c.models)) {
471
482
  db.combos[i] = c;
472
483
  fs.writeFileSync(p, JSON.stringify(db, null, 2));
473
- console.log('[sync-combo] Updated smart-route: ' + c.models.length + ' models from: ' + a.join(','));
484
+ dbUpdated = true;
474
485
  }
475
486
  } else {
476
487
  db.combos.push(c);
477
488
  fs.writeFileSync(p, JSON.stringify(db, null, 2));
478
- console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models from: ' + a.join(','));
489
+ dbUpdated = true;
490
+ }
491
+ const inMemory = memoryCombos.find(x => x.id === 'smart-route');
492
+ let memUpdated = false;
493
+ if (inMemory) {
494
+ if (JSON.stringify(inMemory.models) !== JSON.stringify(c.models)) {
495
+ await fetch(ROUTER + '/api/combos/smart-route', { method: 'DELETE' }).catch(()=>{});
496
+ await fetch(ROUTER + '/api/combos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(c) }).catch(()=>{});
497
+ memUpdated = true;
498
+ }
499
+ } else {
500
+ await fetch(ROUTER + '/api/combos', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(c) }).catch(()=>{});
501
+ memUpdated = true;
502
+ }
503
+ if (dbUpdated || memUpdated) {
504
+ console.log('[sync-combo] Synced smart-route (Memory+Disk): ' + c.models.length + ' models from: ' + a.join(','));
479
505
  }
480
506
  } catch(e) { console.log('[sync-combo] Error:', e.message); }
481
507
  };
@@ -578,7 +604,9 @@ function build9RouterComposeEntrypointScript(syncScriptBase64) {
578
604
  }
579
605
 
580
606
  async function writeNative9RouterSyncScript(projectDir) {
581
- const syncScriptPath = path.join(projectDir, '.openclaw', '9router-smart-route-sync.js');
607
+ // Write to .9router/ (DATA_DIR) so 9router's own data dir has the sync helper,
608
+ // keeping .openclaw/ focused on openclaw configs only.
609
+ const syncScriptPath = path.join(projectDir, '.9router', '9router-smart-route-sync.js');
582
610
  await fs.ensureDir(path.dirname(syncScriptPath));
583
611
  await fs.writeFile(syncScriptPath, build9RouterSmartRouteSyncScript(path.join(getProject9RouterDataDir(projectDir), 'db.json')));
584
612
  return syncScriptPath;
@@ -2593,7 +2621,7 @@ if exist "%TARGET%" (
2593
2621
  echo.
2594
2622
  echo [4/4] Xoa thu muc .9router va .openclaw trong Home (neu co)...
2595
2623
  if exist "%USERPROFILE%\\.9router" (
2596
- set /p CLEAN_HOME=Xoa ca %USERPROFILE%\\.9router? (YES/no):
2624
+ set /p CLEAN_HOME=Xoa ca %USERPROFILE%\\.9router? [YES/no]:
2597
2625
  if /i "%CLEAN_HOME%"=="YES" rd /s /q "%USERPROFILE%\\.9router" >nul 2>&1
2598
2626
  )
2599
2627
  echo.
@@ -2667,7 +2695,7 @@ echo ""
2667
2695
  echo "[4/4] Checking for home-level .9router / .openclaw..."
2668
2696
  for dir in "$HOME/.9router" "$HOME/.openclaw"; do
2669
2697
  if [ -d "$dir" ]; then
2670
- read -rp "Delete $dir ? (YES/no): " CLEAN
2698
+ read -rp "Delete $dir ? [YES/no]: " CLEAN
2671
2699
  if [ "$CLEAN" = "YES" ]; then
2672
2700
  rm -rf "$dir"
2673
2701
  echo " OK: Deleted $dir"
@@ -2757,7 +2785,7 @@ echo ""
2757
2785
  echo "[5/5] Checking for home-level .9router / .openclaw..."
2758
2786
  for dir in "$HOME/.9router" "$HOME/.openclaw"; do
2759
2787
  if [ -d "$dir" ]; then
2760
- read -rp "Delete $dir ? (YES/no): " CLEAN
2788
+ read -rp "Delete $dir ? [YES/no]: " CLEAN
2761
2789
  if [ "$CLEAN" = "YES" ]; then
2762
2790
  rm -rf "$dir"
2763
2791
  echo " OK: Deleted $dir"
@@ -2827,7 +2855,7 @@ fi
2827
2855
  echo ""
2828
2856
  echo "[3/3] Checking for home-level .openclaw..."
2829
2857
  if [ -d "$HOME/.openclaw" ]; then
2830
- read -rp "Delete $HOME/.openclaw? (YES/no): " CLEAN
2858
+ read -rp "Delete $HOME/.openclaw? [YES/no]: " CLEAN
2831
2859
  if [ "$CLEAN" = "YES" ]; then
2832
2860
  rm -rf "$HOME/.openclaw"
2833
2861
  echo " OK: Deleted $HOME/.openclaw"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.3.3",
3
+ "version": "5.3.4",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "cli.js",
6
6
  "bin": {
package/patch-tray.js ADDED
@@ -0,0 +1,7 @@
1
+ const fs = require('fs');
2
+ let b = fs.readFileSync('C:/Users/Admin/Downloads/setup-openclaw-win.bat', 'utf8');
3
+ const before = '9router -n -H 0.0.0.0 -p 20128 --skip-update"';
4
+ const after = '9router -n -H 0.0.0.0 -p 20128 --skip-update --tray"';
5
+ b = b.split(before).join(after);
6
+ fs.writeFileSync('C:/Users/Admin/Downloads/setup-openclaw-win.bat', b);
7
+ console.log('Fixed! Has --tray:', b.includes('--tray'));
package/setup.js CHANGED
@@ -1933,7 +1933,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
1933
1933
  },
1934
1934
  list: [{
1935
1935
  id: agentId,
1936
- workspace: 'workspace',
1936
+ workspace: `.openclaw/workspace-${agentId}`,
1937
1937
  agentDir: `agents/${agentId}/agent`,
1938
1938
  model: { primary: state.config.model, fallbacks: [] },
1939
1939
  }],
@@ -2050,7 +2050,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
2050
2050
  clawConfig.agents.list = multiBotAgentMetas.map((meta) => ({
2051
2051
  id: meta.agentId,
2052
2052
  name: meta.name,
2053
- workspace: meta.workspaceDir,
2053
+ workspace: '.openclaw/' + meta.workspaceDir,
2054
2054
  agentDir: `agents/${meta.agentId}/agent`,
2055
2055
  model: { primary: state.config.model, fallbacks: [] },
2056
2056
  }));
@@ -3022,7 +3022,7 @@ fi
3022
3022
  botConfig.agents.defaults.model = { primary: state.config.model, fallbacks: [] };
3023
3023
  botConfig.agents.list = [{
3024
3024
  id: botAgentId,
3025
- workspace: 'workspace',
3025
+ workspace: `.openclaw/workspace-${botAgentId}`,
3026
3026
  agentDir: `agents/${botAgentId}/agent`,
3027
3027
  model: { primary: state.config.model, fallbacks: [] },
3028
3028
  }];
@@ -3240,6 +3240,7 @@ I am **${botName}**. When asked my name, I answer: _"I'm ${botName}"_.`;
3240
3240
  .filter((skill) => skill && skill.id !== 'scheduler' && skill.slug && skill.slug !== 'browser-automation');
3241
3241
  const selectedModel = (state.config.model || 'ollama/gemma4:e2b').replace('ollama/', '');
3242
3242
  const isMultiBot = state.botCount > 1 && state.channel === 'telegram';
3243
+ const isComboChannel = state.channel === 'telegram+zalo-personal';
3243
3244
  const projectDir = state.config.projectPath || '.';
3244
3245
  const todayStamp = new Date().toISOString().slice(0, 10);
3245
3246
 
@@ -3282,7 +3283,12 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
3282
3283
  function providerLines(arr, shell) {
3283
3284
  if (is9Router) {
3284
3285
  if (shell === 'bat') {
3286
+ arr.push(':: Dung 9Router dang chay (neu co) - tranh loi EBUSY khi npm cap nhat file dang bi lock');
3287
+ arr.push('wmic process where "Name=\'node.exe\' and CommandLine like \'%%9router%%\'" delete >nul 2>&1');
3288
+ arr.push('wmic process where "Name=\'cmd.exe\' and CommandLine like \'%%9router%%\'" delete >nul 2>&1');
3289
+ arr.push('timeout /t 3 /nobreak >nul');
3285
3290
  arr.push('call npm install -g 9router || goto :fail');
3291
+ arr.push('echo [OK] 9Router da duoc cai dat thanh cong.');
3286
3292
  // Pre-create DATA_DIR and seed db.json with requireLogin:false BEFORE starting 9router.
3287
3293
  // If db.json is missing when 9router boots, it defaults to ~/.9router and requireLogin:true,
3288
3294
  // blocking the dashboard. Must be done BEFORE the `start` command below.
@@ -3308,9 +3314,20 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
3308
3314
  arr.push('echo(}');
3309
3315
  arr.push(')');
3310
3316
  arr.push(')');
3311
- // NOTE: -l (stdin listen mode) intentionally omitted causes hangs in non-TTY cmd windows.
3312
- // DATA_DIR passed via env so 9router reads from the project-local data folder.
3313
- arr.push('start "9Router Dashboard" /min cmd /c "set DATA_DIR=%DATA_DIR%&& 9router -n -H 0.0.0.0 -p 20128 --skip-update"');
3317
+ // Launch 9Router as a fully detached process via PowerShell Start-Process -WindowStyle Hidden.
3318
+ // Using `start ... cmd /c "...--tray"` caused 9Router to die when the CMD window was closed
3319
+ // because cmd /c is a child of the started CMD, which is killed when that window closes.
3320
+ // Start-Process with WindowStyle Hidden creates a truly independent process that survives
3321
+ // even if all visible terminal windows are closed.
3322
+ // Write a temp .ps1 launcher to avoid CMD->PS quoting issues.
3323
+ // Start-Process cannot run .cmd files directly — cmd.exe must be the FilePath.
3324
+ arr.push('echo Khoi dong 9Router (background)...');
3325
+ arr.push('echo $env:DATA_DIR = \'%DATA_DIR%\' > "%TEMP%\\oc-start9r.ps1"');
3326
+ arr.push('echo $b = Join-Path $env:APPDATA \'npm\\9router.cmd\' >> "%TEMP%\\oc-start9r.ps1"');
3327
+ arr.push('echo if ^(-not ^(Test-Path $b^)^) { $b = Join-Path $env:APPDATA \'npm\\9router\' } >> "%TEMP%\\oc-start9r.ps1"');
3328
+ arr.push(`echo Start-Process 'cmd.exe' -WindowStyle Hidden -WorkingDirectory '${projectDir}' -ArgumentList ^('/c "' + $b + '" -n -H 0.0.0.0 -p 20128 --skip-update'^) >> "%TEMP%\\oc-start9r.ps1"`);
3329
+ arr.push('powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\\oc-start9r.ps1"');
3330
+ arr.push('del "%TEMP%\\oc-start9r.ps1" >nul 2>&1');
3314
3331
  arr.push('timeout /t 8 /nobreak >nul');
3315
3332
  } else {
3316
3333
  arr.push('npm install -g 9router');
@@ -3319,8 +3336,8 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
3319
3336
  arr.push('if [ ! -f ".9router/db.json" ]; then cat > ".9router/db.json" << \'DBJSON\'\n{\n "providerConnections": [],\n "providerNodes": [],\n "proxyPools": [],\n "modelAliases": {},\n "mitmAlias": {},\n "combos": [],\n "apiKeys": [],\n "settings": {\n "requireLogin": false,\n "cloudEnabled": false,\n "tunnelEnabled": false,\n "comboStrategy": "fallback",\n "mitmRouterBaseUrl": "http://localhost:20128"\n },\n "pricing": {}\n}\nDBJSON\nfi');
3320
3337
  arr.push('NINE_ROUTER_BIN="$(command -v 9router)"');
3321
3338
  // NOTE: -l (stdin listen mode) intentionally omitted — causes hangs in non-TTY environments
3322
- arr.push('nohup env PORT=20128 HOSTNAME=0.0.0.0 DATA_DIR="$PWD/.9router" "$NINE_ROUTER_BIN" -n -H 0.0.0.0 -p 20128 --skip-update >/tmp/9router.log 2>&1 &');
3323
- arr.push('nohup env DATA_DIR="$PWD/.9router" node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &');
3339
+ arr.push('nohup env PORT=20128 HOSTNAME=0.0.0.0 DATA_DIR="$PWD/.9router" "$NINE_ROUTER_BIN" -n -H 0.0.0.0 -p 20128 --skip-update > /tmp/9router.log 2>&1 &');
3340
+ arr.push('nohup env DATA_DIR="$PWD/.9router" node ./.9router/9router-smart-route-sync.js > /tmp/9router-sync.log 2>&1 &');
3324
3341
  arr.push('sleep 5');
3325
3342
  }
3326
3343
  } else if (isOllama) {
@@ -3437,7 +3454,7 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
3437
3454
  list: multiBotAgentMetas.map((meta) => ({
3438
3455
  id: meta.agentId,
3439
3456
  name: meta.name,
3440
- workspace: meta.workspaceDir,
3457
+ workspace: '.openclaw/' + meta.workspaceDir,
3441
3458
  agentDir: `agents/${meta.agentId}/agent`,
3442
3459
  model: { primary: state.config.model, fallbacks: [] },
3443
3460
  })),
@@ -3504,7 +3521,7 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
3504
3521
  '.openclaw/auth-profiles.json': sharedNativeAuthProfilesContent(),
3505
3522
  'TELEGRAM-POST-INSTALL.md': buildTelegramPostInstallChecklist(),
3506
3523
  };
3507
- if (is9Router) files['.openclaw/9router-smart-route-sync.js'] = native9RouterSyncScriptContent();
3524
+ if (is9Router) files['.9router/9router-smart-route-sync.js'] = native9RouterSyncScriptContent();
3508
3525
  const teamMd = isVi
3509
3526
  ? `# 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.`
3510
3527
  : `# 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.`;
@@ -3598,7 +3615,7 @@ const sync=async()=>{try{const res=await fetch(ROUTER+'/api/providers');if(!res.
3598
3615
  },
3599
3616
  list: [{
3600
3617
  id: agentId,
3601
- workspace: 'workspace',
3618
+ workspace: `.openclaw/workspace-${agentId}`,
3602
3619
  agentDir: `agents/${agentId}/agent`,
3603
3620
  model: { primary: actualModel }
3604
3621
  }],
@@ -3870,6 +3887,7 @@ You are **${botName}**, ${botDesc.toLowerCase()}.
3870
3887
  - Prefer English unless user uses another language
3871
3888
  - When asked your name: _"I'm ${botName}"_
3872
3889
  - Never fabricate information`;
3890
+ const _secRules = state.config.securityRules || DEFAULT_SECURITY_RULES[isVi ? 'vi' : 'en'];
3873
3891
  const extraAgentsMd = isVi
3874
3892
  ? `\n\n## Khi nao nen tra loi\n- Trong group, chi tra loi khi tin nhan co alias cua ban: ${ownAliases.map((alias) => `\`${alias}\``).join(', ')} hoac username Telegram cua ban.\n- Neu tin nhan khong goi ban, hay im lang hoan toan.\n- Neu tin nhan chi goi ro bot khac ${otherBotNames.length ? otherBotNames.map((name) => `\`${name}\``).join(', ') : '`bot khac`'} thi khong cuop loi.\n- Khi da biet user dang goi ban, hay tha reaction co dinh \`👍\` truoc roi moi tra loi bang text. Khong dung emoji khac.\n- Khi can phoi hop noi bo, dung dung agent id ky thuat trong \`TEAM.md\`, khong dung ten hien thi.\n- Khi hoi ve vai tro cac bot, dung \`TEAM.md\` lam nguon su that.`
3875
3893
  : `\n\n## When To Reply\n- In group chats, only reply when the message contains one of your aliases: ${ownAliases.map((alias) => `\`${alias}\``).join(', ')} or your Telegram username.\n- If the message is not calling you, stay completely silent.\n- If the message is clearly calling another bot such as ${otherBotNames.length ? otherBotNames.map((name) => `\`${name}\``).join(', ') : '`another bot`'}, do not hijack it.\n- Once you know the user is calling you, add the fixed reaction \`👍\` first, then send the text reply. Do not use any other reaction emoji.\n- When you need internal coordination, use the exact technical agent id from \`TEAM.md\`, not the display name.\n- Use \`TEAM.md\` as the source of truth for team roles.`;
@@ -3898,7 +3916,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(Chưa có ski
3898
3916
  ## Quy ước
3899
3917
  - Ưu tiên dùng tool thay vì đoán
3900
3918
  - Browser: dùng khi user yêu cầu thao tác web
3901
- - Memory: cập nhật khi biết thông tin quan trọng`
3919
+ - Memory: cập nhật khi biết thông tin quan trọng\n\n## Ghi chú thiết lập của bạn\n\nGhi lại cấu hình riêng của môi trường bạn, ví dụ:\n- Tên thiết bị, camera, SSH hosts\n- Giọng nói ưa thích (TTS)\n- Alias và shortcut\n\n---\n\nThêm ghi chú nào giúp ích cho công việc của bạn.`
3902
3920
  : `# Tool Usage Guide
3903
3921
 
3904
3922
  ## Installed Skills
@@ -3907,7 +3925,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3907
3925
  ## Conventions
3908
3926
  - Prefer tools over guessing
3909
3927
  - Use Browser for explicit web tasks
3910
- - Update Memory when important user info appears`;
3928
+ - Update Memory when important user info appears\n\n## Your Setup Notes\n\nRecord environment-specific config, e.g.:\n- Device names, cameras, SSH hosts\n- Preferred TTS voice\n- Aliases and shortcuts\n\n---\n\nAdd whatever helps you do your job.`;
3911
3929
  const memoryMd = isVi
3912
3930
  ? `# Bộ nhớ dài hạn
3913
3931
 
@@ -3920,7 +3938,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3920
3938
  const files = {
3921
3939
  'IDENTITY.md': identityMd,
3922
3940
  'SOUL.md': soulMd,
3923
- 'AGENTS.md': agentsMd + extraAgentsMd,
3941
+ 'AGENTS.md': agentsMd + extraAgentsMd + '\n\n' + _secRules,
3924
3942
  'TEAM.md': teamMd,
3925
3943
  'USER.md': userMd,
3926
3944
  'TOOLS.md': toolsMd,
@@ -3945,11 +3963,11 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3945
3963
  files[`${base}/.openclaw/openclaw.json`] = botConfigContent(botIndex);
3946
3964
  files[`${base}/.openclaw/exec-approvals.json`] = botExecApprovalsContent(botIndex);
3947
3965
  files[`${base}/.openclaw/auth-profiles.json`] = botAuthProfilesContent(botIndex);
3948
- if (is9Router) files[`${base}/.openclaw/9router-smart-route-sync.js`] = native9RouterSyncScriptContent();
3966
+ if (is9Router) files[`${base}/.9router/9router-smart-route-sync.js`] = native9RouterSyncScriptContent();
3949
3967
  files[`${base}/.openclaw/agents/${agentId}.yaml`] = botAgentYamlContent(botIndex);
3950
3968
  files[`${base}/.openclaw/agents/${agentId}/agent/auth-profiles.json`] = botAuthProfilesContent(botIndex);
3951
3969
  Object.entries(botWorkspaceFiles(botIndex)).forEach(([name, content]) => {
3952
- files[`${base}/.openclaw/workspace/${name}`] = content;
3970
+ files[`${base}/.openclaw/workspace-${agentId}/${name}`] = content;
3953
3971
  });
3954
3972
  return files;
3955
3973
  }
@@ -3991,6 +4009,9 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
3991
4009
  return Object.fromEntries(Object.entries(files).map(([relPath, content]) => {
3992
4010
  const normalized = relPath.replace(/\\/g, '/');
3993
4011
  if (normalized === '.env') return ['%PROJECT_DIR%\\.env', content];
4012
+ if (normalized.startsWith('.9router/')) {
4013
+ return [`%DATA_DIR%\\${normalized.slice('.9router/'.length).replace(/\//g, '\\')}`, content];
4014
+ }
3994
4015
  if (normalized.startsWith('.openclaw/')) {
3995
4016
  return [`%OPENCLAW_HOME%\\${normalized.slice('.openclaw/'.length).replace(/\//g, '\\')}`, content];
3996
4017
  }
@@ -4024,6 +4045,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4024
4045
  'where node >nul 2>&1 || (echo ERROR: Node.js chua cai! Tai tai: https://nodejs.org && pause && exit /b 1)',
4025
4046
  'echo [2/5] Cai OpenClaw CLI...',
4026
4047
  `call npm install -g openclaw@2026.4.5 ${openClawRuntimePackages} || goto :fail`,
4048
+ 'echo [OK] OpenClaw da duoc cai dat thanh cong.',
4027
4049
  ];
4028
4050
  providerLines(lines, 'bat');
4029
4051
  if (hasBrowser) {
@@ -4044,7 +4066,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4044
4066
  if (isMultiBot) {
4045
4067
  lines.push('echo [4/5] Tao runtime multi-agent dung chung...');
4046
4068
  appendBatWriteCommands(lines, mapWindowsNativeFiles(sharedNativeFileMap()));
4047
- if (is9Router) lines.push(windowsHiddenNodeLaunch('%OPENCLAW_HOME%\\9router-smart-route-sync.js', { DATA_DIR: '%DATA_DIR%' }));
4069
+ if (is9Router) lines.push(windowsHiddenNodeLaunch('%DATA_DIR%\\9router-smart-route-sync.js', { DATA_DIR: '%DATA_DIR%' }));
4048
4070
  lines.push('if not exist "%OPENCLAW_HOME%\\openclaw.json" (echo ERROR: Khong tim thay "%OPENCLAW_HOME%\\openclaw.json" && goto :fail)');
4049
4071
  lines.push('echo.');
4050
4072
  lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
@@ -4063,7 +4085,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4063
4085
  lines.push('echo Cho gateway khoi dong (15 giay)...');
4064
4086
  lines.push('timeout /t 15 /nobreak >nul');
4065
4087
  lines.push('echo [6/6] Dang nhap Zalo - dang tao ma QR...');
4066
- lines.push('openclaw channels login --channel zalouser --verbose');
4088
+ lines.push('openclaw channels login --channel zalouser --instance default --verbose');
4067
4089
  lines.push('echo.');
4068
4090
  // Copy QR PNG from TEMP to project dir so user can open it easily
4069
4091
  lines.push('set "QR_TMP=%TEMP%\\openclaw\\openclaw-zalouser-qr-default.png"');
@@ -4082,12 +4104,155 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4082
4104
  lines.push('echo De khoi dong lai: openclaw gateway run');
4083
4105
  } else {
4084
4106
  lines.push('echo [5/5] Khoi dong gateway multi-bot...');
4085
- lines.push('call openclaw gateway run');
4107
+ lines.push(':: Khoi dong OpenClaw Gateway trong cua so moi');
4108
+ lines.push('echo $env:OPENCLAW_HOME = \'%OPENCLAW_HOME%\' > "%TEMP%\\oc-startgw.ps1"');
4109
+ lines.push('echo $env:OPENCLAW_STATE_DIR = \'%OPENCLAW_HOME%\' >> "%TEMP%\\oc-startgw.ps1"');
4110
+ lines.push('echo $b = Join-Path $env:APPDATA \'npm\\openclaw.cmd\' >> "%TEMP%\\oc-startgw.ps1"');
4111
+ lines.push('echo if ^(-not ^(Test-Path $b^)^) { $b = Join-Path $env:APPDATA \'npm\\openclaw\' } >> "%TEMP%\\oc-startgw.ps1"');
4112
+ lines.push("echo Start-Process 'cmd.exe' -WindowStyle Normal -WorkingDirectory '%PROJECT_DIR%' -ArgumentList ^('/c \"' + $b + '\" gateway run'^) >> \"%TEMP%\\oc-startgw.ps1\"");
4113
+ lines.push('powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\\oc-startgw.ps1"');
4114
+ lines.push('del "%TEMP%\\oc-startgw.ps1" >nul 2>&1');
4115
+ lines.push('timeout /t 5 /nobreak >nul');
4116
+ lines.push('echo.');
4117
+ lines.push('echo [OK] OpenClaw Gateway dang khoi dong trong cua so moi!');
4118
+ lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
4119
+ lines.push('echo If the dashboard asks for a Gateway Token, run: openclaw dashboard');
4120
+ }
4121
+ } else if (isComboChannel) {
4122
+ // ── Combo: Telegram + Zalo Personal — 2 bots, 1 gateway ─────────────
4123
+ lines.push('echo [4/5] Tao file cau hinh cho 2 bot (Telegram + Zalo Personal)...');
4124
+ // Bot 0 = Telegram bot
4125
+ const bot0Files = botFiles(0);
4126
+ // Bot 1 = Zalo bot (same workspace root, separate agent/workspace dirs)
4127
+ const bot1 = state.bots[1] || {};
4128
+ const zaloName = bot1.name || 'Zalo Bot';
4129
+ const zaloDesc = bot1.desc || (isVi ? 'Tro ly Zalo ca nhan' : 'Personal Zalo assistant');
4130
+ const zaloPersona = bot1.persona || '';
4131
+ const zaloAgentId = zaloName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'zalo-bot';
4132
+ const bot0Name = (state.bots[0] || {}).name || 'Bot 1';
4133
+ const bot0AgentId = bot0Name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'bot-1';
4134
+ // Merge bot0's config to also include zalo agent in agents.list + bindings
4135
+ const mergedConfig = JSON.parse(bot0Files['./.openclaw/openclaw.json'] || '{}');
4136
+ if (!mergedConfig.agents) mergedConfig.agents = { defaults: {}, list: [] };
4137
+ if (!Array.isArray(mergedConfig.agents.list)) mergedConfig.agents.list = [];
4138
+ // Add Zalo agent to list if not already there
4139
+ const hasZaloInList = mergedConfig.agents.list.some(a => a.id === zaloAgentId);
4140
+ if (!hasZaloInList) {
4141
+ mergedConfig.agents.list.push({
4142
+ id: zaloAgentId,
4143
+ name: zaloName,
4144
+ workspace: `.openclaw/workspace-${zaloAgentId}`,
4145
+ agentDir: `agents/${zaloAgentId}/agent`,
4146
+ model: { primary: (bot1.model || state.config.model) },
4147
+ });
4148
+ }
4149
+ // Ensure bindings exist
4150
+ if (!Array.isArray(mergedConfig.bindings)) mergedConfig.bindings = [];
4151
+ // Bind Telegram bot to bot0
4152
+ const hasTelegramBinding = mergedConfig.bindings.some(b => b && b.match && b.match.channel === 'telegram');
4153
+ if (!hasTelegramBinding) {
4154
+ mergedConfig.bindings.push({ agentId: bot0AgentId, match: { channel: 'telegram', accountId: 'default' } });
4086
4155
  }
4156
+ // Bind Zalo channel to zalo agent
4157
+ const hasZaloBinding = mergedConfig.bindings.some(b => b && b.match && b.match.channel === 'zalouser');
4158
+ if (!hasZaloBinding) {
4159
+ mergedConfig.bindings.push({ agentId: zaloAgentId, match: { channel: 'zalouser', accountId: 'default' } });
4160
+ }
4161
+ // Ensure zalouser channel is in config
4162
+ if (!mergedConfig.channels) mergedConfig.channels = {};
4163
+ if (!mergedConfig.channels.zalouser) {
4164
+ mergedConfig.channels.zalouser = { enabled: true, dmPolicy: 'open', autoReply: true };
4165
+ }
4166
+ bot0Files['./.openclaw/openclaw.json'] = JSON.stringify(mergedConfig, null, 2);
4167
+ appendBatWriteCommands(lines, mapWindowsNativeFiles(bot0Files));
4168
+ // Zalo agent YAML
4169
+ const zaloAgentYaml = `name: ${zaloAgentId}\ndescription: "${zaloDesc}"\n\nmodel:\n primary: ${bot1.model || state.config.model}`;
4170
+ const zaloWorkspaceDir = `workspace-${zaloAgentId}`;
4171
+ const _zaloSecRules = state.config.securityRules || DEFAULT_SECURITY_RULES[isVi ? 'vi' : 'en'];
4172
+ const zaloFiles = {
4173
+ [`.openclaw/agents/${zaloAgentId}.yaml`]: zaloAgentYaml,
4174
+ [`.openclaw/agents/${zaloAgentId}/agent/auth-profiles.json`]: sharedNativeAuthProfilesContent(),
4175
+ [`.openclaw/${zaloWorkspaceDir}/IDENTITY.md`]: isVi
4176
+ ? `# Danh tinh\n\n- **Ten:** ${zaloName}\n- **Vai tro:** ${zaloDesc}\n\n---\n\nMinh la **${zaloName}**. Khi ai hoi ten, minh tra loi: _"Minh la ${zaloName}"_.`
4177
+ : `# Identity\n\n- **Name:** ${zaloName}\n- **Role:** ${zaloDesc}\n\n---\n\nI am **${zaloName}**. When asked my name, I answer: _"I'm ${zaloName}"_.`,
4178
+ [`.openclaw/${zaloWorkspaceDir}/SOUL.md`]: isVi
4179
+ ? `# Tinh cach\n\n**Huu ich that su.** Bo qua cau ne, cu giup thang.\n**Co ca tinh.** Tro ly khong co ca tinh thi chi la cong cu.\n\n## Phong cach\n- Tu nhien, gan gui\n- Truc tiep, ngan gon${zaloPersona ? `\n\n## Custom Rules\n${zaloPersona}` : ''}`
4180
+ : `# Soul\n\n**Be genuinely helpful.** Skip filler and just help.\n**Have personality.** An assistant with no personality is just a tool.\n\n## Style\n- Natural and concise\n- Direct and practical${zaloPersona ? `\n\n## Custom Rules\n${zaloPersona}` : ''}`,
4181
+ [`.openclaw/${zaloWorkspaceDir}/AGENTS.md`]: isVi
4182
+ ? `# Huong dan van hanh\n\n## Vai tro\nBan la **${zaloName}**, ${zaloDesc.toLowerCase()}.\n\n## Kenh Zalo Personal\n- Ban hoat dong tren kenh Zalo Personal (zca-js).\n- Tra loi moi tin nhan DM theo chinh sach dmPolicy: open.\n- Khong can duoc goi ten moi tra loi (DM la rieng tu).\n\n## Quy tac tra loi\n- Tra loi ngan gon, suc tich\n- Uu tien tieng Viet\n- Khi hoi ten: _"Minh la ${zaloName}"_\n- Khong bia thong tin\n\n${_zaloSecRules}`
4183
+ : `# Operating Manual\n\n## Role\nYou are **${zaloName}**, ${zaloDesc.toLowerCase()}.\n\n## Zalo Personal Channel\n- You operate on the Zalo Personal channel (zca-js).\n- Reply to all DMs with dmPolicy: open.\n- DMs are private — no need to be mentioned to reply.\n\n## Reply Rules\n- Be concise\n- Prefer Vietnamese\n- When asked your name: _"I'm ${zaloName}"_\n- Never fabricate information\n\n${_zaloSecRules}`,
4184
+ [`.openclaw/${zaloWorkspaceDir}/TEAM.md`]: isVi
4185
+ ? `# Doi Bot\n\n## ${bot0Name}\n- Vai tro: ${(state.bots[0] || {}).desc || 'Tro ly Telegram'}\n- Kenh: Telegram\n\n## ${zaloName}\n- Vai tro: ${zaloDesc}\n- Kenh: Zalo Personal`
4186
+ : `# Bot Team\n\n## ${bot0Name}\n- Role: ${(state.bots[0] || {}).desc || 'Telegram assistant'}\n- Channel: Telegram\n\n## ${zaloName}\n- Role: ${zaloDesc}\n- Channel: Zalo Personal`,
4187
+ [`.openclaw/${zaloWorkspaceDir}/USER.md`]: isVi
4188
+ ? `# Thong tin nguoi dung\n\n## Tong quan\n- **Ngon ngu uu tien:** Tieng Viet\n\n## Thong tin ca nhan\n${state.config.userInfo || '- _(Chua co gi)_'}`
4189
+ : `# User Profile\n\n## Overview\n- **Preferred language:** Vietnamese\n\n## Notes\n${state.config.userInfo || '- _(Nothing yet)_'}`,
4190
+ [`.openclaw/${zaloWorkspaceDir}/MEMORY.md`]: isVi
4191
+ ? `# Bo nho dai han\n\n## Ghi chu\n- _(Chua co gi)_`
4192
+ [`.openclaw/${zaloWorkspaceDir}/TOOLS.md`]: isVi
4193
+ ? `# Hướng dẫn sử dụng Tools\n\n## Skills đã cài\n${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(Chưa có skill nào)_'}\n\n## Quy ước\n- Ưu tiên dùng tool thay vì đoán\n- Browser: dùng khi user yêu cầu thao tác web\n- Memory: cập nhật khi biết thông tin quan trọng\n\n## Ghi chú thiết lập của bạn\n\nGhi lại cấu hình riêng của môi trường bạn, ví dụ:\n- Tên thiết bị, camera, SSH hosts\n- Giọng nói ưa thích (TTS)\n\n---\n\nThêm ghi chú nào giúp ích cho công việc của bạn.`
4194
+ : `# Tool Usage Guide\n\n## Installed Skills\n${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills installed)_'}\n\n## Conventions\n- Prefer tools over guessing\n- Use Browser for explicit web tasks\n- Update Memory when important user info appears\n\n## Your Setup Notes\n\nRecord environment-specific config, e.g.:\n- Device names, cameras, SSH hosts\n- Preferred TTS voice\n\n---\n\nAdd whatever helps you do your job.`,
4195
+ : `# Long-term Memory\n\n## Notes\n- _(Nothing yet)_`,
4196
+ };
4197
+ appendBatWriteCommands(lines, mapWindowsNativeFiles(zaloFiles));
4198
+ if (is9Router) lines.push(windowsHiddenNodeLaunch('%DATA_DIR%\\9router-smart-route-sync.js', { DATA_DIR: '%DATA_DIR%' }));
4199
+ lines.push('if not exist "%OPENCLAW_HOME%\\openclaw.json" (echo ERROR: Khong tim thay "%OPENCLAW_HOME%\\openclaw.json" && goto :fail)');
4200
+ lines.push('echo.');
4201
+ lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
4202
+ lines.push('echo Other reachable URLs: http://localhost:18791');
4203
+ lines.push('echo If the dashboard asks for a Gateway Token, run: openclaw dashboard');
4204
+ if (is9Router) {
4205
+ lines.push('echo.');
4206
+ lines.push('echo 9Router Dashboard: http://127.0.0.1:20128/dashboard');
4207
+ lines.push('echo Other reachable URLs: http://localhost:20128/dashboard');
4208
+ }
4209
+ // Login Zalo trực tiếp (không cần gateway chạy trước — openclaw channels login standalone)
4210
+ // Sau khi login thành công, gateway sẽ tự nhận credentials khi khởi động.
4211
+ lines.push('echo [5/6] Dang nhap Zalo Personal...');
4212
+ lines.push('echo.');
4213
+ lines.push('echo === HUONG DAN DANG NHAP ZALO ===');
4214
+ lines.push('echo OpenClaw se hien duong dan file anh QR trong cua so nay.');
4215
+ lines.push('echo Hay mo file anh do, chon Quet QR trong app Zalo va quet.');
4216
+ lines.push('echo Neu het gio, nhap R de tao ma QR moi.');
4217
+ lines.push('echo ================================');
4218
+ lines.push('echo.');
4219
+ lines.push(':retry_zalo');
4220
+ lines.push('echo Dang tao ma QR Zalo moi...');
4221
+ lines.push('echo.');
4222
+ lines.push('call openclaw channels login --channel zalouser --verbose');
4223
+ lines.push('if %ERRORLEVEL% equ 0 goto :zalo_done');
4224
+ lines.push('echo.');
4225
+ lines.push('echo [WARN] Ma QR het han hoac chua duoc quet kip.');
4226
+ lines.push('echo R - Tao ma QR moi va thu lai');
4227
+ lines.push('echo Enter - Bo qua dang nhap Zalo (Zalo se khong hoat dong)');
4228
+ lines.push('set /p RETRY_ZALO=Ban chon: ');
4229
+ lines.push('if /i "%RETRY_ZALO%"=="R" goto :retry_zalo');
4230
+ lines.push('echo [SKIP] Bo qua. Zalo se khong hoat dong cho den khi dang nhap lai.');
4231
+ lines.push('goto :zalo_continue');
4232
+ lines.push(':zalo_done');
4233
+ lines.push('echo [OK] Dang nhap Zalo thanh cong!');
4234
+ lines.push(':zalo_continue');
4235
+ lines.push(':: Dong gateway cu (neu co lock file) truoc khi khoi dong lai');
4236
+ lines.push('call openclaw gateway stop 2>nul');
4237
+ lines.push('timeout /t 2 /nobreak >nul');
4238
+ lines.push('echo [6/6] Khoi dong bot (Telegram + Zalo)...');
4239
+ lines.push(':: Khoi dong OpenClaw Gateway trong cua so moi');
4240
+ lines.push('echo $env:OPENCLAW_HOME = \'%OPENCLAW_HOME%\' > "%TEMP%\\oc-startgw.ps1"');
4241
+ lines.push('echo $env:OPENCLAW_STATE_DIR = \'%OPENCLAW_HOME%\' >> "%TEMP%\\oc-startgw.ps1"');
4242
+ lines.push('echo $b = Join-Path $env:APPDATA \'npm\\openclaw.cmd\' >> "%TEMP%\\oc-startgw.ps1"');
4243
+ lines.push('echo if ^(-not ^(Test-Path $b^)^) { $b = Join-Path $env:APPDATA \'npm\\openclaw\' } >> "%TEMP%\\oc-startgw.ps1"');
4244
+ lines.push("echo Start-Process 'cmd.exe' -WindowStyle Normal -WorkingDirectory '%PROJECT_DIR%' -ArgumentList ^('/c \"' + $b + '\" gateway run'^) >> \"%TEMP%\\oc-startgw.ps1\"");
4245
+ lines.push('powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\\oc-startgw.ps1"');
4246
+ lines.push('del "%TEMP%\\oc-startgw.ps1" >nul 2>&1');
4247
+ lines.push('timeout /t 5 /nobreak >nul');
4248
+ lines.push('echo.');
4249
+ lines.push('echo [OK] OpenClaw Gateway dang khoi dong trong cua so moi!');
4250
+ lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
4251
+ lines.push('echo If the dashboard asks for a Gateway Token, run: openclaw dashboard');
4087
4252
  } else {
4088
4253
  lines.push('echo [4/5] Tao file cau hinh...');
4089
4254
  appendBatWriteCommands(lines, mapWindowsNativeFiles(botFiles(0)));
4090
- if (is9Router) lines.push(windowsHiddenNodeLaunch('%OPENCLAW_HOME%\\9router-smart-route-sync.js', { DATA_DIR: '%DATA_DIR%' }));
4255
+ if (is9Router) lines.push(windowsHiddenNodeLaunch('%DATA_DIR%\\9router-smart-route-sync.js', { DATA_DIR: '%DATA_DIR%' }));
4091
4256
  lines.push('if not exist "%OPENCLAW_HOME%\\openclaw.json" (echo ERROR: Khong tim thay "%OPENCLAW_HOME%\\openclaw.json" && goto :fail)');
4092
4257
  lines.push('echo.');
4093
4258
  lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
@@ -4098,27 +4263,54 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4098
4263
  lines.push('echo 9Router Dashboard: http://127.0.0.1:20128/dashboard');
4099
4264
  lines.push('echo Other reachable URLs: http://localhost:20128/dashboard');
4100
4265
  }
4101
- const needsZaloLogin = state.channel === 'zalo-personal' || state.channel === 'telegram+zalo-personal';
4266
+ const needsZaloLogin = state.channel === 'zalo-personal';
4102
4267
  if (needsZaloLogin) {
4103
- lines.push('echo [5/6] Khoi dong gateway (cua so moi) de chuan bi dang nhap Zalo...');
4104
- lines.push('start "OpenClaw Gateway" cmd /c "cd /d %PROJECT_DIR% && set OPENCLAW_HOME=%OPENCLAW_HOME% && set OPENCLAW_STATE_DIR=%OPENCLAW_HOME% && openclaw gateway run"');
4105
- lines.push('echo Cho gateway khoi dong (15 giay)...');
4106
- lines.push('timeout /t 15 /nobreak >nul');
4107
- lines.push('echo [6/6] Dang nhap Zalo...');
4108
- lines.push('echo.');
4109
- lines.push('echo ============================================================');
4110
- lines.push('echo Cua so CMD moi se mo de dang nhap Zalo.');
4111
- lines.push('echo Hay lam theo huong dan trong cua so do:');
4112
- lines.push('echo 1. Chon "Install Zalo Personal plugin?" (Enter)');
4113
- lines.push('echo 2. Doi QR hien ra - mo app Zalo quet ma QR');
4114
- lines.push('echo 3. Doi den khi thay "Login success" hoac token');
4115
- lines.push('echo ============================================================');
4116
- lines.push('start "Zalo Login" cmd /k "cd /d \"%PROJECT_DIR%\" && set OPENCLAW_HOME=%OPENCLAW_HOME% && set OPENCLAW_STATE_DIR=%OPENCLAW_HOME% && openclaw channels login --channel zalouser --verbose"');
4117
- lines.push('echo Gateway dang chay trong cua so rieng.');
4118
- lines.push('echo De khoi dong lai: openclaw gateway run');
4268
+ // Login Zalo trực tiếp (không cần gateway chạy trước)
4269
+ lines.push('echo [5/5] Dang nhap Zalo Personal...');
4270
+ lines.push('echo.');
4271
+ lines.push('echo === HUONG DAN DANG NHAP ZALO ===');
4272
+ lines.push('echo Cua so Zalo Login se mo. Hay:');
4273
+ lines.push('echo 1. Doi QR hien ra trong cua so Zalo Login');
4274
+ lines.push('echo 2. Mo app Zalo, chon Quet QR va quet ma');
4275
+ lines.push('echo 3. Doi thay chu "Login successful" trong cua so do');
4276
+ lines.push('echo 4. Dong cua so Zalo Login');
4277
+ lines.push('echo ================================');
4278
+ lines.push('echo.');
4279
+ lines.push('start "Zalo Login" cmd /k "cd /d \"%PROJECT_DIR%\" && set OPENCLAW_HOME=%OPENCLAW_HOME% && set OPENCLAW_STATE_DIR=%OPENCLAW_HOME% && openclaw channels login --channel zalouser --verbose"');
4280
+ lines.push('echo Nhan phim bat ky sau khi dong cua so Zalo Login...');
4281
+ lines.push('pause >nul');
4282
+ lines.push(':: Dong gateway cu (neu co lock file tu cua so Zalo Login) truoc khi khoi dong lai');
4283
+ lines.push('call openclaw gateway stop 2>nul');
4284
+ lines.push('timeout /t 2 /nobreak >nul');
4285
+ lines.push('echo [6/6] Khoi dong bot...');
4286
+ lines.push(':: Khoi dong OpenClaw Gateway trong cua so moi');
4287
+ lines.push('echo $env:OPENCLAW_HOME = \'%OPENCLAW_HOME%\' > "%TEMP%\\oc-startgw.ps1"');
4288
+ lines.push('echo $env:OPENCLAW_STATE_DIR = \'%OPENCLAW_HOME%\' >> "%TEMP%\\oc-startgw.ps1"');
4289
+ lines.push('echo $b = Join-Path $env:APPDATA \'npm\\openclaw.cmd\' >> "%TEMP%\\oc-startgw.ps1"');
4290
+ lines.push('echo if ^(-not ^(Test-Path $b^)^) { $b = Join-Path $env:APPDATA \'npm\\openclaw\' } >> "%TEMP%\\oc-startgw.ps1"');
4291
+ lines.push("echo Start-Process 'cmd.exe' -WindowStyle Normal -WorkingDirectory '%PROJECT_DIR%' -ArgumentList ^('/c \"' + $b + '\" gateway run'^) >> \"%TEMP%\\oc-startgw.ps1\"");
4292
+ lines.push('powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\\oc-startgw.ps1"');
4293
+ lines.push('del "%TEMP%\\oc-startgw.ps1" >nul 2>&1');
4294
+ lines.push('timeout /t 5 /nobreak >nul');
4295
+ lines.push('echo.');
4296
+ lines.push('echo [OK] OpenClaw Gateway dang khoi dong trong cua so moi!');
4297
+ lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
4298
+ lines.push('echo If the dashboard asks for a Gateway Token, run: openclaw dashboard');
4119
4299
  } else {
4120
4300
  lines.push('echo [5/5] Khoi dong bot...');
4121
- lines.push('call openclaw gateway run');
4301
+ lines.push(':: Khoi dong OpenClaw Gateway trong cua so moi');
4302
+ lines.push('echo $env:OPENCLAW_HOME = \'%OPENCLAW_HOME%\' > "%TEMP%\\oc-startgw.ps1"');
4303
+ lines.push('echo $env:OPENCLAW_STATE_DIR = \'%OPENCLAW_HOME%\' >> "%TEMP%\\oc-startgw.ps1"');
4304
+ lines.push('echo $b = Join-Path $env:APPDATA \'npm\\openclaw.cmd\' >> "%TEMP%\\oc-startgw.ps1"');
4305
+ lines.push('echo if ^(-not ^(Test-Path $b^)^) { $b = Join-Path $env:APPDATA \'npm\\openclaw\' } >> "%TEMP%\\oc-startgw.ps1"');
4306
+ lines.push("echo Start-Process 'cmd.exe' -WindowStyle Normal -WorkingDirectory '%PROJECT_DIR%' -ArgumentList ^('/c \"' + $b + '\" gateway run'^) >> \"%TEMP%\\oc-startgw.ps1\"");
4307
+ lines.push('powershell -NoProfile -ExecutionPolicy Bypass -File "%TEMP%\\oc-startgw.ps1"');
4308
+ lines.push('del "%TEMP%\\oc-startgw.ps1" >nul 2>&1');
4309
+ lines.push('timeout /t 5 /nobreak >nul');
4310
+ lines.push('echo.');
4311
+ lines.push('echo [OK] OpenClaw Gateway dang khoi dong trong cua so moi!');
4312
+ lines.push('echo OpenClaw Dashboard: http://127.0.0.1:18791');
4313
+ lines.push('echo If the dashboard asks for a Gateway Token, run: openclaw dashboard');
4122
4314
  }
4123
4315
  }
4124
4316
 
@@ -4227,7 +4419,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4227
4419
  if (is9Router) {
4228
4420
  vps.push(`NINE_ROUTER_ENTRY="$(${native9RouterServerEntryLookup()})"`);
4229
4421
  vps.push('PORT=20128 HOSTNAME=0.0.0.0 pm2 start "$NINE_ROUTER_ENTRY" --name openclaw-multibot-9router --interpreter "$(command -v node)"');
4230
- vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
4422
+ vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.9router/9router-smart-route-sync.js"');
4231
4423
  }
4232
4424
  vps.push('pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"');
4233
4425
  vps.push('pm2 save && pm2 startup');
@@ -4241,7 +4433,7 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4241
4433
  if (is9Router) {
4242
4434
  vps.push(`NINE_ROUTER_ENTRY="$(${native9RouterServerEntryLookup()})"`);
4243
4435
  vps.push('PORT=20128 HOSTNAME=0.0.0.0 pm2 start "$NINE_ROUTER_ENTRY" --name openclaw-9router --interpreter "$(command -v node)"');
4244
- vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
4436
+ vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.9router/9router-smart-route-sync.js"');
4245
4437
  }
4246
4438
  vps.push('pm2 start --name openclaw -- sh -c "openclaw gateway run"');
4247
4439
  vps.push('pm2 save && pm2 startup');
@@ -4344,60 +4536,60 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
4344
4536
  if (os === 'win' && !isDocker) {
4345
4537
  return {
4346
4538
  name: 'uninstall-openclaw-win.bat',
4347
- content: `@echo off
4348
- setlocal EnableExtensions
4349
- chcp 65001 >nul
4350
- echo.
4351
- echo ============================================================
4352
- echo OpenClaw Uninstaller - Windows Native
4353
- echo Project: ${absWin}
4354
- echo ============================================================
4355
- echo.
4356
- echo [WARNING] This will:
4357
- echo 1. Kill openclaw and 9router background processes
4358
- echo 2. Uninstall global npm packages (openclaw, 9router)
4359
- echo 3. Delete the project folder and all its data
4360
- echo.
4361
- set /p CONFIRM=Nhap YES de xac nhan xoa toan bo:
4362
- if /i not "%CONFIRM%"=="YES" (
4363
- echo Huy bo. Khong xoa gi ca.
4364
- pause
4365
- exit /b 0
4366
- )
4367
- echo.
4368
- echo [1/4] Dang dung cac tien trinh openclaw va 9router...
4369
- taskkill /F /IM openclaw.exe >nul 2>&1
4370
- taskkill /F /IM 9router.exe >nul 2>&1
4371
- powershell -NoProfile -Command "Get-Process node -ErrorAction SilentlyContinue | Where-Object { $_.Path -like '*${absWin.replace(/\/g, '\\\\')}*' } | Stop-Process -Force" >nul 2>&1
4372
- powershell -NoProfile -Command "& { $p=@(18791,20128); foreach($port in $p){ $id=(netstat -ano | Select-String \":\$($port) \").Line -split ' +' | Select-Object -Last 1; if($id -and $id -ne '0'){ Stop-Process -Id $id -Force -ErrorAction SilentlyContinue } } }" >nul 2>&1
4373
- echo OK: Tien trinh da dung.
4374
- echo.
4375
- echo [2/4] Dang go cai npm packages toan cau...
4376
- set "PATH=%APPDATA%\npm;%PATH%"
4377
- call npm uninstall -g openclaw 9router grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api 2>nul
4378
- echo OK: npm packages da duoc go cai.
4379
- echo.
4380
- echo [3/4] Xoa thu muc project...
4381
- set "TARGET=${absWin}"
4382
- if exist "%TARGET%" (
4383
- rd /s /q "%TARGET%"
4384
- echo OK: Da xoa %TARGET%
4385
- ) else (
4386
- echo INFO: Thu muc khong ton tai: %TARGET%
4387
- )
4388
- echo.
4389
- echo [4/4] Xoa thu muc .9router trong Home (neu co)...
4390
- if exist "%USERPROFILE%\.9router" (
4391
- set /p CLEAN_HOME=Xoa ca %USERPROFILE%\.9router? (YES/no):
4392
- if /i "%CLEAN_HOME%"=="YES" rd /s /q "%USERPROFILE%\.9router" >nul 2>&1
4393
- )
4394
- echo.
4395
- echo ============================================================
4396
- echo Go cai hoan tat!
4397
- echo De cai lai: chay lai file setup hoac npx create-openclaw-bot
4398
- echo ============================================================
4399
- pause
4400
- endlocal
4539
+ content: `@echo off
4540
+ setlocal EnableExtensions
4541
+ chcp 65001 >nul
4542
+ echo.
4543
+ echo ============================================================
4544
+ echo OpenClaw Uninstaller - Windows Native
4545
+ echo Project: ${absWin}
4546
+ echo ============================================================
4547
+ echo.
4548
+ echo [WARNING] This will:
4549
+ echo 1. Kill openclaw and 9router background processes
4550
+ echo 2. Uninstall global npm packages (openclaw, 9router)
4551
+ echo 3. Delete the project folder and all its data
4552
+ echo.
4553
+ set /p CONFIRM=Nhap YES de xac nhan xoa toan bo:
4554
+ if /i not "%CONFIRM%"=="YES" (
4555
+ echo Huy bo. Khong xoa gi ca.
4556
+ pause
4557
+ exit /b 0
4558
+ )
4559
+ echo.
4560
+ echo [1/4] Dang dung cac tien trinh openclaw va 9router...
4561
+ wmic process where "Name='node.exe' and CommandLine like '%%9router%%'" delete >nul 2>&1
4562
+ wmic process where "Name='cmd.exe' and CommandLine like '%%9router%%'" delete >nul 2>&1
4563
+ wmic process where "Name='node.exe' and CommandLine like '%%openclaw.mjs%%'" delete >nul 2>&1
4564
+ timeout /t 2 /nobreak >nul
4565
+ echo OK: Tien trinh da dung.
4566
+ echo.
4567
+ echo [2/4] Dang go cai npm packages toan cau...
4568
+ set "PATH=%APPDATA%\\npm;%PATH%"
4569
+ call npm uninstall -g openclaw 9router grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api 2>nul
4570
+ echo OK: npm packages da duoc go cai.
4571
+ echo.
4572
+ echo [3/4] Xoa thu muc project...
4573
+ set "TARGET=${absWin}"
4574
+ if exist "%TARGET%" (
4575
+ rd /s /q "%TARGET%"
4576
+ echo OK: Da xoa %TARGET%
4577
+ ) else (
4578
+ echo INFO: Thu muc khong ton tai: %TARGET%
4579
+ )
4580
+ echo.
4581
+ echo [4/4] Xoa thu muc .9router trong Home (neu co)...
4582
+ if exist "%USERPROFILE%\\.9router" (
4583
+ set /p CLEAN_HOME=Xoa ca %USERPROFILE%\\.9router? [YES/no]:
4584
+ if /i "%CLEAN_HOME%"=="YES" rd /s /q "%USERPROFILE%\\.9router" >nul 2>&1
4585
+ )
4586
+ echo.
4587
+ echo ============================================================
4588
+ echo Go cai hoan tat!
4589
+ echo De cai lai: chay lai file setup hoac npx create-openclaw-bot
4590
+ echo ============================================================
4591
+ pause
4592
+ endlocal
4401
4593
  `
4402
4594
  };
4403
4595
  }
@@ -4406,42 +4598,42 @@ endlocal
4406
4598
  if (os === 'win' && isDocker) {
4407
4599
  return {
4408
4600
  name: 'uninstall-openclaw-docker.bat',
4409
- content: `@echo off
4410
- setlocal EnableExtensions
4411
- chcp 65001 >nul
4412
- echo.
4413
- echo ============================================================
4414
- echo OpenClaw Uninstaller - Docker (Windows)
4415
- echo Project: ${absWin}
4416
- echo ============================================================
4417
- echo.
4418
- echo [WARNING] This will stop Docker containers and delete the project folder.
4419
- echo.
4420
- set /p CONFIRM=Nhap YES de xac nhan xoa toan bo:
4421
- if /i not "%CONFIRM%"=="YES" (
4422
- echo Huy bo. Khong xoa gi ca.
4423
- pause
4424
- exit /b 0
4425
- )
4426
- echo.
4427
- echo [1/2] Dang dung Docker containers...
4428
- cd /d "${absWin}\docker\openclaw" 2>nul && (
4429
- docker compose down --volumes --remove-orphans 2>nul || docker-compose down --volumes --remove-orphans 2>nul
4430
- echo OK: Containers da dung.
4431
- ) || echo INFO: Khong tim thay docker compose.
4432
- echo.
4433
- echo [2/2] Xoa thu muc project...
4434
- cd /d "%USERPROFILE%"
4435
- if exist "${absWin}" (
4436
- rd /s /q "${absWin}"
4437
- echo OK: Da xoa ${absWin}
4438
- )
4439
- echo.
4440
- echo ============================================================
4441
- echo Go cai hoan tat! De cai lai: npx create-openclaw-bot@latest
4442
- echo ============================================================
4443
- pause
4444
- endlocal
4601
+ content: `@echo off
4602
+ setlocal EnableExtensions
4603
+ chcp 65001 >nul
4604
+ echo.
4605
+ echo ============================================================
4606
+ echo OpenClaw Uninstaller - Docker (Windows)
4607
+ echo Project: ${absWin}
4608
+ echo ============================================================
4609
+ echo.
4610
+ echo [WARNING] This will stop Docker containers and delete the project folder.
4611
+ echo.
4612
+ set /p CONFIRM=Nhap YES de xac nhan xoa toan bo:
4613
+ if /i not "%CONFIRM%"=="YES" (
4614
+ echo Huy bo. Khong xoa gi ca.
4615
+ pause
4616
+ exit /b 0
4617
+ )
4618
+ echo.
4619
+ echo [1/2] Dang dung Docker containers...
4620
+ cd /d "${absWin}\docker\openclaw" 2>nul && (
4621
+ docker compose down --volumes --remove-orphans 2>nul || docker-compose down --volumes --remove-orphans 2>nul
4622
+ echo OK: Containers da dung.
4623
+ ) || echo INFO: Khong tim thay docker compose.
4624
+ echo.
4625
+ echo [2/2] Xoa thu muc project...
4626
+ cd /d "%USERPROFILE%"
4627
+ if exist "${absWin}" (
4628
+ rd /s /q "${absWin}"
4629
+ echo OK: Da xoa ${absWin}
4630
+ )
4631
+ echo.
4632
+ echo ============================================================
4633
+ echo Go cai hoan tat! De cai lai: npx create-openclaw-bot@latest
4634
+ echo ============================================================
4635
+ pause
4636
+ endlocal
4445
4637
  `
4446
4638
  };
4447
4639
  }
@@ -4487,7 +4679,7 @@ echo "[4/5] Removing project directory..."
4487
4679
  echo "[5/5] Checking home-level .9router / .openclaw..."
4488
4680
  for dir in "\$HOME/.9router" "\$HOME/.openclaw"; do
4489
4681
  if [ -d "\$dir" ]; then
4490
- read -rp "Delete \$dir ? (YES/no): " CLEAN
4682
+ read -rp "Delete \$dir ? [YES/no]: " CLEAN
4491
4683
  [ "\$CLEAN" = "YES" ] && rm -rf "\$dir" && echo " OK: Deleted \$dir" || echo " Kept: \$dir"
4492
4684
  fi
4493
4685
  done
@@ -4539,7 +4731,7 @@ echo "[3/4] Removing project directory..."
4539
4731
  echo "[4/4] Checking home-level .9router / .openclaw..."
4540
4732
  for dir in "\$HOME/.9router" "\$HOME/.openclaw"; do
4541
4733
  if [ -d "\$dir" ]; then
4542
- read -rp "Delete \$dir ? (YES/no): " CLEAN
4734
+ read -rp "Delete \$dir ? [YES/no]: " CLEAN
4543
4735
  [ "\$CLEAN" = "YES" ] && rm -rf "\$dir" && echo " OK: Deleted \$dir" || echo " Kept: \$dir"
4544
4736
  fi
4545
4737
  done
@@ -4582,7 +4774,7 @@ echo "[2/3] Removing project directory..."
4582
4774
 
4583
4775
  echo "[3/3] Checking home-level .openclaw..."
4584
4776
  if [ -d "\$HOME/.openclaw" ]; then
4585
- read -rp "Delete \$HOME/.openclaw? (YES/no): " CLEAN
4777
+ read -rp "Delete \$HOME/.openclaw? [YES/no]: " CLEAN
4586
4778
  [ "\$CLEAN" = "YES" ] && rm -rf "\$HOME/.openclaw" && echo " OK." || echo " Kept."
4587
4779
  fi
4588
4780
 
@@ -4715,7 +4907,7 @@ Write-Host " 🎉 ${isVi ? 'Setup hoan tat!' : 'Setup complete!'}" -ForegroundC
4715
4907
  ps += `Write-Host " ${isVi ? 'Mo http://localhost:30128/dashboard de login OAuth' : 'Open http://localhost:30128/dashboard to login OAuth'}" -ForegroundColor White\n`;
4716
4908
  }
4717
4909
  if (state.channel === 'zalo-personal' || state.channel === 'telegram+zalo-personal') {
4718
- ps += `Write-Host " ${isVi ? 'Chay: 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`;
4910
+ ps += `Write-Host " ${isVi ? 'Chay: docker compose exec -it ai-bot openclaw channels login --channel zalouser --instance default --verbose' : 'Run: docker compose exec -it ai-bot openclaw channels login --channel zalouser --instance default --verbose'}" -ForegroundColor White\n`;
4719
4911
  ps += `Write-Host " ${isVi ? 'QR se nam tai /tmp/openclaw/openclaw-zalouser-qr-default.png' : 'QR will be written to /tmp/openclaw/openclaw-zalouser-qr-default.png'}" -ForegroundColor DarkGray\n`;
4720
4912
  ps += `Write-Host " ${isVi ? 'Copy QR ra ngoai: 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`;
4721
4913
  }
@@ -4896,7 +5088,7 @@ echo ""
4896
5088
 
4897
5089
  function generateZaloOnboardGuide() {
4898
5090
  const lang = document.getElementById('cfg-language')?.value || 'vi';
4899
- setOutput('out-zalo-onboard-cmd', `docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose`);
5091
+ setOutput('out-zalo-onboard-cmd', `docker compose exec -it ai-bot openclaw channels login --channel zalouser --instance default --verbose`);
4900
5092
 
4901
5093
  if (lang === 'vi') {
4902
5094
  setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
package/test-path.bat ADDED
@@ -0,0 +1,4 @@
1
+ @echo off
2
+ set "PROJECT_DIR=D:\test"
3
+ echo Path1: "%PROJECT_DIR%\.env"
4
+ echo Path2: "%PROJECT_DIR%\\.env"
@@ -391,12 +391,11 @@ checks.push(() => expect(
391
391
  && setup.includes('function native9RouterServerEntryLookup() {')
392
392
  && setup.includes('return "node -e ')
393
393
  && !setup.includes('return "node -p ')
394
- && setup.includes("start \"9Router Dashboard\" /min cmd /c")
395
- && setup.includes("9router -n -H 0.0.0.0 -p 20128 --skip-update")
394
+ && setup.includes("oc-start9r.ps1") // Windows: writes temp PS1 launcher to avoid CMD→PS quoting issues
396
395
  && setup.includes('NINE_ROUTER_BIN="$(command -v 9router)"')
397
396
  && setup.includes('"$NINE_ROUTER_BIN" -n -H 0.0.0.0 -p 20128 --skip-update')
398
397
  && setup.includes("const p=path.join(process.env.DATA_DIR||'.9router','db.json');")
399
- && setup.includes('nohup env DATA_DIR="$PWD/.9router" node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &')
398
+ && setup.includes('nohup env DATA_DIR="$PWD/.9router" node ./.9router/9router-smart-route-sync.js > /tmp/9router-sync.log 2>&1 &')
400
399
  && setup.includes('set "PROJECT_DIR=')
401
400
  && setup.includes('set "OPENCLAW_HOME=%PROJECT_DIR%\\\\.openclaw"')
402
401
  && setup.includes('set "OPENCLAW_STATE_DIR=%PROJECT_DIR%\\\\.openclaw"')
@@ -434,8 +433,8 @@ checks.push(() => expect(
434
433
  && setup.includes("lines.push('echo Cai skills...');")
435
434
  && setup.includes("const openClawRuntimePackages = 'grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api';")
436
435
  && setup.includes("memory: 'none'")
437
- && setup.includes("workspace: 'workspace'")
438
- && setup.includes("workspace: meta.workspaceDir")
436
+ && setup.includes("workspace-\${agentId}\`")
437
+ && setup.includes("workspace: '.openclaw/' + meta.workspaceDir")
439
438
  && !setup.includes("const authProviderName = provider.isProxy ? '9router' : provider.id;")
440
439
  && !setup.includes("const authProviderName = botProvider.isProxy ? '9router' : botProvider.id;"),
441
440
  'Wizard native config generation must keep gateway loopback-local, preserve concrete auth provider ids, disable memory search by default, and sync single-bot provider/model selections into bot state'
@@ -479,7 +478,7 @@ checks.push(() => expect(
479
478
 
480
479
  checks.push(() => expectMatch(
481
480
  setup,
482
- /\.openclaw\/9router-smart-route-sync\.js[\s\S]*pm2 start --name openclaw-9router-sync/s,
481
+ /\.9router\/9router-smart-route-sync\.js[\s\S]*pm2 start --name openclaw-9router-sync/s,
483
482
  'VPS native script generation must write and run the 9Router smart-route sync loop'
484
483
  ));
485
484
 
@@ -537,8 +536,8 @@ checks.push(() => expectMatch(
537
536
 
538
537
  checks.push(() => expectMatch(
539
538
  setup,
540
- /Native setup now auto-runs the login flow and copies the QR into the project folder[\s\S]*docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose[\s\S]*docker compose cp ai-bot:\/tmp\/openclaw\/openclaw-zalouser-qr-default\.png \.\/zalo-login-qr\.png/s,
541
- 'Wizard copy must mention native auto-login and still show the dedicated Docker QR login command'
539
+ /docker compose exec -it ai-bot openclaw channels login --channel zalouser[\s\S]*docker compose cp ai-bot:\/tmp\/openclaw\/openclaw-zalouser-qr-default\.png \.\/zalo-login-qr\.png/s,
540
+ 'Wizard must show dedicated Docker Zalo login and QR copy commands'
542
541
  ));
543
542
 
544
543
  checks.push(() => expect(
@@ -567,6 +566,17 @@ checks.push(() => expectMatch(
567
566
  'Web wizard Docker patch command must add interface-based control UI allowed origins'
568
567
  ));
569
568
 
569
+ checks.push(() => expect(
570
+ setup.includes("echo [OK] OpenClaw da duoc cai dat thanh cong.")
571
+ && setup.includes("echo [OK] 9Router da duoc cai dat thanh cong."),
572
+ 'Windows BAT must print install success messages after openclaw and 9router are installed'
573
+ ));
574
+
575
+ checks.push(() => expect(
576
+ setup.includes("openclaw gateway stop 2>nul"),
577
+ 'Windows Zalo flow must clear stale gateway lock (from channels login mini-runtime) before starting the main gateway'
578
+ ));
579
+
570
580
  for (const check of checks) {
571
581
  check();
572
582
  }
@@ -1,54 +0,0 @@
1
- # 1.0.0 - 2016-01-07
2
-
3
- - Removed: unused speed test
4
- - Added: Automatic routing between previously unsupported conversions
5
- ([#27](https://github.com/Qix-/color-convert/pull/27))
6
- - Removed: `xxx2xxx()` and `xxx2xxxRaw()` functions
7
- ([#27](https://github.com/Qix-/color-convert/pull/27))
8
- - Removed: `convert()` class
9
- ([#27](https://github.com/Qix-/color-convert/pull/27))
10
- - Changed: all functions to lookup dictionary
11
- ([#27](https://github.com/Qix-/color-convert/pull/27))
12
- - Changed: `ansi` to `ansi256`
13
- ([#27](https://github.com/Qix-/color-convert/pull/27))
14
- - Fixed: argument grouping for functions requiring only one argument
15
- ([#27](https://github.com/Qix-/color-convert/pull/27))
16
-
17
- # 0.6.0 - 2015-07-23
18
-
19
- - Added: methods to handle
20
- [ANSI](https://en.wikipedia.org/wiki/ANSI_escape_code#Colors) 16/256 colors:
21
- - rgb2ansi16
22
- - rgb2ansi
23
- - hsl2ansi16
24
- - hsl2ansi
25
- - hsv2ansi16
26
- - hsv2ansi
27
- - hwb2ansi16
28
- - hwb2ansi
29
- - cmyk2ansi16
30
- - cmyk2ansi
31
- - keyword2ansi16
32
- - keyword2ansi
33
- - ansi162rgb
34
- - ansi162hsl
35
- - ansi162hsv
36
- - ansi162hwb
37
- - ansi162cmyk
38
- - ansi162keyword
39
- - ansi2rgb
40
- - ansi2hsl
41
- - ansi2hsv
42
- - ansi2hwb
43
- - ansi2cmyk
44
- - ansi2keyword
45
- ([#18](https://github.com/harthur/color-convert/pull/18))
46
-
47
- # 0.5.3 - 2015-06-02
48
-
49
- - Fixed: hsl2hsv does not return `NaN` anymore when using `[0,0,0]`
50
- ([#15](https://github.com/harthur/color-convert/issues/15))
51
-
52
- ---
53
-
54
- Check out commit logs for older releases