evolclaw 2.8.3 → 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 +21 -12
- package/bin/ec.js +29 -0
- package/dist/agents/baseagent-normalize.js +19 -0
- package/dist/agents/claude-runner.js +108 -46
- package/dist/agents/codex-runner.js +13 -14
- package/dist/agents/gemini-runner.js +15 -17
- package/dist/agents/kit-renderer.js +281 -0
- package/dist/agents/resolve.js +134 -0
- package/dist/aun/aid/agentmd.js +186 -0
- package/dist/aun/aid/client.js +134 -0
- package/dist/aun/aid/identity.js +159 -0
- package/dist/aun/aid/index.js +3 -0
- package/dist/aun/aid/lifecycle-log.js +33 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/msg/group.js +293 -0
- package/dist/aun/msg/index.js +4 -0
- package/dist/aun/msg/p2p.js +147 -0
- package/dist/aun/msg/payload-type.js +27 -0
- package/dist/aun/msg/upload.js +98 -0
- package/dist/aun/outbox.js +138 -0
- package/dist/aun/rpc/caller.js +42 -0
- package/dist/aun/rpc/connection.js +34 -0
- package/dist/aun/rpc/index.js +2 -0
- package/dist/aun/storage/download.js +29 -0
- package/dist/aun/storage/index.js +3 -0
- package/dist/aun/storage/manage.js +10 -0
- package/dist/aun/storage/upload.js +35 -0
- package/dist/channels/aun.js +1340 -349
- package/dist/channels/dingtalk.js +59 -5
- package/dist/channels/feishu.js +381 -32
- package/dist/channels/qqbot.js +68 -12
- package/dist/channels/wechat.js +63 -4
- package/dist/channels/wecom.js +59 -5
- package/dist/cli/agent.js +800 -0
- package/dist/cli/bench.js +1219 -0
- package/dist/cli/index.js +4513 -0
- package/dist/{utils → cli}/init-channel.js +211 -621
- package/dist/cli/init.js +178 -0
- 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 +645 -0
- package/dist/core/{agent-loader.js → baseagent-loader.js} +6 -12
- package/dist/core/channel-loader.js +176 -12
- package/dist/core/command-handler.js +883 -848
- package/dist/core/evolagent-registry.js +191 -371
- package/dist/core/evolagent.js +202 -238
- package/dist/core/interaction-router.js +52 -5
- package/dist/core/message/im-renderer.js +486 -0
- package/dist/core/message/items-formatter.js +68 -0
- package/dist/core/message/message-bridge.js +109 -56
- package/dist/core/message/message-log.js +93 -0
- package/dist/core/message/message-processor.js +430 -212
- package/dist/core/message/message-queue.js +13 -6
- package/dist/core/permission.js +116 -11
- package/dist/core/session/adapters/codex-session-file-adapter.js +24 -2
- package/dist/core/session/session-fs-store.js +230 -0
- package/dist/core/session/session-manager.js +740 -777
- package/dist/core/session/session-mapper.js +87 -0
- package/dist/core/trigger/manager.js +122 -0
- package/dist/core/trigger/parser.js +128 -0
- package/dist/core/trigger/scheduler.js +224 -0
- 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 +560 -283
- package/dist/ipc.js +49 -0
- package/dist/net-check.js +640 -0
- package/dist/paths.js +73 -9
- package/dist/types.js +8 -2
- package/dist/utils/aid-lifecycle-log.js +33 -0
- package/dist/utils/atomic-write.js +89 -0
- package/dist/utils/channel-helpers.js +46 -0
- package/dist/utils/cross-platform.js +17 -26
- package/dist/utils/error-utils.js +10 -2
- package/dist/utils/instance-registry.js +434 -0
- package/dist/utils/log-writer.js +217 -0
- package/dist/utils/logger.js +34 -77
- package/dist/utils/media-cache.js +23 -0
- package/dist/utils/npm-ops.js +163 -0
- package/dist/utils/process-introspect.js +122 -0
- package/dist/utils/stats.js +192 -0
- package/dist/watch-msg.js +544 -0
- package/evolclaw-install-aun.md +127 -47
- 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/aun.md +25 -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/AGENT_CMD.md +31 -0
- package/kits/docs/evolclaw/MSG_GROUP.md +30 -0
- package/kits/docs/evolclaw/MSG_PRIVATE.md +25 -0
- package/kits/docs/evolclaw/self-summary.md +29 -0
- package/kits/docs/evolclaw/tools.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/identity/identity-tools.md +26 -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 +10 -6
- package/data/evolclaw.sample.json +0 -60
- package/dist/agents/templates.js +0 -122
- package/dist/channels/aun-ops.js +0 -275
- package/dist/cli.js +0 -2178
- package/dist/config.js +0 -591
- package/dist/core/agent-registry.js +0 -450
- package/dist/core/evolagent-schema.js +0 -72
- package/dist/core/message/stream-flusher.js +0 -238
- package/dist/core/message/thought-emitter.js +0 -162
- package/dist/core/reload-hooks.js +0 -87
- package/dist/prompts/templates.js +0 -122
- package/dist/templates/prompts.md +0 -104
- package/dist/templates/skills.md +0 -66
- package/dist/utils/channel-fingerprint.js +0 -59
- package/dist/utils/error-dict.js +0 -63
- package/dist/utils/format.js +0 -32
- package/dist/utils/init.js +0 -645
- package/dist/utils/migrate-project.js +0 -122
- package/dist/utils/reload-hooks.js +0 -87
- package/dist/utils/stats-collector.js +0 -99
- package/dist/utils/upgrade.js +0 -100
package/dist/utils/init.js
DELETED
|
@@ -1,645 +0,0 @@
|
|
|
1
|
-
import fs from 'fs';
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import os from 'os';
|
|
4
|
-
import readline from 'readline';
|
|
5
|
-
import { createRequire } from 'module';
|
|
6
|
-
import { execFileSync } from 'child_process';
|
|
7
|
-
import { promisify } from 'util';
|
|
8
|
-
import { execFile } from 'child_process';
|
|
9
|
-
import { resolveRoot, resolvePaths, ensureDataDirs, getPackageRoot } from '../paths.js';
|
|
10
|
-
import { isWindows, commandExists } from './cross-platform.js';
|
|
11
|
-
const execFileAsync = promisify(execFile);
|
|
12
|
-
// ==================== Helpers ====================
|
|
13
|
-
function ask(rl, question) {
|
|
14
|
-
return new Promise(resolve => rl.question(question, resolve));
|
|
15
|
-
}
|
|
16
|
-
async function npmInstallGlobal(pkg) {
|
|
17
|
-
try {
|
|
18
|
-
await execFileAsync('npm', ['install', '-g', pkg], { timeout: 120000 });
|
|
19
|
-
}
|
|
20
|
-
catch (e) {
|
|
21
|
-
if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES')) {
|
|
22
|
-
if (isWindows) {
|
|
23
|
-
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
24
|
-
}
|
|
25
|
-
await execFileAsync('sudo', ['npm', 'install', '-g', pkg], { timeout: 120000 });
|
|
26
|
-
}
|
|
27
|
-
else {
|
|
28
|
-
throw e;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
async function sudoExec(cmd, args) {
|
|
33
|
-
// 让 n 安装到当前 node 所在的 prefix 目录
|
|
34
|
-
const env = { ...process.env };
|
|
35
|
-
if (cmd === 'n' && !env.N_PREFIX) {
|
|
36
|
-
const nodePrefix = process.config.variables?.node_prefix;
|
|
37
|
-
if (nodePrefix)
|
|
38
|
-
env.N_PREFIX = nodePrefix;
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
await execFileAsync(cmd, args, { timeout: 120000, env });
|
|
42
|
-
}
|
|
43
|
-
catch (e) {
|
|
44
|
-
if (e.stderr?.includes('EACCES') || e.message?.includes('EACCES') || e.code === 'EACCES') {
|
|
45
|
-
if (isWindows) {
|
|
46
|
-
throw new Error('权限不足。请以管理员身份运行 PowerShell 或 CMD,然后重试');
|
|
47
|
-
}
|
|
48
|
-
await execFileAsync('sudo', [cmd, ...args], { timeout: 120000, env });
|
|
49
|
-
}
|
|
50
|
-
else {
|
|
51
|
-
throw e;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
// ==================== Environment Check ====================
|
|
56
|
-
async function checkEnvironment(rl) {
|
|
57
|
-
console.log('🔍 环境检查...\n');
|
|
58
|
-
// Node.js >= 22
|
|
59
|
-
const nodeVer = parseInt(process.versions.node.split('.')[0], 10);
|
|
60
|
-
if (nodeVer >= 22) {
|
|
61
|
-
console.log(` ✓ Node.js v${process.versions.node}`);
|
|
62
|
-
}
|
|
63
|
-
else {
|
|
64
|
-
console.log(` ✗ Node.js v${process.versions.node} — 需要 >= 22(node:sqlite 依赖)`);
|
|
65
|
-
// 检测 nvm
|
|
66
|
-
// 检测 bash 是否存在(nvm 和 n 都依赖 bash)
|
|
67
|
-
const hasBash = commandExists('bash');
|
|
68
|
-
if (!hasBash) {
|
|
69
|
-
if (isWindows) {
|
|
70
|
-
console.log(' ⚠ Windows 环境,请从 https://nodejs.org 下载安装 Node.js 22+');
|
|
71
|
-
}
|
|
72
|
-
else {
|
|
73
|
-
console.log(' ⚠ 当前环境没有 bash(Alpine 容器?),无法自动升级 Node.js');
|
|
74
|
-
console.log(' → 请手动升级: apk add nodejs-current 或重建容器使用 node:22-alpine');
|
|
75
|
-
}
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
const hasNvm = !!process.env.NVM_DIR && fs.existsSync(process.env.NVM_DIR);
|
|
79
|
-
if (hasNvm) {
|
|
80
|
-
const answer = (await ask(rl, ' → 是否通过 nvm 升级到 Node.js 22?[Y/n] ')).trim().toLowerCase();
|
|
81
|
-
if (answer === 'n' || answer === 'no') {
|
|
82
|
-
console.log(' 已取消');
|
|
83
|
-
return false;
|
|
84
|
-
}
|
|
85
|
-
console.log(' 正在升级 Node.js...');
|
|
86
|
-
try {
|
|
87
|
-
const nvmDir = process.env.NVM_DIR;
|
|
88
|
-
const { stdout } = await execFileAsync('bash', ['-c', `source "${nvmDir}/nvm.sh" && nvm install 22 && nvm alias default 22`], { timeout: 120000 });
|
|
89
|
-
console.log(stdout.trim().split('\n').map(l => ` ${l}`).join('\n'));
|
|
90
|
-
console.log(' ✓ Node.js 升级完成');
|
|
91
|
-
console.log(' → 请打开新终端后重新运行 evolclaw init');
|
|
92
|
-
return false;
|
|
93
|
-
}
|
|
94
|
-
catch (e) {
|
|
95
|
-
console.log(` ✗ 升级失败: ${e.message?.slice(0, 200) || e}`);
|
|
96
|
-
return false;
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
else {
|
|
100
|
-
// 检测 n
|
|
101
|
-
const hasN = commandExists('n');
|
|
102
|
-
if (hasN) {
|
|
103
|
-
const answer = (await ask(rl, ' → 是否通过 n 升级到 Node.js 22?[Y/n] ')).trim().toLowerCase();
|
|
104
|
-
if (answer === 'n' || answer === 'no') {
|
|
105
|
-
console.log(' 已取消');
|
|
106
|
-
return false;
|
|
107
|
-
}
|
|
108
|
-
console.log(' 正在升级 Node.js...');
|
|
109
|
-
try {
|
|
110
|
-
await sudoExec('n', ['22']);
|
|
111
|
-
console.log(' ✓ Node.js 升级完成');
|
|
112
|
-
console.log(' → 请打开新终端后重新运行 evolclaw init');
|
|
113
|
-
return false;
|
|
114
|
-
}
|
|
115
|
-
catch (e) {
|
|
116
|
-
console.log(` ✗ 升级失败: ${e.message?.slice(0, 200) || e}`);
|
|
117
|
-
return false;
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
// 无版本管理器,用 npm 安装 n 再升级
|
|
121
|
-
const answer = (await ask(rl, ' → 是否通过 npm 安装 n 并升级到 Node.js 22?[Y/n] ')).trim().toLowerCase();
|
|
122
|
-
if (answer === 'n' || answer === 'no') {
|
|
123
|
-
console.log(' 已取消');
|
|
124
|
-
return false;
|
|
125
|
-
}
|
|
126
|
-
console.log(' 正在安装 n...');
|
|
127
|
-
try {
|
|
128
|
-
await npmInstallGlobal('n');
|
|
129
|
-
console.log(' 正在升级 Node.js...');
|
|
130
|
-
await sudoExec('n', ['22']);
|
|
131
|
-
console.log(' ✓ Node.js 升级完成');
|
|
132
|
-
console.log(' → 请打开新终端后重新运行 evolclaw init');
|
|
133
|
-
return false;
|
|
134
|
-
}
|
|
135
|
-
catch (e) {
|
|
136
|
-
console.log(` ✗ 升级失败: ${e.message?.slice(0, 200) || e}`);
|
|
137
|
-
return false;
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
// claude CLI >= 2.1.32
|
|
142
|
-
const MIN_CLAUDE_VER = [2, 1, 32];
|
|
143
|
-
const claudeInstalled = commandExists('claude');
|
|
144
|
-
if (claudeInstalled) {
|
|
145
|
-
try {
|
|
146
|
-
const verOutput = execFileSync('claude', ['--version'], { encoding: 'utf-8' }).trim();
|
|
147
|
-
const verMatch = verOutput.match(/^(\d+\.\d+\.\d+)/);
|
|
148
|
-
if (verMatch) {
|
|
149
|
-
const parts = verMatch[1].split('.').map(Number);
|
|
150
|
-
const isOk = parts[0] > MIN_CLAUDE_VER[0]
|
|
151
|
-
|| (parts[0] === MIN_CLAUDE_VER[0] && parts[1] > MIN_CLAUDE_VER[1])
|
|
152
|
-
|| (parts[0] === MIN_CLAUDE_VER[0] && parts[1] === MIN_CLAUDE_VER[1] && parts[2] >= MIN_CLAUDE_VER[2]);
|
|
153
|
-
if (isOk) {
|
|
154
|
-
console.log(` ✓ claude CLI v${verMatch[1]}`);
|
|
155
|
-
}
|
|
156
|
-
else {
|
|
157
|
-
console.log(` ✗ claude CLI v${verMatch[1]} — 需要 >= ${MIN_CLAUDE_VER.join('.')}`);
|
|
158
|
-
const answer = (await ask(rl, ' → 是否升级 claude CLI?[Y/n] ')).trim().toLowerCase();
|
|
159
|
-
if (answer === 'n' || answer === 'no') {
|
|
160
|
-
console.log(' 已取消');
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
console.log(' 正在升级 claude CLI...');
|
|
164
|
-
try {
|
|
165
|
-
await npmInstallGlobal('@anthropic-ai/claude-code@latest');
|
|
166
|
-
console.log(' ✓ claude CLI 升级完成');
|
|
167
|
-
}
|
|
168
|
-
catch (e) {
|
|
169
|
-
console.log(` ✗ 升级失败: ${e.message?.slice(0, 200) || e}`);
|
|
170
|
-
return false;
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
else {
|
|
175
|
-
console.log(` ✓ claude CLI (${verOutput})`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// claude command exists but --version failed
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
else {
|
|
183
|
-
console.log(' ✗ claude CLI 未找到');
|
|
184
|
-
console.log(' → 请先安装: npm install -g @anthropic-ai/claude-code');
|
|
185
|
-
return false;
|
|
186
|
-
}
|
|
187
|
-
// Agent SDK 检查:claude-agent-sdk / codex-sdk,至少需要一个
|
|
188
|
-
const MIN_CLAUDE_SDK = [0, 2, 75];
|
|
189
|
-
let hasClaudeSdk = false;
|
|
190
|
-
let hasCodexSdk = false;
|
|
191
|
-
// Check claude-agent-sdk
|
|
192
|
-
try {
|
|
193
|
-
const esmRequire = createRequire(import.meta.url);
|
|
194
|
-
const sdkEntry = esmRequire.resolve('@anthropic-ai/claude-agent-sdk');
|
|
195
|
-
const sdkPkgPath = path.join(path.dirname(sdkEntry), 'package.json');
|
|
196
|
-
const sdkPkg = JSON.parse(fs.readFileSync(sdkPkgPath, 'utf-8'));
|
|
197
|
-
const sdkVer = sdkPkg.version;
|
|
198
|
-
const parts = sdkVer.split('.').map(Number);
|
|
199
|
-
const sdkOk = parts[0] > MIN_CLAUDE_SDK[0]
|
|
200
|
-
|| (parts[0] === MIN_CLAUDE_SDK[0] && parts[1] > MIN_CLAUDE_SDK[1])
|
|
201
|
-
|| (parts[0] === MIN_CLAUDE_SDK[0] && parts[1] === MIN_CLAUDE_SDK[1] && parts[2] >= MIN_CLAUDE_SDK[2]);
|
|
202
|
-
if (sdkOk) {
|
|
203
|
-
console.log(` ✓ claude-agent-sdk v${sdkVer}`);
|
|
204
|
-
hasClaudeSdk = true;
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
console.log(` ✗ claude-agent-sdk v${sdkVer} — 需要 >= ${MIN_CLAUDE_SDK.join('.')}`);
|
|
208
|
-
const answer = (await ask(rl, ' → 是否升级 claude-agent-sdk?[Y/n] ')).trim().toLowerCase();
|
|
209
|
-
if (answer !== 'n' && answer !== 'no') {
|
|
210
|
-
console.log(' 正在升级 claude-agent-sdk...');
|
|
211
|
-
try {
|
|
212
|
-
await npmInstallGlobal('@anthropic-ai/claude-agent-sdk@latest');
|
|
213
|
-
console.log(' ✓ claude-agent-sdk 升级完成');
|
|
214
|
-
hasClaudeSdk = true;
|
|
215
|
-
}
|
|
216
|
-
catch (e) {
|
|
217
|
-
console.log(` ✗ 升级失败: ${e.message?.slice(0, 200) || e}`);
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
catch {
|
|
223
|
-
console.log(' - claude-agent-sdk 未安装');
|
|
224
|
-
}
|
|
225
|
-
// Check @openai/codex-sdk (ESM-only, cannot use require.resolve)
|
|
226
|
-
try {
|
|
227
|
-
const codexPkgPath = path.join(getPackageRoot(), 'node_modules', '@openai', 'codex-sdk', 'package.json');
|
|
228
|
-
if (fs.existsSync(codexPkgPath)) {
|
|
229
|
-
const codexPkg = JSON.parse(fs.readFileSync(codexPkgPath, 'utf-8'));
|
|
230
|
-
console.log(` ✓ codex-sdk v${codexPkg.version}`);
|
|
231
|
-
hasCodexSdk = true;
|
|
232
|
-
}
|
|
233
|
-
else {
|
|
234
|
-
console.log(' - codex-sdk 未安装');
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
catch {
|
|
238
|
-
console.log(' - codex-sdk 未安装');
|
|
239
|
-
}
|
|
240
|
-
if (!hasClaudeSdk && !hasCodexSdk) {
|
|
241
|
-
console.log('\n ✗ 需要至少安装一个 Agent SDK:claude-agent-sdk 或 codex-sdk');
|
|
242
|
-
const answer = (await ask(rl, ' → 是否安装 claude-agent-sdk?[Y/n] ')).trim().toLowerCase();
|
|
243
|
-
if (answer === 'n' || answer === 'no') {
|
|
244
|
-
console.log(' 已取消');
|
|
245
|
-
return false;
|
|
246
|
-
}
|
|
247
|
-
console.log(' 正在安装 claude-agent-sdk...');
|
|
248
|
-
try {
|
|
249
|
-
await npmInstallGlobal('@anthropic-ai/claude-agent-sdk@latest');
|
|
250
|
-
console.log(' ✓ claude-agent-sdk 安装完成');
|
|
251
|
-
}
|
|
252
|
-
catch (e) {
|
|
253
|
-
console.log(` ✗ 安装失败: ${e.message?.slice(0, 200) || e}`);
|
|
254
|
-
return false;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
console.log('');
|
|
258
|
-
return true;
|
|
259
|
-
}
|
|
260
|
-
// ==================== Shell Profile ====================
|
|
261
|
-
function setupEnvVar(home) {
|
|
262
|
-
if (isWindows) {
|
|
263
|
-
// Windows: use setx to set user environment variable
|
|
264
|
-
try {
|
|
265
|
-
execFileSync('setx', ['EVOLCLAW_HOME', home], { encoding: 'utf-8', stdio: 'pipe' });
|
|
266
|
-
console.log(` ✓ 已设置用户环境变量: EVOLCLAW_HOME=${home}`);
|
|
267
|
-
console.log(' ⚠ 请重新打开终端使其生效');
|
|
268
|
-
}
|
|
269
|
-
catch (e) {
|
|
270
|
-
console.log(` ⚠ 设置环境变量失败: ${e.message?.slice(0, 100) || e}`);
|
|
271
|
-
console.log(` → 请手动设置环境变量 EVOLCLAW_HOME=${home}`);
|
|
272
|
-
}
|
|
273
|
-
return;
|
|
274
|
-
}
|
|
275
|
-
const exportLine = `export EVOLCLAW_HOME="${home}"`;
|
|
276
|
-
const candidates = [
|
|
277
|
-
path.join(os.homedir(), '.zshrc'),
|
|
278
|
-
path.join(os.homedir(), '.bashrc'),
|
|
279
|
-
path.join(os.homedir(), '.bash_profile'),
|
|
280
|
-
];
|
|
281
|
-
let written = false;
|
|
282
|
-
for (const profilePath of candidates) {
|
|
283
|
-
if (!fs.existsSync(profilePath))
|
|
284
|
-
continue;
|
|
285
|
-
const content = fs.readFileSync(profilePath, 'utf-8');
|
|
286
|
-
if (content.includes('EVOLCLAW_HOME')) {
|
|
287
|
-
console.log(` ✓ EVOLCLAW_HOME 已在 ${profilePath} 中配置`);
|
|
288
|
-
written = true;
|
|
289
|
-
continue;
|
|
290
|
-
}
|
|
291
|
-
fs.appendFileSync(profilePath, `\n# EvolClaw\n${exportLine}\n`);
|
|
292
|
-
console.log(` ✓ 已写入 ${profilePath}: ${exportLine}`);
|
|
293
|
-
written = true;
|
|
294
|
-
}
|
|
295
|
-
if (!written) {
|
|
296
|
-
const shell = process.env.SHELL || '/bin/bash';
|
|
297
|
-
const profilePath = shell.endsWith('zsh')
|
|
298
|
-
? path.join(os.homedir(), '.zshrc')
|
|
299
|
-
: path.join(os.homedir(), '.bashrc');
|
|
300
|
-
fs.appendFileSync(profilePath, `\n# EvolClaw\n${exportLine}\n`);
|
|
301
|
-
console.log(` ✓ 已写入 ${profilePath}: ${exportLine}`);
|
|
302
|
-
}
|
|
303
|
-
console.log(' ⚠ 请重新打开终端或执行 source 使其生效');
|
|
304
|
-
}
|
|
305
|
-
// ==================== Feishu Manual Input ====================
|
|
306
|
-
async function initFeishuManual(rl, config) {
|
|
307
|
-
let appId = '';
|
|
308
|
-
while (!appId) {
|
|
309
|
-
appId = (await ask(rl, ' 飞书 App ID: ')).trim();
|
|
310
|
-
if (!appId)
|
|
311
|
-
console.log(' ⚠ 不能为空');
|
|
312
|
-
}
|
|
313
|
-
let appSecret = '';
|
|
314
|
-
while (!appSecret) {
|
|
315
|
-
appSecret = (await ask(rl, ' 飞书 App Secret: ')).trim();
|
|
316
|
-
if (!appSecret)
|
|
317
|
-
console.log(' ⚠ 不能为空');
|
|
318
|
-
}
|
|
319
|
-
console.log(' 正在验证飞书凭证...');
|
|
320
|
-
try {
|
|
321
|
-
const lark = await import('@larksuiteoapi/node-sdk');
|
|
322
|
-
const client = new lark.Client({ appId, appSecret });
|
|
323
|
-
const res = await client.auth.tenantAccessToken.internal({
|
|
324
|
-
data: { app_id: appId, app_secret: appSecret },
|
|
325
|
-
});
|
|
326
|
-
if (res.code === 0) {
|
|
327
|
-
console.log(' ✓ 飞书凭证验证通过');
|
|
328
|
-
}
|
|
329
|
-
else {
|
|
330
|
-
console.log(` ✗ 飞书凭证验证失败: ${res.msg}`);
|
|
331
|
-
const answer = (await ask(rl, ' → 是否继续?[y/N] ')).trim().toLowerCase();
|
|
332
|
-
if (answer !== 'y' && answer !== 'yes') {
|
|
333
|
-
return false;
|
|
334
|
-
}
|
|
335
|
-
}
|
|
336
|
-
}
|
|
337
|
-
catch (e) {
|
|
338
|
-
console.log(` ⚠ 飞书凭证验证跳过: ${e.message?.slice(0, 100) || e}`);
|
|
339
|
-
}
|
|
340
|
-
config.channels.feishu.appId = appId;
|
|
341
|
-
config.channels.feishu.appSecret = appSecret;
|
|
342
|
-
config.channels.feishu.enabled = true;
|
|
343
|
-
return true;
|
|
344
|
-
}
|
|
345
|
-
// ==================== AUN Environment Check ====================
|
|
346
|
-
// Moved to init-channel.ts
|
|
347
|
-
// ==================== Rich Content Renderer ====================
|
|
348
|
-
async function offerRichContentRenderer(rl, config) {
|
|
349
|
-
const answer = (await ask(rl, '\n是否启用 LaTeX + Mermaid 渲染模块(约 35MB)?[y/N] ')).trim().toLowerCase();
|
|
350
|
-
const enableRich = answer === 'y' || answer === 'yes';
|
|
351
|
-
// 记录用户选择到全局配置
|
|
352
|
-
config.enableRichContent = enableRich;
|
|
353
|
-
if (!enableRich) {
|
|
354
|
-
console.log(' ✓ 已跳过富内容渲染模块安装');
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
console.log(' 正在安装 katex 和 mermaid(可能需要 1-2 分钟)...');
|
|
358
|
-
try {
|
|
359
|
-
await npmInstallGlobal('katex');
|
|
360
|
-
await npmInstallGlobal('mermaid');
|
|
361
|
-
console.log(' ✓ LaTeX + Mermaid 渲染模块安装完成');
|
|
362
|
-
}
|
|
363
|
-
catch (e) {
|
|
364
|
-
const msg = e.message || '';
|
|
365
|
-
if (e.killed || msg.includes('ETIMEDOUT') || msg.includes('timed out')) {
|
|
366
|
-
console.log(' ✗ 安装超时,网络可能较慢');
|
|
367
|
-
}
|
|
368
|
-
else {
|
|
369
|
-
console.log(` ✗ 安装失败: ${msg.slice(0, 200)}`);
|
|
370
|
-
}
|
|
371
|
-
console.log(' → 可稍后手动安装: npm install -g katex mermaid');
|
|
372
|
-
// 安装失败时,将配置设为 false
|
|
373
|
-
config.enableRichContent = false;
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
|
-
// ==================== AUN AID Helpers ====================
|
|
377
|
-
// Moved to init-channel.ts
|
|
378
|
-
// ==================== Main ====================
|
|
379
|
-
export async function cmdInit(options) {
|
|
380
|
-
const p = resolvePaths();
|
|
381
|
-
ensureDataDirs();
|
|
382
|
-
if (fs.existsSync(p.pid)) {
|
|
383
|
-
const pid = parseInt(fs.readFileSync(p.pid, 'utf-8').trim(), 10);
|
|
384
|
-
try {
|
|
385
|
-
process.kill(pid, 0);
|
|
386
|
-
console.log(`❌ EvolClaw 正在运行 (PID: ${pid}),请先执行 evolclaw stop`);
|
|
387
|
-
return;
|
|
388
|
-
}
|
|
389
|
-
catch { }
|
|
390
|
-
}
|
|
391
|
-
const sampleSrc = path.join(getPackageRoot(), 'data', 'evolclaw.sample.json');
|
|
392
|
-
if (!fs.existsSync(sampleSrc)) {
|
|
393
|
-
console.log(`❌ 找不到示例配置: ${sampleSrc}`);
|
|
394
|
-
return;
|
|
395
|
-
}
|
|
396
|
-
// 非交互式模式
|
|
397
|
-
if (options?.nonInteractive) {
|
|
398
|
-
const config = JSON.parse(fs.readFileSync(sampleSrc, 'utf-8'));
|
|
399
|
-
const defaultPath = options.defaultPath || path.join(os.homedir(), 'projects', 'default');
|
|
400
|
-
if (!fs.existsSync(defaultPath))
|
|
401
|
-
fs.mkdirSync(defaultPath, { recursive: true });
|
|
402
|
-
config.projects.defaultPath = defaultPath;
|
|
403
|
-
config.projects.list = { [path.basename(defaultPath)]: defaultPath };
|
|
404
|
-
if (options.channel === 'aun' && !options.aunAid) {
|
|
405
|
-
throw new Error('--aun-aid is required for AUN channel (e.g. --aun-aid mybot.agentid.pub)');
|
|
406
|
-
}
|
|
407
|
-
if (options.channel === 'aun' && options.aunAid) {
|
|
408
|
-
const { ensureAunSdk, aidCreate, agentmdPut, buildInitialAgentMd } = await import('../channels/aun-ops.js');
|
|
409
|
-
await ensureAunSdk();
|
|
410
|
-
const aunPath = path.join(os.homedir(), '.aun');
|
|
411
|
-
const aidDir = path.join(aunPath, 'AIDs', options.aunAid);
|
|
412
|
-
if (!fs.existsSync(path.join(aidDir, 'private'))) {
|
|
413
|
-
const result = await aidCreate(options.aunAid);
|
|
414
|
-
try {
|
|
415
|
-
const content = buildInitialAgentMd({ aid: options.aunAid });
|
|
416
|
-
try {
|
|
417
|
-
await agentmdPut(content, { aid: options.aunAid, client: result.client });
|
|
418
|
-
}
|
|
419
|
-
catch (e) {
|
|
420
|
-
console.warn(`⚠ agent.md 网络发布失败(首次连接将自动重试): ${String(e?.message || e).slice(0, 100)}`);
|
|
421
|
-
fs.mkdirSync(aidDir, { recursive: true });
|
|
422
|
-
fs.writeFileSync(path.join(aidDir, 'agent.md'), content, 'utf-8');
|
|
423
|
-
}
|
|
424
|
-
if (!fs.existsSync(path.join(aidDir, 'agent.md'))) {
|
|
425
|
-
throw new Error(`agent.md 写入校验失败: ${path.join(aidDir, 'agent.md')}`);
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
finally {
|
|
429
|
-
try {
|
|
430
|
-
await result.client.close();
|
|
431
|
-
}
|
|
432
|
-
catch { }
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
config.channels.aun = {
|
|
436
|
-
enabled: true,
|
|
437
|
-
aid: options.aunAid,
|
|
438
|
-
...(options.aunOwner && { owner: options.aunOwner }),
|
|
439
|
-
};
|
|
440
|
-
config.channels.defaultChannel = 'aun';
|
|
441
|
-
}
|
|
442
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
443
|
-
console.log(`✓ 已创建配置文件: ${p.config}`);
|
|
444
|
-
setupEnvVar(resolveRoot());
|
|
445
|
-
return;
|
|
446
|
-
}
|
|
447
|
-
// 交互式模式
|
|
448
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
449
|
-
try {
|
|
450
|
-
if (fs.existsSync(p.config)) {
|
|
451
|
-
const answer = (await ask(rl, `配置文件已存在: ${p.config}\n 是否重新初始化?[y/N] `)).trim().toLowerCase();
|
|
452
|
-
if (answer !== 'y' && answer !== 'yes') {
|
|
453
|
-
console.log(' 已取消');
|
|
454
|
-
return;
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
if (!await checkEnvironment(rl)) {
|
|
458
|
-
return;
|
|
459
|
-
}
|
|
460
|
-
console.log('📝 交互式配置\n');
|
|
461
|
-
// 通用配置
|
|
462
|
-
const defaultSuggestion = path.join(os.homedir(), 'projects', 'default');
|
|
463
|
-
let defaultPath = (await ask(rl, ` 默认项目路径 [${defaultSuggestion}]: `)).trim();
|
|
464
|
-
if (!defaultPath)
|
|
465
|
-
defaultPath = defaultSuggestion;
|
|
466
|
-
if (defaultPath.startsWith('~/')) {
|
|
467
|
-
defaultPath = path.join(os.homedir(), defaultPath.slice(2));
|
|
468
|
-
}
|
|
469
|
-
else if (defaultPath === '~') {
|
|
470
|
-
defaultPath = os.homedir();
|
|
471
|
-
}
|
|
472
|
-
if (!fs.existsSync(defaultPath)) {
|
|
473
|
-
fs.mkdirSync(defaultPath, { recursive: true });
|
|
474
|
-
console.log(` ✓ 已创建目录: ${defaultPath}`);
|
|
475
|
-
}
|
|
476
|
-
const modelInput = (await ask(rl, ' 模型 [sonnet(默认)/opus/haiku]: ')).trim().toLowerCase();
|
|
477
|
-
const model = ['opus', 'haiku'].includes(modelInput) ? modelInput : 'sonnet';
|
|
478
|
-
// 渠道选择(支持退回重选)
|
|
479
|
-
const config = JSON.parse(fs.readFileSync(sampleSrc, 'utf-8'));
|
|
480
|
-
config.projects.defaultPath = defaultPath;
|
|
481
|
-
config.projects.list = { [path.basename(defaultPath)]: defaultPath };
|
|
482
|
-
config.agents.claude.model = model;
|
|
483
|
-
let channelConfigured = false;
|
|
484
|
-
while (!channelConfigured) {
|
|
485
|
-
console.log('\n选择消息渠道:');
|
|
486
|
-
console.log(' 1. 飞书 (Feishu)');
|
|
487
|
-
console.log(' 2. 微信 (WeChat)');
|
|
488
|
-
console.log(' 3. AUN (AgentUnin.Network)');
|
|
489
|
-
console.log(' 4. 钉钉 (DingTalk)');
|
|
490
|
-
console.log(' 5. QQ 机器人 (QQBot)');
|
|
491
|
-
const channelChoice = (await ask(rl, '请选择 [1]: ')).trim() || '1';
|
|
492
|
-
if (channelChoice === '1') {
|
|
493
|
-
console.log('\n飞书配置方式:');
|
|
494
|
-
console.log(' 1. 扫码自动注册(推荐)');
|
|
495
|
-
console.log(' 2. 手动输入 App ID/Secret');
|
|
496
|
-
const feishuMethod = (await ask(rl, '请选择 [1]: ')).trim() || '1';
|
|
497
|
-
if (feishuMethod === '1') {
|
|
498
|
-
const { runFeishuQrFlow } = await import('./init-channel.js');
|
|
499
|
-
const result = await runFeishuQrFlow();
|
|
500
|
-
if (!result) {
|
|
501
|
-
console.log('已取消');
|
|
502
|
-
return;
|
|
503
|
-
}
|
|
504
|
-
config.channels.feishu.appId = result.appId;
|
|
505
|
-
config.channels.feishu.appSecret = result.appSecret;
|
|
506
|
-
config.channels.feishu.enabled = true;
|
|
507
|
-
if (result.openId)
|
|
508
|
-
config.channels.feishu.owner = result.openId;
|
|
509
|
-
}
|
|
510
|
-
else {
|
|
511
|
-
if (!await initFeishuManual(rl, config)) {
|
|
512
|
-
console.log('已取消');
|
|
513
|
-
return;
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
channelConfigured = true;
|
|
517
|
-
config.channels.defaultChannel = 'feishu';
|
|
518
|
-
}
|
|
519
|
-
else if (channelChoice === '2') {
|
|
520
|
-
const { runWechatQrFlow } = await import('./init-channel.js');
|
|
521
|
-
const result = await runWechatQrFlow();
|
|
522
|
-
if (!result) {
|
|
523
|
-
console.log('已取消');
|
|
524
|
-
return;
|
|
525
|
-
}
|
|
526
|
-
config.channels.wechat = {
|
|
527
|
-
enabled: true,
|
|
528
|
-
baseUrl: result.baseUrl,
|
|
529
|
-
token: result.token,
|
|
530
|
-
};
|
|
531
|
-
channelConfigured = true;
|
|
532
|
-
config.channels.defaultChannel = 'wechat';
|
|
533
|
-
}
|
|
534
|
-
else if (channelChoice === '3') {
|
|
535
|
-
const { checkAunEnvironment, setupAunAid } = await import('./init-channel.js');
|
|
536
|
-
const aunReady = await checkAunEnvironment(rl);
|
|
537
|
-
if (!aunReady)
|
|
538
|
-
continue; // 退回重选渠道
|
|
539
|
-
const result = await setupAunAid(rl, config);
|
|
540
|
-
if (!result)
|
|
541
|
-
continue;
|
|
542
|
-
config.channels.aun = {
|
|
543
|
-
enabled: true,
|
|
544
|
-
aid: result.aid,
|
|
545
|
-
owner: result.owner,
|
|
546
|
-
};
|
|
547
|
-
channelConfigured = true;
|
|
548
|
-
config.channels.defaultChannel = 'aun';
|
|
549
|
-
}
|
|
550
|
-
else if (channelChoice === '4') {
|
|
551
|
-
const { runDingtalkQrFlowSimple } = await import('./init-channel.js');
|
|
552
|
-
const result = await runDingtalkQrFlowSimple();
|
|
553
|
-
if (!result) {
|
|
554
|
-
console.log('已取消');
|
|
555
|
-
return;
|
|
556
|
-
}
|
|
557
|
-
config.channels.dingtalk = {
|
|
558
|
-
enabled: true,
|
|
559
|
-
clientId: result.clientId,
|
|
560
|
-
clientSecret: result.clientSecret,
|
|
561
|
-
};
|
|
562
|
-
channelConfigured = true;
|
|
563
|
-
config.channels.defaultChannel = 'dingtalk';
|
|
564
|
-
}
|
|
565
|
-
else if (channelChoice === '5') {
|
|
566
|
-
const { runQQBotBindFlowSimple } = await import('./init-channel.js');
|
|
567
|
-
const result = await runQQBotBindFlowSimple();
|
|
568
|
-
if (!result) {
|
|
569
|
-
console.log('已取消');
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
config.channels.qqbot = {
|
|
573
|
-
enabled: true,
|
|
574
|
-
appId: result.appId,
|
|
575
|
-
clientSecret: result.clientSecret,
|
|
576
|
-
};
|
|
577
|
-
channelConfigured = true;
|
|
578
|
-
config.channels.defaultChannel = 'qqbot';
|
|
579
|
-
}
|
|
580
|
-
else {
|
|
581
|
-
console.log(' 无效选择,请重新输入');
|
|
582
|
-
}
|
|
583
|
-
}
|
|
584
|
-
// 可选:富内容渲染模块(仅 Feishu 通道需要)
|
|
585
|
-
if (config.channels?.feishu?.enabled) {
|
|
586
|
-
await offerRichContentRenderer(rl, config);
|
|
587
|
-
}
|
|
588
|
-
fs.writeFileSync(p.config, JSON.stringify(config, null, 2) + '\n');
|
|
589
|
-
console.log(`\n✓ 已创建配置文件: ${p.config}`);
|
|
590
|
-
setupEnvVar(resolveRoot());
|
|
591
|
-
}
|
|
592
|
-
finally {
|
|
593
|
-
rl.close();
|
|
594
|
-
}
|
|
595
|
-
}
|
|
596
|
-
/**
|
|
597
|
-
* Present instance selection menu when existing instances are found.
|
|
598
|
-
* Returns the user's choice, or null if cancelled.
|
|
599
|
-
*/
|
|
600
|
-
export async function selectInstance(rl, channelType, instances) {
|
|
601
|
-
const typeLabel = channelType === 'feishu' ? '飞书' : channelType === 'wechat' ? '微信' : channelType === 'dingtalk' ? '钉钉' : channelType === 'qqbot' ? 'QQ机器人' : 'AUN';
|
|
602
|
-
console.log(`\n发现已有 ${typeLabel} 机器人:`);
|
|
603
|
-
const letters = 'abcdefghijklmnopqrstuvwxyz';
|
|
604
|
-
for (let i = 0; i < instances.length; i++) {
|
|
605
|
-
const inst = instances[i];
|
|
606
|
-
const id = inst.aid || inst.appId || inst.botId || inst.clientId || inst.token?.slice(0, 16) || '';
|
|
607
|
-
const suffix = id ? ` (${id})` : '';
|
|
608
|
-
console.log(` ${letters[i]}. ${inst.name}${suffix}`);
|
|
609
|
-
}
|
|
610
|
-
const addLetter = letters[instances.length];
|
|
611
|
-
console.log(` ${addLetter}. 添加新机器人`);
|
|
612
|
-
console.log('');
|
|
613
|
-
const validOptions = letters.slice(0, instances.length + 1).split('');
|
|
614
|
-
let choice = '';
|
|
615
|
-
while (!validOptions.includes(choice)) {
|
|
616
|
-
choice = (await ask(rl, '请选择: ')).trim().toLowerCase();
|
|
617
|
-
if (!validOptions.includes(choice)) {
|
|
618
|
-
console.log(`无效选择,请输入 ${validOptions.join('/')}`);
|
|
619
|
-
}
|
|
620
|
-
}
|
|
621
|
-
const choiceIndex = letters.indexOf(choice);
|
|
622
|
-
if (choiceIndex === instances.length) {
|
|
623
|
-
// Add new — ask for name
|
|
624
|
-
let name = '';
|
|
625
|
-
while (!name) {
|
|
626
|
-
name = (await ask(rl, '请输入新机器人名称: ')).trim();
|
|
627
|
-
if (!name)
|
|
628
|
-
console.log(' 名称不能为空');
|
|
629
|
-
if (instances.some(i => i.name === name)) {
|
|
630
|
-
console.log(` 名称 "${name}" 已存在,请换一个`);
|
|
631
|
-
name = '';
|
|
632
|
-
}
|
|
633
|
-
}
|
|
634
|
-
return { action: 'add', name };
|
|
635
|
-
}
|
|
636
|
-
// Overwrite — requires confirmation
|
|
637
|
-
const target = instances[choiceIndex];
|
|
638
|
-
console.log(`\n已选择:${target.name}`);
|
|
639
|
-
const confirm = (await ask(rl, `⚠️ 即将覆盖该机器人配置,确认?(y/N) `)).trim().toLowerCase();
|
|
640
|
-
if (confirm !== 'y' && confirm !== 'yes') {
|
|
641
|
-
console.log('已取消');
|
|
642
|
-
return null;
|
|
643
|
-
}
|
|
644
|
-
return { action: 'overwrite', index: choiceIndex, name: target.name };
|
|
645
|
-
}
|