create-openclaw-bot 5.8.21 → 5.8.24

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.
@@ -91,11 +91,11 @@
91
91
  }));
92
92
 
93
93
  const cfg = {
94
- meta: {
95
- lastTouchedVersion: (_common.OPENCLAW_NPM_SPEC || 'latest').replace('openclaw@', ''),
96
- osChoice,
97
- deployMode,
98
- },
94
+ meta: {
95
+ lastTouchedVersion: (_common.OPENCLAW_NPM_SPEC || 'latest').replace('openclaw@', ''),
96
+ osChoice,
97
+ deployMode,
98
+ },
99
99
  agents: {
100
100
  defaults: {
101
101
  model: { primary: model, fallbacks: [] },
@@ -165,8 +165,31 @@
165
165
 
166
166
  // ── tools ────────────────────────────────────────────────────────────────
167
167
  cfg.tools = { profile: 'full', exec: { host: 'gateway', security: 'full', ask: 'off' } };
168
- if (selectedSkills.includes('scheduler')) {
169
- cfg.tools.alsoAllow = ['group:automation'];
168
+ const alsoAllow = [];
169
+ if (selectedSkills.includes('scheduler') || selectedSkills.includes('cron')) {
170
+ alsoAllow.push('group:automation');
171
+ }
172
+ if (
173
+ selectedSkills.includes('web-search') ||
174
+ selectedSkills.includes('web_search') ||
175
+ selectedSkills.includes('browser') ||
176
+ selectedSkills.includes('browser-automation') ||
177
+ hasBrowserDesktop ||
178
+ hasBrowserServer
179
+ ) {
180
+ alsoAllow.push('group:web');
181
+ }
182
+ if (alsoAllow.length > 0) {
183
+ cfg.tools.alsoAllow = alsoAllow;
184
+ }
185
+ // DuckDuckGo is the bundled, credential-free web_search provider. Auto-detect only
186
+ // picks providers that have credentials, so a free provider must be selected
187
+ // explicitly, otherwise web_search reports "no provider is available".
188
+ if (selectedSkills.includes('web-search') || selectedSkills.includes('web_search')) {
189
+ cfg.tools.web = {
190
+ ...(cfg.tools.web || {}),
191
+ search: { ...((cfg.tools.web && cfg.tools.web.search) || {}), provider: 'duckduckgo' },
192
+ };
170
193
  }
171
194
  if (isMultiBot) {
172
195
  cfg.tools.agentToAgent = {
@@ -203,6 +226,8 @@
203
226
  selectedSkills,
204
227
  botName: agentMetas[0]?.name || 'Bot',
205
228
  agentId: agentMetas[0]?.agentId || 'bot',
229
+ hasBrowser: hasBrowserDesktop || hasBrowserServer
230
+ || selectedSkills.includes('browser') || selectedSkills.includes('browser-automation'),
206
231
  });
207
232
  cfg.plugins = pluginsConfig.plugins;
208
233
 
@@ -298,7 +323,7 @@
298
323
  // buildPluginsConfig — returns { plugins: { ... } }
299
324
  // ═══════════════════════════════════════════════════════════════════════════════
300
325
  function buildPluginsConfig(opts) {
301
- const { channelKey, selectedSkills = [], botName = 'Bot', agentId = 'bot' } = opts;
326
+ const { channelKey, selectedSkills = [], botName = 'Bot', agentId = 'bot', hasBrowser = false } = opts;
302
327
 
303
328
  const entries = {};
304
329
 
@@ -326,8 +351,16 @@
326
351
  }
327
352
  }
328
353
 
354
+ // Browser automation depends on the bundled `browser` plugin, which provides the
355
+ // browser-control service. With an allowlist in use it must be explicitly allowed,
356
+ // otherwise browser control stays disabled ("browser control is disabled").
357
+ if (hasBrowser && !allow.includes('browser')) {
358
+ allow.push('browser');
359
+ }
360
+
329
361
  const plugins = { entries };
330
362
  plugins.allow = allow;
363
+ if (allow.length) plugins.bundledDiscovery = 'compat';
331
364
 
332
365
  return { plugins };
333
366
  }
@@ -185,7 +185,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
185
185
  const port = routerPort || 20128;
186
186
  const nineRouterSpec = (typeof globalThis !== 'undefined' && globalThis.__openclawCommon && globalThis.__openclawCommon.NINE_ROUTER_NPM_SPEC) || '9router@latest';
187
187
  return [
188
- `npm install -g ` + nineRouterSpec + ` better-sqlite3`,
188
+ `npm install -g ` + nineRouterSpec,
189
189
  'node /tmp/patch-9router.js || true',
190
190
  'node -e "const fs=require(\'fs\'),path=require(\'path\'); const DB_PATH=\'/root/.9router/db/data.sqlite\'; const dir=path.dirname(DB_PATH); if(!fs.existsSync(dir))fs.mkdirSync(dir,{recursive:true}); try{ const {DatabaseSync}=require(\'node:sqlite\'); const db=new DatabaseSync(DB_PATH); db.prepare(\'CREATE TABLE IF NOT EXISTS settings (id INTEGER PRIMARY KEY CHECK (id = 1), data TEXT NOT NULL)\').run(); const existing=db.prepare(\'SELECT * FROM settings WHERE id = 1\').get(); if(!existing){ db.prepare(\'INSERT INTO settings (id, data) VALUES (1, ?)\').run(JSON.stringify({requireLogin:false})); } db.close(); }catch(e){}" || true',
191
191
  'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
@@ -221,12 +221,12 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
221
221
  singleOllamaContainerName = 'ollama',
222
222
  multiOllamaContainerName = 'ollama-multibot',
223
223
  plainSingleExtraHosts = false,
224
- multiOllamaNumParallel = 1,
225
- singleOllamaNumParallel = 1,
226
- gatewayPort = 18789,
227
- routerPort = 20128,
228
- osChoice = '',
229
- } = options;
224
+ multiOllamaNumParallel = 1,
225
+ singleOllamaNumParallel = 1,
226
+ gatewayPort = 18789,
227
+ routerPort = 20128,
228
+ osChoice = '',
229
+ } = options;
230
230
  const skillLines = dockerfileSkillInstallMode === 'build' && allSkills.length > 0
231
231
  ? `\n# Install skills (ClawHub)\n${allSkills.map((skill) => `RUN openclaw skills install ${skill} || echo "Warning: Failed to install ${skill} due to rate limits."`).join('\n')}\n`
232
232
  : '';
@@ -356,7 +356,7 @@ CMD ["/bin/sh", "/usr/local/bin/openclaw-entrypoint.sh"]`;
356
356
  const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(routerPort);
357
357
  const extraHostsBlock = ` extra_hosts:\n - "host.docker.internal:host-gateway"`;
358
358
 
359
- const appEnvironmentBlock = ` environment:\n - HOME=/home/node/project/.openclaw\n - OPENCLAW_HOME=/home/node/project/.openclaw\n - OPENCLAW_STATE_DIR=/home/node/project/.openclaw\n - OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1\n - OPENCLAW_SETUP_OS=${osChoice || ''}\n - OPENCLAW_BROWSER_HOST_OS=${osChoice || ''}\n - OPENCLAW_GATEWAY_PORT=${gatewayPort}\n - OPENCLAW_PORT=${gatewayPort}\n tmpfs:\n - /home/node/project/.openclaw/plugin-runtime-deps\n`;
359
+ const appEnvironmentBlock = ` environment:\n - HOME=/home/node/project/.openclaw\n - OPENCLAW_HOME=/home/node/project/.openclaw\n - OPENCLAW_STATE_DIR=/home/node/project/.openclaw\n - OPENCLAW_ALLOW_INSECURE_PRIVATE_WS=1\n - OPENCLAW_SETUP_OS=${osChoice || ''}\n - OPENCLAW_BROWSER_HOST_OS=${osChoice || ''}\n - OPENCLAW_GATEWAY_PORT=${gatewayPort}\n - OPENCLAW_PORT=${gatewayPort}\n tmpfs:\n - /home/node/project/.openclaw/plugin-runtime-deps\n`;
360
360
 
361
361
  let compose;
362
362
  if (isMultiBot) {
@@ -471,6 +471,7 @@ services:
471
471
  ${appEnvironmentBlock}${extraHostsBlock}\n volumes:
472
472
  - ${volumeMount}
473
473
  - openclaw-plugins:/home/node/project/.openclaw/npm
474
+ - openclaw-extensions:/home/node/project/.openclaw/extensions
474
475
  ports:
475
476
  - "${gatewayPort}:${gatewayPort}"
476
477
 
@@ -496,7 +497,8 @@ ${indentBlock(docker9RouterEntrypointScript, 8)}
496
497
 
497
498
  volumes:
498
499
  9router-data:
499
- openclaw-plugins:`;
500
+ openclaw-plugins:
501
+ openclaw-extensions:`;
500
502
  } else if (isLocal) {
501
503
  const ollamaModelTag = String(selectedModel || 'ollama/gemma4:e2b').replace('ollama/', '');
502
504
  compose = `name: ${singleComposeName}
package/dist/web/app.js CHANGED
@@ -31,25 +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 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>`; }
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>`; }
53
53
  async function withButtonLoading(btn, task) {
54
54
  if (!btn || btn.classList.contains('is-loading')) return;
55
55
  const prevDisabled = btn.disabled;
@@ -433,7 +433,7 @@ function renderFilesPanel() {
433
433
  // Re-wire only file-panel-specific handlers
434
434
  panel.querySelectorAll('[data-select-file]').forEach(btn => btn.onclick = () => { state.selectedFile = btn.dataset.selectFile; renderFilesPanel(); });
435
435
  panel.querySelectorAll('[data-toggle-dir]').forEach(btn => btn.onclick = () => { const p = btn.dataset.toggleDir; state.openDirs[p] = !(state.openDirs[p] ?? true); renderFilesPanel(); });
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); }));
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); }));
437
437
  }
438
438
 
439
439
  function renderSkillsPanel() {
@@ -452,7 +452,7 @@ function wireSkillsHandlers(scope = document) {
452
452
  state.featureLoading[key] = true;
453
453
  renderSkillsPanel();
454
454
  try {
455
- await api('/api/features/toggle', { method:'POST', body:{ projectDir: activeProjectDir(), kind, id, enabled, agentId: currentBotId() } });
455
+ await api('/api/features/toggle', { method:'POST', body:{ projectDir: activeProjectDir(), kind, id, enabled, agentId: currentBotId() } });
456
456
  await loadFeatureFlags(true);
457
457
  if (kind === 'skill') await loadFiles(true);
458
458
  } finally {
@@ -466,7 +466,7 @@ function wireSkillsHandlers(scope = document) {
466
466
  state.featureLoading[key] = true;
467
467
  renderSkillsPanel();
468
468
  try {
469
- await api('/api/features/install', { method:'POST', body:{ projectDir: activeProjectDir(), kind, id, agentId: currentBotId() } });
469
+ await api('/api/features/install', { method:'POST', body:{ projectDir: activeProjectDir(), kind, id, agentId: currentBotId() } });
470
470
  await loadFeatureFlags(true);
471
471
  await loadFiles(true);
472
472
  const label = kind === 'skill' ? t('kỹ năng: ', 'skill: ') : t('plugin: ', 'plugin: ');
@@ -855,6 +855,7 @@ function botSkillsPanel() {
855
855
  { id: 'image-gen', title: 'Tạo ảnh Infographic', desc: 'Tạo ảnh infographic, poster tự động qua 9Router' },
856
856
  { id: 'web-search', title: 'Web Search', desc: 'Tìm kiếm web thời gian thực (DuckDuckGo)' },
857
857
  { id: 'sticker-mention', title: 'Sticker & Auto-Tag (Zalo)', desc: 'Tự động tag người gửi và gửi sticker Zalo theo từ khóa' },
858
+ { id: 'learning-memory', title: 'Siêu Trí Nhớ Dài Hạn (learning-memory)', desc: 'Tự động ghi nhớ bài học vào MEMORY.md, tự đóng gói và tiến hóa kỹ năng mới vào skills/' },
858
859
  ];
859
860
  const plugins = [
860
861
  { id: 'openclaw-browser-automation', title: 'openclaw-browser-automation', desc: 'Smart Search + Browser (headless & Chrome thật)' },
@@ -867,7 +868,7 @@ function botSkillsPanel() {
867
868
  const row = (item, group) => {
868
869
  const key = `${group}:${item.id}`;
869
870
  const loading = !!state.featureLoading[key];
870
- const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === 'sticker-mention'));
871
+ const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === 'sticker-mention' || item.id === 'learning-memory'));
871
872
  const isInstalled = !requiresInstall || !!state.featureInstalled?.[key];
872
873
 
873
874
  let actionsHtml = '';
@@ -922,7 +923,7 @@ function wireTab() {
922
923
  state.zaloLoginLines = [t('Đang chuẩn bị quét mã Zalo...', 'Preparing Zalo QR login...')];
923
924
  render();
924
925
  try {
925
- await api('/api/zalo/login', { method: 'POST', body: { projectDir: activeProjectDir(), agentId: state.activeBotId } });
926
+ await api('/api/zalo/login', { method: 'POST', body: { projectDir: activeProjectDir(), agentId: state.activeBotId } });
926
927
  } catch (err) {
927
928
  state.zaloLoginOpen = false;
928
929
  state.confirmModal = { title: t('Lỗi đăng nhập','Login error'), message: err.message, okText: t('Đóng','Close'), onConfirm: () => {} };
@@ -1029,12 +1030,12 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1029
1030
  }
1030
1031
  }));
1031
1032
  document.querySelectorAll('[data-update-app]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
1032
- await api('/api/runtime/update', { method: 'POST', body: { projectDir: activeProjectDir(), target: 'openclaw' } });
1033
+ await api('/api/runtime/update', { method: 'POST', body: { projectDir: activeProjectDir(), target: 'openclaw' } });
1033
1034
  await loadSystem();
1034
1035
  await loadStatus();
1035
1036
  }));
1036
1037
  document.querySelectorAll('[data-update-router]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
1037
- await api('/api/runtime/update', { method: 'POST', body: { projectDir: activeProjectDir(), target: '9router' } });
1038
+ await api('/api/runtime/update', { method: 'POST', body: { projectDir: activeProjectDir(), target: '9router' } });
1038
1039
  await loadSystem();
1039
1040
  await loadStatus();
1040
1041
  }));document.querySelectorAll('[data-project-remove]').forEach(btn => btn.onclick = (ev) => {
@@ -1083,7 +1084,7 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1083
1084
  okText: t('Xóa bot','Delete bot'),
1084
1085
  onConfirm: async () => {
1085
1086
  try {
1086
- await api('/api/bot/'+encodeURIComponent(id) + projectQuery(), { method: 'DELETE' });
1087
+ await api('/api/bot/'+encodeURIComponent(id) + projectQuery(), { method: 'DELETE' });
1087
1088
  state.confirmModal = null;
1088
1089
  showToast(t('Đã xóa bot', 'Bot deleted'), `${t('Đã xóa bot','Bot deleted')}: ${id}`, 'success');
1089
1090
  if (state.activeBotId === id) { state.activeBotId = ''; state.selectedFile = ''; state.files = []; }
@@ -1119,7 +1120,7 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1119
1120
  const submitBtn = ev.currentTarget.querySelector('button[type="submit"]');
1120
1121
  withButtonLoading(submitBtn, async () => {
1121
1122
  const nineRouterApiKey = ev.currentTarget.querySelector('[name="nineRouterApiKey"]')?.value || '';
1122
- await api('/api/bot/credentials', { method: 'PUT', body: { projectDir: activeProjectDir(), nineRouterApiKey } });
1123
+ await api('/api/bot/credentials', { method: 'PUT', body: { projectDir: activeProjectDir(), nineRouterApiKey } });
1123
1124
  const msg = ev.currentTarget.querySelector('[data-cred-msg]');
1124
1125
  if (msg) msg.textContent = t('Đã lưu','Saved');
1125
1126
  await loadStatus();
@@ -1194,10 +1195,10 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1194
1195
  const submitBtn = ev.currentTarget.querySelector('button[type="submit"]');
1195
1196
  if (submitBtn?.classList.contains('is-loading')) return;
1196
1197
  await withButtonLoading(submitBtn, async () => {
1197
- const fd = new FormData(ev.currentTarget);
1198
- const body = Object.fromEntries(fd.entries());
1199
- body.projectDir = activeProjectDir();
1200
- body.channel = state.botChannel || 'telegram';
1198
+ const fd = new FormData(ev.currentTarget);
1199
+ const body = Object.fromEntries(fd.entries());
1200
+ body.projectDir = activeProjectDir();
1201
+ body.channel = state.botChannel || 'telegram';
1201
1202
  if (body.channel === 'zalo-personal') {
1202
1203
  state.botModalOpen = false;
1203
1204
  state.zaloLoginOpen = true;
@@ -1241,21 +1242,21 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
1241
1242
  }
1242
1243
  });
1243
1244
  });
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); }));
1245
+ 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); }));
1245
1246
  wireSkillsHandlers(document);
1246
1247
  }
1247
1248
 
1248
1249
  function escapeHtml(s='') { return String(s).replace(/[&<>"']/g, c => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c])); }
1249
1250
  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
- }
1251
+ async function loadSystem(silent=false){ state.system = await api('/api/system'); if (!silent) render(); }
1252
+ async function loadStatus(silent=false){
1253
+ const requestedProjectDir = activeProjectDir();
1254
+ const install = await api('/api/bot/status' + projectQuery());
1255
+ if (requestedProjectDir && install?.projectDir && install.projectDir !== requestedProjectDir) return;
1256
+ state.install = install;
1257
+ if (!state.selectedProjectDir && state.install?.projectDir) state.selectedProjectDir = state.install.projectDir;
1258
+ if (!silent) render();
1259
+ }
1259
1260
  function autoSwitchBotChannel() {
1260
1261
  const bots = state.install?.bots || [];
1261
1262
  const ch = state.botChannel || 'telegram';
@@ -1276,32 +1277,32 @@ function currentBotId() {
1276
1277
  }
1277
1278
  return state.activeBotId || '';
1278
1279
  }
1279
- async function loadFiles(silent=false){
1280
- const botId = currentBotId();
1281
- if (!botId) { state.files = []; if (!silent) render(); return; }
1282
- try {
1283
- state.files = (await api('/api/bot/files' + projectQuery({ agentId: botId }))).files;
1284
- } catch (_) {
1285
- state.files = [];
1286
- }
1287
- if (!silent) render();
1288
- }
1289
- async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
1290
- async function loadFeatureFlags(silent=false){
1291
- const botId=currentBotId();
1292
- if (!activeProjectDir()) { state.featureFlags = {}; state.featureInstalled = {}; state.featureVersions = {}; if (!silent) render(); return; }
1293
- try {
1294
- const data = (await api('/api/features' + projectQuery({ agentId: botId }))) || {};
1295
- state.featureFlags = data.flags || {};
1296
- state.featureInstalled = data.installed || {};
1297
- state.featureVersions = data.versions || {};
1298
- } catch (_) {
1299
- state.featureFlags = {};
1300
- state.featureInstalled = {};
1301
- state.featureVersions = {};
1302
- }
1303
- if (!silent) render();
1304
- }
1280
+ async function loadFiles(silent=false){
1281
+ const botId = currentBotId();
1282
+ if (!botId) { state.files = []; if (!silent) render(); return; }
1283
+ try {
1284
+ state.files = (await api('/api/bot/files' + projectQuery({ agentId: botId }))).files;
1285
+ } catch (_) {
1286
+ state.files = [];
1287
+ }
1288
+ if (!silent) render();
1289
+ }
1290
+ async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
1291
+ async function loadFeatureFlags(silent=false){
1292
+ const botId=currentBotId();
1293
+ if (!activeProjectDir()) { state.featureFlags = {}; state.featureInstalled = {}; state.featureVersions = {}; if (!silent) render(); return; }
1294
+ try {
1295
+ const data = (await api('/api/features' + projectQuery({ agentId: botId }))) || {};
1296
+ state.featureFlags = data.flags || {};
1297
+ state.featureInstalled = data.installed || {};
1298
+ state.featureVersions = data.versions || {};
1299
+ } catch (_) {
1300
+ state.featureFlags = {};
1301
+ state.featureInstalled = {};
1302
+ state.featureVersions = {};
1303
+ }
1304
+ if (!silent) render();
1305
+ }
1305
1306
  function appendLogLine(line) {
1306
1307
  if (line.includes('Setup Wizard updated successfully! Please restart the installer.')) {
1307
1308
  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);
@@ -1404,10 +1405,10 @@ function showToast(title, desc, type = 'info', duration = 4000) {
1404
1405
  }
1405
1406
  }
1406
1407
 
1407
- render();
1408
- Promise.allSettled([loadSystem(true), loadStatus(true), loadCatalog(true)])
1409
- .then(() => loadFeatureFlags(true).catch(() => {}))
1410
- .finally(() => { render(); connectLogs(); });
1408
+ render();
1409
+ Promise.allSettled([loadSystem(true), loadStatus(true), loadCatalog(true)])
1410
+ .then(() => loadFeatureFlags(true).catch(() => {}))
1411
+ .finally(() => { render(); connectLogs(); });
1411
1412
 
1412
1413
 
1413
1414
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "5.8.21",
3
+ "version": "5.8.24",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {