evolcore 0.0.3 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +83 -0
- package/README.md +45 -10
- package/bin/ec-safe-output.js +89 -24
- package/dist/agents/baseagent.js +46 -0
- package/dist/agents/claude-runner.js +1 -31
- package/dist/agents/codex-app-server-client.js +68 -0
- package/dist/agents/codex-runner.js +157 -5
- package/dist/agents/runner-types.js +2 -2
- package/dist/aun/aid/agentmd.js +79 -0
- package/dist/aun/aid/encryption-seed-policy.js +29 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/store.js +2 -5
- package/dist/channels/aun.js +220 -112
- package/dist/channels/daemon.js +18 -4
- package/dist/channels/dingtalk.js +52 -137
- package/dist/channels/feishu.js +56 -4
- package/dist/channels/qqbot.js +23 -1
- package/dist/channels/wechat.js +303 -155
- package/dist/channels/wecom.js +383 -7
- package/dist/cli/aun-commands.js +11 -11
- package/dist/cli/daemon-commands.js +46 -15
- package/dist/cli/handoff-command.js +2 -1
- package/dist/cli/init-channel.js +185 -67
- package/dist/cli/init.js +49 -27
- package/dist/cli/trigger-command.js +92 -11
- package/dist/config/access-policy-domain.js +18 -0
- package/dist/config/access-policy.js +110 -0
- package/dist/config/builtin-role-templates.js +27 -14
- package/dist/config/builtin-roles.js +25 -6
- package/dist/config/config-field-policy.js +17 -5
- package/dist/config/config-manager.js +198 -22
- package/dist/config/config-operation-service.js +35 -38
- package/dist/config/contact-bind-code.js +274 -0
- package/dist/config/contact-book-store.js +173 -5
- package/dist/config/contact-book.js +32 -0
- package/dist/config/contact-operation-service.js +9 -3
- package/dist/config/contact-request-service.js +331 -0
- package/dist/config/peer-role-resolver.js +3 -1
- package/dist/config/schema-registry.js +49 -24
- package/dist/config-store.js +14 -2
- package/dist/core/auth/agent-delegation.js +105 -11
- package/dist/core/auth/authorization-audit.js +6 -0
- package/dist/core/auth/operation-authorizer.js +8 -0
- package/dist/core/auth/operation-catalog.js +51 -3
- package/dist/core/auth/trigger-authorization.js +15 -0
- package/dist/core/bootstrap-service.js +2 -9
- package/dist/core/channel-loader.js +6 -2
- package/dist/core/command/command-handler.js +10 -13
- package/dist/core/command/connect-menu.js +229 -26
- package/dist/core/command/evol-menu-version-gate.js +38 -0
- package/dist/core/command/group-menu.js +421 -0
- package/dist/core/command/menu-handler.js +269 -85
- package/dist/core/command/menu-protocol.js +8 -2
- package/dist/core/command/menu-token-store.js +102 -0
- package/dist/core/command/role-menu.js +16 -0
- package/dist/core/command/slash-gate.js +1 -1
- package/dist/core/command/slash-handler.js +158 -31
- package/dist/core/daemon-file-cache.js +28 -0
- package/dist/core/event-catalog.js +29 -0
- package/dist/core/evolagent-registry.js +4 -39
- package/dist/core/handoff/runtime.js +162 -37
- package/dist/core/handoff/store.js +46 -2
- package/dist/core/handoff/types.js +1 -0
- package/dist/core/inference/text-inference.js +16 -74
- package/dist/core/message/file-markers.js +7 -0
- package/dist/core/message/im-renderer.js +18 -14
- package/dist/core/message/inbound-admission.js +134 -0
- package/dist/core/message/message-bridge.js +616 -74
- package/dist/core/message/message-log.js +1 -0
- package/dist/core/message/message-queue.js +185 -10
- package/dist/core/message/peer-mode.js +7 -8
- package/dist/core/message/response-engine.js +381 -79
- package/dist/core/permission/approval-gateway.js +17 -10
- package/dist/core/permission/ec-command-parser.js +101 -46
- package/dist/core/permission/tool-policy.js +69 -24
- package/dist/core/protected-paths.js +36 -11
- package/dist/core/session/session-fs-store.js +19 -4
- package/dist/core/session/session-manager.js +232 -4
- package/dist/core/session/session-mapper.js +2 -0
- package/dist/core/session/session-renew.js +125 -69
- package/dist/core/session/session-turn-coordinator.js +5 -1
- package/dist/eck/kit-renderer.js +12 -1
- package/dist/eck/message-renderer.js +79 -1
- package/dist/index.js +224 -89
- package/dist/ipc.js +81 -4
- package/dist/paths.js +3 -0
- package/dist/response-system/config-resolver.js +29 -0
- package/dist/response-system/coordinator.js +8 -32
- package/dist/response-system/engines/v1/proactive-flow.js +1 -1
- package/dist/response-system/index.js +1 -0
- package/dist/response-system/modes/single-session/index.js +7 -4
- package/dist/trigger/parser.js +55 -15
- package/dist/trigger/patch.js +4 -1
- package/dist/trigger/scheduler.js +441 -39
- package/dist/utils/cross-platform.js +8 -2
- package/dist/utils/error-dict.json +7 -0
- package/dist/utils/evolcore-version.js +21 -0
- package/dist/utils/logger.js +2 -2
- package/dist/utils/process-introspect.js +19 -3
- package/dist/utils/stable-semver.js +21 -0
- package/dist/utils/stats.js +33 -9
- package/kits/docs/channels/aun.md +4 -13
- package/kits/docs/evolcore/INDEX.md +1 -1
- package/kits/docs/evolcore/config.md +47 -2
- package/kits/docs/evolcore/contact.md +8 -3
- package/kits/docs/evolcore/group-rules.md +46 -4
- package/kits/docs/evolcore/group.md +4 -4
- package/kits/docs/evolcore/msg.md +5 -5
- package/kits/docs/evolcore/trigger.md +25 -8
- package/kits/docs/path-registry.md +36 -17
- package/kits/eck_message_manifest.json +12 -1
- package/kits/migrations/migrate-contact-book-v2.mjs +7 -0
- package/kits/rules/01-overview.md +17 -7
- package/kits/rules/02-navigation.md +38 -18
- package/kits/rules/03-identity.md +28 -24
- package/kits/rules/04-relation.md +44 -28
- package/kits/rules/05-venue.md +31 -15
- package/kits/rules/06-channel.md +11 -7
- package/kits/schemas/_meta.json +18 -7
- package/kits/schemas/agent-config.schema.7.json +303 -0
- package/kits/schemas/agent-config.schema.8.json +304 -0
- package/kits/schemas/agent-config.schema.9.json +364 -0
- package/kits/schemas/contact-book.schema.3.json +67 -0
- package/kits/schemas/daemon.schema.1.json +3 -2
- package/kits/schemas/daemon.schema.2.json +101 -0
- package/kits/schemas/daemon.schema.3.json +123 -0
- package/kits/schemas/defaults.schema.2.json +85 -0
- package/kits/schemas/defaults.schema.3.json +73 -0
- package/kits/schemas/relation-config.schema.6.json +46 -0
- package/kits/schemas/relation-config.schema.7.json +59 -0
- package/kits/schemas/single-session.schema.2.json +57 -0
- package/kits/templates/message-fragments/handoff-context-to-target.md +9 -0
- package/kits/templates/message-fragments/handoff-request-to-target.md +14 -8
- package/kits/templates/message-fragments/handoff-response-to-origin.md +5 -6
- package/kits/templates/roles/admin.json +46 -0
- package/kits/templates/roles/member.json +4 -0
- package/kits/templates/roles/visitor.json +4 -0
- package/kits/templates/system-fragments/channel.md +12 -2
- package/kits/templates/system-fragments/identity.md +3 -1
- package/kits/templates/system-fragments/relation.md +1 -1
- package/kits/templates/system-fragments/session.md +6 -2
- package/package.json +4 -2
- package/MIGRATION-0.5.0.md +0 -378
- package/ROLE_ACCESS_CONTROL.md +0 -174
- package/dist/channels/contact-bind-code.js +0 -134
- package/dist/channels/wecom-card.js +0 -101
- package/dist/channels/wecom-onboarding.js +0 -82
- package/dist/channels/wecom-state.js +0 -191
package/dist/cli/init-channel.js
CHANGED
|
@@ -16,7 +16,7 @@ import { ipcQuery } from '../ipc.js';
|
|
|
16
16
|
import { cmdStart } from './daemon-commands.js';
|
|
17
17
|
import * as platform from '../utils/cross-platform.js';
|
|
18
18
|
import { cleanupInstances } from '../utils/instance-registry.js';
|
|
19
|
-
import { bindContactAlias } from '../config/contact-book.js';
|
|
19
|
+
import { bindContactAlias, getContactSnapshot } from '../config/contact-book.js';
|
|
20
20
|
import { listStaticAgentAdmins } from '../config/peer-role-resolver.js';
|
|
21
21
|
import { setupRawKeyListener } from './raw-key-input.js';
|
|
22
22
|
function ask(rl, question) {
|
|
@@ -384,6 +384,7 @@ export async function cmdInitFeishu() {
|
|
|
384
384
|
let agentConfig = null;
|
|
385
385
|
let choice = null;
|
|
386
386
|
let contactBinding = null;
|
|
387
|
+
let operation = 'configure';
|
|
387
388
|
try {
|
|
388
389
|
aid = await pickAgentForChannel(rl);
|
|
389
390
|
if (!aid)
|
|
@@ -394,16 +395,25 @@ export async function cmdInitFeishu() {
|
|
|
394
395
|
return;
|
|
395
396
|
}
|
|
396
397
|
const existing = (agentConfig.channels || []).filter(c => c.type === 'feishu');
|
|
397
|
-
choice = await pickInstanceWithinAgent(rl, 'feishu', existing);
|
|
398
|
+
choice = await pickInstanceWithinAgent(rl, 'feishu', existing, { confirmOverwrite: false });
|
|
398
399
|
if (choice === null)
|
|
399
400
|
return;
|
|
400
|
-
|
|
401
|
+
if (choice.action === 'overwrite') {
|
|
402
|
+
operation = await pickExistingChannelOperation(rl, '飞书', choice.name);
|
|
403
|
+
if (operation === 'configure' && !await confirmOverwriteChannel(rl, '飞书', choice.name))
|
|
404
|
+
return;
|
|
405
|
+
}
|
|
406
|
+
contactBinding = await pickContactForChannelBinding(rl, agentConfig, 'feishu');
|
|
401
407
|
if (!contactBinding)
|
|
402
408
|
return;
|
|
403
409
|
}
|
|
404
410
|
finally {
|
|
405
411
|
rl.close();
|
|
406
412
|
}
|
|
413
|
+
if (operation === 'rebind-contact') {
|
|
414
|
+
await registerFeishuContactBind(aid, contactBindChannelKey('feishu', aid, choice.name), contactBinding);
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
407
417
|
console.log('\n正在获取飞书登录二维码...\n');
|
|
408
418
|
let result;
|
|
409
419
|
try {
|
|
@@ -444,7 +454,15 @@ export async function cmdInitFeishu() {
|
|
|
444
454
|
console.log(` Login Open ID: ${result.openId}`);
|
|
445
455
|
if (result.domain !== 'unknown')
|
|
446
456
|
console.log(` Domain: ${result.domain}`);
|
|
447
|
-
|
|
457
|
+
if (result.openId) {
|
|
458
|
+
bindQrActorToContact(aid, contactBinding, 'feishu', result.openId, commitResult.channelKey);
|
|
459
|
+
}
|
|
460
|
+
else if (commitResult.reloaded) {
|
|
461
|
+
await registerFeishuContactBind(aid, commitResult.channelKey, contactBinding);
|
|
462
|
+
}
|
|
463
|
+
else {
|
|
464
|
+
console.log(' ⚠ 服务未运行或热重载失败,无法注册飞书绑定码;请启动服务后重新执行 ec init feishu。');
|
|
465
|
+
}
|
|
448
466
|
// Show unified init success message
|
|
449
467
|
const { generateInitSuccessMessage } = await import('../utils/welcome.js');
|
|
450
468
|
console.log(generateInitSuccessMessage('feishu', contactBinding !== null));
|
|
@@ -545,6 +563,7 @@ export async function cmdInitWechat() {
|
|
|
545
563
|
let agentConfig = null;
|
|
546
564
|
let choice = null;
|
|
547
565
|
let contactBinding = null;
|
|
566
|
+
let operation = 'configure';
|
|
548
567
|
try {
|
|
549
568
|
aid = await pickAgentForChannel(rl);
|
|
550
569
|
if (!aid)
|
|
@@ -555,16 +574,25 @@ export async function cmdInitWechat() {
|
|
|
555
574
|
return;
|
|
556
575
|
}
|
|
557
576
|
const existing = (agentConfig.channels || []).filter(c => c.type === 'wechat');
|
|
558
|
-
choice = await pickInstanceWithinAgent(rl, 'wechat', existing);
|
|
577
|
+
choice = await pickInstanceWithinAgent(rl, 'wechat', existing, { confirmOverwrite: false });
|
|
559
578
|
if (choice === null)
|
|
560
579
|
return;
|
|
561
|
-
|
|
580
|
+
if (choice.action === 'overwrite') {
|
|
581
|
+
operation = await pickExistingChannelOperation(rl, '微信', choice.name);
|
|
582
|
+
if (operation === 'configure' && !await confirmOverwriteChannel(rl, '微信', choice.name))
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
contactBinding = await pickContactForChannelBinding(rl, agentConfig, 'wechat');
|
|
562
586
|
if (!contactBinding)
|
|
563
587
|
return;
|
|
564
588
|
}
|
|
565
589
|
finally {
|
|
566
590
|
rl.close();
|
|
567
591
|
}
|
|
592
|
+
if (operation === 'rebind-contact') {
|
|
593
|
+
await registerWechatContactBind(aid, contactBindChannelKey('wechat', aid, choice.name), contactBinding);
|
|
594
|
+
return;
|
|
595
|
+
}
|
|
568
596
|
console.log('\n正在获取微信登录二维码...\n');
|
|
569
597
|
const result = await runWechatQrFlow();
|
|
570
598
|
if (!result) {
|
|
@@ -581,7 +609,15 @@ export async function cmdInitWechat() {
|
|
|
581
609
|
const commitResult = await commitChannel(aid, channel, choice.action);
|
|
582
610
|
if (!commitResult)
|
|
583
611
|
return;
|
|
584
|
-
|
|
612
|
+
if (firstNonEmpty(result.userId)) {
|
|
613
|
+
bindQrActorToContact(aid, contactBinding, 'wechat', result.userId, commitResult.channelKey);
|
|
614
|
+
}
|
|
615
|
+
else if (commitResult.reloaded) {
|
|
616
|
+
await registerWechatContactBind(aid, commitResult.channelKey, contactBinding);
|
|
617
|
+
}
|
|
618
|
+
else {
|
|
619
|
+
console.log(' ⚠ 服务未运行或热重载失败,无法注册微信绑定码;请启动服务后重新执行 ec init wechat。');
|
|
620
|
+
}
|
|
585
621
|
// Show unified init success message
|
|
586
622
|
const { generateInitSuccessMessage } = await import('../utils/welcome.js');
|
|
587
623
|
console.log(generateInitSuccessMessage('wechat', contactBinding !== null));
|
|
@@ -788,6 +824,7 @@ export async function cmdInitDingtalk() {
|
|
|
788
824
|
let agentConfig = null;
|
|
789
825
|
let choice = null;
|
|
790
826
|
let contactBinding = null;
|
|
827
|
+
let operation = 'configure';
|
|
791
828
|
try {
|
|
792
829
|
aid = await pickAgentForChannel(rl);
|
|
793
830
|
if (!aid)
|
|
@@ -798,16 +835,25 @@ export async function cmdInitDingtalk() {
|
|
|
798
835
|
return;
|
|
799
836
|
}
|
|
800
837
|
const existing = (agentConfig.channels || []).filter(c => c.type === 'dingtalk');
|
|
801
|
-
choice = await pickInstanceWithinAgent(rl, 'dingtalk', existing);
|
|
838
|
+
choice = await pickInstanceWithinAgent(rl, 'dingtalk', existing, { confirmOverwrite: false });
|
|
802
839
|
if (choice === null)
|
|
803
840
|
return;
|
|
804
|
-
|
|
841
|
+
if (choice.action === 'overwrite') {
|
|
842
|
+
operation = await pickExistingChannelOperation(rl, '钉钉', choice.name);
|
|
843
|
+
if (operation === 'configure' && !await confirmOverwriteChannel(rl, '钉钉', choice.name))
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
contactBinding = await pickContactForChannelBinding(rl, agentConfig, 'dingtalk');
|
|
805
847
|
if (!contactBinding)
|
|
806
848
|
return;
|
|
807
849
|
}
|
|
808
850
|
finally {
|
|
809
851
|
rl.close();
|
|
810
852
|
}
|
|
853
|
+
if (operation === 'rebind-contact') {
|
|
854
|
+
await registerDingtalkContactBind(aid, contactBindChannelKey('dingtalk', aid, choice.name), contactBinding);
|
|
855
|
+
return;
|
|
856
|
+
}
|
|
811
857
|
console.log('\n正在获取钉钉登录二维码...\n');
|
|
812
858
|
let result;
|
|
813
859
|
try {
|
|
@@ -997,6 +1043,7 @@ export async function cmdInitQQBot() {
|
|
|
997
1043
|
let agentConfig = null;
|
|
998
1044
|
let choice = null;
|
|
999
1045
|
let contactBinding = null;
|
|
1046
|
+
let operation = 'configure';
|
|
1000
1047
|
try {
|
|
1001
1048
|
aid = await pickAgentForChannel(rl);
|
|
1002
1049
|
if (!aid)
|
|
@@ -1007,16 +1054,25 @@ export async function cmdInitQQBot() {
|
|
|
1007
1054
|
return;
|
|
1008
1055
|
}
|
|
1009
1056
|
const existing = (agentConfig.channels || []).filter(c => c.type === 'qqbot');
|
|
1010
|
-
choice = await pickInstanceWithinAgent(rl, 'qqbot', existing);
|
|
1057
|
+
choice = await pickInstanceWithinAgent(rl, 'qqbot', existing, { confirmOverwrite: false });
|
|
1011
1058
|
if (choice === null)
|
|
1012
1059
|
return;
|
|
1013
|
-
|
|
1060
|
+
if (choice.action === 'overwrite') {
|
|
1061
|
+
operation = await pickExistingChannelOperation(rl, 'QQ', choice.name);
|
|
1062
|
+
if (operation === 'configure' && !await confirmOverwriteChannel(rl, 'QQ', choice.name))
|
|
1063
|
+
return;
|
|
1064
|
+
}
|
|
1065
|
+
contactBinding = await pickContactForChannelBinding(rl, agentConfig, 'qqbot');
|
|
1014
1066
|
if (!contactBinding)
|
|
1015
1067
|
return;
|
|
1016
1068
|
}
|
|
1017
1069
|
finally {
|
|
1018
1070
|
rl.close();
|
|
1019
1071
|
}
|
|
1072
|
+
if (operation === 'rebind-contact') {
|
|
1073
|
+
await registerQQBotContactBind(aid, contactBindChannelKey('qqbot', aid, choice.name), contactBinding);
|
|
1074
|
+
return;
|
|
1075
|
+
}
|
|
1020
1076
|
console.log('\n正在创建 QQ 机器人绑定任务...\n');
|
|
1021
1077
|
let result;
|
|
1022
1078
|
try {
|
|
@@ -1066,7 +1122,15 @@ export async function cmdInitQQBot() {
|
|
|
1066
1122
|
if (!commitResult)
|
|
1067
1123
|
return;
|
|
1068
1124
|
console.log(` App ID: ${result.appId}`);
|
|
1069
|
-
|
|
1125
|
+
if (firstNonEmpty(result.userOpenId)) {
|
|
1126
|
+
bindQrActorToContact(aid, contactBinding, 'qqbot', result.userOpenId, commitResult.channelKey);
|
|
1127
|
+
}
|
|
1128
|
+
else if (commitResult.reloaded) {
|
|
1129
|
+
await registerQQBotContactBind(aid, commitResult.channelKey, contactBinding);
|
|
1130
|
+
}
|
|
1131
|
+
else {
|
|
1132
|
+
console.log(' ⚠ 服务未运行或热重载失败,无法注册 QQ 绑定码;请启动服务后重新执行 ec init qqbot。');
|
|
1133
|
+
}
|
|
1070
1134
|
const { generateInitSuccessMessage } = await import('../utils/welcome.js');
|
|
1071
1135
|
console.log(generateInitSuccessMessage('qqbot', contactBinding !== null));
|
|
1072
1136
|
}
|
|
@@ -1077,6 +1141,7 @@ export async function cmdInitWecom() {
|
|
|
1077
1141
|
let agentConfig = null;
|
|
1078
1142
|
let choice = null;
|
|
1079
1143
|
let contactBinding = null;
|
|
1144
|
+
let operation = 'configure';
|
|
1080
1145
|
let botId = '';
|
|
1081
1146
|
let secret = '';
|
|
1082
1147
|
try {
|
|
@@ -1089,60 +1154,71 @@ export async function cmdInitWecom() {
|
|
|
1089
1154
|
return;
|
|
1090
1155
|
}
|
|
1091
1156
|
const existing = (agentConfig.channels || []).filter(c => c.type === 'wecom');
|
|
1092
|
-
choice = await pickInstanceWithinAgent(rl, 'wecom', existing);
|
|
1157
|
+
choice = await pickInstanceWithinAgent(rl, 'wecom', existing, { confirmOverwrite: false });
|
|
1093
1158
|
if (choice === null)
|
|
1094
1159
|
return;
|
|
1095
|
-
|
|
1160
|
+
if (choice.action === 'overwrite') {
|
|
1161
|
+
operation = await pickExistingChannelOperation(rl, '企微', choice.name);
|
|
1162
|
+
if (operation === 'configure' && !await confirmOverwriteChannel(rl, '企微', choice.name))
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
contactBinding = await pickContactForChannelBinding(rl, agentConfig, 'wecom');
|
|
1096
1166
|
if (!contactBinding)
|
|
1097
1167
|
return;
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1168
|
+
if (operation !== 'rebind-contact') {
|
|
1169
|
+
console.log('\n企业微信 AI Bot 配置');
|
|
1170
|
+
const method = (await ask(rl, ' 接入方式:1. 扫码创建(推荐) 2. 手工输入 [1]: ')).trim() || '1';
|
|
1171
|
+
if (method === '1') {
|
|
1172
|
+
try {
|
|
1173
|
+
const { createWecomQrSession, pollWecomQrCredentials } = await import('../channels/wecom.js');
|
|
1174
|
+
const session = await createWecomQrSession();
|
|
1175
|
+
const qrterm = await import('qrcode-terminal');
|
|
1176
|
+
console.log('\n请使用企业微信扫描以下二维码:\n');
|
|
1177
|
+
await new Promise(resolve => {
|
|
1178
|
+
qrterm.default.generate(session.authUrl, { small: true }, (qr) => {
|
|
1179
|
+
console.log(qr);
|
|
1180
|
+
resolve();
|
|
1181
|
+
});
|
|
1182
|
+
});
|
|
1183
|
+
console.log(`也可在浏览器打开:${session.browserUrl}`);
|
|
1184
|
+
console.log('等待扫码确认,二维码 5 分钟内有效...\n');
|
|
1185
|
+
const credentials = await pollWecomQrCredentials(session.scode, {
|
|
1186
|
+
onStatus: status => {
|
|
1187
|
+
if (status !== 'waiting')
|
|
1188
|
+
console.log(` 扫码状态: ${status}`);
|
|
1189
|
+
},
|
|
1110
1190
|
});
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
});
|
|
1120
|
-
botId = credentials.botId;
|
|
1121
|
-
secret = credentials.secret;
|
|
1122
|
-
console.log(' ✓ 已取得 Bot ID 和 Secret');
|
|
1191
|
+
botId = credentials.botId;
|
|
1192
|
+
secret = credentials.secret;
|
|
1193
|
+
console.log(' ✓ 已取得 Bot ID 和 Secret');
|
|
1194
|
+
}
|
|
1195
|
+
catch (error) {
|
|
1196
|
+
console.log(` ⚠ 扫码接入失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
1197
|
+
console.log(' 将切换到手工输入模式。\n');
|
|
1198
|
+
}
|
|
1123
1199
|
}
|
|
1124
|
-
|
|
1125
|
-
console.log(
|
|
1126
|
-
|
|
1200
|
+
if (!botId || !secret) {
|
|
1201
|
+
console.log('请在企业微信管理后台 → AI Bot 页面获取 Bot ID 和 Secret\n');
|
|
1202
|
+
}
|
|
1203
|
+
while (!botId) {
|
|
1204
|
+
botId = (await ask(rl, ' Bot ID: ')).trim();
|
|
1205
|
+
if (!botId)
|
|
1206
|
+
console.log(' ⚠ 不能为空');
|
|
1207
|
+
}
|
|
1208
|
+
while (!secret) {
|
|
1209
|
+
secret = (await ask(rl, ' Secret: ')).trim();
|
|
1210
|
+
if (!secret)
|
|
1211
|
+
console.log(' ⚠ 不能为空');
|
|
1127
1212
|
}
|
|
1128
|
-
}
|
|
1129
|
-
if (!botId || !secret) {
|
|
1130
|
-
console.log('请在企业微信管理后台 → AI Bot 页面获取 Bot ID 和 Secret\n');
|
|
1131
|
-
}
|
|
1132
|
-
while (!botId) {
|
|
1133
|
-
botId = (await ask(rl, ' Bot ID: ')).trim();
|
|
1134
|
-
if (!botId)
|
|
1135
|
-
console.log(' ⚠ 不能为空');
|
|
1136
|
-
}
|
|
1137
|
-
while (!secret) {
|
|
1138
|
-
secret = (await ask(rl, ' Secret: ')).trim();
|
|
1139
|
-
if (!secret)
|
|
1140
|
-
console.log(' ⚠ 不能为空');
|
|
1141
1213
|
}
|
|
1142
1214
|
}
|
|
1143
1215
|
finally {
|
|
1144
1216
|
rl.close();
|
|
1145
1217
|
}
|
|
1218
|
+
if (operation === 'rebind-contact') {
|
|
1219
|
+
await registerWecomContactBind(aid, contactBindChannelKey('wecom', aid, choice.name), contactBinding);
|
|
1220
|
+
return;
|
|
1221
|
+
}
|
|
1146
1222
|
const channel = {
|
|
1147
1223
|
type: 'wecom',
|
|
1148
1224
|
name: choice.name,
|
|
@@ -1201,24 +1277,42 @@ async function pickAgentForChannel(rl) {
|
|
|
1201
1277
|
*
|
|
1202
1278
|
* If existing.length === 0 → returns { action:'add', name:'main' } directly.
|
|
1203
1279
|
*/
|
|
1204
|
-
async function pickInstanceWithinAgent(rl, channelType, existing) {
|
|
1280
|
+
async function pickInstanceWithinAgent(rl, channelType, existing, options) {
|
|
1205
1281
|
if (existing.length === 0) {
|
|
1206
1282
|
return { action: 'add', name: 'main' };
|
|
1207
1283
|
}
|
|
1208
1284
|
const view = existing.map((c, i) => ({ ...c, name: c.name, originalIndex: i }));
|
|
1209
|
-
return await selectInstance(rl, channelType, view);
|
|
1285
|
+
return await selectInstance(rl, channelType, view, options);
|
|
1210
1286
|
}
|
|
1211
|
-
async function
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1287
|
+
async function pickExistingChannelOperation(rl, displayName, channelName) {
|
|
1288
|
+
console.log(`\n请选择对${displayName}实例 ${channelName} 的操作:`);
|
|
1289
|
+
console.log(' a. 覆盖机器人配置(重新扫码或输入 App ID / App Secret)');
|
|
1290
|
+
console.log(' b. 重新绑定联系人(保留机器人配置,生成一次性 /bind 验证码)');
|
|
1291
|
+
while (true) {
|
|
1292
|
+
const choice = (await ask(rl, '请选择 [a/b]: ')).trim().toLowerCase();
|
|
1293
|
+
if (choice === 'a')
|
|
1294
|
+
return 'configure';
|
|
1295
|
+
if (choice === 'b')
|
|
1296
|
+
return 'rebind-contact';
|
|
1297
|
+
console.log('无效选择,请输入 a 或 b');
|
|
1217
1298
|
}
|
|
1218
|
-
|
|
1299
|
+
}
|
|
1300
|
+
async function confirmOverwriteChannel(rl, displayName, channelName) {
|
|
1301
|
+
const confirm = (await ask(rl, `⚠️ 即将覆盖${displayName}实例 ${channelName} 的机器人配置,确认?(y/N) `)).trim().toLowerCase();
|
|
1302
|
+
if (confirm === 'y' || confirm === 'yes')
|
|
1303
|
+
return true;
|
|
1304
|
+
console.log('已取消');
|
|
1305
|
+
return false;
|
|
1306
|
+
}
|
|
1307
|
+
async function pickContactForChannelBinding(rl, agentConfig, channelType) {
|
|
1308
|
+
const candidates = preferredContactBindingCandidates(agentConfig);
|
|
1309
|
+
const contacts = getContactSnapshot(agentConfig.aid).contacts;
|
|
1310
|
+
console.log(`\n请选择 ${channelType} 账号要绑定到的联系人 AID:`);
|
|
1219
1311
|
candidates.forEach((candidate, index) => {
|
|
1220
1312
|
console.log(` ${index + 1}. ${candidate.primaryId} (${candidate.label})`);
|
|
1221
1313
|
});
|
|
1314
|
+
const manualChoice = candidates.length + 1;
|
|
1315
|
+
console.log(` ${manualChoice}. 手工输入已有联系人 AID`);
|
|
1222
1316
|
const defaultIndex = candidates.length === 1 ? 1 : null;
|
|
1223
1317
|
const prompt = defaultIndex ? `请选择 [${defaultIndex}]: ` : '请选择: ';
|
|
1224
1318
|
while (true) {
|
|
@@ -1227,14 +1321,26 @@ async function pickOwnerAdminForChannelBinding(rl, agentConfig, channelType) {
|
|
|
1227
1321
|
if (Number.isInteger(selected) && selected >= 1 && selected <= candidates.length) {
|
|
1228
1322
|
return candidates[selected - 1];
|
|
1229
1323
|
}
|
|
1230
|
-
|
|
1324
|
+
if (selected === manualChoice) {
|
|
1325
|
+
const primaryId = (await ask(rl, '请输入要绑定的联系人 AID: ')).trim();
|
|
1326
|
+
if (isValidAid(primaryId) && contacts.has(primaryId))
|
|
1327
|
+
return { primaryId, label: 'contact' };
|
|
1328
|
+
if (isValidAid(primaryId)) {
|
|
1329
|
+
console.log('该 AID 不是当前 Agent 的联系人,请先添加联系人或将其设为 Owner。');
|
|
1330
|
+
continue;
|
|
1331
|
+
}
|
|
1332
|
+
console.log('AID 格式无效,请重新输入。');
|
|
1333
|
+
continue;
|
|
1334
|
+
}
|
|
1335
|
+
console.log(`无效选择,请输入 1-${manualChoice}`);
|
|
1231
1336
|
}
|
|
1232
1337
|
}
|
|
1233
|
-
function
|
|
1338
|
+
function preferredContactBindingCandidates(agentConfig) {
|
|
1339
|
+
const contacts = getContactSnapshot(agentConfig.aid).contacts;
|
|
1234
1340
|
const labels = new Map();
|
|
1235
1341
|
const add = (value, label) => {
|
|
1236
1342
|
const aid = String(value || '').trim();
|
|
1237
|
-
if (!isValidAid(aid))
|
|
1343
|
+
if (!isValidAid(aid) || !contacts.has(aid))
|
|
1238
1344
|
return;
|
|
1239
1345
|
let set = labels.get(aid);
|
|
1240
1346
|
if (!set) {
|
|
@@ -1273,6 +1379,18 @@ function bindQrActorToContact(aid, binding, channelType, actorId, channelKey) {
|
|
|
1273
1379
|
async function registerDingtalkContactBind(aid, channelName, binding) {
|
|
1274
1380
|
await registerChannelContactBind('dingtalk', '钉钉', aid, channelName, binding);
|
|
1275
1381
|
}
|
|
1382
|
+
function contactBindChannelKey(channelType, aid, channelName) {
|
|
1383
|
+
return `${channelType}#${aid}#${channelName}`;
|
|
1384
|
+
}
|
|
1385
|
+
async function registerFeishuContactBind(aid, channelName, binding) {
|
|
1386
|
+
await registerChannelContactBind('feishu', '飞书', aid, channelName, binding);
|
|
1387
|
+
}
|
|
1388
|
+
async function registerWechatContactBind(aid, channelName, binding) {
|
|
1389
|
+
await registerChannelContactBind('wechat', '微信', aid, channelName, binding);
|
|
1390
|
+
}
|
|
1391
|
+
async function registerQQBotContactBind(aid, channelName, binding) {
|
|
1392
|
+
await registerChannelContactBind('qqbot', 'QQ', aid, channelName, binding);
|
|
1393
|
+
}
|
|
1276
1394
|
async function registerWecomContactBind(aid, channelName, binding) {
|
|
1277
1395
|
await registerChannelContactBind('wecom', '企微', aid, channelName, binding);
|
|
1278
1396
|
}
|
|
@@ -1292,7 +1410,7 @@ async function registerChannelContactBind(channelType, displayName, aid, channel
|
|
|
1292
1410
|
}
|
|
1293
1411
|
const expiresAt = new Date(Number(result.expiresAt)).toLocaleString();
|
|
1294
1412
|
console.log(` ✓ ${displayName}身份绑定码: ${result.code}`);
|
|
1295
|
-
console.log(` 请在 10 分钟内私聊${displayName}
|
|
1413
|
+
console.log(` 请在 10 分钟内私聊${displayName}机器人,发送:/bind ${result.code}。有效期至: ${expiresAt}`);
|
|
1296
1414
|
}
|
|
1297
1415
|
function firstNonEmpty(...values) {
|
|
1298
1416
|
for (const value of values) {
|
package/dist/cli/init.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
+
import os from 'os';
|
|
2
3
|
import path from 'path';
|
|
3
4
|
import readline from 'readline';
|
|
4
5
|
import { resolvePaths, ensureDataDirs } from '../paths.js';
|
|
@@ -13,6 +14,15 @@ import { WEB_CLI_BIN, WEB_PACKAGE_LATEST } from '../product.js';
|
|
|
13
14
|
function ask(rl, question) {
|
|
14
15
|
return new Promise(resolve => rl.question(question, resolve));
|
|
15
16
|
}
|
|
17
|
+
/** 展开用户在交互式输入中使用的 home 目录简写(shell 不会替 readline 自动做这件事)。 */
|
|
18
|
+
export function expandLeadingTildePath(value, homeDir = os.homedir()) {
|
|
19
|
+
if (value === '~')
|
|
20
|
+
return homeDir;
|
|
21
|
+
if (value.startsWith('~/') || value.startsWith('~\\')) {
|
|
22
|
+
return path.join(homeDir, value.slice(2));
|
|
23
|
+
}
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
16
26
|
const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
|
|
17
27
|
function isBaseagentAvailable(baseagent) {
|
|
18
28
|
if (baseagent === 'codex')
|
|
@@ -167,7 +177,7 @@ export async function cmdInit(options) {
|
|
|
167
177
|
await runInteractive();
|
|
168
178
|
}
|
|
169
179
|
// ── 共享 tail(单一出口):提示创建 agent + 生成控制 AID ──
|
|
170
|
-
await initTail();
|
|
180
|
+
await initTail({ invokedByStart: options?.invokedByStart });
|
|
171
181
|
// ── 内部函数 ──
|
|
172
182
|
async function runInteractive() {
|
|
173
183
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
@@ -194,29 +204,36 @@ export async function cmdInit(options) {
|
|
|
194
204
|
}
|
|
195
205
|
async function askProjectsDefaultPath() {
|
|
196
206
|
const defaultDir = path.join(defaultProjectsRoot(p.root), 'default');
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
207
|
+
while (true) {
|
|
208
|
+
const input = (await ask(rl, `项目默认目录 [${defaultDir}](输入 skip 跳过): `)).trim();
|
|
209
|
+
if (input.toLowerCase() === 'skip') {
|
|
210
|
+
console.log(' 已跳过项目默认目录配置');
|
|
211
|
+
return undefined;
|
|
212
|
+
}
|
|
213
|
+
const resolved = expandLeadingTildePath(input || defaultDir);
|
|
214
|
+
if (!path.isAbsolute(resolved)) {
|
|
215
|
+
console.log(' ⚠ 请输入绝对路径或回车使用默认值;输入 skip 可跳过');
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (!fs.existsSync(resolved)) {
|
|
219
|
+
const create = (await ask(rl, ` 目录不存在,是否创建?[Y/n]: `)).trim().toLowerCase();
|
|
220
|
+
if (create === '' || create === 'y' || create === 'yes') {
|
|
221
|
+
try {
|
|
222
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
223
|
+
}
|
|
224
|
+
catch (e) {
|
|
225
|
+
console.log(` ⚠ 创建目录失败: ${e?.message || e}`);
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
console.log(` ✓ 已创建 ${resolved}`);
|
|
208
229
|
}
|
|
209
|
-
|
|
210
|
-
console.log(
|
|
211
|
-
|
|
230
|
+
else {
|
|
231
|
+
console.log(' 未创建目录,请重新输入路径;输入 skip 可跳过');
|
|
232
|
+
continue;
|
|
212
233
|
}
|
|
213
|
-
console.log(` ✓ 已创建 ${resolved}`);
|
|
214
|
-
}
|
|
215
|
-
else {
|
|
216
|
-
return undefined;
|
|
217
234
|
}
|
|
235
|
+
return resolved;
|
|
218
236
|
}
|
|
219
|
-
return resolved;
|
|
220
237
|
}
|
|
221
238
|
try {
|
|
222
239
|
if (defaultsExisted) {
|
|
@@ -249,10 +266,10 @@ export async function cmdInit(options) {
|
|
|
249
266
|
}
|
|
250
267
|
}
|
|
251
268
|
/** 补全控制 AID + owners(可单独调用,不走 baseagent 向导)。 */
|
|
252
|
-
export async function initTail() {
|
|
269
|
+
export async function initTail(options = {}) {
|
|
253
270
|
// 提示创建 agent(两分支汇合后执行一次)
|
|
254
271
|
const { agents } = loadAllAgents();
|
|
255
|
-
if (agents.length === 0) {
|
|
272
|
+
if (agents.length === 0 && !options.invokedByStart) {
|
|
256
273
|
console.log('\n提示:尚无 agent,运行以下命令创建:');
|
|
257
274
|
console.log(' ec agent new <aid>.agentid.pub');
|
|
258
275
|
}
|
|
@@ -368,6 +385,9 @@ export async function initTail() {
|
|
|
368
385
|
}
|
|
369
386
|
}
|
|
370
387
|
}
|
|
388
|
+
// ec start 会接着启动 daemon,并在其 ready 后给出可用的 Agent 创建入口。
|
|
389
|
+
if (options.invokedByStart)
|
|
390
|
+
return;
|
|
371
391
|
// 初始化完成总结
|
|
372
392
|
console.log('\n✓ EvolCore 初始化完成');
|
|
373
393
|
const finalCfg = loadDaemonConfig();
|
|
@@ -390,7 +410,7 @@ export async function initTail() {
|
|
|
390
410
|
* Present instance selection menu when existing instances are found.
|
|
391
411
|
* Returns the user's choice, or null if cancelled.
|
|
392
412
|
*/
|
|
393
|
-
export async function selectInstance(rl, channelType, instances) {
|
|
413
|
+
export async function selectInstance(rl, channelType, instances, options = {}) {
|
|
394
414
|
const typeLabel = channelType === 'feishu' ? '飞书' : channelType === 'wechat' ? '微信' : channelType === 'dingtalk' ? '钉钉' : channelType === 'qqbot' ? 'QQ机器人' : channelType === 'wecom' ? '企业微信' : channelType.toUpperCase();
|
|
395
415
|
console.log(`\n发现已有 ${typeLabel} 配置:`);
|
|
396
416
|
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
@@ -427,10 +447,12 @@ export async function selectInstance(rl, channelType, instances) {
|
|
|
427
447
|
}
|
|
428
448
|
const target = instances[choiceIndex];
|
|
429
449
|
console.log(`\n已选择:${target.name}`);
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
450
|
+
if (options.confirmOverwrite !== false) {
|
|
451
|
+
const confirm = (await new Promise(r => rl.question(`⚠️ 即将覆盖该配置,确认?(y/N) `, r))).trim().toLowerCase();
|
|
452
|
+
if (confirm !== 'y' && confirm !== 'yes') {
|
|
453
|
+
console.log('已取消');
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
434
456
|
}
|
|
435
457
|
return { action: 'overwrite', index: choiceIndex, name: target.name };
|
|
436
458
|
}
|