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/paths.js
CHANGED
|
@@ -10,7 +10,7 @@ export function resolveRoot() {
|
|
|
10
10
|
if (process.env.EVOLCLAW_HOME) {
|
|
11
11
|
_root = process.env.EVOLCLAW_HOME;
|
|
12
12
|
}
|
|
13
|
-
else if (fs.existsSync(path.join(process.cwd(), '
|
|
13
|
+
else if (fs.existsSync(path.join(process.cwd(), 'agents', 'defaults.json'))) {
|
|
14
14
|
_root = process.cwd();
|
|
15
15
|
}
|
|
16
16
|
else {
|
|
@@ -26,31 +26,95 @@ export function resolvePaths() {
|
|
|
26
26
|
const root = resolveRoot();
|
|
27
27
|
return {
|
|
28
28
|
root,
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
pid: path.join(root, 'logs', 'evolclaw.pid'),
|
|
29
|
+
sessionsDir: path.join(root, 'data', 'sessions'),
|
|
30
|
+
instanceDir: path.join(root, 'data', 'instance'),
|
|
31
|
+
outboxDir: path.join(root, 'data', 'outbox'),
|
|
33
32
|
dataDir: path.join(root, 'data'),
|
|
34
33
|
logs: path.join(root, 'logs'),
|
|
35
34
|
agentsDir: path.join(root, 'agents'),
|
|
36
35
|
lineStats: path.join(root, 'logs', 'line-stats.log'),
|
|
37
|
-
readySignal: path.join(root, '
|
|
36
|
+
readySignal: path.join(root, 'data', 'instance', 'ready.signal'),
|
|
38
37
|
selfHealLog: path.join(root, 'logs', 'self-heal.md'),
|
|
39
|
-
socket:
|
|
38
|
+
socket: resolveInstanceSocketPath(root),
|
|
39
|
+
// ── 新结构(evolclaw-directory-design.md)────────────────
|
|
40
|
+
defaultsConfig: path.join(root, 'agents', 'defaults.json'),
|
|
41
|
+
processConfig: path.join(root, 'config.json'),
|
|
42
|
+
eckDir: path.join(root, 'eck'),
|
|
43
|
+
instanceReadySignal: path.join(root, 'data', 'instance', 'ready.signal'),
|
|
44
|
+
instanceSocket: resolveInstanceSocketPath(root),
|
|
45
|
+
aidLogsDir: path.join(root, 'logs', 'aids'),
|
|
40
46
|
};
|
|
41
47
|
}
|
|
42
|
-
|
|
48
|
+
// ── per-agent 路径(参数化,不进 resolvePaths() 的固定 map)──
|
|
49
|
+
export function agentDir(aid) {
|
|
50
|
+
return path.join(resolveRoot(), 'agents', aid);
|
|
51
|
+
}
|
|
52
|
+
export function agentConfig(aid) {
|
|
53
|
+
return path.join(agentDir(aid), 'config.json');
|
|
54
|
+
}
|
|
55
|
+
export function agentPersonalDir(aid) {
|
|
56
|
+
return path.join(agentDir(aid), 'personal');
|
|
57
|
+
}
|
|
58
|
+
export function agentRelationsDir(aid) {
|
|
59
|
+
return path.join(agentDir(aid), 'relations');
|
|
60
|
+
}
|
|
61
|
+
/** @deprecated Use agentRelationsDir instead */
|
|
62
|
+
export function agentIdentitiesDir(aid) {
|
|
63
|
+
return agentRelationsDir(aid);
|
|
64
|
+
}
|
|
65
|
+
export function agentIndexDir(aid) {
|
|
66
|
+
return path.join(agentDir(aid), 'index');
|
|
67
|
+
}
|
|
68
|
+
export function agentVenuesDir(aid) {
|
|
69
|
+
return path.join(agentDir(aid), 'venues');
|
|
70
|
+
}
|
|
71
|
+
export function agentSessionsDir(aid) {
|
|
72
|
+
return path.join(agentDir(aid), 'sessions');
|
|
73
|
+
}
|
|
74
|
+
export function agentDataDir(aid) {
|
|
75
|
+
return path.join(agentDir(aid), 'data');
|
|
76
|
+
}
|
|
77
|
+
export function agentDataCacheDir(aid) {
|
|
78
|
+
return path.join(agentDataDir(aid), 'cache');
|
|
79
|
+
}
|
|
80
|
+
export function agentTriggersDir(aid) {
|
|
81
|
+
return path.join(resolveRoot(), 'data', 'triggers', aid);
|
|
82
|
+
}
|
|
83
|
+
function resolveInstanceSocketPath(root) {
|
|
43
84
|
if (isWindows) {
|
|
44
85
|
const hash = crypto.createHash('sha1').update(root).digest('hex').slice(0, 12);
|
|
45
86
|
return `\\\\.\\pipe\\evolclaw-${hash}`;
|
|
46
87
|
}
|
|
47
|
-
return path.join(root, '
|
|
88
|
+
return path.join(root, 'data', 'instance', 'evolclaw.sock');
|
|
48
89
|
}
|
|
49
90
|
export function ensureDataDirs() {
|
|
50
91
|
const p = resolvePaths();
|
|
51
92
|
fs.mkdirSync(p.dataDir, { recursive: true });
|
|
52
93
|
fs.mkdirSync(p.logs, { recursive: true });
|
|
94
|
+
fs.mkdirSync(p.aidLogsDir, { recursive: true });
|
|
53
95
|
fs.mkdirSync(p.agentsDir, { recursive: true });
|
|
96
|
+
fs.mkdirSync(p.sessionsDir, { recursive: true });
|
|
97
|
+
fs.mkdirSync(p.instanceDir, { recursive: true });
|
|
98
|
+
fs.mkdirSync(p.outboxDir, { recursive: true });
|
|
99
|
+
fs.mkdirSync(p.eckDir, { recursive: true });
|
|
100
|
+
fs.mkdirSync(eckDebugDir(), { recursive: true });
|
|
101
|
+
}
|
|
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');
|
|
54
118
|
}
|
|
55
119
|
export function getPackageRoot() {
|
|
56
120
|
// import.meta.dirname is available in Node.js 21.2+ and always returns
|
package/dist/types.js
CHANGED
|
@@ -3,5 +3,11 @@
|
|
|
3
3
|
// Array form: `name` is required to distinguish instances.
|
|
4
4
|
/** Default permission mode applied to new sessions. Change here to affect all roles. */
|
|
5
5
|
export const DEFAULT_PERMISSION_MODE = 'bypass';
|
|
6
|
-
/**
|
|
7
|
-
export const DEFAULT_AGENT_NAME = '
|
|
6
|
+
/** Placeholder agent name for legacy code paths that haven't been migrated. */
|
|
7
|
+
export const DEFAULT_AGENT_NAME = '<unknown>';
|
|
8
|
+
// ── 新结构:defaults.json + per-agent config.json(evolclaw-home-directory.md)──
|
|
9
|
+
//
|
|
10
|
+
// 旧 Config / EvolAgentConfig 保留至阶段 3 退场。新结构的承载是
|
|
11
|
+
// DefaultsConfig(agents/defaults.json)+ AgentConfig(agents/<aid>/config.json),
|
|
12
|
+
// 由 ConfigStore 加载并算 effective merged 值。
|
|
13
|
+
export const CONFIG_SCHEMA_VERSION = 1;
|
|
@@ -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
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomic write via dual rename.
|
|
3
|
+
*
|
|
4
|
+
* 写入流程(写 foo.json):
|
|
5
|
+
* 1. 把当前 foo.json 改名为 foo.json_ (保留旧版作为热备)
|
|
6
|
+
* 2. 把新内容写到 foo.json__ (写入完成才有完整内容)
|
|
7
|
+
* 3. 把 foo.json__ rename 为 foo.json (原子切换)
|
|
8
|
+
* foo.json_ 留到下次写入时被覆盖。
|
|
9
|
+
*
|
|
10
|
+
* 读取时按以下顺序探测,自动恢复中途崩溃的状态:
|
|
11
|
+
* - foo.json__ 存在 → 上次写入未完成,丢弃
|
|
12
|
+
* - foo.json 存在 → 直接读
|
|
13
|
+
* - foo.json_ 存在 → rename 步骤崩溃,rename 回 foo.json 再读
|
|
14
|
+
* - 都不存在 → null
|
|
15
|
+
*/
|
|
16
|
+
import fs from 'fs';
|
|
17
|
+
import path from 'path';
|
|
18
|
+
const HOT = '_'; // foo.json_ 旧版热备
|
|
19
|
+
const TMP = '__'; // foo.json__ 写入中
|
|
20
|
+
/**
|
|
21
|
+
* 原子写入 JSON 文件(双 rename)。dirname 不存在时会自动创建。
|
|
22
|
+
*
|
|
23
|
+
* @param filePath 目标文件
|
|
24
|
+
* @param content 完整内容(不含末尾换行也可,由调用方控制)
|
|
25
|
+
*/
|
|
26
|
+
export function atomicWrite(filePath, content) {
|
|
27
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
28
|
+
const hot = filePath + HOT;
|
|
29
|
+
const tmp = filePath + TMP;
|
|
30
|
+
// 步骤 1:当前文件 → hot(保留旧版作热备)
|
|
31
|
+
if (fs.existsSync(filePath)) {
|
|
32
|
+
if (fs.existsSync(hot))
|
|
33
|
+
fs.unlinkSync(hot);
|
|
34
|
+
fs.renameSync(filePath, hot);
|
|
35
|
+
}
|
|
36
|
+
// 步骤 2:写到 tmp
|
|
37
|
+
fs.writeFileSync(tmp, content, 'utf-8');
|
|
38
|
+
// 步骤 3:tmp → 目标(原子)
|
|
39
|
+
fs.renameSync(tmp, filePath);
|
|
40
|
+
}
|
|
41
|
+
export function atomicWriteJson(filePath, value) {
|
|
42
|
+
atomicWrite(filePath, JSON.stringify(value, null, 2) + '\n');
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 原子读取——自动按状态恢复。文件不存在时返回 null。
|
|
46
|
+
*
|
|
47
|
+
* 调用方拿到 null 应当作"文件不存在"处理;解析失败抛错。
|
|
48
|
+
*/
|
|
49
|
+
export function atomicRead(filePath) {
|
|
50
|
+
const hot = filePath + HOT;
|
|
51
|
+
const tmp = filePath + TMP;
|
|
52
|
+
// 中途崩溃残留 → 丢弃
|
|
53
|
+
if (fs.existsSync(tmp)) {
|
|
54
|
+
try {
|
|
55
|
+
fs.unlinkSync(tmp);
|
|
56
|
+
}
|
|
57
|
+
catch { /* best effort */ }
|
|
58
|
+
}
|
|
59
|
+
if (fs.existsSync(filePath)) {
|
|
60
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
61
|
+
}
|
|
62
|
+
// rename 步骤崩溃 → 把 hot 移回正式名
|
|
63
|
+
if (fs.existsSync(hot)) {
|
|
64
|
+
fs.renameSync(hot, filePath);
|
|
65
|
+
return fs.readFileSync(filePath, 'utf-8');
|
|
66
|
+
}
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
export function atomicReadJson(filePath) {
|
|
70
|
+
const text = atomicRead(filePath);
|
|
71
|
+
if (text === null)
|
|
72
|
+
return null;
|
|
73
|
+
return JSON.parse(text);
|
|
74
|
+
}
|
|
75
|
+
export function ensureDir(dirPath) {
|
|
76
|
+
if (!fs.existsSync(dirPath)) {
|
|
77
|
+
fs.mkdirSync(dirPath, { recursive: true });
|
|
78
|
+
}
|
|
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
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Channel 配置归一化工具——给 ChannelLoader 时代的 channel adapter 用。
|
|
3
|
+
*
|
|
4
|
+
* 旧结构里 `Config.channels.<type>` 可以是 single object 或 array;这些工具
|
|
5
|
+
* 用来抹平这种差异。新结构(agents/<aid>/config.json)的 channels 已经统一为
|
|
6
|
+
* 列表形态,这些工具仍被 ChannelLoader 调用方使用——index.ts 把新结构翻成旧
|
|
7
|
+
* dict 形态喂给 ChannelLoader,所以 normalizeChannelInstances 仍然有用武之地。
|
|
8
|
+
*
|
|
9
|
+
* 当 ChannelLoader 重写为直接吃 ChannelInstance[] 后,本文件可删。
|
|
10
|
+
*/
|
|
11
|
+
export const channelTypes = ['feishu', 'wechat', 'aun', 'dingtalk', 'qqbot', 'wecom'];
|
|
12
|
+
/**
|
|
13
|
+
* 把 channel 配置(单对象 / 数组 / undefined)归一为带 name 的数组。
|
|
14
|
+
*/
|
|
15
|
+
export function normalizeChannelInstances(cfg, defaultName) {
|
|
16
|
+
if (cfg === undefined || cfg === null)
|
|
17
|
+
return [];
|
|
18
|
+
if (Array.isArray(cfg)) {
|
|
19
|
+
return cfg.map((item, i) => ({
|
|
20
|
+
...item,
|
|
21
|
+
name: item.name ?? (cfg.length === 1 ? defaultName : `${defaultName}-${i + 1}`),
|
|
22
|
+
}));
|
|
23
|
+
}
|
|
24
|
+
return [{ ...cfg, name: cfg.name ?? defaultName }];
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* 从 globalConfig.channels 字典里按实例名找 showActivities,找不到回退到全局。
|
|
28
|
+
*/
|
|
29
|
+
export function getChannelShowActivities(config, instanceName) {
|
|
30
|
+
for (const type of channelTypes) {
|
|
31
|
+
const raw = config.channels?.[type];
|
|
32
|
+
if (raw === undefined)
|
|
33
|
+
continue;
|
|
34
|
+
if (Array.isArray(raw)) {
|
|
35
|
+
const inst = raw.find((item) => item.name === instanceName);
|
|
36
|
+
if (inst)
|
|
37
|
+
return inst.showActivities ?? config.showActivities ?? 'all';
|
|
38
|
+
}
|
|
39
|
+
else {
|
|
40
|
+
const effectiveName = raw.name ?? type;
|
|
41
|
+
if (effectiveName === instanceName)
|
|
42
|
+
return raw.showActivities ?? config.showActivities ?? 'all';
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
return config.showActivities ?? 'all';
|
|
46
|
+
}
|
|
@@ -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+/);
|
|
@@ -106,40 +107,30 @@ function formatUptime(totalSeconds) {
|
|
|
106
107
|
parts.push(`${seconds}s`);
|
|
107
108
|
return parts.join(' ');
|
|
108
109
|
}
|
|
109
|
-
/**
|
|
110
|
-
* Read a specific environment variable from a running process.
|
|
111
|
-
* Returns undefined if the process doesn't exist or the variable is not set.
|
|
112
|
-
* Linux: reads /proc/<pid>/environ; Windows: not supported (returns undefined).
|
|
113
|
-
*/
|
|
114
|
-
export function getProcessEnv(pid, varName) {
|
|
115
|
-
if (isWindows)
|
|
116
|
-
return undefined;
|
|
117
|
-
try {
|
|
118
|
-
const environ = fs.readFileSync(`/proc/${pid}/environ`, 'utf-8');
|
|
119
|
-
const prefix = `${varName}=`;
|
|
120
|
-
const entry = environ.split('\0').find(e => e.startsWith(prefix));
|
|
121
|
-
return entry ? entry.slice(prefix.length) : undefined;
|
|
122
|
-
}
|
|
123
|
-
catch {
|
|
124
|
-
return undefined;
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
110
|
/**
|
|
128
111
|
* Cross-platform command existence check.
|
|
129
112
|
*/
|
|
113
|
+
const _commandExistsCache = new Map();
|
|
130
114
|
export function commandExists(cmd) {
|
|
115
|
+
const cached = _commandExistsCache.get(cmd);
|
|
116
|
+
if (cached !== undefined)
|
|
117
|
+
return cached;
|
|
118
|
+
let exists = false;
|
|
131
119
|
try {
|
|
132
120
|
if (isWindows) {
|
|
133
|
-
|
|
121
|
+
const r = spawnSync('where', [cmd], { encoding: 'utf-8', stdio: 'pipe', windowsHide: true });
|
|
122
|
+
exists = r.status === 0;
|
|
134
123
|
}
|
|
135
124
|
else {
|
|
136
125
|
execFileSync('which', [cmd], { encoding: 'utf-8', stdio: 'pipe' });
|
|
126
|
+
exists = true;
|
|
137
127
|
}
|
|
138
|
-
return true;
|
|
139
128
|
}
|
|
140
129
|
catch {
|
|
141
|
-
|
|
130
|
+
exists = false;
|
|
142
131
|
}
|
|
132
|
+
_commandExistsCache.set(cmd, exists);
|
|
133
|
+
return exists;
|
|
143
134
|
}
|
|
144
135
|
/**
|
|
145
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')) {
|