@stage-labs/metro 0.1.0-beta.4 → 0.1.0-beta.40

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 (249) hide show
  1. package/dist/api.js +131 -0
  2. package/dist/bedrock-eventstream.js +120 -0
  3. package/dist/bedrock-proxy.js +260 -0
  4. package/dist/bedrock-upstream.js +72 -0
  5. package/dist/bedrock.js +106 -0
  6. package/dist/claude.js +15 -0
  7. package/dist/cli.js +181 -207
  8. package/dist/control.js +173 -0
  9. package/dist/plugin.js +82 -0
  10. package/dist/prompt.js +61 -0
  11. package/dist/runtime.js +117 -0
  12. package/dist/serve.js +77 -0
  13. package/dist/store.js +93 -0
  14. package/dist/tail.js +120 -0
  15. package/dist/update.js +33 -0
  16. package/dist/version.js +97 -0
  17. package/package.json +34 -21
  18. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  19. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +119 -0
  20. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  21. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  22. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  23. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +210 -0
  24. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  25. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  26. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  27. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  28. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  29. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  30. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  31. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  32. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  33. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  34. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  35. package/runtime/node_modules/@metro-labs/mcp/package.json +67 -0
  36. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
  37. package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +367 -0
  38. package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
  39. package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
  40. package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
  41. package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
  42. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
  43. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +334 -0
  44. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-connector-api.ts +150 -0
  45. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-import.ts +76 -0
  46. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-pair.ts +20 -0
  47. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
  48. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +153 -0
  49. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
  50. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
  51. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +133 -0
  52. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +278 -0
  53. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +383 -0
  54. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +143 -0
  55. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +33 -0
  56. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +315 -0
  57. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
  58. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
  59. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
  60. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +100 -0
  61. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +57 -0
  62. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +333 -0
  63. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
  64. package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
  65. package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +117 -0
  66. package/runtime/node_modules/@metro-labs/mcp/src/daemon/hosted-connectors.ts +91 -0
  67. package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +369 -0
  68. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +86 -0
  69. package/runtime/node_modules/@metro-labs/mcp/src/daemon/import-api.ts +67 -0
  70. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-connectors-api.ts +56 -0
  71. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +187 -0
  72. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-secret.ts +21 -0
  73. package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
  74. package/runtime/node_modules/@metro-labs/mcp/src/daemon/mode-api.ts +31 -0
  75. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +162 -0
  76. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +120 -0
  77. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
  78. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +58 -0
  79. package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +145 -0
  80. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +214 -0
  81. package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +224 -0
  82. package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +303 -0
  83. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +139 -0
  84. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +105 -0
  85. package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
  86. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +27 -0
  87. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +63 -0
  88. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-config.ts +27 -0
  89. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +218 -0
  90. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-auth.ts +82 -0
  91. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-nonces.ts +43 -0
  92. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-routes.ts +89 -0
  93. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
  94. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +320 -0
  95. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
  96. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
  97. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +257 -0
  98. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +295 -0
  99. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
  100. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +91 -0
  101. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +214 -0
  102. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-connectors.ts +143 -0
  103. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +84 -0
  104. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
  105. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +37 -0
  106. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +147 -0
  107. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +126 -0
  108. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +326 -0
  109. package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +319 -0
  110. package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +152 -0
  111. package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +21 -0
  112. package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
  113. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +373 -0
  114. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +309 -0
  115. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +24 -0
  116. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +105 -0
  117. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +118 -0
  118. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +45 -0
  119. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +72 -0
  120. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +18 -0
  121. package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +141 -0
  122. package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +203 -0
  123. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
  124. package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
  125. package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
  126. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
  127. package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
  128. package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +192 -0
  129. package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
  130. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
  131. package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
  132. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +73 -0
  133. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
  134. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
  135. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +211 -0
  136. package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
  137. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +212 -0
  138. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +397 -0
  139. package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
  140. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +240 -0
  141. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
  142. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +80 -0
  143. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
  144. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
  145. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
  146. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
  147. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +182 -0
  148. package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
  149. package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
  150. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
  151. package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
  152. package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
  153. package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -0
  154. package/runtime/node_modules/@metro-labs/telegram/package.json +28 -0
  155. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +81 -0
  156. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  157. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  158. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  159. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +141 -0
  160. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  161. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  162. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  163. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  164. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  165. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  166. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  167. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  168. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  169. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  170. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  171. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  172. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  173. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  174. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  175. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  176. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
  177. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  178. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  179. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  180. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  181. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  183. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  184. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  185. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +40 -0
  186. package/runtime/node_modules/@metro-labs/whatsapp/package.json +29 -0
  187. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +67 -0
  188. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  189. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  190. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  191. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  192. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +318 -0
  193. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  194. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +132 -0
  195. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  196. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  197. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  198. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  199. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  200. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  201. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  202. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  203. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +141 -0
  204. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  205. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  206. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  207. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  208. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  209. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +113 -0
  210. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  211. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  212. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  213. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  214. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  215. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  216. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  217. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  218. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  219. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +333 -0
  220. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +78 -0
  221. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  222. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  223. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  224. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  225. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  226. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  227. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  228. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  229. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  230. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  231. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  232. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  233. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +39 -0
  234. package/runtime/runtime.json +3 -0
  235. package/runtime/trains/.keep +0 -0
  236. package/LICENSE +0 -21
  237. package/README.md +0 -117
  238. package/dist/agents/claude.js +0 -207
  239. package/dist/agents/codex.js +0 -207
  240. package/dist/agents/types.js +0 -2
  241. package/dist/channels/discord.js +0 -104
  242. package/dist/channels/telegram.js +0 -189
  243. package/dist/helpers/scope-cache.js +0 -65
  244. package/dist/helpers/streaming.js +0 -209
  245. package/dist/helpers/telegram-format.js +0 -39
  246. package/dist/helpers/turn.js +0 -40
  247. package/dist/log.js +0 -10
  248. package/dist/orchestrator.js +0 -208
  249. package/dist/paths.js +0 -68
@@ -0,0 +1,178 @@
1
+ import { TrainError } from '@metro-labs/mcp/train-error';
2
+ import { errMsg } from '@metro-labs/mcp/log';
3
+ import { kindOf } from '@metro-labs/mcp/stations/attachments';
4
+ import {
5
+ makeStation,
6
+ respond,
7
+ type CallMsg,
8
+ type StationHandler,
9
+ } from '@metro-labs/mcp/stations/station-runtime';
10
+ import { accountFor, accounts, targetOf } from './accounts.js';
11
+ import { normalizeWhatsApp } from './normalize.js';
12
+ import type { WAClient } from './client.js';
13
+
14
+ type Args = Record<string, unknown>;
15
+
16
+ interface Resolved {
17
+ accountId: string;
18
+ client: WAClient;
19
+ jid: string;
20
+ }
21
+
22
+ type ClientFor = (accountId: string) => WAClient;
23
+
24
+ function str(v: unknown): string | undefined {
25
+ return typeof v === 'string' ? v : undefined;
26
+ }
27
+
28
+ function resolve(args: Args, clientFor: ClientFor): Resolved {
29
+ const line = str(args.line);
30
+ if (!line) throw new TrainError('bad_request', 'missing line');
31
+ const target = targetOf(line);
32
+ if (!target) throw new TrainError('bad_request', `bad line '${line}'`);
33
+ const accountId = accountFor({ account: str(args.account), line });
34
+ return { accountId, client: clientFor(accountId), jid: target.jid };
35
+ }
36
+
37
+ async function guard<T>(run: () => Promise<T>): Promise<T> {
38
+ try {
39
+ return await run();
40
+ } catch (e) {
41
+ if (e instanceof TrainError) throw e;
42
+ const msg = errMsg(e);
43
+ if (/rate.?over.?limit|too many|429/i.test(msg))
44
+ throw new TrainError('rate_limited', msg);
45
+ throw new TrainError('whatsapp_call', msg);
46
+ }
47
+ }
48
+
49
+ interface WireAttachment {
50
+ kind?: string;
51
+ path?: string;
52
+ url?: string;
53
+ mime?: string;
54
+ name?: string;
55
+ }
56
+
57
+ function attachmentsOf(args: Args): WireAttachment[] {
58
+ const raw = args.attachments;
59
+ if (!Array.isArray(raw)) return [];
60
+ return (raw as WireAttachment[]).filter((a) => Boolean(a.path ?? a.url));
61
+ }
62
+
63
+ async function sendMediaSet(
64
+ client: WAClient,
65
+ jid: string,
66
+ atts: WireAttachment[],
67
+ text: string,
68
+ replyTo: string | undefined,
69
+ ): Promise<{ messageId: string; labels: string[] }> {
70
+ const labels: string[] = [];
71
+ let messageId = '';
72
+ for (let i = 0; i < atts.length; i++) {
73
+ const att = atts[i];
74
+ if (!att) continue;
75
+ const path = att.path ?? att.url ?? '';
76
+ const kind = att.kind ?? kindOf(att.mime ?? '', path);
77
+ messageId = await guard(() =>
78
+ client.sendMedia(
79
+ jid,
80
+ {
81
+ kind,
82
+ path,
83
+ mime: att.mime ?? 'application/octet-stream',
84
+ name: att.name ?? path.split('/').pop() ?? 'attachment',
85
+ ...(i === 0 && text ? { caption: text } : {}),
86
+ },
87
+ replyTo,
88
+ ),
89
+ );
90
+ labels.push(kind);
91
+ }
92
+ return { messageId, labels };
93
+ }
94
+
95
+ function makeSend(clientFor: ClientFor): StationHandler {
96
+ return async (id, args) => {
97
+ const { accountId, client, jid } = resolve(args, clientFor);
98
+ const text = str(args.text) ?? '';
99
+ const replyTo = str(args.replyTo);
100
+ const atts = attachmentsOf(args);
101
+ if (atts.length) {
102
+ const { messageId, labels } = await sendMediaSet(
103
+ client,
104
+ jid,
105
+ atts,
106
+ text,
107
+ replyTo,
108
+ );
109
+ respond(id, {
110
+ result: { messageId, account: accountId, attachments: labels },
111
+ });
112
+ return;
113
+ }
114
+ const messageId = await guard(() => client.sendText(jid, text, replyTo));
115
+ respond(id, { result: { messageId, account: accountId } });
116
+ };
117
+ }
118
+
119
+ function makeReact(clientFor: ClientFor): StationHandler {
120
+ return async (id, args) => {
121
+ const { accountId, client, jid } = resolve(args, clientFor);
122
+ const messageId = str(args.messageId) ?? '';
123
+ const emoji = str(args.emoji) ?? '';
124
+ await guard(() => client.sendReaction(jid, messageId, emoji));
125
+ respond(id, { result: { ok: true, account: accountId } });
126
+ };
127
+ }
128
+
129
+ function makeEdit(clientFor: ClientFor): StationHandler {
130
+ return async (id, args) => {
131
+ const { accountId, client, jid } = resolve(args, clientFor);
132
+ const messageId = str(args.messageId) ?? '';
133
+ const text = str(args.text) ?? '';
134
+ await guard(() => client.editMessage(jid, messageId, text));
135
+ respond(id, { result: { ok: true, account: accountId } });
136
+ };
137
+ }
138
+
139
+ function makeDelete(clientFor: ClientFor): StationHandler {
140
+ return async (id, args) => {
141
+ const { accountId, client, jid } = resolve(args, clientFor);
142
+ const messageId = str(args.messageId) ?? '';
143
+ await guard(() => client.deleteMessage(jid, messageId));
144
+ respond(id, { result: { ok: true, account: accountId } });
145
+ };
146
+ }
147
+
148
+ function makeAccounts(clientFor: ClientFor): StationHandler {
149
+ return (id) => {
150
+ const list = [...accounts.values()].map((a) => {
151
+ const phone = clientFor(a.id).self();
152
+ return {
153
+ id: a.id,
154
+ handle: phone === null ? null : `+${phone}`,
155
+ url: phone === null ? null : `https://wa.me/${phone}`,
156
+ connected: phone !== null,
157
+ owner: a.owner ?? null,
158
+ };
159
+ });
160
+ respond(id, { result: { accounts: list } });
161
+ return Promise.resolve();
162
+ };
163
+ }
164
+
165
+ export function makeHandleCall(
166
+ clientFor: ClientFor,
167
+ ): (msg: CallMsg) => Promise<void> {
168
+ return makeStation({
169
+ handlers: {
170
+ accounts: makeAccounts(clientFor),
171
+ send: makeSend(clientFor),
172
+ react: makeReact(clientFor),
173
+ edit: makeEdit(clientFor),
174
+ delete: makeDelete(clientFor),
175
+ },
176
+ normalize: normalizeWhatsApp,
177
+ });
178
+ }
@@ -0,0 +1,72 @@
1
+ import { downloadMediaMessage, type WAMessage } from 'baileys';
2
+ import {
3
+ assertAttachmentSize,
4
+ saveStreamToCache,
5
+ type SavedAttachment,
6
+ } from '@metro-labs/mcp/stations/attachments';
7
+ import { TrainError } from '@metro-labs/mcp/train-error';
8
+ import { baileysLogger } from './logger.js';
9
+ import type { WAMediaRef } from './media.js';
10
+
11
+ export type { SavedAttachment };
12
+
13
+ const IDLE_TIMEOUT_MS = 60_000;
14
+
15
+ export type ReuploadRequest = (m: WAMessage) => Promise<WAMessage>;
16
+
17
+ export async function* withIdleTimeout(
18
+ chunks: AsyncIterable<Uint8Array>,
19
+ ms: number,
20
+ ): AsyncGenerator<Uint8Array> {
21
+ const iterator = chunks[Symbol.asyncIterator]();
22
+ for (;;) {
23
+ let timer: ReturnType<typeof setTimeout> | undefined;
24
+ const idle = new Promise<never>((_resolve, reject) => {
25
+ timer = setTimeout(() => {
26
+ reject(
27
+ new TrainError(
28
+ 'whatsapp_media_stalled',
29
+ `WhatsApp sent no media bytes for ${ms}ms, so the download was abandoned`,
30
+ ),
31
+ );
32
+ }, ms);
33
+ });
34
+ try {
35
+ const next = await Promise.race([iterator.next(), idle]);
36
+ if (next.done === true) return;
37
+ yield next.value;
38
+ } finally {
39
+ clearTimeout(timer);
40
+ }
41
+ }
42
+ }
43
+
44
+ export async function saveWhatsAppMedia(
45
+ accountId: string,
46
+ raw: WAMessage,
47
+ ref: WAMediaRef,
48
+ messageId: string,
49
+ index = 0,
50
+ reupload?: ReuploadRequest,
51
+ ): Promise<SavedAttachment> {
52
+ if (ref.bytes !== undefined) assertAttachmentSize(ref.bytes);
53
+ const stream = await downloadMediaMessage(
54
+ raw,
55
+ 'stream',
56
+ {},
57
+ reupload
58
+ ? { reuploadRequest: reupload, logger: baileysLogger(accountId) }
59
+ : undefined,
60
+ );
61
+ try {
62
+ return await saveStreamToCache(
63
+ withIdleTimeout(stream, IDLE_TIMEOUT_MS),
64
+ messageId,
65
+ index,
66
+ { mime: ref.mime, name: ref.name },
67
+ );
68
+ } catch (err) {
69
+ stream.destroy();
70
+ throw err;
71
+ }
72
+ }
@@ -0,0 +1,129 @@
1
+ import {
2
+ BufferJSON,
3
+ initAuthCreds,
4
+ proto,
5
+ type AuthenticationCreds,
6
+ type AuthenticationState,
7
+ type SignalDataSet,
8
+ type SignalDataTypeMap,
9
+ type SignalKeyStore,
10
+ } from 'baileys';
11
+ import { TrainError } from '@metro-labs/mcp/train-error';
12
+ import {
13
+ isPersistedKeyType,
14
+ tokenStoreFor,
15
+ type KeyTable,
16
+ } from './token-store.js';
17
+
18
+ interface AuthBlob {
19
+ creds: AuthenticationCreds;
20
+ keys: KeyTable;
21
+ }
22
+
23
+ function encode(value: unknown): unknown {
24
+ return JSON.parse(JSON.stringify(value, BufferJSON.replacer));
25
+ }
26
+
27
+ function decode(value: unknown): unknown {
28
+ return JSON.parse(JSON.stringify(value), BufferJSON.reviver);
29
+ }
30
+
31
+ function loadBlob(raw: unknown): AuthBlob {
32
+ const obj = raw as { creds?: unknown; keys?: KeyTable } | null;
33
+ const creds = obj?.creds;
34
+ if (creds === undefined || creds === null)
35
+ throw new TrainError(
36
+ 'whatsapp_auth',
37
+ 'stored credentials blob has no creds',
38
+ );
39
+ return {
40
+ creds: decode(creds) as AuthenticationCreds,
41
+ keys: obj?.keys ?? {},
42
+ };
43
+ }
44
+
45
+ function makeKeyStore(
46
+ table: KeyTable,
47
+ onPersistedWrite?: (table: KeyTable) => void,
48
+ ): SignalKeyStore {
49
+ return {
50
+ get: <T extends keyof SignalDataTypeMap>(type: T, ids: string[]) => {
51
+ const bucket = table[type] ?? {};
52
+ const data: Record<string, SignalDataTypeMap[T]> = {};
53
+ for (const id of ids) {
54
+ const raw = bucket[id];
55
+ if (raw === undefined || raw === null) continue;
56
+ const revived = decode(raw);
57
+ data[id] = (
58
+ type === 'app-state-sync-key'
59
+ ? proto.Message.AppStateSyncKeyData.fromObject(
60
+ revived as Record<string, unknown>,
61
+ )
62
+ : revived
63
+ ) as SignalDataTypeMap[T];
64
+ }
65
+ return data;
66
+ },
67
+ set: (data: SignalDataSet) => {
68
+ let persisted = false;
69
+ for (const category of Object.keys(data) as (keyof SignalDataSet)[]) {
70
+ const items = data[category];
71
+ if (!items) continue;
72
+ if (isPersistedKeyType(category)) persisted = true;
73
+ const bucket = (table[category] ??= {});
74
+ for (const id of Object.keys(items)) {
75
+ const value = items[id];
76
+ bucket[id] = value ? encode(value) : undefined;
77
+ }
78
+ }
79
+ if (persisted) onPersistedWrite?.(table);
80
+ },
81
+ };
82
+ }
83
+
84
+ export function inMemoryAuthState(
85
+ raw?: unknown,
86
+ opts?: { seed?: KeyTable; onPersistedWrite?: (table: KeyTable) => void },
87
+ ): {
88
+ state: AuthenticationState;
89
+ serialize: () => unknown;
90
+ } {
91
+ const blob: AuthBlob =
92
+ raw === undefined || raw === null
93
+ ? { creds: initAuthCreds(), keys: {} }
94
+ : loadBlob(raw);
95
+ for (const [type, bucket] of Object.entries(opts?.seed ?? {}))
96
+ blob.keys[type] = { ...blob.keys[type], ...bucket };
97
+ const state: AuthenticationState = {
98
+ creds: blob.creds,
99
+ keys: makeKeyStore(blob.keys, opts?.onPersistedWrite),
100
+ };
101
+ return {
102
+ state,
103
+ serialize: () => ({ creds: encode(blob.creds), keys: blob.keys }),
104
+ };
105
+ }
106
+
107
+ export function useAccountAuthState(
108
+ credentials: unknown,
109
+ accountId: string,
110
+ ): {
111
+ state: AuthenticationState;
112
+ saveCreds: () => Promise<void>;
113
+ } {
114
+ if (credentials === undefined || credentials === null)
115
+ throw new TrainError(
116
+ 'whatsapp_auth',
117
+ `no WhatsApp credentials in accounts for '${accountId}' — run scripts/login.ts to pair`,
118
+ );
119
+ const store = tokenStoreFor(accountId, (message) => {
120
+ process.stderr.write(`whatsapp[${accountId}] token store: ${message}\n`);
121
+ });
122
+ const { state } = inMemoryAuthState(credentials, {
123
+ seed: store.load(),
124
+ onPersistedWrite: (table) => {
125
+ store.scheduleSave(table);
126
+ },
127
+ });
128
+ return { state, saveCreds: () => Promise.resolve() };
129
+ }
@@ -0,0 +1,318 @@
1
+ import makeWASocket, {
2
+ Browsers,
3
+ DisconnectReason,
4
+ fetchLatestWaWebVersion,
5
+ type proto,
6
+ type WAMessage,
7
+ type WAMessageKey,
8
+ type WASocket,
9
+ } from 'baileys';
10
+ import { TrainError } from '@metro-labs/mcp/train-error';
11
+ import { errMsg } from '@metro-labs/mcp/log';
12
+ import type { WhatsAppAccount } from './types.js';
13
+ import type { InboundMessage, ReactionInput } from './format.js';
14
+ import { toInbound, toReaction, type ReactionEvent } from './parse.js';
15
+ import { baileysLogger } from './logger.js';
16
+ import { useAccountAuthState } from './auth-state.js';
17
+ import { knownKey, makeKeyCache, targetKey, type KeyCache } from './keys.js';
18
+ import { makeOutbox, type Outbox } from './outbox.js';
19
+ import { deliveryNotes, describeNote } from './delivery.js';
20
+ import {
21
+ ACK_WAIT_MS,
22
+ bindAcks,
23
+ makeAckWatch,
24
+ rejection,
25
+ type AckWatch,
26
+ } from './ack.js';
27
+
28
+ export interface InboundHandlers {
29
+ onMessage(m: InboundMessage, raw: WAMessage): void;
30
+ onReaction(r: ReactionInput): void;
31
+ }
32
+
33
+ export interface WAMedia {
34
+ kind: string;
35
+ path: string;
36
+ mime: string;
37
+ name: string;
38
+ caption?: string;
39
+ }
40
+
41
+ export interface WAClient {
42
+ account: WhatsAppAccount;
43
+ self(): string | null;
44
+ start(handlers: InboundHandlers): Promise<void>;
45
+ sendText(jid: string, text: string, quotedId?: string): Promise<string>;
46
+ sendMedia(jid: string, media: WAMedia, quotedId?: string): Promise<string>;
47
+ sendReaction(jid: string, messageId: string, emoji: string): Promise<void>;
48
+ editMessage(jid: string, messageId: string, text: string): Promise<void>;
49
+ deleteMessage(jid: string, messageId: string): Promise<void>;
50
+ reuploadMedia(m: WAMessage): Promise<WAMessage>;
51
+ disconnect(): Promise<void>;
52
+ }
53
+
54
+ interface State {
55
+ account: WhatsAppAccount;
56
+ handlers?: InboundHandlers;
57
+ sock?: WASocket;
58
+ closed: boolean;
59
+ openResolve?: () => void;
60
+ openPromise: Promise<void>;
61
+ keys: KeyCache;
62
+ outbox: Outbox;
63
+ acks: AckWatch;
64
+ }
65
+
66
+ type SendOpts = Parameters<WASocket['sendMessage']>[2];
67
+ type SendContent = Parameters<WASocket['sendMessage']>[1];
68
+
69
+ function resetGate(st: State): void {
70
+ st.openPromise = new Promise<void>((resolve) => {
71
+ st.openResolve = resolve;
72
+ });
73
+ }
74
+
75
+ function bindInbound(st: State, sock: WASocket): void {
76
+ sock.ev.on('messages.upsert', ({ messages, type }) => {
77
+ for (const m of messages) st.keys.remember(m.key);
78
+ if (type !== 'notify' || !st.handlers) return;
79
+ for (const m of messages) {
80
+ if (m.key.fromMe) continue;
81
+ const inbound = toInbound(st.account.id, m);
82
+ if (inbound) st.handlers.onMessage(inbound, m);
83
+ }
84
+ });
85
+ sock.ev.on('messages.reaction', (events: ReactionEvent[]) => {
86
+ if (!st.handlers) return;
87
+ for (const event of events) {
88
+ const reaction = toReaction(st.account.id, event);
89
+ if (reaction) st.handlers.onReaction(reaction);
90
+ }
91
+ });
92
+ }
93
+
94
+ function bindDelivery(st: State, sock: WASocket): void {
95
+ sock.ev.on('messages.update', (updates) => {
96
+ for (const note of deliveryNotes(updates))
97
+ process.stderr.write(
98
+ `whatsapp[${st.account.id}] send ${note.messageId} to ${note.jid}: ${describeNote(note)}\n`,
99
+ );
100
+ });
101
+ bindAcks(sock, st.acks, (ack) => {
102
+ process.stderr.write(
103
+ `whatsapp[${st.account.id}] send ${ack.messageId} to ${ack.jid} REFUSED by WhatsApp: ack error ${ack.error ?? '?'}\n`,
104
+ );
105
+ });
106
+ }
107
+
108
+ function servedFromOutbox(
109
+ st: State,
110
+ key: WAMessageKey,
111
+ ): proto.IMessage | undefined {
112
+ const message = st.outbox.lookup(key);
113
+ if (key.fromMe === true)
114
+ process.stderr.write(
115
+ message
116
+ ? `whatsapp[${st.account.id}] asked to send ${key.id} to ${key.remoteJid} again — served from the outbox\n`
117
+ : `whatsapp[${st.account.id}] asked to send ${key.id} to ${key.remoteJid} again — NOT in the outbox, so that message can never arrive\n`,
118
+ );
119
+ return message;
120
+ }
121
+
122
+ const TERMINAL: Record<number, string> = {
123
+ [DisconnectReason.loggedOut]: ' — re-pair required',
124
+ [DisconnectReason.connectionReplaced]:
125
+ ' — another metro took this device; not reconnecting (two sockets on one ' +
126
+ 'identity evict each other and risk a 463 timelock)',
127
+ };
128
+
129
+ export function terminalReason(code: number | undefined): string | undefined {
130
+ return code === undefined ? undefined : TERMINAL[code];
131
+ }
132
+
133
+ function onClose(st: State, code: number | undefined): void {
134
+ const terminal = terminalReason(code);
135
+ if (st.closed || terminal !== undefined) {
136
+ process.stderr.write(
137
+ `whatsapp[${st.account.id}] disconnected (code=${code ?? '?'})${terminal ?? ''}\n`,
138
+ );
139
+ return;
140
+ }
141
+ resetGate(st);
142
+ process.stderr.write(`whatsapp[${st.account.id}] reconnecting\n`);
143
+ void connect(st).catch((e: unknown) => {
144
+ process.stderr.write(
145
+ `whatsapp[${st.account.id}] reconnect failed: ${errMsg(e)}\n`,
146
+ );
147
+ });
148
+ }
149
+
150
+ function bindConnection(st: State, sock: WASocket): void {
151
+ sock.ev.on('connection.update', (update) => {
152
+ const { connection, lastDisconnect } = update;
153
+ if (connection === 'open') {
154
+ process.stderr.write(`whatsapp[${st.account.id}] connected\n`);
155
+ st.openResolve?.();
156
+ return;
157
+ }
158
+ if (connection !== 'close') return;
159
+ const code = (
160
+ lastDisconnect?.error as { output?: { statusCode?: number } } | undefined
161
+ )?.output?.statusCode;
162
+ onClose(st, code);
163
+ });
164
+ }
165
+
166
+ async function connect(st: State): Promise<void> {
167
+ const { state, saveCreds } = useAccountAuthState(
168
+ st.account.credentials,
169
+ st.account.id,
170
+ );
171
+ const { version, error } = await fetchLatestWaWebVersion({});
172
+ if (error) {
173
+ throw new TrainError(
174
+ 'whatsapp_connect',
175
+ `failed to fetch WhatsApp web version: ${errMsg(error)}`,
176
+ );
177
+ }
178
+ const sock = makeWASocket({
179
+ version,
180
+ auth: state,
181
+ browser: Browsers.macOS('Safari'),
182
+ markOnlineOnConnect: false,
183
+ syncFullHistory: false,
184
+ logger: baileysLogger(st.account.id),
185
+ getMessage: (key) => Promise.resolve(servedFromOutbox(st, key)),
186
+ });
187
+ st.sock = sock;
188
+ sock.ev.on('creds.update', () => void saveCreds());
189
+ bindConnection(st, sock);
190
+ bindInbound(st, sock);
191
+ bindDelivery(st, sock);
192
+ }
193
+
194
+ async function ready(st: State): Promise<WASocket> {
195
+ await st.openPromise;
196
+ if (!st.sock) throw new TrainError('whatsapp_call', 'socket not connected');
197
+ return st.sock;
198
+ }
199
+
200
+ async function send(
201
+ st: State,
202
+ jid: string,
203
+ content: SendContent,
204
+ opts?: SendOpts,
205
+ ): Promise<string> {
206
+ const sock = await ready(st);
207
+ const sent = await sock.sendMessage(jid, content, opts);
208
+ st.keys.remember(sent?.key);
209
+ st.outbox.remember(sent?.key, sent?.message);
210
+ const messageId = sent?.key.id;
211
+ if (!messageId)
212
+ throw new TrainError(
213
+ 'whatsapp_call',
214
+ `WhatsApp accepted no message for ${jid}, so there is nothing that can have arrived`,
215
+ );
216
+ const ack = await st.acks.wait(messageId, ACK_WAIT_MS);
217
+ const refused = ack ? rejection(ack) : undefined;
218
+ if (refused) throw refused;
219
+ return messageId;
220
+ }
221
+
222
+ function quotedOpts(st: State, jid: string, quotedId: string): SendOpts {
223
+ return {
224
+ quoted: {
225
+ key: knownKey(st.keys, jid, quotedId, false),
226
+ message: { conversation: '' },
227
+ },
228
+ };
229
+ }
230
+
231
+ function mediaContent(m: WAMedia): SendContent {
232
+ const source = { url: m.path };
233
+ const caption = m.caption ? { caption: m.caption } : {};
234
+ if (m.kind === 'image') return { image: source, ...caption };
235
+ if (m.kind === 'video')
236
+ return { video: source, mimetype: m.mime, ...caption };
237
+ if (m.kind === 'audio')
238
+ return { audio: source, mimetype: m.mime, ptt: false };
239
+ return {
240
+ document: source,
241
+ mimetype: m.mime,
242
+ fileName: m.name,
243
+ ...caption,
244
+ };
245
+ }
246
+
247
+ export function createClient(account: WhatsAppAccount): WAClient {
248
+ const st: State = {
249
+ account,
250
+ closed: false,
251
+ openPromise: Promise.resolve(),
252
+ keys: makeKeyCache(),
253
+ outbox: makeOutbox(),
254
+ acks: makeAckWatch(),
255
+ };
256
+ resetGate(st);
257
+ return {
258
+ account,
259
+ self() {
260
+ const jid = st.sock?.user?.id;
261
+ if (jid === undefined) return null;
262
+ return jid.split(':')[0]?.split('@')[0] ?? null;
263
+ },
264
+ async start(handlers) {
265
+ st.handlers = handlers;
266
+ try {
267
+ await connect(st);
268
+ } catch (e) {
269
+ process.stderr.write(
270
+ `whatsapp[${account.id}] connect failed: ${errMsg(e)}\n`,
271
+ );
272
+ }
273
+ },
274
+ sendText(jid, text, quotedId) {
275
+ return send(
276
+ st,
277
+ jid,
278
+ { text },
279
+ quotedId ? quotedOpts(st, jid, quotedId) : undefined,
280
+ );
281
+ },
282
+ sendMedia(jid, media, quotedId) {
283
+ return send(
284
+ st,
285
+ jid,
286
+ mediaContent(media),
287
+ quotedId ? quotedOpts(st, jid, quotedId) : undefined,
288
+ );
289
+ },
290
+ async sendReaction(jid, messageId, emoji) {
291
+ const target = targetKey(st.keys, jid, messageId, 'react to');
292
+ await send(st, jid, { react: { text: emoji, key: target } });
293
+ },
294
+ async editMessage(jid, messageId, text) {
295
+ await send(st, jid, {
296
+ text,
297
+ edit: knownKey(st.keys, jid, messageId, true),
298
+ });
299
+ },
300
+ async deleteMessage(jid, messageId) {
301
+ await send(st, jid, {
302
+ delete: knownKey(st.keys, jid, messageId, true),
303
+ });
304
+ },
305
+ async reuploadMedia(m) {
306
+ const sock = await ready(st);
307
+ return sock.updateMediaMessage(m);
308
+ },
309
+ async disconnect() {
310
+ st.closed = true;
311
+ try {
312
+ await st.sock?.end(undefined);
313
+ } catch {
314
+ st.sock = undefined;
315
+ }
316
+ },
317
+ };
318
+ }