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.
Files changed (2) hide show
  1. package/cli.js +78 -32
  2. 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
- /\\O _ O/\\
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
- 'proxy': { name: '9Router Proxy (Khuyên dùng)', icon: '🔀', isProxy: true },
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 \\\\"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:'0.0.0.0'});fs.writeFileSync(p,JSON.stringify(c,null,2));}\\\\" && ${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"`;
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 = `services:
167
+ compose = `name: oc-${agentId}
168
+ services:
162
169
  ai-bot:
163
170
  build: .
164
- container_name: openclaw-bot
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
- - NINEROUTER_PORT=18789
185
- - NINEROUTER_SECURE=false
189
+ - PORT=20128
190
+ - HOSTNAME=0.0.0.0
191
+ - CI=true
186
192
  volumes:
187
- - ../../.9router:/root/.9router`;
193
+ - 9router-data:/root/.9router
194
+ ports:
195
+ - "20128:20128"
196
+
197
+ volumes:
198
+ 9router-data:`;
188
199
  } else {
189
- compose = `services:
200
+ compose = `name: oc-${agentId}
201
+ services:
190
202
  ai-bot:
191
203
  build: .
192
- container_name: openclaw-bot
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
- const authProfileObj = {};
215
+ let authProfilesJson = {};
204
216
  if (providerKey && !provider.isLocal) {
205
- const authProviderName = providerKey === '9router' ? '9router' : 'openai';
206
- authProfileObj[authProviderName] = [{
207
- type: "api_key",
208
- field: "key",
209
- order: 1,
210
- value: providerKey === '9router' ? "9r-dummy" : providerKeyVal
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
- authProfileObj[authProviderName][0].url = provider.baseURL;
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
- await fs.writeJson(path.join(projectDir, '.openclaw', 'auth-profiles.json'), authProfileObj, { spaces: 2 });
222
- await fs.writeJson(path.join(projectDir, '.openclaw', 'agents', agentId, 'agent', 'auth-profiles.json'), authProfileObj, { spaces: 2 });
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
- botConfig.channels['telegram'] = { enabled: true, mode: 'polling' };
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
- if (channelKey === 'zalo-personal') {
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-openclaw-bot",
3
- "version": "4.0.2",
3
+ "version": "4.0.4",
4
4
  "description": "Interactive CLI installer for OpenClaw Bot",
5
5
  "main": "cli.js",
6
6
  "bin": {