create-openclaw-bot 5.8.2 → 5.8.4
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/README.md +2 -2
- package/README.vi.md +2 -2
- package/dist/cli.js +11 -15
- package/dist/server/local-server.js +266 -188
- package/dist/setup/data/header.js +1 -1
- package/dist/setup/data/skills.js +2 -4
- package/dist/setup/shared/bot-config-gen.js +21 -16
- package/dist/setup/shared/workspace-gen.js +307 -475
- package/dist/web/app.js +107 -33
- package/dist/web/styles.css +477 -10
- package/node_modules/color-convert/CHANGELOG.md +54 -0
- package/package.json +1 -1
- package/dist/legacy-cli.js +0 -2812
- package/dist/setup.js +0 -6931
package/dist/web/app.js
CHANGED
|
@@ -311,7 +311,7 @@ function render() {
|
|
|
311
311
|
if (!mainContainer) {
|
|
312
312
|
$('#app').innerHTML = `
|
|
313
313
|
<aside class="sidebar">
|
|
314
|
-
<div class="brand"><img src="/openclaw-logo.svg" onerror="this.src='/openclaw-logo.png'" alt="OpenClaw"/><div style="display: flex; flex-direction: column; align-items: center; text-align: center;"><b>OpenClaw Setup</b><span style="display: block; width: 100%; text-align: center; font-size: 13.5px; font-weight: 600; margin-top: 6px; color: var(--muted);">v${state.system?.versions?.setup || '
|
|
314
|
+
<div class="brand"><img src="/openclaw-logo.svg" onerror="this.src='/openclaw-logo.png'" alt="OpenClaw"/><div style="display: flex; flex-direction: column; align-items: center; text-align: center;"><b>OpenClaw Setup</b><span id="sidebar-version" style="display: block; width: 100%; text-align: center; font-size: 13.5px; font-weight: 600; margin-top: 6px; color: var(--muted);">v${state.system?.versions?.setup || '...'}</span></div></div>
|
|
315
315
|
<nav class="sidebar-nav">${tabs.map(([id,label]) => `<button class="nav ${state.tab===id?'active':''}" data-tab="${id}">${icon(id)}<span>${label}</span></button>`).join('')}</nav>
|
|
316
316
|
${sidebarExtras()}
|
|
317
317
|
</aside>
|
|
@@ -346,6 +346,9 @@ function render() {
|
|
|
346
346
|
const titleEl = $('#app-page-title');
|
|
347
347
|
if (titleEl) titleEl.innerHTML = title();
|
|
348
348
|
|
|
349
|
+
const sidebarVerEl = $('#sidebar-version');
|
|
350
|
+
if (sidebarVerEl) sidebarVerEl.textContent = `v${state.system?.versions?.setup || '...'}`;
|
|
351
|
+
|
|
349
352
|
const panelEl = $('.panel');
|
|
350
353
|
if (panelEl) panelEl.innerHTML = content();
|
|
351
354
|
|
|
@@ -473,9 +476,10 @@ function dashboardView() {
|
|
|
473
476
|
const cat = state.catalog || {};
|
|
474
477
|
const allSkills = cat.skills || [];
|
|
475
478
|
const allPlugins = cat.plugins || [];
|
|
476
|
-
const featureFlags = state.
|
|
477
|
-
const
|
|
478
|
-
const
|
|
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;
|
|
479
483
|
|
|
480
484
|
const openclawVer = String((s.runtimeVersions?.openclaw || sys.versions?.currentOpenclaw || sys.versions?.openclaw || '-')).replace(/^openclaw@/, '').replace(/^create-openclaw-bot@/, '');
|
|
481
485
|
const routerVer = String((s.runtimeVersions?.nineRouter || sys.versions?.currentNineRouter || sys.versions?.nineRouter || '-')).replace(/^9router@/, '');
|
|
@@ -491,10 +495,10 @@ function dashboardView() {
|
|
|
491
495
|
const pluginsPercent = allPlugins.length ? Math.round((installedPluginsCount / allPlugins.length) * 100) : 0;
|
|
492
496
|
|
|
493
497
|
const widgets = [
|
|
494
|
-
{ label: t('Project
|
|
498
|
+
{ label: t('Project hiện tại','Current project'), value: escapeHtml(fileBaseName(s.projectDir || '-')), meta: `${projects.length} projects` },
|
|
495
499
|
{ label: t('Bots','Bots'), value: String(bots.length), meta: `${byChannel('telegram')} Telegram \u00b7 ${byChannel('zalo-personal')} Zalo` },
|
|
496
|
-
{ label: t('Provider (LLM)','Provider (LLM)'), value: '9Router', meta: t('
|
|
497
|
-
{ label: t('Model (AI)','Model (AI)'), value: '
|
|
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') }
|
|
498
502
|
];
|
|
499
503
|
|
|
500
504
|
return `<div class="dash-shell">
|
|
@@ -623,30 +627,62 @@ function botView() {
|
|
|
623
627
|
const channelBots = bots.filter(b => b.channel === ch);
|
|
624
628
|
if (channelBots.length && !channelBots.some(b => b.id === state.activeBotId)) state.activeBotId = (channelBots.find(b => b.id !== 'bot') || channelBots[0]).id;
|
|
625
629
|
if (!channelBots.length && state.activeBotId) { state.activeBotId = ''; state.selectedFile = ''; state.files = []; }
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
<section class="card bot-main">
|
|
638
|
-
<div class="card-head">
|
|
639
|
-
<h3>${t('Bot','Bot')}</h3>
|
|
640
|
-
<div style="display:flex;gap:8px;">
|
|
641
|
-
${(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>` : ''}
|
|
642
|
-
<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>
|
|
643
641
|
</div>
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
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>
|
|
650
686
|
</div>`;
|
|
651
687
|
}
|
|
652
688
|
|
|
@@ -661,6 +697,12 @@ function botCreateForm(ch, empty, data = {}) {
|
|
|
661
697
|
: `<div class="choice-grid bot-channel-grid">${BOT_CHANNELS.map(o => choiceCard('bot-channel', o, ch)).join('')}</div>`}
|
|
662
698
|
<div class="bot-form-grid">
|
|
663
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
|
+
` : ''}
|
|
664
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>
|
|
665
707
|
<label><span>Emoji</span><input name="emoji" maxlength="8" placeholder="\uD83E\uDD16" value="${escapeHtml(data.emoji || '')}"/></label>
|
|
666
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 || '')}"/>`}
|
|
@@ -687,10 +729,17 @@ function botCreateModal() {
|
|
|
687
729
|
}
|
|
688
730
|
|
|
689
731
|
function botListPanel(bots) {
|
|
690
|
-
|
|
732
|
+
const listItems = bots.map(b => {
|
|
691
733
|
const role = (b.role || b.desc || b.description || '').trim() || t('Tr\u1ee3 l\u00fd OpenClaw','OpenClaw assistant');
|
|
692
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')}">×</button></div><b>${escapeHtml(b.name)}</b><small title="${escapeHtml(role)}">${escapeHtml(role)}</small></article>`;
|
|
693
|
-
})
|
|
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>`;
|
|
694
743
|
}
|
|
695
744
|
|
|
696
745
|
function statusBadge(v) { return `<span class="runtime-badge ${v === 'online' ? 'ok' : v === 'unknown' ? 'warn' : 'bad'}">${v || 'offline'}</span>`; }
|
|
@@ -777,7 +826,9 @@ function filesView() { return `<div class="files">${state.files.map(f=>`<article
|
|
|
777
826
|
function botSkillsPanel() {
|
|
778
827
|
const flags = state.featureFlags || {};
|
|
779
828
|
const skills = [
|
|
780
|
-
{ id: 'cron', title: 'Cron', desc: '
|
|
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)' },
|
|
781
832
|
];
|
|
782
833
|
const plugins = [
|
|
783
834
|
{ id: 'openclaw-browser-automation', title: 'openclaw-browser-automation', desc: 'Smart Search + Browser (headless & Chrome thật)' },
|
|
@@ -1191,6 +1242,29 @@ async function loadFiles(silent=false){
|
|
|
1191
1242
|
async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
|
|
1192
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(); }
|
|
1193
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
|
+
|
|
1194
1268
|
const qrMatch = String(line).match(/^\[zalouser:qr\]\s+(data:image\/[a-zA-Z0-9.+-]+;base64,\S+)/);
|
|
1195
1269
|
if (qrMatch) {
|
|
1196
1270
|
state.zaloQrDataUrl = qrMatch[1];
|