create-openclaw-bot 5.8.3 β 5.8.4
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 +2 -2
- package/README.vi.md +2 -2
- package/dist/cli.js +11 -15
- package/dist/server/local-server.js +150 -114
- package/dist/setup/data/header.js +1 -1
- package/dist/setup/data/skills.js +2 -4
- package/dist/setup/shared/bot-config-gen.js +21 -16
- package/dist/setup/shared/workspace-gen.js +307 -485
- package/dist/web/app.js +102 -31
- package/dist/web/styles.css +477 -10
- package/node_modules/color-convert/CHANGELOG.md +54 -0
- package/package.json +1 -1
- package/dist/legacy-cli.js +0 -2812
- package/dist/setup.js +0 -6941
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.4-0EA5E9?style=for-the-badge" alt="Version 5.8.4" /></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.4
|
|
27
27
|
|
|
28
28
|
- π **Smart Header Update Button**: Instantly upgrades the setup wizard from the UI! The button queries the npm registry dynamically and only reveals itself when a newer release is published.
|
|
29
29
|
- π‘ **Live Log-Streaming Upgrade**: Kicking off the update automatically executes the migration (running `git pull && npm install && npm run build` for Git clones, or `npm install -g create-openclaw-bot` for NPM installs) while streaming standard outputs in real-time straight to the dashboard's Logs terminal.
|
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.4-0EA5E9?style=for-the-badge" alt="Version 5.8.4" /></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.4
|
|
27
27
|
|
|
28
28
|
- π **NΓΊt cαΊp nhαΊt Header thΓ΄ng minh**: NΓ’ng cαΊ₯p trα»±c tiαΊΏp setup wizard tα»« giao diα»n! NΓΊt cαΊp nhαΊt tα»± truy vαΊ₯n npm registry vΓ chα» hiα»n thα» khi cΓ³ phiΓͺn bαΊ£n mα»i hΖ‘n.
|
|
29
29
|
- π‘ **NΓ’ng cαΊ₯p Stream Log trα»±c tiαΊΏp**: Khα»i chαΊ‘y cαΊp nhαΊt sαΊ½ tα»± Δα»ng nΓ’ng cαΊ₯p (chαΊ‘y `git pull && npm install && npm run build` cho bαΊ£n Git, hoαΊ·c `npm install -g create-openclaw-bot` cho bαΊ£n NPM) vΓ ΔαΊ©y dΓ²ng log theo thα»i gian thα»±c vα» tab Logs.
|
package/dist/cli.js
CHANGED
|
@@ -32,21 +32,17 @@ const args = process.argv.slice(2);
|
|
|
32
32
|
if (isLocalRepo()) {
|
|
33
33
|
const { startLocalInstaller } = await import('./server/local-server.js');
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
preferredPort: portArg ? Number(portArg.slice('--port='.length)) : 51789,
|
|
47
|
-
projectDir: projectDirArg ? projectDirArg.slice('--project-dir='.length) : process.cwd(),
|
|
48
|
-
});
|
|
49
|
-
}
|
|
35
|
+
const noOpen = args.includes('--no-open');
|
|
36
|
+
const hostArg = args.find((arg) => arg.startsWith('--host='));
|
|
37
|
+
const portArg = args.find((arg) => arg.startsWith('--port='));
|
|
38
|
+
const projectDirArg = args.find((arg) => arg.startsWith('--project-dir='));
|
|
39
|
+
|
|
40
|
+
await startLocalInstaller({
|
|
41
|
+
openBrowser: !noOpen,
|
|
42
|
+
host: hostArg ? hostArg.slice('--host='.length) : '127.0.0.1',
|
|
43
|
+
preferredPort: portArg ? Number(portArg.slice('--port='.length)) : 51789,
|
|
44
|
+
projectDir: projectDirArg ? projectDirArg.slice('--project-dir='.length) : process.cwd(),
|
|
45
|
+
});
|
|
50
46
|
} else {
|
|
51
47
|
console.log('\n============================================================');
|
|
52
48
|
console.log(' π¦ OpenClaw Setup β Auto-downloader & Installer');
|
|
@@ -13,7 +13,7 @@ function loadSharedModule(modulePath, globalName) {
|
|
|
13
13
|
if (loaded && Object.keys(loaded).length > 0) return loaded;
|
|
14
14
|
return globalThis[globalName] || loaded || {};
|
|
15
15
|
}
|
|
16
|
-
const { buildWorkspaceFileMap } = loadSharedModule('../setup/shared/workspace-gen.js', '__openclawWorkspace');
|
|
16
|
+
const { buildWorkspaceFileMap, buildCronjobSkillMd, buildInfographicGeneratorSkillMd, buildInfographicGeneratorJs } = loadSharedModule('../setup/shared/workspace-gen.js', '__openclawWorkspace');
|
|
17
17
|
const { buildOpenclawJson, buildEnvFileContent, buildExecApprovalsJson } = loadSharedModule('../setup/shared/bot-config-gen.js', '__openclawBotConfig');
|
|
18
18
|
const { buildDockerArtifacts } = loadSharedModule('../setup/shared/docker-gen.js', '__openclawDockerGen');
|
|
19
19
|
const { OPENCLAW_NPM_SPEC, NINE_ROUTER_NPM_SPEC, build9RouterProviderConfig, get9RouterBaseUrl } = loadSharedModule('../setup/shared/common-gen.js', '__openclawCommon');
|
|
@@ -27,6 +27,7 @@ const STATE_FILE = '.openclaw-setup-state.json';
|
|
|
27
27
|
const DEFAULT_MODEL = 'smart-route';
|
|
28
28
|
const logClients = new Set();
|
|
29
29
|
let zaloLoginInFlight = false;
|
|
30
|
+
let activeServerInstance = null;
|
|
30
31
|
const state = {
|
|
31
32
|
installing: false,
|
|
32
33
|
installed: false,
|
|
@@ -901,7 +902,32 @@ async function buildBotStatus() {
|
|
|
901
902
|
resolveProjectRuntimeVersions(state.projectDir, state.mode).catch(() => ({ openclaw: '', nineRouter: '', node: process.version || '' })),
|
|
902
903
|
]);
|
|
903
904
|
const credentials = await readBotCredentials(state.projectDir).catch(() => ({ openclawToken: '', nineRouterApiKey: '' }));
|
|
904
|
-
|
|
905
|
+
|
|
906
|
+
let activeModel = 'smart-route';
|
|
907
|
+
let activeProvider = '9Router';
|
|
908
|
+
if (state.projectDir) {
|
|
909
|
+
const cfgPath = join(state.projectDir, '.openclaw', 'openclaw.json');
|
|
910
|
+
if (existsSync(cfgPath)) {
|
|
911
|
+
try {
|
|
912
|
+
const raw = await fsp.readFile(cfgPath, 'utf8');
|
|
913
|
+
const cfg = JSON.parse(raw);
|
|
914
|
+
const modelStr = cfg.agents?.defaults?.model?.primary || cfg.agents?.list?.[0]?.model?.primary || 'smart-route';
|
|
915
|
+
if (modelStr.includes('/')) {
|
|
916
|
+
const parts = modelStr.split('/');
|
|
917
|
+
activeProvider = parts[0];
|
|
918
|
+
activeModel = parts.slice(1).join('/');
|
|
919
|
+
} else {
|
|
920
|
+
activeModel = modelStr;
|
|
921
|
+
activeProvider = cfg.models?.providers?.openai ? 'openai' : '9router';
|
|
922
|
+
}
|
|
923
|
+
} catch (e) {}
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
|
|
927
|
+
const cap = (s) => String(s).toLowerCase() === 'openai' ? 'OpenAI' : String(s).toLowerCase() === '9router' ? '9Router' : s;
|
|
928
|
+
activeProvider = cap(activeProvider);
|
|
929
|
+
|
|
930
|
+
return { ...state, gatewayStatus, routerStatus, bots, credentials, runtimeVersions, activeModel, activeProvider };
|
|
905
931
|
}
|
|
906
932
|
|
|
907
933
|
async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
@@ -933,10 +959,16 @@ async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
|
933
959
|
agentMetas: [],
|
|
934
960
|
}));
|
|
935
961
|
|
|
936
|
-
const existingAgentCount = cfg.agents.list.length;
|
|
937
962
|
const used = new Set(cfg.agents.list.map((a) => a.id));
|
|
938
963
|
const botName = uniqueDisplayName(requestedBotName, new Set(cfg.agents.list.map((a) => a.name || a.id)));
|
|
939
|
-
|
|
964
|
+
let agentId = body.agentId ? String(body.agentId).trim().toLowerCase().replace(/[^a-z0-9-_]+/g, '-') : '';
|
|
965
|
+
if (!agentId) {
|
|
966
|
+
agentId = uniqueSlug(slugify(botName), used);
|
|
967
|
+
} else {
|
|
968
|
+
if (used.has(agentId)) {
|
|
969
|
+
throw httpError(400, `Bot ID "${agentId}" ΔΓ£ tα»n tαΊ‘i. Vui lΓ²ng chα»n ID khΓ‘c.`);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
940
972
|
const workspaceDir = `workspace-${agentId}`;
|
|
941
973
|
const model = cfg.agents.defaults?.model?.primary || cfg.agents.list[0]?.model?.primary || DEFAULT_MODEL;
|
|
942
974
|
cfg.agents.list.push({
|
|
@@ -980,6 +1012,7 @@ async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
|
980
1012
|
await fsp.writeFile(cfgPath, JSON.stringify(cfg, null, 2), 'utf8');
|
|
981
1013
|
|
|
982
1014
|
const hasScheduler = !!(cfg.tools?.alsoAllow || []).includes('group:automation');
|
|
1015
|
+
const hasImageGen = !!(cfg.skills?.entries?.['image-gen']?.enabled);
|
|
983
1016
|
const files = buildWorkspaceFileMap({
|
|
984
1017
|
isVi: true,
|
|
985
1018
|
botName,
|
|
@@ -991,6 +1024,7 @@ async function createBotInProject(projectDir, body = {}, runtime = {}) {
|
|
|
991
1024
|
workspacePath: `.openclaw/${workspaceDir}`,
|
|
992
1025
|
hasZaloMod: channel === 'zalo-personal',
|
|
993
1026
|
hasScheduler,
|
|
1027
|
+
hasImageGen,
|
|
994
1028
|
});
|
|
995
1029
|
const wsRoot = join(openclawHome, workspaceDir);
|
|
996
1030
|
for (const [name, content] of Object.entries(files)) {
|
|
@@ -1080,6 +1114,7 @@ async function updateBotInProject(projectDir, agentId, body = {}, runtime = {})
|
|
|
1080
1114
|
}
|
|
1081
1115
|
|
|
1082
1116
|
const hasScheduler = !!(cfg.tools?.alsoAllow || []).includes('group:automation');
|
|
1117
|
+
const hasImageGen = !!(cfg.skills?.entries?.['image-gen']?.enabled);
|
|
1083
1118
|
const files = buildWorkspaceFileMap({
|
|
1084
1119
|
isVi: true,
|
|
1085
1120
|
botName,
|
|
@@ -1091,6 +1126,7 @@ async function updateBotInProject(projectDir, agentId, body = {}, runtime = {})
|
|
|
1091
1126
|
workspacePath: `.openclaw/${workspaceDir}`,
|
|
1092
1127
|
hasZaloMod: channel === 'zalo-personal',
|
|
1093
1128
|
hasScheduler,
|
|
1129
|
+
hasImageGen,
|
|
1094
1130
|
});
|
|
1095
1131
|
const wsRoot = join(projectDir, '.openclaw', workspaceDir);
|
|
1096
1132
|
for (const [name, content] of Object.entries(files)) {
|
|
@@ -1531,10 +1567,9 @@ async function writeCoreProject({ projectDir, osChoice, mode, gatewayPort = 1878
|
|
|
1531
1567
|
await fsp.mkdir(openclawHome, { recursive: true });
|
|
1532
1568
|
await fsp.mkdir(join(openclawHome, 'plugin-runtime-deps'), { recursive: true });
|
|
1533
1569
|
|
|
1534
|
-
const selectedSkills = [];
|
|
1535
|
-
const
|
|
1536
|
-
const
|
|
1537
|
-
const common = { botName, channelKey: 'telegram', providerKey: '9router', model: DEFAULT_MODEL, deployMode: mode, osChoice, selectedSkills, skills: dataExport.SKILLS || [], agentMetas, gatewayPort, routerPort };
|
|
1570
|
+
const selectedSkills = ['memory', 'image-gen', 'web-search'];
|
|
1571
|
+
const agentMetas = [];
|
|
1572
|
+
const common = { channelKey: 'telegram', providerKey: '9router', model: DEFAULT_MODEL, deployMode: mode, osChoice, selectedSkills, skills: dataExport.SKILLS || [], agentMetas, gatewayPort, routerPort };
|
|
1538
1573
|
const cfg = buildOpenclawJson(common);
|
|
1539
1574
|
const env = buildEnvFileContent({ ...common, apiKey: '', botToken: '' });
|
|
1540
1575
|
const approvals = buildExecApprovalsJson({ agentMetas });
|
|
@@ -1543,26 +1578,6 @@ async function writeCoreProject({ projectDir, osChoice, mode, gatewayPort = 1878
|
|
|
1543
1578
|
await fsp.writeFile(join(projectDir, '.env'), env, 'utf8');
|
|
1544
1579
|
await fsp.writeFile(join(openclawHome, 'exec-approvals.json'), JSON.stringify(approvals, null, 2), 'utf8');
|
|
1545
1580
|
|
|
1546
|
-
const workspaceDir = 'workspace-bot';
|
|
1547
|
-
const workspace = buildWorkspaceFileMap({
|
|
1548
|
-
isVi: true,
|
|
1549
|
-
botName,
|
|
1550
|
-
channelKey: 'telegram',
|
|
1551
|
-
providerKey: '9router',
|
|
1552
|
-
selectedSkills,
|
|
1553
|
-
skillsCatalog: dataExport.SKILLS || [],
|
|
1554
|
-
agentMetas,
|
|
1555
|
-
deployMode: mode,
|
|
1556
|
-
osChoice,
|
|
1557
|
-
agentWorkspaceDir: workspaceDir,
|
|
1558
|
-
workspacePath: `.openclaw/${workspaceDir}`,
|
|
1559
|
-
});
|
|
1560
|
-
const wsRoot = join(openclawHome, workspaceDir);
|
|
1561
|
-
for (const [name, content] of Object.entries(workspace)) {
|
|
1562
|
-
await fsp.mkdir(dirname(join(wsRoot, name)), { recursive: true });
|
|
1563
|
-
await fsp.writeFile(join(wsRoot, name), content || '', 'utf8');
|
|
1564
|
-
}
|
|
1565
|
-
|
|
1566
1581
|
if (mode === 'docker') {
|
|
1567
1582
|
const projectName = slugify(basename(projectDir)) || 'bot';
|
|
1568
1583
|
const docker = buildDockerArtifacts({
|
|
@@ -1616,7 +1631,7 @@ async function installCore({ osChoice, mode, projectDir, gatewayPort = 18789, ro
|
|
|
1616
1631
|
await fsp.mkdir(dockerDir, { recursive: true });
|
|
1617
1632
|
const envContent = existsSync(rootEnvPath)
|
|
1618
1633
|
? await fsp.readFile(rootEnvPath, 'utf8')
|
|
1619
|
-
: buildEnvFileContent({
|
|
1634
|
+
: buildEnvFileContent({ channelKey: 'telegram', providerKey: '9router', deployMode: mode, osChoice, selectedSkills: [], skills: dataExport.SKILLS || [], agentMetas: [], apiKey: '', botToken: '' });
|
|
1620
1635
|
await fsp.writeFile(dockerEnvPath, envContent, 'utf8');
|
|
1621
1636
|
sendLog(`Docker env ready: ${dockerEnvPath}`);
|
|
1622
1637
|
await run('docker', ['compose', 'up', '-d', '--build'], { cwd: dockerDir });
|
|
@@ -1971,72 +1986,23 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
1971
1986
|
const k = `${kind}:${id}`;
|
|
1972
1987
|
|
|
1973
1988
|
if (kind === 'skill' && id === 'browser') {
|
|
1989
|
+
delete cfg.browser;
|
|
1990
|
+
cfg.plugins = cfg.plugins || { entries: {} };
|
|
1991
|
+
cfg.plugins.entries = cfg.plugins.entries || {};
|
|
1992
|
+
const aliases = ['browser-automation', 'openclaw-browser-automation'];
|
|
1993
|
+
const existingKey = aliases.find((a) => cfg.plugins.entries[a]) || aliases[0];
|
|
1994
|
+
cfg.plugins.entries[existingKey] = cfg.plugins.entries[existingKey] || {};
|
|
1995
|
+
cfg.plugins.entries[existingKey].enabled = !!enabled;
|
|
1996
|
+
cfg.plugins.allow = cfg.plugins.allow || [];
|
|
1974
1997
|
if (enabled) {
|
|
1975
|
-
cfg.
|
|
1976
|
-
enabled: true,
|
|
1977
|
-
defaultProfile: 'host-chrome',
|
|
1978
|
-
profiles: { 'host-chrome': { cdpUrl: 'http://127.0.0.1:9222', color: '#4285F4' } },
|
|
1979
|
-
};
|
|
1980
|
-
const isHeadlessServer = process.platform === 'linux';
|
|
1981
|
-
const docVariant = 'cli-server';
|
|
1982
|
-
|
|
1983
|
-
for (const a of cfg.agents.list) {
|
|
1984
|
-
const wm = buildWorkspaceFileMap({
|
|
1985
|
-
isVi: true,
|
|
1986
|
-
botName: a.name || a.id,
|
|
1987
|
-
botDesc: '',
|
|
1988
|
-
hasBrowser: false,
|
|
1989
|
-
hasScheduler: true,
|
|
1990
|
-
workspacePath: `.openclaw/${workspaceRelForAgent(a, cfg, projectDir)}/`,
|
|
1991
|
-
agentWorkspaceDir: workspaceRelForAgent(a, cfg, projectDir),
|
|
1992
|
-
variant: cfg.agents.list.length > 1 ? 'relay' : 'single',
|
|
1993
|
-
browserDocVariant: docVariant,
|
|
1994
|
-
});
|
|
1995
|
-
const browserDoc = wm['BROWSER.md'] || '# BROWSER';
|
|
1996
|
-
const browserTool = wm['browser-tool.js'] || '';
|
|
1997
|
-
const bf = await readWorkspaceText(projectDir, a, 'BROWSER.md');
|
|
1998
|
-
await fsp.writeFile(bf.file, browserDoc, 'utf8');
|
|
1999
|
-
const bt = await readWorkspaceText(projectDir, a, 'browser-tool.js');
|
|
2000
|
-
if (browserTool) await fsp.writeFile(bt.file, browserTool, 'utf8');
|
|
2001
|
-
|
|
2002
|
-
const af = await readWorkspaceText(projectDir, a, 'AGENTS.md');
|
|
2003
|
-
const agentsManaged = upsertManagedBlock(af.content, 'BROWSER_LINK', '- Browser docs: `BROWSER.md`');
|
|
2004
|
-
await fsp.writeFile(af.file, agentsManaged, 'utf8');
|
|
2005
|
-
|
|
2006
|
-
// Add to TOOLS.md
|
|
2007
|
-
const tf = await readWorkspaceText(projectDir, a, 'TOOLS.md');
|
|
2008
|
-
const browserGuide = isHeadlessServer
|
|
2009
|
-
? `## π Browser Automation
|
|
2010
|
-
- Xem hΖ°α»ng dαΊ«n chi tiαΊΏt tαΊ‘i **BROWSER.md**
|
|
2011
|
-
- Script Δiα»u khiα»n: \`browser-tool.js\`
|
|
2012
|
-
- ChαΊΏ Δα» hiα»n tαΊ‘i: ChαΊ‘y ngαΊ§m Δα»c lαΊp qua Docker hoαΊ·c Xvfb trΓͺn VPS.
|
|
2013
|
-
- **TΓ¬m kiαΊΏm Web:** NαΊΏu khΓ΄ng cΓ³ cΓ΄ng cα»₯ Web Search (hoαΊ·c Web Search khΓ΄ng khαΊ£ dα»₯ng/bα» lα»i), hΓ£y **luΓ΄n sα» dα»₯ng ngay cΓ΄ng cα»₯ terminal (exec/run_command) Δα» chαΊ‘y lα»nh: \`node search-tool.js "<tα»« khΓ³a>" 5\`**! Lα»nh nΓ y sαΊ½ tα»± Δα»ng chαΊ‘y ngαΊ§m qua DuckDuckGo/Google/Bing bαΊ±ng trΓ¬nh duyα»t ngαΊ§m tΓ ng hΓ¬nh cα»§a bαΊ‘n vΓ trαΊ£ vα» kαΊΏt quαΊ£ JSON sαΊ‘ch ngay lαΊp tα»©c. Tuyα»t Δα»i KHΓNG Δược mα» trΓ¬nh duyα»t thα»§ cΓ΄ng, chα»₯p αΊ£nh mΓ n hΓ¬nh hay click tΓ¬m kiαΊΏm bαΊ±ng tay tα»«ng bΖ°α»c!
|
|
2014
|
-
- NαΊΏu browser lα»i, thα» lαΊ‘i 1 lαΊ§n rα»i mα»i bΓ‘o user vα»i lα»i cα»₯ thα»`
|
|
2015
|
-
: `## π Browser Automation
|
|
2016
|
-
- Xem hΖ°α»ng dαΊ«n chi tiαΊΏt tαΊ‘i **BROWSER.md**
|
|
2017
|
-
- Script Δiα»u khiα»n: \`browser-tool.js\`
|
|
2018
|
-
- ChαΊΏ Δα» hiα»n tαΊ‘i:
|
|
2019
|
-
- **MαΊ·c Δα»nh:** ChαΊ‘y ngαΊ§m Δα»c lαΊp qua Docker hoαΊ·c Server.
|
|
2020
|
-
- **ChαΊΏ Δα» xem Chrome thαΊt:** ChαΊ‘y file \`start-chrome-debug.bat\` / \`start-chrome-debug.sh\` trΓͺn host trΖ°α»c Δα» bot kαΊΏt nα»i Δiα»u khiα»n trα»±c quan.
|
|
2021
|
-
- KαΊΏt nα»i mαΊ·c Δα»nh: \`http://127.0.0.1:9222\`
|
|
2022
|
-
- **TΓ¬m kiαΊΏm Web:** NαΊΏu khΓ΄ng cΓ³ cΓ΄ng cα»₯ Web Search (hoαΊ·c Web Search khΓ΄ng khαΊ£ dα»₯ng/bα» lα»i), hΓ£y **luΓ΄n sα» dα»₯ng ngay cΓ΄ng cα»₯ terminal (exec/run_command) Δα» chαΊ‘y lα»nh: \`node search-tool.js "<tα»« khΓ³a>" 5\`**! Lα»nh nΓ y sαΊ½ tα»± Δα»ng chαΊ‘y ngαΊ§m qua DuckDuckGo/Google/Bing bαΊ±ng trΓ¬nh duyα»t ngαΊ§m tΓ ng hΓ¬nh cα»§a bαΊ‘n vΓ trαΊ£ vα» kαΊΏt quαΊ£ JSON sαΊ‘ch ngay lαΊp tα»©c. Tuyα»t Δα»i KHΓNG Δược mα» trΓ¬nh duyα»t thα»§ cΓ΄ng, chα»₯p αΊ£nh mΓ n hΓ¬nh hay click tΓ¬m kiαΊΏm bαΊ±ng tay tα»«ng bΖ°α»c!
|
|
2023
|
-
- NαΊΏu browser lα»i, thα» lαΊ‘i 1 lαΊ§n rα»i mα»i bΓ‘o user vα»i lα»i cα»₯ thα»`;
|
|
2024
|
-
await fsp.writeFile(tf.file, upsertManagedBlock(tf.content, 'BROWSER_GUIDE', browserGuide), 'utf8');
|
|
2025
|
-
}
|
|
1998
|
+
if (!cfg.plugins.allow.includes(existingKey)) cfg.plugins.allow.push(existingKey);
|
|
2026
1999
|
} else {
|
|
2027
|
-
|
|
2000
|
+
cfg.plugins.allow = cfg.plugins.allow.filter((x) => x !== existingKey);
|
|
2028
2001
|
for (const a of cfg.agents.list) {
|
|
2029
2002
|
const bf = await readWorkspaceText(projectDir, a, 'BROWSER.md');
|
|
2030
2003
|
if (existsSync(bf.file)) await fsp.rm(bf.file, { force: true });
|
|
2031
2004
|
const bt = await readWorkspaceText(projectDir, a, 'browser-tool.js');
|
|
2032
2005
|
if (existsSync(bt.file)) await fsp.rm(bt.file, { force: true });
|
|
2033
|
-
|
|
2034
|
-
const af = await readWorkspaceText(projectDir, a, 'AGENTS.md');
|
|
2035
|
-
await fsp.writeFile(af.file, removeManagedBlock(af.content, 'BROWSER_LINK'), 'utf8');
|
|
2036
|
-
|
|
2037
|
-
// Remove from TOOLS.md
|
|
2038
|
-
const tf = await readWorkspaceText(projectDir, a, 'TOOLS.md');
|
|
2039
|
-
await fsp.writeFile(tf.file, removeManagedBlock(tf.content, 'BROWSER_GUIDE'), 'utf8');
|
|
2040
2006
|
}
|
|
2041
2007
|
}
|
|
2042
2008
|
|
|
@@ -2059,37 +2025,17 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
2059
2025
|
cfg.tools.alsoAllow = Array.from(new Set([...(cfg.tools.alsoAllow || []), 'group:automation']));
|
|
2060
2026
|
cfg.commands = cfg.commands || {};
|
|
2061
2027
|
cfg.commands.ownerAllowFrom = Array.from(new Set([...(cfg.commands.ownerAllowFrom || []), '*']));
|
|
2062
|
-
const cronGuide = `## β° Cron / LΓͺn lα»ch nhαΊ―c nhα» (tool: \`cron\`)
|
|
2063
|
-
- **TΓͺn tool chΓnh xΓ‘c:** TΓͺn cΓ΄ng cα»₯ lΓ \`cron\` (tuyα»t Δα»i khΓ΄ng nhαΊ§m lΓ \`native\` hay command line bΓͺn ngoΓ i).
|
|
2064
|
-
- **β TUYα»T Δα»I KHΓNG sα»a trα»±c tiαΊΏp file JSON** nhΖ° \`jobs.json\`, \`jobs-state.json\` trong thΖ° mα»₯c \`.openclaw/cron/\`. Dα»― liα»u cron Δược lΖ°u trong SQLite database, file JSON chα» lΓ legacy format ΔΓ£ ngΖ°ng hα» trợ. Mα»i thao tΓ‘c PHαΊ’I thΓ΄ng qua tool \`cron\`.
|
|
2065
|
-
- **Khi tαΊ‘o cronjob mα»i (action \`add\`):**
|
|
2066
|
-
- **TUYα»T Δα»I KHΓNG Δiα»n trΖ°α»ng \`agentId\`** trong object \`job\` (hΓ£y bα» qua/omitted trΖ°α»ng nΓ y). Hα» thα»ng OpenClaw sαΊ½ tα»± Δα»ng gΓ‘n chΓnh xΓ‘c ID cα»§a bαΊ‘n vΓ o job ΔΓ³.
|
|
2067
|
-
- Tuyα»t Δα»i **khΓ΄ng tα»± Δiα»n** \`agentId\` lΓ \`"bot"\` hay \`"main"\`, vΓ¬ lΓ m vαΊy sαΊ½ khiαΊΏn cronjob thuα»c vα» agent khΓ‘c vΓ bαΊ‘n sαΊ½ mαΊ₯t quyα»n kiα»m soΓ‘t/xΓ³a nΓ³ sau nΓ y.
|
|
2068
|
-
- **Session:** LuΓ΄n dΓΉng \`sessionTarget: "isolated"\` cho cΓ‘c job chαΊ‘y nα»n (bΓ‘o cΓ‘o, nhαΊ―c nhα», gα»i tin nhαΊ―n tα»± Δα»ng). Chα» dΓΉng \`"main"\` cho system event/reminder ngαΊ―n.
|
|
2069
|
-
- **Timezone:** LuΓ΄n chα» Δα»nh timezone rΓ΅ rΓ ng bαΊ±ng trΖ°α»ng \`tz\` (vΓ dα»₯: \`"Asia/Ho_Chi_Minh"\`). NαΊΏu khΓ΄ng chα» Δα»nh, hα» thα»ng sαΊ½ dΓΉng timezone cα»§a Gateway host (thΖ°α»ng lΓ UTC) vΓ job sαΊ½ chαΊ‘y sai giα».
|
|
2070
|
-
- **Delivery:** Δα»i vα»i job cαΊ§n gα»i kαΊΏt quαΊ£ ra chat, set \`delivery.mode: "announce"\` kΓ¨m \`delivery.channel\` vΓ \`delivery.to\`.
|
|
2071
|
-
- **Khi user yΓͺu cαΊ§u tαΊ―t/bαΊt/xΓ³a cronjob:**
|
|
2072
|
-
1. **BΖ°α»c 1 (TΓ¬m kiαΊΏm):** Gα»i tool \`cron\` vα»i action \`list\` (vΓ \`includeDisabled: true\`) Δα» xem danh sΓ‘ch tαΊ₯t cαΊ£ cronjob Δang chαΊ‘y trΓͺn hα» thα»ng vΓ tΓ¬m ΔΓΊng \`jobId\` phΓΉ hợp vα»i yΓͺu cαΊ§u.
|
|
2073
|
-
2. **BΖ°α»c 2 (Xα» lΓ½):**
|
|
2074
|
-
- Δα» xΓ³a: Gα»i action \`remove\` vα»i \`id\` tΓ¬m Δược.
|
|
2075
|
-
- Δα» tαΊ―t/tαΊ‘m dα»«ng: Gα»i action \`update\` vα»i \`id\` vΓ patch \`{"enabled": false}\`.
|
|
2076
|
-
- Δα» bαΊt lαΊ‘i: Gα»i action \`update\` vα»i \`id\` vΓ patch \`{"enabled": true}\`.
|
|
2077
|
-
3. **TuyΓͺn bα» trung thα»±c:** Tuyα»t Δα»i khΓ΄ng bao giα» trαΊ£ lα»i "ΔΓ£ xΓ³a" hay "khΓ΄ng cΓ³" dα»±a trΓͺn suy ΔoΓ‘n cα»§a bαΊ£n thΓ’n mΓ chΖ°a gα»i tool \`cron\` Δα» kiα»m tra thα»±c tαΊΏ.
|
|
2078
|
-
- Khi user yΓͺu cαΊ§u tαΊ‘o nhαΊ―c nhα» / lα»nh tα»± Δα»ng Δα»nh kα»³, bαΊ‘n hΓ£y Tα»° Δα»NG dΓΉng tool \`cron\` (action \`add\`) Δα» tαΊ‘o. **Tuyα»t Δα»i khΓ΄ng** bαΊ―t user dΓΉng crontab hay Task Scheduler chαΊ‘y tay trΓͺn host.
|
|
2079
|
-
- Khi thao tΓ‘c tool cho cron/scheduler, **khΓ΄ng Δiα»n \`current\` vΓ o thΖ° mα»₯c Session**.
|
|
2080
|
-
- **QUAN TRα»NG Vα» TARGETING GROUP CHAT**: Khi tαΊ‘o hoαΊ·c cαΊ₯u hΓ¬nh cron job gα»i tin nhαΊ―n thΓ΄ng bΓ‘o (announce mode) ΔαΊΏn mα»t Group Chat, giΓ‘ trα» cα»§a trΖ°α»ng \`delivery.to\` **bαΊ―t buα»c** phαΊ£i sα» dα»₯ng tiα»n tα» thΓch hợp trΖ°α»c ID cα»§a group. Vα»i kΓͺnh Telegram/Matrix/Discord/Slack, dΓΉng tiα»n tα» \`group:\` (vΓ dα»₯: \`group:123456\`). RIΓNG vα»i kΓͺnh Zalo (\`zalouser\`), **bαΊ―t buα»c** phαΊ£i sα» dα»₯ng tiα»n tα» \`g:\` (vΓ dα»₯: \`g:3815464776067464419\`) Δα» trΓ‘nh bα» OpenClaw core lược bα» tiα»n tα» vΓ gα»i nhαΊ§m vΓ o DM chat cΓ‘ nhΓ’n.
|
|
2081
|
-
- **One-shot job:** DΓΉng schedule kind \`"at"\` vα»i ISO 8601 timestamp. Job sαΊ½ tα»± xΓ³a sau khi chαΊ‘y thΓ nh cΓ΄ng trα»« khi set \`deleteAfterRun: false\`.
|
|
2082
|
-
- Bα» qua viα»c tra cα»©u docs nα»i bα» nhΖ° \`cron-jobs.mdx\`; tin tΖ°α»ng khαΊ£ nΔng dΓΉng tool hiα»n cΓ³ Δα» hoΓ n thΓ nh yΓͺu cαΊ§u.`;
|
|
2083
2028
|
for (const a of cfg.agents.list) {
|
|
2084
|
-
const
|
|
2085
|
-
await fsp.
|
|
2029
|
+
const sf = await readWorkspaceText(projectDir, a, 'skills/cronjob/SKILL.md');
|
|
2030
|
+
await fsp.mkdir(dirname(sf.file), { recursive: true });
|
|
2031
|
+
await fsp.writeFile(sf.file, buildCronjobSkillMd(true), 'utf8');
|
|
2086
2032
|
}
|
|
2087
2033
|
} else {
|
|
2088
2034
|
if (cfg.tools?.alsoAllow) cfg.tools.alsoAllow = cfg.tools.alsoAllow.filter((x) => x !== 'group:automation');
|
|
2089
2035
|
if (cfg.commands?.ownerAllowFrom) cfg.commands.ownerAllowFrom = cfg.commands.ownerAllowFrom.filter((x) => x !== '*');
|
|
2090
2036
|
for (const a of cfg.agents.list) {
|
|
2091
|
-
const
|
|
2092
|
-
await fsp.
|
|
2037
|
+
const sf = await readWorkspaceText(projectDir, a, 'skills/cronjob/SKILL.md');
|
|
2038
|
+
if (existsSync(sf.file)) await fsp.rm(sf.file, { force: true });
|
|
2093
2039
|
}
|
|
2094
2040
|
}
|
|
2095
2041
|
|
|
@@ -2104,6 +2050,59 @@ async function applyFeatureToggle(projectDir, agentId, kind, id, enabled) {
|
|
|
2104
2050
|
}
|
|
2105
2051
|
}
|
|
2106
2052
|
|
|
2053
|
+
if (kind === 'skill' && id === 'image-gen') {
|
|
2054
|
+
cfg.skills = cfg.skills || { entries: {} };
|
|
2055
|
+
cfg.skills.entries = cfg.skills.entries || {};
|
|
2056
|
+
cfg.skills.entries['image-gen'] = cfg.skills.entries['image-gen'] || {};
|
|
2057
|
+
cfg.skills.entries['image-gen'].enabled = !!enabled;
|
|
2058
|
+
|
|
2059
|
+
for (const a of cfg.agents.list) {
|
|
2060
|
+
const sf = await readWorkspaceText(projectDir, a, 'skills/infographic-generator/SKILL.md');
|
|
2061
|
+
const js = await readWorkspaceText(projectDir, a, 'skills/infographic-generator/image-generator.js');
|
|
2062
|
+
if (enabled) {
|
|
2063
|
+
await fsp.mkdir(dirname(sf.file), { recursive: true });
|
|
2064
|
+
await fsp.writeFile(sf.file, buildInfographicGeneratorSkillMd(), 'utf8');
|
|
2065
|
+
await fsp.writeFile(js.file, buildInfographicGeneratorJs(), 'utf8');
|
|
2066
|
+
} else {
|
|
2067
|
+
if (existsSync(sf.file)) await fsp.rm(sf.file, { force: true });
|
|
2068
|
+
if (existsSync(js.file)) await fsp.rm(js.file, { force: true });
|
|
2069
|
+
}
|
|
2070
|
+
}
|
|
2071
|
+
|
|
2072
|
+
// Write cfgPath early so recreation reads updated openclaw.json
|
|
2073
|
+
await fsp.writeFile(cfgPath, JSON.stringify(cfg, null, 2), 'utf8');
|
|
2074
|
+
|
|
2075
|
+
// Recreate container to apply updated openclaw.json
|
|
2076
|
+
const hasDocker = existsSync(join(projectDir, 'docker', 'openclaw', 'docker-compose.yml'));
|
|
2077
|
+
if (hasDocker) {
|
|
2078
|
+
sendLog(`[docker] Infographic skill toggled to ${enabled}. Recreating containers...`);
|
|
2079
|
+
await recreateDockerBot(projectDir).catch((err) => sendLog(`[docker] Warning: Failed to recreate container: ${err.message}`));
|
|
2080
|
+
}
|
|
2081
|
+
}
|
|
2082
|
+
|
|
2083
|
+
if (kind === 'skill' && id === 'web-search') {
|
|
2084
|
+
cfg.plugins = cfg.plugins || { entries: {} };
|
|
2085
|
+
cfg.plugins.entries = cfg.plugins.entries || {};
|
|
2086
|
+
cfg.plugins.entries['duckduckgo'] = cfg.plugins.entries['duckduckgo'] || {};
|
|
2087
|
+
cfg.plugins.entries['duckduckgo'].enabled = !!enabled;
|
|
2088
|
+
cfg.plugins.allow = cfg.plugins.allow || [];
|
|
2089
|
+
if (enabled) {
|
|
2090
|
+
if (!cfg.plugins.allow.includes('duckduckgo')) cfg.plugins.allow.push('duckduckgo');
|
|
2091
|
+
} else {
|
|
2092
|
+
cfg.plugins.allow = cfg.plugins.allow.filter((x) => x !== 'duckduckgo');
|
|
2093
|
+
}
|
|
2094
|
+
|
|
2095
|
+
// Write cfgPath early so recreation reads updated openclaw.json
|
|
2096
|
+
await fsp.writeFile(cfgPath, JSON.stringify(cfg, null, 2), 'utf8');
|
|
2097
|
+
|
|
2098
|
+
// Recreate container to apply updated openclaw.json
|
|
2099
|
+
const hasDocker = existsSync(join(projectDir, 'docker', 'openclaw', 'docker-compose.yml'));
|
|
2100
|
+
if (hasDocker) {
|
|
2101
|
+
sendLog(`[docker] Web Search skill toggled to ${enabled}. Recreating containers...`);
|
|
2102
|
+
await recreateDockerBot(projectDir).catch((err) => sendLog(`[docker] Warning: Failed to recreate container: ${err.message}`));
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
2107
2106
|
if (kind === 'plugin') {
|
|
2108
2107
|
cfg.plugins = cfg.plugins || { entries: {} };
|
|
2109
2108
|
cfg.plugins.entries = cfg.plugins.entries || {};
|
|
@@ -2344,6 +2343,8 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
2344
2343
|
Array.from(installedSpecs).some((spec) => spec.includes(a)) ||
|
|
2345
2344
|
allowSet.has(a)
|
|
2346
2345
|
);
|
|
2346
|
+
const imageGenOn = !!cfg.skills?.entries?.['image-gen']?.enabled;
|
|
2347
|
+
const webSearchOn = isEnabled(['duckduckgo']);
|
|
2347
2348
|
const aliases = {
|
|
2348
2349
|
browser: ['openclaw-browser-automation', 'browser-automation'],
|
|
2349
2350
|
zalo: ['openclaw-zalo-mod', 'zalo-mod'],
|
|
@@ -2353,6 +2354,8 @@ async function getFeatureFlags(projectDir, agentId = '') {
|
|
|
2353
2354
|
const flags = {
|
|
2354
2355
|
'skill:browser': browserOn,
|
|
2355
2356
|
'skill:cron': cronOn,
|
|
2357
|
+
'skill:image-gen': imageGenOn,
|
|
2358
|
+
'skill:web-search': webSearchOn,
|
|
2356
2359
|
'plugin:openclaw-browser-automation': isEnabled(aliases.browser),
|
|
2357
2360
|
'plugin:openclaw-zalo-mod': isEnabled(aliases.zalo),
|
|
2358
2361
|
'plugin:openclaw-facebook-crawler': isEnabled(aliases.crawler),
|
|
@@ -2544,6 +2547,7 @@ async function handler(req, res, rootProjectDir) {
|
|
|
2544
2547
|
await run('npm', ['install'], { cwd: rootProjectDir });
|
|
2545
2548
|
await run('npm', ['run', 'build'], { cwd: rootProjectDir });
|
|
2546
2549
|
sendLog('[update-setup] Setup Wizard updated successfully! Please restart the installer.');
|
|
2550
|
+
restartInstaller();
|
|
2547
2551
|
} catch (err) {
|
|
2548
2552
|
sendLog(`[update-setup] Error updating: ${err.message}`);
|
|
2549
2553
|
}
|
|
@@ -2555,6 +2559,7 @@ async function handler(req, res, rootProjectDir) {
|
|
|
2555
2559
|
try {
|
|
2556
2560
|
await run('npm', ['install', '-g', 'create-openclaw-bot@latest'], { cwd: rootProjectDir });
|
|
2557
2561
|
sendLog('[update-setup] Setup Wizard updated successfully! Please restart the installer.');
|
|
2562
|
+
restartInstaller();
|
|
2558
2563
|
} catch (err) {
|
|
2559
2564
|
sendLog(`[update-setup] Error updating: ${err.message}`);
|
|
2560
2565
|
}
|
|
@@ -2652,6 +2657,8 @@ async function handler(req, res, rootProjectDir) {
|
|
|
2652
2657
|
skills: [
|
|
2653
2658
|
{ name: 'Browser', slug: 'browser' },
|
|
2654
2659
|
{ name: 'Cron', slug: 'cron' },
|
|
2660
|
+
{ name: 'TαΊ‘o αΊ£nh Infographic', slug: 'image-gen' },
|
|
2661
|
+
{ name: 'Web Search', slug: 'web-search' },
|
|
2655
2662
|
],
|
|
2656
2663
|
plugins: [
|
|
2657
2664
|
{ name: 'openclaw-browser-automation', package: 'openclaw-browser-automation' },
|
|
@@ -2700,13 +2707,42 @@ function openUrl(url) {
|
|
|
2700
2707
|
child.unref();
|
|
2701
2708
|
}
|
|
2702
2709
|
|
|
2710
|
+
function restartInstaller() {
|
|
2711
|
+
sendLog('[update-setup] Restarting Setup Wizard to apply update...');
|
|
2712
|
+
setTimeout(() => {
|
|
2713
|
+
try {
|
|
2714
|
+
if (activeServerInstance) {
|
|
2715
|
+
activeServerInstance.close();
|
|
2716
|
+
}
|
|
2717
|
+
|
|
2718
|
+
const entryFile = process.argv[1];
|
|
2719
|
+
const args = process.argv.slice(2);
|
|
2720
|
+
|
|
2721
|
+
if (!args.includes('--no-open')) {
|
|
2722
|
+
args.push('--no-open');
|
|
2723
|
+
}
|
|
2724
|
+
|
|
2725
|
+
const child = spawn(process.argv[0], [entryFile, ...args], {
|
|
2726
|
+
detached: true,
|
|
2727
|
+
stdio: 'inherit',
|
|
2728
|
+
shell: process.platform === 'win32'
|
|
2729
|
+
});
|
|
2730
|
+
child.unref();
|
|
2731
|
+
|
|
2732
|
+
process.exit(0);
|
|
2733
|
+
} catch (err) {
|
|
2734
|
+
sendLog(`[update-setup] Failed to restart: ${err.message}`);
|
|
2735
|
+
}
|
|
2736
|
+
}, 2000);
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2703
2739
|
export async function startLocalInstaller({ host = '127.0.0.1', preferredPort = 51789, openBrowser = true, projectDir = process.cwd() } = {}) {
|
|
2704
2740
|
const port = await findPort(host, preferredPort);
|
|
2705
2741
|
const server = http.createServer((req, res) => handler(req, res, projectDir));
|
|
2742
|
+
activeServerInstance = server;
|
|
2706
2743
|
await new Promise((resolve) => server.listen(port, host, resolve));
|
|
2707
2744
|
const url = `http://${host}:${port}`;
|
|
2708
2745
|
console.log(`OpenClaw Setup UI: ${url}`);
|
|
2709
|
-
console.log('Legacy CLI: create-openclaw-bot legacy');
|
|
2710
2746
|
if (openBrowser) openUrl(url);
|
|
2711
2747
|
}
|
|
2712
2748
|
|
|
@@ -43,12 +43,10 @@
|
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
id: 'image-gen',
|
|
46
|
-
name: '
|
|
46
|
+
name: 'TαΊ‘o αΊ£nh Infographic',
|
|
47
47
|
icon: 'π¨',
|
|
48
|
-
descVi: 'TαΊ‘o αΊ£nh
|
|
48
|
+
descVi: 'TαΊ‘o αΊ£nh infographic, poster tα»± Δα»ng qua 9Router', descEn: 'Generate infographic & poster images via 9Router',
|
|
49
49
|
slug: 'image-gen',
|
|
50
|
-
noteVi: 'DΓΉng chung OPENAI_API_KEY (DALL-E) hoαΊ·c thΓͺm FLUX_API_KEY', noteEn: 'Uses OPENAI_API_KEY (DALL-E) or FLUX_API_KEY',
|
|
51
|
-
envVars: ['# FLUX_API_KEY=<your_flux_key> # chα» cαΊ§n nαΊΏu dΓΉng Flux'],
|
|
52
50
|
},
|
|
53
51
|
{
|
|
54
52
|
id: 'code-interpreter',
|
|
@@ -140,18 +140,24 @@
|
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
// ββ bindings (multi-bot or Zalo) βββββββββββββββββββββββββββββββββββββββββ
|
|
143
|
-
if (isMultiBot && channelKey === 'telegram') {
|
|
143
|
+
if (agentMetas.length > 0 && isMultiBot && channelKey === 'telegram') {
|
|
144
144
|
cfg.bindings = agentMetas.map((meta) => ({
|
|
145
145
|
agentId: meta.agentId,
|
|
146
146
|
match: { channel: 'telegram', accountId: meta.accountId || 'default' },
|
|
147
147
|
}));
|
|
148
|
+
} else {
|
|
149
|
+
cfg.bindings = [];
|
|
148
150
|
}
|
|
149
151
|
|
|
150
152
|
// ββ channels βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
if (agentMetas.length > 0) {
|
|
154
|
+
cfg.channels = buildChannelConfig({
|
|
155
|
+
channelKey, isMultiBot, groupId, agentMetas, botName: agentMetas[0]?.name || 'Bot',
|
|
156
|
+
agentId: agentMetas[0]?.agentId || 'bot',
|
|
157
|
+
});
|
|
158
|
+
} else {
|
|
159
|
+
cfg.channels = {};
|
|
160
|
+
}
|
|
155
161
|
|
|
156
162
|
// ββ tools ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
157
163
|
cfg.tools = { profile: 'full', exec: { host: 'gateway', security: 'full', ask: 'off' } };
|
|
@@ -179,16 +185,7 @@
|
|
|
179
185
|
auth: { mode: 'token', token: generateToken() },
|
|
180
186
|
};
|
|
181
187
|
|
|
182
|
-
// ββ browser
|
|
183
|
-
if (hasBrowserDesktop) {
|
|
184
|
-
cfg.browser = {
|
|
185
|
-
enabled: true,
|
|
186
|
-
defaultProfile: 'host-chrome',
|
|
187
|
-
profiles: { 'host-chrome': { cdpUrl: 'http://127.0.0.1:9222', color: '#4285F4' } },
|
|
188
|
-
};
|
|
189
|
-
} else if (hasBrowserServer) {
|
|
190
|
-
cfg.browser = { enabled: true };
|
|
191
|
-
}
|
|
188
|
+
// ββ browser (delegated to browser-automation plugin) ββββββββββββββββββββ
|
|
192
189
|
|
|
193
190
|
// ββ skills βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
194
191
|
const skillEntries = buildSkillsEntries(skills, selectedSkills);
|
|
@@ -206,7 +203,7 @@
|
|
|
206
203
|
cfg.plugins = pluginsConfig.plugins;
|
|
207
204
|
|
|
208
205
|
// ββ bindings for zalouser ββββββββββββββββββββββββββββββββββββββββββββββββ
|
|
209
|
-
if (isZaloPersonal(channelKey)) {
|
|
206
|
+
if (agentMetas.length > 0 && isZaloPersonal(channelKey)) {
|
|
210
207
|
cfg.bindings = cfg.bindings || [];
|
|
211
208
|
const firstAgentId = agentMetas[0]?.agentId || 'bot';
|
|
212
209
|
if (!cfg.bindings.some(b => b.match && b.match.channel === 'zalouser')) {
|
|
@@ -317,6 +314,14 @@
|
|
|
317
314
|
allow.push('zalouser');
|
|
318
315
|
}
|
|
319
316
|
|
|
317
|
+
// DuckDuckGo search plugin for web-search
|
|
318
|
+
if (selectedSkills.includes('web-search') || selectedSkills.includes('web_search')) {
|
|
319
|
+
entries['duckduckgo'] = { enabled: true };
|
|
320
|
+
if (!allow.includes('duckduckgo')) {
|
|
321
|
+
allow.push('duckduckgo');
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
|
|
320
325
|
const plugins = { entries };
|
|
321
326
|
plugins.allow = allow;
|
|
322
327
|
|