@jungjaehoon/mama-os 0.77.0 → 0.79.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (338) hide show
  1. package/CHANGELOG.md +446 -0
  2. package/README.md +59 -30
  3. package/db/migrations/002-model-runs-parent-index.sql +11 -0
  4. package/db/migrations/003-upkeep-settled.sql +15 -0
  5. package/dist/api/google-read-actions.d.ts +30 -0
  6. package/dist/api/google-read-actions.d.ts.map +1 -0
  7. package/dist/api/google-read-actions.js +256 -0
  8. package/dist/api/google-read-actions.js.map +1 -0
  9. package/dist/api/record-actions.d.ts.map +1 -1
  10. package/dist/api/record-actions.js +23 -5
  11. package/dist/api/record-actions.js.map +1 -1
  12. package/dist/api/report-handler.d.ts +0 -6
  13. package/dist/api/report-handler.d.ts.map +1 -1
  14. package/dist/api/report-handler.js +0 -54
  15. package/dist/api/report-handler.js.map +1 -1
  16. package/dist/api/security-events.d.ts +1 -1
  17. package/dist/api/security-events.d.ts.map +1 -1
  18. package/dist/api/security-events.js +10 -1
  19. package/dist/api/security-events.js.map +1 -1
  20. package/dist/api/source-actions.d.ts +9 -0
  21. package/dist/api/source-actions.d.ts.map +1 -1
  22. package/dist/api/source-actions.js +49 -0
  23. package/dist/api/source-actions.js.map +1 -1
  24. package/dist/api/viewer-request-security.d.ts +14 -1
  25. package/dist/api/viewer-request-security.d.ts.map +1 -1
  26. package/dist/api/viewer-request-security.js +26 -3
  27. package/dist/api/viewer-request-security.js.map +1 -1
  28. package/dist/api/viewer-server.d.ts +8 -0
  29. package/dist/api/viewer-server.d.ts.map +1 -1
  30. package/dist/api/viewer-server.js +66 -0
  31. package/dist/api/viewer-server.js.map +1 -1
  32. package/dist/api/work-actions.d.ts +19 -2
  33. package/dist/api/work-actions.d.ts.map +1 -1
  34. package/dist/api/work-actions.js +42 -2
  35. package/dist/api/work-actions.js.map +1 -1
  36. package/dist/cli/commands/backfill.d.ts.map +1 -1
  37. package/dist/cli/commands/backfill.js +1 -0
  38. package/dist/cli/commands/backfill.js.map +1 -1
  39. package/dist/cli/commands/backup.d.ts +14 -0
  40. package/dist/cli/commands/backup.d.ts.map +1 -0
  41. package/dist/cli/commands/backup.js +174 -0
  42. package/dist/cli/commands/backup.js.map +1 -0
  43. package/dist/cli/commands/control.d.ts +44 -0
  44. package/dist/cli/commands/control.d.ts.map +1 -0
  45. package/dist/cli/commands/control.js +354 -0
  46. package/dist/cli/commands/control.js.map +1 -0
  47. package/dist/cli/commands/daemon.d.ts +60 -3
  48. package/dist/cli/commands/daemon.d.ts.map +1 -1
  49. package/dist/cli/commands/daemon.js +454 -78
  50. package/dist/cli/commands/daemon.js.map +1 -1
  51. package/dist/cli/commands/doctor.d.ts +13 -0
  52. package/dist/cli/commands/doctor.d.ts.map +1 -0
  53. package/dist/cli/commands/doctor.js +216 -0
  54. package/dist/cli/commands/doctor.js.map +1 -0
  55. package/dist/cli/commands/init-questions.d.ts +98 -0
  56. package/dist/cli/commands/init-questions.d.ts.map +1 -0
  57. package/dist/cli/commands/init-questions.js +420 -0
  58. package/dist/cli/commands/init-questions.js.map +1 -0
  59. package/dist/cli/commands/init-section.d.ts +13 -0
  60. package/dist/cli/commands/init-section.d.ts.map +1 -0
  61. package/dist/cli/commands/init-section.js +375 -0
  62. package/dist/cli/commands/init-section.js.map +1 -0
  63. package/dist/cli/commands/init.d.ts +56 -2
  64. package/dist/cli/commands/init.d.ts.map +1 -1
  65. package/dist/cli/commands/init.js +562 -350
  66. package/dist/cli/commands/init.js.map +1 -1
  67. package/dist/cli/commands/logs.d.ts +9 -0
  68. package/dist/cli/commands/logs.d.ts.map +1 -0
  69. package/dist/cli/commands/logs.js +109 -0
  70. package/dist/cli/commands/logs.js.map +1 -0
  71. package/dist/cli/commands/node-move.d.ts +20 -0
  72. package/dist/cli/commands/node-move.d.ts.map +1 -0
  73. package/dist/cli/commands/node-move.js +289 -0
  74. package/dist/cli/commands/node-move.js.map +1 -0
  75. package/dist/cli/commands/register-owner.d.ts +3 -0
  76. package/dist/cli/commands/register-owner.d.ts.map +1 -1
  77. package/dist/cli/commands/register-owner.js +14 -0
  78. package/dist/cli/commands/register-owner.js.map +1 -1
  79. package/dist/cli/commands/restore.d.ts +16 -0
  80. package/dist/cli/commands/restore.d.ts.map +1 -0
  81. package/dist/cli/commands/restore.js +137 -0
  82. package/dist/cli/commands/restore.js.map +1 -0
  83. package/dist/cli/commands/service.d.ts +44 -0
  84. package/dist/cli/commands/service.d.ts.map +1 -0
  85. package/dist/cli/commands/service.js +306 -0
  86. package/dist/cli/commands/service.js.map +1 -0
  87. package/dist/cli/commands/tools.d.ts +1 -1
  88. package/dist/cli/commands/tools.d.ts.map +1 -1
  89. package/dist/cli/commands/tools.js +8 -3
  90. package/dist/cli/commands/tools.js.map +1 -1
  91. package/dist/cli/commands/uninstall.d.ts +7 -0
  92. package/dist/cli/commands/uninstall.d.ts.map +1 -0
  93. package/dist/cli/commands/uninstall.js +335 -0
  94. package/dist/cli/commands/uninstall.js.map +1 -0
  95. package/dist/cli/commands/update.d.ts +59 -0
  96. package/dist/cli/commands/update.d.ts.map +1 -0
  97. package/dist/cli/commands/update.js +948 -0
  98. package/dist/cli/commands/update.js.map +1 -0
  99. package/dist/cli/index.js +74 -23
  100. package/dist/cli/index.js.map +1 -1
  101. package/dist/cli/launch-files.d.ts +119 -0
  102. package/dist/cli/launch-files.d.ts.map +1 -1
  103. package/dist/cli/launch-files.js +324 -15
  104. package/dist/cli/launch-files.js.map +1 -1
  105. package/dist/cli/prompt.d.ts +2 -1
  106. package/dist/cli/prompt.d.ts.map +1 -1
  107. package/dist/cli/prompt.js +5 -2
  108. package/dist/cli/prompt.js.map +1 -1
  109. package/dist/cli/runtime/backend-probe.d.ts +34 -0
  110. package/dist/cli/runtime/backend-probe.d.ts.map +1 -0
  111. package/dist/cli/runtime/backend-probe.js +83 -0
  112. package/dist/cli/runtime/backend-probe.js.map +1 -0
  113. package/dist/cli/runtime/backup.d.ts +130 -0
  114. package/dist/cli/runtime/backup.d.ts.map +1 -0
  115. package/dist/cli/runtime/backup.js +592 -0
  116. package/dist/cli/runtime/backup.js.map +1 -0
  117. package/dist/cli/runtime/daemon-control.d.ts +169 -0
  118. package/dist/cli/runtime/daemon-control.d.ts.map +1 -0
  119. package/dist/cli/runtime/daemon-control.js +441 -0
  120. package/dist/cli/runtime/daemon-control.js.map +1 -0
  121. package/dist/cli/runtime/default-roots.d.ts +27 -0
  122. package/dist/cli/runtime/default-roots.d.ts.map +1 -0
  123. package/dist/cli/runtime/default-roots.js +61 -0
  124. package/dist/cli/runtime/default-roots.js.map +1 -0
  125. package/dist/cli/runtime/diagnostics.d.ts +68 -0
  126. package/dist/cli/runtime/diagnostics.d.ts.map +1 -0
  127. package/dist/cli/runtime/diagnostics.js +1139 -0
  128. package/dist/cli/runtime/diagnostics.js.map +1 -0
  129. package/dist/cli/runtime/init-state.d.ts +20 -0
  130. package/dist/cli/runtime/init-state.d.ts.map +1 -0
  131. package/dist/cli/runtime/init-state.js +51 -0
  132. package/dist/cli/runtime/init-state.js.map +1 -0
  133. package/dist/cli/runtime/install-kind.d.ts +54 -0
  134. package/dist/cli/runtime/install-kind.d.ts.map +1 -0
  135. package/dist/cli/runtime/install-kind.js +164 -0
  136. package/dist/cli/runtime/install-kind.js.map +1 -0
  137. package/dist/cli/runtime/node-move-journal.d.ts +44 -0
  138. package/dist/cli/runtime/node-move-journal.d.ts.map +1 -0
  139. package/dist/cli/runtime/node-move-journal.js +48 -0
  140. package/dist/cli/runtime/node-move-journal.js.map +1 -0
  141. package/dist/cli/runtime/release-check.d.ts +94 -0
  142. package/dist/cli/runtime/release-check.d.ts.map +1 -0
  143. package/dist/cli/runtime/release-check.js +316 -0
  144. package/dist/cli/runtime/release-check.js.map +1 -0
  145. package/dist/cli/runtime/restore.d.ts +98 -0
  146. package/dist/cli/runtime/restore.d.ts.map +1 -0
  147. package/dist/cli/runtime/restore.js +756 -0
  148. package/dist/cli/runtime/restore.js.map +1 -0
  149. package/dist/cli/runtime/setup-page.d.ts +57 -0
  150. package/dist/cli/runtime/setup-page.d.ts.map +1 -0
  151. package/dist/cli/runtime/setup-page.js +266 -0
  152. package/dist/cli/runtime/setup-page.js.map +1 -0
  153. package/dist/cli/runtime/source-tools.d.ts +60 -0
  154. package/dist/cli/runtime/source-tools.d.ts.map +1 -0
  155. package/dist/cli/runtime/source-tools.js +170 -0
  156. package/dist/cli/runtime/source-tools.js.map +1 -0
  157. package/dist/cli/runtime/systemd-service.d.ts +13 -0
  158. package/dist/cli/runtime/systemd-service.d.ts.map +1 -0
  159. package/dist/cli/runtime/systemd-service.js +32 -0
  160. package/dist/cli/runtime/systemd-service.js.map +1 -0
  161. package/dist/cli/runtime/telegram-pairing.d.ts +73 -0
  162. package/dist/cli/runtime/telegram-pairing.d.ts.map +1 -0
  163. package/dist/cli/runtime/telegram-pairing.js +184 -0
  164. package/dist/cli/runtime/telegram-pairing.js.map +1 -0
  165. package/dist/cli/runtime/update-journal.d.ts +135 -0
  166. package/dist/cli/runtime/update-journal.d.ts.map +1 -0
  167. package/dist/cli/runtime/update-journal.js +144 -0
  168. package/dist/cli/runtime/update-journal.js.map +1 -0
  169. package/dist/cli/runtime/update-progress.d.ts +95 -0
  170. package/dist/cli/runtime/update-progress.d.ts.map +1 -0
  171. package/dist/cli/runtime/update-progress.js +154 -0
  172. package/dist/cli/runtime/update-progress.js.map +1 -0
  173. package/dist/cli/runtime/update-request.d.ts +58 -0
  174. package/dist/cli/runtime/update-request.d.ts.map +1 -0
  175. package/dist/cli/runtime/update-request.js +172 -0
  176. package/dist/cli/runtime/update-request.js.map +1 -0
  177. package/dist/connectors/calendar/index.d.ts +5 -1
  178. package/dist/connectors/calendar/index.d.ts.map +1 -1
  179. package/dist/connectors/calendar/index.js +19 -28
  180. package/dist/connectors/calendar/index.js.map +1 -1
  181. package/dist/connectors/chatwork/index.d.ts.map +1 -1
  182. package/dist/connectors/chatwork/index.js +7 -3
  183. package/dist/connectors/chatwork/index.js.map +1 -1
  184. package/dist/connectors/config-loader.d.ts +7 -1
  185. package/dist/connectors/config-loader.d.ts.map +1 -1
  186. package/dist/connectors/config-loader.js +11 -2
  187. package/dist/connectors/config-loader.js.map +1 -1
  188. package/dist/connectors/discord/index.d.ts.map +1 -1
  189. package/dist/connectors/discord/index.js +4 -3
  190. package/dist/connectors/discord/index.js.map +1 -1
  191. package/dist/connectors/drive/index.d.ts.map +1 -1
  192. package/dist/connectors/drive/index.js +15 -13
  193. package/dist/connectors/drive/index.js.map +1 -1
  194. package/dist/connectors/framework/gws-utils.d.ts +62 -0
  195. package/dist/connectors/framework/gws-utils.d.ts.map +1 -1
  196. package/dist/connectors/framework/gws-utils.js +133 -3
  197. package/dist/connectors/framework/gws-utils.js.map +1 -1
  198. package/dist/connectors/framework/polling-scheduler.d.ts.map +1 -1
  199. package/dist/connectors/framework/polling-scheduler.js +12 -11
  200. package/dist/connectors/framework/polling-scheduler.js.map +1 -1
  201. package/dist/connectors/framework/types.d.ts +24 -0
  202. package/dist/connectors/framework/types.d.ts.map +1 -1
  203. package/dist/connectors/framework/types.js +9 -0
  204. package/dist/connectors/framework/types.js.map +1 -1
  205. package/dist/connectors/gmail/index.d.ts.map +1 -1
  206. package/dist/connectors/gmail/index.js +14 -13
  207. package/dist/connectors/gmail/index.js.map +1 -1
  208. package/dist/connectors/imessage/index.d.ts.map +1 -1
  209. package/dist/connectors/imessage/index.js +2 -1
  210. package/dist/connectors/imessage/index.js.map +1 -1
  211. package/dist/connectors/index.d.ts +4 -4
  212. package/dist/connectors/index.d.ts.map +1 -1
  213. package/dist/connectors/index.js +4 -10
  214. package/dist/connectors/index.js.map +1 -1
  215. package/dist/connectors/notion/index.d.ts.map +1 -1
  216. package/dist/connectors/notion/index.js +5 -3
  217. package/dist/connectors/notion/index.js.map +1 -1
  218. package/dist/connectors/sheets/index.d.ts.map +1 -1
  219. package/dist/connectors/sheets/index.js +17 -12
  220. package/dist/connectors/sheets/index.js.map +1 -1
  221. package/dist/connectors/slack/index.d.ts.map +1 -1
  222. package/dist/connectors/slack/index.js +9 -3
  223. package/dist/connectors/slack/index.js.map +1 -1
  224. package/dist/connectors/telegram/index.d.ts.map +1 -1
  225. package/dist/connectors/telegram/index.js +4 -3
  226. package/dist/connectors/telegram/index.js.map +1 -1
  227. package/dist/connectors/trello/index.d.ts +1 -1
  228. package/dist/connectors/trello/index.d.ts.map +1 -1
  229. package/dist/connectors/trello/index.js +10 -5
  230. package/dist/connectors/trello/index.js.map +1 -1
  231. package/dist/gateways/discord.d.ts +6 -0
  232. package/dist/gateways/discord.d.ts.map +1 -1
  233. package/dist/gateways/discord.js +38 -27
  234. package/dist/gateways/discord.js.map +1 -1
  235. package/dist/gateways/slack.d.ts +6 -0
  236. package/dist/gateways/slack.d.ts.map +1 -1
  237. package/dist/gateways/slack.js +38 -27
  238. package/dist/gateways/slack.js.map +1 -1
  239. package/dist/gateways/telegram-commands.d.ts +124 -0
  240. package/dist/gateways/telegram-commands.d.ts.map +1 -0
  241. package/dist/gateways/telegram-commands.js +131 -0
  242. package/dist/gateways/telegram-commands.js.map +1 -0
  243. package/dist/gateways/telegram.d.ts +20 -0
  244. package/dist/gateways/telegram.d.ts.map +1 -1
  245. package/dist/gateways/telegram.js +138 -34
  246. package/dist/gateways/telegram.js.map +1 -1
  247. package/dist/gateways/turn-contract.d.ts +11 -0
  248. package/dist/gateways/turn-contract.d.ts.map +1 -1
  249. package/dist/runtime/action-surface.d.ts +1 -1
  250. package/dist/runtime/action-surface.d.ts.map +1 -1
  251. package/dist/runtime/action-surface.js +10 -0
  252. package/dist/runtime/action-surface.js.map +1 -1
  253. package/dist/runtime/backend-security.d.ts +26 -2
  254. package/dist/runtime/backend-security.d.ts.map +1 -1
  255. package/dist/runtime/backend-security.js +63 -10
  256. package/dist/runtime/backend-security.js.map +1 -1
  257. package/dist/runtime/config.d.ts +3 -0
  258. package/dist/runtime/config.d.ts.map +1 -1
  259. package/dist/runtime/config.js +4 -4
  260. package/dist/runtime/config.js.map +1 -1
  261. package/dist/runtime/connectors.d.ts +21 -1
  262. package/dist/runtime/connectors.d.ts.map +1 -1
  263. package/dist/runtime/connectors.js +28 -17
  264. package/dist/runtime/connectors.js.map +1 -1
  265. package/dist/runtime/core-db.d.ts.map +1 -1
  266. package/dist/runtime/core-db.js +2 -0
  267. package/dist/runtime/core-db.js.map +1 -1
  268. package/dist/runtime/member-paths.d.ts +15 -7
  269. package/dist/runtime/member-paths.d.ts.map +1 -1
  270. package/dist/runtime/member-paths.js +64 -12
  271. package/dist/runtime/member-paths.js.map +1 -1
  272. package/dist/runtime/member-session.d.ts +4 -0
  273. package/dist/runtime/member-session.d.ts.map +1 -1
  274. package/dist/runtime/member-session.js +23 -2
  275. package/dist/runtime/member-session.js.map +1 -1
  276. package/dist/runtime/native-session.d.ts +2 -0
  277. package/dist/runtime/native-session.d.ts.map +1 -1
  278. package/dist/runtime/native-session.js +1 -0
  279. package/dist/runtime/native-session.js.map +1 -1
  280. package/dist/runtime/owner-runtime.d.ts +63 -1
  281. package/dist/runtime/owner-runtime.d.ts.map +1 -1
  282. package/dist/runtime/owner-runtime.js +180 -3
  283. package/dist/runtime/owner-runtime.js.map +1 -1
  284. package/dist/runtime/owner-system-prompt.d.ts.map +1 -1
  285. package/dist/runtime/owner-system-prompt.js +6 -5
  286. package/dist/runtime/owner-system-prompt.js.map +1 -1
  287. package/dist/runtime/principal-access.d.ts +2 -0
  288. package/dist/runtime/principal-access.d.ts.map +1 -1
  289. package/dist/runtime/principal-access.js +7 -0
  290. package/dist/runtime/principal-access.js.map +1 -1
  291. package/dist/runtime/principal-sessions.d.ts.map +1 -1
  292. package/dist/runtime/principal-sessions.js +8 -1
  293. package/dist/runtime/principal-sessions.js.map +1 -1
  294. package/dist/runtime/record-orders.d.ts.map +1 -1
  295. package/dist/runtime/record-orders.js +3 -29
  296. package/dist/runtime/record-orders.js.map +1 -1
  297. package/dist/runtime/report-scheduler.d.ts +9 -2
  298. package/dist/runtime/report-scheduler.d.ts.map +1 -1
  299. package/dist/runtime/report-scheduler.js +37 -10
  300. package/dist/runtime/report-scheduler.js.map +1 -1
  301. package/dist/runtime/rotating-log.d.ts +44 -0
  302. package/dist/runtime/rotating-log.d.ts.map +1 -0
  303. package/dist/runtime/rotating-log.js +124 -0
  304. package/dist/runtime/rotating-log.js.map +1 -0
  305. package/dist/runtime/stimulus-delivery.d.ts +3 -0
  306. package/dist/runtime/stimulus-delivery.d.ts.map +1 -1
  307. package/dist/runtime/stimulus-delivery.js +19 -3
  308. package/dist/runtime/stimulus-delivery.js.map +1 -1
  309. package/dist/runtime/tools-paths.d.ts +7 -0
  310. package/dist/runtime/tools-paths.d.ts.map +1 -1
  311. package/dist/runtime/tools-paths.js +11 -5
  312. package/dist/runtime/tools-paths.js.map +1 -1
  313. package/dist/runtime/upkeep-close.d.ts +17 -0
  314. package/dist/runtime/upkeep-close.d.ts.map +1 -0
  315. package/dist/runtime/upkeep-close.js +127 -0
  316. package/dist/runtime/upkeep-close.js.map +1 -0
  317. package/dist/runtime/upkeep-orders.d.ts +124 -0
  318. package/dist/runtime/upkeep-orders.d.ts.map +1 -0
  319. package/dist/runtime/upkeep-orders.js +310 -0
  320. package/dist/runtime/upkeep-orders.js.map +1 -0
  321. package/dist/runtime/upkeep-reconcile.d.ts +25 -0
  322. package/dist/runtime/upkeep-reconcile.d.ts.map +1 -0
  323. package/dist/runtime/upkeep-reconcile.js +231 -0
  324. package/dist/runtime/upkeep-reconcile.js.map +1 -0
  325. package/dist/runtime/upkeep-writeback.d.ts +22 -0
  326. package/dist/runtime/upkeep-writeback.d.ts.map +1 -0
  327. package/dist/runtime/upkeep-writeback.js +203 -0
  328. package/dist/runtime/upkeep-writeback.js.map +1 -0
  329. package/dist/tools/document-toolkit.d.ts +2 -0
  330. package/dist/tools/document-toolkit.d.ts.map +1 -1
  331. package/dist/tools/document-toolkit.js +2 -0
  332. package/dist/tools/document-toolkit.js.map +1 -1
  333. package/dist/utils/untrusted-content.d.ts.map +1 -1
  334. package/dist/utils/untrusted-content.js +8 -1
  335. package/dist/utils/untrusted-content.js.map +1 -1
  336. package/install/install.sh +457 -0
  337. package/install/node.json +9 -0
  338. package/package.json +5 -5
@@ -33,6 +33,8 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.waitForBoot = waitForBoot;
37
+ exports.viewerHealth = viewerHealth;
36
38
  exports.runInit = runInit;
37
39
  const node_crypto_1 = require("node:crypto");
38
40
  const node_fs_1 = require("node:fs");
@@ -40,383 +42,593 @@ const node_os_1 = require("node:os");
40
42
  const node_path_1 = require("node:path");
41
43
  const yaml = __importStar(require("js-yaml"));
42
44
  const config_js_1 = require("../../runtime/config.js");
45
+ const member_paths_js_1 = require("../../runtime/member-paths.js");
46
+ const tools_paths_js_1 = require("../../runtime/tools-paths.js");
47
+ const backend_security_js_1 = require("../../runtime/backend-security.js");
48
+ const viewer_server_js_1 = require("../../api/viewer-server.js");
43
49
  const document_toolkit_js_1 = require("../../tools/document-toolkit.js");
44
- const tools_js_1 = require("./tools.js");
45
50
  const launch_files_js_1 = require("../launch-files.js");
46
51
  const prompt_js_1 = require("../prompt.js");
47
52
  const secrets_js_1 = require("../secrets.js");
48
- const connectorNames = [
49
- 'slack',
50
- 'chatwork',
51
- 'trello',
52
- 'kagemusha',
53
- 'calendar',
54
- 'gmail',
55
- 'drive',
56
- 'sheets',
57
- 'notion',
58
- 'obsidian',
59
- 'discord',
60
- 'telegram',
61
- 'imessage',
62
- 'claude-code',
63
- ];
64
- const connectorSecrets = {
65
- slack: ['MAMA_SLACK_TOKEN'],
66
- chatwork: ['MAMA_CHATWORK_TOKEN'],
67
- trello: ['MAMA_TRELLO_KEY', 'MAMA_TRELLO_TOKEN'],
68
- kagemusha: [],
69
- calendar: [],
70
- gmail: [],
71
- drive: [],
72
- sheets: [],
73
- notion: ['MAMA_NOTION_TOKEN'],
74
- obsidian: [],
75
- discord: ['MAMA_DISCORD_TOKEN'],
76
- telegram: ['MAMA_TELEGRAM_SOURCE_TOKEN'],
77
- imessage: [],
78
- 'claude-code': [],
79
- };
80
- const gwsConnectorNames = new Set(['calendar', 'gmail', 'drive', 'sheets']);
81
- const noSecretConnectorNames = new Set(['kagemusha', 'obsidian', 'imessage', 'claude-code']);
82
- async function yes(prompt, label) {
83
- const value = (await prompt.text(`${label} [y/N]`)).trim().toLowerCase();
84
- if (['', 'n', 'no'].includes(value))
85
- return false;
86
- if (['y', 'yes'].includes(value))
87
- return true;
88
- throw new prompt_js_1.CliInputError('Answer yes or no.');
89
- }
90
- async function text(prompt, label) {
91
- return (0, prompt_js_1.nonblankLine)(await prompt.text(label)).trim();
53
+ const diagnostics_js_1 = require("../runtime/diagnostics.js");
54
+ const setup_page_js_1 = require("../runtime/setup-page.js");
55
+ const default_roots_js_1 = require("../runtime/default-roots.js");
56
+ const telegram_pairing_js_1 = require("../runtime/telegram-pairing.js");
57
+ const init_state_js_1 = require("../runtime/init-state.js");
58
+ const daemon_control_js_1 = require("../runtime/daemon-control.js");
59
+ const systemd_service_js_1 = require("../runtime/systemd-service.js");
60
+ const daemon_js_1 = require("./daemon.js");
61
+ const init_questions_js_1 = require("./init-questions.js");
62
+ const init_section_js_1 = require("./init-section.js");
63
+ const register_owner_js_1 = require("./register-owner.js");
64
+ const service_js_1 = require("./service.js");
65
+ /** What init says to do about a source's tool the service cannot find. */
66
+ const SOURCE_TOOL_NEXT = 'Do that now; if setup stops waiting for MAMA to start, running the install command again continues it.';
67
+ function requiredVariable(env, name) {
68
+ const value = env[name]?.trim();
69
+ if (!value)
70
+ throw new prompt_js_1.CliInputError(`mama init --non-interactive needs ${name}`);
71
+ return (0, prompt_js_1.nonblankLine)(value).trim();
92
72
  }
93
- async function optionalText(prompt, label) {
94
- const value = (await prompt.text(label)).trim();
95
- return value === '' ? undefined : (0, prompt_js_1.nonblankLine)(value).trim();
96
- }
97
- async function collectConnectors(prompt, secrets) {
98
- const selected = (await prompt.text(`Connectors to enable (${connectorNames.join(', ')}; comma-separated, blank for none)`)).trim();
99
- const names = selected === '' ? [] : [...new Set(selected.split(',').map((name) => name.trim()))];
100
- if (names.some((name) => !connectorNames.includes(name))) {
101
- throw new prompt_js_1.CliInputError(`Choose connectors from: ${connectorNames.join(', ')}`);
102
- }
103
- const config = {};
104
- for (const name of names) {
105
- for (const tokenName of connectorSecrets[name])
106
- secrets[tokenName] = (0, prompt_js_1.nonblankLine)(await prompt.secret(tokenName));
107
- if (name === 'slack')
108
- secrets.MAMA_SLACK_APP_TOKEN = (0, prompt_js_1.nonblankLine)(await prompt.secret('MAMA_SLACK_APP_TOKEN'));
109
- if (name === 'calendar')
110
- prompt.write('Calendar currently reads the primary calendar only; its source channel id is calendar.');
111
- if (name === 'gmail') {
112
- config.gmail = {
113
- enabled: true,
114
- pollIntervalMinutes: 5,
115
- channels: { inbox: { role: 'hub' } },
116
- auth: { type: 'cli', cli: 'gws', cliAuthCommand: 'gws auth login' },
117
- };
118
- continue;
119
- }
120
- if (name === 'notion') {
121
- config.notion = {
122
- enabled: true,
123
- pollIntervalMinutes: 5,
124
- channels: { workspace: { role: 'hub', name: 'Notion workspace' } },
125
- auth: { type: 'token', tokenName: 'MAMA_NOTION_TOKEN' },
126
- };
127
- continue;
128
- }
129
- if (name === 'obsidian') {
130
- const vaultPath = await text(prompt, 'Obsidian vault path');
131
- config.obsidian = {
132
- enabled: true,
133
- pollIntervalMinutes: 5,
134
- channels: { vault: { role: 'hub', name: 'Vault', vaultPath } },
135
- auth: { type: 'none' },
136
- };
137
- continue;
138
- }
139
- if (name === 'claude-code') {
140
- const selectedProjects = (await text(prompt, 'Claude Code project directory names (comma-separated)'))
141
- .split(',')
142
- .map((project) => project.trim());
143
- if (selectedProjects.some((project) => !project || /[/\\\s]/.test(project))) {
144
- throw new prompt_js_1.CliInputError('Enter Claude Code project directory names without paths or spaces.');
145
- }
146
- const channels = {};
147
- for (const project of selectedProjects) {
148
- const alias = await text(prompt, `Display alias for ${project}`);
149
- channels[project] = { role: 'hub', name: alias };
150
- }
151
- config['claude-code'] = {
152
- enabled: true,
153
- pollIntervalMinutes: 5,
154
- channels,
155
- auth: { type: 'none' },
156
- };
157
- continue;
158
- }
159
- if (name === 'drive') {
160
- const folderIds = (await text(prompt, 'Drive folder ids (comma-separated)'))
161
- .split(',')
162
- .map((id) => id.trim());
163
- if (folderIds.some((id) => !id || /\s/.test(id)))
164
- throw new prompt_js_1.CliInputError('Enter Drive folder ids separated by commas without spaces.');
165
- config.drive = {
166
- enabled: true,
167
- pollIntervalMinutes: 5,
168
- channels: Object.fromEntries(folderIds.map((folderId) => [folderId, { role: 'hub', folderId }])),
169
- auth: { type: 'cli', cli: 'gws', cliAuthCommand: 'gws auth login' },
170
- };
171
- continue;
172
- }
173
- if (name === 'sheets') {
174
- const spreadsheetId = await text(prompt, 'Google Sheets spreadsheet id');
175
- const sheetRange = await text(prompt, 'Google Sheets header-and-data range');
176
- const dataRange = await optionalText(prompt, 'Separate data range (blank to use the full range)');
177
- config.sheets = {
178
- enabled: true,
179
- pollIntervalMinutes: 5,
180
- channels: {
181
- spreadsheet: {
182
- role: 'hub',
183
- spreadsheetId,
184
- sheetRange,
185
- ...(dataRange ? { dataRange } : {}),
186
- },
187
- },
188
- auth: { type: 'cli', cli: 'gws', cliAuthCommand: 'gws auth login' },
189
- };
190
- continue;
191
- }
192
- const ids = (await text(prompt, `${name} ${name === 'trello' ? 'board' : 'channel'} ids (comma-separated ids only)`))
193
- .split(',')
194
- .map((id) => id.trim());
195
- if (ids.some((id) => !id || /\s/.test(id)))
196
- throw new prompt_js_1.CliInputError('Enter channel ids separated by commas, without display names.');
197
- if (name === 'calendar' && (ids.length !== 1 || ids[0] !== 'calendar')) {
198
- throw new prompt_js_1.CliInputError('Calendar supports only the source channel id calendar (primary calendar).');
199
- }
200
- config[name] = {
201
- enabled: true,
202
- pollIntervalMinutes: 5,
203
- channels: Object.fromEntries(ids.map((id) => [id, { role: 'hub', ...(name === 'trello' ? { boardId: id } : {}) }])),
204
- auth: gwsConnectorNames.has(name)
205
- ? { type: 'cli', cli: 'gws', cliAuthCommand: 'gws auth login' }
206
- : noSecretConnectorNames.has(name)
207
- ? { type: 'none' }
208
- : { type: 'token', tokenName: connectorSecrets[name].at(-1) },
209
- };
73
+ function validChatId(chatId) {
74
+ if (!/^-[1-9]\d*$/.test(chatId)) {
75
+ throw new prompt_js_1.CliInputError('Enter a Telegram group or supergroup id (negative); private chats go to personal intake and cannot reach the common session.');
210
76
  }
211
- return config;
77
+ return chatId;
212
78
  }
213
- async function runInit(options = {}) {
214
- const prompt = options.prompt ?? (0, prompt_js_1.createTerminalPrompt)();
215
- (0, prompt_js_1.requireTTY)(prompt);
216
- const home = options.home ?? (0, node_os_1.homedir)();
217
- const root = (0, node_path_1.join)(home, '.mama');
218
- const configPath = (0, node_path_1.join)(root, 'config.yaml');
219
- if ((0, node_fs_1.existsSync)(configPath))
220
- throw new prompt_js_1.CliInputError('config.yaml already exists; init will not overwrite it.');
221
- // A partial manual setup must also be reviewed by the owner before replacing files.
222
- for (const name of ['connectors.json', 'start.sh']) {
223
- if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, name)))
224
- throw new prompt_js_1.CliInputError(`${name} already exists; init will not overwrite it.`);
225
- }
226
- const backend = await text(prompt, 'Backend (claude|codex)');
227
- if (backend !== 'claude' && backend !== 'codex')
228
- throw new prompt_js_1.CliInputError('Backend must be claude or codex.');
229
- const model = await text(prompt, 'Model');
230
- const machineTimeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
231
- const timezoneAnswer = (await prompt.text(`Owner timezone [${machineTimeZone}]`)).trim();
232
- const timezone = timezoneAnswer || machineTimeZone;
79
+ function validTelegramId(id) {
80
+ if (!/^[1-9]\d*$/.test(id))
81
+ throw new prompt_js_1.CliInputError('Enter your numeric Telegram user ID (only digits).');
82
+ return id;
83
+ }
84
+ /** The variables start.sh exports for a viewer behind a Cloudflare Access tunnel (service.ts). */
85
+ const TUNNEL_VARIABLES = [
86
+ 'MAMA_CF_ACCESS_ISSUER',
87
+ 'MAMA_CF_ACCESS_AUD',
88
+ 'MAMA_VIEWER_HOSTNAMES',
89
+ 'MAMA_VIEWER_OWNER_EMAILS',
90
+ ];
91
+ /**
92
+ * The tunnel, from those variables: none set, no tunnel; any set, the issuer, audience and
93
+ * hostname are needed and the owner emails stay optional.
94
+ */
95
+ function tunnelVariables(env) {
96
+ if (!TUNNEL_VARIABLES.some((name) => env[name]?.trim()))
97
+ return {};
98
+ const issuer = requiredVariable(env, 'MAMA_CF_ACCESS_ISSUER');
99
+ let url;
233
100
  try {
234
- new Intl.DateTimeFormat('en', { timeZone: timezone });
101
+ url = new URL(issuer);
235
102
  }
236
103
  catch {
237
- throw new prompt_js_1.CliInputError(`Timezone "${timezone}" is not a valid IANA time zone.`);
104
+ throw new prompt_js_1.CliInputError('MAMA_CF_ACCESS_ISSUER must be an HTTPS origin.');
238
105
  }
239
- const secrets = {
240
- MAMA_TELEGRAM_TOKEN: (0, prompt_js_1.nonblankLine)(await prompt.secret('Telegram bot token')),
241
- };
242
- const chatId = await text(prompt, 'Telegram common group/supergroup chat id (negative)');
243
- if (!/^-[1-9]\d*$/.test(chatId)) {
244
- throw new prompt_js_1.CliInputError('Enter a Telegram group or supergroup id (negative); private chats go to personal intake and cannot reach the common session.');
106
+ if (url.protocol !== 'https:' ||
107
+ url.username ||
108
+ url.password ||
109
+ url.search ||
110
+ url.hash ||
111
+ url.pathname !== '/') {
112
+ throw new prompt_js_1.CliInputError('MAMA_CF_ACCESS_ISSUER must be an HTTPS origin.');
245
113
  }
246
- const connectors = await collectConnectors(prompt, secrets);
247
- const discordEnabled = await yes(prompt, 'Enable Discord owner messages');
248
- let discordConfig = {
249
- enabled: false,
250
- allowed_channels: [],
251
- owner_user_ids: [],
114
+ const viewer = {
115
+ MAMA_CF_ACCESS_ISSUER: url.origin,
116
+ MAMA_CF_ACCESS_AUD: requiredVariable(env, 'MAMA_CF_ACCESS_AUD'),
252
117
  };
253
- if (discordEnabled) {
254
- secrets.MAMA_DISCORD_TOKEN ??= (0, prompt_js_1.nonblankLine)(await prompt.secret('Discord bot token'));
255
- const channel = await text(prompt, 'Discord owner channel id');
256
- const user = await text(prompt, 'Discord owner user id');
257
- discordConfig = {
258
- enabled: true,
259
- owner_channel_id: channel,
260
- allowed_channels: [channel],
261
- owner_user_ids: [user],
262
- };
118
+ const hostname = requiredVariable(env, 'MAMA_VIEWER_HOSTNAMES');
119
+ if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/i.test(hostname))
120
+ throw new prompt_js_1.CliInputError('MAMA_VIEWER_HOSTNAMES must be one hostname, without scheme or path.');
121
+ viewer.MAMA_VIEWER_HOSTNAMES = hostname;
122
+ const emails = env.MAMA_VIEWER_OWNER_EMAILS?.trim();
123
+ if (emails)
124
+ viewer.MAMA_VIEWER_OWNER_EMAILS = (0, prompt_js_1.nonblankLine)(emails);
125
+ return viewer;
126
+ }
127
+ /**
128
+ * The bot token and the pairing code, entered on the setup page (install.md, W2.4.5b). The token
129
+ * is checked as Telegram answers it; then pairOwner's `ask` is answered by the code's entry, whose
130
+ * reply waits for pairOwner: another `ask` means a wrong code, a return the right one. One process
131
+ * runs from the token to /bind, since the codes and the update offset live in pairOwner's memory.
132
+ */
133
+ async function pairOnSetupPage(options) {
134
+ const { call, write } = options;
135
+ let taken;
136
+ const tokenTaken = new Promise((resolve) => {
137
+ taken = resolve;
138
+ });
139
+ // pairOwner's first ask: it has dropped what the bot held from before (telegram-pairing.ts).
140
+ let asked;
141
+ let notAsked;
142
+ const firstAsk = new Promise((resolve, reject) => {
143
+ asked = resolve;
144
+ notAsked = reject;
145
+ });
146
+ firstAsk.catch(() => { });
147
+ /** The ask waiting for an entry, and the entry waiting for pairOwner's verdict. */
148
+ let waiting;
149
+ let entry;
150
+ /** pairOwner's last line: after a wrong code, the one that says so. */
151
+ let lastLine = '';
152
+ const page = await (0, setup_page_js_1.openSetupPage)({
153
+ fields: [
154
+ {
155
+ name: 'token',
156
+ label: 'Telegram bot token (from BotFather)',
157
+ secret: true,
158
+ check: async (value) => {
159
+ let token;
160
+ let botUsername;
161
+ try {
162
+ token = (0, prompt_js_1.nonblankLine)(value).trim();
163
+ // The page asks again, so a network failure names the entry, not a rerun.
164
+ await (0, init_questions_js_1.checkTelegram)(token, undefined, call, write, {
165
+ next: 'enter it again on the setup page',
166
+ });
167
+ botUsername = await (0, telegram_pairing_js_1.botName)({ token, call, write });
168
+ }
169
+ catch (error) {
170
+ if (error instanceof prompt_js_1.CliInputError)
171
+ write(error.message);
172
+ throw error;
173
+ }
174
+ write(`bot token accepted for @${botUsername}`);
175
+ taken({ token, botUsername });
176
+ // A message sent before pairOwner's first poll would be dropped with the held ones.
177
+ await firstAsk;
178
+ return `The bot token is accepted for @${botUsername}. In Telegram, send @${botUsername} a new private message; it answers with a pairing code.`;
179
+ },
180
+ },
181
+ {
182
+ name: 'code',
183
+ label: 'Pairing code (from the bot)',
184
+ secret: false,
185
+ check: (value) => new Promise((accept, refuse) => {
186
+ const answer = waiting;
187
+ // The page takes one entry at a time, and asks for the code only after pairOwner did.
188
+ if (answer === undefined)
189
+ throw new Error('a pairing code was entered with none asked for');
190
+ waiting = undefined;
191
+ entry = { accept, refuse };
192
+ answer(value);
193
+ }),
194
+ },
195
+ ],
196
+ write,
197
+ platform: options.platform,
198
+ ...(options.openRun ? { openRun: options.openRun } : {}),
199
+ ...(options.lifetimeMs === undefined ? {} : { lifetimeMs: options.lifetimeMs }),
200
+ });
201
+ try {
202
+ const { token, botUsername } = await Promise.race([tokenTaken, page.stopped]);
203
+ const owner = await (0, telegram_pairing_js_1.pairOwner)({
204
+ token,
205
+ call,
206
+ botUsername,
207
+ write: (line) => {
208
+ lastLine = line;
209
+ write(line);
210
+ },
211
+ ask: (_label, signal) => {
212
+ // pairOwner asks again only after a wrong code, and wrote why just before.
213
+ entry?.refuse(new prompt_js_1.CliInputError(lastLine));
214
+ entry = undefined;
215
+ asked();
216
+ return new Promise((resolve, reject) => {
217
+ waiting = resolve;
218
+ signal.addEventListener('abort', () => reject(new Error('pairing stopped')), {
219
+ once: true,
220
+ });
221
+ page.stopped.catch(reject);
222
+ });
223
+ },
224
+ });
225
+ write('pairing code accepted');
226
+ entry?.accept(`The pairing code is accepted. Add @${botUsername} to the group for common work as an administrator, then send /bind in that group. This page has closed.`);
227
+ const group = await (0, telegram_pairing_js_1.bindCommonGroup)({
228
+ token,
229
+ call,
230
+ write,
231
+ botUsername,
232
+ ownerId: owner.ownerId,
233
+ offset: owner.offset,
234
+ earlier: owner.groupMessages,
235
+ });
236
+ await (0, telegram_pairing_js_1.confirmUpdates)({ token, call, write, offset: group.offset });
237
+ // The bot's post in the group proved the token; the group's privacy and membership are only
238
+ // reported, and doctor checks them again later.
239
+ (0, init_questions_js_1.report)(await (0, diagnostics_js_1.telegramCheck)(token, group.chatId, call), write);
240
+ return { token, ownerId: owner.ownerId, chatId: group.chatId };
263
241
  }
264
- const slackEnabled = await yes(prompt, 'Enable Slack owner messages');
265
- let slackConfig = {
266
- enabled: false,
267
- allowed_channels: [],
268
- owner_user_ids: [],
269
- };
270
- if (slackEnabled) {
271
- secrets.MAMA_SLACK_TOKEN ??= (0, prompt_js_1.nonblankLine)(await prompt.secret('Slack bot token'));
272
- secrets.MAMA_SLACK_APP_TOKEN ??= (0, prompt_js_1.nonblankLine)(await prompt.secret('Slack Socket Mode app token'));
273
- const channel = await text(prompt, 'Slack owner channel id');
274
- const user = await text(prompt, 'Slack owner user id');
275
- slackConfig = {
276
- enabled: true,
277
- owner_channel_id: channel,
278
- allowed_channels: [channel],
279
- owner_user_ids: [user],
280
- };
242
+ catch (error) {
243
+ // An entry still waiting gets the sentence that ends the setup.
244
+ notAsked(error);
245
+ entry?.refuse(error);
246
+ throw error;
281
247
  }
282
- const viewer = {};
283
- // Jev (TypeSafe) is the owner's choice: the agent's judge tool, off unless chosen here.
284
- const jevKeyPath = (0, node_path_1.join)(root, 'jev-key');
285
- const jevEnabled = await yes(prompt, 'Use Jev (TypeSafe) so the agent can judge many messages or items without reading them all? Owner text in those calls goes to the Jev service');
286
- let jevKey;
287
- if (jevEnabled && !(0, node_fs_1.existsSync)(jevKeyPath)) {
288
- prompt.write('Get a Jev API key from TypeSafe: https://docs.typesafe.ai');
289
- jevKey = (0, prompt_js_1.nonblankLine)(await prompt.secret('Jev API key'));
248
+ finally {
249
+ page.close();
290
250
  }
291
- if (await yes(prompt, 'Expose the viewer through a tunnel')) {
292
- const issuer = await text(prompt, 'Access issuer (HTTPS URL)');
293
- let url;
294
- try {
295
- url = new URL(issuer);
251
+ }
252
+ /**
253
+ * The setup's answers. Those from the environment are all checked before the setup page opens,
254
+ * so a refused one does not make the owner pair again. The page takes the bot token and the
255
+ * pairing code: a secret never comes from a variable or a flag, which `ps` shows, and no Telegram
256
+ * ID is typed. Sources, Discord, Slack and Jev are set up afterwards with `mama init --section`.
257
+ */
258
+ async function environmentAnswers(env, defaults, checks) {
259
+ const backend = requiredVariable(env, 'MAMA_INIT_BACKEND');
260
+ if (backend !== 'claude' && backend !== 'codex')
261
+ throw new prompt_js_1.CliInputError('MAMA_INIT_BACKEND must be claude or codex.');
262
+ checks.backend(backend);
263
+ const model = requiredVariable(env, 'MAMA_INIT_MODEL');
264
+ const timezone = (0, init_questions_js_1.validTimezone)(env.MAMA_INIT_TIMEZONE?.trim() || Intl.DateTimeFormat().resolvedOptions().timeZone);
265
+ const memberAnswer = env.MAMA_INIT_MEMBER_ROOT?.trim() || defaults?.memberRoot;
266
+ const toolsAnswer = env.MAMA_INIT_TOOLS_ROOT?.trim() || defaults?.toolsRoot;
267
+ if (!memberAnswer || !toolsAnswer)
268
+ throw new prompt_js_1.CliInputError('This platform has no default folders; set MAMA_INIT_MEMBER_ROOT and MAMA_INIT_TOOLS_ROOT.');
269
+ const { memberRoot, toolsRoot } = checks.roots(memberAnswer, toolsAnswer, {
270
+ member: !env.MAMA_INIT_MEMBER_ROOT?.trim(),
271
+ tools: !env.MAMA_INIT_TOOLS_ROOT?.trim(),
272
+ });
273
+ const viewer = tunnelVariables(env);
274
+ const paired = await checks.page();
275
+ return {
276
+ backend,
277
+ model,
278
+ timezone,
279
+ secrets: { MAMA_TELEGRAM_TOKEN: paired.token },
280
+ chatId: validChatId(paired.chatId),
281
+ ownerTelegramId: validTelegramId(paired.ownerId),
282
+ memberRoot,
283
+ toolsRoot,
284
+ viewer,
285
+ };
286
+ }
287
+ /**
288
+ * Wait until the started daemon reports its boot finished, then until the viewer answers. With a
289
+ * `version`, the daemon must be that version: `mama update` waits for the one it installed.
290
+ */
291
+ async function waitForBoot(options) {
292
+ const started = Date.now();
293
+ let reported = started;
294
+ let stage = 'not started';
295
+ const next = options.next ?? 'running the install command again continues from here';
296
+ for (;;) {
297
+ // A daemon busy booting may miss one status deadline; that is no answer yet, not a failure.
298
+ const reply = await options.status().catch(() => null);
299
+ if (reply?.state === 'running' && reply.reply.ok === true) {
300
+ const status = (reply.reply.status ?? {});
301
+ stage = String(status.stage);
302
+ if (options.version !== undefined && status.version !== options.version) {
303
+ stage = `${stage} at version ${String(status.version)}, not ${options.version}`;
304
+ }
305
+ else if (stage === 'running')
306
+ break;
296
307
  }
297
- catch {
298
- throw new prompt_js_1.CliInputError('Access issuer must be an HTTPS origin.');
308
+ if (Date.now() - started >= options.timeoutMs) {
309
+ throw new prompt_js_1.CliInputError(`MAMA did not finish starting (last stage: ${stage}). mama logs shows why; ${next}.`);
299
310
  }
300
- if (url.protocol !== 'https:' ||
301
- url.username ||
302
- url.password ||
303
- url.search ||
304
- url.hash ||
305
- url.pathname !== '/') {
306
- throw new prompt_js_1.CliInputError('Access issuer must be an HTTPS origin.');
311
+ if (Date.now() - reported >= 30_000) {
312
+ options.write(`still starting (stage: ${stage})`);
313
+ reported = Date.now();
307
314
  }
308
- viewer.MAMA_CF_ACCESS_ISSUER = url.origin;
309
- viewer.MAMA_CF_ACCESS_AUD = await text(prompt, 'Access audience');
310
- const hostname = await text(prompt, 'Viewer hostname (no scheme or path)');
311
- if (!/^[a-z0-9]+(?:[.-][a-z0-9]+)*$/i.test(hostname))
312
- throw new prompt_js_1.CliInputError('Enter a hostname without scheme or path.');
313
- viewer.MAMA_VIEWER_HOSTNAMES = hostname;
314
- const emails = (await prompt.text('Viewer owner emails for access monitoring (comma-separated, optional)')).trim();
315
- if (emails)
316
- viewer.MAMA_VIEWER_OWNER_EMAILS = (0, prompt_js_1.nonblankLine)(emails);
315
+ await new Promise((resolve) => setTimeout(resolve, options.pollMs));
317
316
  }
318
- const installLaunchAgent = await yes(prompt, 'Write ~/Library/LaunchAgents/com.mama.server.plist');
319
- const plistPath = (0, node_path_1.join)(home, 'Library', 'LaunchAgents', 'com.mama.server.plist');
320
- if (installLaunchAgent && (0, node_fs_1.existsSync)(plistPath))
321
- throw new prompt_js_1.CliInputError('com.mama.server.plist already exists; init will not overwrite it.');
322
- const config = (0, config_js_1.parseConfig)({
323
- version: 1,
324
- timezone,
325
- tools_root: options.toolsRoot ?? (0, tools_js_1.defaultToolsRoot)(options.platform, home),
326
- agent: {
327
- backend,
328
- model,
329
- effort: 'medium',
330
- max_turns: 100,
331
- // A turn is stopped after 10 minutes without progress, or after an hour in all
332
- // (owner, 2026-10-03); a full report on 2026-09-29 was cut off at 300 s while working.
333
- timeout: 600_000,
334
- max_turn_ms: 3_600_000,
335
- run_token_budget: 0,
336
- },
337
- database: { path: (0, node_path_1.join)(root, 'memory.db') },
338
- logging: { level: 'info', file: (0, node_path_1.join)(root, 'logs', 'daemon.log') },
339
- telegram: {
340
- enabled: true,
341
- owner_chat_id: chatId,
342
- allowed_chats: [chatId],
343
- polling: true,
344
- },
345
- discord: discordConfig,
346
- slack: slackConfig,
347
- delivery: { reports: 'telegram', notifications: 'telegram', security_alerts: 'telegram' },
348
- wiki: {
349
- enabled: true,
350
- vaultPath: (0, node_path_1.join)(root, 'workspace'),
351
- wikiDir: (0, node_path_1.join)(root, 'workspace', 'wiki'),
352
- },
353
- ...(jevEnabled ? { jev: { enabled: true } } : {}),
354
- }, { home });
355
- const locate = options.findExecutable ?? launch_files_js_1.findExecutable;
356
- const backendPath = locate(backend);
357
- const gwsPath = locate('gws');
358
- const script = (0, launch_files_js_1.startScript)({
359
- home,
360
- viewer,
361
- nodePath: options.nodePath ?? process.execPath,
362
- cliPath: options.cliPath ?? (0, node_path_1.join)(__dirname, '..', 'index.js'),
363
- executablePaths: [backendPath, gwsPath].filter((path) => path !== undefined),
364
- });
365
- secrets.MAMA_AUTH_TOKEN = (0, node_crypto_1.randomBytes)(32).toString('hex');
366
- (0, node_fs_1.mkdirSync)(root, { recursive: true, mode: 0o700 });
367
- (0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, 'logs'), { recursive: true, mode: 0o700 });
368
- (0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, 'workspace', 'wiki'), { recursive: true, mode: 0o700 });
369
- // Finish all prompts before any write, and publish config last as the setup completion marker.
370
- (0, secrets_js_1.updateSecrets)(home, secrets);
371
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, 'connectors.json'), `${JSON.stringify(connectors, null, 2)}\n`, {
372
- flag: 'wx',
373
- mode: 0o600,
374
- });
375
- (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, 'start.sh'), script, { flag: 'wx', mode: 0o700 });
376
- if (jevKey !== undefined)
377
- (0, node_fs_1.writeFileSync)(jevKeyPath, `${jevKey}\n`, { flag: 'wx', mode: 0o600 });
378
- if (installLaunchAgent) {
379
- (0, node_fs_1.mkdirSync)((0, node_path_1.dirname)(plistPath), { recursive: true });
380
- (0, node_fs_1.writeFileSync)(plistPath, (0, launch_files_js_1.launchAgent)(home), { flag: 'wx', mode: 0o600 });
317
+ if (!(await options.health())) {
318
+ throw new prompt_js_1.CliInputError(`MAMA started but its viewer does not answer /health; mama doctor checks the install${options.next === undefined ? '' : `; ${options.next}`}.`);
381
319
  }
382
- (0, node_fs_1.writeFileSync)(configPath, yaml.dump(config), { flag: 'wx', mode: 0o600 });
383
- prompt.write('Setup written. Credentials are stored only in auth.env (0600).');
384
- prompt.write('Before starting the daemon, run: mama register-owner <telegram id>.');
385
- if (!backendPath)
386
- prompt.write(`Install ${backend} and add its bin directory to PATH in ~/.mama/start.sh.`);
387
- prompt.write(`If you have not logged in, run: ${backend === 'claude' ? 'claude auth login' : `CODEX_HOME=${(0, secrets_js_1.shellQuote)((0, node_path_1.join)(root, '.codex'))} codex login`}`);
388
- if (Object.keys(connectors).some((name) => gwsConnectorNames.has(name))) {
389
- if (!gwsPath)
390
- prompt.write('Install gws and add its bin directory to PATH in ~/.mama/start.sh.');
391
- prompt.write('Log in with the Google scopes your selected connectors need: gws auth login.');
320
+ }
321
+ const DEFAULT_VIEWER_PORT = (0, viewer_server_js_1.resolveApiPort)(undefined);
322
+ /** The first viewer port nothing listens on, from the default up. */
323
+ async function freeViewerPort(answers) {
324
+ for (let port = DEFAULT_VIEWER_PORT; port < DEFAULT_VIEWER_PORT + 50; port += 1) {
325
+ if (!(await answers(port, '127.0.0.1')))
326
+ return port;
392
327
  }
393
- if (jevEnabled)
394
- prompt.write(`Jev is on (jev.enabled in config.yaml); its key is ${(0, secrets_js_1.shellQuote)(jevKeyPath)} (0600). Turn it off by removing jev.enabled.`);
395
- if (viewer.MAMA_VIEWER_HOSTNAMES)
396
- prompt.write('Configure your tunnel to the local viewer and protect its hostname with the Access application above.');
397
- if (installLaunchAgent)
398
- prompt.write(`After login, start with: launchctl bootstrap gui/$(id -u) ${(0, secrets_js_1.shellQuote)(plistPath)}`);
399
- else
400
- prompt.write(`After login, start with: ${(0, secrets_js_1.shellQuote)((0, node_path_1.join)(root, 'start.sh'))}`);
401
- // Host provisioning is last; a failure leaves setup available for mama tools install.
402
- if (!config.tools_root) {
403
- prompt.write('Set tools_root to a writable directory outside HOME in config.yaml, then run mama tools install on the host.');
404
- return;
328
+ throw new prompt_js_1.CliInputError(`Ports ${DEFAULT_VIEWER_PORT}-${DEFAULT_VIEWER_PORT + 49} are all in use; free one, then run the install command again.`);
329
+ }
330
+ async function viewerHealth(port) {
331
+ try {
332
+ const response = await fetch(`http://127.0.0.1:${port}/health`, {
333
+ signal: AbortSignal.timeout(5_000),
334
+ });
335
+ return response.ok;
336
+ }
337
+ catch {
338
+ return false;
339
+ }
340
+ }
341
+ /** How much longer than the daemon's own turn limit init waits for the backend check. */
342
+ const PROBE_MARGIN_MS = 60_000;
343
+ const initUsage = `Usage: mama init --non-interactive | mama init --section <${init_section_js_1.initSections.join('|')}> | mama init --section connectors --non-interactive`;
344
+ async function runInit(args = [], options = {}) {
345
+ if (args[0] === '--section') {
346
+ const nonInteractive = args.length === 3 && args[2] === '--non-interactive';
347
+ if (args.length !== 2 && !nonInteractive)
348
+ throw new prompt_js_1.CliInputError(initUsage);
349
+ return (0, init_section_js_1.runInitSection)(args[1], options, nonInteractive);
405
350
  }
351
+ // The setup reads no terminal: the person's agent runs it (owner-setup.md).
352
+ if (args.length !== 1 || args[0] !== '--non-interactive')
353
+ throw new prompt_js_1.CliInputError(initUsage);
354
+ const write = (line) => (options.prompt ? options.prompt.write(line) : console.log(line));
355
+ const home = options.home ?? (0, node_os_1.homedir)();
356
+ const platform = options.platform ?? process.platform;
357
+ const root = (0, node_path_1.join)(home, '.mama');
358
+ const configPath = (0, node_path_1.join)(root, 'config.yaml');
359
+ const lockPaths = (0, daemon_control_js_1.daemonControlPaths)(root);
360
+ // While init writes the install, no daemon, restore or second init may run beside it, and the
361
+ // step record is read and repaired under the same lock, so a second init cannot act on one the
362
+ // first is still writing. A setup with only the service step left takes no lock: that step
363
+ // writes no install files, and the daemon a failed wait left starting holds the lock.
364
+ const peek = (0, init_state_js_1.readInitState)(root);
365
+ // An install set up before init kept its steps, or by hand: init will not take it over. Said
366
+ // before the lock, since that install's running daemon holds it.
367
+ if (peek === null && (0, node_fs_1.existsSync)(configPath))
368
+ throw new prompt_js_1.CliInputError('config.yaml already exists; init will not overwrite it. mama doctor checks this install.');
369
+ const serviceOnly = peek !== null &&
370
+ ['config', 'owner', 'toolkit'].every((step) => peek.done.includes(step));
371
+ let lock = (0, node_fs_1.existsSync)(root) && !serviceOnly ? (0, daemon_control_js_1.acquireInstallLock)(lockPaths, 'init') : undefined;
372
+ let progress = { version: 1, done: [] };
373
+ const mark = (step) => {
374
+ progress.done.push(step);
375
+ (0, init_state_js_1.writeInitState)(root, progress);
376
+ };
377
+ const done = (step) => progress.done.includes(step);
406
378
  try {
407
- if (options.installToolkit)
408
- await options.installToolkit(config);
409
- else
410
- await (0, document_toolkit_js_1.installDocumentToolkit)({
411
- toolsRoot: config.tools_root,
379
+ let state = lock ? (0, init_state_js_1.readInitState)(root) : peek;
380
+ // The record is written before the setup's files, so one without the config step means an
381
+ // init stopped part-way through writing them. The files only init creates are removed and the
382
+ // step starts over; auth.env is merged, and a Codex login made before init stays.
383
+ if (state !== null && !state.done.includes('config')) {
384
+ for (const name of ['config.yaml', 'connectors.json', 'jev-key'])
385
+ (0, node_fs_1.rmSync)((0, node_path_1.join)(root, name), { force: true });
386
+ write('An earlier mama init stopped while writing its files; that step starts over.');
387
+ state = null;
388
+ }
389
+ if ((0, node_fs_1.existsSync)(configPath)) {
390
+ // An install set up before init kept its steps, or by hand: init will not take it over.
391
+ if (state === null)
392
+ throw new prompt_js_1.CliInputError('config.yaml already exists; init will not overwrite it.');
393
+ // A status, not a refusal: running the install command again on a set-up install ends here.
394
+ if ((0, init_state_js_1.initComplete)(state)) {
395
+ write('MAMA is already set up here; mama doctor checks it, mama service restart restarts it and mama update updates it.');
396
+ return;
397
+ }
398
+ write(`continuing setup: done so far: ${state.done.join(', ')}`);
399
+ }
400
+ else {
401
+ state = null;
402
+ // A partial manual setup must also be reviewed by the owner before replacing files.
403
+ for (const name of ['connectors.json', 'start.sh']) {
404
+ if ((0, node_fs_1.existsSync)((0, node_path_1.join)(root, name)))
405
+ throw new prompt_js_1.CliInputError(`${name} already exists; init will not overwrite it.`);
406
+ }
407
+ }
408
+ progress = state ?? { version: 1, done: [] };
409
+ const locate = options.findExecutable ?? launch_files_js_1.findExecutable;
410
+ // Questions and checks come first, so a refused or cancelled setup leaves nothing in the MAMA
411
+ // home. The two folders are made as soon as they are named: a folder that cannot be this
412
+ // user's alone is refused before pairing, which would otherwise have to be done again.
413
+ let answers;
414
+ if (!done('config')) {
415
+ const defaults = (0, default_roots_js_1.defaultRoots)({
416
+ platform,
412
417
  home,
413
- memberRoot: config.member_root,
414
- write: prompt.write,
418
+ ...(options.user === undefined ? {} : { user: options.user }),
415
419
  });
420
+ const call = options.telegramCall ?? diagnostics_js_1.defaultTelegramCall;
421
+ const checks = {
422
+ // A signed-out backend's login is named, with the backend's full path; the agent runs it.
423
+ backend: (name) => (0, init_questions_js_1.checkBackend)(name, (0, node_path_1.join)(root, '.codex'), locate, options.probeRun, 'run the install command again'),
424
+ roots: (memberAnswer, toolsAnswer, isDefault) => {
425
+ const memberRoot = (0, node_path_1.resolve)(memberAnswer);
426
+ const toolsRoot = (0, node_path_1.resolve)(toolsAnswer);
427
+ try {
428
+ (0, member_paths_js_1.validateMemberRoot)(memberRoot, [home, root]);
429
+ (0, tools_paths_js_1.assertRootAncestors)((0, backend_security_js_1.physicalReadPath)(memberRoot), 'member_root');
430
+ (0, tools_paths_js_1.validateToolsRoot)(toolsRoot, [home, root, memberRoot]);
431
+ }
432
+ catch (error) {
433
+ throw new prompt_js_1.CliInputError(error.message);
434
+ }
435
+ for (const [folder, ownParent] of [
436
+ [memberRoot, isDefault.member],
437
+ [toolsRoot, isDefault.tools],
438
+ ]) {
439
+ (0, default_roots_js_1.prepareRoot)(folder, {
440
+ ownParent,
441
+ ...(options.uid === undefined ? {} : { uid: options.uid }),
442
+ });
443
+ }
444
+ return { memberRoot, toolsRoot };
445
+ },
446
+ page: () => pairOnSetupPage({
447
+ call,
448
+ write,
449
+ platform,
450
+ ...(options.openRun ? { openRun: options.openRun } : {}),
451
+ ...(options.setupPageMs === undefined ? {} : { lifetimeMs: options.setupPageMs }),
452
+ }),
453
+ };
454
+ answers = await environmentAnswers(options.env ?? process.env, defaults, checks);
455
+ }
456
+ if (lock === undefined && !serviceOnly) {
457
+ // A new install: the MAMA home is made only once the questions are answered, and another
458
+ // init may have started one meanwhile.
459
+ (0, node_fs_1.mkdirSync)(root, { recursive: true, mode: 0o700 });
460
+ lock = (0, daemon_control_js_1.acquireInstallLock)(lockPaths, 'init');
461
+ if ((0, init_state_js_1.readInitState)(root) !== null || (0, node_fs_1.existsSync)(configPath))
462
+ throw new prompt_js_1.CliInputError('Another mama init set up here meanwhile; run the install command again.');
463
+ }
464
+ // A restore sets the launch files aside too, so even the service step waits for it.
465
+ (0, daemon_control_js_1.refuseWhileRestoring)(root);
466
+ let config;
467
+ if (answers !== undefined) {
468
+ const { memberRoot, toolsRoot } = answers;
469
+ // Each account on a machine runs its own MAMA; another's viewer may already hold the port.
470
+ const port = await freeViewerPort(options.portAnswers ?? diagnostics_js_1.portAnswers);
471
+ if (port !== DEFAULT_VIEWER_PORT)
472
+ answers.viewer.MAMA_API_PORT = String(port);
473
+ config = (0, config_js_1.parseConfig)({
474
+ version: 1,
475
+ timezone: answers.timezone,
476
+ member_root: memberRoot,
477
+ tools_root: toolsRoot,
478
+ agent: {
479
+ backend: answers.backend,
480
+ model: answers.model,
481
+ effort: 'medium',
482
+ max_turns: 100,
483
+ // A turn is stopped after 10 minutes without progress, or after an hour in all
484
+ // (owner, 2026-10-03); a full report on 2026-09-29 was cut off at 300 s while working.
485
+ timeout: 600_000,
486
+ max_turn_ms: 3_600_000,
487
+ run_token_budget: 0,
488
+ },
489
+ database: { path: (0, node_path_1.join)(root, 'memory.db') },
490
+ logging: { level: 'info', file: (0, node_path_1.join)(root, 'logs', 'daemon.log') },
491
+ telegram: {
492
+ enabled: true,
493
+ owner_chat_id: answers.chatId,
494
+ allowed_chats: [answers.chatId],
495
+ polling: true,
496
+ },
497
+ discord: { enabled: false, allowed_channels: [], owner_user_ids: [] },
498
+ slack: { enabled: false, allowed_channels: [], owner_user_ids: [] },
499
+ delivery: { reports: 'telegram', notifications: 'telegram', security_alerts: 'telegram' },
500
+ wiki: {
501
+ enabled: true,
502
+ vaultPath: (0, node_path_1.join)(root, 'workspace'),
503
+ wikiDir: (0, node_path_1.join)(root, 'workspace', 'wiki'),
504
+ },
505
+ }, { home });
506
+ answers.secrets.MAMA_AUTH_TOKEN = (0, node_crypto_1.randomBytes)(32).toString('hex');
507
+ (0, init_state_js_1.writeInitState)(root, progress);
508
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, 'logs'), { recursive: true, mode: 0o700 });
509
+ (0, node_fs_1.mkdirSync)((0, node_path_1.join)(root, 'workspace', 'wiki'), { recursive: true, mode: 0o700 });
510
+ // All answers are in before any write; the step is marked done once config.yaml exists.
511
+ (0, secrets_js_1.updateSecrets)(home, answers.secrets);
512
+ // No source yet: `mama init --section connectors` adds them.
513
+ (0, node_fs_1.writeFileSync)((0, node_path_1.join)(root, 'connectors.json'), '{}\n', { flag: 'wx', mode: 0o600 });
514
+ (0, node_fs_1.writeFileSync)(configPath, yaml.dump(config), { flag: 'wx', mode: 0o600 });
515
+ progress.viewer = answers.viewer;
516
+ progress.pendingOwner = answers.ownerTelegramId;
517
+ mark('config');
518
+ write('Setup written. Credentials are stored only in auth.env (0600).');
519
+ }
520
+ else {
521
+ config = (0, config_js_1.loadConfig)({ home, path: configPath });
522
+ // A setup that stopped (often at the boot wait, when a source's tool is missing) names the
523
+ // tools again: the service does not start until they are installed.
524
+ await (0, init_questions_js_1.writeSourceTools)({ root, home, platform, configPath }, config, options, write, SOURCE_TOOL_NEXT);
525
+ }
526
+ if (!done('owner')) {
527
+ const owner = progress.pendingOwner;
528
+ if (!owner)
529
+ throw new prompt_js_1.CliInputError('No owner Telegram ID was recorded; run the install command again.');
530
+ await (0, register_owner_js_1.registerOwnerHolding)(root, config, config.member_root, owner);
531
+ delete progress.pendingOwner;
532
+ mark('owner');
533
+ write("You are registered as this install's first owner.");
534
+ }
535
+ if (!done('toolkit')) {
536
+ try {
537
+ if (options.installToolkit)
538
+ await options.installToolkit(config);
539
+ else
540
+ await (0, document_toolkit_js_1.installDocumentToolkit)({
541
+ toolsRoot: config.tools_root,
542
+ home,
543
+ memberRoot: config.member_root,
544
+ write,
545
+ });
546
+ write('Document toolkit installed for owner and member sessions.');
547
+ }
548
+ catch (error) {
549
+ // MAMA runs without the document tools; their host requirements must not keep it from
550
+ // starting. The daemon log names them as absent until mama tools install succeeds.
551
+ write(`The document tools could not be installed: ${error instanceof Error ? error.message : 'unknown error'}. MAMA runs without them; run mama tools install once that is fixed.`);
552
+ }
553
+ mark('toolkit');
554
+ }
416
555
  }
417
- catch (error) {
418
- throw new prompt_js_1.CliInputError(`Document toolkit setup failed. Run mama tools install on the host. ${error instanceof Error ? error.message : ''}`);
556
+ finally {
557
+ // The daemon takes this lock at boot; it must be free before the service starts it.
558
+ lock?.release();
559
+ }
560
+ if (!done('service')) {
561
+ const startServiceFor = options.startService ??
562
+ (async (viewer) => {
563
+ const serviceOptions = {
564
+ home,
565
+ platform,
566
+ write,
567
+ viewer,
568
+ ...(options.serviceRun ? { run: options.serviceRun } : {}),
569
+ ...(options.findExecutable ? { findExecutable: options.findExecutable } : {}),
570
+ ...(options.nodePath ? { nodePath: options.nodePath } : {}),
571
+ ...(options.cliPath ? { cliPath: options.cliPath } : {}),
572
+ ...(options.toolDirectoryExists
573
+ ? { toolDirectoryExists: options.toolDirectoryExists }
574
+ : {}),
575
+ };
576
+ // Linux keeps the user's services running after logout only with linger (B4). Enabling it
577
+ // can need privileges this account lacks; that must not stop the service from starting.
578
+ if (platform === 'linux') {
579
+ const user = options.user ?? (0, node_os_1.userInfo)().username;
580
+ const result = (options.serviceRun ?? systemd_service_js_1.defaultServiceRun)('loginctl', [
581
+ 'enable-linger',
582
+ user,
583
+ ]);
584
+ if (result.error || result.status !== 0)
585
+ write(`Linger could not be enabled, so MAMA stops when you log out. Run once: sudo loginctl enable-linger ${user}`);
586
+ }
587
+ await (0, service_js_1.installService)([], serviceOptions);
588
+ await (0, service_js_1.startService)(serviceOptions);
589
+ });
590
+ // The port was free when the setup was written; a reboot or another account's MAMA started
591
+ // since may have taken it. This install's own daemon, if it runs, holds it legitimately.
592
+ const ownDaemon = await (options.daemonStatus ?? (() => (0, daemon_js_1.askDaemon)('status', { home })))().catch(() => null);
593
+ const answers = options.portAnswers ?? diagnostics_js_1.portAnswers;
594
+ if (ownDaemon?.state !== 'running') {
595
+ const stored = (0, viewer_server_js_1.resolveApiPort)(progress.viewer?.MAMA_API_PORT);
596
+ if (await answers(stored, '127.0.0.1')) {
597
+ const port = await freeViewerPort(answers);
598
+ // Named even when it is the default, so it replaces the port start.sh carries.
599
+ progress.viewer = { ...progress.viewer, MAMA_API_PORT: String(port) };
600
+ (0, init_state_js_1.writeInitState)(root, progress);
601
+ }
602
+ }
603
+ await startServiceFor(progress.viewer ?? {});
604
+ const viewerPort = (0, viewer_server_js_1.resolveApiPort)(progress.viewer?.MAMA_API_PORT);
605
+ await waitForBoot({
606
+ status: options.daemonStatus ?? (() => (0, daemon_js_1.askDaemon)('status', { home })),
607
+ health: options.health ?? (() => viewerHealth(viewerPort)),
608
+ timeoutMs: options.bootTimeoutMs ?? 600_000,
609
+ pollMs: options.pollMs ?? 1_000,
610
+ write,
611
+ });
612
+ // The backend's version and login were read from this shell. One real turn through the
613
+ // daemon proves it answers as the service runs it: its PATH, environment, sandbox and model.
614
+ // The step is done only then, so running the install command again checks once more after a
615
+ // fix.
616
+ write('Checking the backend with one real turn through MAMA…');
617
+ // A turn that keeps progressing may run until agent.max_turn_ms, and the daemon answers with its
618
+ // own error before then; wait a little longer than that.
619
+ const timeoutMs = (0, config_js_1.loadConfig)({ home, path: configPath }).agent.max_turn_ms + PROBE_MARGIN_MS;
620
+ const probe = await (options.daemonProbe ?? (() => (0, daemon_js_1.askDaemon)('probe', { home, timeoutMs })))().catch((error) => {
621
+ throw new prompt_js_1.CliInputError(`MAMA did not answer the backend check (${error instanceof Error ? error.message : 'no answer'}). mama logs shows why; then run the install command again.`);
622
+ });
623
+ if (probe.state !== 'running')
624
+ throw new prompt_js_1.CliInputError('MAMA stopped before its backend could be checked. mama logs shows why; then run the install command again.');
625
+ if (probe.reply.ok !== true)
626
+ throw new prompt_js_1.CliInputError(`The backend did not answer a real turn: ${String(probe.reply.error)}. mama logs shows more, and mama doctor checks the backend's login; fix it, then run the install command again.`);
627
+ const probed = probe.reply.probe;
628
+ write(`${String(probed.backend)} (${String(probed.model)}) answered a real turn in ${String(probed.ms)} ms.`);
629
+ delete progress.viewer;
630
+ mark('service');
419
631
  }
420
- prompt.write('Document toolkit installed for owner and member sessions.');
632
+ write('MAMA is running. Send your bot a message in Telegram.');
421
633
  }
422
634
  //# sourceMappingURL=init.js.map