@voko/lite 0.3.1

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.
Files changed (62) hide show
  1. package/package.json +32 -0
  2. package/scripts/build-native.js +72 -0
  3. package/src/bankHeadOffices.js +20543 -0
  4. package/src/channels/email.js +35 -0
  5. package/src/channels/feishu.js +31 -0
  6. package/src/channels/qq-email.js +30 -0
  7. package/src/channels/registry.js +279 -0
  8. package/src/channels/telegram.js +28 -0
  9. package/src/channels/voko-email.js +7 -0
  10. package/src/channels/wechat.js +35 -0
  11. package/src/cli.js +120 -0
  12. package/src/context.js +164 -0
  13. package/src/core/access-control-api.js +150 -0
  14. package/src/core/access-control.js +56 -0
  15. package/src/core/agent-registration.js +319 -0
  16. package/src/core/api-signature.js +33 -0
  17. package/src/core/audit.js +133 -0
  18. package/src/core/database.js +1409 -0
  19. package/src/core/did-auth.js +54 -0
  20. package/src/core/hermes-paths.js +57 -0
  21. package/src/core/invitation.js +49 -0
  22. package/src/core/lite-bus.js +16 -0
  23. package/src/core/llm-client.js +1032 -0
  24. package/src/core/messenger.js +456 -0
  25. package/src/core/notifier.js +99 -0
  26. package/src/core/offline-sync.js +150 -0
  27. package/src/core/payment.js +285 -0
  28. package/src/core/publish-agent.js +166 -0
  29. package/src/core/register-capabilities.js +119 -0
  30. package/src/core/search-capabilities.js +136 -0
  31. package/src/core/send-message.js +85 -0
  32. package/src/core/set-agent-status.js +65 -0
  33. package/src/core/update-agent-profile.js +102 -0
  34. package/src/core/worker-manager.js +332 -0
  35. package/src/endpoints.json +21 -0
  36. package/src/index.js +712 -0
  37. package/src/mcp/CLAUDE_TEST.md +82 -0
  38. package/src/mcp/FULL_TEST.md +139 -0
  39. package/src/mcp/TEST.md +124 -0
  40. package/src/mcp/TEST_STEPS.md +75 -0
  41. package/src/mcp/server.js +612 -0
  42. package/src/mcp/tools.js +1367 -0
  43. package/src/mcp/transport/http.js +95 -0
  44. package/src/mcp/transport/stdio.js +20 -0
  45. package/src/preload.js +27 -0
  46. package/src/server/agent-email-api.js +120 -0
  47. package/src/server/agent-manager.js +580 -0
  48. package/src/server/email-handler.js +329 -0
  49. package/src/server/feishu-handler.js +249 -0
  50. package/src/server/hermes-api-client.js +166 -0
  51. package/src/server/hermes-discovery.js +80 -0
  52. package/src/server/hermes-handler.js +287 -0
  53. package/src/server/openclaw-handler-cli.js +131 -0
  54. package/src/server/openclaw-websocket-handler.js +1290 -0
  55. package/src/server/oss.js +186 -0
  56. package/src/server/owner-intervention-notifier.js +320 -0
  57. package/src/server/release-page.html +204 -0
  58. package/src/server/telegram-handler.js +208 -0
  59. package/src/server/voko-email-handler.js +68 -0
  60. package/src/server/wechat-handler.js +439 -0
  61. package/src/workers/agent-worker.js +378 -0
  62. package/src/workers/message-content.js +51 -0
@@ -0,0 +1,580 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const os = require('os');
4
+ const { discoverHermes } = require('./hermes-discovery');
5
+ const { getHermesProfilePath } = require('../core/hermes-paths');
6
+
7
+ class AgentManager {
8
+ constructor(databaseAPI) {
9
+ this.databaseAPI = databaseAPI || null;
10
+ this.openclawConfigPath = path.join(os.homedir(), '.openclaw', 'openclaw.json');
11
+ this.channelConfigPath = path.join(__dirname, '..', '..', 'config', 'channel_config.json');
12
+ }
13
+
14
+ // 读取 openclaw.json 配置
15
+ _readOpenclawConfig() {
16
+ try {
17
+ const content = fs.readFileSync(this.openclawConfigPath, 'utf-8');
18
+ return JSON.parse(content);
19
+ } catch (err) {
20
+ console.error('[AgentManager] 读取 openclaw.json 失败:', err.message);
21
+ return { agents: { list: [] } };
22
+ }
23
+ }
24
+
25
+ // 写入 openclaw.json 配置
26
+ _writeOpenclawConfig(config) {
27
+ try {
28
+ fs.writeFileSync(this.openclawConfigPath, JSON.stringify(config, null, 2), 'utf-8');
29
+ return true;
30
+ } catch (err) {
31
+ console.error('[AgentManager] 写入 openclaw.json 失败:', err.message);
32
+ return false;
33
+ }
34
+ }
35
+
36
+ // 读取 OpenClaw 默认模型
37
+ _getDefaultModel() {
38
+ try {
39
+ const config = this._readOpenclawConfig();
40
+ // agents.defaults.model.primary (OpenClaw 常用结构)
41
+ if (config.agents?.defaults?.model?.primary) return config.agents.defaults.model.primary;
42
+ // auth.profiles 中的模型
43
+ const profiles = config.auth?.profiles || {};
44
+ for (const key of Object.keys(profiles)) {
45
+ const profile = profiles[key];
46
+ if (profile.model?.primary) return profile.model.primary;
47
+ if (profile.model) return profile.model;
48
+ }
49
+ // 尝试 agents.defaultModel
50
+ if (config.agents?.defaultModel) return config.agents.defaultModel;
51
+ // 尝试顶层 gateway.model
52
+ if (config.gateway?.model) return config.gateway.model;
53
+ return null;
54
+ } catch (e) {
55
+ return null;
56
+ }
57
+ }
58
+
59
+ getDefaultModel() {
60
+ return this._getDefaultModel();
61
+ }
62
+
63
+ // 读取 channel_config(优先 DB,首次运行从 JSON 迁移)
64
+ _readChannelConfig() {
65
+ try {
66
+ if (this.databaseAPI) {
67
+ const fromDb = this.databaseAPI.getConfigFromDb();
68
+ if (fromDb) return fromDb;
69
+ }
70
+ // fallback: 从 JSON 文件读取(首次迁移)
71
+ const content = fs.readFileSync(this.channelConfigPath, 'utf-8');
72
+ return JSON.parse(content);
73
+ } catch (err) {
74
+ console.error('[AgentManager] 读取 channel_config 失败:', err.message);
75
+ return {};
76
+ }
77
+ }
78
+
79
+ // 写入 channel_config(仅 DB,不落明文文件)
80
+ _writeChannelConfig(config) {
81
+ try {
82
+ if (this.databaseAPI) this.databaseAPI.saveConfigToDb(config);
83
+ return true;
84
+ } catch (err) {
85
+ console.error('[AgentManager] 写入 channel_config 失败:', err.message);
86
+ return false;
87
+ }
88
+ }
89
+
90
+ // 展开 ~ 为用户家目录
91
+ _expandPath(p) {
92
+ if (p && p.startsWith('~')) {
93
+ return path.join(os.homedir(), p.slice(1));
94
+ }
95
+ return p;
96
+ }
97
+
98
+ // 获取 routingRules
99
+ getRoutingRules() {
100
+ const config = this._readChannelConfig();
101
+ return config.routingRules || [];
102
+ }
103
+
104
+ // 获取当前 voko 对接的 agent
105
+ getVokoAgent() {
106
+ const rules = this.getRoutingRules();
107
+ const defaultRule = rules.find(r => r.wsChannel === 'default');
108
+ return defaultRule ? defaultRule.agentId : null;
109
+ }
110
+
111
+ // 设置 voko 对接的 agent
112
+ setVokoAgent(agentId) {
113
+ const config = this._readChannelConfig();
114
+ if (!config.routingRules) {
115
+ config.routingRules = [];
116
+ }
117
+ const idx = config.routingRules.findIndex(r => r.wsChannel === 'default');
118
+ if (idx >= 0) {
119
+ config.routingRules[idx] = { wsChannel: 'default', sessionPrefix: `agent:${agentId}:`, agentId };
120
+ } else {
121
+ config.routingRules.push({ wsChannel: 'default', sessionPrefix: `agent:${agentId}:`, agentId });
122
+ }
123
+ return this._writeChannelConfig(config);
124
+ }
125
+
126
+ // 列出所有 agent
127
+ listAgents() {
128
+ const config = this._readOpenclawConfig();
129
+ const openclawAgents = config?.agents?.list || [];
130
+ const vokoAgentId = this.getVokoAgent();
131
+
132
+ // 发现 Hermes profiles
133
+ let hermesAgents = [];
134
+ try {
135
+ hermesAgents = discoverHermes();
136
+ } catch (err) {
137
+ console.error('[AgentManager] Hermes 发现失败:', err.message);
138
+ }
139
+
140
+ // 读取 channel_config 中的 Hermes 端口配置
141
+ const channelCfg = this._readChannelConfig();
142
+ const hermesProfiles = channelCfg?.hermes_config?.profiles || {};
143
+
144
+ const result = {
145
+ agents: [
146
+ // OpenClaw agents
147
+ ...openclawAgents.map(a => ({
148
+ id: a.id,
149
+ name: a.name || a.id,
150
+ workspace: a.workspace,
151
+ agentDir: a.agentDir,
152
+ model: a.model,
153
+ skills: a.skills || [],
154
+ tools: a.tools || { allow: [], deny: [] },
155
+ isVoko: a.id === vokoAgentId,
156
+ backend: 'openclaw'
157
+ })),
158
+ // Hermes agents
159
+ ...hermesAgents.map(p => ({
160
+ id: p.name,
161
+ name: p.name,
162
+ model: p.model,
163
+ isDefault: p.isDefault,
164
+ backend: 'hermes',
165
+ port: hermesProfiles[p.name]?.port || null
166
+ }))
167
+ ],
168
+ openclawFound: !!config?.agents,
169
+ hermesFound: hermesAgents.length > 0
170
+ };
171
+
172
+ // 合并 SQLite agents 表中已发布的 agent(去重)
173
+ try {
174
+ if (this.databaseAPI) {
175
+ const dbRows = this.databaseAPI.query(
176
+ "SELECT agent_id, agent_name, backend_type, publish_status FROM agents"
177
+ );
178
+ const existingIds = new Set(result.agents.map(a => a.id));
179
+ for (const r of dbRows) {
180
+ if (!existingIds.has(r.agent_id)) {
181
+ result.agents.push({
182
+ id: r.agent_id,
183
+ name: r.agent_name || r.agent_id,
184
+ backend: r.backend_type || 'others',
185
+ workspace: null,
186
+ model: null,
187
+ skills: [],
188
+ tools: { allow: [], deny: [] },
189
+ isVoko: false
190
+ });
191
+ }
192
+ }
193
+ }
194
+ } catch (e) {
195
+ console.error('[AgentManager] 读取 DB agents 失败:', e.message);
196
+ }
197
+
198
+ return result;
199
+ }
200
+
201
+ // 读取单个 agent 详情
202
+ getAgentDetail(agentId) {
203
+ // 先查 OpenClaw
204
+ const config = this._readOpenclawConfig();
205
+ const agents = config.agents?.list || [];
206
+ const agent = agents.find(a => a.id === agentId);
207
+ if (agent) {
208
+ let description = '';
209
+ if (agent.workspace) {
210
+ const workspacePath = this._expandPath(agent.workspace);
211
+ const agentsMdPath = path.join(workspacePath, 'AGENTS.md');
212
+ if (fs.existsSync(agentsMdPath)) {
213
+ const content = fs.readFileSync(agentsMdPath, 'utf-8');
214
+ const lines = content.split('\n');
215
+ for (const line of lines) {
216
+ const trimmed = line.trim();
217
+ if (trimmed && !trimmed.startsWith('#')) {
218
+ description = trimmed;
219
+ break;
220
+ }
221
+ }
222
+ }
223
+ }
224
+ return {
225
+ id: agent.id,
226
+ name: agent.name || agent.id,
227
+ workspace: agent.workspace,
228
+ model: agent.model,
229
+ skills: agent.skills || [],
230
+ tools: agent.tools || { allow: [], deny: [] },
231
+ description,
232
+ isVoko: agent.id === this.getVokoAgent(),
233
+ backend: 'openclaw'
234
+ };
235
+ }
236
+
237
+ // 再查 Hermes
238
+ try {
239
+ const hermesProfiles = discoverHermes();
240
+ const profile = hermesProfiles.find(p => p.name === agentId);
241
+ if (profile) {
242
+ const profilePath = getHermesProfilePath(profile.name);
243
+ // 解析 SOUL.md 获取权限信息
244
+ let hermesTools = { allow: [], deny: [] };
245
+ let hermesSkills = [];
246
+ const soulPath = path.join(profilePath, 'SOUL.md');
247
+ try {
248
+ if (fs.existsSync(soulPath)) {
249
+ const soulContent = fs.readFileSync(soulPath, 'utf-8');
250
+ const permMatch = soulContent.match(/##\s*(权限|Permission|Permissions)[\s\S]*?(?=##|$)/);
251
+ if (permMatch) {
252
+ const permSection = permMatch[0];
253
+ for (const line of permSection.split('\n')) {
254
+ const trimmed = line.trim();
255
+ if (trimmed.includes('✅')) {
256
+ const text = trimmed.replace(/^[-*\s]*✅\s*/, '');
257
+ // 取括号前的内容,然后按 /、, 分割
258
+ const beforeBracket = text.replace(/[((【][\s\S]*$/, '');
259
+ const parts = beforeBracket.split(/[/、,]/);
260
+ for (const part of parts) {
261
+ const name = part.trim();
262
+ if (name && name.length > 1) {
263
+ // 只取第一个词
264
+ const firstWord = name.split(/\s+/)[0];
265
+ // 判断:如果第一个词后紧跟着 "skill" → 技能,否则→工具
266
+ const restAfter = name.substring(firstWord.length).trim();
267
+ const isSkillItem = /^skill/i.test(restAfter);
268
+ if (isSkillItem) {
269
+ if (firstWord && !hermesSkills.includes(firstWord)) hermesSkills.push(firstWord);
270
+ } else if (firstWord.length > 1) {
271
+ if (!hermesTools.allow.includes(firstWord)) hermesTools.allow.push(firstWord);
272
+ }
273
+ }
274
+ }
275
+ }
276
+ if (trimmed.includes('❌')) {
277
+ const text = trimmed.replace(/^[-*\s]*❌\s*/, '');
278
+ const beforeBracket = text.replace(/[((【][\s\S]*$/, '');
279
+ const parts = beforeBracket.split(/[/、,]/);
280
+ for (const part of parts) {
281
+ let name = part.trim();
282
+ // 去掉后面多余的中文描述
283
+ name = name.replace(/\s+(skill|toolset).*$/i, '').trim();
284
+ // 只取第一个词(中文描述前)
285
+ name = name.split(/\s+/)[0];
286
+ if (name && name.length > 1 && !hermesTools.deny.includes(name)) {
287
+ hermesTools.deny.push(name);
288
+ }
289
+ }
290
+ }
291
+ }
292
+ }
293
+ }
294
+ } catch (e) {
295
+ console.error('[AgentManager] 解析 SOUL.md 权限失败:', e.message);
296
+ }
297
+ return {
298
+ id: profile.name,
299
+ name: profile.name,
300
+ model: profile.model,
301
+ description: `Hermes Agent - ${profile.name}${profile.isDefault ? ' (默认)' : ''}`,
302
+ backend: 'hermes',
303
+ profilePath,
304
+ skills: hermesSkills,
305
+ tools: hermesTools
306
+ };
307
+ }
308
+ } catch (err) {
309
+ console.error('[AgentManager] Hermes 详情查询失败:', err.message);
310
+ }
311
+
312
+ // 回退:从 SQLite agents 表查询(MCP 注册的 agent)
313
+ try {
314
+ if (this.databaseAPI) {
315
+ const rows = this.databaseAPI.query(
316
+ "SELECT agent_id, agent_name, description, short_description, category, category_label, backend_type FROM agents WHERE agent_id = '" + agentId.replace(/'/g, "''") + "' LIMIT 1"
317
+ );
318
+ if (rows && rows.length > 0) {
319
+ const r = rows[0];
320
+ return {
321
+ id: r.agent_id,
322
+ name: r.agent_name || r.agent_id,
323
+ description: r.description || r.short_description || '',
324
+ backend: r.backend_type || 'others',
325
+ workspace: null,
326
+ model: null,
327
+ skills: [],
328
+ tools: { allow: [], deny: [] },
329
+ isVoko: false
330
+ };
331
+ }
332
+ }
333
+ } catch (e) {
334
+ console.error('[AgentManager] DB agent 详情查询失败:', e.message);
335
+ }
336
+
337
+ return null;
338
+ }
339
+
340
+ // 获取 agent workspace 下的核心文件列表
341
+ getAgentFiles(agentId) {
342
+ const config = this._readOpenclawConfig();
343
+ const agents = config.agents?.list || [];
344
+ const agent = agents.find(a => a.id === agentId);
345
+ if (!agent || !agent.workspace) {
346
+ return [];
347
+ }
348
+
349
+ const workspacePath = this._expandPath(agent.workspace);
350
+ const coreFiles = ['AGENTS.md', 'SOUL.md', 'IDENTITY.md', 'TOOLS.md', 'USER.md', 'knowledge.md'];
351
+
352
+ try {
353
+ if (!fs.existsSync(workspacePath)) {
354
+ return [];
355
+ }
356
+
357
+ const files = [];
358
+ for (const file of coreFiles) {
359
+ const filePath = path.join(workspacePath, file);
360
+ if (fs.existsSync(filePath)) {
361
+ const stat = fs.statSync(filePath);
362
+ files.push({
363
+ name: file,
364
+ path: filePath,
365
+ size: stat.size,
366
+ modified: stat.mtime
367
+ });
368
+ }
369
+ }
370
+
371
+ // 尝试读取 skills 目录
372
+ const skillsDir = path.join(workspacePath, 'skills');
373
+ if (fs.existsSync(skillsDir)) {
374
+ const skillDirs = fs.readdirSync(skillsDir).filter(d => {
375
+ const subPath = path.join(skillsDir, d);
376
+ return fs.statSync(subPath).isDirectory();
377
+ });
378
+ for (const skillDir of skillDirs) {
379
+ const skillFile = path.join(skillsDir, skillDir, 'SKILL.md');
380
+ if (fs.existsSync(skillFile)) {
381
+ const stat = fs.statSync(skillFile);
382
+ files.push({
383
+ name: `skills/${skillDir}/SKILL.md`,
384
+ path: skillFile,
385
+ size: stat.size,
386
+ modified: stat.mtime
387
+ });
388
+ }
389
+ }
390
+ }
391
+
392
+ return files;
393
+ } catch (err) {
394
+ console.error('[AgentManager] 获取 agent 文件列表失败:', err.message);
395
+ return [];
396
+ }
397
+ }
398
+
399
+ // 读取单个文件内容
400
+ readFile(agentId, filename) {
401
+ const config = this._readOpenclawConfig();
402
+ const agents = config.agents?.list || [];
403
+ const agent = agents.find(a => a.id === agentId);
404
+ if (!agent || !agent.workspace) {
405
+ throw new Error('Agent not found');
406
+ }
407
+
408
+ const workspacePath = this._expandPath(agent.workspace);
409
+ const filePath = path.join(workspacePath, filename);
410
+
411
+ // 安全检查:不允许路径遍历
412
+ if (!filePath.startsWith(workspacePath)) {
413
+ throw new Error('Invalid path');
414
+ }
415
+
416
+ try {
417
+ return fs.readFileSync(filePath, 'utf-8');
418
+ } catch (err) {
419
+ console.error('[AgentManager] 读取文件失败:', err.message);
420
+ throw err;
421
+ }
422
+ }
423
+
424
+ // 写入单个文件
425
+ writeFile(agentId, filename, content) {
426
+ const config = this._readOpenclawConfig();
427
+ const agents = config.agents?.list || [];
428
+ const agent = agents.find(a => a.id === agentId);
429
+ if (!agent || !agent.workspace) {
430
+ throw new Error('Agent not found');
431
+ }
432
+
433
+ const workspacePath = this._expandPath(agent.workspace);
434
+ const filePath = path.join(workspacePath, filename);
435
+
436
+ // 安全检查:不允许路径遍历
437
+ if (!filePath.startsWith(workspacePath)) {
438
+ throw new Error('Invalid path');
439
+ }
440
+
441
+ try {
442
+ fs.writeFileSync(filePath, content, 'utf-8');
443
+ return true;
444
+ } catch (err) {
445
+ console.error('[AgentManager] 写入文件失败:', err.message);
446
+ throw err;
447
+ }
448
+ }
449
+
450
+ // 创建新 agent
451
+ createAgent(agentConfig) {
452
+ const { id, name, desc, workspace, model } = agentConfig;
453
+ if (!id || !workspace) {
454
+ throw new Error('id 和 workspace 是必填项');
455
+ }
456
+
457
+ const config = this._readOpenclawConfig();
458
+ if (!config.agents) {
459
+ config.agents = { list: [] };
460
+ }
461
+
462
+ // 检查是否已存在
463
+ if (config.agents.list.find(a => a.id === id)) {
464
+ throw new Error('Agent ID 已存在');
465
+ }
466
+
467
+ // 创建 workspace 目录
468
+ const workspacePath = this._expandPath(workspace);
469
+ fs.mkdirSync(workspacePath, { recursive: true });
470
+
471
+ // 构建 AGENTS.md 内容
472
+ let agentsContent = `# ${name || id} Agent`;
473
+ if (desc) {
474
+ agentsContent += `\n\n> ${desc}`;
475
+ }
476
+ agentsContent += `
477
+
478
+ 你是一个有用的 AI 助手。
479
+
480
+ ## 核心能力
481
+ - ...
482
+
483
+ ## 限制
484
+ - ...
485
+ `;
486
+
487
+ // 创建默认文件
488
+ const defaultFiles = {
489
+ 'AGENTS.md': agentsContent,
490
+ 'SOUL.md': `# ${name || id} - 灵魂设定
491
+
492
+ ## 角色定义
493
+ ...
494
+
495
+ ## 沟通风格
496
+ ...
497
+ `,
498
+ 'IDENTITY.md': `# ${name || id}
499
+
500
+ ## 基本信息
501
+ - 名称: ${name || id}
502
+ - 角色: AI 助手
503
+
504
+ ## 个性
505
+ ...
506
+ `,
507
+ 'TOOLS.md': `# 工具使用规范
508
+
509
+ ## 可用工具
510
+ ...
511
+ `,
512
+ 'USER.md': `# 用户画像
513
+
514
+ ## 目标用户
515
+ ...
516
+ `
517
+ };
518
+
519
+ for (const [filename, fileContent] of Object.entries(defaultFiles)) {
520
+ const filePath = path.join(workspacePath, filename);
521
+ fs.writeFileSync(filePath, fileContent, 'utf-8');
522
+ }
523
+
524
+ // 添加到 openclaw.json
525
+ config.agents.list.push({
526
+ id,
527
+ name: name || id,
528
+ workspace,
529
+ model: model || null,
530
+ skills: []
531
+ });
532
+
533
+ this._writeOpenclawConfig(config);
534
+ return { id, name: name || id, workspace };
535
+ }
536
+
537
+ // 删除 agent
538
+ deleteAgent(agentId) {
539
+ const config = this._readOpenclawConfig();
540
+ const idx = config.agents?.list?.findIndex(a => a.id === agentId);
541
+ if (idx === undefined || idx < 0) {
542
+ throw new Error('Agent not found');
543
+ }
544
+
545
+ const agent = config.agents.list[idx];
546
+ config.agents.list.splice(idx, 1);
547
+ this._writeOpenclawConfig(config);
548
+
549
+ // 从 routingRules 移除
550
+ const channelConfig = this._readChannelConfig();
551
+ if (channelConfig.routingRules) {
552
+ channelConfig.routingRules = channelConfig.routingRules.filter(r => r.agentId !== agentId);
553
+ this._writeChannelConfig(channelConfig);
554
+ }
555
+
556
+ return true;
557
+ }
558
+
559
+ // 引入人工介入接口
560
+ injectOwnerCli(agentId) {
561
+ const config = this._readOpenclawConfig();
562
+ const agents = config.agents?.list || [];
563
+ const agent = agents.find(a => a.id === agentId);
564
+ if (!agent || !agent.workspace) {
565
+ throw new Error('Agent not found');
566
+ }
567
+ const workspacePath = this._expandPath(agent.workspace);
568
+ const scriptsDir = path.join(workspacePath, 'scripts');
569
+ const srcFile = path.join(__dirname, '..', '..', 'scripts', 'owner-intervention-cli.js');
570
+ const dstFile = path.join(scriptsDir, 'owner-intervention-cli.js');
571
+ if (!fs.existsSync(srcFile)) {
572
+ throw new Error('源文件不存在: scripts/owner-intervention-cli.js');
573
+ }
574
+ fs.mkdirSync(scriptsDir, { recursive: true });
575
+ fs.copyFileSync(srcFile, dstFile);
576
+ return true;
577
+ }
578
+ }
579
+
580
+ module.exports = AgentManager;