create-openclaw-bot 4.0.4 → 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/CHANGELOG.md +8 -0
- package/CHANGELOG.vi.md +8 -0
- package/README.md +22 -29
- package/README.vi.md +22 -30
- package/cli.js +113 -9
- package/index.html +23 -20
- package/package.json +2 -2
- package/setup.js +366 -114
- package/start-chrome-debug.sh +70 -0
- package/style.css +40 -0
- package/docker/openclaw/Dockerfile +0 -14
- package/docker/openclaw/docker-compose.yml +0 -18
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
|
|
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
|
-
|
|
133
|
-
{ id: '9router/
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
{ id: '9router/
|
|
137
|
-
{ id: '9router/
|
|
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
|
-
|
|
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
|
-
//
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
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
|
-
//
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
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
|
-
|
|
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: '
|
|
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\\":[\\"
|
|
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:
|
|
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
|
|