evolclaw 3.1.3 → 3.1.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +27 -0
- package/assets/.env.template +4 -0
- package/assets/config.json.template +6 -0
- package/assets/wechat-group-qr.jpeg +0 -0
- package/dist/agents/claude-runner.js +348 -156
- package/dist/agents/kit-renderer.js +211 -42
- package/dist/aun/aid/agentmd.js +75 -139
- package/dist/aun/aid/client.js +1 -14
- package/dist/aun/aid/identity.js +381 -54
- package/dist/aun/aid/index.js +3 -2
- package/dist/aun/aid/store.js +74 -0
- package/dist/aun/msg/p2p.js +26 -2
- package/dist/aun/rpc/connection.js +23 -35
- package/dist/channels/aun.js +92 -144
- package/dist/channels/dingtalk.js +1 -0
- package/dist/channels/feishu.js +270 -190
- 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 +26 -27
- package/dist/cli/bench.js +45 -34
- package/dist/cli/help.js +23 -0
- package/dist/cli/index.js +538 -77
- package/dist/cli/init-channel.js +7 -4
- package/dist/cli/link-rules.js +2 -1
- package/dist/cli/model.js +324 -0
- package/dist/cli/net-check.js +138 -56
- package/dist/cli/watch-msg.js +7 -7
- package/dist/cli/watch-web/debug-log.js +18 -0
- package/dist/cli/watch-web/server.js +306 -0
- package/dist/cli/watch-web/sources/aid.js +63 -0
- package/dist/cli/watch-web/sources/msg.js +70 -0
- package/dist/cli/watch-web/sources/session.js +638 -0
- package/dist/cli/watch-web/sources/types.js +10 -0
- package/dist/cli/watch-web/static/app.js +546 -0
- package/dist/cli/watch-web/static/index.html +54 -0
- package/dist/cli/watch-web/static/style.css +247 -0
- package/dist/core/channel-loader.js +7 -4
- package/dist/core/command-handler.js +87 -93
- package/dist/core/evolagent-registry.js +1 -1
- package/dist/core/evolagent.js +4 -4
- package/dist/core/interaction-router.js +59 -0
- package/dist/core/message/message-bridge.js +6 -6
- package/dist/core/message/message-log.js +2 -2
- package/dist/core/message/message-processor.js +104 -118
- package/dist/core/message/stream-idle-monitor.js +21 -0
- package/dist/core/model/model-catalog.js +215 -0
- package/dist/core/model/model-scope.js +250 -0
- package/dist/core/relation/peer-identity.js +78 -44
- package/dist/core/relation/peer-key.js +16 -0
- package/dist/core/session/session-fs-store.js +34 -55
- package/dist/core/session/session-key.js +24 -0
- package/dist/core/session/session-manager.js +312 -251
- package/dist/core/session/session-mapper.js +9 -4
- package/dist/core/trigger/manager.js +37 -0
- package/dist/core/trigger/scheduler.js +2 -1
- package/dist/index.js +10 -3
- package/dist/ipc.js +22 -0
- package/dist/paths.js +87 -16
- package/dist/utils/npm-ops.js +18 -11
- package/kits/docs/GUIDE.md +2 -2
- package/kits/docs/INDEX.md +11 -7
- package/kits/docs/channels/aun.md +56 -17
- package/kits/docs/channels/feishu.md +41 -12
- package/kits/docs/context-assembly.md +181 -0
- package/kits/docs/evolclaw/agent.md +49 -0
- package/kits/docs/evolclaw/aid.md +49 -0
- package/kits/docs/evolclaw/ctl.md +46 -0
- package/kits/docs/evolclaw/group.md +82 -0
- package/kits/docs/evolclaw/msg.md +86 -0
- package/kits/docs/evolclaw/rpc.md +35 -0
- package/kits/docs/evolclaw/storage.md +49 -0
- package/kits/docs/venues/aun-group.md +10 -0
- package/kits/docs/venues/aun-private.md +10 -0
- package/kits/docs/venues/client-desktop.md +10 -0
- package/kits/docs/venues/client-mobile.md +10 -0
- package/kits/docs/venues/feishu-group.md +13 -0
- package/kits/docs/venues/feishu-private.md +9 -0
- package/kits/docs/venues/group.md +11 -0
- package/kits/docs/venues/private.md +10 -0
- package/kits/eck_manifest.json +75 -39
- package/kits/rules/01-overview.md +20 -10
- package/kits/rules/05-venue.md +2 -2
- package/kits/rules/06-channel.md +30 -27
- package/kits/templates/system-fragments/baseagent.md +7 -1
- package/kits/templates/system-fragments/channel.md +4 -1
- package/kits/templates/system-fragments/identity.md +4 -4
- package/kits/templates/system-fragments/relation.md +8 -5
- package/kits/templates/system-fragments/session.md +27 -0
- package/kits/templates/system-fragments/venue.md +13 -1
- package/package.json +13 -6
- package/dist/aun/aid/lifecycle-log.js +0 -33
- package/dist/net-check.js +0 -640
- package/dist/utils/aid-lifecycle-log.js +0 -33
- package/dist/watch-msg.js +0 -544
- package/kits/docs/evolclaw/AGENT_CMD.md +0 -31
- package/kits/docs/evolclaw/MSG_GROUP.md +0 -30
- package/kits/docs/evolclaw/MSG_PRIVATE.md +0 -72
- package/kits/docs/evolclaw/tools.md +0 -25
- package/kits/templates/system-fragments/eckruntime.md +0 -14
package/dist/cli/index.js
CHANGED
|
@@ -11,6 +11,7 @@ import { migrateProject } from '../config-store.js';
|
|
|
11
11
|
import { cmdInit } from './init.js';
|
|
12
12
|
import { ipcQuery } from '../ipc.js';
|
|
13
13
|
import { cmdInitWechat, cmdInitFeishu, cmdInitDingtalk, cmdInitQQBot, cmdInitWecom } from './init-channel.js';
|
|
14
|
+
import { isHelpFlag, wantsHelp } from './help.js';
|
|
14
15
|
import * as platform from '../utils/cross-platform.js';
|
|
15
16
|
import { EventBus } from '../core/event-bus.js';
|
|
16
17
|
import { tryUpgrade, tryUpgradeAunSdk } from '../utils/npm-ops.js';
|
|
@@ -883,8 +884,8 @@ async function cmdStatus() {
|
|
|
883
884
|
const configChannelNames = new Set();
|
|
884
885
|
for (const cfg of agents) {
|
|
885
886
|
for (const inst of cfg.channels) {
|
|
886
|
-
// effective key: <type>#<
|
|
887
|
-
configChannelNames.add(`${inst.type}#${
|
|
887
|
+
// effective key: <type>#<selfAID>#<name>
|
|
888
|
+
configChannelNames.add(`${inst.type}#${cfg.aid}#${inst.name}`);
|
|
888
889
|
}
|
|
889
890
|
}
|
|
890
891
|
for (const s of allSessions) {
|
|
@@ -1013,7 +1014,7 @@ async function cmdStatus() {
|
|
|
1013
1014
|
}
|
|
1014
1015
|
}
|
|
1015
1016
|
/**
|
|
1016
|
-
* 把 channel fingerprint 列表(`<type>#<
|
|
1017
|
+
* 把 channel fingerprint 列表(`<type>#<selfAID>#<name>`)折叠成展示用摘要。
|
|
1017
1018
|
*
|
|
1018
1019
|
* 聚合规则:
|
|
1019
1020
|
* - 按 type 分组
|
|
@@ -1413,6 +1414,7 @@ async function cmdWatchMenu() {
|
|
|
1413
1414
|
{ key: 'log', label: 'log', desc: 'real-time log tail' },
|
|
1414
1415
|
{ key: 'aid', label: 'aid', desc: 'AID connection stats' },
|
|
1415
1416
|
{ key: 'msg', label: 'msg', desc: 'message inspector' },
|
|
1417
|
+
{ key: 'web', label: 'web', desc: 'browser dashboard (aid/msg/session)' },
|
|
1416
1418
|
];
|
|
1417
1419
|
let index = 0;
|
|
1418
1420
|
const useColor = !!process.stdout.isTTY;
|
|
@@ -1476,6 +1478,9 @@ async function cmdWatchMenu() {
|
|
|
1476
1478
|
const { cmdWatchMsg } = await import('./watch-msg.js');
|
|
1477
1479
|
await cmdWatchMsg();
|
|
1478
1480
|
}
|
|
1481
|
+
else if (chosen === 'web') {
|
|
1482
|
+
await cmdWatchWeb();
|
|
1483
|
+
}
|
|
1479
1484
|
resolve();
|
|
1480
1485
|
}
|
|
1481
1486
|
};
|
|
@@ -2097,6 +2102,97 @@ async function cmdWatchAid() {
|
|
|
2097
2102
|
}
|
|
2098
2103
|
platform.onShutdown(cleanup);
|
|
2099
2104
|
}
|
|
2105
|
+
async function cmdWatchWeb() {
|
|
2106
|
+
const p = resolvePaths();
|
|
2107
|
+
fs.mkdirSync(p.instanceDir, { recursive: true });
|
|
2108
|
+
const useColor = !!process.stdout.isTTY;
|
|
2109
|
+
const RST = useColor ? '\x1b[0m' : '';
|
|
2110
|
+
const DIM = useColor ? '\x1b[2m' : '';
|
|
2111
|
+
const BOLD = useColor ? '\x1b[1m' : '';
|
|
2112
|
+
const CYAN = useColor ? '\x1b[36m' : '';
|
|
2113
|
+
const GREEN = useColor ? '\x1b[32m' : '';
|
|
2114
|
+
const YELLOW = useColor ? '\x1b[33m' : '';
|
|
2115
|
+
const logLine = (line) => {
|
|
2116
|
+
const t = new Date();
|
|
2117
|
+
const ts = `${String(t.getHours()).padStart(2, '0')}:${String(t.getMinutes()).padStart(2, '0')}:${String(t.getSeconds()).padStart(2, '0')}`;
|
|
2118
|
+
process.stdout.write(`${DIM}${ts}${RST} ${line}\n`);
|
|
2119
|
+
};
|
|
2120
|
+
// 调试日志文件:每次运行 watch web 时清空,便于建立调试闭环
|
|
2121
|
+
// 查看 sessions 调试日志 → 读这个文件
|
|
2122
|
+
const logFile = path.join(p.logs, 'watch-web.log');
|
|
2123
|
+
try {
|
|
2124
|
+
fs.mkdirSync(p.logs, { recursive: true });
|
|
2125
|
+
fs.writeFileSync(logFile, `# watch-web debug log\n# started ${new Date().toISOString()} pid=${process.pid}\n`);
|
|
2126
|
+
}
|
|
2127
|
+
catch { /* best effort */ }
|
|
2128
|
+
const fileLog = (line) => {
|
|
2129
|
+
const t = new Date();
|
|
2130
|
+
const ts = `${String(t.getHours()).padStart(2, '0')}:${String(t.getMinutes()).padStart(2, '0')}:${String(t.getSeconds()).padStart(2, '0')}.${String(t.getMilliseconds()).padStart(3, '0')}`;
|
|
2131
|
+
try {
|
|
2132
|
+
fs.appendFileSync(logFile, `${ts} ${line.replace(/\x1b\[[0-9;]*m/g, '')}\n`);
|
|
2133
|
+
}
|
|
2134
|
+
catch { /* ignore */ }
|
|
2135
|
+
};
|
|
2136
|
+
// 同时输出到终端和日志文件
|
|
2137
|
+
const log = (line) => { logLine(line); fileLog(line); };
|
|
2138
|
+
const { startWatchWebServer } = await import('./watch-web/server.js');
|
|
2139
|
+
let handle;
|
|
2140
|
+
try {
|
|
2141
|
+
handle = await startWatchWebServer({ log });
|
|
2142
|
+
}
|
|
2143
|
+
catch (e) {
|
|
2144
|
+
console.error(`❌ 启动 Web 服务失败: ${e?.message || e}`);
|
|
2145
|
+
process.exit(1);
|
|
2146
|
+
}
|
|
2147
|
+
// 注册 instance 文件
|
|
2148
|
+
const instanceFile = path.join(p.instanceDir, `watch-web-${process.pid}.json`);
|
|
2149
|
+
fs.writeFileSync(instanceFile, JSON.stringify({
|
|
2150
|
+
pid: process.pid, startedAt: Date.now(), startedAtIso: new Date().toISOString(),
|
|
2151
|
+
type: 'watch-web', port: handle.port,
|
|
2152
|
+
}, null, 2));
|
|
2153
|
+
// 列出本机访问地址
|
|
2154
|
+
const os = await import('os');
|
|
2155
|
+
const ifaces = os.networkInterfaces();
|
|
2156
|
+
const lanIps = [];
|
|
2157
|
+
for (const list of Object.values(ifaces)) {
|
|
2158
|
+
for (const ni of list || []) {
|
|
2159
|
+
if (ni.family === 'IPv4' && !ni.internal)
|
|
2160
|
+
lanIps.push(ni.address);
|
|
2161
|
+
}
|
|
2162
|
+
}
|
|
2163
|
+
process.stdout.write(`\n${BOLD}${CYAN}🔭 EvolClaw Watch Web${RST}\n\n`);
|
|
2164
|
+
process.stdout.write(` ${BOLD}配对码:${RST} ${GREEN}${BOLD}${handle.pairingCode}${RST} ${DIM}(5 分钟内有效,配对后 token 缓存 24h 自动续期)${RST}\n\n`);
|
|
2165
|
+
process.stdout.write(` ${BOLD}本机:${RST} http://localhost:${handle.port}\n`);
|
|
2166
|
+
for (const ip of lanIps) {
|
|
2167
|
+
process.stdout.write(` ${BOLD}局域网:${RST} http://${ip}:${handle.port}\n`);
|
|
2168
|
+
}
|
|
2169
|
+
process.stdout.write(`\n ${DIM}绑定 0.0.0.0,远程可访问。按任意键退出。${RST}\n`);
|
|
2170
|
+
process.stdout.write(` ${DIM}调试日志: ${logFile}${RST}\n\n`);
|
|
2171
|
+
const cleanup = () => {
|
|
2172
|
+
try {
|
|
2173
|
+
fs.unlinkSync(instanceFile);
|
|
2174
|
+
}
|
|
2175
|
+
catch { }
|
|
2176
|
+
handle.close().finally(() => process.exit(0));
|
|
2177
|
+
};
|
|
2178
|
+
process.on('exit', () => { try {
|
|
2179
|
+
fs.unlinkSync(instanceFile);
|
|
2180
|
+
}
|
|
2181
|
+
catch { } });
|
|
2182
|
+
process.on('SIGINT', cleanup);
|
|
2183
|
+
process.on('SIGTERM', cleanup);
|
|
2184
|
+
platform.onShutdown(cleanup);
|
|
2185
|
+
// 按任意键退出
|
|
2186
|
+
if (process.stdin.isTTY) {
|
|
2187
|
+
process.stdin.setRawMode(true);
|
|
2188
|
+
process.stdin.resume();
|
|
2189
|
+
process.stdin.on('data', (key) => {
|
|
2190
|
+
logLine(`${YELLOW}收到退出指令,关闭服务…${RST}`);
|
|
2191
|
+
cleanup();
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
await new Promise(() => { });
|
|
2195
|
+
}
|
|
2100
2196
|
async function cmdRestartMonitor() {
|
|
2101
2197
|
const p = resolvePaths();
|
|
2102
2198
|
const restartLog = path.join(p.logs, 'restart.log');
|
|
@@ -2471,14 +2567,13 @@ function archiveSelfHealLog(p, log) {
|
|
|
2471
2567
|
* Searches across all channel types (feishu, wechat, aun) for a matching instance.
|
|
2472
2568
|
*/
|
|
2473
2569
|
function resolveInstanceConfig(instanceName) {
|
|
2474
|
-
// 新结构:channel key 是 <type>#<
|
|
2570
|
+
// 新结构:channel key 是 <type>#<selfAID>#<name>,解析后从对应 agent 的 channels[] 找
|
|
2475
2571
|
const parts = instanceName.split('#');
|
|
2476
2572
|
if (parts.length === 3) {
|
|
2477
|
-
const [type,
|
|
2478
|
-
const selfPeerId = decodeURIComponent(encodedSelfPeerId);
|
|
2573
|
+
const [type, selfAID, name] = parts;
|
|
2479
2574
|
const { agents } = loadAllAgents();
|
|
2480
|
-
// AUN channel 的
|
|
2481
|
-
const agent = agents.find(a => a.aid ===
|
|
2575
|
+
// AUN channel 的 selfAID 就是 agent.aid
|
|
2576
|
+
const agent = agents.find(a => a.aid === selfAID);
|
|
2482
2577
|
if (!agent)
|
|
2483
2578
|
return null;
|
|
2484
2579
|
const inst = agent.channels.find((c) => c.type === type && c.name === name);
|
|
@@ -2765,7 +2860,7 @@ Agent:
|
|
|
2765
2860
|
process.exit(1);
|
|
2766
2861
|
}
|
|
2767
2862
|
// help 不需要连接服务,直接复用无参数时的帮助输出
|
|
2768
|
-
if (args[0]
|
|
2863
|
+
if (isHelpFlag(args[0])) {
|
|
2769
2864
|
return cmdCtl([]);
|
|
2770
2865
|
}
|
|
2771
2866
|
const sessionId = process.env.EVOLCLAW_SESSION_ID;
|
|
@@ -2800,7 +2895,7 @@ Agent:
|
|
|
2800
2895
|
async function cmdAgent(args) {
|
|
2801
2896
|
const sub = args[0];
|
|
2802
2897
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
2803
|
-
if (!sub || sub
|
|
2898
|
+
if (!sub || isHelpFlag(sub)) {
|
|
2804
2899
|
console.log(`用法: evolclaw agent <command>
|
|
2805
2900
|
|
|
2806
2901
|
Commands:
|
|
@@ -2818,6 +2913,7 @@ Commands:
|
|
|
2818
2913
|
|
|
2819
2914
|
Options:
|
|
2820
2915
|
--format json 输出 JSON 格式
|
|
2916
|
+
--help, -h 各子命令均支持,查看详细用法
|
|
2821
2917
|
|
|
2822
2918
|
示例:
|
|
2823
2919
|
evolclaw agent list
|
|
@@ -2832,7 +2928,13 @@ Options:
|
|
|
2832
2928
|
}
|
|
2833
2929
|
const { agentList, agentShow, agentCreateInteractive, agentCreateNonInteractive, agentReload, agentEnable, agentDisable, agentGet, agentSet, agentDelete, agentRename, } = await import('./agent.js');
|
|
2834
2930
|
// --- list ---
|
|
2835
|
-
if (
|
|
2931
|
+
if (sub === 'list') {
|
|
2932
|
+
if (wantsHelp(args)) {
|
|
2933
|
+
console.log(`用法: evolclaw agent list [--format json]
|
|
2934
|
+
|
|
2935
|
+
列出所有 agent,显示名称、状态、渠道、项目、基座、最后活跃时间。`);
|
|
2936
|
+
return;
|
|
2937
|
+
}
|
|
2836
2938
|
const result = await agentList();
|
|
2837
2939
|
if (!result.ok) {
|
|
2838
2940
|
if (formatJson) {
|
|
@@ -2893,6 +2995,24 @@ Options:
|
|
|
2893
2995
|
}
|
|
2894
2996
|
// --- new ---
|
|
2895
2997
|
if (sub === 'new') {
|
|
2998
|
+
if (wantsHelp(args)) {
|
|
2999
|
+
console.log(`用法: evolclaw agent new [aid] 交互式创建
|
|
3000
|
+
evolclaw agent new <aid> --non-interactive [选项]
|
|
3001
|
+
|
|
3002
|
+
非交互模式选项:
|
|
3003
|
+
--baseagent <claude|codex|gemini> 默认: PATH 中第一个可用
|
|
3004
|
+
--project <absolute path> 必填
|
|
3005
|
+
--owner <aid>
|
|
3006
|
+
--name <display-name>
|
|
3007
|
+
--description <text>
|
|
3008
|
+
--force 覆盖已有 config.json
|
|
3009
|
+
--format json 输出 JSON
|
|
3010
|
+
|
|
3011
|
+
示例:
|
|
3012
|
+
evolclaw agent new mybot.agentid.pub
|
|
3013
|
+
evolclaw agent new mybot.agentid.pub --non-interactive --project /abs/path --baseagent claude`);
|
|
3014
|
+
return;
|
|
3015
|
+
}
|
|
2896
3016
|
const name = args[1];
|
|
2897
3017
|
const nonInteractive = args.includes('--non-interactive');
|
|
2898
3018
|
if (nonInteractive) {
|
|
@@ -2989,6 +3109,14 @@ Options:
|
|
|
2989
3109
|
// }
|
|
2990
3110
|
// --- reload ---
|
|
2991
3111
|
if (sub === 'reload') {
|
|
3112
|
+
if (wantsHelp(args)) {
|
|
3113
|
+
console.log(`用法: evolclaw agent reload [aid] [--format json]
|
|
3114
|
+
|
|
3115
|
+
热重载 agent 配置。
|
|
3116
|
+
无参数 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
|
|
3117
|
+
<aid> 仅热重载指定 agent`);
|
|
3118
|
+
return;
|
|
3119
|
+
}
|
|
2992
3120
|
const target = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
2993
3121
|
const result = await agentReload(target);
|
|
2994
3122
|
if (!result.ok) {
|
|
@@ -3016,6 +3144,12 @@ Options:
|
|
|
3016
3144
|
}
|
|
3017
3145
|
// --- enable ---
|
|
3018
3146
|
if (sub === 'enable') {
|
|
3147
|
+
if (wantsHelp(args)) {
|
|
3148
|
+
console.log(`用法: evolclaw agent enable <aid> [--format json]
|
|
3149
|
+
|
|
3150
|
+
启用 agent。若服务运行中会热重载,否则下次 evolclaw start 时生效。`);
|
|
3151
|
+
return;
|
|
3152
|
+
}
|
|
3019
3153
|
const aid = args[1];
|
|
3020
3154
|
if (!aid) {
|
|
3021
3155
|
console.error('用法: evolclaw agent enable <aid>');
|
|
@@ -3041,6 +3175,12 @@ Options:
|
|
|
3041
3175
|
}
|
|
3042
3176
|
// --- disable ---
|
|
3043
3177
|
if (sub === 'disable') {
|
|
3178
|
+
if (wantsHelp(args)) {
|
|
3179
|
+
console.log(`用法: evolclaw agent disable <aid> [--format json]
|
|
3180
|
+
|
|
3181
|
+
停用 agent。若服务运行中会热重载离线,否则在配置中标记为禁用。`);
|
|
3182
|
+
return;
|
|
3183
|
+
}
|
|
3044
3184
|
const aid = args[1];
|
|
3045
3185
|
if (!aid) {
|
|
3046
3186
|
console.error('用法: evolclaw agent disable <aid>');
|
|
@@ -3066,6 +3206,16 @@ Options:
|
|
|
3066
3206
|
}
|
|
3067
3207
|
// --- get ---
|
|
3068
3208
|
if (sub === 'get') {
|
|
3209
|
+
if (wantsHelp(args)) {
|
|
3210
|
+
console.log(`用法: evolclaw agent get <aid> <key> [--format json]
|
|
3211
|
+
|
|
3212
|
+
读取单个配置字段。key 支持点路径,如 "channels.aun.enabled"。
|
|
3213
|
+
|
|
3214
|
+
示例:
|
|
3215
|
+
evolclaw agent get mybot.agentid.pub active_baseagent
|
|
3216
|
+
evolclaw agent get mybot.agentid.pub channels.aun.enabled`);
|
|
3217
|
+
return;
|
|
3218
|
+
}
|
|
3069
3219
|
const aid = args[1];
|
|
3070
3220
|
const key = args[2];
|
|
3071
3221
|
if (!aid || !key) {
|
|
@@ -3093,6 +3243,16 @@ Options:
|
|
|
3093
3243
|
}
|
|
3094
3244
|
// --- set ---
|
|
3095
3245
|
if (sub === 'set') {
|
|
3246
|
+
if (wantsHelp(args)) {
|
|
3247
|
+
console.log(`用法: evolclaw agent set <aid> <key> <value> [--format json]
|
|
3248
|
+
|
|
3249
|
+
修改单个配置字段。key 支持点路径。修改后若服务运行中会自动热重载。
|
|
3250
|
+
|
|
3251
|
+
示例:
|
|
3252
|
+
evolclaw agent set mybot.agentid.pub active_baseagent codex
|
|
3253
|
+
evolclaw agent set mybot.agentid.pub channels.aun.enabled true`);
|
|
3254
|
+
return;
|
|
3255
|
+
}
|
|
3096
3256
|
const aid = args[1];
|
|
3097
3257
|
const key = args[2];
|
|
3098
3258
|
const val = args[3];
|
|
@@ -3120,6 +3280,15 @@ Options:
|
|
|
3120
3280
|
}
|
|
3121
3281
|
// --- rename ---
|
|
3122
3282
|
if (sub === 'rename') {
|
|
3283
|
+
if (wantsHelp(args)) {
|
|
3284
|
+
console.log(`用法: evolclaw agent rename <aid> <name> [--format json]
|
|
3285
|
+
|
|
3286
|
+
修改 agent 显示名称。同时更新本地 agent.md 并尝试重新上传。
|
|
3287
|
+
|
|
3288
|
+
示例:
|
|
3289
|
+
evolclaw agent rename mybot.agentid.pub "My Bot"`);
|
|
3290
|
+
return;
|
|
3291
|
+
}
|
|
3123
3292
|
const aid = args[1];
|
|
3124
3293
|
const newName = args[2];
|
|
3125
3294
|
if (!aid || !newName) {
|
|
@@ -3146,6 +3315,14 @@ Options:
|
|
|
3146
3315
|
}
|
|
3147
3316
|
// --- delete ---
|
|
3148
3317
|
if (sub === 'delete') {
|
|
3318
|
+
if (wantsHelp(args)) {
|
|
3319
|
+
console.log(`用法: evolclaw agent delete <aid> [--purge] [--format json]
|
|
3320
|
+
|
|
3321
|
+
删除 agent 的配置。
|
|
3322
|
+
--purge 同时清除该 agent 的会话、消息、日志等运行时数据
|
|
3323
|
+
默认 仅删除 config.json,运行时数据保留`);
|
|
3324
|
+
return;
|
|
3325
|
+
}
|
|
3149
3326
|
const aid = args[1];
|
|
3150
3327
|
if (!aid) {
|
|
3151
3328
|
console.error('用法: evolclaw agent delete <aid> [--purge]');
|
|
@@ -3172,6 +3349,12 @@ Options:
|
|
|
3172
3349
|
}
|
|
3173
3350
|
// --- show ---
|
|
3174
3351
|
if (sub === 'show') {
|
|
3352
|
+
if (wantsHelp(args)) {
|
|
3353
|
+
console.log(`用法: evolclaw agent show <aid> [--format json]
|
|
3354
|
+
|
|
3355
|
+
查看 agent 详情:身份、配置、连接状态、会话路径等。`);
|
|
3356
|
+
return;
|
|
3357
|
+
}
|
|
3175
3358
|
const aid = args[1];
|
|
3176
3359
|
if (!aid) {
|
|
3177
3360
|
console.error('用法: evolclaw agent show <aid>');
|
|
@@ -3285,63 +3468,134 @@ function resolveAunPath(args) {
|
|
|
3285
3468
|
return process.env.AUN_HOME || undefined;
|
|
3286
3469
|
}
|
|
3287
3470
|
async function cmdAid(args) {
|
|
3288
|
-
const sub = args[0]
|
|
3471
|
+
const sub = args[0];
|
|
3289
3472
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
3290
3473
|
const aunPath = resolveAunPath(args);
|
|
3291
|
-
if (!sub || sub
|
|
3474
|
+
if (!sub || isHelpFlag(sub)) {
|
|
3292
3475
|
console.log(`用法: evolclaw aid <command>
|
|
3293
3476
|
|
|
3294
3477
|
Commands:
|
|
3295
|
-
list 列出本地所有 AID
|
|
3296
|
-
show <aid> 查看本地 AID
|
|
3478
|
+
list 列出本地所有 AID(实测 sign+verify)
|
|
3479
|
+
show <aid> 查看本地 AID 详情(证书、私钥、签名能力)
|
|
3297
3480
|
new <aid> 创建新 AID 身份
|
|
3298
|
-
delete <aid>
|
|
3481
|
+
delete <aid> 删除指定本地 AID(无网络注销)
|
|
3482
|
+
delete --orphan 批量清理无私钥的外部 AID 缓存
|
|
3483
|
+
delete --no-cert 批量清理无私钥也无公钥证书的孤儿目录
|
|
3484
|
+
delete --unrecoverable 批量清理云端公钥已变更、本地不可恢复的 AID
|
|
3485
|
+
批量删除默认 dry-run,加 --yes 执行
|
|
3299
3486
|
lookup <aid> 远程探测 AID(是否存在 + 网关 + agent.md)
|
|
3300
3487
|
agentmd put <aid> 读本地 agent.md → 签名 → 上传
|
|
3301
3488
|
agentmd get <aid> 下载 agent.md → 验签 → 本地持久化
|
|
3302
3489
|
|
|
3303
3490
|
Options:
|
|
3304
3491
|
--format json 输出 JSON 格式
|
|
3492
|
+
--help, -h 各子命令均支持,查看详细用法
|
|
3305
3493
|
|
|
3306
3494
|
示例:
|
|
3307
3495
|
evolclaw aid list
|
|
3308
3496
|
evolclaw aid show toleiliang2.agentid.pub
|
|
3309
3497
|
evolclaw aid new reviewer.agentid.pub
|
|
3498
|
+
evolclaw aid delete --help
|
|
3310
3499
|
evolclaw aid delete old.agentid.pub
|
|
3500
|
+
evolclaw aid delete --orphan
|
|
3501
|
+
evolclaw aid delete --unrecoverable --yes
|
|
3311
3502
|
evolclaw aid lookup someone.agentid.pub
|
|
3312
3503
|
evolclaw aid agentmd put mybot.agentid.pub
|
|
3313
3504
|
evolclaw aid agentmd get someone.agentid.pub`);
|
|
3314
3505
|
return;
|
|
3315
3506
|
}
|
|
3316
|
-
const { aidList, aidCreate, aidShow, aidDelete, aidLookup, agentmdPut, agentmdGet, buildInitialAgentMd, isValidAid } = await import('../aun/aid/index.js');
|
|
3507
|
+
const { aidList, aidListVerified, aidCreate, aidShow, aidDelete, aidLookup, agentmdPut, agentmdGet, buildInitialAgentMd, isValidAid } = await import('../aun/aid/index.js');
|
|
3317
3508
|
if (sub === 'list') {
|
|
3318
|
-
|
|
3509
|
+
if (wantsHelp(args)) {
|
|
3510
|
+
console.log(`用法: evolclaw aid list [筛选选项] [--no-verify] [--format json]
|
|
3511
|
+
|
|
3512
|
+
列出本地 AID 并跑 sign+verify 自检。
|
|
3513
|
+
|
|
3514
|
+
筛选选项(可组合,不指定 = 列出 mine + broken + peer-cert):
|
|
3515
|
+
--mine 仅本地可用身份(实测可签名+验签通过)
|
|
3516
|
+
--broken 仅有私钥但不可用(公钥不匹配 / 证书过期 / sign 失败)
|
|
3517
|
+
--peer-cert 仅对端 AID(无私钥,有公钥证书)
|
|
3518
|
+
--no-cert 仅无私钥无证书的目录(默认隐藏,需用 aid delete --no-cert 清理)
|
|
3519
|
+
|
|
3520
|
+
选项:
|
|
3521
|
+
--no-verify 跳过 sign+verify 实测,仅静态扫描(更快,mine/broken 仅按静态判定近似)
|
|
3522
|
+
--format json JSON 格式输出
|
|
3523
|
+
|
|
3524
|
+
输出图标:
|
|
3525
|
+
🔑 有私钥
|
|
3526
|
+
✅ 实测可签名/验签
|
|
3527
|
+
❌ 不可签名(公钥不匹配 / sign 失败 / verify 失败等)
|
|
3528
|
+
⌛ 证书过期
|
|
3529
|
+
📜 有公钥证书
|
|
3530
|
+
📄 有 agent.md
|
|
3531
|
+
|
|
3532
|
+
示例:
|
|
3533
|
+
evolclaw aid list 列出 mine + broken + peer-cert
|
|
3534
|
+
evolclaw aid list --mine 仅可用身份
|
|
3535
|
+
evolclaw aid list --mine --broken 所有有私钥的 AID
|
|
3536
|
+
evolclaw aid list --no-cert 仅无私钥无证书的孤儿目录
|
|
3537
|
+
evolclaw aid list --no-verify 跳过实测,快速静态扫描`);
|
|
3538
|
+
return;
|
|
3539
|
+
}
|
|
3540
|
+
const wantMine = args.includes('--mine');
|
|
3541
|
+
const wantBroken = args.includes('--broken');
|
|
3542
|
+
const wantPeerCert = args.includes('--peer-cert');
|
|
3543
|
+
const wantNoCert = args.includes('--no-cert');
|
|
3544
|
+
const noVerify = args.includes('--no-verify');
|
|
3545
|
+
const anyFilter = wantMine || wantBroken || wantPeerCert || wantNoCert;
|
|
3546
|
+
// 默认: mine + broken + peer-cert(隐藏 no-cert,需显式 --no-cert 才列)
|
|
3547
|
+
const showMine = anyFilter ? wantMine : true;
|
|
3548
|
+
const showBroken = anyFilter ? wantBroken : true;
|
|
3549
|
+
const showPeerCert = anyFilter ? wantPeerCert : true;
|
|
3550
|
+
const showNoCert = anyFilter ? wantNoCert : false;
|
|
3551
|
+
const all = noVerify ? aidList(aunPath) : await aidListVerified(aunPath);
|
|
3552
|
+
const aids = all.filter(a => (showMine && a.category === 'mine') ||
|
|
3553
|
+
(showBroken && a.category === 'broken') ||
|
|
3554
|
+
(showPeerCert && a.category === 'peer-cert') ||
|
|
3555
|
+
(showNoCert && a.category === 'no-cert'));
|
|
3319
3556
|
if (formatJson) {
|
|
3320
3557
|
console.log(JSON.stringify(aids, null, 2));
|
|
3321
3558
|
return;
|
|
3322
3559
|
}
|
|
3323
3560
|
if (aids.length === 0) {
|
|
3324
|
-
console.log('
|
|
3561
|
+
console.log('无匹配 AID');
|
|
3325
3562
|
return;
|
|
3326
3563
|
}
|
|
3327
|
-
console.log('
|
|
3564
|
+
console.log(`本地 AID${noVerify ? '(静态扫描,未实测)' : ''}(${aunPath ?? resolveRoot()}):`);
|
|
3328
3565
|
for (const a of aids) {
|
|
3329
|
-
const
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
3335
|
-
|
|
3566
|
+
const keyIcon = a.hasPrivateKey ? '🔑' : ' ';
|
|
3567
|
+
let signIcon = ' ';
|
|
3568
|
+
// --no-verify 时 signVerified 始终为 null,用 canSign 作为静态近似
|
|
3569
|
+
const effectiveOk = noVerify ? a.canSign : a.signVerified === true;
|
|
3570
|
+
const effectiveFail = noVerify ? (a.hasPrivateKey && !a.canSign) : (a.hasPrivateKey && a.signVerified === false);
|
|
3571
|
+
if (effectiveOk)
|
|
3572
|
+
signIcon = '✅';
|
|
3573
|
+
else if (a.hasPrivateKey && a.certExpired)
|
|
3574
|
+
signIcon = '⌛';
|
|
3575
|
+
else if (effectiveFail)
|
|
3576
|
+
signIcon = '❌';
|
|
3577
|
+
const certIcon = a.hasCert ? '📜' : ' ';
|
|
3578
|
+
const mdIcon = a.hasAgentMd ? '📄' : ' ';
|
|
3579
|
+
const tail = !noVerify && a.signVerified === false && a.signError && !(a.keyMatchesCert === false || a.certExpired || !a.hasPrivateKey || !a.hasCert)
|
|
3580
|
+
? ` (${a.signError})` : '';
|
|
3581
|
+
console.log(` ${keyIcon} ${signIcon} ${certIcon} ${mdIcon} ${a.aid}${tail}`);
|
|
3582
|
+
}
|
|
3583
|
+
console.log('\n🔑=私钥 ✅=可签名/验签 ❌=不可签名 ⌛=证书过期 📜=公钥证书 📄=agent.md');
|
|
3336
3584
|
return;
|
|
3337
3585
|
}
|
|
3338
3586
|
if (sub === 'show') {
|
|
3587
|
+
if (wantsHelp(args)) {
|
|
3588
|
+
console.log(`用法: evolclaw aid show <aid> [--format json]
|
|
3589
|
+
|
|
3590
|
+
查看本地 AID 详情:私钥/证书/agent.md 状态、签名能力实测。`);
|
|
3591
|
+
return;
|
|
3592
|
+
}
|
|
3339
3593
|
const aid = args[1];
|
|
3340
3594
|
if (!aid) {
|
|
3341
3595
|
console.error('用法: evolclaw aid show <aid>');
|
|
3342
3596
|
process.exit(1);
|
|
3343
3597
|
}
|
|
3344
|
-
const info = aidShow(aid, { aunPath });
|
|
3598
|
+
const info = await aidShow(aid, { aunPath });
|
|
3345
3599
|
if (formatJson) {
|
|
3346
3600
|
console.log(JSON.stringify(info, null, 2));
|
|
3347
3601
|
return;
|
|
@@ -3349,58 +3603,246 @@ Options:
|
|
|
3349
3603
|
console.log(`AID: ${info.aid}`);
|
|
3350
3604
|
console.log(` 私钥: ${info.hasPrivateKey ? '有' : '无'}`);
|
|
3351
3605
|
console.log(` agent.md: ${info.hasAgentMd ? '有' : '无'}`);
|
|
3352
|
-
|
|
3606
|
+
if (info.hasAgentMd) {
|
|
3607
|
+
const sigLabel = info.agentMdSignature === 'verified' ? '✓ 已验签'
|
|
3608
|
+
: info.agentMdSignature === 'unsigned' ? '⚠ 未签名'
|
|
3609
|
+
: info.agentMdSignature === 'invalid' ? `✗ 签名无效${info.agentMdSignatureReason ? ': ' + info.agentMdSignatureReason : ''}`
|
|
3610
|
+
: '? 未知';
|
|
3611
|
+
console.log(` 签名状态: ${sigLabel}`);
|
|
3612
|
+
}
|
|
3613
|
+
console.log(` 证书到期: ${info.certExpiresAt ?? '无证书'}${info.certExpired ? ' (已过期!)' : ''}`);
|
|
3353
3614
|
if (info.certSubject)
|
|
3354
3615
|
console.log(` 证书主体: ${info.certSubject}`);
|
|
3616
|
+
if (info.keyMatchesCert === false)
|
|
3617
|
+
console.log(` 密钥/证书: ✗ 公钥不匹配(cert.pem 与 key.json 公钥不一致)`);
|
|
3618
|
+
else if (info.keyMatchesCert === true)
|
|
3619
|
+
console.log(` 密钥/证书: ✓ 公钥一致`);
|
|
3620
|
+
if (info.signVerified === true)
|
|
3621
|
+
console.log(` 可签名/验签: ✓ 实测通过`);
|
|
3622
|
+
else if (info.signVerified === false)
|
|
3623
|
+
console.log(` 可签名/验签: ✗ 失败${info.signError ? `(${info.signError})` : ''}`);
|
|
3624
|
+
else
|
|
3625
|
+
console.log(` 可签名/验签: ? 未知`);
|
|
3355
3626
|
return;
|
|
3356
3627
|
}
|
|
3357
3628
|
if (sub === 'new') {
|
|
3629
|
+
if (wantsHelp(args)) {
|
|
3630
|
+
console.log(`用法: evolclaw aid new <完整AID> [--force]
|
|
3631
|
+
|
|
3632
|
+
创建新 AID 身份:生成 ECDSA 密钥对、向 Issuer 申请证书、构建并上传初始 agent.md。
|
|
3633
|
+
|
|
3634
|
+
选项:
|
|
3635
|
+
--force 强制重新注册,覆盖已存在的身份(即使签名验证失败)
|
|
3636
|
+
|
|
3637
|
+
例: evolclaw aid new reviewer.agentid.pub
|
|
3638
|
+
evolclaw aid new reviewer.agentid.pub --force`);
|
|
3639
|
+
return;
|
|
3640
|
+
}
|
|
3358
3641
|
const aid = args[1];
|
|
3642
|
+
const force = args.includes('--force');
|
|
3359
3643
|
if (!aid) {
|
|
3360
|
-
console.error('用法: evolclaw aid new <完整AID
|
|
3644
|
+
console.error('用法: evolclaw aid new <完整AID> [--force]\n例: evolclaw aid new reviewer.agentid.pub');
|
|
3361
3645
|
process.exit(1);
|
|
3362
3646
|
}
|
|
3363
3647
|
if (!isValidAid(aid)) {
|
|
3364
3648
|
console.error(`❌ 无效 AID 格式: ${aid}`);
|
|
3365
3649
|
process.exit(1);
|
|
3366
3650
|
}
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3651
|
+
try {
|
|
3652
|
+
const result = await aidCreate(aid, { aunPath, force });
|
|
3653
|
+
if (!result.alreadyExisted) {
|
|
3654
|
+
const content = buildInitialAgentMd({ aid });
|
|
3655
|
+
try {
|
|
3656
|
+
await agentmdPut(content, { aid, aunPath });
|
|
3657
|
+
console.log('✓ agent.md 已发布');
|
|
3658
|
+
}
|
|
3659
|
+
catch (e) {
|
|
3660
|
+
console.warn(`⚠ agent.md 发布失败(首次连接将自动重试): ${String(e.message || e).slice(0, 100)}`);
|
|
3661
|
+
}
|
|
3662
|
+
}
|
|
3370
3663
|
try {
|
|
3371
|
-
await
|
|
3372
|
-
console.log('✓ agent.md 已发布');
|
|
3664
|
+
await result.client.close();
|
|
3373
3665
|
}
|
|
3374
|
-
catch
|
|
3375
|
-
|
|
3666
|
+
catch { }
|
|
3667
|
+
try {
|
|
3668
|
+
result.store?.close();
|
|
3376
3669
|
}
|
|
3670
|
+
catch { }
|
|
3671
|
+
const verb = result.alreadyExisted ? '已存在且有效' : (force ? '已重新创建' : '已创建');
|
|
3672
|
+
console.log(`✓ ${aid} ${verb}`);
|
|
3673
|
+
console.log(' 如需上线 AUN 通道,运行 evolclaw agent new ' + aid);
|
|
3377
3674
|
}
|
|
3378
|
-
|
|
3379
|
-
|
|
3675
|
+
catch (e) {
|
|
3676
|
+
if (e.code === 'AID_INVALID') {
|
|
3677
|
+
console.error(`❌ ${e.message}`);
|
|
3678
|
+
process.exit(1);
|
|
3679
|
+
}
|
|
3680
|
+
if (e.code === -32052 || e.constructor?.name === 'IdentityConflictError') {
|
|
3681
|
+
console.error(`❌ AID ${aid} 已在服务端注册,但本地密钥无法匹配。\n` +
|
|
3682
|
+
`该 AID 可能由其他设备创建,无法在本地恢复。请选择其他名称。`);
|
|
3683
|
+
process.exit(1);
|
|
3684
|
+
}
|
|
3685
|
+
throw e;
|
|
3380
3686
|
}
|
|
3381
|
-
catch { }
|
|
3382
|
-
const verb = result.alreadyExisted ? '已存在' : '已创建';
|
|
3383
|
-
console.log(`✓ ${aid} ${verb}`);
|
|
3384
|
-
console.log(' 如需上线 AUN 通道,运行 evolclaw agent new ' + aid);
|
|
3385
3687
|
return;
|
|
3386
3688
|
}
|
|
3387
3689
|
if (sub === 'delete') {
|
|
3388
|
-
|
|
3389
|
-
|
|
3390
|
-
|
|
3690
|
+
if (wantsHelp(args)) {
|
|
3691
|
+
console.log(`用法: evolclaw aid delete <子命令>
|
|
3692
|
+
|
|
3693
|
+
单个删除:
|
|
3694
|
+
evolclaw aid delete <aid> 删除指定 AID 的本地数据(无网络注销)
|
|
3695
|
+
|
|
3696
|
+
批量删除(默认 dry-run,加 --yes 才真删):
|
|
3697
|
+
evolclaw aid delete --orphan 删除所有"无私钥"的本地缓存(外部 AID)
|
|
3698
|
+
evolclaw aid delete --no-cert 删除所有"无私钥也无公钥证书"的目录
|
|
3699
|
+
条件:!hasPrivateKey && !hasCert
|
|
3700
|
+
这些目录最多只剩 agent.md 或 SQLite 残留,
|
|
3701
|
+
对验签和加密通信都没用,删除安全。
|
|
3702
|
+
evolclaw aid delete --unrecoverable 删除所有不可恢复的 AID
|
|
3703
|
+
条件:本地 sign+verify 实测失败
|
|
3704
|
+
且 PKI 探测确认云端公钥也不等本地 key.json
|
|
3705
|
+
|
|
3706
|
+
选项:
|
|
3707
|
+
--yes 跳过 dry-run,立即执行
|
|
3708
|
+
--skip-pki --unrecoverable 时跳过 PKI 探测,仅依据本地 sign+verify 失败判断(危险,可能误删可恢复 AID)
|
|
3709
|
+
--format json 输出 JSON 格式
|
|
3710
|
+
|
|
3711
|
+
示例:
|
|
3712
|
+
evolclaw aid delete old.agentid.pub
|
|
3713
|
+
evolclaw aid delete --orphan 列出会被清理的孤儿
|
|
3714
|
+
evolclaw aid delete --orphan --yes 实际清理
|
|
3715
|
+
evolclaw aid delete --no-cert 列出无证书孤儿目录
|
|
3716
|
+
evolclaw aid delete --no-cert --yes 实际清理
|
|
3717
|
+
evolclaw aid delete --unrecoverable 联网探测后列出无救 AID
|
|
3718
|
+
evolclaw aid delete --unrecoverable --yes`);
|
|
3719
|
+
return;
|
|
3720
|
+
}
|
|
3721
|
+
const yes = args.includes('--yes');
|
|
3722
|
+
const skipPki = args.includes('--skip-pki');
|
|
3723
|
+
const orphan = args.includes('--orphan');
|
|
3724
|
+
const noCert = args.includes('--no-cert');
|
|
3725
|
+
const unrecoverable = args.includes('--unrecoverable');
|
|
3726
|
+
const modes = [orphan, noCert, unrecoverable].filter(Boolean).length;
|
|
3727
|
+
if (modes > 1) {
|
|
3728
|
+
console.error('❌ --orphan / --no-cert / --unrecoverable 互斥,不能同时使用');
|
|
3391
3729
|
process.exit(1);
|
|
3392
3730
|
}
|
|
3393
|
-
|
|
3394
|
-
if (
|
|
3395
|
-
|
|
3731
|
+
// 单个 aid 删除:保留原有行为
|
|
3732
|
+
if (modes === 0) {
|
|
3733
|
+
const aid = args[1];
|
|
3734
|
+
if (!aid) {
|
|
3735
|
+
console.error('用法: evolclaw aid delete <aid>\n evolclaw aid delete --orphan | --no-cert | --unrecoverable [--yes]\n evolclaw aid delete --help 查看完整用法');
|
|
3736
|
+
process.exit(1);
|
|
3737
|
+
}
|
|
3738
|
+
const deleted = aidDelete(aid, { aunPath });
|
|
3739
|
+
if (deleted) {
|
|
3740
|
+
console.log(`✓ ${aid} 已删除`);
|
|
3741
|
+
}
|
|
3742
|
+
else {
|
|
3743
|
+
console.error(`❌ 本地不存在: ${aid}`);
|
|
3744
|
+
process.exit(1);
|
|
3745
|
+
}
|
|
3746
|
+
return;
|
|
3747
|
+
}
|
|
3748
|
+
// 批量模式:先选出候选
|
|
3749
|
+
const { probePkiRecoverability } = await import('../aun/aid/index.js');
|
|
3750
|
+
const candidates = [];
|
|
3751
|
+
if (orphan) {
|
|
3752
|
+
const aids = aidList(aunPath);
|
|
3753
|
+
for (const a of aids) {
|
|
3754
|
+
if (!a.hasPrivateKey)
|
|
3755
|
+
candidates.push({ aid: a.aid, reason: 'no private key (external AID cache)' });
|
|
3756
|
+
}
|
|
3757
|
+
}
|
|
3758
|
+
else if (noCert) {
|
|
3759
|
+
const aids = aidList(aunPath);
|
|
3760
|
+
for (const a of aids) {
|
|
3761
|
+
if (!a.hasPrivateKey && !a.hasCert) {
|
|
3762
|
+
const traits = [a.hasAgentMd ? 'agent.md' : null].filter(Boolean).join(', ');
|
|
3763
|
+
candidates.push({ aid: a.aid, reason: `no private key, no cert${traits ? ` (only: ${traits})` : ''}` });
|
|
3764
|
+
}
|
|
3765
|
+
}
|
|
3396
3766
|
}
|
|
3397
3767
|
else {
|
|
3398
|
-
|
|
3399
|
-
|
|
3768
|
+
// unrecoverable: 必须先做 sign+verify 实测
|
|
3769
|
+
if (!formatJson)
|
|
3770
|
+
console.log('扫描中: 本地签名/验签实测...');
|
|
3771
|
+
const aids = await aidListVerified(aunPath);
|
|
3772
|
+
const localBroken = aids.filter(a => a.hasPrivateKey && a.signVerified === false);
|
|
3773
|
+
if (skipPki) {
|
|
3774
|
+
for (const a of localBroken) {
|
|
3775
|
+
candidates.push({ aid: a.aid, reason: `sign+verify failed (${a.signError ?? 'unknown'}) [--skip-pki: 未联网验证]` });
|
|
3776
|
+
}
|
|
3777
|
+
}
|
|
3778
|
+
else {
|
|
3779
|
+
if (!formatJson)
|
|
3780
|
+
console.log(`扫描中: 对 ${localBroken.length} 个本地损坏 AID 做 PKI 探测...`);
|
|
3781
|
+
for (const a of localBroken) {
|
|
3782
|
+
const r = await probePkiRecoverability(a.aid, { aunPath });
|
|
3783
|
+
if (r.kind === 'unrecoverable') {
|
|
3784
|
+
candidates.push({ aid: a.aid, reason: `local broken; PKI: ${r.reason}`, pki: 'unrecoverable' });
|
|
3785
|
+
}
|
|
3786
|
+
else if (r.kind === 'no-server-record') {
|
|
3787
|
+
candidates.push({ aid: a.aid, reason: `local broken; PKI: ${r.reason}`, pki: 'no-server-record' });
|
|
3788
|
+
}
|
|
3789
|
+
else {
|
|
3790
|
+
// recoverable / no-key / unknown 一律保守不删
|
|
3791
|
+
if (!formatJson)
|
|
3792
|
+
console.log(` · 跳过 ${a.aid}: PKI=${r.kind}${('reason' in r) ? ' — ' + r.reason : ''}`);
|
|
3793
|
+
}
|
|
3794
|
+
}
|
|
3795
|
+
}
|
|
3400
3796
|
}
|
|
3797
|
+
if (formatJson) {
|
|
3798
|
+
console.log(JSON.stringify({
|
|
3799
|
+
mode: orphan ? 'orphan' : noCert ? 'no-cert' : 'unrecoverable',
|
|
3800
|
+
dryRun: !yes,
|
|
3801
|
+
skipPki: unrecoverable ? skipPki : undefined,
|
|
3802
|
+
candidates,
|
|
3803
|
+
}, null, 2));
|
|
3804
|
+
if (yes) {
|
|
3805
|
+
for (const c of candidates)
|
|
3806
|
+
aidDelete(c.aid, { aunPath });
|
|
3807
|
+
}
|
|
3808
|
+
return;
|
|
3809
|
+
}
|
|
3810
|
+
if (candidates.length === 0) {
|
|
3811
|
+
console.log(orphan ? '✓ 无孤儿 AID' : noCert ? '✓ 无无证书孤儿目录' : '✓ 无不可恢复 AID');
|
|
3812
|
+
return;
|
|
3813
|
+
}
|
|
3814
|
+
console.log(`\n${yes ? '将删除' : '候选删除(dry-run)'}:${candidates.length} 个 AID`);
|
|
3815
|
+
for (const c of candidates) {
|
|
3816
|
+
console.log(` - ${c.aid}`);
|
|
3817
|
+
console.log(` ${c.reason}`);
|
|
3818
|
+
}
|
|
3819
|
+
if (!yes) {
|
|
3820
|
+
console.log('\n(dry-run,未真删除。加 --yes 执行真删。)');
|
|
3821
|
+
return;
|
|
3822
|
+
}
|
|
3823
|
+
let ok = 0;
|
|
3824
|
+
let fail = 0;
|
|
3825
|
+
for (const c of candidates) {
|
|
3826
|
+
const deleted = aidDelete(c.aid, { aunPath });
|
|
3827
|
+
if (deleted) {
|
|
3828
|
+
console.log(` ✓ 删除 ${c.aid}`);
|
|
3829
|
+
ok++;
|
|
3830
|
+
}
|
|
3831
|
+
else {
|
|
3832
|
+
console.log(` ✗ 失败 ${c.aid}(已不存在?)`);
|
|
3833
|
+
fail++;
|
|
3834
|
+
}
|
|
3835
|
+
}
|
|
3836
|
+
console.log(`\n完成:成功 ${ok},失败 ${fail}`);
|
|
3401
3837
|
return;
|
|
3402
3838
|
}
|
|
3403
3839
|
if (sub === 'lookup') {
|
|
3840
|
+
if (wantsHelp(args)) {
|
|
3841
|
+
console.log(`用法: evolclaw aid lookup <aid> [--format json]
|
|
3842
|
+
|
|
3843
|
+
远程探测 AID:是否注册、所在网关、是否有 agent.md(不验签,仅获取)。`);
|
|
3844
|
+
return;
|
|
3845
|
+
}
|
|
3404
3846
|
const aid = args[1];
|
|
3405
3847
|
if (!aid) {
|
|
3406
3848
|
console.error('用法: evolclaw aid lookup <aid>');
|
|
@@ -3438,6 +3880,13 @@ Options:
|
|
|
3438
3880
|
if (sub === 'agentmd') {
|
|
3439
3881
|
const verb = args[1];
|
|
3440
3882
|
const aid = args[2];
|
|
3883
|
+
if (!verb || isHelpFlag(verb) || wantsHelp(args)) {
|
|
3884
|
+
console.log(`用法: evolclaw aid agentmd <put|get> <aid> [--format json]
|
|
3885
|
+
|
|
3886
|
+
put <aid> 读本地 agent.md → 用本地私钥签名 → 上传到 PKI
|
|
3887
|
+
get <aid> 从 PKI 下载 agent.md → 验签 → 持久化到本地`);
|
|
3888
|
+
return;
|
|
3889
|
+
}
|
|
3441
3890
|
if (verb === 'put') {
|
|
3442
3891
|
if (!aid) {
|
|
3443
3892
|
console.error('用法: evolclaw aid agentmd put <aid>');
|
|
@@ -3509,7 +3958,7 @@ Options:
|
|
|
3509
3958
|
}
|
|
3510
3959
|
// ==================== RPC ====================
|
|
3511
3960
|
async function cmdRpc(args) {
|
|
3512
|
-
if (args
|
|
3961
|
+
if (args.length === 0 || isHelpFlag(args[0])) {
|
|
3513
3962
|
console.log(`用法: evolclaw rpc --as <aid> --params <params>
|
|
3514
3963
|
|
|
3515
3964
|
通用 AUN RPC 调用。
|
|
@@ -3588,7 +4037,7 @@ async function cmdStorage(args) {
|
|
|
3588
4037
|
const sub = args[0];
|
|
3589
4038
|
const aunPath = resolveAunPath(args);
|
|
3590
4039
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
3591
|
-
if (!sub || sub
|
|
4040
|
+
if (!sub || isHelpFlag(sub)) {
|
|
3592
4041
|
console.log(`用法: evolclaw storage <command> <aid> [options]
|
|
3593
4042
|
|
|
3594
4043
|
Commands:
|
|
@@ -3737,8 +4186,7 @@ async function cmdMsg(args) {
|
|
|
3737
4186
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
3738
4187
|
const appIdx = args.indexOf('--app');
|
|
3739
4188
|
const appSlot = appIdx >= 0 ? args[appIdx + 1] : undefined;
|
|
3740
|
-
|
|
3741
|
-
if (!sub || sub === 'help') {
|
|
4189
|
+
if (!sub || isHelpFlag(sub)) {
|
|
3742
4190
|
console.log(`用法: evolclaw msg <command> <from-aid> [args...] [options]
|
|
3743
4191
|
|
|
3744
4192
|
Commands:
|
|
@@ -3747,13 +4195,12 @@ Commands:
|
|
|
3747
4195
|
send <from> <to> --link <url> [--title T] 发送链接卡片
|
|
3748
4196
|
send <from> <to> --payload <json> 发送自定义 payload
|
|
3749
4197
|
pull <from> [--after-seq N] [--limit N] 拉取收件箱
|
|
3750
|
-
ack <from> <seq> --app <name>
|
|
4198
|
+
ack <from> <seq> [--app <name>] 确认已读
|
|
3751
4199
|
recall <from> <message-id> [<message-id>...] 撤回消息
|
|
3752
4200
|
online <from> <target-aid> [<target-aid>...] 查询在线状态
|
|
3753
4201
|
|
|
3754
4202
|
Options:
|
|
3755
|
-
--app <name> 指定应用 slot
|
|
3756
|
-
--as-daemon ack 时显式以 daemon 身份(高危,会污染 daemon 游标)
|
|
4203
|
+
--app <name> 指定应用 slot(独立消费通道,不影响 daemon)
|
|
3757
4204
|
--format json 输出 JSON 格式
|
|
3758
4205
|
--encrypt 启用端到端加密
|
|
3759
4206
|
--thread <id> 指定话题 ID(用于多话题路由)
|
|
@@ -3879,7 +4326,7 @@ Options:
|
|
|
3879
4326
|
}
|
|
3880
4327
|
if (sub === 'pull') {
|
|
3881
4328
|
if (!appSlot) {
|
|
3882
|
-
console.
|
|
4329
|
+
console.warn('⚠ 警告: 未传 --app,当前与 daemon 共享 evolclaw 消费通道。pull 会看到/影响 daemon 的消息消费;如需独立消费请用 --app <name>');
|
|
3883
4330
|
}
|
|
3884
4331
|
const afterSeqStr = getArgValue(args, '--after-seq');
|
|
3885
4332
|
const limitStr = getArgValue(args, '--limit');
|
|
@@ -3921,7 +4368,7 @@ Options:
|
|
|
3921
4368
|
if (sub === 'ack') {
|
|
3922
4369
|
const seqStr = args[2];
|
|
3923
4370
|
if (!seqStr) {
|
|
3924
|
-
console.error('用法: evolclaw msg ack <from> <seq> --app <name>');
|
|
4371
|
+
console.error('用法: evolclaw msg ack <from> <seq> [--app <name>]');
|
|
3925
4372
|
process.exit(1);
|
|
3926
4373
|
}
|
|
3927
4374
|
const seq = Number(seqStr);
|
|
@@ -3929,10 +4376,8 @@ Options:
|
|
|
3929
4376
|
console.error(`❌ seq 必须是数字: ${seqStr}`);
|
|
3930
4377
|
process.exit(1);
|
|
3931
4378
|
}
|
|
3932
|
-
if (!appSlot
|
|
3933
|
-
console.
|
|
3934
|
-
console.error(' 理由: 不传 --app 会推进 daemon 共享的 ack 游标,导致 daemon 丢消息');
|
|
3935
|
-
process.exit(1);
|
|
4379
|
+
if (!appSlot) {
|
|
4380
|
+
console.warn('⚠ 警告: 未传 --app,ack 将推进与 daemon 共享的 evolclaw 消费游标,可能影响 daemon 收消息;如需独立请用 --app <name>');
|
|
3936
4381
|
}
|
|
3937
4382
|
const result = await msgAck({ from, seq, ...commonOpts });
|
|
3938
4383
|
if (!result.ok) {
|
|
@@ -4023,8 +4468,7 @@ async function cmdGroup(args) {
|
|
|
4023
4468
|
const formatJson = args.includes('--format') && args[args.indexOf('--format') + 1] === 'json';
|
|
4024
4469
|
const appIdx = args.indexOf('--app');
|
|
4025
4470
|
const appSlot = appIdx >= 0 ? args[appIdx + 1] : undefined;
|
|
4026
|
-
|
|
4027
|
-
if (!sub || sub === 'help') {
|
|
4471
|
+
if (!sub || isHelpFlag(sub)) {
|
|
4028
4472
|
console.log(`用法: evolclaw group <command> <from-aid> [args...] [options]
|
|
4029
4473
|
|
|
4030
4474
|
消息:
|
|
@@ -4032,7 +4476,7 @@ async function cmdGroup(args) {
|
|
|
4032
4476
|
send <from> <group-id> --file <path> [--as <type>] 发送群文件
|
|
4033
4477
|
send <from> <group-id> --payload <json> 发送自定义 payload
|
|
4034
4478
|
pull <from> <group-id> [--after-seq N] [--limit N] 拉取群消息
|
|
4035
|
-
ack <from> <group-id> <seq> --app <name>
|
|
4479
|
+
ack <from> <group-id> <seq> [--app <name>] 确认已读
|
|
4036
4480
|
|
|
4037
4481
|
群管理:
|
|
4038
4482
|
create <from> <name> [--visibility public|private] [--description D] [--join-mode M] 创建群
|
|
@@ -4050,8 +4494,7 @@ async function cmdGroup(args) {
|
|
|
4050
4494
|
online <from> <group-id> 查看在线成员
|
|
4051
4495
|
|
|
4052
4496
|
Options:
|
|
4053
|
-
--app <name> 指定应用 slot
|
|
4054
|
-
--as-daemon ack 时显式以 daemon 身份(高危)
|
|
4497
|
+
--app <name> 指定应用 slot(独立消费通道,不影响 daemon)
|
|
4055
4498
|
--format json 输出 JSON 格式
|
|
4056
4499
|
--mention <aid> 发送时 @ 某个成员(可多次)
|
|
4057
4500
|
--mention-all 发送时 @ 所有人
|
|
@@ -4164,7 +4607,7 @@ Options:
|
|
|
4164
4607
|
if (sub === 'pull') {
|
|
4165
4608
|
const groupId = requireGroupId();
|
|
4166
4609
|
if (!appSlot) {
|
|
4167
|
-
console.
|
|
4610
|
+
console.warn('⚠ 警告: 未传 --app,当前与 daemon 共享 evolclaw 消费通道。pull 会看到/影响 daemon 的消息消费;如需独立消费请用 --app <name>');
|
|
4168
4611
|
}
|
|
4169
4612
|
const afterSeqStr = getArgValue(args, '--after-seq');
|
|
4170
4613
|
const limitStr = getArgValue(args, '--limit');
|
|
@@ -4185,7 +4628,7 @@ Options:
|
|
|
4185
4628
|
const groupId = requireGroupId();
|
|
4186
4629
|
const seqStr = args[3];
|
|
4187
4630
|
if (!seqStr) {
|
|
4188
|
-
console.error('用法: evolclaw group ack <from> <group-id> <seq> --app <name>');
|
|
4631
|
+
console.error('用法: evolclaw group ack <from> <group-id> <seq> [--app <name>]');
|
|
4189
4632
|
process.exit(1);
|
|
4190
4633
|
}
|
|
4191
4634
|
const seq = Number(seqStr);
|
|
@@ -4193,9 +4636,8 @@ Options:
|
|
|
4193
4636
|
console.error(`❌ seq 必须是数字: ${seqStr}`);
|
|
4194
4637
|
process.exit(1);
|
|
4195
4638
|
}
|
|
4196
|
-
if (!appSlot
|
|
4197
|
-
console.
|
|
4198
|
-
process.exit(1);
|
|
4639
|
+
if (!appSlot) {
|
|
4640
|
+
console.warn('⚠ 警告: 未传 --app,ack 将推进与 daemon 共享的 evolclaw 消费游标,可能影响 daemon 收消息;如需独立请用 --app <name>');
|
|
4199
4641
|
}
|
|
4200
4642
|
const result = await groupAck({ from, groupId, seq, ...commonOpts });
|
|
4201
4643
|
outputResult(result, () => {
|
|
@@ -4424,7 +4866,7 @@ export async function main(args) {
|
|
|
4424
4866
|
}
|
|
4425
4867
|
switch (cmd) {
|
|
4426
4868
|
case 'init':
|
|
4427
|
-
if (args[1]
|
|
4869
|
+
if (isHelpFlag(args[1])) {
|
|
4428
4870
|
console.log(`用法: evolclaw init [渠道] [选项]
|
|
4429
4871
|
|
|
4430
4872
|
仅初始化 defaults.json:
|
|
@@ -4500,7 +4942,7 @@ export async function main(args) {
|
|
|
4500
4942
|
await cmdWatchAid();
|
|
4501
4943
|
}
|
|
4502
4944
|
else if (args[1] === 'msg') {
|
|
4503
|
-
if (args[2]
|
|
4945
|
+
if (isHelpFlag(args[2])) {
|
|
4504
4946
|
console.log(`用法: evolclaw watch msg
|
|
4505
4947
|
|
|
4506
4948
|
三面板交互式消息监控 TUI。
|
|
@@ -4525,6 +4967,9 @@ export async function main(args) {
|
|
|
4525
4967
|
else if (args[1] === 'log') {
|
|
4526
4968
|
cmdWatch();
|
|
4527
4969
|
}
|
|
4970
|
+
else if (args[1] === 'web' || args[1] === 'session') {
|
|
4971
|
+
await cmdWatchWeb();
|
|
4972
|
+
}
|
|
4528
4973
|
else if (!args[1]) {
|
|
4529
4974
|
await cmdWatchMenu();
|
|
4530
4975
|
}
|
|
@@ -4588,6 +5033,11 @@ export async function main(args) {
|
|
|
4588
5033
|
await cmdGroup(args.slice(1));
|
|
4589
5034
|
break;
|
|
4590
5035
|
}
|
|
5036
|
+
case 'model': {
|
|
5037
|
+
const { cmdModel } = await import('./model.js');
|
|
5038
|
+
await cmdModel(args.slice(1));
|
|
5039
|
+
break;
|
|
5040
|
+
}
|
|
4591
5041
|
case 'bench': {
|
|
4592
5042
|
const { suppressSdkLogs } = await import('../aun/aid/index.js');
|
|
4593
5043
|
suppressSdkLogs();
|
|
@@ -4638,14 +5088,25 @@ Commands:
|
|
|
4638
5088
|
--force (覆盖已有 config.json)
|
|
4639
5089
|
agent reload 全量 resync(扫磁盘,新增上线、删除下线、修改热更新)
|
|
4640
5090
|
agent reload <n> 热重载指定 agent 配置
|
|
5091
|
+
model 模型管理(查看/切换,作用域:全局/agent/关系/会话)
|
|
5092
|
+
model list 列出可用模型,标注各作用域命中
|
|
5093
|
+
model current 显示实际生效的模型 + 来源
|
|
5094
|
+
model info <id> 查看单个模型详情(价格/上下文/模态等)
|
|
5095
|
+
model use <id> 切换模型(--self/--peer/--session 决定作用域)
|
|
5096
|
+
model effort <lv> 设置推理强度
|
|
5097
|
+
model reset 清除指定作用域设置,回落上一级
|
|
5098
|
+
model help 查看完整用法
|
|
4641
5099
|
aid AID 身份管理
|
|
4642
5100
|
aid list 列出本地所有 AID
|
|
4643
5101
|
aid show <aid> 查看本地 AID 详情(证书有效期、私钥状态)
|
|
4644
5102
|
aid new <aid> 创建新 AID 身份
|
|
4645
|
-
aid delete <aid> 删除本地 AID
|
|
4646
5103
|
aid lookup <aid> 远程探测 AID(是否存在 + 网关 + agent.md)
|
|
4647
5104
|
aid agentmd put <aid> 签名并上传 agent.md
|
|
4648
5105
|
aid agentmd get <aid> 下载并验签 agent.md
|
|
5106
|
+
aid delete <aid> 删除指定 AID
|
|
5107
|
+
aid delete --orphan 清理无私钥的外部 AID 缓存
|
|
5108
|
+
aid delete --unrecoverable 清理云端公钥已变更、不可恢复的 AID
|
|
5109
|
+
默认 dry-run,加 --yes 执行
|
|
4649
5110
|
net 网络链路诊断
|
|
4650
5111
|
net check [<aid>] 10 步链路检测(DNS→Discovery→TCP→TLS→WSS→Auth→Ping→Echo)
|
|
4651
5112
|
net help 查看详细帮助
|