create-openclaw-bot 5.17.1 → 5.17.2

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.
@@ -196,17 +196,17 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
196
196
 
197
197
  // Idempotent config upgrade replayed on every runtime start. Docker embeds it in the container
198
198
  // entrypoint; the native runtime (local-server.js) runs it via `node -e` before every gateway
199
- // (re)start one script, both deploy modes, so native bots stop missing config fixes like the
199
+ // (re)start - one script, both deploy modes, so native bots stop missing config fixes like the
200
200
  // smart-route contextWindow 200000/131072 → 1048576 (Kent chot 01/09/2026; chi dung den DUNG
201
201
  // hai gia tri setup tung ghi, custom tuning giu nguyen). Full rationale sits with the
202
202
  // entrypoint block inside buildDockerArtifacts.
203
203
  // ⚠️ toolResultMaxChars: openclaw ≥2026.8 BO key nay khoi schema va TU CHOI BOOT khi thay no.
204
- // Ban cu cua script nay them key vo dieu kien moi lan container start nen mot khach rebuild
204
+ // Ban cu cua script nay them key vo dieu kien moi lan container start - nen mot khach rebuild
205
205
  // image (Dockerfile keo openclaw moi) la roi vao vong: gateway chet vi key ⇒ xoa tay ⇒ entrypoint
206
206
  // them lai ⇒ chet tiep, khong lo ra thu pham (ca that 103.98.149.154, 31/08/2026, 66 lan restart).
207
207
  // Gio gate theo version openclaw THAT trong container: <2026.8 thi backfill nhu cu, ≥2026.8 thi
208
208
  // GO key neu con (tu chua cac project da nhiem). Khong doc duoc version thi KHONG dong nao chay
209
- // tha thieu mot default con hon gieo key lam gateway tu choi boot.
209
+ // - tha thieu mot default con hon gieo key lam gateway tu choi boot.
210
210
  const contextDefaultsScript = `const fs=require('fs'),path=require('path');const p=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));let ch=false;c.skills=c.skills||{};c.skills.workshop=c.skills.workshop||{};if(!c.skills.workshop.approvalPolicy){c.skills.workshop.approvalPolicy='auto';ch=true;}if(c.browser&&c.browser.enabled!==false){c.tools=c.tools||{};const dn=Array.isArray(c.tools.deny)?c.tools.deny:[];if(!dn.includes('browser')){dn.push('browser');c.tools.deny=dn;ch=true;}}let ocMajorMinor=0;try{const v=String(require('child_process').execSync('openclaw --version',{stdio:['ignore','pipe','ignore']})).match(/(\\d{4})\\.(\\d+)/);if(v)ocMajorMinor=Number(v[1])*100+Number(v[2]);}catch(e){}const d=(c.agents&&c.agents.defaults)?c.agents.defaults:null;if(d){if(d.imageMaxDimensionPx===undefined){d.imageMaxDimensionPx=1024;ch=true;}if(d.imageQuality===undefined){d.imageQuality='efficient';ch=true;}if(ocMajorMinor&&ocMajorMinor<202608){d.contextLimits=d.contextLimits||{};if(d.contextLimits.toolResultMaxChars===undefined){d.contextLimits.toolResultMaxChars=12000;ch=true;}}else if(ocMajorMinor>=202608&&d.contextLimits&&d.contextLimits.toolResultMaxChars!==undefined){delete d.contextLimits.toolResultMaxChars;ch=true;}}if(ocMajorMinor>=202609){if(c.messages&&c.messages.removeAckAfterReply!==undefined){delete c.messages.removeAckAfterReply;ch=true;console.log('[migrate] go messages.removeAckAfterReply (2026.9 khong con nhan khoa nay)');}if(c.meta&&c.meta.lastTouchedAt!==undefined){delete c.meta.lastTouchedAt;ch=true;console.log('[migrate] go meta.lastTouchedAt (2026.9 khong con nhan khoa nay)');}if(c.browser&&c.browser.profiles&&typeof c.browser.profiles==='object'){for(const pn of Object.keys(c.browser.profiles)){const bp=c.browser.profiles[pn];if(bp&&typeof bp==='object'&&bp.color!==undefined){delete bp.color;ch=true;console.log('[migrate] go browser.profiles.'+pn+'.color (2026.9 khong con nhan khoa nay)');}}}}c.gateway=(c.gateway&&typeof c.gateway==='object')?c.gateway:{};if(c.gateway.mode===undefined){c.gateway.mode='local';ch=true;console.log('[migrate] gateway.mode=local (2026.9 tu choi chay neu thieu)');}c.tools=c.tools||{};c.tools.sessions=(c.tools.sessions&&typeof c.tools.sessions==='object')?c.tools.sessions:{};if(c.tools.sessions.visibility===undefined){c.tools.sessions.visibility='agent';ch=true;console.log('[migrate] tools.sessions.visibility=agent (khong cho bot doc phien cua bot khac)');}if(ocMajorMinor>=202608&&d&&d.modelSelectionScope===undefined){d.modelSelectionScope='session';ch=true;console.log('[migrate] agents.defaults.modelSelectionScope=session (khong ghi de model trong config)');}if(ocMajorMinor>=202608){const ag=c.agents||{};const nEntries=(Array.isArray(ag.list)?ag.list.length:0)+(ag.entries&&typeof ag.entries==='object'?Object.keys(ag.entries).length:0);if(nEntries>1&&ag.ownership!=='explicit'){ag.ownership='explicit';c.agents=ag;ch=true;}try{const agRoot=path.join(process.cwd(),'.openclaw','agents');for(const id of fs.readdirSync(agRoot)){const sf=path.join(agRoot,id,'sessions','sessions.json');if(fs.existsSync(sf)){fs.renameSync(sf,sf+'.bak-legacy-'+Date.now());console.log('[migrate] parked legacy session store '+sf);}}}catch(e){}if(c.commands&&c.commands.ownerDisplay!==undefined){delete c.commands.ownerDisplay;ch=true;}if(c.plugins&&c.plugins.bundledDiscovery!==undefined){delete c.plugins.bundledDiscovery;ch=true;}if(Array.isArray(ag.list)){ag.entries=(ag.entries&&typeof ag.entries==='object'&&!Array.isArray(ag.entries))?ag.entries:{};for(const a of ag.list){if(a&&a.id&&!ag.entries[a.id]){const{id:_aid,...rest}=a;ag.entries[_aid]=rest;}}delete ag.list;c.agents=ag;ch=true;console.log('[migrate] moved agents.list into agents.entries');}try{const ea=path.join(process.cwd(),'.openclaw','exec-approvals.json');if(fs.existsSync(ea)){fs.renameSync(ea,ea+'.bak-legacy-'+Date.now());console.log('[migrate] parked legacy exec approvals '+ea);}}catch(e){}try{const ag2=c.agents||{};const ids=(ag2.entries&&typeof ag2.entries==='object')?Object.keys(ag2.entries):(Array.isArray(ag2.list)?ag2.list.map(function(a){return a&&a.id;}).filter(Boolean):[]);if(ids.length>1&&d){if(!(d.heartbeat&&d.heartbeat.agentId)){d.heartbeat=(d.heartbeat&&typeof d.heartbeat==='object')?d.heartbeat:{};d.heartbeat.agentId=ids[0];ch=true;console.log('[migrate] agents.defaults.heartbeat.agentId='+ids[0]+' (khong thi heartbeat tat cam)');}if(!(d.systemAgent&&d.systemAgent.agentId)){d.systemAgent=(d.systemAgent&&typeof d.systemAgent==='object')?d.systemAgent:{};d.systemAgent.agentId=ids[0];ch=true;console.log('[migrate] agents.defaults.systemAgent.agentId='+ids[0]+' (khong thi cron.list vo ca trang Automations)');}const tk=(c.talk&&typeof c.talk==='object')?c.talk:{};if(!tk.agentId){tk.agentId=ids[0];c.talk=tk;ch=true;console.log('[migrate] talk.agentId='+ids[0]+' (khong thi talk.catalog loi khi mo Control UI)');}}}catch(e){}}const pr=c.models&&c.models.providers&&c.models.providers['9router'];if(pr&&Array.isArray(pr.models)){for(const m of pr.models){if(m&&m.id==='smart-route'&&(m.contextWindow===200000||m.contextWindow===131072)){m.contextWindow=1048576;ch=true;}}}if(ch)fs.writeFileSync(p,JSON.stringify(c,null,2));}`;
211
211
 
212
212
  function buildDockerArtifacts(options) {
@@ -249,11 +249,11 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
249
249
  const extVolMount = useExtensionsVolume ? '\n - openclaw-extensions:/home/node/project/.openclaw/extensions' : '';
250
250
  const extVolDecl = useExtensionsVolume ? '\n openclaw-extensions:' : '';
251
251
  // SQLite state on Docker Desktop (macOS/Windows): the host bind mount goes through a
252
- // virtualized file share (virtiofs/gRPC-FUSE) whose locking/mmap semantics break SQLite WAL
252
+ // virtualized file share (virtiofs/gRPC-FUSE) whose locking/mmap semantics break SQLite WAL -
253
253
  // OpenClaw crashes with `Error: disk I/O error` on write (e.g. when a Zalo message arrives).
254
254
  // Keep `.openclaw/state` on a named volume (the Linux VM's native filesystem) instead; the
255
255
  // rest of `.openclaw` stays bind-mounted so workspaces/config remain visible on the host.
256
- // Linux/VPS bind mounts are native ext4 unchanged there (and state stays host-visible).
256
+ // Linux/VPS bind mounts are native ext4 - unchanged there (and state stays host-visible).
257
257
  const useStateVolume = osChoice === 'macos' || osChoice === 'win';
258
258
  const stateVolMount = useStateVolume ? '\n - openclaw-state:/home/node/project/.openclaw/state' : '';
259
259
  const stateVolDecl = useStateVolume ? '\n openclaw-state:' : '';
@@ -278,7 +278,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
278
278
  const deprecatedCleanupScript = `const fs=require('fs'),path=require('path');const p=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));let ch=false;if(c.plugins&&c.plugins.entries&&c.plugins.entries['memory-tencentdb']){delete c.plugins.entries['memory-tencentdb'];ch=true;}if(c.plugins&&Array.isArray(c.plugins.allow)){const n=c.plugins.allow.filter(x=>x!=='memory-tencentdb'&&x!=='@tencentdb-agent-memory/memory-tencentdb');if(n.length!==c.plugins.allow.length){c.plugins.allow=n;ch=true;}}if(c.skills&&c.skills.entries&&c.skills.entries['learning-memory']){delete c.skills.entries['learning-memory'];ch=true;}if(ch)fs.writeFileSync(p,JSON.stringify(c,null,2));}`;
279
279
  // One-shot upgrade for projects created by an older setup (their openclaw.json was
280
280
  // generated before these defaults existed, and bot-config-gen only runs when a bot is
281
- // created/regenerated so a plain rebuild would never pick them up):
281
+ // created/regenerated - so a plain rebuild would never pick them up):
282
282
  // • skills.workshop.approvalPolicy:'auto' → the assistant can author a workspace
283
283
  // skill end-to-end on request instead of stopping at "proposal awaiting approval".
284
284
  // • tools.deny gains `browser` on projects that have browsing enabled, so the model
@@ -288,19 +288,19 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
288
288
  // heavy turn (deep research, 4K chart read-back) from overflowing the context
289
289
  // window mid tool-loop, which cannot be compacted and poisons the session.
290
290
  // • smart-route contextWindow 200000 → 131072: the old declared window exceeded the
291
- // smallest upstream in typical free-model combos, so full sessions deadlocked
291
+ // smallest upstream in typical free-model combos, so full sessions deadlocked -
292
292
  // the compaction summarize call itself overflowed and only /new recovered. Only the
293
293
  // exact setup-written 200000 is rewritten; an operator's custom value is left alone.
294
294
  // Each key is only filled in when absent, so an operator's own tuning is never clobbered.
295
- // (The script itself lives at module scope the native runtime replays the exact same
295
+ // (The script itself lives at module scope - the native runtime replays the exact same
296
296
  // migration before every gateway (re)start, since native projects have no entrypoint.)
297
297
  // Companion backfill for the same older projects: their TOOLS.md was generated before the
298
298
  // skill-authoring / long-turn guidance existed, and workspace files are only written when a
299
- // bot is created so a rebuild alone leaves the assistant stopping at "proposal awaiting
299
+ // bot is created - so a rebuild alone leaves the assistant stopping at "proposal awaiting
300
300
  // approval" even once approvalPolicy is 'auto'. (TOOLS.md is listed in AGENTS.md as required
301
301
  // reading, so guidance lands there rather than in the main manual.) Appends the block once
302
302
  // per workspace, keyed off the `skill_workshop` marker, never rewriting a file that has it.
303
- const agentsGuidanceScript = `const fs=require('fs'),path=require('path');const vi=['','## 🛠️ Tự tạo skill & việc dài','','**Tự tạo skill:** Khi user nhờ tạo một kỹ năng/quy trình dùng lại (vd _"tạo skill X"_), bạn TỰ làm từ đầu đến cuối dùng tool **skill_workshop**: tạo proposal RỒI **apply luôn trong cùng lượt** (môi trường này bật approvalPolicy auto, không cần chờ ai duyệt). Skill ghi vào skills/<tên>/SKILL.md và **tự nạp** qua watcher KHÔNG cần sửa openclaw.json. Đừng dừng ở _"đã tạo proposal, chờ duyệt"_. KHÔNG nhúng bí mật vào skill đọc từ biến môi trường lúc chạy.','','**Việc dài, nhiều bước (research sâu, dựng báo cáo/PDF):** ảnh/chart bạn vừa tạo thì **đừng đọc lại** bằng tool ảnh (chèn thẳng file vào tài liệu; kiểm bằng file tồn tại / build thành công) đọc lại ảnh lớn ngốn ngữ cảnh gấp nhiều lần bạn tưởng. Tạo hình cạnh dài khoảng 1280px, đừng 4K trừ khi user yêu cầu. Research nhiều nguồn thì dùng **sessions_spawn** mỗi nhánh một subagent, bảo chúng **ghi dữ liệu thô ra file** và **chỉ trả tóm tắt ngắn kèm đường dẫn**; bạn tổng hợp từ file.',''].join('\\n');const en=['','## 🛠️ Authoring skills & long work','','**Authoring skills:** When the user asks for a reusable capability (e.g. _"make a skill for X"_), do it end-to-end use the **skill_workshop** tool: create the proposal AND **apply it in the same turn** (this environment sets approvalPolicy auto, so no separate approval is needed). The skill lands in skills/<name>/SKILL.md and **auto-loads** via the watcher no openclaw.json edit required. Do not stop at _"proposal created, awaiting approval"_. Never hardcode secrets into a skill read them from environment variables at runtime.','','**Long multi-step work (deep research, building reports/PDFs):** do not read back an image or chart you just made (drop the file straight in; verify via file exists / build succeeded) re-reading a large image costs far more context than it looks. Render at about 1280px on the long edge, skip 4K unless asked. For multi-source research, use **sessions_spawn** with one subagent per branch, have them **write raw findings to files** and **return only a short summary plus paths**; synthesize from the files.',''].join('\\n');const root=path.join(process.cwd(),'.openclaw');let dirs=[];try{dirs=fs.readdirSync(root).filter(n=>n.indexOf('workspace')===0);}catch(e){}for(const d of dirs){const f=path.join(root,d,'TOOLS.md');try{if(!fs.existsSync(f))continue;const cur=fs.readFileSync(f,'utf8');if(cur.indexOf('skill_workshop')!==-1)continue;const isVi=/Kỹ năng|Ghi chú|Xử lý lỗi|môi trường/.test(cur);fs.appendFileSync(f,(cur.endsWith('\\n')?'':'\\n')+(isVi?vi:en));}catch(e){}}`;
303
+ const agentsGuidanceScript = `const fs=require('fs'),path=require('path');const vi=['','## 🛠️ Tự tạo skill & việc dài','','**Tự tạo skill:** Khi user nhờ tạo một kỹ năng/quy trình dùng lại (vd _"tạo skill X"_), bạn TỰ làm từ đầu đến cuối - dùng tool **skill_workshop**: tạo proposal RỒI **apply luôn trong cùng lượt** (môi trường này bật approvalPolicy auto, không cần chờ ai duyệt). Skill ghi vào skills/<tên>/SKILL.md và **tự nạp** qua watcher - KHÔNG cần sửa openclaw.json. Đừng dừng ở _"đã tạo proposal, chờ duyệt"_. KHÔNG nhúng bí mật vào skill - đọc từ biến môi trường lúc chạy.','','**Việc dài, nhiều bước (research sâu, dựng báo cáo/PDF):** ảnh/chart bạn vừa tạo thì **đừng đọc lại** bằng tool ảnh (chèn thẳng file vào tài liệu; kiểm bằng file tồn tại / build thành công) - đọc lại ảnh lớn ngốn ngữ cảnh gấp nhiều lần bạn tưởng. Tạo hình cạnh dài khoảng 1280px, đừng 4K trừ khi user yêu cầu. Research nhiều nguồn thì dùng **sessions_spawn** mỗi nhánh một subagent, bảo chúng **ghi dữ liệu thô ra file** và **chỉ trả tóm tắt ngắn kèm đường dẫn**; bạn tổng hợp từ file.',''].join('\\n');const en=['','## 🛠️ Authoring skills & long work','','**Authoring skills:** When the user asks for a reusable capability (e.g. _"make a skill for X"_), do it end-to-end - use the **skill_workshop** tool: create the proposal AND **apply it in the same turn** (this environment sets approvalPolicy auto, so no separate approval is needed). The skill lands in skills/<name>/SKILL.md and **auto-loads** via the watcher - no openclaw.json edit required. Do not stop at _"proposal created, awaiting approval"_. Never hardcode secrets into a skill - read them from environment variables at runtime.','','**Long multi-step work (deep research, building reports/PDFs):** do not read back an image or chart you just made (drop the file straight in; verify via file exists / build succeeded) - re-reading a large image costs far more context than it looks. Render at about 1280px on the long edge, skip 4K unless asked. For multi-source research, use **sessions_spawn** with one subagent per branch, have them **write raw findings to files** and **return only a short summary plus paths**; synthesize from the files.',''].join('\\n');const root=path.join(process.cwd(),'.openclaw');let dirs=[];try{dirs=fs.readdirSync(root).filter(n=>n.indexOf('workspace')===0);}catch(e){}for(const d of dirs){const f=path.join(root,d,'TOOLS.md');try{if(!fs.existsSync(f))continue;const cur=fs.readFileSync(f,'utf8');if(cur.indexOf('skill_workshop')!==-1)continue;const isVi=/Kỹ năng|Ghi chú|Xử lý lỗi|môi trường/.test(cur);fs.appendFileSync(f,(cur.endsWith('\\n')?'':'\\n')+(isVi?vi:en));}catch(e){}}`;
304
304
  const securityCompatScript = `const fs=require('fs'),path=require('path');const scopes=['operator.admin','operator.pairing','operator.approvals'];function uniq(a){return Array.from(new Set([...(Array.isArray(a)?a:[]),...scopes]));}function walk(v){if(!v||typeof v!=='object')return;if(Array.isArray(v)){v.forEach(walk);return;}if(Array.isArray(v.scopes)||Array.isArray(v.approvedScopes)){v.scopes=uniq(v.scopes);v.approvedScopes=uniq(v.approvedScopes);}Object.values(v).forEach(walk);}const home=process.env.OPENCLAW_HOME||path.join(process.cwd(),'.openclaw');const state=process.env.OPENCLAW_STATE_DIR||home;const cfgPath=path.join(process.cwd(),'.openclaw','openclaw.json');if(fs.existsSync(cfgPath)){const c=JSON.parse(fs.readFileSync(cfgPath,'utf8'));const p=c.models&&c.models.providers&&c.models.providers['9router'];if(p){p.request=Object.assign({},p.request,{allowPrivateNetwork:true});}fs.writeFileSync(cfgPath,JSON.stringify(c,null,2));}for(const root of Array.from(new Set([home,state]))){const f=path.join(root,'devices','paired.json');if(fs.existsSync(f)){const d=JSON.parse(fs.readFileSync(f,'utf8'));walk(d);fs.writeFileSync(f,JSON.stringify(d,null,2));}}`;
305
305
 
306
306
  const runtimeParts = runtimeCommandParts.filter(Boolean);
@@ -309,7 +309,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
309
309
  'export OPENCLAW_STATE_DIR="${OPENCLAW_STATE_DIR:-$OPENCLAW_HOME}"',
310
310
  'mkdir -p "$OPENCLAW_HOME" "$OPENCLAW_STATE_DIR"',
311
311
  // `openclaw plugins install` unpacks into extensions/.openclaw-install-stage-XXXXXX and removes
312
- // it when it finishes. An interrupted install leaves the staging copy behind and it still
312
+ // it when it finishes. An interrupted install leaves the staging copy behind - and it still
313
313
  // carries a plugin manifest, so the gateway logs "duplicate plugin id detected" on every boot
314
314
  // and a stale build competes with the real one for the same id. Found on a production host: a
315
315
  // zalo-connect 3.0.7 stage dir shadowing 3.0.17 for a week. Nothing is installing at entrypoint
@@ -381,8 +381,8 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
381
381
  // Backfill the inbound-message ack reaction for projects created before it was
382
382
  // seeded. Kept out of the shared migration because zalo-connect reads the GLOBAL
383
383
  // messages.ackReaction, and that same key also feeds Telegram/Discord/Slack/
384
- // WhatsApp which accept only their own fixed reaction sets and would reject an
385
- // arbitrary emoji. Absent-only, so an operator's own choice is never overwritten
384
+ // WhatsApp - which accept only their own fixed reaction sets and would reject an
385
+ // arbitrary emoji. Absent-only, so an operator's own choice is never overwritten -
386
386
  // with one exception: the 🦞 this script itself used to seed is rewritten to
387
387
  // `/-heart`. 🦞 only ships as a Zalo CUSTOM reaction (needs zalo-connect ≥3.0.15,
388
388
  // silently dropped below that); `/-heart` is a built-in every version can send.
@@ -399,11 +399,11 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
399
399
  // Backfill skill-authoring + context defaults for configs from an older setup (see above).
400
400
  runtimeParts.push(`node - <<'NODE'\n${contextDefaultsScript}\nNODE`);
401
401
  runtimeParts.push(`node - <<'NODE'\n${agentsGuidanceScript}\nNODE`);
402
- // browser-tool.js is a CDP client only it has no code to launch a browser, so it
402
+ // browser-tool.js is a CDP client only - it has no code to launch a browser, so it
403
403
  // needs something listening on a debug port. On a desktop that is the operator's own
404
404
  // Chrome (started by start-chrome), reached through the host gateway. On a server there
405
405
  // is no such Chrome, and browsing simply failed. Start a headless Chromium on loopback
406
- // 9222 the second entry in browser-tool.js's candidate list so the same tool works
406
+ // 9222 - the second entry in browser-tool.js's candidate list - so the same tool works
407
407
  // on every OS.
408
408
  //
409
409
  // Emitted unconditionally and gated at RUNTIME on the plugin being installed, not on
@@ -411,7 +411,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
411
411
  // browser-automation on later (the common path) would otherwise never get this block.
412
412
  //
413
413
  // Chromium is baked into the image only when hasBrowser was known at build time. When it
414
- // is missing every project whose image predates the plugin download it once instead
414
+ // is missing - every project whose image predates the plugin - download it once instead
415
415
  // of telling the bot to give up; that message is what makes it answer "there is no
416
416
  // browser in my environment". The download goes to a path under $OPENCLAW_HOME, which is
417
417
  // a bind mount, so recreating the container does not pay for it again. It runs in the
@@ -463,7 +463,7 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
463
463
  ' echo "[entrypoint] Chromium ready at $installed_bin"',
464
464
  ' launch_headless_chrome "$installed_bin"',
465
465
  ' else',
466
- ' echo "[entrypoint] Chromium download failed; see /tmp/openclaw-chromium-install.log browsing still works if the operator runs start-chrome on the host"',
466
+ ' echo "[entrypoint] Chromium download failed; see /tmp/openclaw-chromium-install.log - browsing still works if the operator runs start-chrome on the host"',
467
467
  ' fi',
468
468
  ' ) &',
469
469
  '}',
@@ -473,9 +473,9 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
473
473
  // openclaw ≥2026.8 TACH duckduckgo khoi bundle thanh plugin external co capability consent.
474
474
  // Config do setup sinh (bot chon skill web-search) van khai `plugins.entries.duckduckgo`,
475
475
  // va gateway 2026.8.x TU CHOI ready khi entry khai ma plugin chua cai ("Plugin verification
476
- // failed … requires capability consent") do that tren 103.98.149.154, 31/08/2026.
476
+ // failed … requires capability consent") - do that tren 103.98.149.154, 31/08/2026.
477
477
  // Cai o day, truoc gateway run, dung lenh + co ma chinh gateway goi y; `yes |` cho cau hoi
478
- // consent; loi khong chan boot (|| true) thieu web search chi la mat mot tool phu.
478
+ // consent; loi khong chan boot (|| true) - thieu web search chi la mat mot tool phu.
479
479
  runtimeParts.push([
480
480
  'if grep -q \'"duckduckgo"\' "$OPENCLAW_HOME/openclaw.json" 2>/dev/null && [ ! -d "$OPENCLAW_HOME/extensions/duckduckgo" ]; then',
481
481
  ' echo "[entrypoint] installing external duckduckgo plugin (openclaw >=2026.8 unbundled it)"',
@@ -485,10 +485,10 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
485
485
  // ── Doctor-on-upgrade ─────────────────────────────────────────────────────────────────
486
486
  // Update openclaw khong bao gio chi la "doi so version": schema config doi (key cu bi TU CHOI
487
487
  // chu khong bo qua) va state DB doi migration (audit-events-v2…), va doctor chi chay sach khi
488
- // gateway DANG TAT tuc dung ngay tai day, truoc `gateway run`, trong container vua boot.
488
+ // gateway DANG TAT - tuc dung ngay tai day, truoc `gateway run`, trong container vua boot.
489
489
  // So version hien tai voi lan boot truoc (marker trong $OPENCLAW_HOME); khac nhau moi chay,
490
490
  // hai luot vi doctor boc migration theo LOP (ca that 103.98.149.154 can nhieu luot). `yes |`
491
- // vi doctor co cau hoi tuong tac; loi doctor khong chan boot gateway se tu noi not phan con
491
+ // vi doctor co cau hoi tuong tac; loi doctor khong chan boot - gateway se tu noi not phan con
492
492
  // thieu, con hon container chet cung khong ai chan doan duoc.
493
493
  runtimeParts.push([
494
494
  'OC_VER="$(openclaw --version 2>/dev/null | head -1)"',
@@ -566,7 +566,7 @@ ${indentBlock(docker9RouterEntrypointScript, 8)}
566
566
  - HOSTNAME=0.0.0.0
567
567
  - CI=true
568
568
  # 9router blocks the default password for "remote" access, and inside Docker every
569
- # host request arrives via the bridge network, so it always counts as remote without
569
+ # host request arrives via the bridge network, so it always counts as remote - without
570
570
  # this the user can NEVER log in with the documented default (measured 02/09/2026).
571
571
  - INITIAL_PASSWORD=123456
572
572
  volumes:
@@ -664,7 +664,7 @@ ${indentBlock(docker9RouterEntrypointScript, 8)}
664
664
  - HOSTNAME=0.0.0.0
665
665
  - CI=true
666
666
  # 9router blocks the default password for "remote" access, and inside Docker every
667
- # host request arrives via the bridge network, so it always counts as remote without
667
+ # host request arrives via the bridge network, so it always counts as remote - without
668
668
  # this the user can NEVER log in with the documented default (measured 02/09/2026).
669
669
  - INITIAL_PASSWORD=123456
670
670
  volumes:
@@ -10,7 +10,7 @@
10
10
  (function (root) {
11
11
  const HOST_UI_PS1_VERSION = '1';
12
12
 
13
- const HOST_UI_PS1 = String.raw`# OpenClaw host UI helper version __VERSION__
13
+ const HOST_UI_PS1 = String.raw`# OpenClaw host UI helper - version __VERSION__
14
14
  # Generated by openclaw-setup. Edits are overwritten when the version changes.
15
15
  param(
16
16
  [Parameter(Mandatory = $true)][string]$Action,
@@ -1,5 +1,5 @@
1
1
  // @ts-nocheck
2
- // install-gen.js Build install/runtime artifacts (Chrome debug, uninstall, skill catalog)
2
+ // install-gen.js - Build install/runtime artifacts (Chrome debug, uninstall, skill catalog)
3
3
  // Workspace .md files are in workspace-gen.js (single source of truth).
4
4
  (function (root) {
5
5
  function buildSkillCatalogMarkdown(options = {}) {
@@ -366,7 +366,7 @@ fi
366
366
 
367
367
  function buildUpgradePs1() {
368
368
  return [
369
- "# OpenClaw Upgrade Script Windows (PowerShell)",
369
+ "# OpenClaw Upgrade Script - Windows (PowerShell)",
370
370
  "# Cach dung:",
371
371
  "# Nhan dup upgrade.ps1 hoac: .\\upgrade.ps1",
372
372
  "# irm https://raw.githubusercontent.com/tuanminhhole/openclaw-setup/main/upgrade.ps1 | iex",
@@ -396,7 +396,7 @@ fi
396
396
  "foreach ($l in $logo) { Write-Host $l -ForegroundColor Red }",
397
397
  "Write-Host \"\"",
398
398
  "",
399
- "# Box node render (handles emoji visual width correctly on all terminals)",
399
+ "# Box - node render (handles emoji visual width correctly on all terminals)",
400
400
  "$env:L1 = \" 🦞 OpenClaw Setup$VER_STR | Upgrade Script\"",
401
401
  "$env:L2 = \" Windows (PowerShell)\"",
402
402
  "node -e @\"\nconst RED='\\x1b[0;31m',NC='\\x1b[0m';\nfunction vw(s){let w=0;for(const c of[...s]){const cp=c.codePointAt(0);w+=(cp>=0x1F000&&cp<=0x1FFFF?2:1);}return w;}\nconst L1=process.env.L1,L2=process.env.L2;\nconst INNER=Math.max(vw(L1),vw(L2))+2;\nconst D='─'.repeat(INNER);const pad=s=>' '.repeat(Math.max(0,INNER-vw(s)));\nconsole.log(RED+'╭'+D+'╮'+NC);\nconsole.log(RED+'│'+NC+L1+pad(L1)+RED+'│'+NC);\nconsole.log(RED+'│'+NC+L2+pad(L2)+RED+'│'+NC);\nconsole.log(RED+'╰'+D+'╯'+NC);\n\"@",
@@ -426,7 +426,7 @@ fi
426
426
  "",
427
427
  "# ── 3. Chay upgrade ──────────────────────────────────────────────────────────",
428
428
  "Write-Host \" 🔄 Dang lay CLI moi nhat va chay upgrade...\" -ForegroundColor Cyan",
429
- "Write-Host \" npx luon tai create-openclaw-bot@latest khong can cap nhat tay\" -ForegroundColor DarkGray",
429
+ "Write-Host \" npx luon tai create-openclaw-bot@latest - khong can cap nhat tay\" -ForegroundColor DarkGray",
430
430
  "Write-Host \"\"",
431
431
  "",
432
432
  "try {",
@@ -442,7 +442,7 @@ fi
442
442
  " Write-Host \" 🎉 Upgrade hoan tat!\" -ForegroundColor Green",
443
443
  " Write-Host \" Dashboard: http://localhost:18789\" -ForegroundColor Cyan",
444
444
  "} else {",
445
- " Write-Host \" ⚠️ Ma loi: $exitCode xem log o tren.\" -ForegroundColor Yellow",
445
+ " Write-Host \" ⚠️ Ma loi: $exitCode - xem log o tren.\" -ForegroundColor Yellow",
446
446
  "}",
447
447
  "Write-Host \"\"",
448
448
  "Read-Host \"Nhan Enter de dong\"",
@@ -452,7 +452,7 @@ fi
452
452
  function buildUpgradeSh() {
453
453
  return [
454
454
  "#!/bin/bash",
455
- "# OpenClaw Upgrade Script Linux / macOS / Ubuntu",
455
+ "# OpenClaw Upgrade Script - Linux / macOS / Ubuntu",
456
456
  "# Cach dung:",
457
457
  "# bash upgrade.sh",
458
458
  "# curl -fsSL https://raw.githubusercontent.com/tuanminhhole/openclaw-setup/main/upgrade.sh | bash",
@@ -484,7 +484,7 @@ fi
484
484
  "echo ' ╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝'",
485
485
  "echo -e \"${NC}\"",
486
486
  "",
487
- "# Box node render (handles emoji visual width correctly on all terminals)",
487
+ "# Box - node render (handles emoji visual width correctly on all terminals)",
488
488
  "L1=\" 🦞 OpenClaw Setup${VER_STR} | Upgrade Script\"",
489
489
  "L2=\" Linux / macOS / Ubuntu\"",
490
490
  "L1=\"$L1\" L2=\"$L2\" node -e \"\nconst RED='\\x1b[0;31m',NC='\\x1b[0m';\nfunction vw(s){let w=0;for(const c of[...s]){const cp=c.codePointAt(0);w+=(cp>=0x1F000&&cp<=0x1FFFF?2:1);}return w;}\nconst L1=process.env.L1,L2=process.env.L2;\nconst INNER=Math.max(vw(L1),vw(L2))+2;\nconst D='─'.repeat(INNER);const pad=s=>' '.repeat(Math.max(0,INNER-vw(s)));\nconsole.log(RED+'╭'+D+'╮'+NC);\nconsole.log(RED+'│'+NC+L1+pad(L1)+RED+'│'+NC);\nconsole.log(RED+'│'+NC+L2+pad(L2)+RED+'│'+NC);\nconsole.log(RED+'╰'+D+'╯'+NC);\n\"",
@@ -520,7 +520,7 @@ fi
520
520
  "",
521
521
  "# ── 3. Chay upgrade ──────────────────────────────────────────────────────────",
522
522
  "echo -e \"${CYAN} 🔄 Dang lay CLI moi nhat va chay upgrade...${NC}\"",
523
- "echo -e \"${GRAY} npx luon tai create-openclaw-bot@latest khong can cap nhat tay${NC}\"",
523
+ "echo -e \"${GRAY} npx luon tai create-openclaw-bot@latest - khong can cap nhat tay${NC}\"",
524
524
  "echo \"\"",
525
525
  "",
526
526
  "npx create-openclaw-bot@latest upgrade",
@@ -531,7 +531,7 @@ fi
531
531
  " echo -e \"${GREEN} 🎉 Upgrade hoan tat!${NC}\"",
532
532
  " echo -e \"${CYAN} Dashboard: http://localhost:18789${NC}\"",
533
533
  "else",
534
- " echo -e \"${YELLOW} ⚠️ Ma loi: $EXIT_CODE xem log o tren.${NC}\"",
534
+ " echo -e \"${YELLOW} ⚠️ Ma loi: $EXIT_CODE - xem log o tren.${NC}\"",
535
535
  "fi",
536
536
  "echo \"\"",
537
537
  ""
@@ -9,7 +9,7 @@
9
9
  * • anything started over SSH dies the moment the session closes (LastTaskResult 0xC000013A);
10
10
  * • a task set to run at boot is refused outright, because "at startup" and "only while
11
11
  * someone is logged in" contradict each other (0x800710E0).
12
- * Making it run without a login needs the account password stored in the task Task Scheduler
12
+ * Making it run without a login needs the account password stored in the task - Task Scheduler
13
13
  * rejected that too ("The user account is unknown, the password is incorrect"). So the owner
14
14
  * drives it by hand, exactly like the .command files on macOS.
15
15
  *
@@ -17,21 +17,21 @@
17
17
  *
18
18
  * 1. `.cmd` MUST be CRLF. LF-only files break on Windows.
19
19
  * 2. Never name a launcher after the command it calls. `9router.cmd` in the project dir plus
20
- * `cd /d <project>` makes `call 9router` re-enter the same file BATCH RECURSION, and the
20
+ * `cd /d <project>` makes `call 9router` re-enter the same file - BATCH RECURSION, and the
21
21
  * process dies before doing anything.
22
22
  * 3. Never append to a fixed log file. 9router holds its log open for its whole lifetime, so
23
23
  * the SECOND run cannot open it and exits with "The process cannot access the file because
24
- * it is being used by another process" invisible on the first run, which is why it is easy
24
+ * it is being used by another process" - invisible on the first run, which is why it is easy
25
25
  * to ship broken.
26
26
  * 4. 9router shows a `Choose Interface` menu when its stdout looks like a real console. Started
27
27
  * hidden, nobody can answer it, so it hangs and never opens its port. `> NUL` is what tells
28
28
  * it to run unattended. Symptom without it: works when run by hand, silently dead in the
29
- * background same command.
29
+ * background - same command.
30
30
  * 5. `Start-Process -WindowStyle Hidden` does NOT hide a console a .cmd opens for itself. Only
31
31
  * WScript.Shell.Run(..., 0, False) truly hides it. Leaving a visible window is not cosmetic:
32
32
  * it IS the bot, and closing it kills the bot.
33
33
  * 6. Anchor the working directory. Agents may carry relative workspace paths, which resolve
34
- * against the launcher's cwd move the files into a folder and those bots die with
34
+ * against the launcher's cwd - move the files into a folder and those bots die with
35
35
  * WORKSPACE_VANISHED while the others keep working.
36
36
  */
37
37
 
@@ -45,7 +45,7 @@ function buildRunHiddenVbs() {
45
45
  );
46
46
  }
47
47
 
48
- /** Starts 9router. Named start-9router.cmd on purpose see trap 2. */
48
+ /** Starts 9router. Named start-9router.cmd on purpose - see trap 2. */
49
49
  function build9RouterCmd({ projectDir, routerPort }) {
50
50
  return CRLF(
51
51
  '@echo off\n' +
@@ -89,7 +89,7 @@ function buildGatewayCmd({ projectDir, gatewayPort }) {
89
89
  '@echo off\n' +
90
90
  'rem Goi thang node, KHONG qua gateway.cmd cua openclaw: tep do chay kem --task-supervisor,\n' +
91
91
  'rem no di tim Scheduled Task de ban giao; khong co task thi no de them tien trinh + cua so.\n' +
92
- 'rem Khong redirect ra tep co dinh (xem trap 3) openclaw tu ghi nhat ky trong %TEMP%\\openclaw.\n' +
92
+ 'rem Khong redirect ra tep co dinh (xem trap 3) - openclaw tu ghi nhat ky trong %TEMP%\\openclaw.\n' +
93
93
  `cd /d ${projectDir}\n` +
94
94
  'set "HOME=%USERPROFILE%"\n' +
95
95
  `set "OPENCLAW_GATEWAY_PORT=${gatewayPort}"\n` +
@@ -105,7 +105,12 @@ function buildSetupUiCmd({ projectDir, setupPort }) {
105
105
  'rem Ten tep nhat ky kem gio, de lan chay sau khong dung vao tep dang bi giu (trap 3).\n' +
106
106
  `cd /d ${projectDir}\n` +
107
107
  'for /f "tokens=1-4 delims=/: " %%a in ("%TIME%") do set "T=%%a%%b%%c"\n' +
108
- `call npx create-openclaw-bot --host=127.0.0.1 --port=${setupPort} --no-open --project-dir=${projectDir}` +
108
+ // `--yes` is not optional. Whenever the installed version differs from what npx resolves - which
109
+ // is EVERY time a new version is published - npx stops at "Need to install the following
110
+ // packages ... Ok to proceed? (y)". The launcher runs hidden, so nobody can answer it: the
111
+ // window sits there forever and the dashboard simply never opens. Measured on a customer
112
+ // machine, where it looked like the tunnel was broken rather than a prompt waiting offscreen.
113
+ `call npx --yes create-openclaw-bot --host=127.0.0.1 --port=${setupPort} --no-open --project-dir=${projectDir}` +
109
114
  ` > "${projectDir}\\setup-ui-%T%.log" 2>&1\n`,
110
115
  );
111
116
  }
@@ -131,6 +136,15 @@ function buildStartBotCmd({ projectDir, gatewayPort, routerPort }) {
131
136
  'echo Cho bot san sang (co the mat 30-60 giay)...\n' +
132
137
  'echo.\n' +
133
138
  `powershell -NoProfile -Command "$ok=$false; for($i=0;$i -lt 40;$i++){ try{ Invoke-WebRequest 'http://127.0.0.1:${gatewayPort}/health' -UseBasicParsing -TimeoutSec 3 | Out-Null; $ok=$true; break } catch { Start-Sleep -Seconds 3 } }; if($ok){ Write-Host ' [OK] Bot da chay.' -ForegroundColor Green } else { Write-Host ' [LOI] Bot chua len.' -ForegroundColor Red }"\n` +
139
+ // Bring screen control back up too. The node host dies with the login session like everything
140
+ // else, and nothing else restarts it: PC control would quietly stop working after every logout
141
+ // or reboot, with the dashboard still showing it as ON. Only start it when .openclaw-node
142
+ // exists - that directory is written when the operator turns PC control on, so a project that
143
+ // never enabled it starts nothing extra.
144
+ `if exist "${projectDir}\\.openclaw-node\\openclaw.json" (\n` +
145
+ ' echo Dang bat dieu khien may...\n' +
146
+ ` powershell -NoProfile -Command "if (-not (Get-CimInstance Win32_Process -Filter \\"Name='node.exe'\\" | Where-Object { $_.CommandLine -like '*node*run*--host*' })) { Start-Process wscript -ArgumentList '${projectDir}\\run-hidden.vbs','${projectDir}\\node-host.cmd' -WindowStyle Hidden }" >nul 2>&1\n` +
147
+ ')\n' +
134
148
  'echo.\n' +
135
149
  'echo --- Trang thai ---\n' +
136
150
  `powershell -NoProfile -Command "foreach($x in @(@('Bot (gateway)','http://127.0.0.1:${gatewayPort}/health'),@('9Router','http://127.0.0.1:${routerPort}/'),@('Bang dieu khien Zalo','http://127.0.0.1:${dashPort}/dashboard'))){ try{ (Invoke-WebRequest $x[1] -UseBasicParsing -TimeoutSec 6) | Out-Null; Write-Host (' {0,-22} DANG CHAY' -f $x[0]) -ForegroundColor Green } catch { Write-Host (' {0,-22} TAT' -f $x[0]) -ForegroundColor Red } }"\n` +