create-openclaw-bot 5.6.7 → 5.6.10

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/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.5.0-0EA5E9?style=for-the-badge" alt="Version 5.5.0" /></a>
6
+ <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.6.10-0EA5E9?style=for-the-badge" alt="Version 5.6.9" /></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,24 +24,23 @@ 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.6.0
28
-
29
- - 🧠 **Memory & Dreaming enabled by default** — Long-term Memory skill is now pre-selected for all new installations. The `memory-core` plugin with `dreaming.enabled: true` is auto-injected into `openclaw.json`, and `DREAMS.md` is seeded in every workspace.
30
- - 🤝 **Relay plugin card auto-shows** — When selecting Telegram multi-bot (≥2 bots), the Relay plugin card appears with an "Auto-enabled" badge and locked checkbox. Switching back to 1 bot hides it.
31
- - 🔑 **Relay trigger keywords in TEAMS.md** — `TEAMS.md` now documents all relay trigger keywords (question/task/reminder patterns) from the v5.0.9 relay plugin, helping bots understand and coordinate cross-bot communication.
32
- - 🌍 **Proper Vietnamese diacritics** — All workspace `.md` files now use proper UTF-8 Vietnamese with full diacritics, eliminating mojibake.
33
- - 👍 **Tool-based reaction** — `TOOLS.md` mandates bots call the `react` action with 👍 before replying, replacing unreliable gateway auto-ack.
34
-
35
- <details>
36
- <summary><b>Previous: What's new in v5.5.0</b></summary>
37
-
38
- - 📦 **OpenClaw 2026.4.14** — Bumped from 2026.4.5. Includes the new experimental Dreaming memory system, active-memory improvements, Telegram approval button deadlock fix, and WebSocket keepalive stability.
39
- - 📄 **Modular workspace docs** — Multi-bot deployments now generate `TEAM.md` and `RELAY.md` as separate files.
40
- - 🔐 **Full security rules everywhere** — All `AGENTS.md` files now include the complete 4-section security ruleset.
41
- - 🗑️ **Removed `.yaml` agent files** OpenClaw Core reads config exclusively from `openclaw.json`.
42
- - 🤖 **Anti-hallucination handoff** — Relay-variant `AGENTS.md` now mandates tool-first `react` actions.
43
- - 🏗️ **Scaffold-based generation** — Both Wizard and CLI now call shared scaffold builders.
44
-
27
+ ## 🆕 What's new in v5.6.10
28
+
29
+ - 🔧 **OpenAI Codex provider fix** — Updated Codex model registry to match OpenAI's current API. Removed 6 deprecated models, retained 4 active ones: `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2`, `gpt-5.4-mini`.
30
+ - 🔀 **9Router API mode switch** — Switched from `openai-completions` to `openai-responses` to align with OpenAI's Responses API.
31
+ - 🩹 **Auto-patch 9Router** — New `patch-9router.js` automatically patches 9Router source files on setup, upgrade, and before every launch to stay compatible with Codex API changes.
32
+ - 🎯 **Direct Codex model targeting** — 9Router config now exposes individual Codex models alongside `smart-route` so users can pick a specific model.
33
+ - 💬 **Zalo Personal group config** — Added `defaultAccount`, `groupAllowFrom`, `historyLimit`, wildcard group config and `autoReply` for better out-of-the-box group handling.
34
+
35
+ <details>
36
+ <summary><b>Previous: What's new in v5.6.0</b></summary>
37
+
38
+ - 🧠 **Memory & Dreaming enabled by default** — Long-term Memory skill is now pre-selected for all new installations. The `memory-core` plugin with `dreaming.enabled: true` is auto-injected into `openclaw.json`, and `DREAMS.md` is seeded in every workspace.
39
+ - 🤝 **Relay plugin card auto-shows** — When selecting Telegram multi-bot (≥2 bots), the Relay plugin card appears with an "Auto-enabled" badge and locked checkbox. Switching back to 1 bot hides it.
40
+ - 🔑 **Relay trigger keywords in TEAMS.md** — `TEAMS.md` now documents all relay trigger keywords (question/task/reminder patterns) from the v5.0.9 relay plugin, helping bots understand and coordinate cross-bot communication.
41
+ - 🌍 **Proper Vietnamese diacritics** — All workspace `.md` files now use proper UTF-8 Vietnamese with full diacritics, eliminating mojibake.
42
+ - 👍 **Tool-based reaction** — `TOOLS.md` mandates bots call the `react` action with 👍 before replying, replacing unreliable gateway auto-ack.
43
+
45
44
  </details>
46
45
 
47
46
  ---
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.5.0-0EA5E9?style=for-the-badge" alt="Version 5.5.0" /></a>
6
+ <a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.6.10-0EA5E9?style=for-the-badge" alt="Version 5.6.9" /></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,24 +24,23 @@ 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.6.0
28
-
29
- - 🧠 **Memory & Dreaming bật mặc định** — Long-term Memory skill giờ được bật sẵn cho mọi cài đặt mới. Plugin `memory-core` với `dreaming.enabled: true` được inject tự động vào `openclaw.json`, `DREAMS.md` được tạo sẵn trong workspace.
30
- - 🤝 **Card Relay plugin tự động hiển thị** Khi chọn Telegram multi-bot (≥2 bots), card Relay plugin hiện ra với badge "Tự động bật" và checkbox bị khóa. Chuyển về 1 bot sẽ ẩn card.
31
- - 🔑 **Từ khóa relay trong TEAMS.md** — `TEAMS.md` giờ liệt đầy đủ từ khóa kích hoạt relay (hỏi/giao việc/nhắc nhở) từ plugin v5.0.9, giúp bot hiểu phối hợp tốt hơn.
32
- - 🌍 **Tiếng Việt dấu chuẩn** — Tất cả file `.md` workspace giờ dùng UTF-8 chuẩn với dấu tiếng Việt đầy đủ, hết lỗi mojibake.
33
- - 👍 **Tool-based reaction** — `TOOLS.md` bắt buộc bot gọi action `react` thả 👍 trước khi trả lời, thay dùng gateway auto-ack không ổn định.
34
-
35
- <details>
36
- <summary><b>Trước đó: Có gì mới ở v5.5.0</b></summary>
37
-
38
- - 📦 **OpenClaw 2026.4.14** — Nâng từ 2026.4.5. Bao gồm hệ thống Dreaming, cải thiện active-memory, sửa deadlock nút approve Telegram, ổn định WebSocket keepalive.
39
- - 📄 **Tách file workspace docs**Multi-bot giờ sinh `TEAM.md``RELAY.md` thành file riêng.
40
- - 🔐 **Quy tắc bảo mật đầy đủ mọi nơi** — Tất cả `AGENTS.md` giờ đủ 4 section bảo mật.
41
- - 🗑️ **Xóa file `.yaml` agent** OpenClaw Core chỉ đọc từ `openclaw.json`.
42
- - 🤖 **Chống ảo giác handoff** — `AGENTS.md` relay bắt buộc `react` action.
43
- - 🏗️ **Scaffold-based generation** — Cả Wizard lẫn CLI gọi chung scaffold builders.
44
-
27
+ ## 🆕 Có gì mới trong v5.6.10
28
+
29
+ - 🔧 **Sửa provider OpenAI Codex** — Cập nhật registry model Codex phù hợp API hiện tại của OpenAI. Loại 6 model đã dừng, giữ 4 model đang hoạt động: `gpt-5.4`, `gpt-5.3-codex`, `gpt-5.2`, `gpt-5.4-mini`.
30
+ - 🔀 **Chuyển chế độ API 9Router**Đổi từ `openai-completions` sang `openai-responses` cho khớp với Responses API mới của OpenAI.
31
+ - 🩹 **Tự động patch 9Router** — Script `patch-9router.js` mới tự source files 9Router khi setup, upgrade trước mỗi lần khởi động để tương thích thay đổi API Codex.
32
+ - 🎯 **Chọn model Codex trực tiếp** — Config 9Router giờ hiển thị từng model Codex bên cạnh `smart-route` để người dùng thể chọn model cụ thể.
33
+ - 💬 **Config nhóm Zalo Personal** — Bổ sung `defaultAccount`, `groupAllowFrom`, `historyLimit`, wildcard group config `autoReply` để xử nhóm tốt hơn ngay từ đầu.
34
+
35
+ <details>
36
+ <summary><b>Trước đó: Có gì mới ở v5.6.0</b></summary>
37
+
38
+ - 🧠 **Memory & Dreaming bật mặc định** — Long-term Memory skill giờ được bật sẵn cho mọi cài đặt mới. Plugin `memory-core` với `dreaming.enabled: true` được inject tự động vào `openclaw.json`, `DREAMS.md` được tạo sẵn trong workspace.
39
+ - 🤝 **Card Relay plugin tự động hiển thị** Khi chọn Telegram multi-bot (≥2 bots), card Relay plugin hiện ra với badge "Tự động bật" checkbox bị khóa. Chuyển về 1 bot sẽ ẩn card.
40
+ - 🔑 **Từ khóa relay trong TEAMS.md** — `TEAMS.md` giờ liệt kê đầy đủ từ khóa kích hoạt relay (hỏi/giao việc/nhắc nhở) từ plugin v5.0.9, giúp bot hiểu và phối hợp tốt hơn.
41
+ - 🌍 **Tiếng Việt có dấu chuẩn** — Tất cả file `.md` workspace giờ dùng UTF-8 chuẩn với dấu tiếng Việt đầy đủ, hết lỗi mojibake.
42
+ - 👍 **Tool-based reaction** — `TOOLS.md` bắt buộc bot gọi action `react` thả 👍 trước khi trả lời, thay vì dùng gateway auto-ack không ổn định.
43
+
45
44
  </details>
46
45
 
47
46
  ---
package/dist/cli.js CHANGED
@@ -20,18 +20,25 @@ function loadSharedModule(modulePath, globalName) {
20
20
  return globalThis[globalName] || loaded || {};
21
21
  }
22
22
 
23
- const {
24
- OPENCLAW_NPM_SPEC,
25
- OPENCLAW_RUNTIME_PACKAGES,
26
- TELEGRAM_RELAY_PLUGIN_SPEC,
27
- TELEGRAM_SETUP_GUIDE_FILENAME,
28
- buildRelayPluginInstallCommand,
29
- buildTelegramPostInstallChecklist,
30
- } = loadSharedModule('./setup/shared/common-gen.js', '__openclawCommon');
31
-
32
- const {
33
- buildDockerArtifacts,
34
- } = loadSharedModule('./setup/shared/docker-gen.js', '__openclawDockerGen');
23
+ const {
24
+ OPENCLAW_NPM_SPEC,
25
+ OPENCLAW_RUNTIME_PACKAGES,
26
+ NINE_ROUTER_PROXY_API_KEY,
27
+ NINE_ROUTER_API_BASE_URL,
28
+ SMART_ROUTE_PROVIDER_MODELS,
29
+ SMART_ROUTE_PROVIDER_ORDER,
30
+ TELEGRAM_RELAY_PLUGIN_SPEC,
31
+ TELEGRAM_SETUP_GUIDE_FILENAME,
32
+ buildRelayPluginInstallCommand,
33
+ buildTelegramPostInstallChecklist,
34
+ get9RouterBaseUrl,
35
+ build9RouterProviderConfig,
36
+ } = loadSharedModule('./setup/shared/common-gen.js', '__openclawCommon');
37
+
38
+ const {
39
+ buildDockerArtifacts,
40
+ build9RouterPatchScript,
41
+ } = loadSharedModule('./setup/shared/docker-gen.js', '__openclawDockerGen');
35
42
 
36
43
  const {
37
44
  buildWorkspaceFileMap,
@@ -186,32 +193,39 @@ function resolveNative9RouterDesktopLaunch() {
186
193
  args: ['-n', '-H', '0.0.0.0', '-p', '20128', '--skip-update'],
187
194
  env: {
188
195
  PORT: '20128',
189
- HOSTNAME: '0.0.0.0'
196
+ HOSTNAME: '0.0.0.0',
197
+ // Ensures 9router stores data in the user home dir, matching where sync script writes db.json
198
+ DATA_DIR: getNative9RouterDataDir(),
190
199
  }
191
200
  };
192
201
  }
193
202
 
194
203
  function build9RouterSmartRouteSyncScript(dbPath) {
195
204
  const safeDbPath = JSON.stringify(dbPath);
205
+ const safeRouterBaseUrl = JSON.stringify(NINE_ROUTER_API_BASE_URL);
206
+ const safeModelPriority = JSON.stringify(SMART_ROUTE_PROVIDER_MODELS);
207
+ const safeProviderOrder = JSON.stringify(SMART_ROUTE_PROVIDER_ORDER);
196
208
  return `function bootstrap() {
197
209
  const fs = require('fs');
198
210
  const path = require('path');
199
211
  const dbPath = ${safeDbPath};
200
- const ROUTER='http://localhost:20128';
201
- const MODEL_PRIORITY = {
202
- openai: ['openai/gpt-4o', 'openai/gpt-4.1'],
203
- anthropic: ['anthropic/claude-sonnet-4', 'anthropic/claude-haiku-3.5'],
204
- gemini: ['gemini/gemini-2.5-flash', 'gemini/gemini-2.5-pro'],
205
- };
206
- const sync = async () => {
207
- try {
208
- const response = await fetch(ROUTER + '/api/providers');
209
- if (!response.ok) return;
210
- const payload = await response.json();
211
- const a = (payload.connections || [])
212
- .filter((item) => item && item.provider && item.isActive !== false && !item.disabled)
213
- .map((item) => item.provider);
214
- let db = {};
212
+ const ROUTER=${safeRouterBaseUrl};
213
+ const MODEL_PRIORITY=${safeModelPriority};
214
+ const PREF=${safeProviderOrder};
215
+ const sync = async () => {
216
+ try {
217
+ const response = await fetch(ROUTER + '/api/providers');
218
+ if (!response.ok) return;
219
+ const payload = await response.json();
220
+ const rawConnections = Array.isArray(payload.connections)
221
+ ? payload.connections
222
+ : Array.isArray(payload.providerConnections)
223
+ ? payload.providerConnections
224
+ : [];
225
+ const a = [...new Set(rawConnections
226
+ .filter((item) => item && item.provider && item.isActive !== false && !item.disabled)
227
+ .map((item) => item.provider))];
228
+ let db = {};
215
229
  try {
216
230
  db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
217
231
  } catch {}
@@ -225,11 +239,12 @@ function build9RouterSmartRouteSyncScript(dbPath) {
225
239
  console.log('Removed smart-route (no active providers)');
226
240
  }
227
241
  };
228
- if (!a.length) {
229
- removeSmartRoute();
230
- return;
231
- }
232
- const m = a.flatMap((provider) => MODEL_PRIORITY[provider] || []);
242
+ if (!a.length) {
243
+ removeSmartRoute();
244
+ return;
245
+ }
246
+ a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
247
+ const m = a.flatMap((provider) => MODEL_PRIORITY[provider] || []);
233
248
  if (!m.length) {
234
249
  removeSmartRoute();
235
250
  return;
@@ -426,13 +441,69 @@ function resolveCommandOnPath(command) {
426
441
  }
427
442
  }
428
443
 
429
- async function writeNative9RouterSyncScript(projectDir) {
430
- const syncScriptPath = path.join(projectDir, '.openclaw', '9router-smart-route-sync.js');
431
- await fs.ensureDir(path.dirname(syncScriptPath));
432
- await fs.ensureDir(getProject9RouterDataDir(projectDir));
433
- await fs.writeFile(syncScriptPath, build9RouterSmartRouteSyncScript(path.join(getProject9RouterDataDir(projectDir), 'db.json')));
434
- return syncScriptPath;
435
- }
444
+ async function writeNative9RouterSyncScript(projectDir) {
445
+ const syncScriptPath = path.join(projectDir, '.openclaw', '9router-smart-route-sync.js');
446
+ await fs.ensureDir(path.dirname(syncScriptPath));
447
+ // Use native home data dir so sync script writes to same place 9router binary reads from
448
+ const nativeDataDir = getNative9RouterDataDir();
449
+ await fs.ensureDir(nativeDataDir);
450
+ await fs.writeFile(syncScriptPath, build9RouterSmartRouteSyncScript(path.join(nativeDataDir, 'db.json')));
451
+ return syncScriptPath;
452
+ }
453
+
454
+ async function writeNative9RouterPatchScript(projectDir) {
455
+ const patchScriptPath = path.join(projectDir, '.openclaw', 'patch-9router.js');
456
+ await fs.ensureDir(path.dirname(patchScriptPath));
457
+ await fs.writeFile(patchScriptPath, build9RouterPatchScript());
458
+ return patchScriptPath;
459
+ }
460
+
461
+ async function patchProject9RouterOpenClawConfig(projectDir) {
462
+ const configPath = path.join(projectDir, '.openclaw', 'openclaw.json');
463
+ if (!await fs.pathExists(configPath)) return false;
464
+ const config = await fs.readJson(configPath);
465
+ const provider = config?.models?.providers?.['9router'];
466
+ if (!provider) return false;
467
+ provider.baseUrl = get9RouterBaseUrl(detectProjectDeployMode(projectDir));
468
+ provider.apiKey = NINE_ROUTER_PROXY_API_KEY;
469
+ provider.api = 'openai-completions';
470
+ provider.models = build9RouterProviderConfig(provider.baseUrl).models;
471
+ await fs.writeJson(configPath, config, { spaces: 2 });
472
+ return true;
473
+ }
474
+
475
+ async function patchProjectDocker9Router(projectDir) {
476
+ const dockerDir = path.join(projectDir, 'docker', 'openclaw');
477
+ const composePath = path.join(dockerDir, 'docker-compose.yml');
478
+ if (!await fs.pathExists(composePath)) return false;
479
+
480
+ await fs.ensureDir(dockerDir);
481
+ await fs.writeFile(path.join(dockerDir, 'sync.js'), build9RouterSmartRouteSyncScript('/root/.9router/db.json'));
482
+ await fs.writeFile(path.join(dockerDir, 'patch-9router.js'), build9RouterPatchScript());
483
+ let compose = await fs.readFile(composePath, 'utf8');
484
+ compose = compose.replace(
485
+ /node -e "require\('fs'\)\.writeFileSync\('\/tmp\/sync\.js',Buffer\.from\('[^']*','base64'\)\.toString\(\)\)"/,
486
+ "cp /opt/sync.js /tmp/sync.js"
487
+ );
488
+ compose = compose.replace(
489
+ /(npm install -g [^\n]+\n)/,
490
+ `$1 cp /opt/patch-9router.js /tmp/patch-9router.js\n`
491
+ );
492
+ if (!compose.includes('node /tmp/patch-9router.js || true')) {
493
+ compose = compose.replace(
494
+ /(\s*node \/tmp\/sync\.js > \/tmp\/sync\.log 2>&1 &\n)/,
495
+ ` node /tmp/patch-9router.js || true\n$1`
496
+ );
497
+ }
498
+ if (!compose.includes('./sync.js:/opt/sync.js:ro')) {
499
+ compose = compose.replace(
500
+ /(\s*-\s*9router-data:\/root\/\.9router\s*\n)/,
501
+ `$1 - ./sync.js:/opt/sync.js:ro\n - ./patch-9router.js:/opt/patch-9router.js:ro\n`
502
+ );
503
+ }
504
+ await fs.writeFile(composePath, compose, 'utf8');
505
+ return true;
506
+ }
436
507
 
437
508
  function getGatewayAllowedOrigins(port) {
438
509
  const normalizedPort = Number(port) || 18791;
@@ -855,8 +926,26 @@ async function runUpgradeCommand() {
855
926
  isVi: false,
856
927
  }));
857
928
  }
858
-
859
- console.log(chalk.green('\nUpgrade artifacts refreshed successfully.'));
929
+
930
+ if (is9Router) {
931
+ await writeNative9RouterPatchScript(projectDir);
932
+ await patchProject9RouterOpenClawConfig(projectDir);
933
+ if (deployMode === 'docker') {
934
+ await patchProjectDocker9Router(projectDir);
935
+ } else {
936
+ await writeNative9RouterSyncScript(projectDir);
937
+ try {
938
+ execFileSync(process.execPath, [path.join(projectDir, '.openclaw', 'patch-9router.js')], {
939
+ cwd: projectDir,
940
+ stdio: 'ignore',
941
+ });
942
+ } catch {
943
+ // Best effort: start scripts also retry the patch before launch.
944
+ }
945
+ }
946
+ }
947
+
948
+ console.log(chalk.green('\nUpgrade artifacts refreshed successfully.'));
860
949
  if (deployMode === 'docker') {
861
950
  console.log(chalk.white(` Next: cd ${path.join(projectDir, 'docker', 'openclaw')} && docker compose up -d --build`));
862
951
  } else {
@@ -866,6 +955,7 @@ async function runUpgradeCommand() {
866
955
 
867
956
  function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
868
957
  const routerAppName = `${appName}-9router`;
958
+ const syncAppName = `${appName}-9router-sync`;
869
959
  const routerLaunch = resolveNative9RouterDesktopLaunch();
870
960
  const normalizedProjectDir = projectDir.replace(/\\/g, '/');
871
961
  const normalizedSyncScriptPath = syncScriptPath ? syncScriptPath.replace(/\\/g, '/') : '';
@@ -895,7 +985,6 @@ function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
895
985
  env: { ...process.env, ...routerLaunch.env }
896
986
  });
897
987
  if (syncScriptPath) {
898
- const syncAppName = `${appName}-9router-sync`;
899
988
  try {
900
989
  execSync(`pm2 delete ${syncAppName}`, {
901
990
  cwd: projectDir,
@@ -2126,13 +2215,13 @@ async function main() {
2126
2215
  } else if (providerKey === '9router') {
2127
2216
  authProfilesJson = {
2128
2217
  version: 1,
2129
- profiles: {
2130
- '9router-proxy': {
2131
- provider: '9router',
2132
- type: 'api_key',
2133
- key: 'sk-no-key',
2134
- },
2135
- },
2218
+ profiles: {
2219
+ '9router-proxy': {
2220
+ provider: '9router',
2221
+ type: 'api_key',
2222
+ key: NINE_ROUTER_PROXY_API_KEY,
2223
+ },
2224
+ },
2136
2225
  order: { '9router': ['9router-proxy'] },
2137
2226
  };
2138
2227
  }
@@ -2204,21 +2293,14 @@ async function main() {
2204
2293
  model: { primary: modelsPrimary, fallbacks: [] },
2205
2294
  })),
2206
2295
  },
2207
- ...(providerKey === '9router' ? {
2208
- models: {
2209
- mode: 'merge',
2210
- providers: {
2211
- '9router': {
2212
- baseUrl: deployMode === 'native' ? 'http://localhost:20128/v1' : 'http://9router:20128/v1',
2213
- apiKey: 'sk-no-key',
2214
- api: 'openai-completions',
2215
- models: [
2216
- { id: 'smart-route', name: 'Smart Proxy (Auto Route)', contextWindow: 200000, maxTokens: 8192 },
2217
- ],
2218
- },
2219
- },
2220
- },
2221
- } : provider.isLocal ? {
2296
+ ...(providerKey === '9router' ? {
2297
+ models: {
2298
+ mode: 'merge',
2299
+ providers: {
2300
+ '9router': build9RouterProviderConfig(get9RouterBaseUrl(deployMode)),
2301
+ },
2302
+ },
2303
+ } : provider.isLocal ? {
2222
2304
  models: {
2223
2305
  mode: 'merge',
2224
2306
  providers: {
@@ -2273,14 +2355,28 @@ async function main() {
2273
2355
  sharedConfig.skills = { entries: skillEntries };
2274
2356
  }
2275
2357
 
2276
- await fs.writeJson(path.join(rootClawDir, 'openclaw.json'), sharedConfig, { spaces: 2 });
2277
- await fs.writeFile(
2278
- path.join(projectDir, TELEGRAM_SETUP_GUIDE_FILENAME),
2358
+ await fs.writeJson(path.join(rootClawDir, 'openclaw.json'), sharedConfig, { spaces: 2 });
2359
+ await fs.writeFile(
2360
+ path.join(projectDir, TELEGRAM_SETUP_GUIDE_FILENAME),
2279
2361
  buildTelegramPostInstallChecklist({ isVi, bots, groupId }),
2280
2362
  'utf8',
2281
2363
  );
2282
2364
  // Generate ecosystem.config.js for PM2 native multi-bot
2283
2365
  if (deployMode === 'native') {
2366
+ // Also write config to ~/.openclaw/ — openclaw binary on Linux/Mac reads from home dir
2367
+ const homeClawDir = path.join(os.homedir(), '.openclaw');
2368
+ await fs.ensureDir(homeClawDir);
2369
+ const homeConfig = JSON.parse(JSON.stringify(sharedConfig));
2370
+ for (const agent of (homeConfig.agents && homeConfig.agents.list || [])) {
2371
+ // workspace is relative to projectDir (.openclaw/workspace-X); agentDir is relative to rootClawDir (agents/X/agent)
2372
+ if (agent.workspace && !path.isAbsolute(agent.workspace)) agent.workspace = path.join(projectDir, agent.workspace);
2373
+ if (agent.agentDir && !path.isAbsolute(agent.agentDir)) agent.agentDir = path.join(rootClawDir, agent.agentDir);
2374
+ }
2375
+ await fs.writeJson(path.join(homeClawDir, 'openclaw.json'), homeConfig, { spaces: 2 });
2376
+ if (Object.keys(authProfilesJson).length > 0) {
2377
+ await fs.writeJson(path.join(homeClawDir, 'auth-profiles.json'), authProfilesJson, { spaces: 2 });
2378
+ }
2379
+ const safeRootClawDir = rootClawDir.replace(/\\/g, '/');
2284
2380
  const pm2Apps = [
2285
2381
  ' {',
2286
2382
  ` name: 'openclaw-multibot',`,
@@ -2290,7 +2386,11 @@ async function main() {
2290
2386
  ` interpreter: 'none',`,
2291
2387
  ` autorestart: true,`,
2292
2388
  ` watch: false,`,
2293
- ` env: { NODE_ENV: 'production' }`,
2389
+ ` env: {`,
2390
+ ` NODE_ENV: 'production',`,
2391
+ ` OPENCLAW_HOME: '\',`,
2392
+ ` OPENCLAW_STATE_DIR: '\',`,
2393
+ ` }`,
2294
2394
  ' }',
2295
2395
  ].join('\n');
2296
2396
  const ecosystemContent = [
@@ -2392,21 +2492,14 @@ async function main() {
2392
2492
  model: { primary: modelsPrimary, fallbacks: [] }
2393
2493
  }]
2394
2494
  },
2395
- ...(providerKey === '9router' ? {
2396
- models: {
2397
- mode: 'merge',
2398
- providers: {
2399
- '9router': {
2400
- baseUrl: deployMode === 'native' ? 'http://localhost:20128/v1' : 'http://9router:20128/v1',
2401
- apiKey: 'sk-no-key',
2402
- api: 'openai-completions',
2403
- models: [
2404
- { id: 'smart-route', name: 'Smart Proxy (Auto Route)', contextWindow: 200000, maxTokens: 8192 }
2405
- ]
2406
- }
2407
- }
2408
- }
2409
- } : provider.isLocal ? {
2495
+ ...(providerKey === '9router' ? {
2496
+ models: {
2497
+ mode: 'merge',
2498
+ providers: {
2499
+ '9router': build9RouterProviderConfig(get9RouterBaseUrl(deployMode))
2500
+ }
2501
+ }
2502
+ } : provider.isLocal ? {
2410
2503
  models: {
2411
2504
  mode: 'merge',
2412
2505
  providers: {
@@ -2477,19 +2570,30 @@ async function main() {
2477
2570
  };
2478
2571
  }
2479
2572
  botConfig.channels['telegram'] = telegramConfig;
2480
- } else if (hasZaloPersonal(channelKey)) {
2481
- botConfig.channels['zalouser'] = {
2482
- enabled: true,
2483
- dmPolicy: 'open',
2484
- allowFrom: ['*']
2485
- };
2573
+ } else if (hasZaloPersonal(channelKey)) {
2574
+ botConfig.channels['zalouser'] = {
2575
+ enabled: true,
2576
+ defaultAccount: 'default',
2577
+ dmPolicy: 'open',
2578
+ allowFrom: ['*'],
2579
+ groupPolicy: 'allowlist',
2580
+ groupAllowFrom: ['*'],
2581
+ historyLimit: 50,
2582
+ groups: {
2583
+ '*': {
2584
+ enabled: true,
2585
+ requireMention: false,
2586
+ },
2587
+ },
2588
+ autoReply: true,
2589
+ };
2486
2590
  } else if (channelKey === 'zalo-bot') {
2487
2591
  botConfig.channels['zalo'] = { enabled: true, provider: 'official_account' };
2488
2592
  }
2489
2593
 
2490
- await fs.writeJson(path.join(loopBotDir, '.openclaw', 'openclaw.json'), botConfig, { spaces: 2 });
2491
-
2492
- // ── Workspace files: use shared writeWorkspaceFiles() ──────────────────────
2594
+ await fs.writeJson(path.join(loopBotDir, '.openclaw', 'openclaw.json'), botConfig, { spaces: 2 });
2595
+
2596
+ // ── Workspace files: use shared writeWorkspaceFiles() ──────────────────────
2493
2597
  const dockerWorkspaceDir = path.join(loopBotDir, '.openclaw', loopWorkspaceDir);
2494
2598
  const dockerOwnAliases = [loopBotName, bots[bIndex]?.slashCmd || '', `bot ${bIndex + 1}`].filter(Boolean);
2495
2599
  const dockerOtherAgents = teamRoster
@@ -2700,10 +2804,19 @@ async function main() {
2700
2804
  }
2701
2805
  }
2702
2806
 
2703
- let native9RouterSyncScriptPath = null;
2704
- if (providerKey === '9router') {
2705
- native9RouterSyncScriptPath = await writeNative9RouterSyncScript(projectDir);
2706
- }
2807
+ let native9RouterSyncScriptPath = null;
2808
+ if (providerKey === '9router') {
2809
+ await writeNative9RouterPatchScript(projectDir);
2810
+ native9RouterSyncScriptPath = await writeNative9RouterSyncScript(projectDir);
2811
+ try {
2812
+ execFileSync(process.execPath, [path.join(projectDir, '.openclaw', 'patch-9router.js')], {
2813
+ cwd: projectDir,
2814
+ stdio: 'ignore',
2815
+ });
2816
+ } catch {
2817
+ // Start scripts retry this patch before launching 9router.
2818
+ }
2819
+ }
2707
2820
 
2708
2821
  await ensureProjectRuntimeDirs(projectDir, isVi);
2709
2822
 
@@ -2773,10 +2886,10 @@ async function main() {
2773
2886
  cwd: projectDir,
2774
2887
  env: getProjectRuntimeEnv(projectDir, native9RouterLaunch.env)
2775
2888
  }).unref();
2776
- const routerHealth = await waitFor9RouterApiReady();
2777
- if (native9RouterSyncScriptPath) {
2778
- spawnBackgroundProcess(process.execPath, [native9RouterSyncScriptPath], {
2779
- cwd: projectDir
2889
+ const routerHealth = await waitFor9RouterApiReady();
2890
+ if (native9RouterSyncScriptPath) {
2891
+ spawnBackgroundProcess(process.execPath, [native9RouterSyncScriptPath], {
2892
+ cwd: projectDir
2780
2893
  }).unref();
2781
2894
  }
2782
2895
  console.log(chalk.gray(isVi
@@ -65,15 +65,26 @@
65
65
  channelConfig: {
66
66
  zalouser: {
67
67
  enabled: true,
68
+ defaultAccount: 'default',
68
69
  accounts: {
69
70
  default: {
70
71
  dmPolicy: 'open',
71
72
  allowFrom: ['*'],
72
73
  groupPolicy: 'allowlist',
74
+ groupAllowFrom: ['*'],
73
75
  },
74
76
  },
75
77
  dmPolicy: 'open',
78
+ allowFrom: ['*'],
76
79
  groupPolicy: 'allowlist',
80
+ groupAllowFrom: ['*'],
81
+ historyLimit: 50,
82
+ groups: {
83
+ '*': {
84
+ enabled: true,
85
+ requireMention: false,
86
+ },
87
+ },
77
88
  },
78
89
  },
79
90
  pluginInstall: '@openclaw/zalouser',
@@ -161,4 +172,3 @@
161
172
  - ❌ DO NOT run containers with --privileged
162
173
  - ✅ Limit exposed ports (only 38789)`,
163
174
  };
164
-