botmux 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 (202) hide show
  1. package/.env.example +24 -0
  2. package/LICENSE +21 -0
  3. package/README.en.md +267 -0
  4. package/README.md +267 -0
  5. package/dist/adapters/backend/pty-backend.d.ts +13 -0
  6. package/dist/adapters/backend/pty-backend.d.ts.map +1 -0
  7. package/dist/adapters/backend/pty-backend.js +39 -0
  8. package/dist/adapters/backend/pty-backend.js.map +1 -0
  9. package/dist/adapters/backend/tmux-backend.d.ts +20 -0
  10. package/dist/adapters/backend/tmux-backend.d.ts.map +1 -0
  11. package/dist/adapters/backend/tmux-backend.js +30 -0
  12. package/dist/adapters/backend/tmux-backend.js.map +1 -0
  13. package/dist/adapters/backend/types.d.ts +19 -0
  14. package/dist/adapters/backend/types.d.ts.map +1 -0
  15. package/dist/adapters/backend/types.js +2 -0
  16. package/dist/adapters/backend/types.js.map +1 -0
  17. package/dist/adapters/cli/aiden.d.ts +4 -0
  18. package/dist/adapters/cli/aiden.d.ts.map +1 -0
  19. package/dist/adapters/cli/aiden.js +63 -0
  20. package/dist/adapters/cli/aiden.js.map +1 -0
  21. package/dist/adapters/cli/claude-code.d.ts +4 -0
  22. package/dist/adapters/cli/claude-code.d.ts.map +1 -0
  23. package/dist/adapters/cli/claude-code.js +59 -0
  24. package/dist/adapters/cli/claude-code.js.map +1 -0
  25. package/dist/adapters/cli/coco.d.ts +4 -0
  26. package/dist/adapters/cli/coco.d.ts.map +1 -0
  27. package/dist/adapters/cli/coco.js +46 -0
  28. package/dist/adapters/cli/coco.js.map +1 -0
  29. package/dist/adapters/cli/codex.d.ts +4 -0
  30. package/dist/adapters/cli/codex.d.ts.map +1 -0
  31. package/dist/adapters/cli/codex.js +47 -0
  32. package/dist/adapters/cli/codex.js.map +1 -0
  33. package/dist/adapters/cli/registry.d.ts +13 -0
  34. package/dist/adapters/cli/registry.d.ts.map +1 -0
  35. package/dist/adapters/cli/registry.js +42 -0
  36. package/dist/adapters/cli/registry.js.map +1 -0
  37. package/dist/adapters/cli/types.d.ts +39 -0
  38. package/dist/adapters/cli/types.d.ts.map +1 -0
  39. package/dist/adapters/cli/types.js +2 -0
  40. package/dist/adapters/cli/types.js.map +1 -0
  41. package/dist/cli.d.ts +3 -0
  42. package/dist/cli.d.ts.map +1 -0
  43. package/dist/cli.js +245 -0
  44. package/dist/cli.js.map +1 -0
  45. package/dist/config.d.ts +24 -0
  46. package/dist/config.d.ts.map +1 -0
  47. package/dist/config.js +39 -0
  48. package/dist/config.js.map +1 -0
  49. package/dist/core/command-handler.d.ts +11 -0
  50. package/dist/core/command-handler.d.ts.map +1 -0
  51. package/dist/core/command-handler.js +322 -0
  52. package/dist/core/command-handler.js.map +1 -0
  53. package/dist/core/cost-calculator.d.ts +12 -0
  54. package/dist/core/cost-calculator.d.ts.map +1 -0
  55. package/dist/core/cost-calculator.js +61 -0
  56. package/dist/core/cost-calculator.js.map +1 -0
  57. package/dist/core/scheduler.d.ts +38 -0
  58. package/dist/core/scheduler.d.ts.map +1 -0
  59. package/dist/core/scheduler.js +221 -0
  60. package/dist/core/scheduler.js.map +1 -0
  61. package/dist/core/session-manager.d.ts +13 -0
  62. package/dist/core/session-manager.d.ts.map +1 -0
  63. package/dist/core/session-manager.js +125 -0
  64. package/dist/core/session-manager.js.map +1 -0
  65. package/dist/core/types.d.ts +29 -0
  66. package/dist/core/types.d.ts.map +1 -0
  67. package/dist/core/types.js +2 -0
  68. package/dist/core/types.js.map +1 -0
  69. package/dist/core/worker-pool.d.ts +26 -0
  70. package/dist/core/worker-pool.d.ts.map +1 -0
  71. package/dist/core/worker-pool.js +261 -0
  72. package/dist/core/worker-pool.js.map +1 -0
  73. package/dist/daemon.d.ts +3 -0
  74. package/dist/daemon.d.ts.map +1 -0
  75. package/dist/daemon.js +344 -0
  76. package/dist/daemon.js.map +1 -0
  77. package/dist/im/lark/card-builder.d.ts +18 -0
  78. package/dist/im/lark/card-builder.d.ts.map +1 -0
  79. package/dist/im/lark/card-builder.js +194 -0
  80. package/dist/im/lark/card-builder.js.map +1 -0
  81. package/dist/im/lark/card-handler.d.ts +9 -0
  82. package/dist/im/lark/card-handler.d.ts.map +1 -0
  83. package/dist/im/lark/card-handler.js +131 -0
  84. package/dist/im/lark/card-handler.js.map +1 -0
  85. package/dist/im/lark/client.d.ts +23 -0
  86. package/dist/im/lark/client.d.ts.map +1 -0
  87. package/dist/im/lark/client.js +259 -0
  88. package/dist/im/lark/client.js.map +1 -0
  89. package/dist/im/lark/event-dispatcher.d.ts +34 -0
  90. package/dist/im/lark/event-dispatcher.d.ts.map +1 -0
  91. package/dist/im/lark/event-dispatcher.js +195 -0
  92. package/dist/im/lark/event-dispatcher.js.map +1 -0
  93. package/dist/im/lark/message-parser.d.ts +45 -0
  94. package/dist/im/lark/message-parser.d.ts.map +1 -0
  95. package/dist/im/lark/message-parser.js +132 -0
  96. package/dist/im/lark/message-parser.js.map +1 -0
  97. package/dist/im/types.d.ts +78 -0
  98. package/dist/im/types.d.ts.map +1 -0
  99. package/dist/im/types.js +2 -0
  100. package/dist/im/types.js.map +1 -0
  101. package/dist/index-daemon.d.ts +3 -0
  102. package/dist/index-daemon.d.ts.map +1 -0
  103. package/dist/index-daemon.js +21 -0
  104. package/dist/index-daemon.js.map +1 -0
  105. package/dist/index.d.ts +3 -0
  106. package/dist/index.d.ts.map +1 -0
  107. package/dist/index.js +16 -0
  108. package/dist/index.js.map +1 -0
  109. package/dist/scheduler.d.ts +38 -0
  110. package/dist/scheduler.d.ts.map +1 -0
  111. package/dist/scheduler.js +221 -0
  112. package/dist/scheduler.js.map +1 -0
  113. package/dist/server.d.ts +3 -0
  114. package/dist/server.d.ts.map +1 -0
  115. package/dist/server.js +23 -0
  116. package/dist/server.js.map +1 -0
  117. package/dist/services/lark-client.d.ts +22 -0
  118. package/dist/services/lark-client.d.ts.map +1 -0
  119. package/dist/services/lark-client.js +238 -0
  120. package/dist/services/lark-client.js.map +1 -0
  121. package/dist/services/lark-ws.d.ts +5 -0
  122. package/dist/services/lark-ws.d.ts.map +1 -0
  123. package/dist/services/lark-ws.js +79 -0
  124. package/dist/services/lark-ws.js.map +1 -0
  125. package/dist/services/message-queue.d.ts +9 -0
  126. package/dist/services/message-queue.d.ts.map +1 -0
  127. package/dist/services/message-queue.js +77 -0
  128. package/dist/services/message-queue.js.map +1 -0
  129. package/dist/services/message-router.d.ts +8 -0
  130. package/dist/services/message-router.d.ts.map +1 -0
  131. package/dist/services/message-router.js +73 -0
  132. package/dist/services/message-router.js.map +1 -0
  133. package/dist/services/project-scanner.d.ts +12 -0
  134. package/dist/services/project-scanner.d.ts.map +1 -0
  135. package/dist/services/project-scanner.js +109 -0
  136. package/dist/services/project-scanner.js.map +1 -0
  137. package/dist/services/schedule-store.d.ts +14 -0
  138. package/dist/services/schedule-store.d.ts.map +1 -0
  139. package/dist/services/schedule-store.js +89 -0
  140. package/dist/services/schedule-store.js.map +1 -0
  141. package/dist/services/session-store.d.ts +8 -0
  142. package/dist/services/session-store.d.ts.map +1 -0
  143. package/dist/services/session-store.js +93 -0
  144. package/dist/services/session-store.js.map +1 -0
  145. package/dist/tools/close-session.d.ts +22 -0
  146. package/dist/tools/close-session.d.ts.map +1 -0
  147. package/dist/tools/close-session.js +38 -0
  148. package/dist/tools/close-session.js.map +1 -0
  149. package/dist/tools/create-session.d.ts +28 -0
  150. package/dist/tools/create-session.d.ts.map +1 -0
  151. package/dist/tools/create-session.js +40 -0
  152. package/dist/tools/create-session.js.map +1 -0
  153. package/dist/tools/get-thread-messages.d.ts +26 -0
  154. package/dist/tools/get-thread-messages.d.ts.map +1 -0
  155. package/dist/tools/get-thread-messages.js +33 -0
  156. package/dist/tools/get-thread-messages.js.map +1 -0
  157. package/dist/tools/index.d.ts +9 -0
  158. package/dist/tools/index.d.ts.map +1 -0
  159. package/dist/tools/index.js +10 -0
  160. package/dist/tools/index.js.map +1 -0
  161. package/dist/tools/react-to-message.d.ts +41 -0
  162. package/dist/tools/react-to-message.d.ts.map +1 -0
  163. package/dist/tools/react-to-message.js +30 -0
  164. package/dist/tools/react-to-message.js.map +1 -0
  165. package/dist/tools/send-to-thread.d.ts +24 -0
  166. package/dist/tools/send-to-thread.d.ts.map +1 -0
  167. package/dist/tools/send-to-thread.js +83 -0
  168. package/dist/tools/send-to-thread.js.map +1 -0
  169. package/dist/tools/wait-for-reply.d.ts +17 -0
  170. package/dist/tools/wait-for-reply.d.ts.map +1 -0
  171. package/dist/tools/wait-for-reply.js +65 -0
  172. package/dist/tools/wait-for-reply.js.map +1 -0
  173. package/dist/types.d.ts +85 -0
  174. package/dist/types.d.ts.map +1 -0
  175. package/dist/types.js +6 -0
  176. package/dist/types.js.map +1 -0
  177. package/dist/utils/card-builder.d.ts +16 -0
  178. package/dist/utils/card-builder.d.ts.map +1 -0
  179. package/dist/utils/card-builder.js +183 -0
  180. package/dist/utils/card-builder.js.map +1 -0
  181. package/dist/utils/idle-detector.d.ts +21 -0
  182. package/dist/utils/idle-detector.d.ts.map +1 -0
  183. package/dist/utils/idle-detector.js +95 -0
  184. package/dist/utils/idle-detector.js.map +1 -0
  185. package/dist/utils/logger.d.ts +7 -0
  186. package/dist/utils/logger.d.ts.map +1 -0
  187. package/dist/utils/logger.js +22 -0
  188. package/dist/utils/logger.js.map +1 -0
  189. package/dist/utils/message-parser.d.ts +45 -0
  190. package/dist/utils/message-parser.d.ts.map +1 -0
  191. package/dist/utils/message-parser.js +132 -0
  192. package/dist/utils/message-parser.js.map +1 -0
  193. package/dist/utils/terminal-renderer.d.ts +39 -0
  194. package/dist/utils/terminal-renderer.d.ts.map +1 -0
  195. package/dist/utils/terminal-renderer.js +186 -0
  196. package/dist/utils/terminal-renderer.js.map +1 -0
  197. package/dist/worker.d.ts +3 -0
  198. package/dist/worker.d.ts.map +1 -0
  199. package/dist/worker.js +411 -0
  200. package/dist/worker.js.map +1 -0
  201. package/ecosystem.config.cjs +15 -0
  202. package/package.json +60 -0
@@ -0,0 +1,238 @@
1
+ import * as Lark from '@larksuiteoapi/node-sdk';
2
+ import { writeFileSync, mkdirSync, existsSync } from 'node:fs';
3
+ import { dirname } from 'node:path';
4
+ import { config } from '../config.js';
5
+ import { logger } from '../utils/logger.js';
6
+ let client;
7
+ export function getLarkClient() {
8
+ if (!client) {
9
+ client = new Lark.Client({
10
+ appId: config.lark.appId,
11
+ appSecret: config.lark.appSecret,
12
+ });
13
+ }
14
+ return client;
15
+ }
16
+ export async function sendMessage(chatId, content, msgType = 'text') {
17
+ const c = getLarkClient();
18
+ const body = msgType === 'text' ? JSON.stringify({ text: content }) : content;
19
+ const res = await c.im.v1.message.create({
20
+ params: { receive_id_type: 'chat_id' },
21
+ data: {
22
+ receive_id: chatId,
23
+ msg_type: msgType,
24
+ content: body,
25
+ },
26
+ });
27
+ if (res.code !== 0) {
28
+ throw new Error(`Failed to send message: ${res.msg} (code: ${res.code})`);
29
+ }
30
+ const messageId = res.data?.message_id;
31
+ if (!messageId)
32
+ throw new Error('No message_id in response');
33
+ logger.info(`Sent message ${messageId} to chat ${chatId}`);
34
+ return messageId;
35
+ }
36
+ export async function replyMessage(messageId, content, msgType = 'text', replyInThread = false) {
37
+ const c = getLarkClient();
38
+ const body = msgType === 'text' ? JSON.stringify({ text: content }) : content;
39
+ const res = await c.im.v1.message.reply({
40
+ path: { message_id: messageId },
41
+ data: {
42
+ msg_type: msgType,
43
+ content: body,
44
+ ...(replyInThread ? { reply_in_thread: true } : {}),
45
+ },
46
+ });
47
+ if (res.code !== 0) {
48
+ throw new Error(`Failed to reply message: ${res.msg} (code: ${res.code})`);
49
+ }
50
+ const replyId = res.data?.message_id;
51
+ if (!replyId)
52
+ throw new Error('No message_id in reply response');
53
+ logger.info(`Replied ${replyId} to message ${messageId}${replyInThread ? ' (in thread)' : ''}`);
54
+ return replyId;
55
+ }
56
+ export async function addReaction(messageId, emojiType) {
57
+ const c = getLarkClient();
58
+ const res = await c.im.v1.messageReaction.create({
59
+ path: { message_id: messageId },
60
+ data: { reaction_type: { emoji_type: emojiType } },
61
+ });
62
+ if (res.code !== 0) {
63
+ throw new Error(`Failed to add reaction: ${res.msg} (code: ${res.code})`);
64
+ }
65
+ const reactionId = res.data?.reaction_id;
66
+ logger.info(`Added reaction ${emojiType} (${reactionId}) to message ${messageId}`);
67
+ return reactionId ?? '';
68
+ }
69
+ export async function removeReaction(messageId, reactionId) {
70
+ const c = getLarkClient();
71
+ const res = await c.im.v1.messageReaction.delete({
72
+ path: { message_id: messageId, reaction_id: reactionId },
73
+ });
74
+ if (res.code !== 0) {
75
+ throw new Error(`Failed to remove reaction: ${res.msg} (code: ${res.code})`);
76
+ }
77
+ logger.info(`Removed reaction ${reactionId} from message ${messageId}`);
78
+ }
79
+ export async function sendUserMessage(openId, content, msgType = 'text') {
80
+ const c = getLarkClient();
81
+ const body = msgType === 'text' ? JSON.stringify({ text: content }) : content;
82
+ const res = await c.im.v1.message.create({
83
+ params: { receive_id_type: 'open_id' },
84
+ data: {
85
+ receive_id: openId,
86
+ msg_type: msgType,
87
+ content: body,
88
+ },
89
+ });
90
+ if (res.code !== 0) {
91
+ throw new Error(`Failed to send user message: ${res.msg} (code: ${res.code})`);
92
+ }
93
+ const messageId = res.data?.message_id;
94
+ if (!messageId)
95
+ throw new Error('No message_id in response');
96
+ logger.info(`Sent DM ${messageId} to user ${openId}`);
97
+ return messageId;
98
+ }
99
+ export async function getChatInfo(chatId) {
100
+ const c = getLarkClient();
101
+ const res = await c.im.v1.chat.get({
102
+ path: { chat_id: chatId },
103
+ });
104
+ if (res.code !== 0) {
105
+ throw new Error(`Failed to get chat info: ${res.msg} (code: ${res.code})`);
106
+ }
107
+ // user_count excludes bots, only real users
108
+ return { userCount: Number(res.data?.user_count ?? 0) };
109
+ }
110
+ export async function updateMessage(messageId, cardJson) {
111
+ const c = getLarkClient();
112
+ const res = await c.im.v1.message.patch({
113
+ path: { message_id: messageId },
114
+ data: { content: cardJson },
115
+ });
116
+ if (res.code !== 0) {
117
+ throw new Error(`Failed to update message: ${res.msg} (code: ${res.code})`);
118
+ }
119
+ }
120
+ export async function getMessageDetail(messageId) {
121
+ const c = getLarkClient();
122
+ const res = await c.im.v1.message.get({
123
+ path: { message_id: messageId },
124
+ });
125
+ if (res.code !== 0) {
126
+ throw new Error(`Failed to get message: ${res.msg} (code: ${res.code})`);
127
+ }
128
+ return res.data;
129
+ }
130
+ export async function downloadMessageResource(messageId, fileKey, type, savePath) {
131
+ const c = getLarkClient();
132
+ const dir = dirname(savePath);
133
+ if (!existsSync(dir)) {
134
+ mkdirSync(dir, { recursive: true });
135
+ }
136
+ const res = await c.im.v1.messageResource.get({
137
+ path: { message_id: messageId, file_key: fileKey },
138
+ params: { type },
139
+ });
140
+ if (res instanceof Buffer) {
141
+ writeFileSync(savePath, res);
142
+ }
143
+ else if (res && typeof res === 'object' && 'writeFile' in res) {
144
+ await res.writeFile(savePath);
145
+ }
146
+ else {
147
+ // Response is likely a readable stream or buffer-like
148
+ const chunks = [];
149
+ for await (const chunk of res) {
150
+ chunks.push(Buffer.from(chunk));
151
+ }
152
+ writeFileSync(savePath, Buffer.concat(chunks));
153
+ }
154
+ logger.info(`Downloaded ${type} ${fileKey} → ${savePath}`);
155
+ }
156
+ /**
157
+ * Resolve email prefixes (e.g. "shenhan.sh") to Lark open_ids via batch user lookup.
158
+ * Accepts mixed input: items starting with "ou_" are kept as-is; everything else
159
+ * is treated as an email prefix and looked up as `${prefix}@bytedance.com`.
160
+ * Returns an array of open_ids (unresolvable entries are dropped with a warning).
161
+ */
162
+ export async function resolveAllowedUsers(raw) {
163
+ const openIds = [];
164
+ const emailPrefixes = [];
165
+ for (const v of raw) {
166
+ if (v.startsWith('ou_')) {
167
+ openIds.push(v);
168
+ }
169
+ else {
170
+ emailPrefixes.push(v);
171
+ }
172
+ }
173
+ if (emailPrefixes.length === 0)
174
+ return openIds;
175
+ const emails = emailPrefixes.map(p => p.includes('@') ? p : `${p}@bytedance.com`);
176
+ const c = getLarkClient();
177
+ try {
178
+ const res = await c.contact.v3.user.batchGetId({
179
+ params: { user_id_type: 'open_id' },
180
+ data: { emails, include_resigned: false },
181
+ });
182
+ if (res.code !== 0) {
183
+ logger.warn(`Failed to resolve emails to open_ids: ${res.msg} (code: ${res.code})`);
184
+ return openIds;
185
+ }
186
+ const userList = res.data?.user_list ?? [];
187
+ for (const item of userList) {
188
+ if (item.user_id) {
189
+ openIds.push(item.user_id);
190
+ logger.info(`Resolved ${item.email} → ${item.user_id}`);
191
+ }
192
+ else {
193
+ logger.warn(`Could not resolve email: ${item.email}`);
194
+ }
195
+ }
196
+ }
197
+ catch (err) {
198
+ logger.warn(`resolveAllowedUsers failed: ${err.message}`);
199
+ }
200
+ return openIds;
201
+ }
202
+ export async function listThreadMessages(chatId, rootMessageId, pageSize = 50) {
203
+ const c = getLarkClient();
204
+ const allMessages = [];
205
+ let pageToken;
206
+ // Lark API only supports container_id_type="chat", so we list chat messages
207
+ // and filter by root_id to get thread messages
208
+ do {
209
+ const res = await c.im.v1.message.list({
210
+ params: {
211
+ container_id_type: 'chat',
212
+ container_id: chatId,
213
+ page_size: pageSize,
214
+ sort_type: 'ByCreateTimeDesc',
215
+ ...(pageToken ? { page_token: pageToken } : {}),
216
+ },
217
+ });
218
+ if (res.code !== 0) {
219
+ throw new Error(`Failed to list messages: ${res.msg} (code: ${res.code})`);
220
+ }
221
+ if (res.data?.items) {
222
+ for (const item of res.data.items) {
223
+ // Include the root message itself and all its thread replies
224
+ if (item.message_id === rootMessageId || item.root_id === rootMessageId) {
225
+ allMessages.push(item);
226
+ }
227
+ }
228
+ }
229
+ pageToken = res.data?.page_token;
230
+ // Stop early if we've collected enough or gone past the root message timestamp
231
+ if (allMessages.length >= pageSize)
232
+ break;
233
+ } while (pageToken);
234
+ // Sort by create_time ascending
235
+ allMessages.sort((a, b) => (a.create_time ?? '').localeCompare(b.create_time ?? ''));
236
+ return allMessages;
237
+ }
238
+ //# sourceMappingURL=lark-client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lark-client.js","sourceRoot":"","sources":["../../src/services/lark-client.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ export declare function getBotOpenId(): string | null;
2
+ export declare function fetchBotOpenId(): Promise<void>;
3
+ export declare function start(): void;
4
+ export declare function stop(): void;
5
+ //# sourceMappingURL=lark-ws.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lark-ws.d.ts","sourceRoot":"","sources":["../../src/services/lark-ws.ts"],"names":[],"mappings":"AASA,wBAAgB,YAAY,IAAI,MAAM,GAAG,IAAI,CAE5C;AAED,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAiBpD;AAED,wBAAgB,KAAK,IAAI,IAAI,CAgD5B;AAED,wBAAgB,IAAI,IAAI,IAAI,CAI3B"}
@@ -0,0 +1,79 @@
1
+ import * as Lark from '@larksuiteoapi/node-sdk';
2
+ import { config } from '../config.js';
3
+ import { logger } from '../utils/logger.js';
4
+ import { parseEventMessage } from '../utils/message-parser.js';
5
+ import * as messageRouter from './message-router.js';
6
+ let wsClient = null;
7
+ let botOpenId = null;
8
+ export function getBotOpenId() {
9
+ return botOpenId;
10
+ }
11
+ export async function fetchBotOpenId() {
12
+ try {
13
+ const { getLarkClient } = await import('./lark-client.js');
14
+ const client = getLarkClient();
15
+ const res = await client.contact.v3.user.get({
16
+ params: { user_id_type: 'open_id' },
17
+ path: { user_id: 'me' },
18
+ });
19
+ if (res.code === 0 && res.data?.user?.open_id) {
20
+ botOpenId = res.data.user.open_id;
21
+ logger.info(`Bot open_id: ${botOpenId}`);
22
+ }
23
+ else {
24
+ logger.debug('Bot open_id not available (non-critical, using sender_type filter)');
25
+ }
26
+ }
27
+ catch (e) {
28
+ logger.debug(`Bot open_id fetch skipped (non-critical): ${e}`);
29
+ }
30
+ }
31
+ export function start() {
32
+ if (wsClient) {
33
+ logger.warn('WSClient already started');
34
+ return;
35
+ }
36
+ const eventDispatcher = new Lark.EventDispatcher({}).register({
37
+ 'im.message.receive_v1': async (data) => {
38
+ try {
39
+ logger.debug(`Raw event data keys: ${Object.keys(data).join(', ')}`);
40
+ const message = data.message;
41
+ const sender = data.sender; // sender is at data top-level, NOT inside message
42
+ if (!message) {
43
+ logger.warn('Received event without message');
44
+ return;
45
+ }
46
+ // Only process thread replies (messages with root_id)
47
+ const rootId = message.root_id;
48
+ if (!rootId) {
49
+ logger.debug('Ignoring non-thread message');
50
+ return;
51
+ }
52
+ // Skip bot's own messages (sender is at data level)
53
+ if (sender?.sender_type === 'app') {
54
+ logger.debug('Ignoring bot message');
55
+ return;
56
+ }
57
+ const { parsed } = parseEventMessage(data);
58
+ logger.info(`WS received message in thread ${rootId}: ${parsed.content.substring(0, 100)}`);
59
+ messageRouter.dispatch(rootId, parsed);
60
+ }
61
+ catch (err) {
62
+ logger.error(`Error handling message event: ${err}`);
63
+ }
64
+ },
65
+ });
66
+ wsClient = new Lark.WSClient({
67
+ appId: config.lark.appId,
68
+ appSecret: config.lark.appSecret,
69
+ loggerLevel: Lark.LoggerLevel.info,
70
+ });
71
+ wsClient.start({ eventDispatcher });
72
+ logger.info('WSClient started, listening for messages...');
73
+ }
74
+ export function stop() {
75
+ // WSClient doesn't expose a stop method directly, but we null it out
76
+ wsClient = null;
77
+ logger.info('WSClient stopped');
78
+ }
79
+ //# sourceMappingURL=lark-ws.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lark-ws.js","sourceRoot":"","sources":["../../src/services/lark-ws.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { LarkMessage } from '../types.js';
2
+ export declare function ensureQueue(rootMessageId: string): void;
3
+ export declare function appendMessage(rootMessageId: string, message: LarkMessage): void;
4
+ /** Reset offset to re-read all messages from a given byte position (0 = beginning). */
5
+ export declare function rewindOffset(rootMessageId: string, to?: number): void;
6
+ /** Return the current read offset (byte position) without advancing it. */
7
+ export declare function getOffset(rootMessageId: string): number;
8
+ export declare function readUnread(rootMessageId: string): LarkMessage[];
9
+ //# sourceMappingURL=message-queue.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-queue.d.ts","sourceRoot":"","sources":["../../src/services/message-queue.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAc/C,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CASvD;AAED,wBAAgB,aAAa,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAK/E;AAgBD,uFAAuF;AACvF,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,EAAE,EAAE,SAAI,GAAG,IAAI,CAEhE;AAED,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,CAEvD;AAED,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,EAAE,CA0B/D"}
@@ -0,0 +1,77 @@
1
+ import { readFileSync, writeFileSync, appendFileSync, mkdirSync, existsSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { config } from '../config.js';
4
+ import { logger } from '../utils/logger.js';
5
+ function getQueuesDir() {
6
+ return join(config.session.dataDir, 'queues');
7
+ }
8
+ function getQueueFile(rootMessageId) {
9
+ return join(getQueuesDir(), `${rootMessageId}.jsonl`);
10
+ }
11
+ function getOffsetFile(rootMessageId) {
12
+ return join(getQueuesDir(), `${rootMessageId}.offset`);
13
+ }
14
+ export function ensureQueue(rootMessageId) {
15
+ const dir = getQueuesDir();
16
+ if (!existsSync(dir)) {
17
+ mkdirSync(dir, { recursive: true });
18
+ }
19
+ const queueFile = getQueueFile(rootMessageId);
20
+ if (!existsSync(queueFile)) {
21
+ writeFileSync(queueFile, '', 'utf-8');
22
+ }
23
+ }
24
+ export function appendMessage(rootMessageId, message) {
25
+ ensureQueue(rootMessageId);
26
+ const line = JSON.stringify(message) + '\n';
27
+ appendFileSync(getQueueFile(rootMessageId), line, 'utf-8');
28
+ logger.debug(`MessageQueue: appended message to ${rootMessageId}`);
29
+ }
30
+ function readOffset(rootMessageId) {
31
+ const offsetFile = getOffsetFile(rootMessageId);
32
+ if (!existsSync(offsetFile))
33
+ return 0;
34
+ try {
35
+ return parseInt(readFileSync(offsetFile, 'utf-8').trim(), 10) || 0;
36
+ }
37
+ catch {
38
+ return 0;
39
+ }
40
+ }
41
+ function writeOffset(rootMessageId, offset) {
42
+ writeFileSync(getOffsetFile(rootMessageId), String(offset), 'utf-8');
43
+ }
44
+ /** Reset offset to re-read all messages from a given byte position (0 = beginning). */
45
+ export function rewindOffset(rootMessageId, to = 0) {
46
+ writeOffset(rootMessageId, to);
47
+ }
48
+ /** Return the current read offset (byte position) without advancing it. */
49
+ export function getOffset(rootMessageId) {
50
+ return readOffset(rootMessageId);
51
+ }
52
+ export function readUnread(rootMessageId) {
53
+ const queueFile = getQueueFile(rootMessageId);
54
+ if (!existsSync(queueFile))
55
+ return [];
56
+ const content = readFileSync(queueFile, 'utf-8');
57
+ const offset = readOffset(rootMessageId);
58
+ if (offset >= content.length)
59
+ return [];
60
+ const unread = content.slice(offset);
61
+ const messages = [];
62
+ for (const line of unread.split('\n')) {
63
+ if (line.trim()) {
64
+ try {
65
+ messages.push(JSON.parse(line));
66
+ }
67
+ catch {
68
+ logger.warn(`MessageQueue: failed to parse line: ${line}`);
69
+ }
70
+ }
71
+ }
72
+ if (messages.length > 0) {
73
+ writeOffset(rootMessageId, content.length);
74
+ }
75
+ return messages;
76
+ }
77
+ //# sourceMappingURL=message-queue.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-queue.js","sourceRoot":"","sources":["../../src/services/message-queue.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { LarkMessage } from '../types.js';
2
+ export declare function register(rootMessageId: string): void;
3
+ export declare function unregister(rootMessageId: string): void;
4
+ export declare function dispatch(rootMessageId: string, message: LarkMessage): void;
5
+ export declare function getPendingMessages(rootMessageId: string): LarkMessage[];
6
+ export declare function waitForNext(rootMessageId: string, timeoutMs: number): Promise<LarkMessage[]>;
7
+ export declare function isRegistered(rootMessageId: string): boolean;
8
+ //# sourceMappingURL=message-router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-router.d.ts","sourceRoot":"","sources":["../../src/services/message-router.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAe/C,wBAAgB,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAKpD;AAED,wBAAgB,UAAU,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAWtD;AAED,wBAAgB,QAAQ,CAAC,aAAa,EAAE,MAAM,EAAE,OAAO,EAAE,WAAW,GAAG,IAAI,CAmB1E;AAED,wBAAgB,kBAAkB,CAAC,aAAa,EAAE,MAAM,GAAG,WAAW,EAAE,CAOvE;AAED,wBAAgB,WAAW,CAAC,aAAa,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,EAAE,CAAC,CAwB5F;AAED,wBAAgB,YAAY,CAAC,aAAa,EAAE,MAAM,GAAG,OAAO,CAE3D"}
@@ -0,0 +1,73 @@
1
+ import { logger } from '../utils/logger.js';
2
+ const threads = new Map();
3
+ export function register(rootMessageId) {
4
+ if (!threads.has(rootMessageId)) {
5
+ threads.set(rootMessageId, { queue: [], waiters: [] });
6
+ logger.info(`MessageRouter: registered thread ${rootMessageId}`);
7
+ }
8
+ }
9
+ export function unregister(rootMessageId) {
10
+ const state = threads.get(rootMessageId);
11
+ if (state) {
12
+ // Resolve all pending waiters with empty result
13
+ for (const waiter of state.waiters) {
14
+ clearTimeout(waiter.timer);
15
+ waiter.resolve([]);
16
+ }
17
+ threads.delete(rootMessageId);
18
+ logger.info(`MessageRouter: unregistered thread ${rootMessageId}`);
19
+ }
20
+ }
21
+ export function dispatch(rootMessageId, message) {
22
+ const state = threads.get(rootMessageId);
23
+ if (!state) {
24
+ logger.debug(`MessageRouter: no listener for thread ${rootMessageId}, ignoring`);
25
+ return;
26
+ }
27
+ // If someone is waiting, resolve immediately
28
+ if (state.waiters.length > 0) {
29
+ const waiter = state.waiters.shift();
30
+ clearTimeout(waiter.timer);
31
+ waiter.resolve([message]);
32
+ logger.debug(`MessageRouter: resolved waiter for thread ${rootMessageId}`);
33
+ return;
34
+ }
35
+ // Otherwise queue the message
36
+ state.queue.push(message);
37
+ logger.debug(`MessageRouter: queued message for thread ${rootMessageId}, queue size: ${state.queue.length}`);
38
+ }
39
+ export function getPendingMessages(rootMessageId) {
40
+ const state = threads.get(rootMessageId);
41
+ if (!state)
42
+ return [];
43
+ const messages = [...state.queue];
44
+ state.queue = [];
45
+ return messages;
46
+ }
47
+ export function waitForNext(rootMessageId, timeoutMs) {
48
+ const state = threads.get(rootMessageId);
49
+ if (!state) {
50
+ return Promise.resolve([]);
51
+ }
52
+ // Check queue first
53
+ if (state.queue.length > 0) {
54
+ const messages = [...state.queue];
55
+ state.queue = [];
56
+ return Promise.resolve(messages);
57
+ }
58
+ // Create a promise that resolves when a message arrives or timeout
59
+ return new Promise((resolve) => {
60
+ const timer = setTimeout(() => {
61
+ // Remove this waiter on timeout
62
+ const idx = state.waiters.findIndex(w => w.resolve === resolve);
63
+ if (idx !== -1)
64
+ state.waiters.splice(idx, 1);
65
+ resolve([]);
66
+ }, timeoutMs);
67
+ state.waiters.push({ resolve, timer });
68
+ });
69
+ }
70
+ export function isRegistered(rootMessageId) {
71
+ return threads.has(rootMessageId);
72
+ }
73
+ //# sourceMappingURL=message-router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-router.js","sourceRoot":"","sources":["../../src/services/message-router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ export interface ProjectInfo {
2
+ name: string;
3
+ path: string;
4
+ type: 'repo' | 'worktree';
5
+ branch: string;
6
+ }
7
+ /**
8
+ * Scan a directory for git repositories and their worktrees.
9
+ * Returns a flat list of all projects found.
10
+ */
11
+ export declare function scanProjects(baseDir: string, maxDepth?: number): ProjectInfo[];
12
+ //# sourceMappingURL=project-scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project-scanner.d.ts","sourceRoot":"","sources":["../../src/services/project-scanner.ts"],"names":[],"mappings":"AAKA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,GAAG,UAAU,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;CAChB;AA2CD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,GAAE,MAAU,GAAG,WAAW,EAAE,CA6DjF"}