create-openclaw-bot 5.17.0 → 5.17.1
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 +3 -3
- package/README.vi.md +4 -4
- package/dist/server/local-server.js +461 -905
- package/dist/setup/shared/windows-launcher-gen.js +29 -3
- package/dist/web/app.js +42 -41
- package/package.json +1 -1
|
@@ -58,7 +58,32 @@ function build9RouterCmd({ projectDir, routerPort }) {
|
|
|
58
58
|
);
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
/**
|
|
61
|
+
/**
|
|
62
|
+
* The node host that gives the bot screen control.
|
|
63
|
+
*
|
|
64
|
+
* Two things here are not obvious and both cost a day:
|
|
65
|
+
* - It runs against `<project>\.openclaw-node`, NOT the bot's state dir. Pointed at the bot's, it
|
|
66
|
+
* loads the bot's plugins too, and zalo-mod's dashboard port is already held by the gateway, so
|
|
67
|
+
* the node dies on `listen EADDRINUSE 127.0.0.1:18790` before publishing computer.act.
|
|
68
|
+
* - It is launched as a .cmd through wscript like everything else on Windows. Spawning the
|
|
69
|
+
* `openclaw.cmd` shim from Node with detached+shell fails outright with `spawn EINVAL`.
|
|
70
|
+
*/
|
|
71
|
+
function buildNodeHostCmd({ projectDir, gatewayPort, gatewayToken }) {
|
|
72
|
+
return CRLF(
|
|
73
|
+
'@echo off\n' +
|
|
74
|
+
'rem Node host cho tinh nang dieu khien may (tool computer/screen cua OpenClaw).\n' +
|
|
75
|
+
`cd /d ${projectDir}\n` +
|
|
76
|
+
'set "HOME=%USERPROFILE%"\n' +
|
|
77
|
+
`set "OPENCLAW_HOME=${projectDir}\\.openclaw-node"\n` +
|
|
78
|
+
`set "OPENCLAW_STATE_DIR=${projectDir}\\.openclaw-node"\n` +
|
|
79
|
+
(gatewayToken ? `set "OPENCLAW_GATEWAY_TOKEN=${gatewayToken}"\n` : '') +
|
|
80
|
+
'"%ProgramFiles%\\nodejs\\node.exe" ' +
|
|
81
|
+
'"%APPDATA%\\npm\\node_modules\\openclaw\\dist\\index.js" ' +
|
|
82
|
+
`node run --host 127.0.0.1 --port ${gatewayPort} --no-tls\n`,
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/** Starts the gateway. Calls node directly - see the --task-supervisor note below. */
|
|
62
87
|
function buildGatewayCmd({ projectDir, gatewayPort }) {
|
|
63
88
|
return CRLF(
|
|
64
89
|
'@echo off\n' +
|
|
@@ -177,12 +202,13 @@ function buildReadme({ projectDir, gatewayPort, routerPort, setupPort }) {
|
|
|
177
202
|
}
|
|
178
203
|
|
|
179
204
|
/** All launcher files for a native Windows project, as { relativeName: content }. */
|
|
180
|
-
function buildWindowsLaunchers({ projectDir, gatewayPort = 18789, routerPort = 20128, setupPort = 51789 }) {
|
|
181
|
-
const opts = { projectDir, gatewayPort, routerPort, setupPort };
|
|
205
|
+
function buildWindowsLaunchers({ projectDir, gatewayPort = 18789, routerPort = 20128, setupPort = 51789, gatewayToken = '' }) {
|
|
206
|
+
const opts = { projectDir, gatewayPort, routerPort, setupPort, gatewayToken };
|
|
182
207
|
return {
|
|
183
208
|
'run-hidden.vbs': buildRunHiddenVbs(),
|
|
184
209
|
'start-9router.cmd': build9RouterCmd(opts),
|
|
185
210
|
'gateway-start.cmd': buildGatewayCmd(opts),
|
|
211
|
+
'node-host.cmd': buildNodeHostCmd(opts),
|
|
186
212
|
'setup-ui.cmd': buildSetupUiCmd(opts),
|
|
187
213
|
'0 - DOC TRUOC.txt': buildReadme(opts),
|
|
188
214
|
'1 - KHOI DONG BOT.cmd': buildStartBotCmd(opts),
|
package/dist/web/app.js
CHANGED
|
@@ -324,47 +324,53 @@ function openPathModal({ title, message, value = '', placeholder = '', field2 =
|
|
|
324
324
|
* one thing only the operator can do — grant the OS screen permissions from the system settings.
|
|
325
325
|
*/
|
|
326
326
|
function openComputerUseModal(r = {}) {
|
|
327
|
-
const
|
|
328
|
-
const
|
|
329
|
-
const cli = (r.commands || []).includes('codex');
|
|
327
|
+
const cu = r.computerUse || {};
|
|
328
|
+
const isMac = /mac/i.test(state.system?.os || state.os || '');
|
|
330
329
|
const line = (icon, text) => `<li><span aria-hidden="true">${icon}</span><div>${text}</div></li>`;
|
|
330
|
+
|
|
331
|
+
// OpenClaw's own `computer` tool is the real thing here: screenshot, move, click, type, drag.
|
|
332
|
+
// Codex is only a fallback for machines that have the ChatGPT app, so it must not lead - the
|
|
333
|
+
// old copy said "mouse/keyboard control is macOS-only", which stopped being true once the
|
|
334
|
+
// cua-computer plugin shipped for Windows.
|
|
331
335
|
const statusItems = [
|
|
332
|
-
|
|
333
|
-
? line('✅', t('Bot
|
|
334
|
-
: line('⚠️', t(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
? line('✅', t('Đã thấy ứng dụng ChatGPT/Codex — <b>nhớ để app đang chạy</b>.', 'Found the ChatGPT/Codex desktop app — <b>keep it running</b>.'))
|
|
340
|
-
: line('⚠️', t('CHƯA thấy ứng dụng ChatGPT/Codex — cài rồi mở lên, không có nó thì không điều khiển GUI được.', 'No ChatGPT/Codex desktop app found — install and open it, GUI control needs it.')),
|
|
341
|
-
(r.granted && r.granted.length)
|
|
342
|
-
? line('✅', t(`Đã cấp quyền chạy: ${escapeHtml(r.granted.join(', '))}.`, `Granted: ${escapeHtml(r.granted.join(', '))}.`))
|
|
336
|
+
cu.ok && cu.enabled
|
|
337
|
+
? line('✅', t('Bot <b>nhìn và điều khiển được máy này</b>: chụp màn hình, rê chuột, bấm chuột, gõ phím, kéo thả.', 'Your bot <b>can see and drive this machine</b>: screenshot, move, click, type, drag.'))
|
|
338
|
+
: line('⚠️', t(`Chưa bật được điều khiển máy: ${escapeHtml(cu.error || 'không rõ lý do')}`, `Could not enable computer control: ${escapeHtml(cu.error || 'unknown reason')}`)),
|
|
339
|
+
// No app list any more: the bot opens things the way a person does - Start menu, type the
|
|
340
|
+
// name, Enter - so it is not limited to whatever we managed to enumerate.
|
|
341
|
+
cu.ok && cu.enabled
|
|
342
|
+
? line('✅', t('Mở được <b>mọi ứng dụng đang cài</b> trên máy, kể cả app mới cài hôm qua.', 'Can open <b>any installed app</b>, including one installed yesterday.'))
|
|
343
343
|
: '',
|
|
344
344
|
].filter(Boolean).join('');
|
|
345
|
+
|
|
346
|
+
// Windows needs no privacy prompts; macOS does, and only from System Settings.
|
|
347
|
+
const permBlock = isMac ? `
|
|
348
|
+
<h4>${t('Cấp quyền màn hình cho máy','Grant the OS screen permissions')}</h4>
|
|
349
|
+
<p>${t('macOS chỉ cấp <b>Screen Recording</b> và <b>Accessibility</b> từ System Settings. Bấm nút dưới rồi bật cho <code>node</code>.','macOS only grants <b>Screen Recording</b> and <b>Accessibility</b> from System Settings. Click below, then tick <code>node</code>.')}</p>
|
|
350
|
+
<p class="cu-perm-row">
|
|
351
|
+
<button class="secondary cu-btn" type="button" data-host-perm="screen">${t('Chụp/quay màn hình','Screen recording')}</button>
|
|
352
|
+
<button class="secondary cu-btn" type="button" data-host-perm="accessibility">${t('Accessibility (chuột/bàn phím)','Accessibility (mouse/keys)')}</button>
|
|
353
|
+
</p>` : '';
|
|
354
|
+
|
|
345
355
|
state.confirmModal = {
|
|
346
356
|
icon: '🖥️',
|
|
347
357
|
eyebrow: t('Điều khiển máy','PC control'),
|
|
348
|
-
title: t('
|
|
358
|
+
title: cu.ok && cu.enabled ? t('Đã bật điều khiển máy','Computer control is on') : t('Bật chưa xong','Not fully enabled'),
|
|
349
359
|
message: t('Model chính của bot vẫn là smart-route, không đổi.','Your bot keeps smart-route as its primary model.'),
|
|
350
360
|
bodyHtml: `
|
|
351
361
|
<ul class="cu-status">${statusItems}</ul>
|
|
352
|
-
|
|
353
|
-
<p>${t('
|
|
354
|
-
<p class="cu-
|
|
355
|
-
|
|
356
|
-
<button class="secondary cu-btn" type="button" data-host-perm="accessibility">${t('Accessibility (chuột/bàn phím)','Accessibility (mouse/keys)')}</button>
|
|
357
|
-
</p>
|
|
358
|
-
<p class="cu-note">${t('Cách dùng: nhắn bot bình thường, ví dụ “mở TeamViewer và đọc giúp mật khẩu trên màn hình” — bot tự giao cho Codex rồi báo kết quả về.','How to use it: just ask your bot normally, e.g. “open TeamViewer and read the password on screen” — it hands the job to Codex and reports back.')}</p>
|
|
359
|
-
<p class="cu-note">${t('Việc giao cho Codex chạy bằng gói ChatGPT đã đăng nhập (tốn quota gói đó); chat thường vẫn đi qua các model free của <code>smart-route</code>. Điều khiển chuột/bàn phím hiện chỉ có trên macOS.','Jobs handed to Codex run on the signed-in ChatGPT plan (they spend that quota); ordinary chat still uses the free <code>smart-route</code> models. Mouse/keyboard control is macOS-only for now.')}</p>
|
|
362
|
+
${permBlock}
|
|
363
|
+
<p class="cu-note">${t('Cách dùng: nhắn bot bình thường, ví dụ “chụp màn hình cho anh xem” hoặc “mở TeamViewer rồi đọc mật khẩu trên màn hình”.','How to use it: just ask your bot normally, e.g. "take a screenshot" or "open TeamViewer and read the password on screen".')}</p>
|
|
364
|
+
<p class="cu-note">${t('Chỉ dùng được trên máy có màn hình. Máy chủ VPS không màn hình thì không áp dụng. Tắt nút này là thu hồi lại toàn bộ quyền trên.','Only works on a machine with a screen. A headless VPS cannot use this. Turning the switch off revokes all of it.')}</p>
|
|
365
|
+
<p class="cu-note">${t('<b>Sau khi khởi động lại máy hoặc đăng xuất Windows, phần điều khiển máy sẽ tắt.</b> Mở lại giao diện này và bật lại nút là xong. Bot vẫn chat bình thường, chỉ riêng phần nhìn và điều khiển màn hình là cần bật lại.','<b>Restarting or signing out of Windows turns computer control off.</b> Open this screen and switch it back on. Chat keeps working meanwhile; only seeing and driving the screen needs re-enabling.')}</p>
|
|
360
366
|
`,
|
|
361
367
|
okText: t('Đã hiểu','Got it'),
|
|
362
368
|
okDanger: false,
|
|
363
369
|
hideCancel: true,
|
|
364
370
|
onConfirm: () => { state.confirmModal = null; render(); },
|
|
365
371
|
};
|
|
366
|
-
render();
|
|
367
372
|
}
|
|
373
|
+
|
|
368
374
|
async function pickFolderPathShared() {
|
|
369
375
|
try {
|
|
370
376
|
const picked = await api('/api/project/pick-folder', { method: 'POST', body: {} });
|
|
@@ -1256,8 +1262,8 @@ function wireTab() {
|
|
|
1256
1262
|
const granted = r.screen && r.screen.supported ? r.screen.granted : null;
|
|
1257
1263
|
showToast(t('Đã mở cài đặt quyền','Opened settings'),
|
|
1258
1264
|
granted === true ? t('Quyền chụp/quay màn hình: đã có. Bật thêm Accessibility nếu cần gõ/click.','Screen recording: already granted. Also enable Accessibility for typing/clicking.')
|
|
1259
|
-
: granted === false ? t('Chưa có quyền
|
|
1260
|
-
: t('Bật quyền cho "node"
|
|
1265
|
+
: granted === false ? t('Chưa có quyền. Bật cho "node" trong danh sách vừa mở, rồi khởi động lại bot.','Not granted yet. Tick "node" in the list that just opened, then restart the bot.')
|
|
1266
|
+
: t('Bật quyền cho "node" trong danh sách vừa mở.','Tick "node" in the list that just opened.'),
|
|
1261
1267
|
granted === false ? 'error' : 'success', 8000);
|
|
1262
1268
|
} catch (err) { showToast(t('Thất bại','Failed'), err.message, 'error'); }
|
|
1263
1269
|
}));
|
|
@@ -1410,23 +1416,18 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1410
1416
|
try { cur = await api('/api/host/control' + projectQuery({})); }
|
|
1411
1417
|
catch (err) { return showToast(t('Thất bại','Failed'), err.message, 'error'); }
|
|
1412
1418
|
const on = !!cur.enabled;
|
|
1413
|
-
|
|
1414
|
-
const grants = cur.grants || [];
|
|
1415
|
-
const chips = (items) => items.map((i) => `<code>${escapeHtml(i)}</code>`).join(' ');
|
|
1416
|
-
const scripts = grants.filter((g) => ['node', 'npx', 'codex'].includes(g));
|
|
1417
|
-
// One row per capability instead of a wall of prose — the operator is granting real access to
|
|
1419
|
+
// One row per capability instead of a wall of prose - the operator is granting real access to
|
|
1418
1420
|
// their machine and should be able to see, at a glance, exactly what each row means.
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
];
|
|
1421
|
+
// There is no app allow-list to show: the bot drives the screen, so it reaches anything the
|
|
1422
|
+
// person sitting at this machine could reach. Say that plainly rather than implying limits.
|
|
1423
|
+
const rows = cur.native ? [
|
|
1424
|
+
{ icon: '📸', title: t('Nhìn màn hình','See the screen'), desc: t('bot chụp được màn hình khi bạn nhờ','the bot can screenshot your screen when you ask') },
|
|
1425
|
+
{ icon: '🖱', title: t('Điều khiển chuột/bàn phím','Mouse & keyboard control'), desc: t('bot rê chuột, bấm, gõ phím và kéo thả trên máy này','the bot moves the mouse, clicks, types and drags on this machine') },
|
|
1426
|
+
{ icon: '🗂', title: t('Mở ứng dụng','Open apps'), desc: t('mọi app đang cài, vì bot thao tác như người ngồi trước máy','any installed app, because it operates the machine like a person would') },
|
|
1427
|
+
] : [];
|
|
1427
1428
|
const bodyHtml = on ? '' : `
|
|
1428
1429
|
<ul class="cu-status grant-list">${rows.map((r) => `<li><span aria-hidden="true">${r.icon}</span><div><b>${r.title}</b><br>${r.desc}</div></li>`).join('')}</ul>
|
|
1429
|
-
<p class="cu-note">${t('
|
|
1430
|
+
<p class="cu-note">${t('Chỉ dùng trên máy có màn hình (không áp dụng VPS headless).','Desktop only (not a headless VPS).')}</p>`;
|
|
1430
1431
|
state.confirmModal = {
|
|
1431
1432
|
icon: '🖥️',
|
|
1432
1433
|
eyebrow: t('Điều khiển máy','PC control'),
|
|
@@ -1439,7 +1440,7 @@ document.querySelectorAll('[data-project-pick-folder]').forEach(btn => btn.oncli
|
|
|
1439
1440
|
okDanger: on,
|
|
1440
1441
|
onConfirm: async () => {
|
|
1441
1442
|
state.confirmModal = null; render();
|
|
1442
|
-
if (!on) showToast(t('Đang bật','Enabling'), t('Đang cấp quyền
|
|
1443
|
+
if (!on) showToast(t('Đang bật','Enabling'), t('Đang cấp quyền và khởi động node điều khiển (có thể mất ~30s)…','Granting access and starting the control node (may take ~30s)…'), 'success');
|
|
1443
1444
|
try {
|
|
1444
1445
|
const r = await api('/api/host/control', { method: 'POST', body: { enabled: !on, projectDir: activeProjectDir() } });
|
|
1445
1446
|
if (!on && r.started && r.started.ok === false && r.started.reason) {
|