create-openclaw-bot 5.8.3 → 5.8.5

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
@@ -476,9 +476,10 @@ function dashboardView() {
476
476
  const cat = state.catalog || {};
477
477
  const allSkills = cat.skills || [];
478
478
  const allPlugins = cat.plugins || [];
479
- const featureFlags = state.featureInstalled || {};
480
- const installedSkillsCount = allSkills.filter(sk => featureFlags[`skill:${sk.id}`]).length;
481
- const installedPluginsCount = allPlugins.filter(pl => featureFlags[`plugin:${pl.id}`]).length;
479
+ const featureFlags = state.featureFlags || {};
480
+ const featureInstalled = state.featureInstalled || {};
481
+ const installedSkillsCount = allSkills.filter(sk => !!featureFlags[`skill:${sk.slug || sk.id}`]).length;
482
+ const installedPluginsCount = allPlugins.filter(pl => !!featureInstalled[`plugin:${pl.package || pl.id}`]).length;
482
483
 
483
484
  const openclawVer = String((s.runtimeVersions?.openclaw || sys.versions?.currentOpenclaw || sys.versions?.openclaw || '-')).replace(/^openclaw@/, '').replace(/^create-openclaw-bot@/, '');
484
485
  const routerVer = String((s.runtimeVersions?.nineRouter || sys.versions?.currentNineRouter || sys.versions?.nineRouter || '-')).replace(/^9router@/, '');
@@ -494,10 +495,10 @@ function dashboardView() {
494
495
  const pluginsPercent = allPlugins.length ? Math.round((installedPluginsCount / allPlugins.length) * 100) : 0;
495
496
 
496
497
  const widgets = [
497
- { label: t('Project hi\u1ec7n t\u1ea1i','Current project'), value: escapeHtml(fileBaseName(s.projectDir || '-')), meta: `${projects.length} projects` },
498
+ { label: t('Project hiện tại','Current project'), value: escapeHtml(fileBaseName(s.projectDir || '-')), meta: `${projects.length} projects` },
498
499
  { label: t('Bots','Bots'), value: String(bots.length), meta: `${byChannel('telegram')} Telegram \u00b7 ${byChannel('zalo-personal')} Zalo` },
499
- { label: t('Provider (LLM)','Provider (LLM)'), value: '9Router', meta: t('\u0110ang s\u1eed d\u1ee5ng nhi\u1ec1u nh\u1ea5t','Most used provider') },
500
- { label: t('Model (AI)','Model (AI)'), value: 'gemini-1.5-flash', meta: t('\u0110ang s\u1eed d\u1ee5ng nhi\u1ec1u nh\u1ea5t','Most used model') }
500
+ { label: t('Provider (LLM)','Provider (LLM)'), value: s.activeProvider || '9Router', meta: t('Đang sử dụng nhiều nhất','Most used provider') },
501
+ { label: t('Model (AI)','Model (AI)'), value: s.activeModel || 'smart-route', meta: t('Đang sử dụng nhiều nhất','Most used model') }
501
502
  ];
502
503
 
503
504
  return `<div class="dash-shell">
@@ -626,30 +627,62 @@ function botView() {
626
627
  const channelBots = bots.filter(b => b.channel === ch);
627
628
  if (channelBots.length && !channelBots.some(b => b.id === state.activeBotId)) state.activeBotId = (channelBots.find(b => b.id !== 'bot') || channelBots[0]).id;
628
629
  if (!channelBots.length && state.activeBotId) { state.activeBotId = ''; state.selectedFile = ''; state.files = []; }
629
- return `<div class="bot-layout bot-layout--single">
630
- <section class="card bot-meta">
631
- <div class="card-head"><h3>${t('Project & m\u00f4i tr\u01b0\u1eddng','Project & runtime')}</h3></div>
632
- <div class="project-switcher">${(sys.projects||[]).length ? (sys.projects||[]).map(p => `<button class="project-chip ${s.projectDir===p.projectDir?'active':''}" data-project-connect="${escapeHtml(p.projectDir)}"><b>${escapeHtml(fileBaseName(p.projectDir))}</b><small>${escapeHtml(p.projectDir)}</small></button>`).join('') : ''}</div>
633
- <div class="bot-meta-grid">
634
- <div><span>${t('Project','Project')}</span><b>${escapeHtml(s.projectDir || '-')}</b></div>
635
- <div><span>${t('K\u00eanh','Channel')}</span><b>${escapeHtml(ch)}</b></div>
636
- <div><span>OpenClaw</span><b>${statusBadge(s.gatewayStatus || 'offline')} ${escapeHtml(String(s.runtimeVersions?.openclaw || sys.versions?.openclaw || '-').replace(/^openclaw@/, '').replace(/^create-openclaw-bot@/, ''))}</b></div>
637
- <div><span>9Router</span><b>${statusBadge(s.routerStatus || 'offline')} ${escapeHtml(String(s.runtimeVersions?.nineRouter || sys.versions?.nineRouter || '-').replace(/^9router@/, ''))}</b></div>
638
- </div>
639
- </section>
640
- <section class="card bot-main">
641
- <div class="card-head">
642
- <h3>${t('Bot','Bot')}</h3>
643
- <div style="display:flex;gap:8px;">
644
- ${(ch === 'zalo-personal' && channelBots.length > 0) ? `<button class="secondary btn-inline" data-zalo-login-trigger type="button">🔑 ${t('Đăng nhập Zalo','Zalo Login')}</button>` : ''}
645
- <button class="primary btn-inline" data-bot-modal="open" type="button">+ ${t('Tạo mới','New')}</button>
630
+
631
+ const openclawVer = String((s.runtimeVersions?.openclaw || sys.versions?.currentOpenclaw || sys.versions?.openclaw || '-')).replace(/^openclaw@/, '').replace(/^create-openclaw-bot@/, '');
632
+ const routerVer = String((s.runtimeVersions?.nineRouter || sys.versions?.currentNineRouter || sys.versions?.nineRouter || '-')).replace(/^9router@/, '');
633
+ const nodeVer = String((s.runtimeVersions?.node || sys.versions?.currentNode || sys.versions?.node || sys.node?.output || '-')).replace(/^v/, '');
634
+ const machineLabel = `${sys.os || '-'} \u00b7 ${sys.arch || '-'}`;
635
+
636
+ return `<div class="bot-layout">
637
+ <div class="bot-main-col" style="display: grid; gap: 18px;">
638
+ <section class="card bot-meta bot-main bot-projects-and-bots">
639
+ <div class="card-head" style="margin-bottom: 12px;">
640
+ <h3>${t('Project & bot','Project & bot')}</h3>
646
641
  </div>
647
- </div>
648
- <div class="channel-tabs">${BOT_CHANNELS.map(c => `<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>`).join('')}</div>
649
- ${botListPanel(channelBots)}
650
- </section>
651
- <section class="card bot-skills-panel"><div class="card-head"><h3>${ui('skills')} & ${ui('plugins')}</h3></div>${botSkillsPanel()}</section>
652
- <section class="card bot-files-panel">${channelBots.length ? botFilesPanel() : `<div class="bot-files-head"><div><h3>${t('C\u00e2y th\u01b0 m\u1ee5c bot','Bot file tree')}</h3>${projectPathLine()}</div></div><p>${t('Ch\u01b0a c\u00f3 bot trong k\u00eanh n\u00e0y. T\u1ea1o bot tr\u01b0\u1edbc \u0111\u1ec3 xem file workspace.','No bot in this channel. Create a bot first to view workspace files.')}</p>`}</section>
642
+ <div class="project-switcher">
643
+ ${(sys.projects||[]).length ? (sys.projects||[]).map(p => {
644
+ const active = s.projectDir===p.projectDir;
645
+ return `
646
+ <div class="project-tab-btn ${active?'active':''}" data-project-connect="${escapeHtml(p.projectDir)}">
647
+ <div class="project-tab-info">
648
+ <b>${escapeHtml(fileBaseName(p.projectDir))}</b>
649
+ <small>${escapeHtml(p.projectDir)}</small>
650
+ </div>
651
+ <button class="project-tab-delete" data-project-remove="${escapeHtml(p.projectDir)}" title="${t('Xóa project','Delete project')}">
652
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" stroke-linecap="round" stroke-linejoin="round"/></svg>
653
+ </button>
654
+ </div>
655
+ `;
656
+ }).join('') : ''}
657
+ </div>
658
+
659
+ <div class="bot-main-content" style="margin-top: 14px;">
660
+ <div class="card-head" style="margin-bottom: 14px; display: flex; justify-content: space-between; align-items: center;">
661
+ <h3 style="margin: 0; font-size: 16px;">${t('Danh sách bot','Bot list')}</h3>
662
+ ${(ch === 'zalo-personal' && channelBots.length > 0) ? `<button class="secondary btn-inline" data-zalo-login-trigger type="button" style="min-height: 36px; padding: 6px 12px; font-size: 13px;">🔑 ${t('Đăng nhập Zalo','Zalo Login')}</button>` : ''}
663
+ </div>
664
+ <div class="channel-tabs" style="margin-bottom: 16px;">${BOT_CHANNELS.map(c => `<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>`).join('')}</div>
665
+ ${botListPanel(channelBots)}
666
+ </div>
667
+ </section>
668
+ <section class="card bot-skills-panel"><div class="card-head"><h3>${ui('skills')} & ${ui('plugins')}</h3></div>${botSkillsPanel()}</section>
669
+ <section class="card bot-files-panel">${channelBots.length ? botFilesPanel() : `<div class="bot-files-head"><div><h3>${t('Cây thư mục bot','Bot file tree')}</h3>${projectPathLine()}</div></div><p>${t('Chưa có bot trong kênh này. Tạo bot trước để xem file workspace.','No bot in this channel. Create a bot first to view workspace files.')}</p>`}</section>
670
+ </div>
671
+
672
+ <div class="bot-side-col" style="display: grid; gap: 18px; position: sticky; top: 96px;">
673
+ <section class="card bot-status-card">
674
+ <div class="card-head"><h3>${t('Trạng thái','Status')}</h3></div>
675
+ <div class="runtime-status-grid" style="grid-template-columns: 1fr; margin-top: 14px;">
676
+ <div class="runtime-status-card"><div class="runtime-status-head"><span>OpenClaw</span>${statusBadge(s.gatewayStatus)}</div><div class="runtime-card-actions"><a class="runtime-open-btn secondary icon-btn2" href="${s.gatewayUrl||'http://127.0.0.1:18789'}" target="_blank" rel="noopener" style="justify-content:center; flex:1; font-size:12px; height:36px; border-width:1px;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px; height:14px;"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>${t('Mở web','Open')}</a><button class="runtime-open-btn icon-btn2" data-update-app type="button" style="justify-content:center; flex:1; font-size:12px; height:36px; border:none; background:rgba(255,36,54,.15); color:#ff4b5d;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px; height:14px;"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>${t('Update','Update')}</button></div></div>
677
+ <div class="runtime-status-card"><div class="runtime-status-head"><span>9Router</span>${statusBadge(s.routerStatus)}</div><div class="runtime-card-actions"><a class="runtime-open-btn secondary icon-btn2" href="${s.routerUrl||'http://127.0.0.1:20128'}" target="_blank" rel="noopener" style="justify-content:center; flex:1; font-size:12px; height:36px; border-width:1px;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px; height:14px;"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"></path><polyline points="15 3 21 3 21 9"></polyline><line x1="10" y1="14" x2="21" y2="3"></line></svg>${t('Mở web','Open')}</a><button class="runtime-open-btn icon-btn2" data-update-router type="button" style="justify-content:center; flex:1; font-size:12px; height:36px; border:none; background:rgba(255,36,54,.15); color:#ff4b5d;"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="width:14px; height:14px;"><polyline points="23 4 23 10 17 10"></polyline><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"></path></svg>${t('Update','Update')}</button></div></div>
678
+ </div>
679
+
680
+ <div class="dash-version-list" style="margin-top: 18px;">
681
+ <div><span>OpenClaw</span><b>${escapeHtml(openclawVer || '-')}</b></div>
682
+ <div><span>9Router</span><b>${escapeHtml(routerVer || '-')}</b></div>
683
+ </div>
684
+ </section>
685
+ </div>
653
686
  </div>`;
654
687
  }
655
688
 
@@ -664,6 +697,12 @@ function botCreateForm(ch, empty, data = {}) {
664
697
  : `<div class="choice-grid bot-channel-grid">${BOT_CHANNELS.map(o => choiceCard('bot-channel', o, ch)).join('')}</div>`}
665
698
  <div class="bot-form-grid">
666
699
  <label><span>${t('\u0054\u00ean bot','Bot name')}</span><input name="botName" required placeholder="Williams" value="${escapeHtml(data.botName || '')}"/></label>
700
+ ${data.mode !== 'edit' ? `
701
+ <label>
702
+ <span>${t('ID bot (viết liền, ko dấu)', 'Bot ID (no accent, lowercase)')}</span>
703
+ <input name="agentId" required pattern="^[a-z0-9-_]+$" title="${t('Chỉ dùng chữ cái viết thường không dấu, số, gạch ngang (-) và gạch dưới (_)', 'Only lowercase letters, numbers, hyphens (-) and underscores (_) are allowed')}" placeholder="william" value=""/>
704
+ </label>
705
+ ` : ''}
667
706
  <label><span>${t('\u0056ai tr\u00f2','Role')}</span><input name="role" required placeholder="${t('\u0054r\u1ee3 l\u00fd AI c\u00e1 nh\u00e2n','Personal AI assistant')}" value="${escapeHtml(data.role || '')}"/></label>
668
707
  <label><span>Emoji</span><input name="emoji" maxlength="8" placeholder="\uD83E\uDD16" value="${escapeHtml(data.emoji || '')}"/></label>
669
708
  ${needsToken ? `<label><span>Token</span><input name="token" ${tokenRequired ? 'required' : ''} autocomplete="off" placeholder="${ch==='telegram'?'123456:ABC...':'Zalo OA token'}" value="${escapeHtml(data.token || '')}"/></label>` : `<input name="token" type="hidden" value="${escapeHtml(data.token || '')}"/>`}
@@ -690,10 +729,17 @@ function botCreateModal() {
690
729
  }
691
730
 
692
731
  function botListPanel(bots) {
693
- return bots.length ? `<div class="bot-list">${bots.map(b => {
732
+ const listItems = bots.map(b => {
694
733
  const role = (b.role || b.desc || b.description || '').trim() || t('Tr\u1ee3 l\u00fd OpenClaw','OpenClaw assistant');
695
734
  return `<article class="bot-item ${state.activeBotId===b.id?'active':''}" data-bot-id="${escapeHtml(b.id)}"><div class="bot-item-actions"><button class="bot-edit" data-edit-bot="${escapeHtml(b.id)}" title="${t('Sửa bot','Edit bot')}" aria-label="${t('Sửa bot','Edit bot')}">${actionIcon('edit')}</button><button class="bot-delete" data-delete-bot="${escapeHtml(b.id)}" title="${t('X\u00f3a bot','Delete bot')}" aria-label="${t('X\u00f3a bot','Delete bot')}">&times;</button></div><b>${escapeHtml(b.name)}</b><small title="${escapeHtml(role)}">${escapeHtml(role)}</small></article>`;
696
- }).join('')}</div>` : `<div class="empty-create"><h3>${t('K\u00eanh n\u00e0y ch\u01b0a c\u00f3 bot','No bot in this channel')}</h3><button class="primary" data-bot-modal="open">+ ${t('T\u1ea1o bot','Create bot')}</button></div>`;
735
+ });
736
+ listItems.push(`
737
+ <article class="bot-item bot-create-card" data-bot-modal="open">
738
+ <span class="plus-icon">+</span>
739
+ <span>${t('T\u1ea1o m\u1edbi','New')}</span>
740
+ </article>
741
+ `);
742
+ return `<div class="bot-list">${listItems.join('')}</div>`;
697
743
  }
698
744
 
699
745
  function statusBadge(v) { return `<span class="runtime-badge ${v === 'online' ? 'ok' : v === 'unknown' ? 'warn' : 'bad'}">${v || 'offline'}</span>`; }
@@ -781,6 +827,8 @@ function botSkillsPanel() {
781
827
  const flags = state.featureFlags || {};
782
828
  const skills = [
783
829
  { id: 'cron', title: 'Cron', desc: 'Native scheduler (SQLite) — cron guide in TOOLS.md' },
830
+ { id: 'image-gen', title: 'Tạo ảnh Infographic', desc: 'Tạo ảnh infographic, poster tự động qua 9Router' },
831
+ { id: 'web-search', title: 'Web Search', desc: 'Tìm kiếm web thời gian thực (DuckDuckGo)' },
784
832
  ];
785
833
  const plugins = [
786
834
  { id: 'openclaw-browser-automation', title: 'openclaw-browser-automation', desc: 'Smart Search + Browser (headless & Chrome thật)' },
@@ -1194,6 +1242,29 @@ async function loadFiles(silent=false){
1194
1242
  async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
1195
1243
  async function loadFeatureFlags(silent=false){ const botId=currentBotId(); const data = (await api('/api/features' + (botId ? `?agentId=${encodeURIComponent(botId)}` : ''))) || {}; state.featureFlags = data.flags || {}; state.featureInstalled = data.installed || {}; state.featureVersions = data.versions || {}; if (!silent) render(); }
1196
1244
  function appendLogLine(line) {
1245
+ if (line.includes('Setup Wizard updated successfully! Please restart the installer.')) {
1246
+ showToast(t('Đang khởi động lại UI', 'Restarting Setup UI'), t('Hệ thống đang tự khởi động lại để áp dụng phiên bản mới...', 'The system is restarting to apply the new version...'), 'info', 12000);
1247
+ let attempts = 0;
1248
+ const interval = setInterval(async () => {
1249
+ attempts++;
1250
+ try {
1251
+ const res = await fetch('/api/system');
1252
+ if (res.ok) {
1253
+ clearInterval(interval);
1254
+ showToast(t('Cập nhật thành công', 'Update Success'), t('Setup Wizard đã được cập nhật lên phiên bản mới!', 'Setup Wizard has been updated to the new version!'), 'success');
1255
+ setTimeout(() => {
1256
+ window.location.reload();
1257
+ }, 1500);
1258
+ }
1259
+ } catch (e) {
1260
+ if (attempts > 30) {
1261
+ clearInterval(interval);
1262
+ showToast(t('Lỗi kết nối', 'Connection Error'), t('Không thể kết nối lại với Setup UI. Vui lòng tự chạy lại lệnh khởi động.', 'Cannot reconnect to Setup UI. Please run start command manually.'), 'error');
1263
+ }
1264
+ }
1265
+ }, 2000);
1266
+ }
1267
+
1197
1268
  const qrMatch = String(line).match(/^\[zalouser:qr\]\s+(data:image\/[a-zA-Z0-9.+-]+;base64,\S+)/);
1198
1269
  if (qrMatch) {
1199
1270
  state.zaloQrDataUrl = qrMatch[1];