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

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 +118 -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,38 @@
1
+ import type { WAMessageKey, proto } from 'baileys';
2
+
3
+ const MAX_TRACKED_SENDS = 256;
4
+
5
+ export interface Outbox {
6
+ remember(
7
+ key: WAMessageKey | null | undefined,
8
+ message: proto.IMessage | null | undefined,
9
+ ): void;
10
+ lookup(key: WAMessageKey): proto.IMessage | undefined;
11
+ }
12
+
13
+ const slotOf = (jid: string, id: string): string => `${jid}|${id}`;
14
+
15
+ export function makeOutbox(max: number = MAX_TRACKED_SENDS): Outbox {
16
+ const sent = new Map<string, proto.IMessage>();
17
+ return {
18
+ remember(key, message) {
19
+ const id = key?.id;
20
+ const jid = key?.remoteJid;
21
+ if (!id || !jid || !message || key?.fromMe !== true) return;
22
+ const slot = slotOf(jid, id);
23
+ sent.delete(slot);
24
+ sent.set(slot, message);
25
+ while (sent.size > max) {
26
+ const oldest = sent.keys().next().value;
27
+ if (oldest === undefined) break;
28
+ sent.delete(oldest);
29
+ }
30
+ },
31
+ lookup(key) {
32
+ const id = key.id;
33
+ const jid = key.remoteJid;
34
+ if (!id || !jid) return undefined;
35
+ return sent.get(slotOf(jid, id));
36
+ },
37
+ };
38
+ }
@@ -0,0 +1,141 @@
1
+ import type { WAMessage, WAMessageKey, proto } from 'baileys';
2
+ import { mediaRefIn, mediaTag, type WAMediaRef } from './media.js';
3
+ import type { InboundMessage, ReactionInput } from './format.js';
4
+
5
+ export const isGroupJid = (jid: string): boolean => jid.endsWith('@g.us');
6
+ const isLidJid = (jid: string): boolean => jid.endsWith('@lid');
7
+ export const isPrivateJid = (jid: string): boolean =>
8
+ jid.endsWith('@s.whatsapp.net') || isLidJid(jid);
9
+
10
+ type Timestamp = number | { toNumber(): number } | null | undefined;
11
+
12
+ export function tsToDate(ts: Timestamp): Date {
13
+ if (typeof ts === 'number') return new Date(ts * 1000);
14
+ if (ts && typeof ts.toNumber === 'function')
15
+ return new Date(ts.toNumber() * 1000);
16
+ return new Date();
17
+ }
18
+
19
+ type Content = proto.IMessage | null | undefined;
20
+
21
+ const UNWRAPPERS: ((m: proto.IMessage) => Content)[] = [
22
+ (m) => m.ephemeralMessage?.message,
23
+ (m) => m.viewOnceMessage?.message,
24
+ (m) => m.viewOnceMessageV2?.message,
25
+ (m) => m.viewOnceMessageV2Extension?.message,
26
+ (m) => m.deviceSentMessage?.message,
27
+ (m) => m.documentWithCaptionMessage?.message,
28
+ (m) => m.editedMessage?.message,
29
+ ];
30
+
31
+ function unwrapOnce(m: proto.IMessage): Content {
32
+ for (const pick of UNWRAPPERS) {
33
+ const inner = pick(m);
34
+ if (inner) return inner;
35
+ }
36
+ return undefined;
37
+ }
38
+
39
+ export function unwrap(message: Content): proto.IMessage | undefined {
40
+ let current: Content = message;
41
+ for (let depth = 0; current && depth < 8; depth += 1) {
42
+ const inner = unwrapOnce(current);
43
+ if (!inner) return current;
44
+ current = inner;
45
+ }
46
+ return current ?? undefined;
47
+ }
48
+
49
+ function captionOf(message: proto.IMessage): string {
50
+ const caption =
51
+ message.imageMessage?.caption ??
52
+ message.videoMessage?.caption ??
53
+ message.documentMessage?.caption;
54
+ return typeof caption === 'string' ? caption : '';
55
+ }
56
+
57
+ export function extractText(message: Content): string {
58
+ const inner = unwrap(message);
59
+ if (!inner) return '';
60
+ if (typeof inner.conversation === 'string') return inner.conversation;
61
+ const ext = inner.extendedTextMessage?.text;
62
+ if (typeof ext === 'string') return ext;
63
+ return captionOf(inner);
64
+ }
65
+
66
+ export function mediaRefOf(message: Content): WAMediaRef | undefined {
67
+ const inner = unwrap(message);
68
+ return inner ? mediaRefIn(inner) : undefined;
69
+ }
70
+
71
+ function projectText(
72
+ message: proto.IMessage | undefined,
73
+ ref: WAMediaRef | undefined,
74
+ ): string {
75
+ return [extractText(message), mediaTag(ref)].filter(Boolean).join(' ');
76
+ }
77
+
78
+ function senderJidOf(key: WAMessageKey, chatJid: string): string {
79
+ if (isGroupJid(chatJid)) return key.participant ?? chatJid;
80
+ return chatJid;
81
+ }
82
+
83
+ export function toInbound(
84
+ accountId: string,
85
+ m: WAMessage,
86
+ ): InboundMessage | undefined {
87
+ const chatJid = m.key.remoteJid;
88
+ const messageId = m.key.id;
89
+ if (!chatJid || !messageId) return undefined;
90
+ const inner = unwrap(m.message);
91
+ if (!inner || inner.reactionMessage) return undefined;
92
+ const media = mediaRefOf(inner);
93
+ const text = projectText(inner, media);
94
+ if (!text && !media) return undefined;
95
+ return {
96
+ accountId,
97
+ chatJid,
98
+ senderJid: senderJidOf(m.key, chatJid),
99
+ messageId,
100
+ text,
101
+ date: tsToDate(m.messageTimestamp),
102
+ isPrivate: isPrivateJid(chatJid),
103
+ ...(m.pushName ? { pushName: m.pushName } : {}),
104
+ ...(media ? { media } : {}),
105
+ };
106
+ }
107
+
108
+ export interface ReactionEvent {
109
+ key: WAMessageKey;
110
+ reaction: proto.IReaction;
111
+ }
112
+
113
+ function reactorJid(
114
+ reactorKey: WAMessageKey | undefined,
115
+ chatJid: string,
116
+ ): string {
117
+ return reactorKey?.participant ?? reactorKey?.remoteJid ?? chatJid;
118
+ }
119
+
120
+ export function toReaction(
121
+ accountId: string,
122
+ event: ReactionEvent,
123
+ ): ReactionInput | undefined {
124
+ const chatJid = event.key.remoteJid;
125
+ const messageId = event.key.id;
126
+ if (!chatJid || !messageId) return undefined;
127
+ const reactorKey = event.reaction.key ?? undefined;
128
+ if (reactorKey?.fromMe) return undefined;
129
+ const senderJid = reactorJid(reactorKey, chatJid);
130
+ const emoji = event.reaction.text ?? '';
131
+ return {
132
+ accountId,
133
+ chatJid,
134
+ senderJid,
135
+ messageId,
136
+ emoji,
137
+ date: new Date(),
138
+ isPrivate: isPrivateJid(chatJid),
139
+ removed: emoji === '',
140
+ };
141
+ }
@@ -0,0 +1,17 @@
1
+ import type { Station, Verb } from '@metro-labs/mcp/stations/types';
2
+
3
+ export const whatsappStation: Station = {
4
+ name: 'whatsapp',
5
+ hasAccounts: true,
6
+ hasTrain: true,
7
+ messageVerbs: new Set<Verb>([
8
+ 'send',
9
+ 'reply',
10
+ 'react',
11
+ 'unreact',
12
+ 'edit',
13
+ 'delete',
14
+ ]),
15
+ attachmentMode: 'canonical',
16
+ tools: [],
17
+ };
@@ -0,0 +1,98 @@
1
+ import { homedir } from 'node:os';
2
+ import { join } from 'node:path';
3
+ import { readJson, writeSecure } from '@metro-labs/mcp/secure-fs';
4
+ import { errMsg } from '@metro-labs/mcp/log';
5
+
6
+ export const PERSISTED_KEY_TYPES = ['tctoken', 'lid-mapping'] as const;
7
+
8
+ export type KeyTable = Record<string, Record<string, unknown>>;
9
+
10
+ const PERSISTED = new Set<string>(PERSISTED_KEY_TYPES);
11
+
12
+ const WRITE_DEBOUNCE_MS = 1000;
13
+
14
+ export const isPersistedKeyType = (type: string): boolean =>
15
+ PERSISTED.has(type);
16
+
17
+ const safeSegment = (s: string): string => s.replace(/[^A-Za-z0-9_-]/g, '_');
18
+
19
+ export function tokenStorePath(accountId: string): string {
20
+ const dir = process.env.WHATSAPP_TOKEN_DIR ?? join(homedir(), '.metro');
21
+ return join(dir, `whatsapp-tokens-${safeSegment(accountId)}.json`);
22
+ }
23
+
24
+ export function persistedSubset(table: KeyTable): KeyTable {
25
+ const out: KeyTable = {};
26
+ for (const type of PERSISTED_KEY_TYPES) {
27
+ const bucket = table[type];
28
+ if (!bucket) continue;
29
+ const kept: Record<string, unknown> = {};
30
+ for (const id of Object.keys(bucket)) {
31
+ const value = bucket[id];
32
+ if (value === undefined || value === null) continue;
33
+ kept[id] = value;
34
+ }
35
+ if (Object.keys(kept).length) out[type] = kept;
36
+ }
37
+ return out;
38
+ }
39
+
40
+ export interface TokenStore {
41
+ path: string;
42
+ load(): KeyTable;
43
+ scheduleSave(table: KeyTable): void;
44
+ saveNow(table: KeyTable): void;
45
+ }
46
+
47
+ export function makeTokenStore(
48
+ accountId: string,
49
+ onError: (message: string) => void,
50
+ ): TokenStore {
51
+ const path = tokenStorePath(accountId);
52
+ let timer: ReturnType<typeof setTimeout> | undefined;
53
+ let pending: KeyTable | undefined;
54
+ let written = '';
55
+ const saveNow = (table: KeyTable): void => {
56
+ const body = JSON.stringify(persistedSubset(table));
57
+ if (body === written) return;
58
+ try {
59
+ writeSecure(path, body);
60
+ written = body;
61
+ } catch (err) {
62
+ onError(`could not write ${path}: ${errMsg(err)}`);
63
+ }
64
+ };
65
+ return {
66
+ path,
67
+ load() {
68
+ const table = persistedSubset(readJson<KeyTable>(path, {}));
69
+ written = JSON.stringify(table);
70
+ return table;
71
+ },
72
+ scheduleSave(table) {
73
+ pending = table;
74
+ if (timer) return;
75
+ timer = setTimeout(() => {
76
+ timer = undefined;
77
+ if (pending) saveNow(pending);
78
+ pending = undefined;
79
+ }, WRITE_DEBOUNCE_MS);
80
+ timer.unref?.();
81
+ },
82
+ saveNow,
83
+ };
84
+ }
85
+
86
+ const stores = new Map<string, TokenStore>();
87
+
88
+ export function tokenStoreFor(
89
+ accountId: string,
90
+ onError: (message: string) => void,
91
+ ): TokenStore {
92
+ const path = tokenStorePath(accountId);
93
+ const existing = stores.get(path);
94
+ if (existing) return existing;
95
+ const store = makeTokenStore(accountId, onError);
96
+ stores.set(path, store);
97
+ return store;
98
+ }
@@ -0,0 +1,6 @@
1
+ export interface WhatsAppAccount {
2
+ id: string;
3
+ phone: string;
4
+ owner?: string;
5
+ credentials?: unknown;
6
+ }
@@ -0,0 +1,5 @@
1
+ export { emit, mintId } from '@metro-labs/mcp/stations/station-runtime';
2
+
3
+ export const SELF_URI =
4
+ process.env.METRO_SELF_URI ??
5
+ 'metro://claude/user/8a1857f3-4039-4da6-a4e1-611b432d2082';
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@metro-labs/xmtp",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "exports": {
7
+ ".": "./src/station.ts",
8
+ "./train": "./src/index.ts",
9
+ "./verify": "./src/verify.ts"
10
+ },
11
+ "scripts": {
12
+ "build": "tsc",
13
+ "test": "tsc --noEmit && DYLD_FALLBACK_LIBRARY_PATH=/usr/lib bun test test/"
14
+ },
15
+ "dependencies": {
16
+ "@metro-labs/mcp": "workspace:*",
17
+ "@xmtp/content-type-primitives": "^3.0.1",
18
+ "@xmtp/content-type-reaction": "^2.0.2",
19
+ "@xmtp/content-type-remote-attachment": "^2.0.4",
20
+ "@xmtp/content-type-reply": "^2.0.3",
21
+ "@xmtp/content-type-wallet-send-calls": "^2.0.0",
22
+ "@xmtp/node-bindings": "1.9.1",
23
+ "@xmtp/node-sdk": "^6.0.0",
24
+ "viem": "^2.52.2"
25
+ },
26
+ "devDependencies": {
27
+ "@stage-labs/config": "0.1.0-beta.2",
28
+ "eslint": "^10.3.0",
29
+ "knip": "^6.15.0",
30
+ "madge": "^8.0.0",
31
+ "typescript": "^5"
32
+ }
33
+ }
@@ -0,0 +1,113 @@
1
+ import {
2
+ Client,
3
+ type ClientOptions,
4
+ type Conversation,
5
+ } from '@xmtp/node-sdk';
6
+ import { homedir } from 'node:os';
7
+ import { existsSync } from 'node:fs';
8
+ import { join } from 'node:path';
9
+ import { CODECS } from './codecs.js';
10
+ import { expandHome, signerFor, XMTP_ENV } from './identity.js';
11
+ import {
12
+ makeAccountStore,
13
+ resolveAccountId,
14
+ } from '@metro-labs/mcp/stations/account-store';
15
+ import { Line } from '@metro-labs/mcp/lines';
16
+
17
+ const ACCOUNTS_FILE =
18
+ process.env.XMTP_ACCOUNTS_FILE ??
19
+ join(homedir(), '.metro', 'xmtp-accounts.json');
20
+
21
+ export interface AccountConfig {
22
+ id: string;
23
+ privateKey?: string;
24
+ owner?: string;
25
+ dbPath?: string;
26
+ }
27
+
28
+ export const { loadAccounts } = makeAccountStore<AccountConfig>({
29
+ prefix: 'xmtp',
30
+ file: ACCOUNTS_FILE,
31
+ allowlistEnv: ['XMTP_ONLY_ACCOUNTS', 'XMTP_ACCOUNTS'],
32
+ validate(raw, die) {
33
+ const seen = new Set<string>();
34
+ for (const a of raw) {
35
+ if (!a.id) die('account missing id');
36
+ if (!a.privateKey) die(`account '${a.id}' needs a privateKey`);
37
+ if (seen.has(a.id)) die(`duplicate account id '${a.id}'`);
38
+ seen.add(a.id);
39
+ }
40
+ },
41
+ });
42
+
43
+ function resolvePrivateKey(cfg: AccountConfig): string {
44
+ const privateKey = cfg.privateKey?.trim();
45
+ if (!privateKey) throw new Error(`account '${cfg.id}' has no privateKey`);
46
+ return privateKey;
47
+ }
48
+
49
+ export interface Account {
50
+ cfg: AccountConfig;
51
+ client: Client<unknown>;
52
+ inboxId: string;
53
+ address: string;
54
+ }
55
+ export const accounts = new Map<string, Account>();
56
+
57
+ export async function bootAccount(cfg: AccountConfig): Promise<void> {
58
+ const { signer, address } = signerFor(resolvePrivateKey(cfg));
59
+ const dbPath = expandHome(
60
+ cfg.dbPath ?? join(homedir(), '.metro', `xmtp-${XMTP_ENV}-${cfg.id}.db3`),
61
+ );
62
+ const fresh = !existsSync(dbPath);
63
+ const options: ClientOptions = { env: XMTP_ENV, codecs: CODECS(), dbPath };
64
+ const client: Client<unknown> = await Client.create(signer, options);
65
+ accounts.set(cfg.id, { cfg, client, inboxId: client.inboxId, address });
66
+ if (fresh)
67
+ process.stderr.write(
68
+ `xmtp[${cfg.id}] registered a NEW installation for this machine — ` +
69
+ 'an inbox allows 10, and each machine you run metro on spends one\n',
70
+ );
71
+ process.stderr.write(
72
+ `xmtp[${cfg.id}] ready — inbox ${client.inboxId} (${address}, owner=${cfg.owner ?? '(broadcast)'})\n`,
73
+ );
74
+ }
75
+
76
+ export function lineOf(accountId: string, convId: string): string {
77
+ return `metro://xmtp/${accountId}/${convId}`;
78
+ }
79
+
80
+ export function parseLine(
81
+ line: string,
82
+ ): { accountId: string; convId: string } | null {
83
+ const p = Line.parseXmtp(line);
84
+ return p ? { accountId: p.accountId, convId: p.resource } : null;
85
+ }
86
+
87
+ export function accountForCall(args: {
88
+ account?: string;
89
+ line?: string;
90
+ }): Account {
91
+ const id = resolveAccountId(
92
+ accounts,
93
+ args,
94
+ (line) => parseLine(line)?.accountId,
95
+ );
96
+ const acct = accounts.get(id);
97
+ if (!acct) throw new Error(`unknown account '${id}'`);
98
+ return acct;
99
+ }
100
+
101
+ export async function convOf(
102
+ line: string,
103
+ ): Promise<{ acct: Account; conv: Conversation | undefined }> {
104
+ const parsed = parseLine(line);
105
+ if (!parsed) throw new Error(`bad xmtp line: ${line}`);
106
+ const acct = accounts.get(parsed.accountId);
107
+ if (!acct)
108
+ throw new Error(`unknown account '${parsed.accountId}' in line ${line}`);
109
+ return {
110
+ acct,
111
+ conv: await acct.client.conversations.getConversationById(parsed.convId),
112
+ };
113
+ }
@@ -0,0 +1,110 @@
1
+ import { errMsg } from '@metro-labs/mcp/log';
2
+ import { accountForCall, convOf, lineOf, type Account } from './accounts.js';
3
+ import { respond } from './wire.js';
4
+ import type { GroupLike } from './labels.js';
5
+
6
+ type Args = Record<string, unknown>;
7
+
8
+ async function removeSelfFromGroup(
9
+ id: string,
10
+ group: GroupLike & { removeMembers: (ids: string[]) => Promise<void> },
11
+ acct: Account,
12
+ resolvedLine: string,
13
+ removed: string[],
14
+ ): Promise<boolean> {
15
+ try {
16
+ await group.removeMembers([acct.inboxId]);
17
+ return true;
18
+ } catch (e) {
19
+ respond(id, {
20
+ result: {
21
+ line: resolvedLine,
22
+ id: group.id,
23
+ account: acct.cfg.id,
24
+ removed,
25
+ leftSelf: false,
26
+ selfRemovalError: `self-removal not supported: ${errMsg(e)}`,
27
+ },
28
+ });
29
+ return false;
30
+ }
31
+ }
32
+
33
+ type ClosableGroup = GroupLike & {
34
+ removeMembers: (ids: string[]) => Promise<void>;
35
+ };
36
+
37
+ async function resolveCloseTarget(
38
+ id: string,
39
+ args: Args,
40
+ acct: Account,
41
+ ): Promise<{ group: ClosableGroup; resolvedLine: string } | null> {
42
+ const { line, groupId } = args as { line?: string; groupId?: string };
43
+ const resolvedLine =
44
+ line ?? (groupId ? lineOf(acct.cfg.id, groupId) : undefined);
45
+ if (!resolvedLine) {
46
+ respond(id, { error: 'closeGroup requires `line` or `groupId`' });
47
+ return null;
48
+ }
49
+ const { conv } = await convOf(resolvedLine);
50
+ if (!conv) {
51
+ respond(id, { error: `conversation not found for ${resolvedLine}` });
52
+ return null;
53
+ }
54
+ const group = conv as unknown as ClosableGroup;
55
+ if (typeof group.removeMembers !== 'function') {
56
+ respond(id, {
57
+ error: 'closeGroup target is not a group (no removeMembers)',
58
+ });
59
+ return null;
60
+ }
61
+ await group.sync?.().catch(() => undefined);
62
+ return { group, resolvedLine };
63
+ }
64
+
65
+ export async function closeGroup(id: string, args: Args): Promise<void> {
66
+ const { removeInboxIds, removeSelf } = args as {
67
+ removeInboxIds?: string[];
68
+ removeSelf?: boolean;
69
+ };
70
+ const acct = accountForCall(args);
71
+ const target = await resolveCloseTarget(id, args, acct);
72
+ if (!target) return;
73
+ const { group, resolvedLine } = target;
74
+ const removeMembers = group.removeMembers.bind(group);
75
+
76
+ const others = (removeInboxIds ?? []).filter(
77
+ (iid) => typeof iid === 'string' && iid && iid !== acct.inboxId,
78
+ );
79
+ const removed: string[] = [];
80
+ let leftSelf = false;
81
+ try {
82
+ if (others.length) {
83
+ await removeMembers(others);
84
+ removed.push(...others);
85
+ }
86
+ if (removeSelf) {
87
+ leftSelf = await removeSelfFromGroup(
88
+ id,
89
+ group,
90
+ acct,
91
+ resolvedLine,
92
+ removed,
93
+ );
94
+ if (!leftSelf) return;
95
+ }
96
+ } catch (err) {
97
+ respond(id, { error: errMsg(err) });
98
+ return;
99
+ }
100
+
101
+ respond(id, {
102
+ result: {
103
+ line: resolvedLine,
104
+ id: group.id,
105
+ account: acct.cfg.id,
106
+ removed,
107
+ leftSelf,
108
+ },
109
+ });
110
+ }