opencode-bridge 2.9.0-beta

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 (237) hide show
  1. package/.env.example +131 -0
  2. package/LICENSE +674 -0
  3. package/README.md +1195 -0
  4. package/bin/opencode-bridge.js +31 -0
  5. package/dist/commands/effort.d.ts +9 -0
  6. package/dist/commands/effort.d.ts.map +1 -0
  7. package/dist/commands/effort.js +56 -0
  8. package/dist/commands/effort.js.map +1 -0
  9. package/dist/commands/parser.d.ts +37 -0
  10. package/dist/commands/parser.d.ts.map +1 -0
  11. package/dist/commands/parser.js +355 -0
  12. package/dist/commands/parser.js.map +1 -0
  13. package/dist/config.d.ts +91 -0
  14. package/dist/config.d.ts.map +1 -0
  15. package/dist/config.js +340 -0
  16. package/dist/config.js.map +1 -0
  17. package/dist/feishu/cards-stream.d.ts +65 -0
  18. package/dist/feishu/cards-stream.d.ts.map +1 -0
  19. package/dist/feishu/cards-stream.js +448 -0
  20. package/dist/feishu/cards-stream.js.map +1 -0
  21. package/dist/feishu/cards.d.ts +81 -0
  22. package/dist/feishu/cards.d.ts.map +1 -0
  23. package/dist/feishu/cards.js +560 -0
  24. package/dist/feishu/cards.js.map +1 -0
  25. package/dist/feishu/client.d.ts +132 -0
  26. package/dist/feishu/client.d.ts.map +1 -0
  27. package/dist/feishu/client.js +952 -0
  28. package/dist/feishu/client.js.map +1 -0
  29. package/dist/feishu/streamer.d.ts +30 -0
  30. package/dist/feishu/streamer.d.ts.map +1 -0
  31. package/dist/feishu/streamer.js +95 -0
  32. package/dist/feishu/streamer.js.map +1 -0
  33. package/dist/handlers/card-action.d.ts +12 -0
  34. package/dist/handlers/card-action.d.ts.map +1 -0
  35. package/dist/handlers/card-action.js +154 -0
  36. package/dist/handlers/card-action.js.map +1 -0
  37. package/dist/handlers/command.d.ts +76 -0
  38. package/dist/handlers/command.d.ts.map +1 -0
  39. package/dist/handlers/command.js +1773 -0
  40. package/dist/handlers/command.js.map +1 -0
  41. package/dist/handlers/discord.d.ts +78 -0
  42. package/dist/handlers/discord.d.ts.map +1 -0
  43. package/dist/handlers/discord.js +1832 -0
  44. package/dist/handlers/discord.js.map +1 -0
  45. package/dist/handlers/file-sender.d.ts +22 -0
  46. package/dist/handlers/file-sender.d.ts.map +1 -0
  47. package/dist/handlers/file-sender.js +183 -0
  48. package/dist/handlers/file-sender.js.map +1 -0
  49. package/dist/handlers/group.d.ts +21 -0
  50. package/dist/handlers/group.d.ts.map +1 -0
  51. package/dist/handlers/group.js +414 -0
  52. package/dist/handlers/group.js.map +1 -0
  53. package/dist/handlers/lifecycle.d.ts +17 -0
  54. package/dist/handlers/lifecycle.d.ts.map +1 -0
  55. package/dist/handlers/lifecycle.js +129 -0
  56. package/dist/handlers/lifecycle.js.map +1 -0
  57. package/dist/handlers/p2p.d.ts +44 -0
  58. package/dist/handlers/p2p.d.ts.map +1 -0
  59. package/dist/handlers/p2p.js +625 -0
  60. package/dist/handlers/p2p.js.map +1 -0
  61. package/dist/index.d.ts +33 -0
  62. package/dist/index.d.ts.map +1 -0
  63. package/dist/index.js +1562 -0
  64. package/dist/index.js.map +1 -0
  65. package/dist/opencode/client.d.ts +176 -0
  66. package/dist/opencode/client.d.ts.map +1 -0
  67. package/dist/opencode/client.js +1126 -0
  68. package/dist/opencode/client.js.map +1 -0
  69. package/dist/opencode/delayed-handler.d.ts +33 -0
  70. package/dist/opencode/delayed-handler.d.ts.map +1 -0
  71. package/dist/opencode/delayed-handler.js +74 -0
  72. package/dist/opencode/delayed-handler.js.map +1 -0
  73. package/dist/opencode/output-buffer.d.ts +56 -0
  74. package/dist/opencode/output-buffer.d.ts.map +1 -0
  75. package/dist/opencode/output-buffer.js +202 -0
  76. package/dist/opencode/output-buffer.js.map +1 -0
  77. package/dist/opencode/question-handler.d.ts +61 -0
  78. package/dist/opencode/question-handler.d.ts.map +1 -0
  79. package/dist/opencode/question-handler.js +183 -0
  80. package/dist/opencode/question-handler.js.map +1 -0
  81. package/dist/opencode/question-parser.d.ts +9 -0
  82. package/dist/opencode/question-parser.d.ts.map +1 -0
  83. package/dist/opencode/question-parser.js +69 -0
  84. package/dist/opencode/question-parser.js.map +1 -0
  85. package/dist/opencode/session-queue.d.ts +16 -0
  86. package/dist/opencode/session-queue.d.ts.map +1 -0
  87. package/dist/opencode/session-queue.js +41 -0
  88. package/dist/opencode/session-queue.js.map +1 -0
  89. package/dist/permissions/handler.d.ts +36 -0
  90. package/dist/permissions/handler.d.ts.map +1 -0
  91. package/dist/permissions/handler.js +141 -0
  92. package/dist/permissions/handler.js.map +1 -0
  93. package/dist/platform/adapters/discord-adapter.d.ts +45 -0
  94. package/dist/platform/adapters/discord-adapter.d.ts.map +1 -0
  95. package/dist/platform/adapters/discord-adapter.js +497 -0
  96. package/dist/platform/adapters/discord-adapter.js.map +1 -0
  97. package/dist/platform/adapters/feishu-adapter.d.ts +29 -0
  98. package/dist/platform/adapters/feishu-adapter.d.ts.map +1 -0
  99. package/dist/platform/adapters/feishu-adapter.js +150 -0
  100. package/dist/platform/adapters/feishu-adapter.js.map +1 -0
  101. package/dist/platform/registry.d.ts +41 -0
  102. package/dist/platform/registry.d.ts.map +1 -0
  103. package/dist/platform/registry.js +87 -0
  104. package/dist/platform/registry.js.map +1 -0
  105. package/dist/platform/types.d.ts +92 -0
  106. package/dist/platform/types.d.ts.map +1 -0
  107. package/dist/platform/types.js +4 -0
  108. package/dist/platform/types.js.map +1 -0
  109. package/dist/reliability/audit-log.d.ts +93 -0
  110. package/dist/reliability/audit-log.d.ts.map +1 -0
  111. package/dist/reliability/audit-log.js +248 -0
  112. package/dist/reliability/audit-log.js.map +1 -0
  113. package/dist/reliability/config-guard.d.ts +42 -0
  114. package/dist/reliability/config-guard.d.ts.map +1 -0
  115. package/dist/reliability/config-guard.js +264 -0
  116. package/dist/reliability/config-guard.js.map +1 -0
  117. package/dist/reliability/conversation-heartbeat.d.ts +37 -0
  118. package/dist/reliability/conversation-heartbeat.d.ts.map +1 -0
  119. package/dist/reliability/conversation-heartbeat.js +179 -0
  120. package/dist/reliability/conversation-heartbeat.js.map +1 -0
  121. package/dist/reliability/cron-api-server.d.ts +13 -0
  122. package/dist/reliability/cron-api-server.d.ts.map +1 -0
  123. package/dist/reliability/cron-api-server.js +247 -0
  124. package/dist/reliability/cron-api-server.js.map +1 -0
  125. package/dist/reliability/cron-control.d.ts +34 -0
  126. package/dist/reliability/cron-control.d.ts.map +1 -0
  127. package/dist/reliability/cron-control.js +864 -0
  128. package/dist/reliability/cron-control.js.map +1 -0
  129. package/dist/reliability/cron-semantic.d.ts +9 -0
  130. package/dist/reliability/cron-semantic.d.ts.map +1 -0
  131. package/dist/reliability/cron-semantic.js +208 -0
  132. package/dist/reliability/cron-semantic.js.map +1 -0
  133. package/dist/reliability/environment-doctor.d.ts +56 -0
  134. package/dist/reliability/environment-doctor.d.ts.map +1 -0
  135. package/dist/reliability/environment-doctor.js +213 -0
  136. package/dist/reliability/environment-doctor.js.map +1 -0
  137. package/dist/reliability/job-registry.d.ts +26 -0
  138. package/dist/reliability/job-registry.d.ts.map +1 -0
  139. package/dist/reliability/job-registry.js +77 -0
  140. package/dist/reliability/job-registry.js.map +1 -0
  141. package/dist/reliability/opencode-probe.d.ts +37 -0
  142. package/dist/reliability/opencode-probe.d.ts.map +1 -0
  143. package/dist/reliability/opencode-probe.js +195 -0
  144. package/dist/reliability/opencode-probe.js.map +1 -0
  145. package/dist/reliability/opencode-restart.d.ts +42 -0
  146. package/dist/reliability/opencode-restart.d.ts.map +1 -0
  147. package/dist/reliability/opencode-restart.js +155 -0
  148. package/dist/reliability/opencode-restart.js.map +1 -0
  149. package/dist/reliability/proactive-heartbeat.d.ts +39 -0
  150. package/dist/reliability/proactive-heartbeat.d.ts.map +1 -0
  151. package/dist/reliability/proactive-heartbeat.js +147 -0
  152. package/dist/reliability/proactive-heartbeat.js.map +1 -0
  153. package/dist/reliability/process-check-job.d.ts +73 -0
  154. package/dist/reliability/process-check-job.d.ts.map +1 -0
  155. package/dist/reliability/process-check-job.js +254 -0
  156. package/dist/reliability/process-check-job.js.map +1 -0
  157. package/dist/reliability/process-guard.d.ts +53 -0
  158. package/dist/reliability/process-guard.d.ts.map +1 -0
  159. package/dist/reliability/process-guard.js +344 -0
  160. package/dist/reliability/process-guard.js.map +1 -0
  161. package/dist/reliability/recovery-reporter.d.ts +37 -0
  162. package/dist/reliability/recovery-reporter.d.ts.map +1 -0
  163. package/dist/reliability/recovery-reporter.js +161 -0
  164. package/dist/reliability/recovery-reporter.js.map +1 -0
  165. package/dist/reliability/rescue-executor.d.ts +52 -0
  166. package/dist/reliability/rescue-executor.d.ts.map +1 -0
  167. package/dist/reliability/rescue-executor.js +244 -0
  168. package/dist/reliability/rescue-executor.js.map +1 -0
  169. package/dist/reliability/rescue-policy.d.ts +39 -0
  170. package/dist/reliability/rescue-policy.d.ts.map +1 -0
  171. package/dist/reliability/rescue-policy.js +85 -0
  172. package/dist/reliability/rescue-policy.js.map +1 -0
  173. package/dist/reliability/runtime-cron-dispatcher.d.ts +30 -0
  174. package/dist/reliability/runtime-cron-dispatcher.d.ts.map +1 -0
  175. package/dist/reliability/runtime-cron-dispatcher.js +100 -0
  176. package/dist/reliability/runtime-cron-dispatcher.js.map +1 -0
  177. package/dist/reliability/runtime-cron-orphan.d.ts +18 -0
  178. package/dist/reliability/runtime-cron-orphan.d.ts.map +1 -0
  179. package/dist/reliability/runtime-cron-orphan.js +87 -0
  180. package/dist/reliability/runtime-cron-orphan.js.map +1 -0
  181. package/dist/reliability/runtime-cron-registry.d.ts +4 -0
  182. package/dist/reliability/runtime-cron-registry.d.ts.map +1 -0
  183. package/dist/reliability/runtime-cron-registry.js +8 -0
  184. package/dist/reliability/runtime-cron-registry.js.map +1 -0
  185. package/dist/reliability/runtime-cron.d.ts +75 -0
  186. package/dist/reliability/runtime-cron.d.ts.map +1 -0
  187. package/dist/reliability/runtime-cron.js +309 -0
  188. package/dist/reliability/runtime-cron.js.map +1 -0
  189. package/dist/reliability/scheduler.d.ts +38 -0
  190. package/dist/reliability/scheduler.d.ts.map +1 -0
  191. package/dist/reliability/scheduler.js +174 -0
  192. package/dist/reliability/scheduler.js.map +1 -0
  193. package/dist/reliability/types.d.ts +151 -0
  194. package/dist/reliability/types.d.ts.map +1 -0
  195. package/dist/reliability/types.js +178 -0
  196. package/dist/reliability/types.js.map +1 -0
  197. package/dist/router/action-handlers.d.ts +27 -0
  198. package/dist/router/action-handlers.d.ts.map +1 -0
  199. package/dist/router/action-handlers.js +226 -0
  200. package/dist/router/action-handlers.js.map +1 -0
  201. package/dist/router/opencode-event-hub.d.ts +159 -0
  202. package/dist/router/opencode-event-hub.d.ts.map +1 -0
  203. package/dist/router/opencode-event-hub.js +589 -0
  204. package/dist/router/opencode-event-hub.js.map +1 -0
  205. package/dist/router/root-router.d.ts +94 -0
  206. package/dist/router/root-router.d.ts.map +1 -0
  207. package/dist/router/root-router.js +214 -0
  208. package/dist/router/root-router.js.map +1 -0
  209. package/dist/store/chat-session.d.ts +150 -0
  210. package/dist/store/chat-session.d.ts.map +1 -0
  211. package/dist/store/chat-session.js +640 -0
  212. package/dist/store/chat-session.js.map +1 -0
  213. package/dist/store/session-directory.d.ts +12 -0
  214. package/dist/store/session-directory.d.ts.map +1 -0
  215. package/dist/store/session-directory.js +47 -0
  216. package/dist/store/session-directory.js.map +1 -0
  217. package/dist/store/session-group.d.ts +19 -0
  218. package/dist/store/session-group.d.ts.map +1 -0
  219. package/dist/store/session-group.js +92 -0
  220. package/dist/store/session-group.js.map +1 -0
  221. package/dist/store/user-session.d.ts +19 -0
  222. package/dist/store/user-session.d.ts.map +1 -0
  223. package/dist/store/user-session.js +112 -0
  224. package/dist/store/user-session.js.map +1 -0
  225. package/dist/utils/async-queue.d.ts +12 -0
  226. package/dist/utils/async-queue.d.ts.map +1 -0
  227. package/dist/utils/async-queue.js +51 -0
  228. package/dist/utils/async-queue.js.map +1 -0
  229. package/dist/utils/directory-policy.d.ts +50 -0
  230. package/dist/utils/directory-policy.d.ts.map +1 -0
  231. package/dist/utils/directory-policy.js +379 -0
  232. package/dist/utils/directory-policy.js.map +1 -0
  233. package/dist/utils/session-title.d.ts +2 -0
  234. package/dist/utils/session-title.d.ts.map +1 -0
  235. package/dist/utils/session-title.js +10 -0
  236. package/dist/utils/session-title.js.map +1 -0
  237. package/package.json +73 -0
@@ -0,0 +1,414 @@
1
+ import { feishuClient } from '../feishu/client.js';
2
+ import { opencodeClient } from '../opencode/client.js';
3
+ import { chatSessionStore } from '../store/chat-session.js';
4
+ import { outputBuffer } from '../opencode/output-buffer.js';
5
+ import { questionHandler } from '../opencode/question-handler.js';
6
+ import { parseQuestionAnswerText } from '../opencode/question-parser.js';
7
+ import { parseCommand } from '../commands/parser.js';
8
+ import { commandHandler } from './command.js';
9
+ import { modelConfig, attachmentConfig } from '../config.js';
10
+ import { DirectoryPolicy } from '../utils/directory-policy.js';
11
+ import { buildSessionTimestamp } from '../utils/session-title.js';
12
+ import { randomUUID } from 'crypto';
13
+ import path from 'path';
14
+ import { promises as fs } from 'fs';
15
+ // 附件相关配置
16
+ const ATTACHMENT_BASE_DIR = path.resolve(process.cwd(), 'tmp', 'feishu-uploads');
17
+ const ALLOWED_ATTACHMENT_EXTENSIONS = new Set([
18
+ '.png', '.jpg', '.jpeg', '.gif', '.webp', '.pdf',
19
+ '.pjp', '.pjpeg', '.jfif', '.jpe'
20
+ ]);
21
+ // Helper functions for file type detection
22
+ function getHeaderValue(headers, name) {
23
+ const target = name.toLowerCase();
24
+ for (const [key, value] of Object.entries(headers)) {
25
+ if (key.toLowerCase() === target) {
26
+ if (typeof value === 'string')
27
+ return value;
28
+ if (Array.isArray(value) && typeof value[0] === 'string')
29
+ return value[0];
30
+ }
31
+ }
32
+ return '';
33
+ }
34
+ function extractExtension(name) {
35
+ return path.extname(name).toLowerCase();
36
+ }
37
+ function normalizeExtension(ext) {
38
+ if (!ext)
39
+ return '';
40
+ const withDot = ext.startsWith('.') ? ext.toLowerCase() : `.${ext.toLowerCase()}`;
41
+ if (withDot === '.jpeg' || withDot === '.pjpeg' || withDot === '.pjp' || withDot === '.jpe' || withDot === '.jfif') {
42
+ return '.jpg';
43
+ }
44
+ return withDot;
45
+ }
46
+ function extensionFromContentType(contentType) {
47
+ const type = contentType.split(';')[0]?.trim().toLowerCase();
48
+ if (type === 'image/png')
49
+ return '.png';
50
+ if (type === 'image/jpeg')
51
+ return '.jpg';
52
+ if (type === 'image/gif')
53
+ return '.gif';
54
+ if (type === 'image/webp')
55
+ return '.webp';
56
+ if (type === 'application/pdf')
57
+ return '.pdf';
58
+ return '';
59
+ }
60
+ function mimeFromExtension(ext) {
61
+ switch (ext) {
62
+ case '.png':
63
+ return 'image/png';
64
+ case '.jpg':
65
+ case '.jpeg':
66
+ case '.pjpeg':
67
+ case '.pjp':
68
+ case '.jfif':
69
+ case '.jpe':
70
+ return 'image/jpeg';
71
+ case '.gif':
72
+ return 'image/gif';
73
+ case '.webp':
74
+ return 'image/webp';
75
+ case '.pdf':
76
+ return 'application/pdf';
77
+ default:
78
+ return 'application/octet-stream';
79
+ }
80
+ }
81
+ function sanitizeFilename(name) {
82
+ const cleaned = name.replace(/[\\/:*?"<>|]+/g, '_').trim();
83
+ return cleaned || 'attachment';
84
+ }
85
+ export class GroupHandler {
86
+ ensureStreamingBuffer(chatId, sessionId, replyMessageId) {
87
+ const key = `chat:${chatId}`;
88
+ const current = outputBuffer.get(key);
89
+ if (current && current.status !== 'running') {
90
+ outputBuffer.clear(key);
91
+ }
92
+ if (!outputBuffer.get(key)) {
93
+ outputBuffer.getOrCreate(key, chatId, sessionId, replyMessageId);
94
+ }
95
+ }
96
+ formatDispatchError(error) {
97
+ const message = error instanceof Error ? error.message : String(error);
98
+ const normalized = message.toLowerCase();
99
+ if (normalized.includes('fetch failed') || normalized.includes('networkerror')) {
100
+ return '与 OpenCode 的连接失败,请检查服务是否在线或网络是否超时';
101
+ }
102
+ if (normalized.includes('timed out') || normalized.includes('timeout')) {
103
+ return '请求 OpenCode 超时,请稍后重试';
104
+ }
105
+ return `请求失败: ${message}`;
106
+ }
107
+ // 处理群聊消息
108
+ async handleMessage(event) {
109
+ const { chatId, content, messageId, senderId, attachments } = event;
110
+ const trimmed = content.trim();
111
+ // 1. 优先处理命令
112
+ const command = parseCommand(trimmed);
113
+ if (command.type !== 'prompt') {
114
+ console.log(`[Group] 收到命令: ${command.type}`);
115
+ await commandHandler.handle(command, {
116
+ chatId,
117
+ messageId,
118
+ senderId,
119
+ chatType: 'group'
120
+ });
121
+ return;
122
+ }
123
+ // 2. 检查是否有待回答的问题
124
+ const hasPending = await this.checkPendingQuestion(chatId, trimmed, messageId, attachments);
125
+ if (hasPending)
126
+ return;
127
+ // 3. 获取或创建会话
128
+ let sessionId = chatSessionStore.getSessionId(chatId);
129
+ if (!sessionId) {
130
+ // 如果没有绑定会话,自动创建一个(走 DirectoryPolicy)
131
+ const title = `群聊-${buildSessionTimestamp()}`;
132
+ const chatDefault = chatSessionStore.getSession(chatId)?.defaultDirectory;
133
+ const dirResult = DirectoryPolicy.resolve({ chatDefaultDirectory: chatDefault });
134
+ const effectiveDir = dirResult.ok && dirResult.source !== 'server_default' ? dirResult.directory : undefined;
135
+ const session = await opencodeClient.createSession(title, effectiveDir);
136
+ if (session) {
137
+ sessionId = session.id;
138
+ chatSessionStore.setSession(chatId, sessionId, senderId, title, {
139
+ chatType: 'group',
140
+ resolvedDirectory: session.directory,
141
+ }); // senderId 暂时作为 creator
142
+ }
143
+ else {
144
+ await feishuClient.reply(messageId, '❌ 无法创建 OpenCode 会话');
145
+ return;
146
+ }
147
+ }
148
+ // 4. 处理 Prompt
149
+ // 记录用户消息ID
150
+ chatSessionStore.updateLastInteraction(chatId, messageId);
151
+ // 获取当前会话配置
152
+ const sessionConfig = chatSessionStore.getSession(chatId);
153
+ const promptText = command.text ?? trimmed;
154
+ await this.processPrompt(sessionId, promptText, chatId, messageId, attachments, sessionConfig, command.promptEffort);
155
+ }
156
+ // 检查待回答问题
157
+ async checkPendingQuestion(chatId, text, messageId, attachments, source = 'text') {
158
+ const pending = questionHandler.getByConversationKey(`chat:${chatId}`);
159
+ if (!pending)
160
+ return false;
161
+ // 如果有附件,提示先完成回答
162
+ if (attachments && attachments.length > 0) {
163
+ await feishuClient.reply(messageId, '当前有待回答问题,请先完成问题回答');
164
+ return true;
165
+ }
166
+ const currentIndex = pending.currentQuestionIndex;
167
+ const question = pending.request.questions[currentIndex];
168
+ // 解析答案
169
+ const parsed = parseQuestionAnswerText(text, question);
170
+ if (!parsed) {
171
+ await feishuClient.reply(messageId, '未识别答案,请回复选项编号/字母,或直接输入自定义内容。');
172
+ return true;
173
+ }
174
+ // 更新草稿
175
+ if (parsed.type === 'skip') {
176
+ questionHandler.setDraftAnswer(pending.request.id, currentIndex, []);
177
+ questionHandler.setDraftCustomAnswer(pending.request.id, currentIndex, '');
178
+ }
179
+ else if (parsed.type === 'custom') {
180
+ questionHandler.setDraftAnswer(pending.request.id, currentIndex, []);
181
+ questionHandler.setDraftCustomAnswer(pending.request.id, currentIndex, parsed.custom || text);
182
+ }
183
+ else {
184
+ questionHandler.setDraftCustomAnswer(pending.request.id, currentIndex, '');
185
+ questionHandler.setDraftAnswer(pending.request.id, currentIndex, parsed.values || []);
186
+ }
187
+ // 进入下一题或提交
188
+ const nextIndex = currentIndex + 1;
189
+ if (nextIndex < pending.request.questions.length) {
190
+ questionHandler.setCurrentQuestionIndex(pending.request.id, nextIndex);
191
+ outputBuffer.touch(`chat:${chatId}`);
192
+ }
193
+ else {
194
+ // 提交所有答案
195
+ await this.submitQuestionAnswers(pending, messageId, chatId);
196
+ }
197
+ return true;
198
+ }
199
+ // 处理题目卡片中的“跳过本题”按钮
200
+ async handleQuestionSkipAction(params) {
201
+ const pending = questionHandler.getByConversationKey(`chat:${params.chatId}`);
202
+ if (!pending) {
203
+ return 'not_found';
204
+ }
205
+ if (params.requestId && params.requestId !== pending.request.id) {
206
+ return 'stale_card';
207
+ }
208
+ if (typeof params.questionIndex === 'number' && params.questionIndex !== pending.currentQuestionIndex) {
209
+ return 'stale_card';
210
+ }
211
+ const messageId = params.messageId || pending.feishuCardMessageId;
212
+ if (!messageId) {
213
+ return 'invalid_state';
214
+ }
215
+ try {
216
+ const handled = await this.checkPendingQuestion(params.chatId, '跳过', messageId, undefined, 'button');
217
+ return handled ? 'applied' : 'not_found';
218
+ }
219
+ catch (error) {
220
+ console.error('[Group] 处理跳过按钮失败:', error);
221
+ return 'invalid_state';
222
+ }
223
+ }
224
+ // 提交问题答案
225
+ async submitQuestionAnswers(pending, replyMessageId, chatId) {
226
+ const answers = [];
227
+ const totalQuestions = pending.request.questions.length;
228
+ for (let i = 0; i < totalQuestions; i++) {
229
+ const custom = (pending.draftCustomAnswers[i] || '').trim();
230
+ if (custom) {
231
+ answers.push([custom]);
232
+ }
233
+ else {
234
+ answers.push(pending.draftAnswers[i] || []);
235
+ }
236
+ }
237
+ console.log(`[Group] 提交问题回答: requestId=${pending.request.id.slice(0, 8)}...`);
238
+ this.ensureStreamingBuffer(chatId, pending.request.sessionID, replyMessageId || null);
239
+ const success = await opencodeClient.replyQuestion(pending.request.id, answers);
240
+ if (success) {
241
+ questionHandler.remove(pending.request.id);
242
+ outputBuffer.touch(`chat:${chatId}`);
243
+ }
244
+ else {
245
+ await feishuClient.reply(replyMessageId, '⚠️ 回答提交失败,请重试');
246
+ }
247
+ }
248
+ // 清除上下文
249
+ async handleClear(chatId, messageId) {
250
+ const sessionId = chatSessionStore.getSessionId(chatId);
251
+ if (sessionId) {
252
+ // OpenCode 目前可能没有 deleteSession 接口,或者仅仅是解绑?
253
+ // 按照之前的逻辑,可能是 deleteSession
254
+ await opencodeClient.deleteSession(sessionId);
255
+ chatSessionStore.removeSession(chatId);
256
+ await feishuClient.reply(messageId, '🧹 会话上下文已清除,新消息将开启新会话。');
257
+ }
258
+ else {
259
+ await feishuClient.reply(messageId, '当前没有活跃的会话。');
260
+ }
261
+ }
262
+ // 处理消息发送
263
+ async processPrompt(sessionId, text, chatId, messageId, attachments, config, promptEffort) {
264
+ const bufferKey = `chat:${chatId}`;
265
+ this.ensureStreamingBuffer(chatId, sessionId, messageId);
266
+ try {
267
+ console.log(`[Group] 发送消息: chat=${chatId}, session=${sessionId.slice(0, 8)}...`);
268
+ const parts = [];
269
+ // 按需注入文件发送指令:仅在检测到发送意图关键词时注入
270
+ let effectiveText = text;
271
+ if (effectiveText && /发给我|发送文件|上传|传给我|send.*file/i.test(effectiveText)) {
272
+ effectiveText += '\n[feishu-bridge: 如需发送文件到当前群聊,执行 echo FEISHU_SEND_FILE <文件绝对路径>]';
273
+ }
274
+ if (effectiveText) {
275
+ parts.push({ type: 'text', text: effectiveText });
276
+ }
277
+ if (attachments && attachments.length > 0) {
278
+ const prepared = await this.prepareAttachmentParts(messageId, attachments);
279
+ if (prepared.warnings.length > 0) {
280
+ await feishuClient.reply(messageId, `⚠️ 附件警告:\n${prepared.warnings.join('\n')}`);
281
+ }
282
+ parts.push(...prepared.parts);
283
+ }
284
+ if (parts.length === 0) {
285
+ await feishuClient.reply(messageId, '未检测到有效内容');
286
+ outputBuffer.setStatus(`chat:${chatId}`, 'completed');
287
+ return;
288
+ }
289
+ // 提取 providerId 和 modelId
290
+ let providerId;
291
+ let modelId;
292
+ if (modelConfig.defaultProvider && modelConfig.defaultModel) {
293
+ providerId = modelConfig.defaultProvider;
294
+ modelId = modelConfig.defaultModel;
295
+ }
296
+ if (config?.preferredModel) {
297
+ const [p, m] = config.preferredModel.split(':');
298
+ if (p && m) {
299
+ providerId = p;
300
+ modelId = m;
301
+ }
302
+ else {
303
+ // 兼容历史数据:仅模型名时,尝试复用环境中声明的 provider
304
+ // 若未声明 provider,则不显式传 model,交由 OpenCode 默认模型决策
305
+ if (providerId) {
306
+ modelId = config.preferredModel;
307
+ }
308
+ }
309
+ }
310
+ // 异步触发 OpenCode 请求,后续输出通过事件流持续推送
311
+ const variant = promptEffort || config?.preferredEffort;
312
+ // 从 store 获取会话的工作目录,传递给 OpenCode 以切换 Instance 上下文
313
+ const sessionData = chatSessionStore.getSession(chatId);
314
+ let directory = sessionData?.resolvedDirectory;
315
+ // 如果 store 没有记录(老会话),尝试从 OpenCode 聚合查询并回写缓存
316
+ if (!directory) {
317
+ try {
318
+ const storeKnownDirs = chatSessionStore.getKnownDirectories();
319
+ const sessions = await opencodeClient.listAllSessions(storeKnownDirs);
320
+ const matched = sessions.find(s => s.id === sessionId);
321
+ if (matched?.directory) {
322
+ directory = matched.directory;
323
+ // 回写缓存,后续消息不再重复查询
324
+ chatSessionStore.updateResolvedDirectory(chatId, directory);
325
+ }
326
+ }
327
+ catch {
328
+ // 获取失败不阻塞消息发送
329
+ }
330
+ }
331
+ await opencodeClient.sendMessagePartsAsync(sessionId, parts, {
332
+ providerId,
333
+ modelId,
334
+ agent: config?.preferredAgent,
335
+ ...(variant ? { variant } : {}),
336
+ ...(directory ? { directory } : {}),
337
+ });
338
+ }
339
+ catch (error) {
340
+ const errorMessage = this.formatDispatchError(error);
341
+ console.error('[Group] 请求派发失败:', error);
342
+ outputBuffer.append(bufferKey, `\n\n❌ ${errorMessage}`);
343
+ outputBuffer.setStatus(bufferKey, 'failed');
344
+ const currentBuffer = outputBuffer.get(bufferKey);
345
+ if (!currentBuffer?.messageId) {
346
+ await feishuClient.reply(messageId, `❌ ${errorMessage}`);
347
+ }
348
+ }
349
+ }
350
+ // 公开的 prompt 调度方法,供 command 层(如 /send 命令)调用
351
+ async dispatchPrompt(sessionId, text, chatId, messageId) {
352
+ const config = chatSessionStore.getSession(chatId);
353
+ await this.processPrompt(sessionId, text, chatId, messageId, undefined, config);
354
+ }
355
+ // 处理附件
356
+ async prepareAttachmentParts(messageId, attachments) {
357
+ const parts = [];
358
+ const warnings = [];
359
+ await fs.mkdir(ATTACHMENT_BASE_DIR, { recursive: true }).catch(() => undefined);
360
+ for (const attachment of attachments) {
361
+ if (attachment.fileSize && attachment.fileSize > attachmentConfig.maxSize) {
362
+ warnings.push(`附件 ${attachment.fileName} 过大,已跳过`);
363
+ continue;
364
+ }
365
+ const resource = await feishuClient.downloadMessageResource(messageId, attachment.fileKey, attachment.type);
366
+ if (!resource) {
367
+ warnings.push(`附件 ${attachment.fileName || '未知'} 下载失败`);
368
+ continue;
369
+ }
370
+ const contentType = getHeaderValue(resource.headers || {}, 'content-type');
371
+ const extFromName = attachment.fileName ? extractExtension(attachment.fileName) : '';
372
+ const extFromType = attachment.fileType ? normalizeExtension(attachment.fileType) : '';
373
+ const extFromContent = contentType ? extensionFromContentType(contentType) : '';
374
+ let ext = normalizeExtension(extFromName || extFromType || extFromContent);
375
+ if (!ext && attachment.type === 'image') {
376
+ ext = '.jpg';
377
+ }
378
+ if (!ext || !ALLOWED_ATTACHMENT_EXTENSIONS.has(ext)) {
379
+ console.log(`[附件] 不支持的格式: ext=${ext || 'unknown'}, contentType=${contentType}`);
380
+ warnings.push(`附件格式不支持 (${ext || 'unknown'}),已跳过`);
381
+ continue;
382
+ }
383
+ const fileId = randomUUID();
384
+ const filePath = path.join(ATTACHMENT_BASE_DIR, `${fileId}${ext}`);
385
+ const rawName = attachment.fileName || `attachment${ext}`;
386
+ const safeName = sanitizeFilename(rawName.endsWith(ext) ? rawName : `${rawName}${ext}`);
387
+ try {
388
+ await resource.writeFile(filePath);
389
+ const buffer = await fs.readFile(filePath);
390
+ const base64 = buffer.toString('base64');
391
+ let mime = contentType ? contentType.split(';')[0].trim() : '';
392
+ if (!mime || mime === 'application/octet-stream') {
393
+ mime = mimeFromExtension(ext);
394
+ }
395
+ const dataUrl = `data:${mime};base64,${base64}`;
396
+ parts.push({
397
+ type: 'file',
398
+ mime,
399
+ url: dataUrl,
400
+ filename: safeName
401
+ });
402
+ }
403
+ catch (e) {
404
+ warnings.push(`附件处理失败: ${attachment.fileName}`);
405
+ }
406
+ finally {
407
+ fs.unlink(filePath).catch(() => { });
408
+ }
409
+ }
410
+ return { parts, warnings };
411
+ }
412
+ }
413
+ export const groupHandler = new GroupHandler();
414
+ //# sourceMappingURL=group.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"group.js","sourceRoot":"","sources":["../../src/handlers/group.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ export interface CleanupStats {
2
+ scannedChats: number;
3
+ disbandedChats: number;
4
+ deletedSessions: number;
5
+ skippedProtectedSessions: number;
6
+ removedOrphanMappings: number;
7
+ removedCronJobs: number;
8
+ }
9
+ export declare class LifecycleHandler {
10
+ cleanUpOnStart(): Promise<void>;
11
+ runCleanupScan(): Promise<CleanupStats>;
12
+ handleMemberLeft(chatId: string, memberId: string): Promise<void>;
13
+ private checkAndDisbandIfEmpty;
14
+ private cleanupAndDisband;
15
+ }
16
+ export declare const lifecycleHandler: LifecycleHandler;
17
+ //# sourceMappingURL=lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.d.ts","sourceRoot":"","sources":["../../src/handlers/lifecycle.ts"],"names":[],"mappings":"AAOA,MAAM,WAAW,YAAY;IAC3B,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,wBAAwB,EAAE,MAAM,CAAC;IACjC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,eAAe,EAAE,MAAM,CAAC;CACzB;AAED,qBAAa,gBAAgB;IAErB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC;IAS/B,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;IA2CvC,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;YAMzD,sBAAsB;YAwCtB,iBAAiB;CAmChC;AAED,eAAO,MAAM,gBAAgB,kBAAyB,CAAC"}
@@ -0,0 +1,129 @@
1
+ import { feishuClient } from '../feishu/client.js';
2
+ import { chatSessionStore } from '../store/chat-session.js';
3
+ import { opencodeClient } from '../opencode/client.js';
4
+ import { reliabilityConfig, userConfig } from '../config.js';
5
+ import { getRuntimeCronManager } from '../reliability/runtime-cron-registry.js';
6
+ import { cleanupRuntimeCronJobsByConversation } from '../reliability/runtime-cron-orphan.js';
7
+ export class LifecycleHandler {
8
+ // 启动时清理无效群
9
+ async cleanUpOnStart() {
10
+ console.log('[Lifecycle] 正在检查无效群聊...');
11
+ const stats = await this.runCleanupScan();
12
+ console.log(`[Lifecycle] 清理统计: scanned=${stats.scannedChats}, disbanded=${stats.disbandedChats}, deletedSession=${stats.deletedSessions}, skippedProtected=${stats.skippedProtectedSessions}, removedOrphanMappings=${stats.removedOrphanMappings}, removedCronJobs=${stats.removedCronJobs}`);
13
+ console.log('[Lifecycle] 清理完成');
14
+ }
15
+ async runCleanupScan() {
16
+ const stats = {
17
+ scannedChats: 0,
18
+ disbandedChats: 0,
19
+ deletedSessions: 0,
20
+ skippedProtectedSessions: 0,
21
+ removedOrphanMappings: 0,
22
+ removedCronJobs: 0,
23
+ };
24
+ const chats = await feishuClient.getUserChats();
25
+ const activeChatIdSet = new Set(chats);
26
+ // 仅处理 Feishu 平台的映射
27
+ const feishuChatIds = chatSessionStore.getChatIdsByPlatform('feishu');
28
+ if (chats.length === 0) {
29
+ console.log('[Lifecycle] 当前未检索到任何群聊,跳过孤儿映射清理');
30
+ }
31
+ else {
32
+ for (const mappedChatId of feishuChatIds) {
33
+ if (activeChatIdSet.has(mappedChatId))
34
+ continue;
35
+ if (!chatSessionStore.isGroupChatSession(mappedChatId)) {
36
+ continue;
37
+ }
38
+ if (reliabilityConfig.cronOrphanAutoCleanup) {
39
+ const cronCleanup = cleanupRuntimeCronJobsByConversation(getRuntimeCronManager(), 'feishu', mappedChatId);
40
+ stats.removedCronJobs += cronCleanup.removedJobIds.length;
41
+ }
42
+ chatSessionStore.removeSession(mappedChatId);
43
+ stats.removedOrphanMappings += 1;
44
+ console.log(`[Lifecycle] 已移除孤儿映射: chat=${mappedChatId}`);
45
+ }
46
+ }
47
+ for (const chatId of chats) {
48
+ stats.scannedChats += 1;
49
+ await this.checkAndDisbandIfEmpty(chatId, stats);
50
+ }
51
+ return stats;
52
+ }
53
+ // 处理用户退群事件
54
+ async handleMemberLeft(chatId, memberId) {
55
+ console.log(`[Lifecycle] 用户 ${memberId} 退出群 ${chatId}`);
56
+ await this.checkAndDisbandIfEmpty(chatId);
57
+ }
58
+ // 检查群是否为空,为空则解散
59
+ async checkAndDisbandIfEmpty(chatId, stats) {
60
+ const members = await feishuClient.getChatMembers(chatId);
61
+ console.log(`[Lifecycle] 检查群 ${chatId} 成员数: ${members.length}`);
62
+ // 未配置白名单时:只要群里还有任意成员,就不自动解散
63
+ // 仅在成员数为 0 时才执行清理,避免误删仅剩 1 名用户的群
64
+ if (!userConfig.isWhitelistEnabled) {
65
+ if (members.length > 0) {
66
+ console.log(`[Lifecycle] 群 ${chatId} 未启用白名单且仍有成员,跳过解散`);
67
+ return;
68
+ }
69
+ console.log(`[Lifecycle] 群 ${chatId} 未启用白名单且成员为 0,准备解散...`);
70
+ await this.cleanupAndDisband(chatId, stats);
71
+ return;
72
+ }
73
+ // 检查是否有白名单用户在群内
74
+ const hasAllowedUser = members.some(memberId => userConfig.allowedUsers.includes(memberId));
75
+ if (hasAllowedUser) {
76
+ console.log(`[Lifecycle] 群 ${chatId} 包含白名单用户,跳过解散检查`);
77
+ return;
78
+ }
79
+ // 二次确认:检查群主是否在白名单中(防止成员列表获取失败导致误删)
80
+ const chatInfo = await feishuClient.getChat(chatId);
81
+ if (chatInfo && userConfig.allowedUsers.includes(chatInfo.ownerId)) {
82
+ console.log(`[Lifecycle] 群 ${chatId} 群主(${chatInfo.ownerId})在白名单中,跳过解散检查`);
83
+ return;
84
+ }
85
+ // 如果成员数 <= 1,认为群为空(只有机器人或无人)
86
+ if (members.length <= 1) {
87
+ console.log(`[Lifecycle] 群 ${chatId} 成员不足且无白名单用户,准备解散...`);
88
+ await this.cleanupAndDisband(chatId, stats);
89
+ }
90
+ }
91
+ async cleanupAndDisband(chatId, stats) {
92
+ // 1. 清理 OpenCode 会话
93
+ const sessionId = chatSessionStore.getSessionId(chatId);
94
+ if (sessionId) {
95
+ const deleteProtected = chatSessionStore.isSessionDeleteProtected(chatId);
96
+ if (deleteProtected) {
97
+ console.log(`[Lifecycle] 会话删除受保护,跳过删除: ${sessionId}`);
98
+ if (stats)
99
+ stats.skippedProtectedSessions += 1;
100
+ }
101
+ else {
102
+ // 尝试删除会话(如果 API 支持)
103
+ try {
104
+ const deleted = await opencodeClient.deleteSession(sessionId);
105
+ if (deleted && stats) {
106
+ stats.deletedSessions += 1;
107
+ }
108
+ }
109
+ catch (e) {
110
+ console.warn(`[Lifecycle] 删除 OpenCode 会话 ${sessionId} 失败:`, e);
111
+ }
112
+ }
113
+ chatSessionStore.removeSession(chatId);
114
+ }
115
+ if (reliabilityConfig.cronOrphanAutoCleanup) {
116
+ const cronCleanup = cleanupRuntimeCronJobsByConversation(getRuntimeCronManager(), 'feishu', chatId);
117
+ if (stats) {
118
+ stats.removedCronJobs += cronCleanup.removedJobIds.length;
119
+ }
120
+ }
121
+ // 2. 解散飞书群
122
+ const disbanded = await feishuClient.disbandChat(chatId);
123
+ if (disbanded && stats) {
124
+ stats.disbandedChats += 1;
125
+ }
126
+ }
127
+ }
128
+ export const lifecycleHandler = new LifecycleHandler();
129
+ //# sourceMappingURL=lifecycle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lifecycle.js","sourceRoot":"","sources":["../../src/handlers/lifecycle.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import { type FeishuMessageEvent, type FeishuCardActionEvent } from '../feishu/client.js';
2
+ export declare class P2PHandler {
3
+ private static readonly CARD_SELECTION_TTL_MS;
4
+ private createChatSelectionMap;
5
+ private createChatProjectMap;
6
+ private createChatDirectoryInputMap;
7
+ private createChatNameInputMap;
8
+ private safeReply;
9
+ private getStringValue;
10
+ private getCardActionOption;
11
+ private getCardActionInputValue;
12
+ private getCreateChatSelectionKeys;
13
+ private rememberCreateChatSelection;
14
+ private getRememberedCreateChatSelection;
15
+ private clearCreateChatSelection;
16
+ private rememberCreateChatProjectSelection;
17
+ private getRememberedCreateChatProjectSelection;
18
+ private clearCreateChatProjectSelection;
19
+ private rememberCreateChatDirectoryInput;
20
+ private getRememberedCreateChatDirectoryInput;
21
+ private clearCreateChatDirectoryInput;
22
+ private rememberCreateChatNameInput;
23
+ private getRememberedCreateChatNameInput;
24
+ private clearCreateChatNameInput;
25
+ private getSessionDirectory;
26
+ private getSessionOptionLabel;
27
+ private sortSessionsForCreateChat;
28
+ private buildCreateChatCardData;
29
+ private pushCreateChatCard;
30
+ private getPrivateSessionShortId;
31
+ private getPrivateSessionTitle;
32
+ private isCreateGroupCommand;
33
+ private isSessionMissingInOpenCode;
34
+ private ensurePrivateSession;
35
+ private shouldSkipImmediateCommand;
36
+ private pushFirstContactGuidance;
37
+ handleMessage(event: FeishuMessageEvent): Promise<void>;
38
+ private ensureUserInGroup;
39
+ private findSessionById;
40
+ private createGroupWithSessionSelection;
41
+ handleCardAction(event: FeishuCardActionEvent): Promise<object | void>;
42
+ }
43
+ export declare const p2pHandler: P2PHandler;
44
+ //# sourceMappingURL=p2p.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"p2p.d.ts","sourceRoot":"","sources":["../../src/handlers/p2p.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,kBAAkB,EAAE,KAAK,qBAAqB,EAAE,MAAM,qBAAqB,CAAC;AA0BxG,qBAAa,UAAU;IACrB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAkB;IAE/D,OAAO,CAAC,sBAAsB,CAAkC;IAChE,OAAO,CAAC,oBAAoB,CAAgE;IAC5F,OAAO,CAAC,2BAA2B,CAAgE;IACnG,OAAO,CAAC,sBAAsB,CAAgE;YAEhF,SAAS;IAkBvB,OAAO,CAAC,cAAc;IAMtB,OAAO,CAAC,mBAAmB;IAe3B,OAAO,CAAC,uBAAuB;IAgB/B,OAAO,CAAC,0BAA0B;IAgBlC,OAAO,CAAC,2BAA2B;IAenC,OAAO,CAAC,gCAAgC;IAWxC,OAAO,CAAC,wBAAwB;IAOhC,OAAO,CAAC,kCAAkC;IAa1C,OAAO,CAAC,uCAAuC;IAiB/C,OAAO,CAAC,+BAA+B;IASvC,OAAO,CAAC,gCAAgC;IAaxC,OAAO,CAAC,qCAAqC;IAiB7C,OAAO,CAAC,6BAA6B;IASrC,OAAO,CAAC,2BAA2B;IAanC,OAAO,CAAC,gCAAgC;IAiBxC,OAAO,CAAC,wBAAwB;IAUhC,OAAO,CAAC,mBAAmB;IAK7B,OAAO,CAAC,qBAAqB;IAY3B,OAAO,CAAC,yBAAyB;YAiBnB,uBAAuB;YA4DvB,kBAAkB;IAuBhC,OAAO,CAAC,wBAAwB;IAKhC,OAAO,CAAC,sBAAsB;IAI9B,OAAO,CAAC,oBAAoB;YAad,0BAA0B;YAU1B,oBAAoB;IA8BlC,OAAO,CAAC,0BAA0B;YAQpB,wBAAwB;IAoBhC,aAAa,CAAC,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;YA4C/C,iBAAiB;YAmCjB,eAAe;YASf,+BAA+B;IAoIvC,gBAAgB,CAAC,KAAK,EAAE,qBAAqB,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;CA8H7E;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}