evolclaw 3.1.1 → 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.
- package/CHANGELOG.md +428 -0
- package/README.md +3 -7
- package/SKILLS.md +311 -0
- package/dist/agents/claude-runner.js +1 -1
- package/dist/agents/codex-runner.js +75 -19
- package/dist/agents/gemini-runner.js +0 -2
- package/dist/agents/kit-renderer.js +59 -10
- package/dist/aun/aid/agentmd.js +50 -27
- package/dist/aun/aid/client.js +5 -11
- package/dist/aun/aid/identity.js +32 -13
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/msg/group.js +1 -0
- package/dist/aun/msg/p2p.js +15 -2
- package/dist/aun/msg/upload.js +57 -18
- package/dist/aun/rpc/connection.js +3 -0
- package/dist/channels/aun.js +122 -48
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +5 -4
- package/dist/channels/qqbot.js +1 -0
- package/dist/channels/wechat.js +1 -0
- package/dist/channels/wecom.js +1 -0
- package/dist/cli/agent.js +142 -40
- package/dist/cli/index.js +103 -58
- package/dist/cli/init-channel.js +4 -2
- package/dist/cli/init.js +55 -26
- package/dist/cli/watch-msg.js +3 -1
- package/dist/config-store.js +22 -1
- package/dist/core/channel-loader.js +4 -4
- package/dist/core/command-handler.js +626 -538
- package/dist/core/evolagent-registry.js +45 -9
- package/dist/core/evolagent.js +35 -4
- package/dist/core/message/im-renderer.js +14 -4
- package/dist/core/message/message-bridge.js +149 -25
- package/dist/core/message/message-processor.js +45 -38
- package/dist/core/session/session-fs-store.js +23 -0
- package/dist/core/session/session-manager.js +188 -42
- package/dist/index.js +15 -17
- package/dist/paths.js +35 -0
- package/dist/utils/cross-platform.js +2 -1
- package/kits/docs/INDEX.md +6 -0
- package/kits/eck_manifest.json +3 -3
- package/kits/rules/02-navigation.md +1 -0
- package/kits/rules/06-channel.md +2 -18
- package/kits/templates/system-fragments/baseagent.md +2 -2
- package/kits/templates/system-fragments/channel.md +18 -9
- package/kits/templates/system-fragments/eckruntime.md +14 -0
- package/kits/templates/system-fragments/identity.md +5 -6
- package/kits/templates/system-fragments/relation.md +7 -5
- package/kits/templates/system-fragments/venue.md +2 -3
- package/package.json +5 -2
- package/kits/templates/system-fragments/runtime.md +0 -19
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(
|
|
25
|
+
return BASEAGENT_CANDIDATES.filter(isBaseagentAvailable);
|
|
20
26
|
}
|
|
21
27
|
function pickDefaultBaseagent(available) {
|
|
22
28
|
if (available.length === 0)
|
|
@@ -239,22 +245,30 @@ export async function agentShow(aid) {
|
|
|
239
245
|
}
|
|
240
246
|
export async function agentCreateInteractive(opts = {}) {
|
|
241
247
|
const p = resolvePaths();
|
|
242
|
-
const
|
|
248
|
+
const ownRl = !opts.rl;
|
|
249
|
+
const rl = opts.rl ?? readline.createInterface({
|
|
243
250
|
input: opts.stdin || process.stdin,
|
|
244
251
|
output: opts.stdout || process.stdout,
|
|
245
252
|
});
|
|
246
253
|
const ask = (q) => new Promise(r => rl.question(q, r));
|
|
247
254
|
try {
|
|
255
|
+
const { isValidAid, aidCreate } = await import('../aun/aid/index.js');
|
|
248
256
|
const aidPrompt = opts.suggestedName
|
|
249
257
|
? `AID [${opts.suggestedName}]: `
|
|
250
258
|
: 'AID (e.g. mybot.agentid.pub): ';
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
259
|
+
let aid = '';
|
|
260
|
+
while (!aid) {
|
|
261
|
+
const aidInput = (await ask(aidPrompt)).trim();
|
|
262
|
+
const candidate = aidInput || opts.suggestedName;
|
|
263
|
+
if (!candidate) {
|
|
264
|
+
console.log(' ⚠ AID is required.');
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
if (!isValidAid(candidate)) {
|
|
268
|
+
console.log(` ⚠ Invalid AID "${candidate}": must be a valid multi-level domain (e.g. mybot.agentid.pub)`);
|
|
269
|
+
continue;
|
|
270
|
+
}
|
|
271
|
+
aid = candidate;
|
|
258
272
|
}
|
|
259
273
|
const agentDirPath = path.join(p.agentsDir, aid);
|
|
260
274
|
const configExists = fs.existsSync(path.join(agentDirPath, 'config.json'));
|
|
@@ -308,29 +322,51 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
308
322
|
// Baseagent
|
|
309
323
|
const available = detectAvailableBaseagents();
|
|
310
324
|
if (available.length === 0) {
|
|
311
|
-
return { ok: false, error: `No baseagent
|
|
325
|
+
return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
|
|
312
326
|
}
|
|
313
327
|
const defaultBa = pickDefaultBaseagent(available);
|
|
314
|
-
let baseagent
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
328
|
+
let baseagent;
|
|
329
|
+
if (available.length === 1) {
|
|
330
|
+
console.log(` Baseagent: ${defaultBa}`);
|
|
331
|
+
baseagent = defaultBa;
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
let chosen = null;
|
|
335
|
+
while (chosen === null) {
|
|
336
|
+
const input = (await ask(`Baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
|
|
337
|
+
if (!BASEAGENT_CANDIDATES.includes(input)) {
|
|
338
|
+
console.log(` Invalid choice. Options: ${BASEAGENT_CANDIDATES.join('/')}`);
|
|
339
|
+
continue;
|
|
340
|
+
}
|
|
341
|
+
if (!available.includes(input)) {
|
|
342
|
+
console.log(` ${input} is not available in the current environment. Available: ${available.join('/')}`);
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
chosen = input;
|
|
320
346
|
}
|
|
321
|
-
|
|
322
|
-
|
|
347
|
+
baseagent = chosen;
|
|
348
|
+
}
|
|
349
|
+
// Owner
|
|
350
|
+
let owner;
|
|
351
|
+
while (true) {
|
|
352
|
+
const ownerInput = (await ask('Owner AID (leave empty for auto-bind on first message): ')).trim();
|
|
353
|
+
if (!ownerInput) {
|
|
354
|
+
owner = undefined;
|
|
355
|
+
break;
|
|
356
|
+
}
|
|
357
|
+
if (!isValidAid(ownerInput)) {
|
|
358
|
+
console.log(` ⚠ Invalid Owner AID "${ownerInput}": must be a valid multi-level domain (e.g. alice.agentid.pub)`);
|
|
323
359
|
continue;
|
|
324
360
|
}
|
|
325
|
-
|
|
361
|
+
owner = ownerInput;
|
|
362
|
+
break;
|
|
326
363
|
}
|
|
327
|
-
// Owner
|
|
328
|
-
const owner = (await ask('Owner AID (leave empty for auto-bind on first message): ')).trim() || undefined;
|
|
329
364
|
// Name + description for agent.md
|
|
330
365
|
const defaultName = aid.split('.')[0];
|
|
331
366
|
const agentName = (await ask(`Display name [${defaultName}]: `)).trim() || defaultName;
|
|
332
367
|
const agentDescription = (await ask('Description (optional): ')).trim() || '';
|
|
333
|
-
|
|
368
|
+
if (ownRl)
|
|
369
|
+
rl.close();
|
|
334
370
|
const agentConfig = {
|
|
335
371
|
$schema_version: CONFIG_SCHEMA_VERSION,
|
|
336
372
|
aid,
|
|
@@ -359,31 +395,64 @@ export async function agentCreateInteractive(opts = {}) {
|
|
|
359
395
|
const agentMdPath = path.join(aunPath, 'AIDs', aid, 'agent.md');
|
|
360
396
|
fs.mkdirSync(path.dirname(agentMdPath), { recursive: true });
|
|
361
397
|
fs.writeFileSync(agentMdPath, content, 'utf-8');
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
398
|
+
// Upload with retry (3 attempts, 2s delay between retries)
|
|
399
|
+
const MAX_ATTEMPTS = 3;
|
|
400
|
+
const RETRY_DELAY_MS = 2000;
|
|
401
|
+
let lastError;
|
|
402
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
403
|
+
try {
|
|
404
|
+
if (attempt > 1) {
|
|
405
|
+
process.stdout.write(` ↻ agent.md 上传重试 (${attempt}/${MAX_ATTEMPTS})...\n`);
|
|
406
|
+
await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
|
|
407
|
+
}
|
|
408
|
+
await agentmdPut(content, { aid, aunPath });
|
|
409
|
+
agentmdUploaded = true;
|
|
410
|
+
break;
|
|
411
|
+
}
|
|
412
|
+
catch (e) {
|
|
413
|
+
lastError = e;
|
|
414
|
+
}
|
|
365
415
|
}
|
|
366
|
-
|
|
367
|
-
console.warn(` ⚠ agent.md upload failed: ${
|
|
416
|
+
if (!agentmdUploaded) {
|
|
417
|
+
console.warn(` ⚠ agent.md upload failed: ${lastError?.message || lastError}`);
|
|
368
418
|
console.warn(` → Retry later with: evolclaw aid agentmd put ${aid}`);
|
|
369
419
|
}
|
|
420
|
+
// Yield to allow the SDK WebSocket to fully close before IPC
|
|
421
|
+
await new Promise(r => setTimeout(r, 0));
|
|
370
422
|
}
|
|
371
423
|
catch (e) {
|
|
372
424
|
console.warn(` ⚠ agent.md generation failed: ${e?.message || e}`);
|
|
373
425
|
}
|
|
426
|
+
// Attempt hot-load via IPC (if daemon is running)
|
|
427
|
+
let hotLoaded = false;
|
|
428
|
+
let hotLoadError;
|
|
429
|
+
try {
|
|
430
|
+
const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid });
|
|
431
|
+
if (ipcResult?.ok) {
|
|
432
|
+
hotLoaded = true;
|
|
433
|
+
}
|
|
434
|
+
else if (ipcResult) {
|
|
435
|
+
hotLoadError = ipcResult.error;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
catch { /* daemon not running */ }
|
|
374
439
|
return {
|
|
375
440
|
ok: true,
|
|
376
441
|
aid,
|
|
377
442
|
configPath: toPosix(path.join(agentDirPath, 'config.json')),
|
|
378
443
|
aidCreated,
|
|
379
444
|
agentmdUploaded,
|
|
445
|
+
hotLoaded,
|
|
446
|
+
hotLoadError,
|
|
380
447
|
};
|
|
381
448
|
}
|
|
382
449
|
finally {
|
|
383
|
-
|
|
384
|
-
|
|
450
|
+
if (ownRl) {
|
|
451
|
+
try {
|
|
452
|
+
rl.close();
|
|
453
|
+
}
|
|
454
|
+
catch { /* ignore */ }
|
|
385
455
|
}
|
|
386
|
-
catch { /* ignore */ }
|
|
387
456
|
}
|
|
388
457
|
}
|
|
389
458
|
export async function agentCreateNonInteractive(opts) {
|
|
@@ -400,7 +469,7 @@ export async function agentCreateNonInteractive(opts) {
|
|
|
400
469
|
// Baseagent
|
|
401
470
|
const available = detectAvailableBaseagents();
|
|
402
471
|
if (available.length === 0) {
|
|
403
|
-
return { ok: false, error: `No baseagent
|
|
472
|
+
return { ok: false, error: `No usable baseagent detected. Install claude/gemini CLI or optional dependency @openai/codex-sdk.` };
|
|
404
473
|
}
|
|
405
474
|
let baseagent;
|
|
406
475
|
if (opts.baseagent) {
|
|
@@ -408,7 +477,7 @@ export async function agentCreateNonInteractive(opts) {
|
|
|
408
477
|
return { ok: false, error: `Invalid baseagent: ${opts.baseagent} (options: ${BASEAGENT_CANDIDATES.join('/')})` };
|
|
409
478
|
}
|
|
410
479
|
if (!available.includes(opts.baseagent)) {
|
|
411
|
-
return { ok: false, error: `${opts.baseagent} not
|
|
480
|
+
return { ok: false, error: `${opts.baseagent} is not available in the current environment (available: ${available.join('/')})` };
|
|
412
481
|
}
|
|
413
482
|
baseagent = opts.baseagent;
|
|
414
483
|
}
|
|
@@ -482,27 +551,55 @@ export async function agentCreateNonInteractive(opts) {
|
|
|
482
551
|
const agentMdPath = path.join(aunPath, 'AIDs', opts.aid, 'agent.md');
|
|
483
552
|
fs.mkdirSync(path.dirname(agentMdPath), { recursive: true });
|
|
484
553
|
fs.writeFileSync(agentMdPath, content, 'utf-8');
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
554
|
+
const MAX_ATTEMPTS = 3;
|
|
555
|
+
const RETRY_DELAY_MS = 2000;
|
|
556
|
+
let lastError;
|
|
557
|
+
for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt++) {
|
|
558
|
+
try {
|
|
559
|
+
if (attempt > 1)
|
|
560
|
+
await new Promise(r => setTimeout(r, RETRY_DELAY_MS));
|
|
561
|
+
await agentmdPut(content, { aid: opts.aid, aunPath });
|
|
562
|
+
agentmdUploaded = true;
|
|
563
|
+
break;
|
|
564
|
+
}
|
|
565
|
+
catch (e) {
|
|
566
|
+
lastError = e;
|
|
567
|
+
}
|
|
488
568
|
}
|
|
489
|
-
|
|
490
|
-
console.warn(`⚠ agent.md upload failed: ${
|
|
569
|
+
if (!agentmdUploaded) {
|
|
570
|
+
console.warn(`⚠ agent.md upload failed: ${lastError?.message || lastError}`);
|
|
491
571
|
console.warn(` Retry later with: evolclaw aid agentmd put ${opts.aid}`);
|
|
492
572
|
}
|
|
573
|
+
await new Promise(r => setTimeout(r, 0));
|
|
493
574
|
}
|
|
494
575
|
catch (e) {
|
|
495
576
|
console.warn(`⚠ agent.md generation failed: ${e?.message || e}`);
|
|
496
577
|
}
|
|
578
|
+
// Attempt hot-load via IPC (if daemon is running)
|
|
579
|
+
let hotLoaded = false;
|
|
580
|
+
let hotLoadError;
|
|
581
|
+
try {
|
|
582
|
+
const ipcResult = await ipcQuery(p.socket, { type: 'evolagent.load', aid: opts.aid });
|
|
583
|
+
if (ipcResult?.ok) {
|
|
584
|
+
hotLoaded = true;
|
|
585
|
+
}
|
|
586
|
+
else if (ipcResult) {
|
|
587
|
+
hotLoadError = ipcResult.error;
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
catch { /* daemon not running */ }
|
|
497
591
|
return {
|
|
498
592
|
ok: true,
|
|
499
593
|
aid: opts.aid,
|
|
500
594
|
configPath: toPosix(path.join(agentDirPath, 'config.json')),
|
|
501
595
|
aidCreated,
|
|
502
596
|
agentmdUploaded,
|
|
597
|
+
hotLoaded,
|
|
598
|
+
hotLoadError,
|
|
503
599
|
};
|
|
504
600
|
}
|
|
505
|
-
// ==================== agentSyncAids ====================
|
|
601
|
+
// ==================== agentSyncAids (deprecated) ====================
|
|
602
|
+
/** @deprecated sync-aids 已废弃,不再从 CLI 调用 */
|
|
506
603
|
export async function agentSyncAids() {
|
|
507
604
|
const p = resolvePaths();
|
|
508
605
|
const { aidList } = await import('../aun/aid/index.js');
|
|
@@ -665,7 +762,12 @@ export async function agentSet(aid, key, rawValue) {
|
|
|
665
762
|
}
|
|
666
763
|
const value = parseJsonValue(rawValue);
|
|
667
764
|
setNestedValue(config, key, value);
|
|
668
|
-
|
|
765
|
+
try {
|
|
766
|
+
saveAgent(config);
|
|
767
|
+
}
|
|
768
|
+
catch (e) {
|
|
769
|
+
return { ok: false, error: e?.message || String(e) };
|
|
770
|
+
}
|
|
669
771
|
// Try hot-reload
|
|
670
772
|
let reloaded = false;
|
|
671
773
|
try {
|
|
@@ -733,7 +835,7 @@ export async function agentChannelUpsert(opts) {
|
|
|
733
835
|
return {
|
|
734
836
|
ok: true,
|
|
735
837
|
aid: opts.aid,
|
|
736
|
-
channelKey: `${opts.
|
|
838
|
+
channelKey: `${opts.channel.type}#${encodeURIComponent(opts.aid)}#${opts.channel.name}`,
|
|
737
839
|
reloaded,
|
|
738
840
|
};
|
|
739
841
|
}
|
package/dist/cli/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import path from 'path';
|
|
|
4
4
|
import os from 'os';
|
|
5
5
|
import { spawn, execFileSync, execFile } from 'child_process';
|
|
6
6
|
import { promisify } from 'util';
|
|
7
|
-
import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot } from '../paths.js';
|
|
7
|
+
import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot, agentMdPath } from '../paths.js';
|
|
8
8
|
import { loadDefaults, loadAllAgents, mergeForAgent } from '../config-store.js';
|
|
9
9
|
import { resolveAnthropicConfig } from '../agents/resolve.js';
|
|
10
10
|
import { migrateProject } from '../config-store.js';
|
|
@@ -235,7 +235,7 @@ function formatLocalTime(ms) {
|
|
|
235
235
|
const d = new Date(ms);
|
|
236
236
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')} ${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}:${String(d.getSeconds()).padStart(2, '0')}`;
|
|
237
237
|
}
|
|
238
|
-
function printStartupInfo() {
|
|
238
|
+
function printStartupInfo(opts = {}) {
|
|
239
239
|
const pkgRoot = getPackageRoot();
|
|
240
240
|
const isNpmInstall = pkgRoot.includes('node_modules');
|
|
241
241
|
const cliRunsSource = !import.meta.url.includes('/dist/');
|
|
@@ -268,7 +268,15 @@ function printStartupInfo() {
|
|
|
268
268
|
version = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf-8')).version;
|
|
269
269
|
}
|
|
270
270
|
catch { }
|
|
271
|
-
|
|
271
|
+
let aunVer = null;
|
|
272
|
+
try {
|
|
273
|
+
aunVer = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'node_modules', '@agentunion', 'fastaun', 'package.json'), 'utf-8')).version;
|
|
274
|
+
}
|
|
275
|
+
catch { }
|
|
276
|
+
const pidPart = opts.pid ? ` (PID: ${opts.pid})` : '';
|
|
277
|
+
const aunPart = aunVer ? ` fastaun v${aunVer}` : '';
|
|
278
|
+
const prefix = opts.running ? '✓ EvolClaw is running , v' : ' EvolClaw v';
|
|
279
|
+
console.log(`${prefix}${version}${pidPart}${aunPart}`);
|
|
272
280
|
console.log(` 包路径: ${pkgRoot}`);
|
|
273
281
|
console.log(` 安装类型: ${isNpmInstall ? 'npm全局安装' : '开发仓(link)'}`);
|
|
274
282
|
console.log(` CLI执行: ${cliRunsSource ? '源码(tsx)' : '编译产物(dist)'}`);
|
|
@@ -829,7 +837,7 @@ async function cmdStatus() {
|
|
|
829
837
|
console.log('');
|
|
830
838
|
}
|
|
831
839
|
if (pid) {
|
|
832
|
-
|
|
840
|
+
printStartupInfo({ pid, running: true });
|
|
833
841
|
console.log('');
|
|
834
842
|
console.log('📊 Process Info:');
|
|
835
843
|
try {
|
|
@@ -875,8 +883,8 @@ async function cmdStatus() {
|
|
|
875
883
|
const configChannelNames = new Set();
|
|
876
884
|
for (const cfg of agents) {
|
|
877
885
|
for (const inst of cfg.channels) {
|
|
878
|
-
// effective key: <
|
|
879
|
-
configChannelNames.add(`${
|
|
886
|
+
// effective key: <type>#<urlEncode(selfPeerId)>#<name>
|
|
887
|
+
configChannelNames.add(`${inst.type}#${encodeURIComponent(cfg.aid)}#${inst.name}`);
|
|
880
888
|
}
|
|
881
889
|
}
|
|
882
890
|
for (const s of allSessions) {
|
|
@@ -1005,7 +1013,7 @@ async function cmdStatus() {
|
|
|
1005
1013
|
}
|
|
1006
1014
|
}
|
|
1007
1015
|
/**
|
|
1008
|
-
* 把 channel fingerprint 列表(`<
|
|
1016
|
+
* 把 channel fingerprint 列表(`<type>#<selfPeerId>#<name>`)折叠成展示用摘要。
|
|
1009
1017
|
*
|
|
1010
1018
|
* 聚合规则:
|
|
1011
1019
|
* - 按 type 分组
|
|
@@ -1027,8 +1035,8 @@ function summarizeChannelFingerprints(fingerprints) {
|
|
|
1027
1035
|
}
|
|
1028
1036
|
continue;
|
|
1029
1037
|
}
|
|
1030
|
-
const type = parts[
|
|
1031
|
-
const name = parts
|
|
1038
|
+
const type = parts[0];
|
|
1039
|
+
const name = parts[2];
|
|
1032
1040
|
if (!groups.has(type)) {
|
|
1033
1041
|
groups.set(type, []);
|
|
1034
1042
|
order.push(type);
|
|
@@ -1734,10 +1742,10 @@ async function cmdWatchAid() {
|
|
|
1734
1742
|
const refreshedAids = new Set();
|
|
1735
1743
|
function readLocalName(aid) {
|
|
1736
1744
|
try {
|
|
1737
|
-
const
|
|
1738
|
-
if (!fs.existsSync(
|
|
1745
|
+
const mdPath = agentMdPath(aid);
|
|
1746
|
+
if (!fs.existsSync(mdPath))
|
|
1739
1747
|
return undefined;
|
|
1740
|
-
const content = fs.readFileSync(
|
|
1748
|
+
const content = fs.readFileSync(mdPath, 'utf-8');
|
|
1741
1749
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1742
1750
|
if (!fmMatch)
|
|
1743
1751
|
return undefined;
|
|
@@ -2463,12 +2471,14 @@ function archiveSelfHealLog(p, log) {
|
|
|
2463
2471
|
* Searches across all channel types (feishu, wechat, aun) for a matching instance.
|
|
2464
2472
|
*/
|
|
2465
2473
|
function resolveInstanceConfig(instanceName) {
|
|
2466
|
-
// 新结构:channel key 是 <
|
|
2474
|
+
// 新结构:channel key 是 <type>#<selfPeerId>#<name>,解析后从对应 agent 的 channels[] 找
|
|
2467
2475
|
const parts = instanceName.split('#');
|
|
2468
2476
|
if (parts.length === 3) {
|
|
2469
|
-
const [
|
|
2477
|
+
const [type, encodedSelfPeerId, name] = parts;
|
|
2478
|
+
const selfPeerId = decodeURIComponent(encodedSelfPeerId);
|
|
2470
2479
|
const { agents } = loadAllAgents();
|
|
2471
|
-
|
|
2480
|
+
// AUN channel 的 selfPeerId 就是 agent.aid
|
|
2481
|
+
const agent = agents.find(a => a.aid === selfPeerId);
|
|
2472
2482
|
if (!agent)
|
|
2473
2483
|
return null;
|
|
2474
2484
|
const inst = agent.channels.find((c) => c.type === type && c.name === name);
|
|
@@ -2729,6 +2739,7 @@ async function cmdCtl(args) {
|
|
|
2729
2739
|
查询:
|
|
2730
2740
|
status 查看会话状态
|
|
2731
2741
|
check 检查渠道健康状态
|
|
2742
|
+
pwd 显示当前项目路径
|
|
2732
2743
|
help 显示帮助
|
|
2733
2744
|
|
|
2734
2745
|
配置:
|
|
@@ -2737,15 +2748,14 @@ async function cmdCtl(args) {
|
|
|
2737
2748
|
compact 压缩当前会话上下文
|
|
2738
2749
|
perm [mode] 查看/切换权限模式
|
|
2739
2750
|
|
|
2740
|
-
项目:
|
|
2741
|
-
bind <path> 注册项目目录(不切换当前会话)
|
|
2742
|
-
|
|
2743
2751
|
消息:
|
|
2744
2752
|
send <消息内容> 主动发送文本消息(proactive 模式)
|
|
2745
2753
|
file [channel] <path> 发送项目内文件
|
|
2746
2754
|
|
|
2755
|
+
Agent:
|
|
2756
|
+
agent <subcommand> EvolAgent 管理(list/show/new/enable/disable/reload/delete)
|
|
2757
|
+
|
|
2747
2758
|
运维:
|
|
2748
|
-
agentmd [put|set <内容>] 查看/管理 agent.md(仅 AUN 通道)
|
|
2749
2759
|
restart [channel] 重启服务或重连指定渠道
|
|
2750
2760
|
|
|
2751
2761
|
示例:
|
|
@@ -2798,7 +2808,6 @@ Commands:
|
|
|
2798
2808
|
show <aid> 查看 agent 详情(身份 + 配置 + 连接 + 会话 + 路径)
|
|
2799
2809
|
new [aid] 交互式创建 agent
|
|
2800
2810
|
new <aid> --non-interactive ... 非交互式创建
|
|
2801
|
-
sync-aids 从本地 AID 批量创建 agent
|
|
2802
2811
|
enable <aid> 启用 agent
|
|
2803
2812
|
disable <aid> 停用 agent
|
|
2804
2813
|
get <aid> <key> 读取单个配置字段(支持点路径)
|
|
@@ -2821,7 +2830,7 @@ Options:
|
|
|
2821
2830
|
evolclaw agent delete mybot.agentid.pub --purge`);
|
|
2822
2831
|
return;
|
|
2823
2832
|
}
|
|
2824
|
-
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive,
|
|
2833
|
+
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
|
|
2825
2834
|
// --- list ---
|
|
2826
2835
|
if (!sub || sub === 'list') {
|
|
2827
2836
|
const result = await agentList();
|
|
@@ -2921,7 +2930,11 @@ Options:
|
|
|
2921
2930
|
console.log(result.agentmdUploaded
|
|
2922
2931
|
? ' ✓ agent.md 已发布'
|
|
2923
2932
|
: ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
|
|
2924
|
-
console.log(
|
|
2933
|
+
console.log(result.hotLoaded
|
|
2934
|
+
? ' ✓ 已热重载,agent 已上线'
|
|
2935
|
+
: result.hotLoadError
|
|
2936
|
+
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
2937
|
+
: ' ⚠ 服务未运行,下次 evolclaw start 时生效');
|
|
2925
2938
|
}
|
|
2926
2939
|
}
|
|
2927
2940
|
else {
|
|
@@ -2943,41 +2956,37 @@ Options:
|
|
|
2943
2956
|
console.log(result.agentmdUploaded
|
|
2944
2957
|
? ' ✓ agent.md 已发布'
|
|
2945
2958
|
: ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
|
|
2946
|
-
console.log(
|
|
2959
|
+
console.log(result.hotLoaded
|
|
2960
|
+
? ' ✓ 已热重载,agent 已上线'
|
|
2961
|
+
: result.hotLoadError
|
|
2962
|
+
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
2963
|
+
: ' ⚠ 服务未运行,下次 evolclaw start 时生效');
|
|
2947
2964
|
}
|
|
2948
2965
|
}
|
|
2949
2966
|
return;
|
|
2950
2967
|
}
|
|
2951
|
-
// --- sync-aids ---
|
|
2952
|
-
if (sub === 'sync-aids') {
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
2959
|
-
|
|
2960
|
-
|
|
2961
|
-
|
|
2962
|
-
|
|
2963
|
-
|
|
2964
|
-
|
|
2965
|
-
|
|
2966
|
-
|
|
2967
|
-
|
|
2968
|
-
|
|
2969
|
-
|
|
2970
|
-
|
|
2971
|
-
|
|
2972
|
-
|
|
2973
|
-
console.log(` ✓ ${aid}`);
|
|
2974
|
-
if (result.hotReloaded)
|
|
2975
|
-
console.log(' ✓ 已热加载到运行中的进程');
|
|
2976
|
-
else
|
|
2977
|
-
console.log(' evolclaw 未运行,新 agent 将在下次启动时加载。');
|
|
2978
|
-
}
|
|
2979
|
-
return;
|
|
2980
|
-
}
|
|
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
|
+
// }
|
|
2981
2990
|
// --- reload ---
|
|
2982
2991
|
if (sub === 'reload') {
|
|
2983
2992
|
const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
@@ -3438,8 +3447,7 @@ Options:
|
|
|
3438
3447
|
console.error(`❌ 无效 AID 格式: ${aid}`);
|
|
3439
3448
|
process.exit(1);
|
|
3440
3449
|
}
|
|
3441
|
-
const
|
|
3442
|
-
const localPath = path.join(aunBase, 'AIDs', aid, 'agent.md');
|
|
3450
|
+
const localPath = agentMdPath(aid);
|
|
3443
3451
|
if (!fs.existsSync(localPath)) {
|
|
3444
3452
|
console.error(`❌ 本地无 agent.md: ${aid}`);
|
|
3445
3453
|
process.exit(1);
|
|
@@ -3747,12 +3755,15 @@ Options:
|
|
|
3747
3755
|
--app <name> 指定应用 slot(隔离 ack 游标)
|
|
3748
3756
|
--as-daemon ack 时显式以 daemon 身份(高危,会污染 daemon 游标)
|
|
3749
3757
|
--format json 输出 JSON 格式
|
|
3758
|
+
--encrypt 启用端到端加密
|
|
3759
|
+
--thread <id> 指定话题 ID(用于多话题路由)
|
|
3750
3760
|
--content-type <mime> 显式覆盖 MIME(仅 --file 模式)
|
|
3751
3761
|
--text <说明> 附件说明文字(仅 --file 模式)
|
|
3752
3762
|
--transcript <text> 语音转写(仅 --as voice)
|
|
3753
3763
|
|
|
3754
3764
|
示例:
|
|
3755
3765
|
evolclaw msg send alice.agentid.pub bob.agentid.pub "hello"
|
|
3766
|
+
evolclaw msg send alice.agentid.pub bob.agentid.pub "讨论项目A" --thread "project-A"
|
|
3756
3767
|
evolclaw msg send alice.agentid.pub bob.agentid.pub --file ./pic.png
|
|
3757
3768
|
evolclaw msg send alice.agentid.pub bob.agentid.pub --file ./demo.mp4 --as video
|
|
3758
3769
|
evolclaw msg send alice.agentid.pub bob.agentid.pub --link https://example.com --title "AUN"
|
|
@@ -3826,7 +3837,29 @@ Options:
|
|
|
3826
3837
|
body = { mode: 'text', text };
|
|
3827
3838
|
}
|
|
3828
3839
|
const encrypt = args.includes('--encrypt');
|
|
3829
|
-
const
|
|
3840
|
+
const thread = getArgValue(args, '--thread');
|
|
3841
|
+
// 文件上传进度展示(非 JSON 输出时)。仅在大文件降级到 HTTP PUT 阶段会逐块更新。
|
|
3842
|
+
let lastPctShown = -1;
|
|
3843
|
+
const onUploadProgress = formatJson ? undefined : (info) => {
|
|
3844
|
+
if (info.phase === 'inline')
|
|
3845
|
+
return; // 内联阶段不分块,跳过
|
|
3846
|
+
if (info.phase === 'http-put') {
|
|
3847
|
+
const pct = info.total > 0 ? Math.floor((info.bytes / info.total) * 100) : 0;
|
|
3848
|
+
if (pct === lastPctShown && info.bytes < info.total)
|
|
3849
|
+
return;
|
|
3850
|
+
lastPctShown = pct;
|
|
3851
|
+
const mb = (n) => (n / 1024 / 1024).toFixed(2);
|
|
3852
|
+
const eol = info.bytes >= info.total ? '\n' : '\r';
|
|
3853
|
+
process.stderr.write(` ⏫ uploading: ${pct}% (${mb(info.bytes)}/${mb(info.total)} MB)${eol}`);
|
|
3854
|
+
}
|
|
3855
|
+
else if (info.phase === 'session-create') {
|
|
3856
|
+
process.stderr.write(' ⏫ requesting upload session...\n');
|
|
3857
|
+
}
|
|
3858
|
+
else if (info.phase === 'session-complete') {
|
|
3859
|
+
process.stderr.write(' ⏫ finalizing upload...\n');
|
|
3860
|
+
}
|
|
3861
|
+
};
|
|
3862
|
+
const result = await msgSend({ from, to, body, encrypt, thread, onUploadProgress, ...commonOpts });
|
|
3830
3863
|
if (!result.ok) {
|
|
3831
3864
|
if (formatJson) {
|
|
3832
3865
|
console.log(JSON.stringify(result));
|
|
@@ -4408,15 +4441,23 @@ export async function main(args) {
|
|
|
4408
4441
|
evolclaw init wecom 企业微信 AI Bot 配置(手动输入)`);
|
|
4409
4442
|
}
|
|
4410
4443
|
else if (args[1] === 'wechat') {
|
|
4444
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4445
|
+
suppressSdkLogs();
|
|
4411
4446
|
await cmdInitWechat();
|
|
4412
4447
|
}
|
|
4413
4448
|
else if (args[1] === 'feishu') {
|
|
4449
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4450
|
+
suppressSdkLogs();
|
|
4414
4451
|
await cmdInitFeishu();
|
|
4415
4452
|
}
|
|
4416
4453
|
else if (args[1] === 'dingtalk') {
|
|
4454
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4455
|
+
suppressSdkLogs();
|
|
4417
4456
|
await cmdInitDingtalk();
|
|
4418
4457
|
}
|
|
4419
4458
|
else if (args[1] === 'qqbot') {
|
|
4459
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4460
|
+
suppressSdkLogs();
|
|
4420
4461
|
await cmdInitQQBot();
|
|
4421
4462
|
}
|
|
4422
4463
|
else if (args[1] === 'wecom') {
|
|
@@ -4429,6 +4470,8 @@ export async function main(args) {
|
|
|
4429
4470
|
process.exit(1);
|
|
4430
4471
|
}
|
|
4431
4472
|
else {
|
|
4473
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4474
|
+
suppressSdkLogs();
|
|
4432
4475
|
const nonInteractive = args.includes('--non-interactive');
|
|
4433
4476
|
await cmdInit({
|
|
4434
4477
|
nonInteractive,
|
|
@@ -4509,9 +4552,12 @@ export async function main(args) {
|
|
|
4509
4552
|
case 'ctl':
|
|
4510
4553
|
await cmdCtl(args.slice(1));
|
|
4511
4554
|
break;
|
|
4512
|
-
case 'agent':
|
|
4555
|
+
case 'agent': {
|
|
4556
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4557
|
+
suppressSdkLogs();
|
|
4513
4558
|
await cmdAgent(args.slice(1));
|
|
4514
4559
|
break;
|
|
4560
|
+
}
|
|
4515
4561
|
case 'aid': {
|
|
4516
4562
|
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4517
4563
|
suppressSdkLogs();
|
|
@@ -4590,7 +4636,6 @@ Commands:
|
|
|
4590
4636
|
--name <display-name>
|
|
4591
4637
|
--description <text>
|
|
4592
4638
|
--force (覆盖已有 config.json)
|
|
4593
|
-
agent sync-aids 从本地 AID 批量同步创建 agent(以最早 agent 为模板)
|
|
4594
4639
|
agent reload 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
|
|
4595
4640
|
agent reload <n> 热重载指定 agent 配置
|
|
4596
4641
|
aid AID 身份管理
|
package/dist/cli/init-channel.js
CHANGED
|
@@ -10,6 +10,7 @@ import readline from 'readline';
|
|
|
10
10
|
import path from 'path';
|
|
11
11
|
import os from 'os';
|
|
12
12
|
import crypto from 'crypto';
|
|
13
|
+
import { aidLocalDir } from '../paths.js';
|
|
13
14
|
import { selectInstance } from './init.js';
|
|
14
15
|
import { npmInstallGlobal } from '../utils/npm-ops.js';
|
|
15
16
|
import { loadAllAgents, loadAgent } from '../config-store.js';
|
|
@@ -470,8 +471,9 @@ export async function setupAunAid(rl, _config) {
|
|
|
470
471
|
console.log(` ⚠ agent.md 发布失败(首次连接将自动重试): ${String(e.message || e).slice(0, 100)}`);
|
|
471
472
|
// Still write local copy as fallback
|
|
472
473
|
try {
|
|
473
|
-
|
|
474
|
-
fs.
|
|
474
|
+
const localDir = aidLocalDir(aid);
|
|
475
|
+
fs.mkdirSync(localDir, { recursive: true });
|
|
476
|
+
fs.writeFileSync(path.join(localDir, 'agent.md'), content, 'utf-8');
|
|
475
477
|
console.log(' ✓ agent.md 已写入本地');
|
|
476
478
|
}
|
|
477
479
|
catch (we) {
|