create-openclaw-bot 5.5.0 → 5.6.0

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.
Files changed (39) hide show
  1. package/README.md +18 -17
  2. package/README.vi.md +18 -17
  3. package/{cli.js → dist/cli.js} +295 -224
  4. package/dist/setup/shared/install-gen.js +485 -0
  5. package/{setup/shared/scaffold-gen.js → dist/setup/shared/workspace-gen.js} +247 -25
  6. package/{setup.js → dist/setup.js} +771 -1158
  7. package/package.json +10 -7
  8. package/.github/workflows/check-openclaw-update.yml +0 -106
  9. package/CHANGELOG.md +0 -602
  10. package/CHANGELOG.vi.md +0 -588
  11. package/docs/SETUP.md +0 -532
  12. package/docs/SETUP.vi.md +0 -439
  13. package/docs/ai-providers.md +0 -144
  14. package/docs/ai-providers.vi.md +0 -144
  15. package/docs/browser-automation-guide.md +0 -207
  16. package/docs/faq.md +0 -63
  17. package/docs/faq.vi.md +0 -63
  18. package/docs/hardware-guide.md +0 -55
  19. package/docs/hardware-guide.vi.md +0 -55
  20. package/docs/install-docker.md +0 -161
  21. package/docs/install-docker.vi.md +0 -161
  22. package/docs/install-native.md +0 -96
  23. package/docs/install-native.vi.md +0 -96
  24. package/docs/preview.png +0 -0
  25. package/docs/skills-plugins-guide.md +0 -126
  26. package/index.html +0 -589
  27. package/old_v510.js +0 -0
  28. package/setup/shared/runtime-gen.js +0 -710
  29. package/style.css +0 -1653
  30. package/upgrade.ps1 +0 -90
  31. package/upgrade.sh +0 -93
  32. /package/{setup → dist/setup}/data/channels.js +0 -0
  33. /package/{setup → dist/setup}/data/header.js +0 -0
  34. /package/{setup → dist/setup}/data/index.js +0 -0
  35. /package/{setup → dist/setup}/data/plugins.js +0 -0
  36. /package/{setup → dist/setup}/data/providers.js +0 -0
  37. /package/{setup → dist/setup}/data/skills.js +0 -0
  38. /package/{setup → dist/setup}/shared/common-gen.js +0 -0
  39. /package/{setup → dist/setup}/shared/docker-gen.js +0 -0
@@ -9,91 +9,50 @@ import { spawn, execSync, execFileSync } from 'child_process';
9
9
  import { createRequire } from 'module';
10
10
 
11
11
  // ─── Shared generators (dual-mode IIFE + CJS) ────────────────────────────────
12
- // These modules export via module.exports when required from Node.js
13
- const _require = createRequire(import.meta.url);
14
- const {
15
- OPENCLAW_NPM_SPEC,
16
- OPENCLAW_RUNTIME_PACKAGES,
17
- TELEGRAM_RELAY_PLUGIN_SPEC,
18
- buildRelayPluginInstallCommand,
19
- buildRelayPluginInstallCommandWin,
20
- buildTelegramPostInstallChecklist,
21
- buildAuthProfilesString,
22
- buildAuthProfilesJson,
23
- } = _require('./setup/shared/common-gen.js');
24
-
25
- const {
26
- build9RouterSmartRouteSyncScript: build9RouterSmartRouteSyncScriptShared,
27
- build9RouterComposeEntrypointScript,
28
- buildGatewayPatchCmd,
29
- indentBlock,
30
- buildDockerArtifacts,
31
- encodeBase64Utf8,
32
- } = _require('./setup/shared/docker-gen.js');
33
-
34
- const {
35
- buildIdentityDoc,
36
- buildSoulDoc,
37
- buildTeamDoc,
38
- buildUserDoc,
39
- buildMemoryDoc,
40
- buildBrowserToolJs,
41
- buildBrowserDoc,
42
- buildSecurityRules,
43
- buildAgentsDoc,
44
- buildToolsDoc,
45
- buildRelayDoc,
46
- } = _require('./setup/shared/scaffold-gen.js');
12
+ // These modules export via module.exports when required from Node.js
13
+ const _require = createRequire(import.meta.url);
47
14
 
48
- let dataExport = _require('./setup/data/index.js');
49
- if (!dataExport.CHANNELS) dataExport = globalThis.__openclawData || {};
15
+ function loadSharedModule(modulePath, globalName) {
16
+ const loaded = _require(modulePath);
17
+ if (loaded && Object.keys(loaded).length > 0) {
18
+ return loaded;
19
+ }
20
+ return globalThis[globalName] || loaded || {};
21
+ }
50
22
 
51
23
  const {
52
- PROVIDERS: _PROVIDERS,
53
- SKILLS: _SKILLS,
54
- CHANNELS: _CHANNELS,
55
- OLLAMA_MODELS,
56
- } = dataExport;
24
+ OPENCLAW_NPM_SPEC,
25
+ OPENCLAW_RUNTIME_PACKAGES,
26
+ TELEGRAM_RELAY_PLUGIN_SPEC,
27
+ buildRelayPluginInstallCommand,
28
+ buildTelegramPostInstallChecklist,
29
+ } = loadSharedModule('./setup/shared/common-gen.js', '__openclawCommon');
57
30
 
58
31
  const {
59
- buildChromeDebugBat,
60
- buildChromeDebugSh,
61
- } = _require('./setup/shared/runtime-gen.js');
32
+ buildDockerArtifacts,
33
+ } = loadSharedModule('./setup/shared/docker-gen.js', '__openclawDockerGen');
62
34
 
63
- function buildCLIUninstallScript({ os, projectDir, botName = 'openclaw', isDocker = false }) {
64
- const absWin = projectDir.replace(/\//g, '\\');
65
- const absUnix = projectDir.replace(/\\/g, '/');
66
- if (os === 'win' && !isDocker) {
67
- return {
68
- name: 'uninstall-openclaw-win.bat',
69
- content: `@echo off\r\nsetlocal EnableExtensions\r\nchcp 65001 >nul\r\necho.\r\necho ============================================================\r\necho OpenClaw Uninstaller - Windows Native\r\necho Project: ${absWin}\r\necho ============================================================\r\necho.\r\necho [WARNING] This will:\r\necho 1. Kill openclaw and 9router background processes\r\necho 2. Uninstall global npm packages (openclaw, 9router)\r\necho 3. Delete the project folder and all its data\r\necho.\r\nset /p CONFIRM=Nhap YES de xac nhan xoa toan bo: \r\nif /i not "%CONFIRM%"=="YES" (\r\n echo Huy bo. Khong xoa gi ca.\r\n pause\r\n exit /b 0\r\n)\r\necho.\r\necho [1/4] Dang dung cac tien trinh openclaw va 9router...\r\nwmic process where "Name='node.exe' and CommandLine like '%%9router%%'" delete >nul 2>&1\r\nwmic process where "Name='cmd.exe' and CommandLine like '%%9router%%'" delete >nul 2>&1\r\nwmic process where "Name='node.exe' and CommandLine like '%%openclaw.mjs%%'" delete >nul 2>&1\r\ntimeout /t 2 /nobreak >nul\r\necho OK: Tien trinh da dung.\r\necho.\r\necho [2/4] Dang go cai npm packages toan cau...\r\nset "PATH=%APPDATA%\\npm;%PATH%"\r\ncall npm uninstall -g openclaw 9router grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api 2>nul\r\necho OK: npm packages da duoc go cai.\r\necho.\r\necho [3/4] Xoa thu muc project...\r\nset "TARGET=${absWin}"\r\nif exist "%TARGET%" (\r\n rd /s /q "%TARGET%"\r\n echo OK: Da xoa %TARGET%\r\n) else (\r\n echo INFO: Thu muc khong ton tai: %TARGET%\r\n)\r\necho.\r\necho [4/4] Xoa thu muc .9router trong Home (neu co)...\r\nif exist "%USERPROFILE%\\.9router" (\r\n set /p CLEAN_HOME=Xoa ca %USERPROFILE%\\.9router? [YES/no]: \r\n if /i "%CLEAN_HOME%"=="YES" rd /s /q "%USERPROFILE%\\.9router" >nul 2>&1\r\n)\r\necho.\r\necho ============================================================\r\necho Go cai hoan tat!\r\necho De cai lai: chay lai file setup hoac npx create-openclaw-bot\r\necho ============================================================\r\npause\r\nendlocal\r\n`,
70
- };
71
- }
72
- if (os === 'win' && isDocker) {
73
- return {
74
- name: 'uninstall-openclaw-docker.bat',
75
- content: `@echo off\r\nsetlocal EnableExtensions\r\nchcp 65001 >nul\r\necho.\r\necho ============================================================\r\necho OpenClaw Uninstaller - Docker (Windows)\r\necho Project: ${absWin}\r\necho ============================================================\r\necho.\r\nset /p CONFIRM=Nhap YES de xac nhan xoa toan bo: \r\nif /i not "%CONFIRM%"=="YES" ( echo Huy bo. & pause & exit /b 0 )\r\necho.\r\necho [1/2] Dang dung Docker containers...\r\ncd /d "${absWin}\\docker\\openclaw" 2>nul && ( docker compose down --volumes --remove-orphans 2>nul || docker-compose down --volumes --remove-orphans 2>nul )\r\necho [2/2] Xoa thu muc project...\r\ncd /d "%USERPROFILE%"\r\nif exist "${absWin}" rd /s /q "${absWin}"\r\necho.\r\necho Go cai hoan tat! De cai lai: npx create-openclaw-bot@latest\r\npause\r\nendlocal\r\n`,
76
- };
77
- }
78
- // macOS / Linux
79
- const label = os === 'linux' ? 'macOS' : 'Linux Desktop';
80
- const scriptName = isDocker ? 'uninstall-openclaw-docker.sh' : 'uninstall-openclaw.sh';
81
- if (!isDocker) {
82
- return {
83
- name: scriptName,
84
- content: `#!/usr/bin/env bash\n# ====== OpenClaw Uninstaller — ${label} (Native) ======\nset -e\nPROJECT_DIR="${absUnix}"\necho ""\necho "============================================================"\necho " OpenClaw Uninstaller — ${label} Native"\necho " Project: $PROJECT_DIR"\necho "============================================================"\necho ""\nread -rp "Type YES to confirm full removal: " CONFIRM\nif [ "$CONFIRM" != "YES" ]; then echo "Cancelled."; exit 0; fi\necho "[1/4] Stopping openclaw and 9router..."\nopenclaw gateway stop 2>/dev/null || true\npkill -f "9router" 2>/dev/null || true\nfor port in 18791 20128; do\n pid=$(lsof -ti tcp:$port 2>/dev/null || true)\n [ -n "$pid" ] && kill -9 $pid 2>/dev/null || true\ndone\necho "[2/4] Uninstalling npm packages..."\nnpm uninstall -g openclaw 9router grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api 2>/dev/null || true\nsudo npm uninstall -g openclaw 9router 2>/dev/null || true\necho "[3/4] Removing project directory..."\n[ -d "$PROJECT_DIR" ] && rm -rf "$PROJECT_DIR" && echo " OK: Deleted $PROJECT_DIR" || echo " INFO: Not found."\necho "[4/4] Checking home-level dirs..."\nfor dir in "$HOME/.9router" "$HOME/.openclaw"; do\n if [ -d "$dir" ]; then\n read -rp "Delete $dir? [YES/no]: " CLEAN\n [ "$CLEAN" = "YES" ] && rm -rf "$dir" && echo " OK." || echo " Kept."\n fi\ndone\necho ""\necho "============================================================"\necho " Uninstall complete! Re-install: run setup or npx create-openclaw-bot"\necho "============================================================"\n`,
85
- };
86
- }
87
- return {
88
- name: scriptName,
89
- content: `#!/usr/bin/env bash\n# ====== OpenClaw Uninstaller — Docker ======\nset -e\nPROJECT_DIR="${absUnix}"\nread -rp "Type YES to confirm: " CONFIRM\n[ "$CONFIRM" = "YES" ] || exit 0\ncd "$PROJECT_DIR/docker/openclaw" 2>/dev/null && docker compose down --volumes --remove-orphans 2>/dev/null || true\nrm -rf "$PROJECT_DIR"\necho "Uninstall complete!"\n`,
90
- };
91
- }
35
+ const {
36
+ buildWorkspaceFileMap,
37
+ } = loadSharedModule('./setup/shared/workspace-gen.js', '__openclawWorkspace');
38
+
39
+ const dataExport = loadSharedModule('./setup/data/index.js', '__openclawData');
40
+
41
+ const {
42
+ PROVIDERS: _PROVIDERS,
43
+ SKILLS: _SKILLS,
44
+ CHANNELS: _CHANNELS,
45
+ OLLAMA_MODELS,
46
+ } = dataExport;
92
47
 
93
- // TELEGRAM_RELAY_PLUGIN_SPEC đã được import từ common-gen
94
- const TELEGRAM_RELAY_PLUGIN_ID = TELEGRAM_RELAY_PLUGIN_SPEC;
48
+ const {
49
+ buildCliChromeDebugArtifacts,
50
+ buildCliUninstallArtifacts,
51
+ buildCliUpgradeArtifacts,
52
+ buildCliStartBotArtifacts,
53
+ } = loadSharedModule('./setup/shared/install-gen.js', '__openclawInstall');
95
54
 
96
- function installRelayPluginForProject(projectDir, isVi) {
55
+ function installRelayPluginForProject(projectDir, isVi) {
97
56
  try {
98
57
  execSync(`openclaw plugins install ${TELEGRAM_RELAY_PLUGIN_SPEC}`, { cwd: projectDir, stdio: 'ignore' });
99
58
  return true;
@@ -598,18 +557,28 @@ function printZaloPersonalLoginInfo({ isVi, deployMode, projectDir }) {
598
557
  : ` → If needed, copy the QR into the project folder with: ${copyCmd}`));
599
558
  }
600
559
 
601
- async function waitForFile(filePath, timeoutMs = 15000, intervalMs = 500) {
602
- const deadline = Date.now() + timeoutMs;
603
- while (Date.now() < deadline) {
604
- if (await fs.pathExists(filePath)) {
605
- return true;
560
+ async function waitForFile(filePath, timeoutMs = 15000, intervalMs = 500) {
561
+ const deadline = Date.now() + timeoutMs;
562
+ while (Date.now() < deadline) {
563
+ if (await fs.pathExists(filePath)) {
564
+ return true;
606
565
  }
607
566
  await new Promise((resolve) => setTimeout(resolve, intervalMs));
608
- }
609
- return fs.pathExists(filePath);
610
- }
611
-
612
- function extractZaloPairingCode(text) {
567
+ }
568
+ return fs.pathExists(filePath);
569
+ }
570
+
571
+ async function writeGeneratedArtifacts(targetDir, artifacts = []) {
572
+ for (const artifact of artifacts.filter(Boolean)) {
573
+ const artifactPath = path.join(targetDir, artifact.name);
574
+ await fs.writeFile(artifactPath, artifact.content, 'utf8');
575
+ if (artifact.executable || artifact.name.endsWith('.sh')) {
576
+ try { await fs.chmod(artifactPath, 0o755); } catch (_) {}
577
+ }
578
+ }
579
+ }
580
+
581
+ function extractZaloPairingCode(text) {
613
582
  const value = String(text || '');
614
583
  const explicitCommandMatch = value.match(/openclaw pairing approve zalouser\s+([A-Z0-9-]+)/i);
615
584
  if (explicitCommandMatch) {
@@ -729,10 +698,10 @@ async function runNativeZaloPersonalLoginFlow({ isVi, projectDir }) {
729
698
  }
730
699
  }
731
700
 
732
- function runPm2Save({ projectDir, isVi }) {
733
- try {
734
- execSync('pm2 save', {
735
- cwd: projectDir,
701
+ function runPm2Save({ projectDir, isVi }) {
702
+ try {
703
+ execSync('pm2 save', {
704
+ cwd: projectDir,
736
705
  stdio: 'inherit',
737
706
  shell: true,
738
707
  env: process.env
@@ -741,10 +710,138 @@ function runPm2Save({ projectDir, isVi }) {
741
710
  console.log(chalk.yellow(isVi
742
711
  ? '⚠️ PM2 save khong hoan tat. Bot van co the dang chay, nhung hay thu chay lai `pm2 save` sau.'
743
712
  : '⚠️ PM2 save did not complete. The app may still be running, but try `pm2 save` again afterwards.'));
744
- }
745
- }
746
-
747
- function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
713
+ }
714
+ }
715
+
716
+ function getDetectedOsChoice() {
717
+ const detectedPlatform = process.platform;
718
+ return detectedPlatform === 'win32' ? 'windows'
719
+ : detectedPlatform === 'darwin' ? 'macos'
720
+ : 'vps';
721
+ }
722
+
723
+ function getCliSubcommand() {
724
+ return String(process.argv[2] || '').trim().toLowerCase();
725
+ }
726
+
727
+ function findProjectDir(startDir = process.cwd()) {
728
+ let currentDir = path.resolve(startDir);
729
+
730
+ while (true) {
731
+ if (
732
+ fs.existsSync(path.join(currentDir, '.openclaw'))
733
+ || fs.existsSync(path.join(currentDir, 'docker', 'openclaw'))
734
+ ) {
735
+ return currentDir;
736
+ }
737
+
738
+ const isDockerOpenClawDir =
739
+ path.basename(currentDir).toLowerCase() === 'openclaw'
740
+ && path.basename(path.dirname(currentDir)).toLowerCase() === 'docker';
741
+ if (isDockerOpenClawDir) {
742
+ const projectDir = path.dirname(path.dirname(currentDir));
743
+ if (
744
+ fs.existsSync(path.join(projectDir, '.openclaw'))
745
+ || fs.existsSync(path.join(currentDir, 'docker-compose.yml'))
746
+ ) {
747
+ return projectDir;
748
+ }
749
+ }
750
+
751
+ const parentDir = path.dirname(currentDir);
752
+ if (parentDir === currentDir) {
753
+ return null;
754
+ }
755
+ currentDir = parentDir;
756
+ }
757
+ }
758
+
759
+ function detectProjectDeployMode(projectDir) {
760
+ const dockerDir = path.join(projectDir, 'docker', 'openclaw');
761
+ if (
762
+ fs.existsSync(path.join(dockerDir, 'docker-compose.yml'))
763
+ || fs.existsSync(path.join(dockerDir, '.env'))
764
+ ) {
765
+ return 'docker';
766
+ }
767
+ return 'native';
768
+ }
769
+
770
+ function detectProjectBotName(projectDir) {
771
+ try {
772
+ const configPath = path.join(projectDir, '.openclaw', 'openclaw.json');
773
+ if (fs.existsSync(configPath)) {
774
+ const config = fs.readJsonSync(configPath);
775
+ const firstAgentId = config?.agents?.list?.[0]?.id;
776
+ if (firstAgentId) {
777
+ return firstAgentId;
778
+ }
779
+ }
780
+ } catch {
781
+ // fallback below
782
+ }
783
+ return path.basename(projectDir);
784
+ }
785
+
786
+ function detectProjectUses9Router(projectDir) {
787
+ try {
788
+ const configPath = path.join(projectDir, '.openclaw', 'openclaw.json');
789
+ if (fs.existsSync(configPath)) {
790
+ const config = fs.readJsonSync(configPath);
791
+ if (config?.models?.providers?.['9router']) {
792
+ return true;
793
+ }
794
+ }
795
+ } catch {
796
+ // fallback below
797
+ }
798
+ return fs.existsSync(path.join(projectDir, '.9router'));
799
+ }
800
+
801
+ async function runUpgradeCommand() {
802
+ const projectDir = findProjectDir();
803
+ if (!projectDir) {
804
+ console.error(chalk.red('Error: no OpenClaw project found in the current directory tree.'));
805
+ console.error(chalk.yellow('Run this inside the bot project folder that contains .openclaw or docker/openclaw.'));
806
+ process.exit(1);
807
+ }
808
+
809
+ const deployMode = detectProjectDeployMode(projectDir);
810
+ const osChoice = getDetectedOsChoice();
811
+ const botName = detectProjectBotName(projectDir);
812
+ const is9Router = detectProjectUses9Router(projectDir);
813
+
814
+ console.log(chalk.cyan('\nRefreshing generated OpenClaw project artifacts...'));
815
+ console.log(chalk.gray(` Project: ${projectDir}`));
816
+ console.log(chalk.gray(` Mode: ${deployMode}`));
817
+
818
+ await writeGeneratedArtifacts(projectDir, buildCliChromeDebugArtifacts());
819
+ await writeGeneratedArtifacts(projectDir, buildCliUninstallArtifacts({
820
+ deployMode,
821
+ osChoice,
822
+ projectDir,
823
+ botName,
824
+ }));
825
+ await writeGeneratedArtifacts(projectDir, buildCliUpgradeArtifacts());
826
+
827
+ if (deployMode !== 'docker') {
828
+ await writeGeneratedArtifacts(projectDir, buildCliStartBotArtifacts({
829
+ projectDir,
830
+ openclawHome: path.join(projectDir, '.openclaw'),
831
+ is9Router,
832
+ isVi: false,
833
+ }));
834
+ }
835
+
836
+ console.log(chalk.green('\nUpgrade artifacts refreshed successfully.'));
837
+ if (deployMode === 'docker') {
838
+ console.log(chalk.white(` Next: cd ${path.join(projectDir, 'docker', 'openclaw')} && docker compose up -d --build`));
839
+ } else {
840
+ console.log(chalk.white(` Next: run ${process.platform === 'win32' ? '.\\start-bot.bat' : './start-bot.sh'} from ${projectDir}`));
841
+ }
842
+ }
843
+
844
+ function startNative9RouterPm2({ isVi, projectDir, appName, syncScriptPath }) {
748
845
  const routerAppName = `${appName}-9router`;
749
846
  const routerLaunch = resolveNative9RouterDesktopLaunch();
750
847
  const normalizedProjectDir = projectDir.replace(/\\/g, '/');
@@ -874,10 +971,11 @@ async function writeWorkspaceFiles({
874
971
  ownAliases = [],
875
972
  otherAgents = [], // [{ name, agentId }]
876
973
  teamRoster = [],
877
- userInfo = '',
878
- agentWorkspaceDir = 'workspace',
879
- isRelayBot = false,
880
- }) {
974
+ userInfo = '',
975
+ agentWorkspaceDir = 'workspace',
976
+ isRelayBot = false,
977
+ replyToDirectMessages = true,
978
+ }) {
881
979
  const skillListStr = SKILLS
882
980
  .filter((s) => selectedSkills.includes(s.value))
883
981
  .map((s) => {
@@ -888,58 +986,58 @@ async function writeWorkspaceFiles({
888
986
  })
889
987
  .join('\n') || (isVi ? '- _(Chưa có skill nào)_' : '- _(No skills installed)_');
890
988
 
891
- const workspacePath = `/root/.openclaw/${agentWorkspaceDir}/`;
892
-
893
- const identityMd = buildIdentityDoc({ isVi, name: botName, desc: botDesc, richAiNote: false });
894
- const soulMd = buildSoulDoc({ isVi, persona, variant: isRelayBot ? 'cli-simple' : 'cli-rich' });
895
- const userMd = buildUserDoc({ isVi, userInfo, variant: isRelayBot ? 'cli-multi' : 'cli-single' });
896
- const memoryMd = buildMemoryDoc({ isVi, variant: isRelayBot ? 'cli-multi' : 'cli-single' });
897
- const agentsMd = buildAgentsDoc({
898
- isVi, botName, botDesc, ownAliases, otherAgents,
899
- workspacePath,
900
- variant: isRelayBot ? 'relay' : 'single',
901
- includeSecurity: !isRelayBot,
902
- });
903
- const toolsMd = buildToolsDoc({
904
- isVi, skillListStr, workspacePath,
989
+ const workspacePath = `.openclaw/${agentWorkspaceDir}/`;
990
+ const teamRosterFormatted = teamRoster
991
+ .map((peer, idx) => {
992
+ const agentId = peer.agentId || String(peer.name || `Bot ${idx + 1}`).toLowerCase().replace(/[^a-z0-9]+/g, '-');
993
+ const desc = peer.desc || (isVi ? 'Tro ly AI ca nhan' : 'Personal AI assistant');
994
+ const accountId = peer.accountId ? `, accountId: ${peer.accountId}` : '';
995
+ const slashCmd = peer.slashCmd ? `, slash: ${peer.slashCmd}` : '';
996
+ return `- \`${agentId}\`: ${peer.name || `Bot ${idx + 1}`} - ${desc}${accountId}${slashCmd}`;
997
+ })
998
+ .join('\n');
999
+
1000
+ const files = buildWorkspaceFileMap({
1001
+ isVi,
905
1002
  variant: isRelayBot ? 'relay' : 'single',
1003
+ botName,
1004
+ botDesc,
1005
+ ownAliases,
1006
+ otherAgents,
1007
+ replyToDirectMessages,
1008
+ skillListStr,
1009
+ workspacePath,
906
1010
  agentWorkspaceDir,
1011
+ persona,
1012
+ userInfo,
1013
+ hasBrowser: isDesktop || isServer,
1014
+ soulVariant: isRelayBot ? 'cli-simple' : 'cli-rich',
1015
+ userVariant: isRelayBot ? 'cli-multi' : 'cli-single',
1016
+ memoryVariant: isRelayBot ? 'cli-multi' : 'cli-single',
1017
+ browserDocVariant: isServer ? 'cli-server' : 'cli-desktop',
1018
+ browserToolVariant: 'cli',
1019
+ includeBrowserTool: isDesktop,
1020
+ teamRosterFormatted,
1021
+ hasScheduler: selectedSkills.includes('scheduler'),
907
1022
  });
908
- const teamSection = teamRoster.length > 0
909
- ? `\n\n${buildTeamDoc({
910
- isVi,
911
- teamRoster,
912
- includeAgentIds: isRelayBot,
913
- includeAccountIds: isRelayBot && teamRoster.some((p) => p.accountId),
914
- relayMode: isRelayBot && otherAgents.length > 0,
915
- })}`
916
- : '';
917
- const relaySection = isRelayBot
918
- ? `\n\n${buildRelayDoc(isVi)}`
919
- : '';
920
1023
 
921
1024
  await fs.ensureDir(workspaceDir);
922
- await fs.writeFile(path.join(workspaceDir, 'IDENTITY.md'), identityMd);
923
- await fs.writeFile(path.join(workspaceDir, 'SOUL.md'), soulMd);
924
- await fs.writeFile(path.join(workspaceDir, 'AGENTS.md'), `${agentsMd}${teamSection}`);
925
- await fs.writeFile(path.join(workspaceDir, 'USER.md'), userMd);
926
- await fs.writeFile(path.join(workspaceDir, 'TOOLS.md'), `${toolsMd}${relaySection}`);
927
- await fs.writeFile(path.join(workspaceDir, 'MEMORY.md'), memoryMd);
1025
+ for (const [name, content] of Object.entries(files)) {
1026
+ await fs.writeFile(path.join(workspaceDir, name), content, 'utf8');
1027
+ }
1028
+ }
1029
+
1030
+
1031
+ async function main() {
1032
+ const cliSubcommand = getCliSubcommand();
1033
+ if (cliSubcommand === 'upgrade') {
1034
+ await runUpgradeCommand();
1035
+ return;
1036
+ }
928
1037
 
929
- // Browser files
930
- if (isDesktop) {
931
- await fs.writeFile(path.join(workspaceDir, 'browser-tool.js'), buildBrowserToolJs('cli'));
932
- await fs.writeFile(path.join(workspaceDir, 'BROWSER.md'), buildBrowserDoc({ isVi, variant: 'cli-desktop', workspaceRoot: '/root/.openclaw' }));
933
- } else if (isServer) {
934
- await fs.writeFile(path.join(workspaceDir, 'BROWSER.md'), buildBrowserDoc({ isVi, variant: 'cli-server' }));
935
- }
936
- }
937
-
938
-
939
- async function main() {
940
- console.log(chalk.red('\n=================================='));
941
- console.log(chalk.redBright(LOGO));
942
- console.log(chalk.greenBright(' OpenClaw Auto Setup CLI '));
1038
+ console.log(chalk.red('\n=================================='));
1039
+ console.log(chalk.redBright(LOGO));
1040
+ console.log(chalk.greenBright(' OpenClaw Auto Setup CLI '));
943
1041
  console.log(chalk.red('==================================\n'));
944
1042
 
945
1043
  // 1. Language
@@ -1458,7 +1556,7 @@ async function main() {
1458
1556
  [groupId || '*']: { enabled: true, requireMention: false },
1459
1557
  },
1460
1558
  replyToMode: 'first',
1461
- reactionLevel: 'ack',
1559
+ reactionLevel: 'minimal',
1462
1560
  actions: {
1463
1561
  sendMessage: true,
1464
1562
  reactions: true,
@@ -1481,13 +1579,13 @@ async function main() {
1481
1579
  timeoutSeconds: provider.isLocal ? 900 : 120,
1482
1580
  ...(provider.isLocal ? { llm: { idleTimeoutSeconds: 300 } } : {}),
1483
1581
  },
1484
- list: agentMetas.map((meta) => ({
1485
- id: meta.agentId,
1486
- name: meta.name,
1487
- workspace: `/root/.openclaw/${meta.workspaceDir}`,
1488
- agentDir: `/root/.openclaw/agents/${meta.agentId}/agent`,
1489
- model: { primary: modelsPrimary, fallbacks: [] },
1490
- })),
1582
+ list: agentMetas.map((meta) => ({
1583
+ id: meta.agentId,
1584
+ name: meta.name,
1585
+ workspace: `.openclaw/${meta.workspaceDir}`,
1586
+ agentDir: `agents/${meta.agentId}/agent`,
1587
+ model: { primary: modelsPrimary, fallbacks: [] },
1588
+ })),
1491
1589
  },
1492
1590
  ...(providerKey === '9router' ? {
1493
1591
  models: {
@@ -1543,11 +1641,7 @@ async function main() {
1543
1641
  auth: { mode: 'token', token: 'cli-dummy-token-xyz123' },
1544
1642
  },
1545
1643
  };
1546
- sharedConfig.plugins = {
1547
- entries: {
1548
- [TELEGRAM_RELAY_PLUGIN_ID]: { enabled: true },
1549
- },
1550
- };
1644
+ sharedConfig.plugins = { entries: {} };
1551
1645
 
1552
1646
  if (hasBrowserDesktop) {
1553
1647
  sharedConfig.browser = {
@@ -1621,8 +1715,7 @@ async function main() {
1621
1715
  .map((peer) => ({ name: peer.name, agentId: peer.agentId }));
1622
1716
 
1623
1717
  // agentYaml & auth still needed, keep non-workspace writes here
1624
- const agentYaml = `name: ${meta.agentId}\ndescription: "${meta.desc}"\n\nmodel:\n primary: ${modelsPrimary}`;
1625
- await fs.writeFile(path.join(rootClawDir, 'agents', `${meta.agentId}.yaml`), agentYaml);
1718
+ // .yaml removed OpenClaw reads config exclusively from openclaw.json
1626
1719
  if (Object.keys(authProfilesJson).length > 0) {
1627
1720
  await fs.writeJson(path.join(rootClawDir, 'agents', meta.agentId, 'agent', 'auth-profiles.json'), authProfilesJson, { spaces: 2 });
1628
1721
  }
@@ -1634,10 +1727,11 @@ async function main() {
1634
1727
  selectedSkills, deployMode,
1635
1728
  isDesktop: hasBrowserDesktop, isServer: hasBrowserServer,
1636
1729
  isMultiBot: true, ownAliases, otherAgents,
1637
- teamRoster: teamMdRoster, userInfo,
1638
- agentWorkspaceDir: meta.workspaceDir,
1639
- isRelayBot: true,
1640
- });
1730
+ teamRoster: teamMdRoster, userInfo,
1731
+ agentWorkspaceDir: meta.workspaceDir,
1732
+ isRelayBot: true,
1733
+ replyToDirectMessages: true,
1734
+ });
1641
1735
  }
1642
1736
  } else {
1643
1737
  const numBotsToConfigure = 1;
@@ -1654,8 +1748,9 @@ async function main() {
1654
1748
  }));
1655
1749
  const ownAliases = [loopBotName, bots[bIndex]?.slashCmd || '', `bot ${bIndex + 1}`].filter(Boolean);
1656
1750
  const otherBotNames = teamRoster.filter((peer) => peer.idx !== bIndex).map((peer) => peer.name);
1657
- const loopAgentId = loopBotName.replace(/\s+/g, '-').toLowerCase();
1658
- const loopBotDir = isMultiBot ? path.join(projectDir, `bot${bIndex+1}`) : projectDir;
1751
+ const loopAgentId = loopBotName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || `bot${bIndex + 1}`;
1752
+ const loopWorkspaceDir = `workspace-${loopAgentId}`;
1753
+ const loopBotDir = isMultiBot ? path.join(projectDir, `bot${bIndex+1}`) : projectDir;
1659
1754
 
1660
1755
  await fs.ensureDir(path.join(loopBotDir, '.openclaw', 'agents', loopAgentId, 'agent'));
1661
1756
  if (Object.keys(authProfilesJson).length > 0) {
@@ -1672,11 +1767,13 @@ async function main() {
1672
1767
  compaction: { mode: 'safeguard' },
1673
1768
  timeoutSeconds: provider.isLocal ? 900 : 120,
1674
1769
  ...(provider.isLocal ? { llm: { idleTimeoutSeconds: 300 } } : {}),
1675
- },
1676
- list: [{
1677
- id: loopAgentId,
1678
- model: { primary: modelsPrimary, fallbacks: [] }
1679
- }]
1770
+ },
1771
+ list: [{
1772
+ id: loopAgentId,
1773
+ workspace: `.openclaw/${loopWorkspaceDir}`,
1774
+ agentDir: `agents/${loopAgentId}/agent`,
1775
+ model: { primary: modelsPrimary, fallbacks: [] }
1776
+ }]
1680
1777
  },
1681
1778
  ...(providerKey === '9router' ? {
1682
1779
  models: {
@@ -1760,7 +1857,7 @@ async function main() {
1760
1857
  await fs.writeJson(path.join(loopBotDir, '.openclaw', 'openclaw.json'), botConfig, { spaces: 2 });
1761
1858
 
1762
1859
  // ── Workspace files: use shared writeWorkspaceFiles() ──────────────────────
1763
- const dockerWorkspaceDir = path.join(loopBotDir, '.openclaw', 'workspace');
1860
+ const dockerWorkspaceDir = path.join(loopBotDir, '.openclaw', loopWorkspaceDir);
1764
1861
  const dockerOwnAliases = [loopBotName, bots[bIndex]?.slashCmd || '', `bot ${bIndex + 1}`].filter(Boolean);
1765
1862
  const dockerOtherAgents = teamRoster
1766
1863
  .filter((peer) => peer.idx !== bIndex)
@@ -1779,70 +1876,44 @@ async function main() {
1779
1876
  isMultiBot,
1780
1877
  ownAliases: dockerOwnAliases,
1781
1878
  otherAgents: dockerOtherAgents,
1782
- teamRoster,
1783
- userInfo,
1784
- agentWorkspaceDir: 'workspace',
1785
- isRelayBot: isMultiBot,
1786
- });
1879
+ teamRoster,
1880
+ userInfo,
1881
+ agentWorkspaceDir: loopWorkspaceDir,
1882
+ isRelayBot: isMultiBot,
1883
+ replyToDirectMessages: true,
1884
+ });
1787
1885
 
1788
1886
  if (isMultiBot) {
1789
1887
  // Append per-bot reply rules to AGENTS.md
1790
1888
  const otherBotNames = teamRoster.filter((p) => p.idx !== bIndex).map((p) => p.name);
1791
1889
  const extraAgentsMd = isVi
1792
- ? `\n\n## Khi nao nen tra loi\n- Trong group, chi tra loi khi tin nhan co alias cua ban: ${dockerOwnAliases.map((a) => `\`${a}\``).join(', ')} hoac username Telegram cua ban.\n- Neu tin nhan khong goi ban, hay im lang hoan toan.\n- Neu tin nhan chi goi ro bot khac ${otherBotNames.length ? otherBotNames.map((n) => `\`${n}\``).join(', ') : '`bot khac`'} thi khong cuop loi.\n- Khi da biet user dang goi ban, hay tha reaction co dinh \`👍\` truoc roi moi tra loi bang text. Khong dung emoji khac.\n- Khi can phoi hop noi bo, dung dung agent id ky thuat trong \`AGENTS.md\`, khong dung ten hien thi.\n- Khi hoi ve vai tro cac bot, dung \`AGENTS.md\` lam nguon su that.`
1793
- : `\n\n## When To Reply\n- In group chats, only reply when the message contains one of your aliases: ${dockerOwnAliases.map((a) => `\`${a}\``).join(', ')} or your Telegram username.\n- If the message is not calling you, stay completely silent.\n- If the message is clearly calling another bot such as ${otherBotNames.length ? otherBotNames.map((n) => `\`${n}\``).join(', ') : '`another bot`'}, do not hijack it.\n- Once you know the user is calling you, add the fixed reaction \`👍\` first, then send the text reply. Do not use any other reaction emoji.\n- When you need internal coordination, use the exact technical agent id from \`AGENTS.md\`, not the display name.\n- Use \`AGENTS.md\` as the source of truth for team roles.`;
1890
+ ? `\n\n## Khi nao nen tra loi\n- Neu metadata khong noi ro day la group/supergroup, mac dinh xem la chat rieng/DM va tra loi binh thuong.\n- Trong group, chi tra loi khi tin nhan co alias cua ban: ${dockerOwnAliases.map((a) => `\`${a}\``).join(', ')} hoac username Telegram cua ban.\n- Quy tac im lang khi tin nhan khong goi ban chi ap dung cho group chat, khong ap dung cho DM/chat rieng.\n- Neu group message chi goi ro bot khac ${otherBotNames.length ? otherBotNames.map((n) => `\`${n}\``).join(', ') : '`bot khac`'} thi khong cuop loi.\n- Gateway tu dong tha ack reaction; khong goi action react thu cong tru khi user yeu cau.\n- Khi can phoi hop noi bo, dung dung agent id ky thuat trong \`AGENTS.md\`, khong dung ten hien thi.\n- Khi hoi ve vai tro cac bot, dung \`AGENTS.md\` lam nguon su that.`
1891
+ : `\n\n## When To Reply\n- If metadata does not clearly say this is a group/supergroup, treat it as a private DM and reply normally.\n- In group chats, only reply when the message contains one of your aliases: ${dockerOwnAliases.map((a) => `\`${a}\``).join(', ')} or your Telegram username.\n- The stay-silent rule for messages not addressed to you applies only to group chats, never to DMs/private chats.\n- If a group message is clearly calling another bot such as ${otherBotNames.length ? otherBotNames.map((n) => `\`${n}\``).join(', ') : '`another bot`'}, do not hijack it.\n- The gateway automatically sends ack reactions; do not call react manually unless the user asks.\n- When you need internal coordination, use the exact technical agent id from \`AGENTS.md\`, not the display name.\n- Use \`AGENTS.md\` as the source of truth for team roles.`;
1794
1892
  await fs.appendFile(path.join(dockerWorkspaceDir, 'AGENTS.md'), extraAgentsMd);
1795
1893
  }
1796
1894
  } // END FOR LOOP
1797
1895
  }
1798
1896
 
1799
1897
  // ── Chrome Debug scripts — via shared builder (same content as wizard ZIP) ─
1800
- const batPath = path.join(projectDir, 'start-chrome-debug.bat');
1801
- await fs.writeFile(batPath, buildChromeDebugBat(), 'utf8');
1802
- const shPath = path.join(projectDir, 'start-chrome-debug.sh');
1803
- await fs.writeFile(shPath, buildChromeDebugSh(), 'utf8');
1804
- try { await fs.chmod(shPath, 0o755); } catch (_) {}
1805
-
1806
- // ── Uninstall script — write to project dir (native only) ─────────────────
1807
- if (deployMode !== 'docker') {
1808
- const _nativeOs = process.platform === 'win32' ? 'win'
1809
- : process.platform === 'darwin' ? 'linux'
1810
- : 'linux-desktop';
1811
- const _uninstallScript = buildCLIUninstallScript({
1812
- os: _nativeOs, projectDir, botName, isDocker: false,
1813
- });
1814
- if (_uninstallScript) {
1815
- await fs.writeFile(path.join(projectDir, _uninstallScript.name), _uninstallScript.content, 'utf8');
1816
- if (_uninstallScript.name.endsWith('.sh')) {
1817
- try { await fs.chmod(path.join(projectDir, _uninstallScript.name), 0o755); } catch (_) {}
1818
- }
1819
- }
1820
- }
1898
+ await writeGeneratedArtifacts(projectDir, buildCliChromeDebugArtifacts());
1821
1899
 
1822
- // ── start-bot.bat / start-bot.sh — one-click restart scripts ─────────────
1823
- // Generated for native deployments only (docker has docker compose up)
1824
- if (deployMode !== 'docker') {
1825
- const { generateStartBotBat, generateStartBotSh } = _require('./setup/generators/gateway-start-gen.js');
1826
-
1827
- // Windows: start-bot.bat
1828
- const startBotBatPath = path.join(projectDir, 'start-bot.bat');
1829
- const startBotBatContent = generateStartBotBat({
1830
- projectDir,
1831
- openclawHome: path.join(projectDir, '.openclaw'),
1832
- is9Router: providerKey === '9router',
1833
- isVi,
1834
- });
1835
- await fs.writeFile(startBotBatPath, startBotBatContent, 'utf8');
1900
+ // ── Uninstall scripts ───────────────────────────────────────────────────────
1901
+ await writeGeneratedArtifacts(projectDir, buildCliUninstallArtifacts({
1902
+ deployMode, osChoice: detectedOS, projectDir, botName,
1903
+ }));
1904
+
1905
+ // ── Upgrade scripts ─────────────────────────────────────────────────────────
1906
+ await writeGeneratedArtifacts(projectDir, buildCliUpgradeArtifacts());
1836
1907
 
1837
- // macOS/Linux: start-bot.sh
1838
- const startBotShPath = path.join(projectDir, 'start-bot.sh');
1839
- const startBotShContent = generateStartBotSh({
1840
- projectDir,
1841
- is9Router: providerKey === '9router',
1842
- isVi,
1843
- });
1844
- await fs.writeFile(startBotShPath, startBotShContent, 'utf8');
1845
- try { await fs.chmod(startBotShPath, 0o755); } catch (_) {}
1908
+ // ── start-bot.bat / start-bot.sh — one-click restart scripts ─────────────
1909
+ // Generated for native deployments only (docker has docker compose up)
1910
+ if (deployMode !== 'docker') {
1911
+ await writeGeneratedArtifacts(projectDir, buildCliStartBotArtifacts({
1912
+ projectDir,
1913
+ openclawHome: path.join(projectDir, '.openclaw'),
1914
+ is9Router: providerKey === '9router',
1915
+ isVi,
1916
+ }));
1846
1917
 
1847
1918
  console.log(chalk.cyan(
1848
1919
  isVi
@@ -1993,7 +2064,7 @@ async function main() {
1993
2064
  native9RouterSyncScriptPath = await writeNative9RouterSyncScript(projectDir);
1994
2065
  }
1995
2066
 
1996
- await ensureProjectRuntimeDirs(projectDir, isVi);
2067
+ await ensureProjectRuntimeDirs(projectDir, isVi);
1997
2068
 
1998
2069
  if (isMultiBot && channelKey === 'telegram') {
1999
2070
  installRelayPluginForProject(projectDir, isVi);