evolclaw 3.1.0 → 3.1.2
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 +407 -0
- package/README.md +1 -1
- package/SKILLS.md +311 -0
- package/dist/agents/claude-runner.js +40 -3
- package/dist/aun/aid/agentmd.js +7 -6
- package/dist/aun/aid/client.js +5 -11
- package/dist/aun/aid/identity.js +32 -13
- package/dist/aun/msg/group.js +1 -0
- package/dist/aun/msg/p2p.js +51 -0
- package/dist/aun/msg/upload.js +57 -18
- package/dist/channels/aun.js +124 -50
- package/dist/channels/dingtalk.js +2 -0
- package/dist/channels/feishu.js +15 -6
- package/dist/channels/qqbot.js +2 -0
- package/dist/channels/wechat.js +2 -0
- package/dist/channels/wecom.js +2 -0
- package/dist/cli/agent.js +130 -35
- package/dist/cli/index.js +221 -48
- package/dist/cli/init-channel.js +4 -2
- package/dist/cli/init.js +44 -23
- package/dist/cli/watch-msg.js +109 -30
- package/dist/config-store.js +67 -1
- package/dist/core/channel-loader.js +4 -4
- package/dist/core/command-handler.js +95 -84
- package/dist/core/evolagent-registry.js +45 -9
- package/dist/core/evolagent.js +4 -4
- package/dist/core/message/im-renderer.js +47 -8
- package/dist/core/message/message-bridge.js +30 -1
- package/dist/core/message/message-log.js +6 -1
- package/dist/core/message/message-processor.js +29 -35
- package/dist/core/relation/peer-identity.js +161 -0
- package/dist/core/session/session-fs-store.js +23 -0
- package/dist/core/session/session-manager.js +11 -4
- package/dist/core/trigger/manager.js +16 -0
- package/dist/core/trigger/parser.js +110 -0
- package/dist/core/trigger/scheduler.js +6 -0
- package/dist/index.js +64 -20
- package/dist/paths.js +35 -0
- package/dist/utils/cross-platform.js +2 -1
- package/dist/utils/error-utils.js +17 -13
- package/dist/utils/stats.js +216 -2
- package/kits/docs/INDEX.md +6 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +53 -6
- package/kits/rules/06-channel.md +30 -0
- package/package.json +6 -3
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';
|
|
@@ -231,11 +231,61 @@ function reportOrphans(orphans) {
|
|
|
231
231
|
console.log(' 这些进程不属于当前 HOME 的实例登记簿,自动清理不会处理它们。');
|
|
232
232
|
console.log(' 使用 evolclaw restart --clear 一并清掉,或手动 kill。');
|
|
233
233
|
}
|
|
234
|
-
|
|
235
|
-
const
|
|
234
|
+
function formatLocalTime(ms) {
|
|
235
|
+
const d = new Date(ms);
|
|
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
|
+
}
|
|
238
|
+
function printStartupInfo(opts = {}) {
|
|
236
239
|
const pkgRoot = getPackageRoot();
|
|
237
240
|
const isNpmInstall = pkgRoot.includes('node_modules');
|
|
238
|
-
|
|
241
|
+
const cliRunsSource = !import.meta.url.includes('/dist/');
|
|
242
|
+
const daemonEntry = path.join(pkgRoot, 'dist', 'index.js');
|
|
243
|
+
const daemonRunsDist = fs.existsSync(daemonEntry);
|
|
244
|
+
const scanDir = path.join(pkgRoot, daemonRunsDist ? 'dist' : 'src');
|
|
245
|
+
let latestMtime = 0;
|
|
246
|
+
const scanRecursive = (dir) => {
|
|
247
|
+
try {
|
|
248
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
249
|
+
if (entry.name === 'node_modules')
|
|
250
|
+
continue;
|
|
251
|
+
const full = path.join(dir, entry.name);
|
|
252
|
+
if (entry.isDirectory()) {
|
|
253
|
+
scanRecursive(full);
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (entry.name.endsWith('.js') || entry.name.endsWith('.ts')) {
|
|
257
|
+
const mt = fs.statSync(full).mtimeMs;
|
|
258
|
+
if (mt > latestMtime)
|
|
259
|
+
latestMtime = mt;
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
catch { }
|
|
264
|
+
};
|
|
265
|
+
scanRecursive(scanDir);
|
|
266
|
+
let version = '?';
|
|
267
|
+
try {
|
|
268
|
+
version = JSON.parse(fs.readFileSync(path.join(pkgRoot, 'package.json'), 'utf-8')).version;
|
|
269
|
+
}
|
|
270
|
+
catch { }
|
|
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}`);
|
|
280
|
+
console.log(` 包路径: ${pkgRoot}`);
|
|
281
|
+
console.log(` 安装类型: ${isNpmInstall ? 'npm全局安装' : '开发仓(link)'}`);
|
|
282
|
+
console.log(` CLI执行: ${cliRunsSource ? '源码(tsx)' : '编译产物(dist)'}`);
|
|
283
|
+
console.log(` Daemon执行: ${daemonRunsDist ? '编译产物(dist)' : '未知'}`);
|
|
284
|
+
console.log(` 代码时间: ${latestMtime ? formatLocalTime(latestMtime) : '?'}`);
|
|
285
|
+
}
|
|
286
|
+
async function cmdStart() {
|
|
287
|
+
const cmdStartedAt = Date.now();
|
|
288
|
+
printStartupInfo();
|
|
239
289
|
const p = resolvePaths();
|
|
240
290
|
ensureDataDirs();
|
|
241
291
|
// 旧配置自动迁移(evolclaw.json → 新结构)
|
|
@@ -263,7 +313,7 @@ async function cmdStart() {
|
|
|
263
313
|
if (aliveMains.length > 0) {
|
|
264
314
|
const first = aliveMains[0];
|
|
265
315
|
console.log(`❌ EvolClaw is already running (PID: ${aliveMains.map(m => m.record.pid).join(', ')})`);
|
|
266
|
-
console.log(` 启动于: ${first.record.startedAtIso}`);
|
|
316
|
+
console.log(` 启动于: ${new Date(first.record.startedAtIso).toLocaleString()}`);
|
|
267
317
|
console.log(` 启动方式: ${first.record.launchedBy}`);
|
|
268
318
|
// 报告 AID 状态
|
|
269
319
|
if (status.aidLastActivity.size > 0) {
|
|
@@ -444,9 +494,7 @@ async function cmdStop() {
|
|
|
444
494
|
}
|
|
445
495
|
async function cmdRestart(opts = {}) {
|
|
446
496
|
const cmdStartedAt = Date.now();
|
|
447
|
-
|
|
448
|
-
const isNpmInstall = pkgRoot.includes('node_modules');
|
|
449
|
-
console.log(`⏱ ${new Date().toLocaleString()} [${isNpmInstall ? 'pkg' : 'dev'}] ${pkgRoot}`);
|
|
497
|
+
printStartupInfo();
|
|
450
498
|
console.log('🔄 Restarting EvolClaw...');
|
|
451
499
|
// 版本检查与自动升级
|
|
452
500
|
console.log('📦 Checking for updates...');
|
|
@@ -789,7 +837,7 @@ async function cmdStatus() {
|
|
|
789
837
|
console.log('');
|
|
790
838
|
}
|
|
791
839
|
if (pid) {
|
|
792
|
-
|
|
840
|
+
printStartupInfo({ pid, running: true });
|
|
793
841
|
console.log('');
|
|
794
842
|
console.log('📊 Process Info:');
|
|
795
843
|
try {
|
|
@@ -835,8 +883,8 @@ async function cmdStatus() {
|
|
|
835
883
|
const configChannelNames = new Set();
|
|
836
884
|
for (const cfg of agents) {
|
|
837
885
|
for (const inst of cfg.channels) {
|
|
838
|
-
// effective key: <
|
|
839
|
-
configChannelNames.add(`${
|
|
886
|
+
// effective key: <type>#<urlEncode(selfPeerId)>#<name>
|
|
887
|
+
configChannelNames.add(`${inst.type}#${encodeURIComponent(cfg.aid)}#${inst.name}`);
|
|
840
888
|
}
|
|
841
889
|
}
|
|
842
890
|
for (const s of allSessions) {
|
|
@@ -965,7 +1013,7 @@ async function cmdStatus() {
|
|
|
965
1013
|
}
|
|
966
1014
|
}
|
|
967
1015
|
/**
|
|
968
|
-
* 把 channel fingerprint 列表(`<
|
|
1016
|
+
* 把 channel fingerprint 列表(`<type>#<selfPeerId>#<name>`)折叠成展示用摘要。
|
|
969
1017
|
*
|
|
970
1018
|
* 聚合规则:
|
|
971
1019
|
* - 按 type 分组
|
|
@@ -987,8 +1035,8 @@ function summarizeChannelFingerprints(fingerprints) {
|
|
|
987
1035
|
}
|
|
988
1036
|
continue;
|
|
989
1037
|
}
|
|
990
|
-
const type = parts[
|
|
991
|
-
const name = parts
|
|
1038
|
+
const type = parts[0];
|
|
1039
|
+
const name = parts[2];
|
|
992
1040
|
if (!groups.has(type)) {
|
|
993
1041
|
groups.set(type, []);
|
|
994
1042
|
order.push(type);
|
|
@@ -1523,7 +1571,7 @@ function cmdWatch() {
|
|
|
1523
1571
|
}
|
|
1524
1572
|
const m = aliveMainEntries[0].record;
|
|
1525
1573
|
const uptime = formatTimeAgo(Date.now() - m.startedAt);
|
|
1526
|
-
console.log(`📦 Instance: PID ${m.pid} | 启动于 ${m.startedAtIso} (${uptime}) | via ${m.launchedBy}`);
|
|
1574
|
+
console.log(`📦 Instance: PID ${m.pid} | 启动于 ${new Date(m.startedAtIso).toLocaleString()} (${uptime}) | via ${m.launchedBy}`);
|
|
1527
1575
|
if (instStatus.aidLastActivity.size > 0) {
|
|
1528
1576
|
const now = Date.now();
|
|
1529
1577
|
const aidLines = [];
|
|
@@ -1694,10 +1742,10 @@ async function cmdWatchAid() {
|
|
|
1694
1742
|
const refreshedAids = new Set();
|
|
1695
1743
|
function readLocalName(aid) {
|
|
1696
1744
|
try {
|
|
1697
|
-
const
|
|
1698
|
-
if (!fs.existsSync(
|
|
1745
|
+
const mdPath = agentMdPath(aid);
|
|
1746
|
+
if (!fs.existsSync(mdPath))
|
|
1699
1747
|
return undefined;
|
|
1700
|
-
const content = fs.readFileSync(
|
|
1748
|
+
const content = fs.readFileSync(mdPath, 'utf-8');
|
|
1701
1749
|
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
1702
1750
|
if (!fmMatch)
|
|
1703
1751
|
return undefined;
|
|
@@ -1773,6 +1821,11 @@ async function cmdWatchAid() {
|
|
|
1773
1821
|
const COL_LRECV = 10;
|
|
1774
1822
|
const COL_LSENT = 10;
|
|
1775
1823
|
const COL_PEERS = 5;
|
|
1824
|
+
// 表头跟随系统语言
|
|
1825
|
+
const isChinese = (process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || Intl.DateTimeFormat().resolvedOptions().locale || '').toLowerCase().includes('zh');
|
|
1826
|
+
const HEADERS = isChinese
|
|
1827
|
+
? { aid: 'AID', status: '状态', uptime: '运行', state: '工作', reconn: '重连', recv: '收', sent: '发', sys: '系统', bin: '入流量', bout: '出流量', lrecv: '最后收', lsent: '最后发', peers: '对端' }
|
|
1828
|
+
: { aid: 'AID', status: 'STATUS', uptime: 'UPTIME', state: 'STATE', reconn: 'RECONN', recv: 'RECV', sent: 'SENT', sys: 'SYS R/S', bin: 'BYTES IN', bout: 'BYTES OUT', lrecv: 'LAST RECV', lsent: 'LAST SENT', peers: 'PEERS' };
|
|
1776
1829
|
function formatDuration(ms) {
|
|
1777
1830
|
const sec = Math.floor(ms / 1000);
|
|
1778
1831
|
if (sec < 60)
|
|
@@ -1790,19 +1843,19 @@ async function cmdWatchAid() {
|
|
|
1790
1843
|
}
|
|
1791
1844
|
function renderHeader() {
|
|
1792
1845
|
return ' ' +
|
|
1793
|
-
padRight(
|
|
1794
|
-
padRight(
|
|
1795
|
-
padRight(
|
|
1796
|
-
padRight(
|
|
1797
|
-
padRight(
|
|
1798
|
-
padRight(
|
|
1799
|
-
padRight(
|
|
1800
|
-
padRight(
|
|
1801
|
-
padRight(
|
|
1802
|
-
padRight(
|
|
1803
|
-
padRight(
|
|
1804
|
-
padRight(
|
|
1805
|
-
padRight(
|
|
1846
|
+
padRight(HEADERS.aid, COL_AID) +
|
|
1847
|
+
padRight(HEADERS.status, COL_STATUS) +
|
|
1848
|
+
padRight(HEADERS.uptime, COL_UPTIME) +
|
|
1849
|
+
padRight(HEADERS.state, COL_STATE) +
|
|
1850
|
+
padRight(HEADERS.reconn, COL_RECONN) +
|
|
1851
|
+
padRight(HEADERS.recv, COL_RECV) +
|
|
1852
|
+
padRight(HEADERS.sent, COL_SENT) +
|
|
1853
|
+
padRight(HEADERS.sys, COL_SYS) +
|
|
1854
|
+
padRight(HEADERS.bin, COL_BIN) +
|
|
1855
|
+
padRight(HEADERS.bout, COL_BOUT) +
|
|
1856
|
+
padRight(HEADERS.lrecv, COL_LRECV) +
|
|
1857
|
+
padRight(HEADERS.lsent, COL_LSENT) +
|
|
1858
|
+
padRight(HEADERS.peers, COL_PEERS);
|
|
1806
1859
|
}
|
|
1807
1860
|
function renderRow(aid, stats, projectPath) {
|
|
1808
1861
|
const aidLabel = aid.aid.length > COL_AID - 2 ? aid.aid.slice(0, COL_AID - 4) + '..' : aid.aid;
|
|
@@ -1841,6 +1894,13 @@ async function cmdWatchAid() {
|
|
|
1841
1894
|
const nameReset = refreshedAids.has(aid.aid) ? '' : RST;
|
|
1842
1895
|
const BLUE = useColor ? '\x1b[34m' : '';
|
|
1843
1896
|
const ORANGE = useColor ? '\x1b[38;5;208m' : '';
|
|
1897
|
+
const MAGENTA = useColor ? '\x1b[35m' : '';
|
|
1898
|
+
// 标记生成:[明文/密文|自主/响应](紫色=工具渲染标记)
|
|
1899
|
+
const mkTags = (encrypt, chatmode) => {
|
|
1900
|
+
const enc = encrypt ? '密文' : '明文';
|
|
1901
|
+
const mode = chatmode === 'proactive' ? '自主' : '响应';
|
|
1902
|
+
return `${MAGENTA}[${enc}|${mode}]${RST}`;
|
|
1903
|
+
};
|
|
1844
1904
|
let msgPreview = '';
|
|
1845
1905
|
if (stats?.lastReceivedAt || stats?.lastSentAt) {
|
|
1846
1906
|
const recvTs = stats.lastReceivedAt ?? 0;
|
|
@@ -1851,13 +1911,53 @@ async function cmdWatchAid() {
|
|
|
1851
1911
|
}
|
|
1852
1912
|
else if (stats.lastSentText) {
|
|
1853
1913
|
const toShort = stats.lastSentTo ? stats.lastSentTo.split('.')[0] : '';
|
|
1854
|
-
|
|
1914
|
+
const tags = mkTags(stats.lastSentEncrypt, stats.lastSentChatmode);
|
|
1915
|
+
// task 进行中时也显示计数(processing > 0 说明还在跑)
|
|
1916
|
+
const isWorking = (stats.processing ?? 0) > 0;
|
|
1917
|
+
const taskEnd = stats?.lastTaskEnd;
|
|
1918
|
+
const counts = isWorking && taskEnd
|
|
1919
|
+
? `${MAGENTA}[大模型${taskEnd.numTurns}|调用${taskEnd.toolUseCount}|thought${taskEnd.thoughtPutCount}|msg${taskEnd.replyCount}]${RST}`
|
|
1920
|
+
: '';
|
|
1921
|
+
msgPreview = `${BLUE}↑${tags}${counts} ${toShort ? `${ORANGE}${toShort}${RST}${BLUE}: ` : ''}${stats.lastSentText.replace(/\n/g, ' ').slice(0, 60)}${RST}`;
|
|
1855
1922
|
}
|
|
1856
1923
|
else if (stats.lastReceivedText) {
|
|
1857
1924
|
const fromShort = stats.lastReceivedFrom ? stats.lastReceivedFrom.split('.')[0] : '';
|
|
1858
1925
|
msgPreview = `${GREEN}↓ ${fromShort ? `${ORANGE}${fromShort}${RST}${GREEN}: ` : ''}${stats.lastReceivedText.replace(/\n/g, ' ').slice(0, 60)}${RST}`;
|
|
1859
1926
|
}
|
|
1860
1927
|
}
|
|
1928
|
+
// 任务结束状态覆盖:仅当 taskEnd 比最后收发都新时才覆盖
|
|
1929
|
+
const taskEnd = stats?.lastTaskEnd;
|
|
1930
|
+
if (taskEnd && taskEnd.ts >= (stats?.lastSentAt ?? 0) && taskEnd.ts >= (stats?.lastReceivedAt ?? 0)) {
|
|
1931
|
+
const tags = mkTags(taskEnd.encrypt, taskEnd.chatmode);
|
|
1932
|
+
// 计数标记: [大模型N|调用N|thoughtN(streamN)|msgN]
|
|
1933
|
+
const thoughtLabel = taskEnd.thoughtPutCount > 0
|
|
1934
|
+
? `thought${taskEnd.numTurns}(stream${taskEnd.thoughtPutCount})`
|
|
1935
|
+
: `thought${taskEnd.numTurns}`;
|
|
1936
|
+
const counts = `${MAGENTA}[大模型${taskEnd.numTurns}|调用${taskEnd.toolUseCount}|${thoughtLabel}|msg${taskEnd.replyCount}]${RST}`;
|
|
1937
|
+
if (taskEnd.status === 'error') {
|
|
1938
|
+
msgPreview = `${RED}${tags}${counts} 错误: ${taskEnd.errorType ?? '未知错误'}${RST}`;
|
|
1939
|
+
}
|
|
1940
|
+
else if (taskEnd.sentDuringTask) {
|
|
1941
|
+
// 有 message.send:蓝色加粗 + 内容
|
|
1942
|
+
const toShort = stats?.lastSentTo ? stats.lastSentTo.split('.')[0] : '';
|
|
1943
|
+
const textPreview = stats?.lastSentText ? stats.lastSentText.replace(/\n/g, ' ').slice(0, 60) : '';
|
|
1944
|
+
msgPreview = `${BOLD}${BLUE}↑${tags}${counts} ${toShort ? `${ORANGE}${toShort}${RST}${BOLD}${BLUE}: ` : ''}${textPreview}${RST}`;
|
|
1945
|
+
}
|
|
1946
|
+
else if (taskEnd.thoughtDuringTask) {
|
|
1947
|
+
// 只有 thought:普通蓝色 + thought 内容
|
|
1948
|
+
const textPreview = taskEnd.lastThoughtText
|
|
1949
|
+
? taskEnd.lastThoughtText.replace(/\n/g, ' ').slice(0, 60)
|
|
1950
|
+
: (taskEnd.finalText ? taskEnd.finalText.replace(/\n/g, ' ').slice(0, 60) : '');
|
|
1951
|
+
msgPreview = `${BLUE}↑${tags}${counts} ${textPreview}${RST}`;
|
|
1952
|
+
}
|
|
1953
|
+
else {
|
|
1954
|
+
// 既没 send 也没 thought
|
|
1955
|
+
const textPreview = taskEnd.finalText
|
|
1956
|
+
? taskEnd.finalText.replace(/\n/g, ' ').slice(0, 60)
|
|
1957
|
+
: '(无输出)';
|
|
1958
|
+
msgPreview = `${ORANGE}${tags}${counts} ${textPreview}${RST}`;
|
|
1959
|
+
}
|
|
1960
|
+
}
|
|
1861
1961
|
const subLine1 = ` ${nameColor}${namePart}${nameReset}${msgPreview ? ' ' + msgPreview : ''}`;
|
|
1862
1962
|
const dirLabel = projectPath || '—';
|
|
1863
1963
|
const subLine2 = `${DIM} ${dirLabel}${RST}`;
|
|
@@ -2371,12 +2471,14 @@ function archiveSelfHealLog(p, log) {
|
|
|
2371
2471
|
* Searches across all channel types (feishu, wechat, aun) for a matching instance.
|
|
2372
2472
|
*/
|
|
2373
2473
|
function resolveInstanceConfig(instanceName) {
|
|
2374
|
-
// 新结构:channel key 是 <
|
|
2474
|
+
// 新结构:channel key 是 <type>#<selfPeerId>#<name>,解析后从对应 agent 的 channels[] 找
|
|
2375
2475
|
const parts = instanceName.split('#');
|
|
2376
2476
|
if (parts.length === 3) {
|
|
2377
|
-
const [
|
|
2477
|
+
const [type, encodedSelfPeerId, name] = parts;
|
|
2478
|
+
const selfPeerId = decodeURIComponent(encodedSelfPeerId);
|
|
2378
2479
|
const { agents } = loadAllAgents();
|
|
2379
|
-
|
|
2480
|
+
// AUN channel 的 selfPeerId 就是 agent.aid
|
|
2481
|
+
const agent = agents.find(a => a.aid === selfPeerId);
|
|
2380
2482
|
if (!agent)
|
|
2381
2483
|
return null;
|
|
2382
2484
|
const inst = agent.channels.find((c) => c.type === type && c.name === name);
|
|
@@ -2698,7 +2800,7 @@ async function cmdCtl(args) {
|
|
|
2698
2800
|
async function cmdAgent(args) {
|
|
2699
2801
|
const sub = args[0];
|
|
2700
2802
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
2701
|
-
if (sub === 'help' || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
2803
|
+
if (!sub || sub === 'help' || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
2702
2804
|
console.log(`用法: evolclaw agent <command>
|
|
2703
2805
|
|
|
2704
2806
|
Commands:
|
|
@@ -2750,17 +2852,43 @@ Options:
|
|
|
2750
2852
|
console.log('No agents configured.');
|
|
2751
2853
|
return;
|
|
2752
2854
|
}
|
|
2753
|
-
|
|
2754
|
-
|
|
2855
|
+
// 表头跟随系统语言
|
|
2856
|
+
const isChinese = (process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || Intl.DateTimeFormat().resolvedOptions().locale || '').toLowerCase().includes('zh');
|
|
2857
|
+
const headers = isChinese
|
|
2858
|
+
? { name: '名称', status: '状态', channels: '渠道', project: '项目', baseagent: '基座', lastActive: '最后活跃' }
|
|
2859
|
+
: { name: 'NAME', status: 'STATUS', channels: 'CHANNELS', project: 'PROJECT', baseagent: 'BASEAGENT', lastActive: 'LAST ACTIVE' };
|
|
2860
|
+
// 计算各列实际需要的宽度
|
|
2861
|
+
let maxNameLen = headers.name.length;
|
|
2862
|
+
let maxStatusLen = headers.status.length;
|
|
2863
|
+
let maxChannelsLen = headers.channels.length;
|
|
2864
|
+
let maxProjectLen = headers.project.length;
|
|
2865
|
+
let maxBaseagentLen = headers.baseagent.length;
|
|
2866
|
+
for (const info of result.agents) {
|
|
2867
|
+
maxNameLen = Math.max(maxNameLen, info.name.length);
|
|
2868
|
+
maxStatusLen = Math.max(maxStatusLen, (info.status || 'stopped').length);
|
|
2869
|
+
const channelsStr = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
2870
|
+
maxChannelsLen = Math.max(maxChannelsLen, channelsStr.length);
|
|
2871
|
+
const projectStr = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
2872
|
+
maxProjectLen = Math.max(maxProjectLen, projectStr.length);
|
|
2873
|
+
maxBaseagentLen = Math.max(maxBaseagentLen, (info.baseagent || '—').length);
|
|
2874
|
+
}
|
|
2875
|
+
// 加 2 作为列间距
|
|
2876
|
+
const colName = maxNameLen + 2;
|
|
2877
|
+
const colStatus = maxStatusLen + 2;
|
|
2878
|
+
const colChannels = maxChannelsLen + 1;
|
|
2879
|
+
const colProject = maxProjectLen + 2;
|
|
2880
|
+
const colBaseagent = maxBaseagentLen + 2;
|
|
2881
|
+
console.log(headers.name.padEnd(colName) + headers.status.padEnd(colStatus) + headers.channels.padEnd(colChannels) +
|
|
2882
|
+
headers.project.padEnd(colProject) + headers.baseagent.padEnd(colBaseagent) + headers.lastActive);
|
|
2755
2883
|
for (const info of result.agents) {
|
|
2756
2884
|
const name = info.name;
|
|
2757
2885
|
const status = info.status || 'stopped';
|
|
2758
|
-
const channels = info.channels?.length > 0 ? info.channels.join(', ')
|
|
2886
|
+
const channels = info.channels?.length > 0 ? info.channels.join(', ') : '—';
|
|
2759
2887
|
const project = info.projectPath ? path.basename(info.projectPath) : '—';
|
|
2760
2888
|
const baseagent = info.baseagent || '—';
|
|
2761
2889
|
const lastActive = info.lastActivity ? formatTimeAgo(Date.now() - info.lastActivity) : '—';
|
|
2762
|
-
console.log(name.padEnd(
|
|
2763
|
-
project.padEnd(
|
|
2890
|
+
console.log(name.padEnd(colName) + status.padEnd(colStatus) + channels.padEnd(colChannels) +
|
|
2891
|
+
project.padEnd(colProject) + baseagent.padEnd(colBaseagent) + lastActive);
|
|
2764
2892
|
}
|
|
2765
2893
|
return;
|
|
2766
2894
|
}
|
|
@@ -2803,7 +2931,11 @@ Options:
|
|
|
2803
2931
|
console.log(result.agentmdUploaded
|
|
2804
2932
|
? ' ✓ agent.md 已发布'
|
|
2805
2933
|
: ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
|
|
2806
|
-
console.log(
|
|
2934
|
+
console.log(result.hotLoaded
|
|
2935
|
+
? ' ✓ 已热重载,agent 已上线'
|
|
2936
|
+
: result.hotLoadError
|
|
2937
|
+
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
2938
|
+
: ' ⚠ 服务未运行,下次 evolclaw start 时生效');
|
|
2807
2939
|
}
|
|
2808
2940
|
}
|
|
2809
2941
|
else {
|
|
@@ -2825,7 +2957,11 @@ Options:
|
|
|
2825
2957
|
console.log(result.agentmdUploaded
|
|
2826
2958
|
? ' ✓ agent.md 已发布'
|
|
2827
2959
|
: ' ⚠ agent.md 上传失败(可用 evolclaw aid agentmd put 重试)');
|
|
2828
|
-
console.log(
|
|
2960
|
+
console.log(result.hotLoaded
|
|
2961
|
+
? ' ✓ 已热重载,agent 已上线'
|
|
2962
|
+
: result.hotLoadError
|
|
2963
|
+
? ` ✗ 热重载失败:${result.hotLoadError}`
|
|
2964
|
+
: ' ⚠ 服务未运行,下次 evolclaw start 时生效');
|
|
2829
2965
|
}
|
|
2830
2966
|
}
|
|
2831
2967
|
return;
|
|
@@ -3161,7 +3297,7 @@ async function cmdAid(args) {
|
|
|
3161
3297
|
const sub = args[0] || 'list';
|
|
3162
3298
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
3163
3299
|
const aunPath = resolveAunPath(args);
|
|
3164
|
-
if (sub === 'help' || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
3300
|
+
if (!sub || sub === 'help' || sub === '--help' || sub === '-h' || args.includes('--help') || args.includes('-h')) {
|
|
3165
3301
|
console.log(`用法: evolclaw aid <command>
|
|
3166
3302
|
|
|
3167
3303
|
Commands:
|
|
@@ -3320,8 +3456,7 @@ Options:
|
|
|
3320
3456
|
console.error(`❌ 无效 AID 格式: ${aid}`);
|
|
3321
3457
|
process.exit(1);
|
|
3322
3458
|
}
|
|
3323
|
-
const
|
|
3324
|
-
const localPath = path.join(aunBase, 'AIDs', aid, 'agent.md');
|
|
3459
|
+
const localPath = agentMdPath(aid);
|
|
3325
3460
|
if (!fs.existsSync(localPath)) {
|
|
3326
3461
|
console.error(`❌ 本地无 agent.md: ${aid}`);
|
|
3327
3462
|
process.exit(1);
|
|
@@ -3629,12 +3764,15 @@ Options:
|
|
|
3629
3764
|
--app <name> 指定应用 slot(隔离 ack 游标)
|
|
3630
3765
|
--as-daemon ack 时显式以 daemon 身份(高危,会污染 daemon 游标)
|
|
3631
3766
|
--format json 输出 JSON 格式
|
|
3767
|
+
--encrypt 启用端到端加密
|
|
3768
|
+
--thread <id> 指定话题 ID(用于多话题路由)
|
|
3632
3769
|
--content-type <mime> 显式覆盖 MIME(仅 --file 模式)
|
|
3633
3770
|
--text <说明> 附件说明文字(仅 --file 模式)
|
|
3634
3771
|
--transcript <text> 语音转写(仅 --as voice)
|
|
3635
3772
|
|
|
3636
3773
|
示例:
|
|
3637
3774
|
evolclaw msg send alice.agentid.pub bob.agentid.pub "hello"
|
|
3775
|
+
evolclaw msg send alice.agentid.pub bob.agentid.pub "讨论项目A" --thread "project-A"
|
|
3638
3776
|
evolclaw msg send alice.agentid.pub bob.agentid.pub --file ./pic.png
|
|
3639
3777
|
evolclaw msg send alice.agentid.pub bob.agentid.pub --file ./demo.mp4 --as video
|
|
3640
3778
|
evolclaw msg send alice.agentid.pub bob.agentid.pub --link https://example.com --title "AUN"
|
|
@@ -3708,7 +3846,29 @@ Options:
|
|
|
3708
3846
|
body = { mode: 'text', text };
|
|
3709
3847
|
}
|
|
3710
3848
|
const encrypt = args.includes('--encrypt');
|
|
3711
|
-
const
|
|
3849
|
+
const thread = getArgValue(args, '--thread');
|
|
3850
|
+
// 文件上传进度展示(非 JSON 输出时)。仅在大文件降级到 HTTP PUT 阶段会逐块更新。
|
|
3851
|
+
let lastPctShown = -1;
|
|
3852
|
+
const onUploadProgress = formatJson ? undefined : (info) => {
|
|
3853
|
+
if (info.phase === 'inline')
|
|
3854
|
+
return; // 内联阶段不分块,跳过
|
|
3855
|
+
if (info.phase === 'http-put') {
|
|
3856
|
+
const pct = info.total > 0 ? Math.floor((info.bytes / info.total) * 100) : 0;
|
|
3857
|
+
if (pct === lastPctShown && info.bytes < info.total)
|
|
3858
|
+
return;
|
|
3859
|
+
lastPctShown = pct;
|
|
3860
|
+
const mb = (n) => (n / 1024 / 1024).toFixed(2);
|
|
3861
|
+
const eol = info.bytes >= info.total ? '\n' : '\r';
|
|
3862
|
+
process.stderr.write(` ⏫ uploading: ${pct}% (${mb(info.bytes)}/${mb(info.total)} MB)${eol}`);
|
|
3863
|
+
}
|
|
3864
|
+
else if (info.phase === 'session-create') {
|
|
3865
|
+
process.stderr.write(' ⏫ requesting upload session...\n');
|
|
3866
|
+
}
|
|
3867
|
+
else if (info.phase === 'session-complete') {
|
|
3868
|
+
process.stderr.write(' ⏫ finalizing upload...\n');
|
|
3869
|
+
}
|
|
3870
|
+
};
|
|
3871
|
+
const result = await msgSend({ from, to, body, encrypt, thread, onUploadProgress, ...commonOpts });
|
|
3712
3872
|
if (!result.ok) {
|
|
3713
3873
|
if (formatJson) {
|
|
3714
3874
|
console.log(JSON.stringify(result));
|
|
@@ -4290,15 +4450,23 @@ export async function main(args) {
|
|
|
4290
4450
|
evolclaw init wecom 企业微信 AI Bot 配置(手动输入)`);
|
|
4291
4451
|
}
|
|
4292
4452
|
else if (args[1] === 'wechat') {
|
|
4453
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4454
|
+
suppressSdkLogs();
|
|
4293
4455
|
await cmdInitWechat();
|
|
4294
4456
|
}
|
|
4295
4457
|
else if (args[1] === 'feishu') {
|
|
4458
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4459
|
+
suppressSdkLogs();
|
|
4296
4460
|
await cmdInitFeishu();
|
|
4297
4461
|
}
|
|
4298
4462
|
else if (args[1] === 'dingtalk') {
|
|
4463
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4464
|
+
suppressSdkLogs();
|
|
4299
4465
|
await cmdInitDingtalk();
|
|
4300
4466
|
}
|
|
4301
4467
|
else if (args[1] === 'qqbot') {
|
|
4468
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4469
|
+
suppressSdkLogs();
|
|
4302
4470
|
await cmdInitQQBot();
|
|
4303
4471
|
}
|
|
4304
4472
|
else if (args[1] === 'wecom') {
|
|
@@ -4311,6 +4479,8 @@ export async function main(args) {
|
|
|
4311
4479
|
process.exit(1);
|
|
4312
4480
|
}
|
|
4313
4481
|
else {
|
|
4482
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4483
|
+
suppressSdkLogs();
|
|
4314
4484
|
const nonInteractive = args.includes('--non-interactive');
|
|
4315
4485
|
await cmdInit({
|
|
4316
4486
|
nonInteractive,
|
|
@@ -4391,9 +4561,12 @@ export async function main(args) {
|
|
|
4391
4561
|
case 'ctl':
|
|
4392
4562
|
await cmdCtl(args.slice(1));
|
|
4393
4563
|
break;
|
|
4394
|
-
case 'agent':
|
|
4564
|
+
case 'agent': {
|
|
4565
|
+
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4566
|
+
suppressSdkLogs();
|
|
4395
4567
|
await cmdAgent(args.slice(1));
|
|
4396
4568
|
break;
|
|
4569
|
+
}
|
|
4397
4570
|
case 'aid': {
|
|
4398
4571
|
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4399
4572
|
suppressSdkLogs();
|
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) {
|
package/dist/cli/init.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
2
|
import readline from 'readline';
|
|
4
3
|
import { resolvePaths, ensureDataDirs } from '../paths.js';
|
|
5
4
|
import { commandExists } from '../utils/cross-platform.js';
|
|
6
5
|
import { scanInstances } from '../utils/instance-registry.js';
|
|
6
|
+
import { saveDefaultsSafe, loadAllAgents } from '../config-store.js';
|
|
7
7
|
// ==================== Helpers ====================
|
|
8
8
|
function ask(rl, question) {
|
|
9
9
|
return new Promise(resolve => rl.question(question, resolve));
|
|
@@ -28,9 +28,8 @@ function buildDefaults(chosen) {
|
|
|
28
28
|
baseagents: { [chosen]: env ? { apiKey: `$ENV:${env}` } : {} },
|
|
29
29
|
};
|
|
30
30
|
}
|
|
31
|
-
function writeDefaults(
|
|
32
|
-
|
|
33
|
-
fs.writeFileSync(defaultsPath, JSON.stringify(buildDefaults(chosen), null, 2) + '\n');
|
|
31
|
+
function writeDefaults(_defaultsPath, chosen) {
|
|
32
|
+
saveDefaultsSafe(buildDefaults(chosen));
|
|
34
33
|
}
|
|
35
34
|
// ==================== Main ====================
|
|
36
35
|
export async function cmdInit(options) {
|
|
@@ -82,19 +81,21 @@ export async function cmdInit(options) {
|
|
|
82
81
|
writeDefaults(defaultsPath, chosen);
|
|
83
82
|
console.log(`✓ 已${exists ? '覆盖' : '创建'}: ${defaultsPath}`);
|
|
84
83
|
console.log(` active_baseagent: ${chosen}`);
|
|
84
|
+
const { agents } = loadAllAgents();
|
|
85
|
+
if (agents.length === 0) {
|
|
86
|
+
console.log('\n提示:尚无 agent,运行以下命令创建:');
|
|
87
|
+
console.log(' evolclaw agent new <aid>.agentid.pub');
|
|
88
|
+
}
|
|
85
89
|
return;
|
|
86
90
|
}
|
|
87
91
|
// ── 4. 交互式分支 ──
|
|
88
92
|
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
89
|
-
|
|
90
|
-
if (exists) {
|
|
91
|
-
const ans = (await ask(rl, `配置文件已存在: ${defaultsPath}\n 是否覆盖?[y/N] `)).trim().toLowerCase();
|
|
92
|
-
if (ans !== 'y' && ans !== 'yes') {
|
|
93
|
-
console.log(' 已取消');
|
|
94
|
-
return;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
93
|
+
async function askBaseagent() {
|
|
97
94
|
const defaultBa = pickDefault(available);
|
|
95
|
+
if (available.length === 1) {
|
|
96
|
+
console.log(` baseagent: ${defaultBa}`);
|
|
97
|
+
return defaultBa;
|
|
98
|
+
}
|
|
98
99
|
let chosen = null;
|
|
99
100
|
while (chosen === null) {
|
|
100
101
|
const input = (await ask(rl, `默认 baseagent (${available.join('/')}) [${defaultBa}]: `)).trim() || defaultBa;
|
|
@@ -108,17 +109,37 @@ export async function cmdInit(options) {
|
|
|
108
109
|
}
|
|
109
110
|
chosen = input;
|
|
110
111
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
112
|
+
return chosen;
|
|
113
|
+
}
|
|
114
|
+
try {
|
|
115
|
+
if (exists) {
|
|
116
|
+
const ans = (await ask(rl, `配置文件已存在: ${defaultsPath}\n 是否覆盖?[y/N] `)).trim().toLowerCase();
|
|
117
|
+
if (ans === 'y' || ans === 'yes') {
|
|
118
|
+
const chosen = await askBaseagent();
|
|
119
|
+
writeDefaults(defaultsPath, chosen);
|
|
120
|
+
console.log(`\n✓ 已覆盖: ${defaultsPath}`);
|
|
121
|
+
console.log(` active_baseagent: ${chosen}\n`);
|
|
122
|
+
}
|
|
123
|
+
else {
|
|
124
|
+
console.log(' 已跳过(保留现有配置)\n');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const chosen = await askBaseagent();
|
|
129
|
+
writeDefaults(defaultsPath, chosen);
|
|
130
|
+
console.log(`\n✓ 已创建: ${defaultsPath}`);
|
|
131
|
+
console.log(` active_baseagent: ${chosen}\n`);
|
|
132
|
+
}
|
|
133
|
+
// ── 5. 无 agent 时自动进入 agent new ──
|
|
134
|
+
const { agents } = loadAllAgents();
|
|
135
|
+
if (agents.length === 0) {
|
|
136
|
+
console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━');
|
|
137
|
+
console.log('下一步:创建 agent\n');
|
|
138
|
+
const { agentCreateInteractive } = await import('./agent.js');
|
|
139
|
+
const result = await agentCreateInteractive({ rl });
|
|
140
|
+
if (!result.ok) {
|
|
141
|
+
console.error(`❌ ${result.error}`);
|
|
142
|
+
}
|
|
122
143
|
}
|
|
123
144
|
}
|
|
124
145
|
finally {
|