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,95 @@
1
+ /** Spinner frames — animate while CLI is working */
2
+ const SPINNER_RE = /[·✢✳✶✻✽]/;
3
+ /** Default quiescence timeout (ms) — idle if PTY silent + no recent spinner */
4
+ const QUIESCENCE_MS = 2_000;
5
+ /** Spinner guard — don't declare idle if spinner seen within this window */
6
+ const SPINNER_GUARD_MS = 3_000;
7
+ export class IdleDetector {
8
+ outputTail = '';
9
+ lastSpinnerAt = 0;
10
+ quiescenceTimer = null;
11
+ isIdle = false;
12
+ idleCallback = null;
13
+ completionPattern;
14
+ readyPattern;
15
+ readySeen = false;
16
+ constructor(cli) {
17
+ this.completionPattern = cli.completionPattern;
18
+ this.readyPattern = cli.readyPattern;
19
+ }
20
+ onIdle(cb) {
21
+ this.idleCallback = cb;
22
+ }
23
+ feed(data) {
24
+ if (this.isIdle)
25
+ return;
26
+ const stripped = this.stripAnsi(data);
27
+ this.outputTail = (this.outputTail + stripped).slice(-500);
28
+ // Track when the CLI's input prompt appears
29
+ if (this.readyPattern && !this.readySeen) {
30
+ if (this.readyPattern.test(this.outputTail)) {
31
+ this.readySeen = true;
32
+ }
33
+ }
34
+ // Track spinner — but not if it's part of completion marker
35
+ if (SPINNER_RE.test(stripped) && !(this.completionPattern?.test(this.outputTail))) {
36
+ this.lastSpinnerAt = Date.now();
37
+ }
38
+ // Strategy 1: CLI-specific completion marker
39
+ if (this.completionPattern?.test(this.outputTail)) {
40
+ this.clearTimer();
41
+ this.quiescenceTimer = setTimeout(() => {
42
+ this.quiescenceTimer = null;
43
+ if (!this.isIdle)
44
+ this.markIdle();
45
+ }, 500);
46
+ return;
47
+ }
48
+ // Strategy 2: quiescence (PTY silence + no recent spinner)
49
+ // When readyPattern is set, suppress quiescence until the input prompt appears.
50
+ if (this.readyPattern && !this.readySeen)
51
+ return;
52
+ this.clearTimer();
53
+ this.quiescenceTimer = setTimeout(() => this.quiescenceCheck(), QUIESCENCE_MS);
54
+ }
55
+ reset() {
56
+ this.isIdle = false;
57
+ this.outputTail = '';
58
+ this.readySeen = false;
59
+ this.lastSpinnerAt = Date.now();
60
+ this.clearTimer();
61
+ }
62
+ dispose() {
63
+ this.clearTimer();
64
+ this.idleCallback = null;
65
+ }
66
+ quiescenceCheck() {
67
+ this.quiescenceTimer = null;
68
+ if (this.isIdle)
69
+ return;
70
+ const sinceSpinner = Date.now() - this.lastSpinnerAt;
71
+ if (sinceSpinner < SPINNER_GUARD_MS) {
72
+ this.quiescenceTimer = setTimeout(() => this.quiescenceCheck(), SPINNER_GUARD_MS - sinceSpinner + 200);
73
+ return;
74
+ }
75
+ this.markIdle();
76
+ }
77
+ markIdle() {
78
+ this.isIdle = true;
79
+ this.outputTail = '';
80
+ this.clearTimer();
81
+ this.idleCallback?.();
82
+ }
83
+ clearTimer() {
84
+ if (this.quiescenceTimer) {
85
+ clearTimeout(this.quiescenceTimer);
86
+ this.quiescenceTimer = null;
87
+ }
88
+ }
89
+ stripAnsi(str) {
90
+ return str
91
+ .replace(/\x1b\[(\d*)C/g, (_m, n) => ' '.repeat(Number(n) || 1))
92
+ .replace(/\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\x1b[()][0-9A-B]|\x1b\[[\?]?[0-9;]*[hlmsuJ]/g, '');
93
+ }
94
+ }
95
+ //# sourceMappingURL=idle-detector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"idle-detector.js","sourceRoot":"","sources":["../../src/utils/idle-detector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export declare const logger: {
2
+ info(msg: string, ...args: unknown[]): void;
3
+ warn(msg: string, ...args: unknown[]): void;
4
+ error(msg: string, ...args: unknown[]): void;
5
+ debug(msg: string, ...args: unknown[]): void;
6
+ };
7
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,MAAM;cACP,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;cAGjC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;eAGhC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;eAGjC,MAAM,WAAW,OAAO,EAAE,GAAG,IAAI;CAK7C,CAAC"}
@@ -0,0 +1,22 @@
1
+ function timestamp() {
2
+ return new Date().toISOString();
3
+ }
4
+ // All log levels write to stderr. stdout is reserved for data protocols
5
+ // (e.g. MCP stdio transport) and must never be polluted with log text.
6
+ export const logger = {
7
+ info(msg, ...args) {
8
+ process.stderr.write(`[${timestamp()}] [INFO] ${msg} ${args.map(a => JSON.stringify(a)).join(' ')}\n`);
9
+ },
10
+ warn(msg, ...args) {
11
+ process.stderr.write(`[${timestamp()}] [WARN] ${msg} ${args.map(a => JSON.stringify(a)).join(' ')}\n`);
12
+ },
13
+ error(msg, ...args) {
14
+ process.stderr.write(`[${timestamp()}] [ERROR] ${msg} ${args.map(a => JSON.stringify(a)).join(' ')}\n`);
15
+ },
16
+ debug(msg, ...args) {
17
+ if (process.env.DEBUG) {
18
+ process.stderr.write(`[${timestamp()}] [DEBUG] ${msg} ${args.map(a => JSON.stringify(a)).join(' ')}\n`);
19
+ }
20
+ },
21
+ };
22
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,SAAS,SAAS;IAChB,OAAO,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;AAClC,CAAC;AAED,wEAAwE;AACxE,uEAAuE;AACvE,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,YAAY,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzG,CAAC;IACD,IAAI,CAAC,GAAW,EAAE,GAAG,IAAe;QAClC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,YAAY,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACzG,CAAC;IACD,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,aAAa,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC1G,CAAC;IACD,KAAK,CAAC,GAAW,EAAE,GAAG,IAAe;QACnC,IAAI,OAAO,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC;YACtB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,SAAS,EAAE,aAAa,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC1G,CAAC;IACH,CAAC;CACF,CAAC"}
@@ -0,0 +1,45 @@
1
+ import type { LarkMessage } from '../types.js';
2
+ interface RawEventData {
3
+ sender: {
4
+ sender_id: {
5
+ open_id?: string;
6
+ user_id?: string;
7
+ union_id?: string;
8
+ };
9
+ sender_type: string;
10
+ tenant_key?: string;
11
+ };
12
+ message: {
13
+ message_id: string;
14
+ root_id?: string;
15
+ parent_id?: string;
16
+ message_type: string;
17
+ content: string;
18
+ chat_id: string;
19
+ chat_type: string;
20
+ create_time: string;
21
+ mentions?: Array<{
22
+ key: string;
23
+ name: string;
24
+ id?: {
25
+ open_id?: string;
26
+ user_id?: string;
27
+ union_id?: string;
28
+ };
29
+ tenant_key?: string;
30
+ }>;
31
+ };
32
+ }
33
+ export interface MessageResource {
34
+ type: 'image' | 'file';
35
+ key: string;
36
+ name: string;
37
+ }
38
+ export declare function extractResources(msgType: string, rawContent: string): MessageResource[];
39
+ export declare function parseEventMessage(data: RawEventData): {
40
+ parsed: LarkMessage;
41
+ resources: MessageResource[];
42
+ };
43
+ export declare function parseApiMessage(msg: any): LarkMessage;
44
+ export {};
45
+ //# sourceMappingURL=message-parser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-parser.d.ts","sourceRoot":"","sources":["../../src/utils/message-parser.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK/C,UAAU,YAAY;IACpB,MAAM,EAAE;QACN,SAAS,EAAE;YACT,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,OAAO,CAAC,EAAE,MAAM,CAAC;YACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;SACnB,CAAC;QACF,WAAW,EAAE,MAAM,CAAC;QACpB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,OAAO,EAAE;QACP,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,CAAC;QACrB,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,WAAW,EAAE,MAAM,CAAC;QACpB,QAAQ,CAAC,EAAE,KAAK,CAAC;YACf,GAAG,EAAE,MAAM,CAAC;YACZ,IAAI,EAAE,MAAM,CAAC;YACb,EAAE,CAAC,EAAE;gBAAE,OAAO,CAAC,EAAE,MAAM,CAAC;gBAAC,OAAO,CAAC,EAAE,MAAM,CAAC;gBAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;aAAE,CAAC;YAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,OAAO,GAAG,MAAM,CAAC;IACvB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;CACd;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,eAAe,EAAE,CAmCvF;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,YAAY,GAAG;IAAE,MAAM,EAAE,WAAW,CAAC;IAAC,SAAS,EAAE,eAAe,EAAE,CAAA;CAAE,CAmB3G;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,GAAG,GAAG,WAAW,CAUrD"}
@@ -0,0 +1,132 @@
1
+ import { logger } from './logger.js';
2
+ export function extractResources(msgType, rawContent) {
3
+ try {
4
+ const parsed = JSON.parse(rawContent);
5
+ if (msgType === 'image') {
6
+ const imageKey = parsed.image_key;
7
+ if (imageKey) {
8
+ return [{ type: 'image', key: imageKey, name: `${imageKey}.jpg` }];
9
+ }
10
+ }
11
+ if (msgType === 'file') {
12
+ const fileKey = parsed.file_key;
13
+ if (fileKey) {
14
+ return [{ type: 'file', key: fileKey, name: parsed.file_name ?? fileKey }];
15
+ }
16
+ }
17
+ if (msgType === 'post') {
18
+ const resources = [];
19
+ const { content: contentBlocks } = resolvePostBody(parsed);
20
+ for (const block of contentBlocks) {
21
+ const nodes = Array.isArray(block) ? block : [block];
22
+ for (const node of nodes) {
23
+ if (node.tag === 'img' && node.image_key) {
24
+ resources.push({ type: 'image', key: node.image_key, name: `${node.image_key}.jpg` });
25
+ }
26
+ }
27
+ }
28
+ return resources;
29
+ }
30
+ }
31
+ catch {
32
+ // ignore parse errors
33
+ }
34
+ return [];
35
+ }
36
+ export function parseEventMessage(data) {
37
+ const { sender, message } = data;
38
+ // Debug: log raw message for non-text types
39
+ if (message.message_type !== 'text') {
40
+ logger.info(`[parser] type=${message.message_type} content=${message.content} keys=${Object.keys(message).join(',')}`);
41
+ }
42
+ const resources = extractResources(message.message_type, message.content);
43
+ const parsed = {
44
+ messageId: message.message_id,
45
+ rootId: message.root_id ?? '',
46
+ senderId: sender.sender_id?.open_id ?? '',
47
+ senderType: sender.sender_type,
48
+ msgType: message.message_type,
49
+ content: extractTextContent(message.message_type, message.content, message.mentions),
50
+ createTime: message.create_time,
51
+ };
52
+ return { parsed, resources };
53
+ }
54
+ export function parseApiMessage(msg) {
55
+ return {
56
+ messageId: msg.message_id ?? '',
57
+ rootId: msg.root_id ?? msg.thread_id ?? '',
58
+ senderId: msg.sender?.id ?? '',
59
+ senderType: msg.sender?.sender_type ?? 'unknown',
60
+ msgType: msg.msg_type ?? 'text',
61
+ content: extractTextContent(msg.msg_type ?? 'text', msg.body?.content ?? ''),
62
+ createTime: msg.create_time ?? '',
63
+ };
64
+ }
65
+ /** Resolve post body from either wrapped {"zh_cn":{title,content}} or unwrapped {title,content} format */
66
+ function resolvePostBody(parsed) {
67
+ // Unwrapped: has content array directly
68
+ if (Array.isArray(parsed.content)) {
69
+ return { title: parsed.title ?? '', content: parsed.content };
70
+ }
71
+ // Wrapped in language key: {"zh_cn": {title, content}}
72
+ for (const key of Object.keys(parsed)) {
73
+ const val = parsed[key];
74
+ if (val && typeof val === 'object' && Array.isArray(val.content)) {
75
+ return { title: val.title ?? '', content: val.content };
76
+ }
77
+ }
78
+ return { title: '', content: [] };
79
+ }
80
+ function resolveMentions(text, mentions) {
81
+ if (!mentions || mentions.length === 0) {
82
+ // No mention info available — strip placeholders
83
+ return text.replace(/@_user_\d+/g, '').replace(/\s{2,}/g, ' ').trim();
84
+ }
85
+ let result = text;
86
+ for (const m of mentions) {
87
+ result = result.replace(m.key, `@${m.name}`);
88
+ }
89
+ return result.trim();
90
+ }
91
+ function extractTextContent(msgType, rawContent, mentions) {
92
+ try {
93
+ if (msgType === 'text') {
94
+ const parsed = JSON.parse(rawContent);
95
+ return resolveMentions(parsed.text ?? rawContent, mentions);
96
+ }
97
+ if (msgType === 'post') {
98
+ const parsed = JSON.parse(rawContent);
99
+ const { title, content } = resolvePostBody(parsed);
100
+ const body = content
101
+ .flat()
102
+ .filter((node) => node.tag === 'text' || node.tag === 'a' || node.tag === 'at')
103
+ .map((node) => {
104
+ if (node.tag === 'at')
105
+ return `@${node.user_name ?? 'unknown'}`;
106
+ return node.text ?? node.href ?? '';
107
+ })
108
+ .join('');
109
+ return title ? `${title}\n${body}` : body;
110
+ }
111
+ if (msgType === 'image') {
112
+ return '[图片]';
113
+ }
114
+ if (msgType === 'file') {
115
+ try {
116
+ const p = JSON.parse(rawContent);
117
+ return `[文件: ${p.file_name ?? 'unknown'}]`;
118
+ }
119
+ catch {
120
+ return '[文件]';
121
+ }
122
+ }
123
+ if (msgType === 'interactive') {
124
+ return '[interactive card]';
125
+ }
126
+ return rawContent;
127
+ }
128
+ catch {
129
+ return rawContent;
130
+ }
131
+ }
132
+ //# sourceMappingURL=message-parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"message-parser.js","sourceRoot":"","sources":["../../src/utils/message-parser.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ export declare class TerminalRenderer {
2
+ private terminal;
3
+ private lastHash;
4
+ /** Absolute line index where the current turn starts. */
5
+ private turnBaselineY;
6
+ /** Best content seen this turn — prevents TUI redraws from wiping output. */
7
+ private peakContent;
8
+ constructor(cols: number, rows: number);
9
+ /** Feed raw PTY data into the virtual terminal. */
10
+ write(data: string): void;
11
+ /**
12
+ * Mark the start of a new conversation turn.
13
+ * Subsequent snapshots will only include content from after this point.
14
+ */
15
+ markNewTurn(): void;
16
+ /**
17
+ * Snapshot the current screen content.
18
+ *
19
+ * Strategy:
20
+ * 1. Try from turn baseline with Phase 1 marker gating (Claude Code style).
21
+ * 2. If empty, fall back to full viewport without Phase 1 (CoCo/TUI style).
22
+ * TUI apps redraw the entire screen, so content may appear anywhere.
23
+ * 3. Peak retention: save the best content seen this turn. When the TUI
24
+ * redraws to idle (empty screen), return the saved peak instead.
25
+ */
26
+ snapshot(): {
27
+ content: string;
28
+ changed: boolean;
29
+ };
30
+ /**
31
+ * Read and filter terminal content starting from `startY`.
32
+ * @param skipPhase1 When true, include all non-chrome lines (no marker gating).
33
+ * Used for TUI viewport fallback where output markers differ.
34
+ */
35
+ private extractContent;
36
+ resize(cols: number, rows: number): void;
37
+ dispose(): void;
38
+ }
39
+ //# sourceMappingURL=terminal-renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-renderer.d.ts","sourceRoot":"","sources":["../../src/utils/terminal-renderer.ts"],"names":[],"mappings":"AAmFA,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,QAAQ,CAAgC;IAChD,OAAO,CAAC,QAAQ,CAAM;IACtB,yDAAyD;IACzD,OAAO,CAAC,aAAa,CAAK;IAC1B,6EAA6E;IAC7E,OAAO,CAAC,WAAW,CAAM;gBAEb,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM;IAItC,mDAAmD;IACnD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB;;;OAGG;IACH,WAAW,IAAI,IAAI;IAoBnB;;;;;;;;;OASG;IACH,QAAQ,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IA4BjD;;;;OAIG;IACH,OAAO,CAAC,cAAc;IA2CtB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxC,OAAO,IAAI,IAAI;CAGhB"}
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Headless terminal renderer: feeds PTY data into an xterm-headless instance
3
+ * and periodically snapshots the rendered screen for Feishu card updates.
4
+ *
5
+ * Filters out TUI chrome and preamble (logo, version, prompt echo, system
6
+ * instructions) so only the CLI's actual work output appears in the card.
7
+ *
8
+ * Supports two rendering styles:
9
+ * - CLI-style (Claude Code, Aiden): output appends to scrollback. Baseline
10
+ * tracking isolates current-turn content. Phase 1 (OUTPUT_MARKER_RE) gates
11
+ * content detection.
12
+ * - TUI-style (CoCo, Codex): cursor-positioned full-screen UI. Content can
13
+ * be overwritten when the TUI redraws (e.g. response → idle prompt).
14
+ * Viewport fallback + peak retention ensure response content is captured.
15
+ *
16
+ * Timer overlay avoidance: The PTY is intentionally wider than normal so that
17
+ * right-aligned TUI overlays (elapsed time, timeout counters) are rendered
18
+ * far to the right. Snapshots only read the first `contentCols` columns,
19
+ * cleanly excluding the overlay area — no fragile regex stripping needed.
20
+ */
21
+ import xtermHeadless from '@xterm/headless';
22
+ const { Terminal } = xtermHeadless;
23
+ import { createHash } from 'node:crypto';
24
+ // ─── Box-Drawing Cleanup ─────────────────────────────────────────────────────
25
+ // Claude Code TUI renders panel borders with box-drawing characters.
26
+ // The headless terminal captures them overlapping with content text.
27
+ /** Strip box-drawing horizontal/vertical/corner characters, collapse spaces. */
28
+ function cleanBoxDrawing(line) {
29
+ return line
30
+ .replace(/[─━│┌┐└┘├┤┬┴┼╭╮╯╰]/g, ' ')
31
+ .replace(/ +/g, ' ')
32
+ .trimEnd();
33
+ }
34
+ // ─── Line Filters ────────────────────────────────────────────────────────────
35
+ /** Bare prompt line: ❯ (Claude) or > (Aiden) with optional trailing whitespace */
36
+ const BARE_PROMPT_RE = /^[❯>]\s*$/;
37
+ /** Input echo: ❯ or > followed by user text */
38
+ const INPUT_ECHO_RE = /^[❯>]\s+\S/;
39
+ /** Status bar: Claude ("bypass permissions", "⏵⏵", "/model"), Aiden ("agent full mode"),
40
+ * CoCo ("accept all tools", "shell mode") */
41
+ const STATUS_BAR_RE = /bypass permissions|⏵⏵|shift\+tab|\/model|auto-update|agent full mode|IDE: \w+|accept all tools/;
42
+ /** CLI logo — block drawing characters used in ASCII art splash screens.
43
+ * Includes Claude Code (▐▛█▜▝▘) and CoCo (▄▀◆) character sets. */
44
+ const LOGO_RE = /[▐▛█▜▝▘▄▀◆]{2,}/;
45
+ /** CLI version / banner info lines */
46
+ const VERSION_RE = /Claude Code v\d|^\s*(Opus|Sonnet|Haiku)\s+\d|>_ Aiden \(v[\d.]+\)|Trae CLI \d|Formerly Coco/;
47
+ /** CoCo TUI chrome: input placeholder, mode/keyboard hints, welcome screen */
48
+ const COCO_CHROME_RE = /Ask anything|shell mode.*command mode|Ctrl\+J new line|Codebase Copilot|Did you know|Welcome back,|Use \/status|Announcements|code\.byted\.org/;
49
+ /** Empty or whitespace-only */
50
+ const BLANK_RE = /^\s*$/;
51
+ function shouldSkipLine(line) {
52
+ return (BARE_PROMPT_RE.test(line) ||
53
+ INPUT_ECHO_RE.test(line) ||
54
+ STATUS_BAR_RE.test(line) ||
55
+ LOGO_RE.test(line) ||
56
+ VERSION_RE.test(line) ||
57
+ COCO_CHROME_RE.test(line));
58
+ }
59
+ /** CLI output markers — lines starting with these indicate real work output.
60
+ * Includes CoCo spinner chars (❇❋✢) alongside Claude Code's markers. */
61
+ const OUTPUT_MARKER_RE = /^[●·⎿✓⚠★☐☑⏵✽✻❇❋✢]|^\s+⎿/;
62
+ /**
63
+ * How many columns to read from each line for the Feishu card snapshot.
64
+ * Content beyond this is ignored — this is where TUI overlays (timer, timeout)
65
+ * live when the PTY is wider than this value.
66
+ */
67
+ const SNAPSHOT_COLS = 160;
68
+ export class TerminalRenderer {
69
+ terminal;
70
+ lastHash = '';
71
+ /** Absolute line index where the current turn starts. */
72
+ turnBaselineY = 0;
73
+ /** Best content seen this turn — prevents TUI redraws from wiping output. */
74
+ peakContent = '';
75
+ constructor(cols, rows) {
76
+ this.terminal = new Terminal({ cols, rows, allowProposedApi: true });
77
+ }
78
+ /** Feed raw PTY data into the virtual terminal. */
79
+ write(data) {
80
+ this.terminal.write(data);
81
+ }
82
+ /**
83
+ * Mark the start of a new conversation turn.
84
+ * Subsequent snapshots will only include content from after this point.
85
+ */
86
+ markNewTurn() {
87
+ const buffer = this.terminal.buffer.active;
88
+ const baseY = buffer.baseY;
89
+ const rows = this.terminal.rows;
90
+ // Find the last non-empty line in the current viewport
91
+ let lastContentY = baseY;
92
+ for (let y = rows - 1; y >= 0; y--) {
93
+ const line = buffer.getLine(baseY + y);
94
+ if (line && line.translateToString(true).trimEnd()) {
95
+ lastContentY = baseY + y + 1;
96
+ break;
97
+ }
98
+ }
99
+ this.turnBaselineY = lastContentY;
100
+ this.peakContent = '';
101
+ this.lastHash = '';
102
+ }
103
+ /**
104
+ * Snapshot the current screen content.
105
+ *
106
+ * Strategy:
107
+ * 1. Try from turn baseline with Phase 1 marker gating (Claude Code style).
108
+ * 2. If empty, fall back to full viewport without Phase 1 (CoCo/TUI style).
109
+ * TUI apps redraw the entire screen, so content may appear anywhere.
110
+ * 3. Peak retention: save the best content seen this turn. When the TUI
111
+ * redraws to idle (empty screen), return the saved peak instead.
112
+ */
113
+ snapshot() {
114
+ const buffer = this.terminal.buffer.active;
115
+ const baseY = buffer.baseY;
116
+ // Strategy 1: baseline read with Phase 1 marker gating (CLI-style output)
117
+ let content = this.extractContent(this.turnBaselineY, false);
118
+ // Strategy 2: full viewport without Phase 1 (TUI-style — content anywhere on screen)
119
+ if (!content) {
120
+ content = this.extractContent(baseY, true);
121
+ }
122
+ // Peak retention — TUI redraws can wipe response from the terminal buffer.
123
+ // Save non-empty content; return saved peak when current screen is empty.
124
+ if (content) {
125
+ this.peakContent = content;
126
+ }
127
+ else {
128
+ content = this.peakContent;
129
+ }
130
+ // Hash-based change detection
131
+ const hash = createHash('md5').update(content).digest('hex');
132
+ const changed = hash !== this.lastHash;
133
+ this.lastHash = hash;
134
+ return { content, changed };
135
+ }
136
+ /**
137
+ * Read and filter terminal content starting from `startY`.
138
+ * @param skipPhase1 When true, include all non-chrome lines (no marker gating).
139
+ * Used for TUI viewport fallback where output markers differ.
140
+ */
141
+ extractContent(startY, skipPhase1) {
142
+ const buffer = this.terminal.buffer.active;
143
+ const baseY = buffer.baseY;
144
+ const rows = this.terminal.rows;
145
+ const readCols = Math.min(SNAPSHOT_COLS, this.terminal.cols);
146
+ const endY = baseY + rows;
147
+ const rawLines = [];
148
+ for (let y = startY; y < endY; y++) {
149
+ const line = buffer.getLine(y);
150
+ if (!line)
151
+ continue;
152
+ rawLines.push(cleanBoxDrawing(line.translateToString(true, 0, readCols)));
153
+ }
154
+ let foundOutput = skipPhase1;
155
+ const filtered = [];
156
+ for (const line of rawLines) {
157
+ if (!foundOutput) {
158
+ // Phase 1: skip lines until we see an output marker
159
+ if (OUTPUT_MARKER_RE.test(line)) {
160
+ foundOutput = true;
161
+ filtered.push(line);
162
+ }
163
+ continue;
164
+ }
165
+ // Phase 2: filter TUI chrome but keep content
166
+ if (shouldSkipLine(line))
167
+ continue;
168
+ filtered.push(line);
169
+ }
170
+ // Trim leading and trailing empty lines
171
+ while (filtered.length > 0 && BLANK_RE.test(filtered[0])) {
172
+ filtered.shift();
173
+ }
174
+ while (filtered.length > 0 && BLANK_RE.test(filtered[filtered.length - 1])) {
175
+ filtered.pop();
176
+ }
177
+ return filtered.join('\n');
178
+ }
179
+ resize(cols, rows) {
180
+ this.terminal.resize(cols, rows);
181
+ }
182
+ dispose() {
183
+ this.terminal.dispose();
184
+ }
185
+ }
186
+ //# sourceMappingURL=terminal-renderer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"terminal-renderer.js","sourceRoot":"","sources":["../../src/utils/terminal-renderer.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":""}