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 +12 -0
- package/CHANGELOG.vi.md +12 -0
- package/cli.js +38 -10
- package/package.json +1 -1
- package/patch-tray.js +7 -0
- package/setup.js +327 -135
- package/test-path.bat +4 -0
- package/tests/smoke-cli-logic.mjs +18 -8
- package/node_modules/color-convert/CHANGELOG.md +0 -54
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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?
|
|
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 ?
|
|
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 ?
|
|
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?
|
|
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
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:
|
|
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:
|
|
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
|
-
//
|
|
3312
|
-
//
|
|
3313
|
-
|
|
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
|
|
3323
|
-
arr.push('nohup env DATA_DIR="$PWD/.9router" node ./.
|
|
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['.
|
|
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:
|
|
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}/.
|
|
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('%
|
|
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
|
-
|
|
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('%
|
|
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'
|
|
4266
|
+
const needsZaloLogin = state.channel === 'zalo-personal';
|
|
4102
4267
|
if (needsZaloLogin) {
|
|
4103
|
-
|
|
4104
|
-
|
|
4105
|
-
|
|
4106
|
-
|
|
4107
|
-
|
|
4108
|
-
|
|
4109
|
-
|
|
4110
|
-
|
|
4111
|
-
|
|
4112
|
-
|
|
4113
|
-
|
|
4114
|
-
|
|
4115
|
-
|
|
4116
|
-
|
|
4117
|
-
|
|
4118
|
-
|
|
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
|
-
|
|
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 ./.
|
|
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 ./.
|
|
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
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
echo OK: Tien trinh da dung.
|
|
4374
|
-
echo.
|
|
4375
|
-
echo [2/4] Dang go cai npm packages toan cau...
|
|
4376
|
-
set "PATH=%APPDATA
|
|
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
|
|
4391
|
-
set /p CLEAN_HOME=Xoa ca %USERPROFILE
|
|
4392
|
-
if /i "%CLEAN_HOME%"=="YES" rd /s /q "%USERPROFILE
|
|
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 ?
|
|
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 ?
|
|
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?
|
|
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
|
@@ -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("
|
|
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 ./.
|
|
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
|
|
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
|
-
/\.
|
|
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
|
-
/
|
|
541
|
-
'Wizard
|
|
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
|