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/README.md +23 -6
- package/README.vi.md +23 -6
- package/dist/server/local-server.js +208 -325
- package/dist/setup/shared/docker-gen.js +22 -12
- package/dist/setup/shared/workspace-gen.js +819 -154
- package/dist/web/app.js +50 -16
- package/dist/web/styles.css +6 -1
- package/package.json +1 -1
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('
|
|
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> và 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"
|
|
173
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
1375
|
-
//
|
|
1376
|
-
|
|
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
|
|
package/dist/web/styles.css
CHANGED
|
@@ -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. */
|