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,149 @@
1
+ import { describe, it, expect, beforeEach, afterEach } from 'bun:test';
2
+ import { mkdtemp, readFile, writeFile, rm, access } from 'fs/promises';
3
+ import { tmpdir } from 'os';
4
+ import { join } from 'path';
5
+ import { resolveAgentCwd, defaultAgentCwd, imhubWorkspacesRoot, ensureAgentWorkspace, bootstrapAgentWorkspaces, } from './agent-cwd.js';
6
+ const FULL_IM_OPTS = { threadId: 'thread-A', platform: 'feishu', channelId: 'chan-1', userId: 'u1' };
7
+ const NO_IM_OPTS = { model: 'sonnet' };
8
+ describe('resolveAgentCwd', () => {
9
+ let tmpRoot;
10
+ let prevRoot;
11
+ let prevClaudeOverride;
12
+ let prevOpencodeOverride;
13
+ beforeEach(async () => {
14
+ tmpRoot = await mkdtemp(join(tmpdir(), 'imhub-cwdtest-'));
15
+ prevRoot = process.env.IMHUB_WORKSPACES_ROOT;
16
+ prevClaudeOverride = process.env.IMHUB_CLAUDE_CODE_CWD;
17
+ prevOpencodeOverride = process.env.IMHUB_OPENCODE_CWD;
18
+ process.env.IMHUB_WORKSPACES_ROOT = tmpRoot;
19
+ delete process.env.IMHUB_CLAUDE_CODE_CWD;
20
+ delete process.env.IMHUB_OPENCODE_CWD;
21
+ });
22
+ afterEach(async () => {
23
+ if (prevRoot === undefined)
24
+ delete process.env.IMHUB_WORKSPACES_ROOT;
25
+ else
26
+ process.env.IMHUB_WORKSPACES_ROOT = prevRoot;
27
+ if (prevClaudeOverride === undefined)
28
+ delete process.env.IMHUB_CLAUDE_CODE_CWD;
29
+ else
30
+ process.env.IMHUB_CLAUDE_CODE_CWD = prevClaudeOverride;
31
+ if (prevOpencodeOverride === undefined)
32
+ delete process.env.IMHUB_OPENCODE_CWD;
33
+ else
34
+ process.env.IMHUB_OPENCODE_CWD = prevOpencodeOverride;
35
+ await rm(tmpRoot, { recursive: true, force: true });
36
+ });
37
+ it('returns default workspace dir when full IM context is present', () => {
38
+ expect(resolveAgentCwd('claude-code', FULL_IM_OPTS)).toBe(join(tmpRoot, 'claude-code'));
39
+ });
40
+ it('returns undefined for non-IM call (web/scheduler), preserving inherited cwd', () => {
41
+ expect(resolveAgentCwd('claude-code', NO_IM_OPTS)).toBeUndefined();
42
+ });
43
+ it('returns undefined when only one of threadId/platform is set', () => {
44
+ expect(resolveAgentCwd('claude-code', { threadId: 't1' })).toBeUndefined();
45
+ expect(resolveAgentCwd('claude-code', { platform: 'feishu' })).toBeUndefined();
46
+ });
47
+ it('env override beats default IM dir', () => {
48
+ process.env.IMHUB_CLAUDE_CODE_CWD = '/srv/explicit';
49
+ expect(resolveAgentCwd('claude-code', FULL_IM_OPTS)).toBe('/srv/explicit');
50
+ });
51
+ it('env override also wins for non-IM calls (admin escape hatch)', () => {
52
+ process.env.IMHUB_CLAUDE_CODE_CWD = '/srv/explicit';
53
+ expect(resolveAgentCwd('claude-code', NO_IM_OPTS)).toBe('/srv/explicit');
54
+ });
55
+ it('env key respects per-agent naming (hyphen to underscore, uppercase)', () => {
56
+ process.env.IMHUB_OPENCODE_CWD = '/srv/oc';
57
+ expect(resolveAgentCwd('opencode', FULL_IM_OPTS)).toBe('/srv/oc');
58
+ expect(resolveAgentCwd('claude-code', FULL_IM_OPTS)).toBe(join(tmpRoot, 'claude-code'));
59
+ });
60
+ it('IMHUB_WORKSPACES_ROOT moves the default for every agent at once', () => {
61
+ expect(imhubWorkspacesRoot()).toBe(tmpRoot);
62
+ expect(defaultAgentCwd('claude-code')).toBe(join(tmpRoot, 'claude-code'));
63
+ expect(defaultAgentCwd('opencode')).toBe(join(tmpRoot, 'opencode'));
64
+ });
65
+ });
66
+ describe('ensureAgentWorkspace', () => {
67
+ let tmpRoot;
68
+ let prevRoot;
69
+ beforeEach(async () => {
70
+ tmpRoot = await mkdtemp(join(tmpdir(), 'imhub-cwdtest-'));
71
+ prevRoot = process.env.IMHUB_WORKSPACES_ROOT;
72
+ process.env.IMHUB_WORKSPACES_ROOT = tmpRoot;
73
+ });
74
+ afterEach(async () => {
75
+ if (prevRoot === undefined)
76
+ delete process.env.IMHUB_WORKSPACES_ROOT;
77
+ else
78
+ process.env.IMHUB_WORKSPACES_ROOT = prevRoot;
79
+ await rm(tmpRoot, { recursive: true, force: true });
80
+ });
81
+ it('creates the dir and seed file when missing', async () => {
82
+ const dir = await ensureAgentWorkspace('claude-code', { filename: 'CLAUDE.md', content: '# seed' });
83
+ expect(dir).toBe(join(tmpRoot, 'claude-code'));
84
+ expect(await readFile(join(dir, 'CLAUDE.md'), 'utf8')).toBe('# seed');
85
+ });
86
+ it('does NOT overwrite an existing seed file', async () => {
87
+ const dir = join(tmpRoot, 'claude-code');
88
+ await ensureAgentWorkspace('claude-code', { filename: 'CLAUDE.md', content: '# seed' });
89
+ await writeFile(join(dir, 'CLAUDE.md'), 'USER EDITED', 'utf8');
90
+ await ensureAgentWorkspace('claude-code', { filename: 'CLAUDE.md', content: '# new seed' });
91
+ expect(await readFile(join(dir, 'CLAUDE.md'), 'utf8')).toBe('USER EDITED');
92
+ });
93
+ it('is fully idempotent across many calls', async () => {
94
+ for (let i = 0; i < 5; i++) {
95
+ await ensureAgentWorkspace('opencode', { filename: 'AGENTS.md', content: '# x' });
96
+ }
97
+ expect(await readFile(join(tmpRoot, 'opencode', 'AGENTS.md'), 'utf8')).toBe('# x');
98
+ });
99
+ it('honors IMHUB_<AGENT>_CWD override and bootstraps THAT path instead', async () => {
100
+ const altDir = join(tmpRoot, 'custom-spot');
101
+ process.env.IMHUB_CLAUDE_CODE_CWD = altDir;
102
+ try {
103
+ const dir = await ensureAgentWorkspace('claude-code', { filename: 'CLAUDE.md', content: '# alt' });
104
+ expect(dir).toBe(altDir);
105
+ expect(await readFile(join(altDir, 'CLAUDE.md'), 'utf8')).toBe('# alt');
106
+ let defaultExists = false;
107
+ try {
108
+ await access(join(tmpRoot, 'claude-code'));
109
+ defaultExists = true;
110
+ }
111
+ catch { /* expected */ }
112
+ expect(defaultExists).toBe(false);
113
+ }
114
+ finally {
115
+ delete process.env.IMHUB_CLAUDE_CODE_CWD;
116
+ }
117
+ });
118
+ });
119
+ describe('bootstrapAgentWorkspaces', () => {
120
+ let tmpRoot;
121
+ let prevRoot;
122
+ beforeEach(async () => {
123
+ tmpRoot = await mkdtemp(join(tmpdir(), 'imhub-cwdtest-'));
124
+ prevRoot = process.env.IMHUB_WORKSPACES_ROOT;
125
+ process.env.IMHUB_WORKSPACES_ROOT = tmpRoot;
126
+ });
127
+ afterEach(async () => {
128
+ if (prevRoot === undefined)
129
+ delete process.env.IMHUB_WORKSPACES_ROOT;
130
+ else
131
+ process.env.IMHUB_WORKSPACES_ROOT = prevRoot;
132
+ await rm(tmpRoot, { recursive: true, force: true });
133
+ });
134
+ it('creates both built-in agent workspaces with their canonical seed files', async () => {
135
+ const result = await bootstrapAgentWorkspaces();
136
+ const byAgent = Object.fromEntries(result.map((r) => [r.agent, r.dir]));
137
+ expect(byAgent['claude-code']).toBe(join(tmpRoot, 'claude-code'));
138
+ expect(byAgent['opencode']).toBe(join(tmpRoot, 'opencode'));
139
+ expect(await readFile(join(tmpRoot, 'claude-code', 'CLAUDE.md'), 'utf8')).toContain('Claude Code');
140
+ expect(await readFile(join(tmpRoot, 'opencode', 'AGENTS.md'), 'utf8')).toContain('OpenCode');
141
+ });
142
+ it('safe to run twice and does not stomp user edits', async () => {
143
+ await bootstrapAgentWorkspaces();
144
+ await writeFile(join(tmpRoot, 'claude-code', 'CLAUDE.md'), 'USER', 'utf8');
145
+ await bootstrapAgentWorkspaces();
146
+ expect(await readFile(join(tmpRoot, 'claude-code', 'CLAUDE.md'), 'utf8')).toBe('USER');
147
+ });
148
+ });
149
+ //# sourceMappingURL=agent-cwd.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-cwd.test.js","sourceRoot":"","sources":["../../src/core/agent-cwd.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,232 @@
1
+ export interface RunContext {
2
+ threadId: string;
3
+ platform: string;
4
+ userId: string;
5
+ channelId: string;
6
+ }
7
+ /** Wire-format decision sent back to the sidecar. `autoAllowFurther` is
8
+ * internal-only — `sendDecision` strips it before serializing. */
9
+ export type Decision = {
10
+ behavior: 'allow';
11
+ updatedInput?: Record<string, unknown>;
12
+ autoAllowFurther?: boolean;
13
+ } | {
14
+ behavior: 'deny';
15
+ message?: string;
16
+ };
17
+ export interface ApprovalNotification {
18
+ runId: string;
19
+ reqId: string;
20
+ toolName: string;
21
+ input: Record<string, unknown>;
22
+ toolUseId: string;
23
+ ctx: RunContext;
24
+ /** Present iff this request matched a per-thread auto-allow rule. The
25
+ * notifier should render a softer prompt ("⏱ 自动放行中, 5s 内回 n 可拒绝")
26
+ * instead of the regular y/n one — the bus will allow on timer expiry,
27
+ * unless the user actively denies first. */
28
+ autoAllow?: {
29
+ graceMs: number;
30
+ };
31
+ }
32
+ /** Notifier 只负责"通知 IM 推卡片/消息"。不要在这里返回决策;决策走 resolvePending。 */
33
+ export type ApprovalNotifier = (n: ApprovalNotification) => Promise<void>;
34
+ /** Snapshot returned by {@link ApprovalBus.resolvePending} so the router can
35
+ * send the user a confirmation receipt ("✅ 已批准并启用自动放行 …" or
36
+ * "❌ 已拒绝并撤销自动放行 …") without re-scanning bus internals. */
37
+ export interface ResolvedInfo {
38
+ runId: string;
39
+ threadId: string;
40
+ platform: string;
41
+ toolName: string;
42
+ /** Same fingerprint the bus uses for the auto-allow rule key. */
43
+ fingerprint: string;
44
+ /** True iff the resolved pending had been created in auto-allow grace
45
+ * mode — i.e. matched an existing rule. Lets the router distinguish
46
+ * "user denied a normal prompt" from "user denied an auto-allow prompt
47
+ * (which also revoked the rule)". */
48
+ wasAutoAllow: boolean;
49
+ }
50
+ /** Why an approval was resolved. Used by ResolutionListener consumers (e.g.
51
+ * approval-router) to decide whether to edit a UI card to "expired" — only
52
+ * non-`user` causes need cleanup, since the user-driven path (button click /
53
+ * text reply) edits the card itself before the listener fires. */
54
+ export type ResolutionCause = 'user' | 'timeout' | 'sidecar-disconnect' | 'run-terminated' | 'shutdown' | 'notifier-error';
55
+ /** Fired exactly once per pending when it transitions from pending → resolved.
56
+ * Mirrors the wire decision sent to sidecar/dispatch but adds context (cause,
57
+ * threadId, platform) so listeners can route side-effects without scanning
58
+ * bus internals. Synchronous; listener errors are logged + swallowed. */
59
+ export interface ResolutionEvent {
60
+ reqId: string;
61
+ runId: string;
62
+ threadId: string;
63
+ platform: string;
64
+ toolName: string;
65
+ fingerprint: string;
66
+ decision: Decision;
67
+ wasAutoAllow: boolean;
68
+ cause: ResolutionCause;
69
+ }
70
+ export type ResolutionListener = (e: ResolutionEvent) => void;
71
+ export interface ApprovalBusOptions {
72
+ approvalTimeoutMs?: number;
73
+ /** Window between sending the auto-allow notice and auto-resolving as
74
+ * allow (when the user doesn't actively deny in the meantime). */
75
+ autoAllowGraceMs?: number;
76
+ }
77
+ export declare class ApprovalBus {
78
+ private server;
79
+ private socketPath;
80
+ private readonly approvalTimeoutMs;
81
+ private readonly autoAllowGraceMs;
82
+ private runContexts;
83
+ private pendingById;
84
+ private pendingByThread;
85
+ private connections;
86
+ private notifier;
87
+ private resolutionListener;
88
+ /** threadId → set of `${toolName}::${prefix}` keys the user has marked
89
+ * as auto-allow within this conversation. Cleared by clearAutoAllowForThread
90
+ * (called from session.resetConversation) and on stop(). */
91
+ private autoAllowByThread;
92
+ /**
93
+ * Lifetime counters surfaced via {@link getMetrics}. Help ops detect
94
+ * leaks (pending growing unbounded), spikes (totalRequests rate), and
95
+ * approval skew (deny:allow ratio). Reset on stop() so the gauge for a
96
+ * fresh process starts at zero.
97
+ */
98
+ private metricsSnapshot;
99
+ constructor(opts?: ApprovalBusOptions);
100
+ /** 注入"通知 IM 推送"的回调。messenger 层启动时调一次。 */
101
+ setNotifier(n: ApprovalNotifier | null): void;
102
+ /** Subscribe to resolution events. Replaces any previous listener.
103
+ * approval-router uses this to keep its UI cards in sync with bus-side
104
+ * cancellations (timeout / sidecar disconnect / run terminated). The
105
+ * user-driven path (button or y/n text) already edits its own card; the
106
+ * listener still fires there with cause='user' so consumers can dedup. */
107
+ setResolutionListener(l: ResolutionListener | null): void;
108
+ /** 启动 unix socket 服务。返回最终使用的 socket 路径。 */
109
+ start(socketPath?: string): Promise<string>;
110
+ stop(): Promise<void>;
111
+ registerRun(runId: string, ctx: RunContext): void;
112
+ /** 进程结束时调。pending 全 deny,runContext 清掉。 */
113
+ unregisterRun(runId: string): void;
114
+ hasPendingFor(threadId: string): boolean;
115
+ /** True iff a notifier has been installed (i.e. messenger layer has wired
116
+ * the bus into IM). Callers that have a fallback path (e.g. the opencode
117
+ * HTTP adapter) check this before registerSyntheticPending so they can
118
+ * short-circuit when the bus is dormant — mostly relevant in tests and in
119
+ * non-IM call paths (web, scheduler). */
120
+ hasNotifier(): boolean;
121
+ /**
122
+ * 由 messenger.onMessage 拦截层调用。把 thread 队列头部的 pending 用
123
+ * 给定决策 resolve 掉。返回被 resolve 的 pending 描述(platform / tool /
124
+ * fingerprint / 是否处于 auto-allow grace 模式);router 用这些信息发回执。
125
+ * 没有 pending 时返回 null。
126
+ *
127
+ * Auto-allow side-effect: a user-initiated deny against a pending that
128
+ * was running in auto-allow mode revokes the matching rule (the user is
129
+ * signaling "stop auto-approving this"). Revocation is intentionally
130
+ * scoped to this user-path so sidecar disconnects / shutdown / run-
131
+ * terminated denies don't accidentally clear rules the user still wants.
132
+ */
133
+ resolvePending(threadId: string, decision: Decision): ResolvedInfo | null;
134
+ /** Drop every auto-allow rule registered for this thread. Called from
135
+ * session.resetConversation so `/new` truly returns to "ask every time". */
136
+ clearAutoAllowForThread(threadId: string): void;
137
+ /** Test/diagnostic helper — current rule keys for a thread. */
138
+ getAutoAllowKeys(threadId: string): string[];
139
+ /** 测试用:当前 socket 路径。 */
140
+ getSocketPath(): string | null;
141
+ /**
142
+ * Operational metrics snapshot used by /api/metrics (M14). `pending` is
143
+ * a live count; the totals are lifetime counters that monotonically
144
+ * increase until stop(). The three result buckets (allowed / denied /
145
+ * timedOut) are mutually exclusive and sum to totalResolved — see
146
+ * cancelPending for the bucketing rule. Cheap to call — no allocations
147
+ * beyond the returned object.
148
+ */
149
+ getMetrics(): {
150
+ pending: number;
151
+ totalRequests: number;
152
+ totalResolved: number;
153
+ totalAllowed: number;
154
+ totalDenied: number;
155
+ totalTimedOut: number;
156
+ };
157
+ /**
158
+ * Snapshot of every currently-pending approval, sanitized for surface
159
+ * to the operator dashboard. Used by the web `/api/approvals` endpoint
160
+ * (and any future ops tooling). Returns a stable JSON shape — input
161
+ * is included verbatim so the UI can render the same preview the
162
+ * IM-side card would; sockets / dispatch closures / timer handles are
163
+ * intentionally omitted.
164
+ *
165
+ * Sorted oldest-first so the queue head is at index 0 (matches the
166
+ * head-of-thread queue semantics used by resolvePending).
167
+ */
168
+ listPending(): Array<{
169
+ reqId: string;
170
+ runId: string;
171
+ threadId: string;
172
+ platform: string;
173
+ toolName: string;
174
+ input: Record<string, unknown>;
175
+ fingerprint: string;
176
+ autoAllow: boolean;
177
+ registeredAt: number;
178
+ ageMs: number;
179
+ }>;
180
+ private handleConnection;
181
+ private handleLine;
182
+ private handleApproval;
183
+ /**
184
+ * Register an approval request that did NOT come from the unix-socket
185
+ * sidecar. Used by the opencode HTTP bridge (P2): SSE event from opencode
186
+ * → bridge calls this with a `dispatch` callback that POSTs the decision
187
+ * back to opencode's REST API.
188
+ *
189
+ * Behavior is identical to the socket path — same notifier, same timeout,
190
+ * same auto-allow rules — just the delivery channel differs. The
191
+ * `dispatch` is invoked with the final Decision exactly once, on:
192
+ * - user reply via {@link resolvePending}
193
+ * - timeout (deny in normal mode, allow in auto-allow mode)
194
+ * - {@link unregisterRun} (deny: "run terminated")
195
+ * - {@link stop} (deny: "approval-bus shutting down")
196
+ *
197
+ * dispatch errors are logged and swallowed — the bus must not crash on
198
+ * a misbehaving callback.
199
+ *
200
+ * Idempotent on duplicate reqId: returns silently without firing notify
201
+ * (matches the socket path's "duplicate reqId" handling, minus the wire
202
+ * deny since the synthetic caller has no socket to deny on).
203
+ *
204
+ * Throws synchronously only when the caller's bus state is invalid (no
205
+ * notifier installed). The caller should avoid registering the synthetic
206
+ * pending in that case and fall back to its own deny path.
207
+ */
208
+ registerSyntheticPending(input: {
209
+ runId: string;
210
+ reqId: string;
211
+ toolName: string;
212
+ input: Record<string, unknown>;
213
+ /** Optional — used by Claude's MCP path; defaults to '' for synthetic. */
214
+ toolUseId?: string;
215
+ ctx: RunContext;
216
+ dispatch: (decision: Decision) => void;
217
+ }): Promise<void>;
218
+ /**
219
+ * Shared register-and-notify pipeline used by both the socket path and the
220
+ * synthetic path. Builds the PendingApproval, wires the timer, fires the
221
+ * notifier, and ensures the timer is cleared if the notifier itself throws.
222
+ */
223
+ private _registerPending;
224
+ private cancelPending;
225
+ private addAutoAllowRule;
226
+ private removeAutoAllowRule;
227
+ private removePending;
228
+ private sendDecision;
229
+ }
230
+ /** 进程级单例。im-hub 启动时 await approvalBus.start() 一次。 */
231
+ export declare const approvalBus: ApprovalBus;
232
+ //# sourceMappingURL=approval-bus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"approval-bus.d.ts","sourceRoot":"","sources":["../../src/core/approval-bus.ts"],"names":[],"mappings":"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"}