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/init-channel.js
CHANGED
|
@@ -8,9 +8,8 @@
|
|
|
8
8
|
import fs from 'fs';
|
|
9
9
|
import readline from 'readline';
|
|
10
10
|
import path from 'path';
|
|
11
|
-
import os from 'os';
|
|
12
11
|
import crypto from 'crypto';
|
|
13
|
-
import { aidLocalDir } from '../paths.js';
|
|
12
|
+
import { aidLocalDir, aunPath as defaultAunPath } from '../paths.js';
|
|
14
13
|
import { selectInstance } from './init.js';
|
|
15
14
|
import { npmInstallGlobal } from '../utils/npm-ops.js';
|
|
16
15
|
import { loadAllAgents, loadAgent } from '../config-store.js';
|
|
@@ -442,7 +441,7 @@ export async function setupAunAid(rl, _config) {
|
|
|
442
441
|
}
|
|
443
442
|
}
|
|
444
443
|
// Check if AID exists locally
|
|
445
|
-
const aunPath =
|
|
444
|
+
const aunPath = defaultAunPath();
|
|
446
445
|
const aidDir = path.join(aunPath, 'AIDs', aid);
|
|
447
446
|
if (fs.existsSync(aidDir) && fs.existsSync(path.join(aidDir, 'private'))) {
|
|
448
447
|
console.log(` ✓ AID ${aid} 已存在`);
|
|
@@ -464,7 +463,7 @@ export async function setupAunAid(rl, _config) {
|
|
|
464
463
|
const agentType = typeInput === 'human' ? 'human' : 'ai';
|
|
465
464
|
const content = buildInitialAgentMd({ aid, type: agentType });
|
|
466
465
|
try {
|
|
467
|
-
await agentmdPut(content, { aid
|
|
466
|
+
await agentmdPut(content, { aid });
|
|
468
467
|
console.log(' ✓ agent.md 已发布并写入本地');
|
|
469
468
|
}
|
|
470
469
|
catch (e) {
|
|
@@ -485,6 +484,10 @@ export async function setupAunAid(rl, _config) {
|
|
|
485
484
|
await result.client.close();
|
|
486
485
|
}
|
|
487
486
|
catch { /* ignore */ }
|
|
487
|
+
try {
|
|
488
|
+
result.store?.close();
|
|
489
|
+
}
|
|
490
|
+
catch { /* ignore */ }
|
|
488
491
|
}
|
|
489
492
|
catch (e) {
|
|
490
493
|
const msg = e.message || String(e);
|
package/dist/cli/link-rules.js
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { kitsRulesDir, resolvePaths } from '../paths.js';
|
|
4
4
|
import { atomicWriteJson, atomicReadJson } from '../utils/atomic-write.js';
|
|
5
|
+
import { wantsHelp } from './help.js';
|
|
5
6
|
const isWindows = process.platform === 'win32';
|
|
6
7
|
const KNOWN_BASEAGENTS = ['cc', 'codex', 'gemini'];
|
|
7
8
|
function statePath() {
|
|
@@ -196,7 +197,7 @@ function disconnect(ba) {
|
|
|
196
197
|
}
|
|
197
198
|
// ── 入口 ──
|
|
198
199
|
export function cmdLinkRules(args) {
|
|
199
|
-
if (
|
|
200
|
+
if (wantsHelp(args)) {
|
|
200
201
|
showHelp();
|
|
201
202
|
return;
|
|
202
203
|
}
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `evolclaw model` —— 面向 agent 的模型管理命令集。
|
|
3
|
+
*
|
|
4
|
+
* 三级作用域由参数决定:
|
|
5
|
+
* (无) → 全局 defaults.json
|
|
6
|
+
* --self → agent config.json
|
|
7
|
+
* --self --peer → 关系 relations/<peerKey>/preferences.json
|
|
8
|
+
*
|
|
9
|
+
* 改某作用域后,对应范围所有会话的下一条消息即时生效(运行时按 关系>agent>全局 解析)。
|
|
10
|
+
* 与对话内 slash(/model /setmodel /effort /baseagent)互不影响。
|
|
11
|
+
* 设计见 docs/model-command-design.md。
|
|
12
|
+
*/
|
|
13
|
+
import { isHelpFlag, wantsHelp } from './help.js';
|
|
14
|
+
import { ModelScopeError, normalizePeer, determineScope, activeBaseagent, readScope, writeScope, clearScope, resolveEffectiveModel, } from '../core/model/model-scope.js';
|
|
15
|
+
import { getCatalog, getModelInfo } from '../core/model/model-catalog.js';
|
|
16
|
+
const ALL_EFFORTS = ['low', 'medium', 'high', 'xhigh', 'max', 'auto'];
|
|
17
|
+
const SCOPE_LABEL = {
|
|
18
|
+
global: '全局', agent: 'agent级', relation: '关系级',
|
|
19
|
+
};
|
|
20
|
+
function getArgValue(args, flag) {
|
|
21
|
+
const idx = args.indexOf(flag);
|
|
22
|
+
return idx !== -1 && idx + 1 < args.length ? args[idx + 1] : undefined;
|
|
23
|
+
}
|
|
24
|
+
/** 输出 JSON 并退出(success=false 时 exit 1)。 */
|
|
25
|
+
function emit(formatJson, payload, textFn) {
|
|
26
|
+
if (formatJson) {
|
|
27
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
console.log(textFn());
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
function fail(formatJson, code, message) {
|
|
34
|
+
if (formatJson) {
|
|
35
|
+
console.log(JSON.stringify({ ok: false, code, error: message }, null, 2));
|
|
36
|
+
}
|
|
37
|
+
else {
|
|
38
|
+
console.error(`❌ ${message} (${code})`);
|
|
39
|
+
}
|
|
40
|
+
process.exit(1);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 解析 --self / --peer 为作用域选择器。
|
|
44
|
+
*/
|
|
45
|
+
function parseSelector(args, formatJson) {
|
|
46
|
+
const self = getArgValue(args, '--self');
|
|
47
|
+
const peerRaw = getArgValue(args, '--peer');
|
|
48
|
+
let peerKey;
|
|
49
|
+
if (peerRaw !== undefined) {
|
|
50
|
+
try {
|
|
51
|
+
peerKey = normalizePeer(peerRaw);
|
|
52
|
+
}
|
|
53
|
+
catch (e) {
|
|
54
|
+
if (e instanceof ModelScopeError)
|
|
55
|
+
fail(formatJson, e.code, e.message);
|
|
56
|
+
throw e;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const sel = { self, peerKey };
|
|
60
|
+
// 触发依赖校验(PEER_WITHOUT_SELF)
|
|
61
|
+
try {
|
|
62
|
+
determineScope(sel);
|
|
63
|
+
}
|
|
64
|
+
catch (e) {
|
|
65
|
+
if (e instanceof ModelScopeError)
|
|
66
|
+
fail(formatJson, e.code, e.message);
|
|
67
|
+
throw e;
|
|
68
|
+
}
|
|
69
|
+
return sel;
|
|
70
|
+
}
|
|
71
|
+
const HELP = `用法: evolclaw model <command> [options]
|
|
72
|
+
|
|
73
|
+
Commands:
|
|
74
|
+
list 列出可用模型,标注各作用域命中
|
|
75
|
+
current 显示按优先级解析后实际生效的模型 + 来源
|
|
76
|
+
info <model-id> 查看单个模型详情(厂商/上下文/价格/模态/effort/状态)
|
|
77
|
+
use <model-id> 设置模型(作用域由 --self/--peer 决定)
|
|
78
|
+
reset 清除指定作用域的设置,回落上一级
|
|
79
|
+
effort <level> 设置推理强度(low|medium|high|xhigh|max|auto)
|
|
80
|
+
|
|
81
|
+
作用域(由参数决定,越具体越优先:关系 > agent > 全局):
|
|
82
|
+
(无参数) 全局默认 → defaults.json
|
|
83
|
+
--self <aid> agent级 → config.json
|
|
84
|
+
--self <aid> --peer <X> 关系级 → relations/<peerKey>/preferences.json
|
|
85
|
+
|
|
86
|
+
改某作用域后,对应范围所有会话的下一条消息即时生效。
|
|
87
|
+
|
|
88
|
+
Options:
|
|
89
|
+
--self <aid> 本端 AID
|
|
90
|
+
--peer <X> 对端:channelType#channelId 或裸 aid(裸 aid 视为 aun#<aid>)
|
|
91
|
+
--effort <level> (use 专用)同时设置推理强度
|
|
92
|
+
--format json 输出 JSON
|
|
93
|
+
--help, -h 各子命令均支持
|
|
94
|
+
|
|
95
|
+
示例:
|
|
96
|
+
evolclaw model list
|
|
97
|
+
evolclaw model current --self bot.agentid.pub --peer aun#alice.agentid.pub
|
|
98
|
+
evolclaw model info deepseek-v4-pro
|
|
99
|
+
evolclaw model use opus
|
|
100
|
+
evolclaw model use deepseek-v4-pro --self bot.agentid.pub --peer alice.agentid.pub
|
|
101
|
+
evolclaw model effort high --self bot.agentid.pub
|
|
102
|
+
evolclaw model reset --self bot.agentid.pub --peer alice.agentid.pub`;
|
|
103
|
+
async function dispatch(sub, args, formatJson) {
|
|
104
|
+
switch (sub) {
|
|
105
|
+
case 'list': return await cmdList(args, formatJson);
|
|
106
|
+
case 'current': return await cmdCurrent(args, formatJson);
|
|
107
|
+
case 'info': return await cmdInfo(args, formatJson);
|
|
108
|
+
case 'use': return await cmdUse(args, formatJson);
|
|
109
|
+
case 'reset': return await cmdReset(args, formatJson);
|
|
110
|
+
case 'effort': return await cmdEffort(args, formatJson);
|
|
111
|
+
default:
|
|
112
|
+
fail(formatJson, 'UNKNOWN_SUBCOMMAND', `未知子命令: ${sub}(model --help 查看用法)`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ── list ──────────────────────────────────────────────────────────────
|
|
116
|
+
const ICON = {
|
|
117
|
+
global: '⬡', agent: '◆', relation: '★',
|
|
118
|
+
};
|
|
119
|
+
async function cmdList(args, formatJson) {
|
|
120
|
+
if (wantsHelp(args)) {
|
|
121
|
+
console.log(HELP);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const sel = parseSelector(args, formatJson);
|
|
125
|
+
const ba = activeBaseagent(sel.self);
|
|
126
|
+
const cat = await getCatalog(sel.self, ba);
|
|
127
|
+
const resolved = resolveEffectiveModel(sel, ba);
|
|
128
|
+
// 各作用域当前值(仅可达作用域)
|
|
129
|
+
const scopes = {};
|
|
130
|
+
scopes.global = readScope('global', sel, ba);
|
|
131
|
+
if (sel.self)
|
|
132
|
+
scopes.agent = readScope('agent', sel, ba);
|
|
133
|
+
if (sel.self && sel.peerKey)
|
|
134
|
+
scopes.relation = readScope('relation', sel, ba);
|
|
135
|
+
emit(formatJson, {
|
|
136
|
+
ok: true,
|
|
137
|
+
effective: { model: resolved.model ?? null, source: resolved.source ?? null },
|
|
138
|
+
scopes,
|
|
139
|
+
catalogSource: cat.source,
|
|
140
|
+
models: cat.models,
|
|
141
|
+
}, () => {
|
|
142
|
+
const lines = [];
|
|
143
|
+
lines.push(`当前生效: ${resolved.model ?? '(未设置,回落 SDK 默认)'}` +
|
|
144
|
+
(resolved.source ? ` (来源: ${SCOPE_LABEL[resolved.source]})` : ''));
|
|
145
|
+
lines.push('');
|
|
146
|
+
const srcTag = cat.source === 'mock' ? ' [mock]'
|
|
147
|
+
: cat.source === 'remote' ? ' [remote]'
|
|
148
|
+
: '';
|
|
149
|
+
lines.push(`可用模型 (${cat.models.length})${srcTag}:`);
|
|
150
|
+
const byScope = (m) => {
|
|
151
|
+
const tags = [];
|
|
152
|
+
for (const s of ['relation', 'agent', 'global']) {
|
|
153
|
+
if (scopes[s]?.model === m)
|
|
154
|
+
tags.push(`${ICON[s]}${SCOPE_LABEL[s]}`);
|
|
155
|
+
}
|
|
156
|
+
return tags.join(' ');
|
|
157
|
+
};
|
|
158
|
+
for (const e of cat.models) {
|
|
159
|
+
const live = resolved.model === e.id ? '✓' : ' ';
|
|
160
|
+
const tag = byScope(e.id);
|
|
161
|
+
lines.push(` ${live} ${e.id.padEnd(28)} ${tag}`.trimEnd());
|
|
162
|
+
}
|
|
163
|
+
return lines.join('\n');
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
// ── current ───────────────────────────────────────────────────────────
|
|
167
|
+
async function cmdCurrent(args, formatJson) {
|
|
168
|
+
if (wantsHelp(args)) {
|
|
169
|
+
console.log(HELP);
|
|
170
|
+
return;
|
|
171
|
+
}
|
|
172
|
+
const sel = parseSelector(args, formatJson);
|
|
173
|
+
const ba = activeBaseagent(sel.self);
|
|
174
|
+
const resolved = resolveEffectiveModel(sel, ba);
|
|
175
|
+
emit(formatJson, {
|
|
176
|
+
ok: true,
|
|
177
|
+
model: resolved.model ?? null,
|
|
178
|
+
effort: resolved.effort ?? null,
|
|
179
|
+
source: resolved.source ?? null,
|
|
180
|
+
chain: resolved.chain.map(c => ({ scope: c.scope, model: c.model ?? null, hit: c.hit })),
|
|
181
|
+
}, () => {
|
|
182
|
+
const lines = [];
|
|
183
|
+
lines.push(`当前生效模型: ${resolved.model ?? '(未设置,回落 SDK 默认)'}`);
|
|
184
|
+
lines.push(`推理强度: ${resolved.effort ?? 'auto'}`);
|
|
185
|
+
lines.push(`来源: ${resolved.source ? SCOPE_LABEL[resolved.source] : '无'}`);
|
|
186
|
+
const chain = resolved.chain.map(c => `${SCOPE_LABEL[c.scope]}${c.hit ? ' ✓' : ''}${c.model ? `(${c.model})` : ''}`).join(' > ');
|
|
187
|
+
lines.push(`解析链: ${chain}`);
|
|
188
|
+
return lines.join('\n');
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// ── info ──────────────────────────────────────────────────────────────
|
|
192
|
+
async function cmdInfo(args, formatJson) {
|
|
193
|
+
if (wantsHelp(args)) {
|
|
194
|
+
console.log(HELP);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
const modelId = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
198
|
+
if (!modelId)
|
|
199
|
+
fail(formatJson, 'MISSING_MODEL_ID', 'info 需要 <model-id>');
|
|
200
|
+
const self = getArgValue(args, '--self');
|
|
201
|
+
const info = await getModelInfo(modelId, self);
|
|
202
|
+
emit(formatJson, { ok: true, ...info }, () => {
|
|
203
|
+
const price = info.pricing;
|
|
204
|
+
const fmtPrice = (v) => v === null ? '— (mock)' : `$${v} / 1M tokens`;
|
|
205
|
+
return [
|
|
206
|
+
`模型: ${info.id}`,
|
|
207
|
+
` 厂商: ${info.owned_by}`,
|
|
208
|
+
` 上下文窗口: ${info.context_window ?? '—'} tokens`,
|
|
209
|
+
` 最大输出: ${info.max_output_tokens ?? '—'} tokens`,
|
|
210
|
+
` 输入价格: ${fmtPrice(price?.input_per_mtok ?? null)}`,
|
|
211
|
+
` 输出价格: ${fmtPrice(price?.output_per_mtok ?? null)}`,
|
|
212
|
+
` 支持模态: ${info.modalities.join(', ')}${info.mocked ? ' (mock)' : ''}`,
|
|
213
|
+
` 推理强度: ${info.supports_effort ? '支持' : '不支持'}`,
|
|
214
|
+
` 状态: ${info.status === 'available' ? '✓ 可用' : info.status}`,
|
|
215
|
+
].join('\n');
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
// ── use / effort / reset 共用写入逻辑 ───────────────────────────────────
|
|
219
|
+
function describeWrite(scope, sel, model, effort, formatJson) {
|
|
220
|
+
emit(formatJson, {
|
|
221
|
+
ok: true,
|
|
222
|
+
scope,
|
|
223
|
+
self: sel.self ?? null,
|
|
224
|
+
peerKey: sel.peerKey ?? null,
|
|
225
|
+
model: model ?? undefined,
|
|
226
|
+
effort: effort ?? undefined,
|
|
227
|
+
}, () => {
|
|
228
|
+
const lines = ['✓ 已设置', ` 作用域: ${SCOPE_LABEL[scope]}` +
|
|
229
|
+
(sel.peerKey ? ` (${sel.peerKey})` : '') + (sel.self ? ` [self=${sel.self}]` : '')];
|
|
230
|
+
if (model !== null)
|
|
231
|
+
lines.push(` 模型: ${model}`);
|
|
232
|
+
if (effort !== null)
|
|
233
|
+
lines.push(` 推理强度: ${effort}`);
|
|
234
|
+
lines.push(' 生效: 该范围所有会话的下一条消息起生效。');
|
|
235
|
+
return lines.join('\n');
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
async function cmdUse(args, formatJson) {
|
|
239
|
+
if (wantsHelp(args)) {
|
|
240
|
+
console.log(HELP);
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
const modelId = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
244
|
+
if (!modelId)
|
|
245
|
+
fail(formatJson, 'MISSING_MODEL_ID', 'use 需要 <model-id>');
|
|
246
|
+
const sel = parseSelector(args, formatJson);
|
|
247
|
+
const scope = determineScope(sel);
|
|
248
|
+
const ba = activeBaseagent(sel.self);
|
|
249
|
+
// 校验模型在 catalog 中
|
|
250
|
+
const cat = await getCatalog(sel.self, ba);
|
|
251
|
+
if (!cat.models.some(m => m.id === modelId)) {
|
|
252
|
+
fail(formatJson, 'UNKNOWN_MODEL', `模型不在目录中: ${modelId}(model list 查看可用模型)`);
|
|
253
|
+
}
|
|
254
|
+
const effort = getArgValue(args, '--effort');
|
|
255
|
+
if (effort !== undefined && !ALL_EFFORTS.includes(effort)) {
|
|
256
|
+
fail(formatJson, 'INVALID_EFFORT', `无效推理强度: ${effort}(${ALL_EFFORTS.join('/')})`);
|
|
257
|
+
}
|
|
258
|
+
const effortVal = effort === 'auto' ? null : (effort ?? undefined);
|
|
259
|
+
try {
|
|
260
|
+
writeScope(scope, sel, ba, { model: modelId, effort: effortVal });
|
|
261
|
+
}
|
|
262
|
+
catch (e) {
|
|
263
|
+
if (e instanceof ModelScopeError)
|
|
264
|
+
fail(formatJson, e.code, e.message);
|
|
265
|
+
throw e;
|
|
266
|
+
}
|
|
267
|
+
describeWrite(scope, sel, modelId, effort ?? null, formatJson);
|
|
268
|
+
}
|
|
269
|
+
async function cmdEffort(args, formatJson) {
|
|
270
|
+
if (wantsHelp(args)) {
|
|
271
|
+
console.log(HELP);
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
const level = args[1] && !args[1].startsWith('--') ? args[1] : undefined;
|
|
275
|
+
if (!level)
|
|
276
|
+
fail(formatJson, 'INVALID_EFFORT', 'effort 需要 <level>(low|medium|high|xhigh|max|auto)');
|
|
277
|
+
if (!ALL_EFFORTS.includes(level)) {
|
|
278
|
+
fail(formatJson, 'INVALID_EFFORT', `无效推理强度: ${level}(${ALL_EFFORTS.join('/')})`);
|
|
279
|
+
}
|
|
280
|
+
const sel = parseSelector(args, formatJson);
|
|
281
|
+
const scope = determineScope(sel);
|
|
282
|
+
const ba = activeBaseagent(sel.self);
|
|
283
|
+
const val = level === 'auto' ? null : level;
|
|
284
|
+
try {
|
|
285
|
+
writeScope(scope, sel, ba, { effort: val });
|
|
286
|
+
}
|
|
287
|
+
catch (e) {
|
|
288
|
+
if (e instanceof ModelScopeError)
|
|
289
|
+
fail(formatJson, e.code, e.message);
|
|
290
|
+
throw e;
|
|
291
|
+
}
|
|
292
|
+
describeWrite(scope, sel, null, level, formatJson);
|
|
293
|
+
}
|
|
294
|
+
async function cmdReset(args, formatJson) {
|
|
295
|
+
if (wantsHelp(args)) {
|
|
296
|
+
console.log(HELP);
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
const sel = parseSelector(args, formatJson);
|
|
300
|
+
const scope = determineScope(sel);
|
|
301
|
+
const ba = activeBaseagent(sel.self);
|
|
302
|
+
try {
|
|
303
|
+
clearScope(scope, sel, ba);
|
|
304
|
+
}
|
|
305
|
+
catch (e) {
|
|
306
|
+
if (e instanceof ModelScopeError)
|
|
307
|
+
fail(formatJson, e.code, e.message);
|
|
308
|
+
throw e;
|
|
309
|
+
}
|
|
310
|
+
emit(formatJson, {
|
|
311
|
+
ok: true, scope, self: sel.self ?? null, peerKey: sel.peerKey ?? null,
|
|
312
|
+
}, () => {
|
|
313
|
+
return `✓ 已清除 ${SCOPE_LABEL[scope]} 设置,回落上一级(该范围所有会话下条消息生效)`;
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
export async function cmdModel(args) {
|
|
317
|
+
const sub = args[0];
|
|
318
|
+
const formatJson = getArgValue(args, '--format') === 'json';
|
|
319
|
+
if (!sub || isHelpFlag(sub)) {
|
|
320
|
+
console.log(HELP);
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
await dispatch(sub, args, formatJson);
|
|
324
|
+
}
|
package/dist/cli/net-check.js
CHANGED
|
@@ -7,6 +7,9 @@ import dns from 'dns/promises';
|
|
|
7
7
|
import https from 'https';
|
|
8
8
|
// @ts-ignore
|
|
9
9
|
import { WebSocket } from 'ws';
|
|
10
|
+
import { aunPath as defaultAunPath } from '../paths.js';
|
|
11
|
+
import { getAidStore, loadClient, SLOT } from '../aun/aid/store.js';
|
|
12
|
+
import { isHelpFlag } from './help.js';
|
|
10
13
|
const GREEN = '\x1b[32m';
|
|
11
14
|
const RED = '\x1b[31m';
|
|
12
15
|
const YELLOW = '\x1b[33m';
|
|
@@ -19,7 +22,7 @@ function ok(msg) { return ` ${GREEN}✓${RST} ${msg}`; }
|
|
|
19
22
|
function fail(msg) { return ` ${RED}✗${RST} ${msg}`; }
|
|
20
23
|
function skip(msg) { return ` ${YELLOW}○${RST} ${DIM}${msg}${RST}`; }
|
|
21
24
|
function ms(n) { return `${DIM}${n}ms${RST}`; }
|
|
22
|
-
function step(n, label) { return `${DIM}[${n}/
|
|
25
|
+
function step(n, label) { return `${DIM}[${n}/11]${RST} ${CYAN}${label}${RST}`; }
|
|
23
26
|
const isZh = (process.env.LANG || process.env.LC_ALL || process.env.LANGUAGE || Intl.DateTimeFormat().resolvedOptions().locale || '').toLowerCase().startsWith('zh');
|
|
24
27
|
const i18n = {
|
|
25
28
|
resolve: isZh ? '解析' : 'resolve',
|
|
@@ -122,7 +125,7 @@ function suppressSdkOutput(fn) {
|
|
|
122
125
|
});
|
|
123
126
|
}
|
|
124
127
|
// ==================== Check pipeline ====================
|
|
125
|
-
async function runCheck(aid, formatJson) {
|
|
128
|
+
async function runCheck(aid, formatJson, kickTest) {
|
|
126
129
|
const results = [];
|
|
127
130
|
const log = (r) => {
|
|
128
131
|
results.push(r);
|
|
@@ -214,19 +217,23 @@ async function runCheck(aid, formatJson) {
|
|
|
214
217
|
}
|
|
215
218
|
}
|
|
216
219
|
// ── Step 7: AID 认证 ──
|
|
217
|
-
let
|
|
220
|
+
let authResult;
|
|
218
221
|
try {
|
|
219
222
|
const start = Date.now();
|
|
220
|
-
const aunPath = process.env.AUN_HOME ||
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
223
|
+
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
224
|
+
authResult = await suppressSdkOutput(async () => {
|
|
225
|
+
const store = await getAidStore({ slotId: SLOT.netcheck, aunPath });
|
|
226
|
+
try {
|
|
227
|
+
const client = await loadClient(store, aid);
|
|
228
|
+
const result = await client.authenticate();
|
|
229
|
+
await client.close();
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
finally {
|
|
233
|
+
store.close();
|
|
234
|
+
}
|
|
228
235
|
});
|
|
229
|
-
accessToken =
|
|
236
|
+
const accessToken = authResult?.access_token;
|
|
230
237
|
const elapsed = Date.now() - start;
|
|
231
238
|
if (accessToken) {
|
|
232
239
|
log({ step: 'Auth', index: 7, ok: true, detail: `${aid} ${i18n.authOk} (login1→login2→token)`, ms: elapsed });
|
|
@@ -268,18 +275,18 @@ async function runCheck(aid, formatJson) {
|
|
|
268
275
|
// ── Step 9: RPC 调用 (meta.ping) ──
|
|
269
276
|
try {
|
|
270
277
|
const start = Date.now();
|
|
271
|
-
const aunPath = process.env.AUN_HOME ||
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
278
|
+
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
279
|
+
await suppressSdkOutput(async () => {
|
|
280
|
+
const store = await getAidStore({ slotId: SLOT.netcheck, aunPath });
|
|
281
|
+
try {
|
|
282
|
+
const client = await loadClient(store, aid);
|
|
283
|
+
await client.connect();
|
|
284
|
+
await client.call('meta.ping', {});
|
|
285
|
+
await client.close();
|
|
286
|
+
}
|
|
287
|
+
finally {
|
|
288
|
+
store.close();
|
|
289
|
+
}
|
|
283
290
|
});
|
|
284
291
|
const elapsed = Date.now() - start;
|
|
285
292
|
log({ step: 'Ping', index: 9, ok: true, detail: `meta.ping ${i18n.pingOk}`, ms: elapsed });
|
|
@@ -291,8 +298,7 @@ async function runCheck(aid, formatJson) {
|
|
|
291
298
|
// CLI 模拟 app 发送 echo[nc],向多个目标测试链路
|
|
292
299
|
try {
|
|
293
300
|
const echoStart = Date.now();
|
|
294
|
-
const aunPath = process.env.AUN_HOME ||
|
|
295
|
-
const { AUNClient } = await import('@agentunion/fastaun');
|
|
301
|
+
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
296
302
|
// 选择 6 个测试目标,按消息数量、域、有无证书均衡选择
|
|
297
303
|
const allAids = await getAidList();
|
|
298
304
|
const myDomain = aid.split('.').slice(1).join('.');
|
|
@@ -363,14 +369,14 @@ async function runCheck(aid, formatJson) {
|
|
|
363
369
|
return results;
|
|
364
370
|
}
|
|
365
371
|
if (!formatJson) {
|
|
366
|
-
console.log(` ${DIM}[10/
|
|
372
|
+
console.log(` ${DIM}[10/11]${RST} ${CYAN}Echo${RST} ${DIM}${targets.length} target(s)${RST}`);
|
|
367
373
|
}
|
|
368
374
|
const echoResults = [];
|
|
369
375
|
// 读取所有目标的 agent.md(含签名验证)获取昵称和类型
|
|
370
376
|
const { agentmdGet: agentmdGetFn } = await import('../aun/aid/index.js');
|
|
371
377
|
const targetMeta = new Map();
|
|
372
378
|
if (!formatJson) {
|
|
373
|
-
console.log(` ${DIM}[10/
|
|
379
|
+
console.log(` ${DIM}[10/11]${RST} ${CYAN}Echo${RST} ${DIM}reading agent.md for ${targets.length} target(s)...${RST}`);
|
|
374
380
|
}
|
|
375
381
|
await Promise.all(targets.map(async (t) => {
|
|
376
382
|
const start = Date.now();
|
|
@@ -421,29 +427,31 @@ async function runCheck(aid, formatJson) {
|
|
|
421
427
|
const label = targetLabel(target);
|
|
422
428
|
try {
|
|
423
429
|
const replyText = await suppressSdkOutput(async () => {
|
|
424
|
-
const
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
await client.call('message.
|
|
430
|
+
const store = await getAidStore({ slotId: SLOT.netcheck, aunPath });
|
|
431
|
+
try {
|
|
432
|
+
const client = await loadClient(store, aid);
|
|
433
|
+
await client.connect();
|
|
434
|
+
// 取基线 seq
|
|
435
|
+
const baseline = await client.call('message.pull', { limit: 100 });
|
|
436
|
+
const baselineSeq = baseline?.latest_seq ?? 0;
|
|
437
|
+
if (baselineSeq > 0) {
|
|
438
|
+
await client.call('message.ack', { seq: baselineSeq });
|
|
439
|
+
}
|
|
440
|
+
await client.call('message.send', {
|
|
441
|
+
to: target,
|
|
442
|
+
payload: { type: 'text', text: 'echo[nc]' },
|
|
443
|
+
encrypt: false,
|
|
444
|
+
});
|
|
445
|
+
await new Promise(r => setTimeout(r, 1500));
|
|
446
|
+
const pullResult = await client.call('message.pull', { after_seq: baselineSeq, limit: 10 });
|
|
447
|
+
await client.close();
|
|
448
|
+
const messages = pullResult?.messages || [];
|
|
449
|
+
const reply = messages.find((m) => m.from === target && m.payload?.text?.includes('[EvolClaw.'));
|
|
450
|
+
return reply?.payload?.text || null;
|
|
451
|
+
}
|
|
452
|
+
finally {
|
|
453
|
+
store.close();
|
|
435
454
|
}
|
|
436
|
-
await client.call('message.send', {
|
|
437
|
-
to: target,
|
|
438
|
-
payload: { type: 'text', text: 'echo[nc]' },
|
|
439
|
-
encrypt: false,
|
|
440
|
-
});
|
|
441
|
-
await new Promise(r => setTimeout(r, 1500));
|
|
442
|
-
const pullResult = await client.call('message.pull', { after_seq: baselineSeq, limit: 10 });
|
|
443
|
-
await client.close().catch(() => { });
|
|
444
|
-
const messages = pullResult?.messages || [];
|
|
445
|
-
const reply = messages.find((m) => m.from === target && m.payload?.text?.includes('[EvolClaw.'));
|
|
446
|
-
return reply?.payload?.text || null;
|
|
447
455
|
});
|
|
448
456
|
const elapsed = Date.now() - targetStart;
|
|
449
457
|
if (replyText) {
|
|
@@ -482,6 +490,77 @@ async function runCheck(aid, formatJson) {
|
|
|
482
490
|
catch (e) {
|
|
483
491
|
log({ step: 'Echo', index: 10, ok: false, detail: `echo ${i18n.failed}: ${e.message?.slice(0, 100) || String(e)}` });
|
|
484
492
|
}
|
|
493
|
+
// ── Step 11: 踢人测试 / extra_info 验证 ──
|
|
494
|
+
if (kickTest) {
|
|
495
|
+
try {
|
|
496
|
+
const kickStart = Date.now();
|
|
497
|
+
const aunPath = process.env.AUN_HOME || defaultAunPath();
|
|
498
|
+
const hostname = os.hostname();
|
|
499
|
+
const kickResult = await suppressSdkOutput(async () => {
|
|
500
|
+
const store = await getAidStore({ slotId: SLOT.netcheck, aunPath });
|
|
501
|
+
try {
|
|
502
|
+
const client = await loadClient(store, aid);
|
|
503
|
+
const disconnectEvents = [];
|
|
504
|
+
client.on('gateway.disconnect', (payload) => {
|
|
505
|
+
disconnectEvents.push(payload);
|
|
506
|
+
});
|
|
507
|
+
await client.connect({
|
|
508
|
+
connection_kind: 'long',
|
|
509
|
+
extra_info: {
|
|
510
|
+
app: 'evolclaw',
|
|
511
|
+
role: 'netcheck-kicktest',
|
|
512
|
+
pid: process.pid,
|
|
513
|
+
hostname,
|
|
514
|
+
},
|
|
515
|
+
});
|
|
516
|
+
// 等待 3 秒收集可能的 disconnect 事件
|
|
517
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
518
|
+
await client.close();
|
|
519
|
+
return { connected: true, disconnectEvents };
|
|
520
|
+
}
|
|
521
|
+
finally {
|
|
522
|
+
store.close();
|
|
523
|
+
}
|
|
524
|
+
});
|
|
525
|
+
const elapsed = Date.now() - kickStart;
|
|
526
|
+
const { connected, disconnectEvents } = kickResult;
|
|
527
|
+
if (connected) {
|
|
528
|
+
if (disconnectEvents.length > 0) {
|
|
529
|
+
const evt = disconnectEvents[0];
|
|
530
|
+
const selfInfo = evt.detail?.self_extra_info ? JSON.stringify(evt.detail.self_extra_info) : 'none';
|
|
531
|
+
const newInfo = evt.detail?.new_extra_info ? JSON.stringify(evt.detail.new_extra_info) : 'none';
|
|
532
|
+
log({
|
|
533
|
+
step: 'KickTest',
|
|
534
|
+
index: 11,
|
|
535
|
+
ok: true,
|
|
536
|
+
detail: `connected, received disconnect (code=${evt.code}, self=${selfInfo}, new=${newInfo})`,
|
|
537
|
+
ms: elapsed,
|
|
538
|
+
});
|
|
539
|
+
if (!formatJson) {
|
|
540
|
+
console.log(` ${YELLOW}⚠${RST} ${DIM}This test may have disconnected the running daemon (it will auto-reconnect)${RST}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
log({
|
|
545
|
+
step: 'KickTest',
|
|
546
|
+
index: 11,
|
|
547
|
+
ok: true,
|
|
548
|
+
detail: `connected, no disconnect event (daemon may not be running or already disconnected)`,
|
|
549
|
+
ms: elapsed,
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
log({ step: 'KickTest', index: 11, ok: false, detail: `failed to connect`, ms: elapsed });
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
catch (e) {
|
|
558
|
+
log({ step: 'KickTest', index: 11, ok: false, detail: `kick test failed: ${e.message?.slice(0, 100) || String(e)}` });
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
else {
|
|
562
|
+
log({ step: 'KickTest', index: 11, ok: true, detail: `skipped (use --kick-test to enable)`, skipped: true });
|
|
563
|
+
}
|
|
485
564
|
return results;
|
|
486
565
|
}
|
|
487
566
|
// 添加这个常量到 ANSI 块(如果还没有)
|
|
@@ -564,10 +643,11 @@ function shuffle(arr) {
|
|
|
564
643
|
export async function cmdNet(args) {
|
|
565
644
|
const sub = args[0];
|
|
566
645
|
const formatJson = args.includes('--format') && args.includes('json');
|
|
567
|
-
|
|
568
|
-
|
|
646
|
+
const kickTest = args.includes('--kick-test');
|
|
647
|
+
if (isHelpFlag(sub)) {
|
|
648
|
+
console.log(`用法: evolclaw net check [<aid>] [--format json] [--kick-test]
|
|
569
649
|
|
|
570
|
-
检查 AUN 网络链路连通性(
|
|
650
|
+
检查 AUN 网络链路连通性(11 步逐层诊断)。
|
|
571
651
|
|
|
572
652
|
步骤:
|
|
573
653
|
1. DNS (AID) AID 域名解析
|
|
@@ -579,13 +659,15 @@ export async function cmdNet(args) {
|
|
|
579
659
|
7. Auth AID 认证(login1 + login2 → token)
|
|
580
660
|
8. Session 会话建立
|
|
581
661
|
9. Ping meta.ping RPC
|
|
582
|
-
10.
|
|
662
|
+
10. Echo self-to-self 消息收发
|
|
663
|
+
11. KickTest 踢人测试 / extra_info 验证(可选)
|
|
583
664
|
|
|
584
665
|
参数:
|
|
585
666
|
<aid> 要检查的 AID(可选,默认取前 3 个本地 AID)
|
|
586
667
|
|
|
587
668
|
选项:
|
|
588
|
-
--format json JSON
|
|
669
|
+
--format json JSON 格式输出
|
|
670
|
+
--kick-test 启用踢人测试(会断开 daemon 长连接,daemon 会自动重连)`);
|
|
589
671
|
return;
|
|
590
672
|
}
|
|
591
673
|
if (sub && sub !== 'check' && !sub.startsWith('-') && !sub.includes('.')) {
|
|
@@ -617,7 +699,7 @@ export async function cmdNet(args) {
|
|
|
617
699
|
if (!formatJson) {
|
|
618
700
|
console.log(`\n${BOLD}── ${targetAid} ──${RST}\n`);
|
|
619
701
|
}
|
|
620
|
-
const results = await runCheck(targetAid, formatJson);
|
|
702
|
+
const results = await runCheck(targetAid, formatJson, kickTest);
|
|
621
703
|
allResults.push({ aid: targetAid, checks: results });
|
|
622
704
|
}
|
|
623
705
|
if (formatJson) {
|