create-openclaw-bot 5.8.15 → 5.8.17
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 +2 -2
- package/dist/cli.js +1 -1
- package/dist/server/local-server.js +148 -44
- package/dist/setup/data/header.js +1 -1
- package/dist/setup/shared/docker-gen.js +7 -0
- package/dist/setup/shared/workspace-gen.js +2 -2
- package/dist/web/app.js +6 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🦞 OpenClaw Setup
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.8.
|
|
6
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.8.17-0EA5E9?style=for-the-badge" alt="Version 5.8.17" /></a>
|
|
7
7
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
9
9
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -23,7 +23,7 @@ A next-generation **Web UI Setup** and management dashboard that automates 100%
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
## 🆕 What's New in v5.8.
|
|
26
|
+
## 🆕 What's New in v5.8.17
|
|
27
27
|
|
|
28
28
|
### 🚀 New Features: Deep Integration of Infographic Image Generator, Zalo Sticker & Auto-Tag Skills, and Workspace Docs Optimization
|
|
29
29
|
|
|
@@ -173,6 +173,6 @@ Yes. You can edit the config JSON directly via the integrated File Editor in the
|
|
|
173
173
|
|
|
174
174
|
<div align="center">
|
|
175
175
|
|
|
176
|
-
Made with 🦞 by [
|
|
176
|
+
Made with 🦞 by [tuanminhhole](https://github.com/tuanminhhole)
|
|
177
177
|
|
|
178
178
|
</div>
|
package/README.vi.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🦞 OpenClaw Setup
|
|
4
4
|
|
|
5
5
|
<p align="center">
|
|
6
|
-
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.8.
|
|
6
|
+
<a href="https://github.com/tuanminhhole/openclaw-setup/releases"><img src="https://img.shields.io/badge/RELEASE-v5.8.17-0EA5E9?style=for-the-badge" alt="Version 5.8.17" /></a>
|
|
7
7
|
<a href="https://github.com/tuanminhhole/openclaw-setup?tab=MIT-1-ov-file"><img src="https://img.shields.io/badge/LICENSE-MIT-success?style=for-the-badge" alt="MIT License" /></a>
|
|
8
8
|
<a href="https://www.npmjs.com/package/create-openclaw-bot"><img src="https://img.shields.io/npm/v/create-openclaw-bot?style=for-the-badge&label=CLI&color=2563EB&logo=npm&logoColor=white" alt="NPM Version" /></a>
|
|
9
9
|
<a href="https://github.com/tuanminhhole/openclaw-setup/stargazers"><img src="https://img.shields.io/github/stars/tuanminhhole/openclaw-setup?style=for-the-badge&color=eab308&logo=github&logoColor=white" alt="GitHub Stars" /></a>
|
|
@@ -23,7 +23,7 @@ Trình cài đặt và quản trị **Web UI Setup** thế hệ mới giúp tự
|
|
|
23
23
|
|
|
24
24
|
---
|
|
25
25
|
|
|
26
|
-
## 🆕 Có gì mới trong v5.8.
|
|
26
|
+
## 🆕 Có gì mới trong v5.8.17
|
|
27
27
|
|
|
28
28
|
### 🚀 Tính năng mới: Tích hợp sâu Skill Tạo ảnh Infographic, Skill Sticker & Auto-Tag (Zalo) cùng Tối ưu hóa Workspace Docs
|
|
29
29
|
|
package/dist/cli.js
CHANGED
|
@@ -115,7 +115,7 @@ if (process.env.OPENCLAW_SETUP_WIZARD === 'true' || isLocalRepo()) {
|
|
|
115
115
|
|
|
116
116
|
const child = spawn(process.argv[0], [cliPath, ...args, `--project-dir=${projectDir}`], {
|
|
117
117
|
cwd: targetPath,
|
|
118
|
-
shell:
|
|
118
|
+
shell: false,
|
|
119
119
|
stdio: 'inherit',
|
|
120
120
|
env: {
|
|
121
121
|
...process.env,
|
|
@@ -1085,7 +1085,7 @@ async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
|
1085
1085
|
providerKey: '9router',
|
|
1086
1086
|
deployMode: runtime.mode || state.mode || 'docker',
|
|
1087
1087
|
osChoice: runtime.os || state.os || detectOs(),
|
|
1088
|
-
selectedSkills: ['memory', '
|
|
1088
|
+
selectedSkills: ['memory', 'web-search', 'scheduler'],
|
|
1089
1089
|
skills: dataExport.SKILLS || [],
|
|
1090
1090
|
agentMetas: [],
|
|
1091
1091
|
}));
|
|
@@ -1156,7 +1156,7 @@ async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
|
1156
1156
|
workspacePath: `/home/node/project/.openclaw/${workspaceDir}`,
|
|
1157
1157
|
channel,
|
|
1158
1158
|
hasZaloMod: channel === 'zalo-personal',
|
|
1159
|
-
hasZaloSticker:
|
|
1159
|
+
hasZaloSticker: false,
|
|
1160
1160
|
hasScheduler,
|
|
1161
1161
|
hasImageGen,
|
|
1162
1162
|
});
|
|
@@ -1261,7 +1261,7 @@ async function updateBotInProject(projectDir, agentId, body = {}, runtime = {})
|
|
|
1261
1261
|
workspacePath: `/home/node/project/.openclaw/${workspaceDir}`,
|
|
1262
1262
|
channel,
|
|
1263
1263
|
hasZaloMod: channel === 'zalo-personal',
|
|
1264
|
-
hasZaloSticker:
|
|
1264
|
+
hasZaloSticker: false,
|
|
1265
1265
|
hasScheduler,
|
|
1266
1266
|
hasImageGen,
|
|
1267
1267
|
});
|
|
@@ -1697,10 +1697,15 @@ async function recreateDockerBot(projectDir) {
|
|
|
1697
1697
|
const channel = binding?.match?.channel || 'telegram';
|
|
1698
1698
|
if (channel === 'zalo-personal' || channel === 'zalouser') {
|
|
1699
1699
|
const workspaceDir = workspaceRelForAgent(a, cfg, projectDir) || `workspace-${a.id}`;
|
|
1700
|
-
|
|
1700
|
+
let mentionsJsPath = join(projectDir, '.openclaw', workspaceDir, 'skills/zalo-sticker-mention/mentions.js');
|
|
1701
|
+
let containerJsPath = `/home/node/project/.openclaw/${workspaceDir}/skills/zalo-sticker-mention/mentions.js`;
|
|
1702
|
+
if (!existsSync(mentionsJsPath)) {
|
|
1703
|
+
mentionsJsPath = join(projectDir, '.openclaw', workspaceDir, 'skills/sticker-mention/mentions.js');
|
|
1704
|
+
containerJsPath = `/home/node/project/.openclaw/${workspaceDir}/skills/sticker-mention/mentions.js`;
|
|
1705
|
+
}
|
|
1701
1706
|
if (existsSync(mentionsJsPath)) {
|
|
1702
1707
|
sendLog(`[zalo-patch] Automatically running mentions.js inside container ${containerName}...`);
|
|
1703
|
-
const patchCmd = await runCapture('docker', ['exec', containerName, 'node',
|
|
1708
|
+
const patchCmd = await runCapture('docker', ['exec', containerName, 'node', containerJsPath], { cwd: projectDir, shell: false });
|
|
1704
1709
|
sendLog(`[zalo-patch] Output: ${patchCmd.stdout || ''} ${patchCmd.stderr || ''}`);
|
|
1705
1710
|
}
|
|
1706
1711
|
}
|
|
@@ -1771,7 +1776,7 @@ async function writeCoreProject({ projectDir, osChoice, mode, gatewayPort = 1878
|
|
|
1771
1776
|
await fsp.mkdir(openclawHome, { recursive: true });
|
|
1772
1777
|
await fsp.mkdir(join(openclawHome, 'plugin-runtime-deps'), { recursive: true });
|
|
1773
1778
|
|
|
1774
|
-
const selectedSkills = ['memory', '
|
|
1779
|
+
const selectedSkills = ['memory', 'web-search', 'scheduler'];
|
|
1775
1780
|
const agentMetas = [];
|
|
1776
1781
|
const common = { channelKey: 'telegram', providerKey: '9router', model: DEFAULT_MODEL, deployMode: mode, osChoice, selectedSkills, skills: dataExport.SKILLS || [], agentMetas, gatewayPort, routerPort };
|
|
1777
1782
|
const cfg = buildOpenclawJson(common);
|
|
@@ -1833,7 +1838,7 @@ async function installCore({ osChoice, mode, projectDir, gatewayPort = 18789, ro
|
|
|
1833
1838
|
await fsp.mkdir(dockerDir, { recursive: true });
|
|
1834
1839
|
const envContent = existsSync(rootEnvPath)
|
|
1835
1840
|
? await fsp.readFile(rootEnvPath, 'utf8')
|
|
1836
|
-
: buildEnvFileContent({ channelKey: 'telegram', providerKey: '9router', deployMode: mode, osChoice, selectedSkills: ['memory', '
|
|
1841
|
+
: buildEnvFileContent({ channelKey: 'telegram', providerKey: '9router', deployMode: mode, osChoice, selectedSkills: ['memory', 'web-search', 'scheduler'], skills: dataExport.SKILLS || [], agentMetas: [], apiKey: '', botToken: '' });
|
|
1837
1842
|
await fsp.writeFile(dockerEnvPath, envContent, 'utf8');
|
|
1838
1843
|
sendLog(`Docker env ready: ${dockerEnvPath}`);
|
|
1839
1844
|
await run('docker', ['compose', 'up', '-d', '--build'], { cwd: dockerDir });
|
|
@@ -2324,19 +2329,6 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
2324
2329
|
cfg.skills.entries['image-gen'] = cfg.skills.entries['image-gen'] || {};
|
|
2325
2330
|
cfg.skills.entries['image-gen'].enabled = !!enabled;
|
|
2326
2331
|
|
|
2327
|
-
for (const a of cfg.agents.list) {
|
|
2328
|
-
const sf = await readWorkspaceText(projectDir, a, 'skills/infographic-generator/SKILL.md');
|
|
2329
|
-
const js = await readWorkspaceText(projectDir, a, 'skills/infographic-generator/image-generator.js');
|
|
2330
|
-
if (enabled) {
|
|
2331
|
-
await fsp.mkdir(dirname(sf.file), { recursive: true });
|
|
2332
|
-
await fsp.writeFile(sf.file, buildInfographicGeneratorSkillMd(), 'utf8');
|
|
2333
|
-
await fsp.writeFile(js.file, buildInfographicGeneratorJs(), 'utf8');
|
|
2334
|
-
} else {
|
|
2335
|
-
if (existsSync(sf.file)) await fsp.rm(sf.file, { force: true });
|
|
2336
|
-
if (existsSync(js.file)) await fsp.rm(js.file, { force: true });
|
|
2337
|
-
}
|
|
2338
|
-
}
|
|
2339
|
-
|
|
2340
2332
|
// Write cfgPath early so recreation reads updated openclaw.json
|
|
2341
2333
|
await fsp.writeFile(cfgPath, JSON.stringify(cfg, null, 2), 'utf8');
|
|
2342
2334
|
|
|
@@ -2378,30 +2370,40 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
2378
2370
|
cfg.skills.entries['sticker-mention'].enabled = !!enabled;
|
|
2379
2371
|
|
|
2380
2372
|
for (const a of cfg.agents.list) {
|
|
2381
|
-
const
|
|
2382
|
-
const
|
|
2383
|
-
if (
|
|
2384
|
-
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
|
|
2373
|
+
const binding = (cfg.bindings || []).find((b) => b.agentId === a.id);
|
|
2374
|
+
const channel = binding?.match?.channel || 'telegram';
|
|
2375
|
+
if (channel === 'zalo-personal' || channel === 'zalouser') {
|
|
2376
|
+
try {
|
|
2377
|
+
const hasDocker = existsSync(join(projectDir, 'docker', 'openclaw', 'docker-compose.yml'));
|
|
2378
|
+
const workspaceDir = workspaceRelForAgent(a, cfg, projectDir) || `workspace-${a.id}`;
|
|
2379
|
+
let mentionsJsPath = join(projectDir, '.openclaw', workspaceDir, 'skills/zalo-sticker-mention/mentions.js');
|
|
2380
|
+
let containerJsPath = `/home/node/project/.openclaw/${workspaceDir}/skills/zalo-sticker-mention/mentions.js`;
|
|
2381
|
+
if (!existsSync(mentionsJsPath)) {
|
|
2382
|
+
mentionsJsPath = join(projectDir, '.openclaw', workspaceDir, 'skills/sticker-mention/mentions.js');
|
|
2383
|
+
containerJsPath = `/home/node/project/.openclaw/${workspaceDir}/skills/sticker-mention/mentions.js`;
|
|
2384
|
+
}
|
|
2385
|
+
if (existsSync(mentionsJsPath)) {
|
|
2386
|
+
if (enabled) {
|
|
2387
|
+
sendLog('[zalo-patch] Running patch on enable...');
|
|
2388
|
+
if (hasDocker) {
|
|
2389
|
+
const botContainer = getBotContainerName(projectDir);
|
|
2390
|
+
await runCapture('docker', ['exec', botContainer, 'node', containerJsPath], { cwd: projectDir, shell: false });
|
|
2391
|
+
} else {
|
|
2392
|
+
await run('node', [mentionsJsPath], { cwd: projectDir });
|
|
2393
|
+
}
|
|
2394
|
+
} else {
|
|
2396
2395
|
sendLog('[zalo-patch] Running restore before disabling skill...');
|
|
2397
|
-
|
|
2396
|
+
if (hasDocker) {
|
|
2397
|
+
const botContainer = getBotContainerName(projectDir);
|
|
2398
|
+
await runCapture('docker', ['exec', botContainer, 'node', containerJsPath, '--restore'], { cwd: projectDir, shell: false });
|
|
2399
|
+
} else {
|
|
2400
|
+
await run('node', [mentionsJsPath, '--restore'], { cwd: projectDir });
|
|
2401
|
+
}
|
|
2398
2402
|
}
|
|
2399
|
-
} catch (e) {
|
|
2400
|
-
sendLog(`[zalo-patch] Restore failed: ${e.message}`);
|
|
2401
2403
|
}
|
|
2404
|
+
} catch (e) {
|
|
2405
|
+
sendLog(`[zalo-patch] Zalo patch/restore action failed: ${e.message}`);
|
|
2402
2406
|
}
|
|
2403
|
-
if (existsSync(sf.file)) await fsp.rm(sf.file, { force: true });
|
|
2404
|
-
if (existsSync(jsf.file)) await fsp.rm(jsf.file, { force: true });
|
|
2405
2407
|
}
|
|
2406
2408
|
}
|
|
2407
2409
|
|
|
@@ -2461,6 +2463,64 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
2461
2463
|
}
|
|
2462
2464
|
|
|
2463
2465
|
async function installFeature(projectDir, agentId, kind, id) {
|
|
2466
|
+
if (kind === 'skill') {
|
|
2467
|
+
const skillSlugMap = {
|
|
2468
|
+
'image-gen': 'infographic-generator',
|
|
2469
|
+
'sticker-mention': 'zalo-sticker-mention',
|
|
2470
|
+
};
|
|
2471
|
+
const slug = skillSlugMap[id] || id;
|
|
2472
|
+
|
|
2473
|
+
let composeDir = null;
|
|
2474
|
+
if (existsSync(join(projectDir, 'docker-compose.yml'))) {
|
|
2475
|
+
composeDir = projectDir;
|
|
2476
|
+
} else if (existsSync(join(projectDir, 'docker', 'openclaw', 'docker-compose.yml'))) {
|
|
2477
|
+
composeDir = join(projectDir, 'docker', 'openclaw');
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
if (composeDir) {
|
|
2481
|
+
const botContainer = getBotContainerName(projectDir);
|
|
2482
|
+
sendLog(`[skill] Installing/updating clawhub:${slug} inside container ${botContainer} for agent ${agentId}...`);
|
|
2483
|
+
|
|
2484
|
+
const cmd = `cd /home/node/project && openclaw skills install ${slug} --agent ${agentId} --force`;
|
|
2485
|
+
const cmdOut = await runCapture('docker', ['exec', botContainer, 'sh', '-lc', cmd], { cwd: projectDir, shell: false });
|
|
2486
|
+
|
|
2487
|
+
if (cmdOut) {
|
|
2488
|
+
for (const line of `${cmdOut.stdout}\n${cmdOut.stderr}`.split(/\r?\n/).filter(Boolean)) sendLog(line);
|
|
2489
|
+
}
|
|
2490
|
+
|
|
2491
|
+
if (cmdOut.code !== 0) {
|
|
2492
|
+
const installed = isSkillFolderExists(projectDir, agentId, slug);
|
|
2493
|
+
if (installed) {
|
|
2494
|
+
sendLog(`[skill] Warning: installation reported errors, but skill folder exists. Proceeding.`);
|
|
2495
|
+
} else {
|
|
2496
|
+
throw new Error(cmdOut.stderr || cmdOut.stdout || `Failed to install skill ${slug} inside container.`);
|
|
2497
|
+
}
|
|
2498
|
+
}
|
|
2499
|
+
|
|
2500
|
+
sendLog(`[skill] Restarting docker container to apply skill...`);
|
|
2501
|
+
await run('docker', ['restart', botContainer], { shell: false });
|
|
2502
|
+
} else {
|
|
2503
|
+
await run('openclaw', ['doctor', '--fix'], { cwd: projectDir, env: openclawProjectEnv(projectDir) }).catch((err) => sendLog(`[skill] doctor --fix skipped: ${err.message}`));
|
|
2504
|
+
sendLog(`[skill] Installing clawhub:${slug} for agent ${agentId}...`);
|
|
2505
|
+
|
|
2506
|
+
await run('openclaw', ['skills', 'install', slug, '--agent', agentId, '--force'], {
|
|
2507
|
+
cwd: projectDir,
|
|
2508
|
+
env: openclawProjectEnv(projectDir)
|
|
2509
|
+
});
|
|
2510
|
+
}
|
|
2511
|
+
|
|
2512
|
+
// Automatically enable it in config after install
|
|
2513
|
+
const cfgPath = join(projectDir, '.openclaw', 'openclaw.json');
|
|
2514
|
+
if (existsSync(cfgPath)) {
|
|
2515
|
+
const cfg = ensureConfigShape(JSON.parse(await fsp.readFile(cfgPath, 'utf8')));
|
|
2516
|
+
cfg.skills = cfg.skills || { entries: {} };
|
|
2517
|
+
cfg.skills.entries = cfg.skills.entries || {};
|
|
2518
|
+
cfg.skills.entries[id] = cfg.skills.entries[id] || {};
|
|
2519
|
+
cfg.skills.entries[id].enabled = true;
|
|
2520
|
+
await fsp.writeFile(cfgPath, JSON.stringify(cfg, null, 2), 'utf8');
|
|
2521
|
+
}
|
|
2522
|
+
}
|
|
2523
|
+
|
|
2464
2524
|
if (kind === 'plugin') {
|
|
2465
2525
|
let composeDir = null;
|
|
2466
2526
|
if (existsSync(join(projectDir, 'docker-compose.yml'))) {
|
|
@@ -2624,6 +2684,48 @@ async function getInstalledPluginVersion(projectDir, aliases = []) {
|
|
|
2624
2684
|
return '';
|
|
2625
2685
|
}
|
|
2626
2686
|
|
|
2687
|
+
function isSkillFolderExists(projectDir, agentId, skillFolder, cfg = null) {
|
|
2688
|
+
if (!projectDir) return false;
|
|
2689
|
+
if (!cfg) {
|
|
2690
|
+
const cfgPath = join(projectDir, '.openclaw', 'openclaw.json');
|
|
2691
|
+
if (!existsSync(cfgPath)) return false;
|
|
2692
|
+
try {
|
|
2693
|
+
cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8'));
|
|
2694
|
+
} catch (e) {
|
|
2695
|
+
return false;
|
|
2696
|
+
}
|
|
2697
|
+
}
|
|
2698
|
+
const agent = cfg?.agents?.list?.find((a) => a.id === agentId) || cfg?.agents?.list?.[0];
|
|
2699
|
+
if (!agent) return false;
|
|
2700
|
+
const rel = workspaceRelForAgent(agent, cfg, projectDir);
|
|
2701
|
+
const skillPath = join(projectDir, '.openclaw', rel, 'skills', skillFolder);
|
|
2702
|
+
return existsSync(skillPath);
|
|
2703
|
+
}
|
|
2704
|
+
|
|
2705
|
+
async function getInstalledSkillVersion(projectDir, agentId, skillFolder, cfg = null) {
|
|
2706
|
+
if (!projectDir) return '';
|
|
2707
|
+
if (!cfg) {
|
|
2708
|
+
const cfgPath = join(projectDir, '.openclaw', 'openclaw.json');
|
|
2709
|
+
if (!existsSync(cfgPath)) return '';
|
|
2710
|
+
try {
|
|
2711
|
+
cfg = JSON.parse(await fsp.readFile(cfgPath, 'utf8'));
|
|
2712
|
+
} catch (e) {
|
|
2713
|
+
return '';
|
|
2714
|
+
}
|
|
2715
|
+
}
|
|
2716
|
+
const agent = cfg?.agents?.list?.find((a) => a.id === agentId) || cfg?.agents?.list?.[0];
|
|
2717
|
+
if (!agent) return '';
|
|
2718
|
+
const rel = workspaceRelForAgent(agent, cfg, projectDir);
|
|
2719
|
+
const pkgPath = join(projectDir, '.openclaw', rel, 'skills', skillFolder, 'package.json');
|
|
2720
|
+
try {
|
|
2721
|
+
if (existsSync(pkgPath)) {
|
|
2722
|
+
const pkg = JSON.parse(await fsp.readFile(pkgPath, 'utf8'));
|
|
2723
|
+
return pkg.version || '';
|
|
2724
|
+
}
|
|
2725
|
+
} catch (e) {}
|
|
2726
|
+
return '';
|
|
2727
|
+
}
|
|
2728
|
+
|
|
2627
2729
|
async function getFeatureFlags(projectDir, agentId = '') {
|
|
2628
2730
|
const cfgPath = join(projectDir || '', '.openclaw', 'openclaw.json');
|
|
2629
2731
|
const cfg = existsSync(cfgPath) ? ensureConfigShape(JSON.parse(await fsp.readFile(cfgPath, 'utf8').catch(() => '{}'))) : {};
|
|
@@ -2682,12 +2784,16 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
2682
2784
|
const isActuallyInstalled = (aliases = []) =>
|
|
2683
2785
|
extensionDirExists(aliases) || isInstalledByRecord(aliases);
|
|
2684
2786
|
const installed = {
|
|
2787
|
+
'skill:image-gen': isSkillFolderExists(projectDir, aid, 'infographic-generator', cfg),
|
|
2788
|
+
'skill:sticker-mention': isSkillFolderExists(projectDir, aid, 'zalo-sticker-mention', cfg),
|
|
2685
2789
|
'plugin:openclaw-browser-automation': isActuallyInstalled(aliases.browser),
|
|
2686
2790
|
'plugin:openclaw-zalo-mod': isActuallyInstalled(aliases.zalo),
|
|
2687
2791
|
'plugin:openclaw-facebook-crawler': isActuallyInstalled(aliases.crawler),
|
|
2688
2792
|
'plugin:openclaw-n8n-facebook-poster': isActuallyInstalled(aliases.poster),
|
|
2689
2793
|
};
|
|
2690
2794
|
const versions = {
|
|
2795
|
+
'skill:image-gen': await getInstalledSkillVersion(projectDir, aid, 'infographic-generator', cfg),
|
|
2796
|
+
'skill:sticker-mention': await getInstalledSkillVersion(projectDir, aid, 'zalo-sticker-mention', cfg),
|
|
2691
2797
|
'plugin:openclaw-browser-automation': await getInstalledPluginVersion(projectDir, aliases.browser),
|
|
2692
2798
|
'plugin:openclaw-zalo-mod': await getInstalledPluginVersion(projectDir, aliases.zalo),
|
|
2693
2799
|
'plugin:openclaw-facebook-crawler': await getInstalledPluginVersion(projectDir, aliases.crawler),
|
|
@@ -3050,13 +3156,11 @@ function restartInstaller() {
|
|
|
3050
3156
|
args.push('--no-open');
|
|
3051
3157
|
}
|
|
3052
3158
|
|
|
3053
|
-
const
|
|
3054
|
-
const rawBin = process.argv[0];
|
|
3055
|
-
const bin = shell && rawBin.includes(' ') && !rawBin.startsWith('"') ? `"${rawBin}"` : rawBin;
|
|
3159
|
+
const bin = process.argv[0];
|
|
3056
3160
|
const child = spawn(bin, [entryFile, ...args], {
|
|
3057
3161
|
detached: true,
|
|
3058
3162
|
stdio: 'inherit',
|
|
3059
|
-
shell
|
|
3163
|
+
shell: false
|
|
3060
3164
|
});
|
|
3061
3165
|
child.unref();
|
|
3062
3166
|
|
|
@@ -304,6 +304,13 @@ if(touched){console.log('[patch-9router] Applied Codex compatibility patch.');}e
|
|
|
304
304
|
' sed -i "s/LISTENER_WATCHDOG_MAX_GAP_MS\\\\s*=\\\\s*35e3/LISTENER_WATCHDOG_MAX_GAP_MS = 90e3/" "$ZALO_JS"',
|
|
305
305
|
' echo "[entrypoint] patched zalouser watchdog gap: 35s -> 90s"',
|
|
306
306
|
'fi',
|
|
307
|
+
'# Automatically run Zalo sticker-mention patch if skill is present',
|
|
308
|
+
'for MENTIONS_JS in $(find "$OPENCLAW_HOME" -maxdepth 5 -path "*/skills/*/mentions.js" -type f 2>/dev/null); do',
|
|
309
|
+
' if [ -f "$MENTIONS_JS" ]; then',
|
|
310
|
+
' echo "[entrypoint] Running patch: $MENTIONS_JS"',
|
|
311
|
+
' node "$MENTIONS_JS" || echo "[entrypoint] Warning: failed to run patch $MENTIONS_JS"',
|
|
312
|
+
' fi',
|
|
313
|
+
'done',
|
|
307
314
|
].join('\n'));
|
|
308
315
|
runtimeParts.push([
|
|
309
316
|
'# Zalo channel auto-restart monitor (background)',
|
|
@@ -531,8 +531,8 @@ for (let i = 0; i < 5; i++) {
|
|
|
531
531
|
}
|
|
532
532
|
|
|
533
533
|
// Resolve API Key and Base URL from openclaw.json
|
|
534
|
-
let apiKey = '
|
|
535
|
-
let baseUrl = 'http://9router:20128/v1'; // default fallback URL
|
|
534
|
+
let apiKey = process.env.NINE_ROUTER_API_KEY || ''; // default fallback key
|
|
535
|
+
let baseUrl = process.env.NINE_ROUTER_BASE_URL || 'http://9router:20128/v1'; // default fallback URL
|
|
536
536
|
if (openclawJsonPath) {
|
|
537
537
|
try {
|
|
538
538
|
const config = JSON.parse(fs.readFileSync(openclawJsonPath, 'utf8'));
|
package/dist/web/app.js
CHANGED
|
@@ -445,7 +445,8 @@ function wireSkillsHandlers(scope = document) {
|
|
|
445
445
|
await api('/api/features/install', { method:'POST', body:{ kind, id, agentId: currentBotId() } });
|
|
446
446
|
await loadFeatureFlags(true);
|
|
447
447
|
await loadFiles(true);
|
|
448
|
-
|
|
448
|
+
const label = kind === 'skill' ? t('kỹ năng: ', 'skill: ') : t('plugin: ', 'plugin: ');
|
|
449
|
+
showToast(t('Thành công', 'Success'), t('Cài đặt/Cập nhật thành công ', 'Successfully installed/updated ') + label + id, 'success');
|
|
449
450
|
} catch (err) {
|
|
450
451
|
showToast(t('Thất bại', 'Failed'), err.message, 'error');
|
|
451
452
|
} finally {
|
|
@@ -842,13 +843,13 @@ function botSkillsPanel() {
|
|
|
842
843
|
const row = (item, group) => {
|
|
843
844
|
const key = `${group}:${item.id}`;
|
|
844
845
|
const loading = !!state.featureLoading[key];
|
|
845
|
-
const
|
|
846
|
-
const isInstalled = !
|
|
846
|
+
const requiresInstall = group === 'plugin' || (group === 'skill' && (item.id === 'image-gen' || item.id === 'sticker-mention'));
|
|
847
|
+
const isInstalled = !requiresInstall || !!state.featureInstalled?.[key];
|
|
847
848
|
|
|
848
849
|
let actionsHtml = '';
|
|
849
850
|
if (isInstalled) {
|
|
850
851
|
actionsHtml = `<div style="display:flex; align-items:center; gap:8px;">`;
|
|
851
|
-
if (
|
|
852
|
+
if (requiresInstall) {
|
|
852
853
|
actionsHtml += `<button class="secondary icon-btn2 update-plugin-btn" type="button" data-feature-install="${key}" ${loading ? 'disabled' : ''} title="${t('Cập nhật lên bản mới nhất','Update to latest version')}" style="padding: 4px 8px; font-size: 11px; height: 28px; border-width: 1px; color:#ffb020; border-color: rgba(255,176,32,0.25); background: rgba(255,176,32,0.05);">${actionIcon('refresh')}<span>${t('Cập nhật','Update')}</span></button>`;
|
|
853
854
|
}
|
|
854
855
|
actionsHtml += `<label class="feature-switch"><input type="checkbox" data-feature-toggle="${key}" ${flags[key] ? 'checked' : ''} ${loading ? 'disabled' : ''}/><span></span></label></div>`;
|
|
@@ -856,7 +857,7 @@ function botSkillsPanel() {
|
|
|
856
857
|
actionsHtml = `<button class="secondary icon-btn2" type="button" data-feature-install="${key}" ${loading ? 'disabled' : ''}>${actionIcon('download')} ${ui('installVerb')}</button>`;
|
|
857
858
|
}
|
|
858
859
|
|
|
859
|
-
const version =
|
|
860
|
+
const version = requiresInstall && isInstalled ? (state.featureVersions?.[key] || '') : '';
|
|
860
861
|
const versionBadge = version ? `<span class="plugin-version-badge" style="display:inline-block; font-size: 11px; background: rgba(66, 133, 244, 0.15); color: #4285F4; padding: 2px 6px; border-radius: 4px; font-weight: 600; margin-left: 8px; border: 1px solid rgba(66,133,244,0.25);">v${escapeHtml(version)}</span>` : '';
|
|
861
862
|
|
|
862
863
|
return `<article class="card feature-card ${loading ? 'is-loading' : ''}"><div class="feature-head"><div><b>${escapeHtml(item.title)}${versionBadge}</b><p>${escapeHtml(item.desc)}</p></div>` +
|