evolclaw 3.1.2 → 3.1.3

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.
@@ -6,7 +6,7 @@ import os from 'os';
6
6
  import { logger, localTimestamp } from '../utils/logger.js';
7
7
  import { LogWriter } from '../utils/log-writer.js';
8
8
  import { normalizeChannelInstances, getChannelShowActivities } from '../utils/channel-helpers.js';
9
- import { resolvePaths, getPackageRoot, agentMdPath as agentMdPathFn, agentDir as agentDirPath } from '../paths.js';
9
+ import { resolvePaths, getPackageRoot, agentMdPath as agentMdPathFn, agentDir as agentDirPath, resolveRoot } from '../paths.js';
10
10
  import { saveToUploads, sanitizeFileName } from '../utils/media-cache.js';
11
11
  import { appendAidEvent } from '../utils/instance-registry.js';
12
12
  import { appendMessageLog, buildOutboundEntry } from '../core/message/message-log.js';
@@ -57,6 +57,46 @@ function getEvolclawVersion() {
57
57
  }
58
58
  return _cachedVersion;
59
59
  }
60
+ function migrateAunData(targetPath) {
61
+ const legacyPath = path.join(os.homedir(), '.aun');
62
+ if (legacyPath === targetPath)
63
+ return;
64
+ // AIDs 迁移:逐个检查每个 AID,缺少 private 的就从 legacy 复制
65
+ const srcAIDs = path.join(legacyPath, 'AIDs');
66
+ const dstAIDs = path.join(targetPath, 'AIDs');
67
+ if (fs.existsSync(srcAIDs)) {
68
+ fs.mkdirSync(dstAIDs, { recursive: true });
69
+ try {
70
+ for (const entry of fs.readdirSync(srcAIDs, { withFileTypes: true })) {
71
+ if (!entry.isDirectory())
72
+ continue;
73
+ const aidName = entry.name;
74
+ const srcAidDir = path.join(srcAIDs, aidName);
75
+ const dstAidDir = path.join(dstAIDs, aidName);
76
+ const srcPrivate = path.join(srcAidDir, 'private');
77
+ const dstPrivate = path.join(dstAidDir, 'private');
78
+ // 如果目标 AID 目录不存在或缺少 private,从源复制整个 AID 目录
79
+ if (fs.existsSync(srcPrivate) && !fs.existsSync(dstPrivate)) {
80
+ fs.cpSync(srcAidDir, dstAidDir, { recursive: true });
81
+ }
82
+ }
83
+ }
84
+ catch { }
85
+ }
86
+ // CA 迁移
87
+ const srcCA = path.join(legacyPath, 'CA');
88
+ const dstCA = path.join(targetPath, 'CA');
89
+ if (fs.existsSync(srcCA) && !fs.existsSync(dstCA)) {
90
+ fs.cpSync(srcCA, dstCA, { recursive: true });
91
+ }
92
+ for (const file of ['.seed', '.device_id']) {
93
+ const src = path.join(legacyPath, file);
94
+ const dst = path.join(targetPath, file);
95
+ if (fs.existsSync(src) && !fs.existsSync(dst)) {
96
+ fs.copyFileSync(src, dst);
97
+ }
98
+ }
99
+ }
60
100
  export class AUNChannel {
61
101
  config;
62
102
  client = null;
@@ -445,6 +485,10 @@ export class AUNChannel {
445
485
  'text', 'quote', 'image', 'video', 'voice', 'file', 'json',
446
486
  'merge', 'link', 'location', 'personal_card',
447
487
  ]);
488
+ /** Menu protocol 请求类型:自定义消息快速路径,绕过白名单直接分发到 bridge */
489
+ static MENU_REQUEST_TYPES = new Set([
490
+ 'menu.list', 'menu.query', 'menu.options', 'menu.update', 'menu.action',
491
+ ]);
448
492
  // Reconnect state
449
493
  // SDK 自己跑无限指数退避(1s → 5min);TS 层只在 SDK 够不到的两类场景下接管:
450
494
  // 1. flap:短命 connected 反复出现(SDK 不记忆跨轮 base delay,会从 1s 重新开始)
@@ -541,9 +585,11 @@ export class AUNChannel {
541
585
  this.client = null;
542
586
  }
543
587
  this.connected = false;
544
- const aunPath = this.config.keystorePath || path.join(os.homedir(), '.aun');
588
+ const aunPath = this.config.keystorePath || resolveRoot();
545
589
  const aidName = this.config.aid;
546
590
  const encryptionSeed = this.config.encryptionSeed || process.env.AUN_ENCRYPTION_SEED || undefined;
591
+ // Migrate legacy ~/.aun data to EVOLCLAW_HOME on first run
592
+ migrateAunData(aunPath);
547
593
  // Gateway URL 解析:优先用配置的 gatewayUrl,否则通过 well-known 自动发现
548
594
  let gateway = this.config.gatewayUrl || '';
549
595
  if (!gateway) {
@@ -781,9 +827,9 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
781
827
  fs.mkdirSync(path.dirname(agentMdLocalPath), { recursive: true });
782
828
  fs.writeFileSync(agentMdLocalPath, newAgentMd, 'utf-8');
783
829
  logger.info(`${this.logPrefix()} Updated agent.md for ${aidName}`);
784
- // Publish to AUN network via auth.uploadAgentMd
830
+ // Publish to AUN network via publishAgentMd (auto-sign)
785
831
  try {
786
- await this.client.auth.uploadAgentMd(newAgentMd);
832
+ await this.client.publishAgentMd();
787
833
  logger.info(`${this.logPrefix()} Published agent.md to AUN network`);
788
834
  }
789
835
  catch (e) {
@@ -800,8 +846,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
800
846
  2. **查看帮助**:发送 \`/help\` 查看所有可用命令
801
847
  3. **切换项目**:发送 \`/project <项目名>\` 切换到其他项目
802
848
  4. **查看状态**:发送 \`/status\` 查看当前会话状态
803
- 5. **查看 Agent 信息**:发送 \`/agentmd\` 查看 agent.md 内容
804
- 6. **会话管理**:发送 \`/session\` 查看和切换会话
849
+ 5. **会话管理**:发送 \`/session\` 查看和切换会话
805
850
 
806
851
  💡 **提示**:
807
852
  - 直接发送消息即可与 Claude/Codex 对话
@@ -963,8 +1008,8 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
963
1008
  // action_card_reply 已在 extractTextPayload 中消费,不分发给 agent
964
1009
  if (p2pPayloadType === 'action_card_reply')
965
1010
  return;
966
- // menu.query:自定义消息快速路径,需要原始 payload JSON 传递给 bridge
967
- if (p2pPayloadType === 'menu.query') {
1011
+ // menu.* 协议:自定义消息快速路径,需要原始 payload JSON 传递给 bridge
1012
+ if (AUNChannel.MENU_REQUEST_TYPES.has(p2pPayloadType)) {
968
1013
  this.acknowledgeImmediately(messageId, seq);
969
1014
  this.dispatchMessage({
970
1015
  channelId: chatId, userId: fromAid,
@@ -1069,8 +1114,8 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
1069
1114
  {
1070
1115
  const payloadObj = (payload && typeof payload === 'object') ? payload : null;
1071
1116
  const payloadType = payloadObj?.type ?? '';
1072
- // menu.query:自定义消息快速路径
1073
- if (payloadType === 'menu.query') {
1117
+ // menu.* 协议:自定义消息快速路径
1118
+ if (AUNChannel.MENU_REQUEST_TYPES.has(payloadType)) {
1074
1119
  this.acknowledgeImmediately(messageId, seq);
1075
1120
  this.dispatchMessage({
1076
1121
  channelId: groupId, userId: senderAid,
@@ -2373,7 +2418,9 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
2373
2418
  if (!this.client)
2374
2419
  return { type: null };
2375
2420
  try {
2376
- const md = await this.client.auth.downloadAgentMd(aid);
2421
+ const { agentmdSync } = await import('../aun/aid/agentmd.js');
2422
+ const result = await agentmdSync(aid, { client: this.client });
2423
+ const md = result.content ?? '';
2377
2424
  const typeMatch = md.match(/^type:\s*["']?(\w+)["']?/m);
2378
2425
  const nameMatch = md.match(/^name:\s*["']?(.+?)["']?\s*$/m);
2379
2426
  const type = typeMatch?.[1] === 'human' ? 'human' : 'ai';
@@ -2385,7 +2432,7 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
2385
2432
  }
2386
2433
  catch (e) {
2387
2434
  logger.debug(`${this.logPrefix()} fetchPeerInfo failed for ${aid}: ${e}`);
2388
- return { type: null }; // no agent.md → unknown
2435
+ return { type: null };
2389
2436
  }
2390
2437
  }
2391
2438
  /** 同步取 peerInfo 缓存,未命中返回 undefined,不发起任何网络请求。 */
@@ -2401,12 +2448,18 @@ EvolClaw AI Agent 网关,支持多项目会话管理和多 AI 后端切换。
2401
2448
  async uploadAgentMd(content) {
2402
2449
  if (!this.client)
2403
2450
  throw new Error('not connected');
2404
- await this.client.auth.uploadAgentMd(content);
2451
+ const { agentMdPath } = await import('../paths.js');
2452
+ const localPath = agentMdPath(this.config.aid);
2453
+ fs.mkdirSync(path.dirname(localPath), { recursive: true });
2454
+ fs.writeFileSync(localPath, content, 'utf-8');
2455
+ await this.client.publishAgentMd();
2405
2456
  }
2406
2457
  async downloadAgentMd(aid) {
2407
2458
  if (!this.client)
2408
2459
  throw new Error('not connected');
2409
- return this.client.auth.downloadAgentMd(aid);
2460
+ const { agentmdSync } = await import('../aun/aid/agentmd.js');
2461
+ const result = await agentmdSync(aid, { client: this.client });
2462
+ return result.content ?? '';
2410
2463
  }
2411
2464
  }
2412
2465
  // Plugin implementation
package/dist/cli/agent.js CHANGED
@@ -8,6 +8,7 @@ import { ipcQuery } from '../ipc.js';
8
8
  import { CONFIG_SCHEMA_VERSION } from '../types.js';
9
9
  import { isValidChannelName } from '../core/channel-loader.js';
10
10
  import { commandExists } from '../utils/cross-platform.js';
11
+ import { isCodexSdkAvailable } from '../agents/codex-runner.js';
11
12
  // ==================== Helpers ====================
12
13
  const BASEAGENT_CANDIDATES = ['claude', 'codex', 'gemini'];
13
14
  const BASEAGENT_ENV_KEY = {
@@ -15,8 +16,13 @@ const BASEAGENT_ENV_KEY = {
15
16
  codex: 'OPENAI_API_KEY',
16
17
  gemini: 'GEMINI_API_KEY',
17
18
  };
19
+ function isBaseagentAvailable(baseagent) {
20
+ if (baseagent === 'codex')
21
+ return isCodexSdkAvailable();
22
+ return commandExists(baseagent);
23
+ }
18
24
  function detectAvailableBaseagents() {
19
- return BASEAGENT_CANDIDATES.filter(b => commandExists(b));
25
+ return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
20
26
  }
21
27
  function pickDefaultBaseagent(available) {
22
28
  if (available.length === 0)
@@ -316,7 +322,7 @@ export async function agentCreateInteractive(opts = {}) {
316
322
  // Baseagent
317
323
  const available = detectAvailableBaseagents();
318
324
  if (available.length === 0) {
319
- return { ok: false, error: `No baseagent CLI detected on PATH. Install one of: ${BASEAGENT_CANDIDATES.join('/')}` };
325
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
320
326
  }
321
327
  const defaultBa = pickDefaultBaseagent(available);
322
328
  let baseagent;
@@ -333,7 +339,7 @@ export async function agentCreateInteractive(opts = {}) {
333
339
  continue;
334
340
  }
335
341
  if (!available.includes(input)) {
336
- console.log(` ${input} not detected on PATH. Available: ${available.join('/')}`);
342
+ console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
337
343
  continue;
338
344
  }
339
345
  chosen = input;
@@ -463,7 +469,7 @@ export async function agentCreateNonInteractive(opts) {
463
469
  // Baseagent
464
470
  const available = detectAvailableBaseagents();
465
471
  if (available.length === 0) {
466
- return { ok: false, error: `No baseagent CLI detected on PATH. Install one of: ${BASEAGENT_CANDIDATES.join('/')}` };
472
+ return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
467
473
  }
468
474
  let baseagent;
469
475
  if (opts.baseagent) {
@@ -471,7 +477,7 @@ export async function agentCreateNonInteractive(opts) {
471
477
  return { ok: false, error: `Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})` };
472
478
  }
473
479
  if (!available.includes(opts.baseagent)) {
474
- return { ok: false, error: `${opts.baseagent} not detected on PATH (available: ${available.join('/')})` };
480
+ return { ok: false, error: `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})` };
475
481
  }
476
482
  baseagent = opts.baseagent;
477
483
  }
@@ -592,7 +598,8 @@ export async function agentCreateNonInteractive(opts) {
592
598
  hotLoadError,
593
599
  };
594
600
  }
595
- // ==================== agentSyncAids ====================
601
+ // ==================== agentSyncAids (deprecated) ====================
602
+ /** @deprecated sync-aids 已废弃,不再从 CLI 调用 */
596
603
  export async function agentSyncAids() {
597
604
  const p = resolvePaths();
598
605
  const { aidList } = await import('../aun/aid/index.js');
package/dist/cli/index.js CHANGED
@@ -2739,6 +2739,7 @@ async function cmdCtl(args) {
2739
2739
  查询:
2740
2740
  status 查看会话状态
2741
2741
  check 检查渠道健康状态
2742
+ pwd 显示当前项目路径
2742
2743
  help 显示帮助
2743
2744
 
2744
2745
  配置:
@@ -2747,15 +2748,14 @@ async function cmdCtl(args) {
2747
2748
  compact 压缩当前会话上下文
2748
2749
  perm [mode] 查看/切换权限模式
2749
2750
 
2750
- 项目:
2751
- bind <path> 注册项目目录(不切换当前会话)
2752
-
2753
2751
  消息:
2754
2752
  send <消息内容> 主动发送文本消息(proactive 模式)
2755
2753
  file [channel] <path> 发送项目内文件
2756
2754
 
2755
+ Agent:
2756
+ agent <subcommand> EvolAgent 管理(list/show/new/enable/disable/reload/delete)
2757
+
2757
2758
  运维:
2758
- agentmd [put|set <内容>] 查看/管理 agent.md(仅 AUN 通道)
2759
2759
  restart [channel] 重启服务或重连指定渠道
2760
2760
 
2761
2761
  示例:
@@ -2808,7 +2808,6 @@ Commands:
2808
2808
  show <aid> 查看 agent 详情(身份 + 配置 + 连接 + 会话 + 路径)
2809
2809
  new [aid] 交互式创建 agent
2810
2810
  new <aid> --non-interactive ... 非交互式创建
2811
- sync-aids 从本地 AID 批量创建 agent
2812
2811
  enable <aid> 启用 agent
2813
2812
  disable <aid> 停用 agent
2814
2813
  get <aid> <key> 读取单个配置字段(支持点路径)
@@ -2831,7 +2830,7 @@ Options:
2831
2830
  evolclaw agent delete mybot.agentid.pub --purge`);
2832
2831
  return;
2833
2832
  }
2834
- const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentSyncAids, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
2833
+ const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
2835
2834
  // --- list ---
2836
2835
  if (!sub || sub === 'list') {
2837
2836
  const result = await agentList();
@@ -2966,36 +2965,28 @@ Options:
2966
2965
  }
2967
2966
  return;
2968
2967
  }
2969
- // --- sync-aids ---
2970
- if (sub === 'sync-aids') {
2971
- const result = await agentSyncAids();
2972
- if (!result.ok) {
2973
- if (formatJson) {
2974
- console.log(JSON.stringify(result));
2975
- }
2976
- else {
2977
- console.error(`❌ ${result.error}`);
2978
- }
2979
- process.exit(1);
2980
- }
2981
- if (formatJson) {
2982
- console.log(JSON.stringify(result, null, 2));
2983
- return;
2984
- }
2985
- if (result.created.length === 0) {
2986
- console.log('所有本地 AID 都已有对应 agent,无需同步。');
2987
- }
2988
- else {
2989
- console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
2990
- for (const aid of result.created)
2991
- console.log(` ✓ ${aid}`);
2992
- if (result.hotReloaded)
2993
- console.log(' ✓ 已热加载到运行中的进程');
2994
- else
2995
- console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
2996
- }
2997
- return;
2998
- }
2968
+ // --- sync-aids (deprecated, commented out) ---
2969
+ // if (sub === 'sync-aids') {
2970
+ // const result = await agentSyncAids();
2971
+ // if (!result.ok) {
2972
+ // if (formatJson) { console.log(JSON.stringify(result)); }
2973
+ // else { console.error(`❌ ${result.error}`); }
2974
+ // process.exit(1);
2975
+ // }
2976
+ // if (formatJson) {
2977
+ // console.log(JSON.stringify(result, null, 2));
2978
+ // return;
2979
+ // }
2980
+ // if (result.created.length === 0) {
2981
+ // console.log('所有本地 AID 都已有对应 agent,无需同步。');
2982
+ // } else {
2983
+ // console.log(`✓ 同步完成:新建 ${result.created.length} 个 agent(模板: ${result.template})`);
2984
+ // for (const aid of result.created) console.log(` ✓ ${aid}`);
2985
+ // if (result.hotReloaded) console.log(' 已热加载到运行中的进程');
2986
+ // else console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
2987
+ // }
2988
+ // return;
2989
+ // }
2999
2990
  // --- reload ---
3000
2991
  if (sub === 'reload') {
3001
2992
  const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
@@ -4645,7 +4636,6 @@ Commands:
4645
4636
  --name <display-name>
4646
4637
  --description <text>
4647
4638
  --force (覆盖已有 config.json)
4648
- agent sync-aids 从本地 AID 批量同步创建 agent(以最早 agent 为模板)
4649
4639
  agent reload 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
4650
4640
  agent reload <n> 热重载指定 agent 配置
4651
4641
  aid AID 身份管理
package/dist/cli/init.js CHANGED
@@ -4,6 +4,7 @@ import { resolvePaths, ensureDataDirs } from '../paths.js';
4
4
  import { commandExists } from '../utils/cross-platform.js';
5
5
  import { scanInstances } from '../utils/instance-registry.js';
6
6
  import { saveDefaultsSafe, loadAllAgents } from '../config-store.js';
7
+ import { isCodexSdkAvailable } from '../agents/codex-runner.js';
7
8
  // ==================== Helpers ====================
8
9
  function ask(rl, question) {
9
10
  return new Promise(resolve => rl.question(question, resolve));
@@ -14,8 +15,13 @@ const BASEAGENT_ENV_KEY = {
14
15
  codex: 'OPENAI_API_KEY',
15
16
  gemini: 'GEMINI_API_KEY',
16
17
  };
18
+ function isBaseagentAvailable(baseagent) {
19
+ if (baseagent === 'codex')
20
+ return isCodexSdkAvailable();
21
+ return commandExists(baseagent);
22
+ }
17
23
  function detectAvailable() {
18
- return BASEAGENT_CANDIDATES.filter(b => commandExists(b));
24
+ return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
19
25
  }
20
26
  function pickDefault(available) {
21
27
  return (available.includes('claude') ? 'claude' : available[0]);
@@ -45,9 +51,10 @@ export async function cmdInit(options) {
45
51
  // ── 2. 探测 baseagent ──
46
52
  const available = detectAvailable();
47
53
  if (available.length === 0) {
48
- console.log('❌ 未检测到任何 baseagent CLI,请先安装至少一款:');
49
- for (const b of BASEAGENT_CANDIDATES)
50
- console.log(` - ${b}`);
54
+ console.log('❌ 未检测到可用 baseagent。请安装至少一款:');
55
+ console.log(' - claude CLI');
56
+ console.log(' - gemini CLI');
57
+ console.log(' - optional dependency @openai/codex-sdk');
51
58
  console.log('\n安装后重新运行 evolclaw init');
52
59
  return;
53
60
  }
@@ -70,7 +77,7 @@ export async function cmdInit(options) {
70
77
  return;
71
78
  }
72
79
  if (!available.includes(options.baseagent)) {
73
- console.log(`❌ ${options.baseagent} 未在 PATH 中检测到(可用: ${available.join('/')})`);
80
+ console.log(`❌ ${options.baseagent} 当前环境不可用(可用: ${available.join('/')})`);
74
81
  return;
75
82
  }
76
83
  chosen = options.baseagent;
@@ -104,7 +111,7 @@ export async function cmdInit(options) {
104
111
  continue;
105
112
  }
106
113
  if (!available.includes(input)) {
107
- console.log(` ${input} 未在 PATH 中检测到(可用: ${available.join('/')})`);
114
+ console.log(` ${input} 当前环境不可用(可用: ${available.join('/')})`);
108
115
  continue;
109
116
  }
110
117
  chosen = input;