create-openclaw-bot 5.1.0 → 5.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -3
- package/CHANGELOG.vi.md +45 -3
- package/README.md +5 -4
- package/README.vi.md +5 -4
- package/cli.js +2376 -2085
- package/package.json +1 -1
- package/setup.js +180 -181
- package/tests/smoke-cli-logic.mjs +119 -15
- package/tmp/live-enable-zalouser.cjs +0 -20
- package/tmp/live-enable-zalouser.js +0 -20
- package/tmp/live-patch-9router.cjs +0 -40
- package/tmp/live-patch-9router.js +0 -40
package/package.json
CHANGED
package/setup.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/* ============================================
|
|
1
|
+
/* ============================================
|
|
2
2
|
OpenClaw Setup Wizard — Logic v2
|
|
3
3
|
Multi-model, Multi-plugin, Multi-channel
|
|
4
4
|
============================================ */
|
|
@@ -271,16 +271,16 @@
|
|
|
271
271
|
},
|
|
272
272
|
pluginInstall: '',
|
|
273
273
|
},
|
|
274
|
-
'zalo-personal': {
|
|
275
|
-
name: 'Zalo Personal',
|
|
276
|
-
envKeys: [],
|
|
277
|
-
envExtra: '',
|
|
278
|
-
credSteps: [
|
|
279
|
-
{ textVi: '⚠️ Zalo Personal dùng <strong>unofficial API (zca-js)</strong> — chỉ nên dùng tài khoản phụ', textEn: '⚠️ Zalo Personal uses <strong>unofficial API (zca-js)</strong> — use an alternate account' },
|
|
280
|
-
{ textVi: '
|
|
281
|
-
],
|
|
282
|
-
channelConfig: {
|
|
283
|
-
zalouser: {
|
|
274
|
+
'zalo-personal': {
|
|
275
|
+
name: 'Zalo Personal',
|
|
276
|
+
envKeys: [],
|
|
277
|
+
envExtra: '',
|
|
278
|
+
credSteps: [
|
|
279
|
+
{ textVi: '⚠️ Zalo Personal dùng <strong>unofficial API (zca-js)</strong> — chỉ nên dùng tài khoản phụ', textEn: '⚠️ Zalo Personal uses <strong>unofficial API (zca-js)</strong> — use an alternate account' },
|
|
280
|
+
{ textVi: 'Native setup sẽ tự chạy login và copy QR về thư mục project. Nếu cần chạy lại thủ công, dùng <code>openclaw channels login --channel zalouser --verbose</code>.', textEn: 'Native setup now auto-runs the login flow and copies the QR into the project folder. If needed, rerun it manually with <code>openclaw channels login --channel zalouser --verbose</code>.' },
|
|
281
|
+
],
|
|
282
|
+
channelConfig: {
|
|
283
|
+
zalouser: {
|
|
284
284
|
enabled: true,
|
|
285
285
|
accounts: {
|
|
286
286
|
default: {
|
|
@@ -728,8 +728,8 @@
|
|
|
728
728
|
icon: '🐧',
|
|
729
729
|
titleVi: 'Ubuntu / VPS — Khuyên dùng Native (Không Docker)',
|
|
730
730
|
titleEn: 'Ubuntu / VPS — Recommended: Native (No Docker)',
|
|
731
|
-
descVi: 'Chạy thẳng trên máy, tiết kiệm RAM, khởi động nhanh. Script tự cài Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama và giữ bot chạy liên tục sau reboot.',
|
|
732
|
-
descEn: 'Run directly on machine — lower RAM, faster startup. Script auto-installs Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama and keeps bot running across reboots.',
|
|
731
|
+
descVi: 'Chạy thẳng trên máy, tiết kiệm RAM, khởi động nhanh. Script tự cài Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama và giữ bot chạy liên tục sau reboot.',
|
|
732
|
+
descEn: 'Run directly on machine — lower RAM, faster startup. Script auto-installs Node.js 20 LTS, OpenClaw CLI, PM2, 9Router/Ollama and keeps bot running across reboots.',
|
|
733
733
|
deploy: 'native',
|
|
734
734
|
badgeVi: '💻 Native + PM2',
|
|
735
735
|
badgeEn: '💻 Native + PM2',
|
|
@@ -739,8 +739,8 @@
|
|
|
739
739
|
icon: '🖥️',
|
|
740
740
|
titleVi: 'Linux Desktop — Khuyên dùng Native',
|
|
741
741
|
titleEn: 'Linux Desktop — Recommended: Native',
|
|
742
|
-
descVi: 'Không cần Docker. Script tự cài Node.js 20 LTS nếu chưa có, cài OpenClaw CLI, rồi cài 9Router hoặc Ollama theo provider bạn chọn và khởi động bot ngay.',
|
|
743
|
-
descEn: 'No Docker needed. Script auto-installs Node.js 20 LTS if missing, installs OpenClaw CLI, then installs 9Router or Ollama based on your provider choice and starts the bot immediately.',
|
|
742
|
+
descVi: 'Không cần Docker. Script tự cài Node.js 20 LTS nếu chưa có, cài OpenClaw CLI, rồi cài 9Router hoặc Ollama theo provider bạn chọn và khởi động bot ngay.',
|
|
743
|
+
descEn: 'No Docker needed. Script auto-installs Node.js 20 LTS if missing, installs OpenClaw CLI, then installs 9Router or Ollama based on your provider choice and starts the bot immediately.',
|
|
744
744
|
deploy: 'native',
|
|
745
745
|
badgeVi: '💻 Native',
|
|
746
746
|
badgeEn: '💻 Native',
|
|
@@ -1520,7 +1520,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
1520
1520
|
// Generate native script if native mode
|
|
1521
1521
|
if (isNativeMode) generateNativeScript();
|
|
1522
1522
|
|
|
1523
|
-
// Show/hide Zalo Personal login notice
|
|
1523
|
+
// Show/hide Zalo Personal login notice
|
|
1524
1524
|
const zaloNotice = document.getElementById('zalo-onboard-notice');
|
|
1525
1525
|
const isZaloPersonal = state.channel === 'zalo-personal';
|
|
1526
1526
|
if (zaloNotice) {
|
|
@@ -1802,7 +1802,7 @@ model:
|
|
|
1802
1802
|
const browserPrefix = hasBrowser
|
|
1803
1803
|
? 'socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 & '
|
|
1804
1804
|
: '';
|
|
1805
|
-
// Patch config on every startup to keep gateway settings stable
|
|
1805
|
+
// Patch config on every startup to keep gateway settings stable
|
|
1806
1806
|
const patchCmd = `node -e \\"const fs=require('fs'),p='/root/.openclaw/openclaw.json';if(fs.existsSync(p)){const c=JSON.parse(fs.readFileSync(p,'utf8'));c.tools=Object.assign({},c.tools,{profile:'full',exec:{host:'gateway',security:'full',ask:'off'}});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0'});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\" && `;
|
|
1807
1807
|
// Auto-approve device pairing after gateway starts (required since v2026.3.x)
|
|
1808
1808
|
const autoApproveCmd = '(while true; do sleep 5; openclaw devices approve --latest 2>/dev/null || true; done) & ';
|
|
@@ -1815,7 +1815,7 @@ RUN apt-get update && apt-get install -y git curl${browserAptExtra} && rm -rf /v
|
|
|
1815
1815
|
|
|
1816
1816
|
ARG CACHEBUST=${Date.now()}
|
|
1817
1817
|
RUN npm install -g openclaw@latest${skillLines}${browserInstallLines}
|
|
1818
|
-
RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const file=(fs.readdirSync(dir).find(n=>/^gateway-cli-.*\\.js$/.test(n))||'');if(!file){console.warn('gateway cli dist file not found; skipping timeout patch');process.exit(0);}const p=path.join(dir,file);let s=fs.readFileSync(p,'utf8');const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';if(s.includes(to)){process.exit(0);}if(!s.includes(from)){console.warn('chat.send patch anchor not found; skipping timeout patch');process.exit(0);}s=s.replace(from,to);fs.writeFileSync(p,s);}"
|
|
1818
|
+
RUN node -e "const fs=require('fs');const path=require('path');const dir='/usr/local/lib/node_modules/openclaw/dist';const file=(fs.readdirSync(dir).find(n=>/^gateway-cli-.*\\.js$/.test(n))||'');if(!file){console.warn('gateway cli dist file not found; skipping timeout patch');process.exit(0);}const p=path.join(dir,file);let s=fs.readFileSync(p,'utf8');const from='\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';const to='\\t\\t\\t\\t\\ttimeoutOverrideSeconds: Math.max(1, Math.ceil(timeoutMs / 1e3)),\\n\\t\\t\\t\\t\\tonAgentRunStart: (runId) => {';if(s.includes(to)){process.exit(0);}if(!s.includes(from)){console.warn('chat.send patch anchor not found; skipping timeout patch');process.exit(0);}s=s.replace(from,to);fs.writeFileSync(p,s);}"
|
|
1819
1819
|
WORKDIR /root/.openclaw
|
|
1820
1820
|
|
|
1821
1821
|
EXPOSE 18791
|
|
@@ -1841,31 +1841,34 @@ const sync = async () => {
|
|
|
1841
1841
|
try {
|
|
1842
1842
|
let db = {};
|
|
1843
1843
|
try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
|
|
1844
|
-
if (!db.combos) db.combos = [];
|
|
1845
|
-
const removeSmartRoute = () => {
|
|
1846
|
-
const next = db.combos.filter(x => x.id !== 'smart-route');
|
|
1847
|
-
if (next.length !== db.combos.length) {
|
|
1848
|
-
db.combos = next;
|
|
1849
|
-
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
1850
|
-
console.log('[sync-combo] Removed smart-route (no active providers)');
|
|
1851
|
-
}
|
|
1852
|
-
};
|
|
1853
|
-
const
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1844
|
+
if (!db.combos) db.combos = [];
|
|
1845
|
+
const removeSmartRoute = () => {
|
|
1846
|
+
const next = db.combos.filter(x => x.id !== 'smart-route');
|
|
1847
|
+
if (next.length !== db.combos.length) {
|
|
1848
|
+
db.combos = next;
|
|
1849
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
1850
|
+
console.log('[sync-combo] Removed smart-route (no active providers)');
|
|
1851
|
+
}
|
|
1852
|
+
};
|
|
1853
|
+
const res = await fetch('http://localhost:20128/api/providers');
|
|
1854
|
+
if (!res.ok) { console.log('[sync-combo] API not ready, retrying...'); return; }
|
|
1855
|
+
const d = await res.json();
|
|
1856
|
+
const a = (d.connections || [])
|
|
1857
|
+
.filter(c => c && c.provider && c.isActive !== false && !c.disabled)
|
|
1858
|
+
.map(c => c.provider);
|
|
1859
|
+
if (!a.length) {
|
|
1860
|
+
removeSmartRoute();
|
|
1861
|
+
return;
|
|
1862
|
+
}
|
|
1860
1863
|
|
|
1861
1864
|
const PREF = ['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];
|
|
1862
1865
|
a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
|
|
1863
1866
|
|
|
1864
|
-
const m = a.flatMap(
|
|
1865
|
-
if (!m.length) {
|
|
1866
|
-
removeSmartRoute();
|
|
1867
|
-
return;
|
|
1868
|
-
}
|
|
1867
|
+
const m = a.flatMap(pv => PM[pv] || []);
|
|
1868
|
+
if (!m.length) {
|
|
1869
|
+
removeSmartRoute();
|
|
1870
|
+
return;
|
|
1871
|
+
}
|
|
1869
1872
|
|
|
1870
1873
|
const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
|
|
1871
1874
|
const i = db.combos.findIndex(x => x.id === 'smart-route');
|
|
@@ -1882,7 +1885,7 @@ const sync = async () => {
|
|
|
1882
1885
|
}
|
|
1883
1886
|
} catch (e) { }
|
|
1884
1887
|
};
|
|
1885
|
-
sync
|
|
1888
|
+
setTimeout(sync, 5000);
|
|
1886
1889
|
setInterval(sync, INTERVAL);`;
|
|
1887
1890
|
|
|
1888
1891
|
let compose;
|
|
@@ -1917,9 +1920,7 @@ ${dependsOn}${extraHosts} volumes:
|
|
|
1917
1920
|
- -c
|
|
1918
1921
|
- |
|
|
1919
1922
|
npm install -g 9router
|
|
1920
|
-
|
|
1921
|
-
${syncScript.replace(/\$/g, '$$$$').replace(/\n/g, '\n ')}
|
|
1922
|
-
CLAWEOF
|
|
1923
|
+
node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('\${Buffer.from(syncScript).toString('base64')}','base64').toString())"
|
|
1923
1924
|
node /tmp/sync.js > /tmp/sync.log 2>&1 &
|
|
1924
1925
|
exec 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update
|
|
1925
1926
|
environment:
|
|
@@ -2015,9 +2016,7 @@ ${extraHostsBlock}
|
|
|
2015
2016
|
- -c
|
|
2016
2017
|
- |
|
|
2017
2018
|
npm install -g 9router
|
|
2018
|
-
|
|
2019
|
-
${syncScript.replace(/\$/g, '$$$$').replace(/\n/g, '\n ')}
|
|
2020
|
-
CLAWEOF
|
|
2019
|
+
node -e "require('fs').writeFileSync('/tmp/sync.js',Buffer.from('\${Buffer.from(syncScript).toString('base64')}','base64').toString())"
|
|
2021
2020
|
node /tmp/sync.js > /tmp/sync.log 2>&1 &
|
|
2022
2021
|
exec 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update
|
|
2023
2022
|
environment:
|
|
@@ -2846,33 +2845,33 @@ I am **${botName}**. When asked my name, I answer: _"I'm ${botName}"_.`;
|
|
|
2846
2845
|
const p = PLUGINS.find((x) => x.id === pid);
|
|
2847
2846
|
if (p) allPlugins.push(p.package);
|
|
2848
2847
|
});
|
|
2849
|
-
if (isMultiBot && state.channel === 'telegram') allPlugins.push(relayPluginSpec);
|
|
2850
|
-
const pluginCmd = allPlugins.length > 0 ? ('npm exec openclaw plugins install ' + allPlugins.join(' ')) : '';
|
|
2851
|
-
|
|
2852
|
-
function native9RouterSyncScriptContent() {
|
|
2853
|
-
return `const fs=require('fs');
|
|
2854
|
-
const path=require('path');
|
|
2855
|
-
const INTERVAL=30000;
|
|
2856
|
-
const p=path.join(process.env.HOME||process.env.USERPROFILE||'.','.9router','db.json');
|
|
2857
|
-
const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],claude-code:['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],cline:['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],iflow:['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],qwen:['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],kiro:['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],ollama:['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],glm:['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],minimax:['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],kimi:['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],deepseek:['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],xai:['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],mistral:['mistral/mistral-large-latest','mistral/codestral-latest'],groq:['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],cerebras:['cerebras/gpt-oss-120b'],alicode:['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],openai:['openai/gpt-4o','openai/gpt-4.1'],anthropic:['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],gemini:['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
|
|
2858
|
-
const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catch{}if(!db.combos)db.combos=[];const removeSmartRoute=()=>{const next=db.combos.filter(x=>x.id!=='smart-route');if(next.length!==db.combos.length){db.combos=next;fs.writeFileSync(p,JSON.stringify(db,null,2));}};const a=(db.providerConnections||[]).filter(c=>c&&c.provider&&c.isActive!==false&&!c.disabled).map(c=>c.provider);if(!a.length){removeSmartRoute();return;}const PREF=['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];a.sort((x,y)=>(PREF.indexOf(x)===-1?99:PREF.indexOf(x))-(PREF.indexOf(y)===-1?99:PREF.indexOf(y)));const m=a.flatMap(provider=>PM[provider]||[]);if(!m.length){removeSmartRoute();return;}const c={id:'smart-route',name:'smart-route',alias:'smart-route',models:m};const i=db.combos.findIndex(x=>x.id==='smart-route');if(i>=0){if(JSON.stringify(db.combos[i].models)!==JSON.stringify(c.models)){db.combos[i]=c;fs.writeFileSync(p,JSON.stringify(db,null,2));}}else{db.combos.push(c);fs.writeFileSync(p,JSON.stringify(db,null,2));}}catch{}};sync();setInterval(sync,INTERVAL);`;
|
|
2859
|
-
}
|
|
2848
|
+
if (isMultiBot && state.channel === 'telegram') allPlugins.push(relayPluginSpec);
|
|
2849
|
+
const pluginCmd = allPlugins.length > 0 ? ('npm exec openclaw plugins install ' + allPlugins.join(' ')) : '';
|
|
2850
|
+
|
|
2851
|
+
function native9RouterSyncScriptContent() {
|
|
2852
|
+
return `const fs=require('fs');
|
|
2853
|
+
const path=require('path');
|
|
2854
|
+
const INTERVAL=30000;
|
|
2855
|
+
const p=path.join(process.env.HOME||process.env.USERPROFILE||'.','.9router','db.json');
|
|
2856
|
+
const PM={codex:['cx/gpt-5.4','cx/gpt-5.3-codex','cx/gpt-5.3-codex-high','cx/gpt-5.2-codex','cx/gpt-5.2','cx/gpt-5.1-codex-max','cx/gpt-5.1-codex','cx/gpt-5.1','cx/gpt-5-codex'],claude-code:['cc/claude-opus-4-6','cc/claude-sonnet-4-6','cc/claude-opus-4-5-20251101','cc/claude-sonnet-4-5-20250929','cc/claude-haiku-4-5-20251001'],github:['gh/gpt-5.4','gh/gpt-5.3-codex','gh/gpt-5.2-codex','gh/gpt-5.2','gh/gpt-5.1-codex-max','gh/gpt-5.1-codex','gh/gpt-5.1','gh/gpt-5','gh/gpt-4.1','gh/gpt-4o','gh/claude-opus-4.6','gh/claude-sonnet-4.6','gh/claude-sonnet-4.5','gh/claude-opus-4.5','gh/claude-haiku-4.5','gh/gemini-3-pro-preview','gh/gemini-3-flash-preview','gh/gemini-2.5-pro'],cursor:['cu/default','cu/claude-4.6-opus-max','cu/claude-4.5-opus-high-thinking','cu/claude-4.5-sonnet-thinking','cu/claude-4.5-sonnet','cu/gpt-5.3-codex','cu/gpt-5.2-codex','cu/gemini-3-flash-preview'],kilo:['kc/anthropic/claude-sonnet-4-20250514','kc/anthropic/claude-opus-4-20250514','kc/google/gemini-2.5-pro','kc/google/gemini-2.5-flash','kc/openai/gpt-4.1','kc/deepseek/deepseek-chat'],cline:['cl/anthropic/claude-sonnet-4.6','cl/anthropic/claude-opus-4.6','cl/openai/gpt-5.3-codex','cl/openai/gpt-5.4','cl/google/gemini-3.1-pro-preview'],'gemini-cli':['gc/gemini-3-flash-preview','gc/gemini-3-pro-preview'],iflow:['if/qwen3-coder-plus','if/kimi-k2','if/kimi-k2-thinking','if/glm-4.7','if/deepseek-r1','if/deepseek-v3.2','if/deepseek-v3','if/qwen3-max','if/qwen3-235b','if/iflow-rome-30ba3b'],qwen:['qw/qwen3-coder-plus','qw/qwen3-coder-flash','qw/vision-model','qw/coder-model'],kiro:['kr/claude-sonnet-4.5','kr/claude-haiku-4.5','kr/deepseek-3.2','kr/deepseek-3.1','kr/qwen3-coder-next'],ollama:['ollama/gemma4:e2b','ollama/gemma4:e4b','ollama/gemma4:26b','ollama/gemma4:31b','ollama/qwen3.5','ollama/kimi-k2.5','ollama/glm-5','ollama/glm-4.7-flash','ollama/minimax-m2.5','ollama/gpt-oss:120b'],'kimi-coding':['kmc/kimi-k2.5','kmc/kimi-k2.5-thinking','kmc/kimi-latest'],glm:['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],'glm-cn':['glm/glm-5.1','glm/glm-5','glm/glm-4.7'],minimax:['minimax/MiniMax-M2.7','minimax/MiniMax-M2.5','minimax/MiniMax-M2.1'],kimi:['kimi/kimi-k2.5','kimi/kimi-k2.5-thinking','kimi/kimi-latest'],deepseek:['deepseek/deepseek-chat','deepseek/deepseek-reasoner'],xai:['xai/grok-4','xai/grok-4-fast-reasoning','xai/grok-code-fast-1'],mistral:['mistral/mistral-large-latest','mistral/codestral-latest'],groq:['groq/llama-3.3-70b-versatile','groq/openai/gpt-oss-120b'],cerebras:['cerebras/gpt-oss-120b'],alicode:['alicode/qwen3.5-plus','alicode/qwen3-coder-plus'],openai:['openai/gpt-4o','openai/gpt-4.1'],anthropic:['anthropic/claude-sonnet-4','anthropic/claude-haiku-3.5'],gemini:['gemini/gemini-2.5-flash','gemini/gemini-2.5-pro']};
|
|
2857
|
+
const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catch{}if(!db.combos)db.combos=[];const removeSmartRoute=()=>{const next=db.combos.filter(x=>x.id!=='smart-route');if(next.length!==db.combos.length){db.combos=next;fs.writeFileSync(p,JSON.stringify(db,null,2));}};const a=(db.providerConnections||[]).filter(c=>c&&c.provider&&c.isActive!==false&&!c.disabled).map(c=>c.provider);if(!a.length){removeSmartRoute();return;}const PREF=['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];a.sort((x,y)=>(PREF.indexOf(x)===-1?99:PREF.indexOf(x))-(PREF.indexOf(y)===-1?99:PREF.indexOf(y)));const m=a.flatMap(provider=>PM[provider]||[]);if(!m.length){removeSmartRoute();return;}const c={id:'smart-route',name:'smart-route',alias:'smart-route',models:m};const i=db.combos.findIndex(x=>x.id==='smart-route');if(i>=0){if(JSON.stringify(db.combos[i].models)!==JSON.stringify(c.models)){db.combos[i]=c;fs.writeFileSync(p,JSON.stringify(db,null,2));}}else{db.combos.push(c);fs.writeFileSync(p,JSON.stringify(db,null,2));}}catch{}};sync();setInterval(sync,INTERVAL);`;
|
|
2858
|
+
}
|
|
2860
2859
|
|
|
2861
2860
|
// ─── Shared initializer (provider install) ───────────────────────────────
|
|
2862
|
-
function providerLines(arr, shell) {
|
|
2863
|
-
if (is9Router) {
|
|
2864
|
-
if (shell === 'bat') {
|
|
2865
|
-
arr.push('npm install -g 9router');
|
|
2866
|
-
arr.push('start "9Router" cmd /k "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2867
|
-
arr.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
|
|
2868
|
-
arr.push('timeout /t 5 /nobreak >nul');
|
|
2869
|
-
} else {
|
|
2870
|
-
arr.push('npm install -g 9router');
|
|
2871
|
-
arr.push('nohup 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update >/tmp/9router.log 2>&1 &');
|
|
2872
|
-
arr.push('nohup node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &');
|
|
2873
|
-
arr.push('sleep 3');
|
|
2874
|
-
}
|
|
2875
|
-
} else if (isOllama) {
|
|
2861
|
+
function providerLines(arr, shell) {
|
|
2862
|
+
if (is9Router) {
|
|
2863
|
+
if (shell === 'bat') {
|
|
2864
|
+
arr.push('npm install -g 9router');
|
|
2865
|
+
arr.push('start "9Router" cmd /k "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
2866
|
+
arr.push('start "9Router Smart Route Sync" cmd /k "node .\\.openclaw\\9router-smart-route-sync.js"');
|
|
2867
|
+
arr.push('timeout /t 5 /nobreak >nul');
|
|
2868
|
+
} else {
|
|
2869
|
+
arr.push('npm install -g 9router');
|
|
2870
|
+
arr.push('nohup 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update >/tmp/9router.log 2>&1 &');
|
|
2871
|
+
arr.push('nohup node ./.openclaw/9router-smart-route-sync.js >/tmp/9router-sync.log 2>&1 &');
|
|
2872
|
+
arr.push('sleep 3');
|
|
2873
|
+
}
|
|
2874
|
+
} else if (isOllama) {
|
|
2876
2875
|
if (shell === 'bat') {
|
|
2877
2876
|
arr.push('where ollama >nul 2>&1 || (powershell -Command "Invoke-WebRequest -Uri https://ollama.com/download/OllamaSetup.exe -OutFile OllamaSetup.exe" && OllamaSetup.exe && del OllamaSetup.exe)');
|
|
2878
2877
|
arr.push('ollama pull ' + selectedModel);
|
|
@@ -3040,14 +3039,14 @@ const sync=()=>{try{let db={};try{db=JSON.parse(fs.readFileSync(p,'utf8'));}catc
|
|
|
3040
3039
|
}
|
|
3041
3040
|
|
|
3042
3041
|
function sharedNativeFileMap() {
|
|
3043
|
-
const files = {
|
|
3044
|
-
'.env': sharedNativeEnvContent(),
|
|
3045
|
-
'.openclaw/openclaw.json': sharedNativeConfigContent(),
|
|
3046
|
-
'.openclaw/exec-approvals.json': sharedNativeExecApprovalsContent(),
|
|
3047
|
-
'.openclaw/auth-profiles.json': sharedNativeAuthProfilesContent(),
|
|
3048
|
-
'TELEGRAM-POST-INSTALL.md': buildTelegramPostInstallChecklist(),
|
|
3049
|
-
};
|
|
3050
|
-
if (is9Router) files['.openclaw/9router-smart-route-sync.js'] = native9RouterSyncScriptContent();
|
|
3042
|
+
const files = {
|
|
3043
|
+
'.env': sharedNativeEnvContent(),
|
|
3044
|
+
'.openclaw/openclaw.json': sharedNativeConfigContent(),
|
|
3045
|
+
'.openclaw/exec-approvals.json': sharedNativeExecApprovalsContent(),
|
|
3046
|
+
'.openclaw/auth-profiles.json': sharedNativeAuthProfilesContent(),
|
|
3047
|
+
'TELEGRAM-POST-INSTALL.md': buildTelegramPostInstallChecklist(),
|
|
3048
|
+
};
|
|
3049
|
+
if (is9Router) files['.openclaw/9router-smart-route-sync.js'] = native9RouterSyncScriptContent();
|
|
3051
3050
|
const teamMd = isVi
|
|
3052
3051
|
? `# Doi Bot\n\n${multiBotAgentMetas.map((meta) => `## ${meta.name}\n- Vai tro: ${meta.desc}\n- Agent ID: \`${meta.agentId}\`\n- Telegram accountId: \`${meta.accountId}\`\n- Slash command: ${meta.slashCmd || '_(chua co)_'}\n- Tinh cach: ${meta.persona || '_(khong ghi ro)_'}`).join('\n\n')}\n\n## Quy uoc phoi hop\n- Tat ca bot trong doi biet ro vai tro cua nhau.\n- Neu user bao ban hoi mot bot khac, hay dung agent-to-agent noi bo thay vi doi Telegram chuyen tin cua bot.\n- Bot mo loi chi noi 1 cau ngan, sau do chuyen turn noi bo cho bot dich.\n- Bot dich phai tra loi cong khai bang chinh Telegram account cua minh trong cung chat/thread hien tai.\n- Neu can fallback, chi bot mo loi moi duoc phep tom tat thay.`
|
|
3053
3052
|
: `# Bot Team\n\n${multiBotAgentMetas.map((meta) => `## ${meta.name}\n- Role: ${meta.desc}\n- Agent ID: \`${meta.agentId}\`\n- Telegram accountId: \`${meta.accountId}\`\n- Slash command: ${meta.slashCmd || '_(not set)_'}\n- Persona: ${meta.persona || '_(not specified)_'}`).join('\n\n')}\n\n## Coordination Rules\n- Every bot knows the full roster.\n- If the user asks you to consult another bot, use internal agent-to-agent handoff instead of waiting for Telegram bot-to-bot delivery.\n- The caller bot only sends one short opener, then hands off internally.\n- The target bot must publish the real answer with its own Telegram account in the same chat/thread.\n- If a fallback is needed, only the caller bot may summarize on behalf of the target.`;
|
|
@@ -3395,11 +3394,11 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3395
3394
|
const base = '.';
|
|
3396
3395
|
const files = {};
|
|
3397
3396
|
files[`${base}/.env`] = botEnvContent(botIndex);
|
|
3398
|
-
files[`${base}/.openclaw/openclaw.json`] = botConfigContent(botIndex);
|
|
3399
|
-
files[`${base}/.openclaw/exec-approvals.json`] = botExecApprovalsContent(botIndex);
|
|
3400
|
-
files[`${base}/.openclaw/auth-profiles.json`] = botAuthProfilesContent(botIndex);
|
|
3401
|
-
if (is9Router) files[`${base}/.openclaw/9router-smart-route-sync.js`] = native9RouterSyncScriptContent();
|
|
3402
|
-
files[`${base}/.openclaw/agents/${agentId}.yaml`] = botAgentYamlContent(botIndex);
|
|
3397
|
+
files[`${base}/.openclaw/openclaw.json`] = botConfigContent(botIndex);
|
|
3398
|
+
files[`${base}/.openclaw/exec-approvals.json`] = botExecApprovalsContent(botIndex);
|
|
3399
|
+
files[`${base}/.openclaw/auth-profiles.json`] = botAuthProfilesContent(botIndex);
|
|
3400
|
+
if (is9Router) files[`${base}/.openclaw/9router-smart-route-sync.js`] = native9RouterSyncScriptContent();
|
|
3401
|
+
files[`${base}/.openclaw/agents/${agentId}.yaml`] = botAgentYamlContent(botIndex);
|
|
3403
3402
|
files[`${base}/.openclaw/agents/${agentId}/agent/auth-profiles.json`] = botAuthProfilesContent(botIndex);
|
|
3404
3403
|
Object.entries(botWorkspaceFiles(botIndex)).forEach(([name, content]) => {
|
|
3405
3404
|
files[`${base}/.openclaw/workspace/${name}`] = content;
|
|
@@ -3479,17 +3478,17 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3479
3478
|
} else if (state.nativeOs === 'linux') {
|
|
3480
3479
|
const isDocker = state.deployMode === 'docker';
|
|
3481
3480
|
scriptName = isDocker ? 'setup-openclaw-docker-macos.sh' : 'setup-openclaw-macos.sh';
|
|
3482
|
-
const sh = [
|
|
3483
|
-
'#!/usr/bin/env bash', 'set -e',
|
|
3484
|
-
`echo "=== OpenClaw Setup — macOS${isDocker ? ' Docker' : ' Native'} ==="`,
|
|
3485
|
-
'command -v node > /dev/null 2>&1 || { echo "ERROR: Node.js chua cai! https://nodejs.org"; exit 1; }',
|
|
3486
|
-
'mkdir -p "$HOME/.local/bin"',
|
|
3487
|
-
'npm config set prefix "$HOME/.local"',
|
|
3488
|
-
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3489
|
-
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.zshrc"',
|
|
3490
|
-
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3491
|
-
'npm install -g openclaw@latest',
|
|
3492
|
-
];
|
|
3481
|
+
const sh = [
|
|
3482
|
+
'#!/usr/bin/env bash', 'set -e',
|
|
3483
|
+
`echo "=== OpenClaw Setup — macOS${isDocker ? ' Docker' : ' Native'} ==="`,
|
|
3484
|
+
'command -v node > /dev/null 2>&1 || { echo "ERROR: Node.js chua cai! https://nodejs.org"; exit 1; }',
|
|
3485
|
+
'mkdir -p "$HOME/.local/bin"',
|
|
3486
|
+
'npm config set prefix "$HOME/.local"',
|
|
3487
|
+
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3488
|
+
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.zshrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.zshrc"',
|
|
3489
|
+
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3490
|
+
'npm install -g openclaw@latest',
|
|
3491
|
+
];
|
|
3493
3492
|
providerLines(sh, 'sh');
|
|
3494
3493
|
if (pluginCmd) sh.push(pluginCmd);
|
|
3495
3494
|
|
|
@@ -3506,47 +3505,47 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3506
3505
|
// ─── VPS/Ubuntu PM2 .SH ──────────────────────────────────────────────────
|
|
3507
3506
|
} else if (state.nativeOs === 'vps') {
|
|
3508
3507
|
scriptName = 'setup-openclaw-vps.sh';
|
|
3509
|
-
const vps = [
|
|
3510
|
-
'#!/usr/bin/env bash', 'set -e',
|
|
3511
|
-
`echo "=== OpenClaw Setup — Ubuntu/VPS${isMultiBot ? ` Multi-Bot (${state.botCount} bots)` : ''} ==="`,
|
|
3512
|
-
'# Auto-install Node.js 20 LTS if missing',
|
|
3513
|
-
'if ! command -v node > /dev/null 2>&1; then',
|
|
3514
|
-
' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
|
|
3515
|
-
' sudo apt-get install -y nodejs',
|
|
3516
|
-
'fi',
|
|
3517
|
-
'mkdir -p "$HOME/.local/bin"',
|
|
3518
|
-
'npm config set prefix "$HOME/.local"',
|
|
3519
|
-
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3520
|
-
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
|
|
3521
|
-
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3522
|
-
'npm install -g openclaw@latest pm2@latest',
|
|
3523
|
-
];
|
|
3508
|
+
const vps = [
|
|
3509
|
+
'#!/usr/bin/env bash', 'set -e',
|
|
3510
|
+
`echo "=== OpenClaw Setup — Ubuntu/VPS${isMultiBot ? ` Multi-Bot (${state.botCount} bots)` : ''} ==="`,
|
|
3511
|
+
'# Auto-install Node.js 20 LTS if missing',
|
|
3512
|
+
'if ! command -v node > /dev/null 2>&1; then',
|
|
3513
|
+
' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
|
|
3514
|
+
' sudo apt-get install -y nodejs',
|
|
3515
|
+
'fi',
|
|
3516
|
+
'mkdir -p "$HOME/.local/bin"',
|
|
3517
|
+
'npm config set prefix "$HOME/.local"',
|
|
3518
|
+
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3519
|
+
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
|
|
3520
|
+
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3521
|
+
'npm install -g openclaw@latest pm2@latest',
|
|
3522
|
+
];
|
|
3524
3523
|
providerLines(vps, 'sh');
|
|
3525
3524
|
if (pluginCmd) vps.push(pluginCmd);
|
|
3526
3525
|
|
|
3527
|
-
if (isMultiBot) {
|
|
3528
|
-
vps.push('echo "--- Creating shared multi-agent runtime ---"');
|
|
3529
|
-
appendShWriteCommands(vps, sharedNativeFileMap());
|
|
3530
|
-
vps.push('echo "--- Starting shared gateway via PM2 ---"');
|
|
3531
|
-
if (is9Router) {
|
|
3532
|
-
vps.push('pm2 start --name openclaw-multibot-9router -- sh -c "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
3533
|
-
vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3534
|
-
}
|
|
3535
|
-
vps.push('pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"');
|
|
3536
|
-
vps.push('pm2 save && pm2 startup');
|
|
3526
|
+
if (isMultiBot) {
|
|
3527
|
+
vps.push('echo "--- Creating shared multi-agent runtime ---"');
|
|
3528
|
+
appendShWriteCommands(vps, sharedNativeFileMap());
|
|
3529
|
+
vps.push('echo "--- Starting shared gateway via PM2 ---"');
|
|
3530
|
+
if (is9Router) {
|
|
3531
|
+
vps.push('pm2 start --name openclaw-multibot-9router -- sh -c "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
3532
|
+
vps.push('pm2 start --name openclaw-multibot-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3533
|
+
}
|
|
3534
|
+
vps.push('pm2 start --name openclaw-multibot -- sh -c "openclaw gateway run"');
|
|
3535
|
+
vps.push('pm2 save && pm2 startup');
|
|
3537
3536
|
vps.push(`echo ""`);
|
|
3538
3537
|
vps.push(`echo "=== ✅ Shared multi-bot gateway running via PM2 ==="`);
|
|
3539
3538
|
vps.push(`echo "Commands:"`);
|
|
3540
3539
|
vps.push(`echo " pm2 status # Status gateway"`);
|
|
3541
3540
|
vps.push(`echo " pm2 logs openclaw-multibot"`);
|
|
3542
|
-
} else {
|
|
3543
|
-
appendShWriteCommands(vps, botFiles(0));
|
|
3544
|
-
if (is9Router) {
|
|
3545
|
-
vps.push('pm2 start --name openclaw-9router -- sh -c "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
3546
|
-
vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3547
|
-
}
|
|
3548
|
-
vps.push('pm2 start --name openclaw -- sh -c "openclaw gateway run"');
|
|
3549
|
-
vps.push('pm2 save && pm2 startup');
|
|
3541
|
+
} else {
|
|
3542
|
+
appendShWriteCommands(vps, botFiles(0));
|
|
3543
|
+
if (is9Router) {
|
|
3544
|
+
vps.push('pm2 start --name openclaw-9router -- sh -c "9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update"');
|
|
3545
|
+
vps.push('pm2 start --name openclaw-9router-sync -- sh -c "node ./.openclaw/9router-smart-route-sync.js"');
|
|
3546
|
+
}
|
|
3547
|
+
vps.push('pm2 start --name openclaw -- sh -c "openclaw gateway run"');
|
|
3548
|
+
vps.push('pm2 save && pm2 startup');
|
|
3550
3549
|
vps.push('echo "Bot dang chay! Xem log: pm2 logs openclaw"');
|
|
3551
3550
|
}
|
|
3552
3551
|
scriptContent = vps.filter(Boolean).join('\n');
|
|
@@ -3554,20 +3553,20 @@ ${selectedSkillNames.length ? selectedSkillNames.join('\n') : '- _(No skills ins
|
|
|
3554
3553
|
// ─── Linux Desktop .SH ───────────────────────────────────────────────────
|
|
3555
3554
|
} else if (state.nativeOs === 'linux-desktop') {
|
|
3556
3555
|
scriptName = 'setup-openclaw-linux.sh';
|
|
3557
|
-
const lnx = [
|
|
3558
|
-
'#!/usr/bin/env bash', 'set -e',
|
|
3559
|
-
`echo "=== OpenClaw Setup — Linux Desktop${isMultiBot ? ' Multi-Bot' : ''} ==="`,
|
|
3560
|
-
'if ! command -v node > /dev/null 2>&1; then',
|
|
3561
|
-
' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
|
|
3562
|
-
' sudo apt-get install -y nodejs',
|
|
3563
|
-
'fi',
|
|
3564
|
-
'mkdir -p "$HOME/.local/bin"',
|
|
3565
|
-
'npm config set prefix "$HOME/.local"',
|
|
3566
|
-
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3567
|
-
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
|
|
3568
|
-
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3569
|
-
'npm install -g openclaw@latest',
|
|
3570
|
-
];
|
|
3556
|
+
const lnx = [
|
|
3557
|
+
'#!/usr/bin/env bash', 'set -e',
|
|
3558
|
+
`echo "=== OpenClaw Setup — Linux Desktop${isMultiBot ? ' Multi-Bot' : ''} ==="`,
|
|
3559
|
+
'if ! command -v node > /dev/null 2>&1; then',
|
|
3560
|
+
' curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -',
|
|
3561
|
+
' sudo apt-get install -y nodejs',
|
|
3562
|
+
'fi',
|
|
3563
|
+
'mkdir -p "$HOME/.local/bin"',
|
|
3564
|
+
'npm config set prefix "$HOME/.local"',
|
|
3565
|
+
'export PATH="$HOME/.local/bin:$PATH"',
|
|
3566
|
+
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.bashrc" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.bashrc"',
|
|
3567
|
+
'grep -Fqx \'export PATH="$HOME/.local/bin:$PATH"\' "$HOME/.profile" 2>/dev/null || echo \'export PATH="$HOME/.local/bin:$PATH"\' >> "$HOME/.profile"',
|
|
3568
|
+
'npm install -g openclaw@latest',
|
|
3569
|
+
];
|
|
3571
3570
|
providerLines(lnx, 'sh');
|
|
3572
3571
|
if (pluginCmd) lnx.push(pluginCmd);
|
|
3573
3572
|
|
|
@@ -3718,11 +3717,11 @@ Write-Host " 🎉 ${isVi ? 'Setup hoàn tất!' : 'Setup complete!'}" -Foregrou
|
|
|
3718
3717
|
if (is9Router) {
|
|
3719
3718
|
ps += `Write-Host " ${isVi ? 'Mở http://localhost:30128/dashboard để login OAuth' : 'Open http://localhost:30128/dashboard to login OAuth'}" -ForegroundColor White\n`;
|
|
3720
3719
|
}
|
|
3721
|
-
if (state.channel === 'zalo-personal') {
|
|
3722
|
-
ps += `Write-Host " ${isVi ? 'Chạy: docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose' : 'Run: docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose'}" -ForegroundColor White\n`;
|
|
3723
|
-
ps += `Write-Host " ${isVi ? 'QR sẽ nằm tại /tmp/openclaw/openclaw-zalouser-qr-default.png' : 'QR will be written to /tmp/openclaw/openclaw-zalouser-qr-default.png'}" -ForegroundColor DarkGray\n`;
|
|
3724
|
-
ps += `Write-Host " ${isVi ? 'Copy QR ra ngoài: docker compose cp ai-bot:/tmp/openclaw/openclaw-zalouser-qr-default.png ./zalo-login-qr.png' : 'Copy the QR out: docker compose cp ai-bot:/tmp/openclaw/openclaw-zalouser-qr-default.png ./zalo-login-qr.png'}" -ForegroundColor DarkGray\n`;
|
|
3725
|
-
}
|
|
3720
|
+
if (state.channel === 'zalo-personal') {
|
|
3721
|
+
ps += `Write-Host " ${isVi ? 'Chạy: docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose' : 'Run: docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose'}" -ForegroundColor White\n`;
|
|
3722
|
+
ps += `Write-Host " ${isVi ? 'QR sẽ nằm tại /tmp/openclaw/openclaw-zalouser-qr-default.png' : 'QR will be written to /tmp/openclaw/openclaw-zalouser-qr-default.png'}" -ForegroundColor DarkGray\n`;
|
|
3723
|
+
ps += `Write-Host " ${isVi ? 'Copy QR ra ngoài: docker compose cp ai-bot:/tmp/openclaw/openclaw-zalouser-qr-default.png ./zalo-login-qr.png' : 'Copy the QR out: docker compose cp ai-bot:/tmp/openclaw/openclaw-zalouser-qr-default.png ./zalo-login-qr.png'}" -ForegroundColor DarkGray\n`;
|
|
3724
|
+
}
|
|
3726
3725
|
|
|
3727
3726
|
ps += `Write-Host ""
|
|
3728
3727
|
} catch {
|
|
@@ -3895,40 +3894,40 @@ echo ""
|
|
|
3895
3894
|
}
|
|
3896
3895
|
|
|
3897
3896
|
|
|
3898
|
-
// ========== Zalo Personal Login Guide (post-setup) ==========
|
|
3899
|
-
|
|
3900
|
-
function generateZaloOnboardGuide() {
|
|
3901
|
-
const lang = document.getElementById('cfg-language')?.value || 'vi';
|
|
3902
|
-
setOutput('out-zalo-onboard-cmd', `docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose`);
|
|
3903
|
-
|
|
3904
|
-
if (lang === 'vi') {
|
|
3905
|
-
setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
|
|
3906
|
-
│ Chạy lệnh bên trái để OpenClaw tạo QR đăng nhập. │
|
|
3907
|
-
├─────────────────────────────────────────────────────┤
|
|
3908
|
-
│ 1. Đảm bảo container/gateway đã chạy xong. │
|
|
3909
|
-
│ 2. Chạy lệnh login để tạo QR cho zalouser. │
|
|
3910
|
-
│ 3. OpenClaw sẽ in ra đường dẫn file QR trong /tmp. │
|
|
3911
|
-
│ 4. Copy file QR ra ngoài nếu cần: │
|
|
3912
|
-
│ docker compose cp ai-bot:/tmp/openclaw/ │
|
|
3913
|
-
│ openclaw-zalouser-qr-default.png . │
|
|
3914
|
-
│ 5. Mở ảnh QR → quét bằng app Zalo → xác nhận. │
|
|
3915
|
-
│ 6. Sau khi login xong, restart bot nếu cần. │
|
|
3916
|
-
└─────────────────────────────────────────────────────┘`);
|
|
3917
|
-
} else {
|
|
3918
|
-
setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
|
|
3919
|
-
│ Run the command on the left to generate a Zalo QR. │
|
|
3920
|
-
├─────────────────────────────────────────────────────┤
|
|
3921
|
-
│ 1. Make sure the container/gateway is already up. │
|
|
3922
|
-
│ 2. Run the login command for zalouser. │
|
|
3923
|
-
│ 3. OpenClaw prints the QR image path under /tmp. │
|
|
3924
|
-
│ 4. Copy the QR out if needed: │
|
|
3925
|
-
│ docker compose cp ai-bot:/tmp/openclaw/ │
|
|
3926
|
-
│ openclaw-zalouser-qr-default.png . │
|
|
3927
|
-
│ 5. Open the image → scan with Zalo mobile app. │
|
|
3928
|
-
│ 6. Restart the bot afterwards if needed. │
|
|
3929
|
-
└─────────────────────────────────────────────────────┘`);
|
|
3930
|
-
}
|
|
3931
|
-
}
|
|
3897
|
+
// ========== Zalo Personal Login Guide (post-setup) ==========
|
|
3898
|
+
|
|
3899
|
+
function generateZaloOnboardGuide() {
|
|
3900
|
+
const lang = document.getElementById('cfg-language')?.value || 'vi';
|
|
3901
|
+
setOutput('out-zalo-onboard-cmd', `docker compose exec -it ai-bot openclaw channels login --channel zalouser --verbose`);
|
|
3902
|
+
|
|
3903
|
+
if (lang === 'vi') {
|
|
3904
|
+
setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
|
|
3905
|
+
│ Chạy lệnh bên trái để OpenClaw tạo QR đăng nhập. │
|
|
3906
|
+
├─────────────────────────────────────────────────────┤
|
|
3907
|
+
│ 1. Đảm bảo container/gateway đã chạy xong. │
|
|
3908
|
+
│ 2. Chạy lệnh login để tạo QR cho zalouser. │
|
|
3909
|
+
│ 3. OpenClaw sẽ in ra đường dẫn file QR trong /tmp. │
|
|
3910
|
+
│ 4. Copy file QR ra ngoài nếu cần: │
|
|
3911
|
+
│ docker compose cp ai-bot:/tmp/openclaw/ │
|
|
3912
|
+
│ openclaw-zalouser-qr-default.png . │
|
|
3913
|
+
│ 5. Mở ảnh QR → quét bằng app Zalo → xác nhận. │
|
|
3914
|
+
│ 6. Sau khi login xong, restart bot nếu cần. │
|
|
3915
|
+
└─────────────────────────────────────────────────────┘`);
|
|
3916
|
+
} else {
|
|
3917
|
+
setOutput('out-zalo-onboard-guide', `┌─────────────────────────────────────────────────────┐
|
|
3918
|
+
│ Run the command on the left to generate a Zalo QR. │
|
|
3919
|
+
├─────────────────────────────────────────────────────┤
|
|
3920
|
+
│ 1. Make sure the container/gateway is already up. │
|
|
3921
|
+
│ 2. Run the login command for zalouser. │
|
|
3922
|
+
│ 3. OpenClaw prints the QR image path under /tmp. │
|
|
3923
|
+
│ 4. Copy the QR out if needed: │
|
|
3924
|
+
│ docker compose cp ai-bot:/tmp/openclaw/ │
|
|
3925
|
+
│ openclaw-zalouser-qr-default.png . │
|
|
3926
|
+
│ 5. Open the image → scan with Zalo mobile app. │
|
|
3927
|
+
│ 6. Restart the bot afterwards if needed. │
|
|
3928
|
+
└─────────────────────────────────────────────────────┘`);
|
|
3929
|
+
}
|
|
3930
|
+
}
|
|
3932
3931
|
|
|
3933
3932
|
function setOutput(id, text) {
|
|
3934
3933
|
const el = document.getElementById(id);
|