@rubytech/create-realagent-code 0.1.582 → 0.1.583

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 (311) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/lib/routine-templates/dist/index.d.ts +1 -1
  3. package/payload/platform/lib/routine-templates/dist/index.d.ts.map +1 -1
  4. package/payload/platform/lib/routine-templates/dist/index.js +5 -1
  5. package/payload/platform/lib/routine-templates/dist/index.js.map +1 -1
  6. package/payload/platform/lib/routine-templates/src/index.ts +4 -0
  7. package/payload/platform/lib/telegram-managed-bot/dist/index.d.ts +269 -0
  8. package/payload/platform/lib/telegram-managed-bot/dist/index.d.ts.map +1 -0
  9. package/payload/platform/lib/telegram-managed-bot/dist/index.js +351 -0
  10. package/payload/platform/lib/telegram-managed-bot/dist/index.js.map +1 -0
  11. package/payload/platform/lib/telegram-managed-bot/src/__tests__/managed-bot.test.ts +251 -0
  12. package/payload/platform/lib/telegram-managed-bot/src/index.ts +529 -0
  13. package/payload/platform/lib/telegram-managed-bot/tsconfig.json +9 -0
  14. package/payload/platform/lib/telegram-managed-bot/vitest.config.ts +9 -0
  15. package/payload/platform/lib/telegram-reach/dist/index.d.ts +80 -0
  16. package/payload/platform/lib/telegram-reach/dist/index.d.ts.map +1 -0
  17. package/payload/platform/lib/telegram-reach/dist/index.js +211 -0
  18. package/payload/platform/lib/telegram-reach/dist/index.js.map +1 -0
  19. package/payload/platform/lib/telegram-reach/src/__tests__/telegram-reach.test.ts +216 -0
  20. package/payload/platform/lib/telegram-reach/src/index.ts +262 -0
  21. package/payload/platform/lib/telegram-reach/tsconfig.json +9 -0
  22. package/payload/platform/lib/telegram-reach/vitest.config.ts +9 -0
  23. package/payload/platform/package.json +2 -2
  24. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  25. package/payload/platform/plugins/admin/hooks/__tests__/fs-schema-guard.test.sh +9 -0
  26. package/payload/platform/plugins/admin/hooks/__tests__/preference-consult-directive.test.sh +26 -0
  27. package/payload/platform/plugins/admin/hooks/fs-schema-guard.sh +20 -3
  28. package/payload/platform/plugins/admin/hooks/preference-consult-directive.sh +50 -5
  29. package/payload/platform/plugins/admin/skills/platform-architecture/SKILL.md +142 -8
  30. package/payload/platform/plugins/admin/skills/whats-new/SKILL.md +10 -0
  31. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.d.ts +2 -0
  32. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.d.ts.map +1 -0
  33. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.js +100 -0
  34. package/payload/platform/plugins/contacts/mcp/dist/__tests__/telegram-user-id.test.js.map +1 -0
  35. package/payload/platform/plugins/contacts/mcp/dist/index.js +11 -5
  36. package/payload/platform/plugins/contacts/mcp/dist/index.js.map +1 -1
  37. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts +5 -0
  38. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.d.ts.map +1 -1
  39. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js +21 -4
  40. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-create.js.map +1 -1
  41. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts +5 -0
  42. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.d.ts.map +1 -1
  43. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js +29 -1
  44. package/payload/platform/plugins/contacts/mcp/dist/tools/contact-lookup.js.map +1 -1
  45. package/payload/platform/plugins/contacts/mcp/vitest.config.ts +14 -0
  46. package/payload/platform/plugins/docs/references/admin-ui.md +17 -3
  47. package/payload/platform/plugins/docs/references/channel-wake-and-prompt.md +15 -0
  48. package/payload/platform/plugins/docs/references/telegram-guide.md +109 -4
  49. package/payload/platform/plugins/filesystem/PLUGIN.md +2 -2
  50. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.d.ts +2 -0
  51. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.d.ts.map +1 -0
  52. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.js +108 -0
  53. package/payload/platform/plugins/filesystem/mcp/dist/lib/__tests__/account-schema.test.js.map +1 -0
  54. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.d.ts +17 -0
  55. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.d.ts.map +1 -0
  56. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.js +162 -0
  57. package/payload/platform/plugins/filesystem/mcp/dist/lib/account-schema.js.map +1 -0
  58. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.d.ts +2 -0
  59. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.d.ts.map +1 -0
  60. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.js +116 -0
  61. package/payload/platform/plugins/filesystem/mcp/dist/tools/__tests__/schema-fence.test.js.map +1 -0
  62. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.d.ts.map +1 -1
  63. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js +9 -0
  64. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-edit.js.map +1 -1
  65. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.d.ts.map +1 -1
  66. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js +10 -0
  67. package/payload/platform/plugins/filesystem/mcp/dist/tools/file-write.js.map +1 -1
  68. package/payload/platform/plugins/scheduling/PLUGIN.md +8 -2
  69. package/payload/platform/plugins/scheduling/mcp/dist/index.js +7 -2
  70. package/payload/platform/plugins/scheduling/mcp/dist/index.js.map +1 -1
  71. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js +53 -6
  72. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate-audits.test.js.map +1 -1
  73. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js +38 -0
  74. package/payload/platform/plugins/scheduling/mcp/dist/lib/__tests__/gate.test.js.map +1 -1
  75. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts +5 -3
  76. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.d.ts.map +1 -1
  77. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js +18 -3
  78. package/payload/platform/plugins/scheduling/mcp/dist/lib/gate.js.map +1 -1
  79. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts +30 -0
  80. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.d.ts.map +1 -1
  81. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js +112 -39
  82. package/payload/platform/plugins/scheduling/mcp/dist/lib/schedule-audit.js.map +1 -1
  83. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js +14 -1
  84. package/payload/platform/plugins/scheduling/mcp/dist/scripts/check-due-events.js.map +1 -1
  85. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js +258 -8
  86. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/schedule-gate-write.test.js.map +1 -1
  87. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js +206 -3
  88. package/payload/platform/plugins/scheduling/mcp/dist/tools/__tests__/scheduling-gate.test.js.map +1 -1
  89. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts +29 -21
  90. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.d.ts.map +1 -1
  91. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js +94 -14
  92. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-event.js.map +1 -1
  93. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.d.ts.map +1 -1
  94. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js +29 -1
  95. package/payload/platform/plugins/scheduling/mcp/dist/tools/schedule-update.js.map +1 -1
  96. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts +30 -0
  97. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.d.ts.map +1 -1
  98. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js +69 -6
  99. package/payload/platform/plugins/scheduling/mcp/dist/tools/scheduling-gate.js.map +1 -1
  100. package/payload/platform/plugins/whatsapp/references/channels-whatsapp.md +12 -0
  101. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts +36 -5
  102. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.d.ts.map +1 -1
  103. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js +103 -10
  104. package/payload/platform/services/claude-session-manager/dist/account-dir-schema-reconcile.js.map +1 -1
  105. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.d.ts.map +1 -1
  106. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js +2 -0
  107. package/payload/platform/services/claude-session-manager/dist/canonical-tool-names.generated.js.map +1 -1
  108. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  109. package/payload/platform/services/claude-session-manager/dist/http-server.js +42 -4
  110. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  111. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  112. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +19 -5
  113. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  114. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts +9 -0
  115. package/payload/platform/services/claude-session-manager/dist/rc-daemon.d.ts.map +1 -1
  116. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js +5 -1
  117. package/payload/platform/services/claude-session-manager/dist/rc-daemon.js.map +1 -1
  118. package/payload/platform/services/telegram-channel/dist/instructions.d.ts.map +1 -1
  119. package/payload/platform/services/telegram-channel/dist/instructions.js +13 -0
  120. package/payload/platform/services/telegram-channel/dist/instructions.js.map +1 -1
  121. package/payload/platform/services/telegram-channel/dist/notification.d.ts +13 -0
  122. package/payload/platform/services/telegram-channel/dist/notification.d.ts.map +1 -1
  123. package/payload/platform/services/telegram-channel/dist/notification.js +13 -2
  124. package/payload/platform/services/telegram-channel/dist/notification.js.map +1 -1
  125. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts +52 -0
  126. package/payload/platform/services/whatsapp-channel/dist/notification.d.ts.map +1 -1
  127. package/payload/platform/services/whatsapp-channel/dist/notification.js +64 -1
  128. package/payload/platform/services/whatsapp-channel/dist/notification.js.map +1 -1
  129. package/payload/platform/services/whatsapp-channel/dist/server.d.ts.map +1 -1
  130. package/payload/platform/services/whatsapp-channel/dist/server.js +14 -2
  131. package/payload/platform/services/whatsapp-channel/dist/server.js.map +1 -1
  132. package/payload/platform/templates/account-schema/SCHEMA.md +17 -10
  133. package/payload/platform/templates/agents/admin/IDENTITY.md +5 -0
  134. package/payload/platform/templates/specialists/agents/data-manager.md +2 -2
  135. package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
  136. package/payload/server/{chunk-BFRB2U75.js → chunk-CV6AEMKH.js} +1 -0
  137. package/payload/server/{manager-CTVTF4YB.js → manager-FQSQSSJ3.js} +1 -1
  138. package/payload/server/public/activity.html +6 -6
  139. package/payload/server/public/agents.html +5 -5
  140. package/payload/server/public/assets/{AdminLoginScreens-AnJZfgFz.js → AdminLoginScreens-BLsDYWVi.js} +1 -1
  141. package/payload/server/public/assets/AdminLoginScreens-BLsDYWVi.js.br +0 -0
  142. package/payload/server/public/assets/AdminLoginScreens-BLsDYWVi.js.gz +0 -0
  143. package/payload/server/public/assets/{AdminShell-CEVqyQqg.js → AdminShell-BZS8jzFA.js} +1 -1
  144. package/payload/server/public/assets/AdminShell-BZS8jzFA.js.br +0 -0
  145. package/payload/server/public/assets/AdminShell-BZS8jzFA.js.gz +0 -0
  146. package/payload/server/public/assets/{activity-czUaUl8I.js → activity-DOjvoLdd.js} +1 -1
  147. package/payload/server/public/assets/activity-DOjvoLdd.js.br +0 -0
  148. package/payload/server/public/assets/activity-DOjvoLdd.js.gz +0 -0
  149. package/payload/server/public/assets/{admin-B0iRcfuk.js → admin-JrSNg29g.js} +1 -1
  150. package/payload/server/public/assets/admin-JrSNg29g.js.br +0 -0
  151. package/payload/server/public/assets/admin-JrSNg29g.js.gz +0 -0
  152. package/payload/server/public/assets/{agents-BocONCeQ.js → agents--9WMJVLC.js} +1 -1
  153. package/payload/server/public/assets/agents--9WMJVLC.js.br +0 -0
  154. package/payload/server/public/assets/agents--9WMJVLC.js.gz +0 -0
  155. package/payload/server/public/assets/{browser-Cy-rJm3t.js → browser-DiiMmTrM.js} +1 -1
  156. package/payload/server/public/assets/browser-DiiMmTrM.js.br +0 -0
  157. package/payload/server/public/assets/browser-DiiMmTrM.js.gz +0 -0
  158. package/payload/server/public/assets/{calendar-DUTKxWtd.js → calendar-CazmCzgO.js} +1 -1
  159. package/payload/server/public/assets/calendar-CazmCzgO.js.br +0 -0
  160. package/payload/server/public/assets/calendar-CazmCzgO.js.gz +0 -0
  161. package/payload/server/public/assets/chat-CB0TcyXX.js +1 -0
  162. package/payload/server/public/assets/chat-CB0TcyXX.js.br +0 -0
  163. package/payload/server/public/assets/chat-CB0TcyXX.js.gz +0 -0
  164. package/payload/server/public/assets/chevron-left-0tVo23Dh.js +1 -0
  165. package/payload/server/public/assets/chevron-left-0tVo23Dh.js.br +0 -0
  166. package/payload/server/public/assets/clock-D2cZ0sge.js +1 -0
  167. package/payload/server/public/assets/clock-D2cZ0sge.js.br +0 -0
  168. package/payload/server/public/assets/clock-D2cZ0sge.js.gz +0 -0
  169. package/payload/server/public/assets/{copy-DE9qmAs5.js → copy-C87QIN_o.js} +1 -1
  170. package/payload/server/public/assets/copy-C87QIN_o.js.br +0 -0
  171. package/payload/server/public/assets/copy-C87QIN_o.js.gz +0 -0
  172. package/payload/server/public/assets/data-CK7AMxPt.js +1 -0
  173. package/payload/server/public/assets/data-CK7AMxPt.js.br +0 -0
  174. package/payload/server/public/assets/data-CK7AMxPt.js.gz +0 -0
  175. package/payload/server/public/assets/{field-DE2tEfTo.js → field-C3kQgGWE.js} +1 -1
  176. package/payload/server/public/assets/field-C3kQgGWE.js.br +0 -0
  177. package/payload/server/public/assets/field-C3kQgGWE.js.gz +0 -0
  178. package/payload/server/public/assets/{file-text-BHfL2gqo.js → file-text-rWowl9RN.js} +1 -1
  179. package/payload/server/public/assets/file-text-rWowl9RN.js.br +0 -0
  180. package/payload/server/public/assets/file-text-rWowl9RN.js.gz +0 -0
  181. package/payload/server/public/assets/{graph-08S_BofN.js → graph-CVJKz1Uo.js} +1 -1
  182. package/payload/server/public/assets/graph-CVJKz1Uo.js.br +0 -0
  183. package/payload/server/public/assets/graph-CVJKz1Uo.js.gz +0 -0
  184. package/payload/server/public/assets/{graph-labels-C640Er5R.js → graph-labels-kneGnsHB.js} +1 -1
  185. package/payload/server/public/assets/graph-labels-kneGnsHB.js.br +0 -0
  186. package/payload/server/public/assets/graph-labels-kneGnsHB.js.gz +0 -0
  187. package/payload/server/public/assets/{jsx-runtime-FxjwOUbA.css → jsx-runtime-rMaiYjNu.css} +1 -1
  188. package/payload/server/public/assets/jsx-runtime-rMaiYjNu.css.br +0 -0
  189. package/payload/server/public/assets/{jsx-runtime-FxjwOUbA.css.gz → jsx-runtime-rMaiYjNu.css.gz} +0 -0
  190. package/payload/server/public/assets/{operator-DH5Kw3fL.js → operator-BGvB4hu4.js} +1 -1
  191. package/payload/server/public/assets/operator-BGvB4hu4.js.br +0 -0
  192. package/payload/server/public/assets/operator-BGvB4hu4.js.gz +0 -0
  193. package/payload/server/public/assets/{page-BDpa4HWT.js → page-DBQLOasI.js} +1 -1
  194. package/payload/server/public/assets/page-DBQLOasI.js.br +0 -0
  195. package/payload/server/public/assets/page-DBQLOasI.js.gz +0 -0
  196. package/payload/server/public/assets/{page-yn7ivq8O.js → page-DyVAu7tm.js} +1 -1
  197. package/payload/server/public/assets/page-DyVAu7tm.js.br +0 -0
  198. package/payload/server/public/assets/page-DyVAu7tm.js.gz +0 -0
  199. package/payload/server/public/assets/play-DgP7vl0r.js +1 -0
  200. package/payload/server/public/assets/play-DgP7vl0r.js.br +0 -0
  201. package/payload/server/public/assets/play-DgP7vl0r.js.gz +0 -0
  202. package/payload/server/public/assets/{public-D_qjqet7.js → public-BvUQR3hd.js} +1 -1
  203. package/payload/server/public/assets/public-BvUQR3hd.js.br +0 -0
  204. package/payload/server/public/assets/public-BvUQR3hd.js.gz +0 -0
  205. package/payload/server/public/assets/{rotate-ccw-Ixkq1iUA.js → rotate-ccw-Dnn7gbPh.js} +1 -1
  206. package/payload/server/public/assets/rotate-ccw-Dnn7gbPh.js.br +0 -0
  207. package/payload/server/public/assets/rotate-ccw-Dnn7gbPh.js.gz +0 -0
  208. package/payload/server/public/assets/{routines-Cxjlmcp7.js → routines-N8NmxPOZ.js} +1 -1
  209. package/payload/server/public/assets/routines-N8NmxPOZ.js.br +0 -0
  210. package/payload/server/public/assets/routines-N8NmxPOZ.js.gz +0 -0
  211. package/payload/server/public/assets/{skills-DEo9hSVX.js → skills-B2CRgO-U.js} +1 -1
  212. package/payload/server/public/assets/skills-B2CRgO-U.js.br +0 -0
  213. package/payload/server/public/assets/skills-B2CRgO-U.js.gz +0 -0
  214. package/payload/server/public/assets/{tasks-BxApOIXI.js → tasks-8QLGtlSj.js} +1 -1
  215. package/payload/server/public/assets/tasks-8QLGtlSj.js.br +0 -0
  216. package/payload/server/public/assets/tasks-8QLGtlSj.js.gz +0 -0
  217. package/payload/server/public/assets/{triangle-alert-BYzMcXv8.js → triangle-alert-el772yUQ.js} +1 -1
  218. package/payload/server/public/assets/triangle-alert-el772yUQ.js.br +0 -0
  219. package/payload/server/public/assets/triangle-alert-el772yUQ.js.gz +0 -0
  220. package/payload/server/public/assets/{useCopyFeedback-Bk2HVcjy.js → useCopyFeedback-DfNAu8hQ.js} +1 -1
  221. package/payload/server/public/assets/useCopyFeedback-DfNAu8hQ.js.br +0 -0
  222. package/payload/server/public/assets/useCopyFeedback-DfNAu8hQ.js.gz +0 -0
  223. package/payload/server/public/assets/{useMediaQuery-DEti-vAx.js → useMediaQuery-B469O83-.js} +1 -1
  224. package/payload/server/public/assets/useMediaQuery-B469O83-.js.br +0 -0
  225. package/payload/server/public/assets/{useMediaQuery-DEti-vAx.js.gz → useMediaQuery-B469O83-.js.gz} +0 -0
  226. package/payload/server/public/assets/{useVoiceRecorder-kQ92rL-f.js → useVoiceRecorder-Bb-51cJv.js} +1 -1
  227. package/payload/server/public/assets/useVoiceRecorder-Bb-51cJv.js.br +0 -0
  228. package/payload/server/public/assets/useVoiceRecorder-Bb-51cJv.js.gz +0 -0
  229. package/payload/server/public/assets/{wrench-BhLkHffv.js → wrench-Ck4phkdB.js} +1 -1
  230. package/payload/server/public/assets/wrench-Ck4phkdB.js.br +0 -0
  231. package/payload/server/public/assets/wrench-Ck4phkdB.js.gz +0 -0
  232. package/payload/server/public/browser.html +5 -5
  233. package/payload/server/public/calendar.html +8 -8
  234. package/payload/server/public/chat.html +14 -14
  235. package/payload/server/public/data.html +12 -12
  236. package/payload/server/public/field.html +3 -3
  237. package/payload/server/public/graph.html +10 -10
  238. package/payload/server/public/index.html +14 -14
  239. package/payload/server/public/operator.html +16 -16
  240. package/payload/server/public/public.html +14 -14
  241. package/payload/server/public/routines.html +8 -8
  242. package/payload/server/public/skills.html +6 -6
  243. package/payload/server/public/tasks.html +7 -7
  244. package/payload/server/server.js +3020 -1267
  245. package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.br +0 -0
  246. package/payload/server/public/assets/AdminLoginScreens-AnJZfgFz.js.gz +0 -0
  247. package/payload/server/public/assets/AdminShell-CEVqyQqg.js.br +0 -0
  248. package/payload/server/public/assets/AdminShell-CEVqyQqg.js.gz +0 -0
  249. package/payload/server/public/assets/activity-czUaUl8I.js.br +0 -0
  250. package/payload/server/public/assets/activity-czUaUl8I.js.gz +0 -0
  251. package/payload/server/public/assets/admin-B0iRcfuk.js.br +0 -0
  252. package/payload/server/public/assets/admin-B0iRcfuk.js.gz +0 -0
  253. package/payload/server/public/assets/agents-BocONCeQ.js.br +0 -0
  254. package/payload/server/public/assets/agents-BocONCeQ.js.gz +0 -0
  255. package/payload/server/public/assets/browser-Cy-rJm3t.js.br +0 -0
  256. package/payload/server/public/assets/browser-Cy-rJm3t.js.gz +0 -0
  257. package/payload/server/public/assets/calendar-DUTKxWtd.js.br +0 -0
  258. package/payload/server/public/assets/calendar-DUTKxWtd.js.gz +0 -0
  259. package/payload/server/public/assets/chat-Cis7sHsZ.js +0 -1
  260. package/payload/server/public/assets/chat-Cis7sHsZ.js.br +0 -0
  261. package/payload/server/public/assets/chat-Cis7sHsZ.js.gz +0 -0
  262. package/payload/server/public/assets/chevron-left-Bhhx8j2r.js +0 -1
  263. package/payload/server/public/assets/chevron-left-Bhhx8j2r.js.br +0 -0
  264. package/payload/server/public/assets/clock-Ca3nx7Cc.js +0 -1
  265. package/payload/server/public/assets/clock-Ca3nx7Cc.js.br +0 -0
  266. package/payload/server/public/assets/clock-Ca3nx7Cc.js.gz +0 -0
  267. package/payload/server/public/assets/copy-DE9qmAs5.js.br +0 -0
  268. package/payload/server/public/assets/copy-DE9qmAs5.js.gz +0 -0
  269. package/payload/server/public/assets/data-CkaAEeP1.js +0 -1
  270. package/payload/server/public/assets/data-CkaAEeP1.js.br +0 -0
  271. package/payload/server/public/assets/data-CkaAEeP1.js.gz +0 -0
  272. package/payload/server/public/assets/field-DE2tEfTo.js.br +0 -0
  273. package/payload/server/public/assets/field-DE2tEfTo.js.gz +0 -0
  274. package/payload/server/public/assets/file-text-BHfL2gqo.js.br +0 -0
  275. package/payload/server/public/assets/file-text-BHfL2gqo.js.gz +0 -0
  276. package/payload/server/public/assets/graph-08S_BofN.js.br +0 -0
  277. package/payload/server/public/assets/graph-08S_BofN.js.gz +0 -0
  278. package/payload/server/public/assets/graph-labels-C640Er5R.js.br +0 -0
  279. package/payload/server/public/assets/graph-labels-C640Er5R.js.gz +0 -0
  280. package/payload/server/public/assets/jsx-runtime-FxjwOUbA.css.br +0 -0
  281. package/payload/server/public/assets/operator-DH5Kw3fL.js.br +0 -0
  282. package/payload/server/public/assets/operator-DH5Kw3fL.js.gz +0 -0
  283. package/payload/server/public/assets/page-BDpa4HWT.js.br +0 -0
  284. package/payload/server/public/assets/page-BDpa4HWT.js.gz +0 -0
  285. package/payload/server/public/assets/page-yn7ivq8O.js.br +0 -0
  286. package/payload/server/public/assets/page-yn7ivq8O.js.gz +0 -0
  287. package/payload/server/public/assets/play-CMN7ht-S.js +0 -1
  288. package/payload/server/public/assets/play-CMN7ht-S.js.br +0 -0
  289. package/payload/server/public/assets/play-CMN7ht-S.js.gz +0 -0
  290. package/payload/server/public/assets/public-D_qjqet7.js.br +0 -0
  291. package/payload/server/public/assets/public-D_qjqet7.js.gz +0 -0
  292. package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.br +0 -0
  293. package/payload/server/public/assets/rotate-ccw-Ixkq1iUA.js.gz +0 -0
  294. package/payload/server/public/assets/routines-Cxjlmcp7.js.br +0 -0
  295. package/payload/server/public/assets/routines-Cxjlmcp7.js.gz +0 -0
  296. package/payload/server/public/assets/skills-DEo9hSVX.js.br +0 -0
  297. package/payload/server/public/assets/skills-DEo9hSVX.js.gz +0 -0
  298. package/payload/server/public/assets/tasks-BxApOIXI.js.br +0 -0
  299. package/payload/server/public/assets/tasks-BxApOIXI.js.gz +0 -0
  300. package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.br +0 -0
  301. package/payload/server/public/assets/triangle-alert-BYzMcXv8.js.gz +0 -0
  302. package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.br +0 -0
  303. package/payload/server/public/assets/useCopyFeedback-Bk2HVcjy.js.gz +0 -0
  304. package/payload/server/public/assets/useMediaQuery-DEti-vAx.js.br +0 -0
  305. package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.br +0 -0
  306. package/payload/server/public/assets/useVoiceRecorder-kQ92rL-f.js.gz +0 -0
  307. package/payload/server/public/assets/wrench-BhLkHffv.js.br +0 -0
  308. package/payload/server/public/assets/wrench-BhLkHffv.js.gz +0 -0
  309. /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js → jsx-runtime-BmNepPEn.js} +0 -0
  310. /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js.br → jsx-runtime-BmNepPEn.js.br} +0 -0
  311. /package/payload/server/public/assets/{jsx-runtime-BL3pljPe.js.gz → jsx-runtime-BmNepPEn.js.gz} +0 -0
@@ -0,0 +1,529 @@
1
+ /**
2
+ * Task 2618 — Telegram's managed-bot surface, and the webhook registration that
3
+ * a newly provisioned bot needs.
4
+ *
5
+ * This is a shared lib rather than plugin code because BOTH sides need it. The
6
+ * `managed_bot` update lands on the UI webhook route, which must fetch the new
7
+ * bot's token, persist it and register its webhook; the `telegram-bot-provision`
8
+ * MCP tool needs the same four Bot API calls for its own actions. UI production
9
+ * code does not import plugin source, so the alternative was a second copy of
10
+ * `TELEGRAM_ALLOWED_UPDATES` on the UI side — and two lists that must agree, of
11
+ * a set where naming any class replaces Telegram's default, is exactly the drift
12
+ * Task 2612 spent a task fixing.
13
+ *
14
+ * `TELEGRAM_ALLOWED_UPDATES` and `setTelegramWebhook` MOVED here from
15
+ * `plugins/telegram/mcp/src/lib/telegram.ts`, which now re-exports both so every
16
+ * existing plugin import keeps its path.
17
+ *
18
+ * Every name below is quoted from core.telegram.org/bots/api, read 2026-08-09.
19
+ */
20
+
21
+ import { randomBytes } from "node:crypto";
22
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
23
+ import { dirname, join } from "node:path";
24
+
25
+ const TELEGRAM_API = "https://api.telegram.org";
26
+
27
+ /**
28
+ * Task 2612 — the update classes this install asks Telegram for.
29
+ *
30
+ * The trap: naming ANY class replaces Telegram's default set, so a class that
31
+ * works today and is missing from this list stops arriving with no error
32
+ * anywhere. Telegram's default is every class except `chat_member`,
33
+ * `message_reaction` and `message_reaction_count`.
34
+ *
35
+ * Task 2618 — `managed_bot` is fifteenth. It IS in Telegram's default set, so it
36
+ * arrives on an install that names nothing; but this install names its list, so
37
+ * omitting it here is the difference between a tapped provisioning button that
38
+ * works and one that silently does nothing.
39
+ *
40
+ * The order is the one Task 2612 states, and the registration test asserts the
41
+ * whole array rather than membership — the failure guarded against is a name
42
+ * silently dropped, which a membership check cannot see.
43
+ */
44
+ export const TELEGRAM_ALLOWED_UPDATES: readonly string[] = [
45
+ "message",
46
+ "edited_message",
47
+ "callback_query",
48
+ "poll_answer",
49
+ "poll",
50
+ "message_reaction",
51
+ "pre_checkout_query",
52
+ "shipping_query",
53
+ "subscription",
54
+ "my_chat_member",
55
+ "business_connection",
56
+ "business_message",
57
+ "edited_business_message",
58
+ "deleted_business_messages",
59
+ "managed_bot",
60
+ // Task 2616 — the group set. `chat_member` (another member's membership
61
+ // changed, as against `my_chat_member` for the bot's own) and
62
+ // `message_reaction_count` (the anonymous tally that pairs with
63
+ // `message_reaction`) are OUTSIDE Telegram's default, so neither arrives until
64
+ // a bot re-registers. `chat_join_request` and the two boost classes are inside
65
+ // it and are named so that making the list explicit is not itself a regression.
66
+ "chat_member",
67
+ "chat_join_request",
68
+ "message_reaction_count",
69
+ "chat_boost",
70
+ "removed_chat_boost",
71
+ // Task 2617 — the channel pair. Both are INSIDE Telegram's default set, so they
72
+ // would arrive on an install that names nothing; this install names its own
73
+ // list, so they are named here for the reason `chat_join_request` and the two
74
+ // boost classes are — making the list explicit must not itself be a regression.
75
+ //
76
+ // Without them a channel post never reaches the route at all, and the failure is
77
+ // silent: Telegram reports no error for a class it was not asked for.
78
+ "channel_post",
79
+ "edited_channel_post",
80
+ ];
81
+
82
+ /**
83
+ * Register a webhook URL with Telegram and generate a cryptographic secret.
84
+ *
85
+ * The secret is generated via crypto.randomBytes (64 hex chars), passed as
86
+ * `secret_token`, and written to disk ONLY after the API confirms success. That
87
+ * order prevents orphaned secret files when the call fails, and avoids
88
+ * overwriting a working secret during a failed re-registration.
89
+ *
90
+ * Telegram sends the secret in the X-Telegram-Bot-Api-Secret-Token header on
91
+ * every delivery; the webhook route validates it before processing.
92
+ *
93
+ * If secretFilePath is omitted, no secret is generated.
94
+ */
95
+ export async function setTelegramWebhook(
96
+ botToken: string,
97
+ webhookUrl: string,
98
+ secretFilePath?: string,
99
+ fetchImpl: typeof fetch = fetch,
100
+ ): Promise<{ ok: boolean; secret?: string; error?: string }> {
101
+ const secret = secretFilePath ? randomBytes(32).toString("hex") : undefined;
102
+
103
+ // Task 2612 — `allowed_updates` travels on every registration.
104
+ const body: Record<string, unknown> = {
105
+ url: webhookUrl,
106
+ allowed_updates: [...TELEGRAM_ALLOWED_UPDATES],
107
+ };
108
+ if (secret) {
109
+ body.secret_token = secret;
110
+ }
111
+
112
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/setWebhook`, {
113
+ method: "POST",
114
+ headers: { "Content-Type": "application/json" },
115
+ body: JSON.stringify(body),
116
+ });
117
+
118
+ const data = (await res.json()) as { ok: boolean; description?: string };
119
+
120
+ if (!data.ok) {
121
+ return { ok: false, error: data.description ?? "Failed to set webhook" };
122
+ }
123
+
124
+ if (secret && secretFilePath) {
125
+ mkdirSync(dirname(secretFilePath), { recursive: true });
126
+ writeFileSync(secretFilePath, secret, { mode: 0o600 });
127
+ console.error(
128
+ `[telegram] webhook secret written to ${secretFilePath} (${secret.length} chars)`,
129
+ );
130
+ }
131
+
132
+ return { ok: true, secret };
133
+ }
134
+
135
+ /** One entry in an account's `telegram.bots[]`.
136
+ *
137
+ * Task 2618 adds the third role: a bot this install provisioned that nothing has
138
+ * bound to an agent yet. It carries no `agent` and no `adminUsers` because
139
+ * neither has an answer between the tap that created it and Task 2619's binding. */
140
+ export type TelegramBotEntryWrite =
141
+ | { id: string; token: string; role: "admin"; adminUsers?: number[]; managed?: boolean }
142
+ | {
143
+ id: string;
144
+ token: string;
145
+ role: "public";
146
+ agent: string;
147
+ dmPolicy?: "open" | "allowlist" | "disabled";
148
+ allowFrom?: number[];
149
+ managed?: boolean;
150
+ }
151
+ | { id: string; token: string; role: "unbound"; managed?: boolean }
152
+ // Task 2619 — a bot bound to one of the account's own specialist cards.
153
+ // `specialist` is a card FILE BASENAME, the only value `--agent` resolves.
154
+ // There is no `dmPolicy`: a specialist session carries the card's own tool
155
+ // surface rather than the zero-tool public ringfence, so an explicit list of
156
+ // ids is the only gate that holds, and the register tool requires a non-empty
157
+ // `allowFrom` even though the shape permits its absence (an absent list admits
158
+ // nobody, which is the safe reading for a config already on disk).
159
+ | {
160
+ id: string;
161
+ token: string;
162
+ role: "specialist";
163
+ specialist: string;
164
+ allowFrom?: number[];
165
+ managed?: boolean;
166
+ };
167
+
168
+ /** Merge one bot entry into an account's `telegram.bots[]`, replacing any entry
169
+ * with the same id and leaving every other entry and every other key
170
+ * byte-identical.
171
+ *
172
+ * THE single writer of that array. Two callers reach it: the plugin's
173
+ * `persistTelegramBot`, which adds the brand-containment assertion it needs
174
+ * because it resolves this directory from an untrusted `accountId`; and the UI
175
+ * webhook route, which already holds a directory that came from the platform's
176
+ * own account enumeration. A bot registry with two writers is one that
177
+ * eventually disagrees with itself about who is registered.
178
+ *
179
+ * Task 2368's invariant is carried unchanged: registering, re-pointing or
180
+ * rotating one bot cannot disturb another. */
181
+ export function writeTelegramBotEntry(
182
+ accountDir: string,
183
+ entry: TelegramBotEntryWrite,
184
+ ): { ok: boolean; error?: string } {
185
+ const configPath = join(accountDir, "account.json");
186
+ if (!existsSync(configPath)) {
187
+ return { ok: false, error: `account.json not found at ${configPath}` };
188
+ }
189
+ let config: Record<string, unknown>;
190
+ try {
191
+ config = JSON.parse(readFileSync(configPath, "utf-8"));
192
+ } catch (e) {
193
+ // Refuse rather than overwrite. A file that will not parse holds state this
194
+ // merge cannot preserve, and replacing it would drop every other bot.
195
+ return { ok: false, error: `account.json parse failed: ${(e as Error).message}` };
196
+ }
197
+ const telegram = (config.telegram as Record<string, unknown>) ?? {};
198
+ const bots = Array.isArray(telegram.bots) ? (telegram.bots as TelegramBotEntryWrite[]) : [];
199
+ const at = bots.findIndex((b) => b && typeof b === "object" && b.id === entry.id);
200
+ if (at >= 0) bots[at] = entry;
201
+ else bots.push(entry);
202
+ telegram.bots = bots;
203
+ config.telegram = telegram;
204
+ writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
205
+ return { ok: true };
206
+ }
207
+
208
+ export interface WebhookInfo {
209
+ url: string;
210
+ pendingUpdateCount: number;
211
+ /** Unix seconds of the most recent delivery error Telegram saw, 0 when it
212
+ * reports none. Telegram KEEPS this across a re-registration, which is why
213
+ * the verdict compares it against the moment we registered. */
214
+ lastErrorDate: number;
215
+ lastErrorMessage?: string;
216
+ /** Task 2612 — the classes Telegram says it will deliver. Telegram omits the
217
+ * field entirely when the registration uses its default set, which reads here
218
+ * as `[]` — and `[]` is exactly the pre-2612 state, so an empty list against a
219
+ * non-empty request is a real mismatch and not a missing field. */
220
+ allowedUpdates: string[];
221
+ }
222
+
223
+ /** What Telegram says it is currently delivering for this bot.
224
+ *
225
+ * Task 2618 — MOVED here from the plugin, which re-exports it. Three callers
226
+ * now: the plugin's registration probe, the tool's rotate action reading the URL
227
+ * to restore, and the UI's hourly managed-bot census. */
228
+ export async function getTelegramWebhookInfo(
229
+ botToken: string,
230
+ fetchImpl: typeof fetch = fetch,
231
+ ): Promise<WebhookInfo> {
232
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/getWebhookInfo`);
233
+ const data = (await res.json()) as {
234
+ result: {
235
+ url: string;
236
+ pending_update_count: number;
237
+ last_error_date?: number;
238
+ last_error_message?: string;
239
+ allowed_updates?: string[];
240
+ };
241
+ };
242
+ return {
243
+ url: data.result.url,
244
+ pendingUpdateCount: data.result.pending_update_count,
245
+ lastErrorDate: data.result.last_error_date ?? 0,
246
+ allowedUpdates: data.result.allowed_updates ?? [],
247
+ ...(data.result.last_error_message !== undefined
248
+ ? { lastErrorMessage: data.result.last_error_message }
249
+ : {}),
250
+ };
251
+ }
252
+
253
+ /** One stored entry, whole, or null when the account holds no bot with that id.
254
+ *
255
+ * The point is "whole". `listRegisteredBots` returns `{ id, role, agent }` for
256
+ * the callers that only need to name a bot, and a rotation that spread THAT into
257
+ * the writer would silently drop `adminUsers`, `dmPolicy`, `allowFrom` and
258
+ * `managed` — replacing a token would quietly un-admit every admin on the bot.
259
+ * A rotation changes one field and must carry the rest through untouched. */
260
+ export function readTelegramBotEntry(
261
+ accountDir: string,
262
+ botId: string,
263
+ ): TelegramBotEntryWrite | null {
264
+ const configPath = join(accountDir, "account.json");
265
+ if (!existsSync(configPath)) return null;
266
+ try {
267
+ const config = JSON.parse(readFileSync(configPath, "utf-8")) as {
268
+ telegram?: { bots?: TelegramBotEntryWrite[] };
269
+ };
270
+ const bots = config.telegram?.bots;
271
+ if (!Array.isArray(bots)) return null;
272
+ return bots.find((b) => b && typeof b === "object" && b.id === botId) ?? null;
273
+ } catch {
274
+ return null;
275
+ }
276
+ }
277
+
278
+ /** Bot API `User`, as far as the managed-bot payloads use it. */
279
+ export interface TelegramManagedUser {
280
+ id?: number;
281
+ is_bot?: boolean;
282
+ first_name?: string;
283
+ username?: string;
284
+ /** "True, if other bots can be created to be controlled by the bot. Returned
285
+ * only in getMe." The managing bot needs this before a request button can be
286
+ * offered; it is switched on in the @BotFather Mini App, not by any API call. */
287
+ can_manage_bots?: boolean;
288
+ }
289
+
290
+ /** Bot API `ManagedBotUpdated`, the `managed_bot` update's payload.
291
+ *
292
+ * Note what is NOT here: no event discriminator and no `request_id`. Telegram
293
+ * documents the update as "A new bot was created to be managed by the bot, or
294
+ * token or owner of a managed bot was changed" and sends the same two fields
295
+ * for all three. Which of them happened is derivable only from local state. */
296
+ export interface ManagedBotUpdated {
297
+ /** The user who created the bot. */
298
+ user?: TelegramManagedUser;
299
+ /** The bot itself. Its `id` is the value every managed-bot method keys on. */
300
+ bot?: TelegramManagedUser;
301
+ }
302
+
303
+ /** Bot API `ManagedBotCreated`, the `managed_bot_created` service message field. */
304
+ export interface ManagedBotCreated {
305
+ bot?: TelegramManagedUser;
306
+ }
307
+
308
+ /** Bot API `KeyboardButtonRequestManagedBot`. `request_id` is a signed 32-bit
309
+ * integer, unique within the message. */
310
+ export interface KeyboardButtonRequestManagedBot {
311
+ request_id: number;
312
+ suggested_name?: string;
313
+ suggested_username?: string;
314
+ }
315
+
316
+ /** The largest `request_id` Telegram accepts: it is a signed 32-bit integer. */
317
+ export const MANAGED_BOT_REQUEST_ID_MAX = 2_147_483_647;
318
+
319
+ /** Bot API `ReplyKeyboardMarkup`, narrowed to the fields this install sends.
320
+ *
321
+ * "Not supported in channels and for messages sent on behalf of a business
322
+ * account", and `request_managed_bot` is further "Available in private chats
323
+ * only" — both are the caller's constraint, not this type's. */
324
+ export interface TelegramReplyKeyboard {
325
+ keyboard: Array<Array<{ text: string; request_managed_bot?: KeyboardButtonRequestManagedBot }>>;
326
+ one_time_keyboard?: boolean;
327
+ resize_keyboard?: boolean;
328
+ is_persistent?: boolean;
329
+ input_field_placeholder?: string;
330
+ }
331
+
332
+ /** The one reply keyboard this install builds: a single button asking the user to
333
+ * create a bot for this bot to manage.
334
+ *
335
+ * Shared because both doors send it — the MCP tool's `offer` action and, if a
336
+ * later caller needs it, the UI's own outbound. One builder, because a button
337
+ * whose `request_id` is built two ways is a button whose answer cannot be
338
+ * correlated.
339
+ *
340
+ * One button in one row: `request_id` must be "unique within the message" and
341
+ * nothing here has a reason to send two at once. `one_time_keyboard` so it
342
+ * disappears once tapped rather than sitting in the operator's chat. */
343
+ export function buildManagedBotRequestKeyboard(input: {
344
+ text: string;
345
+ requestId: number;
346
+ suggestedName?: string;
347
+ suggestedUsername?: string;
348
+ }): TelegramReplyKeyboard {
349
+ if (
350
+ !Number.isInteger(input.requestId) ||
351
+ input.requestId < 1 ||
352
+ input.requestId > MANAGED_BOT_REQUEST_ID_MAX
353
+ ) {
354
+ // Refused here, not by Telegram: Telegram rejects the send with a description
355
+ // that never names the field, so the operator would see a failed offer and no
356
+ // reason for it.
357
+ throw new Error(
358
+ `request_id ${input.requestId} is not a positive signed 32-bit integer, which is what Telegram requires`,
359
+ );
360
+ }
361
+ return {
362
+ keyboard: [
363
+ [
364
+ {
365
+ text: input.text,
366
+ request_managed_bot: {
367
+ request_id: input.requestId,
368
+ ...(input.suggestedName !== undefined ? { suggested_name: input.suggestedName } : {}),
369
+ ...(input.suggestedUsername !== undefined
370
+ ? { suggested_username: input.suggestedUsername }
371
+ : {}),
372
+ },
373
+ },
374
+ ],
375
+ ],
376
+ one_time_keyboard: true,
377
+ resize_keyboard: true,
378
+ };
379
+ }
380
+
381
+ /** Telegram caps `added_user_ids` at ten. Refused locally so the cap is named,
382
+ * rather than surfacing as an opaque API error with no number in it. */
383
+ export const MAX_ADDED_USER_IDS = 10;
384
+
385
+ /** The four Bot API methods this module owns, keyed by the action that uses each.
386
+ * Read by the plugin's `OWNED_METHODS` census and by its `api-guard` refusals,
387
+ * so neither can disagree with this module about what it covers. */
388
+ export const MANAGED_BOT_METHODS = Object.freeze({
389
+ token: "getManagedBotToken",
390
+ rotate: "replaceManagedBotToken",
391
+ accessRead: "getManagedBotAccessSettings",
392
+ accessWrite: "setManagedBotAccessSettings",
393
+ } as const);
394
+
395
+ async function call(
396
+ botToken: string,
397
+ method: string,
398
+ body: Record<string, unknown>,
399
+ fetchImpl: typeof fetch,
400
+ ): Promise<{ ok: true; result: unknown } | { ok: false; error: string }> {
401
+ try {
402
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/${method}`, {
403
+ method: "POST",
404
+ headers: { "Content-Type": "application/json" },
405
+ body: JSON.stringify(body),
406
+ });
407
+ const data = (await res.json()) as { ok: boolean; result?: unknown; description?: string };
408
+ if (!data.ok) return { ok: false, error: data.description ?? `${method} failed` };
409
+ return { ok: true, result: data.result };
410
+ } catch (e) {
411
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
412
+ }
413
+ }
414
+
415
+ /** "Use this method to get the token of a managed bot. Returns the token as
416
+ * String on success." `userId` is the MANAGED bot's own user id, which is also
417
+ * the numeric prefix of the token this returns.
418
+ *
419
+ * The token is a credential. Every caller persists it and nothing logs it. */
420
+ export async function getManagedBotToken(
421
+ managingBotToken: string,
422
+ userId: number,
423
+ fetchImpl: typeof fetch = fetch,
424
+ ): Promise<{ ok: true; token: string } | { ok: false; error: string }> {
425
+ const r = await call(managingBotToken, MANAGED_BOT_METHODS.token, { user_id: userId }, fetchImpl);
426
+ if (!r.ok) return r;
427
+ return typeof r.result === "string"
428
+ ? { ok: true, token: r.result }
429
+ : { ok: false, error: "no-token-in-result" };
430
+ }
431
+
432
+ /** "Use this method to revoke the current token of a managed bot and generate a
433
+ * new one. Returns the new token as String on success."
434
+ *
435
+ * The bot's user id is unchanged by a rotation, so the new token has the same
436
+ * numeric prefix and the bot keys on nothing new. */
437
+ export async function replaceManagedBotToken(
438
+ managingBotToken: string,
439
+ userId: number,
440
+ fetchImpl: typeof fetch = fetch,
441
+ ): Promise<{ ok: true; token: string } | { ok: false; error: string }> {
442
+ const r = await call(managingBotToken, MANAGED_BOT_METHODS.rotate, { user_id: userId }, fetchImpl);
443
+ if (!r.ok) return r;
444
+ return typeof r.result === "string"
445
+ ? { ok: true, token: r.result }
446
+ : { ok: false, error: "no-token-in-result" };
447
+ }
448
+
449
+ export interface ManagedBotAccess {
450
+ isAccessRestricted: boolean;
451
+ /** The ids of users admitted BESIDES the owner, who "can always access it".
452
+ * An absent `added_users` reads as nobody, which is what Telegram means by
453
+ * omitting it, and not as unknown. */
454
+ addedUserIds: number[];
455
+ }
456
+
457
+ /** "Returns a BotAccessSettings object on success." */
458
+ export async function getManagedBotAccessSettings(
459
+ managingBotToken: string,
460
+ userId: number,
461
+ fetchImpl: typeof fetch = fetch,
462
+ ): Promise<({ ok: true } & ManagedBotAccess) | { ok: false; error: string }> {
463
+ const r = await call(
464
+ managingBotToken,
465
+ MANAGED_BOT_METHODS.accessRead,
466
+ { user_id: userId },
467
+ fetchImpl,
468
+ );
469
+ if (!r.ok) return r;
470
+ const s = r.result as { is_access_restricted?: boolean; added_users?: TelegramManagedUser[] };
471
+ return {
472
+ ok: true,
473
+ isAccessRestricted: s?.is_access_restricted === true,
474
+ addedUserIds: (s?.added_users ?? [])
475
+ .map((u) => u?.id)
476
+ .filter((id): id is number => typeof id === "number"),
477
+ };
478
+ }
479
+
480
+ /** "Use this method to change the access settings of a managed bot. Returns True
481
+ * on success." `addedUserIds` is "Ignored if is_access_restricted is False",
482
+ * which is Telegram's behaviour and is not re-implemented here. */
483
+ export async function setManagedBotAccessSettings(
484
+ managingBotToken: string,
485
+ userId: number,
486
+ isAccessRestricted: boolean,
487
+ addedUserIds: number[] | undefined,
488
+ fetchImpl: typeof fetch = fetch,
489
+ ): Promise<{ ok: boolean; error?: string }> {
490
+ if (addedUserIds && addedUserIds.length > MAX_ADDED_USER_IDS) {
491
+ return { ok: false, error: "too-many-added-user-ids" };
492
+ }
493
+ const r = await call(
494
+ managingBotToken,
495
+ MANAGED_BOT_METHODS.accessWrite,
496
+ {
497
+ user_id: userId,
498
+ is_access_restricted: isAccessRestricted,
499
+ ...(addedUserIds ? { added_user_ids: addedUserIds } : {}),
500
+ },
501
+ fetchImpl,
502
+ );
503
+ return r.ok ? { ok: true } : { ok: false, error: r.error };
504
+ }
505
+
506
+ /** The managing bot's own `getMe`, for the one field that gates provisioning.
507
+ * A bot without `can_manage_bots` cannot offer the request button at all, and
508
+ * the switch lives in the @BotFather Mini App rather than behind any API call. */
509
+ export async function botCanManageBots(
510
+ botToken: string,
511
+ fetchImpl: typeof fetch = fetch,
512
+ ): Promise<{ ok: true; canManageBots: boolean; username: string } | { ok: false; error: string }> {
513
+ try {
514
+ const res = await fetchImpl(`${TELEGRAM_API}/bot${botToken}/getMe`);
515
+ const data = (await res.json()) as {
516
+ ok: boolean;
517
+ result?: TelegramManagedUser;
518
+ description?: string;
519
+ };
520
+ if (!data.ok) return { ok: false, error: data.description ?? "getMe failed" };
521
+ return {
522
+ ok: true,
523
+ canManageBots: data.result?.can_manage_bots === true,
524
+ username: data.result?.username ?? "unknown",
525
+ };
526
+ } catch (e) {
527
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
528
+ }
529
+ }
@@ -0,0 +1,9 @@
1
+ {
2
+ "extends": "../../tsconfig.base.json",
3
+ "compilerOptions": {
4
+ "outDir": "dist",
5
+ "rootDir": "src"
6
+ },
7
+ "include": ["src"],
8
+ "exclude": ["src/__tests__"]
9
+ }
@@ -0,0 +1,9 @@
1
+ import { defineConfig } from "vitest/config";
2
+
3
+ export default defineConfig({
4
+ test: {
5
+ environment: "node",
6
+ globals: false,
7
+ include: ["src/__tests__/**/*.test.ts"],
8
+ },
9
+ });
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Is this a channel key that can safely become a path segment?
3
+ *
4
+ * Both halves of an admin key are Telegram numeric ids (a bot id is positive; a
5
+ * chat or sender id is an integer and is negative for groups), so requiring
6
+ * exactly that is both the truthful shape check and a complete traversal guard:
7
+ * no `.`, `/` or `\` can survive it.
8
+ *
9
+ * Task 2615 — the business shape `<botId>:biz:<senderId>` joins the guard rather
10
+ * than bypassing it. Both alternatives are anchored and admit only digits, an
11
+ * optional leading minus and the literal `biz`, so neither can carry a `.`, `/`
12
+ * or `\` either.
13
+ */
14
+ export declare function isStorableChannelKey(channelKey: string): boolean;
15
+ /** Task 2615 — is this the three-part business shape?
16
+ *
17
+ * Its own predicate rather than a `.includes(':biz:')` at each caller: a
18
+ * business conversation is reached through the business-connection send route
19
+ * and its 24-hour window, not by addressing a chat id, so anything that answers
20
+ * "can we message this person now" has to be able to tell the two apart. */
21
+ export declare function isBusinessChannelKey(channelKey: string): boolean;
22
+ /** The filename stem for a channel key.
23
+ *
24
+ * Throws on a key that is neither two numeric ids nor the business shape.
25
+ * Refusing loudly is correct here: every caller reaches this with a key it has
26
+ * already validated, so an unstorable key means a caller skipped its guard, and
27
+ * silently sanitising it would build a path nobody asked for. */
28
+ export declare function channelKeyToFileStem(channelKey: string): string;
29
+ /** The inverse of `channelKeyToFileStem`.
30
+ *
31
+ * Task 2615 — a stem whose second segment is exactly `biz` is a three-part
32
+ * business key. Otherwise only the FIRST `_` is a separator, because both halves
33
+ * of an admin key are numeric Telegram ids and nothing later in that stem is
34
+ * one. */
35
+ export declare function fileStemToChannelKey(stem: string): string;
36
+ /** One registered bot, as much of it as reachability needs. `adminUsers` is
37
+ * present on an admin entry only; absent reads as none. */
38
+ export interface ReachBotInput {
39
+ id: string;
40
+ role: string;
41
+ adminUsers?: number[];
42
+ }
43
+ export interface ReachChat {
44
+ /** Null for an adminUsers entry that has never messaged: there is no chat. */
45
+ chatId: string | null;
46
+ senderId: string;
47
+ /** From the newest inbound row. Null when that row carried no name, and null
48
+ * for a sender who has never messaged. */
49
+ displayName: string | null;
50
+ /** ISO 8601 of the newest inbound row; null means never messaged. */
51
+ lastInbound: string | null;
52
+ /** This sender is an adminUsers entry on this bot. Allowlisted is a DIFFERENT
53
+ * fact from having started the chat, which is why both are carried. */
54
+ admin: boolean;
55
+ /** True exactly when `lastInbound` is non-null. */
56
+ reachable: boolean;
57
+ }
58
+ export interface ReachBot {
59
+ botId: string;
60
+ role: string;
61
+ adminUsers: number[];
62
+ /** Reachable chats newest first, then never-messaged admins in listed order. */
63
+ chats: ReachChat[];
64
+ }
65
+ export interface ReachResult {
66
+ bots: ReachBot[];
67
+ /** Message rows this call actually read, across EVERY conversation file in the
68
+ * directory — including files belonging to a bot the caller did not name.
69
+ * That is deliberate: `chats=0` with `storeRows>0` is then the signature of a
70
+ * wrong bot list or a wrong account scope, which is the failure this number
71
+ * exists to expose. */
72
+ storeRows: number;
73
+ }
74
+ /** Reachability for one account: every registered bot, the private chats that
75
+ * have messaged it, and the allowlisted people who have not. */
76
+ export declare function collectTelegramReach(input: {
77
+ storeDir: string;
78
+ bots: ReachBotInput[];
79
+ }): ReachResult;
80
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAwBA;;;;;;;;;;;;GAYG;AACH,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;6EAK6E;AAC7E,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAEhE;AAED;;;;;kEAKkE;AAClE,wBAAgB,oBAAoB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ/D;AAED;;;;;WAKW;AACX,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAQzD;AAED;4DAC4D;AAC5D,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED,MAAM,WAAW,SAAS;IACxB,8EAA8E;IAC9E,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB;+CAC2C;IAC3C,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,qEAAqE;IACrE,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B;4EACwE;IACxE,KAAK,EAAE,OAAO,CAAC;IACf,mDAAmD;IACnD,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,QAAQ;IACvB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,gFAAgF;IAChF,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,QAAQ,EAAE,CAAC;IACjB;;;;4BAIwB;IACxB,SAAS,EAAE,MAAM,CAAC;CACnB;AA+FD;iEACiE;AACjE,wBAAgB,oBAAoB,CAAC,KAAK,EAAE;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,aAAa,EAAE,CAAC;CACvB,GAAG,WAAW,CAsCd"}