evolclaw 2.8.3 → 3.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/README.md +21 -12
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent-normalize.js +19 -0
- package/dist/agents/claude-runner.js +108 -46
- package/dist/agents/codex-runner.js +13 -14
- package/dist/agents/gemini-runner.js +15 -17
- package/dist/agents/kit-renderer.js +281 -0
- package/dist/agents/resolve.js +134 -0
- package/dist/aun/aid/agentmd.js +186 -0
- package/dist/aun/aid/client.js +134 -0
- package/dist/aun/aid/identity.js +159 -0
- package/dist/aun/aid/index.js +3 -0
- package/dist/aun/aid/lifecycle-log.js +33 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/msg/group.js +293 -0
- package/dist/aun/msg/index.js +4 -0
- package/dist/aun/msg/p2p.js +147 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +98 -0
- package/dist/aun/outbox.js +138 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +34 -0
- package/dist/aun/rpc/index.js +2 -0
- package/dist/aun/storage/download.js +29 -0
- package/dist/aun/storage/index.js +3 -0
- package/dist/aun/storage/manage.js +10 -0
- package/dist/aun/storage/upload.js +35 -0
- package/dist/channels/aun.js +1340 -349
- package/dist/channels/dingtalk.js +59 -5
- package/dist/channels/feishu.js +381 -32
- package/dist/channels/qqbot.js +68 -12
- package/dist/channels/wechat.js +63 -4
- package/dist/channels/wecom.js +59 -5
- package/dist/cli/agent.js +800 -0
- package/dist/cli/bench.js +1219 -0
- package/dist/cli/index.js +4513 -0
- package/dist/{utils → cli}/init-channel.js +211 -621
- package/dist/cli/init.js +178 -0
- package/dist/cli/link-rules.js +245 -0
- package/dist/cli/net-check.js +640 -0
- package/dist/cli/watch-msg.js +589 -0
- package/dist/config-store.js +645 -0
- package/dist/core/{agent-loader.js → baseagent-loader.js} +6 -12
- package/dist/core/channel-loader.js +176 -12
- package/dist/core/command-handler.js +883 -848
- package/dist/core/evolagent-registry.js +191 -371
- package/dist/core/evolagent.js +202 -238
- package/dist/core/interaction-router.js +52 -5
- package/dist/core/message/im-renderer.js +486 -0
- package/dist/core/message/items-formatter.js +68 -0
- package/dist/core/message/message-bridge.js +109 -56
- package/dist/core/message/message-log.js +93 -0
- package/dist/core/message/message-processor.js +430 -212
- package/dist/core/message/message-queue.js +13 -6
- package/dist/core/permission.js +116 -11
- package/dist/core/session/adapters/codex-session-file-adapter.js +24 -2
- package/dist/core/session/session-fs-store.js +230 -0
- package/dist/core/session/session-manager.js +740 -777
- package/dist/core/session/session-mapper.js +87 -0
- package/dist/core/trigger/manager.js +122 -0
- package/dist/core/trigger/parser.js +128 -0
- package/dist/core/trigger/scheduler.js +224 -0
- package/dist/data/error-dict.json +118 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +560 -283
- package/dist/ipc.js +49 -0
- package/dist/net-check.js +640 -0
- package/dist/paths.js +73 -9
- package/dist/types.js +8 -2
- package/dist/utils/aid-lifecycle-log.js +33 -0
- package/dist/utils/atomic-write.js +89 -0
- package/dist/utils/channel-helpers.js +46 -0
- package/dist/utils/cross-platform.js +17 -26
- package/dist/utils/error-utils.js +10 -2
- package/dist/utils/instance-registry.js +434 -0
- package/dist/utils/log-writer.js +217 -0
- package/dist/utils/logger.js +34 -77
- package/dist/utils/media-cache.js +23 -0
- package/dist/utils/npm-ops.js +163 -0
- package/dist/utils/process-introspect.js +122 -0
- package/dist/utils/stats.js +192 -0
- package/dist/watch-msg.js +544 -0
- package/evolclaw-install-aun.md +127 -47
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +52 -0
- package/kits/docs/aun/CHEATSHEET.md +17 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/aun.md +25 -0
- package/kits/docs/channels/feishu.md +27 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolclaw/AGENT_CMD.md +31 -0
- package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +25 -0
- package/kits/docs/evolclaw/self-summary.md +29 -0
- package/kits/docs/evolclaw/tools.md +25 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +20 -0
- package/kits/docs/identity/identity-tools.md +26 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/eck_manifest.json +95 -0
- package/kits/rules/01-overview.md +120 -0
- package/kits/rules/02-navigation.md +75 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +49 -0
- package/kits/rules/05-venue.md +45 -0
- package/kits/rules/06-channel.md +43 -0
- package/kits/templates/system-fragments/baseagent.md +2 -0
- package/kits/templates/system-fragments/channel.md +10 -0
- package/kits/templates/system-fragments/identity.md +12 -0
- package/kits/templates/system-fragments/relation.md +9 -0
- package/kits/templates/system-fragments/runtime.md +19 -0
- package/kits/templates/system-fragments/venue.md +5 -0
- package/package.json +10 -6
- package/data/evolclaw.sample.json +0 -60
- package/dist/agents/templates.js +0 -122
- package/dist/channels/aun-ops.js +0 -275
- package/dist/cli.js +0 -2178
- package/dist/config.js +0 -591
- package/dist/core/agent-registry.js +0 -450
- package/dist/core/evolagent-schema.js +0 -72
- package/dist/core/message/stream-flusher.js +0 -238
- package/dist/core/message/thought-emitter.js +0 -162
- package/dist/core/reload-hooks.js +0 -87
- package/dist/prompts/templates.js +0 -122
- package/dist/templates/prompts.md +0 -104
- package/dist/templates/skills.md +0 -66
- package/dist/utils/channel-fingerprint.js +0 -59
- package/dist/utils/error-dict.js +0 -63
- package/dist/utils/format.js +0 -32
- package/dist/utils/init.js +0 -645
- package/dist/utils/migrate-project.js +0 -122
- package/dist/utils/reload-hooks.js +0 -87
- package/dist/utils/stats-collector.js +0 -99
- package/dist/utils/upgrade.js +0 -100
|
@@ -10,48 +10,11 @@ import readline from 'readline';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
|
-
import { execFile } from 'child_process';
|
|
14
|
-
import { promisify } from 'util';
|
|
15
|
-
import { resolvePaths } from '../paths.js';
|
|
16
|
-
import { normalizeChannelInstances } from '../config.js';
|
|
17
13
|
import { selectInstance } from './init.js';
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const npmCmd = isWindows ? 'npm.cmd' : 'npm';
|
|
23
|
-
const execOpts = { timeout: 180000, shell: isWindows };
|
|
24
|
-
try {
|
|
25
|
-
await execFileAsync(npmCmd, ['install', '-g', pkg], execOpts);
|
|
26
|
-
}
|
|
27
|
-
catch (e) {
|
|
28
|
-
if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES')) {
|
|
29
|
-
if (isWindows) {
|
|
30
|
-
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
31
|
-
}
|
|
32
|
-
await execFileAsync('sudo', ['npm', 'install', '-g', pkg], { timeout: 180000 });
|
|
33
|
-
}
|
|
34
|
-
else {
|
|
35
|
-
throw e;
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
/** Dynamic import with auto-install fallback for optional dependencies */
|
|
40
|
-
export async function requireOptional(pkg, autoInstall = true) {
|
|
41
|
-
try {
|
|
42
|
-
return await import(pkg);
|
|
43
|
-
}
|
|
44
|
-
catch (e) {
|
|
45
|
-
if (e.code !== 'ERR_MODULE_NOT_FOUND' && e.code !== 'MODULE_NOT_FOUND')
|
|
46
|
-
throw e;
|
|
47
|
-
if (!autoInstall)
|
|
48
|
-
throw new Error(`依赖 ${pkg} 未安装。请运行: npm install -g ${pkg}`);
|
|
49
|
-
const { logger } = await import('./logger.js');
|
|
50
|
-
logger.info(`正在安装可选依赖 ${pkg}...`);
|
|
51
|
-
await npmInstallGlobal(pkg);
|
|
52
|
-
return await import(pkg);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
14
|
+
import { npmInstallGlobal } from '../utils/npm-ops.js';
|
|
15
|
+
import { loadAllAgents, loadAgent } from '../config-store.js';
|
|
16
|
+
import { agentChannelUpsert } from './agent.js';
|
|
17
|
+
import { AUN_CORE_SDK_PKG, MIN_AUN_CORE_SDK, resolveAunCoreSdkPkg, isAunSdkVersionOk, isValidAid, aidCreate, agentmdPut, buildInitialAgentMd, } from '../aun/aid/index.js';
|
|
55
18
|
function ask(rl, question) {
|
|
56
19
|
return new Promise(resolve => rl.question(question, resolve));
|
|
57
20
|
}
|
|
@@ -221,38 +184,28 @@ export async function runFeishuQrFlow() {
|
|
|
221
184
|
}
|
|
222
185
|
}
|
|
223
186
|
export async function cmdInitFeishu() {
|
|
224
|
-
const
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
|
|
230
|
-
// Normalize existing instances and filter out placeholders
|
|
231
|
-
const allInstances = normalizeChannelInstances(config.channels?.feishu, 'feishu');
|
|
232
|
-
const validInstances = [];
|
|
233
|
-
for (let i = 0; i < allInstances.length; i++) {
|
|
234
|
-
const inst = allInstances[i];
|
|
235
|
-
if (!inst.appId || !inst.appSecret)
|
|
236
|
-
continue;
|
|
237
|
-
if (inst.appId.includes('your-') || inst.appId.includes('placeholder'))
|
|
238
|
-
continue;
|
|
239
|
-
if (inst.appSecret.includes('your-') || inst.appSecret.includes('placeholder'))
|
|
240
|
-
continue;
|
|
241
|
-
validInstances.push({ ...inst, originalIndex: i });
|
|
242
|
-
}
|
|
187
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
188
|
+
let aid = null;
|
|
189
|
+
let agentConfig = null;
|
|
243
190
|
let choice = null;
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
rl.close();
|
|
191
|
+
try {
|
|
192
|
+
aid = await pickAgentForChannel(rl);
|
|
193
|
+
if (!aid)
|
|
194
|
+
return;
|
|
195
|
+
agentConfig = loadAgent(aid);
|
|
196
|
+
if (!agentConfig) {
|
|
197
|
+
console.error(`❌ 无法加载 agent ${aid} 的配置`);
|
|
198
|
+
return;
|
|
253
199
|
}
|
|
200
|
+
const existing = (agentConfig.channels || []).filter(c => c.type === 'feishu');
|
|
201
|
+
choice = await pickInstanceWithinAgent(rl, 'feishu', existing);
|
|
202
|
+
if (choice === null)
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
finally {
|
|
206
|
+
rl.close();
|
|
254
207
|
}
|
|
255
|
-
console.log('正在获取飞书登录二维码...\n');
|
|
208
|
+
console.log('\n正在获取飞书登录二维码...\n');
|
|
256
209
|
let result;
|
|
257
210
|
try {
|
|
258
211
|
const flowResult = await runQrRegistrationFlow();
|
|
@@ -261,12 +214,12 @@ export async function cmdInitFeishu() {
|
|
|
261
214
|
return;
|
|
262
215
|
}
|
|
263
216
|
if (flowResult === SKIP) {
|
|
264
|
-
const
|
|
217
|
+
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
265
218
|
try {
|
|
266
|
-
result = await manualInput(
|
|
219
|
+
result = await manualInput(rl2);
|
|
267
220
|
}
|
|
268
221
|
finally {
|
|
269
|
-
|
|
222
|
+
rl2.close();
|
|
270
223
|
}
|
|
271
224
|
}
|
|
272
225
|
else {
|
|
@@ -277,77 +230,20 @@ export async function cmdInitFeishu() {
|
|
|
277
230
|
console.error(`\n登录失败: ${error instanceof Error ? error.message : error}`);
|
|
278
231
|
process.exit(1);
|
|
279
232
|
}
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
if (result.openId)
|
|
290
|
-
config.channels.feishu[idx].owner = result.openId;
|
|
291
|
-
}
|
|
292
|
-
else if (choice && choice.action === 'overwrite' && !Array.isArray(config.channels.feishu)) {
|
|
293
|
-
// Overwrite single-object
|
|
294
|
-
config.channels.feishu = config.channels.feishu || {};
|
|
295
|
-
config.channels.feishu.appId = result.appId;
|
|
296
|
-
config.channels.feishu.appSecret = result.appSecret;
|
|
297
|
-
config.channels.feishu.enabled = true;
|
|
298
|
-
if (result.openId)
|
|
299
|
-
config.channels.feishu.owner = result.openId;
|
|
300
|
-
else
|
|
301
|
-
delete config.channels.feishu.owner;
|
|
302
|
-
}
|
|
303
|
-
else if (choice && choice.action === 'add') {
|
|
304
|
-
// Add new instance — upgrade to array if needed
|
|
305
|
-
const newInst = {
|
|
306
|
-
name: choice.name,
|
|
307
|
-
appId: result.appId,
|
|
308
|
-
appSecret: result.appSecret,
|
|
309
|
-
enabled: true,
|
|
310
|
-
...(result.openId ? { owner: result.openId } : {}),
|
|
311
|
-
};
|
|
312
|
-
if (Array.isArray(config.channels.feishu)) {
|
|
313
|
-
config.channels.feishu.push(newInst);
|
|
314
|
-
}
|
|
315
|
-
else if (config.channels.feishu) {
|
|
316
|
-
// Upgrade single object to array
|
|
317
|
-
const oldInst = { ...config.channels.feishu, name: config.channels.feishu.name || 'feishu' };
|
|
318
|
-
config.channels.feishu = [oldInst, newInst];
|
|
319
|
-
}
|
|
320
|
-
else {
|
|
321
|
-
config.channels.feishu = [newInst];
|
|
322
|
-
}
|
|
323
|
-
}
|
|
324
|
-
else {
|
|
325
|
-
// First instance — single object format (backward compat)
|
|
326
|
-
config.channels.feishu = config.channels.feishu || {};
|
|
327
|
-
config.channels.feishu.appId = result.appId;
|
|
328
|
-
config.channels.feishu.appSecret = result.appSecret;
|
|
329
|
-
config.channels.feishu.enabled = true;
|
|
330
|
-
if (result.openId)
|
|
331
|
-
config.channels.feishu.owner = result.openId;
|
|
332
|
-
else
|
|
333
|
-
delete config.channels.feishu.owner;
|
|
334
|
-
}
|
|
335
|
-
if (!config.channels.defaultChannel)
|
|
336
|
-
config.channels.defaultChannel = 'feishu';
|
|
337
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
338
|
-
console.log(`\n✅ 飞书连接成功!`);
|
|
233
|
+
const channel = {
|
|
234
|
+
type: 'feishu',
|
|
235
|
+
name: choice.name,
|
|
236
|
+
enabled: true,
|
|
237
|
+
appId: result.appId,
|
|
238
|
+
appSecret: result.appSecret,
|
|
239
|
+
...(result.openId ? { owners: [result.openId] } : {}),
|
|
240
|
+
};
|
|
241
|
+
await commitChannel(aid, channel, choice.action);
|
|
339
242
|
console.log(` App ID: ${result.appId}`);
|
|
340
|
-
if (result.openId)
|
|
243
|
+
if (result.openId)
|
|
341
244
|
console.log(` Owner: ${result.openId}`);
|
|
342
|
-
|
|
343
|
-
if (result.domain !== 'unknown') {
|
|
245
|
+
if (result.domain !== 'unknown')
|
|
344
246
|
console.log(` Domain: ${result.domain}`);
|
|
345
|
-
}
|
|
346
|
-
if (choice) {
|
|
347
|
-
console.log(` 实例: ${choice.name} (${choice.action === 'add' ? '新增' : '覆盖'})`);
|
|
348
|
-
}
|
|
349
|
-
console.log(` 配置已写入: ${p.config}`);
|
|
350
|
-
console.log(`\n现在可以启动服务: evolclaw restart`);
|
|
351
247
|
}
|
|
352
248
|
// ==================== WeChat ====================
|
|
353
249
|
const DEFAULT_BASE_URL = 'https://ilinkai.weixin.qq.com';
|
|
@@ -439,145 +335,41 @@ export async function runWechatQrFlow() {
|
|
|
439
335
|
return null;
|
|
440
336
|
}
|
|
441
337
|
export async function cmdInitWechat() {
|
|
442
|
-
const
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
|
|
448
|
-
// Normalize existing instances and filter out placeholders
|
|
449
|
-
const allInstances = normalizeChannelInstances(config.channels?.wechat, 'wechat');
|
|
450
|
-
const validInstances = [];
|
|
451
|
-
for (let i = 0; i < allInstances.length; i++) {
|
|
452
|
-
const inst = allInstances[i];
|
|
453
|
-
if (!inst.token)
|
|
454
|
-
continue;
|
|
455
|
-
if (inst.token.includes('your-') || inst.token.includes('placeholder'))
|
|
456
|
-
continue;
|
|
457
|
-
validInstances.push({ ...inst, originalIndex: i });
|
|
458
|
-
}
|
|
338
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
339
|
+
let aid = null;
|
|
340
|
+
let agentConfig = null;
|
|
459
341
|
let choice = null;
|
|
460
|
-
if (validInstances.length > 0) {
|
|
461
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
462
|
-
try {
|
|
463
|
-
choice = await selectInstance(rl, 'wechat', validInstances);
|
|
464
|
-
if (choice === null)
|
|
465
|
-
return; // user cancelled
|
|
466
|
-
}
|
|
467
|
-
finally {
|
|
468
|
-
rl.close();
|
|
469
|
-
}
|
|
470
|
-
}
|
|
471
|
-
console.log('正在获取微信登录二维码...\n');
|
|
472
|
-
const qrResp = await fetchQRCode(DEFAULT_BASE_URL);
|
|
473
|
-
// 终端显示二维码
|
|
474
342
|
try {
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
});
|
|
481
|
-
|
|
343
|
+
aid = await pickAgentForChannel(rl);
|
|
344
|
+
if (!aid)
|
|
345
|
+
return;
|
|
346
|
+
agentConfig = loadAgent(aid);
|
|
347
|
+
if (!agentConfig) {
|
|
348
|
+
console.error(`❌ 无法加载 agent ${aid} 的配置`);
|
|
349
|
+
return;
|
|
350
|
+
}
|
|
351
|
+
const existing = (agentConfig.channels || []).filter(c => c.type === 'wechat');
|
|
352
|
+
choice = await pickInstanceWithinAgent(rl, 'wechat', existing);
|
|
353
|
+
if (choice === null)
|
|
354
|
+
return;
|
|
482
355
|
}
|
|
483
|
-
|
|
484
|
-
|
|
356
|
+
finally {
|
|
357
|
+
rl.close();
|
|
485
358
|
}
|
|
486
|
-
console.log('
|
|
487
|
-
const
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
const status = await pollQRStatus(currentPollUrl, qrResp.qrcode);
|
|
492
|
-
switch (status.status) {
|
|
493
|
-
case 'wait':
|
|
494
|
-
process.stdout.write('.');
|
|
495
|
-
break;
|
|
496
|
-
case 'scaned':
|
|
497
|
-
if (!scannedPrinted) {
|
|
498
|
-
console.log('\n\ud83d\udc40 已扫码,请在微信中确认...');
|
|
499
|
-
scannedPrinted = true;
|
|
500
|
-
}
|
|
501
|
-
break;
|
|
502
|
-
case 'scaned_but_redirect':
|
|
503
|
-
if (status.redirect_host) {
|
|
504
|
-
currentPollUrl = `https://${status.redirect_host}`;
|
|
505
|
-
}
|
|
506
|
-
break;
|
|
507
|
-
case 'expired':
|
|
508
|
-
console.log('\n二维码已过期,请重新运行 evolclaw init wechat');
|
|
509
|
-
process.exit(1);
|
|
510
|
-
break;
|
|
511
|
-
case 'confirmed': {
|
|
512
|
-
if (!status.ilink_bot_id || !status.bot_token) {
|
|
513
|
-
console.error('\n登录失败:服务器未返回完整信息');
|
|
514
|
-
process.exit(1);
|
|
515
|
-
}
|
|
516
|
-
const baseUrl = status.baseurl || DEFAULT_BASE_URL;
|
|
517
|
-
const token = status.bot_token;
|
|
518
|
-
// Write config to the correct slot
|
|
519
|
-
if (!config.channels)
|
|
520
|
-
config.channels = {};
|
|
521
|
-
if (choice && choice.action === 'overwrite' && Array.isArray(config.channels.wechat)) {
|
|
522
|
-
// Overwrite existing instance in array — use originalIndex to find the right slot
|
|
523
|
-
const idx = validInstances[choice.index]?.originalIndex ?? choice.index;
|
|
524
|
-
config.channels.wechat[idx].enabled = true;
|
|
525
|
-
config.channels.wechat[idx].baseUrl = baseUrl;
|
|
526
|
-
config.channels.wechat[idx].token = token;
|
|
527
|
-
}
|
|
528
|
-
else if (choice && choice.action === 'overwrite' && !Array.isArray(config.channels.wechat)) {
|
|
529
|
-
// Overwrite single-object
|
|
530
|
-
config.channels.wechat = config.channels.wechat || {};
|
|
531
|
-
config.channels.wechat.enabled = true;
|
|
532
|
-
config.channels.wechat.baseUrl = baseUrl;
|
|
533
|
-
config.channels.wechat.token = token;
|
|
534
|
-
}
|
|
535
|
-
else if (choice && choice.action === 'add') {
|
|
536
|
-
// Add new instance — upgrade to array if needed
|
|
537
|
-
const newInst = {
|
|
538
|
-
name: choice.name,
|
|
539
|
-
enabled: true,
|
|
540
|
-
baseUrl,
|
|
541
|
-
token,
|
|
542
|
-
};
|
|
543
|
-
if (Array.isArray(config.channels.wechat)) {
|
|
544
|
-
config.channels.wechat.push(newInst);
|
|
545
|
-
}
|
|
546
|
-
else if (config.channels.wechat) {
|
|
547
|
-
// Upgrade single object to array
|
|
548
|
-
const oldInst = { ...config.channels.wechat, name: config.channels.wechat.name || 'wechat' };
|
|
549
|
-
config.channels.wechat = [oldInst, newInst];
|
|
550
|
-
}
|
|
551
|
-
else {
|
|
552
|
-
config.channels.wechat = [newInst];
|
|
553
|
-
}
|
|
554
|
-
}
|
|
555
|
-
else {
|
|
556
|
-
// First instance — single object format (backward compat)
|
|
557
|
-
config.channels.wechat = {
|
|
558
|
-
enabled: true,
|
|
559
|
-
baseUrl,
|
|
560
|
-
token,
|
|
561
|
-
};
|
|
562
|
-
}
|
|
563
|
-
if (!config.channels.defaultChannel)
|
|
564
|
-
config.channels.defaultChannel = 'wechat';
|
|
565
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
566
|
-
console.log(`\n✅ 微信连接成功!`);
|
|
567
|
-
console.log(` Bot ID: ${status.ilink_bot_id}`);
|
|
568
|
-
console.log(` User ID: ${status.ilink_user_id}`);
|
|
569
|
-
if (choice) {
|
|
570
|
-
console.log(` 实例: ${choice.name} (${choice.action === 'add' ? '新增' : '覆盖'})`);
|
|
571
|
-
}
|
|
572
|
-
console.log(` 配置已写入: ${p.config}`);
|
|
573
|
-
console.log(`\n现在可以启动服务: evolclaw restart`);
|
|
574
|
-
return;
|
|
575
|
-
}
|
|
576
|
-
}
|
|
577
|
-
await new Promise(r => setTimeout(r, 1000));
|
|
359
|
+
console.log('\n正在获取微信登录二维码...\n');
|
|
360
|
+
const result = await runWechatQrFlow();
|
|
361
|
+
if (!result) {
|
|
362
|
+
console.log('已取消');
|
|
363
|
+
return;
|
|
578
364
|
}
|
|
579
|
-
|
|
580
|
-
|
|
365
|
+
const channel = {
|
|
366
|
+
type: 'wechat',
|
|
367
|
+
name: choice.name,
|
|
368
|
+
enabled: true,
|
|
369
|
+
baseUrl: result.baseUrl,
|
|
370
|
+
token: result.token,
|
|
371
|
+
};
|
|
372
|
+
await commitChannel(aid, channel, choice.action);
|
|
581
373
|
}
|
|
582
374
|
// ==================== AUN ====================
|
|
583
375
|
//
|
|
@@ -726,90 +518,6 @@ export async function setupAunAid(rl, _config) {
|
|
|
726
518
|
}
|
|
727
519
|
return { aid, owner };
|
|
728
520
|
}
|
|
729
|
-
export async function cmdInitAun() {
|
|
730
|
-
const p = resolvePaths();
|
|
731
|
-
if (!fs.existsSync(p.config)) {
|
|
732
|
-
console.log('❌ 配置文件不存在,请先运行 evolclaw init');
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
|
|
736
|
-
// Normalize existing instances and filter out placeholders
|
|
737
|
-
const allInstances = normalizeChannelInstances(config.channels?.aun, 'aun');
|
|
738
|
-
const validInstances = [];
|
|
739
|
-
for (let i = 0; i < allInstances.length; i++) {
|
|
740
|
-
const inst = allInstances[i];
|
|
741
|
-
if (!inst.aid || inst.aid.includes('your-') || inst.aid.includes('placeholder'))
|
|
742
|
-
continue;
|
|
743
|
-
validInstances.push({ ...inst, originalIndex: i });
|
|
744
|
-
}
|
|
745
|
-
let choice = null;
|
|
746
|
-
if (validInstances.length > 0) {
|
|
747
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
748
|
-
try {
|
|
749
|
-
choice = await selectInstance(rl, 'aun', validInstances);
|
|
750
|
-
if (choice === null)
|
|
751
|
-
return;
|
|
752
|
-
}
|
|
753
|
-
finally {
|
|
754
|
-
rl.close();
|
|
755
|
-
}
|
|
756
|
-
}
|
|
757
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
758
|
-
try {
|
|
759
|
-
if (!await checkAunEnvironment(rl)) {
|
|
760
|
-
return;
|
|
761
|
-
}
|
|
762
|
-
const result = await setupAunAid(rl, config);
|
|
763
|
-
if (!result)
|
|
764
|
-
return;
|
|
765
|
-
if (!config.channels)
|
|
766
|
-
config.channels = {};
|
|
767
|
-
if (choice && choice.action === 'overwrite' && Array.isArray(config.channels.aun)) {
|
|
768
|
-
const idx = validInstances[choice.index]?.originalIndex ?? choice.index;
|
|
769
|
-
config.channels.aun[idx].aid = result.aid;
|
|
770
|
-
config.channels.aun[idx].owner = result.owner;
|
|
771
|
-
config.channels.aun[idx].enabled = true;
|
|
772
|
-
}
|
|
773
|
-
else if (choice && choice.action === 'overwrite' && !Array.isArray(config.channels.aun)) {
|
|
774
|
-
config.channels.aun = config.channels.aun || {};
|
|
775
|
-
config.channels.aun.aid = result.aid;
|
|
776
|
-
config.channels.aun.owner = result.owner;
|
|
777
|
-
config.channels.aun.enabled = true;
|
|
778
|
-
}
|
|
779
|
-
else if (choice && choice.action === 'add') {
|
|
780
|
-
const newInst = {
|
|
781
|
-
name: choice.name,
|
|
782
|
-
enabled: true,
|
|
783
|
-
aid: result.aid,
|
|
784
|
-
owner: result.owner,
|
|
785
|
-
};
|
|
786
|
-
if (Array.isArray(config.channels.aun)) {
|
|
787
|
-
config.channels.aun.push(newInst);
|
|
788
|
-
}
|
|
789
|
-
else if (config.channels.aun) {
|
|
790
|
-
const oldInst = { ...config.channels.aun, name: config.channels.aun.name || 'aun' };
|
|
791
|
-
config.channels.aun = [oldInst, newInst];
|
|
792
|
-
}
|
|
793
|
-
else {
|
|
794
|
-
config.channels.aun = [newInst];
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
|
-
else {
|
|
798
|
-
config.channels.aun = {
|
|
799
|
-
enabled: true,
|
|
800
|
-
aid: result.aid,
|
|
801
|
-
owner: result.owner,
|
|
802
|
-
};
|
|
803
|
-
}
|
|
804
|
-
if (!config.channels.defaultChannel)
|
|
805
|
-
config.channels.defaultChannel = 'aun';
|
|
806
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
807
|
-
console.log('\n✓ AUN 配置已写入');
|
|
808
|
-
}
|
|
809
|
-
finally {
|
|
810
|
-
rl.close();
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
521
|
// ==================== DingTalk ====================
|
|
814
522
|
const DINGTALK_BASE_URL = 'https://oapi.dingtalk.com';
|
|
815
523
|
const DINGTALK_SOURCE = 'openClaw';
|
|
@@ -929,38 +637,28 @@ export async function runDingtalkQrFlowSimple() {
|
|
|
929
637
|
}
|
|
930
638
|
}
|
|
931
639
|
export async function cmdInitDingtalk() {
|
|
932
|
-
const
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
return;
|
|
936
|
-
}
|
|
937
|
-
const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
|
|
938
|
-
// Normalize existing instances and filter out placeholders
|
|
939
|
-
const allInstances = normalizeChannelInstances(config.channels?.dingtalk, 'dingtalk');
|
|
940
|
-
const validInstances = [];
|
|
941
|
-
for (let i = 0; i < allInstances.length; i++) {
|
|
942
|
-
const inst = allInstances[i];
|
|
943
|
-
if (!inst.clientId || !inst.clientSecret)
|
|
944
|
-
continue;
|
|
945
|
-
if (inst.clientId.includes('your-') || inst.clientId.includes('placeholder'))
|
|
946
|
-
continue;
|
|
947
|
-
if (inst.clientSecret.includes('your-') || inst.clientSecret.includes('placeholder'))
|
|
948
|
-
continue;
|
|
949
|
-
validInstances.push({ ...inst, originalIndex: i });
|
|
950
|
-
}
|
|
640
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
641
|
+
let aid = null;
|
|
642
|
+
let agentConfig = null;
|
|
951
643
|
let choice = null;
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
rl.close();
|
|
644
|
+
try {
|
|
645
|
+
aid = await pickAgentForChannel(rl);
|
|
646
|
+
if (!aid)
|
|
647
|
+
return;
|
|
648
|
+
agentConfig = loadAgent(aid);
|
|
649
|
+
if (!agentConfig) {
|
|
650
|
+
console.error(`❌ 无法加载 agent ${aid} 的配置`);
|
|
651
|
+
return;
|
|
961
652
|
}
|
|
653
|
+
const existing = (agentConfig.channels || []).filter(c => c.type === 'dingtalk');
|
|
654
|
+
choice = await pickInstanceWithinAgent(rl, 'dingtalk', existing);
|
|
655
|
+
if (choice === null)
|
|
656
|
+
return;
|
|
962
657
|
}
|
|
963
|
-
|
|
658
|
+
finally {
|
|
659
|
+
rl.close();
|
|
660
|
+
}
|
|
661
|
+
console.log('\n正在获取钉钉登录二维码...\n');
|
|
964
662
|
let result;
|
|
965
663
|
try {
|
|
966
664
|
const flowResult = await runDingtalkQrFlow();
|
|
@@ -969,25 +667,25 @@ export async function cmdInitDingtalk() {
|
|
|
969
667
|
return;
|
|
970
668
|
}
|
|
971
669
|
if (flowResult === SKIP) {
|
|
972
|
-
const
|
|
670
|
+
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
973
671
|
try {
|
|
974
672
|
console.log('\n手动输入模式:\n');
|
|
975
673
|
let clientId = '';
|
|
976
674
|
while (!clientId) {
|
|
977
|
-
clientId = (await ask(
|
|
675
|
+
clientId = (await ask(rl2, ' 钉钉 Client ID (AppKey): ')).trim();
|
|
978
676
|
if (!clientId)
|
|
979
677
|
console.log(' ⚠ 不能为空');
|
|
980
678
|
}
|
|
981
679
|
let clientSecret = '';
|
|
982
680
|
while (!clientSecret) {
|
|
983
|
-
clientSecret = (await ask(
|
|
681
|
+
clientSecret = (await ask(rl2, ' 钉钉 Client Secret (AppSecret): ')).trim();
|
|
984
682
|
if (!clientSecret)
|
|
985
683
|
console.log(' ⚠ 不能为空');
|
|
986
684
|
}
|
|
987
685
|
result = { clientId, clientSecret };
|
|
988
686
|
}
|
|
989
687
|
finally {
|
|
990
|
-
|
|
688
|
+
rl2.close();
|
|
991
689
|
}
|
|
992
690
|
}
|
|
993
691
|
else {
|
|
@@ -998,56 +696,15 @@ export async function cmdInitDingtalk() {
|
|
|
998
696
|
console.error(`\n登录失败: ${error instanceof Error ? error.message : error}`);
|
|
999
697
|
process.exit(1);
|
|
1000
698
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
}
|
|
1010
|
-
else if (choice && choice.action === 'overwrite' && !Array.isArray(config.channels.dingtalk)) {
|
|
1011
|
-
config.channels.dingtalk = config.channels.dingtalk || {};
|
|
1012
|
-
config.channels.dingtalk.clientId = result.clientId;
|
|
1013
|
-
config.channels.dingtalk.clientSecret = result.clientSecret;
|
|
1014
|
-
config.channels.dingtalk.enabled = true;
|
|
1015
|
-
}
|
|
1016
|
-
else if (choice && choice.action === 'add') {
|
|
1017
|
-
const newInst = {
|
|
1018
|
-
name: choice.name,
|
|
1019
|
-
clientId: result.clientId,
|
|
1020
|
-
clientSecret: result.clientSecret,
|
|
1021
|
-
enabled: true,
|
|
1022
|
-
};
|
|
1023
|
-
if (Array.isArray(config.channels.dingtalk)) {
|
|
1024
|
-
config.channels.dingtalk.push(newInst);
|
|
1025
|
-
}
|
|
1026
|
-
else if (config.channels.dingtalk) {
|
|
1027
|
-
const oldInst = { ...config.channels.dingtalk, name: config.channels.dingtalk.name || 'dingtalk' };
|
|
1028
|
-
config.channels.dingtalk = [oldInst, newInst];
|
|
1029
|
-
}
|
|
1030
|
-
else {
|
|
1031
|
-
config.channels.dingtalk = [newInst];
|
|
1032
|
-
}
|
|
1033
|
-
}
|
|
1034
|
-
else {
|
|
1035
|
-
config.channels.dingtalk = {
|
|
1036
|
-
clientId: result.clientId,
|
|
1037
|
-
clientSecret: result.clientSecret,
|
|
1038
|
-
enabled: true,
|
|
1039
|
-
};
|
|
1040
|
-
}
|
|
1041
|
-
if (!config.channels.defaultChannel)
|
|
1042
|
-
config.channels.defaultChannel = 'dingtalk';
|
|
1043
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
1044
|
-
console.log(`\n✅ 钉钉连接成功!`);
|
|
699
|
+
const channel = {
|
|
700
|
+
type: 'dingtalk',
|
|
701
|
+
name: choice.name,
|
|
702
|
+
enabled: true,
|
|
703
|
+
clientId: result.clientId,
|
|
704
|
+
clientSecret: result.clientSecret,
|
|
705
|
+
};
|
|
706
|
+
await commitChannel(aid, channel, choice.action);
|
|
1045
707
|
console.log(` Client ID: ${result.clientId}`);
|
|
1046
|
-
if (choice) {
|
|
1047
|
-
console.log(` 实例: ${choice.name} (${choice.action === 'add' ? '新增' : '覆盖'})`);
|
|
1048
|
-
}
|
|
1049
|
-
console.log(` 配置已写入: ${p.config}`);
|
|
1050
|
-
console.log(`\n现在可以启动服务: evolclaw restart`);
|
|
1051
708
|
}
|
|
1052
709
|
// ==================== QQBot ====================
|
|
1053
710
|
const QQBOT_PORTAL_HOST = 'q.qq.com';
|
|
@@ -1185,38 +842,28 @@ export async function runQQBotBindFlowSimple() {
|
|
|
1185
842
|
}
|
|
1186
843
|
}
|
|
1187
844
|
export async function cmdInitQQBot() {
|
|
1188
|
-
const
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
return;
|
|
1192
|
-
}
|
|
1193
|
-
const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
|
|
1194
|
-
// Normalize existing instances and filter out placeholders
|
|
1195
|
-
const allInstances = normalizeChannelInstances(config.channels?.qqbot, 'qqbot');
|
|
1196
|
-
const validInstances = [];
|
|
1197
|
-
for (let i = 0; i < allInstances.length; i++) {
|
|
1198
|
-
const inst = allInstances[i];
|
|
1199
|
-
if (!inst.appId || !inst.clientSecret)
|
|
1200
|
-
continue;
|
|
1201
|
-
if (inst.appId.includes('your-') || inst.appId.includes('placeholder'))
|
|
1202
|
-
continue;
|
|
1203
|
-
if (inst.clientSecret.includes('your-') || inst.clientSecret.includes('placeholder'))
|
|
1204
|
-
continue;
|
|
1205
|
-
validInstances.push({ ...inst, originalIndex: i });
|
|
1206
|
-
}
|
|
845
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
846
|
+
let aid = null;
|
|
847
|
+
let agentConfig = null;
|
|
1207
848
|
let choice = null;
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
rl.close();
|
|
849
|
+
try {
|
|
850
|
+
aid = await pickAgentForChannel(rl);
|
|
851
|
+
if (!aid)
|
|
852
|
+
return;
|
|
853
|
+
agentConfig = loadAgent(aid);
|
|
854
|
+
if (!agentConfig) {
|
|
855
|
+
console.error(`❌ 无法加载 agent ${aid} 的配置`);
|
|
856
|
+
return;
|
|
1217
857
|
}
|
|
858
|
+
const existing = (agentConfig.channels || []).filter(c => c.type === 'qqbot');
|
|
859
|
+
choice = await pickInstanceWithinAgent(rl, 'qqbot', existing);
|
|
860
|
+
if (choice === null)
|
|
861
|
+
return;
|
|
862
|
+
}
|
|
863
|
+
finally {
|
|
864
|
+
rl.close();
|
|
1218
865
|
}
|
|
1219
|
-
console.log('正在创建 QQ 机器人绑定任务...\n');
|
|
866
|
+
console.log('\n正在创建 QQ 机器人绑定任务...\n');
|
|
1220
867
|
let result;
|
|
1221
868
|
try {
|
|
1222
869
|
const flowResult = await runQQBotBindFlow();
|
|
@@ -1225,25 +872,25 @@ export async function cmdInitQQBot() {
|
|
|
1225
872
|
return;
|
|
1226
873
|
}
|
|
1227
874
|
if (flowResult === SKIP) {
|
|
1228
|
-
const
|
|
875
|
+
const rl2 = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1229
876
|
try {
|
|
1230
877
|
console.log('\n手动输入模式:\n');
|
|
1231
878
|
let appId = '';
|
|
1232
879
|
while (!appId) {
|
|
1233
|
-
appId = (await ask(
|
|
880
|
+
appId = (await ask(rl2, ' QQ 机器人 App ID: ')).trim();
|
|
1234
881
|
if (!appId)
|
|
1235
882
|
console.log(' ⚠ 不能为空');
|
|
1236
883
|
}
|
|
1237
884
|
let clientSecret = '';
|
|
1238
885
|
while (!clientSecret) {
|
|
1239
|
-
clientSecret = (await ask(
|
|
886
|
+
clientSecret = (await ask(rl2, ' QQ 机器人 Client Secret: ')).trim();
|
|
1240
887
|
if (!clientSecret)
|
|
1241
888
|
console.log(' ⚠ 不能为空');
|
|
1242
889
|
}
|
|
1243
890
|
result = { appId, clientSecret };
|
|
1244
891
|
}
|
|
1245
892
|
finally {
|
|
1246
|
-
|
|
893
|
+
rl2.close();
|
|
1247
894
|
}
|
|
1248
895
|
}
|
|
1249
896
|
else {
|
|
@@ -1254,163 +901,121 @@ export async function cmdInitQQBot() {
|
|
|
1254
901
|
console.error(`\n绑定失败: ${error instanceof Error ? error.message : error}`);
|
|
1255
902
|
process.exit(1);
|
|
1256
903
|
}
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
}
|
|
1266
|
-
else if (choice && choice.action === 'overwrite' && !Array.isArray(config.channels.qqbot)) {
|
|
1267
|
-
config.channels.qqbot = config.channels.qqbot || {};
|
|
1268
|
-
config.channels.qqbot.appId = result.appId;
|
|
1269
|
-
config.channels.qqbot.clientSecret = result.clientSecret;
|
|
1270
|
-
config.channels.qqbot.enabled = true;
|
|
1271
|
-
}
|
|
1272
|
-
else if (choice && choice.action === 'add') {
|
|
1273
|
-
const newInst = {
|
|
1274
|
-
name: choice.name,
|
|
1275
|
-
appId: result.appId,
|
|
1276
|
-
clientSecret: result.clientSecret,
|
|
1277
|
-
enabled: true,
|
|
1278
|
-
};
|
|
1279
|
-
if (Array.isArray(config.channels.qqbot)) {
|
|
1280
|
-
config.channels.qqbot.push(newInst);
|
|
1281
|
-
}
|
|
1282
|
-
else if (config.channels.qqbot) {
|
|
1283
|
-
const oldInst = { ...config.channels.qqbot, name: config.channels.qqbot.name || 'qqbot' };
|
|
1284
|
-
config.channels.qqbot = [oldInst, newInst];
|
|
1285
|
-
}
|
|
1286
|
-
else {
|
|
1287
|
-
config.channels.qqbot = [newInst];
|
|
1288
|
-
}
|
|
1289
|
-
}
|
|
1290
|
-
else {
|
|
1291
|
-
config.channels.qqbot = {
|
|
1292
|
-
appId: result.appId,
|
|
1293
|
-
clientSecret: result.clientSecret,
|
|
1294
|
-
enabled: true,
|
|
1295
|
-
};
|
|
1296
|
-
}
|
|
1297
|
-
if (!config.channels.defaultChannel)
|
|
1298
|
-
config.channels.defaultChannel = 'qqbot';
|
|
1299
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
1300
|
-
console.log(`\n✅ QQ 机器人绑定成功!`);
|
|
904
|
+
const channel = {
|
|
905
|
+
type: 'qqbot',
|
|
906
|
+
name: choice.name,
|
|
907
|
+
enabled: true,
|
|
908
|
+
appId: result.appId,
|
|
909
|
+
clientSecret: result.clientSecret,
|
|
910
|
+
};
|
|
911
|
+
await commitChannel(aid, channel, choice.action);
|
|
1301
912
|
console.log(` App ID: ${result.appId}`);
|
|
1302
|
-
if (choice) {
|
|
1303
|
-
console.log(` 实例: ${choice.name} (${choice.action === 'add' ? '新增' : '覆盖'})`);
|
|
1304
|
-
}
|
|
1305
|
-
console.log(` 配置已写入: ${p.config}`);
|
|
1306
|
-
console.log(`\n现在可以启动服务: evolclaw restart`);
|
|
1307
913
|
}
|
|
1308
914
|
// ==================== WeCom (企业微信) ====================
|
|
1309
915
|
export async function cmdInitWecom() {
|
|
1310
|
-
const p = resolvePaths();
|
|
1311
|
-
if (!fs.existsSync(p.config)) {
|
|
1312
|
-
console.log('❌ 配置文件不存在,请先运行 evolclaw init');
|
|
1313
|
-
return;
|
|
1314
|
-
}
|
|
1315
|
-
const config = JSON.parse(fs.readFileSync(p.config, 'utf-8'));
|
|
1316
|
-
// Normalize existing instances and filter out placeholders
|
|
1317
|
-
const allInstances = normalizeChannelInstances(config.channels?.wecom, 'wecom');
|
|
1318
|
-
const validInstances = [];
|
|
1319
|
-
for (let i = 0; i < allInstances.length; i++) {
|
|
1320
|
-
const inst = allInstances[i];
|
|
1321
|
-
if (!inst.botId || !inst.secret)
|
|
1322
|
-
continue;
|
|
1323
|
-
if (inst.botId.includes('your-') || inst.botId.includes('placeholder'))
|
|
1324
|
-
continue;
|
|
1325
|
-
if (inst.secret.includes('your-') || inst.secret.includes('placeholder'))
|
|
1326
|
-
continue;
|
|
1327
|
-
validInstances.push({ ...inst, originalIndex: i });
|
|
1328
|
-
}
|
|
1329
|
-
let choice = null;
|
|
1330
|
-
if (validInstances.length > 0) {
|
|
1331
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1332
|
-
try {
|
|
1333
|
-
choice = await selectInstance(rl, 'wecom', validInstances);
|
|
1334
|
-
if (choice === null)
|
|
1335
|
-
return;
|
|
1336
|
-
}
|
|
1337
|
-
finally {
|
|
1338
|
-
rl.close();
|
|
1339
|
-
}
|
|
1340
|
-
}
|
|
1341
|
-
// WeCom uses manual input only (no QR flow)
|
|
1342
916
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1343
|
-
let
|
|
917
|
+
let aid = null;
|
|
918
|
+
let agentConfig = null;
|
|
919
|
+
let choice = null;
|
|
920
|
+
let botId = '';
|
|
921
|
+
let secret = '';
|
|
1344
922
|
try {
|
|
1345
|
-
|
|
923
|
+
aid = await pickAgentForChannel(rl);
|
|
924
|
+
if (!aid)
|
|
925
|
+
return;
|
|
926
|
+
agentConfig = loadAgent(aid);
|
|
927
|
+
if (!agentConfig) {
|
|
928
|
+
console.error(`❌ 无法加载 agent ${aid} 的配置`);
|
|
929
|
+
return;
|
|
930
|
+
}
|
|
931
|
+
const existing = (agentConfig.channels || []).filter(c => c.type === 'wecom');
|
|
932
|
+
choice = await pickInstanceWithinAgent(rl, 'wecom', existing);
|
|
933
|
+
if (choice === null)
|
|
934
|
+
return;
|
|
935
|
+
console.log('\n企业微信 AI Bot 配置');
|
|
1346
936
|
console.log('请在企业微信管理后台 → AI Bot 页面获取 Bot ID 和 Secret\n');
|
|
1347
|
-
let botId = '';
|
|
1348
937
|
while (!botId) {
|
|
1349
938
|
botId = (await ask(rl, ' Bot ID: ')).trim();
|
|
1350
939
|
if (!botId)
|
|
1351
940
|
console.log(' ⚠ 不能为空');
|
|
1352
941
|
}
|
|
1353
|
-
let secret = '';
|
|
1354
942
|
while (!secret) {
|
|
1355
943
|
secret = (await ask(rl, ' Secret: ')).trim();
|
|
1356
944
|
if (!secret)
|
|
1357
945
|
console.log(' ⚠ 不能为空');
|
|
1358
946
|
}
|
|
1359
|
-
result = { botId, secret };
|
|
1360
947
|
}
|
|
1361
948
|
finally {
|
|
1362
949
|
rl.close();
|
|
1363
950
|
}
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
951
|
+
const channel = {
|
|
952
|
+
type: 'wecom',
|
|
953
|
+
name: choice.name,
|
|
954
|
+
enabled: true,
|
|
955
|
+
botId,
|
|
956
|
+
secret,
|
|
957
|
+
};
|
|
958
|
+
await commitChannel(aid, channel, choice.action);
|
|
959
|
+
console.log(` Bot ID: ${botId}`);
|
|
960
|
+
}
|
|
961
|
+
// ==================== Shared helpers for per-agent init <channel> ====================
|
|
962
|
+
/**
|
|
963
|
+
* Pick the target agent for an `evolclaw init <channel>` flow.
|
|
964
|
+
*
|
|
965
|
+
* - 0 agents → print guidance and return null
|
|
966
|
+
* - ≥1 → letter menu; with 1 agent the prompt accepts Enter (defaults to 'a')
|
|
967
|
+
*/
|
|
968
|
+
async function pickAgentForChannel(rl) {
|
|
969
|
+
const { agents } = loadAllAgents();
|
|
970
|
+
if (agents.length === 0) {
|
|
971
|
+
console.log('❌ 暂无 agent,请先创建:');
|
|
972
|
+
console.log(' evolclaw agent new <aid>.agentid.pub');
|
|
973
|
+
return null;
|
|
974
|
+
}
|
|
975
|
+
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
976
|
+
console.log(`共 ${agents.length} 个 agent:`);
|
|
977
|
+
for (let i = 0; i < agents.length; i++) {
|
|
978
|
+
console.log(` ${letters[i]}. ${agents[i].aid}`);
|
|
979
|
+
}
|
|
980
|
+
const valid = letters.slice(0, agents.length).split('');
|
|
981
|
+
const promptSuffix = agents.length === 1 ? ' [a]' : '';
|
|
982
|
+
let choice = '';
|
|
983
|
+
while (!valid.includes(choice)) {
|
|
984
|
+
choice = (await ask(rl, `请选择${promptSuffix}: `)).trim().toLowerCase();
|
|
985
|
+
if (agents.length === 1 && choice === '')
|
|
986
|
+
choice = 'a';
|
|
987
|
+
if (!valid.includes(choice)) {
|
|
988
|
+
console.log(`无效选择,请输入 ${valid.join('/')}`);
|
|
1395
989
|
}
|
|
1396
990
|
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
991
|
+
return agents[letters.indexOf(choice)].aid;
|
|
992
|
+
}
|
|
993
|
+
/**
|
|
994
|
+
* Pick "add new instance" or "overwrite existing instance" within a single agent
|
|
995
|
+
* for the given channel type. Returns null if user cancels.
|
|
996
|
+
*
|
|
997
|
+
* If existing.length === 0 → returns { action:'add', name:'main' } directly.
|
|
998
|
+
*/
|
|
999
|
+
async function pickInstanceWithinAgent(rl, channelType, existing) {
|
|
1000
|
+
if (existing.length === 0) {
|
|
1001
|
+
return { action: 'add', name: 'main' };
|
|
1403
1002
|
}
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1003
|
+
const view = existing.map((c, i) => ({ ...c, name: c.name, originalIndex: i }));
|
|
1004
|
+
return await selectInstance(rl, channelType, view);
|
|
1005
|
+
}
|
|
1006
|
+
/**
|
|
1007
|
+
* Persist the new/overwritten channel and trigger hot-reload.
|
|
1008
|
+
*/
|
|
1009
|
+
async function commitChannel(aid, channel, mode) {
|
|
1010
|
+
const result = await agentChannelUpsert({ aid, channel, mode });
|
|
1011
|
+
if (result.ok !== true) {
|
|
1012
|
+
console.error(`❌ ${result.error || 'channel upsert failed'}`);
|
|
1013
|
+
return;
|
|
1411
1014
|
}
|
|
1412
|
-
console.log(
|
|
1413
|
-
console.log(
|
|
1015
|
+
console.log(`\n✓ 已写入 agents/${aid}/config.json`);
|
|
1016
|
+
console.log(result.reloaded
|
|
1017
|
+
? ' ✓ 已热重载'
|
|
1018
|
+
: ' ⚠ 服务未运行(或热重载失败),下次 evolclaw start 时生效');
|
|
1414
1019
|
}
|
|
1415
1020
|
export function getChannelCredentialCollector(type) {
|
|
1416
1021
|
switch (type) {
|
|
@@ -1428,21 +1033,6 @@ export function getChannelCredentialCollector(type) {
|
|
|
1428
1033
|
return null;
|
|
1429
1034
|
return { baseUrl: result.baseUrl, token: result.token, enabled: true };
|
|
1430
1035
|
};
|
|
1431
|
-
case 'aun':
|
|
1432
|
-
return async () => {
|
|
1433
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
1434
|
-
try {
|
|
1435
|
-
if (!await checkAunEnvironment(rl))
|
|
1436
|
-
return null;
|
|
1437
|
-
const result = await setupAunAid(rl, {});
|
|
1438
|
-
if (!result)
|
|
1439
|
-
return null;
|
|
1440
|
-
return { aid: result.aid, owner: result.owner, enabled: true };
|
|
1441
|
-
}
|
|
1442
|
-
finally {
|
|
1443
|
-
rl.close();
|
|
1444
|
-
}
|
|
1445
|
-
};
|
|
1446
1036
|
case 'dingtalk':
|
|
1447
1037
|
return async () => {
|
|
1448
1038
|
const result = await runDingtalkQrFlowSimple();
|