create-openclaw-bot 5.6.13 → 5.7.0

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.
@@ -113,23 +113,142 @@ function buildBootstrapDoc(options = {}) {
113
113
  }
114
114
 
115
115
  function buildBrowserToolJs(variant = 'wizard') {
116
- if (variant === 'cli') {
117
- return `const { chromium } = require('playwright');\n(async () => {\n const [,, action, param1, param2] = process.argv;\n if (!action) { console.log('Usage: node browser-tool.js open|get_text|click|fill|press|status [params]'); process.exit(0); }\n let browser;\n try {\n browser = await chromium.connectOverCDP('http://127.0.0.1:9222');\n const ctx = browser.contexts()[0] || await browser.newContext();\n const page = ctx.pages()[0] || await ctx.newPage();\n if (action === 'open') {\n await page.goto(param1, { waitUntil: 'domcontentloaded', timeout: 20000 });\n console.log('[Browser] Opened: ' + (await page.title()) + ' | ' + page.url());\n } else if (action === 'get_text') {\n const text = await page.evaluate(() => document.body.innerText.trim());\n console.log(text.substring(0, 4000));\n } else if (action === 'click') {\n await page.locator(param1).first().click({ timeout: 5000 });\n console.log('[Browser] Clicked: ' + param1);\n } else if (action === 'fill') {\n await page.locator(param1).first().fill(param2, { timeout: 5000 });\n console.log('[Browser] Filled into: ' + param1);\n } else if (action === 'press') {\n await page.keyboard.press(param1);\n console.log('[Browser] Pressed: ' + param1);\n } else if (action === 'status') {\n console.log('[Browser] Connected: ' + page.url());\n }\n } finally {\n if (browser) await browser.close();\n }\n})();\n`;
118
- }
119
- return `const { chromium } = require('playwright');\n(async () => {\n const [,, action, param1, param2] = process.argv;\n const browser = await chromium.connectOverCDP('http://127.0.0.1:9222');\n const ctx = browser.contexts()[0] || await browser.newContext();\n const page = ctx.pages()[0] || await ctx.newPage();\n if (action === 'open') await page.goto(param1, { waitUntil: 'domcontentloaded', timeout: 30000 });\n else if (action === 'click') await page.locator(param1).first().click({ timeout: 5000 });\n else if (action === 'fill') await page.locator(param1).first().fill(param2, { timeout: 5000 });\n else if (action === 'press') await page.keyboard.press(param1);\n else console.log(await page.title(), page.url());\n await browser.close();\n})();\n`;
116
+ // v2: Full-featured browser-tool.js matching OpenClaw native browser plugin capabilities
117
+ // Both 'cli' and 'wizard' variants now use the same full script
118
+ const playwrightRequire = variant === 'cli'
119
+ ? "require('playwright')"
120
+ : "require('/usr/local/lib/node_modules/openclaw/node_modules/playwright-core')";
121
+
122
+ return `/**
123
+ * browser-tool.js v2 — Full-featured Chrome CDP controller
124
+ * Commands: open|get_url|get_text|get_links|get_posts|evaluate|console|screenshot|screenshot_full|pdf|click|fill|press|hover|select|upload|scroll|wait|resize|tabs|new_tab|switch_tab|close_tab|status
125
+ */
126
+ const { chromium } = ${playwrightRequire};
127
+ const action = process.argv[2];
128
+ const param1 = process.argv[3];
129
+ const param2 = process.argv[4];
130
+ const CDP_URL = 'http://127.0.0.1:9222';
131
+ (async () => {
132
+ let browser;
133
+ try {
134
+ browser = await chromium.connectOverCDP(CDP_URL, { timeout: 5000 });
135
+ const ctx = browser.contexts()[0];
136
+ const pages = ctx.pages();
137
+ let page = pages.length > 0 ? pages[0] : await ctx.newPage();
138
+ if (action === 'open') {
139
+ console.log('[Browser] Opening: ' + param1);
140
+ await page.goto(param1, { waitUntil: 'domcontentloaded', timeout: 30000 });
141
+ await page.waitForTimeout(1500);
142
+ console.log('[Browser] Opened: ' + (await page.title()) + ' | ' + page.url());
143
+ } else if (action === 'get_url') {
144
+ console.log(page.url());
145
+ } else if (action === 'status') {
146
+ const allPages = ctx.pages();
147
+ console.log('[Browser] Connected! Tabs: ' + allPages.length);
148
+ console.log('[Browser] Current: ' + (await page.title()) + ' | ' + page.url());
149
+ } else if (action === 'get_text') {
150
+ const maxLen = parseInt(param1) || 4000;
151
+ const text = await page.evaluate(() => { document.querySelectorAll('script,style,noscript,svg').forEach(e => e.remove()); return document.body.innerText.trim(); });
152
+ console.log(text.substring(0, maxLen));
153
+ } else if (action === 'get_links') {
154
+ const filter = param1 || '';
155
+ const links = await page.evaluate((f) => { const a = Array.from(document.querySelectorAll('a[href]')).map(e => e.href).filter(h => h && h.startsWith('http')); return [...new Set(f ? a.filter(h => h.includes(f)) : a)]; }, filter);
156
+ console.log(JSON.stringify(links.slice(0, 50), null, 2));
157
+ } else if (action === 'get_posts') {
158
+ const posts = await page.evaluate(() => {
159
+ const results = [];
160
+ const articles = document.querySelectorAll('[role="article"]');
161
+ for (const article of articles) {
162
+ const textEl = article.querySelector('[data-ad-comet-preview="message"],[data-ad-preview="message"]');
163
+ const fullText = (textEl ? textEl.innerText.trim() : '') || article.innerText.substring(0, 800);
164
+ const allLinks = Array.from(article.querySelectorAll('a[href]'));
165
+ let permalink = '';
166
+ for (const a of allLinks) { const h = a.href || ''; if (h.includes('/posts/') || h.includes('/permalink/') || h.includes('story_fbid')) { permalink = h.split('?')[0]; break; } }
167
+ let author = '';
168
+ for (const el of article.querySelectorAll('a[role="link"] strong, h2 a, h3 a, h4 a')) { const n = el.innerText.trim(); if (n && n.length > 1 && n.length < 50) { author = n; break; } }
169
+ let timePosted = '';
170
+ const timeLinks = allLinks.filter(a => { const h = a.href || ''; return h.includes('/posts/') || h.includes('/permalink/'); });
171
+ if (timeLinks.length > 0) { const t = timeLinks[0].innerText.trim(); if (t && t.length < 30) timePosted = t; }
172
+ if (!timePosted) { const te = article.querySelector('abbr,[data-utime]'); if (te) timePosted = te.innerText.trim() || te.getAttribute('title') || ''; }
173
+ if (fullText.length > 20) results.push({ author: author || 'N/A', text: fullText.substring(0, 500), permalink: permalink || 'N/A', time: timePosted || 'N/A' });
174
+ }
175
+ return results;
176
+ });
177
+ console.log(posts.length === 0 ? '[Browser] No posts found. Try scroll then get_posts again.' : JSON.stringify(posts.slice(0, 10), null, 2));
178
+ } else if (action === 'evaluate') {
179
+ const code = process.argv.slice(3).join(' ');
180
+ if (!code) { console.log('[Browser] Usage: evaluate <js_code>'); process.exit(1); }
181
+ const result = await page.evaluate(code);
182
+ console.log(result !== undefined && result !== null ? (typeof result === 'object' ? JSON.stringify(result, null, 2) : String(result)) : '[Browser] Done');
183
+ } else if (action === 'console') {
184
+ const msgs = []; page.on('console', m => msgs.push('[' + m.type() + '] ' + m.text()));
185
+ await page.waitForTimeout(2000);
186
+ console.log(msgs.length === 0 ? '[Browser] No console messages in 2s' : msgs.join('\\n'));
187
+ } else if (action === 'screenshot') {
188
+ const p = param1 || '/tmp/screenshot.png'; await page.screenshot({ path: p, fullPage: false }); console.log('[Browser] Screenshot: ' + p);
189
+ } else if (action === 'screenshot_full') {
190
+ const p = param1 || '/tmp/screenshot_full.png'; await page.screenshot({ path: p, fullPage: true }); console.log('[Browser] Full screenshot: ' + p);
191
+ } else if (action === 'pdf') {
192
+ const p = param1 || '/tmp/page.pdf'; await page.pdf({ path: p, format: 'A4' }); console.log('[Browser] PDF: ' + p);
193
+ } else if (action === 'click') {
194
+ await page.locator(param1).first().click({ timeout: 5000 }); await page.waitForTimeout(600); console.log('[Browser] Clicked: ' + param1);
195
+ } else if (action === 'fill') {
196
+ await page.locator(param1).first().fill(param2, { timeout: 5000 }); console.log('[Browser] Filled: ' + param1);
197
+ } else if (action === 'press') {
198
+ await page.keyboard.press(param1); await page.waitForTimeout(1000); console.log('[Browser] Pressed: ' + param1);
199
+ } else if (action === 'hover') {
200
+ await page.locator(param1).first().hover({ timeout: 5000 }); console.log('[Browser] Hovered: ' + param1);
201
+ } else if (action === 'select') {
202
+ await page.locator(param1).first().selectOption(param2, { timeout: 5000 }); console.log('[Browser] Selected: ' + param2);
203
+ } else if (action === 'upload') {
204
+ await page.locator(param1).first().setInputFiles(param2, { timeout: 5000 }); console.log('[Browser] Uploaded: ' + param2);
205
+ } else if (action === 'scroll') {
206
+ const px = parseInt(param1) || 800; await page.evaluate((p) => window.scrollBy(0, p), px); await page.waitForTimeout(2000); console.log('[Browser] Scrolled: ' + px + 'px');
207
+ } else if (action === 'wait') {
208
+ const ms = parseInt(param1) || 1000; await page.waitForTimeout(ms); console.log('[Browser] Waited: ' + ms + 'ms');
209
+ } else if (action === 'resize') {
210
+ const w = parseInt(param1) || 1280, h = parseInt(param2) || 720; await page.setViewportSize({ width: w, height: h }); console.log('[Browser] Resized: ' + w + 'x' + h);
211
+ } else if (action === 'tabs') {
212
+ const ap = ctx.pages(); for (let i = 0; i < ap.length; i++) { const t = await ap[i].title().catch(() => '(untitled)'); console.log('[' + i + '] ' + t + ' | ' + ap[i].url() + (ap[i] === page ? ' < current' : '')); }
213
+ } else if (action === 'new_tab') {
214
+ const np = await ctx.newPage(); if (param1) await np.goto(param1, { waitUntil: 'domcontentloaded', timeout: 30000 }); console.log('[Browser] New tab' + (param1 ? ': ' + param1 : ''));
215
+ } else if (action === 'switch_tab') {
216
+ const idx = parseInt(param1), ap = ctx.pages(); if (isNaN(idx) || idx < 0 || idx >= ap.length) { console.log('[Browser] Invalid index. Use tabs to list.'); } else { page = ap[idx]; await page.bringToFront(); console.log('[Browser] Switched to [' + idx + ']: ' + page.url()); }
217
+ } else if (action === 'close_tab') {
218
+ const ap = ctx.pages(), idx = param1 !== undefined ? parseInt(param1) : ap.indexOf(page); if (ap.length <= 1) { console.log('[Browser] Cannot close last tab.'); } else if (isNaN(idx) || idx < 0 || idx >= ap.length) { console.log('[Browser] Invalid index.'); } else { await ap[idx].close(); console.log('[Browser] Closed tab [' + idx + ']'); }
219
+ } else {
220
+ console.log('browser-tool.js v2 — Commands:');
221
+ console.log(' Nav: open <url> | get_url | status');
222
+ console.log(' Content: get_text [max] | get_links [filter] | get_posts | evaluate <js> | console');
223
+ console.log(' Export: screenshot [path] | screenshot_full [path] | pdf [path]');
224
+ console.log(' Interact: click <sel> | fill <sel> <txt> | press <key> | hover <sel> | select <sel> <val> | upload <sel> <path>');
225
+ console.log(' View: scroll [px] | wait <ms> | resize <w> <h>');
226
+ console.log(' Tabs: tabs | new_tab [url] | switch_tab <idx> | close_tab [idx]');
227
+ }
228
+ } catch(e) {
229
+ if (e.message.includes('ECONNREFUSED') || e.message.includes('Timeout')) {
230
+ console.error('[Browser] Chrome Debug not running! Start with --remote-debugging-port=9222');
231
+ } else { console.error('[Browser] Error:', e.message); }
232
+ } finally { if (browser) await browser.close(); }
233
+ })();
234
+ `;
120
235
  }
121
236
 
122
237
  function buildBrowserDoc(options = {}) {
123
238
  const { isVi = true, variant = 'wizard', workspaceRoot = '' } = options;
239
+ // Normalize: strip trailing slash so path joins work cleanly
240
+ const wsRoot = workspaceRoot.replace(/\/+$/, '');
241
+ const btPath = wsRoot ? `${wsRoot}/browser-tool.js` : 'browser-tool.js';
242
+
124
243
  if (variant === 'cli-desktop') {
125
- return `# Browser Automation (Desktop Mode)\n\nBot controls your actual Chrome on screen through Chrome Debug at \`http://127.0.0.1:9222\`. Every action is visible.\n\n## Usage\n\`\`\`bash\nnode ${workspaceRoot}/workspace/browser-tool.js status\nnode ${workspaceRoot}/workspace/browser-tool.js open "https://google.com"\nnode ${workspaceRoot}/workspace/browser-tool.js get_text\nnode ${workspaceRoot}/workspace/browser-tool.js fill "input[name='q']" "search"\nnode ${workspaceRoot}/workspace/browser-tool.js press "Enter"\n\`\`\`\n\n## MANDATORY RULES\n- NEVER refuse to open the browser when user asks.\n- In Desktop mode, always target the \`host-chrome\` / Chrome Debug session first.\n- Do not switch to a generic "user profile attach" explanation when Chrome Debug is the configured path.\n- If Chrome Debug is unreachable or returns \`ECONNREFUSED\`, tell user to run \`start-chrome-debug.bat\` again, then retry.\n- Only mention restarting OpenClaw gateway after Chrome Debug is reachable but browser actions still fail.\n`;
244
+ return `# Browser Automation (Desktop Mode)\n\nBot controls your actual Chrome on screen through Chrome Debug at \`http://127.0.0.1:9222\`. Every action is visible.\n\n## Usage (v2)\n\`\`\`bash\n# Navigation\nnode ${btPath} status\nnode ${btPath} open "https://google.com"\nnode ${btPath} get_url\n\n# Content extraction\nnode ${btPath} get_text\nnode ${btPath} get_text 8000 # custom max length\nnode ${btPath} get_links # all links\nnode ${btPath} get_links "/posts/" # filtered\nnode ${btPath} get_posts # Facebook group posts w/ permalinks\nnode ${btPath} evaluate "document.title"\nnode ${btPath} console\n\n# Screenshots & export\nnode ${btPath} screenshot\nnode ${btPath} screenshot_full\nnode ${btPath} pdf\n\n# Interactions\nnode ${btPath} click "button.submit"\nnode ${btPath} fill "input[name='q']" "search"\nnode ${btPath} press "Enter"\nnode ${btPath} hover "a.link"\nnode ${btPath} select "select#country" "VN"\nnode ${btPath} upload "input[type=file]" "/tmp/photo.jpg"\n\n# Scrolling & viewport\nnode ${btPath} scroll\nnode ${btPath} scroll 1500\nnode ${btPath} wait 3000\nnode ${btPath} resize 1920 1080\n\n# Tab management\nnode ${btPath} tabs\nnode ${btPath} new_tab "https://example.com"\nnode ${btPath} switch_tab 1\nnode ${btPath} close_tab 2\n\`\`\`\n\n## MANDATORY RULES\n- NEVER refuse to open the browser when user asks.\n- In Desktop mode, always target the \`host-chrome\` / Chrome Debug session first.\n- If Chrome Debug is unreachable or returns \`ECONNREFUSED\`, tell user to run \`start-chrome-debug.bat\` again.\n- Use \`get_posts\` instead of \`get_text\` when scraping Facebook it extracts permalinks.\n`;
126
245
  }
127
246
  if (variant === 'cli-server') {
128
247
  return `# Browser Automation (Headless Server Mode)\n\nBot uses a headless Chromium instance running inside the Docker container. No GUI needed!\n\n## Notes\n- Running on Ubuntu Server / VPS (no GUI required)\n- Uses Playwright + Headless Chromium installed inside Docker\n- For Cloudflare bypass, switch to Desktop mode (requires Windows/Mac with Chrome)\n`;
129
248
  }
130
249
  return isVi
131
- ? `# Browser Automation\n\nDùng file \`browser-tool.js\` để điều khiển Chrome debug tại \`http://127.0.0.1:9222\`.`
132
- : `# Browser Automation\n\nUse \`browser-tool.js\` to control Chrome debug on \`http://127.0.0.1:9222\`.`;
250
+ ? `# Browser Automation\n\nDùng file \`browser-tool.js\` để điều khiển Chrome debug tại \`http://127.0.0.1:9222\`.\nScript: \`${btPath}\`\nPhiên bản v2 hỗ trợ: open, get_text, get_links, get_posts, evaluate, screenshot, pdf, click, fill, press, hover, select, upload, scroll, tabs, và nhiều lệnh khác.`
251
+ : `# Browser Automation\n\nUse \`browser-tool.js\` to control Chrome debug on \`http://127.0.0.1:9222\`.\nScript: \`${btPath}\`\nVersion v2 supports: open, get_text, get_links, get_posts, evaluate, screenshot, pdf, click, fill, press, hover, select, upload, scroll, tabs, and more.`;
133
252
  }
134
253
 
135
254
  function buildSecurityRules(isVi = true) {
@@ -147,7 +266,7 @@ function buildBootstrapDoc(options = {}) {
147
266
  ownAliases = [],
148
267
  otherAgents = [], // [{ name, agentId }]
149
268
  replyToDirectMessages = true,
150
- workspacePath = '/root/.openclaw/workspace/',
269
+ workspacePath = '~/',
151
270
  variant = 'single', // 'single' | 'relay'
152
271
  includeSecurity = true,
153
272
  } = options;
@@ -181,7 +300,7 @@ function buildBootstrapDoc(options = {}) {
181
300
  const {
182
301
  isVi = true,
183
302
  skillListStr = '',
184
- workspacePath = '/root/.openclaw/workspace/',
303
+ workspacePath = '~/',
185
304
  variant = 'single', // 'single' | 'relay'
186
305
  agentWorkspaceDir = 'workspace',
187
306
  hasBrowser = false,
@@ -285,7 +404,7 @@ function buildBootstrapDoc(options = {}) {
285
404
  ownAliases = [],
286
405
  otherAgents = [],
287
406
  skillListStr = '',
288
- workspacePath = '/root/.openclaw/workspace/',
407
+ workspacePath = '~/',
289
408
  agentWorkspaceDir = 'workspace',
290
409
  persona = '',
291
410
  userInfo = '',