@leemour/cli-messaging 0.87.0 → 0.89.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 (224) hide show
  1. package/README.md +1 -0
  2. package/dist/background/index.d.ts +5 -0
  3. package/dist/background/index.d.ts.map +1 -0
  4. package/dist/background/index.js +5 -0
  5. package/dist/background/index.js.map +1 -0
  6. package/dist/background/lock.d.ts +17 -0
  7. package/dist/background/lock.d.ts.map +1 -0
  8. package/dist/background/lock.js +34 -0
  9. package/dist/background/lock.js.map +1 -0
  10. package/dist/background/processes.d.ts.map +1 -0
  11. package/dist/background/processes.js.map +1 -0
  12. package/dist/background/system.d.ts +20 -0
  13. package/dist/background/system.d.ts.map +1 -0
  14. package/dist/background/system.js +39 -0
  15. package/dist/background/system.js.map +1 -0
  16. package/dist/background/units.d.ts +42 -0
  17. package/dist/background/units.d.ts.map +1 -0
  18. package/dist/background/units.js +178 -0
  19. package/dist/background/units.js.map +1 -0
  20. package/dist/cli/bot/port.d.ts +29 -0
  21. package/dist/cli/bot/port.d.ts.map +1 -0
  22. package/dist/cli/bot/port.js +2 -0
  23. package/dist/cli/bot/port.js.map +1 -0
  24. package/dist/cli/bot/registry.d.ts +37 -0
  25. package/dist/cli/bot/registry.d.ts.map +1 -0
  26. package/dist/cli/bot/registry.js +94 -0
  27. package/dist/cli/bot/registry.js.map +1 -0
  28. package/dist/cli/bot/token.d.ts +25 -0
  29. package/dist/cli/bot/token.d.ts.map +1 -0
  30. package/dist/cli/bot/token.js +36 -0
  31. package/dist/cli/bot/token.js.map +1 -0
  32. package/dist/cli/config-command.d.ts.map +1 -1
  33. package/dist/cli/config-command.js +17 -5
  34. package/dist/cli/config-command.js.map +1 -1
  35. package/dist/cli/context.d.ts +3 -0
  36. package/dist/cli/context.d.ts.map +1 -1
  37. package/dist/cli/context.js.map +1 -1
  38. package/dist/cli/index.d.ts +5 -1
  39. package/dist/cli/index.d.ts.map +1 -1
  40. package/dist/cli/index.js +3 -0
  41. package/dist/cli/index.js.map +1 -1
  42. package/dist/cli/messenger/admin-moderation-command.d.ts +11 -0
  43. package/dist/cli/messenger/admin-moderation-command.d.ts.map +1 -0
  44. package/dist/cli/messenger/admin-moderation-command.js +83 -0
  45. package/dist/cli/messenger/admin-moderation-command.js.map +1 -0
  46. package/dist/cli/messenger/after.d.ts +18 -5
  47. package/dist/cli/messenger/after.d.ts.map +1 -1
  48. package/dist/cli/messenger/after.js +19 -30
  49. package/dist/cli/messenger/after.js.map +1 -1
  50. package/dist/cli/messenger/ask.d.ts +2 -0
  51. package/dist/cli/messenger/ask.d.ts.map +1 -1
  52. package/dist/cli/messenger/ask.js +7 -0
  53. package/dist/cli/messenger/ask.js.map +1 -1
  54. package/dist/cli/messenger/backfill-jobs.d.ts.map +1 -1
  55. package/dist/cli/messenger/backfill-jobs.js +1 -1
  56. package/dist/cli/messenger/backfill-jobs.js.map +1 -1
  57. package/dist/cli/messenger/chats-command.d.ts.map +1 -1
  58. package/dist/cli/messenger/chats-command.js +3 -0
  59. package/dist/cli/messenger/chats-command.js.map +1 -1
  60. package/dist/cli/messenger/context.d.ts +5 -0
  61. package/dist/cli/messenger/context.d.ts.map +1 -1
  62. package/dist/cli/messenger/context.js +3 -0
  63. package/dist/cli/messenger/context.js.map +1 -1
  64. package/dist/cli/messenger/conversations-command.d.ts +10 -0
  65. package/dist/cli/messenger/conversations-command.d.ts.map +1 -0
  66. package/dist/cli/messenger/conversations-command.js +101 -0
  67. package/dist/cli/messenger/conversations-command.js.map +1 -0
  68. package/dist/cli/messenger/hearing-command.d.ts.map +1 -1
  69. package/dist/cli/messenger/hearing-command.js +4 -1
  70. package/dist/cli/messenger/hearing-command.js.map +1 -1
  71. package/dist/cli/messenger/messages-command.d.ts.map +1 -1
  72. package/dist/cli/messenger/messages-command.js +9 -7
  73. package/dist/cli/messenger/messages-command.js.map +1 -1
  74. package/dist/cli/messenger/port.d.ts +6 -1
  75. package/dist/cli/messenger/port.d.ts.map +1 -1
  76. package/dist/cli/messenger/port.js.map +1 -1
  77. package/dist/cli/messenger/serve-command.d.ts +1 -13
  78. package/dist/cli/messenger/serve-command.d.ts.map +1 -1
  79. package/dist/cli/messenger/serve-command.js +7 -32
  80. package/dist/cli/messenger/serve-command.js.map +1 -1
  81. package/dist/cli/messenger/server-command.d.ts +1 -17
  82. package/dist/cli/messenger/server-command.d.ts.map +1 -1
  83. package/dist/cli/messenger/server-command.js +24 -222
  84. package/dist/cli/messenger/server-command.js.map +1 -1
  85. package/dist/cli/messenger/store-maintenance-command.d.ts.map +1 -1
  86. package/dist/cli/messenger/store-maintenance-command.js +35 -1
  87. package/dist/cli/messenger/store-maintenance-command.js.map +1 -1
  88. package/dist/cli/messenger/transcribe-command.d.ts.map +1 -1
  89. package/dist/cli/messenger/transcribe-command.js +4 -1
  90. package/dist/cli/messenger/transcribe-command.js.map +1 -1
  91. package/dist/cli/settings.d.ts +25 -1
  92. package/dist/cli/settings.d.ts.map +1 -1
  93. package/dist/cli/settings.js +108 -34
  94. package/dist/cli/settings.js.map +1 -1
  95. package/dist/conversations/link.d.ts +2 -2
  96. package/dist/conversations/link.d.ts.map +1 -1
  97. package/dist/conversations/link.js +6 -4
  98. package/dist/conversations/link.js.map +1 -1
  99. package/dist/domain/models.d.ts +4 -0
  100. package/dist/domain/models.d.ts.map +1 -1
  101. package/dist/domain/send-time.d.ts +1 -1
  102. package/dist/domain/send-time.js +4 -4
  103. package/dist/domain/send-time.js.map +1 -1
  104. package/dist/index.d.ts +4 -0
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +2 -0
  107. package/dist/index.js.map +1 -1
  108. package/dist/mcp/confirm.js +1 -1
  109. package/dist/mcp/confirm.js.map +1 -1
  110. package/dist/mcp/tool.d.ts +2 -0
  111. package/dist/mcp/tool.d.ts.map +1 -1
  112. package/dist/mcp/tool.js +2 -0
  113. package/dist/mcp/tool.js.map +1 -1
  114. package/dist/mcp/tools/chats.d.ts.map +1 -1
  115. package/dist/mcp/tools/chats.js +13 -8
  116. package/dist/mcp/tools/chats.js.map +1 -1
  117. package/dist/mcp/tools/conversations.d.ts +5 -0
  118. package/dist/mcp/tools/conversations.d.ts.map +1 -0
  119. package/dist/mcp/tools/conversations.js +56 -0
  120. package/dist/mcp/tools/conversations.js.map +1 -0
  121. package/dist/mcp/tools/inbox.js +3 -3
  122. package/dist/mcp/tools/inbox.js.map +1 -1
  123. package/dist/mcp/tools/messages-send.js +4 -4
  124. package/dist/mcp/tools/messages-send.js.map +1 -1
  125. package/dist/mcp/tools/messages.d.ts.map +1 -1
  126. package/dist/mcp/tools/messages.js +25 -18
  127. package/dist/mcp/tools/messages.js.map +1 -1
  128. package/dist/mcp/tools/moderation.d.ts +8 -0
  129. package/dist/mcp/tools/moderation.d.ts.map +1 -0
  130. package/dist/mcp/tools/moderation.js +36 -0
  131. package/dist/mcp/tools/moderation.js.map +1 -0
  132. package/dist/mcp/tools/review.d.ts.map +1 -1
  133. package/dist/mcp/tools/review.js +3 -3
  134. package/dist/mcp/tools/review.js.map +1 -1
  135. package/dist/mcp/tools.d.ts.map +1 -1
  136. package/dist/mcp/tools.js +4 -0
  137. package/dist/mcp/tools.js.map +1 -1
  138. package/dist/moderation/check.d.ts +83 -0
  139. package/dist/moderation/check.d.ts.map +1 -0
  140. package/dist/moderation/check.js +227 -0
  141. package/dist/moderation/check.js.map +1 -0
  142. package/dist/moderation/rules.d.ts +105 -0
  143. package/dist/moderation/rules.d.ts.map +1 -0
  144. package/dist/moderation/rules.js +158 -0
  145. package/dist/moderation/rules.js.map +1 -0
  146. package/dist/parity/bin.js +15 -2
  147. package/dist/parity/bin.js.map +1 -1
  148. package/dist/parity/index.d.ts +1 -0
  149. package/dist/parity/index.d.ts.map +1 -1
  150. package/dist/parity/index.js +1 -0
  151. package/dist/parity/index.js.map +1 -1
  152. package/dist/parity/wording.d.ts +7 -0
  153. package/dist/parity/wording.d.ts.map +1 -0
  154. package/dist/parity/wording.js +31 -0
  155. package/dist/parity/wording.js.map +1 -0
  156. package/dist/sends/permissions.d.ts +5 -3
  157. package/dist/sends/permissions.d.ts.map +1 -1
  158. package/dist/sends/permissions.js +30 -6
  159. package/dist/sends/permissions.js.map +1 -1
  160. package/dist/services/conversations.d.ts +45 -0
  161. package/dist/services/conversations.d.ts.map +1 -0
  162. package/dist/services/conversations.js +92 -0
  163. package/dist/services/conversations.js.map +1 -0
  164. package/dist/services/deps.d.ts +7 -1
  165. package/dist/services/deps.d.ts.map +1 -1
  166. package/dist/services/deps.js +2 -1
  167. package/dist/services/deps.js.map +1 -1
  168. package/dist/services/index.d.ts +8 -0
  169. package/dist/services/index.d.ts.map +1 -1
  170. package/dist/services/index.js +6 -0
  171. package/dist/services/index.js.map +1 -1
  172. package/dist/services/messages.d.ts +2 -0
  173. package/dist/services/messages.d.ts.map +1 -1
  174. package/dist/services/messages.js +7 -1
  175. package/dist/services/messages.js.map +1 -1
  176. package/dist/services/moderation.d.ts +33 -0
  177. package/dist/services/moderation.d.ts.map +1 -0
  178. package/dist/services/moderation.js +99 -0
  179. package/dist/services/moderation.js.map +1 -0
  180. package/dist/speech/transcribe.d.ts +5 -1
  181. package/dist/speech/transcribe.d.ts.map +1 -1
  182. package/dist/speech/transcribe.js +2 -2
  183. package/dist/speech/transcribe.js.map +1 -1
  184. package/dist/store/sqlite/conversations.d.ts +45 -0
  185. package/dist/store/sqlite/conversations.d.ts.map +1 -0
  186. package/dist/store/sqlite/conversations.js +309 -0
  187. package/dist/store/sqlite/conversations.js.map +1 -0
  188. package/dist/store/sqlite/drizzle/bun.js +1 -1
  189. package/dist/store/sqlite/drizzle/core.d.ts +1 -1
  190. package/dist/store/sqlite/drizzle/core.d.ts.map +1 -1
  191. package/dist/store/sqlite/drizzle/core.js +5 -3
  192. package/dist/store/sqlite/drizzle/core.js.map +1 -1
  193. package/dist/store/sqlite/drizzle/node.js +1 -1
  194. package/dist/store/sqlite/drizzle/{shared-JAIU3GYB.js → shared-VIVPOB3Q.js} +5 -1
  195. package/dist/store/sqlite/drizzle/{shared-JAIU3GYB.js.map → shared-VIVPOB3Q.js.map} +1 -1
  196. package/dist/store/sqlite/manifest.d.ts.map +1 -1
  197. package/dist/store/sqlite/manifest.js +1 -0
  198. package/dist/store/sqlite/manifest.js.map +1 -1
  199. package/dist/store/sqlite/messages.d.ts.map +1 -1
  200. package/dist/store/sqlite/messages.js +2 -0
  201. package/dist/store/sqlite/messages.js.map +1 -1
  202. package/dist/store/sqlite/migrations.generated.d.ts.map +1 -1
  203. package/dist/store/sqlite/migrations.generated.js +15 -0
  204. package/dist/store/sqlite/migrations.generated.js.map +1 -1
  205. package/dist/store/sqlite/reads.d.ts +50 -0
  206. package/dist/store/sqlite/reads.d.ts.map +1 -1
  207. package/dist/store/sqlite/reads.js +2 -1
  208. package/dist/store/sqlite/reads.js.map +1 -1
  209. package/dist/store/sqlite/schema.d.ts +494 -0
  210. package/dist/store/sqlite/schema.d.ts.map +1 -1
  211. package/dist/store/sqlite/schema.js +78 -1
  212. package/dist/store/sqlite/schema.js.map +1 -1
  213. package/dist/store/sqlite/search.d.ts +33 -0
  214. package/dist/store/sqlite/search.d.ts.map +1 -1
  215. package/dist/store/store.d.ts +70 -0
  216. package/dist/store/store.d.ts.map +1 -1
  217. package/dist/store/store.js +46 -0
  218. package/dist/store/store.js.map +1 -1
  219. package/package.json +5 -1
  220. package/parity.json +115 -104
  221. package/dist/cli/messenger/processes.d.ts.map +0 -1
  222. package/dist/cli/messenger/processes.js.map +0 -1
  223. /package/dist/{cli/messenger → background}/processes.d.ts +0 -0
  224. /package/dist/{cli/messenger → background}/processes.js +0 -0
package/README.md CHANGED
@@ -76,6 +76,7 @@ the same day.
76
76
  | `./store` | `openStore`, `MessageStore`, `storePath`, and the file format: `minCompatible` rises only in a breaking release |
77
77
  | `./sends` | `sendGuard`, `SendJournal`, the journal's line format |
78
78
  | `./services` | `servicesFor`, `Override` and the service names |
79
+ | `./background` | `lockPath`, `readLock`, `holdLock`, `releaseLock`, `servingProfiles`, `alive`, `carries`, `holdersOf`, `ServerSystem`, `thisMachine`, `platformFor` and the systemd and launchd units |
79
80
 
80
81
  Everything else may change in any release, and still goes under "Changed — may break callers" when
81
82
  it does. tg-cli and max-cli take new versions through Dependabot pull requests.
@@ -0,0 +1,5 @@
1
+ export { holdLock, type Lock, lockPath, readLock, releaseLock, servingProfiles } from "./lock.js";
2
+ export { alive, carries, holdersOf } from "./processes.js";
3
+ export { type Ran, runProgram, type ServerSystem, thisMachine } from "./system.js";
4
+ export { launchd, locationVariables, noUnits, type Platform, platformFor, systemd, tail, type Unit } from "./units.js";
5
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/background/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAE,KAAK,GAAG,EAAE,UAAU,EAAE,KAAK,YAAY,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAE,KAAK,QAAQ,EAAE,WAAW,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,YAAY,CAAA"}
@@ -0,0 +1,5 @@
1
+ export { holdLock, lockPath, readLock, releaseLock, servingProfiles } from "./lock.js";
2
+ export { alive, carries, holdersOf } from "./processes.js";
3
+ export { runProgram, thisMachine } from "./system.js";
4
+ export { launchd, locationVariables, noUnits, platformFor, systemd, tail } from "./units.js";
5
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/background/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAa,QAAQ,EAAE,QAAQ,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AACjG,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAC1D,OAAO,EAAY,UAAU,EAAqB,WAAW,EAAE,MAAM,aAAa,CAAA;AAClF,OAAO,EAAE,OAAO,EAAE,iBAAiB,EAAE,OAAO,EAAiB,WAAW,EAAE,OAAO,EAAE,IAAI,EAAa,MAAM,YAAY,CAAA"}
@@ -0,0 +1,17 @@
1
+ import type { AppIdentity } from "../cli/app.js";
2
+ export interface Lock {
3
+ pid: number;
4
+ startedAt: string;
5
+ /** Set once the connection is open and updates arrive — before it, `serve` is still starting. */
6
+ listeningAt?: string;
7
+ /** The CLI version this serve runs: after an update it goes on running the old code until restarted. */
8
+ version?: string;
9
+ }
10
+ export declare const lockPath: (app: AppIdentity, profile: string, env: NodeJS.ProcessEnv) => string;
11
+ export declare const readLock: (path: string) => Lock | undefined;
12
+ export declare const holdLock: (path: string, lock: Lock) => void;
13
+ /** Only the process that holds the lock removes it: one refused as a second serve must not free it. */
14
+ export declare const releaseLock: (path: string, pid?: number) => void;
15
+ /** The profiles a serve is running for now — what an update must restart for its new code to run. */
16
+ export declare const servingProfiles: (app: AppIdentity, env: NodeJS.ProcessEnv) => string[];
17
+ //# sourceMappingURL=lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../src/background/lock.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAGhD,MAAM,WAAW,IAAI;IACnB,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,iGAAiG;IACjG,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,wGAAwG;IACxG,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,eAAO,MAAM,QAAQ,QAAS,WAAW,WAAW,MAAM,OAAO,MAAM,CAAC,UAAU,WAC0B,CAAA;AAE5G,eAAO,MAAM,QAAQ,SAAU,MAAM,KAAG,IAAI,GAAG,SAO9C,CAAA;AAED,eAAO,MAAM,QAAQ,SAAU,MAAM,QAAQ,IAAI,SAA4D,CAAA;AAE7G,uGAAuG;AACvG,eAAO,MAAM,WAAW,SAAU,MAAM,uBAEvC,CAAA;AAED,qGAAqG;AACrG,eAAO,MAAM,eAAe,QAAS,WAAW,OAAO,MAAM,CAAC,UAAU,KAAG,MAAM,EAUhF,CAAA"}
@@ -0,0 +1,34 @@
1
+ import { existsSync, readdirSync, readFileSync, rmSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { resolvePaths, writeSecurely } from "@leemour/cli-core";
4
+ import { alive } from "./processes.js";
5
+ export const lockPath = (app, profile, env) => join(resolvePaths({ appName: app.appName, prefix: app.envPrefix, env }).state, "serve", `${profile}.lock`);
6
+ export const readLock = (path) => {
7
+ try {
8
+ const lock = JSON.parse(readFileSync(path, "utf8"));
9
+ return typeof lock.pid === "number" && typeof lock.startedAt === "string" ? lock : undefined;
10
+ }
11
+ catch {
12
+ return undefined;
13
+ }
14
+ };
15
+ export const holdLock = (path, lock) => writeSecurely(path, `${JSON.stringify(lock)}\n`, 0o600);
16
+ /** Only the process that holds the lock removes it: one refused as a second serve must not free it. */
17
+ export const releaseLock = (path, pid = process.pid) => {
18
+ if (readLock(path)?.pid === pid)
19
+ rmSync(path, { force: true });
20
+ };
21
+ /** The profiles a serve is running for now — what an update must restart for its new code to run. */
22
+ export const servingProfiles = (app, env) => {
23
+ const dir = dirname(lockPath(app, "default", env));
24
+ if (!existsSync(dir))
25
+ return [];
26
+ return readdirSync(dir)
27
+ .filter((name) => name.endsWith(".lock"))
28
+ .map((name) => name.slice(0, -".lock".length))
29
+ .filter((profile) => {
30
+ const lock = readLock(lockPath(app, profile, env));
31
+ return lock !== undefined && alive(lock.pid);
32
+ });
33
+ };
34
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/background/lock.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,EAAE,MAAM,SAAS,CAAA;AACvE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAA;AAE/D,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAWtC,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,GAAgB,EAAE,OAAe,EAAE,GAAsB,EAAE,EAAE,CACpF,IAAI,CAAC,YAAY,CAAC,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,SAAS,EAAE,GAAG,EAAE,CAAC,CAAC,KAAK,EAAE,OAAO,EAAE,GAAG,OAAO,OAAO,CAAC,CAAA;AAE5G,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAoB,EAAE;IACzD,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAkB,CAAA;QACpE,OAAO,OAAO,IAAI,CAAC,GAAG,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,SAAS,KAAK,QAAQ,CAAC,CAAC,CAAE,IAAa,CAAC,CAAC,CAAC,SAAS,CAAA;IACxG,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,SAAS,CAAA;IAClB,CAAC;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,IAAY,EAAE,IAAU,EAAE,EAAE,CAAC,aAAa,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;AAE7G,uGAAuG;AACvG,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,IAAY,EAAE,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,EAAE;IAC7D,IAAI,QAAQ,CAAC,IAAI,CAAC,EAAE,GAAG,KAAK,GAAG;QAAE,MAAM,CAAC,IAAI,EAAE,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAA;AAChE,CAAC,CAAA;AAED,qGAAqG;AACrG,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,GAAgB,EAAE,GAAsB,EAAY,EAAE;IACpF,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,GAAG,CAAC,CAAC,CAAA;IAClD,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAA;IAC/B,OAAO,WAAW,CAAC,GAAG,CAAC;SACpB,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;SACxC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC;SAC7C,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QAClB,MAAM,IAAI,GAAG,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,CAAC,CAAC,CAAA;QAClD,OAAO,IAAI,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;IAC9C,CAAC,CAAC,CAAA;AACN,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.d.ts","sourceRoot":"","sources":["../../src/background/processes.ts"],"names":[],"mappings":"AAGA,mEAAmE;AACnE,eAAO,MAAM,KAAK,QAAS,MAAM,KAAG,OAOnC,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,OAAO,QAAS,MAAM,UAAU,MAAM,KAAG,OAgBrD,CAAA;AAED;;;;GAIG;AACH,eAAO,MAAM,SAAS,UAAW,MAAM,EAAE,KAAG,MAAM,EAAE,GAAG,SAiCtD,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"processes.js","sourceRoot":"","sources":["../../src/background/processes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export interface Ran {
2
+ code: number;
3
+ stdout: string;
4
+ stderr: string;
5
+ }
6
+ /** What `server` asks of the machine. Tests hand one in; nothing else does. */
7
+ export interface ServerSystem {
8
+ platform: NodeJS.Platform;
9
+ run: (argv: string[], env: NodeJS.ProcessEnv) => Promise<Ran>;
10
+ /** The command line that starts this CLI: the node binary and the script, both absolute. */
11
+ entry: string[];
12
+ uid: number;
13
+ /** Starts `<cli> <argv>` apart from this process, writing to `log`, and answers its PID. */
14
+ spawn: (argv: string[], env: NodeJS.ProcessEnv, log: string) => number;
15
+ /** The pause between two looks at whether `serve` is listening yet. */
16
+ pause: () => Promise<void>;
17
+ }
18
+ export declare const runProgram: (argv: string[], env: NodeJS.ProcessEnv) => Promise<Ran>;
19
+ export declare const thisMachine: () => ServerSystem;
20
+ //# sourceMappingURL=system.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.d.ts","sourceRoot":"","sources":["../../src/background/system.ts"],"names":[],"mappings":"AAMA,MAAM,WAAW,GAAG;IAClB,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;CACf;AAED,+EAA+E;AAC/E,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC,QAAQ,CAAA;IACzB,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,KAAK,OAAO,CAAC,GAAG,CAAC,CAAA;IAC7D,4FAA4F;IAC5F,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,GAAG,EAAE,MAAM,CAAA;IACX,4FAA4F;IAC5F,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,GAAG,EAAE,MAAM,CAAC,UAAU,EAAE,GAAG,EAAE,MAAM,KAAK,MAAM,CAAA;IACtE,uEAAuE;IACvE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC3B;AAED,eAAO,MAAM,UAAU,SAAU,MAAM,EAAE,OAAO,MAAM,CAAC,UAAU,KAAG,OAAO,CAAC,GAAG,CAU3E,CAAA;AAEJ,eAAO,MAAM,WAAW,QAAO,YAqB9B,CAAA"}
@@ -0,0 +1,39 @@
1
+ import { execFile, spawn } from "node:child_process";
2
+ import { closeSync, mkdirSync, openSync, realpathSync } from "node:fs";
3
+ import { dirname } from "node:path";
4
+ import { setTimeout as sleep } from "node:timers/promises";
5
+ import { CliError } from "@leemour/cli-core";
6
+ export const runProgram = (argv, env) => new Promise((resolve, reject) => {
7
+ const [file = "", ...args] = argv;
8
+ execFile(file, args, { env, encoding: "utf8" }, (error, stdout, stderr) => {
9
+ if (error?.code === "ENOENT") {
10
+ reject(new CliError("configuration_error", `${file} was not found — this machine has no ${file} to run serve`));
11
+ return;
12
+ }
13
+ resolve({ code: typeof error?.code === "number" ? error.code : error ? 1 : 0, stdout, stderr });
14
+ });
15
+ });
16
+ export const thisMachine = () => {
17
+ const entry = [process.execPath, realpathSync(process.argv[1] ?? "")];
18
+ return {
19
+ platform: process.platform,
20
+ run: runProgram,
21
+ entry,
22
+ uid: process.getuid?.() ?? 0,
23
+ spawn: (argv, env, log) => {
24
+ mkdirSync(dirname(log), { recursive: true, mode: 0o700 });
25
+ const out = openSync(log, "a", 0o600);
26
+ try {
27
+ const [file = "", ...rest] = entry;
28
+ const child = spawn(file, [...rest, ...argv], { detached: true, stdio: ["ignore", out, out], env });
29
+ child.unref();
30
+ return child.pid ?? 0;
31
+ }
32
+ finally {
33
+ closeSync(out);
34
+ }
35
+ },
36
+ pause: () => sleep(250),
37
+ };
38
+ };
39
+ //# sourceMappingURL=system.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/background/system.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AACpD,OAAO,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AACtE,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,UAAU,IAAI,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAqB5C,MAAM,CAAC,MAAM,UAAU,GAAG,CAAC,IAAc,EAAE,GAAsB,EAAgB,EAAE,CACjF,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE;IAC9B,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAA;IACjC,QAAQ,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE;QACxE,IAAK,KAAsC,EAAE,IAAI,KAAK,QAAQ,EAAE,CAAC;YAC/D,MAAM,CAAC,IAAI,QAAQ,CAAC,qBAAqB,EAAE,GAAG,IAAI,wCAAwC,IAAI,eAAe,CAAC,CAAC,CAAA;YAC/G,OAAM;QACR,CAAC;QACD,OAAO,CAAC,EAAE,IAAI,EAAE,OAAO,KAAK,EAAE,IAAI,KAAK,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACjG,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEJ,MAAM,CAAC,MAAM,WAAW,GAAG,GAAiB,EAAE;IAC5C,MAAM,KAAK,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,YAAY,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,CAAA;IACrE,OAAO;QACL,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,GAAG,EAAE,UAAU;QACf,KAAK;QACL,GAAG,EAAE,OAAO,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC;QAC5B,KAAK,EAAE,CAAC,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,EAAE;YACxB,SAAS,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;YACzD,MAAM,GAAG,GAAG,QAAQ,CAAC,GAAG,EAAE,GAAG,EAAE,KAAK,CAAC,CAAA;YACrC,IAAI,CAAC;gBACH,MAAM,CAAC,IAAI,GAAG,EAAE,EAAE,GAAG,IAAI,CAAC,GAAG,KAAK,CAAA;gBAClC,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,IAAI,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,GAAG,EAAE,CAAC,CAAA;gBACnG,KAAK,CAAC,KAAK,EAAE,CAAA;gBACb,OAAO,KAAK,CAAC,GAAG,IAAI,CAAC,CAAA;YACvB,CAAC;oBAAS,CAAC;gBACT,SAAS,CAAC,GAAG,CAAC,CAAA;YAChB,CAAC;QACH,CAAC;QACD,KAAK,EAAE,GAAG,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC;KACxB,CAAA;AACH,CAAC,CAAA"}
@@ -0,0 +1,42 @@
1
+ import type { AppIdentity } from "../cli/app.js";
2
+ import type { ServerSystem } from "./system.js";
3
+ export interface Unit {
4
+ profile: string;
5
+ /** What `systemctl` or `launchctl` call it. */
6
+ name: string;
7
+ path: string;
8
+ logPath?: string;
9
+ /** What the unit runs, and the variables it runs with. */
10
+ command: string[];
11
+ environment: Record<string, string>;
12
+ }
13
+ /**
14
+ * Only the variables that say **where** the files are travel into the unit: a unit written from a
15
+ * development checkout must open that checkout's session and store, never the owner's real ones.
16
+ * Nothing that could hold a credential is copied.
17
+ */
18
+ export declare const locationVariables: (app: AppIdentity, env: NodeJS.ProcessEnv) => Record<string, string>;
19
+ export interface Platform {
20
+ unit: (base: Omit<Unit, "name" | "path">) => Unit;
21
+ text: (unit: Unit, app: AppIdentity) => string;
22
+ start: (unit: Unit) => string[][];
23
+ stop: (unit: Unit) => string[][];
24
+ state: (unit: Unit) => Promise<{
25
+ loaded: boolean;
26
+ active: boolean;
27
+ pid?: number;
28
+ detail?: string;
29
+ }>;
30
+ logs: (unit: Unit, lines: number) => Promise<string>;
31
+ /** What must exist before the unit first runs. */
32
+ prepare?: (unit: Unit) => void;
33
+ /** How to have it start at every login too. */
34
+ atLogin: (unit: Unit) => string;
35
+ }
36
+ export declare const systemd: (app: AppIdentity, system: ServerSystem, env: NodeJS.ProcessEnv) => Platform;
37
+ export declare const launchd: (app: AppIdentity, system: ServerSystem, env: NodeJS.ProcessEnv) => Platform;
38
+ export declare const tail: (path: string | undefined, lines: number) => string;
39
+ /** Where there is neither: `start` still runs serve in the background; only `install` needs one of them. */
40
+ export declare const noUnits: (app: AppIdentity, system: ServerSystem) => Platform;
41
+ export declare const platformFor: (app: AppIdentity, system: ServerSystem, env: NodeJS.ProcessEnv) => Platform;
42
+ //# sourceMappingURL=units.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"units.d.ts","sourceRoot":"","sources":["../../src/background/units.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;AAChD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAE/C,MAAM,WAAW,IAAI;IACnB,OAAO,EAAE,MAAM,CAAA;IACf,+CAA+C;IAC/C,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACpC;AAED;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,QAAS,WAAW,OAAO,MAAM,CAAC,UAAU,KAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAOjG,CAAA;AAED,MAAM,WAAW,QAAQ;IACvB,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,KAAK,IAAI,CAAA;IACjD,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,GAAG,EAAE,WAAW,KAAK,MAAM,CAAA;IAC9C,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,EAAE,EAAE,CAAA;IACjC,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,EAAE,EAAE,CAAA;IAChC,KAAK,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,OAAO,CAAC;QAAE,MAAM,EAAE,OAAO,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IACnG,IAAI,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAA;IACpD,kDAAkD;IAClD,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,IAAI,CAAA;IAC9B,+CAA+C;IAC/C,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,KAAK,MAAM,CAAA;CAChC;AAaD,eAAO,MAAM,OAAO,QAAS,WAAW,UAAU,YAAY,OAAO,MAAM,CAAC,UAAU,KAAG,QAmExF,CAAA;AAID,eAAO,MAAM,OAAO,QAAS,WAAW,UAAU,YAAY,OAAO,MAAM,CAAC,UAAU,KAAG,QAgExF,CAAA;AAED,eAAO,MAAM,IAAI,SAAU,MAAM,GAAG,SAAS,SAAS,MAAM,KAAG,MACyD,CAAA;AAExH,4GAA4G;AAC5G,eAAO,MAAM,OAAO,QAAS,WAAW,UAAU,YAAY,KAAG,QAgBhE,CAAA;AAED,eAAO,MAAM,WAAW,QAAS,WAAW,UAAU,YAAY,OAAO,MAAM,CAAC,UAAU,KAAG,QAI5F,CAAA"}
@@ -0,0 +1,178 @@
1
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
2
+ import { dirname, join } from "node:path";
3
+ import { CliError } from "@leemour/cli-core";
4
+ /**
5
+ * Only the variables that say **where** the files are travel into the unit: a unit written from a
6
+ * development checkout must open that checkout's session and store, never the owner's real ones.
7
+ * Nothing that could hold a credential is copied.
8
+ */
9
+ export const locationVariables = (app, env) => {
10
+ const names = [
11
+ ...["CONFIG_DIR", "STATE_DIR", "CACHE_DIR"].map((name) => `${app.envPrefix}_${name}`),
12
+ "MESSAGING_STORE",
13
+ "MESSAGING_STATE_DIR",
14
+ ];
15
+ return Object.fromEntries(names.flatMap((name) => (env[name] === undefined ? [] : [[name, env[name]]])));
16
+ };
17
+ const home = (env) => {
18
+ if (!env.HOME)
19
+ throw new CliError("configuration_error", "HOME is not set, so there is nowhere to put the unit");
20
+ return env.HOME;
21
+ };
22
+ /** `%` is a specifier in both; `$` is a variable only in `ExecStart=` — in `Environment=` it is itself. */
23
+ const systemdQuote = (value, { command = false } = {}) => {
24
+ const quoted = value.replaceAll("\\", "\\\\").replaceAll('"', '\\"').replaceAll("%", "%%");
25
+ return `"${command ? quoted.replaceAll("$", "$$$$") : quoted}"`;
26
+ };
27
+ export const systemd = (app, system, env) => {
28
+ const folder = join(env.XDG_CONFIG_HOME ?? join(home(env), ".config"), "systemd", "user");
29
+ const checked = async (argv) => {
30
+ const ran = await system.run(argv, env);
31
+ if (ran.code !== 0)
32
+ throw new CliError("configuration_error", `${argv.join(" ")} failed: ${ran.stderr.trim()}`);
33
+ return ran.stdout;
34
+ };
35
+ return {
36
+ unit: (base) => {
37
+ const name = `${app.command}-serve-${base.profile}.service`;
38
+ return { ...base, name, path: join(folder, name) };
39
+ },
40
+ text: (unit) => [
41
+ "[Unit]",
42
+ `Description=${app.command} serve — keep the local message archive current (profile ${unit.profile})`,
43
+ "",
44
+ "[Service]",
45
+ `ExecStart=${unit.command.map((word) => systemdQuote(word, { command: true })).join(" ")}`,
46
+ ...Object.entries(unit.environment).map(([name, value]) => `Environment=${systemdQuote(`${name}=${value}`)}`),
47
+ "Restart=on-failure",
48
+ "RestartSec=30",
49
+ "",
50
+ "[Install]",
51
+ "WantedBy=default.target",
52
+ "",
53
+ ].join("\n"),
54
+ start: (unit) => [
55
+ ["systemctl", "--user", "daemon-reload"],
56
+ ["systemctl", "--user", "start", unit.name],
57
+ ],
58
+ stop: (unit) => [["systemctl", "--user", "stop", unit.name]],
59
+ state: async (unit) => {
60
+ const ran = await system.run([
61
+ "systemctl",
62
+ "--user",
63
+ "show",
64
+ unit.name,
65
+ "-p",
66
+ "LoadState",
67
+ "-p",
68
+ "ActiveState",
69
+ "-p",
70
+ "SubState",
71
+ "-p",
72
+ "MainPID",
73
+ ], env);
74
+ const fields = Object.fromEntries(ran.stdout
75
+ .split("\n")
76
+ .filter((line) => line.includes("="))
77
+ .map((line) => [line.slice(0, line.indexOf("=")), line.slice(line.indexOf("=") + 1)]));
78
+ const pid = Number(fields.MainPID);
79
+ return {
80
+ loaded: fields.LoadState === "loaded",
81
+ active: fields.ActiveState === "active",
82
+ ...(pid > 0 ? { pid } : {}),
83
+ ...(fields.ActiveState ? { detail: `${fields.ActiveState} (${fields.SubState})` } : {}),
84
+ };
85
+ },
86
+ logs: (unit, lines) => checked(["journalctl", "--user", "-u", unit.name, "-n", String(lines), "--no-pager"]),
87
+ atLogin: (unit) => `at every login too: systemctl --user enable ${unit.name.replace(/\.service$/, "")}`,
88
+ };
89
+ };
90
+ const xml = (value) => value.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
91
+ export const launchd = (app, system, env) => {
92
+ const folder = join(home(env), "Library", "LaunchAgents");
93
+ const domain = `gui/${system.uid}`;
94
+ return {
95
+ unit: (base) => {
96
+ const name = `${app.appName}.serve.${base.profile}`;
97
+ return { ...base, name, path: join(folder, `${name}.plist`) };
98
+ },
99
+ text: (unit) => [
100
+ '<?xml version="1.0" encoding="UTF-8"?>',
101
+ '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">',
102
+ '<plist version="1.0">',
103
+ "<dict>",
104
+ ` <key>Label</key><string>${xml(unit.name)}</string>`,
105
+ " <key>ProgramArguments</key>",
106
+ " <array>",
107
+ ...unit.command.map((word) => ` <string>${xml(word)}</string>`),
108
+ " </array>",
109
+ " <key>EnvironmentVariables</key>",
110
+ " <dict>",
111
+ ...Object.entries(unit.environment).map(([name, value]) => ` <key>${xml(name)}</key><string>${xml(value)}</string>`),
112
+ " </dict>",
113
+ // launchd loads every agent here at login; disabled, it waits for `server start` to enable it.
114
+ " <key>Disabled</key><true/>",
115
+ " <key>RunAtLoad</key><true/>",
116
+ " <key>KeepAlive</key><dict><key>SuccessfulExit</key><false/></dict>",
117
+ " <key>ThrottleInterval</key><integer>30</integer>",
118
+ ` <key>StandardOutPath</key><string>${xml(unit.logPath ?? "")}</string>`,
119
+ ` <key>StandardErrorPath</key><string>${xml(unit.logPath ?? "")}</string>`,
120
+ "</dict>",
121
+ "</plist>",
122
+ "",
123
+ ].join("\n"),
124
+ // RunAtLoad makes loading the agent the start; `enable` overrides the file's Disabled until `disable`.
125
+ start: (unit) => [
126
+ ["launchctl", "enable", `${domain}/${unit.name}`],
127
+ ["launchctl", "bootstrap", domain, unit.path],
128
+ ],
129
+ stop: (unit) => [
130
+ ["launchctl", "bootout", `${domain}/${unit.name}`],
131
+ ["launchctl", "disable", `${domain}/${unit.name}`],
132
+ ],
133
+ state: async (unit) => {
134
+ const ran = await system.run(["launchctl", "print", `${domain}/${unit.name}`], env);
135
+ if (ran.code !== 0)
136
+ return { loaded: false, active: false };
137
+ const state = /^\s*state = (.+)$/m.exec(ran.stdout)?.[1]?.trim();
138
+ const pid = Number(/^\s*pid = (\d+)$/m.exec(ran.stdout)?.[1]);
139
+ return {
140
+ loaded: true,
141
+ active: state === "running",
142
+ ...(pid > 0 ? { pid } : {}),
143
+ ...(state ? { detail: state } : {}),
144
+ };
145
+ },
146
+ // launchd opens the log itself and does not create its folder.
147
+ prepare: (unit) => {
148
+ if (unit.logPath)
149
+ mkdirSync(dirname(unit.logPath), { recursive: true, mode: 0o700 });
150
+ },
151
+ logs: async (unit, lines) => tail(unit.logPath, lines),
152
+ atLogin: () => "from then on it also starts at every login, until `server stop`",
153
+ };
154
+ };
155
+ export const tail = (path, lines) => path && existsSync(path) ? `${readFileSync(path, "utf8").split("\n").filter(Boolean).slice(-lines).join("\n")}\n` : "";
156
+ /** Where there is neither: `start` still runs serve in the background; only `install` needs one of them. */
157
+ export const noUnits = (app, system) => {
158
+ const refuse = () => {
159
+ throw new CliError("validation_error", `${app.command} server install knows systemd (Linux) and launchd (macOS), not ${system.platform} — \`${app.command} server start\` runs serve without a unit`);
160
+ };
161
+ return {
162
+ unit: (base) => ({ ...base, name: `${app.command}-serve-${base.profile}`, path: "" }),
163
+ text: refuse,
164
+ start: refuse,
165
+ stop: refuse,
166
+ state: async () => ({ loaded: false, active: false }),
167
+ logs: async () => "",
168
+ atLogin: () => "",
169
+ };
170
+ };
171
+ export const platformFor = (app, system, env) => {
172
+ if (system.platform === "linux")
173
+ return systemd(app, system, env);
174
+ if (system.platform === "darwin")
175
+ return launchd(app, system, env);
176
+ return noUnits(app, system);
177
+ };
178
+ //# sourceMappingURL=units.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"units.js","sourceRoot":"","sources":["../../src/background/units.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,SAAS,CAAA;AAC7D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAA;AAe5C;;;;GAIG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,GAAgB,EAAE,GAAsB,EAA0B,EAAE;IACpG,MAAM,KAAK,GAAG;QACZ,GAAG,CAAC,YAAY,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,SAAS,IAAI,IAAI,EAAE,CAAC;QACrF,iBAAiB;QACjB,qBAAqB;KACtB,CAAA;IACD,OAAO,MAAM,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAA;AAC1G,CAAC,CAAA;AAeD,MAAM,IAAI,GAAG,CAAC,GAAsB,EAAU,EAAE;IAC9C,IAAI,CAAC,GAAG,CAAC,IAAI;QAAE,MAAM,IAAI,QAAQ,CAAC,qBAAqB,EAAE,sDAAsD,CAAC,CAAA;IAChH,OAAO,GAAG,CAAC,IAAI,CAAA;AACjB,CAAC,CAAA;AAED,2GAA2G;AAC3G,MAAM,YAAY,GAAG,CAAC,KAAa,EAAE,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,EAAE,EAAE,EAAE;IAC/D,MAAM,MAAM,GAAG,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,IAAI,CAAC,CAAA;IAC1F,OAAO,IAAI,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,GAAG,CAAA;AACjE,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAgB,EAAE,MAAoB,EAAE,GAAsB,EAAY,EAAE;IAClG,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,SAAS,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,CAAA;IACzF,MAAM,OAAO,GAAG,KAAK,EAAE,IAAc,EAAE,EAAE;QACvC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,CAAA;QACvC,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;YAAE,MAAM,IAAI,QAAQ,CAAC,qBAAqB,EAAE,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,YAAY,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;QAC/G,OAAO,GAAG,CAAC,MAAM,CAAA;IACnB,CAAC,CAAA;IACD,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,UAAU,CAAA;YAC3D,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,EAAE,CAAA;QACpD,CAAC;QACD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CACb;YACE,QAAQ;YACR,eAAe,GAAG,CAAC,OAAO,4DAA4D,IAAI,CAAC,OAAO,GAAG;YACrG,EAAE;YACF,WAAW;YACX,aAAa,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE;YAC1F,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,eAAe,YAAY,CAAC,GAAG,IAAI,IAAI,KAAK,EAAE,CAAC,EAAE,CAAC;YAC7G,oBAAoB;YACpB,eAAe;YACf,EAAE;YACF,WAAW;YACX,yBAAyB;YACzB,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC;QACd,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;YACf,CAAC,WAAW,EAAE,QAAQ,EAAE,eAAe,CAAC;YACxC,CAAC,WAAW,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,IAAI,CAAC;SAC5C;QACD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;QAC5D,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAC1B;gBACE,WAAW;gBACX,QAAQ;gBACR,MAAM;gBACN,IAAI,CAAC,IAAI;gBACT,IAAI;gBACJ,WAAW;gBACX,IAAI;gBACJ,aAAa;gBACb,IAAI;gBACJ,UAAU;gBACV,IAAI;gBACJ,SAAS;aACV,EACD,GAAG,CACJ,CAAA;YACD,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAC/B,GAAG,CAAC,MAAM;iBACP,KAAK,CAAC,IAAI,CAAC;iBACX,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;iBACpC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CACxF,CAAA;YACD,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAA;YAClC,OAAO;gBACL,MAAM,EAAE,MAAM,CAAC,SAAS,KAAK,QAAQ;gBACrC,MAAM,EAAE,MAAM,CAAC,WAAW,KAAK,QAAQ;gBACvC,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3B,GAAG,CAAC,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC,WAAW,KAAK,MAAM,CAAC,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACxF,CAAA;QACH,CAAC;QACD,IAAI,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,OAAO,CAAC,CAAC,YAAY,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,IAAI,EAAE,MAAM,CAAC,KAAK,CAAC,EAAE,YAAY,CAAC,CAAC;QAC5G,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,+CAA+C,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,EAAE;KACxG,CAAA;AACH,CAAC,CAAA;AAED,MAAM,GAAG,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,KAAK,CAAC,UAAU,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC,UAAU,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAE7G,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAgB,EAAE,MAAoB,EAAE,GAAsB,EAAY,EAAE;IAClG,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,SAAS,EAAE,cAAc,CAAC,CAAA;IACzD,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,GAAG,EAAE,CAAA;IAClC,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE;YACb,MAAM,IAAI,GAAG,GAAG,GAAG,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,CAAA;YACnD,OAAO,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,MAAM,EAAE,GAAG,IAAI,QAAQ,CAAC,EAAE,CAAA;QAC/D,CAAC;QACD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CACb;YACE,wCAAwC;YACxC,wGAAwG;YACxG,uBAAuB;YACvB,QAAQ;YACR,6BAA6B,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW;YACtD,+BAA+B;YAC/B,WAAW;YACX,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,eAAe,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC;YAClE,YAAY;YACZ,mCAAmC;YACnC,UAAU;YACV,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,GAAG,CACrC,CAAC,CAAC,IAAI,EAAE,KAAK,CAAC,EAAE,EAAE,CAAC,YAAY,GAAG,CAAC,IAAI,CAAC,iBAAiB,GAAG,CAAC,KAAK,CAAC,WAAW,CAC/E;YACD,WAAW;YACX,+FAA+F;YAC/F,8BAA8B;YAC9B,+BAA+B;YAC/B,sEAAsE;YACtE,oDAAoD;YACpD,uCAAuC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,WAAW;YACzE,yCAAyC,GAAG,CAAC,IAAI,CAAC,OAAO,IAAI,EAAE,CAAC,WAAW;YAC3E,SAAS;YACT,UAAU;YACV,EAAE;SACH,CAAC,IAAI,CAAC,IAAI,CAAC;QACd,uGAAuG;QACvG,KAAK,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;YACf,CAAC,WAAW,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACjD,CAAC,WAAW,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC;SAC9C;QACD,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC;YACd,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YAClD,CAAC,WAAW,EAAE,SAAS,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;SACnD;QACD,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,EAAE;YACpB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,GAAG,CAAC,CAAC,WAAW,EAAE,OAAO,EAAE,GAAG,MAAM,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC,EAAE,GAAG,CAAC,CAAA;YACnF,IAAI,GAAG,CAAC,IAAI,KAAK,CAAC;gBAAE,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;YAC3D,MAAM,KAAK,GAAG,oBAAoB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAA;YAChE,MAAM,GAAG,GAAG,MAAM,CAAC,mBAAmB,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,CAAA;YAC7D,OAAO;gBACL,MAAM,EAAE,IAAI;gBACZ,MAAM,EAAE,KAAK,KAAK,SAAS;gBAC3B,GAAG,CAAC,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3B,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACpC,CAAA;QACH,CAAC;QACD,+DAA+D;QAC/D,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAChB,IAAI,IAAI,CAAC,OAAO;gBAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAA;QACtF,CAAC;QACD,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC;QACtD,OAAO,EAAE,GAAG,EAAE,CAAC,iEAAiE;KACjF,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,IAAI,GAAG,CAAC,IAAwB,EAAE,KAAa,EAAU,EAAE,CACtE,IAAI,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAA;AAExH,4GAA4G;AAC5G,MAAM,CAAC,MAAM,OAAO,GAAG,CAAC,GAAgB,EAAE,MAAoB,EAAY,EAAE;IAC1E,MAAM,MAAM,GAAG,GAAU,EAAE;QACzB,MAAM,IAAI,QAAQ,CAChB,kBAAkB,EAClB,GAAG,GAAG,CAAC,OAAO,kEAAkE,MAAM,CAAC,QAAQ,QAAQ,GAAG,CAAC,OAAO,2CAA2C,CAC9J,CAAA;IACH,CAAC,CAAA;IACD,OAAO;QACL,IAAI,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,IAAI,EAAE,GAAG,GAAG,CAAC,OAAO,UAAU,IAAI,CAAC,OAAO,EAAE,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;QACrF,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,MAAM;QACb,IAAI,EAAE,MAAM;QACZ,KAAK,EAAE,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;QACrD,IAAI,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE;QACpB,OAAO,EAAE,GAAG,EAAE,CAAC,EAAE;KAClB,CAAA;AACH,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAgB,EAAE,MAAoB,EAAE,GAAsB,EAAY,EAAE;IACtG,IAAI,MAAM,CAAC,QAAQ,KAAK,OAAO;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IACjE,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,GAAG,CAAC,CAAA;IAClE,OAAO,OAAO,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;AAC7B,CAAC,CAAA"}
@@ -0,0 +1,29 @@
1
+ import type { Command } from "commander";
2
+ import type { Provider } from "../../domain/models.js";
3
+ import type { AppIdentity } from "../app.js";
4
+ import type { MessengerCore } from "../messenger/port.js";
5
+ import type { GlobalFlags, ResolveOptions, Settings } from "../settings.js";
6
+ import type { ChatRegistry } from "./registry.js";
7
+ import type { BotTokenStore } from "./token.js";
8
+ /**
9
+ * The part of the personal core a bot can do. It grows as bot commands move here; a bot never gets
10
+ * `chats` or `history` from this type, because neither Bot API lists a bot's chats.
11
+ */
12
+ export type BotAdapter = Pick<MessengerCore, "me" | "close">;
13
+ /** What one messenger CLI hands the shared bot commands. */
14
+ export interface BotMessenger {
15
+ app: AppIdentity;
16
+ /** How the store tells this messenger's bots from its personal accounts: `max-bot`, `telegram-bot`. */
17
+ provider: Provider;
18
+ /** The messenger's own name, as its users write it — `MAX`, `Telegram`. */
19
+ name?: string;
20
+ /** Called with `kind: "bot"`. */
21
+ resolveSettings: (flags: GlobalFlags, options?: ResolveOptions) => Settings;
22
+ /** A client for this token; `stop` ends a command that runs until told to, and its request in flight. */
23
+ connect: (command: Command, token: string, stop?: AbortSignal) => Promise<BotAdapter>;
24
+ /** Where the token lives; the shared `BotTokenStore` when unset. A CLI's tests put their own here. */
25
+ tokenStore?: (command: Command, profile: string) => BotTokenStore;
26
+ /** The chats the bot has seen; the shared `ChatRegistry` when unset. */
27
+ registry?: (command: Command, profile: string) => ChatRegistry;
28
+ }
29
+ //# sourceMappingURL=port.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.d.ts","sourceRoot":"","sources":["../../../src/cli/bot/port.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACxC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAA;AACtD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAC5C,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC3E,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAE/C;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,IAAI,GAAG,OAAO,CAAC,CAAA;AAE5D,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,GAAG,EAAE,WAAW,CAAA;IAChB,uGAAuG;IACvG,QAAQ,EAAE,QAAQ,CAAA;IAClB,2EAA2E;IAC3E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,iCAAiC;IACjC,eAAe,EAAE,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,cAAc,KAAK,QAAQ,CAAA;IAC3E,yGAAyG;IACzG,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,WAAW,KAAK,OAAO,CAAC,UAAU,CAAC,CAAA;IACrF,sGAAsG;IACtG,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,aAAa,CAAA;IACjE,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,YAAY,CAAA;CAC/D"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=port.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"port.js","sourceRoot":"","sources":["../../../src/cli/bot/port.ts"],"names":[],"mappings":""}
@@ -0,0 +1,37 @@
1
+ import type { Chat } from "../../domain/models.js";
2
+ import type { AppIdentity } from "../app.js";
3
+ export interface SeenChat extends Chat {
4
+ firstSeenAt: string;
5
+ lastSeenAt: string;
6
+ }
7
+ /** Everything this machine keeps about its bots: seen chats, recipients, the send journal. */
8
+ export declare const botsDirectory: (app: AppIdentity, env?: NodeJS.ProcessEnv) => string;
9
+ /** One bot's files. The names are what max-cli has always used: a moved path would lose a recipient list, and with it the limit. */
10
+ export declare const botFiles: (app: AppIdentity, profile: string, env?: NodeJS.ProcessEnv) => {
11
+ registry: string;
12
+ recipients: string;
13
+ journal: string;
14
+ };
15
+ /**
16
+ * The chats this bot has seen, because neither MAX's nor Telegram's Bot API has a call that lists them.
17
+ *
18
+ * State, not cache: the messenger cannot give it back, so clearing a cache must not take it. Titles
19
+ * are in it, hence 0600.
20
+ */
21
+ export declare class ChatRegistry {
22
+ #private;
23
+ constructor(app: AppIdentity, profile: string, env?: NodeJS.ProcessEnv);
24
+ list(): SeenChat[];
25
+ /** The bot's own user id, once any command has learned it — the key its messages are stored under. */
26
+ botId(): string | undefined;
27
+ rememberBot(id: string): void;
28
+ /** Makes this bot known to `bot list` before it has seen any chat. */
29
+ touch(): void;
30
+ /** Newer facts win; a chat seen only as an id keeps the title it had. */
31
+ observe(chats: readonly (Partial<Chat> & {
32
+ id: string;
33
+ })[], now?: Date): void;
34
+ }
35
+ /** Every profile that has ever kept a registry — the bots this machine has used. */
36
+ export declare const registryProfiles: (app: AppIdentity, env?: NodeJS.ProcessEnv) => string[];
37
+ //# sourceMappingURL=registry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../../src/cli/bot/registry.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AAE5C,MAAM,WAAW,QAAS,SAAQ,IAAI;IACpC,WAAW,EAAE,MAAM,CAAA;IACnB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,8FAA8F;AAC9F,eAAO,MAAM,aAAa,QAAS,WAAW,QAAO,MAAM,CAAC,UAAU,KAAiB,MACC,CAAA;AAExF,oIAAoI;AACpI,eAAO,MAAM,QAAQ,QAAS,WAAW,WAAW,MAAM,QAAO,MAAM,CAAC,UAAU;IAG9E,QAAQ;IACR,UAAU;IACV,OAAO;CAEV,CAAA;AAED;;;;;GAKG;AACH,qBAAa,YAAY;;IAGvB,YAAY,GAAG,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,EAAE,GAAG,GAAE,MAAM,CAAC,UAAwB,EAElF;IAED,IAAI,IAAI,QAAQ,EAAE,CAGjB;IAED,sGAAsG;IACtG,KAAK,IAAI,MAAM,GAAG,SAAS,CAG1B;IAED,WAAW,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAG5B;IAED,sEAAsE;IACtE,KAAK,IAAI,IAAI,CAEZ;IAED,yEAAyE;IACzE,OAAO,CAAC,KAAK,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,CAAC,GAAG;QAAE,EAAE,EAAE,MAAM,CAAA;KAAE,CAAC,EAAE,EAAE,GAAG,GAAE,IAAiB,GAAG,IAAI,CAuBxF;CAeF;AAED,oFAAoF;AACpF,eAAO,MAAM,gBAAgB,QAAS,WAAW,QAAO,MAAM,CAAC,UAAU,KAAiB,MAAM,EAO/F,CAAA"}
@@ -0,0 +1,94 @@
1
+ import { existsSync, readdirSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { resolvePaths, writeSecurely } from "@leemour/cli-core";
4
+ /** Everything this machine keeps about its bots: seen chats, recipients, the send journal. */
5
+ export const botsDirectory = (app, env = process.env) => join(resolvePaths({ appName: app.appName, prefix: app.envPrefix, env }).state, "bots");
6
+ /** One bot's files. The names are what max-cli has always used: a moved path would lose a recipient list, and with it the limit. */
7
+ export const botFiles = (app, profile, env = process.env) => {
8
+ const directory = botsDirectory(app, env);
9
+ return {
10
+ registry: join(directory, `${profile}.json`),
11
+ recipients: join(directory, "recipients", `${profile}.json`),
12
+ journal: join(directory, "sends", `${profile}.jsonl`),
13
+ };
14
+ };
15
+ /**
16
+ * The chats this bot has seen, because neither MAX's nor Telegram's Bot API has a call that lists them.
17
+ *
18
+ * State, not cache: the messenger cannot give it back, so clearing a cache must not take it. Titles
19
+ * are in it, hence 0600.
20
+ */
21
+ export class ChatRegistry {
22
+ #path;
23
+ constructor(app, profile, env = process.env) {
24
+ this.#path = botFiles(app, profile, env).registry;
25
+ }
26
+ list() {
27
+ const { chats } = this.#read();
28
+ return Array.isArray(chats) ? chats : [];
29
+ }
30
+ /** The bot's own user id, once any command has learned it — the key its messages are stored under. */
31
+ botId() {
32
+ const { botId } = this.#read();
33
+ return typeof botId === "string" ? botId : undefined;
34
+ }
35
+ rememberBot(id) {
36
+ const file = this.#read();
37
+ if (file.botId !== id)
38
+ this.#write({ ...file, botId: id });
39
+ }
40
+ /** Makes this bot known to `bot list` before it has seen any chat. */
41
+ touch() {
42
+ if (!existsSync(this.#path))
43
+ writeSecurely(this.#path, `${JSON.stringify({ chats: [] }, null, 2)}\n`, 0o600);
44
+ }
45
+ /** Newer facts win; a chat seen only as an id keeps the title it had. */
46
+ observe(chats, now = new Date()) {
47
+ if (chats.length === 0)
48
+ return;
49
+ const byId = new Map(this.list().map((chat) => [chat.id, chat]));
50
+ const at = now.toISOString();
51
+ for (const chat of chats) {
52
+ const known = byId.get(chat.id);
53
+ const defined = Object.fromEntries(Object.entries(chat).filter(([, value]) => value !== undefined && value !== null));
54
+ byId.set(chat.id, {
55
+ title: null,
56
+ kind: "unknown",
57
+ unreadCount: null,
58
+ lastMessageAt: null,
59
+ participantsCount: null,
60
+ ...known,
61
+ ...defined,
62
+ id: chat.id,
63
+ firstSeenAt: known?.firstSeenAt ?? at,
64
+ lastSeenAt: at,
65
+ });
66
+ }
67
+ this.#write({ ...this.#read(), chats: [...byId.values()] });
68
+ }
69
+ #read() {
70
+ if (!existsSync(this.#path))
71
+ return {};
72
+ try {
73
+ const parsed = JSON.parse(readFileSync(this.#path, "utf8"));
74
+ return parsed && typeof parsed === "object" ? parsed : {};
75
+ }
76
+ catch {
77
+ return {};
78
+ }
79
+ }
80
+ #write(file) {
81
+ writeSecurely(this.#path, `${JSON.stringify(file, null, 2)}\n`, 0o600);
82
+ }
83
+ }
84
+ /** Every profile that has ever kept a registry — the bots this machine has used. */
85
+ export const registryProfiles = (app, env = process.env) => {
86
+ const directory = botsDirectory(app, env);
87
+ if (!existsSync(directory))
88
+ return [];
89
+ return readdirSync(directory, { withFileTypes: true })
90
+ .filter((entry) => entry.isFile() && entry.name.endsWith(".json"))
91
+ .map((entry) => entry.name)
92
+ .map((name) => name.slice(0, -".json".length));
93
+ };
94
+ //# sourceMappingURL=registry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"registry.js","sourceRoot":"","sources":["../../../src/cli/bot/registry.ts"],"names":[],"mappings":"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"}