@stage-labs/metro 0.1.0-beta.3 → 0.1.0-beta.30

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 (236) 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 +104 -0
  6. package/dist/claude.js +15 -0
  7. package/dist/cli.js +157 -253
  8. package/dist/control.js +158 -0
  9. package/dist/plugin.js +82 -0
  10. package/dist/prompt.js +61 -0
  11. package/dist/runtime.js +94 -0
  12. package/dist/store.js +70 -0
  13. package/dist/tail.js +120 -0
  14. package/dist/update.js +33 -0
  15. package/dist/version.js +97 -0
  16. package/package.json +34 -21
  17. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  18. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +119 -0
  19. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  20. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  21. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  22. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +210 -0
  23. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  24. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  25. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  26. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  27. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  28. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  29. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  30. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  31. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  32. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  33. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  34. package/runtime/node_modules/@metro-labs/mcp/package.json +66 -0
  35. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
  36. package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +367 -0
  37. package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
  38. package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
  39. package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
  40. package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
  41. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
  42. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +337 -0
  43. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
  44. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +129 -0
  45. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
  46. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
  47. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +129 -0
  48. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +278 -0
  49. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +321 -0
  50. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +123 -0
  51. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +20 -0
  52. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +202 -0
  53. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +339 -0
  54. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
  55. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
  56. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
  57. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +100 -0
  58. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +57 -0
  59. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +333 -0
  60. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
  61. package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
  62. package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +117 -0
  63. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +164 -0
  64. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +208 -0
  65. package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +359 -0
  66. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +79 -0
  67. package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
  68. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +162 -0
  69. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +120 -0
  70. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
  71. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +58 -0
  72. package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +134 -0
  73. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +214 -0
  74. package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +224 -0
  75. package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +302 -0
  76. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +130 -0
  77. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +18 -0
  78. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +105 -0
  79. package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
  80. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +27 -0
  81. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +43 -0
  82. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +218 -0
  83. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
  84. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +323 -0
  85. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
  86. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
  87. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +169 -0
  88. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +295 -0
  89. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
  90. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +91 -0
  91. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +215 -0
  92. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +71 -0
  93. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
  94. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +37 -0
  95. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +218 -0
  96. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +147 -0
  97. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +126 -0
  98. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +308 -0
  99. package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +19 -0
  100. package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
  101. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +382 -0
  102. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +300 -0
  103. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +31 -0
  104. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +105 -0
  105. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +131 -0
  106. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +45 -0
  107. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +57 -0
  108. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +18 -0
  109. package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +145 -0
  110. package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +203 -0
  111. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
  112. package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
  113. package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
  114. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
  115. package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
  116. package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +192 -0
  117. package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
  118. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
  119. package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
  120. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +73 -0
  121. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
  122. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
  123. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +211 -0
  124. package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
  125. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +212 -0
  126. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +397 -0
  127. package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
  128. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +240 -0
  129. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
  130. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +80 -0
  131. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
  132. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
  133. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
  134. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
  135. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +182 -0
  136. package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
  137. package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
  138. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
  139. package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
  140. package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
  141. package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -0
  142. package/runtime/node_modules/@metro-labs/telegram/package.json +28 -0
  143. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +81 -0
  144. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  145. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  146. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  147. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +141 -0
  148. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  149. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  150. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  151. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  152. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  153. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  154. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  155. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  156. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  157. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  158. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  159. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  160. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  161. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  162. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  163. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  164. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
  165. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  166. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  167. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  168. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  169. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
  170. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  171. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  172. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  173. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +40 -0
  174. package/runtime/node_modules/@metro-labs/whatsapp/package.json +29 -0
  175. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +67 -0
  176. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  177. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  178. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  179. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  180. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +318 -0
  181. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  182. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +132 -0
  183. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  184. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  185. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  186. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  187. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  188. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  189. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  190. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  191. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +141 -0
  192. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  193. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  194. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  195. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  196. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  197. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +113 -0
  198. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  199. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  200. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  201. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  202. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  203. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  204. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  205. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  206. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  207. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +333 -0
  208. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +78 -0
  209. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  210. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  211. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  212. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  213. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  214. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  215. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  216. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  217. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  218. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  219. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  220. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  221. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +39 -0
  222. package/runtime/runtime.json +3 -0
  223. package/runtime/trains/.keep +0 -0
  224. package/LICENSE +0 -21
  225. package/README.md +0 -114
  226. package/dist/agents/claude.js +0 -229
  227. package/dist/agents/codex.js +0 -282
  228. package/dist/agents/types.js +0 -3
  229. package/dist/channels/discord.js +0 -142
  230. package/dist/channels/telegram.js +0 -209
  231. package/dist/lib/dotenv.js +0 -31
  232. package/dist/lib/scope-cache.js +0 -85
  233. package/dist/lib/streaming.js +0 -207
  234. package/dist/log.js +0 -12
  235. package/dist/orchestrator.js +0 -399
  236. package/dist/paths.js +0 -33
@@ -0,0 +1,299 @@
1
+ import { IdentifierKind } from '@xmtp/node-sdk';
2
+ import {
3
+ accounts,
4
+ accountForCall,
5
+ convOf,
6
+ lineOf,
7
+ parseLine,
8
+ type Account,
9
+ } from './accounts.js';
10
+ import { respond } from './wire.js';
11
+ import { TrainError } from '@metro-labs/mcp/train-error';
12
+ import { pushHandlers } from './actions-push.js';
13
+ import { cleanLabels, labelsBlob } from './labels.js';
14
+ import {
15
+ applyMemberOp,
16
+ buildGroupInfo,
17
+ buildMemberList,
18
+ createGroupWithMembers,
19
+ ethIdentifiers,
20
+ parseMemberArgs,
21
+ resolveMembers,
22
+ warmGroupName,
23
+ } from './conv-helpers.js';
24
+ import { closeGroup } from './actions-close.js';
25
+ import {
26
+ groupAddMembers,
27
+ groupCreate,
28
+ groupRemoveMembers,
29
+ } from './group.js';
30
+ import {
31
+ updateChannelMeta,
32
+ applyChannelMeta,
33
+ resolveLine,
34
+ setGithub,
35
+ setPreview,
36
+ } from './actions-meta.js';
37
+
38
+ type Args = Record<string, unknown>;
39
+ type Handler = (id: string, args: Args) => Promise<void>;
40
+ type Conv = Awaited<ReturnType<typeof convOf>>['conv'] & object;
41
+
42
+ async function newDm(id: string, args: Args): Promise<void> {
43
+ const { address } = args as { address: string };
44
+ const acct = accountForCall(args);
45
+ const dm = await acct.client.conversations.createDmWithIdentifier({
46
+ identifier: address,
47
+ identifierKind: IdentifierKind.Ethereum,
48
+ });
49
+ respond(id, {
50
+ result: {
51
+ line: lineOf(acct.cfg.id, dm.id),
52
+ id: dm.id,
53
+ account: acct.cfg.id,
54
+ },
55
+ });
56
+ }
57
+
58
+ async function newGroup(id: string, args: Args): Promise<void> {
59
+ const { addresses, name, permissions } = args as {
60
+ addresses: string[];
61
+ name?: string;
62
+ permissions?: 'admin-only' | 'default';
63
+ };
64
+ const acct = accountForCall(args);
65
+ const opts: { groupName?: string; permissions?: number } = {};
66
+ if (name) opts.groupName = name;
67
+ if (permissions === 'admin-only') opts.permissions = 1;
68
+ const group = await acct.client.conversations.createGroupWithIdentifiers(
69
+ ethIdentifiers(addresses),
70
+ opts,
71
+ );
72
+ warmGroupName(group.id, name);
73
+ respond(id, {
74
+ result: {
75
+ line: lineOf(acct.cfg.id, group.id),
76
+ id: group.id,
77
+ account: acct.cfg.id,
78
+ },
79
+ });
80
+ }
81
+
82
+ async function createRequestGroup(id: string, args: Args): Promise<void> {
83
+ const { name, description, labels } = args as {
84
+ name: string;
85
+ description?: string;
86
+ labels?: string[];
87
+ };
88
+ const acct = accountForCall(args);
89
+ if (!name || typeof name !== 'string')
90
+ throw new TrainError('INVALID_ARGS', 'createRequestGroup requires a `name`');
91
+ const { addrs, inboxes } = resolveMembers(args);
92
+ if (addrs.length === 0 && inboxes.length === 0)
93
+ throw new TrainError(
94
+ 'INVALID_ARGS',
95
+ 'createRequestGroup requires addresses[] or inboxIds[]',
96
+ );
97
+ const opts: {
98
+ groupName: string;
99
+ groupDescription?: string;
100
+ appData?: string;
101
+ } = { groupName: name };
102
+ if (description) opts.groupDescription = description;
103
+ if (Array.isArray(labels) && labels.length)
104
+ opts.appData = labelsBlob(undefined, labels);
105
+
106
+ const group = await createGroupWithMembers(acct, addrs, inboxes, opts);
107
+ warmGroupName(group.id, name);
108
+ respond(id, {
109
+ result: {
110
+ line: lineOf(acct.cfg.id, group.id),
111
+ id: group.id,
112
+ account: acct.cfg.id,
113
+ name,
114
+ description: description ?? '',
115
+ labels: cleanLabels(labels ?? []),
116
+ },
117
+ });
118
+ }
119
+
120
+ async function setLabels(id: string, args: Args): Promise<void> {
121
+ const { labels, setName, setDescription, setGithub } = args as {
122
+ line?: string;
123
+ groupId?: string;
124
+ labels: string[];
125
+ setName?: string;
126
+ setDescription?: string;
127
+ setGithub?: string;
128
+ };
129
+ const resolvedLine = resolveLine(args, 'setLabels');
130
+ if (!Array.isArray(labels))
131
+ throw new TrainError('INVALID_ARGS', 'setLabels requires a `labels` array');
132
+ const appData: Record<string, unknown> = { labels };
133
+ if (typeof setGithub === 'string') appData.github = setGithub;
134
+ const result = await applyChannelMeta(
135
+ { line: resolvedLine, name: setName, description: setDescription, appData },
136
+ 'setLabels',
137
+ );
138
+ respond(id, {
139
+ result: {
140
+ line: result.line,
141
+ id: result.id,
142
+ account: result.account,
143
+ labels: result.labels,
144
+ },
145
+ });
146
+ }
147
+
148
+ async function query(id: string, args: Args): Promise<void> {
149
+ const { line, limit } = args as { line: string; limit?: number };
150
+ const { conv } = await convOf(line);
151
+ if (!conv)
152
+ throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
153
+ const lim = Math.min(Math.max(1, limit ?? 20), 200);
154
+ await conv.sync().catch(() => undefined);
155
+ const all = await conv.messages();
156
+ const slice = all.slice(-lim);
157
+ const parsed = parseLine(line);
158
+ if (!parsed)
159
+ throw new TrainError('NOT_FOUND', `could not parse line ${line}`);
160
+ const acctId = parsed.accountId;
161
+ const messages = slice.map((m) => {
162
+ let text = '';
163
+ try {
164
+ const cc: unknown = m.content;
165
+ text =
166
+ typeof cc === 'string' ? cc : `[${m.contentType?.typeId ?? 'unknown'}]`;
167
+ } catch {
168
+ text = `[${m.contentType?.typeId ?? 'unknown'}]`;
169
+ }
170
+ return {
171
+ id: m.id,
172
+ ts: new Date(Number(m.sentAtNs / 1_000_000n)).toISOString(),
173
+ from: `metro://xmtp/${acctId}/user/${m.senderInboxId}`,
174
+ text,
175
+ contentType: m.contentType?.typeId ?? 'unknown',
176
+ };
177
+ });
178
+ respond(id, { result: { line, count: messages.length, messages } });
179
+ }
180
+
181
+ async function addMembers(id: string, args: Args): Promise<void> {
182
+ const { line, addrs, inboxes } = parseMemberArgs(args, 'addMembers');
183
+ const { acct, conv } = await convOf(line);
184
+ if (!conv)
185
+ throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
186
+ await applyMemberOp(conv, addrs, inboxes, 'add');
187
+ const info = await buildGroupInfo(line, acct, conv);
188
+ respond(id, {
189
+ result: { ...info, added: { addresses: addrs, inboxIds: inboxes } },
190
+ });
191
+ }
192
+
193
+ async function removeMembers(id: string, args: Args): Promise<void> {
194
+ const { line, addrs, inboxes } = parseMemberArgs(args, 'removeMembers');
195
+ const { acct, conv } = await convOf(line);
196
+ if (!conv)
197
+ throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
198
+ await applyMemberOp(conv, addrs, inboxes, 'remove');
199
+ const info = await buildGroupInfo(line, acct, conv);
200
+ respond(id, {
201
+ result: { ...info, removed: { addresses: addrs, inboxIds: inboxes } },
202
+ });
203
+ }
204
+
205
+ async function groupInfo(id: string, args: Args): Promise<void> {
206
+ const { line } = args as { line: string };
207
+ const { acct, conv } = await convOf(line);
208
+ if (!conv)
209
+ throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
210
+ respond(id, { result: await buildGroupInfo(line, acct, conv) });
211
+ }
212
+
213
+ async function listMembers(id: string, args: Args): Promise<void> {
214
+ const { line } = args as { line: string };
215
+ const { acct, conv } = await convOf(line);
216
+ if (!conv)
217
+ throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
218
+ respond(id, { result: await buildMemberList(acct, conv) });
219
+ }
220
+
221
+ async function summarizeConv(acct: Account, c: Conv): Promise<unknown> {
222
+ const recent = await c.messages({ limit: 1, direction: 1 }).catch(() => []);
223
+ const last = recent[0];
224
+ let preview = '';
225
+ if (last) {
226
+ const cc: unknown = last.content;
227
+ preview =
228
+ typeof cc === 'string'
229
+ ? cc.slice(0, 80)
230
+ : `[${last.contentType?.typeId ?? 'unknown'}]`;
231
+ }
232
+ const isDm =
233
+ typeof (c as unknown as { peerInboxId?: unknown }).peerInboxId ===
234
+ 'function';
235
+ const gn = (c as unknown as { name?: string | (() => Promise<string>) }).name;
236
+ const resolvedName =
237
+ typeof gn === 'function' ? await gn().catch(() => '') : (gn ?? '');
238
+ return {
239
+ line: lineOf(acct.cfg.id, c.id),
240
+ id: c.id,
241
+ account: acct.cfg.id,
242
+ version: isDm ? 'dm' : 'group',
243
+ name: resolvedName ?? '',
244
+ lastTs: last
245
+ ? new Date(Number(last.sentAtNs / 1_000_000n)).toISOString()
246
+ : null,
247
+ lastPreview: preview,
248
+ };
249
+ }
250
+
251
+ async function listConvs(id: string, args: Args): Promise<void> {
252
+ const { limit, account } = args as { limit?: number; account?: string };
253
+ const lim = Math.min(Math.max(1, limit ?? 50), 200);
254
+ const targets = account
255
+ ? [accounts.get(account)].filter((a): a is Account => a !== undefined)
256
+ : [...accounts.values()];
257
+ const summaries: unknown[] = [];
258
+ for (const acct of targets) {
259
+ await acct.client.conversations.syncAll();
260
+ const all = await acct.client.conversations.list();
261
+ for (const c of all.slice(0, lim))
262
+ summaries.push(await summarizeConv(acct, c));
263
+ }
264
+ summaries.sort((a, b) =>
265
+ ((b as { lastTs?: string }).lastTs ?? '').localeCompare(
266
+ (a as { lastTs?: string }).lastTs ?? '',
267
+ ),
268
+ );
269
+ respond(id, {
270
+ result: { count: summaries.length, conversations: summaries.slice(0, lim) },
271
+ });
272
+ }
273
+
274
+ export const convHandlers: Record<string, Handler> = {
275
+ newDm,
276
+ newGroup,
277
+ createRequestGroup,
278
+ addMembers,
279
+ removeMembers,
280
+ setLabels,
281
+ setGithub,
282
+ setPreview,
283
+ updateChannelMeta,
284
+ closeGroup,
285
+ query,
286
+ groupInfo,
287
+ listMembers,
288
+ listConvs,
289
+ groupCreate: async (id, args) => {
290
+ respond(id, { result: await groupCreate(args) });
291
+ },
292
+ groupAddMembers: async (id, args) => {
293
+ respond(id, { result: await groupAddMembers(args) });
294
+ },
295
+ groupRemoveMembers: async (id, args) => {
296
+ respond(id, { result: await groupRemoveMembers(args) });
297
+ },
298
+ ...pushHandlers,
299
+ };
@@ -0,0 +1,157 @@
1
+ import { accountForCall, convOf, lineOf } from './accounts.js';
2
+ import { respond } from './wire.js';
3
+ import { warmGroupName } from './conv-helpers.js';
4
+ import { mergeAppData, readAppData, type GroupLike } from './labels.js';
5
+ import { TrainError } from '@metro-labs/mcp/train-error';
6
+
7
+ type Args = Record<string, unknown>;
8
+
9
+ export function resolveLine(args: Args, verb: string): string {
10
+ const line = (args as { line?: string }).line;
11
+ if (line) return line;
12
+ const groupId = (args as { groupId?: string }).groupId;
13
+ if (groupId) {
14
+ const acct = accountForCall(args);
15
+ return lineOf(acct.cfg.id, groupId);
16
+ }
17
+ throw new TrainError(
18
+ 'INVALID_ARGS',
19
+ `${verb} requires \`line\` or \`groupId\``,
20
+ );
21
+ }
22
+
23
+ async function applyNameAndDescription(
24
+ group: GroupLike,
25
+ name: string | undefined,
26
+ description: string | undefined,
27
+ ): Promise<void> {
28
+ if (
29
+ typeof name === 'string' &&
30
+ name &&
31
+ typeof group.updateName === 'function'
32
+ ) {
33
+ await group.updateName(name);
34
+ warmGroupName(group.id, name);
35
+ }
36
+ if (
37
+ typeof description === 'string' &&
38
+ typeof group.updateDescription === 'function'
39
+ ) {
40
+ await group.updateDescription(description);
41
+ }
42
+ }
43
+
44
+ async function applyMergedAppData(
45
+ group: GroupLike & { updateAppData: (s: string) => Promise<void> },
46
+ appData: Record<string, unknown> | undefined,
47
+ ): Promise<Record<string, unknown>> {
48
+ if (appData && typeof appData === 'object' && !Array.isArray(appData)) {
49
+ const res = mergeAppData(group.appData, appData);
50
+ await group.updateAppData(res.blob);
51
+ return res.merged;
52
+ }
53
+ return readAppData(group.appData);
54
+ }
55
+
56
+ export async function applyChannelMeta(
57
+ args: {
58
+ line: string;
59
+ name?: string;
60
+ description?: string;
61
+ appData?: Record<string, unknown>;
62
+ },
63
+ verb: string,
64
+ ): Promise<Record<string, unknown>> {
65
+ const { line, name, description, appData } = args;
66
+ const { acct, conv } = await convOf(line);
67
+ if (!conv)
68
+ throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
69
+ const group = conv as unknown as GroupLike;
70
+ if (typeof group.updateAppData !== 'function') {
71
+ throw new TrainError(
72
+ 'INVALID_ARGS',
73
+ `${verb} target is not a group (no updateAppData)`,
74
+ );
75
+ }
76
+ await group.sync?.().catch(() => undefined);
77
+
78
+ await applyNameAndDescription(group, name, description);
79
+ const merged = await applyMergedAppData(
80
+ group as GroupLike & { updateAppData: (s: string) => Promise<void> },
81
+ appData,
82
+ );
83
+
84
+ return {
85
+ line,
86
+ id: group.id,
87
+ account: acct.cfg.id,
88
+ ...(typeof name === 'string' && name ? { name } : {}),
89
+ ...metaFields(merged),
90
+ appData: merged,
91
+ };
92
+ }
93
+
94
+ function metaFields(merged: Record<string, unknown>): {
95
+ labels: string[];
96
+ github: string | undefined;
97
+ preview: string | undefined;
98
+ } {
99
+ return {
100
+ labels: Array.isArray(merged.labels) ? (merged.labels as string[]) : [],
101
+ github: typeof merged.github === 'string' ? merged.github : undefined,
102
+ preview: typeof merged.preview === 'string' ? merged.preview : undefined,
103
+ };
104
+ }
105
+
106
+ export async function updateChannelMeta(id: string, args: Args): Promise<void> {
107
+ const line = resolveLine(args, 'updateChannelMeta');
108
+ const { name, description, appData } = args as {
109
+ name?: string;
110
+ description?: string;
111
+ appData?: Record<string, unknown>;
112
+ };
113
+ const result = await applyChannelMeta(
114
+ { line, name, description, appData },
115
+ 'updateChannelMeta',
116
+ );
117
+ respond(id, { result });
118
+ }
119
+
120
+ export async function setGithub(id: string, args: Args): Promise<void> {
121
+ const line = resolveLine(args, 'setGithub');
122
+ const { url } = args as { url: string };
123
+ if (typeof url !== 'string')
124
+ throw new Error('setGithub requires a `url` string');
125
+ const result = await applyChannelMeta(
126
+ { line, appData: { github: url } },
127
+ 'setGithub',
128
+ );
129
+ respond(id, {
130
+ result: {
131
+ line: result.line,
132
+ id: result.id,
133
+ account: result.account,
134
+ github: result.github,
135
+ },
136
+ });
137
+ }
138
+
139
+ export async function setPreview(id: string, args: Args): Promise<void> {
140
+ const line = resolveLine(args, 'setPreview');
141
+ const a = args as { preview?: unknown; url?: unknown };
142
+ const value = typeof a.preview === 'string' ? a.preview : a.url;
143
+ if (typeof value !== 'string')
144
+ throw new Error('setPreview requires a `preview` string');
145
+ const result = await applyChannelMeta(
146
+ { line, appData: { preview: value } },
147
+ 'setPreview',
148
+ );
149
+ respond(id, {
150
+ result: {
151
+ line: result.line,
152
+ id: result.id,
153
+ account: result.account,
154
+ preview: result.preview,
155
+ },
156
+ });
157
+ }
@@ -0,0 +1,87 @@
1
+ import { accounts } from './accounts.js';
2
+ import { respond } from './wire.js';
3
+ import {
4
+ fcmPushToAll,
5
+ loadPushTokens,
6
+ removePushToken,
7
+ savePushTokens,
8
+ storePushToken,
9
+ } from './push.js';
10
+
11
+ type Args = Record<string, unknown>;
12
+ type Handler = (id: string, args: Args) => Promise<void>;
13
+
14
+ function registerPush(id: string, args: Args): Promise<void> {
15
+ const { token, account, platform, inboxId } = args as {
16
+ token?: string;
17
+ account?: string;
18
+ platform?: string;
19
+ inboxId?: string;
20
+ };
21
+ if (!token || typeof token !== 'string' || token.length < 20) {
22
+ throw new Error('register-push requires a non-empty FCM device token');
23
+ }
24
+ const total = storePushToken({ token, account, platform, inboxId });
25
+ respond(id, { result: { stored: true, total, account: account ?? null } });
26
+ return Promise.resolve();
27
+ }
28
+
29
+ function listPush(id: string): Promise<void> {
30
+ const tokens = loadPushTokens();
31
+ respond(id, {
32
+ result: {
33
+ count: tokens.length,
34
+ tokens: tokens.map((t) => ({
35
+ token: `${t.token.slice(0, 12)}…${t.token.slice(-6)}`,
36
+ registeredAt: t.registeredAt,
37
+ lastSeenAt: t.lastSeenAt ?? null,
38
+ account: t.account ?? null,
39
+ platform: t.platform ?? null,
40
+ inboxId: t.inboxId ?? null,
41
+ })),
42
+ },
43
+ });
44
+ return Promise.resolve();
45
+ }
46
+
47
+ async function testPush(id: string, args: Args): Promise<void> {
48
+ const { account } = args as { account?: string };
49
+ const acctId =
50
+ account ??
51
+ (accounts.size === 1 ? [...accounts.keys()][0] : 'default') ??
52
+ 'default';
53
+ await fcmPushToAll(acctId, { channelId: 'xmtp', source: 'test-push' });
54
+ const sent = loadPushTokens().filter(
55
+ (t) => !t.account || t.account === acctId,
56
+ ).length;
57
+ respond(id, { result: { sent, account: acctId } });
58
+ }
59
+
60
+ function unregisterPush(id: string, args: Args): Promise<void> {
61
+ const { token } = args as { token: string };
62
+ savePushTokens(loadPushTokens().filter((t) => t.token !== token));
63
+ respond(id, { result: { removed: true } });
64
+ return Promise.resolve();
65
+ }
66
+
67
+ function disablePush(id: string, args: Args): Promise<void> {
68
+ const { token } = args as { token?: string };
69
+ if (!token || typeof token !== 'string')
70
+ throw new Error('disable-push requires a token');
71
+ const remaining = removePushToken(token);
72
+ respond(id, {
73
+ result: {
74
+ removed: remaining !== -1,
75
+ remaining: remaining === -1 ? null : remaining,
76
+ },
77
+ });
78
+ return Promise.resolve();
79
+ }
80
+
81
+ export const pushHandlers: Record<string, Handler> = {
82
+ 'register-push': registerPush,
83
+ 'list-push': (id) => listPush(id),
84
+ 'test-push': testPush,
85
+ 'unregister-push': unregisterPush,
86
+ 'disable-push': disablePush,
87
+ };