create-openclaw-bot 5.8.0 → 5.8.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/setup.js CHANGED
@@ -406,6 +406,13 @@
406
406
  */
407
407
  // ========== Available Plugins (npm packages — runtime/channel extensions) ==========
408
408
  const PLUGINS = [
409
+ {
410
+ id: 'browser-automation',
411
+ name: 'Browser Automation ⭐',
412
+ icon: '🌐',
413
+ descVi: 'Smart Search + Điều khiển trình duyệt Chrome/Chromium (ẩn & thật)', descEn: 'Smart Search + Chrome/Chromium browser control (headless & real)',
414
+ package: 'openclaw-browser-automation',
415
+ },
409
416
  {
410
417
  id: 'telegram-multibot-relay',
411
418
  name: 'Telegram Multi-Bot Relay',
@@ -468,14 +475,6 @@
468
475
  */
469
476
  // ========== Available Skills (ClawHub registry — agent capabilities) ==========
470
477
  const SKILLS = [
471
- {
472
- id: 'browser',
473
- name: 'Browser Automation ⭐(Khuyên dùng)',
474
- icon: '🌐',
475
- descVi: 'Tự động thao tác trình duyệt (Playwright)', descEn: 'Automated browser control (Playwright)',
476
- slug: 'browser-automation',
477
- noteVi: 'Cần bật Chrome Debug Mode trên máy host', noteEn: 'Requires Chrome Debug Mode on host',
478
- },
479
478
  {
480
479
  id: 'memory',
481
480
  name: 'Long-term Memory ⭐(Khuyên dùng)',
@@ -574,7 +573,7 @@
574
573
  className: 'plugin-card__badge plugin-card__badge--recommended'
575
574
  };
576
575
  }
577
- if (skill.id === 'browser' || skill.id === 'scheduler') {
576
+ if (skill.id === 'scheduler') {
578
577
  return {
579
578
  text: isVi ? 'Khuyên dùng' : 'Recommended',
580
579
  className: 'plugin-card__badge plugin-card__badge--recommended'
@@ -1059,6 +1058,226 @@ function buildBootstrapDoc(options = {}) {
1059
1058
  : `# BOOTSTRAP\n\n## Startup role\n- You are **${botName}**.\n- Your identity, role, and style are already defined in **IDENTITY.md**, **SOUL.md**, and **AGENTS.md**.\n- Use those files as the highest-priority source of truth for your persona in this workspace.\n\n## Mandatory\n- Do not ask the user to redefine your name, vibe, persona, signature emoji, or assistant style.\n- Do not say you just woke up, just came online, are choosing your identity, or any similar onboarding line.\n- Do not behave like a blank freshly-created bot.\n- If the user only sends a short opener like "hi" or "alo", greet briefly and reply in your existing role.\n\n## Telegram\n- Before EVERY user-visible Telegram reply, if the current turn exposes a native reaction tool/action, use it to add \`👍\` first unless \`👍\` is already present.\n- Never claim that you reacted unless the tool actually ran and the reaction is real on the chat.\n- If the target chat does not support real reactions or the tool is not exposed, still reply normally and do not mention the technical limitation.\n`;
1060
1059
  }
1061
1060
 
1061
+ function buildSearchToolJs() {
1062
+ return `/**
1063
+ * search-tool.js — Stealth search via Playwright Headless Chromium or CDP fallback
1064
+ * Zero tokens, no API keys, concurrent multi-engine scraping (Google + Bing + DuckDuckGo).
1065
+ * Usage: node search-tool.js "<query>" [limit]
1066
+ */
1067
+ let playwright;
1068
+ try {
1069
+ playwright = require('playwright-core');
1070
+ } catch (e) {
1071
+ try {
1072
+ playwright = require('/usr/local/lib/node_modules/openclaw/node_modules/playwright-core');
1073
+ } catch (err) {
1074
+ try {
1075
+ const path = require('path');
1076
+ playwright = require(path.join(process.cwd(), 'node_modules', 'playwright-core'));
1077
+ } catch (x) {
1078
+ console.error(JSON.stringify({ error: 'Playwright not found! Install it or run within OpenClaw environment.' }));
1079
+ process.exit(1);
1080
+ }
1081
+ }
1082
+ }
1083
+ const { chromium } = playwright;
1084
+
1085
+ const query = process.argv[2];
1086
+ const limit = parseInt(process.argv[3]) || 5;
1087
+ const CDP_URL = 'http://127.0.0.1:9222';
1088
+
1089
+ if (!query) {
1090
+ console.error(JSON.stringify({ error: 'Usage: node search-tool.js "<query>" [limit]' }));
1091
+ process.exit(1);
1092
+ }
1093
+
1094
+ (async () => {
1095
+ let browser;
1096
+ let ctx;
1097
+ let isStandalone = false;
1098
+ try {
1099
+ // Try connecting to active Chrome CDP first
1100
+ try {
1101
+ browser = await chromium.connectOverCDP(CDP_URL, { timeout: 3000 });
1102
+ ctx = browser.contexts()[0];
1103
+ } catch (e) {
1104
+ // Fallback to standalone headless Chromium launch
1105
+ browser = await chromium.launch({
1106
+ headless: true,
1107
+ args: [
1108
+ '--no-sandbox',
1109
+ '--disable-gpu',
1110
+ '--disable-dev-shm-usage',
1111
+ '--disable-blink-features=AutomationControlled'
1112
+ ]
1113
+ });
1114
+ isStandalone = true;
1115
+ ctx = await browser.newContext({
1116
+ userAgent: 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36'
1117
+ });
1118
+ }
1119
+
1120
+ // Run search queries concurrently on three search engines
1121
+ const [googleResults, bingResults, ddgResults] = await Promise.all([
1122
+ // Google
1123
+ (async () => {
1124
+ const page = await ctx.newPage();
1125
+ try {
1126
+ await page.goto('https://www.google.com/search?q=' + encodeURIComponent(query) + '&hl=vi', { waitUntil: 'domcontentloaded', timeout: 10000 });
1127
+ const res = await page.evaluate(() => {
1128
+ const list = [];
1129
+ const links = Array.from(document.querySelectorAll('a h3'));
1130
+ for (const head of links) {
1131
+ const a = head.closest('a');
1132
+ if (!a) continue;
1133
+ const url = a.href;
1134
+ const title = head.textContent || '';
1135
+ let snippet = '';
1136
+ let parent = a.parentElement;
1137
+ while (parent && parent.tagName !== 'DIV') {
1138
+ parent = parent.parentElement;
1139
+ }
1140
+ if (parent) {
1141
+ const descEl = parent.parentElement?.querySelector('.VwiC3b, .yHGvwa, div[style*="-webkit-line-clamp"]');
1142
+ if (descEl) {
1143
+ snippet = descEl.textContent || '';
1144
+ } else {
1145
+ const texts = Array.from(parent.parentElement?.querySelectorAll('div, span') || [])
1146
+ .map(el => el.textContent.trim())
1147
+ .filter(txt => txt.length > 30 && !txt.includes(title));
1148
+ if (texts.length > 0) snippet = texts[0];
1149
+ }
1150
+ }
1151
+ if (url && title) {
1152
+ list.push({ title, url, snippet });
1153
+ }
1154
+ }
1155
+ return list;
1156
+ });
1157
+ await page.close();
1158
+ return res;
1159
+ } catch (e) {
1160
+ if (page) await page.close();
1161
+ return [];
1162
+ }
1163
+ })(),
1164
+
1165
+ // Bing
1166
+ (async () => {
1167
+ const page = await ctx.newPage();
1168
+ try {
1169
+ await page.goto('https://www.bing.com/search?q=' + encodeURIComponent(query), { waitUntil: 'domcontentloaded', timeout: 10000 });
1170
+ const res = await page.evaluate(() => {
1171
+ const list = [];
1172
+ const items = document.querySelectorAll('li.b_algo');
1173
+ for (const item of items) {
1174
+ const titleEl = item.querySelector('h2 a');
1175
+ if (!titleEl) continue;
1176
+ const title = titleEl.textContent || '';
1177
+ const url = titleEl.href;
1178
+ let snippet = '';
1179
+ const snippetEl = item.querySelector('.b_caption p, .b_snippet, p');
1180
+ if (snippetEl) {
1181
+ snippet = snippetEl.textContent || '';
1182
+ }
1183
+ if (url && title) {
1184
+ list.push({ title, url, snippet });
1185
+ }
1186
+ }
1187
+ return list;
1188
+ });
1189
+ await page.close();
1190
+ return res;
1191
+ } catch (e) {
1192
+ if (page) await page.close();
1193
+ return [];
1194
+ }
1195
+ })(),
1196
+
1197
+ // DuckDuckGo
1198
+ (async () => {
1199
+ const page = await ctx.newPage();
1200
+ try {
1201
+ await page.goto('https://html.duckduckgo.com/html/?q=' + encodeURIComponent(query), { waitUntil: 'domcontentloaded', timeout: 10000 });
1202
+ const res = await page.evaluate(() => {
1203
+ const list = [];
1204
+ const elements = document.querySelectorAll('.result');
1205
+ for (const el of elements) {
1206
+ const titleEl = el.querySelector('.result__title a');
1207
+ const snippetEl = el.querySelector('.result__snippet');
1208
+ if (titleEl) {
1209
+ list.push({
1210
+ title: titleEl.textContent.trim(),
1211
+ url: titleEl.href,
1212
+ snippet: snippetEl ? snippetEl.textContent.trim() : ''
1213
+ });
1214
+ }
1215
+ }
1216
+ return list;
1217
+ });
1218
+ await page.close();
1219
+ return res;
1220
+ } catch (e) {
1221
+ if (page) await page.close();
1222
+ return [];
1223
+ }
1224
+ })()
1225
+ ]);
1226
+
1227
+ // Deduplicate results by normalized URL
1228
+ const allResults = [...googleResults, ...bingResults, ...ddgResults];
1229
+ const uniqueResults = [];
1230
+ const seenUrls = new Set();
1231
+ for (const res of allResults) {
1232
+ if (!res.url || !res.title) continue;
1233
+ let normUrl = res.url.replace(/^(https?:\\/\\/)?(www\\.)?/, '').toLowerCase();
1234
+ if (normUrl.endsWith('/')) normUrl = normUrl.slice(0, -1);
1235
+ if (!seenUrls.has(normUrl)) {
1236
+ seenUrls.add(normUrl);
1237
+ uniqueResults.push(res);
1238
+ }
1239
+ }
1240
+
1241
+ // Score results to prioritize numeric price data for financial queries
1242
+ const isPriceQuery = /giá|vàng|đô|usd|sjc|sh|hôm nay|price|gold|rate|vnd|xe|vnđ/i.test(query);
1243
+ const scoredResults = uniqueResults.map(res => {
1244
+ let score = 0;
1245
+ // Base length score
1246
+ score += Math.min(res.snippet.length / 50, 5);
1247
+
1248
+ if (isPriceQuery) {
1249
+ // Number density check
1250
+ const numCount = (res.snippet.match(/\\d+/g) || []).length;
1251
+ score += Math.min(numCount * 2, 10);
1252
+
1253
+ // Priority keywords boost
1254
+ if (/lượng|chỉ|triệu|nghìn|vnd|usd|sjc|xe|bán|mua|giá/i.test(res.snippet)) {
1255
+ score += 8;
1256
+ }
1257
+ }
1258
+ return { ...res, score };
1259
+ });
1260
+
1261
+ // Sort by score desc
1262
+ scoredResults.sort((a, b) => b.score - a.score);
1263
+
1264
+ // Map back to output format and limit
1265
+ const output = scoredResults.map(({ score, ...rest }) => rest).slice(0, limit);
1266
+ console.log(JSON.stringify(output, null, 2));
1267
+
1268
+ } catch (err) {
1269
+ console.error(JSON.stringify({ error: err.message }));
1270
+ } finally {
1271
+ if (browser && isStandalone) {
1272
+ try {
1273
+ await browser.close();
1274
+ } catch(e) {}
1275
+ }
1276
+ }
1277
+ })();
1278
+ `;
1279
+ }
1280
+
1062
1281
  function buildBrowserToolJs(variant = 'wizard') {
1063
1282
  // v2: Full-featured browser-tool.js matching OpenClaw native browser plugin capabilities
1064
1283
  // Both 'cli' and 'wizard' variants now use the same full script
@@ -1185,7 +1404,16 @@ const CDP_URL = 'http://127.0.0.1:9222';
1185
1404
  const { isVi = true, variant = 'wizard', workspaceRoot = '' } = options;
1186
1405
  const wsRoot = workspaceRoot.replace(/\/+$/, '');
1187
1406
  const btPath = wsRoot ? `${wsRoot}/browser-tool.js` : 'browser-tool.js';
1188
- const modeHeading = variant === 'cli-server' ? '# Headless Server Mode\n\n' : '';
1407
+ let modeHeading = '';
1408
+ if (variant === 'cli-server') {
1409
+ modeHeading = isVi
1410
+ ? `# 🌍 Trình duyệt ảo (Browser Automation)\n\n## 💡 Hướng dẫn vận hành:\n- **Script điều khiển:** \`browser-tool.js\` (Mọi câu lệnh browser đều chạy qua script này).\n- **Môi trường chạy:**\n - **Trên VPS / Linux Server (Headless):** Trình duyệt chạy ngầm hoàn toàn độc lập (Headless) bên trong Docker / Server qua Xvfb. Không thể mở màn hình Chrome thật.\n - **Trên Máy tính cá nhân (Windows/Mac) - Dù chạy Docker hay Native:**\n - **Mặc định:** Chạy ngầm (headless) cực kỳ ổn định.\n - **Chế độ quan sát (Xem bot click):** Nếu bạn muốn xem trực tiếp Chrome thật hoạt động trên màn hình, hãy chạy file \`start-chrome-debug.bat\` (trên Windows) hoặc \`start-chrome-debug.sh\` (trên Mac) ở máy của bạn **trước khi** bot kết nối! Bot sẽ tự động chuyển sang điều khiển màn hình Chrome thật của bạn.\n- **Kết nối mặc định:** \`http://127.0.0.1:9222\`\n\n`
1411
+ : `# 🌍 Browser Automation\n\n## 💡 Operating Guide:\n- **Control script:** \`browser-tool.js\` (All browser commands are executed through this script).\n- **Running environment:**\n - **On VPS / Linux Server (Headless Server Mode):** The browser runs fully headless and isolated inside Docker / Server via Xvfb. No GUI Chrome can be launched.\n - **On Personal Computers (Windows/Mac) - Docker or Native:**\n - **Default:** Runs headless and stable in the background.\n - **Observer Mode (Visual Chrome GUI):** If you want to see the real Chrome window being controlled, run \`start-chrome-debug.bat\` (on Windows) or \`start-chrome-debug.sh\` (on Mac) on your host machine **before** the bot connects! The bot will automatically hook into your real desktop Chrome.\n- **Default endpoint:** \`http://127.0.0.1:9222\`\n\n`;
1412
+ } else {
1413
+ modeHeading = isVi
1414
+ ? `# 🌍 Hướng dẫn Browser (Chrome CDP)\n- **Script điều khiển:** \`browser-tool.js\`\n- **Kết nối Chrome debug:** \`http://127.0.0.1:9222\`\n- **Xem trực quan:** Hãy chạy file \`start-chrome-debug.bat\` (trên Windows) hoặc \`start-chrome-debug.sh\` (trên Mac) để mở Chrome chế độ Debug.\n\n`
1415
+ : `# 🌍 Browser Guide (Chrome CDP)\n- **Control script:** \`browser-tool.js\`\n- **Chrome debug endpoint:** \`http://127.0.0.1:9222\`\n- **Visual interface:** Run \`start-chrome-debug.bat\` (on Windows) or \`start-chrome-debug.sh\` (on Mac) to open Chrome in Debug mode.\n\n`;
1416
+ }
1189
1417
 
1190
1418
  return `${modeHeading}# Navigation
1191
1419
  node ${btPath} status
@@ -1290,11 +1518,37 @@ node ${btPath} close_tab 2`;
1290
1518
  const browserRef = hasBrowser
1291
1519
  ? (browserDocVariant === 'cli-server'
1292
1520
  ? (isVi
1293
- ? `\n\n## \uD83C\uDF10 Browser Automation\n- Xem hướng dẫn chi tiết tại **BROWSER.md**\n- Chế độ hiện tại: browser native/headless của OpenClaw trong Docker hoặc server\n- Nếu browser lỗi, thử lại 1 lần rồi mới báo user với lỗi cụ thể`
1294
- : `\n\n## \uD83C\uDF10 Browser Automation\n- See detailed guide at **BROWSER.md**\n- Current mode: native/headless OpenClaw browser inside Docker or server\n- If browser fails, retry once before reporting the concrete error to the user`)
1521
+ ? `\n\n## 🌐 Browser Automation
1522
+ - Xem hướng dẫn chi tiết tại **BROWSER.md**
1523
+ - Script điều khiển: \`browser-tool.js\`
1524
+ - Chế độ hiện tại:
1525
+ - **Trên VPS / Linux Server:** Chạy ngầm độc lập qua Docker hoặc Xvfb.
1526
+ - **Trên Windows/Mac (Docker hoặc Native):** Chạy ngầm mặc định, hoặc chạy file \`start-chrome-debug.bat\` / \`start-chrome-debug.sh\` để xem trình duyệt trực quan trên màn hình.
1527
+ - Kết nối mặc định: \`http://127.0.0.1:9222\`
1528
+ - **Tìm kiếm Web:** Nếu không có công cụ Web Search (hoặc Web Search không khả dụng/bị lỗi), hãy **luôn sử dụng ngay công cụ terminal (exec/run_command) để chạy lệnh: \`node search-tool.js "<từ khóa>" 5\`**! Lệnh này sẽ tự động chạy ngầm qua DuckDuckGo/Google/Bing bằng trình duyệt ngầm tàng hình của bạn và trả về kết quả JSON sạch ngay lập tức. Tuyệt đối KHÔNG được mở trình duyệt thủ công, chụp ảnh màn hình hay click tìm kiếm bằng tay từng bước!
1529
+ - Nếu browser lỗi, thử lại 1 lần rồi mới báo user với lỗi cụ thể`
1530
+ : `\n\n## 🌐 Browser Automation
1531
+ - See detailed guide at **BROWSER.md**
1532
+ - Control script: \`browser-tool.js\`
1533
+ - Current mode:
1534
+ - **On VPS / Linux Server:** Runs headless via Docker or Xvfb.
1535
+ - **On Windows/Mac (Docker or Native):** Runs headless by default, or run \`start-chrome-debug.bat\` / \`start-chrome-debug.sh\` to see the GUI.
1536
+ - Default endpoint: \`http://127.0.0.1:9222\`
1537
+ - **Web Searching:** If the Web Search tool is unavailable or fails, **always use your terminal execution tool (exec/run_command) to run: \`node search-tool.js "<query>" 5\`**! This will automatically execute the search via DuckDuckGo/Google/Bing under stealth browser mode and return a clean JSON result immediately. Never open the browser manually, take screenshots, or click the search button step-by-step!
1538
+ - If browser fails, retry once before reporting the concrete error to the user`)
1295
1539
  : (isVi
1296
- ? `\n\n## \uD83C\uDF10 Browser Automation\n- Xem hướng dẫn chi tiết tại **BROWSER.md**\n- Script điều khiển: \`browser-tool.js\`\n- Kết nối Chrome debug: \`http://127.0.0.1:9222\`\n- Nếu tool/profile hỏi đường browser desktop, dùng profile \`host-chrome\` trước`
1297
- : `\n\n## \uD83C\uDF10 Browser Automation\n- See detailed guide at **BROWSER.md**\n- Control script: \`browser-tool.js\`\n- Chrome debug endpoint: \`http://127.0.0.1:9222\`\n- If a desktop browser profile is needed, use the \`host-chrome\` profile first`))
1540
+ ? `\n\n## 🌐 Browser Automation
1541
+ - Xem hướng dẫn chi tiết tại **BROWSER.md**
1542
+ - Script điều khiển: \`browser-tool.js\`
1543
+ - Kết nối Chrome debug: \`http://127.0.0.1:9222\`
1544
+ - **Tìm kiếm Web:** Hãy **luôn sử dụng ngay công cụ terminal (exec/run_command) để chạy lệnh: \`node search-tool.js "<từ khóa>" 5\`**! Lệnh này sẽ tự động chạy ngầm qua DuckDuckGo/Google/Bing bằng trình duyệt ngầm tàng hình của bạn và trả về kết quả JSON sạch ngay lập tức. Tuyệt đối KHÔNG được mở trình duyệt thủ công, chụp ảnh màn hình hay click tìm kiếm bằng tay từng bước!
1545
+ - Nếu tool/profile hỏi đường browser desktop, dùng profile \`host-chrome\` trước`
1546
+ : `\n\n## 🌐 Browser Automation
1547
+ - See detailed guide at **BROWSER.md**
1548
+ - Control script: \`browser-tool.js\`
1549
+ - Chrome debug endpoint: \`http://127.0.0.1:9222\`
1550
+ - **Web Searching:** If the Web Search tool is unavailable or fails, **always use your terminal execution tool (exec/run_command) to run: \`node search-tool.js "<query>" 5\`**! This will automatically execute the search via DuckDuckGo/Google/Bing under stealth browser mode and return a clean JSON result immediately. Never open the browser manually, take screenshots, or click the search button step-by-step!
1551
+ - If a desktop browser profile is needed, use the \`host-chrome\` profile first`))
1298
1552
  : '';
1299
1553
 
1300
1554
  const telegramSection = (variant === 'relay')
@@ -1305,8 +1559,38 @@ node ${btPath} close_tab 2`;
1305
1559
 
1306
1560
  const cronSection = hasScheduler
1307
1561
  ? (isVi
1308
- ? `\n\n## \u23F0 Cron / Lên lịch nhắc nhở\n- OpenClaw có hỗ trợ tool hệ thống để chạy Cron Job.\n- Khi user yêu cầu tạo nhắc nhở / lệnh tự động định kỳ, bạn hãy TỰ ĐỘNG dùng tool hệ thống để tạo. **Tuyệt đối không** bắt user dùng crontab hay Task Scheduler chạy tay trên host.\n- Khi thao tác tool cho cron/scheduler, **không điền \`current\` vào thư mục Session**.\n- Bỏ qua việc tra cứu docs nội bộ như \`cron-jobs.mdx\`; tin tưởng khả năng dùng tool hiện có để hoàn thành yêu cầu.`
1309
- : `\n\n## \u23F0 Cron / Scheduled Tasks\n- OpenClaw natively supports system tools for Cron Jobs.\n- When the user asks to schedule tasks or reminders, use the built-in tools automatically. Do NOT ask users to run crontab or Task Scheduler manually on the host.\n- When operating cron/scheduler tools, do **not** put \`current\` into the Session directory.\n- Skip internal doc lookups such as \`cron-jobs.mdx\`; rely on the available tools and complete the scheduling task directly.`)
1562
+ ? `\n\n## \u23F0 Cron / Lên lịch nhắc nhở (tool: \`cron\`)
1563
+ - **Tên tool chính xác:** Tên công cụ \`cron\` (tuyệt đối không nhầm \`native\` hay command line bên ngoài).
1564
+ - **Khi tạo cronjob mới (action \`add\`):**
1565
+ - **TUYỆT ĐỐI KHÔNG điền trường \`agentId\`** trong object \`job\` (hãy bỏ qua/omitted trường này). Hệ thống OpenClaw sẽ tự động gán chính xác ID của bạn vào job đó.
1566
+ - Tuyệt đối **không tự điền** \`agentId\` là \`"bot"\` hay \`"main"\`, vì làm vậy sẽ khiến cronjob thuộc về agent khác và bạn sẽ mất quyền kiểm soát/xóa nó sau này.
1567
+ - **Khi user yêu cầu tắt/bật/xóa cronjob:**
1568
+ 1. **Bước 1 (Tìm kiếm):** Gọi tool \`cron\` với action \`list\` (và \`includeDisabled: true\`) để xem danh sách tất cả cronjob đang chạy trên hệ thống và tìm đúng \`jobId\` phù hợp với yêu cầu.
1569
+ 2. **Bước 2 (Xử lý):**
1570
+ - Để xóa: Gọi action \`remove\` với \`id\` tìm được.
1571
+ - Để tắt/tạm dừng: Gọi action \`update\` với \`id\` và patch \`{"enabled": false}\`.
1572
+ - Để bật lại: Gọi action \`update\` với \`id\` và patch \`{"enabled": true}\`.
1573
+ 3. **Tuyên bố trung thực:** Tuyệt đối không bao giờ trả lời "đã xóa" hay "không có" dựa trên suy đoán của bản thân mà chưa gọi tool \`cron\` để kiểm tra thực tế.
1574
+ - Khi user yêu cầu tạo nhắc nhở / lệnh tự động định kỳ, bạn hãy TỰ ĐỘNG dùng tool \`cron\` (action \`add\`) để tạo. **Tuyệt đối không** bắt user dùng crontab hay Task Scheduler chạy tay trên host.
1575
+ - Khi thao tác tool cho cron/scheduler, **không điền \`current\` vào thư mục Session**.
1576
+ - **QUAN TRỌNG VỀ TARGETING GROUP CHAT**: Khi tạo hoặc cấu hình cron job gửi tin nhắn thông báo (announce mode) đến một Group Chat, giá trị của trường \`delivery.to\` **bắt buộc** phải sử dụng tiền tố \`group:\` trước ID của group (ví dụ: \`group:3815464776067464419\` hoặc \`group:xxxx\`). Tuyệt đối không được chỉ điền ID thuần túy vì hệ thống sẽ hiểu nhầm đó là một DM chat cá nhân (direct message) và gửi sai địa chỉ.
1577
+ - Bỏ qua việc tra cứu docs nội bộ như \`cron-jobs.mdx\`; tin tưởng khả năng dùng tool hiện có để hoàn thành yêu cầu.`
1578
+ : `\n\n## \u23F0 Cron / Scheduled Tasks (tool: \`cron\`)
1579
+ - **Exact tool name:** The tool name is \`cron\` (never mistake it for \`native\` or external command lines).
1580
+ - **When creating a new cronjob (action \`add\`):**
1581
+ - **ABSOLUTELY DO NOT specify the \`agentId\` field** in the \`job\` object (leave this field omitted). The OpenClaw system will automatically assign your correct agent ID to that job.
1582
+ - Never manually specify \`agentId\` as \`"bot"\` or \`"main"\`, as this will cause the cronjob to belong to another agent and you will lose control to manage/delete it later.
1583
+ - **When the user requests to disable/enable/delete a cronjob:**
1584
+ 1. **Step 1 (Search):** Call the \`cron\` tool with action \`list\` (and \`includeDisabled: true\`) to view all cron jobs on the system and find the matching \`jobId\`.
1585
+ 2. **Step 2 (Processing):**
1586
+ - To delete: Call action \`remove\` with the \`id\` found.
1587
+ - To disable/pause: Call action \`update\` with \`id\` and patch \`{"enabled": false}\`.
1588
+ - To enable: Call action \`update\` with \`id\` and patch \`{"enabled": true}\`.
1589
+ 3. **Honest statement:** Never claim a job is "deleted" or "not found" based on guessing without calling the \`cron\` tool to verify the actual state.
1590
+ - When the user asks to schedule tasks or reminders, use the built-in \`cron\` tool (action \`add\`) automatically. Do NOT ask users to run crontab or Task Scheduler manually on the host.
1591
+ - When operating cron/scheduler tools, do **not** put \`current\` into the Session directory.
1592
+ - **IMPORTANT ABOUT GROUP CHAT TARGETING**: When creating or configuring a cron job to send messages (announce mode) to a Group Chat, the value of the \`delivery.to\` field **must** use the \`group:\` prefix before the group ID (e.g., \`group:3815464776067464419\` or \`group:xxxx\`). Never specify just the numeric ID, as the system will interpret it as a private DM and deliver to the wrong destination.
1593
+ - Skip internal doc lookups such as \`cron-jobs.mdx\`; rely on the available tools and complete the scheduling task directly.`)
1310
1594
  : '';
1311
1595
 
1312
1596
  const zaloModSection = '';
@@ -1322,8 +1606,8 @@ node ${btPath} close_tab 2`;
1322
1606
  }
1323
1607
 
1324
1608
  return isVi
1325
- ? `# Hướng dẫn sử dụng Tools\n\n## Danh sách skills đã cài\n${skillsSection}\n\n## Nguyên tắc chung\n- Ưu tiên dùng tool/skill phù hợp thay vì tự suy đoán\n- Nếu tool trả về lỗi — thử lại 1 lần, sau đó báo user\n- Không chạy tool liên tục mà không có mục đích rõ ràng\n- Luôn tóm tắt kết quả tool cho user thay vì dump raw output${browserRef}\n\n## Quy ước\n- Web Search: chỉ dùng khi cần thông tin realtime hoặc user yêu cầu\n- Browser: chỉ mở trang khi user yêu cầu cụ thể\n- Memory: tự ghi nhớ thông tin tự nhiên, không cần user nhắc${cronSection}${zaloModSection}\n\n## \uD83D\uDCC1 File & Workspace\n- Bot có thể đọc/ghi file trong thư mục workspace: \`${workspacePath}\`\n- Dùng để lưu notes, scripts, cấu hình tạm\n\n## \u26A0\uFE0F Tool Error Handling\n- Retry tối đa 2 lần nếu tool lỗi network\n- Nếu vẫn lỗi: báo user kèm mô tả lỗi cụ thể và gợi ý workaround${dmOverride}\n`
1326
- : `# Tool Usage Guide\n\n## Installed Skills\n${skillsSection}\n\n## General Principles\n- Prefer using the right tool/skill over guessing\n- If a tool returns an error — retry once, then report to user\n- Don't run tools repeatedly without a clear purpose\n- Always summarize tool output for user instead of dumping raw data${browserRef}\n\n## Conventions\n- Web Search: only use when needing real-time info or user explicitly asks\n- Browser: only open pages when user specifically requests\n- Memory: proactively remember important info without user prompting${cronSection}${zaloModSection}\n\n## \uD83D\uDCC1 File & Workspace\n- Bot can read/write files in workspace: \`${workspacePath}\`\n\n## \u26A0\uFE0F Tool Error Handling\n- Retry up to 2 times on network errors\n- If still failing: report to user with specific error description and workaround${dmOverride}\n`;
1609
+ ? `# Hướng dẫn sử dụng Tools\n\n## Danh sách skills đã cài\n${skillsSection}\n\n## Nguyên tắc chung\n- Ưu tiên dùng tool/skill phù hợp thay vì tự suy đoán\n- Nếu tool trả về lỗi — thử lại 1 lần, sau đó báo user\n- Không chạy tool liên tục mà không có mục đích rõ ràng\n- Luôn tóm tắt kết quả tool cho user thay vì dump raw output${browserRef}\n\n## Quy ước\n- Web Search: TUYỆT ĐỐI KHÔNG dùng tool \`web_search\` native vì nó đã bị vô hiệu hóa hoàn toàn trên hệ thống. Thay vào đó, mỗi khi cần tìm kiếm Internet/Web, bạn BẮT BUỘC phải gọi công cụ \`exec\` để chạy lệnh terminal: \`node search-tool.js "<từ khóa>" 5\`! Lệnh này cực kỳ nhanh, tàng hình và miễn phí.\n- Browser: chỉ mở trang khi user yêu cầu cụ thể\n- Memory: tự ghi nhớ thông tin tự nhiên, không cần user nhắc${cronSection}${zaloModSection}\n\n## \uD83D\uDCC1 File & Workspace\n- Bot có thể đọc/ghi file trong thư mục workspace: \`${workspacePath}\`\n- Dùng để lưu notes, scripts, cấu hình tạm\n\n## \u26A0\uFE0F Tool Error Handling\n- Retry tối đa 2 lần nếu tool lỗi network\n- Nếu vẫn lỗi: báo user kèm mô tả lỗi cụ thể và gợi ý workaround${dmOverride}\n`
1610
+ : `# Tool Usage Guide\n\n## Installed Skills\n${skillsSection}\n\n## General Principles\n- Prefer using the right tool/skill over guessing\n- If a tool returns an error — retry once, then report to user\n- Don't run tools repeatedly without a clear purpose\n- Always summarize tool output for user instead of dumping raw data${browserRef}\n\n## Conventions\n- Web Search: DO NOT use the native \`web_search\` tool as it is completely disabled. Instead, whenever you need to search the Internet/Web, you MUST call the \`exec\` tool to run terminal command: \`node search-tool.js "<query>" 5\`! This is extremely fast, stealthy and free.\n- Browser: only open pages when user specifically requests\n- Memory: proactively remember important info without user prompting${cronSection}${zaloModSection}\n\n## \uD83D\uDCC1 File & Workspace\n- Bot can read/write files in workspace: \`${workspacePath}\`\n\n## \u26A0\uFE0F Tool Error Handling\n- Retry up to 2 times on network errors\n- If still failing: report to user with specific error description and workaround${dmOverride}\n`;
1327
1611
  }
1328
1612
  function buildTeamsDoc(options = {}) {
1329
1613
  const {
@@ -1418,6 +1702,7 @@ node ${btPath} close_tab 2`;
1418
1702
  'HEARTBEAT.md': buildHeartbeatDoc({ isVi }),
1419
1703
  'BOOTSTRAP.md': buildBootstrapDoc({ isVi, botName }),
1420
1704
  'DREAMS.md': buildDreamsDoc({ isVi }),
1705
+ 'search-tool.js': buildSearchToolJs(),
1421
1706
  };
1422
1707
 
1423
1708
  if (isMultiBot) {
@@ -1445,6 +1730,7 @@ node ${btPath} close_tab 2`;
1445
1730
  buildDreamsDoc,
1446
1731
  buildHeartbeatDoc,
1447
1732
  buildBootstrapDoc,
1733
+ buildSearchToolJs,
1448
1734
  buildBrowserToolJs,
1449
1735
  buildBrowserDoc,
1450
1736
  buildSecurityRules,
@@ -2705,7 +2991,6 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
2705
2991
  is9Router,
2706
2992
  isLocal,
2707
2993
  isMultiBot,
2708
- hasBrowser,
2709
2994
  selectedModel,
2710
2995
  agentId,
2711
2996
  allSkills = [],
@@ -2724,23 +3009,9 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
2724
3009
  plainSingleExtraHosts = false,
2725
3010
  multiOllamaNumParallel = 1,
2726
3011
  singleOllamaNumParallel = 1,
2727
- emitBrowserInstall = true,
2728
3012
  gatewayPort = 18789,
2729
3013
  routerPort = 20128,
2730
3014
  } = options;
2731
-
2732
- const browserAptExtra = hasBrowser ? ' xvfb socat' : '';
2733
- const browserInstallLines = hasBrowser && emitBrowserInstall
2734
- ? [
2735
- '',
2736
- '# Browser Automation: Playwright engine (needed for native CDP)',
2737
- 'RUN npm install -g agent-browser playwright \\',
2738
- ' && npx playwright install chromium --with-deps \\',
2739
- ' && ln -f -s /root/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /usr/bin/google-chrome',
2740
- '',
2741
- ''
2742
- ].join('\n')
2743
- : '';
2744
3015
  const skillLines = dockerfileSkillInstallMode === 'build' && allSkills.length > 0
2745
3016
  ? `\n# Install skills (ClawHub)\n${allSkills.map((skill) => `RUN openclaw skills install ${skill} || echo "Warning: Failed to install ${skill} due to rate limits."`).join('\n')}\n`
2746
3017
  : '';
@@ -2780,6 +3051,20 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
2780
3051
  ' echo "[entrypoint] plugin $id missing; installing $spec"',
2781
3052
  ' openclaw plugins install "$spec" 2>/dev/null || echo "[entrypoint] warning: failed to install plugin $spec"',
2782
3053
  '}',
3054
+ 'ensure_zalouser() {',
3055
+ ' NPM_DIR="$OPENCLAW_HOME/npm"',
3056
+ ' PKG_DIR="$NPM_DIR/node_modules/@openclaw/zalouser"',
3057
+ ' if [ -d "$PKG_DIR" ]; then',
3058
+ ' echo "[entrypoint] zalouser plugin already installed"',
3059
+ ' else',
3060
+ ' echo "[entrypoint] zalouser plugin missing; installing via npm"',
3061
+ ' mkdir -p "$NPM_DIR"',
3062
+ ' cd "$NPM_DIR"',
3063
+ ' npm init -y 2>/dev/null || true',
3064
+ ' npm install @openclaw/zalouser@latest 2>/dev/null || echo "[entrypoint] warning: failed to install @openclaw/zalouser"',
3065
+ ' cd /root/project',
3066
+ ' fi',
3067
+ '}',
2783
3068
  'ensure_skill() {',
2784
3069
  ' id="$1"',
2785
3070
  ' if find "$OPENCLAW_HOME" -maxdepth 4 -type d -path "*/skills/$id" -print -quit 2>/dev/null | grep -q .; then',
@@ -2805,7 +3090,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
2805
3090
  ' sed -i "s/LISTENER_WATCHDOG_MAX_GAP_MS\\\\s*=\\\\s*35e3/LISTENER_WATCHDOG_MAX_GAP_MS = 90e3/" "$ZALO_JS"',
2806
3091
  ' echo "[entrypoint] patched zalouser watchdog gap: 35s -> 90s"',
2807
3092
  'fi',
2808
- ].join('\\n'));
3093
+ ].join('\n'));
2809
3094
  runtimeParts.push([
2810
3095
  '# Zalo channel auto-restart monitor (background)',
2811
3096
  '(',
@@ -2815,27 +3100,23 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
2815
3100
  ' STATUS=$(openclaw channels status 2>/dev/null | grep -i "Zalo Personal" || true)',
2816
3101
  ' if echo "$STATUS" | grep -qi "stopped"; then',
2817
3102
  ' echo "[zalo-monitor] Zalo channel stopped - restarting container in 5s"',
2818
- ' sleep 5',
3103
+ ' sleep 5',
2819
3104
  ' kill 1 2>/dev/null || true',
2820
3105
  ' fi',
2821
3106
  ' done',
2822
3107
  ') &',
2823
- ].join('\\n'));
2824
- if (hasBrowser) {
2825
- runtimeParts.push('socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 &');
2826
- runtimeParts.push('Xvfb :99 -screen 0 1280x720x24 > /dev/null 2>&1 & DISPLAY=:99 openclaw gateway run');
2827
- } else {
2828
- runtimeParts.push('openclaw gateway run');
2829
- }
3108
+ ].join('\n'));
3109
+ runtimeParts.push('openclaw gateway run');
2830
3110
  const runtimeScript = ['#!/bin/sh', 'set -e', ...runtimeParts].join('\n');
2831
3111
  const dockerfile = `FROM node:22-slim
2832
3112
 
2833
- RUN apt-get update && apt-get install -y git curl python3${browserAptExtra} && rm -rf /var/lib/apt/lists/*
2834
- ${browserInstallLines}
3113
+ RUN apt-get update && apt-get install -y git curl python3 && rm -rf /var/lib/apt/lists/*
3114
+
2835
3115
  ARG OPENCLAW_VER="${openClawNpmSpec}"
2836
3116
  ARG CACHE_BUST=""
2837
3117
  RUN echo "CACHE_BUST=$CACHE_BUST" && npm install -g $OPENCLAW_VER ${openClawRuntimePackages}${skillLines}${pluginLines}
2838
3118
  ${patchLine}
3119
+
2839
3120
  COPY entrypoint.sh /usr/local/bin/openclaw-entrypoint.sh
2840
3121
  RUN chmod +x /usr/local/bin/openclaw-entrypoint.sh
2841
3122
  WORKDIR /root/project
@@ -2858,7 +3139,7 @@ CMD ["/bin/sh", "/usr/local/bin/openclaw-entrypoint.sh"]`;
2858
3139
  : isLocal
2859
3140
  ? ' depends_on:\n ollama:\n condition: service_healthy\n'
2860
3141
  : '';
2861
- const extraHosts = hasBrowser ? `${extraHostsBlock}\n` : '';
3142
+ const extraHosts = `${extraHostsBlock}\n`;
2862
3143
  if (is9Router) {
2863
3144
  compose = `name: ${multiComposeName}
2864
3145
  services:
@@ -2961,7 +3242,7 @@ services:
2961
3242
  - ../../.env
2962
3243
  depends_on:
2963
3244
  - 9router
2964
- ${appEnvironmentBlock}${hasBrowser ? `${extraHostsBlock}\n` : ''} volumes:
3245
+ ${appEnvironmentBlock}${extraHostsBlock}\n volumes:
2965
3246
  - ${volumeMount}
2966
3247
  - openclaw-plugins:/root/project/.openclaw/npm
2967
3248
  ports:
@@ -3002,7 +3283,7 @@ services:
3002
3283
  ${appEnvironmentBlock} depends_on:
3003
3284
  ollama:
3004
3285
  condition: service_healthy
3005
- ${hasBrowser ? `${extraHostsBlock}\n` : ''} ports:
3286
+ ${extraHostsBlock}\n ports:
3006
3287
  - "${gatewayPort}:${gatewayPort}"
3007
3288
  volumes:
3008
3289
  - ${volumeMount}
@@ -3097,10 +3378,9 @@ function buildNativeScriptCtx(options) {
3097
3378
  const ch = CHANNELS[state.channel];
3098
3379
  const is9Router = !!(provider && provider.isProxy);
3099
3380
  const isOllama = !!(provider && provider.isLocal);
3100
- const hasBrowser = state.config.skills.includes('browser');
3101
3381
  const nativeSkillConfigs = state.config.skills
3102
3382
  .map((sid) => SKILLS.find((s) => s.id === sid))
3103
- .filter((skill) => skill && skill.id !== 'scheduler' && skill.slug && skill.slug !== 'browser-automation');
3383
+ .filter((skill) => skill && skill.id !== 'scheduler' && skill.slug);
3104
3384
  const selectedModel = (state.config.model || 'ollama/gemma4:e2b').replace('ollama/', '');
3105
3385
  const isMultiBot = state.botCount > 1 && state.channel === 'telegram';
3106
3386
  const projectDir = state.config.projectPath || '.';
@@ -3120,7 +3400,6 @@ function buildNativeScriptCtx(options) {
3120
3400
  Object.assign(globalThis, {
3121
3401
  isVi,
3122
3402
  provider,
3123
- hasBrowser,
3124
3403
  is9Router,
3125
3404
  selectedModel,
3126
3405
  isMultiBot,
@@ -3474,7 +3753,6 @@ setInterval(sync, INTERVAL);`;
3474
3753
  provider,
3475
3754
  is9Router,
3476
3755
  isOllama,
3477
- hasBrowser,
3478
3756
  selectedModel,
3479
3757
  isMultiBot,
3480
3758
  projectDir,
@@ -3510,7 +3788,6 @@ setInterval(sync, INTERVAL);`;
3510
3788
  * @global {boolean} isVi - Vietnamese language mode
3511
3789
  * @global {object} provider - Current primary provider config
3512
3790
  * @global {boolean} isMultiBot - Multi-bot mode flag
3513
- * @global {boolean} hasBrowser - Browser plugin selected
3514
3791
  * @global {boolean} is9Router - 9Router proxy mode
3515
3792
  * @global {string} projectDir - Output project directory path
3516
3793
  * @global {Function} getGatewayAllowedOrigins
@@ -3575,8 +3852,8 @@ setInterval(sync, INTERVAL);`;
3575
3852
  groupId,
3576
3853
  selectedSkills: state.config.skills,
3577
3854
  skills: SKILLS,
3578
- hasBrowserDesktop: hasBrowser && state.browserMode === 'desktop',
3579
- hasBrowserServer: hasBrowser && state.browserMode !== 'desktop',
3855
+ hasBrowserDesktop: false,
3856
+ hasBrowserServer: false,
3580
3857
  gatewayPort: basePort,
3581
3858
  gatewayAllowedOrigins: getGatewayAllowedOrigins(basePort),
3582
3859
  osChoice: state.nativeOs || '',
@@ -3646,7 +3923,7 @@ setInterval(sync, INTERVAL);`;
3646
3923
  agentWorkspaceDir,
3647
3924
  persona: bot.persona || '',
3648
3925
  userInfo: state.config.userInfo || '',
3649
- hasBrowser,
3926
+ hasBrowser: false,
3650
3927
  soulVariant: 'wizard',
3651
3928
  memoryVariant: 'wizard',
3652
3929
  hasZaloMod: state.channel === 'zalo-personal',
@@ -4151,7 +4428,7 @@ window.__downloadGen = {
4151
4428
  */
4152
4429
  function generateWinBat(ctx) {
4153
4430
  const {
4154
- ch, isVi, provider, is9Router, isOllama, hasBrowser, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4431
+ ch, isVi, provider, is9Router, isOllama, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4155
4432
  } = ctx;
4156
4433
  // state, PROVIDERS, SKILLS, PLUGINS, CHANNELS are IIFE-level globals
4157
4434
  let scriptContent;
@@ -4204,11 +4481,6 @@ function generateWinBat(ctx) {
4204
4481
  }
4205
4482
 
4206
4483
  providerLines(lines, 'bat');
4207
- if (hasBrowser) {
4208
- lines.push('echo Cai Browser Automation runtime...');
4209
- lines.push('call npm install -g agent-browser playwright || goto :fail');
4210
- lines.push('call npx playwright install chromium || goto :fail');
4211
- }
4212
4484
  if (nativeSkillInstallCmds.length > 0) {
4213
4485
  lines.push('echo Cai skills...');
4214
4486
  lines.push(...nativeSkillInstallCmds);
@@ -4296,7 +4568,7 @@ function generateWinBat(ctx) {
4296
4568
  */
4297
4569
  function generateMacOsSh(ctx) {
4298
4570
  const {
4299
- ch, isVi, provider, is9Router, isOllama, hasBrowser, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4571
+ ch, isVi, provider, is9Router, isOllama, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4300
4572
  } = ctx;
4301
4573
  // state, PROVIDERS, SKILLS, PLUGINS, CHANNELS are IIFE-level globals
4302
4574
  let scriptContent;
@@ -4414,7 +4686,7 @@ function generateMacOsSh(ctx) {
4414
4686
  */
4415
4687
  function generateVpsSh(ctx) {
4416
4688
  const {
4417
- ch, isVi, provider, is9Router, isOllama, hasBrowser, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4689
+ ch, isVi, provider, is9Router, isOllama, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4418
4690
  } = ctx;
4419
4691
  // state, PROVIDERS, SKILLS, PLUGINS, CHANNELS are IIFE-level globals
4420
4692
  let scriptContent;
@@ -4539,7 +4811,7 @@ GWEOF`);
4539
4811
  */
4540
4812
  function generateLinuxSh(ctx) {
4541
4813
  const {
4542
- ch, isVi, provider, is9Router, isOllama, hasBrowser, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4814
+ ch, isVi, provider, is9Router, isOllama, selectedModel, isMultiBot, projectDir, todayStamp, allPlugins, pluginCmd, nativeSkillInstallCmds, nativeSkillConfigs, providerLines, sharedNativeFileMap, sharedNativeEnvContent, sharedNativeExecApprovalsContent, sharedNativeConfigContent, native9RouterSyncScriptContent, native9RouterServerEntryLookup, windowsHiddenNodeLaunch, generateUninstallScript,
4543
4815
  } = ctx;
4544
4816
  // state, PROVIDERS, SKILLS, PLUGINS, CHANNELS are IIFE-level globals
4545
4817
  let scriptContent;
@@ -5794,68 +6066,6 @@ function generateLinuxSh(ctx) {
5794
6066
  const routerNotice = document.getElementById('9router-notice');
5795
6067
  if (routerNotice) routerNotice.style.display = is9Router ? '' : 'none';
5796
6068
 
5797
- // Show/hide Browser Automation notice + generate scripts
5798
- const browserNotice = document.getElementById('browser-notice');
5799
- const hasBrowserSkill = state.config.skills.includes('browser');
5800
- if (browserNotice) browserNotice.style.display = hasBrowserSkill ? '' : 'none';
5801
-
5802
- if (hasBrowserSkill) {
5803
- // Chrome Debug .bat script
5804
- const chromeBat = `@echo off
5805
- echo ============================================
5806
- echo OpenClaw - Chrome Debug Mode
5807
- echo ============================================
5808
- echo.
5809
- echo Dang tat Chrome cu (neu co)...
5810
- taskkill /F /IM chrome.exe >nul 2>&1
5811
- timeout /t 3 /nobreak >nul
5812
- echo Dang mo Chrome voi Debug Mode...
5813
- start "" "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" ^
5814
- --remote-debugging-port=9222 ^
5815
- --remote-allow-origins=* ^
5816
- --user-data-dir="%TEMP%\\chrome-debug"
5817
- timeout /t 4 /nobreak >nul
5818
- powershell -Command "try { Invoke-WebRequest -Uri 'http://localhost:9222/json/version' -UseBasicParsing -TimeoutSec 5 | Out-Null; Write-Host 'OK! Chrome Debug Mode dang chay tren port 9222.' -ForegroundColor Green } catch { Write-Host 'LOI: Port 9222 chua mo. Thu lai.' -ForegroundColor Red }"
5819
- echo.
5820
- pause`;
5821
- setOutput('out-chrome-bat', chromeBat);
5822
-
5823
- // Task Scheduler PowerShell script
5824
- const taskPs1 = `# ============================================
5825
- # OpenClaw - Auto-start Chrome Debug khi logon
5826
- # Chay script nay 1 lan voi Run as Administrator
5827
- # ============================================
5828
-
5829
- # Duong dan toi file .bat
5830
- $batPath = "$env:USERPROFILE\\start-chrome-debug.bat"
5831
-
5832
- # Kiem tra file .bat ton tai
5833
- if (-not (Test-Path $batPath)) {
5834
- Write-Host "LOI: Khong tim thay $batPath" -ForegroundColor Red
5835
- Write-Host "Hay luu file start-chrome-debug.bat vao $env:USERPROFILE truoc." -ForegroundColor Yellow
5836
- exit 1
5837
- }
5838
-
5839
- # Tao Scheduled Task
5840
- $action = New-ScheduledTaskAction -Execute $batPath
5841
- $trigger = New-ScheduledTaskTrigger -AtLogOn
5842
- $trigger.Delay = "PT10S" # Delay 10 giay sau khi logon
5843
- $settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable
5844
-
5845
- Register-ScheduledTask \\
5846
- -TaskName "OpenClaw-ChromeDebug" \\
5847
- -Description "Tu dong bat Chrome Debug Mode cho OpenClaw Browser Automation" \\
5848
- -Action $action \\
5849
- -Trigger $trigger \\
5850
- -Settings $settings \\
5851
- -Force
5852
-
5853
- Write-Host ""
5854
- Write-Host "DONE! Task 'OpenClaw-ChromeDebug' da duoc tao." -ForegroundColor Green
5855
- Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (delay 10s)." -ForegroundColor Cyan`;
5856
- setOutput('out-task-ps1', taskPs1);
5857
- }
5858
-
5859
6069
  // Show/hide docker vs native output based on deployMode
5860
6070
  const dockerOut = document.getElementById('docker-output');
5861
6071
  const nativeOut = document.getElementById('native-output');
@@ -5884,8 +6094,6 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
5884
6094
  : 'Script is generated from your choices. Download and run — everything else is handled automatically.';
5885
6095
 
5886
6096
  const agentId = state.config.botName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-$/, '') || 'chat';
5887
-
5888
- const hasBrowser = state.config.skills.includes('browser');
5889
6097
  // isMultiBot => unified into isMultiBot above
5890
6098
  const multiBotAgentMetas = isMultiBot
5891
6099
  ? state.bots.slice(0, state.botCount).map((bot, idx) => {
@@ -5974,19 +6182,6 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
5974
6182
  };
5975
6183
  }
5976
6184
 
5977
- // Browser Automation: inject browser config
5978
- if (hasBrowser) {
5979
- clawConfig.browser = {
5980
- enabled: true,
5981
- defaultProfile: 'host-chrome',
5982
- profiles: {
5983
- 'host-chrome': {
5984
- cdpUrl: 'http://127.0.0.1:9222',
5985
- color: '#4285F4',
5986
- },
5987
- },
5988
- };
5989
- }
5990
6185
 
5991
6186
  // Skills: register all selected skills in openclaw.json → skills.entries
5992
6187
  // This makes OpenClaw actually load and enable them at runtime
@@ -5995,8 +6190,6 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
5995
6190
  state.config.skills.forEach((sid) => {
5996
6191
  const skill = SKILLS.find((s) => s.id === sid);
5997
6192
  if (!skill) return;
5998
- // Native browser tools are loaded automatically via the root 'browser' config
5999
- if (skill.slug === 'browser-automation') return;
6000
6193
  // scheduler is now native cron (not a skill), skip registering in skills.entries
6001
6194
  if (skill.id === 'scheduler' || !skill.slug) return;
6002
6195
 
@@ -6124,7 +6317,7 @@ model:
6124
6317
  const allSkills = [];
6125
6318
  state.config.skills.forEach((sid) => {
6126
6319
  const skill = SKILLS.find((s) => s.id === sid);
6127
- if (skill && skill.slug && skill.slug !== 'browser-automation') {
6320
+ if (skill && skill.slug) {
6128
6321
  allSkills.push(skill.slug);
6129
6322
  }
6130
6323
  });
@@ -6144,7 +6337,6 @@ model:
6144
6337
  is9Router,
6145
6338
  isLocal,
6146
6339
  isMultiBot: state.botCount > 1 && (state.channel === 'telegram'),
6147
- hasBrowser,
6148
6340
  selectedModel: state.config.model || 'ollama/gemma4:e2b',
6149
6341
  agentId: 'bot',
6150
6342
  allSkills,
@@ -6304,12 +6496,6 @@ _This file is yours to evolve. If someone asks to change it, confirm with the us
6304
6496
  `;
6305
6497
 
6306
6498
  // ── AGENTS.md — Hướng dẫn vận hành ("operating manual")
6307
- const browserAgentSection = hasBrowser ? `
6308
- ## Sử dụng Trình Duyệt (Browser Automation)
6309
- - BẠN SỞ HỮU GIAO DIỆN TRÌNH DUYỆT CHROME THẬT CỦA USER thông qua script \`browser-tool.js\`. ĐỌC NGAY FILE \`BROWSER.md\` để biết cách dùng.
6310
- - BẮT BUỘC dùng \`bash\` để gõ \`node ~/browser-tool.js ...\` khi có yêu cầu liên quan đến web thay vì dùng web_search!
6311
- - KHÔNG BAO GIỜ từ chối mở trình duyệt với lý do "không có giao diện" hay "máy chủ không có browser".
6312
- ` : '';
6313
6499
 
6314
6500
  const agentsMd = lang === 'vi'
6315
6501
  ? `# Hướng dẫn vận hành
@@ -6337,7 +6523,6 @@ Bạn hỗ trợ người dùng trong mọi tác vụ hàng ngày thông qua tin
6337
6523
  - Luôn xác nhận kết quả tool trước khi trả lời user
6338
6524
  - Nếu tool lỗi → thông báo rõ ràng, đề xuất cách khác
6339
6525
 
6340
- ${browserAgentSection}
6341
6526
  ${state.config.securityRules}
6342
6527
  `
6343
6528
 
@@ -6421,143 +6606,6 @@ _Update this file as you learn more about the user. Ask before changing._
6421
6606
  // ── MEMORY.md — via scaffold builder
6422
6607
  const memoryMd = _scaffold.buildMemoryDoc({ isVi, variant: 'wizard' });
6423
6608
 
6424
- // Browser tool files (generated into workspace when hasBrowser)
6425
- const browserToolJs = `/**
6426
- * browser-tool.js - Connect to real Windows Chrome via CDP
6427
- * Flow: Docker -> socat (port 9222) -> host.docker.internal:9222 -> user's Chrome
6428
- */
6429
- const { chromium } = require('/usr/local/lib/node_modules/openclaw/node_modules/playwright-core');
6430
- const action = process.argv[2];
6431
- const param1 = process.argv[3];
6432
- const param2 = process.argv[4];
6433
- const CDP_URL = 'http://127.0.0.1:9222';
6434
- (async () => {
6435
- let browser;
6436
- try {
6437
- browser = await chromium.connectOverCDP(CDP_URL, { timeout: 5000 });
6438
- const ctx = browser.contexts()[0];
6439
- const pages = ctx.pages();
6440
- let page = pages.length > 0 ? pages[0] : await ctx.newPage();
6441
- if (action === 'open') {
6442
- console.log('[Browser] Mo trang: ' + param1);
6443
- await page.goto(param1, { waitUntil: 'domcontentloaded', timeout: 30000 });
6444
- await page.waitForTimeout(1500);
6445
- console.log('[Browser] Da mo: ' + (await page.title()) + ' | ' + page.url());
6446
- } else if (action === 'get_text') {
6447
- const text = await page.evaluate(() => {
6448
- document.querySelectorAll('script,style,noscript,svg').forEach(e => e.remove());
6449
- return document.body.innerText.trim();
6450
- });
6451
- console.log(text.substring(0, 4000));
6452
- } else if (action === 'click') {
6453
- await page.locator(param1).first().click({ timeout: 5000 });
6454
- await page.waitForTimeout(600);
6455
- console.log('[Browser] Da click: ' + param1);
6456
- } else if (action === 'fill') {
6457
- await page.locator(param1).first().fill(param2, { timeout: 5000 });
6458
- console.log('[Browser] Da dien "' + param2 + '" vao: ' + param1);
6459
- } else if (action === 'press') {
6460
- await page.keyboard.press(param1);
6461
- await page.waitForTimeout(1000);
6462
- console.log('[Browser] Da nhan phim: ' + param1);
6463
- } else if (action === 'status') {
6464
- console.log('[Browser] Ket noi Chrome that! Tab: ' + (await page.title()) + ' | ' + page.url());
6465
- } else {
6466
- console.log('Lenh: open <url> | get_text | click <sel> | fill <sel> <text> | press <key> | status');
6467
- }
6468
- } catch(e) {
6469
- if (e.message.includes('ECONNREFUSED') || e.message.includes('Timeout')) {
6470
- console.error('[Browser] Ban chua bat Chrome Debug! Hay click dup vao start-chrome-debug.bat roi thu lai.');
6471
- } else {
6472
- console.error('[Browser] Loi:', e.message);
6473
- }
6474
- } finally {
6475
- if (browser) await browser.close();
6476
- }
6477
- `;
6478
-
6479
- const browserMd = `# Dieu Khien Trinh Duyet (Browser Automation)
6480
-
6481
- Bot dieu khien Chrome THAT tren man hinh Windows cua ban. Moi thao tac hien thi truc tiep!
6482
-
6483
- ## Lenh su dung (chay qua bash)
6484
-
6485
- \\\`\\\`\\\`bash
6486
- node ~/browser-tool.js status
6487
- node ~/browser-tool.js open "https://google.com"
6488
- node ~/browser-tool.js get_text
6489
- node ~/browser-tool.js fill "input[name='q']" "tu khoa"
6490
- node ~/browser-tool.js press "Enter"
6491
- node ~/browser-tool.js click "#button"
6492
- \\\`\\\`\\\`
6493
-
6494
- ## QUY TAC BAT BUOC
6495
- - KHONG BAO GIO tu choi mo trinh duyet.
6496
- - Neu loi ECONNREFUSED: bao user bat lai start-chrome-debug.bat.
6497
- - KHONG dung web_search khi user yeu cau browser.
6498
- `;
6499
-
6500
- const chromeBatContent = `@echo off
6501
- echo ====== OpenClaw - Chrome Debug Mode ======
6502
- echo.
6503
- echo Dang tat Chrome cu (neu co)...
6504
- taskkill /F /IM chrome.exe >nul 2>&1
6505
- timeout /t 3 /nobreak >nul
6506
- echo Dang mo Chrome voi Debug Mode...
6507
- start "" "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" ^
6508
- --remote-debugging-port=9222 ^
6509
- --remote-allow-origins=* ^
6510
- --user-data-dir="%TEMP%\\chrome-debug"
6511
- timeout /t 4 /nobreak >nul
6512
- powershell -Command "try { Invoke-WebRequest -Uri 'http://localhost:9222/json/version' -UseBasicParsing -TimeoutSec 5 | Out-Null; Write-Host 'OK! Chrome Debug Mode dang chay.' -ForegroundColor Green } catch { Write-Host 'LOI: Port 9222 chua mo.' -ForegroundColor Red }"
6513
- echo.
6514
- pause
6515
- `;
6516
-
6517
- const chromeShContent = `#!/usr/bin/env bash
6518
- # ====== OpenClaw - Chrome Debug Mode (Mac/Linux) ======
6519
- set -e
6520
- echo "====== OpenClaw - Chrome Debug Mode ======"
6521
- echo ""
6522
-
6523
- # Detect Chrome path
6524
- if [[ "$OSTYPE" == "darwin"* ]]; then
6525
- CHROME_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
6526
- [ ! -f "$CHROME_BIN" ] && CHROME_BIN="/Applications/Chromium.app/Contents/MacOS/Chromium"
6527
- [ ! -f "$CHROME_BIN" ] && CHROME_BIN="/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"
6528
- else
6529
- CHROME_BIN="$(command -v google-chrome || command -v google-chrome-stable || command -v chromium-browser || command -v chromium || echo '')"
6530
- fi
6531
- [ -n "$CHROME_DEBUG_BIN" ] && CHROME_BIN="$CHROME_DEBUG_BIN"
6532
-
6533
- if [ -z "$CHROME_BIN" ] || { [ ! -f "$CHROME_BIN" ] && [ ! -x "$CHROME_BIN" ]; }; then
6534
- echo -e "\\033[31mERROR: Chrome/Chromium not found.\\033[0m"
6535
- echo "Install Chrome or: export CHROME_DEBUG_BIN=/path/to/chrome"
6536
- exit 1
6537
- fi
6538
-
6539
- echo "Using: $CHROME_BIN"
6540
- echo "Killing existing Chrome debug instances..."
6541
- pkill -f -- "--remote-debugging-port=9222" 2>/dev/null || true
6542
- sleep 2
6543
-
6544
- TMP_DIR="\${TMPDIR:-/tmp}/chrome-debug-openclaw"
6545
- mkdir -p "$TMP_DIR"
6546
-
6547
- echo "Starting Chrome in Debug Mode (port 9222)..."
6548
- "$CHROME_BIN" \\
6549
- --remote-debugging-port=9222 \\
6550
- --remote-allow-origins=* \\
6551
- --user-data-dir="$TMP_DIR" &
6552
-
6553
- sleep 4
6554
- if curl -s http://localhost:9222/json/version > /dev/null 2>&1; then
6555
- echo -e "\\033[32mOK! Chrome Debug Mode is running on port 9222.\\033[0m"
6556
- else
6557
- echo -e "\\033[31mERROR: Port 9222 not responding.\\033[0m"
6558
- exit 1
6559
- fi
6560
- `;
6561
6609
 
6562
6610
  const envText = (document.getElementById('env-content')?.textContent || '').trim();
6563
6611
  const rootEnvContent = envText ? `${envText}\n` : '';
@@ -6612,10 +6660,6 @@ fi
6612
6660
  });
6613
6661
  sharedFiles[`.openclaw/${meta.workspaceDir}/TEAMS.md`] = _scaffold.buildTeamsDoc({ isVi });
6614
6662
  sharedFiles[`.openclaw/${meta.workspaceDir}/MEMORY.md`] = memoryMd;
6615
- if (hasBrowser) {
6616
- sharedFiles[`.openclaw/${meta.workspaceDir}/browser-tool.js`] = browserToolJs;
6617
- sharedFiles[`.openclaw/${meta.workspaceDir}/BROWSER.md`] = browserMd;
6618
- }
6619
6663
  }
6620
6664
  state._generatedFiles = sharedFiles;
6621
6665
  } else {
@@ -6635,10 +6679,6 @@ fi
6635
6679
  }),
6636
6680
  [`.openclaw/workspace-${agentId}/MEMORY.md`]: memoryMd,
6637
6681
  '.gitignore': isNativeMode ? '.env\nnode_modules/' : '.env\ndocker/openclaw/.env\nnode_modules/',
6638
- ...(hasBrowser ? {
6639
- [`.openclaw/workspace-${agentId}/browser-tool.js`]: browserToolJs,
6640
- [`.openclaw/workspace-${agentId}/BROWSER.md`]: browserMd,
6641
- } : {}),
6642
6682
  };
6643
6683
  if (rootEnvContent) {
6644
6684
  singleFiles['.env'] = rootEnvContent;
@@ -6660,8 +6700,6 @@ fi
6660
6700
  // chrome-debug, start-bot, uninstall added ONCE here, not per-bot-mode block
6661
6701
  if (isNativeMode) {
6662
6702
  const _files = state._generatedFiles;
6663
- _files['start-chrome-debug.bat'] = chromeBatContent;
6664
- _files['start-chrome-debug.sh'] = chromeShContent;
6665
6703
  _files['start-bot.bat'] = generateStartBotBat({
6666
6704
  projectDir: state.config.projectPath || '.',
6667
6705
  openclawHome: (state.config.projectPath || '.') + '\\.openclaw',