evolclaw 3.1.11 → 3.2.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/CHANGELOG.md +24 -0
- package/README.md +26 -0
- package/dist/agents/kit-renderer.js +5 -1
- package/dist/agents/manifest-engine.js +108 -35
- package/dist/agents/message-renderer.js +2 -0
- package/dist/aun/aid/control-aid.js +67 -0
- package/dist/aun/aid/identity.js +20 -7
- package/dist/aun/aid/store.js +2 -2
- package/dist/channels/aun.js +161 -61
- package/dist/channels/feishu.js +3 -3
- package/dist/cli/agent.js +38 -10
- package/dist/cli/index.js +31 -3
- package/dist/cli/init-channel.js +38 -148
- package/dist/cli/init.js +162 -82
- package/dist/config-store.js +38 -7
- package/dist/core/cache/file-cache.js +216 -0
- package/dist/core/command-handler.js +291 -68
- package/dist/core/evolagent-registry.js +3 -0
- package/dist/core/evolagent.js +28 -23
- package/dist/core/message/command-handler-agent-control.js +153 -0
- package/dist/core/message/create-status.js +67 -0
- package/dist/core/message/message-bridge.js +5 -3
- package/dist/core/message/message-processor.js +44 -36
- package/dist/core/message/message-queue.js +13 -6
- package/dist/core/model/model-scope.js +39 -6
- package/dist/core/session/adapters/claude-session-file-adapter.js +48 -5
- package/dist/evolclaw-config.js +11 -0
- package/dist/index.js +57 -2
- package/dist/ipc.js +6 -0
- package/dist/paths.js +7 -3
- package/kits/templates/message-fragments/item.md +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -6,11 +6,12 @@ import { spawn, execFileSync, execFile } from 'child_process';
|
|
|
6
6
|
import { promisify } from 'util';
|
|
7
7
|
import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot, agentMdPath } from '../paths.js';
|
|
8
8
|
import { loadDefaults, loadAllAgents, mergeForAgent } from '../config-store.js';
|
|
9
|
+
import { loadEvolclawConfig } from '../evolclaw-config.js';
|
|
9
10
|
import { resolveAnthropicConfig } from '../agents/resolve.js';
|
|
10
11
|
import { migrateProject } from '../config-store.js';
|
|
11
|
-
import { cmdInit } from './init.js';
|
|
12
|
+
import { cmdInit, needsControlAidInit, initTail } from './init.js';
|
|
12
13
|
import { ipcQuery } from '../ipc.js';
|
|
13
|
-
import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom } from './init-channel.js';
|
|
14
|
+
import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom, cmdInitAun } from './init-channel.js';
|
|
14
15
|
import { isHelpFlag, wantsHelp, getArgValue } from './help.js';
|
|
15
16
|
import * as platform from '../utils/cross-platform.js';
|
|
16
17
|
import { EventBus } from '../core/event-bus.js';
|
|
@@ -299,6 +300,19 @@ async function cmdStart() {
|
|
|
299
300
|
await cmdInit();
|
|
300
301
|
return;
|
|
301
302
|
}
|
|
303
|
+
// 控制 AID 门禁:缺 aid 且交互式 → 只补全控制 AID + owners(不重走 baseagent 向导)。
|
|
304
|
+
// 非 TTY(restart-monitor/systemd/管道)不补全(无法交互),只提示后继续启动,daemon 侧 warn 兜底。
|
|
305
|
+
const evolclawCfgStart = loadEvolclawConfig();
|
|
306
|
+
if (needsControlAidInit(evolclawCfgStart.aid, !!process.stdin.isTTY)) {
|
|
307
|
+
console.log('⚡ 控制 AID 未配置,自动补全...\n');
|
|
308
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
309
|
+
suppressSdkLogs();
|
|
310
|
+
await initTail();
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
if (!evolclawCfgStart.aid) {
|
|
314
|
+
console.log('⚠ 控制 AID 未配置(非交互式启动,跳过补全)。如需进程身份/远程管理,请运行 evolclaw init');
|
|
315
|
+
}
|
|
302
316
|
// 检查至少有一个 self-agent
|
|
303
317
|
const { agents, skipped } = loadAllAgents();
|
|
304
318
|
if (agents.length === 0) {
|
|
@@ -968,6 +982,14 @@ async function cmdStatus() {
|
|
|
968
982
|
}
|
|
969
983
|
}
|
|
970
984
|
catch { /* ignore */ }
|
|
985
|
+
// 控制 AID(daemon 进程身份)状态
|
|
986
|
+
if (status.controlAid) {
|
|
987
|
+
const state = status.controlAid.connected ? 'connected' : 'disconnected';
|
|
988
|
+
console.log(`control: ${status.controlAid.aid} [${state}]`);
|
|
989
|
+
}
|
|
990
|
+
else {
|
|
991
|
+
console.log('control: not configured');
|
|
992
|
+
}
|
|
971
993
|
if (status.stats) {
|
|
972
994
|
console.log('');
|
|
973
995
|
console.log('📊 Last hour:');
|
|
@@ -4870,12 +4892,18 @@ export async function main(args) {
|
|
|
4870
4892
|
--force 已存在 defaults.json 时覆盖
|
|
4871
4893
|
|
|
4872
4894
|
配置渠道(先 evolclaw agent new 创建 agent):
|
|
4895
|
+
evolclaw init aun AUN 渠道配置(AID 创建/绑定)
|
|
4873
4896
|
evolclaw init feishu 飞书扫码登录
|
|
4874
4897
|
evolclaw init wechat 微信扫码登录
|
|
4875
4898
|
evolclaw init dingtalk 钉钉扫码登录
|
|
4876
4899
|
evolclaw init qqbot QQ 机器人扫码绑定
|
|
4877
4900
|
evolclaw init wecom 企业微信 AI Bot 配置(手动输入)`);
|
|
4878
4901
|
}
|
|
4902
|
+
else if (args[1] === 'aun') {
|
|
4903
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4904
|
+
suppressSdkLogs();
|
|
4905
|
+
await cmdInitAun();
|
|
4906
|
+
}
|
|
4879
4907
|
else if (args[1] === 'wechat') {
|
|
4880
4908
|
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4881
4909
|
suppressSdkLogs();
|
|
@@ -4900,7 +4928,7 @@ export async function main(args) {
|
|
|
4900
4928
|
await cmdInitWecom();
|
|
4901
4929
|
}
|
|
4902
4930
|
else if (args[1] && !args[1].startsWith('-')) {
|
|
4903
|
-
const supported = ['feishu', 'wechat', 'dingtalk', 'qqbot', 'wecom'];
|
|
4931
|
+
const supported = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
|
|
4904
4932
|
console.error(`❌ 不支持的渠道: ${args[1]}`);
|
|
4905
4933
|
console.error(` 支持的渠道: ${supported.join(', ')}`);
|
|
4906
4934
|
process.exit(1);
|
package/dist/cli/init-channel.js
CHANGED
|
@@ -5,16 +5,12 @@
|
|
|
5
5
|
* - cmdInit<Channel>() — standalone `evolclaw init <channel>` entry
|
|
6
6
|
* - run<Channel>QrFlow() or setupAun*() — reusable primitives for the main init wizard
|
|
7
7
|
*/
|
|
8
|
-
import fs from 'fs';
|
|
9
8
|
import readline from 'readline';
|
|
10
|
-
import path from 'path';
|
|
11
9
|
import crypto from 'crypto';
|
|
12
|
-
import { aidLocalDir, aunPath as defaultAunPath } from '../paths.js';
|
|
13
10
|
import { selectInstance } from './init.js';
|
|
14
|
-
import {
|
|
15
|
-
import { loadAllAgents, loadAgent } from '../config-store.js';
|
|
11
|
+
import { loadAllAgents, loadAgent, saveAgent } from '../config-store.js';
|
|
16
12
|
import { agentChannelUpsert } from './agent.js';
|
|
17
|
-
import {
|
|
13
|
+
import { isValidAid } from '../aun/aid/index.js';
|
|
18
14
|
function ask(rl, question) {
|
|
19
15
|
return new Promise(resolve => rl.question(question, resolve));
|
|
20
16
|
}
|
|
@@ -372,156 +368,50 @@ export async function cmdInitWechat() {
|
|
|
372
368
|
await commitChannel(aid, channel, choice.action);
|
|
373
369
|
}
|
|
374
370
|
// ==================== AUN ====================
|
|
375
|
-
|
|
376
|
-
// AUN
|
|
377
|
-
//
|
|
378
|
-
|
|
379
|
-
console.log('\n🔍 AUN 环境检查...\n');
|
|
380
|
-
const minVer = MIN_AUN_CORE_SDK.join('.');
|
|
381
|
-
const installed = resolveAunCoreSdkPkg();
|
|
382
|
-
if (!installed) {
|
|
383
|
-
console.log(` ✗ ${AUN_CORE_SDK_PKG} 未安装`);
|
|
384
|
-
const answer = (await ask(rl, ` → 是否安装 ${AUN_CORE_SDK_PKG}@latest?[Y/n] `)).trim().toLowerCase();
|
|
385
|
-
if (answer === 'n' || answer === 'no') {
|
|
386
|
-
console.log(' 已取消');
|
|
387
|
-
return false;
|
|
388
|
-
}
|
|
389
|
-
console.log(` 正在安装 ${AUN_CORE_SDK_PKG}...`);
|
|
390
|
-
try {
|
|
391
|
-
await npmInstallGlobal(`${AUN_CORE_SDK_PKG}@latest`);
|
|
392
|
-
console.log(` ✓ ${AUN_CORE_SDK_PKG} 安装完成`);
|
|
393
|
-
}
|
|
394
|
-
catch (e) {
|
|
395
|
-
console.log(` ✗ 安装失败: ${e.message?.slice(0, 200) || e}`);
|
|
396
|
-
return false;
|
|
397
|
-
}
|
|
398
|
-
console.log('');
|
|
399
|
-
return true;
|
|
400
|
-
}
|
|
401
|
-
if (isAunSdkVersionOk(installed.version)) {
|
|
402
|
-
console.log(` ✓ ${AUN_CORE_SDK_PKG} v${installed.version}`);
|
|
403
|
-
console.log('');
|
|
404
|
-
return true;
|
|
405
|
-
}
|
|
406
|
-
console.log(` ✗ ${AUN_CORE_SDK_PKG} v${installed.version} — 需要 >= ${minVer}`);
|
|
407
|
-
const answer = (await ask(rl, ` → 是否升级 ${AUN_CORE_SDK_PKG}?[Y/n] `)).trim().toLowerCase();
|
|
408
|
-
if (answer === 'n' || answer === 'no') {
|
|
409
|
-
console.log(' 已取消');
|
|
410
|
-
return false;
|
|
411
|
-
}
|
|
412
|
-
console.log(` 正在升级 ${AUN_CORE_SDK_PKG}...`);
|
|
371
|
+
export async function cmdInitAun() {
|
|
372
|
+
// AUN channel 从 agent.aid 隐式派生,AID 密钥在 `agent new` 时已创建就绪——本命令不碰 aid。
|
|
373
|
+
// 与其他渠道一致,职责是配置 owner;AUN 的 owner 存于 agent 顶层 owners[](见 evolagent.ts)。
|
|
374
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
413
375
|
try {
|
|
414
|
-
await
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
console.log('');
|
|
422
|
-
return true;
|
|
423
|
-
}
|
|
424
|
-
// isValidAid, createAidSilent → 已迁移至 src/channels/aun-ops.ts
|
|
425
|
-
// appendAunInstance → 已迁移至 src/channels/aun-ops.ts
|
|
426
|
-
export async function setupAunAid(rl, _config) {
|
|
427
|
-
let aid = '';
|
|
428
|
-
// Outer loop: allows retrying with a different AID
|
|
429
|
-
while (true) {
|
|
430
|
-
// Ask AID with format validation
|
|
431
|
-
aid = '';
|
|
432
|
-
while (!aid) {
|
|
433
|
-
aid = (await ask(rl, ' AUN Agent ID (例: mybot.agentid.pub): ')).trim();
|
|
434
|
-
if (!aid) {
|
|
435
|
-
console.log(' ⚠ 不能为空');
|
|
436
|
-
continue;
|
|
437
|
-
}
|
|
438
|
-
if (!isValidAid(aid)) {
|
|
439
|
-
console.log(' ⚠ 无效 AID 格式(需要合法域名,至少三级,如 alice.agentid.pub)');
|
|
440
|
-
aid = '';
|
|
441
|
-
}
|
|
442
|
-
}
|
|
443
|
-
// Check if AID exists locally
|
|
444
|
-
const aunPath = defaultAunPath();
|
|
445
|
-
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
446
|
-
if (fs.existsSync(aidDir) && fs.existsSync(path.join(aidDir, 'private'))) {
|
|
447
|
-
console.log(` ✓ AID ${aid} 已存在`);
|
|
448
|
-
break;
|
|
449
|
-
}
|
|
450
|
-
const answer = (await ask(rl, ` ⚠ AID ${aid} 本地不存在,是否创建?[Y/n] `)).trim().toLowerCase();
|
|
451
|
-
if (answer === 'n' || answer === 'no') {
|
|
452
|
-
console.log(' 已跳过 AID 创建(启动时可能连接失败)');
|
|
453
|
-
break;
|
|
376
|
+
const agentId = await pickAgentForChannel(rl);
|
|
377
|
+
if (!agentId)
|
|
378
|
+
return;
|
|
379
|
+
const agentConfig = loadAgent(agentId);
|
|
380
|
+
if (!agentConfig) {
|
|
381
|
+
console.error(`❌ 无法加载 agent ${agentId} 的配置`);
|
|
382
|
+
return;
|
|
454
383
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
console.log(' ✓ agent.md 已发布并写入本地');
|
|
468
|
-
}
|
|
469
|
-
catch (e) {
|
|
470
|
-
console.log(` ⚠ agent.md 发布失败(首次连接将自动重试): ${String(e.message || e).slice(0, 100)}`);
|
|
471
|
-
// Still write local copy as fallback
|
|
472
|
-
try {
|
|
473
|
-
const localDir = aidLocalDir(aid);
|
|
474
|
-
fs.mkdirSync(localDir, { recursive: true });
|
|
475
|
-
fs.writeFileSync(path.join(localDir, 'agent.md'), content, 'utf-8');
|
|
476
|
-
console.log(' ✓ agent.md 已写入本地');
|
|
384
|
+
console.log(`\n📡 AUN 渠道配置 — agent ${agentConfig.aid}`);
|
|
385
|
+
const current = agentConfig.owners?.[0];
|
|
386
|
+
if (current)
|
|
387
|
+
console.log(` 当前 Owner: ${current}`);
|
|
388
|
+
console.log(' Owner 将接收欢迎消息并拥有管理权限\n');
|
|
389
|
+
let owner = '';
|
|
390
|
+
while (!owner) {
|
|
391
|
+
const input = (await ask(rl, ` Owner AID${current ? ` [${current}]` : ''}: `)).trim();
|
|
392
|
+
if (!input) {
|
|
393
|
+
if (current) {
|
|
394
|
+
owner = current;
|
|
395
|
+
break;
|
|
477
396
|
}
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
failed = true;
|
|
481
|
-
}
|
|
482
|
-
}
|
|
483
|
-
try {
|
|
484
|
-
await result.client.close();
|
|
397
|
+
console.log(' ⚠ Owner AID 不能为空');
|
|
398
|
+
continue;
|
|
485
399
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
400
|
+
if (!isValidAid(input)) {
|
|
401
|
+
console.log(' ⚠ Owner AID 格式无效(需合法多级域名,如 alice.agentid.pub)');
|
|
402
|
+
continue;
|
|
489
403
|
}
|
|
490
|
-
|
|
404
|
+
owner = input;
|
|
491
405
|
}
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
if (!failed)
|
|
498
|
-
break;
|
|
499
|
-
// Creation failed — retry or give up
|
|
500
|
-
const retry = (await ask(rl, ' → 重新输入 (r) / 跳过 (s) / 取消 (c)?[r/s/c] ')).trim().toLowerCase();
|
|
501
|
-
if (retry === 'c')
|
|
502
|
-
return null;
|
|
503
|
-
if (retry === 's')
|
|
504
|
-
break;
|
|
505
|
-
// default: retry with new AID
|
|
406
|
+
// 写入顶层 owners:新 owner 置首位(getOwner 取 owners[0]),保留其余去重
|
|
407
|
+
agentConfig.owners = [owner, ...(agentConfig.owners || []).filter(o => o !== owner)];
|
|
408
|
+
saveAgent(agentConfig);
|
|
409
|
+
console.log(`\n✅ AUN 渠道 Owner 已设置: ${owner}`);
|
|
410
|
+
console.log(`\n重启生效: evolclaw restart`);
|
|
506
411
|
}
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
console.log(' Owner 将接收欢迎消息并拥有管理权限');
|
|
510
|
-
let owner = '';
|
|
511
|
-
while (!owner) {
|
|
512
|
-
const ownerInput = (await ask(rl, ' Owner AID (必填): ')).trim();
|
|
513
|
-
if (!ownerInput) {
|
|
514
|
-
console.log(' ⚠ Owner AID 不能为空');
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
if (!isValidAid(ownerInput)) {
|
|
518
|
-
console.log(' ⚠ Owner AID 格式无效');
|
|
519
|
-
continue;
|
|
520
|
-
}
|
|
521
|
-
owner = ownerInput;
|
|
522
|
-
console.log(` ✓ Owner 已设置: ${owner}`);
|
|
412
|
+
finally {
|
|
413
|
+
rl.close();
|
|
523
414
|
}
|
|
524
|
-
return { aid, owner };
|
|
525
415
|
}
|
|
526
416
|
// ==================== DingTalk ====================
|
|
527
417
|
const DINGTALK_BASE_URL = 'https://oapi.dingtalk.com';
|
package/dist/cli/init.js
CHANGED
|
@@ -4,7 +4,9 @@ import readline from 'readline';
|
|
|
4
4
|
import { resolvePaths, ensureDataDirs } from '../paths.js';
|
|
5
5
|
import { commandExists } from '../utils/cross-platform.js';
|
|
6
6
|
import { scanInstances } from '../utils/instance-registry.js';
|
|
7
|
-
import { saveDefaultsSafe, loadAllAgents } from '../config-store.js';
|
|
7
|
+
import { saveDefaultsSafe, loadAllAgents, migrateProcessConfigIfNeeded } from '../config-store.js';
|
|
8
|
+
import { loadEvolclawConfig, saveEvolclawConfig } from '../evolclaw-config.js';
|
|
9
|
+
import { generateControlAid } from '../aun/aid/control-aid.js';
|
|
8
10
|
import { isCodexSdkAvailable } from '../agents/codex-runner.js';
|
|
9
11
|
// ==================== Helpers ====================
|
|
10
12
|
function ask(rl, question) {
|
|
@@ -36,10 +38,35 @@ function buildDefaults(chosen, available, projectsDefaultPath) {
|
|
|
36
38
|
function writeDefaults(chosen, available, projectsDefaultPath) {
|
|
37
39
|
saveDefaultsSafe(buildDefaults(chosen, available, projectsDefaultPath));
|
|
38
40
|
}
|
|
41
|
+
/** 启动门禁判定:缺控制 AID 且处于交互式终端时,应进 init 向导补全。
|
|
42
|
+
* 非 TTY(restart-monitor/systemd/管道)即使缺 aid 也不进 init(无法交互),由 daemon 侧 warn 兜底。 */
|
|
43
|
+
export function needsControlAidInit(aid, isTty) {
|
|
44
|
+
return !aid && isTty;
|
|
45
|
+
}
|
|
46
|
+
/** 解析用户输入的 owner AID 列表:按空白/逗号分隔,去空、去重、按 isValid 分流。
|
|
47
|
+
* 空输入 → valid:[](视为跳过)。 */
|
|
48
|
+
export function parseOwnerAids(raw, isValid) {
|
|
49
|
+
const tokens = raw.split(/[\s,]+/).map(t => t.trim()).filter(Boolean);
|
|
50
|
+
const valid = [];
|
|
51
|
+
const invalid = [];
|
|
52
|
+
for (const t of tokens) {
|
|
53
|
+
if (isValid(t)) {
|
|
54
|
+
if (!valid.includes(t))
|
|
55
|
+
valid.push(t);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
invalid.push(t);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return { valid, invalid };
|
|
62
|
+
}
|
|
39
63
|
// ==================== Main ====================
|
|
40
64
|
export async function cmdInit(options) {
|
|
41
65
|
const p = resolvePaths();
|
|
42
66
|
ensureDataDirs();
|
|
67
|
+
// config.json → evolclaw.json:init 路径也可能先于 daemon 触发 AID 生成(走 getAidStore),
|
|
68
|
+
// 须在任何 getAidStore 之前迁移 encryptionSeed。
|
|
69
|
+
migrateProcessConfigIfNeeded();
|
|
43
70
|
// ── 1. 单进程互斥 ──
|
|
44
71
|
const aliveMains = scanInstances().mains.filter(m => m.alive);
|
|
45
72
|
if (aliveMains.length > 0) {
|
|
@@ -66,110 +93,163 @@ export async function cmdInit(options) {
|
|
|
66
93
|
// ── 3. 非交互式分支 ──
|
|
67
94
|
if (options?.nonInteractive) {
|
|
68
95
|
if (exists && !options.force) {
|
|
69
|
-
|
|
70
|
-
|
|
96
|
+
// 配置已存在且未 --force:不重写 defaults,但仍落到共享 tail(幂等补生成控制 AID)
|
|
97
|
+
console.log(`配置已存在: ${defaultsPath}(加 --force 可覆盖)`);
|
|
71
98
|
}
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
if (
|
|
75
|
-
|
|
76
|
-
|
|
99
|
+
else {
|
|
100
|
+
let chosen;
|
|
101
|
+
if (options.baseagent) {
|
|
102
|
+
if (!BASEAGENT_CANDIDATES.includes(options.baseagent)) {
|
|
103
|
+
console.log(`❌ 无效 baseagent: ${options.baseagent}(可选: ${BASEAGENT_CANDIDATES.join('/')})`);
|
|
104
|
+
return; // 硬错误:不落 tail
|
|
105
|
+
}
|
|
106
|
+
if (!available.includes(options.baseagent)) {
|
|
107
|
+
console.log(`❌ ${options.baseagent} 当前环境不可用(可用: ${available.join('/')})`);
|
|
108
|
+
return; // 硬错误:不落 tail
|
|
109
|
+
}
|
|
110
|
+
chosen = options.baseagent;
|
|
77
111
|
}
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return;
|
|
112
|
+
else {
|
|
113
|
+
chosen = pickDefault(available);
|
|
81
114
|
}
|
|
82
|
-
chosen
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
chosen = pickDefault(available);
|
|
86
|
-
}
|
|
87
|
-
writeDefaults(chosen, available);
|
|
88
|
-
console.log(`✓ 已${exists ? '覆盖' : '创建'}: ${defaultsPath}`);
|
|
89
|
-
console.log(` active_baseagent: ${chosen}`);
|
|
90
|
-
const { agents } = loadAllAgents();
|
|
91
|
-
if (agents.length === 0) {
|
|
92
|
-
console.log('\n提示:尚无 agent,运行以下命令创建:');
|
|
93
|
-
console.log(' evolclaw agent new <aid>.agentid.pub');
|
|
115
|
+
writeDefaults(chosen, available);
|
|
116
|
+
console.log(`✓ 已${exists ? '覆盖' : '创建'}: ${defaultsPath}`);
|
|
117
|
+
console.log(` active_baseagent: ${chosen}`);
|
|
94
118
|
}
|
|
95
|
-
return
|
|
119
|
+
// 落到共享 tail(不 return)
|
|
96
120
|
}
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const
|
|
108
|
-
if (
|
|
109
|
-
console.log(`
|
|
110
|
-
|
|
121
|
+
else {
|
|
122
|
+
// ── 4. 交互式分支(rl 生命周期封装在内部函数,tail 不引用 rl)──
|
|
123
|
+
await runInteractive();
|
|
124
|
+
}
|
|
125
|
+
// ── 共享 tail(单一出口):提示创建 agent + 生成控制 AID ──
|
|
126
|
+
await initTail();
|
|
127
|
+
// ── 内部函数 ──
|
|
128
|
+
async function runInteractive() {
|
|
129
|
+
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
130
|
+
async function askBaseagent() {
|
|
131
|
+
const defaultBa = pickDefault(available);
|
|
132
|
+
if (available.length === 1) {
|
|
133
|
+
console.log(` baseagent: ${defaultBa}`);
|
|
134
|
+
return defaultBa;
|
|
111
135
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
136
|
+
let chosen = null;
|
|
137
|
+
while (chosen === null) {
|
|
138
|
+
const input = (await ask(rl, `默认 baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
|
|
139
|
+
if (!BASEAGENT_CANDIDATES.includes(input)) {
|
|
140
|
+
console.log(` 无效选择,可选: ${BASEAGENT_CANDIDATES.join('/')}`);
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (!available.includes(input)) {
|
|
144
|
+
console.log(` ${input} 当前环境不可用(可用: ${available.join('/')})`);
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
chosen = input;
|
|
115
148
|
}
|
|
116
|
-
chosen
|
|
149
|
+
return chosen;
|
|
117
150
|
}
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
if (!path.isAbsolute(resolved)) {
|
|
125
|
-
console.log(' ⚠ 需要绝对路径,已跳过');
|
|
126
|
-
return undefined;
|
|
127
|
-
}
|
|
128
|
-
if (!fs.existsSync(resolved)) {
|
|
129
|
-
const create = (await ask(rl, ` 目录不存在,是否创建?[Y/n]: `)).trim().toLowerCase();
|
|
130
|
-
if (create === '' || create === 'y' || create === 'yes') {
|
|
131
|
-
fs.mkdirSync(resolved, { recursive: true });
|
|
132
|
-
console.log(` ✓ 已创建 ${resolved}`);
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
151
|
+
async function askProjectsDefaultPath() {
|
|
152
|
+
const defaultDir = path.join(p.root, 'projects', 'default');
|
|
153
|
+
const input = (await ask(rl, `项目默认目录 [${defaultDir}]: `)).trim();
|
|
154
|
+
const resolved = input || defaultDir;
|
|
155
|
+
if (!path.isAbsolute(resolved)) {
|
|
156
|
+
console.log(' ⚠ 需要绝对路径,已跳过');
|
|
135
157
|
return undefined;
|
|
136
158
|
}
|
|
159
|
+
if (!fs.existsSync(resolved)) {
|
|
160
|
+
const create = (await ask(rl, ` 目录不存在,是否创建?[Y/n]: `)).trim().toLowerCase();
|
|
161
|
+
if (create === '' || create === 'y' || create === 'yes') {
|
|
162
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
163
|
+
console.log(` ✓ 已创建 ${resolved}`);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
return undefined;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return resolved;
|
|
137
170
|
}
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
171
|
+
try {
|
|
172
|
+
if (exists) {
|
|
173
|
+
const ans = (await ask(rl, `配置文件已存在: ${defaultsPath}\n 是否覆盖?[y/N] `)).trim().toLowerCase();
|
|
174
|
+
if (ans === 'y' || ans === 'yes') {
|
|
175
|
+
const chosen = await askBaseagent();
|
|
176
|
+
const projectsDefaultPath = await askProjectsDefaultPath();
|
|
177
|
+
writeDefaults(chosen, available, projectsDefaultPath);
|
|
178
|
+
console.log(`\n✓ 已覆盖: ${defaultsPath}`);
|
|
179
|
+
console.log(` active_baseagent: ${chosen}\n`);
|
|
180
|
+
}
|
|
181
|
+
else {
|
|
182
|
+
console.log(' 已跳过(保留现有配置)\n');
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
else {
|
|
144
186
|
const chosen = await askBaseagent();
|
|
145
187
|
const projectsDefaultPath = await askProjectsDefaultPath();
|
|
146
188
|
writeDefaults(chosen, available, projectsDefaultPath);
|
|
147
|
-
console.log(`\n✓
|
|
189
|
+
console.log(`\n✓ 已创建: ${defaultsPath}`);
|
|
148
190
|
console.log(` active_baseagent: ${chosen}\n`);
|
|
149
191
|
}
|
|
150
|
-
|
|
151
|
-
|
|
192
|
+
}
|
|
193
|
+
finally {
|
|
194
|
+
try {
|
|
195
|
+
rl.close();
|
|
152
196
|
}
|
|
197
|
+
catch { /* ignore */ }
|
|
153
198
|
}
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
/** 补全控制 AID + owners(可单独调用,不走 baseagent 向导)。 */
|
|
202
|
+
export async function initTail() {
|
|
203
|
+
// 提示创建 agent(两分支汇合后执行一次)
|
|
204
|
+
const { agents } = loadAllAgents();
|
|
205
|
+
if (agents.length === 0) {
|
|
206
|
+
console.log('\n提示:尚无 agent,运行以下命令创建:');
|
|
207
|
+
console.log(' evolclaw agent new <aid>.agentid.pub');
|
|
208
|
+
}
|
|
209
|
+
// 控制 AID:daemon 进程身份。缺失则生成并写回 evolclaw.json(幂等:已存在则跳过)。
|
|
210
|
+
const evc = loadEvolclawConfig();
|
|
211
|
+
if (evc.aid) {
|
|
212
|
+
console.log(`✓ 控制 AID 已存在: ${evc.aid}`);
|
|
213
|
+
}
|
|
214
|
+
else {
|
|
215
|
+
try {
|
|
216
|
+
const { aid } = await generateControlAid();
|
|
217
|
+
saveEvolclawConfig({ ...evc, $schema_version: evc.$schema_version ?? 1, aid });
|
|
218
|
+
console.log(`✓ 已生成控制 AID: ${aid}`);
|
|
160
219
|
}
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
if (agents.length === 0) {
|
|
164
|
-
console.log('提示:尚无 agent,运行以下命令创建:');
|
|
165
|
-
console.log(' evolclaw agent new <aid>.agentid.pub');
|
|
220
|
+
catch (e) {
|
|
221
|
+
console.error(`⚠️ 控制 AID 生成失败(Gateway 不可达?联网后重跑 evolclaw init 补全): ${e?.message || e}`);
|
|
166
222
|
}
|
|
167
223
|
}
|
|
168
|
-
|
|
224
|
+
// 配置进程级管理者(owners):仅交互式 + aid 已配置 + owners 未配置时询问
|
|
225
|
+
const evcForOwners = loadEvolclawConfig();
|
|
226
|
+
if (process.stdin.isTTY && evcForOwners.aid && (!evcForOwners.owners || evcForOwners.owners.length === 0)) {
|
|
227
|
+
const { isValidAid } = await import('../aun/aid/index.js');
|
|
228
|
+
const rlOwners = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
169
229
|
try {
|
|
170
|
-
|
|
230
|
+
const raw = (await ask(rlOwners, '\n请输入 EvolClaw 管理者 AID: ')).trim();
|
|
231
|
+
if (raw) {
|
|
232
|
+
const { valid, invalid } = parseOwnerAids(raw, isValidAid);
|
|
233
|
+
if (invalid.length > 0)
|
|
234
|
+
console.log(` ⚠ 跳过非法 AID: ${invalid.join(', ')}`);
|
|
235
|
+
if (valid.length > 0) {
|
|
236
|
+
saveEvolclawConfig({ ...loadEvolclawConfig(), owners: valid });
|
|
237
|
+
console.log(` ✓ 已配置管理者: ${valid.join(', ')}`);
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
console.log(' 未输入合法 AID,已跳过 owners 配置');
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
console.log(' 已跳过 owners 配置(可日后编辑 evolclaw.json 或重跑 evolclaw init)');
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
finally {
|
|
248
|
+
try {
|
|
249
|
+
rlOwners.close();
|
|
250
|
+
}
|
|
251
|
+
catch { /* ignore */ }
|
|
171
252
|
}
|
|
172
|
-
catch { /* ignore */ }
|
|
173
253
|
}
|
|
174
254
|
}
|
|
175
255
|
/**
|
package/dist/config-store.js
CHANGED
|
@@ -17,6 +17,7 @@ import fs from 'fs';
|
|
|
17
17
|
import path from 'path';
|
|
18
18
|
import { resolvePaths, agentConfig as agentConfigPath, agentDir, } from './paths.js';
|
|
19
19
|
import { atomicReadJson, atomicWriteJson } from './utils/atomic-write.js';
|
|
20
|
+
import * as evolclawConfigModule from './evolclaw-config.js';
|
|
20
21
|
import { checkAgentDir, isValidAid } from './aun/aid/validation.js';
|
|
21
22
|
import { isValidChannelName } from './core/channel-loader.js';
|
|
22
23
|
import { CONFIG_SCHEMA_VERSION } from './types.js';
|
|
@@ -122,14 +123,42 @@ export function saveDefaultsSafe(patch) {
|
|
|
122
123
|
: { $schema_version: CONFIG_SCHEMA_VERSION, ...patch };
|
|
123
124
|
atomicWriteJson(p, merged);
|
|
124
125
|
}
|
|
125
|
-
|
|
126
|
-
|
|
126
|
+
// ── 进程配置迁移(旧 {root}/config.json ProcessConfig → evolclaw.json)──────
|
|
127
|
+
//
|
|
128
|
+
// ProcessConfig 类型 + loadProcessConfig/saveProcessConfig 已废弃并删除:
|
|
129
|
+
// 唯一有效字段 aun.encryptionSeed 已迁入 evolclaw.json(见 migrateProcessConfigIfNeeded),
|
|
130
|
+
// 读取源切到 loadEvolclawConfig(store.ts)。log / aun.gateway / aun.keystorePath 是死字段。
|
|
131
|
+
/**
|
|
132
|
+
* 一次性迁移:{root}/config.json(旧 ProcessConfig)→ {root}/evolclaw.json。
|
|
133
|
+
* - 仅搬运 aun.encryptionSeed(逐字节原样,含 null);log / aun.gateway / aun.keystorePath 是死字段,丢弃。
|
|
134
|
+
* - 合并写入(不覆盖 evolclaw.json 已有字段如 aid)。
|
|
135
|
+
* - 完成后归档 config.json → config.json.migrated(保留备份,不直接删)。
|
|
136
|
+
*
|
|
137
|
+
* ⚠️ encryptionSeed 是 AID 私钥的加密种子,迁移前后 getAidStore 拿到的 seed 必须逐字节一致,
|
|
138
|
+
* 否则所有已注册 AID 私钥解不开。这里只搬运不改值(含 null)。
|
|
139
|
+
*/
|
|
140
|
+
export function migrateProcessConfigIfNeeded() {
|
|
141
|
+
const p = resolvePaths();
|
|
142
|
+
const oldPath = p.processConfig; // {root}/config.json
|
|
143
|
+
const raw = atomicReadJson(oldPath);
|
|
127
144
|
if (raw === null)
|
|
128
|
-
return
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
145
|
+
return; // 不存在 → no-op
|
|
146
|
+
const { loadEvolclawConfig, saveEvolclawConfig } = evolclawConfigModule;
|
|
147
|
+
const evc = loadEvolclawConfig();
|
|
148
|
+
// 仅当旧文件确实带 aun.encryptionSeed 字段时才搬(hasOwnProperty,保 null 语义)
|
|
149
|
+
const didMigrateSeed = !!(raw.aun && Object.prototype.hasOwnProperty.call(raw.aun, 'encryptionSeed'));
|
|
150
|
+
if (didMigrateSeed) {
|
|
151
|
+
evc.aun = { ...(evc.aun ?? {}), encryptionSeed: raw.aun.encryptionSeed };
|
|
152
|
+
}
|
|
153
|
+
evc.$schema_version = evc.$schema_version ?? 1;
|
|
154
|
+
saveEvolclawConfig(evc);
|
|
155
|
+
// 归档旧文件(不删,留备份)
|
|
156
|
+
try {
|
|
157
|
+
fs.renameSync(oldPath, oldPath + '.migrated');
|
|
158
|
+
}
|
|
159
|
+
catch { /* ignore */ }
|
|
160
|
+
const what = didMigrateSeed ? 'aun.encryptionSeed 已搬运' : 'aun.encryptionSeed 不存在(无需搬运)';
|
|
161
|
+
logger.info(`[migrate] config.json → evolclaw.json (${what},config.json 已归档为 .migrated)`);
|
|
133
162
|
}
|
|
134
163
|
// ── 自动迁移 ───────────────────────────────────────────────────────────
|
|
135
164
|
/**
|
|
@@ -492,6 +521,8 @@ export function mergeForAgent(agent, defaults) {
|
|
|
492
521
|
debounce: agent.debounce ?? d.debounce,
|
|
493
522
|
debug: deepMergeBlocks(d.debug, agent.debug),
|
|
494
523
|
enable_rich_content: agent.enable_rich_content ?? d.enable_rich_content,
|
|
524
|
+
dispatch: agent.dispatch,
|
|
525
|
+
observable: agent.observable,
|
|
495
526
|
};
|
|
496
527
|
return merged;
|
|
497
528
|
}
|