create-openclaw-bot 5.1.0 → 5.1.2
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 +45 -3
- package/CHANGELOG.vi.md +45 -3
- package/README.md +5 -4
- package/README.vi.md +5 -4
- package/cli.js +2376 -2085
- package/package.json +1 -1
- package/setup.js +180 -181
- package/tests/smoke-cli-logic.mjs +119 -15
- package/tmp/live-enable-zalouser.cjs +0 -20
- package/tmp/live-enable-zalouser.js +0 -20
- package/tmp/live-patch-9router.cjs +0 -40
- package/tmp/live-patch-9router.js +0 -40
|
@@ -38,6 +38,30 @@ checks.push(() => expectMatch(
|
|
|
38
38
|
'Docker branch must still auto-install Docker when missing'
|
|
39
39
|
));
|
|
40
40
|
|
|
41
|
+
checks.push(() => expectMatch(
|
|
42
|
+
cli,
|
|
43
|
+
/function shouldReuseInstalledGlobals\(\) \{[\s\S]*OPENCLAW_SETUP_REUSE_GLOBALS[\s\S]*trim\(\)\.toLowerCase\(\)/,
|
|
44
|
+
'CLI must expose an env-flag helper for fast test runs that reuse installed global packages'
|
|
45
|
+
));
|
|
46
|
+
|
|
47
|
+
checks.push(() => expectMatch(
|
|
48
|
+
cli,
|
|
49
|
+
/function installLatestOpenClaw\(\{ isVi, osChoice \}\) \{[\s\S]*installGlobalPackage\('openclaw@latest', \{ isVi, osChoice, displayName: 'openclaw' \}\)[\s\S]*process\.exit\(1\)/,
|
|
50
|
+
'CLI must provide a shared helper that always installs or upgrades openclaw@latest'
|
|
51
|
+
));
|
|
52
|
+
|
|
53
|
+
checks.push(() => expectMatch(
|
|
54
|
+
cli,
|
|
55
|
+
/function installLatestOpenClaw\(\{ isVi, osChoice \}\) \{[\s\S]*shouldReuseInstalledGlobals\(\) && isOpenClawInstalled\(\)[\s\S]*Reusing the installed openclaw/s,
|
|
56
|
+
'CLI fast-test mode must be able to reuse an existing openclaw install'
|
|
57
|
+
));
|
|
58
|
+
|
|
59
|
+
checks.push(() => expectMatch(
|
|
60
|
+
cli,
|
|
61
|
+
/installLatestOpenClaw\(\{ isVi, osChoice \}\);\s*if \(deployMode === 'docker'\) \{/,
|
|
62
|
+
'CLI must install or upgrade openclaw before entering the Docker/native branches'
|
|
63
|
+
));
|
|
64
|
+
|
|
41
65
|
checks.push(() => expectMatch(
|
|
42
66
|
cli,
|
|
43
67
|
/if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\('openclaw@latest', \{ isVi, osChoice, displayName: 'openclaw' \}\)/,
|
|
@@ -52,19 +76,32 @@ checks.push(() => expectMatch(
|
|
|
52
76
|
|
|
53
77
|
checks.push(() => expectMatch(
|
|
54
78
|
cli,
|
|
55
|
-
/if \(providerKey === '9router'
|
|
79
|
+
/if \(providerKey === '9router'\) \{[\s\S]*else if \(!is9RouterInstalled\(\)\) \{[\s\S]*installGlobalPackage\('9router@latest', \{ isVi, osChoice, displayName: '9Router' \}\)/,
|
|
56
80
|
'Native 9Router flow must auto-install 9Router'
|
|
57
81
|
));
|
|
58
82
|
|
|
59
83
|
checks.push(() => expectMatch(
|
|
60
84
|
cli,
|
|
61
|
-
/
|
|
62
|
-
'
|
|
85
|
+
/if \(providerKey === '9router'\) \{[\s\S]*shouldReuseInstalledGlobals\(\) && is9RouterInstalled\(\)[\s\S]*Reusing the installed 9Router[\s\S]*else if \(!is9RouterInstalled\(\)\)/s,
|
|
86
|
+
'CLI fast-test mode must be able to reuse an existing 9Router install'
|
|
87
|
+
));
|
|
88
|
+
|
|
89
|
+
checks.push(() => expectMatch(
|
|
90
|
+
cli,
|
|
91
|
+
/function build9RouterSmartRouteSyncScript\(dbPath\) \{[\s\S]*safeDbPath[\s\S]*providerConnections[\s\S]*smart-route[\s\S]*async function writeNative9RouterSyncScript\(projectDir\) \{[\s\S]*getNative9RouterDataDir\(\), 'db\.json'/s,
|
|
92
|
+
'Native 9Router flow must write a smart-route sync script based on the platform-specific 9Router data directory'
|
|
63
93
|
));
|
|
64
94
|
|
|
65
95
|
checks.push(() => expectMatch(
|
|
66
96
|
cli,
|
|
67
|
-
/
|
|
97
|
+
/function getNative9RouterDataDir\(\) \{[\s\S]*process\.platform === 'win32'[\s\S]*AppData[\s\S]*Roaming[\s\S]*9router[\s\S]*os\.homedir\(\)[\s\S]*\.9router/s,
|
|
98
|
+
'CLI must resolve the correct native 9Router data directory on both Windows and Unix'
|
|
99
|
+
));
|
|
100
|
+
|
|
101
|
+
checks.push(() => expect(
|
|
102
|
+
cli.includes("Removed smart-route (no active providers)")
|
|
103
|
+
&& cli.includes("if(!a.length){removeSmartRoute();return;}")
|
|
104
|
+
&& cli.includes("if(!m.length){removeSmartRoute();return;}"),
|
|
68
105
|
'9Router sync logic in CLI must remove stale smart-route combos when providers are disabled'
|
|
69
106
|
));
|
|
70
107
|
|
|
@@ -94,10 +131,46 @@ checks.push(() => expectMatch(
|
|
|
94
131
|
|
|
95
132
|
checks.push(() => expectMatch(
|
|
96
133
|
cli,
|
|
97
|
-
/function printZaloPersonalLoginInfo\(\{ isVi, deployMode, projectDir \}\) \{[\s\S]*docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose[\s\S]*
|
|
134
|
+
/function printZaloPersonalLoginInfo\(\{ isVi, deployMode, projectDir \}\) \{[\s\S]*docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose[\s\S]*openclaw-zalouser-qr-default\.png[\s\S]*Copy-Item[\s\S]*docker compose cp ai-bot:\$\{qrPath\} \.\/zalo-login-qr\.png/s,
|
|
98
135
|
'CLI must print the dedicated Docker/native Zalo Personal login commands and QR copy path instead of onboarding'
|
|
99
136
|
));
|
|
100
137
|
|
|
138
|
+
checks.push(() => expectMatch(
|
|
139
|
+
cli,
|
|
140
|
+
/function extractZaloPairingCode\(text\) \{[\s\S]*openclaw pairing approve zalouser[\s\S]*Pairing code:/s,
|
|
141
|
+
'CLI must be able to extract a Zalo pairing code from the login output'
|
|
142
|
+
));
|
|
143
|
+
|
|
144
|
+
checks.push(() => expectMatch(
|
|
145
|
+
cli,
|
|
146
|
+
/function approveZaloPairingCode\(\{ pairingCode, projectDir, isVi \}\) \{[\s\S]*openclaw pairing approve zalouser \$\{pairingCode\}/s,
|
|
147
|
+
'CLI must be able to auto-approve a detected Zalo pairing code'
|
|
148
|
+
));
|
|
149
|
+
|
|
150
|
+
checks.push(() => expectMatch(
|
|
151
|
+
cli,
|
|
152
|
+
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*spawn\('openclaw', \['channels', 'login', '--channel', 'zalouser', '--verbose'\]/s,
|
|
153
|
+
'Native Zalo flow must run the zalouser login command'
|
|
154
|
+
));
|
|
155
|
+
|
|
156
|
+
checks.push(() => expectMatch(
|
|
157
|
+
cli,
|
|
158
|
+
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*fs\.remove\(qrSourcePath\)[\s\S]*fs\.remove\(qrProjectPath\)[\s\S]*fs\.copy\(qrSourcePath, qrProjectPath, \{ overwrite: true \}\)/s,
|
|
159
|
+
'Native Zalo flow must clear stale QR files and copy the fresh QR into the project folder'
|
|
160
|
+
));
|
|
161
|
+
|
|
162
|
+
checks.push(() => expectMatch(
|
|
163
|
+
cli,
|
|
164
|
+
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*outputBuffer[\s\S]*extractZaloPairingCode\(outputBuffer\)[\s\S]*approveZaloPairingCode\(\{ pairingCode, projectDir, isVi \}\)/s,
|
|
165
|
+
'Native Zalo flow must auto-approve pairing when the login command emits a pairing code'
|
|
166
|
+
));
|
|
167
|
+
|
|
168
|
+
checks.push(() => expectMatch(
|
|
169
|
+
cli,
|
|
170
|
+
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*let loginSucceeded = false[\s\S]*successPattern[\s\S]*if \(exitCode !== 0 && !loginSucceeded\)[\s\S]*else if \(loginSucceeded && exitCode !== 0\)/s,
|
|
171
|
+
'Native Zalo flow must tolerate non-standard exit codes when the login output already reports success'
|
|
172
|
+
));
|
|
173
|
+
|
|
101
174
|
checks.push(() => expectMatch(
|
|
102
175
|
cli,
|
|
103
176
|
/baseUrl: deployMode === 'native' \? 'http:\/\/localhost:20128\/v1' : 'http:\/\/9router:20128\/v1'/,
|
|
@@ -116,6 +189,36 @@ checks.push(() => expectMatch(
|
|
|
116
189
|
'VPS native 9Router flow must start a standalone 9Router dashboard on port 20128 via PM2'
|
|
117
190
|
));
|
|
118
191
|
|
|
192
|
+
checks.push(() => expectMatch(
|
|
193
|
+
cli,
|
|
194
|
+
/function spawnBackgroundProcess\(command, args, options = \{\}\) \{[\s\S]*if \(process\.platform === 'win32'\)[\s\S]*resolveWindowsCommand[\s\S]*Start-Process -WindowStyle Hidden[\s\S]*powershell\.exe/s,
|
|
195
|
+
'Native desktop background helpers must use hidden Start-Process launches on Windows'
|
|
196
|
+
));
|
|
197
|
+
|
|
198
|
+
checks.push(() => expectMatch(
|
|
199
|
+
cli,
|
|
200
|
+
/function resolveNative9RouterDesktopLaunch\(\) \{[\s\S]*process\.platform === 'win32'[\s\S]*npm root -g[\s\S]*9router', 'app', 'server\.js'[\s\S]*PORT: '20128'[\s\S]*HOSTNAME: '0\.0\.0\.0'[\s\S]*command: '9router'[\s\S]*\['-n', '-t', '-l', '-H', '0\.0\.0\.0', '-p', '20128', '--skip-update'\]/s,
|
|
201
|
+
'Native desktop 9Router launch must bypass the interactive CLI menu on Windows while preserving the standard CLI launch elsewhere'
|
|
202
|
+
));
|
|
203
|
+
|
|
204
|
+
checks.push(() => expectMatch(
|
|
205
|
+
cli,
|
|
206
|
+
/const native9RouterLaunch = resolveNative9RouterDesktopLaunch\(\);[\s\S]*spawnBackgroundProcess\(native9RouterLaunch\.command, native9RouterLaunch\.args, \{[\s\S]*env: native9RouterLaunch\.env/s,
|
|
207
|
+
'Native desktop 9Router flow must launch through the background helper with the resolved launch spec'
|
|
208
|
+
));
|
|
209
|
+
|
|
210
|
+
checks.push(() => expectMatch(
|
|
211
|
+
cli,
|
|
212
|
+
/const routerHealth = await waitFor9RouterApiReady\(\);[\s\S]*admin API chua san sang[\s\S]*admin API is not ready yet/s,
|
|
213
|
+
'Native desktop 9Router flow must warn when the dashboard port opens but the admin API never becomes ready'
|
|
214
|
+
));
|
|
215
|
+
|
|
216
|
+
checks.push(() => expectMatch(
|
|
217
|
+
cli,
|
|
218
|
+
/spawnBackgroundProcess\(process\.execPath, \[native9RouterSyncScriptPath\]/,
|
|
219
|
+
'Native desktop 9Router sync loop must launch through the background helper'
|
|
220
|
+
));
|
|
221
|
+
|
|
119
222
|
checks.push(() => expectMatch(
|
|
120
223
|
cli,
|
|
121
224
|
/function runPm2Save\(\{ projectDir, isVi \}\) \{[\s\S]*execSync\('pm2 save'[\s\S]*PM2 save did not complete/s,
|
|
@@ -124,8 +227,8 @@ checks.push(() => expectMatch(
|
|
|
124
227
|
|
|
125
228
|
checks.push(() => expectMatch(
|
|
126
229
|
cli,
|
|
127
|
-
/const child = spawn\('openclaw', \['gateway', 'run'\], \{
|
|
128
|
-
'Native desktop flows must
|
|
230
|
+
/if \(channelKey === 'zalo-personal'\) \{\s*await runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\);\s*\}[\s\S]*const child = spawn\('openclaw', \['gateway', 'run'\], \{/s,
|
|
231
|
+
'Native desktop flows must finish the Zalo login flow before starting openclaw in foreground'
|
|
129
232
|
));
|
|
130
233
|
|
|
131
234
|
checks.push(() => expectOrder(
|
|
@@ -179,19 +282,20 @@ checks.push(() => expectMatch(
|
|
|
179
282
|
|
|
180
283
|
checks.push(() => expectMatch(
|
|
181
284
|
setup,
|
|
182
|
-
/function providerLines\(arr, shell\) \{[\s\S]*npm install -g 9router[\s\S]*
|
|
285
|
+
/function providerLines\(arr, shell\) \{[\s\S]*npm install -g 9router[\s\S]*npm root -g[\s\S]*Join-Path \$npmRoot[\s\S]*Start-Process -WindowStyle Hidden -FilePath ''node\.exe''[\s\S]*9router-smart-route-sync\.js/s,
|
|
183
286
|
'Native script generation must install and start a standalone 9Router dashboard on port 20128'
|
|
184
287
|
));
|
|
185
288
|
|
|
186
289
|
checks.push(() => expectMatch(
|
|
187
290
|
setup,
|
|
188
|
-
/function native9RouterSyncScriptContent\(\) \{[\s\S]*providerConnections[\s\S]*smart-route/s,
|
|
189
|
-
'Native script generation must embed a 9Router smart-route sync script'
|
|
291
|
+
/function native9RouterSyncScriptContent\(\) \{[\s\S]*process\.platform==='win32'[\s\S]*AppData[\s\S]*Roaming[\s\S]*providerConnections[\s\S]*smart-route/s,
|
|
292
|
+
'Native script generation must embed a 9Router smart-route sync script with the correct Windows data directory'
|
|
190
293
|
));
|
|
191
294
|
|
|
192
|
-
checks.push(() =>
|
|
193
|
-
setup
|
|
194
|
-
|
|
295
|
+
checks.push(() => expect(
|
|
296
|
+
setup.includes("Removed smart-route (no active providers)")
|
|
297
|
+
&& setup.includes("if(!a.length){removeSmartRoute();return;}")
|
|
298
|
+
&& setup.includes("if(!m.length){removeSmartRoute();return;}"),
|
|
195
299
|
'9Router sync logic in setup.js must remove stale smart-route combos when providers are disabled'
|
|
196
300
|
));
|
|
197
301
|
|
|
@@ -245,8 +349,8 @@ checks.push(() => expectMatch(
|
|
|
245
349
|
|
|
246
350
|
checks.push(() => expectMatch(
|
|
247
351
|
setup,
|
|
248
|
-
/docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose[\s\S]*docker compose cp ai-bot:\/tmp\/openclaw\/openclaw-zalouser-qr-default\.png \.\/zalo-login-qr\.png/s,
|
|
249
|
-
'Wizard copy must
|
|
352
|
+
/Native setup now auto-runs the login flow and copies the QR into the project folder[\s\S]*docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose[\s\S]*docker compose cp ai-bot:\/tmp\/openclaw\/openclaw-zalouser-qr-default\.png \.\/zalo-login-qr\.png/s,
|
|
353
|
+
'Wizard copy must mention native auto-login and still show the dedicated Docker QR login command'
|
|
250
354
|
));
|
|
251
355
|
|
|
252
356
|
for (const check of checks) {
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
const configPath = process.argv[2];
|
|
4
|
-
|
|
5
|
-
if (!configPath) {
|
|
6
|
-
console.error('Usage: node live-enable-zalouser.cjs <configPath>');
|
|
7
|
-
process.exit(1);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
11
|
-
config.channels = config.channels || {};
|
|
12
|
-
config.channels.zalouser = {
|
|
13
|
-
...(config.channels.zalouser || {}),
|
|
14
|
-
enabled: true,
|
|
15
|
-
dmPolicy: 'pairing',
|
|
16
|
-
autoReply: true
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
20
|
-
console.log(`Enabled channels.zalouser in ${configPath}`);
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
const configPath = process.argv[2];
|
|
4
|
-
|
|
5
|
-
if (!configPath) {
|
|
6
|
-
console.error('Usage: node live-enable-zalouser.js <configPath>');
|
|
7
|
-
process.exit(1);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
11
|
-
config.channels = config.channels || {};
|
|
12
|
-
config.channels.zalouser = {
|
|
13
|
-
...(config.channels.zalouser || {}),
|
|
14
|
-
enabled: true,
|
|
15
|
-
dmPolicy: 'pairing',
|
|
16
|
-
autoReply: true
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
fs.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
20
|
-
console.log(`Enabled channels.zalouser in ${configPath}`);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
const dbPath = process.argv[2];
|
|
4
|
-
|
|
5
|
-
if (!dbPath) {
|
|
6
|
-
console.error('Usage: node live-patch-9router.cjs <dbPath>');
|
|
7
|
-
process.exit(1);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const models = [
|
|
11
|
-
'cx/gpt-5.4',
|
|
12
|
-
'cx/gpt-5.3-codex',
|
|
13
|
-
'cx/gpt-5.3-codex-high',
|
|
14
|
-
'cx/gpt-5.2-codex',
|
|
15
|
-
'cx/gpt-5.2',
|
|
16
|
-
'cx/gpt-5.1-codex-max',
|
|
17
|
-
'cx/gpt-5.1-codex',
|
|
18
|
-
'cx/gpt-5.1',
|
|
19
|
-
'cx/gpt-5-codex'
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
const db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
|
|
23
|
-
db.combos = db.combos || [];
|
|
24
|
-
|
|
25
|
-
const combo = {
|
|
26
|
-
id: 'smart-route',
|
|
27
|
-
name: 'smart-route',
|
|
28
|
-
alias: 'smart-route',
|
|
29
|
-
models
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const index = db.combos.findIndex((entry) => entry && entry.id === 'smart-route');
|
|
33
|
-
if (index >= 0) {
|
|
34
|
-
db.combos[index] = combo;
|
|
35
|
-
} else {
|
|
36
|
-
db.combos.push(combo);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
|
40
|
-
console.log(`Patched smart-route in ${dbPath}`);
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
|
|
3
|
-
const dbPath = process.argv[2];
|
|
4
|
-
|
|
5
|
-
if (!dbPath) {
|
|
6
|
-
console.error('Usage: node live-patch-9router.js <dbPath>');
|
|
7
|
-
process.exit(1);
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
const models = [
|
|
11
|
-
'cx/gpt-5.4',
|
|
12
|
-
'cx/gpt-5.3-codex',
|
|
13
|
-
'cx/gpt-5.3-codex-high',
|
|
14
|
-
'cx/gpt-5.2-codex',
|
|
15
|
-
'cx/gpt-5.2',
|
|
16
|
-
'cx/gpt-5.1-codex-max',
|
|
17
|
-
'cx/gpt-5.1-codex',
|
|
18
|
-
'cx/gpt-5.1',
|
|
19
|
-
'cx/gpt-5-codex'
|
|
20
|
-
];
|
|
21
|
-
|
|
22
|
-
const db = JSON.parse(fs.readFileSync(dbPath, 'utf8'));
|
|
23
|
-
db.combos = db.combos || [];
|
|
24
|
-
|
|
25
|
-
const combo = {
|
|
26
|
-
id: 'smart-route',
|
|
27
|
-
name: 'smart-route',
|
|
28
|
-
alias: 'smart-route',
|
|
29
|
-
models
|
|
30
|
-
};
|
|
31
|
-
|
|
32
|
-
const index = db.combos.findIndex((entry) => entry && entry.id === 'smart-route');
|
|
33
|
-
if (index >= 0) {
|
|
34
|
-
db.combos[index] = combo;
|
|
35
|
-
} else {
|
|
36
|
-
db.combos.push(combo);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
fs.writeFileSync(dbPath, JSON.stringify(db, null, 2));
|
|
40
|
-
console.log(`Patched smart-route in ${dbPath}`);
|