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 runtimeBadge(label, kind='') { return `<span class="mini-pill ${kind?`mini-pill--${kind}`:''}">${escapeHtml(label)}</span>`; }
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('Ch?a ch?n th? m?c','No folder selected'));
974
- state.projectConnectMessage = `OK ${t('?? k?t n?i','Connected')}: ${result.projectDir}`;
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.channel = state.botChannel || 'telegram';
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 => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
1215
- function fileBaseName(s='') { return String(s).split(/[\\/]/).pop() || s; }
1216
- async function loadSystem(silent=false){ state.system = await api('/api/system'); if (!silent) render(); }
1217
- async function loadStatus(silent=false){ state.install = await api('/api/bot/status'); if (!state.selectedProjectDir && state.install?.projectDir) state.selectedProjectDir = state.install.projectDir; if (!silent) render(); }
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' + (botId ? `?agentId=${encodeURIComponent(botId)}` : ''))).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' + (botId ? `?agentId=${encodeURIComponent(botId)}` : ''))) || {}; state.featureFlags = data.flags || {}; state.featureInstalled = data.installed || {}; state.featureVersions = data.versions || {}; if (!silent) render(); }
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);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.8.17",
3
+ "version": "5.8.19",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {