create-openclaw-bot 5.7.10 → 5.8.1
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 +61 -219
- package/README.vi.md +63 -216
- package/dist/cli.js +92 -2777
- package/dist/legacy-cli.js +2812 -0
- package/dist/server/local-server.js +2568 -0
- package/dist/setup/data/header.js +80 -80
- package/dist/setup/data/index.js +0 -1
- package/dist/setup/data/plugins.js +8 -1
- package/dist/setup/data/skills.js +2 -10
- package/dist/setup/shared/bot-config-gen.js +469 -462
- package/dist/setup/shared/common-gen.js +313 -315
- package/dist/setup/shared/docker-gen.js +193 -124
- package/dist/setup/shared/install-gen.js +566 -566
- package/dist/setup/shared/workspace-gen.js +813 -525
- package/dist/setup.js +729 -499
- package/dist/web/app.js +1247 -0
- package/dist/web/bvvbank.jpg +0 -0
- package/dist/web/index.html +14 -0
- package/dist/web/momo.jpg +0 -0
- package/dist/web/openclaw-logo.png +0 -0
- package/dist/web/openclaw-logo.svg +1 -0
- package/dist/web/styles.css +1375 -0
- package/package.json +40 -39
|
@@ -1,462 +1,469 @@
|
|
|
1
|
-
// @ts-nocheck
|
|
2
|
-
/**
|
|
3
|
-
* @fileoverview Centralized bot configuration builders — single source of truth.
|
|
4
|
-
*
|
|
5
|
-
* Generates openclaw.json, auth-profiles.json, exec-approvals.json, and .env content.
|
|
6
|
-
* Used by BOTH the Wizard (IIFE bundle) and CLI (CJS require).
|
|
7
|
-
*
|
|
8
|
-
* Pattern: same as common-gen.js / workspace-gen.js — IIFE + CJS dual export.
|
|
9
|
-
*/
|
|
10
|
-
(function (root) {
|
|
11
|
-
|
|
12
|
-
const _common = (typeof root !== 'undefined' && root.__openclawCommon) || {};
|
|
13
|
-
|
|
14
|
-
// ── Helper: slugify a bot name into a safe agent ID ─────────────────────────
|
|
15
|
-
function slugify(name) {
|
|
16
|
-
return String(name || 'bot').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'bot';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// ── Helper: detect if channel is zalo personal ───────────────────────────────
|
|
20
|
-
function isZaloPersonal(channelKey) {
|
|
21
|
-
return channelKey === 'zalo-personal';
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// ── Helper: generate a random token (works in both browser + Node) ──────────
|
|
25
|
-
function generateToken() {
|
|
26
|
-
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
27
|
-
return crypto.randomUUID().replace(/-/g, '');
|
|
28
|
-
}
|
|
29
|
-
// Fallback for older Node.js
|
|
30
|
-
const hex = '0123456789abcdef';
|
|
31
|
-
let result = '';
|
|
32
|
-
for (let i = 0; i < 32; i++) result += hex[Math.floor(Math.random() * 16)];
|
|
33
|
-
return result;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
38
|
-
// buildOpenclawJson — the ONE function that generates the full openclaw.json
|
|
39
|
-
// ═══════════════════════════════════════════════════════════════════════════════
|
|
40
|
-
/**
|
|
41
|
-
* @param {object} opts
|
|
42
|
-
* @param {string} opts.channelKey - 'telegram' | 'zalo-personal' | 'zalo-bot'
|
|
43
|
-
* @param {string} opts.deployMode - 'docker' | 'native'
|
|
44
|
-
* @param {string} opts.providerKey - '9router' | 'openai' | 'ollama' | ...
|
|
45
|
-
* @param {object} opts.provider - Provider metadata object from PROVIDERS
|
|
46
|
-
* @param {string} opts.model - Primary model ID (e.g. 'smart-route', 'gemma4:e2b')
|
|
47
|
-
* @param {boolean} opts.isMultiBot - Multi-bot mode
|
|
48
|
-
* @param {Array} opts.agentMetas - [{ agentId, name, desc, persona, token, slashCmd, accountId, workspaceDir }]
|
|
49
|
-
* @param {string} opts.groupId - Telegram group ID (multi-bot only)
|
|
50
|
-
* @param {Array} opts.selectedSkills - ['browser', 'memory', 'scheduler', ...]
|
|
51
|
-
* @param {Array} opts.skills - Full SKILLS registry array
|
|
52
|
-
* @param {boolean} opts.hasBrowserDesktop - Browser desktop mode
|
|
53
|
-
* @param {boolean} opts.hasBrowserServer - Browser server mode
|
|
54
|
-
* @param {number} [opts.gatewayPort=
|
|
55
|
-
* @param {Array} [opts.gatewayAllowedOrigins]
|
|
56
|
-
* @param {string} [opts.osChoice] - 'windows' | 'macos' | 'vps' | 'ubuntu'
|
|
57
|
-
* @param {string} [opts.selectedModel] - For Ollama: specific model selected
|
|
58
|
-
*/
|
|
59
|
-
function buildOpenclawJson(opts) {
|
|
60
|
-
const {
|
|
61
|
-
channelKey = 'telegram',
|
|
62
|
-
deployMode = 'docker',
|
|
63
|
-
providerKey = '9router',
|
|
64
|
-
provider = {},
|
|
65
|
-
model = 'smart-route',
|
|
66
|
-
isMultiBot = false,
|
|
67
|
-
agentMetas = [],
|
|
68
|
-
groupId = '',
|
|
69
|
-
selectedSkills = [],
|
|
70
|
-
skills = [],
|
|
71
|
-
hasBrowserDesktop = false,
|
|
72
|
-
hasBrowserServer = false,
|
|
73
|
-
gatewayPort =
|
|
74
|
-
gatewayAllowedOrigins = [],
|
|
75
|
-
osChoice = '',
|
|
76
|
-
selectedModel = '',
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
const
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
telegramConfig.
|
|
246
|
-
|
|
247
|
-
[
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
telegramConfig.
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
const
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
* @param {
|
|
372
|
-
* @param {
|
|
373
|
-
* @param {string} opts.
|
|
374
|
-
* @param {string} opts.
|
|
375
|
-
* @param {string} opts.
|
|
376
|
-
* @param {
|
|
377
|
-
* @param {string} opts.
|
|
378
|
-
* @param {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
}
|
|
426
|
-
|
|
427
|
-
if (selectedSkills.includes('
|
|
428
|
-
lines.push('');
|
|
429
|
-
lines.push('# ---
|
|
430
|
-
lines.push(`
|
|
431
|
-
lines.push(`
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
1
|
+
// @ts-nocheck
|
|
2
|
+
/**
|
|
3
|
+
* @fileoverview Centralized bot configuration builders — single source of truth.
|
|
4
|
+
*
|
|
5
|
+
* Generates openclaw.json, auth-profiles.json, exec-approvals.json, and .env content.
|
|
6
|
+
* Used by BOTH the Wizard (IIFE bundle) and CLI (CJS require).
|
|
7
|
+
*
|
|
8
|
+
* Pattern: same as common-gen.js / workspace-gen.js — IIFE + CJS dual export.
|
|
9
|
+
*/
|
|
10
|
+
(function (root) {
|
|
11
|
+
|
|
12
|
+
const _common = (typeof root !== 'undefined' && root.__openclawCommon) || {};
|
|
13
|
+
|
|
14
|
+
// ── Helper: slugify a bot name into a safe agent ID ─────────────────────────
|
|
15
|
+
function slugify(name) {
|
|
16
|
+
return String(name || 'bot').toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'bot';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// ── Helper: detect if channel is zalo personal ───────────────────────────────
|
|
20
|
+
function isZaloPersonal(channelKey) {
|
|
21
|
+
return channelKey === 'zalo-personal';
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// ── Helper: generate a random token (works in both browser + Node) ──────────
|
|
25
|
+
function generateToken() {
|
|
26
|
+
if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
|
|
27
|
+
return crypto.randomUUID().replace(/-/g, '');
|
|
28
|
+
}
|
|
29
|
+
// Fallback for older Node.js
|
|
30
|
+
const hex = '0123456789abcdef';
|
|
31
|
+
let result = '';
|
|
32
|
+
for (let i = 0; i < 32; i++) result += hex[Math.floor(Math.random() * 16)];
|
|
33
|
+
return result;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
38
|
+
// buildOpenclawJson — the ONE function that generates the full openclaw.json
|
|
39
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
40
|
+
/**
|
|
41
|
+
* @param {object} opts
|
|
42
|
+
* @param {string} opts.channelKey - 'telegram' | 'zalo-personal' | 'zalo-bot'
|
|
43
|
+
* @param {string} opts.deployMode - 'docker' | 'native'
|
|
44
|
+
* @param {string} opts.providerKey - '9router' | 'openai' | 'ollama' | ...
|
|
45
|
+
* @param {object} opts.provider - Provider metadata object from PROVIDERS
|
|
46
|
+
* @param {string} opts.model - Primary model ID (e.g. 'smart-route', 'gemma4:e2b')
|
|
47
|
+
* @param {boolean} opts.isMultiBot - Multi-bot mode
|
|
48
|
+
* @param {Array} opts.agentMetas - [{ agentId, name, desc, persona, token, slashCmd, accountId, workspaceDir }]
|
|
49
|
+
* @param {string} opts.groupId - Telegram group ID (multi-bot only)
|
|
50
|
+
* @param {Array} opts.selectedSkills - ['browser', 'memory', 'scheduler', ...]
|
|
51
|
+
* @param {Array} opts.skills - Full SKILLS registry array
|
|
52
|
+
* @param {boolean} opts.hasBrowserDesktop - Browser desktop mode
|
|
53
|
+
* @param {boolean} opts.hasBrowserServer - Browser server mode
|
|
54
|
+
* @param {number} [opts.gatewayPort=18789]
|
|
55
|
+
* @param {Array} [opts.gatewayAllowedOrigins]
|
|
56
|
+
* @param {string} [opts.osChoice] - 'windows' | 'macos' | 'vps' | 'ubuntu'
|
|
57
|
+
* @param {string} [opts.selectedModel] - For Ollama: specific model selected
|
|
58
|
+
*/
|
|
59
|
+
function buildOpenclawJson(opts) {
|
|
60
|
+
const {
|
|
61
|
+
channelKey = 'telegram',
|
|
62
|
+
deployMode = 'docker',
|
|
63
|
+
providerKey = '9router',
|
|
64
|
+
provider = {},
|
|
65
|
+
model = 'smart-route',
|
|
66
|
+
isMultiBot = false,
|
|
67
|
+
agentMetas = [],
|
|
68
|
+
groupId = '',
|
|
69
|
+
selectedSkills = [],
|
|
70
|
+
skills = [],
|
|
71
|
+
hasBrowserDesktop = false,
|
|
72
|
+
hasBrowserServer = false,
|
|
73
|
+
gatewayPort = 18789,
|
|
74
|
+
gatewayAllowedOrigins = [],
|
|
75
|
+
osChoice = '',
|
|
76
|
+
selectedModel = '',
|
|
77
|
+
routerPort,
|
|
78
|
+
} = opts;
|
|
79
|
+
|
|
80
|
+
const common = _common;
|
|
81
|
+
const is9Router = providerKey === '9router';
|
|
82
|
+
const isLocal = !!provider.isLocal;
|
|
83
|
+
|
|
84
|
+
// ── agents ────────────────────────────────────────────────────────────────
|
|
85
|
+
const agentsList = agentMetas.map((meta) => ({
|
|
86
|
+
id: meta.agentId,
|
|
87
|
+
...(meta.name ? { name: meta.name } : {}),
|
|
88
|
+
workspace: `/root/project/.openclaw/${meta.workspaceDir || 'workspace-' + meta.agentId}`,
|
|
89
|
+
agentDir: `agents/${meta.agentId}/agent`,
|
|
90
|
+
model: { primary: model, fallbacks: [] },
|
|
91
|
+
}));
|
|
92
|
+
|
|
93
|
+
const cfg = {
|
|
94
|
+
meta: { lastTouchedVersion: (_common.OPENCLAW_NPM_SPEC || 'latest').replace('openclaw@', '') },
|
|
95
|
+
agents: {
|
|
96
|
+
defaults: {
|
|
97
|
+
model: { primary: model, fallbacks: [] },
|
|
98
|
+
compaction: { mode: 'safeguard' },
|
|
99
|
+
timeoutSeconds: isLocal ? 900 : 120,
|
|
100
|
+
...(isLocal ? { llm: { idleTimeoutSeconds: 300 } } : {}),
|
|
101
|
+
},
|
|
102
|
+
list: agentsList,
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// ── models.providers ──────────────────────────────────────────────────────
|
|
107
|
+
if (is9Router && common.build9RouterProviderConfig) {
|
|
108
|
+
cfg.models = {
|
|
109
|
+
mode: 'merge',
|
|
110
|
+
providers: {
|
|
111
|
+
'9router': common.build9RouterProviderConfig(
|
|
112
|
+
common.get9RouterBaseUrl ? common.get9RouterBaseUrl(deployMode, routerPort) : `http://9router:${routerPort || 20128}/v1`
|
|
113
|
+
),
|
|
114
|
+
},
|
|
115
|
+
};
|
|
116
|
+
} else if (isLocal) {
|
|
117
|
+
const ollamaBaseUrl = deployMode === 'docker' ? 'http://ollama:11434' : 'http://localhost:11434';
|
|
118
|
+
const OLLAMA_MODELS = (typeof root !== 'undefined' && root.__openclawData && root.__openclawData.OLLAMA_MODELS)
|
|
119
|
+
|| (typeof _OLLAMA_MODELS !== 'undefined' ? _OLLAMA_MODELS : []);
|
|
120
|
+
const modelList = selectedModel
|
|
121
|
+
? [{ id: selectedModel, name: selectedModel, contextWindow: 128000, maxTokens: 8192 }]
|
|
122
|
+
: OLLAMA_MODELS;
|
|
123
|
+
cfg.models = {
|
|
124
|
+
mode: 'merge',
|
|
125
|
+
providers: {
|
|
126
|
+
ollama: {
|
|
127
|
+
baseUrl: ollamaBaseUrl,
|
|
128
|
+
api: 'ollama',
|
|
129
|
+
apiKey: 'ollama-local',
|
|
130
|
+
models: modelList,
|
|
131
|
+
},
|
|
132
|
+
},
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ── commands ──────────────────────────────────────────────────────────────
|
|
137
|
+
cfg.commands = { native: 'auto', nativeSkills: 'auto', restart: true, ownerDisplay: 'raw' };
|
|
138
|
+
if (selectedSkills.includes('scheduler')) {
|
|
139
|
+
cfg.commands.ownerAllowFrom = ['*'];
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ── bindings (multi-bot or Zalo) ─────────────────────────────────────────
|
|
143
|
+
if (isMultiBot && channelKey === 'telegram') {
|
|
144
|
+
cfg.bindings = agentMetas.map((meta) => ({
|
|
145
|
+
agentId: meta.agentId,
|
|
146
|
+
match: { channel: 'telegram', accountId: meta.accountId || 'default' },
|
|
147
|
+
}));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// ── channels ─────────────────────────────────────────────────────────────
|
|
151
|
+
cfg.channels = buildChannelConfig({
|
|
152
|
+
channelKey, isMultiBot, groupId, agentMetas, botName: agentMetas[0]?.name || 'Bot',
|
|
153
|
+
agentId: agentMetas[0]?.agentId || 'bot',
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
// ── tools ────────────────────────────────────────────────────────────────
|
|
157
|
+
cfg.tools = { profile: 'full', exec: { host: 'gateway', security: 'full', ask: 'off' } };
|
|
158
|
+
if (selectedSkills.includes('scheduler')) {
|
|
159
|
+
cfg.tools.alsoAllow = ['group:automation'];
|
|
160
|
+
}
|
|
161
|
+
if (isMultiBot) {
|
|
162
|
+
cfg.tools.agentToAgent = {
|
|
163
|
+
enabled: true,
|
|
164
|
+
allow: agentMetas.map((meta) => meta.agentId),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// ── gateway ──────────────────────────────────────────────────────────────
|
|
169
|
+
cfg.gateway = {
|
|
170
|
+
port: gatewayPort,
|
|
171
|
+
mode: 'local',
|
|
172
|
+
bind: (deployMode === 'docker' || osChoice === 'vps') ? 'custom' : 'loopback',
|
|
173
|
+
...(deployMode === 'docker' || osChoice === 'vps' ? { customBindHost: '0.0.0.0' } : {}),
|
|
174
|
+
controlUi: {
|
|
175
|
+
allowedOrigins: gatewayAllowedOrigins.length > 0
|
|
176
|
+
? gatewayAllowedOrigins
|
|
177
|
+
: [`http://localhost:${gatewayPort}`, `http://127.0.0.1:${gatewayPort}`, `http://0.0.0.0:${gatewayPort}`],
|
|
178
|
+
},
|
|
179
|
+
auth: { mode: 'token', token: generateToken() },
|
|
180
|
+
};
|
|
181
|
+
|
|
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
|
+
}
|
|
192
|
+
|
|
193
|
+
// ── skills ───────────────────────────────────────────────────────────────
|
|
194
|
+
const skillEntries = buildSkillsEntries(skills, selectedSkills);
|
|
195
|
+
if (Object.keys(skillEntries).length > 0) {
|
|
196
|
+
cfg.skills = { entries: skillEntries };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// ── plugins (memory-core dreaming + openclaw-zalo-mod) ────────────────────────────
|
|
200
|
+
const pluginsConfig = buildPluginsConfig({
|
|
201
|
+
channelKey,
|
|
202
|
+
selectedSkills,
|
|
203
|
+
botName: agentMetas[0]?.name || 'Bot',
|
|
204
|
+
agentId: agentMetas[0]?.agentId || 'bot',
|
|
205
|
+
});
|
|
206
|
+
cfg.plugins = pluginsConfig.plugins;
|
|
207
|
+
|
|
208
|
+
// ── bindings for zalouser ────────────────────────────────────────────────
|
|
209
|
+
if (isZaloPersonal(channelKey)) {
|
|
210
|
+
cfg.bindings = cfg.bindings || [];
|
|
211
|
+
const firstAgentId = agentMetas[0]?.agentId || 'bot';
|
|
212
|
+
if (!cfg.bindings.some(b => b.match && b.match.channel === 'zalouser')) {
|
|
213
|
+
cfg.bindings.push({ agentId: firstAgentId, match: { channel: 'zalouser' } });
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return cfg;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
|
|
221
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
222
|
+
// buildChannelConfig — returns the full `channels: { ... }` object
|
|
223
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
224
|
+
function buildChannelConfig(opts) {
|
|
225
|
+
const { channelKey, isMultiBot, groupId, agentMetas = [], botName, agentId } = opts;
|
|
226
|
+
const channels = {};
|
|
227
|
+
|
|
228
|
+
if (channelKey === 'telegram') {
|
|
229
|
+
const telegramConfig = {
|
|
230
|
+
enabled: true,
|
|
231
|
+
defaultAccount: 'default',
|
|
232
|
+
dmPolicy: 'open',
|
|
233
|
+
allowFrom: ['*'],
|
|
234
|
+
replyToMode: 'first',
|
|
235
|
+
reactionLevel: 'minimal',
|
|
236
|
+
actions: {
|
|
237
|
+
sendMessage: true,
|
|
238
|
+
reactions: true,
|
|
239
|
+
},
|
|
240
|
+
accounts: {},
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
if (isMultiBot) {
|
|
244
|
+
// Multiple accounts — each bot gets its own account keyed by accountId
|
|
245
|
+
telegramConfig.accounts = {};
|
|
246
|
+
for (const meta of agentMetas) {
|
|
247
|
+
telegramConfig.accounts[meta.accountId || 'default'] = {
|
|
248
|
+
botToken: meta.token || '<your_bot_token>',
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
telegramConfig.groupPolicy = groupId ? 'allowlist' : 'open';
|
|
252
|
+
telegramConfig.groupAllowFrom = ['*'];
|
|
253
|
+
telegramConfig.groups = {
|
|
254
|
+
[groupId || '*']: { enabled: true, requireMention: false },
|
|
255
|
+
};
|
|
256
|
+
} else {
|
|
257
|
+
// Single bot
|
|
258
|
+
telegramConfig.accounts = {
|
|
259
|
+
default: {
|
|
260
|
+
botToken: (agentMetas[0] && agentMetas[0].token) || '<your_bot_token>',
|
|
261
|
+
},
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
channels.telegram = telegramConfig;
|
|
266
|
+
} else if (isZaloPersonal(channelKey)) {
|
|
267
|
+
// Zalo Personal — matches live Mkt/Williams configs
|
|
268
|
+
channels.zalouser = {
|
|
269
|
+
enabled: true,
|
|
270
|
+
defaultAccount: 'default',
|
|
271
|
+
accounts: {
|
|
272
|
+
default: {
|
|
273
|
+
dmPolicy: 'open',
|
|
274
|
+
allowFrom: ['*'],
|
|
275
|
+
groupPolicy: 'allowlist',
|
|
276
|
+
groupAllowFrom: ['*'],
|
|
277
|
+
},
|
|
278
|
+
},
|
|
279
|
+
dmPolicy: 'open',
|
|
280
|
+
allowFrom: ['*'],
|
|
281
|
+
groupPolicy: 'allowlist',
|
|
282
|
+
groupAllowFrom: ['*'],
|
|
283
|
+
historyLimit: 50,
|
|
284
|
+
groups: {
|
|
285
|
+
'*': { enabled: true, requireMention: false },
|
|
286
|
+
},
|
|
287
|
+
};
|
|
288
|
+
} else if (channelKey === 'zalo-bot') {
|
|
289
|
+
channels.zalo = { enabled: true, provider: 'official_account' };
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
return channels;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
|
|
296
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
297
|
+
// buildPluginsConfig — returns { plugins: { ... } }
|
|
298
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
299
|
+
function buildPluginsConfig(opts) {
|
|
300
|
+
const { channelKey, selectedSkills = [], botName = 'Bot', agentId = 'bot' } = opts;
|
|
301
|
+
|
|
302
|
+
const entries = {};
|
|
303
|
+
|
|
304
|
+
// memory-core with dreaming — always present
|
|
305
|
+
entries['memory-core'] = {
|
|
306
|
+
config: {
|
|
307
|
+
dreaming: {
|
|
308
|
+
enabled: selectedSkills.includes('memory'),
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
};
|
|
312
|
+
|
|
313
|
+
const allow = ['memory-core'];
|
|
314
|
+
|
|
315
|
+
// Zalo Personal channel is native; install openclaw-zalo-mod manually via ClawHub when needed.
|
|
316
|
+
if (isZaloPersonal(channelKey)) {
|
|
317
|
+
allow.push('zalouser');
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
const plugins = { entries };
|
|
321
|
+
plugins.allow = allow;
|
|
322
|
+
|
|
323
|
+
return { plugins };
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
328
|
+
// buildSkillsEntries — returns { slug: { enabled: true } } map
|
|
329
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
330
|
+
function buildSkillsEntries(skills, selectedSkillIds) {
|
|
331
|
+
const entries = {};
|
|
332
|
+
if (!skills || !selectedSkillIds) return entries;
|
|
333
|
+
|
|
334
|
+
for (const skill of skills) {
|
|
335
|
+
const skillId = skill.value || skill.id;
|
|
336
|
+
if (!selectedSkillIds.includes(skillId)) continue;
|
|
337
|
+
// Skills without a slug are native (browser, scheduler) — not in skills.entries
|
|
338
|
+
const slug = skill.slug;
|
|
339
|
+
if (!slug) continue;
|
|
340
|
+
// Skip browser-automation slug (handled by browser config)
|
|
341
|
+
if (slug === 'browser-automation') continue;
|
|
342
|
+
entries[slug] = { enabled: true };
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
return entries;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
350
|
+
// buildExecApprovalsJson — exec-approvals.json content
|
|
351
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
352
|
+
function buildExecApprovalsJson(opts) {
|
|
353
|
+
const { agentMetas = [] } = opts;
|
|
354
|
+
const agentEntries = {};
|
|
355
|
+
agentEntries.main = { security: 'full', ask: 'off', askFallback: 'full', autoAllowSkills: true };
|
|
356
|
+
for (const meta of agentMetas) {
|
|
357
|
+
agentEntries[meta.agentId] = { security: 'full', ask: 'off', askFallback: 'full', autoAllowSkills: true };
|
|
358
|
+
}
|
|
359
|
+
return {
|
|
360
|
+
version: 1,
|
|
361
|
+
defaults: { security: 'full', ask: 'off', askFallback: 'full' },
|
|
362
|
+
agents: agentEntries,
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
368
|
+
// buildEnvFileContent — .env file content for a single bot
|
|
369
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
370
|
+
/**
|
|
371
|
+
* @param {object} opts
|
|
372
|
+
* @param {object} opts.provider - Provider metadata
|
|
373
|
+
* @param {string} opts.providerKeyVal - API key value
|
|
374
|
+
* @param {string} opts.channelKey - Channel type
|
|
375
|
+
* @param {string} opts.botToken - Bot token
|
|
376
|
+
* @param {boolean} opts.isMultiBot
|
|
377
|
+
* @param {string} opts.groupId
|
|
378
|
+
* @param {Array} opts.selectedSkills
|
|
379
|
+
* @param {string} opts.ttsOpenaiKey
|
|
380
|
+
* @param {string} opts.ttsElevenKey
|
|
381
|
+
* @param {string} opts.smtpHost
|
|
382
|
+
* @param {string} opts.smtpPort
|
|
383
|
+
* @param {string} opts.smtpUser
|
|
384
|
+
* @param {string} opts.smtpPass
|
|
385
|
+
* @param {boolean} opts.isSharedEnv - If true, omit per-bot token (multi-bot shared .env)
|
|
386
|
+
*/
|
|
387
|
+
function buildEnvFileContent(opts) {
|
|
388
|
+
const {
|
|
389
|
+
provider = {},
|
|
390
|
+
providerKeyVal = '',
|
|
391
|
+
channelKey = 'telegram',
|
|
392
|
+
botToken = '',
|
|
393
|
+
isMultiBot = false,
|
|
394
|
+
groupId = '',
|
|
395
|
+
selectedSkills = [],
|
|
396
|
+
ttsOpenaiKey = '',
|
|
397
|
+
ttsElevenKey = '',
|
|
398
|
+
smtpHost = 'smtp.gmail.com',
|
|
399
|
+
smtpPort = '587',
|
|
400
|
+
smtpUser = '',
|
|
401
|
+
smtpPass = '',
|
|
402
|
+
isSharedEnv = false,
|
|
403
|
+
} = opts;
|
|
404
|
+
|
|
405
|
+
const lines = [];
|
|
406
|
+
|
|
407
|
+
if (provider.isLocal) {
|
|
408
|
+
lines.push('OLLAMA_HOST=http://localhost:11434');
|
|
409
|
+
lines.push('OLLAMA_API_KEY=ollama-local');
|
|
410
|
+
} else if (provider.isProxy) {
|
|
411
|
+
lines.push('# 9Router: no API key needed');
|
|
412
|
+
} else if (provider.envKey) {
|
|
413
|
+
lines.push(`${provider.envKey}=${providerKeyVal || '<your_api_key>'}`);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (!isSharedEnv) {
|
|
417
|
+
if (channelKey === 'telegram') {
|
|
418
|
+
lines.push(`TELEGRAM_BOT_TOKEN=${botToken || '<your_bot_token>'}`);
|
|
419
|
+
if (isMultiBot && groupId) lines.push(`TELEGRAM_GROUP_ID=${groupId}`);
|
|
420
|
+
} else if (channelKey === 'zalo-bot') {
|
|
421
|
+
lines.push('ZALO_APP_ID=');
|
|
422
|
+
lines.push('ZALO_APP_SECRET=');
|
|
423
|
+
lines.push(`ZALO_BOT_TOKEN=${botToken || '<your_zalo_bot_token>'}`);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (selectedSkills.includes('tts')) {
|
|
428
|
+
lines.push('');
|
|
429
|
+
lines.push('# --- Text-To-Speech ---');
|
|
430
|
+
if (ttsOpenaiKey) lines.push(`OPENAI_API_KEY=${ttsOpenaiKey}`);
|
|
431
|
+
if (ttsElevenKey) lines.push(`ELEVENLABS_API_KEY=${ttsElevenKey}`);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
if (selectedSkills.includes('email')) {
|
|
435
|
+
lines.push('');
|
|
436
|
+
lines.push('# --- Email ---');
|
|
437
|
+
lines.push(`SMTP_HOST=${smtpHost}`);
|
|
438
|
+
lines.push(`SMTP_PORT=${smtpPort}`);
|
|
439
|
+
lines.push(`SMTP_USER=${smtpUser}`);
|
|
440
|
+
lines.push(`SMTP_PASS=${smtpPass}`);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return lines.join('\n') + '\n';
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
448
|
+
// Export
|
|
449
|
+
// ═══════════════════════════════════════════════════════════════════════════════
|
|
450
|
+
const exports = {
|
|
451
|
+
slugify,
|
|
452
|
+
isZaloPersonal,
|
|
453
|
+
generateToken,
|
|
454
|
+
buildOpenclawJson,
|
|
455
|
+
buildChannelConfig,
|
|
456
|
+
buildPluginsConfig,
|
|
457
|
+
buildSkillsEntries,
|
|
458
|
+
buildExecApprovalsJson,
|
|
459
|
+
buildEnvFileContent,
|
|
460
|
+
};
|
|
461
|
+
|
|
462
|
+
if (typeof root !== 'undefined') {
|
|
463
|
+
root.__openclawBotConfig = exports;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
})(typeof globalThis !== 'undefined' ? globalThis : {});
|
|
467
|
+
if (typeof exports !== 'undefined' && typeof globalThis !== 'undefined' && globalThis.__openclawBotConfig) {
|
|
468
|
+
Object.assign(exports, globalThis.__openclawBotConfig);
|
|
469
|
+
}
|