create-openclaw-bot 4.0.2 → 4.0.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/cli.js +78 -32
- package/package.json +1 -1
package/cli.js
CHANGED
|
@@ -7,10 +7,12 @@ import chalk from 'chalk';
|
|
|
7
7
|
import { spawn } from 'child_process';
|
|
8
8
|
|
|
9
9
|
const LOGO = `
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
10
|
+
████████╗██╗ ██╗ █████╗ ███╗ ██╗███╗ ███╗██╗███╗ ██╗██╗ ██╗██╗ ██╗ ██████╗ ██╗ ███████╗
|
|
11
|
+
╚══██╔══╝██║ ██║██╔══██╗████╗ ██║████╗ ████║██║████╗ ██║██║ ██║██║ ██║██╔═══██╗██║ ██╔════╝
|
|
12
|
+
██║ ██║ ██║███████║██╔██╗ ██║██╔████╔██║██║██╔██╗ ██║███████║███████║██║ ██║██║ █████╗
|
|
13
|
+
██║ ██║ ██║██╔══██║██║╚██╗██║██║╚██╔╝██║██║██║╚██╗██║██╔══██║██╔══██║██║ ██║██║ ██╔══╝
|
|
14
|
+
██║ ╚██████╔╝██║ ██║██║ ╚████║██║ ╚═╝ ██║██║██║ ╚████║██║ ██║██║ ██║╚██████╔╝███████╗███████╗
|
|
15
|
+
╚═╝ ╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝
|
|
14
16
|
`;
|
|
15
17
|
|
|
16
18
|
const CHANNELS = {
|
|
@@ -20,7 +22,7 @@ const CHANNELS = {
|
|
|
20
22
|
};
|
|
21
23
|
|
|
22
24
|
const PROVIDERS = {
|
|
23
|
-
'
|
|
25
|
+
'9router': { name: '9Router Proxy (Khuyên dùng)', icon: '🔀', isProxy: true },
|
|
24
26
|
'openai': { name: 'OpenAI (ChatGPT)', icon: '🧠', envKey: 'OPENAI_API_KEY' },
|
|
25
27
|
'ollama': { name: 'Local Ollama', icon: '🏠', isLocal: true },
|
|
26
28
|
'google': { name: 'Google (Gemini)', icon: '⚡', envKey: 'GEMINI_API_KEY' },
|
|
@@ -66,6 +68,7 @@ async function main() {
|
|
|
66
68
|
});
|
|
67
69
|
}
|
|
68
70
|
|
|
71
|
+
|
|
69
72
|
// 3. Provider
|
|
70
73
|
const providerKey = await select({
|
|
71
74
|
message: isVi ? 'Chọn AI Provider:' : 'Select AI Provider:',
|
|
@@ -142,26 +145,30 @@ async function main() {
|
|
|
142
145
|
}
|
|
143
146
|
await fs.writeFile(path.join(projectDir, 'docker', 'openclaw', '.env'), envContent);
|
|
144
147
|
|
|
148
|
+
const patchScript = `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'});c.gateway=Object.assign({},c.gateway,{port:18791,bind:'custom',customBindHost:'0.0.0.0'});fs.writeFileSync(p,JSON.stringify(c,null,2));}`;
|
|
149
|
+
const b64Patch = Buffer.from(patchScript).toString('base64');
|
|
145
150
|
const dockerfile = `FROM node:22-slim
|
|
146
151
|
|
|
147
152
|
RUN apt-get update && apt-get install -y git curl${selectedSkills.includes('browser') ? ' socat' : ''} && rm -rf /var/lib/apt/lists/*
|
|
148
153
|
|
|
149
154
|
RUN npm install -g openclaw@latest
|
|
150
|
-
${selectedSkills.includes('browser') ? 'RUN npm install -g agent-browser playwright && npx playwright install chromium --with-deps && ln -f -s /root/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /usr/bin/google-chrome\\n' : ''}
|
|
151
|
-
WORKDIR /root/.openclaw
|
|
155
|
+
${selectedSkills.includes('browser') ? 'RUN npm install -g agent-browser playwright && npx playwright install chromium --with-deps && ln -f -s /root/.cache/ms-playwright/chromium-*/chrome-linux*/chrome /usr/bin/google-chrome\\n' : ''}WORKDIR /root/.openclaw
|
|
152
156
|
|
|
153
157
|
EXPOSE 18791
|
|
154
158
|
|
|
155
|
-
CMD sh -c "node -e
|
|
159
|
+
CMD sh -c "node -e \\"eval(Buffer.from('${b64Patch}','base64').toString())\\" && ${selectedSkills.includes('browser') ? 'socat TCP-LISTEN:9222,fork,reuseaddr TCP:host.docker.internal:9222 & ' : ''}(sleep 5 && openclaw devices approve --latest 2>/dev/null || true) & openclaw gateway run"`;
|
|
156
160
|
|
|
157
161
|
await fs.writeFile(path.join(projectDir, 'docker', 'openclaw', 'Dockerfile'), dockerfile);
|
|
158
162
|
|
|
163
|
+
const agentId = botName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-$/, '') || 'chat';
|
|
164
|
+
|
|
159
165
|
let compose = '';
|
|
160
166
|
if (providerKey === '9router') {
|
|
161
|
-
compose = `
|
|
167
|
+
compose = `name: oc-${agentId}
|
|
168
|
+
services:
|
|
162
169
|
ai-bot:
|
|
163
170
|
build: .
|
|
164
|
-
container_name: openclaw
|
|
171
|
+
container_name: openclaw-${agentId}
|
|
165
172
|
restart: always
|
|
166
173
|
env_file:
|
|
167
174
|
- .env
|
|
@@ -171,8 +178,6 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
171
178
|
- "host.docker.internal:host-gateway"
|
|
172
179
|
` : ''} volumes:
|
|
173
180
|
- ../../.openclaw:/root/.openclaw
|
|
174
|
-
ports:
|
|
175
|
-
- "18789:18789"
|
|
176
181
|
|
|
177
182
|
9router:
|
|
178
183
|
image: node:22-slim
|
|
@@ -181,15 +186,22 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
181
186
|
entrypoint: >
|
|
182
187
|
/bin/sh -c "npm install -g 9router && [ ! -f /root/.9router/db.json ] && echo '{\\"combos\\":[{\\"id\\":\\"smart-route\\",\\"name\\":\\"smart-route\\",\\"alias\\":\\"smart-route\\",\\"models\\":[\\"cx/gpt-5.4\\",\\"ag/claude-opus-4-6-thinking\\",\\"cc/claude-opus-4-6\\",\\"gh/gpt-5.4\\",\\"ag/gemini-3.1-pro-high\\",\\"cc/claude-sonnet-4-6\\",\\"gh/claude-opus-4.6\\"]}]}' > /root/.9router/db.json; 9router"
|
|
183
188
|
environment:
|
|
184
|
-
-
|
|
185
|
-
-
|
|
189
|
+
- PORT=20128
|
|
190
|
+
- HOSTNAME=0.0.0.0
|
|
191
|
+
- CI=true
|
|
186
192
|
volumes:
|
|
187
|
-
-
|
|
193
|
+
- 9router-data:/root/.9router
|
|
194
|
+
ports:
|
|
195
|
+
- "20128:20128"
|
|
196
|
+
|
|
197
|
+
volumes:
|
|
198
|
+
9router-data:`;
|
|
188
199
|
} else {
|
|
189
|
-
compose = `
|
|
200
|
+
compose = `name: oc-${agentId}
|
|
201
|
+
services:
|
|
190
202
|
ai-bot:
|
|
191
203
|
build: .
|
|
192
|
-
container_name: openclaw
|
|
204
|
+
container_name: openclaw-${agentId}
|
|
193
205
|
restart: always
|
|
194
206
|
env_file: .env
|
|
195
207
|
${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
@@ -200,26 +212,38 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
200
212
|
|
|
201
213
|
await fs.writeFile(path.join(projectDir, 'docker', 'openclaw', 'docker-compose.yml'), compose);
|
|
202
214
|
|
|
203
|
-
|
|
215
|
+
let authProfilesJson = {};
|
|
204
216
|
if (providerKey && !provider.isLocal) {
|
|
205
|
-
const authProviderName = providerKey === '9router' ? '9router' : 'openai';
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
217
|
+
const authProviderName = providerKey === '9router' ? '9router' : 'openai'; // fallback to openai format for standard providers initially
|
|
218
|
+
const authProfileId = providerKey === '9router' ? '9router-proxy' : `${authProviderName}:default`;
|
|
219
|
+
const authKeyValue = providerKey === '9router' ? 'sk-no-key' : providerKeyVal;
|
|
220
|
+
|
|
221
|
+
authProfilesJson = {
|
|
222
|
+
version: 1,
|
|
223
|
+
profiles: {
|
|
224
|
+
[authProfileId]: {
|
|
225
|
+
provider: authProviderName,
|
|
226
|
+
type: 'api_key',
|
|
227
|
+
key: authKeyValue,
|
|
228
|
+
},
|
|
229
|
+
},
|
|
230
|
+
order: {
|
|
231
|
+
[authProviderName]: [authProfileId],
|
|
232
|
+
},
|
|
233
|
+
};
|
|
234
|
+
|
|
212
235
|
if (providerKey !== '9router' && providerKey !== 'openai' && provider.baseURL) {
|
|
213
|
-
|
|
236
|
+
authProfilesJson.profiles[authProfileId].url = provider.baseURL;
|
|
214
237
|
}
|
|
215
238
|
}
|
|
216
239
|
|
|
217
|
-
const agentId = botName.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/-$/, '') || 'chat';
|
|
218
240
|
const modelsPrimary = providerKey === '9router' ? '9router/smart-route' : (providerKey === 'google' ? 'google/gemini-2.5-flash' : 'openai/gpt-4o');
|
|
219
241
|
|
|
220
242
|
await fs.ensureDir(path.join(projectDir, '.openclaw', 'agents', agentId, 'agent'));
|
|
221
|
-
|
|
222
|
-
|
|
243
|
+
if (Object.keys(authProfilesJson).length > 0) {
|
|
244
|
+
await fs.writeJson(path.join(projectDir, '.openclaw', 'auth-profiles.json'), authProfilesJson, { spaces: 2 });
|
|
245
|
+
await fs.writeJson(path.join(projectDir, '.openclaw', 'agents', agentId, 'agent', 'auth-profiles.json'), authProfilesJson, { spaces: 2 });
|
|
246
|
+
}
|
|
223
247
|
|
|
224
248
|
const botConfig = {
|
|
225
249
|
meta: { lastTouchedVersion: '2026.3.24' },
|
|
@@ -233,11 +257,26 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
233
257
|
model: { primary: modelsPrimary, fallbacks: [] }
|
|
234
258
|
}]
|
|
235
259
|
},
|
|
260
|
+
...(providerKey === '9router' ? {
|
|
261
|
+
models: {
|
|
262
|
+
mode: 'merge',
|
|
263
|
+
providers: {
|
|
264
|
+
'9router': {
|
|
265
|
+
baseUrl: 'http://9router:20128/v1',
|
|
266
|
+
apiKey: 'sk-no-key',
|
|
267
|
+
api: 'openai-completions',
|
|
268
|
+
models: [
|
|
269
|
+
{ id: 'smart-route', name: 'Smart Proxy (Auto Route)', contextWindow: 200000, maxTokens: 8192 }
|
|
270
|
+
]
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
} : {}),
|
|
236
275
|
commands: { native: 'auto', nativeSkills: 'auto', restart: true, ownerDisplay: 'raw' },
|
|
237
276
|
channels: {},
|
|
238
277
|
tools: { profile: 'full' },
|
|
239
278
|
gateway: {
|
|
240
|
-
port: 18791, mode: 'local', bind: '0.0.0.0',
|
|
279
|
+
port: 18791, mode: 'local', bind: 'custom', customBindHost: '0.0.0.0',
|
|
241
280
|
auth: { mode: 'token', token: 'cli-dummy-token-xyz123' }
|
|
242
281
|
}
|
|
243
282
|
};
|
|
@@ -246,7 +285,8 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
246
285
|
await fs.writeFile(path.join(projectDir, '.openclaw', 'agents', agentId, 'agent', 'IDENTITY.md'), identityContent);
|
|
247
286
|
|
|
248
287
|
if (channelKey === 'telegram') {
|
|
249
|
-
|
|
288
|
+
// dmPolicy:'open' = skip pairing step entirely (standard for personal bots)
|
|
289
|
+
botConfig.channels['telegram'] = { enabled: true, dmPolicy: 'open', allowFrom: ['*'] };
|
|
250
290
|
} else if (channelKey === 'zalo-personal') {
|
|
251
291
|
botConfig.channels['zalo'] = { enabled: true, provider: 'client', autoReply: true };
|
|
252
292
|
} else if (channelKey === 'zalo-bot') {
|
|
@@ -280,7 +320,12 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
280
320
|
child.on('close', (code) => {
|
|
281
321
|
if (code === 0) {
|
|
282
322
|
console.log(chalk.green(`\n🎉 ${isVi ? 'Setup hoàn tất! Bot đang chạy.' : 'Setup complete! Bot is running.'}`));
|
|
283
|
-
|
|
323
|
+
|
|
324
|
+
if (channelKey === 'telegram') {
|
|
325
|
+
console.log(chalk.cyan(`\n💬 ${isVi
|
|
326
|
+
? 'Nhắn tin cho bot trên Telegram là dùng được ngay!'
|
|
327
|
+
: 'Just message your bot on Telegram to start chatting!'}`));
|
|
328
|
+
} else if (channelKey === 'zalo-personal') {
|
|
284
329
|
console.log(chalk.yellow(`\n📱 ${isVi ? 'Vui lòng chạy lệnh sau để đăng nhập Zalo Personal (1 lần duy nhất):' : 'Please run this command to login to Zalo Personal (once):'}`));
|
|
285
330
|
console.log(`cd ${projectDir} && docker compose exec -it openclaw bun run core:onboard`);
|
|
286
331
|
}
|
|
@@ -288,6 +333,7 @@ ${selectedSkills.includes('browser') ? ` extra_hosts:
|
|
|
288
333
|
console.log(chalk.red(`\n❌ Docker exited with code ${code}`));
|
|
289
334
|
}
|
|
290
335
|
});
|
|
336
|
+
|
|
291
337
|
} else {
|
|
292
338
|
console.log(chalk.cyan(`\n👉 ${isVi ? 'Tiếp theo, hãy chạy:' : 'Next, run:'}\n cd ${projectDir}/docker/openclaw\n docker compose build\n docker compose up -d`));
|
|
293
339
|
}
|