@jungjaehoon/mama-os 0.78.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 (288) hide show
  1. package/CHANGELOG.md +311 -0
  2. package/README.md +38 -24
  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.map +1 -1
  19. package/dist/api/source-actions.d.ts +9 -0
  20. package/dist/api/source-actions.d.ts.map +1 -1
  21. package/dist/api/source-actions.js +49 -0
  22. package/dist/api/source-actions.js.map +1 -1
  23. package/dist/api/viewer-request-security.d.ts +14 -1
  24. package/dist/api/viewer-request-security.d.ts.map +1 -1
  25. package/dist/api/viewer-request-security.js +26 -3
  26. package/dist/api/viewer-request-security.js.map +1 -1
  27. package/dist/api/viewer-server.d.ts +7 -0
  28. package/dist/api/viewer-server.d.ts.map +1 -1
  29. package/dist/api/viewer-server.js +32 -0
  30. package/dist/api/viewer-server.js.map +1 -1
  31. package/dist/api/work-actions.d.ts +19 -2
  32. package/dist/api/work-actions.d.ts.map +1 -1
  33. package/dist/api/work-actions.js +15 -0
  34. package/dist/api/work-actions.js.map +1 -1
  35. package/dist/cli/commands/backup.d.ts +7 -6
  36. package/dist/cli/commands/backup.d.ts.map +1 -1
  37. package/dist/cli/commands/backup.js +44 -7
  38. package/dist/cli/commands/backup.js.map +1 -1
  39. package/dist/cli/commands/control.d.ts +22 -4
  40. package/dist/cli/commands/control.d.ts.map +1 -1
  41. package/dist/cli/commands/control.js +172 -11
  42. package/dist/cli/commands/control.js.map +1 -1
  43. package/dist/cli/commands/daemon.d.ts +13 -2
  44. package/dist/cli/commands/daemon.d.ts.map +1 -1
  45. package/dist/cli/commands/daemon.js +211 -5
  46. package/dist/cli/commands/daemon.js.map +1 -1
  47. package/dist/cli/commands/init-questions.d.ts +98 -0
  48. package/dist/cli/commands/init-questions.d.ts.map +1 -0
  49. package/dist/cli/commands/init-questions.js +420 -0
  50. package/dist/cli/commands/init-questions.js.map +1 -0
  51. package/dist/cli/commands/init-section.d.ts +13 -0
  52. package/dist/cli/commands/init-section.d.ts.map +1 -0
  53. package/dist/cli/commands/init-section.js +375 -0
  54. package/dist/cli/commands/init-section.js.map +1 -0
  55. package/dist/cli/commands/init.d.ts +56 -2
  56. package/dist/cli/commands/init.d.ts.map +1 -1
  57. package/dist/cli/commands/init.js +562 -351
  58. package/dist/cli/commands/init.js.map +1 -1
  59. package/dist/cli/commands/node-move.d.ts +20 -0
  60. package/dist/cli/commands/node-move.d.ts.map +1 -0
  61. package/dist/cli/commands/node-move.js +289 -0
  62. package/dist/cli/commands/node-move.js.map +1 -0
  63. package/dist/cli/commands/register-owner.d.ts +3 -0
  64. package/dist/cli/commands/register-owner.d.ts.map +1 -1
  65. package/dist/cli/commands/register-owner.js +2 -0
  66. package/dist/cli/commands/register-owner.js.map +1 -1
  67. package/dist/cli/commands/restore.d.ts +2 -0
  68. package/dist/cli/commands/restore.d.ts.map +1 -1
  69. package/dist/cli/commands/restore.js +7 -0
  70. package/dist/cli/commands/restore.js.map +1 -1
  71. package/dist/cli/commands/service.d.ts +16 -0
  72. package/dist/cli/commands/service.d.ts.map +1 -1
  73. package/dist/cli/commands/service.js +36 -8
  74. package/dist/cli/commands/service.js.map +1 -1
  75. package/dist/cli/commands/tools.d.ts +1 -1
  76. package/dist/cli/commands/tools.d.ts.map +1 -1
  77. package/dist/cli/commands/tools.js +8 -3
  78. package/dist/cli/commands/tools.js.map +1 -1
  79. package/dist/cli/commands/uninstall.d.ts.map +1 -1
  80. package/dist/cli/commands/uninstall.js +78 -5
  81. package/dist/cli/commands/uninstall.js.map +1 -1
  82. package/dist/cli/commands/update.d.ts +59 -0
  83. package/dist/cli/commands/update.d.ts.map +1 -0
  84. package/dist/cli/commands/update.js +948 -0
  85. package/dist/cli/commands/update.js.map +1 -0
  86. package/dist/cli/index.js +8 -4
  87. package/dist/cli/index.js.map +1 -1
  88. package/dist/cli/launch-files.d.ts +36 -0
  89. package/dist/cli/launch-files.d.ts.map +1 -1
  90. package/dist/cli/launch-files.js +94 -1
  91. package/dist/cli/launch-files.js.map +1 -1
  92. package/dist/cli/prompt.d.ts +2 -1
  93. package/dist/cli/prompt.d.ts.map +1 -1
  94. package/dist/cli/prompt.js +5 -2
  95. package/dist/cli/prompt.js.map +1 -1
  96. package/dist/cli/runtime/backup.d.ts +17 -1
  97. package/dist/cli/runtime/backup.d.ts.map +1 -1
  98. package/dist/cli/runtime/backup.js +32 -4
  99. package/dist/cli/runtime/backup.js.map +1 -1
  100. package/dist/cli/runtime/daemon-control.d.ts +92 -7
  101. package/dist/cli/runtime/daemon-control.d.ts.map +1 -1
  102. package/dist/cli/runtime/daemon-control.js +183 -8
  103. package/dist/cli/runtime/daemon-control.js.map +1 -1
  104. package/dist/cli/runtime/default-roots.d.ts +27 -0
  105. package/dist/cli/runtime/default-roots.d.ts.map +1 -0
  106. package/dist/cli/runtime/default-roots.js +61 -0
  107. package/dist/cli/runtime/default-roots.js.map +1 -0
  108. package/dist/cli/runtime/diagnostics.d.ts +25 -0
  109. package/dist/cli/runtime/diagnostics.d.ts.map +1 -1
  110. package/dist/cli/runtime/diagnostics.js +249 -63
  111. package/dist/cli/runtime/diagnostics.js.map +1 -1
  112. package/dist/cli/runtime/init-state.d.ts +20 -0
  113. package/dist/cli/runtime/init-state.d.ts.map +1 -0
  114. package/dist/cli/runtime/init-state.js +51 -0
  115. package/dist/cli/runtime/init-state.js.map +1 -0
  116. package/dist/cli/runtime/install-kind.d.ts +54 -0
  117. package/dist/cli/runtime/install-kind.d.ts.map +1 -0
  118. package/dist/cli/runtime/install-kind.js +164 -0
  119. package/dist/cli/runtime/install-kind.js.map +1 -0
  120. package/dist/cli/runtime/node-move-journal.d.ts +44 -0
  121. package/dist/cli/runtime/node-move-journal.d.ts.map +1 -0
  122. package/dist/cli/runtime/node-move-journal.js +48 -0
  123. package/dist/cli/runtime/node-move-journal.js.map +1 -0
  124. package/dist/cli/runtime/release-check.d.ts +94 -0
  125. package/dist/cli/runtime/release-check.d.ts.map +1 -0
  126. package/dist/cli/runtime/release-check.js +316 -0
  127. package/dist/cli/runtime/release-check.js.map +1 -0
  128. package/dist/cli/runtime/setup-page.d.ts +57 -0
  129. package/dist/cli/runtime/setup-page.d.ts.map +1 -0
  130. package/dist/cli/runtime/setup-page.js +266 -0
  131. package/dist/cli/runtime/setup-page.js.map +1 -0
  132. package/dist/cli/runtime/source-tools.d.ts +60 -0
  133. package/dist/cli/runtime/source-tools.d.ts.map +1 -0
  134. package/dist/cli/runtime/source-tools.js +170 -0
  135. package/dist/cli/runtime/source-tools.js.map +1 -0
  136. package/dist/cli/runtime/telegram-pairing.d.ts +73 -0
  137. package/dist/cli/runtime/telegram-pairing.d.ts.map +1 -0
  138. package/dist/cli/runtime/telegram-pairing.js +184 -0
  139. package/dist/cli/runtime/telegram-pairing.js.map +1 -0
  140. package/dist/cli/runtime/update-journal.d.ts +135 -0
  141. package/dist/cli/runtime/update-journal.d.ts.map +1 -0
  142. package/dist/cli/runtime/update-journal.js +144 -0
  143. package/dist/cli/runtime/update-journal.js.map +1 -0
  144. package/dist/cli/runtime/update-progress.d.ts +95 -0
  145. package/dist/cli/runtime/update-progress.d.ts.map +1 -0
  146. package/dist/cli/runtime/update-progress.js +154 -0
  147. package/dist/cli/runtime/update-progress.js.map +1 -0
  148. package/dist/cli/runtime/update-request.d.ts +58 -0
  149. package/dist/cli/runtime/update-request.d.ts.map +1 -0
  150. package/dist/cli/runtime/update-request.js +172 -0
  151. package/dist/cli/runtime/update-request.js.map +1 -0
  152. package/dist/connectors/calendar/index.d.ts +5 -1
  153. package/dist/connectors/calendar/index.d.ts.map +1 -1
  154. package/dist/connectors/calendar/index.js +19 -28
  155. package/dist/connectors/calendar/index.js.map +1 -1
  156. package/dist/connectors/chatwork/index.d.ts.map +1 -1
  157. package/dist/connectors/chatwork/index.js +7 -3
  158. package/dist/connectors/chatwork/index.js.map +1 -1
  159. package/dist/connectors/config-loader.d.ts +5 -0
  160. package/dist/connectors/config-loader.d.ts.map +1 -1
  161. package/dist/connectors/config-loader.js +8 -0
  162. package/dist/connectors/config-loader.js.map +1 -1
  163. package/dist/connectors/discord/index.d.ts.map +1 -1
  164. package/dist/connectors/discord/index.js +4 -3
  165. package/dist/connectors/discord/index.js.map +1 -1
  166. package/dist/connectors/drive/index.d.ts.map +1 -1
  167. package/dist/connectors/drive/index.js +15 -13
  168. package/dist/connectors/drive/index.js.map +1 -1
  169. package/dist/connectors/framework/gws-utils.d.ts +62 -0
  170. package/dist/connectors/framework/gws-utils.d.ts.map +1 -1
  171. package/dist/connectors/framework/gws-utils.js +133 -3
  172. package/dist/connectors/framework/gws-utils.js.map +1 -1
  173. package/dist/connectors/framework/types.d.ts +24 -0
  174. package/dist/connectors/framework/types.d.ts.map +1 -1
  175. package/dist/connectors/framework/types.js +9 -0
  176. package/dist/connectors/framework/types.js.map +1 -1
  177. package/dist/connectors/gmail/index.d.ts.map +1 -1
  178. package/dist/connectors/gmail/index.js +14 -13
  179. package/dist/connectors/gmail/index.js.map +1 -1
  180. package/dist/connectors/index.d.ts +4 -4
  181. package/dist/connectors/index.d.ts.map +1 -1
  182. package/dist/connectors/index.js +4 -10
  183. package/dist/connectors/index.js.map +1 -1
  184. package/dist/connectors/notion/index.d.ts.map +1 -1
  185. package/dist/connectors/notion/index.js +5 -3
  186. package/dist/connectors/notion/index.js.map +1 -1
  187. package/dist/connectors/sheets/index.d.ts.map +1 -1
  188. package/dist/connectors/sheets/index.js +17 -12
  189. package/dist/connectors/sheets/index.js.map +1 -1
  190. package/dist/connectors/slack/index.d.ts.map +1 -1
  191. package/dist/connectors/slack/index.js +9 -3
  192. package/dist/connectors/slack/index.js.map +1 -1
  193. package/dist/connectors/telegram/index.d.ts.map +1 -1
  194. package/dist/connectors/telegram/index.js +4 -3
  195. package/dist/connectors/telegram/index.js.map +1 -1
  196. package/dist/connectors/trello/index.d.ts +1 -1
  197. package/dist/connectors/trello/index.d.ts.map +1 -1
  198. package/dist/connectors/trello/index.js +10 -5
  199. package/dist/connectors/trello/index.js.map +1 -1
  200. package/dist/gateways/discord.d.ts +6 -0
  201. package/dist/gateways/discord.d.ts.map +1 -1
  202. package/dist/gateways/discord.js +38 -27
  203. package/dist/gateways/discord.js.map +1 -1
  204. package/dist/gateways/slack.d.ts +6 -0
  205. package/dist/gateways/slack.d.ts.map +1 -1
  206. package/dist/gateways/slack.js +38 -27
  207. package/dist/gateways/slack.js.map +1 -1
  208. package/dist/gateways/telegram-commands.d.ts +124 -0
  209. package/dist/gateways/telegram-commands.d.ts.map +1 -0
  210. package/dist/gateways/telegram-commands.js +131 -0
  211. package/dist/gateways/telegram-commands.js.map +1 -0
  212. package/dist/gateways/telegram.d.ts +20 -0
  213. package/dist/gateways/telegram.d.ts.map +1 -1
  214. package/dist/gateways/telegram.js +138 -34
  215. package/dist/gateways/telegram.js.map +1 -1
  216. package/dist/gateways/turn-contract.d.ts +11 -0
  217. package/dist/gateways/turn-contract.d.ts.map +1 -1
  218. package/dist/runtime/action-surface.d.ts +1 -1
  219. package/dist/runtime/action-surface.d.ts.map +1 -1
  220. package/dist/runtime/action-surface.js +10 -0
  221. package/dist/runtime/action-surface.js.map +1 -1
  222. package/dist/runtime/backend-security.d.ts +23 -2
  223. package/dist/runtime/backend-security.d.ts.map +1 -1
  224. package/dist/runtime/backend-security.js +50 -5
  225. package/dist/runtime/backend-security.js.map +1 -1
  226. package/dist/runtime/member-paths.d.ts +15 -7
  227. package/dist/runtime/member-paths.d.ts.map +1 -1
  228. package/dist/runtime/member-paths.js +64 -12
  229. package/dist/runtime/member-paths.js.map +1 -1
  230. package/dist/runtime/member-session.d.ts +4 -0
  231. package/dist/runtime/member-session.d.ts.map +1 -1
  232. package/dist/runtime/member-session.js +23 -2
  233. package/dist/runtime/member-session.js.map +1 -1
  234. package/dist/runtime/native-session.d.ts +2 -0
  235. package/dist/runtime/native-session.d.ts.map +1 -1
  236. package/dist/runtime/native-session.js +1 -0
  237. package/dist/runtime/native-session.js.map +1 -1
  238. package/dist/runtime/owner-runtime.d.ts +63 -1
  239. package/dist/runtime/owner-runtime.d.ts.map +1 -1
  240. package/dist/runtime/owner-runtime.js +180 -3
  241. package/dist/runtime/owner-runtime.js.map +1 -1
  242. package/dist/runtime/owner-system-prompt.d.ts.map +1 -1
  243. package/dist/runtime/owner-system-prompt.js +0 -1
  244. package/dist/runtime/owner-system-prompt.js.map +1 -1
  245. package/dist/runtime/principal-access.d.ts +2 -0
  246. package/dist/runtime/principal-access.d.ts.map +1 -1
  247. package/dist/runtime/principal-access.js +7 -0
  248. package/dist/runtime/principal-access.js.map +1 -1
  249. package/dist/runtime/principal-sessions.d.ts.map +1 -1
  250. package/dist/runtime/principal-sessions.js +8 -1
  251. package/dist/runtime/principal-sessions.js.map +1 -1
  252. package/dist/runtime/record-orders.d.ts.map +1 -1
  253. package/dist/runtime/record-orders.js +3 -29
  254. package/dist/runtime/record-orders.js.map +1 -1
  255. package/dist/runtime/report-scheduler.d.ts +9 -2
  256. package/dist/runtime/report-scheduler.d.ts.map +1 -1
  257. package/dist/runtime/report-scheduler.js +37 -10
  258. package/dist/runtime/report-scheduler.js.map +1 -1
  259. package/dist/runtime/stimulus-delivery.d.ts +3 -0
  260. package/dist/runtime/stimulus-delivery.d.ts.map +1 -1
  261. package/dist/runtime/stimulus-delivery.js +19 -3
  262. package/dist/runtime/stimulus-delivery.js.map +1 -1
  263. package/dist/runtime/tools-paths.d.ts +7 -0
  264. package/dist/runtime/tools-paths.d.ts.map +1 -1
  265. package/dist/runtime/tools-paths.js +11 -5
  266. package/dist/runtime/tools-paths.js.map +1 -1
  267. package/dist/runtime/upkeep-close.d.ts +17 -0
  268. package/dist/runtime/upkeep-close.d.ts.map +1 -0
  269. package/dist/runtime/upkeep-close.js +127 -0
  270. package/dist/runtime/upkeep-close.js.map +1 -0
  271. package/dist/runtime/upkeep-orders.d.ts +124 -0
  272. package/dist/runtime/upkeep-orders.d.ts.map +1 -0
  273. package/dist/runtime/upkeep-orders.js +310 -0
  274. package/dist/runtime/upkeep-orders.js.map +1 -0
  275. package/dist/runtime/upkeep-reconcile.d.ts +25 -0
  276. package/dist/runtime/upkeep-reconcile.d.ts.map +1 -0
  277. package/dist/runtime/upkeep-reconcile.js +231 -0
  278. package/dist/runtime/upkeep-reconcile.js.map +1 -0
  279. package/dist/runtime/upkeep-writeback.d.ts +22 -0
  280. package/dist/runtime/upkeep-writeback.d.ts.map +1 -0
  281. package/dist/runtime/upkeep-writeback.js +203 -0
  282. package/dist/runtime/upkeep-writeback.js.map +1 -0
  283. package/dist/utils/untrusted-content.d.ts.map +1 -1
  284. package/dist/utils/untrusted-content.js +8 -1
  285. package/dist/utils/untrusted-content.js.map +1 -1
  286. package/install/install.sh +457 -0
  287. package/install/node.json +9 -0
  288. package/package.json +3 -4
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultRoots = defaultRoots;
4
+ exports.prepareRoot = prepareRoot;
5
+ const node_fs_1 = require("node:fs");
6
+ const node_os_1 = require("node:os");
7
+ const node_path_1 = require("node:path");
8
+ const prompt_js_1 = require("../prompt.js");
9
+ /**
10
+ * Where a new install keeps its member folders and its document tools: outside HOME, one place
11
+ * per person on the machine (plan decision 3). On macOS a folder of this user's sits directly in
12
+ * the sticky Shared folder beside the home folders, so no account uses a parent another set up;
13
+ * on Linux it is /var/lib/mama/<user>, which the installer creates once with sudo.
14
+ */
15
+ function defaultRoots(options = {}) {
16
+ const platform = options.platform ?? process.platform;
17
+ const user = options.user ?? (0, node_os_1.userInfo)().username;
18
+ if (!/^[A-Za-z0-9][A-Za-z0-9._-]*$/.test(user))
19
+ return undefined;
20
+ const base = platform === 'darwin'
21
+ ? (0, node_path_1.join)((0, node_path_1.dirname)(options.home ?? (0, node_os_1.homedir)()), 'Shared', `mama-${user}`)
22
+ : platform === 'linux'
23
+ ? (0, node_path_1.join)('/var/lib/mama', user)
24
+ : undefined;
25
+ return base === undefined
26
+ ? undefined
27
+ : { memberRoot: (0, node_path_1.join)(base, 'members'), toolsRoot: (0, node_path_1.join)(base, 'tools') };
28
+ }
29
+ /**
30
+ * Make a root that this user alone owns and writes, or refuse one that is not: the agent runs
31
+ * what tools_root holds and reads what member_root holds, and the Shared folder lets any account
32
+ * create a folder first. `ownParent` also checks the per-user folder a default sits in. A root
33
+ * this account cannot create (Linux's /var/lib/mama) names the one sudo step that makes its
34
+ * per-user folder; the subfolders are made here.
35
+ */
36
+ function prepareRoot(path, options = {}) {
37
+ const uid = options.uid ?? process.getuid?.();
38
+ if (!(0, node_fs_1.existsSync)(path)) {
39
+ try {
40
+ (0, node_fs_1.mkdirSync)(path, { recursive: true, mode: 0o700 });
41
+ }
42
+ catch (error) {
43
+ const code = error.code;
44
+ if (code !== 'EACCES' && code !== 'EPERM' && code !== 'EROFS')
45
+ throw error;
46
+ const folder = options.ownParent ? (0, node_path_1.dirname)(path) : path;
47
+ throw new prompt_js_1.CliInputError(`${path} cannot be created. Create ${folder} for this account once: sudo install -d -o "$USER" -m 0700 ${folder}`);
48
+ }
49
+ }
50
+ for (const folder of options.ownParent ? [(0, node_path_1.dirname)(path), path] : [path]) {
51
+ const stat = (0, node_fs_1.lstatSync)(folder);
52
+ if (stat.isSymbolicLink() || !stat.isDirectory())
53
+ throw new prompt_js_1.CliInputError(`${folder} must be a folder, not a link or a file`);
54
+ if (uid !== undefined && stat.uid !== uid)
55
+ throw new prompt_js_1.CliInputError(`${folder} belongs to another account; choose another folder`);
56
+ if ((stat.mode & 0o022) !== 0)
57
+ throw new prompt_js_1.CliInputError(`${folder} can be written by other accounts; run chmod 700 on it`);
58
+ }
59
+ (0, node_fs_1.accessSync)(path, node_fs_1.constants.W_OK);
60
+ }
61
+ //# sourceMappingURL=default-roots.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"default-roots.js","sourceRoot":"","sources":["../../../src/cli/runtime/default-roots.ts"],"names":[],"mappings":"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"}
@@ -12,6 +12,12 @@ export interface Check {
12
12
  readonly context?: string;
13
13
  /** What to do about a warn or fail. */
14
14
  readonly fix?: string;
15
+ /**
16
+ * A stable name for this finding where its detail carries values that an update or a backend
17
+ * self-update changes (versions, schema numbers). A check without one is the same finding
18
+ * while its name and detail are; `mama update` compares failures before and after by these.
19
+ */
20
+ readonly code?: string;
15
21
  }
16
22
  export interface DiagnosticsOptions {
17
23
  home?: string;
@@ -24,6 +30,8 @@ export interface DiagnosticsOptions {
24
30
  /** A Telegram Bot API call; tests replace it. */
25
31
  telegramCall?: (token: string, method: string, params?: Record<string, string>) => Promise<Record<string, unknown>>;
26
32
  findIn?: (name: string, dirs: string[]) => string | undefined;
33
+ /** Where this shell finds a command; tests replace it. */
34
+ findExecutable?: (name: string) => string | undefined;
27
35
  /** Ask the install's daemon for its status; tests replace it. */
28
36
  askStatus?: () => Promise<DaemonControlReply>;
29
37
  /** Builds a source as boot does; tests replace it so no source reaches the network. */
@@ -33,6 +41,12 @@ export interface DiagnosticsOptions {
33
41
  /** Whether something listens on the viewer's address; tests replace it. */
34
42
  portInUse?: (port: number, host: string) => Promise<boolean>;
35
43
  }
44
+ /** auth.env's assignments, unquoted as sh reads them; values are never printed. */
45
+ export declare function readAuthEnv(root: string): Record<string, string>;
46
+ export declare function defaultTelegramCall(token: string, method: string, params?: Record<string, string>, timeoutMs?: number): Promise<Record<string, unknown>>;
47
+ /** True when something listens; a refusal means free; any other error (a host that cannot be
48
+ * reached or bound) rejects, since the viewer would fail on it too. */
49
+ export declare function portAnswers(port: number, host: string): Promise<boolean>;
36
50
  /**
37
51
  * Check an install as its service would run it. Nothing here writes MAMA's files or starts a
38
52
  * service; the database is opened read-only (which can leave its -wal and -shm beside it), and the
@@ -40,4 +54,15 @@ export interface DiagnosticsOptions {
40
54
  * Each check names what it found and, when it is not ok, what to do.
41
55
  */
42
56
  export declare function runDiagnostics(options?: DiagnosticsOptions): Promise<Check[]>;
57
+ /**
58
+ * start.sh's Node: still there, outside one version's folder, and able to load MAMA's SQLite. On
59
+ * an install the install command made (its Node in `<root>/node`), also the Node the installed
60
+ * release pins in its install/node.json: running the install command again moves to that one.
61
+ */
62
+ export declare function nodeCheck(node: string, cli: string, run: ProbeRun, root: string): Check;
63
+ export declare function telegramCheck(token: string | undefined, commonChat: string | number | undefined, call: NonNullable<DiagnosticsOptions['telegramCall']>): Promise<Check>;
64
+ /** The environment start.sh gives the daemon: auth.env, then its own exports, then its PATH. */
65
+ export declare function serviceEnvironment(home: string, auth: Record<string, string>, exports: Record<string, string>, path: readonly string[]): NodeJS.ProcessEnv;
66
+ /** Run with process.env as the daemon's process has it, then put this shell's back. */
67
+ export declare function withEnvironment<T>(env: NodeJS.ProcessEnv, run: () => Promise<T>): Promise<T>;
43
68
  //# sourceMappingURL=diagnostics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/cli/runtime/diagnostics.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAyB1D,OAAO,EAAkC,KAAK,QAAQ,EAAmB,MAAM,oBAAoB,CAAC;AACpG,OAAO,EAAsB,KAAK,kBAAkB,EAAE,MAAM,qBAAqB,CAAC;AAOlF,OAAO,EAAoB,KAAK,UAAU,EAAqB,MAAM,sBAAsB,CAAC;AAE5F,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;AAEjD,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;IAC7B,mFAAmF;IACnF,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,uCAAuC;IACvC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC,QAAQ,CAAC;IAC3B,GAAG,CAAC,EAAE,QAAQ,CAAC;IACf,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,0EAA0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,iDAAiD;IACjD,YAAY,CAAC,EAAE,CACb,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAC5B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACtC,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,GAAG,SAAS,CAAC;IAC9D,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,kBAAkB,CAAC,CAAC;IAC9C,uFAAuF;IACvF,UAAU,CAAC,EAAE,OAAO,aAAa,CAAC;IAClC,wEAAwE;IACxE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,2EAA2E;IAC3E,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;CAC9D;AAiFD;;;;;GAKG;AACH,wBAAsB,cAAc,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,KAAK,EAAE,CAAC,CAmSvF"}
1
+ {"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../../src/cli/runtime/diagnostics.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.readAuthEnv = readAuthEnv;
4
+ exports.defaultTelegramCall = defaultTelegramCall;
5
+ exports.portAnswers = portAnswers;
3
6
  exports.runDiagnostics = runDiagnostics;
7
+ exports.nodeCheck = nodeCheck;
8
+ exports.telegramCheck = telegramCheck;
9
+ exports.serviceEnvironment = serviceEnvironment;
10
+ exports.withEnvironment = withEnvironment;
4
11
  const node_fs_1 = require("node:fs");
5
12
  const node_module_1 = require("node:module");
6
13
  const node_net_1 = require("node:net");
@@ -15,28 +22,19 @@ const timezone_js_1 = require("../../runtime/timezone.js");
15
22
  const document_toolkit_js_1 = require("../../tools/document-toolkit.js");
16
23
  const launch_files_js_1 = require("../launch-files.js");
17
24
  const rotating_log_js_1 = require("../../runtime/rotating-log.js");
18
- const secrets_js_1 = require("../secrets.js");
19
25
  const daemon_js_1 = require("../commands/daemon.js");
20
26
  const backend_probe_js_1 = require("./backend-probe.js");
21
27
  const daemon_control_js_1 = require("./daemon-control.js");
28
+ const release_check_js_1 = require("./release-check.js");
29
+ const install_kind_js_1 = require("./install-kind.js");
30
+ const source_tools_js_1 = require("./source-tools.js");
31
+ const node_move_journal_js_1 = require("./node-move-journal.js");
32
+ const update_journal_js_1 = require("./update-journal.js");
22
33
  const db_manager_1 = require("@jungjaehoon/mama-core/db-manager");
23
34
  const migrations_source_js_1 = require("../../storage/migrations-source.js");
24
35
  const systemd_service_js_1 = require("./systemd-service.js");
25
- /** The PATH a service manager gives a unit that does not set one ($PATH in start.sh). */
26
- const SERVICE_DEFAULT_PATH = {
27
- darwin: ['/usr/bin', '/bin', '/usr/sbin', '/sbin'],
28
- linux: ['/usr/local/sbin', '/usr/local/bin', '/usr/sbin', '/usr/bin', '/sbin', '/bin'],
29
- };
30
36
  /** systemd states of an installed unit that can be started or is running. */
31
37
  const SYSTEMD_USABLE = new Set(['active', 'inactive', 'activating', 'deactivating', 'reloading']);
32
- function findIn(name, dirs) {
33
- for (const dir of dirs) {
34
- const path = (0, node_path_1.join)(dir, name);
35
- if ((0, node_fs_1.existsSync)(path))
36
- return path;
37
- }
38
- return undefined;
39
- }
40
38
  function message(error) {
41
39
  return error instanceof Error ? error.message : String(error);
42
40
  }
@@ -64,9 +62,9 @@ function readAuthEnv(root) {
64
62
  });
65
63
  return values;
66
64
  }
67
- async function defaultTelegramCall(token, method, params = {}) {
65
+ async function defaultTelegramCall(token, method, params = {}, timeoutMs = 8_000) {
68
66
  const query = new URLSearchParams(params).toString();
69
- const response = await fetch(`https://api.telegram.org/bot${token}/${method}${query ? `?${query}` : ''}`, { signal: AbortSignal.timeout(8_000) });
67
+ const response = await fetch(`https://api.telegram.org/bot${token}/${method}${query ? `?${query}` : ''}`, { signal: AbortSignal.timeout(timeoutMs) });
70
68
  return (await response.json());
71
69
  }
72
70
  /** True when something listens; a refusal means free; any other error (a host that cannot be
@@ -85,10 +83,17 @@ function portAnswers(port, host) {
85
83
  socket.once('error', (error) => error.code === 'ECONNREFUSED' ? resolve(false) : reject(error));
86
84
  });
87
85
  }
88
- function secretFix(name) {
89
- return secrets_js_1.SECRET_NAMES.includes(name) || (0, secrets_js_1.isIcalSecret)(name)
90
- ? `mama secret set ${name}`
91
- : `add ${name} to auth.env`;
86
+ /**
87
+ * A source's tokens are entered on the setup page by the sources section, run again for that
88
+ * source, which needs no terminal (install.md, W2.4.5e). Every token source is built only with
89
+ * the token name it reads, which auth.env holds.
90
+ */
91
+ function sourceSecretFixes(source, names, mama) {
92
+ return names.length === 0
93
+ ? []
94
+ : [
95
+ `with MAMA stopped, run ${mama} init --section connectors --non-interactive with ${source}'s block from connectors.json in MAMA_INIT_CONNECTORS; its setup page takes ${names.join(' and ')}`,
96
+ ];
92
97
  }
93
98
  /**
94
99
  * Check an install as its service would run it. Nothing here writes MAMA's files or starts a
@@ -102,7 +107,7 @@ async function runDiagnostics(options = {}) {
102
107
  const root = (0, node_path_1.dirname)(configPath);
103
108
  const platform = options.platform ?? process.platform;
104
109
  const run = options.run ?? backend_probe_js_1.defaultProbeRun;
105
- const locate = options.findIn ?? findIn;
110
+ const locate = options.findIn ?? launch_files_js_1.findIn;
106
111
  const checks = [];
107
112
  if ((0, node_fs_1.existsSync)((0, daemon_control_js_1.restoreJournalPath)(root))) {
108
113
  checks.push({
@@ -112,6 +117,7 @@ async function runDiagnostics(options = {}) {
112
117
  fix: 'mama restore --rollback',
113
118
  });
114
119
  }
120
+ checks.push(...updateChecks(root, home));
115
121
  let config = null;
116
122
  // Setting names can be anything the owner typed, so they stay out of the report.
117
123
  let ignored = [];
@@ -149,7 +155,10 @@ async function runDiagnostics(options = {}) {
149
155
  }
150
156
  // What the service runs: the start script's Node, PATH and exports, not this shell's.
151
157
  const startPath = (0, node_path_1.join)(root, 'start.sh');
152
- let servicePath = SERVICE_DEFAULT_PATH[platform] ?? [];
158
+ let servicePath = (0, launch_files_js_1.startScriptPath)(null, home, platform);
159
+ // The Node start.sh runs: on an install the install command made, a source's tool installs
160
+ // beside MAMA with that Node (install.md, PR 3).
161
+ let serviceNode = null;
153
162
  let exports = {};
154
163
  // A script mama did not write is assumed to load auth.env; a generated one must still.
155
164
  let loadsAuth = true;
@@ -186,13 +195,9 @@ async function runDiagnostics(options = {}) {
186
195
  fix: 'mama service install --replace rewrites it',
187
196
  });
188
197
  }
189
- if (reading.path !== null) {
190
- servicePath = [
191
- ...(0, launch_files_js_1.startPathValue)(reading.path, home),
192
- ...(/\$\{?PATH\}?/.test(reading.path) ? servicePath : []),
193
- ];
194
- }
198
+ servicePath = (0, launch_files_js_1.startScriptPath)(reading, home, platform);
195
199
  const [, node, cli] = reading.exec === null ? [] : (0, launch_files_js_1.shellWords)(reading.exec);
200
+ serviceNode = node ?? null;
196
201
  if (!reading.generated) {
197
202
  checks.push({
198
203
  name: 'start.sh',
@@ -210,7 +215,7 @@ async function runDiagnostics(options = {}) {
210
215
  });
211
216
  }
212
217
  else {
213
- checks.push(nodeCheck(node, cli, run));
218
+ checks.push(nodeCheck(node, cli, run, root));
214
219
  }
215
220
  }
216
221
  catch (error) {
@@ -238,14 +243,7 @@ async function runDiagnostics(options = {}) {
238
243
  fix: `correct the quoting in ${(0, node_path_1.join)(root, 'auth.env')}`,
239
244
  });
240
245
  }
241
- const serviceEnv = {
242
- HOME: home,
243
- USER: process.env.USER,
244
- LOGNAME: process.env.LOGNAME,
245
- ...auth,
246
- ...exports,
247
- PATH: servicePath.join(':'),
248
- };
246
+ const serviceEnv = serviceEnvironment(home, auth, exports, servicePath);
249
247
  const secret = (name) => Boolean(serviceEnv[name]?.trim());
250
248
  if (config) {
251
249
  const backend = config.agent.backend;
@@ -268,6 +266,7 @@ async function runDiagnostics(options = {}) {
268
266
  name: 'backend',
269
267
  status: 'fail',
270
268
  detail: probe.problem ?? `${backend} ${probe.version} is older than ${probe.minimum}`,
269
+ ...(probe.problem === null ? { code: 'backend.too_old' } : {}),
271
270
  fix: `update ${backend} to ${probe.minimum} or newer`,
272
271
  });
273
272
  }
@@ -276,6 +275,7 @@ async function runDiagnostics(options = {}) {
276
275
  name: 'backend',
277
276
  status: 'fail',
278
277
  detail: `${backend} ${probe.version} is not signed in for the service`,
278
+ code: 'backend.signed_out',
279
279
  fix: backend === 'claude' ? 'claude auth login' : `CODEX_HOME='${codexHome}' codex login`,
280
280
  });
281
281
  }
@@ -299,7 +299,7 @@ async function runDiagnostics(options = {}) {
299
299
  name: messenger,
300
300
  status: 'fail',
301
301
  detail: `${missing.join(' and ')} not set; boot stops at the messengers stage`,
302
- fix: missing.map(secretFix).join('; '),
302
+ fix: missing.map((name) => `mama secret set ${name}`).join('; '),
303
303
  });
304
304
  }
305
305
  else if (messenger === 'telegram' && !options.offline) {
@@ -309,7 +309,10 @@ async function runDiagnostics(options = {}) {
309
309
  checks.push({ name: messenger, status: 'ok', detail: 'secrets set' });
310
310
  }
311
311
  }
312
- checks.push(...(await sourceChecks(config, configPath, servicePath, locate, secret, {
312
+ checks.push(...(await sourceChecks(config, configPath, locate, secret, {
313
+ service: { path: servicePath, nodePath: serviceNode, exports },
314
+ findShell: options.findExecutable ?? launch_files_js_1.findExecutable,
315
+ home,
313
316
  env: serviceEnv,
314
317
  online: !options.offline,
315
318
  load: options.loadSource ?? index_js_1.loadConnector,
@@ -332,6 +335,7 @@ async function runDiagnostics(options = {}) {
332
335
  }
333
336
  checks.push(...(await daemonChecks(options.askStatus ?? (() => (0, daemon_js_1.askDaemon)('status', { home, configPath })), port, serviceEnv.MAMA_API_HOST ?? '127.0.0.1', options.portInUse ?? portAnswers)));
334
337
  checks.push(serviceCheck(platform, home, options.serviceRun ?? systemd_service_js_1.defaultServiceRun));
338
+ checks.push(releaseCheck(root, home));
335
339
  // Each log against its own cap: the daemon rotates daemon.log and security-events.jsonl before
336
340
  // they pass theirs, and start.sh empties crash.log at start past its own.
337
341
  for (const [name, path, cap] of [
@@ -416,6 +420,7 @@ function schemaCheck(databasePath) {
416
420
  name: 'schema',
417
421
  status: 'fail',
418
422
  detail: `the database is newer than this MAMA (${summary}); boot refuses it`,
423
+ code: 'schema.newer',
419
424
  fix: 'install the version that wrote it, or mama restore a backup taken with this one',
420
425
  };
421
426
  }
@@ -436,8 +441,12 @@ function schemaCheck(databasePath) {
436
441
  };
437
442
  }
438
443
  }
439
- /** start.sh's Node: still there, outside one version's folder, and able to load MAMA's SQLite. */
440
- function nodeCheck(node, cli, run) {
444
+ /**
445
+ * start.sh's Node: still there, outside one version's folder, and able to load MAMA's SQLite. On
446
+ * an install the install command made (its Node in `<root>/node`), also the Node the installed
447
+ * release pins in its install/node.json: running the install command again moves to that one.
448
+ */
449
+ function nodeCheck(node, cli, run, root) {
441
450
  const rewrite = 'mama service install rewrites start.sh with a Node path that survives upgrades';
442
451
  if (!(0, node_fs_1.existsSync)(node)) {
443
452
  return {
@@ -497,6 +506,29 @@ function nodeCheck(node, cli, run) {
497
506
  : 'run mama service install from a shell whose node is not an nvm, volta or fnm version',
498
507
  };
499
508
  }
509
+ if (node === (0, node_move_journal_js_1.managedNodePath)(root)) {
510
+ const packageRoot = (0, install_kind_js_1.packageRootOf)(cli);
511
+ if (packageRoot === null) {
512
+ return {
513
+ name: 'node',
514
+ status: 'fail',
515
+ detail: 'the MAMA command start.sh runs is not inside a MAMA OS package',
516
+ context: cli,
517
+ fix: 'run the install command again',
518
+ };
519
+ }
520
+ const pin = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(packageRoot, 'install', 'node.json'), 'utf8')).version;
521
+ if (version !== `v${pin}`) {
522
+ return {
523
+ name: 'node',
524
+ status: 'warn',
525
+ detail: `start.sh runs ${version}; this release pins Node.js ${pin}`,
526
+ code: 'node.pin',
527
+ context: node,
528
+ fix: `run the install command again, which moves it: ${node_move_journal_js_1.INSTALL_COMMAND}`,
529
+ };
530
+ }
531
+ }
500
532
  return { name: 'node', status: 'ok', detail: `${version} loads SQLite`, context: node };
501
533
  }
502
534
  function sandboxCheck(platform, servicePath, locate, run) {
@@ -619,8 +651,12 @@ async function telegramCheck(token, commonChat, call) {
619
651
  : 'token valid; a member of the common group that reads its messages',
620
652
  };
621
653
  }
654
+ /** The secrets a source's requirements name. */
655
+ function secretNames(needs) {
656
+ return needs.flatMap((need) => need.type === 'token' && need.tokenName !== undefined ? [need.tokenName] : []);
657
+ }
622
658
  /** connectors.json as the connector runtime reads it, and what each enabled source needs. */
623
- async function sourceChecks(config, configPath, servicePath, locate, secret, start) {
659
+ async function sourceChecks(config, configPath, locate, secret, start) {
624
660
  const paths = (0, daemon_js_1.pathsFor)(configPath, config);
625
661
  let plan;
626
662
  try {
@@ -646,14 +682,17 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
646
682
  }
647
683
  const checks = [];
648
684
  const timeZone = (0, timezone_js_1.createTimeZoneSetting)(config.timezone);
685
+ const loaded = [];
649
686
  for (const name of plan.enabledConnectorNames) {
650
- let connector;
651
687
  try {
652
- connector = await start.load(name, plan.config.config[name], (0, connectors_js_1.connectorLoadPaths)(name, {
653
- statePath: paths.connectorsRoot,
654
- kagemushaDbPath: paths.kagemushaDbPath,
655
- timeZone,
656
- }));
688
+ loaded.push({
689
+ name,
690
+ connector: await start.load(name, plan.config.config[name], (0, connectors_js_1.connectorLoadPaths)(name, {
691
+ statePath: paths.connectorsRoot,
692
+ kagemushaDbPath: paths.kagemushaDbPath,
693
+ timeZone,
694
+ })),
695
+ });
657
696
  }
658
697
  catch (error) {
659
698
  checks.push({
@@ -662,26 +701,39 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
662
701
  detail: 'cannot be built from connectors.json; boot stops at the connectors stage',
663
702
  context: message(error),
664
703
  });
665
- continue;
666
704
  }
667
- const needs = connector.getAuthRequirements();
705
+ }
706
+ const enabled = loaded.map(({ name, connector }) => ({
707
+ name,
708
+ connector,
709
+ needs: connector.getAuthRequirements(),
710
+ }));
711
+ for (const { name, connector, needs } of enabled) {
668
712
  // A secret's name can carry an owner's channel key (iCal), so it stays out of the report.
669
- const secrets = needs.flatMap((need) => need.type === 'token' && need.tokenName && !secret(need.tokenName) ? [need.tokenName] : []);
670
- const commands = needs.flatMap((need) => need.type === 'cli' && need.cli && !locate(need.cli, servicePath)
671
- ? [{ cli: need.cli, login: need.cliAuthCommand }]
672
- : []);
673
- if (secrets.length > 0 || commands.length > 0) {
713
+ const secrets = secretNames(needs).filter((tokenName) => !secret(tokenName));
714
+ const tools = (0, source_tools_js_1.sourceTools)([{ name, needs }], start.service, { root: (0, node_path_1.dirname)(configPath), home: start.home }, { inDirs: locate, shell: start.findShell },
715
+ // One login of a tool serves every source that runs it, so it names what all of them need.
716
+ enabled);
717
+ const missing = tools.filter((tool) => tool.servicePath === null);
718
+ // Paths stay out of `detail`, which --report shares.
719
+ const context = [
720
+ ...(secrets.length > 0 ? [secrets.join(', ')] : []),
721
+ ...missing.flatMap(({ tool, shellPath }) => shellPath === null ? [] : [`this shell finds ${tool.command} at ${shellPath}`]),
722
+ ];
723
+ if (secrets.length > 0 || missing.length > 0) {
674
724
  checks.push({
675
725
  name: `source ${name}`,
676
726
  status: 'fail',
677
727
  detail: [
678
728
  ...(secrets.length > 0 ? [`${secrets.length} secret(s) it needs are not set`] : []),
679
- ...commands.map(({ cli }) => `the ${cli} command is not on the service's PATH`),
729
+ ...missing.map(({ tool }) => `the ${tool.command} command is not on the service's PATH`),
680
730
  ].join('; '),
681
- ...(secrets.length > 0 ? { context: secrets.join(', ') } : {}),
731
+ ...(context.length > 0 ? { context: context.join('; ') } : {}),
682
732
  fix: [
683
- ...secrets.map(secretFix),
684
- ...commands.map(({ cli, login }) => `install ${cli}${login ? ` and run ${login}` : ''}`),
733
+ ...sourceSecretFixes(name, secrets, (0, launch_files_js_1.mamaCommand)((0, node_path_1.dirname)(configPath))),
734
+ ...missing.map(({ shellPath, install, login }) => shellPath === null
735
+ ? `install it: ${install.join(' or ')}; then log in: ${login}`
736
+ : `mama service install adds ${(0, node_path_1.dirname)(shellPath)} to start.sh's PATH, mama service restart applies it; then log in: ${login}`),
685
737
  ].join('; '),
686
738
  });
687
739
  continue;
@@ -702,6 +754,9 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
702
754
  status: 'fail',
703
755
  detail: 'does not start; boot stops at the connectors stage',
704
756
  context: message(error),
757
+ // The tool is on the service's PATH here, so a source that runs one most often
758
+ // lacks its sign-in or a scope (Calendar's read check).
759
+ ...(tools.length > 0 ? { fix: tools.map(({ login }) => login).join('; ') } : {}),
705
760
  };
706
761
  return;
707
762
  }
@@ -710,10 +765,10 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
710
765
  name: `source ${name}`,
711
766
  status: 'fail',
712
767
  detail: 'starts, but its credentials or sign-in are refused, so its polls fail',
713
- fix: needs
714
- .map((need) => need.cliAuthCommand ?? (need.tokenName ? secretFix(need.tokenName) : ''))
715
- .filter(Boolean)
716
- .join('; ') || undefined,
768
+ fix: [
769
+ ...tools.map(({ login }) => login),
770
+ ...sourceSecretFixes(name, secretNames(needs), (0, launch_files_js_1.mamaCommand)((0, node_path_1.dirname)(configPath))),
771
+ ].join('; ') || undefined,
717
772
  };
718
773
  }
719
774
  }));
@@ -745,6 +800,17 @@ function bounded(ms, run) {
745
800
  }),
746
801
  ]).finally(() => clearTimeout(timer));
747
802
  }
803
+ /** The environment start.sh gives the daemon: auth.env, then its own exports, then its PATH. */
804
+ function serviceEnvironment(home, auth, exports, path) {
805
+ return {
806
+ HOME: home,
807
+ USER: process.env.USER,
808
+ LOGNAME: process.env.LOGNAME,
809
+ ...auth,
810
+ ...exports,
811
+ PATH: path.join(':'),
812
+ };
813
+ }
748
814
  /** Run with process.env as the daemon's process has it, then put this shell's back. */
749
815
  async function withEnvironment(env, run) {
750
816
  const saved = { ...process.env };
@@ -903,6 +969,126 @@ async function daemonChecks(ask, port, host, portInUse) {
903
969
  }
904
970
  return checks;
905
971
  }
972
+ /**
973
+ * An unfinished `mama update --node` (docs/development/work/install.md, "Moving the managed
974
+ * Node"): its journal, which the install command finishes or undoes. It holds the install on
975
+ * update.hold, so it comes before the hold's own check.
976
+ */
977
+ function nodeMoveCheck(root) {
978
+ const path = (0, node_move_journal_js_1.nodeMovePlaces)(root).journal;
979
+ let journal;
980
+ try {
981
+ journal = (0, node_move_journal_js_1.readNodeMoveJournal)(path);
982
+ }
983
+ catch (error) {
984
+ return {
985
+ name: 'update',
986
+ status: 'fail',
987
+ detail: 'an unfinished move of the managed Node left a journal that cannot be read',
988
+ code: 'node_move.open',
989
+ context: message(error),
990
+ fix: `read ${path}; the install command's recovery needs it`,
991
+ };
992
+ }
993
+ if (journal === null)
994
+ return null;
995
+ return {
996
+ name: 'update',
997
+ status: 'fail',
998
+ detail: journal.phase === 'committed'
999
+ ? `the move of the managed Node to ${journal.to} took effect, but its old folders are not removed yet`
1000
+ : `the move of the managed Node from ${journal.from} to ${journal.to} is unfinished (step ${journal.step}); the daemon may stay stopped until it is finished or undone`,
1001
+ code: 'node_move.open',
1002
+ context: path,
1003
+ fix: `run the install command again, which finishes or undoes it: ${node_move_journal_js_1.INSTALL_COMMAND}`,
1004
+ };
1005
+ }
1006
+ /**
1007
+ * An unfinished `mama update`: its journal (docs/development/work/update.md, PR 3), with the
1008
+ * command that resolves it by absolute paths, or a staging prefix an updater killed before it
1009
+ * wrote its journal left behind.
1010
+ */
1011
+ function updateChecks(root, home) {
1012
+ const move = nodeMoveCheck(root);
1013
+ if (move !== null)
1014
+ return [move];
1015
+ const path = (0, update_journal_js_1.updatePaths)(root).journal;
1016
+ let journal;
1017
+ try {
1018
+ journal = (0, update_journal_js_1.readUpdateJournal)(path);
1019
+ }
1020
+ catch (error) {
1021
+ return [
1022
+ {
1023
+ name: 'update',
1024
+ status: 'fail',
1025
+ detail: 'an unfinished update left a journal that cannot be read',
1026
+ code: 'update.open',
1027
+ context: message(error),
1028
+ fix: `read ${path}; mama update --rollback needs it`,
1029
+ },
1030
+ ];
1031
+ }
1032
+ if (journal !== null) {
1033
+ return [
1034
+ {
1035
+ name: 'update',
1036
+ status: 'fail',
1037
+ detail: journal.phase === 'committed'
1038
+ ? `the update to ${journal.to} took effect, but it was not finished: its inputs may stay held, and nothing runs until the command below finishes it`
1039
+ : `the update from ${journal.from} to ${journal.to} is unfinished (${journal.phase}, step ${journal.step}); the daemon stays stopped or held until it is resolved`,
1040
+ code: 'update.open',
1041
+ fix: (0, update_journal_js_1.rollbackCommand)(journal),
1042
+ },
1043
+ ];
1044
+ }
1045
+ const hold = (0, update_journal_js_1.updatePaths)(root).hold;
1046
+ if ((0, node_fs_1.existsSync)(hold)) {
1047
+ return [
1048
+ {
1049
+ name: 'update',
1050
+ status: 'fail',
1051
+ detail: 'an update hold file is in place with no update journal; start.sh exits at once, so the daemon does not start',
1052
+ code: 'update.hold',
1053
+ context: hold,
1054
+ fix: `rm ${hold}, then mama service start`,
1055
+ },
1056
+ ];
1057
+ }
1058
+ const install = (0, install_kind_js_1.installKind)(root, home);
1059
+ const staging = install.kind === 'npm' ? (0, update_journal_js_1.leftoverStaging)((0, node_path_1.dirname)(install.packageRoot)) : [];
1060
+ return staging.length === 0
1061
+ ? []
1062
+ : [
1063
+ {
1064
+ name: 'update',
1065
+ status: 'warn',
1066
+ detail: 'an interrupted mama update left its staging folder; the install runs as before',
1067
+ code: 'update.staging',
1068
+ context: staging.join(', '),
1069
+ fix: 'the next mama update removes it',
1070
+ },
1071
+ ];
1072
+ }
1073
+ /** The daemon's last daily release check, from its file; doctor itself asks no registry. */
1074
+ function releaseCheck(root, home) {
1075
+ const report = (0, release_check_js_1.releaseReport)(root, home);
1076
+ if (report === null) {
1077
+ return {
1078
+ name: 'release',
1079
+ status: 'ok',
1080
+ detail: 'not checked yet; the running daemon checks once a day',
1081
+ };
1082
+ }
1083
+ return {
1084
+ name: 'release',
1085
+ status: report.status,
1086
+ detail: report.detail,
1087
+ ...(report.context === undefined ? {} : { context: report.context }),
1088
+ ...(report.steps === undefined ? {} : { fix: report.steps.join('; then ') }),
1089
+ ...(report.fix === undefined ? {} : { fix: report.fix }),
1090
+ };
1091
+ }
906
1092
  function serviceCheck(platform, home, serviceRun) {
907
1093
  let launchd;
908
1094
  let unit;