create-openclaw-bot 5.8.17 → 5.8.19
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
|
@@ -31,12 +31,25 @@ function staticChoiceCard(item) {
|
|
|
31
31
|
</div>`;
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
async function api(path, opts) {
|
|
35
|
-
const res = await fetch(path, opts && opts.body ? { ...opts, headers: { 'content-type': 'application/json' }, body: JSON.stringify(opts.body) } : opts);
|
|
36
|
-
if (!res.ok) throw new Error((await res.json()).error || res.statusText);
|
|
37
|
-
return res.json();
|
|
38
|
-
}
|
|
39
|
-
function
|
|
34
|
+
async function api(path, opts) {
|
|
35
|
+
const res = await fetch(path, opts && opts.body ? { ...opts, headers: { 'content-type': 'application/json' }, body: JSON.stringify(opts.body) } : opts);
|
|
36
|
+
if (!res.ok) throw new Error((await res.json()).error || res.statusText);
|
|
37
|
+
return res.json();
|
|
38
|
+
}
|
|
39
|
+
function activeProjectDir() {
|
|
40
|
+
return state.selectedProjectDir || state.install?.projectDir || '';
|
|
41
|
+
}
|
|
42
|
+
function projectQuery(extra = {}) {
|
|
43
|
+
const params = new URLSearchParams();
|
|
44
|
+
const projectDir = activeProjectDir();
|
|
45
|
+
if (projectDir) params.set('projectDir', projectDir);
|
|
46
|
+
Object.entries(extra).forEach(([key, value]) => {
|
|
47
|
+
if (value !== undefined && value !== null && value !== '') params.set(key, value);
|
|
48
|
+
});
|
|
49
|
+
const query = params.toString();
|
|
50
|
+
return query ? `?${query}` : '';
|
|
51
|
+
}
|
|
52
|
+
function runtimeBadge(label, kind='') { return `<span class="mini-pill ${kind?`mini-pill--${kind}`:''}">${escapeHtml(label)}</span>`; }
|
|
40
53
|
async function withButtonLoading(btn, task) {
|
|
41
54
|
if (!btn || btn.classList.contains('is-loading')) return;
|
|
42
55
|
const prevDisabled = btn.disabled;
|
|
@@ -268,7 +281,18 @@ async function pickFolderPathShared() {
|
|
|
268
281
|
const picked = await api('/api/project/pick-folder', { method: 'POST', body: {} });
|
|
269
282
|
const projectDir = String(picked.projectDir || '').trim();
|
|
270
283
|
if (projectDir) return { projectDir };
|
|
271
|
-
} catch {
|
|
284
|
+
} catch (err) {
|
|
285
|
+
return new Promise((resolve) => {
|
|
286
|
+
openPathModal({
|
|
287
|
+
title: t('Nhập đường dẫn project', 'Enter project path'),
|
|
288
|
+
message: t('Vui lòng nhập đường dẫn tuyệt đối đến thư mục project (ví dụ: /Users/mac/my-bot)', 'Please enter the absolute path to your project directory (e.g. /Users/mac/my-bot)'),
|
|
289
|
+
value: state.install?.projectDir || '',
|
|
290
|
+
placeholder: state.os === 'macos' ? '/Users/your-name/my-bot' : '/home/your-name/my-bot',
|
|
291
|
+
onConfirm: (val) => resolve({ projectDir: val }),
|
|
292
|
+
onCancel: () => resolve(null)
|
|
293
|
+
});
|
|
294
|
+
});
|
|
295
|
+
}
|
|
272
296
|
return null;
|
|
273
297
|
}
|
|
274
298
|
|
|
@@ -409,7 +433,7 @@ function renderFilesPanel() {
|
|
|
409
433
|
// Re-wire only file-panel-specific handlers
|
|
410
434
|
panel.querySelectorAll('[data-select-file]').forEach(btn => btn.onclick = () => { state.selectedFile = btn.dataset.selectFile; renderFilesPanel(); });
|
|
411
435
|
panel.querySelectorAll('[data-toggle-dir]').forEach(btn => btn.onclick = () => { const p = btn.dataset.toggleDir; state.openDirs[p] = !(state.openDirs[p] ?? true); renderFilesPanel(); });
|
|
412
|
-
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: { 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); }));
|
|
436
|
+
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); }));
|
|
413
437
|
}
|
|
414
438
|
|
|
415
439
|
function renderSkillsPanel() {
|
|
@@ -428,7 +452,7 @@ function wireSkillsHandlers(scope = document) {
|
|
|
428
452
|
state.featureLoading[key] = true;
|
|
429
453
|
renderSkillsPanel();
|
|
430
454
|
try {
|
|
431
|
-
await api('/api/features/toggle', { method:'POST', body:{ kind, id, enabled, agentId: currentBotId() } });
|
|
455
|
+
await api('/api/features/toggle', { method:'POST', body:{ projectDir: activeProjectDir(), kind, id, enabled, agentId: currentBotId() } });
|
|
432
456
|
await loadFeatureFlags(true);
|
|
433
457
|
if (kind === 'skill') await loadFiles(true);
|
|
434
458
|
} finally {
|
|
@@ -442,7 +466,7 @@ function wireSkillsHandlers(scope = document) {
|
|
|
442
466
|
state.featureLoading[key] = true;
|
|
443
467
|
renderSkillsPanel();
|
|
444
468
|
try {
|
|
445
|
-
await api('/api/features/install', { method:'POST', body:{ kind, id, agentId: currentBotId() } });
|
|
469
|
+
await api('/api/features/install', { method:'POST', body:{ projectDir: activeProjectDir(), kind, id, agentId: currentBotId() } });
|
|
446
470
|
await loadFeatureFlags(true);
|
|
447
471
|
await loadFiles(true);
|
|
448
472
|
const label = kind === 'skill' ? t('kỹ năng: ', 'skill: ') : t('plugin: ', 'plugin: ');
|
|
@@ -898,7 +922,7 @@ function wireTab() {
|
|
|
898
922
|
state.zaloLoginLines = [t('Đang chuẩn bị quét mã Zalo...', 'Preparing Zalo QR login...')];
|
|
899
923
|
render();
|
|
900
924
|
try {
|
|
901
|
-
await api('/api/zalo/login', { method: 'POST', body: { agentId: state.activeBotId } });
|
|
925
|
+
await api('/api/zalo/login', { method: 'POST', body: { projectDir: activeProjectDir(), agentId: state.activeBotId } });
|
|
902
926
|
} catch (err) {
|
|
903
927
|
state.zaloLoginOpen = false;
|
|
904
928
|
state.confirmModal = { title: t('Lỗi đăng nhập','Login error'), message: err.message, okText: t('Đóng','Close'), onConfirm: () => {} };
|
|
@@ -941,6 +965,7 @@ function wireTab() {
|
|
|
941
965
|
state.activeBotId = '';
|
|
942
966
|
state.selectedFile = '';
|
|
943
967
|
state.files = [];
|
|
968
|
+
await loadSystem(true);
|
|
944
969
|
await loadStatus(true);
|
|
945
970
|
// Auto-switch to the first channel that has bots in the new project
|
|
946
971
|
autoSwitchBotChannel();
|
|
@@ -970,12 +995,18 @@ function wireTab() {
|
|
|
970
995
|
}));
|
|
971
996
|
document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
|
|
972
997
|
const result = await pickFolderPathShared();
|
|
973
|
-
if (!result) throw new Error(t('
|
|
974
|
-
|
|
998
|
+
if (!result) throw new Error(t('Chưa chọn thư mục','No folder selected'));
|
|
999
|
+
await api('/api/project/connect', { method: 'POST', body: { projectDir: result.projectDir } });
|
|
1000
|
+
state.projectConnectMessage = `OK ${t('Đã kết nối','Connected')}: ${result.projectDir}`;
|
|
975
1001
|
showToast(t('Đã kết nối', 'Connected'), t('Kết nối thành công project: ', 'Successfully connected project: ') + fileBaseName(result.projectDir), 'success');
|
|
976
1002
|
state.selectedProjectDir = result.projectDir;
|
|
977
1003
|
state.pendingProjectDir = '';
|
|
1004
|
+
state.activeBotId = '';
|
|
1005
|
+
state.selectedFile = '';
|
|
1006
|
+
state.files = [];
|
|
1007
|
+
await loadSystem(true);
|
|
978
1008
|
await loadStatus(true);
|
|
1009
|
+
autoSwitchBotChannel();
|
|
979
1010
|
await loadFiles(true);
|
|
980
1011
|
await loadFeatureFlags(true);
|
|
981
1012
|
state.tab = 'bot';
|
|
@@ -998,12 +1029,12 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
998
1029
|
}
|
|
999
1030
|
}));
|
|
1000
1031
|
document.querySelectorAll('[data-update-app]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
|
|
1001
|
-
await api('/api/runtime/update', { method: 'POST', body: { target: 'openclaw' } });
|
|
1032
|
+
await api('/api/runtime/update', { method: 'POST', body: { projectDir: activeProjectDir(), target: 'openclaw' } });
|
|
1002
1033
|
await loadSystem();
|
|
1003
1034
|
await loadStatus();
|
|
1004
1035
|
}));
|
|
1005
1036
|
document.querySelectorAll('[data-update-router]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
|
|
1006
|
-
await api('/api/runtime/update', { method: 'POST', body: { target: '9router' } });
|
|
1037
|
+
await api('/api/runtime/update', { method: 'POST', body: { projectDir: activeProjectDir(), target: '9router' } });
|
|
1007
1038
|
await loadSystem();
|
|
1008
1039
|
await loadStatus();
|
|
1009
1040
|
}));document.querySelectorAll('[data-project-remove]').forEach(btn => btn.onclick = (ev) => {
|
|
@@ -1052,7 +1083,7 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1052
1083
|
okText: t('Xóa bot','Delete bot'),
|
|
1053
1084
|
onConfirm: async () => {
|
|
1054
1085
|
try {
|
|
1055
|
-
await api('/api/bot/'+encodeURIComponent(id), { method: 'DELETE' });
|
|
1086
|
+
await api('/api/bot/'+encodeURIComponent(id) + projectQuery(), { method: 'DELETE' });
|
|
1056
1087
|
state.confirmModal = null;
|
|
1057
1088
|
showToast(t('Đã xóa bot', 'Bot deleted'), `${t('Đã xóa bot','Bot deleted')}: ${id}`, 'success');
|
|
1058
1089
|
if (state.activeBotId === id) { state.activeBotId = ''; state.selectedFile = ''; state.files = []; }
|
|
@@ -1088,7 +1119,7 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1088
1119
|
const submitBtn = ev.currentTarget.querySelector('button[type="submit"]');
|
|
1089
1120
|
withButtonLoading(submitBtn, async () => {
|
|
1090
1121
|
const nineRouterApiKey = ev.currentTarget.querySelector('[name="nineRouterApiKey"]')?.value || '';
|
|
1091
|
-
await api('/api/bot/credentials', { method: 'PUT', body: { nineRouterApiKey } });
|
|
1122
|
+
await api('/api/bot/credentials', { method: 'PUT', body: { projectDir: activeProjectDir(), nineRouterApiKey } });
|
|
1092
1123
|
const msg = ev.currentTarget.querySelector('[data-cred-msg]');
|
|
1093
1124
|
if (msg) msg.textContent = t('Đã lưu','Saved');
|
|
1094
1125
|
await loadStatus();
|
|
@@ -1152,6 +1183,8 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1152
1183
|
if (action === 'ok') {
|
|
1153
1184
|
const value = document.getElementById('path-modal-input')?.value?.trim() || '';
|
|
1154
1185
|
if (value && typeof modal.onConfirm === 'function') modal.onConfirm(value);
|
|
1186
|
+
} else {
|
|
1187
|
+
if (typeof modal.onCancel === 'function') modal.onCancel();
|
|
1155
1188
|
}
|
|
1156
1189
|
state.pathModal = null;
|
|
1157
1190
|
render();
|
|
@@ -1161,9 +1194,10 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1161
1194
|
const submitBtn = ev.currentTarget.querySelector('button[type="submit"]');
|
|
1162
1195
|
if (submitBtn?.classList.contains('is-loading')) return;
|
|
1163
1196
|
await withButtonLoading(submitBtn, async () => {
|
|
1164
|
-
const fd = new FormData(ev.currentTarget);
|
|
1165
|
-
const body = Object.fromEntries(fd.entries());
|
|
1166
|
-
body.
|
|
1197
|
+
const fd = new FormData(ev.currentTarget);
|
|
1198
|
+
const body = Object.fromEntries(fd.entries());
|
|
1199
|
+
body.projectDir = activeProjectDir();
|
|
1200
|
+
body.channel = state.botChannel || 'telegram';
|
|
1167
1201
|
if (body.channel === 'zalo-personal') {
|
|
1168
1202
|
state.botModalOpen = false;
|
|
1169
1203
|
state.zaloLoginOpen = true;
|
|
@@ -1207,14 +1241,21 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1207
1241
|
}
|
|
1208
1242
|
});
|
|
1209
1243
|
});
|
|
1210
|
-
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: { 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); }));
|
|
1244
|
+
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); }));
|
|
1211
1245
|
wireSkillsHandlers(document);
|
|
1212
1246
|
}
|
|
1213
1247
|
|
|
1214
1248
|
function escapeHtml(s='') { return String(s).replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[c])); }
|
|
1215
|
-
function fileBaseName(s='') {
|
|
1216
|
-
async function loadSystem(silent=false){ state.system = await api('/api/system'); if (!silent) render(); }
|
|
1217
|
-
async function loadStatus(silent=false){
|
|
1249
|
+
function fileBaseName(s='') { const clean = String(s).replace(/[\\/]+$/, ''); return clean.split(/[\\/]/).pop() || s; }
|
|
1250
|
+
async function loadSystem(silent=false){ state.system = await api('/api/system'); if (!silent) render(); }
|
|
1251
|
+
async function loadStatus(silent=false){
|
|
1252
|
+
const requestedProjectDir = activeProjectDir();
|
|
1253
|
+
const install = await api('/api/bot/status' + projectQuery());
|
|
1254
|
+
if (requestedProjectDir && install?.projectDir && install.projectDir !== requestedProjectDir) return;
|
|
1255
|
+
state.install = install;
|
|
1256
|
+
if (!state.selectedProjectDir && state.install?.projectDir) state.selectedProjectDir = state.install.projectDir;
|
|
1257
|
+
if (!silent) render();
|
|
1258
|
+
}
|
|
1218
1259
|
function autoSwitchBotChannel() {
|
|
1219
1260
|
const bots = state.install?.bots || [];
|
|
1220
1261
|
const ch = state.botChannel || 'telegram';
|
|
@@ -1235,14 +1276,14 @@ function currentBotId() {
|
|
|
1235
1276
|
}
|
|
1236
1277
|
return state.activeBotId || '';
|
|
1237
1278
|
}
|
|
1238
|
-
async function loadFiles(silent=false){
|
|
1239
|
-
const botId = currentBotId();
|
|
1240
|
-
if (!botId) { state.files = []; if (!silent) render(); return; }
|
|
1241
|
-
state.files = (await api('/api/bot/files' + (
|
|
1242
|
-
if (!silent) render();
|
|
1243
|
-
}
|
|
1244
|
-
async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
|
|
1245
|
-
async function loadFeatureFlags(silent=false){ const botId=currentBotId(); const data = (await api('/api/features' + (
|
|
1279
|
+
async function loadFiles(silent=false){
|
|
1280
|
+
const botId = currentBotId();
|
|
1281
|
+
if (!botId) { state.files = []; if (!silent) render(); return; }
|
|
1282
|
+
state.files = (await api('/api/bot/files' + projectQuery({ agentId: botId }))).files;
|
|
1283
|
+
if (!silent) render();
|
|
1284
|
+
}
|
|
1285
|
+
async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
|
|
1286
|
+
async function loadFeatureFlags(silent=false){ const botId=currentBotId(); const data = (await api('/api/features' + projectQuery({ agentId: botId }))) || {}; state.featureFlags = data.flags || {}; state.featureInstalled = data.installed || {}; state.featureVersions = data.versions || {}; if (!silent) render(); }
|
|
1246
1287
|
function appendLogLine(line) {
|
|
1247
1288
|
if (line.includes('Setup Wizard updated successfully! Please restart the installer.')) {
|
|
1248
1289
|
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);
|