im-hub-pro 0.2.29

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 (384) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +497 -0
  3. package/README.zh-CN.md +496 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.d.ts.map +1 -0
  6. package/dist/cli.js +921 -0
  7. package/dist/cli.js.map +1 -0
  8. package/dist/core/acp-server.d.ts +8 -0
  9. package/dist/core/acp-server.d.ts.map +1 -0
  10. package/dist/core/acp-server.js +266 -0
  11. package/dist/core/acp-server.js.map +1 -0
  12. package/dist/core/agent-base.d.ts +94 -0
  13. package/dist/core/agent-base.d.ts.map +1 -0
  14. package/dist/core/agent-base.js +374 -0
  15. package/dist/core/agent-base.js.map +1 -0
  16. package/dist/core/agent-cwd.d.ts +45 -0
  17. package/dist/core/agent-cwd.d.ts.map +1 -0
  18. package/dist/core/agent-cwd.js +178 -0
  19. package/dist/core/agent-cwd.js.map +1 -0
  20. package/dist/core/agent-cwd.test.d.ts +2 -0
  21. package/dist/core/agent-cwd.test.d.ts.map +1 -0
  22. package/dist/core/agent-cwd.test.js +149 -0
  23. package/dist/core/agent-cwd.test.js.map +1 -0
  24. package/dist/core/approval-bus.d.ts +232 -0
  25. package/dist/core/approval-bus.d.ts.map +1 -0
  26. package/dist/core/approval-bus.js +703 -0
  27. package/dist/core/approval-bus.js.map +1 -0
  28. package/dist/core/approval-bus.synthetic.test.d.ts +2 -0
  29. package/dist/core/approval-bus.synthetic.test.d.ts.map +1 -0
  30. package/dist/core/approval-bus.synthetic.test.js +182 -0
  31. package/dist/core/approval-bus.synthetic.test.js.map +1 -0
  32. package/dist/core/approval-bus.test.d.ts +2 -0
  33. package/dist/core/approval-bus.test.d.ts.map +1 -0
  34. package/dist/core/approval-bus.test.js +537 -0
  35. package/dist/core/approval-bus.test.js.map +1 -0
  36. package/dist/core/approval-router.d.ts +95 -0
  37. package/dist/core/approval-router.d.ts.map +1 -0
  38. package/dist/core/approval-router.js +450 -0
  39. package/dist/core/approval-router.js.map +1 -0
  40. package/dist/core/approval-router.test.d.ts +2 -0
  41. package/dist/core/approval-router.test.d.ts.map +1 -0
  42. package/dist/core/approval-router.test.js +413 -0
  43. package/dist/core/approval-router.test.js.map +1 -0
  44. package/dist/core/audit-log.d.ts +55 -0
  45. package/dist/core/audit-log.d.ts.map +1 -0
  46. package/dist/core/audit-log.js +203 -0
  47. package/dist/core/audit-log.js.map +1 -0
  48. package/dist/core/bgjob-reader.d.ts +65 -0
  49. package/dist/core/bgjob-reader.d.ts.map +1 -0
  50. package/dist/core/bgjob-reader.js +212 -0
  51. package/dist/core/bgjob-reader.js.map +1 -0
  52. package/dist/core/bgjob-reader.test.d.ts +2 -0
  53. package/dist/core/bgjob-reader.test.d.ts.map +1 -0
  54. package/dist/core/bgjob-reader.test.js +178 -0
  55. package/dist/core/bgjob-reader.test.js.map +1 -0
  56. package/dist/core/circuit-breaker.d.ts +37 -0
  57. package/dist/core/circuit-breaker.d.ts.map +1 -0
  58. package/dist/core/circuit-breaker.js +115 -0
  59. package/dist/core/circuit-breaker.js.map +1 -0
  60. package/dist/core/commands/agent.d.ts +4 -0
  61. package/dist/core/commands/agent.d.ts.map +1 -0
  62. package/dist/core/commands/agent.js +21 -0
  63. package/dist/core/commands/agent.js.map +1 -0
  64. package/dist/core/commands/approval.d.ts +3 -0
  65. package/dist/core/commands/approval.d.ts.map +1 -0
  66. package/dist/core/commands/approval.js +44 -0
  67. package/dist/core/commands/approval.js.map +1 -0
  68. package/dist/core/commands/approval.test.d.ts +2 -0
  69. package/dist/core/commands/approval.test.d.ts.map +1 -0
  70. package/dist/core/commands/approval.test.js +85 -0
  71. package/dist/core/commands/approval.test.js.map +1 -0
  72. package/dist/core/commands/audit.d.ts +3 -0
  73. package/dist/core/commands/audit.d.ts.map +1 -0
  74. package/dist/core/commands/audit.js +84 -0
  75. package/dist/core/commands/audit.js.map +1 -0
  76. package/dist/core/commands/builtin.d.ts +3 -0
  77. package/dist/core/commands/builtin.d.ts.map +1 -0
  78. package/dist/core/commands/builtin.js +26 -0
  79. package/dist/core/commands/builtin.js.map +1 -0
  80. package/dist/core/commands/job.d.ts +3 -0
  81. package/dist/core/commands/job.d.ts.map +1 -0
  82. package/dist/core/commands/job.js +195 -0
  83. package/dist/core/commands/job.js.map +1 -0
  84. package/dist/core/commands/model.d.ts +9 -0
  85. package/dist/core/commands/model.d.ts.map +1 -0
  86. package/dist/core/commands/model.js +183 -0
  87. package/dist/core/commands/model.js.map +1 -0
  88. package/dist/core/commands/plan.d.ts +3 -0
  89. package/dist/core/commands/plan.d.ts.map +1 -0
  90. package/dist/core/commands/plan.js +75 -0
  91. package/dist/core/commands/plan.js.map +1 -0
  92. package/dist/core/commands/plan.test.d.ts +2 -0
  93. package/dist/core/commands/plan.test.d.ts.map +1 -0
  94. package/dist/core/commands/plan.test.js +122 -0
  95. package/dist/core/commands/plan.test.js.map +1 -0
  96. package/dist/core/commands/router.d.ts +3 -0
  97. package/dist/core/commands/router.d.ts.map +1 -0
  98. package/dist/core/commands/router.js +71 -0
  99. package/dist/core/commands/router.js.map +1 -0
  100. package/dist/core/commands/schedule.d.ts +3 -0
  101. package/dist/core/commands/schedule.d.ts.map +1 -0
  102. package/dist/core/commands/schedule.js +123 -0
  103. package/dist/core/commands/schedule.js.map +1 -0
  104. package/dist/core/commands/sessions.d.ts +3 -0
  105. package/dist/core/commands/sessions.d.ts.map +1 -0
  106. package/dist/core/commands/sessions.js +88 -0
  107. package/dist/core/commands/sessions.js.map +1 -0
  108. package/dist/core/commands/stats.d.ts +3 -0
  109. package/dist/core/commands/stats.d.ts.map +1 -0
  110. package/dist/core/commands/stats.js +73 -0
  111. package/dist/core/commands/stats.js.map +1 -0
  112. package/dist/core/commands/think.d.ts +3 -0
  113. package/dist/core/commands/think.d.ts.map +1 -0
  114. package/dist/core/commands/think.js +28 -0
  115. package/dist/core/commands/think.js.map +1 -0
  116. package/dist/core/commands/workspaces.d.ts +3 -0
  117. package/dist/core/commands/workspaces.d.ts.map +1 -0
  118. package/dist/core/commands/workspaces.js +47 -0
  119. package/dist/core/commands/workspaces.js.map +1 -0
  120. package/dist/core/config-schema.d.ts +58 -0
  121. package/dist/core/config-schema.d.ts.map +1 -0
  122. package/dist/core/config-schema.js +63 -0
  123. package/dist/core/config-schema.js.map +1 -0
  124. package/dist/core/cron.d.ts +29 -0
  125. package/dist/core/cron.d.ts.map +1 -0
  126. package/dist/core/cron.js +184 -0
  127. package/dist/core/cron.js.map +1 -0
  128. package/dist/core/event-bus.d.ts +80 -0
  129. package/dist/core/event-bus.d.ts.map +1 -0
  130. package/dist/core/event-bus.js +62 -0
  131. package/dist/core/event-bus.js.map +1 -0
  132. package/dist/core/intent-llm.d.ts +27 -0
  133. package/dist/core/intent-llm.d.ts.map +1 -0
  134. package/dist/core/intent-llm.js +170 -0
  135. package/dist/core/intent-llm.js.map +1 -0
  136. package/dist/core/intent.d.ts +12 -0
  137. package/dist/core/intent.d.ts.map +1 -0
  138. package/dist/core/intent.js +187 -0
  139. package/dist/core/intent.js.map +1 -0
  140. package/dist/core/job-board.d.ts +84 -0
  141. package/dist/core/job-board.d.ts.map +1 -0
  142. package/dist/core/job-board.js +379 -0
  143. package/dist/core/job-board.js.map +1 -0
  144. package/dist/core/logger.d.ts +6 -0
  145. package/dist/core/logger.d.ts.map +1 -0
  146. package/dist/core/logger.js +54 -0
  147. package/dist/core/logger.js.map +1 -0
  148. package/dist/core/metrics.d.ts +55 -0
  149. package/dist/core/metrics.d.ts.map +1 -0
  150. package/dist/core/metrics.js +291 -0
  151. package/dist/core/metrics.js.map +1 -0
  152. package/dist/core/onboarding.d.ts +94 -0
  153. package/dist/core/onboarding.d.ts.map +1 -0
  154. package/dist/core/onboarding.js +426 -0
  155. package/dist/core/onboarding.js.map +1 -0
  156. package/dist/core/onboarding.test.d.ts +2 -0
  157. package/dist/core/onboarding.test.d.ts.map +1 -0
  158. package/dist/core/onboarding.test.js +112 -0
  159. package/dist/core/onboarding.test.js.map +1 -0
  160. package/dist/core/rate-limiter.d.ts +44 -0
  161. package/dist/core/rate-limiter.d.ts.map +1 -0
  162. package/dist/core/rate-limiter.js +115 -0
  163. package/dist/core/rate-limiter.js.map +1 -0
  164. package/dist/core/registry.d.ts +32 -0
  165. package/dist/core/registry.d.ts.map +1 -0
  166. package/dist/core/registry.js +122 -0
  167. package/dist/core/registry.js.map +1 -0
  168. package/dist/core/router.d.ts +41 -0
  169. package/dist/core/router.d.ts.map +1 -0
  170. package/dist/core/router.js +431 -0
  171. package/dist/core/router.js.map +1 -0
  172. package/dist/core/schedule.d.ts +65 -0
  173. package/dist/core/schedule.d.ts.map +1 -0
  174. package/dist/core/schedule.js +316 -0
  175. package/dist/core/schedule.js.map +1 -0
  176. package/dist/core/session-subtasks.test.d.ts +2 -0
  177. package/dist/core/session-subtasks.test.d.ts.map +1 -0
  178. package/dist/core/session-subtasks.test.js +88 -0
  179. package/dist/core/session-subtasks.test.js.map +1 -0
  180. package/dist/core/session.d.ts +182 -0
  181. package/dist/core/session.d.ts.map +1 -0
  182. package/dist/core/session.js +774 -0
  183. package/dist/core/session.js.map +1 -0
  184. package/dist/core/sqlite-helper.d.ts +37 -0
  185. package/dist/core/sqlite-helper.d.ts.map +1 -0
  186. package/dist/core/sqlite-helper.js +79 -0
  187. package/dist/core/sqlite-helper.js.map +1 -0
  188. package/dist/core/transcribe.d.ts +25 -0
  189. package/dist/core/transcribe.d.ts.map +1 -0
  190. package/dist/core/transcribe.js +217 -0
  191. package/dist/core/transcribe.js.map +1 -0
  192. package/dist/core/transcribe.test.d.ts +2 -0
  193. package/dist/core/transcribe.test.d.ts.map +1 -0
  194. package/dist/core/transcribe.test.js +163 -0
  195. package/dist/core/transcribe.test.js.map +1 -0
  196. package/dist/core/types.d.ts +352 -0
  197. package/dist/core/types.d.ts.map +1 -0
  198. package/dist/core/types.js +3 -0
  199. package/dist/core/types.js.map +1 -0
  200. package/dist/core/workspace.d.ts +67 -0
  201. package/dist/core/workspace.d.ts.map +1 -0
  202. package/dist/core/workspace.js +113 -0
  203. package/dist/core/workspace.js.map +1 -0
  204. package/dist/index.d.ts +5 -0
  205. package/dist/index.d.ts.map +1 -0
  206. package/dist/index.js +6 -0
  207. package/dist/index.js.map +1 -0
  208. package/dist/plugins/agents/acp/acp-adapter.d.ts +16 -0
  209. package/dist/plugins/agents/acp/acp-adapter.d.ts.map +1 -0
  210. package/dist/plugins/agents/acp/acp-adapter.js +49 -0
  211. package/dist/plugins/agents/acp/acp-adapter.js.map +1 -0
  212. package/dist/plugins/agents/acp/acp-client.d.ts +32 -0
  213. package/dist/plugins/agents/acp/acp-client.d.ts.map +1 -0
  214. package/dist/plugins/agents/acp/acp-client.js +175 -0
  215. package/dist/plugins/agents/acp/acp-client.js.map +1 -0
  216. package/dist/plugins/agents/acp/discovery.d.ts +19 -0
  217. package/dist/plugins/agents/acp/discovery.d.ts.map +1 -0
  218. package/dist/plugins/agents/acp/discovery.js +109 -0
  219. package/dist/plugins/agents/acp/discovery.js.map +1 -0
  220. package/dist/plugins/agents/acp/index.d.ts +4 -0
  221. package/dist/plugins/agents/acp/index.d.ts.map +1 -0
  222. package/dist/plugins/agents/acp/index.js +4 -0
  223. package/dist/plugins/agents/acp/index.js.map +1 -0
  224. package/dist/plugins/agents/acp/types.d.ts +62 -0
  225. package/dist/plugins/agents/acp/types.d.ts.map +1 -0
  226. package/dist/plugins/agents/acp/types.js +5 -0
  227. package/dist/plugins/agents/acp/types.js.map +1 -0
  228. package/dist/plugins/agents/claude-code/adapter.test.d.ts +2 -0
  229. package/dist/plugins/agents/claude-code/adapter.test.d.ts.map +1 -0
  230. package/dist/plugins/agents/claude-code/adapter.test.js +195 -0
  231. package/dist/plugins/agents/claude-code/adapter.test.js.map +1 -0
  232. package/dist/plugins/agents/claude-code/index.d.ts +25 -0
  233. package/dist/plugins/agents/claude-code/index.d.ts.map +1 -0
  234. package/dist/plugins/agents/claude-code/index.js +184 -0
  235. package/dist/plugins/agents/claude-code/index.js.map +1 -0
  236. package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts +42 -0
  237. package/dist/plugins/agents/claude-code/mcp-approval-server.d.ts.map +1 -0
  238. package/dist/plugins/agents/claude-code/mcp-approval-server.js +235 -0
  239. package/dist/plugins/agents/claude-code/mcp-approval-server.js.map +1 -0
  240. package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts +2 -0
  241. package/dist/plugins/agents/claude-code/mcp-approval-server.test.d.ts.map +1 -0
  242. package/dist/plugins/agents/claude-code/mcp-approval-server.test.js +188 -0
  243. package/dist/plugins/agents/claude-code/mcp-approval-server.test.js.map +1 -0
  244. package/dist/plugins/agents/codex/adapter.test.d.ts +2 -0
  245. package/dist/plugins/agents/codex/adapter.test.d.ts.map +1 -0
  246. package/dist/plugins/agents/codex/adapter.test.js +192 -0
  247. package/dist/plugins/agents/codex/adapter.test.js.map +1 -0
  248. package/dist/plugins/agents/codex/index.d.ts +37 -0
  249. package/dist/plugins/agents/codex/index.d.ts.map +1 -0
  250. package/dist/plugins/agents/codex/index.js +254 -0
  251. package/dist/plugins/agents/codex/index.js.map +1 -0
  252. package/dist/plugins/agents/copilot/index.d.ts +35 -0
  253. package/dist/plugins/agents/copilot/index.d.ts.map +1 -0
  254. package/dist/plugins/agents/copilot/index.js +182 -0
  255. package/dist/plugins/agents/copilot/index.js.map +1 -0
  256. package/dist/plugins/agents/opencode/adapter.test.d.ts +2 -0
  257. package/dist/plugins/agents/opencode/adapter.test.d.ts.map +1 -0
  258. package/dist/plugins/agents/opencode/adapter.test.js +139 -0
  259. package/dist/plugins/agents/opencode/adapter.test.js.map +1 -0
  260. package/dist/plugins/agents/opencode/http-adapter.test.d.ts +2 -0
  261. package/dist/plugins/agents/opencode/http-adapter.test.d.ts.map +1 -0
  262. package/dist/plugins/agents/opencode/http-adapter.test.js +492 -0
  263. package/dist/plugins/agents/opencode/http-adapter.test.js.map +1 -0
  264. package/dist/plugins/agents/opencode/index.d.ts +5 -0
  265. package/dist/plugins/agents/opencode/index.d.ts.map +1 -0
  266. package/dist/plugins/agents/opencode/index.js +30 -0
  267. package/dist/plugins/agents/opencode/index.js.map +1 -0
  268. package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts +138 -0
  269. package/dist/plugins/agents/opencode/opencode-http-adapter.d.ts.map +1 -0
  270. package/dist/plugins/agents/opencode/opencode-http-adapter.js +549 -0
  271. package/dist/plugins/agents/opencode/opencode-http-adapter.js.map +1 -0
  272. package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts +24 -0
  273. package/dist/plugins/agents/opencode/opencode-stdio-adapter.d.ts.map +1 -0
  274. package/dist/plugins/agents/opencode/opencode-stdio-adapter.js +103 -0
  275. package/dist/plugins/agents/opencode/opencode-stdio-adapter.js.map +1 -0
  276. package/dist/plugins/agents/opencode/serve-manager.d.ts +27 -0
  277. package/dist/plugins/agents/opencode/serve-manager.d.ts.map +1 -0
  278. package/dist/plugins/agents/opencode/serve-manager.js +190 -0
  279. package/dist/plugins/agents/opencode/serve-manager.js.map +1 -0
  280. package/dist/plugins/messengers/discord/discord-adapter.d.ts +22 -0
  281. package/dist/plugins/messengers/discord/discord-adapter.d.ts.map +1 -0
  282. package/dist/plugins/messengers/discord/discord-adapter.js +241 -0
  283. package/dist/plugins/messengers/discord/discord-adapter.js.map +1 -0
  284. package/dist/plugins/messengers/discord/discord-adapter.test.d.ts +2 -0
  285. package/dist/plugins/messengers/discord/discord-adapter.test.d.ts.map +1 -0
  286. package/dist/plugins/messengers/discord/discord-adapter.test.js +332 -0
  287. package/dist/plugins/messengers/discord/discord-adapter.test.js.map +1 -0
  288. package/dist/plugins/messengers/discord/index.d.ts +4 -0
  289. package/dist/plugins/messengers/discord/index.d.ts.map +1 -0
  290. package/dist/plugins/messengers/discord/index.js +4 -0
  291. package/dist/plugins/messengers/discord/index.js.map +1 -0
  292. package/dist/plugins/messengers/discord/markdown-to-discord.d.ts +11 -0
  293. package/dist/plugins/messengers/discord/markdown-to-discord.d.ts.map +1 -0
  294. package/dist/plugins/messengers/discord/markdown-to-discord.js +59 -0
  295. package/dist/plugins/messengers/discord/markdown-to-discord.js.map +1 -0
  296. package/dist/plugins/messengers/discord/types.d.ts +9 -0
  297. package/dist/plugins/messengers/discord/types.d.ts.map +1 -0
  298. package/dist/plugins/messengers/discord/types.js +3 -0
  299. package/dist/plugins/messengers/discord/types.js.map +1 -0
  300. package/dist/plugins/messengers/feishu/card-builder.d.ts +23 -0
  301. package/dist/plugins/messengers/feishu/card-builder.d.ts.map +1 -0
  302. package/dist/plugins/messengers/feishu/card-builder.js +89 -0
  303. package/dist/plugins/messengers/feishu/card-builder.js.map +1 -0
  304. package/dist/plugins/messengers/feishu/feishu-adapter.d.ts +33 -0
  305. package/dist/plugins/messengers/feishu/feishu-adapter.d.ts.map +1 -0
  306. package/dist/plugins/messengers/feishu/feishu-adapter.js +195 -0
  307. package/dist/plugins/messengers/feishu/feishu-adapter.js.map +1 -0
  308. package/dist/plugins/messengers/feishu/feishu-client.d.ts +44 -0
  309. package/dist/plugins/messengers/feishu/feishu-client.d.ts.map +1 -0
  310. package/dist/plugins/messengers/feishu/feishu-client.js +120 -0
  311. package/dist/plugins/messengers/feishu/feishu-client.js.map +1 -0
  312. package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts +2 -0
  313. package/dist/plugins/messengers/feishu/feishu-dedup.test.d.ts.map +1 -0
  314. package/dist/plugins/messengers/feishu/feishu-dedup.test.js +70 -0
  315. package/dist/plugins/messengers/feishu/feishu-dedup.test.js.map +1 -0
  316. package/dist/plugins/messengers/feishu/index.d.ts +4 -0
  317. package/dist/plugins/messengers/feishu/index.d.ts.map +1 -0
  318. package/dist/plugins/messengers/feishu/index.js +4 -0
  319. package/dist/plugins/messengers/feishu/index.js.map +1 -0
  320. package/dist/plugins/messengers/feishu/types.d.ts +113 -0
  321. package/dist/plugins/messengers/feishu/types.d.ts.map +1 -0
  322. package/dist/plugins/messengers/feishu/types.js +4 -0
  323. package/dist/plugins/messengers/feishu/types.js.map +1 -0
  324. package/dist/plugins/messengers/telegram/index.d.ts +4 -0
  325. package/dist/plugins/messengers/telegram/index.d.ts.map +1 -0
  326. package/dist/plugins/messengers/telegram/index.js +4 -0
  327. package/dist/plugins/messengers/telegram/index.js.map +1 -0
  328. package/dist/plugins/messengers/telegram/markdown-to-html.d.ts +5 -0
  329. package/dist/plugins/messengers/telegram/markdown-to-html.d.ts.map +1 -0
  330. package/dist/plugins/messengers/telegram/markdown-to-html.js +186 -0
  331. package/dist/plugins/messengers/telegram/markdown-to-html.js.map +1 -0
  332. package/dist/plugins/messengers/telegram/media-download.d.ts +51 -0
  333. package/dist/plugins/messengers/telegram/media-download.d.ts.map +1 -0
  334. package/dist/plugins/messengers/telegram/media-download.js +224 -0
  335. package/dist/plugins/messengers/telegram/media-download.js.map +1 -0
  336. package/dist/plugins/messengers/telegram/media-download.test.d.ts +2 -0
  337. package/dist/plugins/messengers/telegram/media-download.test.d.ts.map +1 -0
  338. package/dist/plugins/messengers/telegram/media-download.test.js +125 -0
  339. package/dist/plugins/messengers/telegram/media-download.test.js.map +1 -0
  340. package/dist/plugins/messengers/telegram/telegram-adapter.d.ts +62 -0
  341. package/dist/plugins/messengers/telegram/telegram-adapter.d.ts.map +1 -0
  342. package/dist/plugins/messengers/telegram/telegram-adapter.js +653 -0
  343. package/dist/plugins/messengers/telegram/telegram-adapter.js.map +1 -0
  344. package/dist/plugins/messengers/telegram/types.d.ts +47 -0
  345. package/dist/plugins/messengers/telegram/types.d.ts.map +1 -0
  346. package/dist/plugins/messengers/telegram/types.js +3 -0
  347. package/dist/plugins/messengers/telegram/types.js.map +1 -0
  348. package/dist/plugins/messengers/wechat/ilink-adapter.d.ts +68 -0
  349. package/dist/plugins/messengers/wechat/ilink-adapter.d.ts.map +1 -0
  350. package/dist/plugins/messengers/wechat/ilink-adapter.js +483 -0
  351. package/dist/plugins/messengers/wechat/ilink-adapter.js.map +1 -0
  352. package/dist/plugins/messengers/wechat/ilink-client.d.ts +66 -0
  353. package/dist/plugins/messengers/wechat/ilink-client.d.ts.map +1 -0
  354. package/dist/plugins/messengers/wechat/ilink-client.js +288 -0
  355. package/dist/plugins/messengers/wechat/ilink-client.js.map +1 -0
  356. package/dist/plugins/messengers/wechat/ilink-types.d.ts +173 -0
  357. package/dist/plugins/messengers/wechat/ilink-types.d.ts.map +1 -0
  358. package/dist/plugins/messengers/wechat/ilink-types.js +12 -0
  359. package/dist/plugins/messengers/wechat/ilink-types.js.map +1 -0
  360. package/dist/utils/backoff.d.ts +35 -0
  361. package/dist/utils/backoff.d.ts.map +1 -0
  362. package/dist/utils/backoff.js +59 -0
  363. package/dist/utils/backoff.js.map +1 -0
  364. package/dist/utils/cross-platform.d.ts +26 -0
  365. package/dist/utils/cross-platform.d.ts.map +1 -0
  366. package/dist/utils/cross-platform.js +58 -0
  367. package/dist/utils/cross-platform.js.map +1 -0
  368. package/dist/utils/message-split.d.ts +14 -0
  369. package/dist/utils/message-split.d.ts.map +1 -0
  370. package/dist/utils/message-split.js +65 -0
  371. package/dist/utils/message-split.js.map +1 -0
  372. package/dist/utils/safe-equal.d.ts +2 -0
  373. package/dist/utils/safe-equal.d.ts.map +1 -0
  374. package/dist/utils/safe-equal.js +11 -0
  375. package/dist/utils/safe-equal.js.map +1 -0
  376. package/dist/web/public/_app.js +196 -0
  377. package/dist/web/public/index.html +935 -0
  378. package/dist/web/public/settings.html +1181 -0
  379. package/dist/web/public/tasks.html +1827 -0
  380. package/dist/web/server.d.ts +11 -0
  381. package/dist/web/server.d.ts.map +1 -0
  382. package/dist/web/server.js +1820 -0
  383. package/dist/web/server.js.map +1 -0
  384. package/package.json +73 -0
@@ -0,0 +1,186 @@
1
+ // Markdown to Telegram HTML converter
2
+ // Telegram supports a subset of HTML: <b>, <i>, <u>, <s>, <code>, <pre>, <a>
3
+ import { marked } from 'marked';
4
+ /**
5
+ * Process math/LaTeX formulas - convert to code blocks
6
+ */
7
+ function processMath(markdown) {
8
+ // Block math $$ ... $$ -> code block
9
+ let result = markdown.replace(/\$\$([\s\S]*?)\$\$/g, (_, formula) => {
10
+ return `\`\`\`math\n${formula.trim()}\n\`\`\``;
11
+ });
12
+ // Inline math $ ... $ -> inline code (be careful not to match currency)
13
+ result = result.replace(/\$([^$\n]+)\$/g, (_, formula) => {
14
+ // Skip if it looks like currency (e.g., $100, $5.00)
15
+ if (/^\d+(\.\d+)?$/.test(formula.trim())) {
16
+ return `$${formula}$`;
17
+ }
18
+ return `\`${formula.trim()}\``;
19
+ });
20
+ return result;
21
+ }
22
+ /**
23
+ * Convert HTML to Telegram-compatible HTML
24
+ * Telegram only supports: b, i, u, s, del, strike, code, pre, a, span (tg-spoiler, tg-emoji)
25
+ */
26
+ function htmlToTelegramHtml(html) {
27
+ let result = html;
28
+ // Convert tables to pre-formatted text
29
+ result = result.replace(/<table[^>]*>([\s\S]*?)<\/table>/gi, (_, tableContent) => {
30
+ const rows = [];
31
+ // Extract header rows
32
+ const theadMatch = tableContent.match(/<thead[^>]*>([\s\S]*?)<\/thead>/i);
33
+ if (theadMatch) {
34
+ const headerRow = extractRowCells(theadMatch[1]);
35
+ if (headerRow.length > 0)
36
+ rows.push(headerRow);
37
+ }
38
+ // Extract body rows
39
+ const tbodyMatch = tableContent.match(/<tbody[^>]*>([\s\S]*?)<\/tbody>/i);
40
+ const bodyContent = tbodyMatch ? tbodyMatch[1] : tableContent;
41
+ // Match all tr elements
42
+ const trMatches = bodyContent.matchAll(/<tr[^>]*>([\s\S]*?)<\/tr>/gi);
43
+ for (const trMatch of trMatches) {
44
+ const row = extractRowCells(trMatch[1]);
45
+ if (row.length > 0)
46
+ rows.push(row);
47
+ }
48
+ if (rows.length === 0)
49
+ return '';
50
+ // Calculate column widths
51
+ const colCount = Math.max(...rows.map(r => r.length));
52
+ const widths = Array(colCount).fill(0);
53
+ for (const row of rows) {
54
+ row.forEach((cell, i) => {
55
+ widths[i] = Math.max(widths[i], cell.length);
56
+ });
57
+ }
58
+ // Cap widths at 20 for mobile
59
+ const cappedWidths = widths.map(w => Math.min(w, 20));
60
+ // Format as text table
61
+ const lines = [];
62
+ rows.forEach((row, rowIdx) => {
63
+ const cells = row.map((cell, i) => {
64
+ const width = cappedWidths[i] || 10;
65
+ const truncated = cell.length > width ? cell.slice(0, width - 1) + '…' : cell;
66
+ return truncated.padEnd(width);
67
+ });
68
+ // Pad missing cells
69
+ while (cells.length < colCount) {
70
+ cells.push(' '.repeat(cappedWidths[cells.length] || 10));
71
+ }
72
+ lines.push('| ' + cells.join(' | ') + ' |');
73
+ // Add separator after header
74
+ if (rowIdx === 0) {
75
+ const separator = cappedWidths.map(w => '-'.repeat(w));
76
+ lines.push('| ' + separator.join(' | ') + ' |');
77
+ }
78
+ });
79
+ return `<pre>${escapeHtml(lines.join('\n'))}</pre>`;
80
+ });
81
+ // Helper to extract cells from a row
82
+ function extractRowCells(rowHtml) {
83
+ const cells = [];
84
+ const cellMatches = rowHtml.matchAll(/<t[dh][^>]*>([\s\S]*?)<\/t[dh]>/gi);
85
+ for (const match of cellMatches) {
86
+ // Strip HTML tags and get plain text
87
+ const text = match[1].replace(/<[^>]+>/g, '').trim();
88
+ cells.push(text);
89
+ }
90
+ return cells;
91
+ }
92
+ // Convert <kbd> to <code>
93
+ result = result.replace(/<kbd>([^<]*)<\/kbd>/gi, '<code>$1</code>');
94
+ // Convert headers to bold
95
+ result = result.replace(/<h[1-6][^>]*>([\s\S]*?)<\/h[1-6]>/gi, '\n<b>$1</b>\n\n');
96
+ // Convert lists to bullet points
97
+ result = result.replace(/<ul[^>]*>([\s\S]*?)<\/ul>/gi, '\n$1\n');
98
+ result = result.replace(/<ol[^>]*>([\s\S]*?)<\/ol>/gi, '\n$1\n');
99
+ result = result.replace(/<li[^>]*>([\s\S]*?)<\/li>/gi, '• $1\n');
100
+ // Convert blockquotes to italic
101
+ result = result.replace(/<blockquote[^>]*>([\s\S]*?)<\/blockquote>/gi, '<i>$1</i>\n\n');
102
+ // Fix code blocks: convert <pre><code class="lang"> to <pre language="lang">
103
+ result = result.replace(/<pre><code(?:\s+class="language-(\w+)")?>([\s\S]*?)<\/code><\/pre>/gi, (_, lang, code) => {
104
+ if (lang) {
105
+ return `<pre language="${lang}">${code}</pre>`;
106
+ }
107
+ return `<pre>${code}</pre>`;
108
+ });
109
+ // Convert <mark>, <highlight> to <b>
110
+ result = result.replace(/<mark[^>]*>([^<]*)<\/mark>/gi, '<b>$1</b>');
111
+ result = result.replace(/<highlight[^>]*>([^<]*)<\/highlight>/gi, '<b>$1</b>');
112
+ // Strip <sub>, <sup> (no good equivalent)
113
+ result = result.replace(/<su[bp][^>]*>([^<]*)<\/su[bp]>/gi, '$1');
114
+ // Convert semantic tags to Telegram equivalents
115
+ result = result.replace(/<(\/?)strong>/gi, '<$1b>');
116
+ result = result.replace(/<(\/?)em>/gi, '<$1i>');
117
+ result = result.replace(/<(\/?)ins>/gi, '<$1u>');
118
+ result = result.replace(/<(\/?)del>/gi, '<$1s>');
119
+ result = result.replace(/<(\/?)strike>/gi, '<$1s>');
120
+ // Replace <hr> with separator
121
+ result = result.replace(/<hr\s*\/?>/gi, '\n―――\n');
122
+ // Replace <br> with newline
123
+ result = result.replace(/<br\s*\/?>/gi, '\n');
124
+ // Convert images to links
125
+ result = result.replace(/<img[^>]*alt="([^"]*)"[^>]*src="([^"]*)"[^>]*\/?>/gi, '<a href="$2">$1</a>');
126
+ result = result.replace(/<img[^>]*src="([^"]*)"[^>]*alt="([^"]*)"[^>]*\/?>/gi, '<a href="$1">$2</a>');
127
+ result = result.replace(/<img[^>]*src="([^"]*)"[^>]*\/?>/gi, '<a href="$1">Image</a>');
128
+ // Strip all other unsupported HTML tags but keep content
129
+ const supportedTags = new Set(['b', 'i', 'u', 's', 'code', 'pre', 'a', 'span', 'tg-spoiler', 'tg-emoji']);
130
+ result = result.replace(/<(\/?)([a-zA-Z][a-zA-Z0-9-]*)\b[^>]*>/g, (match, isClosing, tagName) => {
131
+ const tag = tagName.toLowerCase();
132
+ if (supportedTags.has(tag)) {
133
+ // For <a>, keep only href attribute
134
+ if (tag === 'a' && !isClosing) {
135
+ const hrefMatch = match.match(/href="([^"]*)"/i) || match.match(/href='([^']*)'/i);
136
+ if (hrefMatch) {
137
+ return `<a href="${hrefMatch[1]}">`;
138
+ }
139
+ return '<a href="#">';
140
+ }
141
+ // For <pre>, keep only language attribute (check both class="language-X" and language="X")
142
+ if (tag === 'pre' && !isClosing) {
143
+ // Check for language="X" first (already converted)
144
+ const directLangMatch = match.match(/language="([^"]*)"/i) || match.match(/language='([^']*)'/i);
145
+ if (directLangMatch) {
146
+ return `<pre language="${directLangMatch[1]}">`;
147
+ }
148
+ // Then check for class="language-X" (from marked)
149
+ const classLangMatch = match.match(/class="language-([^"]*)"/i) || match.match(/class='language-([^']*)'/i);
150
+ if (classLangMatch) {
151
+ return `<pre language="${classLangMatch[1]}">`;
152
+ }
153
+ return '<pre>';
154
+ }
155
+ // Keep supported tags without attributes
156
+ return isClosing ? `</${tag}>` : `<${tag}>`;
157
+ }
158
+ // Remove unsupported tag
159
+ return '';
160
+ });
161
+ // Clean up multiple blank lines
162
+ result = result.replace(/\n{3,}/g, '\n\n');
163
+ return result;
164
+ }
165
+ function escapeHtml(text) {
166
+ return text
167
+ .replace(/&/g, '&amp;')
168
+ .replace(/</g, '&lt;')
169
+ .replace(/>/g, '&gt;');
170
+ }
171
+ /**
172
+ * Convert Markdown to Telegram-compatible HTML
173
+ */
174
+ export function markdownToTelegramHtml(markdown) {
175
+ // Pre-process: convert math formulas
176
+ const processed = processMath(markdown);
177
+ // Parse markdown to HTML
178
+ const html = marked.parse(processed, {
179
+ gfm: true,
180
+ breaks: false,
181
+ });
182
+ // Convert to Telegram-compatible HTML
183
+ const telegramHtml = htmlToTelegramHtml(html);
184
+ return telegramHtml.trim();
185
+ }
186
+ //# sourceMappingURL=markdown-to-html.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"markdown-to-html.js","sourceRoot":"","sources":["../../../../src/plugins/messengers/telegram/markdown-to-html.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /** Root for downloaded media. Always resolved absolute so the safety check
2
+ * `path.startsWith(MEDIA_ROOT)` is robust. Override via env for tests. */
3
+ export declare const MEDIA_ROOT: string;
4
+ /** Hard upper bound on a single download. Telegram photo max is ~10 MB and
5
+ * document max is 50 MB on the bot API; we cap at 20 MB so a runaway upload
6
+ * can't fill the disk. */
7
+ export declare const MAX_BYTES: number;
8
+ /** Default cleanup TTL — 7 days. Long enough that a multi-day conversation
9
+ * can re-reference an image, short enough that the directory doesn't grow
10
+ * without bound. Override via env for ops. */
11
+ export declare const DEFAULT_TTL_MS: number;
12
+ export interface DownloadParams {
13
+ /** TG file URL — must be on api.telegram.org. */
14
+ url: string;
15
+ /** Destination folder under MEDIA_ROOT, typically `${platform}/${chatId}`. */
16
+ subdir: string;
17
+ /** Filename (no path components). */
18
+ filename: string;
19
+ }
20
+ export interface DownloadResult {
21
+ path: string;
22
+ bytes: number;
23
+ }
24
+ /**
25
+ * Download `url` and write it to `<MEDIA_ROOT>/<subdir>/<filename>`. Returns
26
+ * the absolute path on success. Errors out (and writes nothing) on:
27
+ * - non-https or non-telegram host
28
+ * - HTTP non-2xx
29
+ * - body larger than MAX_BYTES
30
+ * - destination resolves outside MEDIA_ROOT (path-injection guard)
31
+ *
32
+ * Best-effort cleanup: if write fails partway through, the partial file is
33
+ * removed so a half-saved image never reaches the agent.
34
+ */
35
+ export declare function downloadToMediaRoot(params: DownloadParams): Promise<DownloadResult>;
36
+ /**
37
+ * Walk MEDIA_ROOT recursively, removing regular files older than `maxAgeMs`.
38
+ * Empty directories are also pruned. Errors on a single file are logged and
39
+ * ignored — cleanup must never block the IM pipeline.
40
+ */
41
+ export declare function cleanupOldMedia(maxAgeMs?: number): Promise<{
42
+ deleted: number;
43
+ kept: number;
44
+ }>;
45
+ /**
46
+ * Pick a filename extension from a TG `file_path` (e.g. "photos/file_123.jpg")
47
+ * or a mime-type like "image/png". Falls back to "bin" so we always have an
48
+ * extension on disk for easier debugging.
49
+ */
50
+ export declare function pickExtension(filePath: string | undefined, mime: string | undefined): string;
51
+ //# sourceMappingURL=media-download.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-download.d.ts","sourceRoot":"","sources":["../../../../src/plugins/messengers/telegram/media-download.ts"],"names":[],"mappings":"AAmBA;2EAC2E;AAC3E,eAAO,MAAM,UAAU,EAAE,MAExB,CAAA;AAED;;2BAE2B;AAC3B,eAAO,MAAM,SAAS,QAAmB,CAAA;AAEzC;;+CAE+C;AAC/C,eAAO,MAAM,cAAc,EAAE,MAKzB,CAAA;AAEJ,MAAM,WAAW,cAAc;IAC7B,iDAAiD;IACjD,GAAG,EAAE,MAAM,CAAA;IACX,8EAA8E;IAC9E,MAAM,EAAE,MAAM,CAAA;IACd,qCAAqC;IACrC,QAAQ,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;GAUG;AACH,wBAAsB,mBAAmB,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC,CA6CzF;AAsDD;;;;GAIG;AACH,wBAAsB,eAAe,CAAC,QAAQ,GAAE,MAAuB,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAsCnH;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,MAAM,CAsB5F"}
@@ -0,0 +1,224 @@
1
+ // Telegram media download helper.
2
+ //
3
+ // Downloads photo / document attachments to ~/.im-hub/media/telegram/<chatId>/
4
+ // so the claude-code agent (multimodal) can Read them by path. Decoupled from
5
+ // the adapter class so it's testable in isolation.
6
+ //
7
+ // Lifetime: cleanupOldMedia() prunes files older than the configured TTL,
8
+ // invoked hourly + at startup by the adapter. We do not delete on a per-thread
9
+ // basis — same image may be referenced across turns within a 30-min imhub
10
+ // session.
11
+ import { spawn } from 'child_process';
12
+ import { mkdir, readdir, rm, stat } from 'fs/promises';
13
+ import { homedir } from 'os';
14
+ import { join, resolve } from 'path';
15
+ import { logger as rootLogger } from '../../../core/logger.js';
16
+ const log = rootLogger.child({ component: 'telegram.media' });
17
+ /** Root for downloaded media. Always resolved absolute so the safety check
18
+ * `path.startsWith(MEDIA_ROOT)` is robust. Override via env for tests. */
19
+ export const MEDIA_ROOT = resolve(process.env.IMHUB_MEDIA_ROOT ?? join(homedir(), '.im-hub', 'media'));
20
+ /** Hard upper bound on a single download. Telegram photo max is ~10 MB and
21
+ * document max is 50 MB on the bot API; we cap at 20 MB so a runaway upload
22
+ * can't fill the disk. */
23
+ export const MAX_BYTES = 20 * 1024 * 1024;
24
+ /** Default cleanup TTL — 7 days. Long enough that a multi-day conversation
25
+ * can re-reference an image, short enough that the directory doesn't grow
26
+ * without bound. Override via env for ops. */
27
+ export const DEFAULT_TTL_MS = (() => {
28
+ const raw = process.env.IMHUB_MEDIA_TTL_MS;
29
+ if (!raw)
30
+ return 7 * 24 * 60 * 60 * 1000;
31
+ const n = Number(raw);
32
+ return Number.isFinite(n) && n > 0 ? n : 7 * 24 * 60 * 60 * 1000;
33
+ })();
34
+ /**
35
+ * Download `url` and write it to `<MEDIA_ROOT>/<subdir>/<filename>`. Returns
36
+ * the absolute path on success. Errors out (and writes nothing) on:
37
+ * - non-https or non-telegram host
38
+ * - HTTP non-2xx
39
+ * - body larger than MAX_BYTES
40
+ * - destination resolves outside MEDIA_ROOT (path-injection guard)
41
+ *
42
+ * Best-effort cleanup: if write fails partway through, the partial file is
43
+ * removed so a half-saved image never reaches the agent.
44
+ */
45
+ export async function downloadToMediaRoot(params) {
46
+ // Path-injection guard: filename must not contain separators / .. so the
47
+ // join + resolve stays inside MEDIA_ROOT/subdir. subdir itself is built by
48
+ // the adapter from numeric chatId, so we trust the caller there but still
49
+ // do the post-resolve check below.
50
+ if (params.filename.includes('/') || params.filename.includes('\\') || params.filename.includes('..')) {
51
+ throw new Error(`unsafe filename: ${params.filename}`);
52
+ }
53
+ const u = new URL(params.url);
54
+ if (u.protocol !== 'https:' || u.hostname !== 'api.telegram.org') {
55
+ throw new Error(`refusing to download from ${u.host}: only api.telegram.org allowed`);
56
+ }
57
+ const dir = resolve(MEDIA_ROOT, params.subdir);
58
+ const path = resolve(dir, params.filename);
59
+ if (!path.startsWith(MEDIA_ROOT + '/')) {
60
+ throw new Error(`destination escapes MEDIA_ROOT: ${path}`);
61
+ }
62
+ await mkdir(dir, { recursive: true });
63
+ // Implementation note: we shell out to curl rather than using Node's fetch.
64
+ // Observed in production: undici's fetch hits intermittent ETIMEDOUT against
65
+ // api.telegram.org from this VM (3 retries all fail in ~2s while curl from
66
+ // the same shell succeeds in <1s). Root cause unclear (HTTP/2 vs 1.1
67
+ // negotiation, DNS warm-up, or a VM-specific routing quirk). curl is a
68
+ // boring, well-tested OS tool — pragmatic answer is to use it.
69
+ //
70
+ // Size cap is enforced via curl's --max-filesize plus a post-download stat
71
+ // (the flag relies on Content-Length when the server provides it; the stat
72
+ // covers the case where the server omits it).
73
+ await runCurl(params.url, path);
74
+ let bytes;
75
+ try {
76
+ bytes = (await stat(path)).size;
77
+ }
78
+ catch (err) {
79
+ throw new Error(`download appeared to succeed but file missing: ${err.message}`);
80
+ }
81
+ if (bytes > MAX_BYTES) {
82
+ await rm(path, { force: true }).catch(() => { });
83
+ throw new Error(`payload exceeds ${MAX_BYTES} bytes (got ${bytes})`);
84
+ }
85
+ log.info({ event: 'media.downloaded', path, bytes }, 'media saved');
86
+ return { path, bytes };
87
+ }
88
+ /**
89
+ * Spawn curl to fetch `url` directly to `outPath`. Resolves on exit code 0,
90
+ * rejects with an Error containing curl's stderr on any other exit. Hard
91
+ * timeout 60 s so a hung connection can't wedge the calling handler.
92
+ *
93
+ * Exposed only inside this module — callers stick to {@link downloadToMediaRoot}.
94
+ */
95
+ async function runCurl(url, outPath) {
96
+ const args = [
97
+ '--silent',
98
+ '--show-error',
99
+ '--fail', // non-2xx → exit code 22
100
+ '--location', // follow redirects
101
+ '--max-time', '60',
102
+ '--max-filesize', String(MAX_BYTES),
103
+ '--output', outPath,
104
+ url,
105
+ ];
106
+ return new Promise((resolve, reject) => {
107
+ const child = spawn('curl', args, { stdio: ['ignore', 'ignore', 'pipe'] });
108
+ const stderr = [];
109
+ let settled = false;
110
+ const settle = (fn) => {
111
+ if (settled)
112
+ return;
113
+ settled = true;
114
+ fn();
115
+ };
116
+ const timer = setTimeout(() => {
117
+ try {
118
+ child.kill('SIGKILL');
119
+ }
120
+ catch { /* ignore */ }
121
+ // Best-effort partial-file cleanup
122
+ void rm(outPath, { force: true }).catch(() => { });
123
+ settle(() => reject(new Error('curl wall-clock timeout (60s)')));
124
+ }, 65 * 1000);
125
+ child.stderr.on('data', (b) => stderr.push(b));
126
+ child.on('error', (e) => {
127
+ clearTimeout(timer);
128
+ void rm(outPath, { force: true }).catch(() => { });
129
+ settle(() => reject(new Error(`curl spawn failed: ${e.message}`)));
130
+ });
131
+ child.on('close', (code) => {
132
+ clearTimeout(timer);
133
+ if (code === 0) {
134
+ settle(() => resolve());
135
+ return;
136
+ }
137
+ void rm(outPath, { force: true }).catch(() => { });
138
+ const msg = Buffer.concat(stderr).toString().trim().slice(0, 400) || `curl exit ${code}`;
139
+ settle(() => reject(new Error(msg)));
140
+ });
141
+ });
142
+ }
143
+ /**
144
+ * Walk MEDIA_ROOT recursively, removing regular files older than `maxAgeMs`.
145
+ * Empty directories are also pruned. Errors on a single file are logged and
146
+ * ignored — cleanup must never block the IM pipeline.
147
+ */
148
+ export async function cleanupOldMedia(maxAgeMs = DEFAULT_TTL_MS) {
149
+ let deleted = 0;
150
+ let kept = 0;
151
+ const cutoff = Date.now() - maxAgeMs;
152
+ const walk = async (dir) => {
153
+ let entries;
154
+ try {
155
+ entries = await readdir(dir, { withFileTypes: true });
156
+ }
157
+ catch {
158
+ return;
159
+ }
160
+ for (const entry of entries) {
161
+ const full = join(dir, entry.name);
162
+ if (entry.isDirectory()) {
163
+ await walk(full);
164
+ // Try to remove dir if it became empty; ignore failures.
165
+ try {
166
+ const left = await readdir(full);
167
+ if (left.length === 0)
168
+ await rm(full, { recursive: true });
169
+ }
170
+ catch { /* ignore */ }
171
+ }
172
+ else if (entry.isFile()) {
173
+ try {
174
+ const st = await stat(full);
175
+ if (st.mtimeMs < cutoff) {
176
+ await rm(full, { force: true });
177
+ deleted += 1;
178
+ }
179
+ else {
180
+ kept += 1;
181
+ }
182
+ }
183
+ catch { /* ignore */ }
184
+ }
185
+ }
186
+ };
187
+ await walk(MEDIA_ROOT);
188
+ if (deleted > 0) {
189
+ log.info({ event: 'media.cleanup', deleted, kept, maxAgeMs }, 'media cleanup pass');
190
+ }
191
+ return { deleted, kept };
192
+ }
193
+ /**
194
+ * Pick a filename extension from a TG `file_path` (e.g. "photos/file_123.jpg")
195
+ * or a mime-type like "image/png". Falls back to "bin" so we always have an
196
+ * extension on disk for easier debugging.
197
+ */
198
+ export function pickExtension(filePath, mime) {
199
+ if (filePath) {
200
+ const m = /\.([a-z0-9]+)$/i.exec(filePath);
201
+ if (m)
202
+ return m[1].toLowerCase();
203
+ }
204
+ if (mime) {
205
+ const map = {
206
+ 'image/jpeg': 'jpg',
207
+ 'image/jpg': 'jpg',
208
+ 'image/png': 'png',
209
+ 'image/gif': 'gif',
210
+ 'image/webp': 'webp',
211
+ 'image/heic': 'heic',
212
+ 'audio/ogg': 'ogg',
213
+ 'audio/mpeg': 'mp3',
214
+ 'audio/mp4': 'm4a',
215
+ };
216
+ if (map[mime.toLowerCase()])
217
+ return map[mime.toLowerCase()];
218
+ const m = /\/([a-z0-9]+)$/i.exec(mime);
219
+ if (m)
220
+ return m[1].toLowerCase();
221
+ }
222
+ return 'bin';
223
+ }
224
+ //# sourceMappingURL=media-download.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-download.js","sourceRoot":"","sources":["../../../../src/plugins/messengers/telegram/media-download.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=media-download.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-download.test.d.ts","sourceRoot":"","sources":["../../../../src/plugins/messengers/telegram/media-download.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,125 @@
1
+ // media-download — focuses on the safety checks (path injection, host
2
+ // allowlist, size cap) and the cleanup walker. We don't test the happy-path
3
+ // download against the real TG API (network); a per-test override of `fetch`
4
+ // drives the URL-fetcher behavior.
5
+ import { describe, it, expect, beforeEach, beforeAll, afterAll } from 'bun:test';
6
+ import { mkdir, readdir, rm, stat, writeFile, utimes } from 'fs/promises';
7
+ import { tmpdir } from 'os';
8
+ import { join } from 'path';
9
+ const TEST_ROOT = join(tmpdir(), `imhub-media-test-${process.pid}-${Math.random().toString(36).slice(2)}`);
10
+ process.env.IMHUB_MEDIA_ROOT = TEST_ROOT;
11
+ // Import AFTER setting env so MEDIA_ROOT picks it up at module-eval time.
12
+ const { MEDIA_ROOT, MAX_BYTES, downloadToMediaRoot, cleanupOldMedia, pickExtension } = await import('./media-download.js');
13
+ describe('media-download — pickExtension', () => {
14
+ it('extracts ext from file_path', () => {
15
+ expect(pickExtension('photos/file_123.jpg', undefined)).toBe('jpg');
16
+ expect(pickExtension('docs/X.PNG', undefined)).toBe('png');
17
+ });
18
+ it('falls back to mime when file_path has none', () => {
19
+ expect(pickExtension(undefined, 'image/png')).toBe('png');
20
+ expect(pickExtension('noext', 'image/jpeg')).toBe('jpg');
21
+ expect(pickExtension(undefined, 'audio/ogg')).toBe('ogg');
22
+ });
23
+ it('returns "bin" when nothing matches', () => {
24
+ expect(pickExtension(undefined, undefined)).toBe('bin');
25
+ // octet-stream contains a hyphen which our minimal regex doesn't capture;
26
+ // fallback to "bin" is fine — generic binary anyway.
27
+ expect(pickExtension(undefined, 'application/octet-stream')).toBe('bin');
28
+ });
29
+ });
30
+ describe('media-download — safety guards', () => {
31
+ it('uses the env-overridden MEDIA_ROOT', () => {
32
+ expect(MEDIA_ROOT).toBe(TEST_ROOT);
33
+ });
34
+ it('rejects unsafe filenames (slashes / dotdot)', async () => {
35
+ await expect(downloadToMediaRoot({
36
+ url: 'https://api.telegram.org/file/botX/Y',
37
+ subdir: 'telegram/123',
38
+ filename: '../escape.jpg',
39
+ })).rejects.toThrow(/unsafe filename/);
40
+ await expect(downloadToMediaRoot({
41
+ url: 'https://api.telegram.org/file/botX/Y',
42
+ subdir: 'telegram/123',
43
+ filename: 'a/b.jpg',
44
+ })).rejects.toThrow(/unsafe filename/);
45
+ });
46
+ it('rejects non-telegram hosts', async () => {
47
+ await expect(downloadToMediaRoot({
48
+ url: 'https://evil.example.com/x.jpg',
49
+ subdir: 'telegram/123',
50
+ filename: '1.jpg',
51
+ })).rejects.toThrow(/api\.telegram\.org/);
52
+ });
53
+ it('rejects non-https schemes', async () => {
54
+ await expect(downloadToMediaRoot({
55
+ url: 'http://api.telegram.org/file/botX/Y',
56
+ subdir: 'telegram/123',
57
+ filename: '1.jpg',
58
+ })).rejects.toThrow(/api\.telegram\.org/);
59
+ });
60
+ });
61
+ describe('media-download — cleanupOldMedia', () => {
62
+ beforeAll(async () => {
63
+ await mkdir(TEST_ROOT, { recursive: true });
64
+ });
65
+ afterAll(async () => {
66
+ await rm(TEST_ROOT, { recursive: true, force: true }).catch(() => { });
67
+ });
68
+ beforeEach(async () => {
69
+ // Fresh tree each test
70
+ await rm(TEST_ROOT, { recursive: true, force: true }).catch(() => { });
71
+ await mkdir(TEST_ROOT, { recursive: true });
72
+ });
73
+ it('deletes only files older than the TTL', async () => {
74
+ const dir = join(TEST_ROOT, 'telegram', '111');
75
+ await mkdir(dir, { recursive: true });
76
+ const oldPath = join(dir, 'old.jpg');
77
+ const newPath = join(dir, 'new.jpg');
78
+ await writeFile(oldPath, 'old');
79
+ await writeFile(newPath, 'new');
80
+ // Make oldPath 10 days old
81
+ const tenDaysAgo = Date.now() - 10 * 24 * 60 * 60 * 1000;
82
+ await utimes(oldPath, tenDaysAgo / 1000, tenDaysAgo / 1000);
83
+ const result = await cleanupOldMedia(7 * 24 * 60 * 60 * 1000);
84
+ expect(result.deleted).toBe(1);
85
+ expect(result.kept).toBe(1);
86
+ // Verify which is which
87
+ await expect(stat(oldPath)).rejects.toThrow();
88
+ const newStat = await stat(newPath);
89
+ expect(newStat.isFile()).toBe(true);
90
+ });
91
+ it('removes empty directories left behind after cleanup', async () => {
92
+ const dir = join(TEST_ROOT, 'telegram', '222');
93
+ await mkdir(dir, { recursive: true });
94
+ const filePath = join(dir, 'old.png');
95
+ await writeFile(filePath, 'x');
96
+ const longAgo = Date.now() - 100 * 24 * 60 * 60 * 1000;
97
+ await utimes(filePath, longAgo / 1000, longAgo / 1000);
98
+ await cleanupOldMedia(7 * 24 * 60 * 60 * 1000);
99
+ // Directory should be pruned
100
+ const left = await readdir(TEST_ROOT).catch(() => []);
101
+ expect(left).not.toContain('telegram');
102
+ });
103
+ it('is a no-op when MEDIA_ROOT does not exist', async () => {
104
+ await rm(TEST_ROOT, { recursive: true, force: true });
105
+ const result = await cleanupOldMedia(1000);
106
+ expect(result.deleted).toBe(0);
107
+ expect(result.kept).toBe(0);
108
+ });
109
+ });
110
+ describe('media-download — downloadToMediaRoot (real curl against local server)', () => {
111
+ // The HTTPS-only / api.telegram.org-only host check fires BEFORE we reach
112
+ // curl, so we can't easily exercise the curl path against a local plain-
113
+ // HTTP server through downloadToMediaRoot directly. The safety guards
114
+ // above already cover the most important policy. Rather than spinning up
115
+ // a local TLS-terminating server with a self-signed cert just for unit
116
+ // tests, we lean on:
117
+ // - the safety-guard tests above
118
+ // - manual end-to-end verification via a real TG voice / image upload
119
+ //
120
+ // If this path regresses, expect to see "[图片附件下载失败:...]" or
121
+ // "[语音附件下载失败:...]" markers on the user's screen — that's the
122
+ // observable contract.
123
+ it('runs', () => { expect(true).toBe(true); });
124
+ });
125
+ //# sourceMappingURL=media-download.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"media-download.test.js","sourceRoot":"","sources":["../../../../src/plugins/messengers/telegram/media-download.test.ts"],"names":[],"mappings":"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"}