create-openclaw-bot 4.0.1

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.vi.md ADDED
@@ -0,0 +1,439 @@
1
+ # 🦞 OpenClaw — Hướng Dẫn Kỹ Thuật (cho AI Agent)
2
+
3
+ > **File này dành cho AI Agent (Antigravity) đọc và thực thi tự động.**
4
+ > Người dùng đã tạo sẵn file `.env` chứa API key và Bot token. AI **TUYỆT ĐỐI KHÔNG** được yêu cầu hoặc nhận credentials qua chat.
5
+
6
+ [English](SETUP.md) · [Tiếng Việt](#-quy-tắc-bảo-mật--bắt-buộc)
7
+
8
+ ---
9
+
10
+ ## 🔐 Quy Tắc Bảo Mật — BẮT BUỘC
11
+
12
+ > **AI Agent BẮT BUỘC tuân thủ TẤT CẢ quy tắc bên dưới. Vi phạm = DỪNG ngay.**
13
+
14
+ ### File & thư mục hệ thống
15
+
16
+ - ❌ **KHÔNG** đọc, sao chép, hoặc truy cập bất kỳ file nào ngoài thư mục project
17
+ - ❌ **KHÔNG** quét hoặc liệt kê các thư mục hệ thống: `Documents`, `Desktop`, `Downloads`, `AppData`, `%USERPROFILE%`
18
+ - ❌ **KHÔNG** truy cập registry, system32, hoặc Program Files
19
+ - ❌ **KHÔNG** cài đặt phần mềm, driver, hoặc service ngoài Docker
20
+ - ❌ **KHÔNG** sửa đổi biến môi trường hệ thống
21
+ - ✅ **CHỈ** làm việc trong thư mục project do người dùng chỉ định
22
+
23
+ ### API key & credentials
24
+
25
+ - ❌ **KHÔNG BAO GIỜ** hiển thị API key, token, hoặc mật khẩu trong chat
26
+ - ❌ **KHÔNG** viết API key trực tiếp vào mã nguồn
27
+ - ❌ **KHÔNG** commit file credentials lên Git
28
+ - ✅ **LUÔN** lưu credentials trong file `.env` riêng
29
+ - ✅ **LUÔN** thêm `.env` vào `.gitignore`
30
+ - ✅ **LUÔN** dùng biến môi trường (`${VAR}`) thay vì hardcode
31
+
32
+ ### Ví crypto & tài sản số
33
+
34
+ - ❌ **TUYỆT ĐỐI KHÔNG** truy cập, đọc, hoặc quét các thư mục ví crypto:
35
+ - Metamask, Trust Wallet, Phantom, Exodus, Electrum
36
+ - File: `wallet.dat`, `keystore/`, seed phrases, private keys
37
+ - Extension trình duyệt chứa ví crypto
38
+ - ❌ **KHÔNG** quét clipboard (có thể chứa seed phrases)
39
+ - ❌ **KHÔNG** truy cập browser profile, cookie, hoặc mật khẩu đã lưu
40
+ - ❌ **KHÔNG** cài đặt npm package lạ (chỉ `openclaw` và plugin chính thức)
41
+
42
+ ### Docker
43
+
44
+ - ✅ Chỉ mount **đúng** thư mục cần thiết (config + workspace)
45
+ - ❌ **KHÔNG** mount nguyên ổ đĩa (`C:/` hoặc `D:/`)
46
+ - ❌ **KHÔNG** chạy container với `--privileged`
47
+ - ✅ Giới hạn port expose (chỉ `18789`)
48
+
49
+ ---
50
+
51
+ ## 📂 Cấu Trúc Thư Mục Sau Setup
52
+
53
+ ```
54
+ <PROJECT_DIR>/
55
+ ├── .openclaw/ ← Config chính (KHÔNG commit)
56
+ │ ├── openclaw.json ← Gateway, agent, channel config
57
+ │ ├── auth-profiles.json ← API key profile
58
+ │ ├── credentials/
59
+ │ │ └── <provider> ← API key của nhà cung cấp AI
60
+ │ ├── agents/
61
+ │ │ └── <agent-name>.yaml ← Định nghĩa agent (chỉ name + model)
62
+ │ ├── workspace/ ← ⭐ Persona & hành vi bot
63
+ │ │ ├── IDENTITY.md ← Tên bot, emoji, vibe
64
+ │ │ ├── SOUL.md ← Tính cách, ranh giới
65
+ │ │ ├── AGENTS.md ← Quy tắc vận hành
66
+ │ │ ├── USER.md ← Thông tin người dùng
67
+ │ │ ├── TOOLS.md ← Hướng dẫn dùng tools
68
+ │ │ └── MEMORY.md ← Bộ nhớ dài hạn
69
+ │ ├── skills/ ← Slash commands (tùy chọn)
70
+ │ ├── identity/
71
+ │ │ └── device.json ← Device keypair
72
+ │ ├── memory/ ← Bộ nhớ agent (SQLite)
73
+ │ ├── cron/jobs.json
74
+ │ └── logs/
75
+ │ └── config-audit.jsonl
76
+
77
+ ├── docker/openclaw/
78
+ │ ├── Dockerfile
79
+ │ ├── docker-compose.yml
80
+ │ └── .env ← API keys (KHÔNG commit)
81
+
82
+ └── .gitignore
83
+ ```
84
+
85
+ ---
86
+
87
+ ## 🔌 Kênh & Model Hỗ Trợ
88
+
89
+ ### Kênh chat
90
+
91
+ | Kênh | Key trong .env | Plugin cần cài |
92
+ |------|---------------|----------------|
93
+ | **Telegram** | `TELEGRAM_BOT_TOKEN` | Không (built-in) |
94
+ | **Zalo Bot API** | `ZALO_BOT_TOKEN` | `@openclaw/zalo` |
95
+ | **Zalo Cá nhân** | Không cần | `@openclaw/zalouser` |
96
+
97
+ ### Nhà cung cấp AI
98
+
99
+ | Nhà cung cấp | Key trong .env | Model ID format |
100
+ |--------------|---------------|-----------------|
101
+ | **Google Gemini** | `GOOGLE_API_KEY` | `google/gemini-2.5-flash` |
102
+ | **Anthropic Claude** | `ANTHROPIC_API_KEY` | `anthropic/claude-sonnet-4` |
103
+ | **OpenAI** | `OPENAI_API_KEY` | `openai/gpt-4o` |
104
+ | **OpenRouter** | `OPENROUTER_API_KEY` | `openrouter/model-name` |
105
+ | **Ollama** | `OLLAMA_HOST` | `ollama/qwen3:8b` |
106
+
107
+ ### Logic phát hiện kênh:
108
+
109
+ ```
110
+ NẾU .env chứa TELEGRAM_BOT_TOKEN → Kênh = Telegram
111
+ NẾU .env chứa ZALO_BOT_TOKEN → Kênh = Zalo Bot API
112
+ NGƯỢC LẠI → Kênh = Zalo Cá nhân (hoặc hỏi người dùng)
113
+ ```
114
+
115
+ ### Logic phát hiện AI provider:
116
+
117
+ ```
118
+ NẾU .env chứa GOOGLE_API_KEY → Provider = Google Gemini
119
+ NẾU .env chứa ANTHROPIC_API_KEY → Provider = Anthropic
120
+ NẾU .env chứa OPENAI_API_KEY → Provider = OpenAI
121
+ NẾU .env chứa OPENROUTER_API_KEY → Provider = OpenRouter
122
+ NẾU .env chứa OLLAMA_HOST → Provider = Ollama
123
+ ```
124
+
125
+ ---
126
+
127
+ ## ⚙️ Các Bước Setup
128
+
129
+ ### Bước 1: Tạo thư mục
130
+
131
+ ```bash
132
+ mkdir -p <PROJECT_DIR>/docker/openclaw
133
+ mkdir -p <PROJECT_DIR>/.openclaw/credentials
134
+ mkdir -p <PROJECT_DIR>/.openclaw/agents
135
+ mkdir -p <PROJECT_DIR>/.openclaw/skills
136
+ mkdir -p <PROJECT_DIR>/.openclaw/identity
137
+ mkdir -p <PROJECT_DIR>/.openclaw/memory
138
+ mkdir -p <PROJECT_DIR>/.openclaw/cron
139
+ mkdir -p <PROJECT_DIR>/.openclaw/logs
140
+ mkdir -p <PROJECT_DIR>/.openclaw/completions
141
+ mkdir -p <PROJECT_DIR>/.openclaw/canvas
142
+ ```
143
+
144
+ ### Bước 2: Tạo Dockerfile
145
+
146
+ Tạo `<PROJECT_DIR>/docker/openclaw/Dockerfile`:
147
+
148
+ #### Telegram (không cần plugin):
149
+
150
+ ```dockerfile
151
+ FROM node:22-slim
152
+ RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
153
+ RUN npm install -g openclaw@latest
154
+ WORKDIR /root/.openclaw
155
+ EXPOSE 18789
156
+ CMD ["openclaw", "gateway", "run"]
157
+ ```
158
+
159
+ #### Zalo Bot API:
160
+
161
+ ```dockerfile
162
+ FROM node:22-slim
163
+ RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
164
+ RUN npm install -g openclaw@latest
165
+ RUN openclaw plugins install @openclaw/zalo
166
+ WORKDIR /root/.openclaw
167
+ EXPOSE 18789
168
+ CMD ["openclaw", "gateway", "run"]
169
+ ```
170
+
171
+ #### Zalo Cá nhân:
172
+
173
+ ```dockerfile
174
+ FROM node:22-slim
175
+ RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
176
+ RUN npm install -g openclaw@latest
177
+ RUN openclaw plugins install @openclaw/zalouser
178
+ WORKDIR /root/.openclaw
179
+ EXPOSE 18789
180
+ CMD ["openclaw", "gateway", "run"]
181
+ ```
182
+
183
+ #### Có thêm plugin (ví dụ Browser + Memory):
184
+
185
+ ```dockerfile
186
+ FROM node:22-slim
187
+ RUN apt-get update && apt-get install -y git curl && rm -rf /var/lib/apt/lists/*
188
+ RUN npm install -g openclaw@latest
189
+ # Cài plugin kênh chat + plugin mở rộng
190
+ RUN openclaw plugins install @openclaw/zalo @openclaw/browser @openclaw/memory
191
+ WORKDIR /root/.openclaw
192
+ EXPOSE 18789
193
+ CMD ["openclaw", "gateway", "run"]
194
+ ```
195
+
196
+ ### Bước 3: Tạo docker-compose.yml
197
+
198
+ Tạo `<PROJECT_DIR>/docker/openclaw/docker-compose.yml`:
199
+
200
+ ```yaml
201
+ services:
202
+ ai-bot:
203
+ build: .
204
+ container_name: openclaw-bot
205
+ restart: always
206
+ env_file:
207
+ - .env
208
+ extra_hosts:
209
+ - "host.docker.internal:host-gateway"
210
+ volumes:
211
+ - <PROJECT_DIR>/.openclaw:/root/.openclaw
212
+ ports:
213
+ - "18789:18789"
214
+ ```
215
+
216
+ > Thay `<PROJECT_DIR>` bằng đường dẫn tuyệt đối thực tế. Ví dụ: `D:/MyAIBot/.openclaw:/root/.openclaw`
217
+
218
+ ### Bước 4: Kiểm tra file .env (do người dùng đã tạo)
219
+
220
+ File `.env` đã được người dùng tạo tại `<PROJECT_DIR>/docker/openclaw/.env`.
221
+
222
+ **AI Agent PHẢI kiểm tra:**
223
+ 1. File `.env` **tồn tại** tại `<PROJECT_DIR>/docker/openclaw/.env`
224
+ 2. File chứa ít nhất 1 API key hợp lệ
225
+ 3. **KHÔNG** đọc hoặc hiển thị giá trị key trong chat
226
+
227
+ Nếu file không tồn tại → **DỪNG** và yêu cầu người dùng tạo theo hướng dẫn trong README.
228
+
229
+ > **KHÔNG BAO GIỜ** tạo file `.env` thay người dùng. **KHÔNG BAO GIỜ** yêu cầu credentials qua chat.
230
+
231
+ ### Bước 5: Tạo .gitignore
232
+
233
+ Tạo `<PROJECT_DIR>/.gitignore`:
234
+
235
+ ```gitignore
236
+ # Credentials
237
+ .openclaw/credentials/
238
+ .openclaw/auth-profiles.json
239
+ .openclaw/openclaw.json
240
+ .openclaw/identity/
241
+ docker/openclaw/.env
242
+
243
+ # Runtime data
244
+ .openclaw/memory/
245
+ .openclaw/logs/
246
+ .openclaw/completions/
247
+ .openclaw/telegram/
248
+ .openclaw/canvas/
249
+ ```
250
+
251
+ ### Bước 6: Tạo cấu hình OpenClaw
252
+
253
+ **AI Agent thực hiện:**
254
+
255
+ 1. Đọc API key (và channel token nếu có) từ `.env`
256
+ 2. Phát hiện kênh chat và AI provider theo logic ở trên
257
+ 3. Generate random gateway auth token
258
+ 4. Ghi giá trị vào các file config bên dưới
259
+ 5. **KHÔNG** hiển thị key/token trong chat
260
+
261
+ **Generate gateway auth token:**
262
+
263
+ ```bash
264
+ node -e "console.log(require('crypto').randomBytes(24).toString('hex'))"
265
+ ```
266
+
267
+ #### Config cho Telegram + Google Gemini (ví dụ):
268
+
269
+ **Tạo `<PROJECT_DIR>/.openclaw/openclaw.json`:**
270
+
271
+ ```json
272
+ {
273
+ "meta": { "lastTouchedVersion": "2026.3.27" },
274
+ "agents": {
275
+ "defaults": {
276
+ "model": { "primary": "google/gemini-2.5-flash", "fallbacks": [] },
277
+ "compaction": { "mode": "safeguard" }
278
+ },
279
+ "list": []
280
+ },
281
+ "commands": {
282
+ "native": "auto",
283
+ "nativeSkills": "auto",
284
+ "restart": true,
285
+ "ownerDisplay": "raw"
286
+ },
287
+ "channels": {
288
+ "telegram": {
289
+ "enabled": true,
290
+ "dmPolicy": "open",
291
+ "botToken": "<BOT_TOKEN_TỪ_.ENV>",
292
+ "allowFrom": ["*"],
293
+ "groupPolicy": "allowlist",
294
+ "streaming": "partial"
295
+ }
296
+ },
297
+ "gateway": {
298
+ "port": 18789,
299
+ "mode": "local",
300
+ "auth": { "mode": "token", "token": "<GATEWAY_TOKEN_ĐÃ_GENERATE>" }
301
+ }
302
+ }
303
+ ```
304
+
305
+ #### Config kênh Zalo Bot API:
306
+
307
+ Thay phần `channels` bằng:
308
+
309
+ ```json
310
+ { "channels": { "zalo": { "enabled": true, "botToken": "<ZALO_BOT_TOKEN_TỪ_.ENV>" } } }
311
+ ```
312
+
313
+ #### Config kênh Zalo Cá nhân:
314
+
315
+ Thay phần `channels` bằng:
316
+
317
+ ```json
318
+ { "channels": { "zalouser": { "enabled": true } } }
319
+ ```
320
+
321
+ > **Lưu ý:** Zalo Cá nhân cần quét QR code sau khi container chạy. Xem QR trong logs: `docker logs openclaw-bot`
322
+
323
+ ---
324
+
325
+ **Tạo `<PROJECT_DIR>/.openclaw/auth-profiles.json`** — dùng API key tương ứng:
326
+
327
+ ```json
328
+ { "gemini": { "apiKey": "<GOOGLE_API_KEY_TỪ_.ENV>" } }
329
+ ```
330
+
331
+ _(Đổi `gemini` thành `anthropic`, `openai`, `openrouter` tùy provider)_
332
+
333
+ **Tạo `<PROJECT_DIR>/.openclaw/credentials/<provider>`** — ghi API key (1 dòng, không xuống dòng).
334
+
335
+ **Tạo `<PROJECT_DIR>/.openclaw/cron/jobs.json`:**
336
+
337
+ ```json
338
+ { "version": 1, "jobs": [] }
339
+ ```
340
+
341
+ ### Bước 7: Tạo Agent & Workspace Files
342
+
343
+ #### 7a. Agent YAML (chỉ metadata, KHÔNG chứa system_prompt)
344
+
345
+ Tạo file YAML tại `.openclaw/agents/<tên>.yaml`. Ví dụ — `.openclaw/agents/chat.yaml`:
346
+
347
+ ```yaml
348
+ name: chat
349
+ description: "Trợ lý AI cá nhân"
350
+
351
+ model:
352
+ primary: google/gemini-2.5-flash
353
+ ```
354
+
355
+ > **Lưu ý:** File YAML chỉ khai báo `name`, `description`, `model`. Tính cách bot nằm ở workspace files bên dưới.
356
+
357
+ #### 7b. Workspace Markdown Files (⭐ Bot nhận diện từ đây)
358
+
359
+ OpenClaw **tự động inject** tất cả file `.md` trong `.openclaw/workspace/` vào context đầu mỗi session. Đây là cách bot "biết" tên mình, tính cách, và quy tắc.
360
+
361
+ | File | Mục đích | Bắt buộc |
362
+ |------|----------|----------|
363
+ | `IDENTITY.md` | Tên bot, emoji, cách xưng hô | ✅ |
364
+ | `SOUL.md` | Tính cách, phong cách, ranh giới | ✅ |
365
+ | `AGENTS.md` | Quy tắc vận hành, cách trả lời | ✅ |
366
+ | `USER.md` | Thông tin về user (ngôn ngữ, sở thích) | Nên có |
367
+ | `TOOLS.md` | Hướng dẫn dùng tool/skill | Nên có |
368
+ | `MEMORY.md` | Bộ nhớ dài hạn (bot tự cập nhật) | Tùy chọn |
369
+
370
+ > **Thứ tự ưu tiên:** Per-agent files (`.openclaw/agents/<id>/`) → Global workspace files (`.openclaw/workspace/`) → Config defaults.
371
+
372
+ > **Bảo mật hệ thống** (không xóa file, không truy cập thư mục nhạy cảm, không lộ API key...) được OpenClaw **tự động áp dụng** — không cần viết vào workspace files.
373
+
374
+ #### 7c. Cập nhật `openclaw.json`
375
+
376
+ Thêm agent vào `agents.list`:
377
+
378
+ ```json
379
+ {
380
+ "agents": {
381
+ "list": [
382
+ { "id": "chat", "model": { "primary": "google/gemini-2.5-flash", "fallbacks": [] } }
383
+ ]
384
+ }
385
+ }
386
+ ```
387
+
388
+ ### Bước 8: Build & Chạy
389
+
390
+ ```bash
391
+ cd <PROJECT_DIR>/docker/openclaw
392
+ docker compose build
393
+ docker compose up -d
394
+ docker logs -f openclaw-bot
395
+ ```
396
+
397
+ ### Bước 9: Kiểm tra
398
+
399
+ **Telegram:**
400
+ 1. Mở Telegram → Tìm bot của bạn
401
+ 2. Gửi tin nhắn bất kỳ
402
+ 3. Bot trả lời = **Thành công!** 🎉
403
+
404
+ **Zalo Bot API:**
405
+ 1. Mở Zalo → Tìm bot
406
+ 2. Gửi tin nhắn
407
+ 3. Bot trả lời = **Thành công!** 🎉
408
+
409
+ **Zalo Cá nhân:**
410
+ 1. Xem `docker logs openclaw-bot` để lấy QR code
411
+ 2. Quét QR bằng app Zalo
412
+ 3. Gửi tin nhắn từ tài khoản Zalo khác
413
+ 4. Bot trả lời = **Thành công!** 🎉
414
+
415
+ Nếu bot không phản hồi:
416
+
417
+ ```bash
418
+ docker logs openclaw-bot --tail 50
419
+ docker compose restart
420
+ ```
421
+
422
+ Nếu bot **không tạo được cron job** (lỗi "pairing required"):
423
+
424
+ ```bash
425
+ docker exec -i openclaw-bot openclaw devices approve --latest
426
+ ```
427
+
428
+ > **Lưu ý:** Từ OpenClaw v2026.3.x, gateway yêu cầu device pairing cho CLI connections. Dockerfile đã tự động approve khi khởi động, nhưng nếu lỗi vẫn xảy ra, chạy lệnh trên 1 lần.
429
+
430
+ ---
431
+
432
+ ## ✅ Checklist Bảo Mật Sau Setup
433
+
434
+ - [ ] `.env` **không** xuất hiện trong `git status`
435
+ - [ ] `.openclaw/credentials/` **không** bị Git track
436
+ - [ ] `openclaw.json` **không** bị Git track
437
+ - [ ] Docker **không** mount nguyên ổ đĩa
438
+ - [ ] Gateway auth token được generate ngẫu nhiên
439
+ - [ ] Zalo Cá nhân: đang dùng tài khoản **phụ** (nếu có)
package/cli.js ADDED
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { input, select, checkbox, confirm } from '@inquirer/prompts';
4
+ import fs from 'fs-extra';
5
+ import path from 'path';
6
+ import chalk from 'chalk';
7
+ import { spawn } from 'child_process';
8
+
9
+ const LOGO = `
10
+ \\/
11
+ /\\O _ O/\\
12
+ // /_\\ \\\\
13
+ \\// / \\ \\\\/
14
+ `;
15
+
16
+ const CHANNELS = {
17
+ 'telegram': { name: 'Telegram', type: 'telegram', icon: '🤖' },
18
+ 'zalo-bot': { name: 'Zalo OA (Bot Platform)', type: 'zalo-bot', icon: '🔑' },
19
+ 'zalo-personal': { name: 'Zalo Personal (Quét QR)', type: 'zalo-personal', icon: '📱' }
20
+ };
21
+
22
+ const PROVIDERS = {
23
+ 'proxy': { name: '9Router Proxy (Khuyên dùng)', icon: '🔀', isProxy: true },
24
+ 'openai': { name: 'OpenAI (ChatGPT)', icon: '🧠', envKey: 'OPENAI_API_KEY' },
25
+ 'ollama': { name: 'Local Ollama', icon: '🏠', isLocal: true },
26
+ 'google': { name: 'Google (Gemini)', icon: '⚡', envKey: 'GEMINI_API_KEY' },
27
+ 'anthropic': { name: 'Anthropic (Claude)', icon: '🦄', envKey: 'ANTHROPIC_API_KEY' },
28
+ 'xai': { name: 'xAI (Grok)', icon: '✖️', envKey: 'XAI_API_KEY' },
29
+ 'groq': { name: 'Groq (LPU)', icon: '🏎️', envKey: 'GROQ_API_KEY' }
30
+ };
31
+
32
+ const SKILLS = [
33
+ { value: 'browser', name: 'Web Browser Automation', checked: false },
34
+ { value: 'tavily', name: 'Web Search (Tavily)', checked: false },
35
+ { value: 'tts', name: 'Text-To-Speech (OpenAI/ElevenLabs)', checked: false }
36
+ ];
37
+
38
+ async function main() {
39
+ console.log(chalk.red('\n=================================='));
40
+ console.log(chalk.redBright(LOGO));
41
+ console.log(chalk.greenBright(' OpenClaw Auto Setup CLI '));
42
+ console.log(chalk.red('==================================\n'));
43
+
44
+ // 1. Language
45
+ const lang = await select({
46
+ message: 'Select language / Chọn ngôn ngữ:',
47
+ choices: [
48
+ { name: 'Tiếng Việt', value: 'vi' },
49
+ { name: 'English', value: 'en' }
50
+ ]
51
+ });
52
+ const isVi = lang === 'vi';
53
+
54
+ // 2. Channel
55
+ const channelKey = await select({
56
+ message: isVi ? 'Chọn nền tảng bot:' : 'Select bot platform:',
57
+ choices: Object.entries(CHANNELS).map(([k, v]) => ({ name: `${v.icon} ${v.name}`, value: k }))
58
+ });
59
+ const channel = CHANNELS[channelKey];
60
+
61
+ let botToken = '';
62
+ if (channelKey !== 'zalo-personal') {
63
+ botToken = await input({
64
+ message: isVi ? `Nhập ${channel.name} Token:` : `Enter ${channel.name} Token:`,
65
+ required: true
66
+ });
67
+ }
68
+
69
+ // 3. Provider
70
+ const providerKey = await select({
71
+ message: isVi ? 'Chọn AI Provider:' : 'Select AI Provider:',
72
+ choices: Object.entries(PROVIDERS).map(([k, v]) => ({ name: `${v.icon} ${v.name}`, value: k }))
73
+ });
74
+ const provider = PROVIDERS[providerKey];
75
+
76
+ let providerKeyVal = '';
77
+ if (!provider.isProxy && !provider.isLocal) {
78
+ providerKeyVal = await input({
79
+ message: isVi ? `Nhập ${provider.envKey}:` : `Enter ${provider.envKey}:`,
80
+ required: true
81
+ });
82
+ }
83
+
84
+ // 4. Skills
85
+ const selectedSkills = await checkbox({
86
+ message: isVi ? 'Bật tính năng bổ sung (Space để chọn):' : 'Enable extra skills (Space to select):',
87
+ choices: SKILLS
88
+ });
89
+
90
+ let tavilyKey = '';
91
+ if (selectedSkills.includes('tavily')) {
92
+ tavilyKey = await input({ message: isVi ? 'Nhập TAVILY_API_KEY:' : 'Enter TAVILY_API_KEY:' });
93
+ }
94
+ let ttsOpenaiKey = '';
95
+ let ttsElevenKey = '';
96
+ if (selectedSkills.includes('tts')) {
97
+ ttsOpenaiKey = await input({ message: isVi ? 'Nhập OPENAI_API_KEY (cho TTS, bỏ trống nếu dùng ElevenLabs):' : 'Enter OPENAI_API_KEY (for TTS, leave empty for ElevenLabs):' });
98
+ ttsElevenKey = await input({ message: isVi ? 'Nhập ELEVENLABS_API_KEY (hoặc bỏ trống):' : 'Enter ELEVENLABS_API_KEY (or leave empty):' });
99
+ }
100
+
101
+ // 5. Bot Info
102
+ const botName = await input({ message: isVi ? 'Tên Bot:' : 'Bot Name:', default: 'Chat Bot' });
103
+ const botDesc = await input({ message: isVi ? 'Mô tả Bot:' : 'Bot Description:', default: 'Personal AI assistant' });
104
+
105
+ // 6. Project Dir
106
+ let defaultDir = process.cwd();
107
+ if (!defaultDir.endsWith('openclaw-setup') && !defaultDir.endsWith('openclaw')) {
108
+ defaultDir = path.join(defaultDir, 'openclaw-setup');
109
+ }
110
+ const projectDir = await input({
111
+ message: isVi ? 'Thư mục cài đặt project:' : 'Project install directory:',
112
+ default: defaultDir
113
+ });
114
+
115
+ console.log(chalk.cyan(`\n🚀 ${isVi ? 'Đang tạo thư mục và file cấu hình...' : 'Generating directories and configurations...'}`));
116
+
117
+ await fs.ensureDir(projectDir);
118
+ await fs.ensureDir(path.join(projectDir, '.openclaw'));
119
+ await fs.ensureDir(path.join(projectDir, 'docker', 'openclaw'));
120
+
121
+ // ================= GENERATE FILES =================
122
+ let envContent = '';
123
+ if (provider.isLocal) {
124
+ envContent += 'OLLAMA_HOST=http://host.docker.internal:11434\n';
125
+ } else if (!provider.isProxy) {
126
+ envContent += `${provider.envKey}=${providerKeyVal}\n`;
127
+ }
128
+
129
+ if (channelKey === 'telegram') {
130
+ envContent += `TELEGRAM_BOT_TOKEN=${botToken}\n`;
131
+ } else if (channelKey === 'zalo-bot') {
132
+ envContent += `ZALO_APP_ID=\nZALO_APP_SECRET=\nZALO_BOT_TOKEN=${botToken}\n`;
133
+ }
134
+
135
+ if (selectedSkills.includes('tavily') && tavilyKey) {
136
+ envContent += `\n# --- Web Search ---\nTAVILY_API_KEY=${tavilyKey}\n`;
137
+ }
138
+ if (selectedSkills.includes('tts')) {
139
+ envContent += `\n# --- Text-To-Speech ---\n`;
140
+ if (ttsOpenaiKey) envContent += `OPENAI_API_KEY=${ttsOpenaiKey}\n`;
141
+ if (ttsElevenKey) envContent += `ELEVENLABS_API_KEY=${ttsElevenKey}\n`;
142
+ }
143
+ await fs.writeFile(path.join(projectDir, 'docker', 'openclaw', '.env'), envContent);
144
+
145
+ const dockerfile = `FROM ghcr.io/williamskyze/openclaw:latest
146
+ ${selectedSkills.includes('browser') ? 'RUN apt-get update && apt-get install -y chromium xvfb dbus socat' : '# (No browser config needed)'}`;
147
+ await fs.writeFile(path.join(projectDir, 'docker', 'openclaw', 'Dockerfile'), dockerfile);
148
+
149
+ const compose = `services:
150
+ openclaw:
151
+ build: .
152
+ restart: always
153
+ env_file: .env
154
+ volumes:
155
+ - ../../.openclaw:/app/.openclaw
156
+ - ../../auth-profiles.json:/app/auth-profiles.json
157
+ ${selectedSkills.includes('browser') ? ` extra_hosts:
158
+ - "host.docker.internal:host-gateway"
159
+ ` : ''}`;
160
+ await fs.writeFile(path.join(projectDir, 'docker', 'openclaw', 'docker-compose.yml'), compose);
161
+
162
+ await fs.writeFile(path.join(projectDir, 'auth-profiles.json'), '{}');
163
+
164
+ const botConfig = {
165
+ meta: { version: '1.0' },
166
+ agents: {
167
+ defaults: {
168
+ model: { primary: providerKey === 'openai' ? 'gpt-4o' : (providerKey === 'google' ? 'google/gemini-2.5-flash' : 'smart-route'), fallbacks: [] },
169
+ identity: {
170
+ name: botName,
171
+ description: botDesc,
172
+ system_prompt: isVi ? "Bạn là một trợ lý AI hữu ích." : "You are a helpful AI assistant."
173
+ },
174
+ skills: selectedSkills
175
+ }
176
+ },
177
+ channels: {}
178
+ };
179
+
180
+ if (channelKey === 'telegram') {
181
+ botConfig.channels['telegram'] = { enabled: true };
182
+ } else if (channelKey === 'zalo-personal') {
183
+ botConfig.channels['zalo-personal'] = { enabled: true, autoReply: true };
184
+ } else if (channelKey === 'zalo-bot') {
185
+ botConfig.channels['zalo-bot'] = { enabled: true };
186
+ }
187
+
188
+ await fs.writeJson(path.join(projectDir, '.openclaw', 'openclaw.json'), botConfig, { spaces: 2 });
189
+
190
+ if (selectedSkills.includes('browser')) {
191
+ const batPath = path.join(projectDir, 'start-chrome-debug.bat');
192
+ await fs.writeFile(batPath, `@echo off\necho OpenClaw Chrome Debug\nstart "" "C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe" --remote-debugging-port=9222 --remote-allow-origins=* --user-data-dir="%TEMP%\\chrome-debug"\npause`);
193
+ }
194
+
195
+ console.log(chalk.green(`✅ ${isVi ? 'Tạo cấu hình thành công!' : 'Configs created successfully!'}`));
196
+
197
+ // 7. Auto Run
198
+ const autoRun = await confirm({
199
+ message: isVi ? 'Bạn có muốn tự động tải Docker và khởi động Bot luôn không?' : 'Do you want to run Docker compose and start the bot now?',
200
+ default: true
201
+ });
202
+
203
+ if (autoRun) {
204
+ console.log(chalk.yellow(`\n🐳 ${isVi ? 'Đang khởi động Docker (có thể mất vài phút)...' : 'Starting Docker (might take a few minutes)...'}`));
205
+ const dockerPath = path.join(projectDir, 'docker', 'openclaw');
206
+
207
+ const child = spawn('docker', ['compose', 'up', '-d', '--build'], {
208
+ cwd: dockerPath,
209
+ stdio: 'inherit'
210
+ });
211
+
212
+ child.on('close', (code) => {
213
+ if (code === 0) {
214
+ console.log(chalk.green(`\n🎉 ${isVi ? 'Setup hoàn tất! Bot đang chạy.' : 'Setup complete! Bot is running.'}`));
215
+ if (channelKey === 'zalo-personal') {
216
+ console.log(chalk.yellow(`\n📱 ${isVi ? 'Vui lòng chạy lệnh sau để đăng nhập Zalo Personal (1 lần duy nhất):' : 'Please run this command to login to Zalo Personal (once):'}`));
217
+ console.log(`cd ${projectDir} && docker compose exec -it openclaw bun run core:onboard`);
218
+ }
219
+ } else {
220
+ console.log(chalk.red(`\n❌ Docker exited with code ${code}`));
221
+ }
222
+ });
223
+ } else {
224
+ console.log(chalk.cyan(`\n👉 ${isVi ? 'Tiếp theo, hãy chạy:' : 'Next, run:'}\n cd ${projectDir}/docker/openclaw\n docker compose build\n docker compose up -d`));
225
+ }
226
+ }
227
+
228
+ main().catch(err => {
229
+ console.error(chalk.red('Error:'), err);
230
+ process.exit(1);
231
+ });
@@ -0,0 +1,14 @@
1
+ FROM node:22-slim
2
+
3
+ RUN apt-get update && apt-get install -y git curl socat && rm -rf /var/lib/apt/lists/*
4
+
5
+ RUN npm install -g openclaw@latest
6
+
7
+ # Browser Automation: Playwright engine (needed for native CDP)
8
+ RUN npm install -g agent-browser playwright && npx playwright install chromium --with-deps && ln -f -s /root/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /usr/bin/google-chrome
9
+
10
+ WORKDIR /root/.openclaw
11
+
12
+ EXPOSE 18791
13
+
14
+ CMD sh -c "node -e \"const fs=require('fs'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));c.tools=Object.assign({},c.tools,{profile:'full'});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'0.0.0.0'});fs.writeFileSync(p,JSON.stringify(c,null,2));}\" && socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 & openclaw gateway run"