@jungjaehoon/mama-os 0.78.0 → 0.79.1

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 +341 -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 +110 -0
  48. package/dist/cli/commands/init-questions.d.ts.map +1 -0
  49. package/dist/cli/commands/init-questions.js +445 -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 +439 -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 +123 -15
  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 +250 -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,18 @@ 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. The page asks again for a `stored` token only
90
+ * when MAMA_INIT_REPLACE_SECRETS names it (#552).
91
+ */
92
+ function sourceSecretFixes(source, names, mama, stored) {
93
+ return names.length === 0
94
+ ? []
95
+ : [
96
+ `with MAMA stopped, run ${mama} init --section connectors --non-interactive with ${source}'s block from connectors.json in MAMA_INIT_CONNECTORS${stored ? ` and MAMA_INIT_REPLACE_SECRETS=${names.join(',')}` : ''}; its setup page takes ${names.join(' and ')}`,
97
+ ];
92
98
  }
93
99
  /**
94
100
  * Check an install as its service would run it. Nothing here writes MAMA's files or starts a
@@ -102,7 +108,7 @@ async function runDiagnostics(options = {}) {
102
108
  const root = (0, node_path_1.dirname)(configPath);
103
109
  const platform = options.platform ?? process.platform;
104
110
  const run = options.run ?? backend_probe_js_1.defaultProbeRun;
105
- const locate = options.findIn ?? findIn;
111
+ const locate = options.findIn ?? launch_files_js_1.findIn;
106
112
  const checks = [];
107
113
  if ((0, node_fs_1.existsSync)((0, daemon_control_js_1.restoreJournalPath)(root))) {
108
114
  checks.push({
@@ -112,6 +118,7 @@ async function runDiagnostics(options = {}) {
112
118
  fix: 'mama restore --rollback',
113
119
  });
114
120
  }
121
+ checks.push(...updateChecks(root, home));
115
122
  let config = null;
116
123
  // Setting names can be anything the owner typed, so they stay out of the report.
117
124
  let ignored = [];
@@ -149,7 +156,10 @@ async function runDiagnostics(options = {}) {
149
156
  }
150
157
  // What the service runs: the start script's Node, PATH and exports, not this shell's.
151
158
  const startPath = (0, node_path_1.join)(root, 'start.sh');
152
- let servicePath = SERVICE_DEFAULT_PATH[platform] ?? [];
159
+ let servicePath = (0, launch_files_js_1.startScriptPath)(null, home, platform);
160
+ // The Node start.sh runs: on an install the install command made, a source's tool installs
161
+ // beside MAMA with that Node (install.md, PR 3).
162
+ let serviceNode = null;
153
163
  let exports = {};
154
164
  // A script mama did not write is assumed to load auth.env; a generated one must still.
155
165
  let loadsAuth = true;
@@ -186,13 +196,9 @@ async function runDiagnostics(options = {}) {
186
196
  fix: 'mama service install --replace rewrites it',
187
197
  });
188
198
  }
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
- }
199
+ servicePath = (0, launch_files_js_1.startScriptPath)(reading, home, platform);
195
200
  const [, node, cli] = reading.exec === null ? [] : (0, launch_files_js_1.shellWords)(reading.exec);
201
+ serviceNode = node ?? null;
196
202
  if (!reading.generated) {
197
203
  checks.push({
198
204
  name: 'start.sh',
@@ -210,7 +216,7 @@ async function runDiagnostics(options = {}) {
210
216
  });
211
217
  }
212
218
  else {
213
- checks.push(nodeCheck(node, cli, run));
219
+ checks.push(nodeCheck(node, cli, run, root));
214
220
  }
215
221
  }
216
222
  catch (error) {
@@ -238,14 +244,7 @@ async function runDiagnostics(options = {}) {
238
244
  fix: `correct the quoting in ${(0, node_path_1.join)(root, 'auth.env')}`,
239
245
  });
240
246
  }
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
- };
247
+ const serviceEnv = serviceEnvironment(home, auth, exports, servicePath);
249
248
  const secret = (name) => Boolean(serviceEnv[name]?.trim());
250
249
  if (config) {
251
250
  const backend = config.agent.backend;
@@ -268,6 +267,7 @@ async function runDiagnostics(options = {}) {
268
267
  name: 'backend',
269
268
  status: 'fail',
270
269
  detail: probe.problem ?? `${backend} ${probe.version} is older than ${probe.minimum}`,
270
+ ...(probe.problem === null ? { code: 'backend.too_old' } : {}),
271
271
  fix: `update ${backend} to ${probe.minimum} or newer`,
272
272
  });
273
273
  }
@@ -276,6 +276,7 @@ async function runDiagnostics(options = {}) {
276
276
  name: 'backend',
277
277
  status: 'fail',
278
278
  detail: `${backend} ${probe.version} is not signed in for the service`,
279
+ code: 'backend.signed_out',
279
280
  fix: backend === 'claude' ? 'claude auth login' : `CODEX_HOME='${codexHome}' codex login`,
280
281
  });
281
282
  }
@@ -299,7 +300,7 @@ async function runDiagnostics(options = {}) {
299
300
  name: messenger,
300
301
  status: 'fail',
301
302
  detail: `${missing.join(' and ')} not set; boot stops at the messengers stage`,
302
- fix: missing.map(secretFix).join('; '),
303
+ fix: missing.map((name) => `mama secret set ${name}`).join('; '),
303
304
  });
304
305
  }
305
306
  else if (messenger === 'telegram' && !options.offline) {
@@ -309,7 +310,10 @@ async function runDiagnostics(options = {}) {
309
310
  checks.push({ name: messenger, status: 'ok', detail: 'secrets set' });
310
311
  }
311
312
  }
312
- checks.push(...(await sourceChecks(config, configPath, servicePath, locate, secret, {
313
+ checks.push(...(await sourceChecks(config, configPath, locate, secret, {
314
+ service: { path: servicePath, nodePath: serviceNode, exports },
315
+ findShell: options.findExecutable ?? launch_files_js_1.findExecutable,
316
+ home,
313
317
  env: serviceEnv,
314
318
  online: !options.offline,
315
319
  load: options.loadSource ?? index_js_1.loadConnector,
@@ -332,6 +336,7 @@ async function runDiagnostics(options = {}) {
332
336
  }
333
337
  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
338
  checks.push(serviceCheck(platform, home, options.serviceRun ?? systemd_service_js_1.defaultServiceRun));
339
+ checks.push(releaseCheck(root, home));
335
340
  // Each log against its own cap: the daemon rotates daemon.log and security-events.jsonl before
336
341
  // they pass theirs, and start.sh empties crash.log at start past its own.
337
342
  for (const [name, path, cap] of [
@@ -416,6 +421,7 @@ function schemaCheck(databasePath) {
416
421
  name: 'schema',
417
422
  status: 'fail',
418
423
  detail: `the database is newer than this MAMA (${summary}); boot refuses it`,
424
+ code: 'schema.newer',
419
425
  fix: 'install the version that wrote it, or mama restore a backup taken with this one',
420
426
  };
421
427
  }
@@ -436,8 +442,12 @@ function schemaCheck(databasePath) {
436
442
  };
437
443
  }
438
444
  }
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) {
445
+ /**
446
+ * start.sh's Node: still there, outside one version's folder, and able to load MAMA's SQLite. On
447
+ * an install the install command made (its Node in `<root>/node`), also the Node the installed
448
+ * release pins in its install/node.json: running the install command again moves to that one.
449
+ */
450
+ function nodeCheck(node, cli, run, root) {
441
451
  const rewrite = 'mama service install rewrites start.sh with a Node path that survives upgrades';
442
452
  if (!(0, node_fs_1.existsSync)(node)) {
443
453
  return {
@@ -497,6 +507,29 @@ function nodeCheck(node, cli, run) {
497
507
  : 'run mama service install from a shell whose node is not an nvm, volta or fnm version',
498
508
  };
499
509
  }
510
+ if (node === (0, node_move_journal_js_1.managedNodePath)(root)) {
511
+ const packageRoot = (0, install_kind_js_1.packageRootOf)(cli);
512
+ if (packageRoot === null) {
513
+ return {
514
+ name: 'node',
515
+ status: 'fail',
516
+ detail: 'the MAMA command start.sh runs is not inside a MAMA OS package',
517
+ context: cli,
518
+ fix: 'run the install command again',
519
+ };
520
+ }
521
+ const pin = JSON.parse((0, node_fs_1.readFileSync)((0, node_path_1.join)(packageRoot, 'install', 'node.json'), 'utf8')).version;
522
+ if (version !== `v${pin}`) {
523
+ return {
524
+ name: 'node',
525
+ status: 'warn',
526
+ detail: `start.sh runs ${version}; this release pins Node.js ${pin}`,
527
+ code: 'node.pin',
528
+ context: node,
529
+ fix: `run the install command again, which moves it: ${node_move_journal_js_1.INSTALL_COMMAND}`,
530
+ };
531
+ }
532
+ }
500
533
  return { name: 'node', status: 'ok', detail: `${version} loads SQLite`, context: node };
501
534
  }
502
535
  function sandboxCheck(platform, servicePath, locate, run) {
@@ -619,8 +652,12 @@ async function telegramCheck(token, commonChat, call) {
619
652
  : 'token valid; a member of the common group that reads its messages',
620
653
  };
621
654
  }
655
+ /** The secrets a source's requirements name. */
656
+ function secretNames(needs) {
657
+ return needs.flatMap((need) => need.type === 'token' && need.tokenName !== undefined ? [need.tokenName] : []);
658
+ }
622
659
  /** connectors.json as the connector runtime reads it, and what each enabled source needs. */
623
- async function sourceChecks(config, configPath, servicePath, locate, secret, start) {
660
+ async function sourceChecks(config, configPath, locate, secret, start) {
624
661
  const paths = (0, daemon_js_1.pathsFor)(configPath, config);
625
662
  let plan;
626
663
  try {
@@ -646,14 +683,17 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
646
683
  }
647
684
  const checks = [];
648
685
  const timeZone = (0, timezone_js_1.createTimeZoneSetting)(config.timezone);
686
+ const loaded = [];
649
687
  for (const name of plan.enabledConnectorNames) {
650
- let connector;
651
688
  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
- }));
689
+ loaded.push({
690
+ name,
691
+ connector: await start.load(name, plan.config.config[name], (0, connectors_js_1.connectorLoadPaths)(name, {
692
+ statePath: paths.connectorsRoot,
693
+ kagemushaDbPath: paths.kagemushaDbPath,
694
+ timeZone,
695
+ })),
696
+ });
657
697
  }
658
698
  catch (error) {
659
699
  checks.push({
@@ -662,26 +702,39 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
662
702
  detail: 'cannot be built from connectors.json; boot stops at the connectors stage',
663
703
  context: message(error),
664
704
  });
665
- continue;
666
705
  }
667
- const needs = connector.getAuthRequirements();
706
+ }
707
+ const enabled = loaded.map(({ name, connector }) => ({
708
+ name,
709
+ connector,
710
+ needs: connector.getAuthRequirements(),
711
+ }));
712
+ for (const { name, connector, needs } of enabled) {
668
713
  // 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) {
714
+ const secrets = secretNames(needs).filter((tokenName) => !secret(tokenName));
715
+ 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 },
716
+ // One login of a tool serves every source that runs it, so it names what all of them need.
717
+ enabled);
718
+ const missing = tools.filter((tool) => tool.servicePath === null);
719
+ // Paths stay out of `detail`, which --report shares.
720
+ const context = [
721
+ ...(secrets.length > 0 ? [secrets.join(', ')] : []),
722
+ ...missing.flatMap(({ tool, shellPath }) => shellPath === null ? [] : [`this shell finds ${tool.command} at ${shellPath}`]),
723
+ ];
724
+ if (secrets.length > 0 || missing.length > 0) {
674
725
  checks.push({
675
726
  name: `source ${name}`,
676
727
  status: 'fail',
677
728
  detail: [
678
729
  ...(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`),
730
+ ...missing.map(({ tool }) => `the ${tool.command} command is not on the service's PATH`),
680
731
  ].join('; '),
681
- ...(secrets.length > 0 ? { context: secrets.join(', ') } : {}),
732
+ ...(context.length > 0 ? { context: context.join('; ') } : {}),
682
733
  fix: [
683
- ...secrets.map(secretFix),
684
- ...commands.map(({ cli, login }) => `install ${cli}${login ? ` and run ${login}` : ''}`),
734
+ ...sourceSecretFixes(name, secrets, (0, launch_files_js_1.mamaCommand)((0, node_path_1.dirname)(configPath)), false),
735
+ ...missing.map(({ shellPath, install, login }) => shellPath === null
736
+ ? `install it: ${install.join(' or ')}; then log in: ${login}`
737
+ : `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
738
  ].join('; '),
686
739
  });
687
740
  continue;
@@ -702,6 +755,9 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
702
755
  status: 'fail',
703
756
  detail: 'does not start; boot stops at the connectors stage',
704
757
  context: message(error),
758
+ // The tool is on the service's PATH here, so a source that runs one most often
759
+ // lacks its sign-in or a scope (Calendar's read check).
760
+ ...(tools.length > 0 ? { fix: tools.map(({ login }) => login).join('; ') } : {}),
705
761
  };
706
762
  return;
707
763
  }
@@ -710,10 +766,10 @@ async function sourceChecks(config, configPath, servicePath, locate, secret, sta
710
766
  name: `source ${name}`,
711
767
  status: 'fail',
712
768
  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,
769
+ fix: [
770
+ ...tools.map(({ login }) => login),
771
+ ...sourceSecretFixes(name, secretNames(needs), (0, launch_files_js_1.mamaCommand)((0, node_path_1.dirname)(configPath)), true),
772
+ ].join('; ') || undefined,
717
773
  };
718
774
  }
719
775
  }));
@@ -745,6 +801,17 @@ function bounded(ms, run) {
745
801
  }),
746
802
  ]).finally(() => clearTimeout(timer));
747
803
  }
804
+ /** The environment start.sh gives the daemon: auth.env, then its own exports, then its PATH. */
805
+ function serviceEnvironment(home, auth, exports, path) {
806
+ return {
807
+ HOME: home,
808
+ USER: process.env.USER,
809
+ LOGNAME: process.env.LOGNAME,
810
+ ...auth,
811
+ ...exports,
812
+ PATH: path.join(':'),
813
+ };
814
+ }
748
815
  /** Run with process.env as the daemon's process has it, then put this shell's back. */
749
816
  async function withEnvironment(env, run) {
750
817
  const saved = { ...process.env };
@@ -903,6 +970,126 @@ async function daemonChecks(ask, port, host, portInUse) {
903
970
  }
904
971
  return checks;
905
972
  }
973
+ /**
974
+ * An unfinished `mama update --node` (docs/development/work/install.md, "Moving the managed
975
+ * Node"): its journal, which the install command finishes or undoes. It holds the install on
976
+ * update.hold, so it comes before the hold's own check.
977
+ */
978
+ function nodeMoveCheck(root) {
979
+ const path = (0, node_move_journal_js_1.nodeMovePlaces)(root).journal;
980
+ let journal;
981
+ try {
982
+ journal = (0, node_move_journal_js_1.readNodeMoveJournal)(path);
983
+ }
984
+ catch (error) {
985
+ return {
986
+ name: 'update',
987
+ status: 'fail',
988
+ detail: 'an unfinished move of the managed Node left a journal that cannot be read',
989
+ code: 'node_move.open',
990
+ context: message(error),
991
+ fix: `read ${path}; the install command's recovery needs it`,
992
+ };
993
+ }
994
+ if (journal === null)
995
+ return null;
996
+ return {
997
+ name: 'update',
998
+ status: 'fail',
999
+ detail: journal.phase === 'committed'
1000
+ ? `the move of the managed Node to ${journal.to} took effect, but its old folders are not removed yet`
1001
+ : `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`,
1002
+ code: 'node_move.open',
1003
+ context: path,
1004
+ fix: `run the install command again, which finishes or undoes it: ${node_move_journal_js_1.INSTALL_COMMAND}`,
1005
+ };
1006
+ }
1007
+ /**
1008
+ * An unfinished `mama update`: its journal (docs/development/work/update.md, PR 3), with the
1009
+ * command that resolves it by absolute paths, or a staging prefix an updater killed before it
1010
+ * wrote its journal left behind.
1011
+ */
1012
+ function updateChecks(root, home) {
1013
+ const move = nodeMoveCheck(root);
1014
+ if (move !== null)
1015
+ return [move];
1016
+ const path = (0, update_journal_js_1.updatePaths)(root).journal;
1017
+ let journal;
1018
+ try {
1019
+ journal = (0, update_journal_js_1.readUpdateJournal)(path);
1020
+ }
1021
+ catch (error) {
1022
+ return [
1023
+ {
1024
+ name: 'update',
1025
+ status: 'fail',
1026
+ detail: 'an unfinished update left a journal that cannot be read',
1027
+ code: 'update.open',
1028
+ context: message(error),
1029
+ fix: `read ${path}; mama update --rollback needs it`,
1030
+ },
1031
+ ];
1032
+ }
1033
+ if (journal !== null) {
1034
+ return [
1035
+ {
1036
+ name: 'update',
1037
+ status: 'fail',
1038
+ detail: journal.phase === 'committed'
1039
+ ? `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`
1040
+ : `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`,
1041
+ code: 'update.open',
1042
+ fix: (0, update_journal_js_1.rollbackCommand)(journal),
1043
+ },
1044
+ ];
1045
+ }
1046
+ const hold = (0, update_journal_js_1.updatePaths)(root).hold;
1047
+ if ((0, node_fs_1.existsSync)(hold)) {
1048
+ return [
1049
+ {
1050
+ name: 'update',
1051
+ status: 'fail',
1052
+ detail: 'an update hold file is in place with no update journal; start.sh exits at once, so the daemon does not start',
1053
+ code: 'update.hold',
1054
+ context: hold,
1055
+ fix: `rm ${hold}, then mama service start`,
1056
+ },
1057
+ ];
1058
+ }
1059
+ const install = (0, install_kind_js_1.installKind)(root, home);
1060
+ const staging = install.kind === 'npm' ? (0, update_journal_js_1.leftoverStaging)((0, node_path_1.dirname)(install.packageRoot)) : [];
1061
+ return staging.length === 0
1062
+ ? []
1063
+ : [
1064
+ {
1065
+ name: 'update',
1066
+ status: 'warn',
1067
+ detail: 'an interrupted mama update left its staging folder; the install runs as before',
1068
+ code: 'update.staging',
1069
+ context: staging.join(', '),
1070
+ fix: 'the next mama update removes it',
1071
+ },
1072
+ ];
1073
+ }
1074
+ /** The daemon's last daily release check, from its file; doctor itself asks no registry. */
1075
+ function releaseCheck(root, home) {
1076
+ const report = (0, release_check_js_1.releaseReport)(root, home);
1077
+ if (report === null) {
1078
+ return {
1079
+ name: 'release',
1080
+ status: 'ok',
1081
+ detail: 'not checked yet; the running daemon checks once a day',
1082
+ };
1083
+ }
1084
+ return {
1085
+ name: 'release',
1086
+ status: report.status,
1087
+ detail: report.detail,
1088
+ ...(report.context === undefined ? {} : { context: report.context }),
1089
+ ...(report.steps === undefined ? {} : { fix: report.steps.join('; then ') }),
1090
+ ...(report.fix === undefined ? {} : { fix: report.fix }),
1091
+ };
1092
+ }
906
1093
  function serviceCheck(platform, home, serviceRun) {
907
1094
  let launchd;
908
1095
  let unit;