create-openclaw-bot 4.0.9 → 4.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +171 -144
- package/CHANGELOG.vi.md +165 -139
- package/README.md +7 -7
- package/README.vi.md +7 -7
- package/cli.js +522 -592
- package/package.json +28 -28
- package/setup.js +74 -41
package/package.json
CHANGED
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "create-openclaw-bot",
|
|
3
|
-
"version": "4.0
|
|
4
|
-
"description": "Interactive CLI installer for OpenClaw Bot",
|
|
5
|
-
"main": "cli.js",
|
|
6
|
-
"bin": {
|
|
7
|
-
"create-openclaw-bot": "./cli.js"
|
|
8
|
-
},
|
|
9
|
-
"scripts": {
|
|
10
|
-
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
-
},
|
|
12
|
-
"keywords": [
|
|
13
|
-
"openclaw",
|
|
14
|
-
"cli",
|
|
15
|
-
"bot",
|
|
16
|
-
"zalo",
|
|
17
|
-
"telegram",
|
|
18
|
-
"ai"
|
|
19
|
-
],
|
|
20
|
-
"author": "tuanminhhole",
|
|
21
|
-
"license": "MIT",
|
|
22
|
-
"dependencies": {
|
|
23
|
-
"@inquirer/prompts": "^4.3.1",
|
|
24
|
-
"chalk": "^5.3.0",
|
|
25
|
-
"fs-extra": "^11.2.0"
|
|
26
|
-
},
|
|
27
|
-
"type": "module"
|
|
28
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "create-openclaw-bot",
|
|
3
|
+
"version": "4.1.0",
|
|
4
|
+
"description": "Interactive CLI installer for OpenClaw Bot",
|
|
5
|
+
"main": "cli.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"create-openclaw-bot": "./cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"openclaw",
|
|
14
|
+
"cli",
|
|
15
|
+
"bot",
|
|
16
|
+
"zalo",
|
|
17
|
+
"telegram",
|
|
18
|
+
"ai"
|
|
19
|
+
],
|
|
20
|
+
"author": "tuanminhhole",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"dependencies": {
|
|
23
|
+
"@inquirer/prompts": "^4.3.1",
|
|
24
|
+
"chalk": "^5.3.0",
|
|
25
|
+
"fs-extra": "^11.2.0"
|
|
26
|
+
},
|
|
27
|
+
"type": "module"
|
|
28
|
+
}
|
package/setup.js
CHANGED
|
@@ -345,7 +345,7 @@
|
|
|
345
345
|
- ✅ Chỉ mount đúng thư mục cần thiết (config + workspace)
|
|
346
346
|
- ❌ KHÔNG mount nguyên ổ đĩa (C:/ hoặc D:/)
|
|
347
347
|
- ❌ KHÔNG chạy container với --privileged
|
|
348
|
-
- ✅ Giới hạn port expose (chỉ
|
|
348
|
+
- ✅ Giới hạn port expose (chỉ 38789)`,
|
|
349
349
|
en: `## 🔐 Security Rules — MANDATORY
|
|
350
350
|
|
|
351
351
|
### System files & directories
|
|
@@ -372,7 +372,7 @@
|
|
|
372
372
|
- ✅ Only mount required directories (config + workspace)
|
|
373
373
|
- ❌ DO NOT mount entire drives (C:/ or D:/)
|
|
374
374
|
- ❌ DO NOT run containers with --privileged
|
|
375
|
-
- ✅ Limit exposed ports (only
|
|
375
|
+
- ✅ Limit exposed ports (only 38789)`,
|
|
376
376
|
};
|
|
377
377
|
|
|
378
378
|
// ========== DOM Ready ==========
|
|
@@ -762,8 +762,8 @@
|
|
|
762
762
|
// 9Router: simple message (no API key needed - managed via dashboard)
|
|
763
763
|
pHtml += `<p style="font-size: 13px; color: var(--text-secondary); margin: 0 0 8px;">
|
|
764
764
|
${isVi
|
|
765
|
-
? 'Sau khi Docker khởi động xong, mở <a href="http://localhost:
|
|
766
|
-
: 'After Docker starts, open <a href="http://localhost:
|
|
765
|
+
? 'Sau khi Docker khởi động xong, mở <a href="http://localhost:30128/dashboard" target="_blank" style="color: var(--accent);">localhost:30128/dashboard</a> để đăng nhập OAuth và kết nối các Provider.'
|
|
766
|
+
: 'After Docker starts, open <a href="http://localhost:30128/dashboard" target="_blank" style="color: var(--accent);">localhost:30128/dashboard</a> to OAuth login and connect Providers.'}
|
|
767
767
|
</p>`;
|
|
768
768
|
pHtml += `<p style="font-size: 12px; color: var(--text-muted); margin: 0;">
|
|
769
769
|
${isVi
|
|
@@ -1039,29 +1039,7 @@ Write-Host "Chrome se tu dong bat Debug Mode moi khi ban dang nhap Windows (dela
|
|
|
1039
1039
|
apiKey: 'sk-no-key',
|
|
1040
1040
|
api: 'openai-completions',
|
|
1041
1041
|
models: [
|
|
1042
|
-
|
|
1043
|
-
{ id: 'if/qwen3-coder-plus', name: 'Qwen3 Coder+ (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1044
|
-
{ id: 'if/kimi-k2', name: 'Kimi K2 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1045
|
-
{ id: 'if/kimi-k2-thinking', name: 'Kimi K2 Thinking (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1046
|
-
{ id: 'if/glm-4.7', name: 'GLM 4.7 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1047
|
-
{ id: 'if/minimax-m2', name: 'MiniMax M2 (iFlow FREE)', contextWindow: 1000000, maxTokens: 8192 },
|
|
1048
|
-
{ id: 'if/deepseek-r1', name: 'DeepSeek R1 (iFlow FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1049
|
-
{ id: 'qw/qwen3-coder-plus', name: 'Qwen3 Coder+ (Qwen FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1050
|
-
{ id: 'qw/qwen3-coder-flash', name: 'Qwen3 Coder Flash (Qwen FREE)', contextWindow: 128000, maxTokens: 8192 },
|
|
1051
|
-
{ id: 'kr/claude-sonnet-4.5', name: 'Claude Sonnet 4.5 (Kiro FREE)', contextWindow: 200000, maxTokens: 8192 },
|
|
1052
|
-
{ id: 'kr/claude-haiku-4.5', name: 'Claude Haiku 4.5 (Kiro FREE)', contextWindow: 200000, maxTokens: 8192 },
|
|
1053
|
-
// Ollama Cloud
|
|
1054
|
-
{ id: 'ollama/qwen3.5', name: 'Qwen 3.5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
|
|
1055
|
-
{ id: 'ollama/kimi-k2.5', name: 'Kimi K2.5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
|
|
1056
|
-
{ id: 'ollama/glm-5', name: 'GLM 5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
|
|
1057
|
-
{ id: 'ollama/glm-4.7-flash', name: 'GLM 4.7 Flash (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
|
|
1058
|
-
{ id: 'ollama/minimax-m2.5', name: 'MiniMax M2.5 (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
|
|
1059
|
-
{ id: 'ollama/gpt-oss:120b', name: 'GPT-OSS 120B (Ollama Cloud)', contextWindow: 128000, maxTokens: 8192 },
|
|
1060
|
-
// API Key Providers
|
|
1061
|
-
{ id: 'glm/glm-4.7', name: 'GLM 4.7 ($0.6/1M)', contextWindow: 128000, maxTokens: 8192 },
|
|
1062
|
-
{ id: 'minimax/MiniMax-M2.1', name: 'MiniMax M2.1 ($0.20/1M)', contextWindow: 1000000, maxTokens: 8192 },
|
|
1063
|
-
{ id: 'kimi/kimi-latest', name: 'Kimi Latest ($0.90/1M)', contextWindow: 128000, maxTokens: 8192 },
|
|
1064
|
-
{ id: 'deepseek/deepseek-chat', name: 'DeepSeek V3.2 Chat', contextWindow: 128000, maxTokens: 8192 },
|
|
1042
|
+
{ id: 'smart-route', name: 'Smart Proxy (Auto Route)', contextWindow: 200000, maxTokens: 8192 }
|
|
1065
1043
|
],
|
|
1066
1044
|
},
|
|
1067
1045
|
},
|
|
@@ -1184,7 +1162,42 @@ ${finalCmd}`;
|
|
|
1184
1162
|
// ─── Dynamic Smart Route Sync Script ────────────────────────────────────────
|
|
1185
1163
|
// Background loop inside 9Router container every 30s.
|
|
1186
1164
|
// Queries /api/providers → filters connected+enabled → updates smart-route combo.
|
|
1187
|
-
const syncScript =
|
|
1165
|
+
const syncScript = `const fs=require('fs');const ROUTER='http://localhost:20128';const INTERVAL=30000;const p='/root/.9router/db.json';
|
|
1166
|
+
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/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']};
|
|
1167
|
+
console.log('[sync-combo] 9Router sync loop started...');
|
|
1168
|
+
const sync = async () => {
|
|
1169
|
+
try {
|
|
1170
|
+
const res = await fetch(ROUTER + '/api/providers');
|
|
1171
|
+
const d = await res.json();
|
|
1172
|
+
const a = (d.connections || []).filter(c=>(c.isActive !== false && !c.disabled) && (c.isActive || c.connected > 0 || c.tokens?.length > 0)).map(c=>c.provider);
|
|
1173
|
+
if (!a.length) return;
|
|
1174
|
+
|
|
1175
|
+
const PREF = ['openai','anthropic','claude-code','codex','cursor','github','cline','kimi','minimax','deepseek','glm','alicode','xai','mistral','kilo','kiro','iflow','qwen','gemini-cli','ollama'];
|
|
1176
|
+
a.sort((x, y) => (PREF.indexOf(x) === -1 ? 99 : PREF.indexOf(x)) - (PREF.indexOf(y) === -1 ? 99 : PREF.indexOf(y)));
|
|
1177
|
+
|
|
1178
|
+
const m = a.flatMap(p => PM[p] || []);
|
|
1179
|
+
if (!m.length) return;
|
|
1180
|
+
let db = {};
|
|
1181
|
+
try { db = JSON.parse(fs.readFileSync(p, 'utf8')); } catch(e){}
|
|
1182
|
+
if (!db.combos) db.combos = [];
|
|
1183
|
+
|
|
1184
|
+
const c = { id: 'smart-route', name: 'smart-route', alias: 'smart-route', models: m };
|
|
1185
|
+
const i = db.combos.findIndex(x => x.id === 'smart-route');
|
|
1186
|
+
if (i >= 0) {
|
|
1187
|
+
if (JSON.stringify(db.combos[i].models) !== JSON.stringify(c.models)) {
|
|
1188
|
+
db.combos[i] = c;
|
|
1189
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
1190
|
+
console.log('[sync-combo] Updated smart-route: ' + c.models.length + ' models');
|
|
1191
|
+
}
|
|
1192
|
+
} else {
|
|
1193
|
+
db.combos.push(c);
|
|
1194
|
+
fs.writeFileSync(p, JSON.stringify(db, null, 2));
|
|
1195
|
+
console.log('[sync-combo] Created smart-route: ' + c.models.length + ' models');
|
|
1196
|
+
}
|
|
1197
|
+
} catch (e) { }
|
|
1198
|
+
};
|
|
1199
|
+
sync();
|
|
1200
|
+
setInterval(sync, INTERVAL);`;
|
|
1188
1201
|
|
|
1189
1202
|
let compose;
|
|
1190
1203
|
if (is9Router) {
|
|
@@ -1201,14 +1214,22 @@ ${extraHostsBlock}
|
|
|
1201
1214
|
volumes:
|
|
1202
1215
|
- ../../.openclaw:/root/.openclaw
|
|
1203
1216
|
ports:
|
|
1204
|
-
- "
|
|
1217
|
+
- "38789:38789"
|
|
1205
1218
|
|
|
1206
1219
|
9router:
|
|
1207
1220
|
image: node:22-slim
|
|
1208
1221
|
container_name: 9router
|
|
1209
1222
|
restart: always
|
|
1210
|
-
entrypoint:
|
|
1211
|
-
|
|
1223
|
+
entrypoint:
|
|
1224
|
+
- /bin/sh
|
|
1225
|
+
- -c
|
|
1226
|
+
- |
|
|
1227
|
+
npm install -g 9router
|
|
1228
|
+
cat << 'CLAWEOF' > /tmp/sync.js
|
|
1229
|
+
${syncScript.replace(/\$/g, '$$$$').replace(/\n/g, '\n ')}
|
|
1230
|
+
CLAWEOF
|
|
1231
|
+
node /tmp/sync.js > /tmp/sync.log 2>&1 &
|
|
1232
|
+
exec 9router -n -t -l -H 0.0.0.0 -p 20128 --skip-update
|
|
1212
1233
|
environment:
|
|
1213
1234
|
- PORT=20128
|
|
1214
1235
|
- HOSTNAME=0.0.0.0
|
|
@@ -1232,7 +1253,7 @@ ${extraHostsBlock}
|
|
|
1232
1253
|
volumes:
|
|
1233
1254
|
- ../../.openclaw:/root/.openclaw
|
|
1234
1255
|
ports:
|
|
1235
|
-
- "
|
|
1256
|
+
- "38789:38789"`;
|
|
1236
1257
|
}
|
|
1237
1258
|
|
|
1238
1259
|
setOutput('out-compose', compose);
|
|
@@ -1797,8 +1818,10 @@ New-Item -ItemType Directory -Force -Path "$projectDir" | Out-Null
|
|
|
1797
1818
|
|
|
1798
1819
|
Object.entries(files).forEach(([path, content]) => {
|
|
1799
1820
|
const winPath = path.replace(/\//g, '\\');
|
|
1800
|
-
//
|
|
1801
|
-
const safeContent = content
|
|
1821
|
+
// Fix: escape any "'@" at start of line — would prematurely terminate PowerShell here-string
|
|
1822
|
+
const safeContent = content
|
|
1823
|
+
.replace(/\r\n/g, '\n')
|
|
1824
|
+
.replace(/^'@/mg, "'`@"); // escape with backtick so PS here-string doesn't terminate early
|
|
1802
1825
|
ps += `\n[IO.File]::WriteAllText("$projectDir\\${winPath}", @'\n${safeContent}\n'@, $utf8)\n`;
|
|
1803
1826
|
});
|
|
1804
1827
|
|
|
@@ -1831,7 +1854,7 @@ Write-Host " 🎉 ${isVi ? 'Setup hoàn tất!' : 'Setup complete!'}" -Foregrou
|
|
|
1831
1854
|
// Post-setup notes
|
|
1832
1855
|
const is9Router = state.config.provider === '9router';
|
|
1833
1856
|
if (is9Router) {
|
|
1834
|
-
ps += `Write-Host " ${isVi ? 'Mở http://localhost:
|
|
1857
|
+
ps += `Write-Host " ${isVi ? 'Mở http://localhost:30128/dashboard để login OAuth' : 'Open http://localhost:30128/dashboard to login OAuth'}" -ForegroundColor White\n`;
|
|
1835
1858
|
}
|
|
1836
1859
|
if (state.channel === 'zalo-personal') {
|
|
1837
1860
|
ps += `Write-Host " ${isVi ? 'Chạy: docker exec -it openclaw-bot openclaw onboard (quét QR)' : 'Run: docker exec -it openclaw-bot openclaw onboard (scan QR)'}" -ForegroundColor White\n`;
|
|
@@ -1846,13 +1869,22 @@ Write-Host " 🎉 ${isVi ? 'Setup hoàn tất!' : 'Setup complete!'}" -Foregrou
|
|
|
1846
1869
|
Read-Host "${isVi ? 'Nhấn Enter để thoát' : 'Press Enter to exit'}"
|
|
1847
1870
|
`;
|
|
1848
1871
|
|
|
1849
|
-
// Wrap in
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1872
|
+
// Wrap in a .bat that extracts the PS section to a temp .ps1 then runs it.
|
|
1873
|
+
// This avoids 2 issues:
|
|
1874
|
+
// 1. powershell -File refuses .bat extension (hard error, immediate exit)
|
|
1875
|
+
// 2. Zone.Identifier security block on downloaded files affects -File but not -Command
|
|
1876
|
+
// The extraction command uses NO pipes (CMD treats | as special inside ""), and uses
|
|
1877
|
+
// $env:OPENCLAW_SELF / $env:OPENCLAW_TMP to avoid CMD quote issues with paths.
|
|
1878
|
+
const bat = `@echo off
|
|
1879
|
+
chcp 65001>nul
|
|
1880
|
+
set "OPENCLAW_SELF=%~f0"
|
|
1881
|
+
set "OPENCLAW_TMP=%TEMP%\\openclaw_%RANDOM%.ps1"
|
|
1882
|
+
powershell -ep bypass -nop -c "$l=(Select-String -Path $env:OPENCLAW_SELF -Pattern '^:PS_BEGIN$').LineNumber;$a=[io.file]::ReadAllLines($env:OPENCLAW_SELF,[text.encoding]::UTF8);[io.file]::WriteAllText($env:OPENCLAW_TMP,($a[$l..($a.Length-1)] -join \\"\`n\\"),[text.encoding]::UTF8)"
|
|
1883
|
+
powershell -ep bypass -nop -File "%OPENCLAW_TMP%"
|
|
1853
1884
|
if %errorlevel% neq 0 pause
|
|
1885
|
+
del "%OPENCLAW_TMP%" 2>nul
|
|
1854
1886
|
exit /b
|
|
1855
|
-
|
|
1887
|
+
:PS_BEGIN
|
|
1856
1888
|
${ps}`;
|
|
1857
1889
|
|
|
1858
1890
|
return bat;
|
|
@@ -1863,7 +1895,8 @@ ${ps}`;
|
|
|
1863
1895
|
// Regenerate output first to ensure state._generatedFiles is current
|
|
1864
1896
|
generateOutput();
|
|
1865
1897
|
const content = generateAutoSetupBat();
|
|
1866
|
-
const
|
|
1898
|
+
const winContent = content.replace(/\r\n/g, '\n').replace(/\n/g, '\r\n');
|
|
1899
|
+
const blob = new Blob([winContent], { type: 'application/x-bat;charset=utf-8' });
|
|
1867
1900
|
const url = URL.createObjectURL(blob);
|
|
1868
1901
|
const a = document.createElement('a');
|
|
1869
1902
|
a.href = url;
|