create-openclaw-bot 5.1.10 → 5.1.12
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 +21 -1
- package/CHANGELOG.vi.md +20 -1
- package/README.md +3 -3
- package/README.vi.md +3 -3
- package/cli.js +7 -7
- package/package.json +1 -1
- package/setup.js +192 -116
- package/style.css +1 -1
- package/tests/smoke-cli-logic.mjs +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,6 +1,26 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog (English)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
## [5.1.12] — 2026-04-07
|
|
6
|
+
|
|
7
|
+
### 🧠 Expanded Skills & Auto-Select Multi-Bot Relay Plugin
|
|
8
|
+
|
|
9
|
+
- **3-Column Skill Grid**: Skill cards now display 3 per row instead of 4 — wider cards, better readability.
|
|
10
|
+
- **7 New ClawHub Skills**: Added `Web Search`, `GitHub`, `Google Calendar`, `Google Drive`, `Google Sheets`, `Notion`, `Slack` — covering the most common productivity workflows available on the OpenClaw dashboard.
|
|
11
|
+
- **Telegram Multi-Bot Relay Auto-Select**: When multiple Telegram bots are selected (botCount ≥ 2), the `telegram-multibot-relay` plugin is automatically checked and written to `openclaw.json → plugins.entries`. Switching back to 1 bot deselects it.
|
|
12
|
+
- **Plugin Selections → openclaw.json**: All plugins selected by the user (Voice Call, Matrix, MS Teams, Nostr...) are now injected into `plugins.entries` so the OpenClaw Dashboard receives the correct `enabled` state. Unselected = disabled.
|
|
13
|
+
- **Fix Step 3 "Next" disabled**: Removed mandatory `cfg-user-info` requirement (it's optional), fixed multi-bot validation to use `cfg-bot-tab-name`.
|
|
14
|
+
- **Fix Step 4 multi-bot token validation**: Now validates `key-bot-token-0` instead of `key-bot-token` in Telegram multi-bot mode.
|
|
15
|
+
- **Fix native multi-bot AGENTS.md missing security rules**: Security rules are now appended to each bot's AGENTS.md during native multi-bot deployment.
|
|
16
|
+
|
|
17
|
+
## [5.1.11] — 2026-04-07
|
|
18
|
+
|
|
19
|
+
### 🌟 Zalo Personal DM Policy
|
|
20
|
+
|
|
21
|
+
- **Open Zalo Inboxes**: The default `dmPolicy` for Zalo Personal deployments has been changed from `pairing` to `open`. This allows any user on the Zalo network to interact with the AI assistant immediately without requiring explicit device pairing approvals natively.
|
|
22
|
+
|
|
23
|
+
|
|
4
24
|
## [5.1.10] — 2026-04-07
|
|
5
25
|
|
|
6
26
|
### 🌟 Native UI Auto-Approve Bypasser
|
package/CHANGELOG.vi.md
CHANGED
|
@@ -1,6 +1,25 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog (Tiếng Việt)
|
|
2
2
|
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
## [5.1.12] — 2026-04-07
|
|
6
|
+
|
|
7
|
+
### 🧠 Thêm Skills & Tự động chọn Plugin Relay
|
|
8
|
+
|
|
9
|
+
- **Grid Skills 3 cột**: Layout mới 3 card/hàng thay vì 4, card rộng rãi hơn, dễ đọc hơn.
|
|
10
|
+
- **7 Skills mới từ ClawHub**: Bổ sung đầy đủ `Web Search`, `GitHub`, `Google Calendar`, `Google Drive`, `Google Sheets`, `Notion`, `Slack` — phủ khắp các tác vụ năng suất phổ biến nhất trên OpenClaw dashboard.
|
|
11
|
+
- **Plugin Telegram Multi-Bot Relay tự động**: Khi chọn nhiều bot Telegram (botCount ≥ 2), plugin `telegram-multibot-relay` được tự động tick chọn và ghi vào `openclaw.json → plugins.entries`. Khi quay về 1 bot, plugin bị bỏ chọn.
|
|
12
|
+
- **Plugin selections → openclaw.json**: Tất cả plugin được user chọn (Voice Call, Matrix, MS Teams, Nostr...) đều được inject vào `plugins.entries` để Dashboard OpenClaw nhận trạng thái `enabled` đúng. Không chọn = không bật.
|
|
13
|
+
- **Fix Step 3 "Tiếp theo" bị disabled**: Bỏ yêu cầu bắt buộc `cfg-user-info` (optional), sửa multi-bot check dùng `cfg-bot-tab-name`.
|
|
14
|
+
- **Fix Step 4 multi-bot token**: Validate `key-bot-token-0` thay vì `key-bot-token` khi multi-bot Telegram.
|
|
15
|
+
- **Fix AGENTS.md native multi-bot thiếu quy tắc bảo mật**: Inject `securityRules` vào cuối AGENTS.md của từng bot trong native multi-bot deployment.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### 🌟 Đổi Chính Sách Bảo Mật Zalo Personal
|
|
19
|
+
|
|
20
|
+
- **Thả Ga Inbox Zalo Cầm Tay**: Lược bỏ rào cản duyệt bảo mật của Zalo Personal. Thông số `dmPolicy` trên cài đặt Zalo cá nhân đã được chuyển mặc định từ `pairing` sang `open`. Bây giờ bất cứ ai trên mạng lưới Zalo nhắn tin vào tài khoản của Bot đều sẽ được AI tự động tiếp đón ngay lập tức thay vì bị chặn lại chờ bạn duyệt lệnh kết nối E2E!
|
|
21
|
+
|
|
22
|
+
|
|
4
23
|
## [5.1.10] — 2026-04-07
|
|
5
24
|
|
|
6
25
|
### 🌟 Tự động Auto-Approve Thiết Bị cho Native VPS
|
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🦞 OpenClaw Setup
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.
|
|
6
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.11-0EA5E9?style=for-the-badge" alt="Version 5.1.11" /></a>
|
|
7
7
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
9
9
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -24,7 +24,7 @@ An interactive **CLI tool** and **Setup Wizard** to deploy your own free AI Bot
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
## 🆕 What's new in v5.1.
|
|
27
|
+
## 🆕 What's new in v5.1.11
|
|
28
28
|
|
|
29
29
|
- 💻 **OS-First Setup** — Step 1 is now choosing your OS (Windows, macOS, Ubuntu, VPS). All scripts, configs, and instructions are generated to match.
|
|
30
30
|
- 🧠 **Gemma 4 — 4 sizes** — `gemma4:e2b` (~4 GB), `gemma4:e4b` (~8 GB), `gemma4:26b` (~18 GB), `gemma4:31b` (~24 GB). Auto-pulled on first launch.
|
|
@@ -112,7 +112,7 @@ Run in your terminal → follow the interactive prompts → startup script is ge
|
|
|
112
112
|
2. Open this repo as your workspace
|
|
113
113
|
3. Paste into chat:
|
|
114
114
|
```
|
|
115
|
-
Read SETUP.md and set up OpenClaw v5.1.
|
|
115
|
+
Read SETUP.md and set up OpenClaw v5.1.11 for me.
|
|
116
116
|
My bot token is X. Use 9Router (no API key).
|
|
117
117
|
My project folder: <YOUR_PATH>
|
|
118
118
|
```
|
package/README.vi.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🦞 OpenClaw Setup
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.
|
|
6
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.1.11-0EA5E9?style=for-the-badge" alt="Version 5.1.11" /></a>
|
|
7
7
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
9
9
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -24,7 +24,7 @@ Công cụ **CLI tương tác** và **Setup Wizard** để tự triển khai Bot
|
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
## 🆕 Có gì mới trong v5.1.
|
|
27
|
+
## 🆕 Có gì mới trong v5.1.11
|
|
28
28
|
|
|
29
29
|
- 💻 **OS-First Setup** — Bước đầu tiên bây giờ là chọn hệ điều hành của bạn (Windows, macOS, Ubuntu, VPS). Toàn bộ script, cấu hình và hướng dẫn được tạo ra phù hợp với lựa chọn đó.
|
|
30
30
|
- 🧠 **Gemma 4 — 4 kích thước** — `gemma4:e2b` (~4 GB), `gemma4:e4b` (~8 GB), `gemma4:26b` (~18 GB), `gemma4:31b` (~24 GB). Tự pull về khi bot khởi động lần đầu.
|
|
@@ -112,7 +112,7 @@ Chạy lệnh trên trong Terminal → làm theo các prompt tương tác → sc
|
|
|
112
112
|
2. Mở repo này làm workspace
|
|
113
113
|
3. Paste vào chat:
|
|
114
114
|
```
|
|
115
|
-
Read SETUP.md and set up OpenClaw v5.1.
|
|
115
|
+
Read SETUP.md and set up OpenClaw v5.1.11 for me.
|
|
116
116
|
My bot token is X. Use 9Router (no API key).
|
|
117
117
|
My project folder: <THƯ_MỤC_CỦA_BẠN>
|
|
118
118
|
```
|
package/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { input, select, checkbox, confirm } from '@inquirer/prompts';
|
|
4
4
|
import fs from 'fs-extra';
|
|
@@ -2104,12 +2104,12 @@ ${hasBrowserDesktop ? ` extra_hosts:
|
|
|
2104
2104
|
};
|
|
2105
2105
|
}
|
|
2106
2106
|
botConfig.channels['telegram'] = telegramConfig;
|
|
2107
|
-
} else if (channelKey === 'zalo-personal') {
|
|
2108
|
-
botConfig.channels['zalouser'] = {
|
|
2109
|
-
enabled: true,
|
|
2110
|
-
dmPolicy: '
|
|
2111
|
-
autoReply: true
|
|
2112
|
-
};
|
|
2107
|
+
} else if (channelKey === 'zalo-personal') {
|
|
2108
|
+
botConfig.channels['zalouser'] = {
|
|
2109
|
+
enabled: true,
|
|
2110
|
+
dmPolicy: 'open',
|
|
2111
|
+
autoReply: true
|
|
2112
|
+
};
|
|
2113
2113
|
} else if (channelKey === 'zalo-bot') {
|
|
2114
2114
|
botConfig.channels['zalo'] = { enabled: true, provider: 'official_account' };
|
|
2115
2115
|
}
|
package/package.json
CHANGED
package/setup.js
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
// ========== State ==========
|
|
27
|
-
const state = {
|
|
27
|
+
const state = {
|
|
28
28
|
currentStep: 1,
|
|
29
29
|
totalSteps: 5,
|
|
30
30
|
channel: null,
|
|
@@ -47,21 +47,21 @@
|
|
|
47
47
|
apiKey: '',
|
|
48
48
|
projectPath: '',
|
|
49
49
|
},
|
|
50
|
-
};
|
|
51
|
-
|
|
52
|
-
function getGatewayAllowedOrigins(port) {
|
|
53
|
-
const normalizedPort = Number(port) || 18791;
|
|
54
|
-
const origins = new Set([
|
|
55
|
-
`http://localhost:${normalizedPort}`,
|
|
56
|
-
`http://127.0.0.1:${normalizedPort}`,
|
|
57
|
-
`http://0.0.0.0:${normalizedPort}`,
|
|
58
|
-
]);
|
|
59
|
-
const currentHost = (window.location && window.location.hostname) ? window.location.hostname.trim() : '';
|
|
60
|
-
if (currentHost) {
|
|
61
|
-
origins.add(`http://${currentHost}:${normalizedPort}`);
|
|
62
|
-
}
|
|
63
|
-
return Array.from(origins);
|
|
64
|
-
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
function getGatewayAllowedOrigins(port) {
|
|
53
|
+
const normalizedPort = Number(port) || 18791;
|
|
54
|
+
const origins = new Set([
|
|
55
|
+
`http://localhost:${normalizedPort}`,
|
|
56
|
+
`http://127.0.0.1:${normalizedPort}`,
|
|
57
|
+
`http://0.0.0.0:${normalizedPort}`,
|
|
58
|
+
]);
|
|
59
|
+
const currentHost = (window.location && window.location.hostname) ? window.location.hostname.trim() : '';
|
|
60
|
+
if (currentHost) {
|
|
61
|
+
origins.add(`http://${currentHost}:${normalizedPort}`);
|
|
62
|
+
}
|
|
63
|
+
return Array.from(origins);
|
|
64
|
+
}
|
|
65
65
|
|
|
66
66
|
|
|
67
67
|
// ========== AI Providers & Models ==========
|
|
@@ -161,6 +161,14 @@
|
|
|
161
161
|
|
|
162
162
|
// ========== Available Plugins (npm packages — runtime/channel extensions) ==========
|
|
163
163
|
const PLUGINS = [
|
|
164
|
+
{
|
|
165
|
+
id: 'telegram-multibot-relay',
|
|
166
|
+
name: 'Telegram Multi-Bot Relay',
|
|
167
|
+
icon: '🤝',
|
|
168
|
+
descVi: 'Điều phối nhiều bot Telegram trong cùng group — tự động khi chọn nhiều bot', descEn: 'Coordinate multiple Telegram bots in one group — auto-selected with multi-bot',
|
|
169
|
+
package: 'telegram-multibot-relay',
|
|
170
|
+
hidden: true, // hidden in UI, auto-selected programmatically
|
|
171
|
+
},
|
|
164
172
|
{
|
|
165
173
|
id: 'voice-call',
|
|
166
174
|
name: 'Voice Call',
|
|
@@ -193,7 +201,6 @@
|
|
|
193
201
|
|
|
194
202
|
// ========== Available Skills (ClawHub registry — agent capabilities) ==========
|
|
195
203
|
const SKILLS = [
|
|
196
|
-
// Web Search removed — OpenClaw has native search built-in (no Tavily key needed)
|
|
197
204
|
{
|
|
198
205
|
id: 'browser',
|
|
199
206
|
name: 'Browser Automation ⭐(Khuyên dùng)',
|
|
@@ -227,7 +234,7 @@
|
|
|
227
234
|
id: 'image-gen',
|
|
228
235
|
name: 'Image Generation',
|
|
229
236
|
icon: '🎨',
|
|
230
|
-
descVi: 'Tạo ảnh bằng AI (DALL·E, Flux...)', descEn: 'Generate images
|
|
237
|
+
descVi: 'Tạo ảnh bằng AI (DALL·E, Flux, Midjourney...)', descEn: 'Generate images via AI (DALL-E, Flux, Midjourney...)',
|
|
231
238
|
slug: 'image-gen',
|
|
232
239
|
noteVi: 'Dùng chung OPENAI_API_KEY (DALL-E) hoặc thêm FLUX_API_KEY', noteEn: 'Uses OPENAI_API_KEY (DALL-E) or FLUX_API_KEY',
|
|
233
240
|
envVars: ['# FLUX_API_KEY=<your_flux_key> # chỉ cần nếu dùng Flux'],
|
|
@@ -243,11 +250,36 @@
|
|
|
243
250
|
id: 'email',
|
|
244
251
|
name: 'Email Assistant',
|
|
245
252
|
icon: '📧',
|
|
246
|
-
descVi: 'Quản lý, soạn, tóm tắt email', descEn: 'Manage, compose, summarize emails',
|
|
253
|
+
descVi: 'Quản lý, soạn, tóm tắt email (Gmail, Outlook...)', descEn: 'Manage, compose, summarize emails (Gmail, Outlook...)',
|
|
247
254
|
slug: 'email-assistant',
|
|
248
255
|
noteVi: 'Cần cấu hình SMTP trong .env', noteEn: 'Requires SMTP configuration in .env',
|
|
249
256
|
envVars: ['SMTP_HOST=smtp.gmail.com', 'SMTP_PORT=587', 'SMTP_USER=<your_email>', 'SMTP_PASS=<your_app_password>'],
|
|
250
257
|
},
|
|
258
|
+
{
|
|
259
|
+
id: 'web-search',
|
|
260
|
+
name: 'Web Search',
|
|
261
|
+
icon: '🔍',
|
|
262
|
+
descVi: 'Tìm kiếm web thời gian thực (DuckDuckGo) — không cần API key', descEn: 'Real-time web search (DuckDuckGo) — no API key needed',
|
|
263
|
+
slug: 'web-search',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
id: 'notion',
|
|
267
|
+
name: 'Notion',
|
|
268
|
+
icon: '📓',
|
|
269
|
+
descVi: 'Tạo, chỉnh sửa trang và database Notion', descEn: 'Create and edit Notion pages and databases',
|
|
270
|
+
slug: 'notion',
|
|
271
|
+
noteVi: 'Cần Notion Integration Token', noteEn: 'Requires Notion Integration Token',
|
|
272
|
+
envVars: ['NOTION_API_KEY=<your_notion_integration_token>'],
|
|
273
|
+
},
|
|
274
|
+
{
|
|
275
|
+
id: 'slack',
|
|
276
|
+
name: 'Slack',
|
|
277
|
+
icon: '🗨️',
|
|
278
|
+
descVi: 'Gửi tin, react, ghim tin nhắn trong Slack', descEn: 'Send messages, react, pin items in Slack',
|
|
279
|
+
slug: 'slack',
|
|
280
|
+
noteVi: 'Cần Slack Bot Token', noteEn: 'Requires Slack Bot Token',
|
|
281
|
+
envVars: ['SLACK_BOT_TOKEN=<your_slack_bot_token>'],
|
|
282
|
+
},
|
|
251
283
|
];
|
|
252
284
|
|
|
253
285
|
// ========== Channel definitions ==========
|
|
@@ -530,6 +562,24 @@
|
|
|
530
562
|
state.bots.push({ name: '', slashCmd: '', desc: '', provider: 'google', model: 'google/gemini-2.5-flash', token: '', apiKey: '' });
|
|
531
563
|
}
|
|
532
564
|
|
|
565
|
+
// Auto-select telegram-multibot-relay plugin when multi-bot, deselect when single
|
|
566
|
+
const relayId = 'telegram-multibot-relay';
|
|
567
|
+
if (count > 1) {
|
|
568
|
+
if (!state.config.plugins.includes(relayId)) {
|
|
569
|
+
state.config.plugins.push(relayId);
|
|
570
|
+
}
|
|
571
|
+
} else {
|
|
572
|
+
state.config.plugins = state.config.plugins.filter(p => p !== relayId);
|
|
573
|
+
}
|
|
574
|
+
// Sync relay card checkbox if already rendered
|
|
575
|
+
const relayCard = document.querySelector(`.plugin-card[data-plugin="${relayId}"]`);
|
|
576
|
+
if (relayCard) {
|
|
577
|
+
const isSelected = count > 1;
|
|
578
|
+
relayCard.classList.toggle('plugin-card--selected', isSelected);
|
|
579
|
+
const cb = relayCard.querySelector('input[type="checkbox"]');
|
|
580
|
+
if (cb) cb.checked = isSelected;
|
|
581
|
+
}
|
|
582
|
+
|
|
533
583
|
// Show/hide group option for 2+ bots
|
|
534
584
|
const groupOpt = document.getElementById('multibot-group-option');
|
|
535
585
|
if (groupOpt) groupOpt.style.display = count > 1 ? '' : 'none';
|
|
@@ -922,19 +972,30 @@
|
|
|
922
972
|
// Step 1 (env): always valid
|
|
923
973
|
// Step 2 (channel): require selection
|
|
924
974
|
if (state.currentStep === 2 && !state.channel) isDisabled = true;
|
|
925
|
-
// Step 3 (bot config): require bot name
|
|
975
|
+
// Step 3 (bot config): require at least one bot name
|
|
926
976
|
if (state.currentStep === 3) {
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
977
|
+
if (state.botCount > 1) {
|
|
978
|
+
// Multi-bot: require name for the currently active bot tab
|
|
979
|
+
const tabNameVal = document.getElementById('cfg-bot-tab-name')?.value?.trim();
|
|
980
|
+
if (!tabNameVal) isDisabled = true;
|
|
981
|
+
} else {
|
|
982
|
+
// Single bot: require cfg-name or the shared tab name field
|
|
983
|
+
const nameVal = document.getElementById('cfg-name')?.value?.trim()
|
|
984
|
+
|| document.getElementById('cfg-bot-tab-name')?.value?.trim();
|
|
985
|
+
if (!nameVal) isDisabled = true;
|
|
986
|
+
}
|
|
930
987
|
}
|
|
931
988
|
// Step 4 (api keys): require token/key
|
|
932
989
|
if (state.currentStep === 4) {
|
|
933
|
-
const botTokenEl = document.getElementById('key-bot-token');
|
|
934
990
|
const apiKeyEl = document.getElementById('key-api-key');
|
|
935
991
|
const provider = PROVIDERS[state.config.provider];
|
|
936
|
-
if (
|
|
937
|
-
|
|
992
|
+
if (state.channel === 'telegram' && state.botCount > 1) {
|
|
993
|
+
// Multi-bot Telegram: require at least the first bot's token
|
|
994
|
+
const firstTokenEl = document.getElementById('key-bot-token-0');
|
|
995
|
+
if (!firstTokenEl || !firstTokenEl.value.trim()) isDisabled = true;
|
|
996
|
+
} else if (state.channel === 'telegram' || state.channel === 'zalo-bot') {
|
|
997
|
+
const botTokenEl = document.getElementById('key-bot-token');
|
|
998
|
+
if (!botTokenEl || !botTokenEl.value.trim()) isDisabled = true;
|
|
938
999
|
}
|
|
939
1000
|
if (provider && !provider.isProxy && !provider.isLocal && provider.envKey && apiKeyEl) {
|
|
940
1001
|
if (!apiKeyEl.value.trim()) isDisabled = true;
|
|
@@ -1010,9 +1071,11 @@
|
|
|
1010
1071
|
}
|
|
1011
1072
|
|
|
1012
1073
|
// Plugins grid (npm packages — extra channels/extensions)
|
|
1074
|
+
// Filter out hidden plugins from user-facing grid
|
|
1075
|
+
const visiblePlugins = PLUGINS.filter((p) => !p.hidden);
|
|
1013
1076
|
const pluginGrid = document.getElementById('extra-plugin-grid');
|
|
1014
1077
|
if (pluginGrid) {
|
|
1015
|
-
pluginGrid.innerHTML =
|
|
1078
|
+
pluginGrid.innerHTML = visiblePlugins.map((p) => `
|
|
1016
1079
|
<label class="plugin-card" data-plugin="${p.id}">
|
|
1017
1080
|
<input type="checkbox" class="plugin-checkbox" value="${p.id}" onchange="window.__togglePlugin('${p.id}', this.checked)">
|
|
1018
1081
|
<div class="plugin-card__icon">${p.icon}</div>
|
|
@@ -1591,15 +1654,15 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
1591
1654
|
commands: { native: 'auto', nativeSkills: 'auto', restart: true, ownerDisplay: 'raw' },
|
|
1592
1655
|
channels: ch.channelConfig,
|
|
1593
1656
|
tools: { profile: 'full', exec: { host: 'gateway', security: 'full', ask: 'off' } },
|
|
1594
|
-
gateway: {
|
|
1595
|
-
port: 18791,
|
|
1596
|
-
mode: 'local',
|
|
1597
|
-
bind: '0.0.0.0',
|
|
1598
|
-
controlUi: {
|
|
1599
|
-
allowedOrigins: getGatewayAllowedOrigins(18791),
|
|
1600
|
-
},
|
|
1601
|
-
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
1602
|
-
},
|
|
1657
|
+
gateway: {
|
|
1658
|
+
port: 18791,
|
|
1659
|
+
mode: 'local',
|
|
1660
|
+
bind: '0.0.0.0',
|
|
1661
|
+
controlUi: {
|
|
1662
|
+
allowedOrigins: getGatewayAllowedOrigins(18791),
|
|
1663
|
+
},
|
|
1664
|
+
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
1665
|
+
},
|
|
1603
1666
|
};
|
|
1604
1667
|
|
|
1605
1668
|
// 9Router: add proxy endpoint config under models.providers
|
|
@@ -1740,6 +1803,17 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
1740
1803
|
'telegram-multibot-relay': { enabled: true },
|
|
1741
1804
|
},
|
|
1742
1805
|
};
|
|
1806
|
+
} else if (state.config.plugins.length > 0) {
|
|
1807
|
+
// Non-multibot: write selected visible plugins into openclaw.json
|
|
1808
|
+
const pluginEntries = {};
|
|
1809
|
+
state.config.plugins.forEach((pid) => {
|
|
1810
|
+
const plugin = PLUGINS.find((p) => p.id === pid);
|
|
1811
|
+
if (!plugin || plugin.hidden) return;
|
|
1812
|
+
pluginEntries[plugin.package || pid] = { enabled: true };
|
|
1813
|
+
});
|
|
1814
|
+
if (Object.keys(pluginEntries).length > 0) {
|
|
1815
|
+
clawConfig.plugins = { entries: pluginEntries };
|
|
1816
|
+
}
|
|
1743
1817
|
}
|
|
1744
1818
|
|
|
1745
1819
|
setOutput('out-openclaw-json', JSON.stringify(clawConfig, null, 2));
|
|
@@ -1775,19 +1849,19 @@ model:
|
|
|
1775
1849
|
// 3. Dockerfile
|
|
1776
1850
|
const allPlugins = [];
|
|
1777
1851
|
if (ch.pluginInstall) allPlugins.push(ch.pluginInstall);
|
|
1778
|
-
const encodeBase64Utf8 = (value) => btoa(String.fromCharCode(...new TextEncoder().encode(String(value))));
|
|
1779
|
-
const indentBlock = (text, spaces) => {
|
|
1780
|
-
const prefix = ' '.repeat(spaces);
|
|
1781
|
-
return String(text).split('\n').map((line) => `${prefix}${line}`).join('\n');
|
|
1782
|
-
};
|
|
1783
|
-
const build9RouterComposeEntrypointScript = (syncScriptBase64) => [
|
|
1784
|
-
'npm install -g 9router',
|
|
1785
|
-
`node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('${syncScriptBase64}','base64').toString())"`,
|
|
1786
|
-
'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
|
|
1787
|
-
'exec 9router -n -l -H 0.0.0.0 -p 20128 --skip-update'
|
|
1788
|
-
].join('\n');
|
|
1789
|
-
|
|
1790
|
-
state.config.plugins.forEach((pid) => {
|
|
1852
|
+
const encodeBase64Utf8 = (value) => btoa(String.fromCharCode(...new TextEncoder().encode(String(value))));
|
|
1853
|
+
const indentBlock = (text, spaces) => {
|
|
1854
|
+
const prefix = ' '.repeat(spaces);
|
|
1855
|
+
return String(text).split('\n').map((line) => `${prefix}${line}`).join('\n');
|
|
1856
|
+
};
|
|
1857
|
+
const build9RouterComposeEntrypointScript = (syncScriptBase64) => [
|
|
1858
|
+
'npm install -g 9router',
|
|
1859
|
+
`node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('${syncScriptBase64}','base64').toString())"`,
|
|
1860
|
+
'node /tmp/sync.js > /tmp/sync.log 2>&1 &',
|
|
1861
|
+
'exec 9router -n -l -H 0.0.0.0 -p 20128 --skip-update'
|
|
1862
|
+
].join('\n');
|
|
1863
|
+
|
|
1864
|
+
state.config.plugins.forEach((pid) => {
|
|
1791
1865
|
const plug = PLUGINS.find((p) => p.id === pid);
|
|
1792
1866
|
if (plug) allPlugins.push(plug.package);
|
|
1793
1867
|
});
|
|
@@ -1832,7 +1906,7 @@ model:
|
|
|
1832
1906
|
? 'socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 & '
|
|
1833
1907
|
: '';
|
|
1834
1908
|
// Patch config on every startup to keep gateway settings stable
|
|
1835
|
-
const patchCmd = `node -e \\"const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add(\\\`http://\\\${entry.address}:18791\\\`);}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a)})});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\" && `;
|
|
1909
|
+
const patchCmd = `node -e \\"const fs=require('fs'),os=require('os'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));const a=new Set(['http://localhost:18791','http://127.0.0.1:18791','http://0.0.0.0:18791']);for(const entries of Object.values(os.networkInterfaces()||{})){for(const entry of entries||[]){if(!entry||entry.internal||entry.family!=='IPv4'||!entry.address)continue;a.add(\\\`http://\\\${entry.address}:18791\\\`);}}c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0',controlUi:Object.assign({},c.gateway?.controlUi,{allowedOrigins:Array.from(a)})});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\" && `;
|
|
1836
1910
|
// Auto-approve device pairing after gateway starts (required since v2026.3.x)
|
|
1837
1911
|
const autoApproveCmd = '(while true; do sleep 5; openclaw devices approve --latest 2>/dev/null || true; done) & ';
|
|
1838
1912
|
const finalCmd = `CMD sh -c "${pluginInstallCmd}${patchCmd}${browserPrefix}${autoApproveCmd}${gatewayCmd}"`;
|
|
@@ -1844,7 +1918,7 @@ RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /v
|
|
|
1844
1918
|
|
|
1845
1919
|
ARG CACHEBUST=${Date.now()}
|
|
1846
1920
|
RUN npm install -g openclaw@latest${skillLines}${browserInstallLines}
|
|
1847
|
-
RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const files=fs.readdirSync(dir).filter(n=>/\\.js$/.test(n));let patched=0;for(const file of files){const p=path.join(dir,file);let s='';try{s=fs.readFileSync(p,'utf8');}catch{continue;}if(s.includes(to)||!s.includes(from))continue;s=s.replace(from,to);fs.writeFileSync(p,s);patched++;}if(!patched){process.exit(0);}"
|
|
1921
|
+
RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const files=fs.readdirSync(dir).filter(n=>/\\.js$/.test(n));let patched=0;for(const file of files){const p=path.join(dir,file);let s='';try{s=fs.readFileSync(p,'utf8');}catch{continue;}if(s.includes(to)||!s.includes(from))continue;s=s.replace(from,to);fs.writeFileSync(p,s);patched++;}if(!patched){process.exit(0);}"
|
|
1848
1922
|
WORKDIR /root/.openclaw
|
|
1849
1923
|
|
|
1850
1924
|
EXPOSE 18791
|
|
@@ -1863,7 +1937,7 @@ ${finalCmd}`;
|
|
|
1863
1937
|
// Background loop inside 9Router container every 30s.
|
|
1864
1938
|
// Read providerConnections directly from db.json so smart-route survives
|
|
1865
1939
|
// dashboard auth/response changes in newer 9Router builds.
|
|
1866
|
-
const syncScript = `const fs=require('fs');const INTERVAL=30000;const p='/root/.9router/db.json';
|
|
1940
|
+
const syncScript = `const fs=require('fs');const INTERVAL=30000;const p='/root/.9router/db.json';
|
|
1867
1941
|
const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],'claude-code':['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],cline:['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],iflow:['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],qwen:['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],kiro:['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],ollama:['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],glm:['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],minimax:['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],kimi:['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],deepseek:['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],xai:['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],mistral:['mistral/mistral-large-latest','mistral/codestral-latest'],groq:['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],cerebras:['cerebras/gpt-oss-120b'],alicode:['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],openai:['openai/gpt-4o','openai/gpt-4.1'],anthropic:['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],gemini:['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
|
|
1868
1942
|
console.log('[sync-combo] 9Router sync loop started...');
|
|
1869
1943
|
const sync = async () => {
|
|
@@ -1913,11 +1987,11 @@ const sync = async () => {
|
|
|
1913
1987
|
console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models');
|
|
1914
1988
|
}
|
|
1915
1989
|
} catch (e) { }
|
|
1916
|
-
};
|
|
1917
|
-
setTimeout(sync, 5000);
|
|
1918
|
-
setInterval(sync, INTERVAL);`;
|
|
1919
|
-
const syncScriptBase64 = encodeBase64Utf8(syncScript);
|
|
1920
|
-
const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(syncScriptBase64);
|
|
1990
|
+
};
|
|
1991
|
+
setTimeout(sync, 5000);
|
|
1992
|
+
setInterval(sync, INTERVAL);`;
|
|
1993
|
+
const syncScriptBase64 = encodeBase64Utf8(syncScript);
|
|
1994
|
+
const docker9RouterEntrypointScript = build9RouterComposeEntrypointScript(syncScriptBase64);
|
|
1921
1995
|
|
|
1922
1996
|
let compose;
|
|
1923
1997
|
if (isMultiBotWizard) {
|
|
@@ -1946,14 +2020,14 @@ ${dependsOn}${extraHosts} volumes:
|
|
|
1946
2020
|
image: node:22-slim
|
|
1947
2021
|
container_name: 9router-multibot
|
|
1948
2022
|
restart: always
|
|
1949
|
-
entrypoint:
|
|
1950
|
-
- /bin/sh
|
|
1951
|
-
- -c
|
|
1952
|
-
- |
|
|
1953
|
-
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
1954
|
-
environment:
|
|
1955
|
-
- PORT=20128
|
|
1956
|
-
- HOSTNAME=0.0.0.0
|
|
2023
|
+
entrypoint:
|
|
2024
|
+
- /bin/sh
|
|
2025
|
+
- -c
|
|
2026
|
+
- |
|
|
2027
|
+
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
2028
|
+
environment:
|
|
2029
|
+
- PORT=20128
|
|
2030
|
+
- HOSTNAME=0.0.0.0
|
|
1957
2031
|
- CI=true
|
|
1958
2032
|
volumes:
|
|
1959
2033
|
- 9router-data:/root/.9router
|
|
@@ -2039,14 +2113,14 @@ ${extraHostsBlock}
|
|
|
2039
2113
|
image: node:22-slim
|
|
2040
2114
|
container_name: 9router
|
|
2041
2115
|
restart: always
|
|
2042
|
-
entrypoint:
|
|
2043
|
-
- /bin/sh
|
|
2044
|
-
- -c
|
|
2045
|
-
- |
|
|
2046
|
-
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
2047
|
-
environment:
|
|
2048
|
-
- PORT=20128
|
|
2049
|
-
- HOSTNAME=0.0.0.0
|
|
2116
|
+
entrypoint:
|
|
2117
|
+
- /bin/sh
|
|
2118
|
+
- -c
|
|
2119
|
+
- |
|
|
2120
|
+
${indentBlock(docker9RouterEntrypointScript, 8)}
|
|
2121
|
+
environment:
|
|
2122
|
+
- PORT=20128
|
|
2123
|
+
- HOSTNAME=0.0.0.0
|
|
2050
2124
|
- CI=true
|
|
2051
2125
|
volumes:
|
|
2052
2126
|
- 9router-data:/root/.9router
|
|
@@ -2885,17 +2959,17 @@ const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catc
|
|
|
2885
2959
|
// ─── Shared initializer (provider install) ───────────────────────────────
|
|
2886
2960
|
function providerLines(arr, shell) {
|
|
2887
2961
|
if (is9Router) {
|
|
2888
|
-
if (shell === 'bat') {
|
|
2889
|
-
arr.push('npm install -g 9router');
|
|
2890
|
-
arr.push('start "9Router" cmd /k "9router -n -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2891
|
-
arr.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
|
|
2892
|
-
arr.push('timeout /t 5 /nobreak >nul');
|
|
2893
|
-
} else {
|
|
2894
|
-
arr.push('npm install -g 9router');
|
|
2895
|
-
arr.push('nohup env PORT=20128 HOSTNAME=0.0.0.0 node "$(npm root -g)/9router/app/server.js" >/tmp/9router.log 2>&1 &');
|
|
2896
|
-
arr.push('nohup node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &');
|
|
2897
|
-
arr.push('sleep 3');
|
|
2898
|
-
}
|
|
2962
|
+
if (shell === 'bat') {
|
|
2963
|
+
arr.push('npm install -g 9router');
|
|
2964
|
+
arr.push('start "9Router" cmd /k "9router -n -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2965
|
+
arr.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
|
|
2966
|
+
arr.push('timeout /t 5 /nobreak >nul');
|
|
2967
|
+
} else {
|
|
2968
|
+
arr.push('npm install -g 9router');
|
|
2969
|
+
arr.push('nohup env PORT=20128 HOSTNAME=0.0.0.0 node "$(npm root -g)/9router/app/server.js" >/tmp/9router.log 2>&1 &');
|
|
2970
|
+
arr.push('nohup node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &');
|
|
2971
|
+
arr.push('sleep 3');
|
|
2972
|
+
}
|
|
2899
2973
|
} else if (isOllama) {
|
|
2900
2974
|
if (shell === 'bat') {
|
|
2901
2975
|
arr.push('where ollama >nul 2>&1 || (powershell -Command "Invoke-WebRequest -Uri https://ollama.com/download/OllamaSetup.exe -OutFile OllamaSetup.exe" && OllamaSetup.exe && del OllamaSetup.exe)');
|
|
@@ -3053,15 +3127,15 @@ const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catc
|
|
|
3053
3127
|
'telegram-multibot-relay': { enabled: true },
|
|
3054
3128
|
},
|
|
3055
3129
|
},
|
|
3056
|
-
gateway: {
|
|
3057
|
-
port: 18791,
|
|
3058
|
-
mode: 'local',
|
|
3059
|
-
bind: '0.0.0.0',
|
|
3060
|
-
controlUi: {
|
|
3061
|
-
allowedOrigins: getGatewayAllowedOrigins(18791),
|
|
3062
|
-
},
|
|
3063
|
-
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
3064
|
-
},
|
|
3130
|
+
gateway: {
|
|
3131
|
+
port: 18791,
|
|
3132
|
+
mode: 'local',
|
|
3133
|
+
bind: '0.0.0.0',
|
|
3134
|
+
controlUi: {
|
|
3135
|
+
allowedOrigins: getGatewayAllowedOrigins(18791),
|
|
3136
|
+
},
|
|
3137
|
+
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
3138
|
+
},
|
|
3065
3139
|
};
|
|
3066
3140
|
return JSON.stringify(cfg, null, 2);
|
|
3067
3141
|
}
|
|
@@ -3103,9 +3177,11 @@ const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catc
|
|
|
3103
3177
|
files[`.openclaw/${meta.workspaceDir}/SOUL.md`] = isVi
|
|
3104
3178
|
? `# Tinh cach\n\n${meta.persona || 'Huu ich that su, gan gui, ngan gon.'}`
|
|
3105
3179
|
: `# Soul\n\n${meta.persona || 'Helpful, concise, and practical.'}`;
|
|
3180
|
+
const _secRulesForBot = state.config.securityRules || DEFAULT_SECURITY_RULES[isVi ? 'vi' : 'en'];
|
|
3106
3181
|
files[`.openclaw/${meta.workspaceDir}/AGENTS.md`] = (isVi
|
|
3107
|
-
? `# Huong dan van hanh\n\n## Vai tro\nBan la **${meta.name}**, ${meta.desc.toLowerCase()}.\n\n## Khi nao nen tra loi\n- Trong group, xem user dang goi ban neu tin nhan co alias cua ban: ${ownAliases.map((alias) => `\`${alias}\``).join(', ')}\n- Neu user tag username Telegram cua ban thi luon tra loi.\n- Gateway se tu dong tha ack \`👍\` khi nhan message; khong can tu tha them neu ack da hien.\n- Neu user dang goi ro bot khac ${relayTargetNames} thi khong cuop loi.\n\n## Phoi hop\n- Dung \`TEAM.md\` lam nguon su that.\n- Neu user bao ban hoi hoac xin y kien tu ${otherBots.length ? otherBots.map((peer) => `\`${peer.name}\``).join(', ') : '`bot khac`'} thi dung agent-to-agent noi bo ngay trong turn hien tai.\n- Neu ban la bot mo loi, chi gui 1 cau mo dau ngan roi handoff ngay. Khong tu noi thay bot dich tru khi handoff that bai ro rang.\n- Khi handoff, phai goi dung agent id ky thuat ${relayTargetIds}, khong dung ten hien thi.\n- Neu ban la bot dich nhan handoff, hay tra loi cong khai ngay trong cung Telegram chat/thread bang chinh account cua minh. Uu tien tra loi co \`[[reply_to_current]]\`; neu can, dung Telegram send/sendMessage action thay vi chi output thuong.\n- Khong bao user phai tag lai bot kia neu ban co the hoi noi bo duoc
|
|
3108
|
-
: `# Operating Manual\n\n## Role\nYou are **${meta.name}**, ${meta.desc.toLowerCase()}.\n\n## When To Reply\n- In group chats, treat the message as addressed to you if it contains one of your aliases: ${ownAliases.map((alias) => `\`${alias}\``).join(', ')}\n- Always reply when your Telegram username is tagged.\n- The gateway auto-sends the \`👍\` ack reaction on inbound messages; do not duplicate it manually if it already appeared.\n- If the message is clearly calling another bot such as ${relayTargetNames}, do not hijack it.\n\n## Coordination\n- Use \`TEAM.md\` as the source of truth.\n- If the user asks you to consult ${otherBots.length ? otherBots.map((peer) => `\`${peer.name}\``).join(', ') : '`another bot`'}, use internal agent-to-agent handoff in the same turn.\n- If you are the caller bot, send only one short opener then hand off immediately. Do not speak for the target bot unless the handoff clearly fails.\n- When handing off, use the exact technical agent id ${relayTargetIds}, not the display name.\n- If you are the target bot receiving a handoff, publish the real answer into the same Telegram chat/thread from your own account. Prefer replying with \`[[reply_to_current]]\`; if needed, use the Telegram send/sendMessage action instead of plain assistant output.\n- Do not ask the user to tag the other bot again if you can consult internally
|
|
3182
|
+
? `# Huong dan van hanh\n\n## Vai tro\nBan la **${meta.name}**, ${meta.desc.toLowerCase()}.\n\n## Khi nao nen tra loi\n- Trong group, xem user dang goi ban neu tin nhan co alias cua ban: ${ownAliases.map((alias) => `\`${alias}\``).join(', ')}\n- Neu user tag username Telegram cua ban thi luon tra loi.\n- Gateway se tu dong tha ack \`👍\` khi nhan message; khong can tu tha them neu ack da hien.\n- Neu user dang goi ro bot khac ${relayTargetNames} thi khong cuop loi.\n\n## Phoi hop\n- Dung \`TEAM.md\` lam nguon su that.\n- Neu user bao ban hoi hoac xin y kien tu ${otherBots.length ? otherBots.map((peer) => `\`${peer.name}\``).join(', ') : '`bot khac`'} thi dung agent-to-agent noi bo ngay trong turn hien tai.\n- Neu ban la bot mo loi, chi gui 1 cau mo dau ngan roi handoff ngay. Khong tu noi thay bot dich tru khi handoff that bai ro rang.\n- Khi handoff, phai goi dung agent id ky thuat ${relayTargetIds}, khong dung ten hien thi.\n- Neu ban la bot dich nhan handoff, hay tra loi cong khai ngay trong cung Telegram chat/thread bang chinh account cua minh. Uu tien tra loi co \`[[reply_to_current]]\`; neu can, dung Telegram send/sendMessage action thay vi chi output thuong.\n- Khong bao user phai tag lai bot kia neu ban co the hoi noi bo duoc.\n\n${_secRulesForBot}`
|
|
3183
|
+
: `# Operating Manual\n\n## Role\nYou are **${meta.name}**, ${meta.desc.toLowerCase()}.\n\n## When To Reply\n- In group chats, treat the message as addressed to you if it contains one of your aliases: ${ownAliases.map((alias) => `\`${alias}\``).join(', ')}\n- Always reply when your Telegram username is tagged.\n- The gateway auto-sends the \`👍\` ack reaction on inbound messages; do not duplicate it manually if it already appeared.\n- If the message is clearly calling another bot such as ${relayTargetNames}, do not hijack it.\n\n## Coordination\n- Use \`TEAM.md\` as the source of truth.\n- If the user asks you to consult ${otherBots.length ? otherBots.map((peer) => `\`${peer.name}\``).join(', ') : '`another bot`'}, use internal agent-to-agent handoff in the same turn.\n- If you are the caller bot, send only one short opener then hand off immediately. Do not speak for the target bot unless the handoff clearly fails.\n- When handing off, use the exact technical agent id ${relayTargetIds}, not the display name.\n- If you are the target bot receiving a handoff, publish the real answer into the same Telegram chat/thread from your own account. Prefer replying with \`[[reply_to_current]]\`; if needed, use the Telegram send/sendMessage action instead of plain assistant output.\n- Do not ask the user to tag the other bot again if you can consult internally.\n\n${_secRulesForBot}`);
|
|
3184
|
+
|
|
3109
3185
|
files[`.openclaw/${meta.workspaceDir}/TEAM.md`] = teamMd;
|
|
3110
3186
|
files[`.openclaw/${meta.workspaceDir}/RELAY.md`] = isVi
|
|
3111
3187
|
? `# Telegram Relay Playbook\n\n## Muc tieu\n- Cho phep bot mo loi goi bot dich noi bo, sau do bot dich tra loi cong khai bang chinh account cua minh.\n\n## Protocol\n1. Bot mo loi gui 1 cau ngan xac nhan se hoi bot dich.\n2. Bot mo loi handoff noi bo bang dung agent id trong \`TEAM.md\`.\n3. Bot dich tra loi cong khai trong cung chat/thread hien tai.\n4. Neu thay \`[[reply_to_current]]\` hoac Telegram send/sendMessage action kha dung, uu tien dung de bam dung message goc.\n5. Neu handoff that bai ro rang, chi bot mo loi moi duoc fallback tom tat.\n`
|
|
@@ -3169,15 +3245,15 @@ const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catc
|
|
|
3169
3245
|
},
|
|
3170
3246
|
commands: { native: 'auto', nativeSkills: 'auto', restart: true },
|
|
3171
3247
|
channels: channelConfig,
|
|
3172
|
-
gateway: {
|
|
3173
|
-
port: basePort,
|
|
3174
|
-
mode: 'local',
|
|
3175
|
-
bind: '0.0.0.0',
|
|
3176
|
-
controlUi: {
|
|
3177
|
-
allowedOrigins: getGatewayAllowedOrigins(basePort),
|
|
3178
|
-
},
|
|
3179
|
-
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
3180
|
-
},
|
|
3248
|
+
gateway: {
|
|
3249
|
+
port: basePort,
|
|
3250
|
+
mode: 'local',
|
|
3251
|
+
bind: '0.0.0.0',
|
|
3252
|
+
controlUi: {
|
|
3253
|
+
allowedOrigins: getGatewayAllowedOrigins(basePort),
|
|
3254
|
+
},
|
|
3255
|
+
auth: { mode: 'token', token: crypto.randomUUID().replace(/-/g, '') },
|
|
3256
|
+
},
|
|
3181
3257
|
|
|
3182
3258
|
};
|
|
3183
3259
|
return JSON.stringify(cfg, null, 2);
|
|
@@ -3556,12 +3632,12 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3556
3632
|
|
|
3557
3633
|
if (isMultiBot) {
|
|
3558
3634
|
vps.push('echo "--- Creating shared multi-agent runtime ---"');
|
|
3559
|
-
appendShWriteCommands(vps, sharedNativeFileMap());
|
|
3560
|
-
vps.push('echo "--- Starting shared gateway via PM2 ---"');
|
|
3561
|
-
if (is9Router) {
|
|
3562
|
-
vps.push('PORT=20128 HOSTNAME=0.0.0.0 pm2 start "$(npm root -g)/9router/app/server.js" --name openclaw-multibot-9router --interpreter "$(command -v node)"');
|
|
3563
|
-
vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3564
|
-
}
|
|
3635
|
+
appendShWriteCommands(vps, sharedNativeFileMap());
|
|
3636
|
+
vps.push('echo "--- Starting shared gateway via PM2 ---"');
|
|
3637
|
+
if (is9Router) {
|
|
3638
|
+
vps.push('PORT=20128 HOSTNAME=0.0.0.0 pm2 start "$(npm root -g)/9router/app/server.js" --name openclaw-multibot-9router --interpreter "$(command -v node)"');
|
|
3639
|
+
vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3640
|
+
}
|
|
3565
3641
|
vps.push('pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"');
|
|
3566
3642
|
vps.push('pm2 save && pm2 startup');
|
|
3567
3643
|
vps.push(`echo ""`);
|
|
@@ -3569,12 +3645,12 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3569
3645
|
vps.push(`echo "Commands:"`);
|
|
3570
3646
|
vps.push(`echo " pm2 status # Status gateway"`);
|
|
3571
3647
|
vps.push(`echo " pm2 logs openclaw-multibot"`);
|
|
3572
|
-
} else {
|
|
3573
|
-
appendShWriteCommands(vps, botFiles(0));
|
|
3574
|
-
if (is9Router) {
|
|
3575
|
-
vps.push('PORT=20128 HOSTNAME=0.0.0.0 pm2 start "$(npm root -g)/9router/app/server.js" --name openclaw-9router --interpreter "$(command -v node)"');
|
|
3576
|
-
vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3577
|
-
}
|
|
3648
|
+
} else {
|
|
3649
|
+
appendShWriteCommands(vps, botFiles(0));
|
|
3650
|
+
if (is9Router) {
|
|
3651
|
+
vps.push('PORT=20128 HOSTNAME=0.0.0.0 pm2 start "$(npm root -g)/9router/app/server.js" --name openclaw-9router --interpreter "$(command -v node)"');
|
|
3652
|
+
vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3653
|
+
}
|
|
3578
3654
|
vps.push('pm2 start --name openclaw -- sh -c "openclaw gateway run"');
|
|
3579
3655
|
vps.push('pm2 save && pm2 startup');
|
|
3580
3656
|
vps.push('echo "Bot dang chay! Xem log: pm2 logs openclaw"');
|
package/style.css
CHANGED
|
@@ -218,7 +218,7 @@ checks.push(() => expectMatch(
|
|
|
218
218
|
|
|
219
219
|
checks.push(() => expectMatch(
|
|
220
220
|
cli,
|
|
221
|
-
/channelKey === 'zalo-personal'\) \{\s*botConfig\.channels\['zalouser'\] = \{\s*enabled: true,\s*dmPolicy: '
|
|
221
|
+
/channelKey === 'zalo-personal'\) \{\s*botConfig\.channels\['zalouser'\] = \{\s*enabled: true,\s*dmPolicy: 'open',\s*autoReply: true/s,
|
|
222
222
|
'CLI must configure Zalo Personal under channels.zalouser'
|
|
223
223
|
));
|
|
224
224
|
|