create-openclaw-bot 5.11.0 → 5.12.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.
package/dist/web/app.js CHANGED
@@ -1,5 +1,5 @@
1
1
  const $ = (sel) => document.querySelector(sel);
2
- const state = { tab: 'dashboard', system: null, install: null, files: [], catalog: { skills: [], plugins: [] }, logs: [], zaloLoginOpen: false, zaloLoginLines: [], zaloQrDataUrl: '', lang: localStorage.getItem('openclaw-lang') || 'vi', theme: localStorage.getItem('openclaw-theme') || 'dark', navCollapsed: localStorage.getItem('openclaw-nav')==='1', os: null, mode: null, donateOpen: false, botModalOpen: false, botEditId: '', installModalOpen: false, fbPluginModalOpen: false, installTab: 'docker', installDraft: null, pathModal: null, confirmModal: null, botChannel: 'telegram', botPane: 'list', activeBotId: '', selectedFile: '', botMessage: '', projectConnectMessage: '', pendingProjectDir: '', selectedProjectDir: '', featureFlags: {}, featureInstalled: {}, featureLoading: {}, openDirs: {} };
2
+ const state = { tab: 'dashboard', system: null, install: null, files: [], catalog: { skills: [], plugins: [] }, logs: [], zaloLoginOpen: false, zaloLoginLines: [], zaloQrDataUrl: '', lang: localStorage.getItem('openclaw-lang') || 'vi', theme: localStorage.getItem('openclaw-theme') || 'dark', navCollapsed: localStorage.getItem('openclaw-nav')==='1', os: null, mode: null, donateOpen: false, botModalOpen: false, botEditId: '', installModalOpen: false, fbPluginModalOpen: false, installTab: 'docker', installDraft: null, pathModal: null, confirmModal: null, botChannel: 'telegram', botPane: 'list', activeBotId: '', selectedFile: '', botMessage: '', projectConnectMessage: '', pendingProjectDir: '', selectedProjectDir: '', featureFlags: {}, featureInstalled: {}, featureLoading: {}, zaloBackend: '', zaloHealth: null, openDirs: {} };
3
3
  const SVG_CDN = 'https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons';
4
4
  const OS_OPTIONS = [
5
5
  { id: 'win', title: 'Windows', subtitle: 'Auto-detected desktop', icon: `${SVG_CDN}/windows/default.svg`, badge: 'Desktop' },
@@ -13,7 +13,7 @@ const MODE_OPTIONS = [
13
13
  ];
14
14
  const BOT_CHANNELS = [
15
15
  { id: 'telegram', title: 'Telegram', subtitle: 'Bot API', icon: `${SVG_CDN}/telegram/default.svg`, badge: 'Tele' },
16
- { id: 'zalo-personal', title: 'Zalo User', subtitle: 'Personal account', icon: `${SVG_CDN}/zalo/default.svg`, badge: 'User' },
16
+ { id: 'zalo-personal', title: 'Zalo cá nhân', subtitle: 'OpenClaw Zalo Connect', icon: `${SVG_CDN}/zalo/default.svg`, badge: 'User' },
17
17
  { id: 'zalo-bot', title: 'Zalo API', subtitle: 'Official Account', icon: `${SVG_CDN}/zalo/default.svg`, badge: 'API' },
18
18
  { id: 'fb-messenger', title: 'Facebook', subtitle: 'Messenger', icon: `${SVG_CDN}/messenger/default.svg`, badge: 'FB' },
19
19
  { id: 'discord', title: 'Discord', subtitle: 'Bot', icon: `${SVG_CDN}/discord/default.svg`, badge: 'Discord', comingSoon: true },
@@ -194,7 +194,7 @@ function zaloLoginModal() {
194
194
  return `<div class="modal-backdrop zalo-login-backdrop" data-zalo-login="close">
195
195
  <section class="donate-modal zalo-login-modal" role="dialog" aria-modal="true" aria-label="Zalo login" onclick="event.stopPropagation()">
196
196
  <button class="modal-x" data-zalo-login="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>
197
- <div class="donate-head"><span aria-hidden="true">Z</span><div><p>Zalo User</p><h2>${t('Quét mã QR đăng nhập','Scan login QR')}</h2><small>${t('Mở Zalo trên điện thoại → quét QR trong khung dưới. Nếu QR chưa hiện, đợi vài giây.', 'Open Zalo on your phone → scan the QR below. If it is not visible yet, wait a few seconds.')}</small></div></div>
197
+ <div class="donate-head"><span aria-hidden="true">Z</span><div><p>${t('Zalo cá nhân','Zalo personal')}</p><h2>${t('Quét mã QR đăng nhập','Scan login QR')}</h2><small>${t('Mở Zalo trên điện thoại → quét QR trong khung dưới. Nếu QR chưa hiện, đợi vài giây.', 'Open Zalo on your phone → scan the QR below. If it is not visible yet, wait a few seconds.')}</small></div></div>
198
198
  ${state.zaloQrDataUrl ? `<div class="zalo-qr-image-wrap"><img class="zalo-qr-image" src="${state.zaloQrDataUrl}" alt="Zalo login QR"/></div>` : ''}
199
199
  <pre class="zalo-qr-log" data-zalo-qr-log>${escapeHtml(lines)}</pre>
200
200
  <div class="zalo-login-actions"><button class="secondary" data-zalo-login="close" type="button">${t('\u0110óng','Close')}</button></div>
@@ -806,6 +806,7 @@ function botView() {
806
806
  : `<button class="${ch===c.id?'active':''}" data-bot-channel="${c.id}"><img src="${c.icon}" onerror="this.style.display='none'"/>${c.title}<span>${bots.filter(b=>b.channel===c.id).length}</span></button>`
807
807
  ).join('')}</div>
808
808
  ${botListPanel(channelBots)}
809
+ ${ch === 'zalo-personal' ? zaloHealthCard(channelBots) : ''}
809
810
  </div>
810
811
  </section>
811
812
  <div class="bot-side-col">
@@ -999,7 +1000,6 @@ function botSkillsPanel() {
999
1000
  { id: 'cron', title: 'Cron', desc: 'Native scheduler (SQLite) — cron guide in TOOLS.md' },
1000
1001
  { id: 'image-gen', title: 'Tạo ảnh Infographic', desc: 'Tạo ảnh infographic, poster tự động qua 9Router' },
1001
1002
  { id: 'web-search', title: 'Web Search', desc: 'Tìm kiếm web thời gian thực (DuckDuckGo)' },
1002
- { id: 'sticker-mention', title: 'Sticker & Auto-Tag (Zalo)', desc: 'Tự động tag người gửi và gửi sticker Zalo theo từ khóa', channels: ['zalo-personal'] },
1003
1003
  { id: 'learning-memory', title: 'Siêu Trí Nhớ Dài Hạn (learning-memory)', desc: 'Tự động ghi nhớ bài học vào MEMORY.md, tự đóng gói và tiến hóa kỹ năng mới vào skills/' },
1004
1004
  ];
1005
1005
  const plugins = [
@@ -1015,12 +1015,14 @@ function botSkillsPanel() {
1015
1015
  // channels (e.g. Zalo helpers won't appear on Telegram/FB bots). Items without a
1016
1016
  // `channels` field are universal.
1017
1017
  const activeChannel = bot?.channel || state.botChannel || 'telegram';
1018
- const forChannel = (item) => !item.channels || item.channels.includes(activeChannel);
1018
+ const forChannel = (item) => {
1019
+ return !item.channels || item.channels.includes(activeChannel);
1020
+ };
1019
1021
  const scope = `${state.install?.projectDir || '-'} ? ${bot?.id || '-'}`;
1020
1022
  const row = (item, group) => {
1021
1023
  const key = `${group}:${item.id}`;
1022
1024
  const loading = !!state.featureLoading[key];
1023
- const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === 'sticker-mention' || item.id === 'learning-memory'));
1025
+ const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === 'learning-memory'));
1024
1026
  const isInstalled = !requiresInstall || !!state.featureInstalled?.[key];
1025
1027
 
1026
1028
  const version = requiresInstall && isInstalled ? (state.featureVersions?.[key] || '') : '';
@@ -1091,7 +1093,8 @@ function wireTab() {
1091
1093
  document.querySelectorAll('[data-fbplugin]').forEach(el => el.onclick = () => { state.fbPluginModalOpen = false; render(); });
1092
1094
  document.querySelectorAll('[data-bot-modal]').forEach(el => el.onclick = () => { state.botModalOpen = el.dataset.botModal === 'open'; if (el.dataset.botModal === 'open') state.botEditId = ''; state.botMessage = ''; render(); });
1093
1095
  document.querySelectorAll('[data-edit-bot]').forEach(btn => btn.onclick = (ev) => { ev.stopPropagation(); state.botEditId = btn.dataset.editBot; state.botChannel = (state.install?.bots || []).find((b) => b.id === state.botEditId)?.channel || state.botChannel; state.botModalOpen = true; state.botMessage = ''; render(); });
1094
- document.querySelectorAll('[data-zalo-login]').forEach(el => el.onclick = () => { state.zaloLoginOpen = el.dataset.zaloLogin === 'open'; render(); });
1096
+ document.querySelectorAll('[data-zalo-login]').forEach(el => el.onclick = () => { const opening = el.dataset.zaloLogin === 'open'; if (!opening && state.zaloLoginOpen) api('/api/zalo/login/cancel', { method: 'POST' }).catch(() => {}); state.zaloLoginOpen = opening; render(); });
1097
+ document.querySelectorAll('[data-zalo-health-refresh]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => { await loadZaloHealth(); }));
1095
1098
  document.querySelectorAll('[data-zalo-login-trigger]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
1096
1099
  state.zaloLoginOpen = true;
1097
1100
  state.zaloQrDataUrl = '';
@@ -1508,6 +1511,39 @@ async function loadFiles(silent=false){
1508
1511
  if (!silent) render();
1509
1512
  }
1510
1513
  async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
1514
+ function zaloHealthCard(channelBots = []) {
1515
+ if (state.zaloBackend !== 'zalo-connect' || !channelBots.length) return '';
1516
+ const h = state.zaloHealth;
1517
+ const chip = (label, tone) => `<span class="runtime-badge ${tone}" style="font-size:11px;">${label}</span>`;
1518
+ let statusChip = chip(t('Chưa rõ','Unknown'), 'warn');
1519
+ if (h) {
1520
+ if (h.channelStatus === 'connected') statusChip = chip(t('Đã kết nối','Connected'), 'ok');
1521
+ else if (h.channelStatus === 'container-stopped') statusChip = chip(t('Container tắt','Container stopped'), 'bad');
1522
+ else if (h.channelStatus === 'disconnected') statusChip = chip(t('Mất kết nối','Disconnected'), 'bad');
1523
+ else if (h.channelStatus === 'starting') statusChip = chip(t('Đang khởi động','Starting'), 'warn');
1524
+ }
1525
+ const ver = h?.installedVersion ? `${h.installedVersion}${h.supportedVersion && h.installedVersion !== h.supportedVersion ? ` → ${h.supportedVersion}` : ''}` : (h ? t('chưa cài','not installed') : '…');
1526
+ return `<div class="card zalo-health-card" style="margin-top:14px; padding:14px;">
1527
+ <div class="card-head" style="margin-bottom:10px; display:flex; justify-content:space-between; align-items:center;">
1528
+ <h3 style="margin:0; font-size:14px;">🩺 Zalo (ZaloConnect)</h3>${statusChip}
1529
+ </div>
1530
+ <div class="dash-version-list" style="margin-top:0;">
1531
+ <div><span>ZaloConnect</span><b>${escapeHtml(ver)}</b></div>
1532
+ <div><span>${t('Phiên QR','QR session')}</span><b>${h ? (h.sessionSaved ? t('Đã lưu','Saved') : t('Chưa đăng nhập','Not logged in')) : '…'}</b></div>
1533
+ <div><span>Zalo Mod</span><b>${h ? (h.zaloModInstalled ? t('Đã cài','Installed') : t('Chưa cài','Not installed')) : '…'}</b></div>
1534
+ ${h?.channelStatusLine ? `<div><span>${t('Kênh','Channel')}</span><b style="font-weight:500; font-size:11px; opacity:.8;">${escapeHtml(h.channelStatusLine.slice(0, 60))}</b></div>` : ''}
1535
+ </div>
1536
+ <div style="display:flex; gap:8px; margin-top:12px;">
1537
+ <button class="secondary icon-btn2" data-zalo-health-refresh type="button" style="flex:1; justify-content:center; font-size:12px; height:32px; border-width:1px;">🔄 ${t('Làm mới','Refresh')}</button>
1538
+ <button class="secondary icon-btn2" data-zalo-login-trigger type="button" style="flex:1; justify-content:center; font-size:12px; height:32px; border-width:1px;">🔑 ${t('Kết nối lại','Reconnect')}</button>
1539
+ </div>
1540
+ </div>`;
1541
+ }
1542
+ async function loadZaloHealth(silent=false){
1543
+ if (!activeProjectDir()) { state.zaloHealth = null; return; }
1544
+ try { state.zaloHealth = await api('/api/zalo/health' + projectQuery()); } catch (_) { state.zaloHealth = null; }
1545
+ if (!silent) render();
1546
+ }
1511
1547
  async function loadFeatureFlags(silent=false){
1512
1548
  const botId=currentBotId();
1513
1549
  if (!activeProjectDir()) { state.featureFlags = {}; state.featureInstalled = {}; state.featureVersions = {}; if (!silent) render(); return; }
@@ -1516,6 +1552,8 @@ async function loadFeatureFlags(silent=false){
1516
1552
  state.featureFlags = data.flags || {};
1517
1553
  state.featureInstalled = data.installed || {};
1518
1554
  state.featureVersions = data.versions || {};
1555
+ state.zaloBackend = data.zaloBackend || '';
1556
+ if (state.zaloBackend === 'zalo-connect') loadZaloHealth(true).catch(() => {});
1519
1557
  } catch (_) {
1520
1558
  state.featureFlags = {};
1521
1559
  state.featureInstalled = {};
@@ -1547,7 +1585,7 @@ function appendLogLine(line) {
1547
1585
  }, 2000);
1548
1586
  }
1549
1587
 
1550
- const qrMatch = String(line).match(/^\[zalouser:qr\]\s+(data:image\/[a-zA-Z0-9.+-]+;base64,\S+)/);
1588
+ const qrMatch = String(line).match(/^\[zalo-connect:qr\]\s+(data:image\/[a-zA-Z0-9.+-]+;base64,\S+)/);
1551
1589
  if (qrMatch) {
1552
1590
  state.zaloQrDataUrl = qrMatch[1];
1553
1591
  state.zaloLoginOpen = true;
@@ -1561,11 +1599,11 @@ function appendLogLine(line) {
1561
1599
  return;
1562
1600
  }
1563
1601
  const html = `<p>${escapeHtml(line)}</p>`;
1564
- if (state.zaloLoginOpen || /\[zalouser\]|zalo|qr|login|scan/i.test(line)) {
1602
+ if (state.zaloLoginOpen || /\[zalo-connect\]|zalo|qr|login|scan/i.test(line)) {
1565
1603
  state.zaloLoginLines.push(cleanTerminalLine(line));
1566
1604
  const qr = document.querySelector('[data-zalo-qr-log]');
1567
1605
  if (qr) { qr.textContent = state.zaloLoginLines.slice(-120).join('\n'); qr.scrollTop = qr.scrollHeight; }
1568
- if (/\[zalouser\].*scan.*qr/i.test(line) && state.zaloQrDataUrl) {
1606
+ if (/\[zalo-connect\].*scan.*qr/i.test(line) && state.zaloQrDataUrl) {
1569
1607
  render();
1570
1608
  requestAnimationFrame(() => document.querySelector('.zalo-login-modal')?.scrollTo({ top: 0, behavior: 'smooth' }));
1571
1609
  }
@@ -1629,6 +1667,3 @@ render();
1629
1667
  Promise.allSettled([loadSystem(true), loadStatus(true), loadCatalog(true)])
1630
1668
  .then(() => loadFeatureFlags(true).catch(() => {}))
1631
1669
  .finally(() => { render(); connectLogs(); });
1632
-
1633
-
1634
-
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.11.0",
3
+ "version": "5.12.0",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -13,7 +13,7 @@
13
13
  "build": "node docs_dev/tests/build.mjs --deploy",
14
14
  "dev": "node docs_dev/tests/build.mjs --deploy --watch",
15
15
  "start": "node dist/cli.js",
16
- "test": "node docs_dev/tests/smoke-cli-logic.mjs && node docs_dev/tests/test-generation.mjs && node docs_dev/tests/test-matrix.mjs && node docs_dev/tests/test-local-server.mjs",
16
+ "test": "node docs_dev/tests/smoke-cli-logic.mjs && node docs_dev/tests/test-generation.mjs && node docs_dev/tests/test-matrix.mjs && node docs_dev/tests/test-zalo-connect.mjs && node docs_dev/tests/test-local-server.mjs",
17
17
  "bump": "node docs_dev/tests/bump-version.mjs",
18
18
  "release": "node docs_dev/scripts/update.mjs"
19
19
  },