flashclaw 1.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.
Files changed (138) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +305 -0
  3. package/config/plugins.json +23 -0
  4. package/dist/agent-runner.d.ts +103 -0
  5. package/dist/agent-runner.d.ts.map +1 -0
  6. package/dist/agent-runner.js +530 -0
  7. package/dist/agent-runner.js.map +1 -0
  8. package/dist/cli.d.ts +7 -0
  9. package/dist/cli.d.ts.map +1 -0
  10. package/dist/cli.js +497 -0
  11. package/dist/cli.js.map +1 -0
  12. package/dist/commands.d.ts +68 -0
  13. package/dist/commands.d.ts.map +1 -0
  14. package/dist/commands.js +252 -0
  15. package/dist/commands.js.map +1 -0
  16. package/dist/config-schema.d.ts +21 -0
  17. package/dist/config-schema.d.ts.map +1 -0
  18. package/dist/config-schema.js +26 -0
  19. package/dist/config-schema.js.map +1 -0
  20. package/dist/config.d.ts +11 -0
  21. package/dist/config.d.ts.map +1 -0
  22. package/dist/config.js +36 -0
  23. package/dist/config.js.map +1 -0
  24. package/dist/core/api-client.d.ts +236 -0
  25. package/dist/core/api-client.d.ts.map +1 -0
  26. package/dist/core/api-client.js +369 -0
  27. package/dist/core/api-client.js.map +1 -0
  28. package/dist/core/memory.d.ts +291 -0
  29. package/dist/core/memory.d.ts.map +1 -0
  30. package/dist/core/memory.js +754 -0
  31. package/dist/core/memory.js.map +1 -0
  32. package/dist/core/model-capabilities.d.ts +45 -0
  33. package/dist/core/model-capabilities.d.ts.map +1 -0
  34. package/dist/core/model-capabilities.js +85 -0
  35. package/dist/core/model-capabilities.js.map +1 -0
  36. package/dist/db.d.ts +103 -0
  37. package/dist/db.d.ts.map +1 -0
  38. package/dist/db.js +380 -0
  39. package/dist/db.js.map +1 -0
  40. package/dist/errors.d.ts +22 -0
  41. package/dist/errors.d.ts.map +1 -0
  42. package/dist/errors.js +44 -0
  43. package/dist/errors.js.map +1 -0
  44. package/dist/health.d.ts +27 -0
  45. package/dist/health.d.ts.map +1 -0
  46. package/dist/health.js +55 -0
  47. package/dist/health.js.map +1 -0
  48. package/dist/index.d.ts +11 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +1181 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/logger.d.ts +9 -0
  53. package/dist/logger.d.ts.map +1 -0
  54. package/dist/logger.js +19 -0
  55. package/dist/logger.js.map +1 -0
  56. package/dist/message-queue.d.ts +69 -0
  57. package/dist/message-queue.d.ts.map +1 -0
  58. package/dist/message-queue.js +198 -0
  59. package/dist/message-queue.js.map +1 -0
  60. package/dist/metrics.d.ts +46 -0
  61. package/dist/metrics.d.ts.map +1 -0
  62. package/dist/metrics.js +101 -0
  63. package/dist/metrics.js.map +1 -0
  64. package/dist/paths.d.ts +81 -0
  65. package/dist/paths.d.ts.map +1 -0
  66. package/dist/paths.js +127 -0
  67. package/dist/paths.js.map +1 -0
  68. package/dist/plugins/index.d.ts +9 -0
  69. package/dist/plugins/index.d.ts.map +1 -0
  70. package/dist/plugins/index.js +13 -0
  71. package/dist/plugins/index.js.map +1 -0
  72. package/dist/plugins/installer.d.ts +120 -0
  73. package/dist/plugins/installer.d.ts.map +1 -0
  74. package/dist/plugins/installer.js +1008 -0
  75. package/dist/plugins/installer.js.map +1 -0
  76. package/dist/plugins/loader.d.ts +37 -0
  77. package/dist/plugins/loader.d.ts.map +1 -0
  78. package/dist/plugins/loader.js +429 -0
  79. package/dist/plugins/loader.js.map +1 -0
  80. package/dist/plugins/manager.d.ts +72 -0
  81. package/dist/plugins/manager.d.ts.map +1 -0
  82. package/dist/plugins/manager.js +187 -0
  83. package/dist/plugins/manager.js.map +1 -0
  84. package/dist/plugins/types.d.ts +101 -0
  85. package/dist/plugins/types.d.ts.map +1 -0
  86. package/dist/plugins/types.js +12 -0
  87. package/dist/plugins/types.js.map +1 -0
  88. package/dist/session-tracker.d.ts +81 -0
  89. package/dist/session-tracker.d.ts.map +1 -0
  90. package/dist/session-tracker.js +228 -0
  91. package/dist/session-tracker.js.map +1 -0
  92. package/dist/task-scheduler.d.ts +47 -0
  93. package/dist/task-scheduler.d.ts.map +1 -0
  94. package/dist/task-scheduler.js +331 -0
  95. package/dist/task-scheduler.js.map +1 -0
  96. package/dist/types.d.ts +57 -0
  97. package/dist/types.d.ts.map +1 -0
  98. package/dist/types.js +2 -0
  99. package/dist/types.js.map +1 -0
  100. package/dist/utils/env-substitute.d.ts +63 -0
  101. package/dist/utils/env-substitute.d.ts.map +1 -0
  102. package/dist/utils/env-substitute.js +133 -0
  103. package/dist/utils/env-substitute.js.map +1 -0
  104. package/dist/utils/log-rotate.d.ts +19 -0
  105. package/dist/utils/log-rotate.d.ts.map +1 -0
  106. package/dist/utils/log-rotate.js +85 -0
  107. package/dist/utils/log-rotate.js.map +1 -0
  108. package/dist/utils/rate-limiter.d.ts +38 -0
  109. package/dist/utils/rate-limiter.d.ts.map +1 -0
  110. package/dist/utils/rate-limiter.js +79 -0
  111. package/dist/utils/rate-limiter.js.map +1 -0
  112. package/dist/utils/retry.d.ts +10 -0
  113. package/dist/utils/retry.d.ts.map +1 -0
  114. package/dist/utils/retry.js +47 -0
  115. package/dist/utils/retry.js.map +1 -0
  116. package/dist/utils.d.ts +86 -0
  117. package/dist/utils.d.ts.map +1 -0
  118. package/dist/utils.js +218 -0
  119. package/dist/utils.js.map +1 -0
  120. package/package.json +78 -0
  121. package/plugins/cancel-task/index.ts +161 -0
  122. package/plugins/cancel-task/plugin.json +9 -0
  123. package/plugins/feishu/index.ts +944 -0
  124. package/plugins/feishu/plugin.json +29 -0
  125. package/plugins/list-tasks/index.ts +150 -0
  126. package/plugins/list-tasks/plugin.json +9 -0
  127. package/plugins/memory/index.ts +190 -0
  128. package/plugins/memory/plugin.json +7 -0
  129. package/plugins/pause-task/index.ts +95 -0
  130. package/plugins/pause-task/plugin.json +8 -0
  131. package/plugins/register-group/index.ts +147 -0
  132. package/plugins/register-group/plugin.json +7 -0
  133. package/plugins/resume-task/index.ts +92 -0
  134. package/plugins/resume-task/plugin.json +8 -0
  135. package/plugins/schedule-task/index.ts +248 -0
  136. package/plugins/schedule-task/plugin.json +9 -0
  137. package/plugins/send-message/index.ts +75 -0
  138. package/plugins/send-message/plugin.json +9 -0
@@ -0,0 +1,29 @@
1
+ {
2
+ "name": "feishu",
3
+ "version": "1.0.0",
4
+ "type": "channel",
5
+ "description": "飞书通讯渠道插件",
6
+ "author": "FlashClaw",
7
+ "main": "index.ts",
8
+ "dependencies": [
9
+ "send-message",
10
+ "schedule-task",
11
+ "list-tasks",
12
+ "cancel-task",
13
+ "memory"
14
+ ],
15
+ "config": {
16
+ "appId": {
17
+ "type": "string",
18
+ "required": true,
19
+ "env": "FEISHU_APP_ID"
20
+ },
21
+ "appSecret": {
22
+ "type": "string",
23
+ "required": true,
24
+ "env": "FEISHU_APP_SECRET"
25
+ }
26
+ }
27
+ }
28
+
29
+
@@ -0,0 +1,150 @@
1
+ /**
2
+ * FlashClaw 插件 - 列出定时任务
3
+ * 查看当前群组或所有群组的定时任务
4
+ */
5
+
6
+ import { ToolPlugin, ToolContext, ToolResult } from '../../src/plugins/types.js';
7
+ import { getTasksForGroup, getAllTasks, getTaskRunLogs } from '../../src/db.js';
8
+ import { TIMEZONE, MAIN_GROUP_FOLDER } from '../../src/config.js';
9
+ import { ScheduledTask } from '../../src/types.js';
10
+
11
+ /**
12
+ * 列出任务参数
13
+ */
14
+ interface ListTasksParams {
15
+ /** 是否列出所有群组的任务(仅 main 群组可用) */
16
+ all?: boolean;
17
+ /** 任务状态过滤 */
18
+ status?: 'active' | 'paused' | 'completed' | 'all';
19
+ /** 是否包含最近运行记录 */
20
+ includeRunLogs?: boolean;
21
+ }
22
+
23
+ /**
24
+ * 格式化任务信息
25
+ */
26
+ function formatTask(task: ScheduledTask, includeRunLogs: boolean = false): Record<string, unknown> {
27
+ const formatted: Record<string, unknown> = {
28
+ id: task.id,
29
+ prompt: task.prompt.length > 100 ? task.prompt.slice(0, 100) + '...' : task.prompt,
30
+ scheduleType: task.schedule_type,
31
+ scheduleValue: task.schedule_value,
32
+ status: task.status,
33
+ groupFolder: task.group_folder,
34
+ chatId: task.chat_jid,
35
+ contextMode: task.context_mode,
36
+ createdAt: formatDateTime(task.created_at)
37
+ };
38
+
39
+ if (task.next_run) {
40
+ formatted.nextRun = formatDateTime(task.next_run);
41
+ }
42
+
43
+ if (task.last_run) {
44
+ formatted.lastRun = formatDateTime(task.last_run);
45
+ formatted.lastResult = task.last_result;
46
+ }
47
+
48
+ // 获取最近运行记录
49
+ if (includeRunLogs) {
50
+ const logs = getTaskRunLogs(task.id, 3);
51
+ if (logs.length > 0) {
52
+ formatted.recentRuns = logs.map(log => ({
53
+ runAt: formatDateTime(log.run_at),
54
+ status: log.status,
55
+ durationMs: log.duration_ms,
56
+ result: log.result ? (log.result.length > 50 ? log.result.slice(0, 50) + '...' : log.result) : null,
57
+ error: log.error
58
+ }));
59
+ }
60
+ }
61
+
62
+ return formatted;
63
+ }
64
+
65
+ /**
66
+ * 格式化日期时间为可读格式
67
+ */
68
+ function formatDateTime(isoString: string): string {
69
+ const date = new Date(isoString);
70
+ return date.toLocaleString('zh-CN', { timeZone: TIMEZONE });
71
+ }
72
+
73
+ const plugin: ToolPlugin = {
74
+ name: 'list_tasks',
75
+ version: '1.0.0',
76
+ description: '列出定时任务,查看任务状态和运行记录',
77
+
78
+ schema: {
79
+ name: 'list_tasks',
80
+ description: '列出定时任务。默认只显示当前群组的任务,main 群组可以查看所有任务。',
81
+ input_schema: {
82
+ type: 'object',
83
+ properties: {
84
+ all: {
85
+ type: 'boolean',
86
+ description: '是否列出所有群组的任务(仅 main 群组有权限)'
87
+ },
88
+ status: {
89
+ type: 'string',
90
+ enum: ['active', 'paused', 'completed', 'all'],
91
+ description: '按状态过滤任务。默认显示所有状态'
92
+ },
93
+ includeRunLogs: {
94
+ type: 'boolean',
95
+ description: '是否包含最近 3 次运行记录'
96
+ }
97
+ }
98
+ }
99
+ },
100
+
101
+ async execute(params: unknown, context: ToolContext): Promise<ToolResult> {
102
+ const { all = false, status = 'all', includeRunLogs = false } = (params || {}) as ListTasksParams;
103
+
104
+ const isMainGroup = context.groupId === MAIN_GROUP_FOLDER;
105
+
106
+ // 获取任务列表
107
+ let tasks: ScheduledTask[];
108
+
109
+ if (all && isMainGroup) {
110
+ // main 群组可以查看所有任务
111
+ tasks = getAllTasks();
112
+ } else if (all && !isMainGroup) {
113
+ return {
114
+ success: false,
115
+ error: '只有 main 群组可以查看所有群组的任务'
116
+ };
117
+ } else {
118
+ // 只查看当前群组的任务
119
+ tasks = getTasksForGroup(context.groupId);
120
+ }
121
+
122
+ // 按状态过滤
123
+ if (status !== 'all') {
124
+ tasks = tasks.filter(task => task.status === status);
125
+ }
126
+
127
+ // 格式化任务列表
128
+ const formattedTasks = tasks.map(task => formatTask(task, includeRunLogs));
129
+
130
+ // 统计信息
131
+ const stats = {
132
+ total: tasks.length,
133
+ active: tasks.filter(t => t.status === 'active').length,
134
+ paused: tasks.filter(t => t.status === 'paused').length,
135
+ completed: tasks.filter(t => t.status === 'completed').length
136
+ };
137
+
138
+ return {
139
+ success: true,
140
+ data: {
141
+ tasks: formattedTasks,
142
+ stats,
143
+ scope: all ? 'all_groups' : 'current_group',
144
+ groupId: context.groupId
145
+ }
146
+ };
147
+ }
148
+ };
149
+
150
+ export default plugin;
@@ -0,0 +1,9 @@
1
+ {
2
+ "name": "list-tasks",
3
+ "version": "1.0.0",
4
+ "type": "tool",
5
+ "description": "列出定时任务",
6
+ "main": "index.ts"
7
+ }
8
+
9
+
@@ -0,0 +1,190 @@
1
+ /**
2
+ * FlashClaw 插件 - 长期记忆管理
3
+ * 提供 remember 和 recall 功能
4
+ */
5
+
6
+ import { ToolPlugin, ToolContext, ToolResult } from '../../src/plugins/types.js';
7
+ import { getMemoryManager } from '../../src/core/memory.js';
8
+
9
+ /**
10
+ * 记忆操作参数
11
+ */
12
+ interface MemoryParams {
13
+ /** 操作类型:remember(记住)或 recall(回忆) */
14
+ action: 'remember' | 'recall';
15
+ /** 记忆键(remember 必需,recall 可选) */
16
+ key?: string;
17
+ /** 记忆值(remember 必需) */
18
+ value?: string;
19
+ /** 作用域:user(用户级别,跨会话共享)或 group(会话级别,默认) */
20
+ scope?: 'user' | 'group';
21
+ }
22
+
23
+ const plugin: ToolPlugin = {
24
+ name: 'memory',
25
+ version: '1.0.0',
26
+ description: '长期记忆管理,可以记住和回忆重要信息',
27
+
28
+ schema: {
29
+ name: 'memory',
30
+ description: `管理长期记忆。支持两种操作:
31
+ - remember: 保存重要信息到长期记忆(用户偏好、重要事实等)
32
+ - recall: 回忆之前保存的信息
33
+
34
+ 支持两种作用域:
35
+ - user: 用户级别记忆,跨所有会话共享(推荐用于个人偏好)
36
+ - group: 会话级别记忆,仅在当前会话有效(默认)
37
+
38
+ 记忆会持久化到文件,跨会话保持。`,
39
+ input_schema: {
40
+ type: 'object',
41
+ properties: {
42
+ action: {
43
+ type: 'string',
44
+ enum: ['remember', 'recall'],
45
+ description: 'remember 保存信息,recall 回忆信息'
46
+ },
47
+ key: {
48
+ type: 'string',
49
+ description: '记忆的键名(如 "favorite_food"、"name")。recall 时留空则返回所有记忆'
50
+ },
51
+ value: {
52
+ type: 'string',
53
+ description: 'remember 时要保存的值'
54
+ },
55
+ scope: {
56
+ type: 'string',
57
+ enum: ['user', 'group'],
58
+ description: '作用域。user=用户级别(跨会话共享,适合个人偏好),group=会话级别(默认)'
59
+ }
60
+ },
61
+ required: ['action']
62
+ }
63
+ },
64
+
65
+ async execute(params: unknown, context: ToolContext): Promise<ToolResult> {
66
+ const { action, key, value, scope = 'user' } = params as MemoryParams;
67
+ const mm = getMemoryManager();
68
+
69
+ // 默认使用用户级别记忆(跨会话共享)
70
+ const isUserScope = scope === 'user';
71
+ const scopeLabel = isUserScope ? '用户' : '会话';
72
+
73
+ if (action === 'remember') {
74
+ // 记住信息
75
+ if (!key || typeof key !== 'string') {
76
+ return {
77
+ success: false,
78
+ error: 'remember 操作需要提供 key'
79
+ };
80
+ }
81
+
82
+ if (!value || typeof value !== 'string') {
83
+ return {
84
+ success: false,
85
+ error: 'remember 操作需要提供 value'
86
+ };
87
+ }
88
+
89
+ try {
90
+ if (isUserScope) {
91
+ mm.rememberUser(context.userId, key, value);
92
+ } else {
93
+ mm.remember(context.groupId, key, value);
94
+ }
95
+ return {
96
+ success: true,
97
+ data: {
98
+ action: 'remembered',
99
+ scope: scopeLabel,
100
+ key,
101
+ value,
102
+ message: `已记住(${scopeLabel}级别): ${key} = ${value}`
103
+ }
104
+ };
105
+ } catch (error) {
106
+ return {
107
+ success: false,
108
+ error: `保存记忆失败: ${error instanceof Error ? error.message : String(error)}`
109
+ };
110
+ }
111
+ }
112
+
113
+ if (action === 'recall') {
114
+ // 回忆信息
115
+ try {
116
+ // 默认同时查询用户级别和会话级别记忆
117
+ const userResult = mm.recallUser(context.userId, key);
118
+ const groupResult = mm.recall(context.groupId, key);
119
+
120
+ if (key) {
121
+ // 回忆特定键 - 优先返回用户级别,其次会话级别
122
+ const result = userResult || groupResult;
123
+ const foundScope = userResult ? '用户' : (groupResult ? '会话' : null);
124
+
125
+ if (result) {
126
+ return {
127
+ success: true,
128
+ data: {
129
+ action: 'recalled',
130
+ scope: foundScope,
131
+ key,
132
+ value: result
133
+ }
134
+ };
135
+ } else {
136
+ return {
137
+ success: true,
138
+ data: {
139
+ action: 'recalled',
140
+ key,
141
+ value: null,
142
+ message: `没有找到键为 "${key}" 的记忆`
143
+ }
144
+ };
145
+ }
146
+ } else {
147
+ // 回忆所有 - 合并用户级别和会话级别
148
+ const memories: string[] = [];
149
+ if (userResult) {
150
+ memories.push(`【用户记忆】\n${userResult}`);
151
+ }
152
+ if (groupResult) {
153
+ memories.push(`【会话记忆】\n${groupResult}`);
154
+ }
155
+
156
+ if (memories.length > 0) {
157
+ return {
158
+ success: true,
159
+ data: {
160
+ action: 'recalled',
161
+ memories: memories.join('\n\n')
162
+ }
163
+ };
164
+ } else {
165
+ return {
166
+ success: true,
167
+ data: {
168
+ action: 'recalled',
169
+ memories: null,
170
+ message: '没有保存的记忆'
171
+ }
172
+ };
173
+ }
174
+ }
175
+ } catch (error) {
176
+ return {
177
+ success: false,
178
+ error: `回忆失败: ${error instanceof Error ? error.message : String(error)}`
179
+ };
180
+ }
181
+ }
182
+
183
+ return {
184
+ success: false,
185
+ error: 'action 必须是 remember 或 recall'
186
+ };
187
+ }
188
+ };
189
+
190
+ export default plugin;
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "memory",
3
+ "version": "1.0.0",
4
+ "type": "tool",
5
+ "description": "长期记忆管理(remember/recall)",
6
+ "main": "index.ts"
7
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * FlashClaw 插件 - 暂停定时任务
3
+ */
4
+
5
+ import { ToolPlugin, ToolContext, ToolResult } from '../../src/plugins/types.js';
6
+ import { getTaskById, updateTask } from '../../src/db.js';
7
+ import { MAIN_GROUP_FOLDER } from '../../src/config.js';
8
+
9
+ interface PauseTaskParams {
10
+ task_id: string;
11
+ }
12
+
13
+ const plugin: ToolPlugin = {
14
+ name: 'pause_task',
15
+ version: '1.0.0',
16
+ description: '暂停定时任务',
17
+
18
+ schema: {
19
+ name: 'pause_task',
20
+ description: '暂停一个定时任务。暂停后任务不会执行,直到使用 resume_task 恢复。',
21
+ input_schema: {
22
+ type: 'object',
23
+ properties: {
24
+ task_id: {
25
+ type: 'string',
26
+ description: '要暂停的任务 ID,可以通过 list_tasks 获取'
27
+ }
28
+ },
29
+ required: ['task_id']
30
+ }
31
+ },
32
+
33
+ async execute(params: unknown, context: ToolContext): Promise<ToolResult> {
34
+ const { task_id } = params as PauseTaskParams;
35
+
36
+ if (!task_id || typeof task_id !== 'string') {
37
+ return {
38
+ success: false,
39
+ error: '任务 ID 不能为空'
40
+ };
41
+ }
42
+
43
+ const task = getTaskById(task_id);
44
+
45
+ if (!task) {
46
+ return {
47
+ success: false,
48
+ error: `任务不存在: ${task_id}`
49
+ };
50
+ }
51
+
52
+ // 权限检查
53
+ const isMainGroup = context.groupId === MAIN_GROUP_FOLDER;
54
+ const isOwnTask = task.group_folder === context.groupId;
55
+
56
+ if (!isMainGroup && !isOwnTask) {
57
+ return {
58
+ success: false,
59
+ error: '无权操作其他群组的任务'
60
+ };
61
+ }
62
+
63
+ if (task.status === 'paused') {
64
+ return {
65
+ success: false,
66
+ error: '任务已经是暂停状态'
67
+ };
68
+ }
69
+
70
+ if (task.status === 'completed') {
71
+ return {
72
+ success: false,
73
+ error: '已完成的任务无法暂停'
74
+ };
75
+ }
76
+
77
+ try {
78
+ updateTask(task_id, { status: 'paused' });
79
+ return {
80
+ success: true,
81
+ data: {
82
+ task_id,
83
+ message: '任务已暂停,使用 resume_task 可以恢复执行'
84
+ }
85
+ };
86
+ } catch (error) {
87
+ return {
88
+ success: false,
89
+ error: `暂停任务失败: ${error instanceof Error ? error.message : String(error)}`
90
+ };
91
+ }
92
+ }
93
+ };
94
+
95
+ export default plugin;
@@ -0,0 +1,8 @@
1
+ {
2
+ "name": "pause_task",
3
+ "version": "1.0.0",
4
+ "description": "暂停定时任务",
5
+ "type": "tool",
6
+ "main": "index.ts",
7
+ "dependencies": []
8
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * FlashClaw 插件 - 注册群组
3
+ * 允许 AI Agent 注册新的聊天群组
4
+ */
5
+
6
+ import { ToolPlugin, ToolContext, ToolResult } from '../../src/plugins/types.js';
7
+ import { MAIN_GROUP_FOLDER } from '../../src/config.js';
8
+ import * as fs from 'node:fs';
9
+ import * as path from 'node:path';
10
+
11
+ /**
12
+ * 注册群组参数
13
+ */
14
+ interface RegisterGroupParams {
15
+ /** 聊天 ID(如 "oc_xxxxxxxx") */
16
+ jid: string;
17
+ /** 群组显示名称 */
18
+ name: string;
19
+ /** 群组文件夹名称(小写,用连字符分隔) */
20
+ folder: string;
21
+ /** 触发词(如 "@Andy") */
22
+ trigger: string;
23
+ }
24
+
25
+ const plugin: ToolPlugin = {
26
+ name: 'register_group',
27
+ version: '1.0.0',
28
+ description: '注册新的聊天群组',
29
+
30
+ schema: {
31
+ name: 'register_group',
32
+ description: `注册新的聊天群组,使机器人可以响应该群组的消息。
33
+ 仅限 main 群组使用。文件夹名称应使用小写字母和连字符(如 "family-chat")。`,
34
+ input_schema: {
35
+ type: 'object',
36
+ properties: {
37
+ jid: {
38
+ type: 'string',
39
+ description: '聊天 ID(如 "oc_xxxxxxxx")'
40
+ },
41
+ name: {
42
+ type: 'string',
43
+ description: '群组显示名称'
44
+ },
45
+ folder: {
46
+ type: 'string',
47
+ description: '群组文件夹名称(小写,用连字符分隔)'
48
+ },
49
+ trigger: {
50
+ type: 'string',
51
+ description: '触发词(如 "@Andy")'
52
+ }
53
+ },
54
+ required: ['jid', 'name', 'folder', 'trigger']
55
+ }
56
+ },
57
+
58
+ async execute(params: unknown, context: ToolContext): Promise<ToolResult> {
59
+ const { jid, name, folder, trigger } = params as RegisterGroupParams;
60
+
61
+ // 权限检查:只有 main 群组可以注册新群组
62
+ if (context.groupId !== MAIN_GROUP_FOLDER) {
63
+ return {
64
+ success: false,
65
+ error: '只有 main 群组可以注册新群组'
66
+ };
67
+ }
68
+
69
+ // 参数验证
70
+ if (!jid || typeof jid !== 'string') {
71
+ return {
72
+ success: false,
73
+ error: 'jid(聊天 ID)不能为空'
74
+ };
75
+ }
76
+
77
+ if (!name || typeof name !== 'string') {
78
+ return {
79
+ success: false,
80
+ error: 'name(群组名称)不能为空'
81
+ };
82
+ }
83
+
84
+ if (!folder || typeof folder !== 'string') {
85
+ return {
86
+ success: false,
87
+ error: 'folder(文件夹名称)不能为空'
88
+ };
89
+ }
90
+
91
+ if (!trigger || typeof trigger !== 'string') {
92
+ return {
93
+ success: false,
94
+ error: 'trigger(触发词)不能为空'
95
+ };
96
+ }
97
+
98
+ // 验证 folder 格式
99
+ if (!/^[a-z0-9-]+$/.test(folder)) {
100
+ return {
101
+ success: false,
102
+ error: 'folder 只能包含小写字母、数字和连字符'
103
+ };
104
+ }
105
+
106
+ // 通过 IPC 写入注册请求
107
+ const ipcDir = path.join(process.cwd(), 'data', 'ipc', context.groupId, 'tasks');
108
+ fs.mkdirSync(ipcDir, { recursive: true });
109
+
110
+ const data = {
111
+ type: 'register_group',
112
+ jid,
113
+ name,
114
+ folder,
115
+ trigger,
116
+ timestamp: new Date().toISOString()
117
+ };
118
+
119
+ const filename = `${Date.now()}-${Math.random().toString(36).slice(2, 8)}.json`;
120
+ const filepath = path.join(ipcDir, filename);
121
+
122
+ try {
123
+ // 原子写入
124
+ const tempPath = `${filepath}.tmp`;
125
+ fs.writeFileSync(tempPath, JSON.stringify(data, null, 2));
126
+ fs.renameSync(tempPath, filepath);
127
+
128
+ return {
129
+ success: true,
130
+ data: {
131
+ jid,
132
+ name,
133
+ folder,
134
+ trigger,
135
+ message: `群组 "${name}" 已注册,机器人将开始响应该群组的消息`
136
+ }
137
+ };
138
+ } catch (error) {
139
+ return {
140
+ success: false,
141
+ error: `注册失败: ${error instanceof Error ? error.message : String(error)}`
142
+ };
143
+ }
144
+ }
145
+ };
146
+
147
+ export default plugin;
@@ -0,0 +1,7 @@
1
+ {
2
+ "name": "register_group",
3
+ "version": "1.0.0",
4
+ "type": "tool",
5
+ "description": "注册新的聊天群组",
6
+ "main": "index.ts"
7
+ }