create-openclaw-bot 5.11.1 → 5.12.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/README.md +32 -36
- package/README.vi.md +32 -36
- package/dist/server/local-server.js +264 -353
- package/dist/setup/data/channels.js +13 -14
- package/dist/setup/shared/bot-config-gen.js +39 -32
- package/dist/setup/shared/common-gen.js +11 -0
- package/dist/setup/shared/docker-gen.js +23 -62
- package/dist/setup/shared/workspace-gen.js +9 -26
- package/dist/web/app.js +48 -13
- package/package.json +2 -2
|
@@ -55,31 +55,30 @@
|
|
|
55
55
|
},
|
|
56
56
|
// 'telegram+zalo-personal' — Combo mode tạm ngưng, nghiên cứu thêm.
|
|
57
57
|
'zalo-personal': {
|
|
58
|
-
name: 'Zalo
|
|
58
|
+
name: 'Zalo cá nhân — OpenClaw Zalo Connect',
|
|
59
59
|
hasZaloPersonal: true,
|
|
60
60
|
envKeys: [],
|
|
61
61
|
envExtra: '',
|
|
62
62
|
credSteps: [
|
|
63
|
-
{ textVi: '⚠️ Zalo
|
|
64
|
-
{ textVi: '
|
|
63
|
+
{ textVi: '⚠️ Zalo cá nhân chạy qua <strong>OpenClaw Zalo Connect (unofficial, zca-js)</strong> — tự động hoá tài khoản cá nhân có thể vi phạm điều khoản Zalo và khiến tài khoản bị hạn chế. <strong>Chỉ nên dùng tài khoản phụ.</strong>', textEn: '⚠️ Personal Zalo runs on <strong>OpenClaw Zalo Connect (unofficial, zca-js)</strong> — automating a personal account may violate Zalo terms and can get the account restricted. <strong>Use a secondary account.</strong>' },
|
|
64
|
+
{ textVi: 'Sau khi tạo bot, bấm <strong>Đăng nhập Zalo</strong> để quét QR ngay trong giao diện. Nếu cần chạy thủ công: <code>openclaw channels login --channel zalo-connect --account default</code>.', textEn: 'After creating the bot, click <strong>Zalo Login</strong> to scan the QR right in the UI. Manual fallback: <code>openclaw channels login --channel zalo-connect --account default</code>.' },
|
|
65
|
+
{ textVi: 'Mặc định an toàn: DM theo cơ chế pairing, group tắt hết — sau khi quét QR hãy tự chọn DM/nhóm được phép trong cấu hình.', textEn: 'Secure defaults: DM uses pairing, all groups start disabled — after the QR scan, explicitly choose the allowed DMs/groups in the config.' },
|
|
65
66
|
],
|
|
67
|
+
// Mirrors buildZaloConnectChannelConfig() in bot-config-gen.js — keys validated
|
|
68
|
+
// against OpenClaw Zalo Connect 3.0.0's strict channel schema.
|
|
66
69
|
channelConfig: {
|
|
67
|
-
|
|
70
|
+
'zalo-connect': {
|
|
68
71
|
enabled: true,
|
|
69
72
|
defaultAccount: 'default',
|
|
70
73
|
accounts: {
|
|
71
|
-
default: {
|
|
72
|
-
dmPolicy: 'open',
|
|
73
|
-
allowFrom: ['*'],
|
|
74
|
-
groupPolicy: 'allowlist',
|
|
75
|
-
groupAllowFrom: ['*'],
|
|
76
|
-
},
|
|
74
|
+
default: { enabled: true },
|
|
77
75
|
},
|
|
78
|
-
dmPolicy: '
|
|
79
|
-
allowFrom: [
|
|
76
|
+
dmPolicy: 'pairing',
|
|
77
|
+
allowFrom: [],
|
|
80
78
|
groupPolicy: 'allowlist',
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
groups: {
|
|
80
|
+
'*': { enabled: false, requireMention: true },
|
|
81
|
+
},
|
|
83
82
|
},
|
|
84
83
|
},
|
|
85
84
|
pluginInstall: '',
|
|
@@ -248,14 +248,14 @@
|
|
|
248
248
|
});
|
|
249
249
|
cfg.plugins = pluginsConfig.plugins;
|
|
250
250
|
|
|
251
|
-
// ── bindings for
|
|
251
|
+
// ── bindings for zalo-connect ────────────────────────────────────────────────
|
|
252
252
|
if (agentMetas.length > 0 && isZaloPersonal(channelKey)) {
|
|
253
253
|
cfg.bindings = cfg.bindings || [];
|
|
254
254
|
const firstAgentId = agentMetas[0]?.agentId || 'bot';
|
|
255
|
-
if (!cfg.bindings.some(b => b.match && b.match.channel === '
|
|
256
|
-
// Account-specific from
|
|
257
|
-
//
|
|
258
|
-
cfg.bindings.push({ agentId: firstAgentId, match: { channel: '
|
|
255
|
+
if (!cfg.bindings.some(b => b.match && b.match.channel === 'zalo-connect')) {
|
|
256
|
+
// Account-specific from day one (plan §5.2): each account needs its own
|
|
257
|
+
// binding + credential namespace even while zalo-connect 2.4.6 is single-account.
|
|
258
|
+
cfg.bindings.push({ agentId: firstAgentId, match: { channel: 'zalo-connect', accountId: 'default' } });
|
|
259
259
|
}
|
|
260
260
|
}
|
|
261
261
|
|
|
@@ -309,31 +309,15 @@
|
|
|
309
309
|
|
|
310
310
|
channels.telegram = telegramConfig;
|
|
311
311
|
} else if (isZaloPersonal(channelKey)) {
|
|
312
|
-
// Zalo Personal
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
groupAllowFrom: ['*'],
|
|
322
|
-
},
|
|
323
|
-
},
|
|
324
|
-
dmPolicy: 'open',
|
|
325
|
-
allowFrom: ['*'],
|
|
326
|
-
groupPolicy: 'allowlist',
|
|
327
|
-
groupAllowFrom: ['*'],
|
|
328
|
-
historyLimit: 50,
|
|
329
|
-
// NOTE: do NOT add `reactionLevel`/`actions` here — the zalouser plugin's config
|
|
330
|
-
// schema is strict and rejects them ("must not have additional properties"), which
|
|
331
|
-
// crashes the gateway on boot. zalouser already supports the `react` action by
|
|
332
|
-
// default; reaction behavior is driven by the prompt (TOOLS.md), not channel config.
|
|
333
|
-
groups: {
|
|
334
|
-
'*': { enabled: true, requireMention: false },
|
|
335
|
-
},
|
|
336
|
-
};
|
|
312
|
+
// Zalo Personal → OpenClaw Zalo Connect, the only personal-Zalo backend.
|
|
313
|
+
// Secure defaults: DM pairing, empty
|
|
314
|
+
// allowFrom, all groups disabled + requireMention until the owner explicitly
|
|
315
|
+
// enables them post-QR. Never default to `allowFrom: ["*"]` / open groups.
|
|
316
|
+
// Every key below is validated against OpenClaw Zalo Connect 3.0.0's schema
|
|
317
|
+
// (`additionalProperties: false`) — do NOT add keys (e.g. historyLimit,
|
|
318
|
+
// groupAllowFrom) without re-checking `openclaw.plugin.json` first, an unknown
|
|
319
|
+
// key crashes the gateway on boot.
|
|
320
|
+
channels['zalo-connect'] = buildZaloConnectChannelConfig();
|
|
337
321
|
} else if (channelKey === 'zalo-bot') {
|
|
338
322
|
channels.zalo = { enabled: true, provider: 'official_account' };
|
|
339
323
|
}
|
|
@@ -342,6 +326,26 @@
|
|
|
342
326
|
}
|
|
343
327
|
|
|
344
328
|
|
|
329
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
330
|
+
// buildZaloConnectChannelConfig — secure-default channels['zalo-connect'] block
|
|
331
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
332
|
+
function buildZaloConnectChannelConfig() {
|
|
333
|
+
return {
|
|
334
|
+
enabled: true,
|
|
335
|
+
defaultAccount: 'default',
|
|
336
|
+
accounts: {
|
|
337
|
+
default: { enabled: true },
|
|
338
|
+
},
|
|
339
|
+
dmPolicy: 'pairing',
|
|
340
|
+
allowFrom: [],
|
|
341
|
+
groupPolicy: 'allowlist',
|
|
342
|
+
groups: {
|
|
343
|
+
'*': { enabled: false, requireMention: true },
|
|
344
|
+
},
|
|
345
|
+
};
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
|
|
345
349
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
346
350
|
// buildPluginsConfig — returns { plugins: { ... } }
|
|
347
351
|
// ═══════════════════════════════════════════════════════════════════════════════
|
|
@@ -361,9 +365,11 @@
|
|
|
361
365
|
|
|
362
366
|
const allow = ['memory-core'];
|
|
363
367
|
|
|
364
|
-
// Zalo Personal channel
|
|
368
|
+
// Zalo Personal → zalo-connect channel plugin (pinned install happens in the
|
|
369
|
+
// entrypoint / creation flow). openclaw-zalo-mod stays a separate opt-in plugin.
|
|
365
370
|
if (isZaloPersonal(channelKey)) {
|
|
366
|
-
|
|
371
|
+
entries['zalo-connect'] = { enabled: true };
|
|
372
|
+
allow.push('zalo-connect');
|
|
367
373
|
}
|
|
368
374
|
|
|
369
375
|
// DuckDuckGo search plugin for web-search
|
|
@@ -518,6 +524,7 @@
|
|
|
518
524
|
generateToken,
|
|
519
525
|
buildOpenclawJson,
|
|
520
526
|
buildChannelConfig,
|
|
527
|
+
buildZaloConnectChannelConfig,
|
|
521
528
|
buildPluginsConfig,
|
|
522
529
|
buildSkillsEntries,
|
|
523
530
|
buildExecApprovalsJson,
|
|
@@ -40,6 +40,13 @@
|
|
|
40
40
|
const TELEGRAM_RELAY_PLUGIN_ID = 'telegram-multibot-relay';
|
|
41
41
|
const TELEGRAM_SETUP_GUIDE_FILENAME = 'TELEGRAM-GROUP-SETUP.md';
|
|
42
42
|
|
|
43
|
+
// ── OpenClaw Zalo Connect — single source of truth ──────────────────────────
|
|
44
|
+
// Pin the maintained fork tag so setup never falls back to upstream/ClawHub.
|
|
45
|
+
const ZALO_CHANNEL_ID = 'zalo-connect';
|
|
46
|
+
const ZALO_PLUGIN_ID = 'zalo-connect';
|
|
47
|
+
const ZALO_CONNECT_VERSION = '3.0.0';
|
|
48
|
+
const ZALO_CONNECT_PLUGIN_SPEC = 'https://github.com/tuanminhhole/openclaw-zalo-connect.git#v3.0.0';
|
|
49
|
+
|
|
43
50
|
function buildRelayPluginInstallCommand(prefix = 'openclaw') {
|
|
44
51
|
return `if [ ! -d "$OPENCLAW_STATE_DIR/extensions/${TELEGRAM_RELAY_PLUGIN_ID}" ]; then ${prefix} plugins install ${TELEGRAM_RELAY_PLUGIN_SPEC} 2>/dev/null || true; fi`;
|
|
45
52
|
}
|
|
@@ -298,6 +305,10 @@ If setup reported a plugin install error, run this after the bot is running:
|
|
|
298
305
|
TELEGRAM_RELAY_PLUGIN_SPEC,
|
|
299
306
|
TELEGRAM_RELAY_PLUGIN_ID,
|
|
300
307
|
TELEGRAM_SETUP_GUIDE_FILENAME,
|
|
308
|
+
ZALO_CHANNEL_ID,
|
|
309
|
+
ZALO_PLUGIN_ID,
|
|
310
|
+
ZALO_CONNECT_VERSION,
|
|
311
|
+
ZALO_CONNECT_PLUGIN_SPEC,
|
|
301
312
|
buildRelayPluginInstallCommand,
|
|
302
313
|
buildRelayPluginInstallCommandWin,
|
|
303
314
|
buildTelegramPostInstallChecklist,
|
|
@@ -223,6 +223,8 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
223
223
|
gatewayPort = 18789,
|
|
224
224
|
routerPort = 20128,
|
|
225
225
|
osChoice = '',
|
|
226
|
+
// Personal-Zalo backend: empty or the single supported `zalo-connect` channel.
|
|
227
|
+
zaloBackend = '',
|
|
226
228
|
} = options;
|
|
227
229
|
// Windows bind-mounts give ClawHub-installed plugins world-writable perms (which openclaw
|
|
228
230
|
// blocks), so on Windows we isolate extensions in a named volume. On macOS/Linux bind-mounts
|
|
@@ -278,21 +280,20 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
278
280
|
' return 0',
|
|
279
281
|
' fi',
|
|
280
282
|
' echo "[entrypoint] plugin $id missing; installing $spec"',
|
|
281
|
-
'
|
|
282
|
-
'
|
|
283
|
-
'
|
|
284
|
-
'
|
|
285
|
-
'
|
|
286
|
-
'
|
|
287
|
-
'
|
|
288
|
-
'
|
|
289
|
-
'
|
|
290
|
-
'
|
|
291
|
-
'
|
|
292
|
-
'
|
|
293
|
-
'
|
|
294
|
-
'
|
|
295
|
-
' fi',
|
|
283
|
+
' case "$spec" in',
|
|
284
|
+
' https://*.git#*)',
|
|
285
|
+
' repo="${spec%%#*}"',
|
|
286
|
+
' ref="${spec##*#}"',
|
|
287
|
+
' tmp="/tmp/openclaw-plugin-$id-$ref"',
|
|
288
|
+
' rm -rf "$tmp"',
|
|
289
|
+
' if git clone --depth 1 --branch "$ref" "$repo" "$tmp" 2>/dev/null; then',
|
|
290
|
+
' openclaw plugins install "$tmp" 2>/dev/null || echo "[entrypoint] warning: failed to install cloned plugin $id"',
|
|
291
|
+
' else',
|
|
292
|
+
' echo "[entrypoint] warning: failed to clone plugin $spec"',
|
|
293
|
+
' fi',
|
|
294
|
+
' ;;',
|
|
295
|
+
' *) openclaw plugins install "$spec" 2>/dev/null || echo "[entrypoint] warning: failed to install plugin $spec" ;;',
|
|
296
|
+
' esac',
|
|
296
297
|
'}',
|
|
297
298
|
'ensure_skill() {',
|
|
298
299
|
' id="$1"',
|
|
@@ -311,52 +312,14 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
311
312
|
// Restore config AFTER plugin installs (which may clobber openclaw.json)
|
|
312
313
|
runtimeParts.push(`node - <<'NODE'\n${restoreConfigScript}\nNODE`);
|
|
313
314
|
runtimeParts.push(`node - <<'NODE'\n${securityCompatScript}\nNODE`);
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
'fi',
|
|
322
|
-
'# Automatically run Zalo sticker-mention patch if skill is present',
|
|
323
|
-
'for MENTIONS_JS in $(find "$OPENCLAW_HOME" -maxdepth 5 -path "*/skills/*/mentions.js" -type f 2>/dev/null); do',
|
|
324
|
-
' if [ -f "$MENTIONS_JS" ]; then',
|
|
325
|
-
' echo "[entrypoint] Running patch: $MENTIONS_JS"',
|
|
326
|
-
' node "$MENTIONS_JS" || echo "[entrypoint] Warning: failed to run patch $MENTIONS_JS"',
|
|
327
|
-
' fi',
|
|
328
|
-
'done',
|
|
329
|
-
].join('\n'));
|
|
330
|
-
// Expose zalouser's ZCA API map (globalThis.__zcaApiByProfile) BEFORE the gateway imports
|
|
331
|
-
// zalouser, so openclaw-zalo-mod can reach the live Zalo API (dashboard Sync Account, group
|
|
332
|
-
// admin lookups, …). zalo-mod's own runtime patch lands AFTER zalouser is already imported,
|
|
333
|
-
// so it never takes effect on the running module — patching here (post sticker-mention restore,
|
|
334
|
-
// pre gateway-run) is the reliable fix.
|
|
335
|
-
{
|
|
336
|
-
const exposeZcaScript = [
|
|
337
|
-
"const fs=require('fs'),cp=require('child_process');",
|
|
338
|
-
"let files=[];",
|
|
339
|
-
"try{ files=cp.execSync('find \"'+(process.env.OPENCLAW_HOME||'')+'/npm\" -path \"*/@openclaw/zalouser/dist/zalo-js-*.js\" -type f 2>/dev/null',{encoding:'utf8'}).split('\\n').filter(Boolean); }catch(e){}",
|
|
340
|
-
"const reps=[['const apiByProfile = /* @__PURE__ */ new Map();','const apiByProfile = globalThis.__zcaApiByProfile || (globalThis.__zcaApiByProfile = /* @__PURE__ */ new Map());'],['const apiByProfile = new Map();','const apiByProfile = globalThis.__zcaApiByProfile || (globalThis.__zcaApiByProfile = new Map());']];",
|
|
341
|
-
"for(const f of files){ try{ let s=fs.readFileSync(f,'utf8'); if(s.includes('globalThis.__zcaApiByProfile')) continue; for(const pair of reps){ if(s.includes(pair[0])){ s=s.replace(pair[0],pair[1]); fs.writeFileSync(f,s); console.log('[entrypoint] exposed __zcaApiByProfile for zalo-mod in '+f); break; } } }catch(e){} }",
|
|
342
|
-
].join('\n');
|
|
343
|
-
runtimeParts.push(`# Expose zalouser ZCA API map for openclaw-zalo-mod (before gateway imports zalouser)\nnode - <<'NODE'\n${exposeZcaScript}\nNODE`);
|
|
315
|
+
if (zaloBackend === 'zalo-connect') {
|
|
316
|
+
// Pinned ZaloConnect install (never `latest` — plan §4.1). ensure_plugin skips the
|
|
317
|
+
// install when extensions/zalo-connect already exists, so restarts never re-download
|
|
318
|
+
// and a reconnect never reinstalls. No dist patching, no mentions.js, no watchdog
|
|
319
|
+
// hacks: sticker/mention/reaction are native ZaloConnect actions.
|
|
320
|
+
const zaloConnectSpec = common.ZALO_CONNECT_PLUGIN_SPEC || 'https://github.com/tuanminhhole/openclaw-zalo-connect.git#v3.0.0';
|
|
321
|
+
runtimeParts.push(`ensure_plugin zalo-connect "${zaloConnectSpec}"`);
|
|
344
322
|
}
|
|
345
|
-
runtimeParts.push([
|
|
346
|
-
'# Zalo channel auto-restart monitor (background)',
|
|
347
|
-
'(',
|
|
348
|
-
' sleep 180',
|
|
349
|
-
' while true; do',
|
|
350
|
-
' sleep 60',
|
|
351
|
-
' STATUS=$(openclaw channels status 2>/dev/null | grep -i "Zalo Personal" || true)',
|
|
352
|
-
' if echo "$STATUS" | grep -qi "stopped"; then',
|
|
353
|
-
' echo "[zalo-monitor] Zalo channel stopped - restarting container in 5s"',
|
|
354
|
-
' sleep 5',
|
|
355
|
-
' kill 1 2>/dev/null || true',
|
|
356
|
-
' fi',
|
|
357
|
-
' done',
|
|
358
|
-
') &',
|
|
359
|
-
].join('\n'));
|
|
360
323
|
runtimeParts.push('openclaw gateway run');
|
|
361
324
|
const runtimeScript = ['#!/bin/sh', 'set -e', ...runtimeParts].join('\n');
|
|
362
325
|
let browserInstall = '';
|
|
@@ -609,5 +572,3 @@ ${appEnvironmentBlock}${plainSingleExtraHosts ? `${extraHostsBlock}\n` : ''}
|
|
|
609
572
|
if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globalThis.__openclawDockerGen) {
|
|
610
573
|
Object.assign(exports, globalThis.__openclawDockerGen);
|
|
611
574
|
}
|
|
612
|
-
|
|
613
|
-
|
|
@@ -73,9 +73,9 @@ Notes:
|
|
|
73
73
|
function buildZaloSoulSection(isVi, botName) {
|
|
74
74
|
const name = botName || 'Bot';
|
|
75
75
|
if (isVi) {
|
|
76
|
-
return `\n\n**RULE — Zalo Group: Phản hồi theo chế độ Silent Mode:**\nKhi nhận tin từ \`channel:
|
|
76
|
+
return `\n\n**RULE — Zalo Group: Phản hồi theo chế độ Silent Mode:**\nKhi nhận tin từ \`channel: zalo-connect\` và \`group_id\` có giá trị:\n\n- Nếu tin nhắn chứa \`@${name}\` → **LUÔN reply** (bất kể silent mode).\n- Nếu tin nhắn bắt đầu bằng \`/\` (slash command) → KHÔNG reply, plugin đã xử lý rồi.\n- Tin thường trong group (không mention, không slash):\n - Nếu **Silent Mode BẬT** → tin này KHÔNG đến được bot (plugin đã chặn).\n - Nếu **Silent Mode TẮT** → tin này ĐẾN ĐƯỢC bot → **reply bình thường** như DM.\n- DM (không có group_id) → reply bình thường.`;
|
|
77
77
|
}
|
|
78
|
-
return `\n\n**RULE — Zalo Group: Reply based on Silent Mode:**\nWhen receiving messages from \`channel:
|
|
78
|
+
return `\n\n**RULE — Zalo Group: Reply based on Silent Mode:**\nWhen receiving messages from \`channel: zalo-connect\` with a \`group_id\`:\n\n- If the message contains \`@${name}\` → **ALWAYS reply** (regardless of silent mode).\n- If the message starts with \`/\` (slash command) → DO NOT reply, the plugin already handled it.\n- Regular group messages (no mention, no slash):\n - If **Silent Mode is ON** → this message does NOT reach the bot (plugin blocks it).\n - If **Silent Mode is OFF** → this message DOES reach the bot → **reply normally** like DM.\n- DM (no group_id) → reply normally.`;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// Structure mirrors OpenClaw's default SOUL.md (Core Truths / Boundaries / Vibe / Continuity);
|
|
@@ -547,7 +547,7 @@ _Good luck out there. Make it count._
|
|
|
547
547
|
- [Operating Manual](./AGENTS.md)`;
|
|
548
548
|
}
|
|
549
549
|
|
|
550
|
-
|
|
550
|
+
function buildCronjobSkillMd(isVi = true, zaloDeliveryChannel = 'zalo-connect') {
|
|
551
551
|
return `---
|
|
552
552
|
name: cronjob
|
|
553
553
|
description: Lên lịch tác vụ định kỳ sử dụng công cụ cron.
|
|
@@ -571,7 +571,7 @@ Truyền tham số \`job\` (object) gồm:
|
|
|
571
571
|
- \`message\`: Nội dung tin nhắn nhắc nhở.
|
|
572
572
|
- **\`delivery\`**:
|
|
573
573
|
- \`mode\`: \`"announce"\`.
|
|
574
|
-
- \`channel\`: \`"
|
|
574
|
+
- \`channel\`: \`"${zaloDeliveryChannel}"\`.
|
|
575
575
|
- \`to\`: ID người nhận hoặc ID nhóm.
|
|
576
576
|
- ⚠️ **QUAN TRỌNG:** Nếu gửi tới Group Zalo, ID nhóm bắt buộc phải thêm tiền tố **\`g:\`** ở đầu (Ví dụ: \`g:1925989252066183028\`). Nếu thiếu \`g:\`, tin nhắn sẽ bị gửi nhầm thành tin cá nhân (DM) hoặc lỗi.
|
|
577
577
|
|
|
@@ -594,14 +594,6 @@ Truyền tham số \`job\` (object) gồm:
|
|
|
594
594
|
return '';
|
|
595
595
|
}
|
|
596
596
|
|
|
597
|
-
function buildStickerMentionSkillMd(botName = 'Williams') {
|
|
598
|
-
return '';
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
function buildStickerMentionJs() {
|
|
602
|
-
return '';
|
|
603
|
-
}
|
|
604
|
-
|
|
605
597
|
function buildSecurityRules(isVi = true) {
|
|
606
598
|
if (isVi) {
|
|
607
599
|
return `\n\n## 🔐 Quy Tắc Bảo Mật — BẮT BUỘC (Red Lines)\n\n**GIỚI HẠN FILE & HỆ THỐNG:**\n- ✅ Được phép đọc/ghi trong: (1) workspace của bạn, và (2) các thư mục/ổ đĩa được CHỦ cấp quyền — mount tại \`/mnt/...\` (xem mục "💽 Thư mục/ổ đĩa được cấp quyền" ở trên nếu có). Mặc định quyền theo PROJECT: mọi bot dùng chung các mount này, trừ khi mục đó ghi giới hạn riêng cho từng bot.\n- ❌ KHÔNG truy cập file/thư mục NGOÀI workspace và các mount \`/mnt/...\` đã được cấp.\n- ❌ KHÔNG tiết lộ file nội bộ \`.openclaw\` (config.json, credentials, registry.json, token...).\n- ❌ KHÔNG tự ý quét/liệt kê thư mục hệ thống (Documents, Desktop, Downloads, AppData, registry, system32, Program Files) khi chưa được mount.\n- ❌ KHÔNG cài đặt phần mềm, driver, hoặc service ngoài Docker\n\n**API KEY & CREDENTIALS:**\n- ❌ KHÔNG BAO GIỜ hiển thị API key, token, hoặc mật khẩu trong chat\n- ❌ KHÔNG viết API key trực tiếp vào mã nguồn\n- ❌ KHÔNG commit file credentials lên Git\n- ✅ LUÔN lưu credentials trong file .env riêng\n- ✅ LUÔN dùng biến môi trường thay vì hardcode\n\n**VÍ CRYPTO & TÀI SẢN SỐ:**\n- ❌ TUYỆT ĐỐI KHÔNG truy cập, đọc, hoặc quét các thư mục ví crypto\n- ❌ KHÔNG quét clipboard (có thể chứa seed phrases)\n- ❌ KHÔNG truy cập browser profile, cookie, hoặc mật khẩu đã lưu\n- ❌ KHÔNG cài đặt npm package lạ (chỉ openclaw và plugin chính thức)\n\n**DOCKER:**\n- ✅ Việc cấp thêm ổ đĩa/thư mục cho bot là do CHỦ chủ động làm qua dashboard (nút "Cấp quyền ổ đĩa") — khi đó mount xuất hiện ở \`/mnt/...\` và bot ĐƯỢC PHÉP dùng. Đây là hợp lệ.\n- ❌ KHÔNG tự thêm/sửa mount trong docker-compose.yml; KHÔNG đề nghị mount nguyên ổ đĩa nếu chủ không yêu cầu.\n- ❌ KHÔNG chạy container với \`--privileged\`.\n- ✅ Chỉ expose cổng thật sự cần thiết.`;
|
|
@@ -1065,11 +1057,10 @@ This is a starting point. Add your own conventions, style, and rules as you figu
|
|
|
1065
1057
|
hasBrowser = false,
|
|
1066
1058
|
hasScheduler = false,
|
|
1067
1059
|
hasZaloMod = false,
|
|
1068
|
-
hasZaloSticker = false,
|
|
1069
1060
|
browserDocVariant = '',
|
|
1070
1061
|
} = options;
|
|
1071
1062
|
|
|
1072
|
-
const isZalo = !!
|
|
1063
|
+
const isZalo = !!hasZaloMod;
|
|
1073
1064
|
|
|
1074
1065
|
const frontmatter = isVi
|
|
1075
1066
|
? `---
|
|
@@ -1091,7 +1082,7 @@ description: Guide for utilizing tools and skills
|
|
|
1091
1082
|
|
|
1092
1083
|
// Reaction guide (DM only). Detailed: lists the emoji palette + concrete examples + the
|
|
1093
1084
|
// exact tool-call shape, to maximize the model's adherence (it's prompt-driven — there is
|
|
1094
|
-
// no config gate for
|
|
1085
|
+
// no separate config gate for Zalo reactions).
|
|
1095
1086
|
// Zalo reactions = EXACTLY the 6 native codes (Zalo renders them as icons). Telegram uses
|
|
1096
1087
|
// unicode. `emoji` value sent to the react action MUST be one of these codes on Zalo.
|
|
1097
1088
|
const reactList = isVi
|
|
@@ -1277,7 +1268,6 @@ Add whatever helps you do your job. This is your cheat sheet.
|
|
|
1277
1268
|
* @property {boolean} [hasScheduler]
|
|
1278
1269
|
* @property {boolean} [hasImageGen]
|
|
1279
1270
|
* @property {boolean} [hasZaloMod]
|
|
1280
|
-
* @property {boolean} [hasZaloSticker]
|
|
1281
1271
|
*/
|
|
1282
1272
|
|
|
1283
1273
|
/**
|
|
@@ -1313,7 +1303,7 @@ Add whatever helps you do your job. This is your cheat sheet.
|
|
|
1313
1303
|
hasScheduler = false,
|
|
1314
1304
|
hasImageGen = false,
|
|
1315
1305
|
hasZaloMod = false,
|
|
1316
|
-
|
|
1306
|
+
zaloBackend = 'zalo-connect',
|
|
1317
1307
|
} = opts;
|
|
1318
1308
|
|
|
1319
1309
|
const isMultiBot = variant === 'relay';
|
|
@@ -1327,7 +1317,7 @@ Add whatever helps you do your job. This is your cheat sheet.
|
|
|
1327
1317
|
}),
|
|
1328
1318
|
'USER.md': buildUserDoc({ isVi, userInfo, variant: userVariant || (isMultiBot ? 'cli-multi' : 'wizard') }),
|
|
1329
1319
|
'TOOLS.md': buildToolsDoc({
|
|
1330
|
-
isVi, skillListStr, workspacePath, variant, agentWorkspaceDir, hasBrowser, hasScheduler, hasZaloMod,
|
|
1320
|
+
isVi, skillListStr, workspacePath, variant, agentWorkspaceDir, hasBrowser, hasScheduler, hasZaloMod, browserDocVariant,
|
|
1331
1321
|
}),
|
|
1332
1322
|
'MEMORY.md': buildMemoryDoc({ isVi, variant: memoryVariant }),
|
|
1333
1323
|
'HEARTBEAT.md': buildHeartbeatDoc({ isVi }),
|
|
@@ -1340,7 +1330,7 @@ Add whatever helps you do your job. This is your cheat sheet.
|
|
|
1340
1330
|
}
|
|
1341
1331
|
|
|
1342
1332
|
if (hasScheduler) {
|
|
1343
|
-
files['skills/cronjob/SKILL.md'] = buildCronjobSkillMd(isVi);
|
|
1333
|
+
files['skills/cronjob/SKILL.md'] = buildCronjobSkillMd(isVi, 'zalo-connect');
|
|
1344
1334
|
}
|
|
1345
1335
|
|
|
1346
1336
|
if (hasImageGen) {
|
|
@@ -1348,11 +1338,6 @@ Add whatever helps you do your job. This is your cheat sheet.
|
|
|
1348
1338
|
files['skills/infographic-generator/image-generator.js'] = buildInfographicGeneratorJs();
|
|
1349
1339
|
}
|
|
1350
1340
|
|
|
1351
|
-
if (hasZaloSticker) {
|
|
1352
|
-
files['skills/sticker-mention/SKILL.md'] = buildStickerMentionSkillMd(botName);
|
|
1353
|
-
files['skills/sticker-mention/mentions.js'] = buildStickerMentionJs();
|
|
1354
|
-
}
|
|
1355
|
-
|
|
1356
1341
|
return files;
|
|
1357
1342
|
}
|
|
1358
1343
|
|
|
@@ -1372,8 +1357,6 @@ Add whatever helps you do your job. This is your cheat sheet.
|
|
|
1372
1357
|
buildCronjobSkillMd,
|
|
1373
1358
|
buildInfographicGeneratorSkillMd,
|
|
1374
1359
|
buildInfographicGeneratorJs,
|
|
1375
|
-
buildStickerMentionSkillMd,
|
|
1376
|
-
buildStickerMentionJs,
|
|
1377
1360
|
buildWorkspaceFileMap,
|
|
1378
1361
|
};
|
|
1379
1362
|
|
package/dist/web/app.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
const $ = (sel) => document.querySelector(sel);
|
|
2
|
-
const state = { tab: 'dashboard', system: null, install: null, files: [], catalog: { skills: [], plugins: [] }, logs: [], zaloLoginOpen: false, zaloLoginLines: [], zaloQrDataUrl: '', lang: localStorage.getItem('openclaw-lang') || 'vi', theme: localStorage.getItem('openclaw-theme') || 'dark', navCollapsed: localStorage.getItem('openclaw-nav')==='1', os: null, mode: null, donateOpen: false, botModalOpen: false, botEditId: '', installModalOpen: false, fbPluginModalOpen: false, installTab: 'docker', installDraft: null, pathModal: null, confirmModal: null, botChannel: 'telegram', botPane: 'list', activeBotId: '', selectedFile: '', botMessage: '', projectConnectMessage: '', pendingProjectDir: '', selectedProjectDir: '', featureFlags: {}, featureInstalled: {}, featureLoading: {}, openDirs: {} };
|
|
2
|
+
const state = { tab: 'dashboard', system: null, install: null, files: [], catalog: { skills: [], plugins: [] }, logs: [], zaloLoginOpen: false, zaloLoginLines: [], zaloQrDataUrl: '', lang: localStorage.getItem('openclaw-lang') || 'vi', theme: localStorage.getItem('openclaw-theme') || 'dark', navCollapsed: localStorage.getItem('openclaw-nav')==='1', os: null, mode: null, donateOpen: false, botModalOpen: false, botEditId: '', installModalOpen: false, fbPluginModalOpen: false, installTab: 'docker', installDraft: null, pathModal: null, confirmModal: null, botChannel: 'telegram', botPane: 'list', activeBotId: '', selectedFile: '', botMessage: '', projectConnectMessage: '', pendingProjectDir: '', selectedProjectDir: '', featureFlags: {}, featureInstalled: {}, featureLoading: {}, zaloBackend: '', zaloHealth: null, openDirs: {} };
|
|
3
3
|
const SVG_CDN = 'https://cdn.jsdelivr.net/gh/glincker/thesvg@main/public/icons';
|
|
4
4
|
const OS_OPTIONS = [
|
|
5
5
|
{ id: 'win', title: 'Windows', subtitle: 'Auto-detected desktop', icon: `${SVG_CDN}/windows/default.svg`, badge: 'Desktop' },
|
|
@@ -13,7 +13,7 @@ const MODE_OPTIONS = [
|
|
|
13
13
|
];
|
|
14
14
|
const BOT_CHANNELS = [
|
|
15
15
|
{ id: 'telegram', title: 'Telegram', subtitle: 'Bot API', icon: `${SVG_CDN}/telegram/default.svg`, badge: 'Tele' },
|
|
16
|
-
{ id: 'zalo-personal', title: 'Zalo
|
|
16
|
+
{ id: 'zalo-personal', title: 'Zalo cá nhân', subtitle: 'OpenClaw Zalo Connect', icon: `${SVG_CDN}/zalo/default.svg`, badge: 'User' },
|
|
17
17
|
{ id: 'zalo-bot', title: 'Zalo API', subtitle: 'Official Account', icon: `${SVG_CDN}/zalo/default.svg`, badge: 'API' },
|
|
18
18
|
{ id: 'fb-messenger', title: 'Facebook', subtitle: 'Messenger', icon: `${SVG_CDN}/messenger/default.svg`, badge: 'FB' },
|
|
19
19
|
{ id: 'discord', title: 'Discord', subtitle: 'Bot', icon: `${SVG_CDN}/discord/default.svg`, badge: 'Discord', comingSoon: true },
|
|
@@ -194,7 +194,7 @@ function zaloLoginModal() {
|
|
|
194
194
|
return `<div class="modal-backdrop zalo-login-backdrop" data-zalo-login="close">
|
|
195
195
|
<section class="donate-modal zalo-login-modal" role="dialog" aria-modal="true" aria-label="Zalo login" onclick="event.stopPropagation()">
|
|
196
196
|
<button class="modal-x" data-zalo-login="close" aria-label="Close"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round"><path d="M6 6l12 12M18 6L6 18"/></svg></button>
|
|
197
|
-
<div class="donate-head"><span aria-hidden="true">Z</span><div><p
|
|
197
|
+
<div class="donate-head"><span aria-hidden="true">Z</span><div><p>${t('Zalo cá nhân','Zalo personal')}</p><h2>${t('Quét mã QR đăng nhập','Scan login QR')}</h2><small>${t('Mở Zalo trên điện thoại → quét QR trong khung dưới. Nếu QR chưa hiện, đợi vài giây.', 'Open Zalo on your phone → scan the QR below. If it is not visible yet, wait a few seconds.')}</small></div></div>
|
|
198
198
|
${state.zaloQrDataUrl ? `<div class="zalo-qr-image-wrap"><img class="zalo-qr-image" src="${state.zaloQrDataUrl}" alt="Zalo login QR"/></div>` : ''}
|
|
199
199
|
<pre class="zalo-qr-log" data-zalo-qr-log>${escapeHtml(lines)}</pre>
|
|
200
200
|
<div class="zalo-login-actions"><button class="secondary" data-zalo-login="close" type="button">${t('\u0110óng','Close')}</button></div>
|
|
@@ -806,6 +806,7 @@ function botView() {
|
|
|
806
806
|
: `<button class="${ch===c.id?'active':''}" data-bot-channel="${c.id}"><img src="${c.icon}" onerror="this.style.display='none'"/>${c.title}<span>${bots.filter(b=>b.channel===c.id).length}</span></button>`
|
|
807
807
|
).join('')}</div>
|
|
808
808
|
${botListPanel(channelBots)}
|
|
809
|
+
${ch === 'zalo-personal' ? zaloHealthCard(channelBots) : ''}
|
|
809
810
|
</div>
|
|
810
811
|
</section>
|
|
811
812
|
<div class="bot-side-col">
|
|
@@ -999,7 +1000,6 @@ function botSkillsPanel() {
|
|
|
999
1000
|
{ id: 'cron', title: 'Cron', desc: 'Native scheduler (SQLite) — cron guide in TOOLS.md' },
|
|
1000
1001
|
{ id: 'image-gen', title: 'Tạo ảnh Infographic', desc: 'Tạo ảnh infographic, poster tự động qua 9Router' },
|
|
1001
1002
|
{ id: 'web-search', title: 'Web Search', desc: 'Tìm kiếm web thời gian thực (DuckDuckGo)' },
|
|
1002
|
-
{ id: 'sticker-mention', title: 'Sticker & Auto-Tag (Zalo)', desc: 'Tự động tag người gửi và gửi sticker Zalo theo từ khóa', channels: ['zalo-personal'] },
|
|
1003
1003
|
{ id: 'learning-memory', title: 'Siêu Trí Nhớ Dài Hạn (learning-memory)', desc: 'Tự động ghi nhớ bài học vào MEMORY.md, tự đóng gói và tiến hóa kỹ năng mới vào skills/' },
|
|
1004
1004
|
];
|
|
1005
1005
|
const plugins = [
|
|
@@ -1015,12 +1015,14 @@ function botSkillsPanel() {
|
|
|
1015
1015
|
// channels (e.g. Zalo helpers won't appear on Telegram/FB bots). Items without a
|
|
1016
1016
|
// `channels` field are universal.
|
|
1017
1017
|
const activeChannel = bot?.channel || state.botChannel || 'telegram';
|
|
1018
|
-
const forChannel = (item) =>
|
|
1018
|
+
const forChannel = (item) => {
|
|
1019
|
+
return !item.channels || item.channels.includes(activeChannel);
|
|
1020
|
+
};
|
|
1019
1021
|
const scope = `${state.install?.projectDir || '-'} ? ${bot?.id || '-'}`;
|
|
1020
1022
|
const row = (item, group) => {
|
|
1021
1023
|
const key = `${group}:${item.id}`;
|
|
1022
1024
|
const loading = !!state.featureLoading[key];
|
|
1023
|
-
const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === '
|
|
1025
|
+
const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === 'learning-memory'));
|
|
1024
1026
|
const isInstalled = !requiresInstall || !!state.featureInstalled?.[key];
|
|
1025
1027
|
|
|
1026
1028
|
const version = requiresInstall && isInstalled ? (state.featureVersions?.[key] || '') : '';
|
|
@@ -1091,7 +1093,8 @@ function wireTab() {
|
|
|
1091
1093
|
document.querySelectorAll('[data-fbplugin]').forEach(el => el.onclick = () => { state.fbPluginModalOpen = false; render(); });
|
|
1092
1094
|
document.querySelectorAll('[data-bot-modal]').forEach(el => el.onclick = () => { state.botModalOpen = el.dataset.botModal === 'open'; if (el.dataset.botModal === 'open') state.botEditId = ''; state.botMessage = ''; render(); });
|
|
1093
1095
|
document.querySelectorAll('[data-edit-bot]').forEach(btn => btn.onclick = (ev) => { ev.stopPropagation(); state.botEditId = btn.dataset.editBot; state.botChannel = (state.install?.bots || []).find((b) => b.id === state.botEditId)?.channel || state.botChannel; state.botModalOpen = true; state.botMessage = ''; render(); });
|
|
1094
|
-
document.querySelectorAll('[data-zalo-login]').forEach(el => el.onclick = () => {
|
|
1096
|
+
document.querySelectorAll('[data-zalo-login]').forEach(el => el.onclick = () => { const opening = el.dataset.zaloLogin === 'open'; if (!opening && state.zaloLoginOpen) api('/api/zalo/login/cancel', { method: 'POST' }).catch(() => {}); state.zaloLoginOpen = opening; render(); });
|
|
1097
|
+
document.querySelectorAll('[data-zalo-health-refresh]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => { await loadZaloHealth(); }));
|
|
1095
1098
|
document.querySelectorAll('[data-zalo-login-trigger]').forEach(btn => btn.onclick = () => withButtonLoading(btn, async () => {
|
|
1096
1099
|
state.zaloLoginOpen = true;
|
|
1097
1100
|
state.zaloQrDataUrl = '';
|
|
@@ -1508,6 +1511,39 @@ async function loadFiles(silent=false){
|
|
|
1508
1511
|
if (!silent) render();
|
|
1509
1512
|
}
|
|
1510
1513
|
async function loadCatalog(silent=false){ state.catalog = await api('/api/catalog'); if (!silent) render(); }
|
|
1514
|
+
function zaloHealthCard(channelBots = []) {
|
|
1515
|
+
if (state.zaloBackend !== 'zalo-connect' || !channelBots.length) return '';
|
|
1516
|
+
const h = state.zaloHealth;
|
|
1517
|
+
const chip = (label, tone) => `<span class="runtime-badge ${tone}" style="font-size:11px;">${label}</span>`;
|
|
1518
|
+
let statusChip = chip(t('Chưa rõ','Unknown'), 'warn');
|
|
1519
|
+
if (h) {
|
|
1520
|
+
if (h.channelStatus === 'connected') statusChip = chip(t('Đã kết nối','Connected'), 'ok');
|
|
1521
|
+
else if (h.channelStatus === 'container-stopped') statusChip = chip(t('Container tắt','Container stopped'), 'bad');
|
|
1522
|
+
else if (h.channelStatus === 'disconnected') statusChip = chip(t('Mất kết nối','Disconnected'), 'bad');
|
|
1523
|
+
else if (h.channelStatus === 'starting') statusChip = chip(t('Đang khởi động','Starting'), 'warn');
|
|
1524
|
+
}
|
|
1525
|
+
const ver = h?.installedVersion ? `${h.installedVersion}${h.supportedVersion && h.installedVersion !== h.supportedVersion ? ` → ${h.supportedVersion}` : ''}` : (h ? t('chưa cài','not installed') : '…');
|
|
1526
|
+
return `<div class="card zalo-health-card" style="margin-top:14px; padding:14px;">
|
|
1527
|
+
<div class="card-head" style="margin-bottom:10px; display:flex; justify-content:space-between; align-items:center;">
|
|
1528
|
+
<h3 style="margin:0; font-size:14px;">🩺 Zalo (ZaloConnect)</h3>${statusChip}
|
|
1529
|
+
</div>
|
|
1530
|
+
<div class="dash-version-list" style="margin-top:0;">
|
|
1531
|
+
<div><span>ZaloConnect</span><b>${escapeHtml(ver)}</b></div>
|
|
1532
|
+
<div><span>${t('Phiên QR','QR session')}</span><b>${h ? (h.sessionSaved ? t('Đã lưu','Saved') : t('Chưa đăng nhập','Not logged in')) : '…'}</b></div>
|
|
1533
|
+
<div><span>Zalo Mod</span><b>${h ? (h.zaloModInstalled ? t('Đã cài','Installed') : t('Chưa cài','Not installed')) : '…'}</b></div>
|
|
1534
|
+
${h?.channelStatusLine ? `<div><span>${t('Kênh','Channel')}</span><b style="font-weight:500; font-size:11px; opacity:.8;">${escapeHtml(h.channelStatusLine.slice(0, 60))}</b></div>` : ''}
|
|
1535
|
+
</div>
|
|
1536
|
+
<div style="display:flex; gap:8px; margin-top:12px;">
|
|
1537
|
+
<button class="secondary icon-btn2" data-zalo-health-refresh type="button" style="flex:1; justify-content:center; font-size:12px; height:32px; border-width:1px;">🔄 ${t('Làm mới','Refresh')}</button>
|
|
1538
|
+
<button class="secondary icon-btn2" data-zalo-login-trigger type="button" style="flex:1; justify-content:center; font-size:12px; height:32px; border-width:1px;">🔑 ${t('Kết nối lại','Reconnect')}</button>
|
|
1539
|
+
</div>
|
|
1540
|
+
</div>`;
|
|
1541
|
+
}
|
|
1542
|
+
async function loadZaloHealth(silent=false){
|
|
1543
|
+
if (!activeProjectDir()) { state.zaloHealth = null; return; }
|
|
1544
|
+
try { state.zaloHealth = await api('/api/zalo/health' + projectQuery()); } catch (_) { state.zaloHealth = null; }
|
|
1545
|
+
if (!silent) render();
|
|
1546
|
+
}
|
|
1511
1547
|
async function loadFeatureFlags(silent=false){
|
|
1512
1548
|
const botId=currentBotId();
|
|
1513
1549
|
if (!activeProjectDir()) { state.featureFlags = {}; state.featureInstalled = {}; state.featureVersions = {}; if (!silent) render(); return; }
|
|
@@ -1516,6 +1552,8 @@ async function loadFeatureFlags(silent=false){
|
|
|
1516
1552
|
state.featureFlags = data.flags || {};
|
|
1517
1553
|
state.featureInstalled = data.installed || {};
|
|
1518
1554
|
state.featureVersions = data.versions || {};
|
|
1555
|
+
state.zaloBackend = data.zaloBackend || '';
|
|
1556
|
+
if (state.zaloBackend === 'zalo-connect') loadZaloHealth(true).catch(() => {});
|
|
1519
1557
|
} catch (_) {
|
|
1520
1558
|
state.featureFlags = {};
|
|
1521
1559
|
state.featureInstalled = {};
|
|
@@ -1547,7 +1585,7 @@ function appendLogLine(line) {
|
|
|
1547
1585
|
}, 2000);
|
|
1548
1586
|
}
|
|
1549
1587
|
|
|
1550
|
-
const qrMatch = String(line).match(/^\[
|
|
1588
|
+
const qrMatch = String(line).match(/^\[zalo-connect:qr\]\s+(data:image\/[a-zA-Z0-9.+-]+;base64,\S+)/);
|
|
1551
1589
|
if (qrMatch) {
|
|
1552
1590
|
state.zaloQrDataUrl = qrMatch[1];
|
|
1553
1591
|
state.zaloLoginOpen = true;
|
|
@@ -1561,11 +1599,11 @@ function appendLogLine(line) {
|
|
|
1561
1599
|
return;
|
|
1562
1600
|
}
|
|
1563
1601
|
const html = `<p>${escapeHtml(line)}</p>`;
|
|
1564
|
-
if (state.zaloLoginOpen || /\[
|
|
1602
|
+
if (state.zaloLoginOpen || /\[zalo-connect\]|zalo|qr|login|scan/i.test(line)) {
|
|
1565
1603
|
state.zaloLoginLines.push(cleanTerminalLine(line));
|
|
1566
1604
|
const qr = document.querySelector('[data-zalo-qr-log]');
|
|
1567
1605
|
if (qr) { qr.textContent = state.zaloLoginLines.slice(-120).join('\n'); qr.scrollTop = qr.scrollHeight; }
|
|
1568
|
-
if (/\[
|
|
1606
|
+
if (/\[zalo-connect\].*scan.*qr/i.test(line) && state.zaloQrDataUrl) {
|
|
1569
1607
|
render();
|
|
1570
1608
|
requestAnimationFrame(() => document.querySelector('.zalo-login-modal')?.scrollTo({ top: 0, behavior: 'smooth' }));
|
|
1571
1609
|
}
|
|
@@ -1629,6 +1667,3 @@ render();
|
|
|
1629
1667
|
Promise.allSettled([loadSystem(true), loadStatus(true), loadCatalog(true)])
|
|
1630
1668
|
.then(() => loadFeatureFlags(true).catch(() => {}))
|
|
1631
1669
|
.finally(() => { render(); connectLogs(); });
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-openclaw-bot",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.12.0",
|
|
4
4
|
"description": "Interactive CLI installer for OpenClaw Bot",
|
|
5
5
|
"main": "dist/cli.js",
|
|
6
6
|
"bin": {
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
"build": "node docs_dev/tests/build.mjs --deploy",
|
|
14
14
|
"dev": "node docs_dev/tests/build.mjs --deploy --watch",
|
|
15
15
|
"start": "node dist/cli.js",
|
|
16
|
-
"test": "node docs_dev/tests/smoke-cli-logic.mjs && node docs_dev/tests/test-generation.mjs && node docs_dev/tests/test-matrix.mjs && node docs_dev/tests/test-local-server.mjs",
|
|
16
|
+
"test": "node docs_dev/tests/smoke-cli-logic.mjs && node docs_dev/tests/test-generation.mjs && node docs_dev/tests/test-matrix.mjs && node docs_dev/tests/test-zalo-connect.mjs && node docs_dev/tests/test-local-server.mjs",
|
|
17
17
|
"bump": "node docs_dev/tests/bump-version.mjs",
|
|
18
18
|
"release": "node docs_dev/scripts/update.mjs"
|
|
19
19
|
},
|