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/README.md +2 -2
- package/README.vi.md +2 -2
- package/dist/cli.js +11 -15
- package/dist/server/local-server.js +231 -135
- 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 -485
- package/dist/web/app.js +102 -31
- 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 -6941
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.
|
|
480
|
-
const
|
|
481
|
-
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;
|
|
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
|
|
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('
|
|
500
|
-
{ 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') }
|
|
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
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
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
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
-
|
|
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')}">×</button></div><b>${escapeHtml(b.name)}</b><small title="${escapeHtml(role)}">${escapeHtml(role)}</small></article>`;
|
|
696
|
-
})
|
|
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];
|