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
package/.env.example ADDED
@@ -0,0 +1,24 @@
1
+ # Lark (Feishu) App Credentials — required
2
+ LARK_APP_ID=
3
+ LARK_APP_SECRET=
4
+
5
+ # Session data directory (default: ./data)
6
+ # SESSION_DATA_DIR=
7
+
8
+ # Daemon settings
9
+ # CLI_ID=claude-code # CLI adapter: claude-code, aiden, coco, codex
10
+ # WORKING_DIR=~ # Default working directory
11
+
12
+ # Access control (comma-separated email prefixes or Lark open_ids, empty = allow all)
13
+ # ALLOWED_USERS=alice,bob
14
+ # LARK_EMAIL_DOMAIN=example.com # Default email domain for ALLOWED_USERS prefixes
15
+
16
+ # Project scanning directory for /repo command (default: parent of WORKING_DIR)
17
+ # PROJECT_SCAN_DIR=
18
+
19
+ # Web terminal settings
20
+ # WEB_HOST=0.0.0.0 # HTTP server bind address
21
+ # WEB_EXTERNAL_HOST= # External hostname/IP for terminal URLs (default: auto-detect LAN IP)
22
+
23
+ # Debug logging
24
+ # DEBUG=1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 botmux contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,267 @@
1
+ # botmux
2
+
3
+ [中文](README.md) | English
4
+
5
+ Bridge between Lark (Feishu) topic groups and AI coding CLIs. The daemon listens for Lark messages and automatically spawns an independent CLI process (supporting Claude Code, Aiden, CoCo, Codex) for each new topic thread, with live streaming cards and a web-based terminal.
6
+
7
+ ## Features
8
+
9
+ - **One topic = one AI coding session** — each Lark thread gets its own isolated CLI process
10
+ - **Multi-CLI support** — adapter architecture supports Claude Code, Aiden, CoCo, Codex, and is extensible
11
+ - **Live streaming cards** — real-time terminal output rendered in Feishu cards with markdown support, per-turn card lifecycle
12
+ - **Web terminal (xterm.js)** — full PTY output in the browser with optional write access via on-demand DM link
13
+ - **Session persistence** — sessions survive daemon restarts and resume automatically
14
+ - **Scheduled tasks** — cron-based recurring prompts with natural language scheduling (Chinese supported)
15
+ - **Project management** — interactive repo selector, per-session working directory
16
+ - **MCP integration** — CLI can reply to Lark threads, read message history, and add reactions via MCP tools
17
+ - **Access control** — allowlist for users, token-based write access for terminals, button restrictions on cards
18
+
19
+ ## Architecture
20
+
21
+ ```
22
+ Lark WebSocket Events
23
+ |
24
+ Daemon (daemon.ts → core/ modules)
25
+ |-- im/lark/event-dispatcher: Lark event routing
26
+ |-- im/lark/card-handler: card interaction handling
27
+ |-- core/worker-pool: worker process pool management
28
+ |-- core/command-handler: slash command processing
29
+ |-- core/session-manager: session lifecycle
30
+ |-- core/scheduler: cron task scheduling
31
+ |
32
+ Worker (worker.ts) -- forked child process per session
33
+ |-- adapters/cli/*: CLI adapters (Claude Code / Aiden / CoCo / Codex)
34
+ |-- adapters/backend/pty-backend: pseudo-terminal management (node-pty)
35
+ |-- utils/idle-detector: idle detection (quiescence + spinner + completion marker)
36
+ |-- HTTP + WebSocket server: serves xterm.js web terminal
37
+ |-- Headless xterm: captures screen for streaming cards
38
+ |-- IPC: communicates with daemon
39
+ |
40
+ AI Coding CLI (interactive TTY mode)
41
+ |-- MCP Server (stdio): send_to_thread, get_thread_messages, react_to_message
42
+ |
43
+ Lark API
44
+ |-- Replies, reactions, card updates, DMs
45
+ ```
46
+
47
+ ## Prerequisites
48
+
49
+ - **Node.js** >= 20
50
+ - **AI coding CLI** installed and authenticated (`claude`, `aiden`, `coco`, or `codex` in PATH)
51
+ - **Lark app** with Bot and Message permissions (WebSocket event subscription)
52
+
53
+ ## Installation
54
+
55
+ ```bash
56
+ npm install -g botmux
57
+ ```
58
+
59
+ ## Quick Start
60
+
61
+ ```bash
62
+ # 1. Interactive setup — creates ~/.botmux/.env
63
+ botmux setup
64
+
65
+ # 2. Start the daemon
66
+ botmux start
67
+ ```
68
+
69
+ The `setup` command will guide you through:
70
+ - Creating a Lark app (with required permissions listed)
71
+ - Entering App ID, App Secret, Chat ID
72
+ - Optional: Claude model, working directory, access control
73
+
74
+ ## CLI Commands
75
+
76
+ | Command | Description |
77
+ |---------|-------------|
78
+ | `botmux setup` | Interactive first-time configuration |
79
+ | `botmux start` | Start daemon (PM2 managed) |
80
+ | `botmux stop` | Stop daemon |
81
+ | `botmux restart` | Restart daemon (auto-restores active sessions) |
82
+ | `botmux logs` | View daemon logs (`--lines N` for more) |
83
+ | `botmux status` | Show daemon status |
84
+ | `botmux upgrade` | Upgrade to latest version |
85
+
86
+ ## Configuration
87
+
88
+ Configuration is stored at `~/.botmux/.env`. Run `botmux setup` to create it interactively, or edit manually:
89
+
90
+ ### Required
91
+
92
+ | Variable | Description |
93
+ |----------|-------------|
94
+ | `LARK_APP_ID` | Lark app ID |
95
+ | `LARK_APP_SECRET` | Lark app secret |
96
+
97
+ ### Optional
98
+
99
+ | Variable | Default | Description |
100
+ |----------|---------|-------------|
101
+ | `CLI_ID` | `claude-code` | CLI adapter (`claude-code`, `aiden`, `coco`, `codex`) |
102
+ | `CLI_PATH` | _(auto-detect by CLI_ID)_ | CLI binary path override |
103
+ | `BACKEND_TYPE` | `pty` | Session backend (`pty`, `tmux`) |
104
+ | `WORKING_DIR` | `~` | Default working directory |
105
+ | `ALLOWED_USERS` | _(empty = allow all)_ | Comma-separated email prefixes or Lark open_ids |
106
+ | `PROJECT_SCAN_DIR` | _(parent of CWD)_ | Directory to scan for git repos |
107
+ | `WEB_HOST` | `0.0.0.0` | HTTP server bind address |
108
+ | `WEB_EXTERNAL_HOST` | _(auto-detect LAN IP)_ | External hostname/IP for terminal URLs |
109
+ | `SESSION_DATA_DIR` | `~/.botmux/data` | Where sessions and queues are stored |
110
+ | `DEBUG` | _(unset)_ | Set to `1` for debug logging |
111
+
112
+ ## File Locations
113
+
114
+ | Path | Description |
115
+ |------|-------------|
116
+ | `~/.botmux/.env` | Configuration |
117
+ | `~/.botmux/data/` | Session data, message queues |
118
+ | `~/.botmux/logs/` | Daemon logs |
119
+
120
+ ## Usage
121
+
122
+ ### Workflow
123
+
124
+ 1. Send a message in your Lark topic group to create a new thread
125
+ 2. The bot shows a repo selection card — pick a project or click "Start directly"
126
+ 3. Claude Code spawns in the selected directory
127
+ 4. A live streaming card appears in the thread, showing real-time terminal output with markdown rendering
128
+ 5. Each reply creates a new streaming card for that turn; previous cards freeze at their last state
129
+ 6. Click "🔑 Get Write Link" on the card to receive a write-enabled terminal URL via DM
130
+ 7. Claude replies in the thread via MCP tools
131
+
132
+ ### Slash Commands
133
+
134
+ | Command | Description |
135
+ |---------|-------------|
136
+ | `/repo` | Show project selector card |
137
+ | `/repo <N>` | Switch to Nth project from last scan |
138
+ | `/cd <path>` | Change working directory |
139
+ | `/status` | Show session info (uptime, terminal URL, etc.) |
140
+ | `/cost` | Show token usage and estimated cost |
141
+ | `/restart` | Restart Claude process |
142
+ | `/close` | Close session and terminate Claude |
143
+ | `/clear` | Clear context (new session, same thread) |
144
+ | `/schedule` | Manage scheduled tasks |
145
+ | `/help` | Show available commands |
146
+
147
+ ### Scheduled Tasks
148
+
149
+ Create recurring tasks with natural language:
150
+
151
+ ```
152
+ /schedule every day at 17:50 check AI news
153
+ /schedule weekdays at 9:00 run health check
154
+ /schedule every Monday at 10:00 generate weekly report
155
+ ```
156
+
157
+ Manage tasks:
158
+
159
+ ```
160
+ /schedule list
161
+ /schedule remove <id>
162
+ /schedule enable <id>
163
+ /schedule disable <id>
164
+ /schedule run <id>
165
+ ```
166
+
167
+ ### Streaming Cards
168
+
169
+ Each conversation turn gets a live-updating Feishu card that shows:
170
+
171
+ - Real-time terminal output (rendered via headless xterm + Feishu Card v2 markdown)
172
+ - Status indicator: 🟡 Starting → 🔵 Working → 🟢 Idle
173
+ - Action buttons: Open Terminal, Get Write Link, Restart Claude, Close Session
174
+
175
+ The card content is captured from a headless xterm terminal that filters out TUI chrome (logo, status bar, prompts, box-drawing characters) and shows only Claude's actual work output.
176
+
177
+ ### Web Terminal
178
+
179
+ Each session exposes a web terminal at `http://<WEB_EXTERNAL_HOST>:<port>`.
180
+
181
+ - **Read-only link** — shown on the streaming card in the group thread
182
+ - **Write-enabled link** — sent via DM on demand (click "🔑 Get Write Link" on the card)
183
+
184
+ Features: xterm.js with fit/unicode11/web-links addons, TokyoNight theme, scrollback buffer, mobile-friendly viewport.
185
+
186
+ ## MCP Tools
187
+
188
+ Claude Code has access to three MCP tools for interacting with Lark:
189
+
190
+ | Tool | Description |
191
+ |------|-------------|
192
+ | `send_to_thread` | Send a message (text or rich post) to the Lark thread |
193
+ | `get_thread_messages` | Retrieve message history from the thread |
194
+ | `react_to_message` | Add or remove emoji reactions on messages |
195
+
196
+ ## Development
197
+
198
+ ```bash
199
+ git clone <repo-url>
200
+ cd botmux
201
+ pnpm install
202
+ pnpm build
203
+
204
+ # Run directly (no PM2)
205
+ pnpm daemon
206
+
207
+ # Or with PM2
208
+ pnpm daemon:start
209
+ pnpm daemon:logs
210
+ ```
211
+
212
+ ## Project Structure
213
+
214
+ ```
215
+ src/
216
+ cli.ts # CLI entry point (setup/start/stop/restart/logs)
217
+ daemon.ts # Daemon orchestrator (~400 lines, wires modules together)
218
+ worker.ts # Worker process: uses adapters to manage CLI + PTY
219
+ config.ts # Configuration from environment variables
220
+ server.ts # MCP server setup
221
+ types.ts # IPC message types
222
+ adapters/
223
+ cli/
224
+ types.ts # CliAdapter interface, CliId type
225
+ registry.ts # Adapter factory + resolveCommand
226
+ claude-code.ts # Claude Code adapter
227
+ aiden.ts # Aiden adapter
228
+ coco.ts # CoCo adapter
229
+ codex.ts # Codex adapter
230
+ backend/
231
+ types.ts # SessionBackend interface
232
+ pty-backend.ts # node-pty backend
233
+ tmux-backend.ts # tmux backend (stub)
234
+ core/
235
+ types.ts # DaemonSession core type
236
+ worker-pool.ts # Worker process pool management
237
+ command-handler.ts # Slash command processing
238
+ session-manager.ts # Session lifecycle + path resolution
239
+ cost-calculator.ts # Token usage & cost estimation
240
+ scheduler.ts # Cron scheduling with natural language parsing
241
+ im/
242
+ types.ts # ImAdapter interface definitions (multi-IM abstraction)
243
+ lark/
244
+ client.ts # Lark API wrapper
245
+ event-dispatcher.ts # Lark WebSocket event routing
246
+ card-handler.ts # Lark card interaction handling
247
+ card-builder.ts # Lark interactive card builders
248
+ message-parser.ts # Lark event message parsing
249
+ tools/
250
+ index.ts # MCP tool registry
251
+ send-to-thread.ts # MCP tool: send message
252
+ get-thread-messages.ts # MCP tool: read messages
253
+ react-to-message.ts # MCP tool: emoji reactions
254
+ services/
255
+ session-store.ts # Session persistence (JSON)
256
+ schedule-store.ts # Scheduled task persistence
257
+ message-queue.ts # Per-thread JSONL message queue
258
+ project-scanner.ts # Git repo/worktree discovery
259
+ utils/
260
+ idle-detector.ts # CLI idle detection (quiescence + spinner + completion marker)
261
+ terminal-renderer.ts # Headless xterm renderer for screen capture & TUI filtering
262
+ logger.ts # Logging utility
263
+ ```
264
+
265
+ ## License
266
+
267
+ [MIT](LICENSE)
package/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # botmux
2
+
3
+ 中文 | [English](README.en.md)
4
+
5
+ 飞书话题群与 AI 编程 CLI 的桥接工具。Daemon 监听飞书消息,为每个新话题自动启动一个独立的 CLI 进程(支持 Claude Code、Aiden、CoCo、Codex),提供实时流式卡片和 Web 终端。
6
+
7
+ ## 功能特性
8
+
9
+ - **一个话题 = 一个 AI 编程会话** — 每个飞书话题线程对应一个独立的 CLI 进程
10
+ - **多 CLI 支持** — 通过适配器架构支持 Claude Code、Aiden、CoCo、Codex,可扩展
11
+ - **实时流式卡片** — 终端输出实时渲染到飞书卡片中,支持 Markdown 格式,每轮对话独立卡片
12
+ - **Web 终端 (xterm.js)** — 浏览器查看完整 PTY 输出,按需获取可操作链接
13
+ - **会话持久化** — 会话在 Daemon 重启后自动恢复
14
+ - **定时任务** — 基于 Cron 的周期性任务,支持中文自然语言配置
15
+ - **项目管理** — 交互式仓库选择器,每个会话独立工作目录
16
+ - **MCP 集成** — CLI 可通过 MCP 工具回复飞书话题、读取消息历史、添加表情回应
17
+ - **权限控制** — 用户白名单、终端 Token 写入权限、卡片按钮操作限制
18
+
19
+ ## 架构
20
+
21
+ ```
22
+ 飞书 WebSocket 事件
23
+ |
24
+ Daemon (daemon.ts → core/ 模块)
25
+ |-- im/lark/event-dispatcher: 飞书事件路由
26
+ |-- im/lark/card-handler: 卡片交互处理
27
+ |-- core/worker-pool: Worker 进程池管理
28
+ |-- core/command-handler: 斜杠命令处理
29
+ |-- core/session-manager: 会话生命周期
30
+ |-- core/scheduler: 定时任务调度
31
+ |
32
+ Worker (worker.ts) -- 每个会话 fork 一个子进程
33
+ |-- adapters/cli/*: CLI 适配器 (Claude Code / Aiden / CoCo / Codex)
34
+ |-- adapters/backend/pty-backend: 伪终端管理 (node-pty)
35
+ |-- utils/idle-detector: 空闲检测(静默 + Spinner + 完成标记)
36
+ |-- HTTP + WebSocket: 提供 xterm.js Web 终端
37
+ |-- Headless xterm: 捕获屏幕内容用于流式卡片
38
+ |-- IPC: 与 Daemon 通信
39
+ |
40
+ AI 编程 CLI (交互式 TTY 模式)
41
+ |-- MCP Server (stdio): send_to_thread, get_thread_messages, react_to_message
42
+ |
43
+ 飞书 API
44
+ |-- 回复消息、表情回应、卡片更新、私聊
45
+ ```
46
+
47
+ ## 前置要求
48
+
49
+ - **Node.js** >= 20
50
+ - **AI 编程 CLI** 已安装并完成认证(`claude`、`aiden`、`coco` 或 `codex` 在 PATH 中)
51
+ - **飞书应用** 具备机器人和消息权限(WebSocket 事件订阅)
52
+
53
+ ## 安装
54
+
55
+ ```bash
56
+ npm install -g botmux
57
+ ```
58
+
59
+ ## 快速开始
60
+
61
+ ```bash
62
+ # 1. 交互式配置 — 创建 ~/.botmux/.env
63
+ botmux setup
64
+
65
+ # 2. 启动 daemon
66
+ botmux start
67
+ ```
68
+
69
+ `setup` 命令会引导你完成:
70
+ - 创建飞书应用(列出所需权限)
71
+ - 输入 App ID、App Secret、Chat ID
72
+ - 可选:Claude 模型、工作目录、权限控制
73
+
74
+ ## CLI 命令
75
+
76
+ | 命令 | 说明 |
77
+ |------|------|
78
+ | `botmux setup` | 交互式首次配置 |
79
+ | `botmux start` | 启动 daemon(PM2 管理) |
80
+ | `botmux stop` | 停止 daemon |
81
+ | `botmux restart` | 重启 daemon(自动恢复活跃会话) |
82
+ | `botmux logs` | 查看日志(`--lines N`) |
83
+ | `botmux status` | 查看 daemon 状态 |
84
+ | `botmux upgrade` | 升级到最新版本 |
85
+
86
+ ## 配置
87
+
88
+ 配置文件位于 `~/.botmux/.env`。运行 `botmux setup` 交互式创建,或手动编辑:
89
+
90
+ ### 必填
91
+
92
+ | 变量 | 说明 |
93
+ |------|------|
94
+ | `LARK_APP_ID` | 飞书应用 App ID |
95
+ | `LARK_APP_SECRET` | 飞书应用 App Secret |
96
+
97
+ ### 可选
98
+
99
+ | 变量 | 默认值 | 说明 |
100
+ |------|--------|------|
101
+ | `CLI_ID` | `claude-code` | CLI 适配器(`claude-code`、`aiden`、`coco`、`codex`) |
102
+ | `CLI_PATH` | _(按 CLI_ID 自动检测)_ | CLI 可执行文件路径覆盖 |
103
+ | `BACKEND_TYPE` | `pty` | 会话后端(`pty`、`tmux`) |
104
+ | `WORKING_DIR` | `~` | 默认工作目录 |
105
+ | `ALLOWED_USERS` | _(空 = 不限制)_ | 允许的用户,邮箱前缀或 open_id,逗号分隔 |
106
+ | `PROJECT_SCAN_DIR` | _(工作目录的上级)_ | 扫描 Git 仓库的目录 |
107
+ | `WEB_HOST` | `0.0.0.0` | HTTP 服务绑定地址 |
108
+ | `WEB_EXTERNAL_HOST` | _(自动检测局域网 IP)_ | 终端链接中的外部主机名/IP |
109
+ | `SESSION_DATA_DIR` | `~/.botmux/data` | 会话和队列的存储目录 |
110
+ | `DEBUG` | _(未设置)_ | 设为 `1` 启用调试日志 |
111
+
112
+ ## 文件位置
113
+
114
+ | 路径 | 说明 |
115
+ |------|------|
116
+ | `~/.botmux/.env` | 配置文件 |
117
+ | `~/.botmux/data/` | 会话数据、消息队列 |
118
+ | `~/.botmux/logs/` | Daemon 日志 |
119
+
120
+ ## 使用
121
+
122
+ ### 使用流程
123
+
124
+ 1. 在飞书话题群中发送消息创建新话题
125
+ 2. 机器人弹出仓库选择卡片 — 选择项目或点击「直接开启会话」
126
+ 3. Claude Code 在所选目录下启动
127
+ 4. 话题中出现实时流式卡片,展示终端输出并支持 Markdown 渲染
128
+ 5. 每次回复创建新的流式卡片,上一轮卡片冻结在最后状态
129
+ 6. 点击卡片上的「🔑 获取操作链接」通过私聊获取可写终端链接
130
+ 7. Claude 通过 MCP 工具在话题中回复
131
+
132
+ ### 斜杠命令
133
+
134
+ | 命令 | 说明 |
135
+ |------|------|
136
+ | `/repo` | 显示项目选择卡片 |
137
+ | `/repo <N>` | 切换到上次扫描的第 N 个项目 |
138
+ | `/cd <路径>` | 切换工作目录 |
139
+ | `/status` | 查看会话信息(运行时间、终端地址等) |
140
+ | `/cost` | 查看 Token 用量和费用估算 |
141
+ | `/restart` | 重启 Claude 进程 |
142
+ | `/close` | 关闭会话并终止 Claude |
143
+ | `/clear` | 清除上下文(新会话,同一话题) |
144
+ | `/schedule` | 管理定时任务 |
145
+ | `/help` | 显示可用命令 |
146
+
147
+ ### 定时任务
148
+
149
+ 用自然语言创建周期性任务:
150
+
151
+ ```
152
+ /schedule 每日17:50 帮我看看AI圈有什么新闻
153
+ /schedule 工作日每天9:00 检查服务状态
154
+ /schedule 每周一10:00 生成周报
155
+ ```
156
+
157
+ 管理任务:
158
+
159
+ ```
160
+ /schedule list
161
+ /schedule remove <id>
162
+ /schedule enable <id>
163
+ /schedule disable <id>
164
+ /schedule run <id>
165
+ ```
166
+
167
+ ### 流式卡片
168
+
169
+ 每轮对话会生成一个实时更新的飞书卡片,展示:
170
+
171
+ - 实时终端输出(通过 headless xterm 捕获 + 飞书卡片 v2 Markdown 渲染)
172
+ - 状态指示:🟡 启动中 → 🔵 工作中 → 🟢 就绪
173
+ - 操作按钮:打开终端、获取操作链接、重启 Claude、关闭会话
174
+
175
+ 卡片内容由 headless xterm 终端捕获,自动过滤 TUI 装饰(Logo、状态栏、提示符、框线字符),仅展示 Claude 的实际工作输出。
176
+
177
+ ### Web 终端
178
+
179
+ 每个会话提供一个 Web 终端,地址为 `http://<WEB_EXTERNAL_HOST>:<端口>`。
180
+
181
+ - **只读链接** — 展示在群话题的流式卡片上
182
+ - **可操作链接** — 按需获取(点击卡片上的「🔑 获取操作链接」通过私聊发送)
183
+
184
+ 特性:xterm.js + fit/unicode11/web-links 插件、TokyoNight 主题、滚动缓冲区、移动端适配。
185
+
186
+ ## MCP 工具
187
+
188
+ Claude Code 可使用三个 MCP 工具与飞书交互:
189
+
190
+ | 工具 | 说明 |
191
+ |------|------|
192
+ | `send_to_thread` | 向飞书话题发送消息(纯文本或富文本) |
193
+ | `get_thread_messages` | 获取话题的消息历史 |
194
+ | `react_to_message` | 添加或移除消息的表情回应 |
195
+
196
+ ## 开发
197
+
198
+ ```bash
199
+ git clone <repo-url>
200
+ cd botmux
201
+ pnpm install
202
+ pnpm build
203
+
204
+ # 直接运行(不经 PM2)
205
+ pnpm daemon
206
+
207
+ # 或使用 PM2
208
+ pnpm daemon:start
209
+ pnpm daemon:logs
210
+ ```
211
+
212
+ ## 项目结构
213
+
214
+ ```
215
+ src/
216
+ cli.ts # CLI 入口(setup/start/stop/restart/logs)
217
+ daemon.ts # Daemon 编排入口(~400 行,调用各模块)
218
+ worker.ts # Worker 进程:使用适配器管理 CLI + PTY
219
+ config.ts # 环境变量配置
220
+ server.ts # MCP Server
221
+ types.ts # IPC 消息类型
222
+ adapters/
223
+ cli/
224
+ types.ts # CliAdapter 接口、CliId 类型
225
+ registry.ts # 适配器工厂 + resolveCommand
226
+ claude-code.ts # Claude Code 适配器
227
+ aiden.ts # Aiden 适配器
228
+ coco.ts # CoCo 适配器
229
+ codex.ts # Codex 适配器
230
+ backend/
231
+ types.ts # SessionBackend 接口
232
+ pty-backend.ts # node-pty 后端
233
+ tmux-backend.ts # tmux 后端(stub)
234
+ core/
235
+ types.ts # DaemonSession 核心类型
236
+ worker-pool.ts # Worker 进程池管理
237
+ command-handler.ts # 斜杠命令处理
238
+ session-manager.ts # 会话生命周期 + 路径解析
239
+ cost-calculator.ts # Token 用量 & 费用估算
240
+ scheduler.ts # 定时任务调度(自然语言解析)
241
+ im/
242
+ types.ts # ImAdapter 接口定义(多 IM 抽象)
243
+ lark/
244
+ client.ts # 飞书 API 封装
245
+ event-dispatcher.ts # 飞书 WebSocket 事件路由
246
+ card-handler.ts # 飞书卡片交互处理
247
+ card-builder.ts # 飞书交互卡片构建
248
+ message-parser.ts # 飞书事件消息解析
249
+ tools/
250
+ index.ts # MCP 工具注册表
251
+ send-to-thread.ts # MCP 工具:发送消息
252
+ get-thread-messages.ts # MCP 工具:读取消息
253
+ react-to-message.ts # MCP 工具:表情回应
254
+ services/
255
+ session-store.ts # 会话持久化 (JSON)
256
+ schedule-store.ts # 定时任务持久化
257
+ message-queue.ts # 话题消息队列 (JSONL)
258
+ project-scanner.ts # Git 仓库/Worktree 扫描
259
+ utils/
260
+ idle-detector.ts # CLI 空闲检测(静默 + Spinner + 完成标记)
261
+ terminal-renderer.ts # Headless xterm 渲染器(屏幕捕获 & TUI 过滤)
262
+ logger.ts # 日志工具
263
+ ```
264
+
265
+ ## 许可证
266
+
267
+ [MIT](LICENSE)
@@ -0,0 +1,13 @@
1
+ import type { SessionBackend, SpawnOpts } from './types.js';
2
+ export declare class PtyBackend implements SessionBackend {
3
+ private process;
4
+ spawn(bin: string, args: string[], opts: SpawnOpts): void;
5
+ write(data: string): void;
6
+ resize(cols: number, rows: number): void;
7
+ /** Must be called AFTER spawn(). Callbacks registered before spawn are silently lost. */
8
+ onData(cb: (data: string) => void): void;
9
+ /** Must be called AFTER spawn(). Callbacks registered before spawn are silently lost. */
10
+ onExit(cb: (code: number | null, signal: string | null) => void): void;
11
+ kill(): void;
12
+ }
13
+ //# sourceMappingURL=pty-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/pty-backend.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5D,qBAAa,UAAW,YAAW,cAAc;IAC/C,OAAO,CAAC,OAAO,CAAyB;IAExC,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAUzD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI;IAIxC,yFAAyF;IACzF,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIxC,yFAAyF;IACzF,MAAM,CAAC,EAAE,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;IAMtE,IAAI,IAAI,IAAI;CAMb"}
@@ -0,0 +1,39 @@
1
+ import * as pty from 'node-pty';
2
+ export class PtyBackend {
3
+ process = null;
4
+ spawn(bin, args, opts) {
5
+ this.process = pty.spawn(bin, args, {
6
+ name: 'xterm-256color',
7
+ cols: opts.cols,
8
+ rows: opts.rows,
9
+ cwd: opts.cwd,
10
+ env: opts.env,
11
+ });
12
+ }
13
+ write(data) {
14
+ this.process?.write(data);
15
+ }
16
+ resize(cols, rows) {
17
+ this.process?.resize(cols, rows);
18
+ }
19
+ /** Must be called AFTER spawn(). Callbacks registered before spawn are silently lost. */
20
+ onData(cb) {
21
+ this.process?.onData(cb);
22
+ }
23
+ /** Must be called AFTER spawn(). Callbacks registered before spawn are silently lost. */
24
+ onExit(cb) {
25
+ this.process?.onExit(({ exitCode, signal }) => {
26
+ cb(exitCode, signal !== undefined ? String(signal) : null);
27
+ });
28
+ }
29
+ kill() {
30
+ if (this.process) {
31
+ try {
32
+ this.process.kill();
33
+ }
34
+ catch { /* already dead */ }
35
+ this.process = null;
36
+ }
37
+ }
38
+ }
39
+ //# sourceMappingURL=pty-backend.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pty-backend.js","sourceRoot":"","sources":["../../../src/adapters/backend/pty-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAGhC,MAAM,OAAO,UAAU;IACb,OAAO,GAAoB,IAAI,CAAC;IAExC,KAAK,CAAC,GAAW,EAAE,IAAc,EAAE,IAAe;QAChD,IAAI,CAAC,OAAO,GAAG,GAAG,CAAC,KAAK,CAAC,GAAG,EAAE,IAAI,EAAE;YAClC,IAAI,EAAE,gBAAgB;YACtB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,GAAG,EAAE,IAAI,CAAC,GAAG;YACb,GAAG,EAAE,IAAI,CAAC,GAAG;SACd,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAY;QAChB,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;IAC5B,CAAC;IAED,MAAM,CAAC,IAAY,EAAE,IAAY;QAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC;IACnC,CAAC;IAED,yFAAyF;IACzF,MAAM,CAAC,EAA0B;QAC/B,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,EAAE,CAAC,CAAC;IAC3B,CAAC;IAED,yFAAyF;IACzF,MAAM,CAAC,EAAwD;QAC7D,IAAI,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE;YAC5C,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAC7D,CAAC,CAAC,CAAC;IACL,CAAC;IAED,IAAI;QACF,IAAI,IAAI,CAAC,OAAO,EAAE,CAAC;YACjB,IAAI,CAAC;gBAAC,IAAI,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;YAAC,CAAC;YAAC,MAAM,CAAC,CAAC,kBAAkB,CAAC,CAAC;YACzD,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QACtB,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,20 @@
1
+ import type { SessionBackend, SpawnOpts } from './types.js';
2
+ /**
3
+ * TmuxBackend — experimental session backend using tmux.
4
+ * Enables: physical `tmux attach`, web terminal, and IM all on same session.
5
+ * TODO: Full implementation.
6
+ */
7
+ export declare class TmuxBackend implements SessionBackend {
8
+ private sessionName;
9
+ spawn(bin: string, args: string[], opts: SpawnOpts): void;
10
+ write(data: string): void;
11
+ resize(_cols: number, _rows: number): void;
12
+ onData(_cb: (data: string) => void): void;
13
+ onExit(_cb: (code: number | null, signal: string | null) => void): void;
14
+ kill(): void;
15
+ getAttachInfo(): {
16
+ type: "tmux";
17
+ sessionName: string;
18
+ };
19
+ }
20
+ //# sourceMappingURL=tmux-backend.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tmux-backend.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/tmux-backend.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5D;;;;GAIG;AACH,qBAAa,WAAY,YAAW,cAAc;IAChD,OAAO,CAAC,WAAW,CAAM;IAEzB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI;IAIzD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI;IAIzB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAI1C,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,GAAG,IAAI;IAIzC,MAAM,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,GAAG,IAAI;IAIvE,IAAI,IAAI,IAAI;IAIZ,aAAa;;;;CAGd"}