evolclaw 2.8.2 → 3.0.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/dist/agents/claude-runner.js +105 -30
- package/dist/agents/codex-runner.js +15 -7
- package/dist/agents/gemini-runner.js +14 -5
- package/dist/agents/resolve.js +134 -0
- package/dist/agents/templates.js +3 -3
- package/dist/aun/aid/agentmd.js +186 -0
- package/dist/aun/aid/client.js +134 -0
- package/dist/aun/aid/identity.js +131 -0
- package/dist/aun/aid/index.js +3 -0
- package/dist/aun/aid/types.js +1 -0
- package/dist/aun/aid/validation.js +21 -0
- package/dist/aun/msg/group.js +291 -0
- package/dist/aun/msg/index.js +4 -0
- package/dist/aun/msg/p2p.js +144 -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 +1064 -279
- package/dist/channels/dingtalk.js +58 -5
- package/dist/channels/feishu.js +266 -30
- package/dist/channels/qqbot.js +67 -12
- package/dist/channels/wechat.js +61 -4
- package/dist/channels/wecom.js +58 -5
- package/dist/cli/agent.js +800 -0
- package/dist/cli/index.js +4253 -0
- package/dist/{utils → cli}/init-channel.js +211 -621
- package/dist/cli/init.js +178 -0
- package/dist/config-store.js +613 -0
- package/dist/core/baseagent-loader.js +48 -0
- package/dist/core/channel-loader.js +162 -11
- package/dist/core/command-handler.js +1090 -838
- package/dist/core/evolagent-registry.js +191 -360
- package/dist/core/evolagent.js +203 -234
- package/dist/core/interaction-router.js +52 -5
- package/dist/core/message/im-renderer.js +480 -0
- package/dist/core/message/items-formatter.js +61 -0
- package/dist/core/message/message-bridge.js +104 -56
- package/dist/core/message/message-log.js +91 -0
- package/dist/core/message/message-processor.js +326 -145
- package/dist/core/message/message-queue.js +5 -5
- package/dist/core/permission.js +21 -8
- 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 +704 -775
- 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/{templates → data}/prompts.md +34 -1
- package/dist/index.js +437 -273
- package/dist/ipc.js +49 -0
- package/dist/paths.js +82 -9
- package/dist/types.js +8 -2
- package/dist/utils/atomic-write.js +79 -0
- package/dist/utils/channel-helpers.js +46 -0
- package/dist/utils/cross-platform.js +0 -18
- package/dist/utils/instance-registry.js +433 -0
- package/dist/utils/log-writer.js +216 -0
- package/dist/utils/logger.js +24 -77
- package/dist/utils/media-cache.js +23 -0
- package/dist/utils/{upgrade.js → npm-ops.js} +52 -21
- package/dist/utils/process-introspect.js +144 -0
- package/dist/utils/stats.js +192 -0
- package/dist/watch-msg.js +529 -0
- package/evolclaw-install-aun.md +114 -46
- package/kits/aun/meta.md +25 -0
- package/kits/aun/role.md +25 -0
- package/kits/channels/aun.md +25 -0
- package/kits/evolclaw/commands.md +31 -0
- package/kits/evolclaw/identity-tools.md +26 -0
- package/kits/evolclaw/self-summary.md +29 -0
- package/kits/evolclaw/tools.md +25 -0
- package/kits/templates/group.md +20 -0
- package/kits/templates/private.md +9 -0
- package/kits/templates/system-fragments/personal-context.md +3 -0
- package/kits/templates/system-fragments/self-intro.md +5 -0
- package/kits/templates/system-fragments/speaker-intro.md +5 -0
- package/kits/templates/system-fragments/venue-intro.md +5 -0
- package/package.json +7 -5
- package/data/evolclaw.sample.json +0 -60
- package/dist/channels/aun-ops.js +0 -275
- package/dist/cli.js +0 -2178
- package/dist/config.js +0 -576
- package/dist/core/agent-loader.js +0 -39
- 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/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/ipc.js
CHANGED
|
@@ -9,6 +9,8 @@ export class IpcServer {
|
|
|
9
9
|
commandExecutor;
|
|
10
10
|
server = null;
|
|
11
11
|
agentRegistry;
|
|
12
|
+
aunAidProvider;
|
|
13
|
+
aunAidStatsProvider;
|
|
12
14
|
constructor(socketPath, getStatus, commandExecutor) {
|
|
13
15
|
this.socketPath = socketPath;
|
|
14
16
|
this.getStatus = getStatus;
|
|
@@ -18,6 +20,14 @@ export class IpcServer {
|
|
|
18
20
|
setAgentRegistry(registry) {
|
|
19
21
|
this.agentRegistry = registry;
|
|
20
22
|
}
|
|
23
|
+
/** Inject AUN AID state aggregator for aun-aids IPC handler */
|
|
24
|
+
setAunAidProvider(provider) {
|
|
25
|
+
this.aunAidProvider = provider;
|
|
26
|
+
}
|
|
27
|
+
/** Inject AUN AID stats provider for aun-aid-stats IPC handler */
|
|
28
|
+
setAunAidStatsProvider(provider) {
|
|
29
|
+
this.aunAidStatsProvider = provider;
|
|
30
|
+
}
|
|
21
31
|
start() {
|
|
22
32
|
// Remove stale socket file (Unix only — named pipes auto-cleanup on process exit)
|
|
23
33
|
if (!isNamedPipe(this.socketPath)) {
|
|
@@ -79,6 +89,14 @@ export class IpcServer {
|
|
|
79
89
|
return this.getStatus();
|
|
80
90
|
case 'ping':
|
|
81
91
|
return { pong: true, pid: process.pid };
|
|
92
|
+
case 'aun-aids': {
|
|
93
|
+
const aids = this.aunAidProvider ? this.aunAidProvider() : [];
|
|
94
|
+
return { ok: true, aids };
|
|
95
|
+
}
|
|
96
|
+
case 'aun-aid-stats': {
|
|
97
|
+
const stats = this.aunAidStatsProvider ? this.aunAidStatsProvider() : [];
|
|
98
|
+
return { ok: true, stats };
|
|
99
|
+
}
|
|
82
100
|
case 'ctl': {
|
|
83
101
|
if (!this.commandExecutor)
|
|
84
102
|
return { ok: false, error: 'ctl not configured' };
|
|
@@ -129,6 +147,37 @@ export class IpcServer {
|
|
|
129
147
|
return { ok: false, error: e?.message || String(e) };
|
|
130
148
|
}
|
|
131
149
|
}
|
|
150
|
+
case 'evolagent.load': {
|
|
151
|
+
if (!this.agentRegistry)
|
|
152
|
+
return { ok: false, error: 'EvolAgentRegistry not available' };
|
|
153
|
+
const aid = cmd.aid;
|
|
154
|
+
if (!aid || typeof aid !== 'string')
|
|
155
|
+
return { ok: false, error: 'missing aid' };
|
|
156
|
+
const hotLoad = globalThis.__evolclaw_hotLoadAgent;
|
|
157
|
+
if (!hotLoad)
|
|
158
|
+
return { ok: false, error: 'Hot-load handler not initialized' };
|
|
159
|
+
try {
|
|
160
|
+
await hotLoad(aid);
|
|
161
|
+
return { ok: true, result: `Agent "${aid}" loaded and online` };
|
|
162
|
+
}
|
|
163
|
+
catch (e) {
|
|
164
|
+
return { ok: false, error: e?.message || String(e) };
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
case 'evolagent.resync': {
|
|
168
|
+
if (!this.agentRegistry)
|
|
169
|
+
return { ok: false, error: 'EvolAgentRegistry not available' };
|
|
170
|
+
const resync = globalThis.__evolclaw_resyncAgents;
|
|
171
|
+
if (!resync)
|
|
172
|
+
return { ok: false, error: 'Resync handler not initialized' };
|
|
173
|
+
try {
|
|
174
|
+
const results = await resync();
|
|
175
|
+
return { ok: true, results };
|
|
176
|
+
}
|
|
177
|
+
catch (e) {
|
|
178
|
+
return { ok: false, error: e?.message || String(e) };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
132
181
|
default:
|
|
133
182
|
return { error: `unknown command: ${cmd.type}` };
|
|
134
183
|
}
|
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,104 @@ 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-home-directory.md)────────────────
|
|
40
|
+
defaultsConfig: path.join(root, 'agents', 'defaults.json'),
|
|
41
|
+
kitsDir: path.join(root, 'kits'),
|
|
42
|
+
kitsAunDir: path.join(root, 'kits', 'aun'),
|
|
43
|
+
kitsChannelsDir: path.join(root, 'kits', 'channels'),
|
|
44
|
+
kitsEvolclawDir: path.join(root, 'kits', 'evolclaw'),
|
|
45
|
+
kitsTemplatesDir: path.join(root, 'kits', 'templates'),
|
|
46
|
+
instanceReadySignal: path.join(root, 'data', 'instance', 'ready.signal'),
|
|
47
|
+
instanceSocket: resolveInstanceSocketPath(root),
|
|
40
48
|
};
|
|
41
49
|
}
|
|
42
|
-
|
|
50
|
+
// ── per-agent 路径(参数化,不进 resolvePaths() 的固定 map)──
|
|
51
|
+
export function agentDir(aid) {
|
|
52
|
+
return path.join(resolveRoot(), 'agents', aid);
|
|
53
|
+
}
|
|
54
|
+
export function agentConfig(aid) {
|
|
55
|
+
return path.join(agentDir(aid), 'config.json');
|
|
56
|
+
}
|
|
57
|
+
export function agentPersonalDir(aid) {
|
|
58
|
+
return path.join(agentDir(aid), 'personal');
|
|
59
|
+
}
|
|
60
|
+
export function agentIdentitiesDir(aid) {
|
|
61
|
+
return path.join(agentDir(aid), 'identities');
|
|
62
|
+
}
|
|
63
|
+
export function agentVenuesDir(aid) {
|
|
64
|
+
return path.join(agentDir(aid), 'venues');
|
|
65
|
+
}
|
|
66
|
+
export function agentSessionsDir(aid) {
|
|
67
|
+
return path.join(agentDir(aid), 'sessions');
|
|
68
|
+
}
|
|
69
|
+
export function agentDataDir(aid) {
|
|
70
|
+
return path.join(agentDir(aid), 'data');
|
|
71
|
+
}
|
|
72
|
+
export function agentDataCacheDir(aid) {
|
|
73
|
+
return path.join(agentDataDir(aid), 'cache');
|
|
74
|
+
}
|
|
75
|
+
export function agentTriggersDir(aid) {
|
|
76
|
+
return path.join(resolveRoot(), 'data', 'triggers', aid);
|
|
77
|
+
}
|
|
78
|
+
function resolveInstanceSocketPath(root) {
|
|
43
79
|
if (isWindows) {
|
|
44
80
|
const hash = crypto.createHash('sha1').update(root).digest('hex').slice(0, 12);
|
|
45
81
|
return `\\\\.\\pipe\\evolclaw-${hash}`;
|
|
46
82
|
}
|
|
47
|
-
return path.join(root, '
|
|
83
|
+
return path.join(root, 'data', 'instance', 'evolclaw.sock');
|
|
48
84
|
}
|
|
49
85
|
export function ensureDataDirs() {
|
|
50
86
|
const p = resolvePaths();
|
|
51
87
|
fs.mkdirSync(p.dataDir, { recursive: true });
|
|
52
88
|
fs.mkdirSync(p.logs, { recursive: true });
|
|
53
89
|
fs.mkdirSync(p.agentsDir, { recursive: true });
|
|
90
|
+
fs.mkdirSync(p.sessionsDir, { recursive: true });
|
|
91
|
+
fs.mkdirSync(p.instanceDir, { recursive: true });
|
|
92
|
+
fs.mkdirSync(p.outboxDir, { recursive: true });
|
|
93
|
+
fs.mkdirSync(p.kitsDir, { recursive: true });
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* 首次启动或升级时,把包内 kits/ 复制到 EVOLCLAW_HOME/kits/。
|
|
97
|
+
* 策略:如果目标 kits/ 为空或包版本更新,整体覆盖。
|
|
98
|
+
*/
|
|
99
|
+
export function syncKitsFromPackage() {
|
|
100
|
+
const p = resolvePaths();
|
|
101
|
+
const srcKits = path.join(getPackageRoot(), 'kits');
|
|
102
|
+
if (!fs.existsSync(srcKits))
|
|
103
|
+
return;
|
|
104
|
+
const destKits = p.kitsDir;
|
|
105
|
+
// 包内自用场景:EVOLCLAW_HOME 等于包根(开发仓 / 用户家目录恰好是安装目录),
|
|
106
|
+
// src === dest 会让 cpSync 抛 ERR_FS_CP_EINVAL。直接跳过同步。
|
|
107
|
+
if (path.resolve(srcKits) === path.resolve(destKits))
|
|
108
|
+
return;
|
|
109
|
+
// 用 .kits-version 文件跟踪已安装的版本
|
|
110
|
+
const versionFile = path.join(destKits, '.kits-version');
|
|
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');
|
|
54
127
|
}
|
|
55
128
|
export function getPackageRoot() {
|
|
56
129
|
// 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,79 @@
|
|
|
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
|
+
}
|
|
@@ -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
|
+
}
|
|
@@ -106,24 +106,6 @@ function formatUptime(totalSeconds) {
|
|
|
106
106
|
parts.push(`${seconds}s`);
|
|
107
107
|
return parts.join(' ');
|
|
108
108
|
}
|
|
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
109
|
/**
|
|
128
110
|
* Cross-platform command existence check.
|
|
129
111
|
*/
|