create-openclaw-bot 4.0.5 → 4.0.7

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/setup.js CHANGED
@@ -125,16 +125,176 @@
125
125
  envKey: null,
126
126
  envLabel: null,
127
127
  envLink: 'https://github.com/decolua/9router',
128
- envInstructionsVi: '9Router chạy cùng Docker — <strong>không cần API key</strong>. Sau khi <code>docker compose up</code>, mở <a href="http://localhost:20128/dashboard" target="_blank">localhost:20128/dashboard</a> → đăng nhập OAuth.<br><span style="color:var(--danger)">⚠️ <b>CẢNH BÁO:</b> TUYỆT ĐỐI KHÔNG dùng OAuth Provider Antigravity (nguy cơ bị ban Google Account vì lạm dụng AI Ultra vĩnh viễn).</span>', envInstructionsEn: '9Router runs with Docker — <strong>no API key needed</strong>. After <code>docker compose up</code>, open <a href="http://localhost:20128/dashboard" target="_blank">localhost:20128/dashboard</a> and OAuth login.<br><span style="color:var(--danger)">⚠️ <b>WARNING:</b> DO NOT use Antigravity as an OAuth Provider (high risk of permanent Google Account ban for AI Ultra abuse).</span>',
128
+ envInstructionsVi: '9Router chạy cùng Docker — <strong>không cần API key</strong>. Sau khi <code>docker compose up</code>, mở <a href="http://localhost:20128/dashboard" target="_blank">localhost:20128/dashboard</a> → đăng nhập OAuth.<br><span style="color:var(--danger)">⚠️ <b>CẢNH BÁO:</b> TUYỆT ĐỐI KHÔNG dùng Provider Antigravity (nguy cơ bị ban Google Account vĩnh viễn).</span>', envInstructionsEn: '9Router runs with Docker — <strong>no API key needed</strong>. After <code>docker compose up</code>, open <a href="http://localhost:20128/dashboard" target="_blank">localhost:20128/dashboard</a> and OAuth login.<br><span style="color:var(--danger)">⚠️ <b>WARNING:</b> DO NOT use Antigravity as an OAuth Provider (high risk of permanent Google Account ban).</span>',
129
129
  free: true,
130
130
  isProxy: true,
131
131
  models: [
132
- { id: '9router/smart-route', name: 'Smart Proxy (Auto Route)', descVi: 'Tự động luân chuyển vương bài mọi Provider', descEn: 'Smart auto-routing across top providers', badgeVi: '🌟 Khuyên dùng', badgeEn: '🌟 Recommended' },
133
- { id: '9router/cx/gpt-5.4', name: 'GPT 5.4 (Codex)', descVi: 'Sức mạnh code tối đa từ OpenAI Codex', descEn: 'Max coding power from OpenAI Codex', badge: '🤖 Codex' },
134
- { id: '9router/cc/claude-opus-4-6', name: 'Claude Opus 4.6 (Claude Code)', descVi: 'Thuần tuý Anthropic', descEn: 'Pure Anthropic engine', badge: '✨ Claude' },
135
- { id: '9router/cc/claude-sonnet-4-6', name: 'Claude Sonnet 4.6 (Claude Code)', descVi: 'Nhanh, thông minh', descEn: 'Fast & smart', badge: '✨ Claude' },
136
- { id: '9router/gh/gpt-5.4', name: 'GPT 5.4 (Copilot)', descVi: 'Cân bằng, tốc độ từ GitHub Copilot', descEn: 'Balanced & fast from GitHub Copilot', badge: '💻 Copilot' },
137
- { id: '9router/gh/claude-opus-4.6', name: 'Claude Opus 4.6 (Copilot)', descVi: 'Suy luận mạnh nhất từ Copilot', descEn: 'Strongest reasoning from Copilot', badge: '💻 Copilot' },
132
+ // ── Smart Route (Combo) ──
133
+ { id: '9router/smart-route', name: 'Smart Proxy (Auto Route)', descVi: 'Tự động luân chuyển FREE models không tốn xu', descEn: 'Auto-routing across FREE providers zero cost', badgeVi: '🌟 Khuyên dùng', badgeEn: '🌟 Recommended' },
134
+
135
+ // ── OAuth: Claude Code (cc/) ──
136
+ { id: '9router/cc/claude-opus-4-6', name: 'Claude Opus 4.6', descVi: 'Mạnh nhất Anthropic', descEn: 'Strongest Anthropic', badge: ' Claude Code' },
137
+ { id: '9router/cc/claude-sonnet-4-6', name: 'Claude Sonnet 4.6', descVi: 'Nhanh, thông minh', descEn: 'Fast & smart', badge: ' Claude Code' },
138
+ { id: '9router/cc/claude-opus-4-5-20251101', name: 'Claude 4.5 Opus', descVi: 'Phiên bản 4.5 ổn định', descEn: 'Stable 4.5 version', badge: '✨ Claude Code' },
139
+ { id: '9router/cc/claude-sonnet-4-5-20250929', name: 'Claude 4.5 Sonnet', descVi: 'Cân bằng tốc độ-chất lượng', descEn: 'Speed-quality balance', badge: '✨ Claude Code' },
140
+ { id: '9router/cc/claude-haiku-4-5-20251001', name: 'Claude 4.5 Haiku', descVi: 'Siêu nhanh, nhẹ', descEn: 'Ultra fast & light', badge: '✨ Claude Code' },
141
+
142
+ // ── OAuth: OpenAI Codex (cx/) ──
143
+ { id: '9router/cx/gpt-5.4', name: 'GPT 5.4', descVi: 'Flagship mới nhất OpenAI', descEn: 'Latest OpenAI flagship', badge: '🤖 Codex' },
144
+ { id: '9router/cx/gpt-5.3-codex', name: 'GPT 5.3 Codex', descVi: 'Tối ưu code generation', descEn: 'Code generation optimized', badge: '🤖 Codex' },
145
+ { id: '9router/cx/gpt-5.3-codex-xhigh', name: 'GPT 5.3 Codex (xHigh)', descVi: 'Suy luận tối đa', descEn: 'Maximum reasoning', badge: '🤖 Codex' },
146
+ { id: '9router/cx/gpt-5.3-codex-high', name: 'GPT 5.3 Codex (High)', descVi: 'Suy luận cao', descEn: 'High reasoning', badge: '🤖 Codex' },
147
+ { id: '9router/cx/gpt-5.3-codex-low', name: 'GPT 5.3 Codex (Low)', descVi: 'Nhanh, tiết kiệm', descEn: 'Fast & economical', badge: '🤖 Codex' },
148
+ { id: '9router/cx/gpt-5.3-codex-none', name: 'GPT 5.3 Codex (None)', descVi: 'Không thinking, nhanh nhất', descEn: 'No thinking, fastest', badge: '🤖 Codex' },
149
+ { id: '9router/cx/gpt-5.3-codex-spark', name: 'GPT 5.3 Codex Spark', descVi: 'Phiên bản Spark', descEn: 'Spark edition', badge: '🤖 Codex' },
150
+ { id: '9router/cx/gpt-5.1-codex-mini', name: 'GPT 5.1 Codex Mini', descVi: 'Mini, siêu nhanh', descEn: 'Mini, ultra fast', badge: '🤖 Codex' },
151
+ { id: '9router/cx/gpt-5.1-codex-mini-high', name: 'GPT 5.1 Codex Mini (High)', descVi: 'Mini + suy luận cao', descEn: 'Mini + high reasoning', badge: '🤖 Codex' },
152
+ { id: '9router/cx/gpt-5.2-codex', name: 'GPT 5.2 Codex', descVi: 'Ổn định, code tốt', descEn: 'Stable, great coding', badge: '🤖 Codex' },
153
+ { id: '9router/cx/gpt-5.2', name: 'GPT 5.2', descVi: 'Đa năng', descEn: 'Versatile', badge: '🤖 Codex' },
154
+ { id: '9router/cx/gpt-5.1-codex-max', name: 'GPT 5.1 Codex Max', descVi: 'Sức mạnh tối đa 5.1', descEn: 'Max power 5.1', badge: '🤖 Codex' },
155
+ { id: '9router/cx/gpt-5.1-codex', name: 'GPT 5.1 Codex', descVi: 'Codex 5.1', descEn: 'Codex 5.1', badge: '🤖 Codex' },
156
+ { id: '9router/cx/gpt-5.1', name: 'GPT 5.1', descVi: 'GPT 5.1 base', descEn: 'GPT 5.1 base', badge: '🤖 Codex' },
157
+ { id: '9router/cx/gpt-5-codex', name: 'GPT 5 Codex', descVi: 'GPT 5 Codex', descEn: 'GPT 5 Codex', badge: '🤖 Codex' },
158
+ { id: '9router/cx/gpt-5-codex-mini', name: 'GPT 5 Codex Mini', descVi: 'GPT 5 Mini', descEn: 'GPT 5 Mini', badge: '🤖 Codex' },
159
+
160
+ // ── OAuth: Gemini CLI (gc/) — FREE 180K/month ──
161
+ { id: '9router/gc/gemini-3-flash-preview', name: 'Gemini 3 Flash Preview', descVi: 'Google miễn phí 180K/tháng', descEn: 'Google free 180K/month', badge: '🆓 Gemini CLI' },
162
+ { id: '9router/gc/gemini-3-pro-preview', name: 'Gemini 3 Pro Preview', descVi: 'Gemini Pro miễn phí', descEn: 'Gemini Pro free', badge: '🆓 Gemini CLI' },
163
+
164
+ // ── OAuth: GitHub Copilot (gh/) ──
165
+ { id: '9router/gh/gpt-5.4', name: 'GPT 5.4 (Copilot)', descVi: 'Flagship qua Copilot', descEn: 'Flagship via Copilot', badge: '💻 Copilot' },
166
+ { id: '9router/gh/gpt-5.3-codex', name: 'GPT 5.3 Codex (Copilot)', descVi: 'Codex qua Copilot', descEn: 'Codex via Copilot', badge: '💻 Copilot' },
167
+ { id: '9router/gh/gpt-5.2-codex', name: 'GPT 5.2 Codex (Copilot)', descVi: 'GPT 5.2 Codex', descEn: 'GPT 5.2 Codex', badge: '💻 Copilot' },
168
+ { id: '9router/gh/gpt-5.2', name: 'GPT 5.2 (Copilot)', descVi: 'GPT 5.2', descEn: 'GPT 5.2', badge: '💻 Copilot' },
169
+ { id: '9router/gh/gpt-5.1-codex-max', name: 'GPT 5.1 Codex Max (Copilot)', descVi: 'Max thinking', descEn: 'Max thinking', badge: '💻 Copilot' },
170
+ { id: '9router/gh/gpt-5.1-codex', name: 'GPT 5.1 Codex (Copilot)', descVi: 'Codex 5.1', descEn: 'Codex 5.1', badge: '💻 Copilot' },
171
+ { id: '9router/gh/gpt-5.1-codex-mini', name: 'GPT 5.1 Codex Mini (Copilot)', descVi: 'Mini 5.1', descEn: 'Mini 5.1', badge: '💻 Copilot' },
172
+ { id: '9router/gh/gpt-5.1', name: 'GPT 5.1 (Copilot)', descVi: 'GPT 5.1', descEn: 'GPT 5.1', badge: '💻 Copilot' },
173
+ { id: '9router/gh/gpt-5', name: 'GPT 5 (Copilot)', descVi: 'GPT 5', descEn: 'GPT 5', badge: '💻 Copilot' },
174
+ { id: '9router/gh/gpt-5-mini', name: 'GPT 5 Mini (Copilot)', descVi: 'GPT 5 Mini', descEn: 'GPT 5 Mini', badge: '💻 Copilot' },
175
+ { id: '9router/gh/gpt-5-codex', name: 'GPT 5 Codex (Copilot)', descVi: 'GPT 5 Codex', descEn: 'GPT 5 Codex', badge: '💻 Copilot' },
176
+ { id: '9router/gh/gpt-4.1', name: 'GPT 4.1 (Copilot)', descVi: 'GPT 4.1', descEn: 'GPT 4.1', badge: '💻 Copilot' },
177
+ { id: '9router/gh/gpt-4o', name: 'GPT 4o (Copilot)', descVi: 'GPT 4o', descEn: 'GPT 4o', badge: '💻 Copilot' },
178
+ { id: '9router/gh/gpt-4o-mini', name: 'GPT 4o Mini (Copilot)', descVi: 'GPT 4o Mini', descEn: 'GPT 4o Mini', badge: '💻 Copilot' },
179
+ { id: '9router/gh/gpt-4', name: 'GPT 4 (Copilot)', descVi: 'GPT 4', descEn: 'GPT 4', badge: '💻 Copilot' },
180
+ { id: '9router/gh/gpt-3.5-turbo', name: 'GPT 3.5 Turbo (Copilot)', descVi: 'GPT 3.5 Turbo', descEn: 'GPT 3.5 Turbo', badge: '💻 Copilot' },
181
+ { id: '9router/gh/claude-opus-4.6', name: 'Claude Opus 4.6 (Copilot)', descVi: 'Claude Opus qua Copilot', descEn: 'Claude Opus via Copilot', badge: '💻 Copilot' },
182
+ { id: '9router/gh/claude-sonnet-4.6', name: 'Claude Sonnet 4.6 (Copilot)', descVi: 'Claude Sonnet qua Copilot', descEn: 'Claude Sonnet via Copilot', badge: '💻 Copilot' },
183
+ { id: '9router/gh/claude-sonnet-4.5', name: 'Claude Sonnet 4.5 (Copilot)', descVi: 'Claude 4.5 Sonnet', descEn: 'Claude 4.5 Sonnet', badge: '💻 Copilot' },
184
+ { id: '9router/gh/claude-opus-4.5', name: 'Claude Opus 4.5 (Copilot)', descVi: 'Claude 4.5 Opus', descEn: 'Claude 4.5 Opus', badge: '💻 Copilot' },
185
+ { id: '9router/gh/claude-opus-4.1', name: 'Claude Opus 4.1 (Copilot)', descVi: 'Claude 4.1 Opus', descEn: 'Claude 4.1 Opus', badge: '💻 Copilot' },
186
+ { id: '9router/gh/claude-sonnet-4', name: 'Claude Sonnet 4 (Copilot)', descVi: 'Claude 4 Sonnet', descEn: 'Claude 4 Sonnet', badge: '💻 Copilot' },
187
+ { id: '9router/gh/claude-haiku-4.5', name: 'Claude Haiku 4.5 (Copilot)', descVi: 'Claude 4.5 Haiku', descEn: 'Claude 4.5 Haiku', badge: '💻 Copilot' },
188
+ { id: '9router/gh/gemini-3-pro-preview', name: 'Gemini 3 Pro (Copilot)', descVi: 'Gemini Pro qua Copilot', descEn: 'Gemini Pro via Copilot', badge: '💻 Copilot' },
189
+ { id: '9router/gh/gemini-3-flash-preview', name: 'Gemini 3 Flash (Copilot)', descVi: 'Gemini Flash qua Copilot', descEn: 'Gemini Flash via Copilot', badge: '💻 Copilot' },
190
+ { id: '9router/gh/gemini-2.5-pro', name: 'Gemini 2.5 Pro (Copilot)', descVi: 'Gemini 2.5 Pro', descEn: 'Gemini 2.5 Pro', badge: '💻 Copilot' },
191
+ { id: '9router/gh/grok-code-fast-1', name: 'Grok Code Fast (Copilot)', descVi: 'xAI Grok qua Copilot', descEn: 'xAI Grok via Copilot', badge: '💻 Copilot' },
192
+ { id: '9router/gh/oswe-vscode-prime', name: 'Raptor Mini (Copilot)', descVi: 'Raptor Mini', descEn: 'Raptor Mini', badge: '💻 Copilot' },
193
+
194
+ // ── OAuth: Cursor IDE (cu/) ──
195
+ { id: '9router/cu/default', name: 'Cursor Auto', descVi: 'Server tự chọn model', descEn: 'Server picks model', badge: '🎯 Cursor' },
196
+ { id: '9router/cu/claude-4.6-opus-max', name: 'Claude 4.6 Opus Max (Cursor)', descVi: 'Opus Max, mạnh nhất', descEn: 'Opus Max, strongest', badge: '🎯 Cursor' },
197
+ { id: '9router/cu/claude-4.6-sonnet-medium-thinking', name: 'Claude 4.6 Sonnet Thinking (Cursor)', descVi: 'Sonnet + thinking', descEn: 'Sonnet + thinking', badge: '🎯 Cursor' },
198
+ { id: '9router/cu/claude-4.5-opus-high-thinking', name: 'Claude 4.5 Opus High Thinking (Cursor)', descVi: 'Opus 4.5 + thinking', descEn: 'Opus 4.5 + thinking', badge: '🎯 Cursor' },
199
+ { id: '9router/cu/claude-4.5-opus-high', name: 'Claude 4.5 Opus High (Cursor)', descVi: 'Opus 4.5 High', descEn: 'Opus 4.5 High', badge: '🎯 Cursor' },
200
+ { id: '9router/cu/claude-4.5-sonnet-thinking', name: 'Claude 4.5 Sonnet Thinking (Cursor)', descVi: 'Sonnet 4.5 + thinking', descEn: 'Sonnet 4.5 + thinking', badge: '🎯 Cursor' },
201
+ { id: '9router/cu/claude-4.5-sonnet', name: 'Claude 4.5 Sonnet (Cursor)', descVi: 'Sonnet 4.5', descEn: 'Sonnet 4.5', badge: '🎯 Cursor' },
202
+ { id: '9router/cu/claude-4.5-haiku', name: 'Claude 4.5 Haiku (Cursor)', descVi: 'Haiku 4.5', descEn: 'Haiku 4.5', badge: '🎯 Cursor' },
203
+ { id: '9router/cu/claude-4.5-opus', name: 'Claude 4.5 Opus (Cursor)', descVi: 'Opus 4.5', descEn: 'Opus 4.5', badge: '🎯 Cursor' },
204
+ { id: '9router/cu/gpt-5.3-codex', name: 'GPT 5.3 Codex (Cursor)', descVi: 'GPT 5.3 Codex', descEn: 'GPT 5.3 Codex', badge: '🎯 Cursor' },
205
+ { id: '9router/cu/gpt-5.2-codex', name: 'GPT 5.2 Codex (Cursor)', descVi: 'GPT 5.2 Codex', descEn: 'GPT 5.2 Codex', badge: '🎯 Cursor' },
206
+ { id: '9router/cu/gpt-5.2', name: 'GPT 5.2 (Cursor)', descVi: 'GPT 5.2', descEn: 'GPT 5.2', badge: '🎯 Cursor' },
207
+ { id: '9router/cu/kimi-k2.5', name: 'Kimi K2.5 (Cursor)', descVi: 'Kimi K2.5', descEn: 'Kimi K2.5', badge: '🎯 Cursor' },
208
+ { id: '9router/cu/gemini-3-flash-preview', name: 'Gemini 3 Flash (Cursor)', descVi: 'Gemini Flash', descEn: 'Gemini Flash', badge: '🎯 Cursor' },
209
+
210
+ // ── OAuth: KiloCode (kc/) ──
211
+ { id: '9router/kc/anthropic/claude-sonnet-4-20250514', name: 'Claude Sonnet 4 (KiloCode)', descVi: 'Claude Sonnet 4', descEn: 'Claude Sonnet 4', badge: '🔷 KiloCode' },
212
+ { id: '9router/kc/anthropic/claude-opus-4-20250514', name: 'Claude Opus 4 (KiloCode)', descVi: 'Claude Opus 4', descEn: 'Claude Opus 4', badge: '🔷 KiloCode' },
213
+ { id: '9router/kc/google/gemini-2.5-pro', name: 'Gemini 2.5 Pro (KiloCode)', descVi: 'Gemini 2.5 Pro', descEn: 'Gemini 2.5 Pro', badge: '🔷 KiloCode' },
214
+ { id: '9router/kc/google/gemini-2.5-flash', name: 'Gemini 2.5 Flash (KiloCode)', descVi: 'Gemini 2.5 Flash', descEn: 'Gemini 2.5 Flash', badge: '🔷 KiloCode' },
215
+ { id: '9router/kc/openai/gpt-4.1', name: 'GPT 4.1 (KiloCode)', descVi: 'GPT 4.1', descEn: 'GPT 4.1', badge: '🔷 KiloCode' },
216
+ { id: '9router/kc/openai/o3', name: 'O3 (KiloCode)', descVi: 'O3 Reasoning', descEn: 'O3 Reasoning', badge: '🔷 KiloCode' },
217
+ { id: '9router/kc/deepseek/deepseek-chat', name: 'DeepSeek Chat (KiloCode)', descVi: 'DeepSeek V3.2', descEn: 'DeepSeek V3.2', badge: '🔷 KiloCode' },
218
+ { id: '9router/kc/deepseek/deepseek-reasoner', name: 'DeepSeek Reasoner (KiloCode)', descVi: 'DeepSeek Reasoner', descEn: 'DeepSeek Reasoner', badge: '🔷 KiloCode' },
219
+
220
+ // ── OAuth: Cline (cl/) ──
221
+ { id: '9router/cl/anthropic/claude-sonnet-4.6', name: 'Claude Sonnet 4.6 (Cline)', descVi: 'Sonnet 4.6 qua Cline', descEn: 'Sonnet 4.6 via Cline', badge: '🔶 Cline' },
222
+ { id: '9router/cl/anthropic/claude-opus-4.6', name: 'Claude Opus 4.6 (Cline)', descVi: 'Opus 4.6 qua Cline', descEn: 'Opus 4.6 via Cline', badge: '🔶 Cline' },
223
+ { id: '9router/cl/openai/gpt-5.3-codex', name: 'GPT 5.3 Codex (Cline)', descVi: 'GPT 5.3 qua Cline', descEn: 'GPT 5.3 via Cline', badge: '🔶 Cline' },
224
+ { id: '9router/cl/openai/gpt-5.4', name: 'GPT 5.4 (Cline)', descVi: 'GPT 5.4 qua Cline', descEn: 'GPT 5.4 via Cline', badge: '🔶 Cline' },
225
+ { id: '9router/cl/google/gemini-3.1-pro-preview', name: 'Gemini 3.1 Pro (Cline)', descVi: 'Gemini 3.1 Pro', descEn: 'Gemini 3.1 Pro', badge: '🔶 Cline' },
226
+ { id: '9router/cl/google/gemini-3.1-flash-lite-preview', name: 'Gemini 3.1 Flash Lite (Cline)', descVi: 'Gemini 3.1 Flash Lite', descEn: 'Gemini 3.1 Flash Lite', badge: '🔶 Cline' },
227
+ { id: '9router/cl/kwaipilot/kat-coder-pro', name: 'KAT Coder Pro (Cline)', descVi: 'KAT Coder Pro', descEn: 'KAT Coder Pro', badge: '🔶 Cline' },
228
+
229
+ // ── OAuth FREE: iFlow (if/) — Unlimited ──
230
+ { id: '9router/if/qwen3-coder-plus', name: 'Qwen3 Coder Plus (iFlow)', descVi: 'Miễn phí không giới hạn', descEn: 'Free unlimited', badge: '🆓 iFlow' },
231
+ { id: '9router/if/kimi-k2', name: 'Kimi K2 (iFlow)', descVi: 'Kimi K2 miễn phí', descEn: 'Kimi K2 free', badge: '🆓 iFlow' },
232
+ { id: '9router/if/glm-4.7', name: 'GLM 4.7 (iFlow)', descVi: 'GLM miễn phí', descEn: 'GLM free', badge: '🆓 iFlow' },
233
+ { id: '9router/if/deepseek-r1', name: 'DeepSeek R1 (iFlow)', descVi: 'DeepSeek R1 miễn phí', descEn: 'DeepSeek R1 free', badge: '🆓 iFlow' },
234
+ { id: '9router/if/deepseek-v3.2', name: 'DeepSeek V3.2 (iFlow)', descVi: 'V3.2 miễn phí', descEn: 'V3.2 free', badge: '🆓 iFlow' },
235
+ { id: '9router/if/deepseek-v3.1', name: 'DeepSeek V3.1 (iFlow)', descVi: 'V3.1 miễn phí', descEn: 'V3.1 free', badge: '🆓 iFlow' },
236
+ { id: '9router/if/deepseek-v3', name: 'DeepSeek V3 (iFlow)', descVi: 'V3 miễn phí', descEn: 'V3 free', badge: '🆓 iFlow' },
237
+ { id: '9router/if/qwen3-max', name: 'Qwen3 Max (iFlow)', descVi: 'Qwen3 Max free', descEn: 'Qwen3 Max free', badge: '🆓 iFlow' },
238
+ { id: '9router/if/qwen3-235b', name: 'Qwen3 235B (iFlow)', descVi: '235B params free', descEn: '235B params free', badge: '🆓 iFlow' },
239
+ { id: '9router/if/qwen3-32b', name: 'Qwen3 32B (iFlow)', descVi: 'Qwen3 32B free', descEn: 'Qwen3 32B free', badge: '🆓 iFlow' },
240
+ { id: '9router/if/iflow-rome-30ba3b', name: 'iFlow ROME (iFlow)', descVi: 'Agentic model', descEn: 'Agentic model', badge: '🆓 iFlow' },
241
+
242
+ // ── OAuth FREE: Qwen Code (qw/) — Unlimited ──
243
+ { id: '9router/qw/qwen3-coder-plus', name: 'Qwen3 Coder Plus', descVi: 'Alibaba miễn phí', descEn: 'Alibaba free', badge: '🆓 Qwen' },
244
+ { id: '9router/qw/qwen3-coder-flash', name: 'Qwen3 Coder Flash', descVi: 'Nhanh, miễn phí', descEn: 'Fast, free', badge: '🆓 Qwen' },
245
+ { id: '9router/qw/vision-model', name: 'Qwen3 Vision', descVi: 'Vision model', descEn: 'Vision model', badge: '🆓 Qwen' },
246
+ { id: '9router/qw/coder-model', name: 'Qwen3.5 Coder', descVi: 'Qwen3.5 Coder', descEn: 'Qwen3.5 Coder', badge: '🆓 Qwen' },
247
+
248
+ // ── OAuth FREE: Kiro (kr/) — Unlimited ──
249
+ { id: '9router/kr/claude-sonnet-4.5', name: 'Claude Sonnet 4.5 (Kiro)', descVi: 'Claude miễn phí qua AWS', descEn: 'Free Claude via AWS', badge: '🆓 Kiro' },
250
+ { id: '9router/kr/claude-haiku-4.5', name: 'Claude Haiku 4.5 (Kiro)', descVi: 'Haiku miễn phí', descEn: 'Haiku free', badge: '🆓 Kiro' },
251
+ { id: '9router/kr/deepseek-3.2', name: 'DeepSeek 3.2 (Kiro)', descVi: 'DeepSeek via Kiro', descEn: 'DeepSeek via Kiro', badge: '🆓 Kiro' },
252
+ { id: '9router/kr/deepseek-3.1', name: 'DeepSeek 3.1 (Kiro)', descVi: 'DeepSeek via Kiro', descEn: 'DeepSeek via Kiro', badge: '🆓 Kiro' },
253
+ { id: '9router/kr/qwen3-coder-next', name: 'Qwen3 Coder Next (Kiro)', descVi: 'Qwen Next via Kiro', descEn: 'Qwen Next via Kiro', badge: '🆓 Kiro' },
254
+
255
+ // ── OAuth FREE: Kimi Coding (kmc/) ──
256
+ { id: '9router/kmc/kimi-k2.5', name: 'Kimi K2.5 (Kimi Coding)', descVi: 'K2.5 miễn phí', descEn: 'K2.5 free', badge: '🆓 Kimi' },
257
+ { id: '9router/kmc/kimi-k2.5-thinking', name: 'Kimi K2.5 Thinking', descVi: 'K2.5 + suy luận', descEn: 'K2.5 + thinking', badge: '🆓 Kimi' },
258
+ { id: '9router/kmc/kimi-latest', name: 'Kimi Latest', descVi: 'Phiên bản mới nhất', descEn: 'Latest version', badge: '🆓 Kimi' },
259
+
260
+ // ── API Key: GLM (glm/) — Cheap ──
261
+ { id: '9router/glm/glm-5.1', name: 'GLM 5.1', descVi: 'Zhipu AI mới nhất', descEn: 'Zhipu AI latest', badge: '💰 GLM' },
262
+ { id: '9router/glm/glm-5', name: 'GLM 5', descVi: 'GLM 5', descEn: 'GLM 5', badge: '💰 GLM' },
263
+ { id: '9router/glm/glm-4.7', name: 'GLM 4.7', descVi: '$0.6/1M tokens', descEn: '$0.6/1M tokens', badge: '💰 GLM' },
264
+
265
+ // ── API Key: MiniMax (minimax/) — Cheap ──
266
+ { id: '9router/minimax/MiniMax-M2.7', name: 'MiniMax M2.7', descVi: 'Mới nhất MiniMax', descEn: 'Latest MiniMax', badge: '💰 MiniMax' },
267
+ { id: '9router/minimax/MiniMax-M2.5', name: 'MiniMax M2.5', descVi: 'MiniMax M2.5', descEn: 'MiniMax M2.5', badge: '💰 MiniMax' },
268
+ { id: '9router/minimax/MiniMax-M2.1', name: 'MiniMax M2.1', descVi: '$0.20/1M tokens', descEn: '$0.20/1M tokens', badge: '💰 MiniMax' },
269
+
270
+ // ── API Key: Kimi (kimi/) ──
271
+ { id: '9router/kimi/kimi-k2.5', name: 'Kimi K2.5', descVi: 'Kimi K2.5 API', descEn: 'Kimi K2.5 API', badge: '💰 Kimi' },
272
+ { id: '9router/kimi/kimi-k2.5-thinking', name: 'Kimi K2.5 Thinking', descVi: 'K2.5 + suy luận', descEn: 'K2.5 + thinking', badge: '💰 Kimi' },
273
+ { id: '9router/kimi/kimi-latest', name: 'Kimi Latest', descVi: 'Phiên bản mới nhất', descEn: 'Latest version', badge: '💰 Kimi' },
274
+
275
+ // ── API Key: DeepSeek (deepseek/) ──
276
+ { id: '9router/deepseek/deepseek-chat', name: 'DeepSeek V3.2 Chat', descVi: 'DeepSeek Chat', descEn: 'DeepSeek Chat', badge: '💰 DeepSeek' },
277
+ { id: '9router/deepseek/deepseek-reasoner', name: 'DeepSeek V3.2 Reasoner', descVi: 'DeepSeek Reasoner', descEn: 'DeepSeek Reasoner', badge: '💰 DeepSeek' },
278
+
279
+ // ── API Key: xAI (xai/) ──
280
+ { id: '9router/xai/grok-4', name: 'Grok 4', descVi: 'xAI flagship', descEn: 'xAI flagship', badge: '💰 xAI' },
281
+ { id: '9router/xai/grok-4-fast-reasoning', name: 'Grok 4 Fast Reasoning', descVi: 'Suy luận nhanh', descEn: 'Fast reasoning', badge: '💰 xAI' },
282
+ { id: '9router/xai/grok-code-fast-1', name: 'Grok Code Fast', descVi: 'Code nhanh', descEn: 'Fast coding', badge: '💰 xAI' },
283
+
284
+ // ── API Key: Mistral (mistral/) ──
285
+ { id: '9router/mistral/mistral-large-latest', name: 'Mistral Large 3', descVi: 'Mistral flagship', descEn: 'Mistral flagship', badge: '💰 Mistral' },
286
+ { id: '9router/mistral/codestral-latest', name: 'Codestral', descVi: 'Code-optimized', descEn: 'Code-optimized', badge: '💰 Mistral' },
287
+
288
+ // ── API Key: Groq (groq/) ──
289
+ { id: '9router/groq/llama-3.3-70b-versatile', name: 'Llama 3.3 70B (Groq)', descVi: 'Siêu nhanh qua Groq', descEn: 'Ultra fast via Groq', badge: '⚡ Groq' },
290
+ { id: '9router/groq/openai/gpt-oss-120b', name: 'GPT OSS 120B (Groq)', descVi: 'GPT OSS qua Groq', descEn: 'GPT OSS via Groq', badge: '⚡ Groq' },
291
+
292
+ // ── API Key: Cerebras (cerebras/) ──
293
+ { id: '9router/cerebras/gpt-oss-120b', name: 'GPT OSS 120B (Cerebras)', descVi: 'Siêu nhanh Cerebras', descEn: 'Ultra fast Cerebras', badge: '⚡ Cerebras' },
294
+
295
+ // ── API Key: AliCode (alicode/) ──
296
+ { id: '9router/alicode/qwen3.5-plus', name: 'Qwen3.5 Plus (AliCode)', descVi: 'Alibaba Cloud mới nhất', descEn: 'Alibaba Cloud latest', badge: '💰 AliCode' },
297
+ { id: '9router/alicode/qwen3-coder-plus', name: 'Qwen3 Coder Plus (AliCode)', descVi: 'Coder Plus', descEn: 'Coder Plus', badge: '💰 AliCode' },
138
298
  ],
139
299
  },
140
300
  };
@@ -739,123 +899,148 @@
739
899
  const provider = PROVIDERS[state.config.provider];
740
900
  if (!ch || !provider) return;
741
901
 
742
- const credContainer = document.getElementById('cred-steps');
743
- if (credContainer) {
744
- const steps = [];
745
-
746
- const lang = document.getElementById('cfg-language')?.value || 'vi';
747
-
748
- // Provider credential step
749
- let pInst = lang === 'vi' ? (provider.envInstructionsVi || provider.envInstructions) : (provider.envInstructionsEn || provider.envInstructions);
750
- if (provider.isProxy) {
751
- steps.push({ text: pInst });
752
- } else if (provider.isLocal) {
753
- steps.push({ text: pInst });
754
- } else {
755
- steps.push({ text: `${lang === 'vi' ? 'Lấy' : 'Get'} <strong>${provider.envLabel}</strong>: ${pInst}` });
756
- }
757
-
758
- // Channel-specific steps
759
- ch.credSteps.forEach((s) => steps.push({ text: lang === 'vi' ? (s.textVi || s.text) : (s.textEn || s.text) }));
760
-
761
- // Final step
762
- if (provider.isProxy) {
763
- steps.push({ text: lang === 'vi' ? 'Tạo file <code>docker/openclaw/.env</code> trong project — chỉ cần Bot Token (không cần AI API key!)' : 'Create <code>docker/openclaw/.env</code> in project — only Bot Token needed (no AI API keys!)' });
764
- } else {
765
- steps.push({ text: lang === 'vi' ? 'Tạo file <code>docker/openclaw/.env</code> trong project và paste tất cả key vào' : 'Create <code>docker/openclaw/.env</code> in project and paste all keys' });
766
- }
767
-
768
- credContainer.innerHTML = steps.map((s, i) => `
769
- <div class="cred-step">
770
- <span class="cred-step__number">${i + 1}</span>
771
- <span class="cred-step__text">${s.text}</span>
772
- </div>
773
- `).join('');
774
- }
775
-
776
- // Build .env (now handled by populateEnvContent called from generateOutput)
777
-
778
- // Zalo Personal warning
779
- const warningBox = document.getElementById('zalo-warning');
780
- if (warningBox) {
781
- warningBox.style.display = state.channel === 'zalo-personal' ? 'flex' : 'none';
782
- }
783
-
784
- // Render key input fields
902
+ // Render all 3 sections
785
903
  renderKeyInputs();
786
904
  }
787
905
 
788
906
 
789
907
  // ========== Render Key Input Fields (Step 3) ==========
790
908
  function renderKeyInputs() {
791
- const container = document.getElementById('key-inputs');
792
- if (!container) return;
793
-
794
909
  const ch = CHANNELS[state.channel];
795
910
  const provider = PROVIDERS[state.config.provider];
796
911
  if (!ch || !provider) return;
797
912
 
798
913
  const lang = document.getElementById('cfg-language')?.value || 'vi';
799
914
  const isVi = lang === 'vi';
800
- let html = '';
801
-
802
- // Channel token input
803
- if (state.channel === 'telegram') {
804
- html += `<div class="form-group" style="margin-bottom: 16px;">
805
- <label class="form-group__label" for="key-bot-token">🤖 Telegram Bot Token</label>
806
- <input type="text" class="form-input" id="key-bot-token" placeholder="VD: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
807
- <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>
808
- </div>`;
809
- } else if (state.channel === 'zalo-bot') {
810
- html += `<div class="form-group" style="margin-bottom: 16px;">
811
- <label class="form-group__label" for="key-bot-token">🔑 Zalo Bot Token</label>
812
- <input type="text" class="form-input" id="key-bot-token" placeholder="Zalo Bot Token" style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
813
- <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>
814
- </div>`;
815
- } else if (state.channel === 'zalo-personal') {
816
- html += `<div style="padding: 12px 16px; background: rgba(255,193,7,0.06); border: 1px solid rgba(255,193,7,0.2); border-radius: 8px; margin-bottom: 16px; font-size: 13px; color: var(--text-secondary);">
817
- ℹ️ ${isVi ? 'Zalo Personal không cần nhập key — bạn sẽ quét QR code sau khi Docker chạy.' : 'Zalo Personal needs no key — you will scan QR code after Docker starts.'}
818
- </div>`;
819
- }
820
915
 
821
- // Provider API key input
822
- if (!provider.isProxy && !provider.isLocal && provider.envKey) {
823
- html += `<div class="form-group" style="margin-bottom: 16px;">
824
- <label class="form-group__label" for="key-api-key">🔑 ${provider.envLabel}</label>
825
- <input type="text" class="form-input" id="key-api-key" placeholder="${provider.envKey}=..." style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
826
- <p class="form-group__hint">${isVi ? 'Lấy từ' : 'Get from'} <a href="${provider.envLink}" target="_blank">${provider.envLink.replace('https://', '')}</a></p>
827
- </div>`;
828
- } else if (provider.isProxy) {
829
- html += `<div style="padding: 12px 16px; background: rgba(16,185,129,0.06); border: 1px solid rgba(16,185,129,0.2); border-radius: 8px; margin-bottom: 16px; font-size: 13px; color: var(--text-secondary);">
830
- ✅ ${isVi ? '9Router không cần API key — sau khi Docker chạy, mở dashboard để login OAuth.' : '9Router needs no API key — after Docker starts, open dashboard to login OAuth.'}
831
- </div>`;
832
- } else if (provider.isLocal) {
833
- html += `<div style="padding: 12px 16px; background: rgba(139,92,246,0.06); border: 1px solid rgba(139,92,246,0.2); border-radius: 8px; margin-bottom: 16px; font-size: 13px; color: var(--text-secondary);">
834
- 🏠 ${isVi ? 'Ollama chạy local — đảm bảo <code>ollama serve</code> đang chạy trên máy.' : 'Ollama runs locally — make sure <code>ollama serve</code> is running.'}
835
- </div>`;
916
+ // ─── Section 1: AI Provider ───
917
+ const providerEl = document.getElementById('key-section-provider');
918
+ if (providerEl) {
919
+ let pHtml = '';
920
+ const providerName = provider.isProxy ? '9Router (Proxy)' : (provider.isLocal ? 'Ollama (Local)' : provider.name);
921
+ const providerIcon = provider.isProxy ? '🔀' : (provider.isLocal ? '🏠' : '🤖');
922
+
923
+ pHtml += `<div style="padding: 16px 20px; border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; background: rgba(255,255,255,0.02);">`;
924
+ pHtml += `<h3 style="margin: 0 0 12px; font-size: 15px; font-weight: 700; color: var(--text-primary);">${providerIcon} ${isVi ? 'AI Provider' : 'AI Provider'} — ${providerName}</h3>`;
925
+
926
+ if (provider.isProxy) {
927
+ // 9Router: simple message + toggle switch
928
+ pHtml += `<p style="font-size: 13px; color: var(--text-secondary); margin: 0 0 14px;">
929
+ ${isVi
930
+ ? '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.'
931
+ : '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.'}
932
+ </p>`;
933
+ pHtml += `<div style="padding: 12px 16px; border-radius: 10px; background: rgba(139,92,246,0.04); border: 1px solid rgba(139,92,246,0.15);">
934
+ <div style="display: flex; align-items: center; justify-content: space-between; gap: 12px;">
935
+ <span style="font-size: 13px; font-weight: 600; color: var(--text-primary);">🔐 ${isVi ? 'Bảo mật 9Router' : 'Secure 9Router'}</span>
936
+ <label class="toggle-switch">
937
+ <input type="checkbox" id="key-9router-secure" onchange="window.__toggle9RouterSecurity(this.checked)">
938
+ <span class="toggle-slider"></span>
939
+ </label>
940
+ </div>
941
+ <p style="font-size: 11px; color: var(--text-muted); margin: 6px 0 0;">
942
+ ${isVi ? 'Khuyên dùng khi chạy trên VPS/mạng chung — tự tạo API Key để bảo vệ proxy.' : 'Recommended for VPS/shared networks — auto-generates API Key to protect your proxy.'}
943
+ </p>
944
+ <div id="9router-key-display" style="display: none; margin-top: 10px;">
945
+ <div style="display: flex; gap: 8px; align-items: center;">
946
+ <input type="text" class="form-input" id="key-9router-apikey" readonly style="font-family: monospace; font-size: 12px; background: rgba(0,0,0,0.2); flex: 1; padding: 8px 12px;" value="">
947
+ <button type="button" onclick="navigator.clipboard.writeText(document.getElementById('key-9router-apikey').value);this.textContent='✅';setTimeout(()=>this.textContent='📋',1500)" style="padding: 6px 10px; border-radius: 8px; border: 1px solid rgba(139,92,246,0.3); background: rgba(139,92,246,0.1); cursor: pointer; font-size: 13px;">📋</button>
948
+ </div>
949
+ </div>
950
+ </div>`;
951
+ } else if (provider.isLocal) {
952
+ // Ollama
953
+ pHtml += `<p style="font-size: 13px; color: var(--text-secondary); margin: 0;">
954
+ ${isVi ? 'Đảm bảo <code>ollama serve</code> đang chạy trên máy trước khi start Docker.' : 'Make sure <code>ollama serve</code> is running before starting Docker.'}
955
+ </p>`;
956
+ } else {
957
+ // Direct API provider: show key input
958
+ pHtml += `<div class="form-group" style="margin: 0;">
959
+ <label class="form-group__label" for="key-api-key">🔑 ${provider.envLabel}</label>
960
+ <input type="text" class="form-input" id="key-api-key" placeholder="${provider.envKey}=..." style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
961
+ <p class="form-group__hint">${isVi ? 'Lấy từ' : 'Get from'} <a href="${provider.envLink}" target="_blank">${provider.envLink.replace('https://', '')}</a></p>
962
+ </div>`;
963
+ }
964
+
965
+ pHtml += `</div>`;
966
+ providerEl.innerHTML = pHtml;
836
967
  }
837
968
 
838
- // Skill env vars
839
- state.config.skills.forEach(sid => {
840
- const skill = SKILLS.find(s => s.id === sid);
841
- if (skill && skill.envVars && skill.envVars.length > 0) {
842
- skill.envVars.forEach(envLine => {
843
- const eq = envLine.indexOf('=');
844
- if (eq > 0 && !envLine.startsWith('#')) {
845
- const envKey = envLine.substring(0, eq);
846
- html += `<div class="form-group" style="margin-bottom: 16px;">
847
- <label class="form-group__label" for="key-${envKey.toLowerCase()}">${skill.icon} ${envKey}</label>
848
- <input type="text" class="form-input" id="key-${envKey.toLowerCase()}" placeholder="${envLine}" style="font-family: monospace; font-size: 13px;">
849
- <p class="form-group__hint">${skill.noteVi || skill.noteEn || ''}</p>
850
- </div>`;
851
- }
852
- });
969
+ // ─── Section 2: Channel ───
970
+ const channelEl = document.getElementById('key-section-channel');
971
+ if (channelEl) {
972
+ let cHtml = '';
973
+ const channelName = state.channel === 'telegram' ? 'Telegram' : (state.channel === 'zalo-personal' ? 'Zalo Personal' : 'Zalo Bot API');
974
+ const channelIcon = state.channel === 'telegram' ? '📨' : '💬';
975
+
976
+ cHtml += `<div style="padding: 16px 20px; border: 1px solid rgba(255,255,255,0.08); border-radius: 12px; background: rgba(255,255,255,0.02);">`;
977
+ cHtml += `<h3 style="margin: 0 0 12px; font-size: 15px; font-weight: 700; color: var(--text-primary);">${channelIcon} ${isVi ? 'Kênh chat' : 'Chat Channel'} — ${channelName}</h3>`;
978
+
979
+ if (state.channel === 'telegram') {
980
+ cHtml += `<div class="form-group" style="margin: 0;">
981
+ <label class="form-group__label" for="key-bot-token">🤖 Telegram Bot Token</label>
982
+ <input type="text" class="form-input" id="key-bot-token" placeholder="VD: 1234567890:ABCdefGHIjklMNOpqrsTUVwxyz" style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
983
+ <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>
984
+ </div>`;
985
+ } else if (state.channel === 'zalo-bot') {
986
+ cHtml += `<div class="form-group" style="margin: 0;">
987
+ <label class="form-group__label" for="key-bot-token">🔑 Zalo Bot Token</label>
988
+ <input type="text" class="form-input" id="key-bot-token" placeholder="Zalo Bot Token" style="font-family: monospace; font-size: 13px;" oninput="window.__validateKeys()">
989
+ <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>
990
+ </div>`;
991
+ } else if (state.channel === 'zalo-personal') {
992
+ cHtml += `<div style="display: flex; gap: 8px; align-items: flex-start; padding: 12px 14px; background: rgba(245,158,11,0.06); border: 1px solid rgba(245,158,11,0.2); border-radius: 8px; font-size: 13px; color: var(--warning); margin: 0;">
993
+ <span style="font-size: 16px; margin-top: -2px;">⚠️</span>
994
+ <span style="line-height: 1.5;">${isVi
995
+ ? '<strong>Zalo Personal</strong> sử dụng unofficial API (zca-js). Tài khoản Zalo của bạn có thể bị hạn chế hoặc khóa. Chỉ nên dùng với tài khoản phụ.'
996
+ : '<strong>Zalo Personal</strong> uses an unofficial API (zca-js). Your Zalo account may be restricted or blocked. Only use with a secondary account.'}</span>
997
+ </div>`;
853
998
  }
854
- });
855
999
 
856
- container.innerHTML = html;
1000
+ cHtml += `</div>`;
1001
+ channelEl.innerHTML = cHtml;
1002
+ }
1003
+
1004
+ // ─── Section 3: Skill env vars ───
1005
+ const skillsEl = document.getElementById('key-section-skills');
1006
+ if (skillsEl) {
1007
+ let sHtml = '';
1008
+ state.config.skills.forEach(sid => {
1009
+ const skill = SKILLS.find(s => s.id === sid);
1010
+ if (skill && skill.envVars && skill.envVars.length > 0) {
1011
+ skill.envVars.forEach(envLine => {
1012
+ const eq = envLine.indexOf('=');
1013
+ if (eq > 0 && !envLine.startsWith('#')) {
1014
+ const envKey = envLine.substring(0, eq);
1015
+ sHtml += `<div class="form-group" style="margin-bottom: 16px;">
1016
+ <label class="form-group__label" for="key-${envKey.toLowerCase()}">${skill.icon} ${envKey}</label>
1017
+ <input type="text" class="form-input" id="key-${envKey.toLowerCase()}" placeholder="${envLine}" style="font-family: monospace; font-size: 13px;">
1018
+ <p class="form-group__hint">${skill.noteVi || skill.noteEn || ''}</p>
1019
+ </div>`;
1020
+ }
1021
+ });
1022
+ }
1023
+ });
1024
+ skillsEl.innerHTML = sHtml;
1025
+ }
857
1026
  }
858
1027
  window.__validateKeys = function() { updateNavButtons(); };
1028
+ window.__toggle9RouterSecurity = function(checked) {
1029
+ const display = document.getElementById('9router-key-display');
1030
+ const keyInput = document.getElementById('key-9router-apikey');
1031
+ if (!display || !keyInput) return;
1032
+ if (checked) {
1033
+ // Auto-generate a random 32-char hex key
1034
+ const key = (typeof crypto !== 'undefined' && crypto.randomUUID)
1035
+ ? crypto.randomUUID().replace(/-/g, '')
1036
+ : Array.from({length: 32}, () => Math.floor(Math.random() * 16).toString(16)).join('');
1037
+ keyInput.value = 'oc9r-' + key;
1038
+ display.style.display = 'block';
1039
+ } else {
1040
+ keyInput.value = '';
1041
+ display.style.display = 'none';
1042
+ }
1043
+ };
859
1044
 
860
1045
  // ========== Build .env content from key inputs ==========
861
1046
  function populateEnvContent() {
@@ -872,6 +1057,11 @@
872
1057
 
873
1058
  if (provider.isProxy) {
874
1059
  lines.push('# Không cần AI API key — 9Router xử lý qua dashboard');
1060
+ const routerApiKey = document.getElementById('key-9router-apikey')?.value?.trim() || '';
1061
+ if (routerApiKey) {
1062
+ lines.push(`\n# 9Router API Key (bảo mật proxy)`);
1063
+ lines.push(`ROUTER_API_KEY=${routerApiKey}`);
1064
+ }
875
1065
  } else if (provider.isLocal) {
876
1066
  lines.push('OLLAMA_HOST=http://host.docker.internal:11434');
877
1067
  } else {
@@ -1038,23 +1228,36 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
1038
1228
 
1039
1229
  // 9Router: add proxy endpoint config under models.providers
1040
1230
  // Per official 9Router docs: use custom provider name '9router', models use cx/ prefix
1231
+ const routerKeyForConfig = document.getElementById('key-9router-apikey')?.value?.trim() || '';
1041
1232
  if (is9Router) {
1042
1233
  clawConfig.models = {
1043
1234
  mode: 'merge',
1044
1235
  providers: {
1045
1236
  '9router': {
1046
1237
  baseUrl: 'http://9router:20128/v1',
1047
- apiKey: 'sk-no-key',
1238
+ apiKey: routerKeyForConfig || 'sk-no-key',
1048
1239
  api: 'openai-completions',
1049
1240
  models: [
1050
1241
  { id: 'smart-route', name: 'Smart Proxy (Auto Route)', contextWindow: 200000, maxTokens: 8192 },
1242
+ // OAuth Subscription
1243
+ { id: 'cc/claude-opus-4-6', name: 'Claude Opus 4.6', contextWindow: 200000, maxTokens: 8192 },
1244
+ { id: 'cc/claude-sonnet-4-6', name: 'Claude Sonnet 4.6', contextWindow: 200000, maxTokens: 8192 },
1051
1245
  { id: 'cx/gpt-5.4', name: 'GPT 5.4 (Codex)', contextWindow: 128000, maxTokens: 8192 },
1052
- { id: 'ag/claude-opus-4-6-thinking', name: 'Claude Opus 4.6 Thinking (AG)', contextWindow: 200000, maxTokens: 8192 },
1053
- { id: 'ag/gemini-3.1-pro-high', name: 'Gemini 3.1 Pro High (AG)', contextWindow: 1000000, maxTokens: 8192 },
1054
- { id: 'cc/claude-opus-4-6', name: 'Claude Opus 4.6 (Claude Code)', contextWindow: 200000, maxTokens: 8192 },
1055
- { id: 'cc/claude-sonnet-4-6', name: 'Claude Sonnet 4.6 (Claude Code)', contextWindow: 200000, maxTokens: 8192 },
1246
+ { id: 'cx/gpt-5.3-codex', name: 'GPT 5.3 Codex', contextWindow: 128000, maxTokens: 8192 },
1056
1247
  { id: 'gh/gpt-5.4', name: 'GPT 5.4 (Copilot)', contextWindow: 128000, maxTokens: 8192 },
1057
1248
  { id: 'gh/claude-opus-4.6', name: 'Claude Opus 4.6 (Copilot)', contextWindow: 200000, maxTokens: 8192 },
1249
+ { id: 'gc/gemini-3-flash-preview', name: 'Gemini 3 Flash (FREE)', contextWindow: 1000000, maxTokens: 8192 },
1250
+ // OAuth FREE
1251
+ { id: 'if/qwen3-coder-plus', name: 'Qwen3 Coder Plus (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1252
+ { id: 'if/kimi-k2', name: 'Kimi K2 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1253
+ { id: 'if/glm-4.7', name: 'GLM 4.7 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1254
+ { id: 'if/deepseek-r1', name: 'DeepSeek R1 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
1255
+ { id: 'qw/qwen3-coder-plus', name: 'Qwen3 Coder Plus (Qwen FREE)', contextWindow: 128000, maxTokens: 8192 },
1256
+ { id: 'kr/claude-sonnet-4.5', name: 'Claude Sonnet 4.5 (Kiro FREE)', contextWindow: 200000, maxTokens: 8192 },
1257
+ // API Key
1258
+ { id: 'glm/glm-4.7', name: 'GLM 4.7 ($0.6/1M)', contextWindow: 128000, maxTokens: 8192 },
1259
+ { id: 'minimax/MiniMax-M2.1', name: 'MiniMax M2.1 ($0.20/1M)', contextWindow: 1000000, maxTokens: 8192 },
1260
+ { id: 'deepseek/deepseek-chat', name: 'DeepSeek V3.2 Chat', contextWindow: 128000, maxTokens: 8192 },
1058
1261
  ],
1059
1262
  },
1060
1263
  },
@@ -1196,11 +1399,13 @@ ${extraHostsBlock}
1196
1399
  container_name: 9router
1197
1400
  restart: always
1198
1401
  entrypoint: >
1199
- /bin/sh -c "npm install -g 9router && [ ! -f /root/.9router/db.json ] && echo '{\\"combos\\":[{\\"id\\":\\"smart-route\\",\\"name\\":\\"smart-route\\",\\"alias\\":\\"smart-route\\",\\"models\\":[\\"cx/gpt-5.4\\",\\"ag/claude-opus-4-6-thinking\\",\\"cc/claude-opus-4-6\\",\\"gh/gpt-5.4\\",\\"ag/gemini-3.1-pro-high\\",\\"cc/claude-sonnet-4-6\\",\\"gh/claude-opus-4.6\\"]}]}' > /root/.9router/db.json; 9router"
1402
+ /bin/sh -c "npm install -g 9router && [ ! -f /root/.9router/db.json ] && echo '{\\"combos\\":[{\\"id\\":\\"smart-route\\",\\"name\\":\\"smart-route\\",\\"alias\\":\\"smart-route\\",\\"models\\":[\\"if/qwen3-coder-plus\\",\\"if/kimi-k2\\",\\"if/glm-4.7\\",\\"if/deepseek-r1\\",\\"qw/qwen3-coder-plus\\",\\"kr/claude-sonnet-4.5\\",\\"gc/gemini-3-flash-preview\\",\\"cc/claude-opus-4-6\\",\\"cx/gpt-5.3-codex\\",\\"gh/gpt-5.4\\"]}]}' > /root/.9router/db.json; 9router"
1200
1403
  environment:
1201
1404
  - PORT=20128
1202
1405
  - HOSTNAME=0.0.0.0
1203
- - CI=true
1406
+ - CI=true${routerKeyForConfig ? `\n - API_KEY=\${ROUTER_API_KEY}` : ''}
1407
+ env_file:
1408
+ - .env
1204
1409
  volumes:
1205
1410
  - 9router-data:/root/.9router
1206
1411
  ports:
@@ -1249,11 +1454,12 @@ docker logs -f openclaw-bot${approveNote}`);
1249
1454
 
1250
1455
  // 6. Generate auth-profiles.json (root + agent level)
1251
1456
  // OpenClaw v1 format requires: type="api_key", field="key", and "order" block
1252
- // For 9Router: provider is '9router', key is dummy (9Router has 'Require API key' = OFF by default)
1457
+ // For 9Router: if user set API key use it (enables Bearer auth); otherwise 'sk-no-key' (open access)
1253
1458
  const authProviderName = is9Router ? '9router' : state.config.provider;
1254
1459
  const authProfileId = is9Router ? '9router-proxy' : `${authProviderName}:default`;
1460
+ const router9ApiKey = document.getElementById('key-9router-apikey')?.value?.trim() || '';
1255
1461
  const authKeyValue = is9Router
1256
- ? 'sk-no-key'
1462
+ ? (router9ApiKey || 'sk-no-key')
1257
1463
  : `<your_${(provider.envKey || 'API_KEY').toLowerCase()}>`;
1258
1464
 
1259
1465
  const authProfilesJson = {
@@ -1660,6 +1866,51 @@ timeout /t 4 /nobreak >nul
1660
1866
  powershell -Command "try { Invoke-WebRequest -Uri 'http://localhost:9222/json/version' -UseBasicParsing -TimeoutSec 5 | Out-Null; Write-Host 'OK! Chrome Debug Mode dang chay.' -ForegroundColor Green } catch { Write-Host 'LOI: Port 9222 chua mo.' -ForegroundColor Red }"
1661
1867
  echo.
1662
1868
  pause
1869
+ `;
1870
+
1871
+ const chromeShContent = `#!/usr/bin/env bash
1872
+ # ====== OpenClaw - Chrome Debug Mode (Mac/Linux) ======
1873
+ set -e
1874
+ echo "====== OpenClaw - Chrome Debug Mode ======"
1875
+ echo ""
1876
+
1877
+ # Detect Chrome path
1878
+ if [[ "$OSTYPE" == "darwin"* ]]; then
1879
+ CHROME_BIN="/Applications/Google Chrome.app/Contents/MacOS/Google Chrome"
1880
+ [ ! -f "$CHROME_BIN" ] && CHROME_BIN="/Applications/Chromium.app/Contents/MacOS/Chromium"
1881
+ [ ! -f "$CHROME_BIN" ] && CHROME_BIN="/Applications/Google Chrome Canary.app/Contents/MacOS/Google Chrome Canary"
1882
+ else
1883
+ CHROME_BIN="$(command -v google-chrome || command -v google-chrome-stable || command -v chromium-browser || command -v chromium || echo '')"
1884
+ fi
1885
+ [ -n "$CHROME_DEBUG_BIN" ] && CHROME_BIN="$CHROME_DEBUG_BIN"
1886
+
1887
+ if [ -z "$CHROME_BIN" ] || { [ ! -f "$CHROME_BIN" ] && [ ! -x "$CHROME_BIN" ]; }; then
1888
+ echo -e "\\033[31mERROR: Chrome/Chromium not found.\\033[0m"
1889
+ echo "Install Chrome or: export CHROME_DEBUG_BIN=/path/to/chrome"
1890
+ exit 1
1891
+ fi
1892
+
1893
+ echo "Using: $CHROME_BIN"
1894
+ echo "Killing existing Chrome debug instances..."
1895
+ pkill -f -- "--remote-debugging-port=9222" 2>/dev/null || true
1896
+ sleep 2
1897
+
1898
+ TMP_DIR="\${TMPDIR:-/tmp}/chrome-debug-openclaw"
1899
+ mkdir -p "$TMP_DIR"
1900
+
1901
+ echo "Starting Chrome in Debug Mode (port 9222)..."
1902
+ "$CHROME_BIN" \\
1903
+ --remote-debugging-port=9222 \\
1904
+ --remote-allow-origins=* \\
1905
+ --user-data-dir="$TMP_DIR" &
1906
+
1907
+ sleep 4
1908
+ if curl -s http://localhost:9222/json/version > /dev/null 2>&1; then
1909
+ echo -e "\\033[32mOK! Chrome Debug Mode is running on port 9222.\\033[0m"
1910
+ else
1911
+ echo -e "\\033[31mERROR: Port 9222 not responding.\\033[0m"
1912
+ exit 1
1913
+ fi
1663
1914
  `;
1664
1915
 
1665
1916
  // Store generated files for download
@@ -1682,6 +1933,7 @@ pause
1682
1933
  '.openclaw/workspace/browser-tool.js': browserToolJs,
1683
1934
  '.openclaw/workspace/BROWSER.md': browserMd,
1684
1935
  'start-chrome-debug.bat': chromeBatContent,
1936
+ 'start-chrome-debug.sh': chromeShContent,
1685
1937
  } : {}),
1686
1938
  };
1687
1939