evolclaw 3.0.0 → 3.1.0
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/README.md +1 -1
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent-normalize.js +19 -0
- package/dist/agents/claude-runner.js +7 -9
- package/dist/agents/codex-runner.js +2 -0
- package/dist/agents/gemini-runner.js +9 -9
- package/dist/agents/kit-renderer.js +281 -0
- package/dist/aun/aid/identity.js +28 -0
- package/dist/aun/aid/index.js +1 -1
- package/dist/aun/aid/lifecycle-log.js +33 -0
- package/dist/aun/msg/group.js +3 -1
- package/dist/aun/msg/p2p.js +4 -1
- package/dist/channels/aun.js +353 -125
- package/dist/channels/dingtalk.js +2 -1
- package/dist/channels/feishu.js +118 -5
- package/dist/channels/qqbot.js +2 -1
- package/dist/channels/wechat.js +3 -1
- package/dist/channels/wecom.js +2 -1
- package/dist/cli/bench.js +1219 -0
- package/dist/cli/index.js +279 -19
- package/dist/cli/link-rules.js +245 -0
- package/dist/cli/net-check.js +640 -0
- package/dist/cli/watch-msg.js +589 -0
- package/dist/config-store.js +37 -5
- package/dist/core/channel-loader.js +23 -10
- package/dist/core/command-handler.js +46 -22
- package/dist/core/evolagent.js +5 -10
- package/dist/core/message/im-renderer.js +50 -44
- package/dist/core/message/items-formatter.js +11 -4
- package/dist/core/message/message-bridge.js +7 -2
- package/dist/core/message/message-log.js +2 -0
- package/dist/core/message/message-processor.js +150 -99
- package/dist/core/message/message-queue.js +10 -3
- package/dist/core/permission.js +95 -3
- package/dist/core/session/session-manager.js +98 -64
- package/dist/core/trigger/scheduler.js +1 -1
- package/dist/data/error-dict.json +118 -0
- package/dist/eck/baseagent-caps.js +18 -0
- package/dist/eck/detect.js +47 -0
- package/dist/eck/init.js +77 -0
- package/dist/eck/rules-loader.js +28 -0
- package/dist/index.js +137 -16
- package/dist/net-check.js +640 -0
- package/dist/paths.js +31 -40
- package/dist/utils/aid-lifecycle-log.js +33 -0
- package/dist/utils/atomic-write.js +10 -0
- package/dist/utils/cross-platform.js +17 -8
- package/dist/utils/error-utils.js +10 -2
- package/dist/utils/instance-registry.js +6 -5
- package/dist/utils/log-writer.js +2 -1
- package/dist/utils/logger.js +10 -0
- package/dist/utils/npm-ops.js +35 -3
- package/dist/utils/process-introspect.js +16 -38
- package/dist/watch-msg.js +26 -11
- package/evolclaw-install-aun.md +14 -2
- package/kits/docs/GUIDE.md +20 -0
- package/kits/docs/INDEX.md +52 -0
- package/kits/docs/aun/CHEATSHEET.md +17 -0
- package/kits/docs/aun/SYNC_PROTOCOL.md +15 -0
- package/kits/docs/channels/feishu.md +27 -0
- package/kits/docs/eck_templates/GUIDE.template.md +22 -0
- package/kits/docs/eck_templates/INDEX.template.md +28 -0
- package/kits/docs/eck_templates/path-registry.template.md +33 -0
- package/kits/docs/eck_templates/runtime.template.md +19 -0
- package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +25 -0
- package/kits/docs/identity/AID_PROFILE_SPEC.md +27 -0
- package/kits/docs/identity/PATH_OPS.md +16 -0
- package/kits/docs/identity/ROLE_DETAIL.md +20 -0
- package/kits/docs/path-registry.md +43 -0
- package/kits/eck_manifest.json +95 -0
- package/kits/rules/01-overview.md +120 -0
- package/kits/rules/02-navigation.md +75 -0
- package/kits/rules/03-identity.md +34 -0
- package/kits/rules/04-relation.md +49 -0
- package/kits/rules/05-venue.md +45 -0
- package/kits/rules/06-channel.md +43 -0
- package/kits/templates/system-fragments/baseagent.md +2 -0
- package/kits/templates/system-fragments/channel.md +10 -0
- package/kits/templates/system-fragments/identity.md +12 -0
- package/kits/templates/system-fragments/relation.md +9 -0
- package/kits/templates/system-fragments/runtime.md +19 -0
- package/kits/templates/system-fragments/venue.md +5 -0
- package/package.json +7 -5
- package/dist/agents/templates.js +0 -122
- package/dist/data/prompts.md +0 -137
- package/kits/aun/meta.md +0 -25
- package/kits/aun/role.md +0 -25
- package/kits/templates/group.md +0 -20
- package/kits/templates/private.md +0 -9
- package/kits/templates/system-fragments/personal-context.md +0 -3
- package/kits/templates/system-fragments/self-intro.md +0 -5
- package/kits/templates/system-fragments/speaker-intro.md +0 -5
- package/kits/templates/system-fragments/venue-intro.md +0 -5
- /package/kits/{channels → docs/channels}/aun.md +0 -0
- /package/kits/{evolclaw/commands.md → docs/evolclaw/AGENT_CMD.md} +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/self-summary.md +0 -0
- /package/kits/{evolclaw → docs/evolclaw}/tools.md +0 -0
- /package/kits/{evolclaw → docs/identity}/identity-tools.md +0 -0
package/dist/paths.js
CHANGED
|
@@ -36,15 +36,13 @@ export function resolvePaths() {
|
|
|
36
36
|
readySignal: path.join(root, 'data', 'instance', 'ready.signal'),
|
|
37
37
|
selfHealLog: path.join(root, 'logs', 'self-heal.md'),
|
|
38
38
|
socket: resolveInstanceSocketPath(root),
|
|
39
|
-
// ── 新结构(evolclaw-
|
|
39
|
+
// ── 新结构(evolclaw-directory-design.md)────────────────
|
|
40
40
|
defaultsConfig: path.join(root, 'agents', 'defaults.json'),
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
kitsChannelsDir: path.join(root, 'kits', 'channels'),
|
|
44
|
-
kitsEvolclawDir: path.join(root, 'kits', 'evolclaw'),
|
|
45
|
-
kitsTemplatesDir: path.join(root, 'kits', 'templates'),
|
|
41
|
+
processConfig: path.join(root, 'config.json'),
|
|
42
|
+
eckDir: path.join(root, 'eck'),
|
|
46
43
|
instanceReadySignal: path.join(root, 'data', 'instance', 'ready.signal'),
|
|
47
44
|
instanceSocket: resolveInstanceSocketPath(root),
|
|
45
|
+
aidLogsDir: path.join(root, 'logs', 'aids'),
|
|
48
46
|
};
|
|
49
47
|
}
|
|
50
48
|
// ── per-agent 路径(参数化,不进 resolvePaths() 的固定 map)──
|
|
@@ -57,8 +55,15 @@ export function agentConfig(aid) {
|
|
|
57
55
|
export function agentPersonalDir(aid) {
|
|
58
56
|
return path.join(agentDir(aid), 'personal');
|
|
59
57
|
}
|
|
58
|
+
export function agentRelationsDir(aid) {
|
|
59
|
+
return path.join(agentDir(aid), 'relations');
|
|
60
|
+
}
|
|
61
|
+
/** @deprecated Use agentRelationsDir instead */
|
|
60
62
|
export function agentIdentitiesDir(aid) {
|
|
61
|
-
return
|
|
63
|
+
return agentRelationsDir(aid);
|
|
64
|
+
}
|
|
65
|
+
export function agentIndexDir(aid) {
|
|
66
|
+
return path.join(agentDir(aid), 'index');
|
|
62
67
|
}
|
|
63
68
|
export function agentVenuesDir(aid) {
|
|
64
69
|
return path.join(agentDir(aid), 'venues');
|
|
@@ -86,44 +91,30 @@ export function ensureDataDirs() {
|
|
|
86
91
|
const p = resolvePaths();
|
|
87
92
|
fs.mkdirSync(p.dataDir, { recursive: true });
|
|
88
93
|
fs.mkdirSync(p.logs, { recursive: true });
|
|
94
|
+
fs.mkdirSync(p.aidLogsDir, { recursive: true });
|
|
89
95
|
fs.mkdirSync(p.agentsDir, { recursive: true });
|
|
90
96
|
fs.mkdirSync(p.sessionsDir, { recursive: true });
|
|
91
97
|
fs.mkdirSync(p.instanceDir, { recursive: true });
|
|
92
98
|
fs.mkdirSync(p.outboxDir, { recursive: true });
|
|
93
|
-
fs.mkdirSync(p.
|
|
99
|
+
fs.mkdirSync(p.eckDir, { recursive: true });
|
|
100
|
+
fs.mkdirSync(eckDebugDir(), { recursive: true });
|
|
94
101
|
}
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
export function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
const pkgJsonPath = path.join(getPackageRoot(), 'package.json');
|
|
112
|
-
let pkgVersion = '0.0.0';
|
|
113
|
-
try {
|
|
114
|
-
pkgVersion = JSON.parse(fs.readFileSync(pkgJsonPath, 'utf-8')).version || '0.0.0';
|
|
115
|
-
}
|
|
116
|
-
catch { }
|
|
117
|
-
let installedVersion = '';
|
|
118
|
-
try {
|
|
119
|
-
installedVersion = fs.readFileSync(versionFile, 'utf-8').trim();
|
|
120
|
-
}
|
|
121
|
-
catch { }
|
|
122
|
-
if (installedVersion === pkgVersion)
|
|
123
|
-
return;
|
|
124
|
-
// 递归复制(覆盖)
|
|
125
|
-
fs.cpSync(srcKits, destKits, { recursive: true, force: true });
|
|
126
|
-
fs.writeFileSync(versionFile, pkgVersion, 'utf-8');
|
|
102
|
+
// ── kits 路径(始终从包内读取,不复制到 EVOLCLAW_HOME)──
|
|
103
|
+
export function kitsDir() {
|
|
104
|
+
return path.join(getPackageRoot(), 'kits');
|
|
105
|
+
}
|
|
106
|
+
export function kitsRulesDir() {
|
|
107
|
+
return path.join(getPackageRoot(), 'kits', 'rules');
|
|
108
|
+
}
|
|
109
|
+
export function kitsDocsDir() {
|
|
110
|
+
return path.join(getPackageRoot(), 'kits', 'docs');
|
|
111
|
+
}
|
|
112
|
+
export function kitsTemplatesDir() {
|
|
113
|
+
return path.join(getPackageRoot(), 'kits', 'templates');
|
|
114
|
+
}
|
|
115
|
+
// ── 调试输出 ──
|
|
116
|
+
export function eckDebugDir() {
|
|
117
|
+
return path.join(resolveRoot(), 'data', 'eck-debug');
|
|
127
118
|
}
|
|
128
119
|
export function getPackageRoot() {
|
|
129
120
|
// import.meta.dirname is available in Node.js 21.2+ and always returns
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { resolvePaths } from '../paths.js';
|
|
4
|
+
function ensureDir(dir) {
|
|
5
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
6
|
+
}
|
|
7
|
+
function logPath(aid) {
|
|
8
|
+
const aidName = aid.startsWith('@') ? aid.slice(1) : aid;
|
|
9
|
+
return path.join(resolvePaths().aidLogsDir, `${aidName}.jsonl`);
|
|
10
|
+
}
|
|
11
|
+
export function appendAidLifecycle(event) {
|
|
12
|
+
const filePath = logPath(event.aid);
|
|
13
|
+
ensureDir(path.dirname(filePath));
|
|
14
|
+
fs.appendFileSync(filePath, JSON.stringify(event) + '\n');
|
|
15
|
+
}
|
|
16
|
+
export function readAidLifecycle(aid, lastN = 50) {
|
|
17
|
+
const filePath = logPath(aid);
|
|
18
|
+
try {
|
|
19
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
20
|
+
const lines = content.trim().split('\n').filter(Boolean);
|
|
21
|
+
const events = [];
|
|
22
|
+
for (const line of lines.slice(-lastN)) {
|
|
23
|
+
try {
|
|
24
|
+
events.push(JSON.parse(line));
|
|
25
|
+
}
|
|
26
|
+
catch { }
|
|
27
|
+
}
|
|
28
|
+
return events;
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return [];
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -77,3 +77,13 @@ export function ensureDir(dirPath) {
|
|
|
77
77
|
fs.mkdirSync(dirPath, { recursive: true });
|
|
78
78
|
}
|
|
79
79
|
}
|
|
80
|
+
/**
|
|
81
|
+
* Simple atomic write for plain-text files (ECK runtime files).
|
|
82
|
+
* Uses write-tmp-then-rename (no hot-backup needed for these files).
|
|
83
|
+
*/
|
|
84
|
+
export function atomicWriteText(filePath, content) {
|
|
85
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
86
|
+
const tmp = filePath + '.tmp.' + process.pid;
|
|
87
|
+
fs.writeFileSync(tmp, content, 'utf-8');
|
|
88
|
+
fs.renameSync(tmp, filePath);
|
|
89
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'path';
|
|
2
2
|
import { fileURLToPath } from 'url';
|
|
3
|
-
import { execFileSync, execFile, spawn } from 'child_process';
|
|
3
|
+
import { execFileSync, execFile, spawn, spawnSync } from 'child_process';
|
|
4
4
|
import { promisify } from 'util';
|
|
5
5
|
import fs from 'fs';
|
|
6
6
|
const execFileAsync = promisify(execFile);
|
|
@@ -33,7 +33,7 @@ export function isProcessRunning(pid) {
|
|
|
33
33
|
export function killProcess(pid, force = false) {
|
|
34
34
|
if (isWindows && force) {
|
|
35
35
|
try {
|
|
36
|
-
|
|
36
|
+
spawnSync('taskkill', ['/PID', String(pid), '/F'], { windowsHide: true });
|
|
37
37
|
}
|
|
38
38
|
catch { }
|
|
39
39
|
}
|
|
@@ -51,7 +51,8 @@ export function killProcess(pid, force = false) {
|
|
|
51
51
|
export function findProcesses(pattern) {
|
|
52
52
|
try {
|
|
53
53
|
if (isWindows) {
|
|
54
|
-
const
|
|
54
|
+
const result = spawnSync('wmic', ['process', 'where', `CommandLine like '%${pattern}%'`, 'get', 'ProcessId'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
55
|
+
const output = result.stdout || '';
|
|
55
56
|
return output.split('\n')
|
|
56
57
|
.map(line => parseInt(line.trim(), 10))
|
|
57
58
|
.filter(pid => !isNaN(pid) && pid !== process.pid);
|
|
@@ -68,8 +69,8 @@ export function findProcesses(pattern) {
|
|
|
68
69
|
export function getProcessInfo(pid) {
|
|
69
70
|
try {
|
|
70
71
|
if (isWindows) {
|
|
71
|
-
|
|
72
|
-
const output =
|
|
72
|
+
const result = spawnSync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'WorkingSetSize,CreationDate'], { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
73
|
+
const output = result.stdout || '';
|
|
73
74
|
const lines = output.trim().split('\n').filter(l => l.trim());
|
|
74
75
|
if (lines.length >= 2) {
|
|
75
76
|
const parts = lines[1].trim().split(/\s+/);
|
|
@@ -109,19 +110,27 @@ function formatUptime(totalSeconds) {
|
|
|
109
110
|
/**
|
|
110
111
|
* Cross-platform command existence check.
|
|
111
112
|
*/
|
|
113
|
+
const _commandExistsCache = new Map();
|
|
112
114
|
export function commandExists(cmd) {
|
|
115
|
+
const cached = _commandExistsCache.get(cmd);
|
|
116
|
+
if (cached !== undefined)
|
|
117
|
+
return cached;
|
|
118
|
+
let exists = false;
|
|
113
119
|
try {
|
|
114
120
|
if (isWindows) {
|
|
115
|
-
|
|
121
|
+
const r = spawnSync('where', [cmd], { encoding: 'utf-8', stdio: 'pipe', windowsHide: true });
|
|
122
|
+
exists = r.status === 0;
|
|
116
123
|
}
|
|
117
124
|
else {
|
|
118
125
|
execFileSync('which', [cmd], { encoding: 'utf-8', stdio: 'pipe' });
|
|
126
|
+
exists = true;
|
|
119
127
|
}
|
|
120
|
-
return true;
|
|
121
128
|
}
|
|
122
129
|
catch {
|
|
123
|
-
|
|
130
|
+
exists = false;
|
|
124
131
|
}
|
|
132
|
+
_commandExistsCache.set(cmd, exists);
|
|
133
|
+
return exists;
|
|
125
134
|
}
|
|
126
135
|
/**
|
|
127
136
|
* Cross-platform live log tailing (replaces tail -f).
|
|
@@ -73,7 +73,14 @@ let _rules = [];
|
|
|
73
73
|
let _lastMtime = 0;
|
|
74
74
|
function getDictPath() {
|
|
75
75
|
if (!_dictPath) {
|
|
76
|
-
|
|
76
|
+
const userDict = path.join(resolvePaths().dataDir, 'error-dict.json');
|
|
77
|
+
if (fs.existsSync(userDict)) {
|
|
78
|
+
_dictPath = userDict;
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
81
|
+
// Bundled default: src/utils/ → src/data/ (dev) or dist/utils/ → dist/data/ (prod)
|
|
82
|
+
_dictPath = path.resolve(import.meta.dirname, '..', 'data', 'error-dict.json');
|
|
83
|
+
}
|
|
77
84
|
}
|
|
78
85
|
return _dictPath;
|
|
79
86
|
}
|
|
@@ -199,7 +206,8 @@ export function classifyError(error) {
|
|
|
199
206
|
}
|
|
200
207
|
// 内置兜底规则(结构性、稳定的错误模式)
|
|
201
208
|
if (msg.includes('context_length_exceeded') || msg.includes('context_compact_failed')
|
|
202
|
-
|| msg.includes('context limit'))
|
|
209
|
+
|| msg.includes('context limit') || msg.includes('input is too long')
|
|
210
|
+
|| msg.includes('上下文过长')) {
|
|
203
211
|
return ErrorType.CONTEXT_TOO_LONG;
|
|
204
212
|
}
|
|
205
213
|
if (msg.includes('401') || msg.includes('authentication_error')) {
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import fs from 'fs';
|
|
12
12
|
import path from 'path';
|
|
13
|
-
import {
|
|
13
|
+
import { spawnSync } from 'child_process';
|
|
14
14
|
import { resolvePaths } from '../paths.js';
|
|
15
15
|
import { isProcessRunning, killProcess, isWindows, findProcesses } from './cross-platform.js';
|
|
16
16
|
import { getProcessStartTime, startTimeMatches } from './process-introspect.js';
|
|
@@ -389,7 +389,8 @@ export function killOrphans(orphans) {
|
|
|
389
389
|
function readCmdline(pid) {
|
|
390
390
|
if (isWindows) {
|
|
391
391
|
try {
|
|
392
|
-
const
|
|
392
|
+
const result = spawnSync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'CommandLine', '/value'], { encoding: 'utf-8', timeout: 3000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
393
|
+
const out = result.stdout || '';
|
|
393
394
|
const m = out.match(/CommandLine=([^\r\n]+)/);
|
|
394
395
|
return m ? m[1].trim() : '';
|
|
395
396
|
}
|
|
@@ -401,13 +402,13 @@ function readCmdline(pid) {
|
|
|
401
402
|
return fs.readFileSync(`/proc/${pid}/cmdline`, 'utf-8').replace(/\0/g, ' ').trim();
|
|
402
403
|
}
|
|
403
404
|
catch {
|
|
404
|
-
// macOS / 权限不足
|
|
405
405
|
try {
|
|
406
|
-
|
|
406
|
+
const result = spawnSync('ps', ['-p', String(pid), '-o', 'args='], {
|
|
407
407
|
encoding: 'utf-8',
|
|
408
408
|
timeout: 3000,
|
|
409
409
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
410
|
-
})
|
|
410
|
+
});
|
|
411
|
+
return result.stdout?.trim() || '';
|
|
411
412
|
}
|
|
412
413
|
catch {
|
|
413
414
|
return '';
|
package/dist/utils/log-writer.js
CHANGED
|
@@ -207,7 +207,8 @@ export class LogWriter {
|
|
|
207
207
|
continue;
|
|
208
208
|
const full = path.join(logDir, name);
|
|
209
209
|
try {
|
|
210
|
-
|
|
210
|
+
const st = fs.lstatSync(full);
|
|
211
|
+
if (st.isSymbolicLink() || st.mtimeMs < cutoff)
|
|
211
212
|
fs.unlinkSync(full);
|
|
212
213
|
}
|
|
213
214
|
catch { }
|
package/dist/utils/logger.js
CHANGED
|
@@ -22,6 +22,10 @@ const messageWriter = config.messageLog
|
|
|
22
22
|
const eventWriter = config.eventLog
|
|
23
23
|
? new LogWriter({ baseName: 'events', logDir: LOG_DIR, rotation: 'hourly', retention: { hours: 12 } })
|
|
24
24
|
: null;
|
|
25
|
+
// 渠道入站日志:记录从渠道收到的原始消息
|
|
26
|
+
const channelInWriter = new LogWriter({ baseName: 'channel-in', logDir: LOG_DIR, rotation: 'hourly', retention: { hours: 12 } });
|
|
27
|
+
// 渠道出站日志:记录发往渠道的所有消息
|
|
28
|
+
const channelOutWriter = new LogWriter({ baseName: 'channel-out', logDir: LOG_DIR, rotation: 'hourly', retention: { hours: 12 } });
|
|
25
29
|
function shouldLog(level) {
|
|
26
30
|
return (LEVELS[level] ?? 1) >= (LEVELS[currentLevel] ?? 1);
|
|
27
31
|
}
|
|
@@ -63,5 +67,11 @@ export const logger = {
|
|
|
63
67
|
if (!eventWriter)
|
|
64
68
|
return;
|
|
65
69
|
eventWriter.write(JSON.stringify({ ts: localTimestamp(), ...data }));
|
|
70
|
+
},
|
|
71
|
+
channelIn: (data) => {
|
|
72
|
+
channelInWriter.write(JSON.stringify({ ts: localTimestamp(), ...data }));
|
|
73
|
+
},
|
|
74
|
+
channelOut: (data) => {
|
|
75
|
+
channelOutWriter.write(JSON.stringify({ ts: localTimestamp(), ...data }));
|
|
66
76
|
}
|
|
67
77
|
};
|
package/dist/utils/npm-ops.js
CHANGED
|
@@ -83,12 +83,12 @@ export function getLocalVersion() {
|
|
|
83
83
|
return pkg.version;
|
|
84
84
|
}
|
|
85
85
|
/**
|
|
86
|
-
* 查询 npm registry
|
|
86
|
+
* 查询 npm registry 上指定包的最新版本。
|
|
87
87
|
* 超时 15 秒,失败返回 null。
|
|
88
88
|
*/
|
|
89
|
-
export function checkLatestVersion() {
|
|
89
|
+
export function checkLatestVersion(pkg = 'evolclaw') {
|
|
90
90
|
return new Promise((resolve) => {
|
|
91
|
-
execFile('npm', ['view',
|
|
91
|
+
execFile('npm', ['view', pkg, 'version'], { timeout: 15000 }, (err, stdout) => {
|
|
92
92
|
if (err) {
|
|
93
93
|
resolve(null);
|
|
94
94
|
return;
|
|
@@ -129,3 +129,35 @@ export async function tryUpgrade() {
|
|
|
129
129
|
}
|
|
130
130
|
return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
|
|
131
131
|
}
|
|
132
|
+
/**
|
|
133
|
+
* AUN SDK 升级流程:检查 → 比较 → 安装
|
|
134
|
+
* 仅在 SDK 已安装时检查升级,未安装则跳过。
|
|
135
|
+
*/
|
|
136
|
+
export async function tryUpgradeAunSdk(resolveAunCoreSdkPkg, AUN_CORE_SDK_PKG) {
|
|
137
|
+
if (isLinkedInstall()) {
|
|
138
|
+
return { status: 'skipped' };
|
|
139
|
+
}
|
|
140
|
+
const installed = resolveAunCoreSdkPkg();
|
|
141
|
+
if (!installed) {
|
|
142
|
+
return { status: 'skipped' }; // SDK not installed, skip
|
|
143
|
+
}
|
|
144
|
+
const localVer = installed.version;
|
|
145
|
+
const remoteVer = await checkLatestVersion(AUN_CORE_SDK_PKG);
|
|
146
|
+
if (!remoteVer) {
|
|
147
|
+
return { status: 'skipped', error: 'Failed to check remote version' };
|
|
148
|
+
}
|
|
149
|
+
if (compareVersions(localVer, remoteVer) >= 0) {
|
|
150
|
+
return { status: 'no-update', from: localVer };
|
|
151
|
+
}
|
|
152
|
+
let lastError;
|
|
153
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
154
|
+
try {
|
|
155
|
+
await npmInstallGlobal(`${AUN_CORE_SDK_PKG}@latest`);
|
|
156
|
+
return { status: 'upgraded', from: localVer, to: remoteVer };
|
|
157
|
+
}
|
|
158
|
+
catch (e) {
|
|
159
|
+
lastError = e.stderr || e.message || String(e);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return { status: 'failed', from: localVer, to: remoteVer, error: lastError };
|
|
163
|
+
}
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* killing the wrong process).
|
|
8
8
|
*/
|
|
9
9
|
import fs from 'fs';
|
|
10
|
-
import {
|
|
10
|
+
import { spawnSync } from 'child_process';
|
|
11
11
|
import { isWindows } from './cross-platform.js';
|
|
12
12
|
const isMacOS = process.platform === 'darwin';
|
|
13
13
|
/** 容差:2 秒(覆盖 macOS 秒级精度 + 时钟漂移) */
|
|
@@ -39,8 +39,6 @@ export function startTimeMatches(recorded, actual) {
|
|
|
39
39
|
}
|
|
40
40
|
// ── Linux ──
|
|
41
41
|
function getStartTimeLinux(pid) {
|
|
42
|
-
// /proc/<pid>/stat 第 22 字段:starttime(jiffies since boot)
|
|
43
|
-
// 注意 comm 字段(第 2 字段)含括号,可能包含空格,从最后一个 ')' 之后切
|
|
44
42
|
let stat;
|
|
45
43
|
try {
|
|
46
44
|
stat = fs.readFileSync(`/proc/${pid}/stat`, 'utf-8');
|
|
@@ -50,7 +48,6 @@ function getStartTimeLinux(pid) {
|
|
|
50
48
|
}
|
|
51
49
|
const tail = stat.slice(stat.lastIndexOf(')') + 2);
|
|
52
50
|
const fields = tail.split(' ');
|
|
53
|
-
// tail 从第 3 字段(state)开始,第 22 字段索引为 22 - 3 = 19
|
|
54
51
|
const starttimeJiffies = parseInt(fields[19], 10);
|
|
55
52
|
if (isNaN(starttimeJiffies))
|
|
56
53
|
return null;
|
|
@@ -63,62 +60,44 @@ function getStartTimeLinux(pid) {
|
|
|
63
60
|
}
|
|
64
61
|
if (isNaN(uptimeSec))
|
|
65
62
|
return null;
|
|
66
|
-
// CLK_TCK 在绝大多数 Linux 系统是 100
|
|
67
63
|
const clkTck = 100;
|
|
68
64
|
const bootTimeMs = Date.now() - uptimeSec * 1000;
|
|
69
65
|
return bootTimeMs + (starttimeJiffies / clkTck) * 1000;
|
|
70
66
|
}
|
|
71
67
|
// ── macOS ──
|
|
72
68
|
function getStartTimeMacOS(pid) {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
}).trim();
|
|
80
|
-
}
|
|
81
|
-
catch {
|
|
82
|
-
return null;
|
|
83
|
-
}
|
|
69
|
+
const result = spawnSync('ps', ['-p', String(pid), '-o', 'lstart='], {
|
|
70
|
+
encoding: 'utf-8',
|
|
71
|
+
timeout: 5000,
|
|
72
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
73
|
+
});
|
|
74
|
+
const out = result.stdout?.trim();
|
|
84
75
|
if (!out)
|
|
85
76
|
return null;
|
|
86
|
-
// 输出格式:"Fri May 16 08:00:00 2026"
|
|
87
77
|
const t = Date.parse(out);
|
|
88
78
|
return isNaN(t) ? null : t;
|
|
89
79
|
}
|
|
90
80
|
// ── Windows ──
|
|
91
81
|
function getStartTimeWindows(pid) {
|
|
92
|
-
// 优先 PowerShell Get-CimInstance(现代)
|
|
93
82
|
const fromPwsh = winPowerShellCreationDate(pid);
|
|
94
83
|
if (fromPwsh !== null)
|
|
95
84
|
return fromPwsh;
|
|
96
|
-
// 降级 wmic
|
|
97
85
|
return winWmicCreationDate(pid);
|
|
98
86
|
}
|
|
99
87
|
function winPowerShellCreationDate(pid) {
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
catch {
|
|
88
|
+
const result = spawnSync('powershell', [
|
|
89
|
+
'-NoProfile',
|
|
90
|
+
'-Command',
|
|
91
|
+
`(Get-CimInstance Win32_Process -Filter 'ProcessId=${pid}').CreationDate`,
|
|
92
|
+
], { encoding: 'utf-8', timeout: 8000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
93
|
+
const out = result.stdout?.trim();
|
|
94
|
+
if (!out)
|
|
109
95
|
return null;
|
|
110
|
-
}
|
|
111
96
|
return parseCimDate(out);
|
|
112
97
|
}
|
|
113
98
|
function winWmicCreationDate(pid) {
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
out = execFileSync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'CreationDate', '/value'], { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'] });
|
|
117
|
-
}
|
|
118
|
-
catch {
|
|
119
|
-
return null;
|
|
120
|
-
}
|
|
121
|
-
// wmic 输出 "CreationDate=20260516080000.000000+480"
|
|
99
|
+
const result = spawnSync('wmic', ['process', 'where', `ProcessId=${pid}`, 'get', 'CreationDate', '/value'], { encoding: 'utf-8', timeout: 5000, stdio: ['pipe', 'pipe', 'pipe'], windowsHide: true });
|
|
100
|
+
const out = result.stdout || '';
|
|
122
101
|
const m = out.match(/CreationDate=([^\r\n]+)/);
|
|
123
102
|
if (!m)
|
|
124
103
|
return null;
|
|
@@ -138,7 +117,6 @@ export function parseCimDate(s) {
|
|
|
138
117
|
const tzMin = parseInt(tz, 10);
|
|
139
118
|
if (isNaN(tzMin))
|
|
140
119
|
return null;
|
|
141
|
-
// CIM 的时间是"本地时区时间",要换成 UTC:UTC = local - offset
|
|
142
120
|
const localUtcMs = Date.UTC(+y, +mo - 1, +d, +h, +mi, +sec, Math.floor(+us / 1000));
|
|
143
121
|
return localUtcMs - tzMin * 60 * 1000;
|
|
144
122
|
}
|
package/dist/watch-msg.js
CHANGED
|
@@ -11,6 +11,7 @@ const CYAN = isTTY ? '\x1b[36m' : '';
|
|
|
11
11
|
const GREEN = isTTY ? '\x1b[32m' : '';
|
|
12
12
|
const BLUE = isTTY ? '\x1b[34m' : '';
|
|
13
13
|
const ORANGE = isTTY ? '\x1b[38;5;208m' : '';
|
|
14
|
+
const BG_SEL = isTTY ? '\x1b[48;5;236m' : ''; // dark gray background for selected row
|
|
14
15
|
// ==================== Helpers ====================
|
|
15
16
|
function visualWidth(s) {
|
|
16
17
|
const stripped = s.replace(/\x1b\[[0-9;]*m/g, '');
|
|
@@ -56,6 +57,14 @@ function formatTime(ts) {
|
|
|
56
57
|
const d = new Date(ts);
|
|
57
58
|
return `${String(d.getHours()).padStart(2, '0')}:${String(d.getMinutes()).padStart(2, '0')}`;
|
|
58
59
|
}
|
|
60
|
+
function formatDateTime(ts) {
|
|
61
|
+
const d = new Date(ts);
|
|
62
|
+
const mo = String(d.getMonth() + 1).padStart(2, '0');
|
|
63
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
64
|
+
const hh = String(d.getHours()).padStart(2, '0');
|
|
65
|
+
const mm = String(d.getMinutes()).padStart(2, '0');
|
|
66
|
+
return `${mo}-${dd} ${hh}:${mm}`;
|
|
67
|
+
}
|
|
59
68
|
function shortAid(aid) {
|
|
60
69
|
return aid.split('.')[0];
|
|
61
70
|
}
|
|
@@ -169,10 +178,12 @@ function renderScopePanel(state, width, height) {
|
|
|
169
178
|
const a = state.localAids[i];
|
|
170
179
|
const sel = isActive && i === state.scopeIndex;
|
|
171
180
|
const chosen = state.selectedLocalAid === a.aid;
|
|
172
|
-
const
|
|
181
|
+
const bg = sel ? BG_SEL : '';
|
|
182
|
+
const marker = sel ? `${bg}${CYAN}${BOLD}▸ ` : (chosen ? `${CYAN} ` : ' ');
|
|
173
183
|
const name = truncate(shortAid(a.aid), width - 4);
|
|
174
184
|
lines.push(padRight(`${marker}${name}${RST}`, width));
|
|
175
|
-
const
|
|
185
|
+
const statsBg = sel ? BG_SEL : '';
|
|
186
|
+
const stats = `${statsBg} ${DIM}↓${a.totalIn} ↑${a.totalOut} peers:${a.peerCount}${RST}`;
|
|
176
187
|
lines.push(padRight(stats, width));
|
|
177
188
|
if (lines.length < height)
|
|
178
189
|
lines.push(padRight('', width));
|
|
@@ -195,19 +206,22 @@ function renderStatsPanel(state, width, height) {
|
|
|
195
206
|
const now = Date.now();
|
|
196
207
|
// "All" item at index 0
|
|
197
208
|
const allSel = isActive && state.statsIndex === 0;
|
|
198
|
-
const
|
|
209
|
+
const allBg = allSel ? BG_SEL : '';
|
|
210
|
+
const allMarker = allSel ? `${allBg}${CYAN}${BOLD}▸ ` : ' ';
|
|
199
211
|
lines.push(padRight(`${allMarker}All (${state.peers.length} peers)${RST}`, width));
|
|
200
212
|
if (lines.length < height)
|
|
201
213
|
lines.push(padRight('', width));
|
|
202
214
|
for (let i = 0; i < state.peers.length && lines.length < height; i++) {
|
|
203
215
|
const p = state.peers[i];
|
|
204
216
|
const sel = isActive && state.statsIndex === i + 1;
|
|
205
|
-
const
|
|
217
|
+
const bg = sel ? BG_SEL : '';
|
|
218
|
+
const marker = sel ? `${bg}${CYAN}${BOLD}▸ ` : ' ';
|
|
206
219
|
const displayName = p.peerName || shortAid(p.peerId);
|
|
207
220
|
const name = truncate(displayName, width - 4);
|
|
208
221
|
lines.push(padRight(`${marker}${name}${RST}`, width));
|
|
222
|
+
const detailBg = sel ? BG_SEL : '';
|
|
209
223
|
const ago = p.lastAt ? formatTimeAgo(now - p.lastAt) : '-';
|
|
210
|
-
const detail =
|
|
224
|
+
const detail = `${detailBg} ${DIM}↓${p.inbound} ↑${p.outbound} ${ago}${RST}`;
|
|
211
225
|
lines.push(padRight(detail, width));
|
|
212
226
|
if (lines.length < height)
|
|
213
227
|
lines.push(padRight('', width));
|
|
@@ -231,10 +245,11 @@ function renderMessagesPanel(state, width, height) {
|
|
|
231
245
|
const msgWidth = width - 3;
|
|
232
246
|
for (let i = startIdx; i < endIdx; i++) {
|
|
233
247
|
const m = msgs[i];
|
|
234
|
-
const time =
|
|
248
|
+
const time = formatDateTime(m.ts);
|
|
235
249
|
const dir = m.dir === 'in' ? `${GREEN}↓${RST}` : `${BLUE}↑${RST}`;
|
|
236
|
-
const
|
|
237
|
-
const
|
|
250
|
+
const from = shortAid(m.from);
|
|
251
|
+
const to = shortAid(m.to);
|
|
252
|
+
const header = `${DIM}${time}${RST} ${dir} ${ORANGE}${from}${RST}${DIM}→${RST}${GREEN}${to}${RST}`;
|
|
238
253
|
const headerLine = padRight(header, msgWidth);
|
|
239
254
|
const sbIdx = lines.length - 1;
|
|
240
255
|
lines.push(`${headerLine} ${scrollbar[sbIdx] || ' '}`);
|
|
@@ -255,9 +270,9 @@ function renderMessagesPanel(state, width, height) {
|
|
|
255
270
|
function renderFrame(state) {
|
|
256
271
|
const cols = process.stdout.columns || 120;
|
|
257
272
|
const rows = (process.stdout.rows || 40) - 3;
|
|
258
|
-
const leftW = Math.max(
|
|
259
|
-
const midW = Math.max(
|
|
260
|
-
const rightW = Math.max(
|
|
273
|
+
const leftW = Math.max(20, Math.floor(cols * 0.20));
|
|
274
|
+
const midW = Math.max(24, Math.floor(cols * 0.22));
|
|
275
|
+
const rightW = Math.max(40, cols - leftW - midW - 4);
|
|
261
276
|
const bodyHeight = rows - 2;
|
|
262
277
|
const leftLines = renderScopePanel(state, leftW, bodyHeight);
|
|
263
278
|
const midLines = renderStatsPanel(state, midW, bodyHeight);
|
package/evolclaw-install-aun.md
CHANGED
|
@@ -19,6 +19,8 @@ EvolClaw 是一个开源的 AI Agent 网关,将 Claude/Codex 等 AI 后端连
|
|
|
19
19
|
|
|
20
20
|
## 安装步骤
|
|
21
21
|
|
|
22
|
+
> **⚠️ 给执行安装的 AI Agent**:本文档中所有破坏性命令(`evolclaw stop` / `evolclaw restart` / `rm -rf` / `evolclaw aid delete` 等)**必须先 `AskUserQuestion` 等用户明确回复后才能执行**,不要因为已经发出确认提示就默认 yes 自动跑。用户在打字的几秒内决定 "等等先别停",比事后回滚要省事得多。
|
|
23
|
+
|
|
22
24
|
### 步骤 1:检查环境
|
|
23
25
|
|
|
24
26
|
```bash
|
|
@@ -169,23 +171,33 @@ AUN 通道由 `agent.aid` 隐式驱动,不需要单独配 channel——`evolcl
|
|
|
169
171
|
cat $EVOLCLAW_HOME/agents/<aid>/config.json
|
|
170
172
|
```
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
检查关键字段:
|
|
173
175
|
|
|
174
176
|
- `aid` — 你的 AID
|
|
175
177
|
- `owners` — Owner AID 数组(可为空,留给自动绑定)
|
|
176
178
|
- `active_baseagent` — `claude` / `codex` / `gemini`
|
|
177
179
|
- `projects.defaultPath` — 项目路径(目录需存在)
|
|
178
|
-
- `channels` —
|
|
180
|
+
- `channels` — 数组,初始为空(AUN 隐式上线,无需在此配置)
|
|
179
181
|
- `initialized` — 应为 `false`(首次连接成功后由系统更新为 `true`)
|
|
180
182
|
|
|
183
|
+
> 注:config.json 还包含 `$schema_version`、`enabled`、`baseagents`、`chatmode`、`dispatch` 等字段,由 `agent new` 自动填入,无需手动检查。
|
|
184
|
+
|
|
181
185
|
如发现缺失或异常,向用户说明并提供修复方案。
|
|
182
186
|
|
|
183
187
|
### 步骤 9:启动服务
|
|
184
188
|
|
|
189
|
+
首次安装(服务未运行):
|
|
190
|
+
|
|
185
191
|
```bash
|
|
186
192
|
evolclaw start
|
|
187
193
|
```
|
|
188
194
|
|
|
195
|
+
若服务已在运行(升级/重装场景,步骤 3 已停止旧进程):
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
evolclaw restart
|
|
199
|
+
```
|
|
200
|
+
|
|
189
201
|
### 步骤 10:验证运行状态
|
|
190
202
|
|
|
191
203
|
```bash
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 文档查阅指南
|
|
2
|
+
|
|
3
|
+
## 查阅流程
|
|
4
|
+
|
|
5
|
+
1. 先看 `$KITS_RULES`(自动加载的 8 个规则文件)了解机制骨架
|
|
6
|
+
2. 需要详细信息时,按 `INDEX.md` 找到对应文档路径
|
|
7
|
+
3. Read 对应文档
|
|
8
|
+
|
|
9
|
+
## 路径解析
|
|
10
|
+
|
|
11
|
+
文档中用 `$名称` 引用路径。解析步骤:
|
|
12
|
+
1. 查 `$KITS_RULES/01-entry.md` 的路径体系速查表
|
|
13
|
+
2. 如需完整定义,Read `$KITS_DOCS/path-registry.md`
|
|
14
|
+
3. 如需运行时实际值,Read `$ECK/path-registry.md`
|
|
15
|
+
|
|
16
|
+
## 不要做的事
|
|
17
|
+
|
|
18
|
+
- 不要一次性加载所有文档——按需逐个 Read
|
|
19
|
+
- 不要猜测路径——查注册表
|
|
20
|
+
- 不要在当前会话输出对外消息——用 CLI
|