create-openclaw-bot 5.10.1 → 5.11.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/web/app.js CHANGED
@@ -7,9 +7,9 @@ const OS_OPTIONS = [
7
7
  { id: 'linux-desktop', title: 'Linux Desktop', subtitle: 'Ubuntu / Debian / Fedora', icon: `${SVG_CDN}/linux/default.svg`, badge: 'Desktop' },
8
8
  { id: 'vps', title: 'Linux VPS', subtitle: 'Server install with public bind', icon: `${SVG_CDN}/ubuntu/default.svg`, badge: 'Server' },
9
9
  ];
10
+ // Docker is the only supported deploy mode. (Native mode was removed.)
10
11
  const MODE_OPTIONS = [
11
12
  { id: 'docker', title: 'Docker', subtitle: 'Isolated containers, safest default', icon: `${SVG_CDN}/docker/default.svg`, badge: 'Recommended' },
12
- { id: 'native', title: 'Native', subtitle: 'Direct host install, lighter runtime', icon: `data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 64 64'%3E%3Crect width='64' height='64' rx='16' fill='%231b1113'/%3E%3Cpath d='M15 22l9 10-9 10' fill='none' stroke='%23ff3b4d' stroke-width='5' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M30 42h19' fill='none' stroke='%23f8f7f7' stroke-width='5' stroke-linecap='round'/%3E%3C/svg%3E`, badge: 'Advanced' },
13
13
  ];
14
14
  const BOT_CHANNELS = [
15
15
  { id: 'telegram', title: 'Telegram', subtitle: 'Bot API', icon: `${SVG_CDN}/telegram/default.svg`, badge: 'Tele' },
@@ -155,7 +155,7 @@ function fbPluginModal() {
155
155
  return `<div class="modal-backdrop confirm-backdrop" data-fbplugin="close">
156
156
  <section class="donate-modal confirm-modal" role="dialog" aria-modal="true" aria-label="fb-messenger plugin" onclick="event.stopPropagation()">
157
157
  <button class="modal-x" data-fbplugin="close" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
158
- <div class="donate-head"><span aria-hidden="true">🔌</span><div><p>${t('Cần plugin','Plugin required')}</p><h2>fb-messenger</h2><small>${t('Để tạo bot <b>Facebook Messenger</b>, bạn cần plugin <b>fb-messenger</b> do <b>tuanminhhole</b> cung cấp. Plugin hiện chưa public vui lòng liên hệ để nhận.','To create a <b>Facebook Messenger</b> bot you need the <b>fb-messenger</b> plugin provided by <b>tuanminhhole</b>. It is not public yet please reach out to get it.')}</small></div></div>
158
+ <div class="donate-head"><span aria-hidden="true">🔌</span><div><p>${t('Cần plugin','Plugin required')}</p><h2>fb-messenger</h2><small>${t('Bot <b>Facebook Messenger</b> cần plugin <b>fb-messenger</b> (đã public trên ClawHub). Bấm <b>Tạo bot</b>, rồi mở <b>Bot Plugins</b> bấm <b>Cài</b> thẻ <b>openclaw-fb-messenger</b>.','A <b>Facebook Messenger</b> bot needs the <b>fb-messenger</b> plugin (now public on ClawHub). Click <b>Create bot</b>, then open <b>Bot Plugins</b> and hit <b>Install</b> on the <b>openclaw-fb-messenger</b> card.')}</small></div></div>
159
159
  <div class="socials" style="justify-content:center;display:flex;gap:10px;width:100%;margin:8px 0 4px;">
160
160
  ${socials.map(([n,u])=>`<a href="${u}" target="_blank" rel="noopener" aria-label="${n}">${socialIcon(n)}</a>`).join('')}
161
161
  </div>
@@ -169,8 +169,9 @@ function confirmModal() {
169
169
  return `<div class="modal-backdrop confirm-backdrop" data-confirm-action="cancel">
170
170
  <section class="donate-modal confirm-modal" role="dialog" aria-modal="true" aria-label="${escapeHtml(m.title)}" onclick="event.stopPropagation()">
171
171
  <button class="modal-x" data-confirm-action="cancel" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
172
- <div class="donate-head"><span aria-hidden="true">⚠</span><div><p>${escapeHtml(m.eyebrow || t('Xác nhận','Confirm'))}</p><h2>${escapeHtml(m.title)}</h2><small>${escapeHtml(m.message || '')}</small></div></div>
173
- <div class="confirm-actions"><button class="secondary" data-confirm-action="cancel">${t('Hủy','Cancel')}</button><button class="primary danger" data-confirm-action="ok">${escapeHtml(m.okText || t('Xóa','Delete'))}</button></div>
172
+ <div class="donate-head"><span aria-hidden="true">${escapeHtml(m.icon || '⚠')}</span><div><p>${escapeHtml(m.eyebrow || t('Xác nhận','Confirm'))}</p><h2>${escapeHtml(m.title)}</h2><small>${escapeHtml(m.message || '')}</small></div></div>
173
+ ${m.bodyHtml ? `<div class="confirm-body">${m.bodyHtml}</div>` : ''}
174
+ <div class="confirm-actions">${m.hideCancel ? '' : `<button class="secondary" data-confirm-action="cancel">${t('Hủy','Cancel')}</button>`}<button class="primary ${m.okDanger === false ? '' : 'danger'}" data-confirm-action="ok">${escapeHtml(m.okText || t('Xóa','Delete'))}</button></div>
174
175
  </section>
175
176
  </div>`;
176
177
  }
@@ -210,7 +211,6 @@ function installModal() {
210
211
  const osChoices = OS_OPTIONS.map(o => [o.id, t(o.title, o.title), trChoice(o).subtitle]);
211
212
  const modeChoices = [
212
213
  ['docker', 'Docker', t('\u0043ontainer c\u00f4 l\u1eadp, an to\u00e0n nh\u1ea5t', 'Isolated containers, safest default')],
213
- ['native', 'Native', t('C\u00e0i tr\u1ef1c ti\u1ebfp, runtime nh\u1eb9 h\u01a1n', 'Direct host install, lighter runtime')],
214
214
  ];
215
215
  return `<div class="modal-backdrop install-backdrop" data-install-modal="close">
216
216
  <section class="donate-modal install-modal" role="dialog" aria-modal="true" aria-label="${t('T\u1ea1o Project','Create Project')}" onclick="event.stopPropagation()">
@@ -519,7 +519,7 @@ function renderFilesPanel() {
519
519
  // Re-wire only file-panel-specific handlers
520
520
  panel.querySelectorAll('[data-select-file]').forEach(btn => btn.onclick = () => { state.selectedFile = btn.dataset.selectFile; renderFilesPanel(); });
521
521
  panel.querySelectorAll('[data-toggle-dir]').forEach(btn => btn.onclick = () => { const p = btn.dataset.toggleDir; state.openDirs[p] = !(state.openDirs[p] ?? true); renderFilesPanel(); });
522
- panel.querySelectorAll('.save').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => { const name = btn.dataset.file; await api('/api/bot/files/'+encodeURIComponent(name), { method: 'PUT', body: { projectDir: activeProjectDir(), content: document.querySelector(`[data-editor="${CSS.escape(name)}"]`).value } }); showToast(t('Đã lưu', 'Saved'), t('Đã lưu tệp tin: ', 'Saved file: ') + fileBaseName(name), 'success'); btn.innerHTML=`${actionIcon('save')} ${ui('saved')}`; setTimeout(()=>btn.innerHTML=`${actionIcon('save')} ${ui('save')}`,1200); }));
522
+ panel.querySelectorAll('.save').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => { const name = btn.dataset.file; try { await api('/api/bot/files/'+encodeURIComponent(name), { method: 'PUT', body: { projectDir: activeProjectDir(), content: document.querySelector(`[data-editor="${CSS.escape(name)}"]`).value } }); showToast(t('Đã lưu', 'Saved'), t('Đã lưu tệp tin: ', 'Saved file: ') + fileBaseName(name), 'success'); btn.innerHTML=`${actionIcon('save')} ${ui('saved')}`; setTimeout(()=>btn.innerHTML=`${actionIcon('save')} ${ui('save')}`,1200); } catch (err) { showToast(t('Lưu thất bại', 'Save failed'), err.message, 'error'); } }));
523
523
  }
524
524
 
525
525
  function renderSkillsPanel() {
@@ -564,6 +564,34 @@ function wireSkillsHandlers(scope = document) {
564
564
  }
565
565
  renderSkillsPanel();
566
566
  });
567
+ scope.querySelectorAll('[data-chrome-debug]').forEach(btn => btn.onclick = async () => {
568
+ btn.disabled = true;
569
+ try {
570
+ const r = await api('/api/browser/start-chrome-debug', { method: 'POST', body: {} });
571
+ if (r.headless) {
572
+ // VPS has no display — guide the user to run Chrome on THEIR machine + a reverse tunnel.
573
+ state.confirmModal = {
574
+ icon: '🌐',
575
+ eyebrow: t('Chrome debug', 'Chrome debug'),
576
+ title: t('Dùng Chrome trên máy của bạn', 'Use Chrome on your computer'),
577
+ message: t('VPS không có màn hình nên không mở Chrome tại đây. Chạy 2 lệnh dưới trên máy của bạn và GIỮ chúng chạy — bot sẽ tự dùng Chrome đó (cổng 9222). Không làm cũng không sao: bot vẫn có trình duyệt ẩn (headless) sẵn trong container.', 'This VPS has no display. Run the two commands below on YOUR computer and keep them running — the bot will use that Chrome (port 9222). Optional: the bot already has a built-in headless browser.'),
578
+ bodyHtml: `<p class="guide-step">1. ${t('Mở Chrome debug trên máy bạn — macOS:', 'Start Chrome debug on your machine — macOS:')}</p><code class="cmd">${escapeHtml(r.chromeCmdMac)}</code><p class="guide-step">${t('Hoặc Windows:', 'Or Windows:')}</p><code class="cmd">${escapeHtml(r.chromeCmdWin)}</code><p class="guide-step">2. ${t('Tạo tunnel về VPS (cửa sổ terminal khác):', 'Create the reverse tunnel (another terminal):')}</p><code class="cmd">${escapeHtml(r.tunnelCmd)}</code>`,
579
+ okText: t('Đã hiểu', 'Got it'),
580
+ okDanger: false,
581
+ hideCancel: true,
582
+ onConfirm: () => { state.confirmModal = null; render(); },
583
+ };
584
+ render();
585
+ } else {
586
+ showToast(t('Đã mở Chrome debug', 'Chrome debug started'),
587
+ `${t('Cổng', 'Port')} ${r.port} — ${t('bot sẽ ưu tiên dùng Chrome này', 'the bot will prefer this Chrome')}`, 'success');
588
+ }
589
+ } catch (err) {
590
+ showToast(t('Thất bại', 'Failed'), err.message, 'error');
591
+ } finally {
592
+ btn.disabled = false;
593
+ }
594
+ });
567
595
  }
568
596
 
569
597
  function title() {
@@ -907,11 +935,17 @@ function joinDisplayPath(root = '', child = '') {
907
935
  const r = String(root || '').replace(/[\\/]+$/, '');
908
936
  const c = String(child || '').replace(/^[\\/]+/, '');
909
937
  if (!r) return c || '-';
910
- return c ? `${r}\\${c.replace(/\//g, '\\')}` : r;
938
+ if (!c) return r;
939
+ // Match the separator style of the project root: Windows paths (C:\...) join with "\",
940
+ // POSIX paths join with "/" (the old always-backslash join rendered Linux paths mangled).
941
+ const win = /^[A-Za-z]:/.test(r) || r.includes('\\');
942
+ return win ? `${r}\\${c.replace(/\//g, '\\')}` : `${r}/${c.replace(/\\/g, '/')}`;
911
943
  }
912
944
  function projectPathLine(bot = currentBot(), fileName = '') {
913
945
  const s = state.install || {};
914
- const workspace = bot?.workspace || '';
946
+ // Legacy configs stored the container-absolute workspace; show it relative to the project root
947
+ // (new configs are already relative — see bot-config-gen).
948
+ const workspace = String(bot?.workspace || '').replace(/^\/home\/node\/project\/?/, '');
915
949
  const relFile = fileName ? String(fileName).replace(/^\.openclaw[\\/][^\\/]+[\\/]?/, '') : '';
916
950
  const full = workspace ? joinDisplayPath(s.projectDir || '', relFile ? `${workspace}/${relFile}` : workspace) : (s.projectDir || '-');
917
951
  const label = workspace ? t('Workspace','Workspace') : ui('project');
@@ -972,6 +1006,7 @@ function botSkillsPanel() {
972
1006
  { id: 'memory-tencentdb', title: 'TencentDB Agent Memory', desc: 'Bộ nhớ phân tầng L0–L3 + nén ngữ cảnh: nhớ tốt session dài, tiết kiệm ~61% token (local SQLite, không cần API)' },
973
1007
  { id: 'openclaw-browser-automation', title: 'openclaw-browser-automation', desc: 'Smart Search + Browser (headless & Chrome thật)' },
974
1008
  { id: 'openclaw-zalo-mod', title: 'openclaw-zalo-mod', desc: 'Zalo group helpers', channels: ['zalo-personal'], openWebPort: 18790, openWebPath: '/dashboard' },
1009
+ { id: 'openclaw-fb-messenger', title: 'openclaw-fb-messenger', desc: t('Kênh Facebook Messenger — webhook + Graph API (bắt buộc cho bot Messenger)', 'Facebook Messenger channel — webhook + Graph API (required for Messenger bots)'), channels: ['fb-messenger'] },
975
1010
  { id: 'openclaw-facebook-crawler', title: 'openclaw-facebook-crawler', desc: 'Facebook crawler automation', channels: ['fb-messenger'] },
976
1011
  { id: 'openclaw-n8n-facebook-poster', title: 'openclaw-n8n-facebook-poster', desc: 'Facebook post automation (n8n)', channels: ['fb-messenger'] },
977
1012
  ];
@@ -1003,6 +1038,9 @@ function botSkillsPanel() {
1003
1038
  if (requiresInstall) {
1004
1039
  secs.push(`<button class="secondary icon-btn2 update-plugin-btn" type="button" data-feature-install="${key}" ${loading ? 'disabled' : ''} title="${t('Cập nhật lên bản mới nhất','Update to latest version')}" style="padding: 4px 8px; font-size: 11px; height: 28px; border-width: 1px; color:#ffb020; border-color: rgba(255,176,32,0.25); background: rgba(255,176,32,0.05);">${actionIcon('refresh')}<span>${t('Cập nhật','Update')}</span></button>`);
1005
1040
  }
1041
+ if (item.id === 'browser-automation' || item.id === 'openclaw-browser-automation') {
1042
+ secs.push(`<button class="secondary icon-btn2" type="button" data-chrome-debug title="${t('Mở Chrome (chế độ debug) trên máy này để bot dùng trình duyệt thật (đã đăng nhập)','Open real Chrome in debug mode on this machine so the bot can use it')}" style="padding: 4px 8px; font-size: 11px; height: 28px; border-width: 1px; color:#4285F4; border-color: rgba(66,133,244,0.3); background: rgba(66,133,244,0.06);">${actionIcon('link')}<span>${t('Mở Chrome debug','Open Chrome debug')}</span></button>`);
1043
+ }
1006
1044
  } else {
1007
1045
  secs.push(`<button class="secondary icon-btn2" type="button" data-feature-install="${key}" ${loading ? 'disabled' : ''}>${actionIcon('download')} ${ui('installVerb')}</button>`);
1008
1046
  }
@@ -1371,13 +1409,9 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1371
1409
  });
1372
1410
  $('#bot-create')?.addEventListener('submit', async (ev) => {
1373
1411
  ev.preventDefault();
1374
- // Facebook Messenger needs the private fb-messenger plugin. If it isn't installed,
1375
- // show a contact modal instead of creating a broken bot.
1376
- if (state.botChannel === 'fb-messenger' && !state.botEditId && !state.featureInstalled?.['plugin:openclaw-fb-messenger']) {
1377
- state.fbPluginModalOpen = true;
1378
- render();
1379
- return;
1380
- }
1412
+ // Facebook Messenger needs the fb-messenger plugin (public on ClawHub). We don't block
1413
+ // creation the heads-up modal on channel-select tells the user to install it from the
1414
+ // bot's Plugins section afterwards (installable via the openclaw-fb-messenger card).
1381
1415
  const submitBtn = ev.currentTarget.querySelector('button[type="submit"]');
1382
1416
  if (submitBtn?.classList.contains('is-loading')) return;
1383
1417
  await withButtonLoading(submitBtn, async () => {
@@ -1428,7 +1462,7 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1428
1462
  }
1429
1463
  });
1430
1464
  });
1431
- document.querySelectorAll('.save').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => { const name = btn.dataset.file; await api('/api/bot/files/'+encodeURIComponent(name), { method: 'PUT', body: { projectDir: activeProjectDir(), content: document.querySelector(`[data-editor="${CSS.escape(name)}"]`).value } }); showToast(t('Đã lưu', 'Saved'), t('Đã lưu tệp tin: ', 'Saved file: ') + fileBaseName(name), 'success'); btn.innerHTML=`${actionIcon('save')} ${ui('saved')}`; setTimeout(()=>btn.innerHTML=`${actionIcon('save')} ${ui('save')}`,1200); }));
1465
+ document.querySelectorAll('.save').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => { const name = btn.dataset.file; try { await api('/api/bot/files/'+encodeURIComponent(name), { method: 'PUT', body: { projectDir: activeProjectDir(), content: document.querySelector(`[data-editor="${CSS.escape(name)}"]`).value } }); showToast(t('Đã lưu', 'Saved'), t('Đã lưu tệp tin: ', 'Saved file: ') + fileBaseName(name), 'success'); btn.innerHTML=`${actionIcon('save')} ${ui('saved')}`; setTimeout(()=>btn.innerHTML=`${actionIcon('save')} ${ui('save')}`,1200); } catch (err) { showToast(t('Lưu thất bại', 'Save failed'), err.message, 'error'); } }));
1432
1466
  wireSkillsHandlers(document);
1433
1467
  }
1434
1468
 
@@ -419,7 +419,6 @@ html[data-theme="light"] .bot-form-grid input,html[data-theme="light"] .bot-form
419
419
  .mini-pill{display:inline-flex;align-items:center;min-height:26px;padding:4px 10px;border-radius:999px;background:rgba(255,255,255,.04);border:1px solid rgba(255,36,54,.55);font-size:12px;font-weight:800;letter-spacing:.2px}
420
420
  .mini-pill--os{border-color:#64748b;color:#cbd5e1}
421
421
  .mini-pill--docker{border-color:#2496ed;color:#9bd5ff}
422
- .mini-pill--native{border-color:#7c5cff;color:#cfc4ff}
423
422
  .channel-tabs{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-bottom:18px}
424
423
  .channel-tabs button{display:inline-flex;align-items:center;gap:8px;border:1px solid rgba(255,36,54,.24);background:rgba(255,255,255,.045);color:var(--body);border-radius:999px;padding:9px 12px;font-weight:900;cursor:pointer}
425
424
  .channel-tabs button.active{background:rgba(255,36,54,.16);border-color:var(--primary);color:var(--ink);box-shadow:0 0 0 1px rgba(255,36,54,.18) inset}
@@ -1931,6 +1930,12 @@ body:has(.modal-backdrop) .bottom{display:none!important}
1931
1930
  /* Bot / Cài đặt hero buttons sit side-by-side — keep their labels on one line. */
1932
1931
  .dash-actions .icon-btn2{white-space:nowrap}
1933
1932
 
1933
+ /* [Confirm modal] Optional body block for step-by-step guides (e.g. Chrome-debug on a VPS):
1934
+ copyable command lines with wrap, compact spacing. */
1935
+ .confirm-body{display:grid;gap:8px;margin:14px 0 2px}
1936
+ .confirm-body .cmd{margin-top:0;user-select:all;word-break:break-all;white-space:pre-wrap;font-size:11.5px;line-height:1.5}
1937
+ .guide-step{margin:4px 0 0;color:var(--body);font-size:13px;font-weight:700}
1938
+
1934
1939
  @media(max-width:760px){
1935
1940
  /* [Topbar] Keep search + theme + language on ONE row; theme becomes icon-only and the
1936
1941
  language globe icon is dropped so the whole bar stays compact. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.10.1",
3
+ "version": "5.11.1",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {