create-openclaw-bot 5.3.4 → 5.4.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.
- package/CHANGELOG.md +43 -1
- package/CHANGELOG.vi.md +43 -0
- package/README.md +18 -17
- package/README.vi.md +114 -121
- package/cli.js +2116 -3295
- package/index.html +2 -1
- package/openclaw_correct_structure.md +203 -0
- package/package.json +3 -1
- package/setup.js +5480 -4612
- package/style.css +40 -10
- package/start-chrome-debug.bat +0 -15
- package/start-chrome-debug.sh +0 -70
- package/test-path.bat +0 -4
- package/tests/smoke-cli-logic.mjs +0 -584
|
@@ -1,584 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
|
|
4
|
-
const root = process.cwd();
|
|
5
|
-
const cli = fs.readFileSync(path.join(root, 'cli.js'), 'utf8');
|
|
6
|
-
const setup = fs.readFileSync(path.join(root, 'setup.js'), 'utf8');
|
|
7
|
-
const indexHtml = fs.readFileSync(path.join(root, 'index.html'), 'utf8');
|
|
8
|
-
const cliBytes = fs.readFileSync(path.join(root, 'cli.js'));
|
|
9
|
-
|
|
10
|
-
function expect(condition, message) {
|
|
11
|
-
if (!condition) {
|
|
12
|
-
throw new Error(message);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function expectMatch(source, regex, message) {
|
|
17
|
-
expect(regex.test(source), message);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function expectOrder(source, before, after, message) {
|
|
21
|
-
const beforeIndex = source.indexOf(before);
|
|
22
|
-
const afterIndex = source.indexOf(after);
|
|
23
|
-
expect(beforeIndex !== -1, `${message} (missing first marker)`);
|
|
24
|
-
expect(afterIndex !== -1, `${message} (missing second marker)`);
|
|
25
|
-
expect(beforeIndex < afterIndex, message);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const checks = [];
|
|
29
|
-
|
|
30
|
-
checks.push(() => expectMatch(
|
|
31
|
-
cli,
|
|
32
|
-
/const deployModeDefault = \(osChoice === 'ubuntu' \|\| osChoice === 'vps'\) \? 'native' : 'docker';/,
|
|
33
|
-
'Ubuntu/VPS must default to native deploy mode'
|
|
34
|
-
));
|
|
35
|
-
|
|
36
|
-
checks.push(() => expect(
|
|
37
|
-
!(cliBytes[0] === 0xef && cliBytes[1] === 0xbb && cliBytes[2] === 0xbf),
|
|
38
|
-
'CLI entrypoint must not include a UTF-8 BOM before the shebang'
|
|
39
|
-
));
|
|
40
|
-
|
|
41
|
-
checks.push(() => expectMatch(
|
|
42
|
-
cli,
|
|
43
|
-
/if \(deployMode === 'docker' && !isDockerInstalled\(\)\)/,
|
|
44
|
-
'Docker branch must still auto-install Docker when missing'
|
|
45
|
-
));
|
|
46
|
-
|
|
47
|
-
checks.push(() => expectMatch(
|
|
48
|
-
cli,
|
|
49
|
-
/function shouldReuseInstalledGlobals\(\) \{[\s\S]*OPENCLAW_SETUP_REUSE_GLOBALS[\s\S]*trim\(\)\.toLowerCase\(\)/,
|
|
50
|
-
'CLI must expose an env-flag helper for fast test runs that reuse installed global packages'
|
|
51
|
-
));
|
|
52
|
-
|
|
53
|
-
checks.push(() => expectMatch(
|
|
54
|
-
cli,
|
|
55
|
-
/function installLatestOpenClaw\(\{ isVi, osChoice \}\) \{[\s\S]*installGlobalPackage\(OPENCLAW_NPM_SPEC, \{ isVi, osChoice, displayName: 'openclaw' \}\)[\s\S]*process\.exit\(1\)/,
|
|
56
|
-
'CLI must provide a shared helper that always installs or upgrades the pinned openclaw version'
|
|
57
|
-
));
|
|
58
|
-
|
|
59
|
-
checks.push(() => expectMatch(
|
|
60
|
-
cli,
|
|
61
|
-
/function installLatestOpenClaw\(\{ isVi, osChoice \}\) \{[\s\S]*shouldReuseInstalledGlobals\(\) && isOpenClawInstalled\(\)[\s\S]*Reusing the installed openclaw/s,
|
|
62
|
-
'CLI fast-test mode must be able to reuse an existing openclaw install'
|
|
63
|
-
));
|
|
64
|
-
|
|
65
|
-
checks.push(() => expectMatch(
|
|
66
|
-
cli,
|
|
67
|
-
/installLatestOpenClaw\(\{ isVi, osChoice \}\);\s*if \(deployMode === 'docker'\) \{/,
|
|
68
|
-
'CLI must install or upgrade openclaw before entering the Docker/native branches'
|
|
69
|
-
));
|
|
70
|
-
|
|
71
|
-
checks.push(() => expectMatch(
|
|
72
|
-
cli,
|
|
73
|
-
/if \(!isOpenClawInstalled\(\)\) \{[\s\S]*installGlobalPackage\(OPENCLAW_NPM_SPEC, \{ isVi, osChoice, displayName: 'openclaw' \}\)/,
|
|
74
|
-
'Native branch must auto-install openclaw'
|
|
75
|
-
));
|
|
76
|
-
|
|
77
|
-
checks.push(() => expectMatch(
|
|
78
|
-
cli,
|
|
79
|
-
/if \(osChoice === 'vps'\) \{[\s\S]*installGlobalPackage\('pm2@latest', \{ isVi, osChoice, displayName: 'PM2' \}\)/,
|
|
80
|
-
'VPS native branch must auto-install PM2'
|
|
81
|
-
));
|
|
82
|
-
|
|
83
|
-
checks.push(() => expectMatch(
|
|
84
|
-
cli,
|
|
85
|
-
/if \(providerKey === '9router'\) \{[\s\S]*else if \(!is9RouterInstalled\(\)\) \{[\s\S]*installGlobalPackage\('9router@latest', \{ isVi, osChoice, displayName: '9Router' \}\)/,
|
|
86
|
-
'Native 9Router flow must auto-install 9Router'
|
|
87
|
-
));
|
|
88
|
-
|
|
89
|
-
checks.push(() => expect(
|
|
90
|
-
cli.includes('function providerSupportsMemoryEmbeddings(providerKey) {')
|
|
91
|
-
&& cli.includes("supportsEmbeddings: true")
|
|
92
|
-
&& cli.includes("supportsEmbeddings: false")
|
|
93
|
-
&& cli.includes("function getCliSkillChoices({ providerKey, isVi }) {")
|
|
94
|
-
&& cli.includes("skill.value !== 'memory'")
|
|
95
|
-
&& cli.includes("providerSupportsMemoryEmbeddings(providerKey)"),
|
|
96
|
-
'CLI skill labels must compute memory recommendations from provider embedding capability instead of hardcoding them'
|
|
97
|
-
));
|
|
98
|
-
|
|
99
|
-
checks.push(() => expectMatch(
|
|
100
|
-
cli,
|
|
101
|
-
/if \(providerKey === '9router'\) \{[\s\S]*shouldReuseInstalledGlobals\(\) && is9RouterInstalled\(\)[\s\S]*Reusing the installed 9Router[\s\S]*else if \(!is9RouterInstalled\(\)\)/s,
|
|
102
|
-
'CLI fast-test mode must be able to reuse an existing 9Router install'
|
|
103
|
-
));
|
|
104
|
-
|
|
105
|
-
checks.push(() => expect(
|
|
106
|
-
cli.includes('function build9RouterSmartRouteSyncScript(dbPath) {')
|
|
107
|
-
&& cli.includes('const safeDbPath = JSON.stringify(dbPath);')
|
|
108
|
-
&& cli.includes("const ROUTER='http://localhost:20128';")
|
|
109
|
-
&& cli.includes("fetch(ROUTER + '/api/providers')")
|
|
110
|
-
&& cli.includes("build9RouterSmartRouteSyncScript(path.join(getProject9RouterDataDir(projectDir), 'db.json'))"),
|
|
111
|
-
'Native 9Router flow must write a smart-route sync script into the project-controlled 9Router data directory'
|
|
112
|
-
));
|
|
113
|
-
|
|
114
|
-
checks.push(() => expectMatch(
|
|
115
|
-
cli,
|
|
116
|
-
/function getProjectRuntimeEnv\(projectDir, extraEnv = \{\}\) \{[\s\S]*OPENCLAW_HOME: getProjectOpenClawHome\(projectDir\)[\s\S]*OPENCLAW_STATE_DIR: getProjectOpenClawHome\(projectDir\)[\s\S]*DATA_DIR: getProject9RouterDataDir\(projectDir\)/s,
|
|
117
|
-
'CLI native runtime must derive OPENCLAW_HOME, OPENCLAW_STATE_DIR, and DATA_DIR from the chosen project directory'
|
|
118
|
-
));
|
|
119
|
-
|
|
120
|
-
checks.push(() => expect(
|
|
121
|
-
cli.includes('function getGatewayAllowedOrigins(port) {')
|
|
122
|
-
&& cli.includes('Object.values(os.networkInterfaces() || {})')
|
|
123
|
-
&& cli.includes('`http://localhost:${normalizedPort}`')
|
|
124
|
-
&& cli.includes('`http://127.0.0.1:${normalizedPort}`')
|
|
125
|
-
&& cli.includes('`http://0.0.0.0:${normalizedPort}`'),
|
|
126
|
-
'CLI must derive control UI allowed origins from localhost plus non-internal IPv4 interfaces'
|
|
127
|
-
));
|
|
128
|
-
|
|
129
|
-
checks.push(() => expect(
|
|
130
|
-
cli.includes('function getReachableDashboardHosts(port) {')
|
|
131
|
-
&& cli.includes('pushHost(\'127.0.0.1\')')
|
|
132
|
-
&& cli.includes('pushHost(\'localhost\')')
|
|
133
|
-
&& cli.includes('function rewriteDashboardUrlHost(urlText, fallbackPort, targetBaseUrl) {'),
|
|
134
|
-
'CLI must derive reachable dashboard hosts and rewrite tokenized dashboard URLs for WSL/LAN access'
|
|
135
|
-
));
|
|
136
|
-
|
|
137
|
-
checks.push(() => expect(
|
|
138
|
-
cli.includes("Removed smart-route (no active providers)")
|
|
139
|
-
&& cli.includes("if (!a.length) {")
|
|
140
|
-
&& cli.includes("if (!m.length) {")
|
|
141
|
-
&& cli.includes("removeSmartRoute();"),
|
|
142
|
-
'9Router sync logic in CLI must remove stale smart-route combos when providers are disabled'
|
|
143
|
-
));
|
|
144
|
-
|
|
145
|
-
checks.push(() => expectMatch(
|
|
146
|
-
cli,
|
|
147
|
-
/function ensureUserWritableGlobalNpm\(\{ isVi, osChoice \}\) \{[\s\S]*process\.env\.npm_config_prefix = npmInfo\.prefixDir[\s\S]*npm config set prefix "\$\{npmInfo\.prefixDir\.replace/s,
|
|
148
|
-
'Native CLI must configure a user-writable npm global prefix for non-Windows installs'
|
|
149
|
-
));
|
|
150
|
-
|
|
151
|
-
checks.push(() => expectMatch(
|
|
152
|
-
cli,
|
|
153
|
-
/execSync\('pm2 start ecosystem\.config\.js && pm2 save'/,
|
|
154
|
-
'Native Telegram multi-bot must start through PM2 ecosystem'
|
|
155
|
-
));
|
|
156
|
-
|
|
157
|
-
checks.push(() => expectMatch(
|
|
158
|
-
cli,
|
|
159
|
-
/execFileSync\('pm2', \[[\s\S]*'openclaw'[\s\S]*'gateway'[\s\S]*'run'[\s\S]*getProjectRuntimeEnv\(projectDir\)/s,
|
|
160
|
-
'Native single-bot VPS must start gateway through PM2 with the project runtime environment'
|
|
161
|
-
));
|
|
162
|
-
|
|
163
|
-
checks.push(() => expectMatch(
|
|
164
|
-
cli,
|
|
165
|
-
/function printNativeDashboardAccessInfo\(\{ isVi, providerKey, projectDir, gatewayPort = 18791 \}\) \{[\s\S]*getReachableDashboardHosts\(gatewayPort\)[\s\S]*rewriteDashboardUrlHost[\s\S]*Other reachable URLs[\s\S]*getReachableDashboardHosts\(20128\)/s,
|
|
166
|
-
'Native PM2 flow must expose dashboard access info and the tokenized dashboard command'
|
|
167
|
-
));
|
|
168
|
-
|
|
169
|
-
checks.push(() => expectMatch(
|
|
170
|
-
cli,
|
|
171
|
-
/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,
|
|
172
|
-
'CLI must print the dedicated Docker/native Zalo Personal login commands and QR copy path instead of onboarding'
|
|
173
|
-
));
|
|
174
|
-
|
|
175
|
-
checks.push(() => expectMatch(
|
|
176
|
-
cli,
|
|
177
|
-
/function extractZaloPairingCode\(text\) \{[\s\S]*openclaw pairing approve zalouser[\s\S]*Pairing code:/s,
|
|
178
|
-
'CLI must be able to extract a Zalo pairing code from the login output'
|
|
179
|
-
));
|
|
180
|
-
|
|
181
|
-
checks.push(() => expectMatch(
|
|
182
|
-
cli,
|
|
183
|
-
/function approveZaloPairingCode\(\{ pairingCode, projectDir, isVi \}\) \{[\s\S]*openclaw pairing approve zalouser \$\{pairingCode\}/s,
|
|
184
|
-
'CLI must be able to auto-approve a detected Zalo pairing code'
|
|
185
|
-
));
|
|
186
|
-
|
|
187
|
-
checks.push(() => expectMatch(
|
|
188
|
-
cli,
|
|
189
|
-
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*spawn\('openclaw', \['channels', 'login', '--channel', 'zalouser', '--verbose'\]/s,
|
|
190
|
-
'Native Zalo flow must run the zalouser login command'
|
|
191
|
-
));
|
|
192
|
-
|
|
193
|
-
checks.push(() => expectMatch(
|
|
194
|
-
cli,
|
|
195
|
-
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*fs\.remove\(qrSourcePath\)[\s\S]*fs\.remove\(qrProjectPath\)[\s\S]*fs\.copy\(qrSourcePath, qrProjectPath, \{ overwrite: true \}\)/s,
|
|
196
|
-
'Native Zalo flow must clear stale QR files and copy the fresh QR into the project folder'
|
|
197
|
-
));
|
|
198
|
-
|
|
199
|
-
checks.push(() => expectMatch(
|
|
200
|
-
cli,
|
|
201
|
-
/async function runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\) \{[\s\S]*outputBuffer[\s\S]*extractZaloPairingCode\(outputBuffer\)[\s\S]*approveZaloPairingCode\(\{ pairingCode, projectDir, isVi \}\)/s,
|
|
202
|
-
'Native Zalo flow must auto-approve pairing when the login command emits a pairing code'
|
|
203
|
-
));
|
|
204
|
-
|
|
205
|
-
checks.push(() => expectMatch(
|
|
206
|
-
cli,
|
|
207
|
-
/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,
|
|
208
|
-
'Native Zalo flow must tolerate non-standard exit codes when the login output already reports success'
|
|
209
|
-
));
|
|
210
|
-
|
|
211
|
-
checks.push(() => expectMatch(
|
|
212
|
-
cli,
|
|
213
|
-
/baseUrl: deployMode === 'native' \? 'http:\/\/localhost:20128\/v1' : 'http:\/\/9router:20128\/v1'/,
|
|
214
|
-
'Native 9Router config must target localhost instead of the Docker hostname'
|
|
215
|
-
));
|
|
216
|
-
|
|
217
|
-
checks.push(() => expectMatch(
|
|
218
|
-
cli,
|
|
219
|
-
/controlUi:\s*\{\s*allowedOrigins: getGatewayAllowedOrigins\(18791\)/s,
|
|
220
|
-
'Native shared gateway config must seed control UI allowed origins'
|
|
221
|
-
));
|
|
222
|
-
|
|
223
|
-
checks.push(() => expectMatch(
|
|
224
|
-
cli,
|
|
225
|
-
/controlUi:\s*\{\s*allowedOrigins: getGatewayAllowedOrigins\(18791 \+ \(isMultiBot \? bIndex : 0\)\)/s,
|
|
226
|
-
'Native per-bot gateway config must seed control UI allowed origins for each port'
|
|
227
|
-
));
|
|
228
|
-
|
|
229
|
-
checks.push(() => expectMatch(
|
|
230
|
-
cli,
|
|
231
|
-
/const dockerDir = path\.join\(projectDir, 'docker', 'openclaw'\);\s*await fs\.ensureDir\(dockerDir\);\s*await fs\.writeFile\(path\.join\(dockerDir, 'Dockerfile'\), dockerfile\);[\s\S]*await fs\.ensureDir\(dockerDir\);\s*await fs\.writeFile\(path\.join\(dockerDir, 'docker-compose\.yml'\), compose\);/s,
|
|
232
|
-
'Docker CLI flow must ensure docker/openclaw exists immediately before writing Dockerfile and docker-compose.yml'
|
|
233
|
-
));
|
|
234
|
-
|
|
235
|
-
checks.push(() => expectMatch(
|
|
236
|
-
cli,
|
|
237
|
-
/RUN npm install -g \$\{OPENCLAW_NPM_SPEC\} \$\{OPENCLAW_RUNTIME_PACKAGES\}/,
|
|
238
|
-
'Docker CLI image must install the full OpenClaw runtime package set alongside openclaw'
|
|
239
|
-
));
|
|
240
|
-
|
|
241
|
-
checks.push(() => expect(
|
|
242
|
-
cli.includes("a.add('http://' + entry.address + ':18791')")
|
|
243
|
-
&& cli.includes('allowedOrigins:Array.from(a).filter(Boolean)')
|
|
244
|
-
&& cli.includes("bind:'custom',customBindHost:'0.0.0.0'")
|
|
245
|
-
&& !cli.includes("bind:'loopback'")
|
|
246
|
-
&& !cli.includes("delete c.gateway.customBindHost;")
|
|
247
|
-
&& !cli.includes("const gatewayBridge = 'socat TCP-LISTEN:18791")
|
|
248
|
-
&& !cli.includes("a.add(`http://${entry.address}:18791`)"),
|
|
249
|
-
'Docker CLI patch script must use bind:custom+customBindHost:0.0.0.0, skip socat gateway bridge, and avoid shell-expanding ${entry.address}'
|
|
250
|
-
));
|
|
251
|
-
|
|
252
|
-
checks.push(() => expectMatch(
|
|
253
|
-
cli,
|
|
254
|
-
/hasZaloPersonal\(channelKey\)\) \{[\s\S]*botConfig\.channels\['zalouser'\] = \{\s*enabled: true,\s*dmPolicy: 'open',\s*allowFrom: \['\*'\]/s,
|
|
255
|
-
'CLI must configure Zalo Personal under channels.zalouser'
|
|
256
|
-
));
|
|
257
|
-
|
|
258
|
-
checks.push(() => expectMatch(
|
|
259
|
-
cli,
|
|
260
|
-
/function startNative9RouterPm2\(\{ isVi, projectDir, appName, syncScriptPath \}\) \{[\s\S]*resolveNative9RouterDesktopLaunch\(\)[\s\S]*execFileSync\('pm2'[\s\S]*routerLaunch\.command[\s\S]*--interpreter'?,?[\s\S]*none[\s\S]*routerLaunch\.args[\s\S]*routerLaunch\.env[\s\S]*nohup "\$\{process\.execPath\}" "\$\{normalizedSyncScriptPath\}" >\/tmp\/\$\{syncAppName\}\.log 2>&1 &[\s\S]*runPm2Save\(\{ projectDir, isVi \}\)/s,
|
|
261
|
-
'VPS native 9Router flow must start a standalone 9Router dashboard on port 20128 via PM2'
|
|
262
|
-
));
|
|
263
|
-
|
|
264
|
-
checks.push(() => expectMatch(
|
|
265
|
-
cli,
|
|
266
|
-
/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,
|
|
267
|
-
'Native desktop background helpers must use hidden Start-Process launches on Windows'
|
|
268
|
-
));
|
|
269
|
-
|
|
270
|
-
checks.push(() => expectMatch(
|
|
271
|
-
cli,
|
|
272
|
-
/function resolveNative9RouterDesktopLaunch\(\) \{[\s\S]*resolveCommandOnPath\('9router'\)[\s\S]*command: routerBin[\s\S]*args: \['-n', '-H', '0\.0\.0\.0', '-p', '20128', '--skip-update'\][\s\S]*PORT: '20128'[\s\S]*HOSTNAME: '0\.0\.0\.0'/s,
|
|
273
|
-
'Native desktop 9Router launch must use the 9router CLI binary directly with server args to bypass the interactive menu'
|
|
274
|
-
));
|
|
275
|
-
|
|
276
|
-
checks.push(() => expectMatch(
|
|
277
|
-
cli,
|
|
278
|
-
/const native9RouterLaunch = resolveNative9RouterDesktopLaunch\(\);[\s\S]*spawnBackgroundProcess\(native9RouterLaunch\.command, native9RouterLaunch\.args, \{[\s\S]*getProjectRuntimeEnv\(projectDir, native9RouterLaunch\.env\)/s,
|
|
279
|
-
'Native desktop 9Router flow must launch through the background helper with the resolved launch spec'
|
|
280
|
-
));
|
|
281
|
-
|
|
282
|
-
checks.push(() => expectMatch(
|
|
283
|
-
cli,
|
|
284
|
-
/const routerHealth = await waitFor9RouterApiReady\(\);[\s\S]*admin API chua san sang[\s\S]*admin API is not ready yet/s,
|
|
285
|
-
'Native desktop 9Router flow must warn when the dashboard port opens but the admin API never becomes ready'
|
|
286
|
-
));
|
|
287
|
-
|
|
288
|
-
checks.push(() => expectMatch(
|
|
289
|
-
cli,
|
|
290
|
-
/spawnBackgroundProcess\(process\.execPath, \[native9RouterSyncScriptPath\]/,
|
|
291
|
-
'Native desktop 9Router sync loop must launch through the background helper'
|
|
292
|
-
));
|
|
293
|
-
|
|
294
|
-
checks.push(() => expectMatch(
|
|
295
|
-
cli,
|
|
296
|
-
/function runPm2Save\(\{ projectDir, isVi \}\) \{[\s\S]*execSync\('pm2 save'[\s\S]*PM2 save did not complete/s,
|
|
297
|
-
'Native PM2 save should be handled as a separate recoverable step'
|
|
298
|
-
));
|
|
299
|
-
|
|
300
|
-
checks.push(() => expectMatch(
|
|
301
|
-
cli,
|
|
302
|
-
/if \(hasZaloPersonal\(channelKey\)\) \{\s*await runNativeZaloPersonalLoginFlow\(\{ isVi, projectDir \}\);\s*\}[\s\S]*const child = spawn\('openclaw', \['gateway', 'run'\], \{/s,
|
|
303
|
-
'Native desktop flows must finish the Zalo login flow before starting openclaw in foreground'
|
|
304
|
-
));
|
|
305
|
-
|
|
306
|
-
checks.push(() => expectOrder(
|
|
307
|
-
cli,
|
|
308
|
-
"await ensureProjectRuntimeDirs(projectDir, isVi);",
|
|
309
|
-
"installRelayPluginForProject(projectDir, isVi);",
|
|
310
|
-
'Relay plugin install must happen after preparing the project runtime directories in native flow'
|
|
311
|
-
));
|
|
312
|
-
|
|
313
|
-
checks.push(() => expect(
|
|
314
|
-
!cli.includes('Config synced to ~/.openclaw/')
|
|
315
|
-
&& !cli.includes('Config đã được sync vào ~/.openclaw/')
|
|
316
|
-
&& !cli.includes("cp -rn ${localClawDir}/. ${globalClawDir}/"),
|
|
317
|
-
'CLI native flow must no longer sync project config into the global ~/.openclaw directory'
|
|
318
|
-
));
|
|
319
|
-
|
|
320
|
-
checks.push(() => expectMatch(
|
|
321
|
-
cli,
|
|
322
|
-
/const pm2Apps = \[[\s\S]*args: 'gateway run'/,
|
|
323
|
-
'Native multi-bot ecosystem must run one gateway process'
|
|
324
|
-
));
|
|
325
|
-
|
|
326
|
-
checks.push(() => expectMatch(
|
|
327
|
-
setup,
|
|
328
|
-
/Linux Desktop[^]*Script tự cài Node\.js 20 LTS nếu chưa có, cài OpenClaw CLI[^]*khởi động bot ngay/s,
|
|
329
|
-
'Web wizard Linux Desktop advisory must mention auto-install OpenClaw and immediate start'
|
|
330
|
-
));
|
|
331
|
-
|
|
332
|
-
checks.push(() => expectMatch(
|
|
333
|
-
setup,
|
|
334
|
-
/Ubuntu \/ VPS[^]*Script tự cài Node\.js 20 LTS, OpenClaw CLI, PM2[^]*giữ bot chạy liên tục sau reboot/s,
|
|
335
|
-
'Web wizard VPS advisory must mention OpenClaw CLI and PM2'
|
|
336
|
-
));
|
|
337
|
-
|
|
338
|
-
checks.push(() => expectMatch(
|
|
339
|
-
indexHtml,
|
|
340
|
-
/Tải file → chạy để cài OpenClaw trực tiếp trên máy/,
|
|
341
|
-
'Native download card copy must mention OpenClaw direct install'
|
|
342
|
-
));
|
|
343
|
-
|
|
344
|
-
checks.push(() => expectMatch(
|
|
345
|
-
setup,
|
|
346
|
-
/if \(state\.nativeOs === 'win'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-win\.bat' : 'setup-openclaw-win\.bat';[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*openclaw gateway run/s,
|
|
347
|
-
'Windows native/docker script generation must use the correct file name and start command'
|
|
348
|
-
));
|
|
349
|
-
|
|
350
|
-
checks.push(() => expectMatch(
|
|
351
|
-
setup,
|
|
352
|
-
/else if \(state\.nativeOs === 'linux'\) \{[\s\S]*scriptName = isDocker \? 'setup-openclaw-docker-macos\.sh' : 'setup-openclaw-macos\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*openclaw gateway run/s,
|
|
353
|
-
'macOS script generation must use the correct file name and start command'
|
|
354
|
-
));
|
|
355
|
-
|
|
356
|
-
checks.push(() => expectMatch(
|
|
357
|
-
setup,
|
|
358
|
-
/RUN npm install -g openclaw@2026\.4\.5 \$\{openClawRuntimePackages\}/,
|
|
359
|
-
'Wizard Dockerfile generation must install the full OpenClaw runtime package set alongside openclaw'
|
|
360
|
-
));
|
|
361
|
-
|
|
362
|
-
checks.push(() => expect(
|
|
363
|
-
setup.includes("a.add('http://' + entry.address + ':18791')")
|
|
364
|
-
&& setup.includes('allowedOrigins:Array.from(a).filter(Boolean)')
|
|
365
|
-
&& setup.includes("bind:'custom',customBindHost:'0.0.0.0'")
|
|
366
|
-
&& !setup.includes("bind:'loopback'")
|
|
367
|
-
&& !setup.includes("delete c.gateway.customBindHost;")
|
|
368
|
-
&& !setup.includes("const gatewayBridgePrefix = 'socat TCP-LISTEN:18791")
|
|
369
|
-
&& !setup.includes("a.add(\\`http://\\${entry.address}:18791\\`)"),
|
|
370
|
-
'Wizard Docker patch command must use bind:custom+customBindHost:0.0.0.0, skip socat gateway bridge, and avoid shell-expanding ${entry.address}'
|
|
371
|
-
));
|
|
372
|
-
|
|
373
|
-
checks.push(() => expectMatch(
|
|
374
|
-
setup,
|
|
375
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*scriptName = 'setup-openclaw-vps\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*PROJECT_DIR="[\s\S]*export OPENCLAW_HOME="\$PROJECT_DIR\/\.openclaw"[\s\S]*export OPENCLAW_STATE_DIR="\$PROJECT_DIR\/\.openclaw"[\s\S]*export DATA_DIR="\$PROJECT_DIR\/\.9router"[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*pm2@latest[\s\S]*pm2 save && pm2 startup/s,
|
|
376
|
-
'VPS native script generation must keep runtime files project-local, install openclaw+pm2, and persist PM2 startup'
|
|
377
|
-
));
|
|
378
|
-
|
|
379
|
-
checks.push(() => expect(
|
|
380
|
-
setup.includes('scriptName = isDocker ? \'setup-openclaw-docker-macos.sh\' : \'setup-openclaw-macos.sh\';')
|
|
381
|
-
&& setup.includes('scriptName = \'setup-openclaw-linux.sh\';')
|
|
382
|
-
&& setup.includes('cd "$PROJECT_DIR"')
|
|
383
|
-
&& setup.includes('export OPENCLAW_HOME="$PROJECT_DIR/.openclaw"')
|
|
384
|
-
&& setup.includes('export OPENCLAW_STATE_DIR="$PROJECT_DIR/.openclaw"')
|
|
385
|
-
&& setup.includes('export DATA_DIR="$PROJECT_DIR/.9router"'),
|
|
386
|
-
'Unix native script generation must use project-local runtime directories and launch from PROJECT_DIR'
|
|
387
|
-
));
|
|
388
|
-
|
|
389
|
-
checks.push(() => expect(
|
|
390
|
-
setup.includes("arr.push('call npm install -g 9router || goto :fail');")
|
|
391
|
-
&& setup.includes('function native9RouterServerEntryLookup() {')
|
|
392
|
-
&& setup.includes('return "node -e ')
|
|
393
|
-
&& !setup.includes('return "node -p ')
|
|
394
|
-
&& setup.includes("oc-start9r.ps1") // Windows: writes temp PS1 launcher to avoid CMD→PS quoting issues
|
|
395
|
-
&& setup.includes('NINE_ROUTER_BIN="$(command -v 9router)"')
|
|
396
|
-
&& setup.includes('"$NINE_ROUTER_BIN" -n -H 0.0.0.0 -p 20128 --skip-update')
|
|
397
|
-
&& setup.includes("const p=path.join(process.env.DATA_DIR||'.9router','db.json');")
|
|
398
|
-
&& setup.includes('nohup env DATA_DIR="$PWD/.9router" node ./.9router/9router-smart-route-sync.js > /tmp/9router-sync.log 2>&1 &')
|
|
399
|
-
&& setup.includes('set "PROJECT_DIR=')
|
|
400
|
-
&& setup.includes('set "OPENCLAW_HOME=%PROJECT_DIR%\\\\.openclaw"')
|
|
401
|
-
&& setup.includes('set "OPENCLAW_STATE_DIR=%PROJECT_DIR%\\\\.openclaw"')
|
|
402
|
-
&& setup.includes('set "DATA_DIR=%PROJECT_DIR%\\\\.9router"'),
|
|
403
|
-
'Native script generation must install and start a standalone 9Router dashboard on port 20128'
|
|
404
|
-
));
|
|
405
|
-
|
|
406
|
-
checks.push(() => expect(
|
|
407
|
-
setup.includes("echo OpenClaw Dashboard: http://127.0.0.1:18791")
|
|
408
|
-
&& setup.includes("echo Other reachable URLs: http://localhost:18791")
|
|
409
|
-
&& setup.includes("echo If the dashboard asks for a Gateway Token, run: openclaw dashboard")
|
|
410
|
-
&& setup.includes("echo 9Router Dashboard: http://127.0.0.1:20128/dashboard")
|
|
411
|
-
&& !setup.includes('set "HOME=%PROJECT_DIR%"')
|
|
412
|
-
&& !setup.includes('set "USERPROFILE=%PROJECT_DIR%"'),
|
|
413
|
-
'Windows native script generation must print OpenClaw and 9Router dashboard URLs'
|
|
414
|
-
));
|
|
415
|
-
|
|
416
|
-
checks.push(() => expect(
|
|
417
|
-
setup.includes("bind: 'loopback'")
|
|
418
|
-
&& !setup.includes("bind: 'custom'")
|
|
419
|
-
&& !setup.includes("customBindHost: '0.0.0.0'")
|
|
420
|
-
&& setup.includes("state.bots[state.activeBotIndex].provider = key;")
|
|
421
|
-
&& setup.includes("state.bots[state.activeBotIndex].model = p.models[0].id;")
|
|
422
|
-
&& setup.includes("state.bots[state.activeBotIndex].provider = state.config.provider;")
|
|
423
|
-
&& setup.includes("state.bots[state.activeBotIndex].model = state.config.model;")
|
|
424
|
-
&& setup.includes("if (state.botCount <= 1 && state.bots[0]) {")
|
|
425
|
-
&& setup.includes("state.bots[0].token = botTokenEl.value;")
|
|
426
|
-
&& setup.includes("state.bots[0].apiKey = apiKeyEl.value;")
|
|
427
|
-
&& setup.includes("const authProviderName = provider.isProxy ? '9router' : state.config.provider;")
|
|
428
|
-
&& setup.includes("const authProviderName = botProvider.isProxy ? '9router' : (bot.provider || state.config.provider);")
|
|
429
|
-
&& setup.includes("const nativeSkillConfigs = state.config.skills")
|
|
430
|
-
&& setup.includes("const nativeSkillInstallCmds = nativeSkillConfigs.map((skill) => `call openclaw skills install ${skill.slug} || echo Warning: Failed to install skill ${skill.slug}`);")
|
|
431
|
-
&& setup.includes("lines.push('call npm install -g agent-browser playwright || goto :fail');")
|
|
432
|
-
&& setup.includes("lines.push('call npx playwright install chromium || goto :fail');")
|
|
433
|
-
&& setup.includes("lines.push('echo Cai skills...');")
|
|
434
|
-
&& setup.includes("const openClawRuntimePackages = 'grammy @grammyjs/runner @grammyjs/transformer-throttler @buape/carbon @larksuiteoapi/node-sdk @slack/web-api';")
|
|
435
|
-
&& setup.includes("memory: 'none'")
|
|
436
|
-
&& setup.includes("workspace-\${agentId}\`")
|
|
437
|
-
&& setup.includes("workspace: '.openclaw/' + meta.workspaceDir")
|
|
438
|
-
&& !setup.includes("const authProviderName = provider.isProxy ? '9router' : provider.id;")
|
|
439
|
-
&& !setup.includes("const authProviderName = botProvider.isProxy ? '9router' : botProvider.id;"),
|
|
440
|
-
'Wizard native config generation must keep gateway loopback-local, preserve concrete auth provider ids, disable memory search by default, and sync single-bot provider/model selections into bot state'
|
|
441
|
-
));
|
|
442
|
-
|
|
443
|
-
checks.push(() => expectMatch(
|
|
444
|
-
setup,
|
|
445
|
-
/window\.downloadNativeScript = function\(\) \{[\s\S]*generateOutput\(\);[\s\S]*const script = window\._nativeScript;/,
|
|
446
|
-
'Native script download must regenerate the latest wizard output before reading the cached script'
|
|
447
|
-
));
|
|
448
|
-
|
|
449
|
-
checks.push(() => expectMatch(
|
|
450
|
-
setup,
|
|
451
|
-
/} else if \(is9Router\) \{[\s\S]*container_name: openclaw-bot[\s\S]*depends_on:[\s\S]*- 9router[\s\S]*container_name: 9router[\s\S]*PORT=20128[\s\S]*HOSTNAME=0\.0\.0\.0[\s\S]*9router-data:/s,
|
|
452
|
-
'Wizard single-bot Docker compose must include the 9Router sidecar service and named volume when provider is 9Router'
|
|
453
|
-
));
|
|
454
|
-
|
|
455
|
-
checks.push(() => expectMatch(
|
|
456
|
-
setup,
|
|
457
|
-
/function native9RouterSyncScriptContent\(\) \{[\s\S]*const p=path\.join\(process\.env\.DATA_DIR\|\|'\.9router','db\.json'\);[\s\S]*const ROUTER='http:\/\/localhost:20128';[\s\S]*fetch\(ROUTER\+'\/api\/providers'\)[\s\S]*d\.connections[\s\S]*smart-route/s,
|
|
458
|
-
'Native script generation must keep the 9Router sync script project-local via DATA_DIR and sync active providers from the 9Router API'
|
|
459
|
-
));
|
|
460
|
-
|
|
461
|
-
checks.push(() => expect(
|
|
462
|
-
setup.includes("Removed smart-route (no active providers)")
|
|
463
|
-
&& setup.includes("if (!a.length) {")
|
|
464
|
-
&& setup.includes("if (!m.length) {")
|
|
465
|
-
&& setup.includes("removeSmartRoute();"),
|
|
466
|
-
'9Router sync logic in setup.js must remove stale smart-route combos when providers are disabled'
|
|
467
|
-
));
|
|
468
|
-
|
|
469
|
-
checks.push(() => expect(
|
|
470
|
-
setup.includes('function providerSupportsMemoryEmbeddings(providerKey) {')
|
|
471
|
-
&& setup.includes('function getSkillDisplayName(skill, providerKey, lang) {')
|
|
472
|
-
&& setup.includes('function getSkillExtraNote(skill, providerKey, lang) {')
|
|
473
|
-
&& setup.includes('renderPluginGrid();')
|
|
474
|
-
&& setup.includes("supportsEmbeddings: true")
|
|
475
|
-
&& setup.includes("supportsEmbeddings: false"),
|
|
476
|
-
'Wizard skill cards must recompute memory recommendation labels from provider embedding capability when the provider changes'
|
|
477
|
-
));
|
|
478
|
-
|
|
479
|
-
checks.push(() => expectMatch(
|
|
480
|
-
setup,
|
|
481
|
-
/\.9router\/9router-smart-route-sync\.js[\s\S]*pm2 start --name openclaw-9router-sync/s,
|
|
482
|
-
'VPS native script generation must write and run the 9Router smart-route sync loop'
|
|
483
|
-
));
|
|
484
|
-
|
|
485
|
-
checks.push(() => expectMatch(
|
|
486
|
-
cli,
|
|
487
|
-
/const files=fs\.readdirSync\(dir\)\.filter\(n=>\/\\\\\.js\$\/\.test\(n\)\)[\s\S]*let patched=0[\s\S]*if\(!patched\)\{process\.exit\(0\);\}/,
|
|
488
|
-
'Dockerfile patching in CLI must scan all OpenClaw dist JS files and silently skip when no timeout patch anchor exists'
|
|
489
|
-
));
|
|
490
|
-
|
|
491
|
-
checks.push(() => expect(
|
|
492
|
-
!cli.includes("Buffer.from('\\${Buffer.from(syncComboScript).toString('base64')}','base64')"),
|
|
493
|
-
'CLI must precompute the 9Router sync script base64 instead of leaking Docker Compose interpolation markers'
|
|
494
|
-
));
|
|
495
|
-
|
|
496
|
-
checks.push(() => expectMatch(
|
|
497
|
-
setup,
|
|
498
|
-
/const files=fs\.readdirSync\(dir\)\.filter\(n=>\/\\\\\.js\$\/\.test\(n\)\)[\s\S]*let patched=0[\s\S]*if\(!patched\)\{process\.exit\(0\);\}/,
|
|
499
|
-
'Dockerfile patching in setup.js must scan all OpenClaw dist JS files and silently skip when no timeout patch anchor exists'
|
|
500
|
-
));
|
|
501
|
-
|
|
502
|
-
checks.push(() => expect(
|
|
503
|
-
!setup.includes("Buffer.from('\\${Buffer.from(syncScript).toString('base64')}','base64')"),
|
|
504
|
-
'Wizard compose generation must precompute the 9Router sync script base64 instead of leaking Docker Compose interpolation markers'
|
|
505
|
-
));
|
|
506
|
-
|
|
507
|
-
checks.push(() => expectMatch(
|
|
508
|
-
setup,
|
|
509
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*NINE_ROUTER_ENTRY="\$\([\s\S]*PORT=20128 HOSTNAME=0\.0\.0\.0 pm2 start "\$NINE_ROUTER_ENTRY" --name openclaw-multibot-9router --interpreter "\$\(command -v node\)"[\s\S]*pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw-multibot/s,
|
|
510
|
-
'VPS multi-bot native script must start the shared gateway via PM2'
|
|
511
|
-
));
|
|
512
|
-
|
|
513
|
-
checks.push(() => expectMatch(
|
|
514
|
-
setup,
|
|
515
|
-
/else if \(state\.nativeOs === 'vps'\) \{[\s\S]*NINE_ROUTER_ENTRY="\$\([\s\S]*PORT=20128 HOSTNAME=0\.0\.0\.0 pm2 start "\$NINE_ROUTER_ENTRY" --name openclaw-9router --interpreter "\$\(command -v node\)"[\s\S]*pm2 start --name openclaw -- sh -c "openclaw gateway run"[\s\S]*pm2 logs openclaw/s,
|
|
516
|
-
'VPS single-bot native script must start one bot via PM2'
|
|
517
|
-
));
|
|
518
|
-
|
|
519
|
-
checks.push(() => expectMatch(
|
|
520
|
-
setup,
|
|
521
|
-
/else if \(state\.nativeOs === 'linux-desktop'\) \{[\s\S]*scriptName = 'setup-openclaw-linux\.sh';[\s\S]*npm config set prefix "\$HOME\/\.local"[\s\S]*npm install -g openclaw@2026\.4\.5[\s\S]*openclaw gateway run/s,
|
|
522
|
-
'Linux Desktop native script generation must install openclaw and run the gateway'
|
|
523
|
-
));
|
|
524
|
-
|
|
525
|
-
checks.push(() => expectMatch(
|
|
526
|
-
setup,
|
|
527
|
-
/instrEl\.innerHTML = state\.nativeOs === 'win'[\s\S]*double-click[\s\S]*chmod \+x \$\{scriptName\} && \.\/\$\{scriptName\}/s,
|
|
528
|
-
'Native instructions must show double-click for Windows and chmod for shell scripts'
|
|
529
|
-
));
|
|
530
|
-
|
|
531
|
-
checks.push(() => expectMatch(
|
|
532
|
-
setup,
|
|
533
|
-
/steps\.push\(isVi \? '.*Cài OpenClaw CLI.*' : '.*Install OpenClaw CLI.*'\);/s,
|
|
534
|
-
'Auto-steps summary must mention OpenClaw CLI installation'
|
|
535
|
-
));
|
|
536
|
-
|
|
537
|
-
checks.push(() => expectMatch(
|
|
538
|
-
setup,
|
|
539
|
-
/docker compose exec -it ai-bot openclaw channels login --channel zalouser[\s\S]*docker compose cp ai-bot:\/tmp\/openclaw\/openclaw-zalouser-qr-default\.png \.\/zalo-login-qr\.png/s,
|
|
540
|
-
'Wizard must show dedicated Docker Zalo login and QR copy commands'
|
|
541
|
-
));
|
|
542
|
-
|
|
543
|
-
checks.push(() => expect(
|
|
544
|
-
setup.includes('function getGatewayAllowedOrigins(port) {')
|
|
545
|
-
&& setup.includes('window.location')
|
|
546
|
-
&& setup.includes('`http://localhost:${normalizedPort}`')
|
|
547
|
-
&& setup.includes('`http://127.0.0.1:${normalizedPort}`'),
|
|
548
|
-
'Web wizard must expose a helper that seeds likely control UI origins'
|
|
549
|
-
));
|
|
550
|
-
|
|
551
|
-
checks.push(() => expectMatch(
|
|
552
|
-
setup,
|
|
553
|
-
/controlUi:\s*\{\s*allowedOrigins: getGatewayAllowedOrigins\(18791\)/s,
|
|
554
|
-
'Web wizard single-bot gateway config must seed control UI allowed origins'
|
|
555
|
-
));
|
|
556
|
-
|
|
557
|
-
checks.push(() => expectMatch(
|
|
558
|
-
setup,
|
|
559
|
-
/controlUi:\s*\{\s*allowedOrigins: getGatewayAllowedOrigins\(basePort\)/s,
|
|
560
|
-
'Web wizard per-bot gateway config must seed control UI allowed origins'
|
|
561
|
-
));
|
|
562
|
-
|
|
563
|
-
checks.push(() => expectMatch(
|
|
564
|
-
setup,
|
|
565
|
-
/const patchCmd = `node -e \\\\"const fs=require\('fs'\),os=require\('os'\),p='\/root\/\.openclaw\/openclaw\.json';if\(fs\.existsSync\(p\)\)\{[\s\S]*allowedOrigins:Array\.from\(a\)/s,
|
|
566
|
-
'Web wizard Docker patch command must add interface-based control UI allowed origins'
|
|
567
|
-
));
|
|
568
|
-
|
|
569
|
-
checks.push(() => expect(
|
|
570
|
-
setup.includes("echo [OK] OpenClaw da duoc cai dat thanh cong.")
|
|
571
|
-
&& setup.includes("echo [OK] 9Router da duoc cai dat thanh cong."),
|
|
572
|
-
'Windows BAT must print install success messages after openclaw and 9router are installed'
|
|
573
|
-
));
|
|
574
|
-
|
|
575
|
-
checks.push(() => expect(
|
|
576
|
-
setup.includes("openclaw gateway stop 2>nul"),
|
|
577
|
-
'Windows Zalo flow must clear stale gateway lock (from channels login mini-runtime) before starting the main gateway'
|
|
578
|
-
));
|
|
579
|
-
|
|
580
|
-
for (const check of checks) {
|
|
581
|
-
check();
|
|
582
|
-
}
|
|
583
|
-
|
|
584
|
-
console.log(`Smoke checks passed: ${checks.length}`);
|