create-openclaw-bot 4.0.8 → 4.1.0

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/index.html CHANGED
@@ -119,7 +119,7 @@
119
119
  <!-- ─── 3. Bot Identity (Name, Role, Emoji, Vibe) ─── -->
120
120
  <div class="identity-grid">
121
121
  <div class="form-group">
122
- <label class="form-group__label" for="cfg-name" data-vi="Tên Bot" data-en="Bot Name">Tên Bot</label>
122
+ <label class="form-group__label" for="cfg-name" data-vi="Tên Bot" data-en="Bot Name">Tên Bot <span style="color: var(--danger, #ef4444);">*</span></label>
123
123
  <input type="text" class="form-input" id="cfg-name" placeholder="VD: Williams, Jarvis, Luna..." oninput="window.__validateKeys()">
124
124
  </div>
125
125
  <div class="form-group">
@@ -145,7 +145,7 @@
145
145
 
146
146
  <!-- ─── 4b. User Info (→ USER.md) ─── -->
147
147
  <div class="form-group">
148
- <label class="form-group__label" for="cfg-user-info" data-vi="👤 Thông tin về bạn (để bot hiểu bạn hơn)" data-en="👤 About You (so the bot replies better)">👤 Thông tin về bạn (để bot hiểu bạn hơn)</label>
148
+ <label class="form-group__label" for="cfg-user-info" data-vi="👤 Thông tin về bạn (để bot hiểu bạn hơn)" data-en="👤 About You (so the bot replies better)">👤 Thông tin về bạn (để bot hiểu bạn hơn) <span style="color: var(--danger, #ef4444);">*</span></label>
149
149
  <textarea class="form-textarea" id="cfg-user-info" placeholder="VD: Mình là dev, thích code Python, múi giờ UTC+7. Thích câu trả lời đi thẳng vào vấn đề, không rào trước đón sau..." style="min-height: 100px;"></textarea>
150
150
  <div class="prompt-notice">
151
151
  <span class="prompt-notice__icon">👤</span>
@@ -201,7 +201,8 @@
201
201
  <!-- ===== Step 3: API Keys & Thư Mục ===== -->
202
202
  <section class="step" data-step="3">
203
203
  <h2 class="step__title" data-vi="Nhập API Keys & Thư mục" data-en="Enter API Keys & Folder">Nhập API Keys & Thư mục</h2>
204
- <p class="step__description" data-vi="Nhập key trực tiếp + chọn nơi lưu project — wizard lo phần còn lại." data-en="Enter keys + choose project folder — the wizard handles the rest.">Nhập key trực tiếp + chọn nơi lưu project — wizard lo phần còn lại.</p>
204
+ <p class="step__description" style="margin-bottom: 4px;" data-vi="Nhập key trực tiếp + chọn nơi lưu project — wizard lo phần còn lại." data-en="Enter keys + choose project folder — the wizard handles the rest.">Nhập key trực tiếp + chọn nơi lưu project — wizard lo phần còn lại.</p>
205
+ <p style="font-size: 13px; color: var(--danger, #ef4444); margin-top: 0; margin-bottom: 20px;" data-vi="Các trường có dấu <span style='color:var(--danger,#ef4444)'>*</span> là bắt buộc điền." data-en="Fields marked with <span style='color:var(--danger,#ef4444)'>*</span> are required.">Các trường có dấu <span style="color:var(--danger,#ef4444)">*</span> là bắt buộc điền.</p>
205
206
 
206
207
  <!-- Section 1: AI Provider (9Router / Direct API / Ollama) -->
207
208
  <div id="key-section-provider" style="margin-bottom: 20px;"></div>
@@ -214,7 +215,7 @@
214
215
 
215
216
  <!-- Project Path Input -->
216
217
  <div class="form-group" style="margin-top: 24px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,0.06);">
217
- <label class="form-group__label" data-vi="Thư mục lưu trữ Bot (Trên máy chứa Docker)" data-en="Bot Directory (On Docker host)">Thư mục lưu trữ Bot (Trên máy chứa Docker)</label>
218
+ <label class="form-group__label" data-vi="Thư mục lưu trữ Bot (Trên máy chứa Docker) <span style='color:var(--danger,#ef4444)'>*</span>" data-en="Bot Directory (On Docker host) <span style='color:var(--danger,#ef4444)'>*</span>">Thư mục lưu trữ Bot (Trên máy chứa Docker) <span style='color:var(--danger,#ef4444)'>*</span></label>
218
219
  <input type="text" class="form-input" id="cfg-project-path" value="D:\openclaw-setup" placeholder="VD: D:\openclaw-setup hoặc /home/user/openclaw-setup" style="font-family: monospace;">
219
220
  <p class="form-group__hint" data-vi="Script sẽ tự động tạo thư mục này và sinh các file config bên trong." data-en="The script will auto-create this folder and generate config files inside.">Script sẽ tự động tạo thư mục này và sinh các file config bên trong.</p>
220
221
  </div>
@@ -246,6 +247,10 @@
246
247
  <span data-vi="📥 Download setup-openclaw.bat" data-en="📥 Download setup-openclaw.bat">📥 Download setup-openclaw.bat</span>
247
248
  </button>
248
249
  </div>
250
+ <div style="font-size: 12.5px; color: var(--warning, #ffc107); margin: 10px 0 8px; text-align: center; padding: 6px; background: rgba(255, 193, 7, 0.08); border-radius: 6px; border: 1px solid rgba(255, 193, 7, 0.2);">
251
+ ⚠️ <strong data-vi="Lưu ý quan trọng:" data-en="Important notice:">Lưu ý quan trọng:</strong>
252
+ <span data-vi="Click chuột phải vào file .bat → Properties (Thuộc tính) → tick Unblock (Mở khóa) trước khi chạy!" data-en="Right-click the .bat file → Properties → check Unblock before running!">Click chuột phải vào file .bat → Properties (Thuộc tính) → tick Unblock (Mở khóa) trước khi chạy!</span>
253
+ </div>
249
254
  <p style="font-size: 12px; color: var(--text-muted); margin: 0; text-align: center;" data-vi="Yêu cầu: Docker Desktop đã cài và đang chạy" data-en="Requires: Docker Desktop installed and running">Yêu cầu: Docker Desktop đã cài và đang chạy</p>
250
255
  </div>
251
256
 
package/package.json CHANGED
@@ -1,28 +1,28 @@
1
- {
2
- "name": "create-openclaw-bot",
3
- "version": "4.0.8",
4
- "description": "Interactive CLI installer for OpenClaw Bot",
5
- "main": "cli.js",
6
- "bin": {
7
- "create-openclaw-bot": "./cli.js"
8
- },
9
- "scripts": {
10
- "test": "echo \"Error: no test specified\" && exit 1"
11
- },
12
- "keywords": [
13
- "openclaw",
14
- "cli",
15
- "bot",
16
- "zalo",
17
- "telegram",
18
- "ai"
19
- ],
20
- "author": "tuanminhhole",
21
- "license": "MIT",
22
- "dependencies": {
23
- "@inquirer/prompts": "^4.3.1",
24
- "chalk": "^5.3.0",
25
- "fs-extra": "^11.2.0"
26
- },
27
- "type": "module"
28
- }
1
+ {
2
+ "name": "create-openclaw-bot",
3
+ "version": "4.1.0",
4
+ "description": "Interactive CLI installer for OpenClaw Bot",
5
+ "main": "cli.js",
6
+ "bin": {
7
+ "create-openclaw-bot": "./cli.js"
8
+ },
9
+ "scripts": {
10
+ "test": "echo \"Error: no test specified\" && exit 1"
11
+ },
12
+ "keywords": [
13
+ "openclaw",
14
+ "cli",
15
+ "bot",
16
+ "zalo",
17
+ "telegram",
18
+ "ai"
19
+ ],
20
+ "author": "tuanminhhole",
21
+ "license": "MIT",
22
+ "dependencies": {
23
+ "@inquirer/prompts": "^4.3.1",
24
+ "chalk": "^5.3.0",
25
+ "fs-extra": "^11.2.0"
26
+ },
27
+ "type": "module"
28
+ }
package/setup.js CHANGED
@@ -345,7 +345,7 @@
345
345
  - ✅ Chỉ mount đúng thư mục cần thiết (config + workspace)
346
346
  - ❌ KHÔNG mount nguyên ổ đĩa (C:/ hoặc D:/)
347
347
  - ❌ KHÔNG chạy container với --privileged
348
- - ✅ Giới hạn port expose (chỉ 18789)`,
348
+ - ✅ Giới hạn port expose (chỉ 38789)`,
349
349
  en: `## 🔐 Security Rules — MANDATORY
350
350
 
351
351
  ### System files & directories
@@ -372,7 +372,7 @@
372
372
  - ✅ Only mount required directories (config + workspace)
373
373
  - ❌ DO NOT mount entire drives (C:/ or D:/)
374
374
  - ❌ DO NOT run containers with --privileged
375
- - ✅ Limit exposed ports (only 18789)`,
375
+ - ✅ Limit exposed ports (only 38789)`,
376
376
  };
377
377
 
378
378
  // ========== DOM Ready ==========
@@ -762,8 +762,8 @@
762
762
  // 9Router: simple message (no API key needed - managed via dashboard)
763
763
  pHtml += `<p style="font-size: 13px; color: var(--text-secondary); margin: 0 0 8px;">
764
764
  ${isVi
765
- ? 'Sau khi Docker khởi động xong, mở <a href="http://localhost:20128/dashboard" target="_blank" style="color: var(--accent);">localhost:20128/dashboard</a> để đăng nhập OAuth và kết nối các Provider.'
766
- : 'After Docker starts, open <a href="http://localhost:20128/dashboard" target="_blank" style="color: var(--accent);">localhost:20128/dashboard</a> to OAuth login and connect Providers.'}
765
+ ? 'Sau khi Docker khởi động xong, mở <a href="http://localhost:30128/dashboard" target="_blank" style="color: var(--accent);">localhost:30128/dashboard</a> để đăng nhập OAuth và kết nối các Provider.'
766
+ : 'After Docker starts, open <a href="http://localhost:30128/dashboard" target="_blank" style="color: var(--accent);">localhost:30128/dashboard</a> to OAuth login and connect Providers.'}
767
767
  </p>`;
768
768
  pHtml += `<p style="font-size: 12px; color: var(--text-muted); margin: 0;">
769
769
  ${isVi
@@ -778,7 +778,7 @@
778
778
  } else {
779
779
  // Direct API provider: show key input
780
780
  pHtml += `<div class="form-group" style="margin: 0;">
781
- <label class="form-group__label" for="key-api-key">🔑 ${provider.envLabel}</label>
781
+ <label class="form-group__label" for="key-api-key">🔑 ${provider.envLabel} <span style="color: var(--danger, #ef4444);">*</span></label>
782
782
  <input type="text" class="form-input" id="key-api-key" placeholder="${provider.envKey}=..." style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
783
783
  <p class="form-group__hint">${isVi ? 'Lấy từ' : 'Get from'} <a href="${provider.envLink}" target="_blank">${provider.envLink.replace('https://', '')}</a></p>
784
784
  </div>`;
@@ -800,13 +800,13 @@
800
800
 
801
801
  if (state.channel === 'telegram') {
802
802
  cHtml += `<div class="form-group" style="margin: 0;">
803
- <label class="form-group__label" for="key-bot-token">🤖 Telegram Bot Token</label>
803
+ <label class="form-group__label" for="key-bot-token">🤖 Telegram Bot Token <span style="color: var(--danger, #ef4444);">*</span></label>
804
804
  <input type="text" class="form-input" id="key-bot-token" placeholder="VD: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
805
805
  <p class="form-group__hint">${isVi ? 'Lấy từ <a href="https://t.me/BotFather" target="_blank">@BotFather</a> trên Telegram' : 'Get from <a href="https://t.me/BotFather" target="_blank">@BotFather</a> on Telegram'}</p>
806
806
  </div>`;
807
807
  } else if (state.channel === 'zalo-bot') {
808
808
  cHtml += `<div class="form-group" style="margin: 0;">
809
- <label class="form-group__label" for="key-bot-token">🔑 Zalo Bot Token</label>
809
+ <label class="form-group__label" for="key-bot-token">🔑 Zalo Bot Token <span style="color: var(--danger, #ef4444);">*</span></label>
810
810
  <input type="text" class="form-input" id="key-bot-token" placeholder="Zalo Bot Token" style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
811
811
  <p class="form-group__hint">${isVi ? 'Lấy từ <a href="https://developers.zalo.me" target="_blank">Zalo Bot Platform</a>' : 'Get from <a href="https://developers.zalo.me" target="_blank">Zalo Bot Platform</a>'}</p>
812
812
  </div>`;
@@ -1039,29 +1039,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
1039
1039
  apiKey: 'sk-no-key',
1040
1040
  api: 'openai-completions',
1041
1041
  models: [
1042
- // Free Tier Providers
1043
- { id: 'if/qwen3-coder-plus', name: 'Qwen3 Coder+ (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1044
- { id: 'if/kimi-k2', name: 'Kimi K2 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1045
- { id: 'if/kimi-k2-thinking', name: 'Kimi K2 Thinking (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1046
- { id: 'if/glm-4.7', name: 'GLM 4.7 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1047
- { id: 'if/minimax-m2', name: 'MiniMax M2 (iFlow FREE)', contextWindow: 1000000, maxTokens: 8192 },
1048
- { id: 'if/deepseek-r1', name: 'DeepSeek R1 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1049
- { id: 'qw/qwen3-coder-plus', name: 'Qwen3 Coder+ (Qwen FREE)', contextWindow: 128000, maxTokens: 8192 },
1050
- { id: 'qw/qwen3-coder-flash', name: 'Qwen3 Coder Flash (Qwen FREE)', contextWindow: 128000, maxTokens: 8192 },
1051
- { id: 'kr/claude-sonnet-4.5', name: 'Claude Sonnet 4.5 (Kiro FREE)', contextWindow: 200000, maxTokens: 8192 },
1052
- { id: 'kr/claude-haiku-4.5', name: 'Claude Haiku 4.5 (Kiro FREE)', contextWindow: 200000, maxTokens: 8192 },
1053
- // Ollama Cloud
1054
- { id: 'ollama/qwen3.5', name: 'Qwen 3.5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
1055
- { id: 'ollama/kimi-k2.5', name: 'Kimi K2.5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
1056
- { id: 'ollama/glm-5', name: 'GLM 5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
1057
- { id: 'ollama/glm-4.7-flash', name: 'GLM 4.7 Flash (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
1058
- { id: 'ollama/minimax-m2.5', name: 'MiniMax M2.5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
1059
- { id: 'ollama/gpt-oss:120b', name: 'GPT-OSS 120B (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
1060
- // API Key Providers
1061
- { id: 'glm/glm-4.7', name: 'GLM 4.7 ($0.6/1M)', contextWindow: 128000, maxTokens: 8192 },
1062
- { id: 'minimax/MiniMax-M2.1', name: 'MiniMax M2.1 ($0.20/1M)', contextWindow: 1000000, maxTokens: 8192 },
1063
- { id: 'kimi/kimi-latest', name: 'Kimi Latest ($0.90/1M)', contextWindow: 128000, maxTokens: 8192 },
1064
- { id: 'deepseek/deepseek-chat', name: 'DeepSeek V3.2 Chat', contextWindow: 128000, maxTokens: 8192 },
1042
+ { id: 'smart-route', name: 'Smart Proxy (Auto Route)', contextWindow: 200000, maxTokens: 8192 }
1065
1043
  ],
1066
1044
  },
1067
1045
  },
@@ -1181,6 +1159,46 @@ ${finalCmd}`;
1181
1159
  // extra_hosts always needed for browser (socat → host Chrome)
1182
1160
  const extraHostsBlock = ` extra_hosts:\n - "host.docker.internal:host-gateway"`;
1183
1161
 
1162
+ // ─── Dynamic Smart Route Sync Script ────────────────────────────────────────
1163
+ // Background loop inside 9Router container every 30s.
1164
+ // Queries /api/providers → filters connected+enabled → updates smart-route combo.
1165
+ const syncScript = `const fs=require('fs');const ROUTER='http://localhost:20128';const INTERVAL=30000;const p='/root/.9router/db.json';
1166
+ const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],'claude-code':['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],cline:['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],iflow:['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],qwen:['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],kiro:['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],ollama:['ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],glm:['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],minimax:['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],kimi:['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],deepseek:['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],xai:['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],mistral:['mistral/mistral-large-latest','mistral/codestral-latest'],groq:['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],cerebras:['cerebras/gpt-oss-120b'],alicode:['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],openai:['openai/gpt-4o','openai/gpt-4.1'],anthropic:['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],gemini:['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
1167
+ console.log('[sync-combo] 9Router sync loop started...');
1168
+ const sync = async () => {
1169
+ try {
1170
+ const res = await fetch(ROUTER + '/api/providers');
1171
+ const d = await res.json();
1172
+ const a = (d.connections || []).filter(c=>(c.isActive !== false && !c.disabled) && (c.isActive || c.connected > 0 || c.tokens?.length > 0)).map(c=>c.provider);
1173
+ if (!a.length) return;
1174
+
1175
+ const PREF = ['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];
1176
+ a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
1177
+
1178
+ const m = a.flatMap(p => PM[p] || []);
1179
+ if (!m.length) return;
1180
+ let db = {};
1181
+ try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
1182
+ if (!db.combos) db.combos = [];
1183
+
1184
+ const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
1185
+ const i = db.combos.findIndex(x => x.id === 'smart-route');
1186
+ if (i >= 0) {
1187
+ if (JSON.stringify(db.combos[i].models) !== JSON.stringify(c.models)) {
1188
+ db.combos[i] = c;
1189
+ fs.writeFileSync(p, JSON.stringify(db, null, 2));
1190
+ console.log('[sync-combo] Updated smart-route: ' + c.models.length + ' models');
1191
+ }
1192
+ } else {
1193
+ db.combos.push(c);
1194
+ fs.writeFileSync(p, JSON.stringify(db, null, 2));
1195
+ console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models');
1196
+ }
1197
+ } catch (e) { }
1198
+ };
1199
+ sync();
1200
+ setInterval(sync, INTERVAL);`;
1201
+
1184
1202
  let compose;
1185
1203
  if (is9Router) {
1186
1204
  compose = `services:
@@ -1196,14 +1214,22 @@ ${extraHostsBlock}
1196
1214
  volumes:
1197
1215
  - ../../.openclaw:/root/.openclaw
1198
1216
  ports:
1199
- - "18789:18789"
1217
+ - "38789:38789"
1200
1218
 
1201
1219
  9router:
1202
1220
  image: node:22-slim
1203
1221
  container_name: 9router
1204
1222
  restart: always
1205
- entrypoint: >
1206
- /bin/sh -c "npm install -g 9router && [ ! -f /root/.9router/db.json ] && echo '{\\"combos\\":[{\\"id\\":\\"smart-route\\",\\"name\\":\\"smart-route\\",\\"alias\\":\\"smart-route\\",\\"models\\":[\\"cc/claude-opus-4-6\\",\\"cc/claude-sonnet-4-6\\",\\"cc/claude-opus-4-5-20251101\\",\\"cc/claude-sonnet-4-5-20250929\\",\\"cc/claude-haiku-4-5-20251001\\",\\"cx/gpt-5.4\\",\\"cx/gpt-5.3-codex\\",\\"cx/gpt-5.3-codex-xhigh\\",\\"cx/gpt-5.3-codex-high\\",\\"cx/gpt-5.3-codex-low\\",\\"cx/gpt-5.3-codex-none\\",\\"cx/gpt-5.3-codex-spark\\",\\"cx/gpt-5.1-codex-mini\\",\\"cx/gpt-5.1-codex-mini-high\\",\\"cx/gpt-5.2-codex\\",\\"cx/gpt-5.2\\",\\"cx/gpt-5.1-codex-max\\",\\"cx/gpt-5.1-codex\\",\\"cx/gpt-5.1\\",\\"cx/gpt-5-codex\\",\\"cx/gpt-5-codex-mini\\",\\"gc/gemini-3-flash-preview\\",\\"gc/gemini-3-pro-preview\\",\\"gh/gpt-5.4\\",\\"gh/gpt-5.3-codex\\",\\"gh/gpt-5.2-codex\\",\\"gh/gpt-5.2\\",\\"gh/gpt-5.1-codex-max\\",\\"gh/gpt-5.1-codex\\",\\"gh/gpt-5.1-codex-mini\\",\\"gh/gpt-5.1\\",\\"gh/gpt-5\\",\\"gh/gpt-5-mini\\",\\"gh/gpt-5-codex\\",\\"gh/gpt-4.1\\",\\"gh/gpt-4o\\",\\"gh/gpt-4o-mini\\",\\"gh/gpt-4\\",\\"gh/gpt-3.5-turbo\\",\\"gh/claude-opus-4.6\\",\\"gh/claude-sonnet-4.6\\",\\"gh/claude-sonnet-4.5\\",\\"gh/claude-opus-4.5\\",\\"gh/claude-opus-4.1\\",\\"gh/claude-sonnet-4\\",\\"gh/claude-haiku-4.5\\",\\"gh/gemini-3-pro-preview\\",\\"gh/gemini-3-flash-preview\\",\\"gh/gemini-2.5-pro\\",\\"gh/grok-code-fast-1\\",\\"gh/oswe-vscode-prime\\",\\"cu/default\\",\\"cu/claude-4.6-opus-max\\",\\"cu/claude-4.6-sonnet-medium-thinking\\",\\"cu/claude-4.5-opus-high-thinking\\",\\"cu/claude-4.5-opus-high\\",\\"cu/claude-4.5-sonnet-thinking\\",\\"cu/claude-4.5-sonnet\\",\\"cu/claude-4.5-haiku\\",\\"cu/claude-4.5-opus\\",\\"cu/gpt-5.3-codex\\",\\"cu/gpt-5.2-codex\\",\\"cu/gpt-5.2\\",\\"cu/kimi-k2.5\\",\\"cu/gemini-3-flash-preview\\",\\"kc/anthropic/claude-sonnet-4-20250514\\",\\"kc/anthropic/claude-opus-4-20250514\\",\\"kc/google/gemini-2.5-pro\\",\\"kc/google/gemini-2.5-flash\\",\\"kc/openai/gpt-4.1\\",\\"kc/openai/o3\\",\\"kc/deepseek/deepseek-chat\\",\\"kc/deepseek/deepseek-reasoner\\",\\"cl/anthropic/claude-sonnet-4.6\\",\\"cl/anthropic/claude-opus-4.6\\",\\"cl/openai/gpt-5.3-codex\\",\\"cl/openai/gpt-5.4\\",\\"cl/google/gemini-3.1-pro-preview\\",\\"cl/google/gemini-3.1-flash-lite-preview\\",\\"cl/kwaipilot/kat-coder-pro\\",\\"if/qwen3-coder-plus\\",\\"if/kimi-k2\\",\\"if/glm-4.7\\",\\"if/deepseek-r1\\",\\"if/deepseek-v3.2\\",\\"if/deepseek-v3.1\\",\\"if/deepseek-v3\\",\\"if/qwen3-max\\",\\"if/qwen3-235b\\",\\"if/qwen3-32b\\",\\"if/iflow-rome-30ba3b\\",\\"qw/qwen3-coder-plus\\",\\"qw/qwen3-coder-flash\\",\\"qw/vision-model\\",\\"qw/coder-model\\",\\"kr/claude-sonnet-4.5\\",\\"kr/claude-haiku-4.5\\",\\"kr/deepseek-3.2\\",\\"kr/deepseek-3.1\\",\\"kr/qwen3-coder-next\\",\\"kmc/kimi-k2.5\\",\\"kmc/kimi-k2.5-thinking\\",\\"kmc/kimi-latest\\",\\"glm/glm-5.1\\",\\"glm/glm-5\\",\\"glm/glm-4.7\\",\\"minimax/MiniMax-M2.7\\",\\"minimax/MiniMax-M2.5\\",\\"minimax/MiniMax-M2.1\\",\\"kimi/kimi-k2.5\\",\\"kimi/kimi-k2.5-thinking\\",\\"kimi/kimi-latest\\",\\"deepseek/deepseek-chat\\",\\"deepseek/deepseek-reasoner\\",\\"xai/grok-4\\",\\"xai/grok-4-fast-reasoning\\",\\"xai/grok-code-fast-1\\",\\"mistral/mistral-large-latest\\",\\"mistral/codestral-latest\\",\\"groq/llama-3.3-70b-versatile\\",\\"groq/openai/gpt-oss-120b\\",\\"cerebras/gpt-oss-120b\\",\\"alicode/qwen3.5-plus\\",\\"alicode/qwen3-coder-plus\\"]}]}' > /root/.9router/db.json; 9router"
1223
+ entrypoint:
1224
+ - /bin/sh
1225
+ - -c
1226
+ - |
1227
+ npm install -g 9router
1228
+ cat << 'CLAWEOF' > /tmp/sync.js
1229
+ ${syncScript.replace(/\$/g, '$$$$').replace(/\n/g, '\n ')}
1230
+ CLAWEOF
1231
+ node /tmp/sync.js > /tmp/sync.log 2>&1 &
1232
+ exec 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update
1207
1233
  environment:
1208
1234
  - PORT=20128
1209
1235
  - HOSTNAME=0.0.0.0
@@ -1227,7 +1253,7 @@ ${extraHostsBlock}
1227
1253
  volumes:
1228
1254
  - ../../.openclaw:/root/.openclaw
1229
1255
  ports:
1230
- - "18789:18789"`;
1256
+ - "38789:38789"`;
1231
1257
  }
1232
1258
 
1233
1259
  setOutput('out-compose', compose);
@@ -1767,8 +1793,12 @@ Write-Host " 🦞 OpenClaw Auto Setup" -ForegroundColor Cyan
1767
1793
  Write-Host " Project: $projectDir" -ForegroundColor White
1768
1794
  Write-Host ""
1769
1795
 
1796
+ try {
1770
1797
  # [1/4] Create directories
1771
1798
  Write-Host "[1/4] ${isVi ? 'Tạo thư mục...' : 'Creating directories...'}" -ForegroundColor Yellow
1799
+
1800
+ # Ensure root directory exists first
1801
+ New-Item -ItemType Directory -Force -Path "$projectDir" | Out-Null
1772
1802
  `;
1773
1803
 
1774
1804
  // Collect unique directories
@@ -1788,8 +1818,10 @@ Write-Host "[1/4] ${isVi ? 'Tạo thư mục...' : 'Creating directories...'}" -
1788
1818
 
1789
1819
  Object.entries(files).forEach(([path, content]) => {
1790
1820
  const winPath = path.replace(/\//g, '\\');
1791
- // Escape content for PowerShell here-string (only issue: content containing "'@" on own line)
1792
- const safeContent = content.replace(/\r\n/g, '\n');
1821
+ // Fix: escape any "'@" at start of line would prematurely terminate PowerShell here-string
1822
+ const safeContent = content
1823
+ .replace(/\r\n/g, '\n')
1824
+ .replace(/^'@/mg, "'`@"); // escape with backtick so PS here-string doesn't terminate early
1793
1825
  ps += `\n[IO.File]::WriteAllText("$projectDir\\${winPath}", @'\n${safeContent}\n'@, $utf8)\n`;
1794
1826
  });
1795
1827
 
@@ -1822,22 +1854,37 @@ Write-Host " 🎉 ${isVi ? 'Setup hoàn tất!' : 'Setup complete!'}" -Foregrou
1822
1854
  // Post-setup notes
1823
1855
  const is9Router = state.config.provider === '9router';
1824
1856
  if (is9Router) {
1825
- ps += `Write-Host " ${isVi ? 'Mở http://localhost:20128/dashboard để login OAuth' : 'Open http://localhost:20128/dashboard to login OAuth'}" -ForegroundColor White\n`;
1857
+ ps += `Write-Host " ${isVi ? 'Mở http://localhost:30128/dashboard để login OAuth' : 'Open http://localhost:30128/dashboard to login OAuth'}" -ForegroundColor White\n`;
1826
1858
  }
1827
1859
  if (state.channel === 'zalo-personal') {
1828
1860
  ps += `Write-Host " ${isVi ? 'Chạy: docker exec -it openclaw-bot openclaw onboard (quét QR)' : 'Run: docker exec -it openclaw-bot openclaw onboard (scan QR)'}" -ForegroundColor White\n`;
1829
1861
  }
1830
1862
 
1831
1863
  ps += `Write-Host ""
1864
+ } catch {
1865
+ Write-Host ""
1866
+ Write-Host " ❌ LỖI / ERROR: $($_.Exception.Message)" -ForegroundColor Red
1867
+ Write-Host ""
1868
+ }
1832
1869
  Read-Host "${isVi ? 'Nhấn Enter để thoát' : 'Press Enter to exit'}"
1833
1870
  `;
1834
1871
 
1835
- // Wrap in polyglot .bat/.ps1
1836
- const bat = `<# : batch wrapper
1837
- @echo off & chcp 65001>nul
1838
- powershell -ExecutionPolicy Bypass -NoProfile -File "%~f0" %*
1872
+ // Wrap in a .bat that extracts the PS section to a temp .ps1 then runs it.
1873
+ // This avoids 2 issues:
1874
+ // 1. powershell -File refuses .bat extension (hard error, immediate exit)
1875
+ // 2. Zone.Identifier security block on downloaded files affects -File but not -Command
1876
+ // The extraction command uses NO pipes (CMD treats | as special inside ""), and uses
1877
+ // $env:OPENCLAW_SELF / $env:OPENCLAW_TMP to avoid CMD quote issues with paths.
1878
+ const bat = `@echo off
1879
+ chcp 65001>nul
1880
+ set "OPENCLAW_SELF=%~f0"
1881
+ set "OPENCLAW_TMP=%TEMP%\\openclaw_%RANDOM%.ps1"
1882
+ powershell -ep bypass -nop -c "$l=(Select-String -Path $env:OPENCLAW_SELF -Pattern '^:PS_BEGIN$').LineNumber;$a=[io.file]::ReadAllLines($env:OPENCLAW_SELF,[text.encoding]::UTF8);[io.file]::WriteAllText($env:OPENCLAW_TMP,($a[$l..($a.Length-1)] -join \\"\`n\\"),[text.encoding]::UTF8)"
1883
+ powershell -ep bypass -nop -File "%OPENCLAW_TMP%"
1884
+ if %errorlevel% neq 0 pause
1885
+ del "%OPENCLAW_TMP%" 2>nul
1839
1886
  exit /b
1840
- #>
1887
+ :PS_BEGIN
1841
1888
  ${ps}`;
1842
1889
 
1843
1890
  return bat;
@@ -1848,7 +1895,8 @@ ${ps}`;
1848
1895
  // Regenerate output first to ensure state._generatedFiles is current
1849
1896
  generateOutput();
1850
1897
  const content = generateAutoSetupBat();
1851
- const blob = new Blob([content], { type: 'application/bat' });
1898
+ const winContent = content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
1899
+ const blob = new Blob([winContent], { type: 'application/x-bat;charset=utf-8' });
1852
1900
  const url = URL.createObjectURL(blob);
1853
1901
  const a = document.createElement('a');
1854
1902
  a.href = url;