botmux 2.106.0 → 2.106.1-canary.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 (243) hide show
  1. package/README.en.md +10 -10
  2. package/README.md +10 -10
  3. package/dist/adapters/backend/sandbox.d.ts +20 -6
  4. package/dist/adapters/backend/sandbox.d.ts.map +1 -1
  5. package/dist/adapters/backend/sandbox.js +150 -26
  6. package/dist/adapters/backend/sandbox.js.map +1 -1
  7. package/dist/adapters/backend/tmux-pipe-backend.d.ts +1 -1
  8. package/dist/adapters/backend/tmux-pipe-backend.js +1 -1
  9. package/dist/adapters/backend/zellij-observe-backend.d.ts +1 -1
  10. package/dist/adapters/backend/zellij-observe-backend.js +1 -1
  11. package/dist/adapters/cli/read-isolation.d.ts +1 -2
  12. package/dist/adapters/cli/read-isolation.d.ts.map +1 -1
  13. package/dist/adapters/cli/read-isolation.js +4 -51
  14. package/dist/adapters/cli/read-isolation.js.map +1 -1
  15. package/dist/bot-registry.d.ts +1 -65
  16. package/dist/bot-registry.d.ts.map +1 -1
  17. package/dist/bot-registry.js +0 -145
  18. package/dist/bot-registry.js.map +1 -1
  19. package/dist/cli.d.ts.map +1 -1
  20. package/dist/cli.js +4 -30
  21. package/dist/cli.js.map +1 -1
  22. package/dist/config.d.ts +0 -8
  23. package/dist/config.d.ts.map +1 -1
  24. package/dist/config.js +1 -13
  25. package/dist/config.js.map +1 -1
  26. package/dist/core/command-handler.d.ts.map +1 -1
  27. package/dist/core/command-handler.js +1 -2
  28. package/dist/core/command-handler.js.map +1 -1
  29. package/dist/core/dashboard-ipc-server.d.ts.map +1 -1
  30. package/dist/core/dashboard-ipc-server.js +2 -47
  31. package/dist/core/dashboard-ipc-server.js.map +1 -1
  32. package/dist/core/idle-worker-sweeper.d.ts +1 -1
  33. package/dist/core/idle-worker-sweeper.js +1 -1
  34. package/dist/core/passthrough-commands.d.ts.map +1 -1
  35. package/dist/core/passthrough-commands.js +1 -2
  36. package/dist/core/passthrough-commands.js.map +1 -1
  37. package/dist/core/session-manager.d.ts +1 -1
  38. package/dist/core/session-manager.d.ts.map +1 -1
  39. package/dist/core/session-manager.js +6 -51
  40. package/dist/core/session-manager.js.map +1 -1
  41. package/dist/core/trigger-session.d.ts.map +1 -1
  42. package/dist/core/trigger-session.js +2 -7
  43. package/dist/core/trigger-session.js.map +1 -1
  44. package/dist/core/types.d.ts +3 -3
  45. package/dist/core/types.d.ts.map +1 -1
  46. package/dist/core/worker-pool.d.ts +0 -4
  47. package/dist/core/worker-pool.d.ts.map +1 -1
  48. package/dist/core/worker-pool.js +9 -27
  49. package/dist/core/worker-pool.js.map +1 -1
  50. package/dist/core/zellij-adopt-discovery.js +1 -1
  51. package/dist/core/zellij-adopt-discovery.js.map +1 -1
  52. package/dist/daemon.d.ts +0 -144
  53. package/dist/daemon.d.ts.map +1 -1
  54. package/dist/daemon.js +65 -3386
  55. package/dist/daemon.js.map +1 -1
  56. package/dist/dashboard/federated-group-core.js +1 -1
  57. package/dist/dashboard/federated-group-core.js.map +1 -1
  58. package/dist/dashboard/federation-api.js +1 -1
  59. package/dist/dashboard/settings-write-applier.d.ts +1 -31
  60. package/dist/dashboard/settings-write-applier.d.ts.map +1 -1
  61. package/dist/dashboard/settings-write-applier.js +0 -47
  62. package/dist/dashboard/settings-write-applier.js.map +1 -1
  63. package/dist/dashboard/web/i18n.d.ts.map +1 -1
  64. package/dist/dashboard/web/i18n.js +0 -22
  65. package/dist/dashboard/web/i18n.js.map +1 -1
  66. package/dist/dashboard/web/insights.js +19 -19
  67. package/dist/dashboard/web/insights.js.map +1 -1
  68. package/dist/dashboard/web/sessions.js +2 -2
  69. package/dist/dashboard/web/settings-page.d.ts.map +1 -1
  70. package/dist/dashboard/web/settings-page.js +46 -101
  71. package/dist/dashboard/web/settings-page.js.map +1 -1
  72. package/dist/dashboard-web/app.js +3 -3
  73. package/dist/dashboard-web/chunks/{bot-defaults-page-3OIY7N76.js → bot-defaults-page-4IF5NB4O.js} +1 -1
  74. package/dist/dashboard-web/chunks/{chunk-DKKOGZCG.js → chunk-2KX53XHS.js} +1 -1
  75. package/dist/dashboard-web/chunks/{chunk-BNEXMNQD.js → chunk-NHI3SY2K.js} +3 -3
  76. package/dist/dashboard-web/chunks/{chunk-VFCACTRF.js → chunk-RRBSPQVK.js} +1 -1
  77. package/dist/dashboard-web/chunks/{connectors-VDOAKUW3.js → connectors-WZR7MAXE.js} +1 -1
  78. package/dist/dashboard-web/chunks/{groups-page-CX5AN4XG.js → groups-page-NK6U5JIU.js} +1 -1
  79. package/dist/dashboard-web/chunks/{insights-page-DOMRL4S6.js → insights-page-2TN3ZR33.js} +1 -1
  80. package/dist/dashboard-web/chunks/{overview-page-AZBAXVZ7.js → overview-page-TADULZBE.js} +1 -1
  81. package/dist/dashboard-web/chunks/{roles-page-3FEAH6S4.js → roles-page-J6CRSIJ6.js} +1 -1
  82. package/dist/dashboard-web/chunks/{schedules-CTQEA7IE.js → schedules-6RONVOOV.js} +1 -1
  83. package/dist/dashboard-web/chunks/{sessions-LVZCXO2Z.js → sessions-I4EZWL5Q.js} +1 -1
  84. package/dist/dashboard-web/chunks/{sessions-page-NHBJDSBC.js → sessions-page-YTQC2EM6.js} +1 -1
  85. package/dist/dashboard-web/chunks/settings-page-TRYWNJ5Y.js +2 -0
  86. package/dist/dashboard-web/chunks/{skills-page-VUG3EPWX.js → skills-page-ZZLGLEEL.js} +1 -1
  87. package/dist/dashboard-web/chunks/{team-federation-page-ZKL3YMGN.js → team-federation-page-QYAP7XJF.js} +1 -1
  88. package/dist/dashboard-web/chunks/{workflows-4BYQQBFP.js → workflows-2L4DKNCK.js} +1 -1
  89. package/dist/dashboard-web/index.html +1 -1
  90. package/dist/dashboard-web/style.css +16 -122
  91. package/dist/dashboard.js +3 -367
  92. package/dist/dashboard.js.map +1 -1
  93. package/dist/global-config.d.ts +0 -23
  94. package/dist/global-config.d.ts.map +1 -1
  95. package/dist/global-config.js +1 -58
  96. package/dist/global-config.js.map +1 -1
  97. package/dist/i18n/en.d.ts.map +1 -1
  98. package/dist/i18n/en.js +6 -9
  99. package/dist/i18n/en.js.map +1 -1
  100. package/dist/i18n/zh.d.ts.map +1 -1
  101. package/dist/i18n/zh.js +6 -9
  102. package/dist/i18n/zh.js.map +1 -1
  103. package/dist/im/lark/card-builder.d.ts +1 -1
  104. package/dist/im/lark/card-builder.js +1 -1
  105. package/dist/im/lark/card-handler.d.ts +0 -3
  106. package/dist/im/lark/card-handler.d.ts.map +1 -1
  107. package/dist/im/lark/card-handler.js +0 -8
  108. package/dist/im/lark/card-handler.js.map +1 -1
  109. package/dist/im/lark/event-dispatcher.d.ts +0 -10
  110. package/dist/im/lark/event-dispatcher.d.ts.map +1 -1
  111. package/dist/im/lark/event-dispatcher.js +3 -82
  112. package/dist/im/lark/event-dispatcher.js.map +1 -1
  113. package/dist/im/lark/grant-command.d.ts +1 -1
  114. package/dist/im/lark/grant-command.js +1 -1
  115. package/dist/im/lark/message-parser.d.ts +0 -6
  116. package/dist/im/lark/message-parser.d.ts.map +1 -1
  117. package/dist/im/lark/message-parser.js +0 -14
  118. package/dist/im/lark/message-parser.js.map +1 -1
  119. package/dist/mir-runner.js +149 -1
  120. package/dist/mir-runner.js.map +1 -1
  121. package/dist/services/deployment-identity.d.ts +1 -1
  122. package/dist/services/deployment-identity.d.ts.map +1 -1
  123. package/dist/services/deployment-identity.js +1 -1
  124. package/dist/services/deployment-identity.js.map +1 -1
  125. package/dist/services/doc-subs-store.js +1 -1
  126. package/dist/services/team-board-store.js +1 -1
  127. package/dist/services/team-board-store.js.map +1 -1
  128. package/dist/services/trigger-types.d.ts +1 -1
  129. package/dist/services/trigger-types.d.ts.map +1 -1
  130. package/dist/services/voice/index.d.ts +1 -6
  131. package/dist/services/voice/index.d.ts.map +1 -1
  132. package/dist/services/voice/index.js +5 -12
  133. package/dist/services/voice/index.js.map +1 -1
  134. package/dist/setup/lark-scopes.json +0 -5
  135. package/dist/setup/open-platform-automation.d.ts +0 -20
  136. package/dist/setup/open-platform-automation.d.ts.map +1 -1
  137. package/dist/setup/open-platform-automation.js +1 -78
  138. package/dist/setup/open-platform-automation.js.map +1 -1
  139. package/dist/setup/verify-permissions.d.ts +0 -6
  140. package/dist/setup/verify-permissions.d.ts.map +1 -1
  141. package/dist/setup/verify-permissions.js +0 -17
  142. package/dist/setup/verify-permissions.js.map +1 -1
  143. package/dist/types.d.ts +0 -5
  144. package/dist/types.d.ts.map +1 -1
  145. package/dist/utils/daemon-discovery.d.ts +0 -2
  146. package/dist/utils/daemon-discovery.d.ts.map +1 -1
  147. package/dist/utils/daemon-discovery.js +0 -2
  148. package/dist/utils/daemon-discovery.js.map +1 -1
  149. package/dist/worker.js +11 -51
  150. package/dist/worker.js.map +1 -1
  151. package/dist/workflows/definition.d.ts +14 -14
  152. package/dist/workflows/events/payloads.d.ts +8 -8
  153. package/dist/workflows/events/schema.d.ts +452 -452
  154. package/dist/workflows/v3/contract.d.ts +3 -3
  155. package/dist/workflows/v3/contract.js +3 -3
  156. package/dist/workflows/v3/runtime.js +1 -1
  157. package/dist/workflows/v3/runtime.js.map +1 -1
  158. package/package.json +1 -1
  159. package/dist/cli/vc-agent.d.ts +0 -2
  160. package/dist/cli/vc-agent.d.ts.map +0 -1
  161. package/dist/cli/vc-agent.js +0 -331
  162. package/dist/cli/vc-agent.js.map +0 -1
  163. package/dist/dashboard-web/chunks/settings-page-R23I3IZG.js +0 -2
  164. package/dist/mir-prompt.d.ts +0 -16
  165. package/dist/mir-prompt.d.ts.map +0 -1
  166. package/dist/mir-prompt.js +0 -170
  167. package/dist/mir-prompt.js.map +0 -1
  168. package/dist/services/vc-meeting-runtime-store.d.ts +0 -59
  169. package/dist/services/vc-meeting-runtime-store.d.ts.map +0 -1
  170. package/dist/services/vc-meeting-runtime-store.js +0 -339
  171. package/dist/services/vc-meeting-runtime-store.js.map +0 -1
  172. package/dist/vc-agent/cards.d.ts +0 -49
  173. package/dist/vc-agent/cards.d.ts.map +0 -1
  174. package/dist/vc-agent/cards.js +0 -505
  175. package/dist/vc-agent/cards.js.map +0 -1
  176. package/dist/vc-agent/meeting-state.d.ts +0 -39
  177. package/dist/vc-agent/meeting-state.d.ts.map +0 -1
  178. package/dist/vc-agent/meeting-state.js +0 -186
  179. package/dist/vc-agent/meeting-state.js.map +0 -1
  180. package/dist/vc-agent/normalizer.d.ts +0 -13
  181. package/dist/vc-agent/normalizer.d.ts.map +0 -1
  182. package/dist/vc-agent/normalizer.js +0 -470
  183. package/dist/vc-agent/normalizer.js.map +0 -1
  184. package/dist/vc-agent/polling-source.d.ts +0 -64
  185. package/dist/vc-agent/polling-source.d.ts.map +0 -1
  186. package/dist/vc-agent/polling-source.js +0 -181
  187. package/dist/vc-agent/polling-source.js.map +0 -1
  188. package/dist/vc-agent/push-source.d.ts +0 -13
  189. package/dist/vc-agent/push-source.d.ts.map +0 -1
  190. package/dist/vc-agent/push-source.js +0 -109
  191. package/dist/vc-agent/push-source.js.map +0 -1
  192. package/dist/vc-agent/realtime/audio-source.d.ts +0 -6
  193. package/dist/vc-agent/realtime/audio-source.d.ts.map +0 -1
  194. package/dist/vc-agent/realtime/audio-source.js +0 -17
  195. package/dist/vc-agent/realtime/audio-source.js.map +0 -1
  196. package/dist/vc-agent/realtime/endpoint.d.ts +0 -6
  197. package/dist/vc-agent/realtime/endpoint.d.ts.map +0 -1
  198. package/dist/vc-agent/realtime/endpoint.js +0 -25
  199. package/dist/vc-agent/realtime/endpoint.js.map +0 -1
  200. package/dist/vc-agent/realtime/errors.d.ts +0 -9
  201. package/dist/vc-agent/realtime/errors.d.ts.map +0 -1
  202. package/dist/vc-agent/realtime/errors.js +0 -17
  203. package/dist/vc-agent/realtime/errors.js.map +0 -1
  204. package/dist/vc-agent/realtime/events.d.ts +0 -24
  205. package/dist/vc-agent/realtime/events.d.ts.map +0 -1
  206. package/dist/vc-agent/realtime/events.js +0 -190
  207. package/dist/vc-agent/realtime/events.js.map +0 -1
  208. package/dist/vc-agent/realtime/frontier.d.ts +0 -34
  209. package/dist/vc-agent/realtime/frontier.d.ts.map +0 -1
  210. package/dist/vc-agent/realtime/frontier.js +0 -40
  211. package/dist/vc-agent/realtime/frontier.js.map +0 -1
  212. package/dist/vc-agent/realtime/index.d.ts +0 -11
  213. package/dist/vc-agent/realtime/index.d.ts.map +0 -1
  214. package/dist/vc-agent/realtime/index.js +0 -11
  215. package/dist/vc-agent/realtime/index.js.map +0 -1
  216. package/dist/vc-agent/realtime/pacer.d.ts +0 -21
  217. package/dist/vc-agent/realtime/pacer.d.ts.map +0 -1
  218. package/dist/vc-agent/realtime/pacer.js +0 -80
  219. package/dist/vc-agent/realtime/pacer.js.map +0 -1
  220. package/dist/vc-agent/realtime/protobuf.d.ts +0 -33
  221. package/dist/vc-agent/realtime/protobuf.d.ts.map +0 -1
  222. package/dist/vc-agent/realtime/protobuf.js +0 -159
  223. package/dist/vc-agent/realtime/protobuf.js.map +0 -1
  224. package/dist/vc-agent/realtime/protocol.d.ts +0 -4
  225. package/dist/vc-agent/realtime/protocol.d.ts.map +0 -1
  226. package/dist/vc-agent/realtime/protocol.js +0 -59
  227. package/dist/vc-agent/realtime/protocol.js.map +0 -1
  228. package/dist/vc-agent/realtime/session.d.ts +0 -39
  229. package/dist/vc-agent/realtime/session.d.ts.map +0 -1
  230. package/dist/vc-agent/realtime/session.js +0 -221
  231. package/dist/vc-agent/realtime/session.js.map +0 -1
  232. package/dist/vc-agent/realtime/transport.d.ts +0 -23
  233. package/dist/vc-agent/realtime/transport.d.ts.map +0 -1
  234. package/dist/vc-agent/realtime/transport.js +0 -112
  235. package/dist/vc-agent/realtime/transport.js.map +0 -1
  236. package/dist/vc-agent/realtime/types.d.ts +0 -84
  237. package/dist/vc-agent/realtime/types.d.ts.map +0 -1
  238. package/dist/vc-agent/realtime/types.js +0 -5
  239. package/dist/vc-agent/realtime/types.js.map +0 -1
  240. package/dist/vc-agent/types.d.ts +0 -143
  241. package/dist/vc-agent/types.d.ts.map +0 -1
  242. package/dist/vc-agent/types.js +0 -2
  243. package/dist/vc-agent/types.js.map +0 -1
package/README.en.md CHANGED
@@ -15,14 +15,14 @@
15
15
  <a href="#design-philosophy">Design</a> &middot;
16
16
  <a href="#key-advantages">Advantages</a> &middot;
17
17
  <a href="#5-minute-setup">Quick Start</a> &middot;
18
- <a href="https://github.com/deepcoldy/botmux/tree/master/docs-site/docs/en"><b>📖 Docs</b></a>
18
+ <a href="https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/"><b>📖 Docs</b></a>
19
19
  </p>
20
20
 
21
21
  [中文](README.md) | English
22
22
 
23
23
  **Plug any AI coding CLI into Feishu/Lark — every DM, group or topic gets its own CLI session, with live-streaming cards, a web terminal, and zero glue code.**
24
24
 
25
- > 📖 **Full docs** (commands / config / best practices / troubleshooting): **<https://github.com/deepcoldy/botmux/tree/master/docs-site/docs/en>** — this README only covers why and how to get started fast.
25
+ > 📖 **Full docs** (commands / config / best practices / troubleshooting): **<https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/>** — this README only covers why and how to get started fast.
26
26
 
27
27
  | Lark Streaming Cards | Web Terminal | tmux Session Management | Multi-Bot Collaboration |
28
28
  |:-:|:-:|:-:|:-:|
@@ -223,7 +223,7 @@ On mobile/tablet, a floating shortcut toolbar provides Esc, Ctrl+C, Tab, arrow k
223
223
 
224
224
  ### Multi-Bot Collaboration
225
225
 
226
- Run multiple Lark bots on a single machine, each mapped to a different CLI. In the same group chat, messages are routed via @mention — each bot gets its own isolated CLI process. With a single bot in the group, it responds automatically without @. In a regular (non-topic) group, `@<bot1> @<bot2> /t xxx` spawns one independent thread per mentioned bot anchored at the same message. Send `@<bot1> @<bot2> /introduce` once so they register each other's open_id; afterwards each bot can explicitly @-mention the others from within its own session (commands: [📖 Docs · Slash Commands](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/slash-commands.md)).
226
+ Run multiple Lark bots on a single machine, each mapped to a different CLI. In the same group chat, messages are routed via @mention — each bot gets its own isolated CLI process. With a single bot in the group, it responds automatically without @. In a regular (non-topic) group, `@<bot1> @<bot2> /t xxx` spawns one independent thread per mentioned bot anchored at the same message. Send `@<bot1> @<bot2> /introduce` once so they register each other's open_id; afterwards each bot can explicitly @-mention the others from within its own session (commands: [📖 Docs · Slash Commands](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/slash-commands)).
227
227
 
228
228
  ### Multi-Topic Collaboration
229
229
 
@@ -387,16 +387,16 @@ Notes:
387
387
 
388
388
  The full reference — commands, config, best practices, troubleshooting — lives in the docs site; not duplicated here —
389
389
 
390
- ### 👉 https://github.com/deepcoldy/botmux/tree/master/docs-site/docs/en
390
+ ### 👉 https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/
391
391
 
392
392
  | Topic | Docs |
393
393
  |-------|------|
394
- | Slash commands / CLI commands / agent-facing subcommands | [Commands](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/slash-commands.md) |
395
- | `bots.json` fields / env vars / file locations | [Configuration](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/bots-json.md) |
396
- | Multi-CLI adapters (incl. wrapper / gateway integration) | [Adapters](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/adapters.md) |
397
- | Scenario-based best practices (Oncall / alerting-ops / solo dev / team) | [Best Practices](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/best-practices.md) |
398
- | Common pitfalls / FAQ | [Pitfalls](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/pitfalls.md) · [FAQ](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/faq.md) |
399
- | Features: scheduled tasks / Oncall / Dashboard / multi-bot / session relay | [Schedule](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/schedule.md) · [Oncall](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/oncall.md) · [Dashboard](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/dashboard.md) · [Multi-bot](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/multi-bot.mdx) · [Relay](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/en/relay.md) |
394
+ | Slash commands / CLI commands / agent-facing subcommands | [Commands](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/slash-commands) |
395
+ | `bots.json` fields / env vars / file locations | [Configuration](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/bots-json) |
396
+ | Multi-CLI adapters (incl. wrapper / gateway integration) | [Adapters](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/adapters) |
397
+ | Scenario-based best practices (Oncall / alerting-ops / solo dev / team) | [Best Practices](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/best-practices) |
398
+ | Common pitfalls / FAQ | [Pitfalls](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/pitfalls) · [FAQ](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/faq) |
399
+ | Features: scheduled tasks / Oncall / Dashboard / multi-bot / session relay | [Schedule](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/schedule) · [Oncall](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/oncall) · [Dashboard](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/dashboard) · [Multi-bot](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/multi-bot) · [Relay](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/relay) |
400
400
 
401
401
  ## Contributing
402
402
 
package/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
  </p>
13
13
 
14
14
  <p align="center">
15
- <a href="#设计理念">设计理念</a> · <a href="#核心优势">核心优势</a> · <a href="#5-分钟快速接入">快速接入</a> · <a href="https://github.com/deepcoldy/botmux/tree/master/docs-site/docs/zh"><b>📖 文档</b></a>
15
+ <a href="#设计理念">设计理念</a> · <a href="#核心优势">核心优势</a> · <a href="#5-分钟快速接入">快速接入</a> · <a href="https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/"><b>📖 文档</b></a>
16
16
  </p>
17
17
 
18
18
  <p align="center">
@@ -23,7 +23,7 @@
23
23
 
24
24
  **飞书 + AI 编程 CLI——私聊 / 群聊 / 话题,每个会话一个独立 CLI 进程,实时流式回传。** Daemon 监听飞书消息,为每个新会话自动启动独立 CLI 进程(Claude Code / Codex / Cursor / Gemini / OpenCode / Antigravity / GitHub Copilot / Kimi Code),提供实时流式卡片和可交互 Web 终端。
25
25
 
26
- > 📖 **完整文档**(命令 / 配置 / 最佳实践 / 排错):**<https://github.com/deepcoldy/botmux/tree/master/docs-site/docs/zh>** ——本 README 只讲为什么用它和怎么快速上手。
26
+ > 📖 **完整文档**(命令 / 配置 / 最佳实践 / 排错):**<https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/>** ——本 README 只讲为什么用它和怎么快速上手。
27
27
 
28
28
  ## 演示
29
29
 
@@ -92,7 +92,7 @@ botmux 可以管理 CLI 无关的自定义 Skill Registry,并按 bot 配置在
92
92
 
93
93
  ### 多机器人协作
94
94
 
95
- 同一台机器上可运行多个飞书机器人,每个机器人可对应不同的 CLI。同一群聊中通过 @mention 路由消息,仅有一个机器人时无需 @ 自动响应;多机器人时 `@<bot1> @<bot2> /t xxx` 可让每个被 @ 的机器人在同一条消息上各自独立开新话题。先发一次 `@<bot1> @<bot2> /introduce` 让它们互相登记 open_id,之后各 bot 就能在自己的会话里显式 @mention 对方协作(命令详见 [📖 文档 · 斜杠命令](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/slash-commands.md))。
95
+ 同一台机器上可运行多个飞书机器人,每个机器人可对应不同的 CLI。同一群聊中通过 @mention 路由消息,仅有一个机器人时无需 @ 自动响应;多机器人时 `@<bot1> @<bot2> /t xxx` 可让每个被 @ 的机器人在同一条消息上各自独立开新话题。先发一次 `@<bot1> @<bot2> /introduce` 让它们互相登记 open_id,之后各 bot 就能在自己的会话里显式 @mention 对方协作(命令详见 [📖 文档 · 斜杠命令](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/slash-commands))。
96
96
 
97
97
  ### 多话题协作模式
98
98
 
@@ -384,16 +384,16 @@ PersonalAgent 默认配好事件订阅 + bot 能力,正常情况下不用动
384
384
 
385
385
  命令、配置、最佳实践、排错的完整内容都在文档站,这里不再重复 ——
386
386
 
387
- ### 👉 https://github.com/deepcoldy/botmux/tree/master/docs-site/docs/zh
387
+ ### 👉 https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/
388
388
 
389
389
  | 主题 | 文档 |
390
390
  |------|------|
391
- | 斜杠命令 / CLI 命令 / 会话内子命令 | [命令参考](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/slash-commands.md) |
392
- | `bots.json` 字段 / 环境变量 / 文件位置 | [配置参考](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/bots-json.md) |
393
- | 多 CLI 适配器(含 wrapper / 网关接入) | [适配器](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/adapters.md) |
394
- | 按场景的最佳实践(Oncall / 报警运维 / 个人研发 / 多人协作) | [最佳实践](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/best-practices.md) |
395
- | 常见踩坑 / FAQ 排错 | [踩坑](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/pitfalls.md) · [FAQ](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/faq.md) |
396
- | 功能详解:定时任务 / Oncall / Dashboard / 多机器人协作 / 会话接力 | [定时](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/schedule.md) · [Oncall](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/oncall.md) · [Dashboard](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/dashboard.md) · [多 bot](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/multi-bot.mdx) · [接力](https://github.com/deepcoldy/botmux/blob/master/docs-site/docs/zh/relay.md) |
391
+ | 斜杠命令 / CLI 命令 / 会话内子命令 | [命令参考](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/slash-commands) |
392
+ | `bots.json` 字段 / 环境变量 / 文件位置 | [配置参考](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/bots-json) |
393
+ | 多 CLI 适配器(含 wrapper / 网关接入) | [适配器](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/adapters) |
394
+ | 按场景的最佳实践(Oncall / 报警运维 / 个人研发 / 多人协作) | [最佳实践](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/best-practices) |
395
+ | 常见踩坑 / FAQ 排错 | [踩坑](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/pitfalls) · [FAQ](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/faq) |
396
+ | 功能详解:定时任务 / Oncall / Dashboard / 多机器人协作 / 会话接力 | [定时](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/schedule) · [Oncall](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/oncall) · [Dashboard](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/dashboard) · [多 bot](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/multi-bot) · [接力](https://bytedance.aiforce.cloud/app/app_4k9smq6rdxher/relay) |
397
397
 
398
398
  ## 贡献
399
399
 
@@ -1,3 +1,4 @@
1
+ export declare function sandboxVartmpRoot(): string;
1
2
  /**
2
3
  * Mount an overlayfs: reads fall through to `lower` (real, zero copy); writes
3
4
  * copy-up into `upper` (the landable changeset). `work` is overlayfs scratch on
@@ -123,7 +124,7 @@ export interface SandboxSpawn {
123
124
  outbox: string;
124
125
  /** Project overlay UPPER dir — THE LANDABLE CHANGESET (used by sandbox-land). */
125
126
  workDir: string;
126
- /** HOME overlay UPPER dir (/var/tmp/botmux-sbx/<sid>/home-upper). The sandboxed
127
+ /** HOME overlay UPPER dir (/var/tmp/botmux-sbx-<uid>/<sid>/home-upper). The sandboxed
127
128
  * CLI's $HOME writes — INCLUDING its session jsonl under CLAUDE_CONFIG_DIR —
128
129
  * land here (invisible at the real path). The worker redirects its bridge/idle
129
130
  * watch into this via sandboxedClaudeDataDir() so it sees the CLI's turns. */
@@ -149,10 +150,12 @@ export declare function sandboxedClaudeDataDir(sessionId: string, realDataDir: s
149
150
  * is off / unsupported / a required overlay mount fails (fail-safe = the worker
150
151
  * treats null as a hard error and does NOT silently run unsandboxed).
151
152
  *
152
- * Layout under <dataDir>/sandboxes/<sessionId>/: outbox, shimbin, proj-upper
153
- * (the landable changeset), proj-work, proj-merged, home-merged. The HOME
154
- * overlay's upper/work live under /var/tmp/botmux-sbx/<sessionId>/ because
155
- * overlayfs forbids upper/work inside the lower (= the real home).
153
+ * Layout under <dataDir>/sandboxes/<sessionId>/: shimbin, proj-upper (the
154
+ * landable changeset), proj-work, proj-merged, home-merged. The HOME overlay's
155
+ * upper/work and the daemon-mediated outbox live under
156
+ * /var/tmp/botmux-sbx-<uid>/<sessionId>/ because overlayfs forbids upper/work inside
157
+ * the lower (= the real home), and keeping the outbox outside HOME avoids a
158
+ * nested bind under the HOME overlay on bwrap hosts that hang on that pattern.
156
159
  */
157
160
  export declare function prepareSandbox(opts: {
158
161
  /** Whether the sandbox is on for THIS session (per-bot BotConfig.sandbox OR
@@ -194,7 +197,10 @@ export declare function prepareSandbox(opts: {
194
197
  * path back so the watcher can keep servicing the live CLI's `botmux send`, plus
195
198
  * the workDir (upper changeset for landing) and a cleanup that tears the residue
196
199
  * down at close/exit. Returns null if the session has no sandbox tree on disk
197
- * (never sandboxed). Linux-only, mirrors prepareSandbox's layout.
200
+ * (never sandboxed). Linux-only, mirrors prepareSandbox's layout. New sessions
201
+ * keep outbox under /var/tmp/botmux-sbx-<uid>/<sid>/outbox; fall back to legacy
202
+ * /var/tmp/botmux-sbx/<sid>/outbox or <dataDir>/sandboxes/<sid>/outbox for
203
+ * sessions that were already running before the layout changed.
198
204
  */
199
205
  export declare function attachSandboxOutbox(opts: {
200
206
  sessionId: string;
@@ -269,6 +275,14 @@ export declare function validateRelayRequest(req: RelayRequest): {
269
275
  * be swapped under us. Returns false (reject) on symlink / non-regular / any error.
270
276
  */
271
277
  export declare function materializeOutboxFile(outbox: string, name: string, dest: string): boolean;
278
+ /**
279
+ * TOCTOU-safe read of the `.req.json` trigger file itself. The request file is
280
+ * also sandbox-controlled input, so it needs the same hardening as content and
281
+ * attachments: no symlinks, no FIFO/device files, no blocking opens, and a small
282
+ * size cap before JSON.parse. Returns null on any unsafe/non-regular/oversized
283
+ * input so the watcher can reject and keep its event loop moving.
284
+ */
285
+ export declare function readRelayRequestFile(reqPath: string): string | null;
272
286
  /**
273
287
  * Daemon/worker-side outbox watcher. The sandboxed `botmux send` (relay mode)
274
288
  * drops `<id>.req.json`; we validate (validateRelayRequest) and then MATERIALIZE
@@ -1 +1 @@
1
- {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/sandbox.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sandbox.d.ts","sourceRoot":"","sources":["../../../src/adapters/backend/sandbox.ts"],"names":[],"mappings":"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"}
@@ -20,14 +20,61 @@
20
20
  * sandbox-exec approach and is handled elsewhere.
21
21
  */
22
22
  import { homedir } from 'node:os';
23
- import { mkdirSync, existsSync, writeFileSync, chmodSync, readdirSync, readFileSync, rmSync, statSync, realpathSync, openSync, fstatSync, readSync, writeSync, closeSync, constants as fsConstants } from 'node:fs';
23
+ import { mkdirSync, existsSync, writeFileSync, chmodSync, readdirSync, readFileSync, rmSync, statSync, lstatSync, realpathSync, openSync, fstatSync, readSync, writeSync, closeSync, constants as fsConstants } from 'node:fs';
24
24
  import { atomicWriteFileSync } from '../../utils/atomic-write.js';
25
25
  import { join, dirname, relative, resolve } from 'node:path';
26
26
  import { fileURLToPath } from 'node:url';
27
27
  import { spawn, spawnSync } from 'node:child_process';
28
28
  /** Host root for the HOME overlay's upper/work — MUST be OUTSIDE the home lower
29
- * (overlayfs forbids upper/work inside lower). */
30
- const VARTMP_ROOT = '/var/tmp/botmux-sbx';
29
+ * (overlayfs forbids upper/work inside lower). Keep it per-uid: /var/tmp is a
30
+ * shared multi-user surface, and a global 0700 root would let the first user
31
+ * that starts sandbox block every other Unix user on the same machine. */
32
+ const LEGACY_VARTMP_ROOT = '/var/tmp/botmux-sbx';
33
+ export function sandboxVartmpRoot() {
34
+ const uid = process.getuid?.() ?? 0;
35
+ return `/var/tmp/botmux-sbx-${uid}`;
36
+ }
37
+ function legacySandboxVartmpDir(sessionId) {
38
+ return join(LEGACY_VARTMP_ROOT, sessionId);
39
+ }
40
+ function sandboxVartmpDir(sessionId) {
41
+ return join(sandboxVartmpRoot(), sessionId);
42
+ }
43
+ function sandboxHomeUpperDir(sessionId) {
44
+ const next = join(sandboxVartmpDir(sessionId), 'home-upper');
45
+ const legacy = join(legacySandboxVartmpDir(sessionId), 'home-upper');
46
+ // Published versions stored HOME upper under the global /var/tmp/botmux-sbx.
47
+ // A daemon upgrade may reattach a still-running tmux sandbox whose bwrap
48
+ // namespace continues writing that old overlay upper. If the new per-uid upper
49
+ // does not exist, keep the bridge pointed at the legacy path.
50
+ if (existsSync(legacy) && !existsSync(next))
51
+ return legacy;
52
+ return next;
53
+ }
54
+ /** Create a daemon-private directory and repair legacy/default mkdir modes.
55
+ * Anything under /var/tmp is on a shared multi-user surface, while the outbox
56
+ * carries in-flight Lark message bodies/attachments. Refuse to proceed unless
57
+ * the path is a real directory owned by this daemon user, and group/other bits
58
+ * are gone after chmod. */
59
+ function ensurePrivateDir(dir) {
60
+ try {
61
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
62
+ const lst = lstatSync(dir);
63
+ if (lst.isSymbolicLink() || !lst.isDirectory())
64
+ return false;
65
+ const stBefore = statSync(dir);
66
+ const uid = process.getuid?.();
67
+ if (typeof uid === 'number' && stBefore.uid !== uid)
68
+ return false;
69
+ chmodSync(dir, 0o700); // repair dirs created before this hardening or under a loose umask
70
+ const st = statSync(dir);
71
+ return st.isDirectory() && (typeof uid !== 'number' || st.uid === uid) && (st.mode & 0o077) === 0;
72
+ }
73
+ catch (err) {
74
+ console.error(`[sandbox] failed to prepare private dir ${dir}: ${err instanceof Error ? err.message : String(err)}`);
75
+ return false;
76
+ }
77
+ }
31
78
  // ───────────────────────────── overlay primitives ────────────────────────────
32
79
  /**
33
80
  * Mount an overlayfs: reads fall through to `lower` (real, zero copy); writes
@@ -302,7 +349,7 @@ export function sandboxEnabled() {
302
349
  export function sandboxedClaudeDataDir(sessionId, realDataDir) {
303
350
  const raw = relative(homedir(), realDataDir);
304
351
  const rel = raw.startsWith('..') ? relative(resolveSandboxMountPath(homedir()), realDataDir) : raw;
305
- return join(VARTMP_ROOT, sessionId, 'home-upper', rel);
352
+ return join(sandboxHomeUpperDir(sessionId), rel);
306
353
  }
307
354
  /** Proxy env vars forwarded into the sandbox so the CLI reaches the API even on
308
355
  * the tmux backend (which otherwise only forwards a fixed whitelist). */
@@ -312,10 +359,12 @@ const PROXY_ENV_KEYS = ['http_proxy', 'https_proxy', 'HTTP_PROXY', 'HTTPS_PROXY'
312
359
  * is off / unsupported / a required overlay mount fails (fail-safe = the worker
313
360
  * treats null as a hard error and does NOT silently run unsandboxed).
314
361
  *
315
- * Layout under <dataDir>/sandboxes/<sessionId>/: outbox, shimbin, proj-upper
316
- * (the landable changeset), proj-work, proj-merged, home-merged. The HOME
317
- * overlay's upper/work live under /var/tmp/botmux-sbx/<sessionId>/ because
318
- * overlayfs forbids upper/work inside the lower (= the real home).
362
+ * Layout under <dataDir>/sandboxes/<sessionId>/: shimbin, proj-upper (the
363
+ * landable changeset), proj-work, proj-merged, home-merged. The HOME overlay's
364
+ * upper/work and the daemon-mediated outbox live under
365
+ * /var/tmp/botmux-sbx-<uid>/<sessionId>/ because overlayfs forbids upper/work inside
366
+ * the lower (= the real home), and keeping the outbox outside HOME avoids a
367
+ * nested bind under the HOME overlay on bwrap hosts that hang on that pattern.
319
368
  */
320
369
  export function prepareSandbox(opts) {
321
370
  if (!opts.enabled)
@@ -329,7 +378,6 @@ export function prepareSandbox(opts) {
329
378
  return null;
330
379
  const dataDir = resolveSandboxMountPath(opts.dataDir);
331
380
  const sessionRoot = join(dataDir, 'sandboxes', opts.sessionId);
332
- const outbox = join(sessionRoot, 'outbox');
333
381
  const shimBin = join(sessionRoot, 'shimbin');
334
382
  const empties = join(sessionRoot, 'empties');
335
383
  const projUpper = join(sessionRoot, 'proj-upper'); // THE LANDABLE CHANGESET
@@ -337,9 +385,12 @@ export function prepareSandbox(opts) {
337
385
  const projMerged = join(sessionRoot, 'proj-merged');
338
386
  const homeMerged = join(sessionRoot, 'home-merged'); // merged may live under sessionRoot
339
387
  // HOME overlay upper/work MUST be OUTSIDE the home lower (overlayfs constraint).
340
- const vartmp = join(VARTMP_ROOT, opts.sessionId);
388
+ const vartmp = sandboxVartmpDir(opts.sessionId);
389
+ const outbox = join(vartmp, 'outbox');
341
390
  const homeUpper = join(vartmp, 'home-upper');
342
391
  const homeWork = join(vartmp, 'home-work');
392
+ if (!ensurePrivateDir(sandboxVartmpRoot()) || !ensurePrivateDir(vartmp) || !ensurePrivateDir(outbox))
393
+ return null;
343
394
  for (const d of [outbox, shimBin, empties])
344
395
  mkdirSync(d, { recursive: true });
345
396
  const home = resolveSandboxMountPath(homedir());
@@ -475,6 +526,10 @@ export function prepareSandbox(opts) {
475
526
  rmSync(vartmp, { recursive: true, force: true });
476
527
  }
477
528
  catch { /* */ }
529
+ try {
530
+ rmSync(legacySandboxVartmpDir(opts.sessionId), { recursive: true, force: true });
531
+ }
532
+ catch { /* */ }
478
533
  },
479
534
  };
480
535
  }
@@ -487,25 +542,37 @@ export function prepareSandbox(opts) {
487
542
  * path back so the watcher can keep servicing the live CLI's `botmux send`, plus
488
543
  * the workDir (upper changeset for landing) and a cleanup that tears the residue
489
544
  * down at close/exit. Returns null if the session has no sandbox tree on disk
490
- * (never sandboxed). Linux-only, mirrors prepareSandbox's layout.
545
+ * (never sandboxed). Linux-only, mirrors prepareSandbox's layout. New sessions
546
+ * keep outbox under /var/tmp/botmux-sbx-<uid>/<sid>/outbox; fall back to legacy
547
+ * /var/tmp/botmux-sbx/<sid>/outbox or <dataDir>/sandboxes/<sid>/outbox for
548
+ * sessions that were already running before the layout changed.
491
549
  */
492
550
  export function attachSandboxOutbox(opts) {
493
551
  if (process.platform !== 'linux')
494
552
  return null;
495
553
  const dataDir = resolveSandboxMountPath(opts.dataDir);
496
554
  const sessionRoot = join(dataDir, 'sandboxes', opts.sessionId);
497
- const outbox = join(sessionRoot, 'outbox');
555
+ const vartmp = sandboxVartmpDir(opts.sessionId);
556
+ const newOutbox = join(vartmp, 'outbox');
557
+ const legacyVartmpOutbox = join(legacySandboxVartmpDir(opts.sessionId), 'outbox');
558
+ const legacyOutbox = join(sessionRoot, 'outbox');
498
559
  const projUpper = join(sessionRoot, 'proj-upper');
499
- if (!existsSync(outbox) && !existsSync(projUpper))
560
+ if (!existsSync(newOutbox) && !existsSync(legacyVartmpOutbox) && !existsSync(legacyOutbox) && !existsSync(projUpper))
500
561
  return null; // never sandboxed
562
+ const outbox = existsSync(newOutbox) ? newOutbox : (existsSync(legacyVartmpOutbox) ? legacyVartmpOutbox : (existsSync(legacyOutbox) ? legacyOutbox : newOutbox));
501
563
  // Ensure the outbox exists (the watcher reads it); never (re)mount here.
502
- try {
503
- mkdirSync(outbox, { recursive: true });
564
+ if (outbox === newOutbox) {
565
+ if (!ensurePrivateDir(sandboxVartmpRoot()) || !ensurePrivateDir(vartmp) || !ensurePrivateDir(outbox))
566
+ return null;
504
567
  }
505
- catch { /* */ }
568
+ else if (outbox === legacyVartmpOutbox) {
569
+ if (!ensurePrivateDir(legacySandboxVartmpDir(opts.sessionId)) || !ensurePrivateDir(outbox))
570
+ return null;
571
+ }
572
+ else if (!ensurePrivateDir(outbox))
573
+ return null;
506
574
  const projMerged = join(sessionRoot, 'proj-merged');
507
575
  const homeMerged = join(sessionRoot, 'home-merged');
508
- const vartmp = join(VARTMP_ROOT, opts.sessionId);
509
576
  return {
510
577
  outbox,
511
578
  workDir: projUpper,
@@ -520,6 +587,10 @@ export function attachSandboxOutbox(opts) {
520
587
  rmSync(vartmp, { recursive: true, force: true });
521
588
  }
522
589
  catch { /* */ }
590
+ try {
591
+ rmSync(legacySandboxVartmpDir(opts.sessionId), { recursive: true, force: true });
592
+ }
593
+ catch { /* */ }
523
594
  },
524
595
  };
525
596
  }
@@ -534,13 +605,17 @@ function reclaimSandbox(dataDir, sid) {
534
605
  }
535
606
  catch { /* */ }
536
607
  try {
537
- rmSync(join(VARTMP_ROOT, sid), { recursive: true, force: true });
608
+ rmSync(join(sandboxVartmpRoot(), sid), { recursive: true, force: true });
609
+ }
610
+ catch { /* */ }
611
+ try {
612
+ rmSync(legacySandboxVartmpDir(sid), { recursive: true, force: true });
538
613
  }
539
614
  catch { /* */ }
540
615
  }
541
616
  /** Scan the process table for sandbox session-ids referenced by any running
542
617
  * process's argv. A live bwrap's bind/overlay paths contain `sandboxes/<sid>`
543
- * and `botmux-sbx/<sid>`, so this physically detects which sandbox dirs are
618
+ * and `botmux-sbx-<uid>/<sid>`, so this physically detects which sandbox dirs are
544
619
  * still in use — by overlay sessions AND old clone-model sessions alike. Used as
545
620
  * a hard guard so the sweep never deletes a dir out from under a live CLI. */
546
621
  function liveSandboxSids() {
@@ -552,7 +627,7 @@ function liveSandboxSids() {
552
627
  catch {
553
628
  return live;
554
629
  }
555
- const re = /(?:sandboxes|botmux-sbx)\/([^/\0]+)/g;
630
+ const re = /(?:sandboxes|botmux-sbx(?:-[^/\0]+)?)\/([^/\0]+)/g;
556
631
  for (const pid of pids) {
557
632
  if (!/^\d+$/.test(pid))
558
633
  continue;
@@ -602,9 +677,9 @@ export function sweepOrphanSandboxes(dataDir, activeSessionIds) {
602
677
  return;
603
678
  } // no sandboxes dir yet
604
679
  // Grace before reclaiming an ACTIVE-but-unmounted sandbox: a worker that just
605
- // (re)spawned creates the outbox/shimbin dirs a few syscalls BEFORE it mounts
606
- // the overlay. Without this, a sweep firing in that tiny window would nuke an
607
- // in-progress session's outbox. Non-active orphans are reclaimed immediately
680
+ // (re)spawned creates the sandbox dirs a few syscalls BEFORE it mounts the
681
+ // overlay. Without this, a sweep firing in that tiny window would nuke an
682
+ // in-progress session's shimbin/project tree. Non-active orphans are reclaimed immediately
608
683
  // (no live worker can be mid-spawn for a session that isn't active).
609
684
  const ACTIVE_DEAD_GRACE_MS = 60_000;
610
685
  const now = Date.now();
@@ -749,6 +824,49 @@ export function materializeOutboxFile(outbox, name, dest) {
749
824
  closeSync(outFd);
750
825
  }
751
826
  }
827
+ const RELAY_REQUEST_MAX_BYTES = 64 * 1024;
828
+ /**
829
+ * TOCTOU-safe read of the `.req.json` trigger file itself. The request file is
830
+ * also sandbox-controlled input, so it needs the same hardening as content and
831
+ * attachments: no symlinks, no FIFO/device files, no blocking opens, and a small
832
+ * size cap before JSON.parse. Returns null on any unsafe/non-regular/oversized
833
+ * input so the watcher can reject and keep its event loop moving.
834
+ */
835
+ export function readRelayRequestFile(reqPath) {
836
+ let fd;
837
+ try {
838
+ fd = openSync(reqPath, fsConstants.O_RDONLY | fsConstants.O_NOFOLLOW | fsConstants.O_NONBLOCK);
839
+ }
840
+ catch {
841
+ return null;
842
+ }
843
+ try {
844
+ const st = fstatSync(fd);
845
+ if (!st.isFile())
846
+ return null;
847
+ if (st.size > RELAY_REQUEST_MAX_BYTES)
848
+ return null;
849
+ const chunks = [];
850
+ let total = 0;
851
+ const buf = Buffer.alloc(Math.min(8192, RELAY_REQUEST_MAX_BYTES));
852
+ for (;;) {
853
+ const n = readSync(fd, buf, 0, buf.length, null);
854
+ if (n <= 0)
855
+ break;
856
+ total += n;
857
+ if (total > RELAY_REQUEST_MAX_BYTES)
858
+ return null;
859
+ chunks.push(Buffer.from(buf.subarray(0, n)));
860
+ }
861
+ return Buffer.concat(chunks, total).toString('utf8');
862
+ }
863
+ catch {
864
+ return null;
865
+ }
866
+ finally {
867
+ closeSync(fd);
868
+ }
869
+ }
752
870
  /**
753
871
  * Daemon/worker-side outbox watcher. The sandboxed `botmux send` (relay mode)
754
872
  * drops `<id>.req.json`; we validate (validateRelayRequest) and then MATERIALIZE
@@ -768,7 +886,7 @@ export function startOutboxWatcher(outbox, baseEnv, sessionId) {
768
886
  const finish = (id, reqPath, name, staged, code, stdout, stderr) => {
769
887
  // 原子写:沙盒侧 CLI 在 existsSync 轮询这个 res.json,rename 保证它读到完整 JSON。
770
888
  try {
771
- atomicWriteFileSync(join(outbox, `${id}.res.json`), JSON.stringify({ code, stdout, stderr }));
889
+ atomicWriteFileSync(join(outbox, `${id}.res.json`), JSON.stringify({ code, stdout, stderr }), { mode: 0o600 });
772
890
  }
773
891
  catch { /* */ }
774
892
  try {
@@ -799,8 +917,13 @@ export function startOutboxWatcher(outbox, baseEnv, sessionId) {
799
917
  const id = name.slice(0, -'.req.json'.length);
800
918
  const staged = [];
801
919
  let req;
920
+ const reqText = readRelayRequestFile(reqPath);
921
+ if (reqText === null) {
922
+ finish(id, reqPath, name, staged, 1, '', 'relay rejected: request not a regular file or too large');
923
+ continue;
924
+ }
802
925
  try {
803
- req = JSON.parse(readFileSync(reqPath, 'utf8'));
926
+ req = JSON.parse(reqText);
804
927
  }
805
928
  catch {
806
929
  finish(id, reqPath, name, staged, 1, '', 'relay: bad json');
@@ -812,7 +935,8 @@ export function startOutboxWatcher(outbox, baseEnv, sessionId) {
812
935
  continue;
813
936
  }
814
937
  try {
815
- mkdirSync(staging, { recursive: true });
938
+ mkdirSync(staging, { recursive: true, mode: 0o700 });
939
+ chmodSync(staging, 0o700);
816
940
  }
817
941
  catch { /* */ }
818
942
  // Materialize content (TOCTOU-safe) into the private staging dir.