@stage-labs/metro 0.1.0-beta.23 → 0.1.0-beta.231

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 (457) hide show
  1. package/dist/api.js +9 -101
  2. package/dist/background.js +123 -0
  3. package/dist/claude-settings.js +40 -0
  4. package/dist/claude.js +197 -0
  5. package/dist/cli.js +52 -110
  6. package/dist/control.js +19 -103
  7. package/dist/hold.js +210 -0
  8. package/dist/local.js +43 -0
  9. package/dist/mcp-config.js +25 -0
  10. package/dist/memory.js +222 -0
  11. package/dist/node-name.js +71 -0
  12. package/dist/onboarding.js +44 -0
  13. package/dist/provider-flags.js +9 -0
  14. package/dist/route.js +58 -0
  15. package/dist/runtime-install.js +96 -0
  16. package/dist/runtime.js +29 -52
  17. package/dist/serve.js +157 -0
  18. package/dist/service.js +304 -0
  19. package/dist/tail.js +119 -0
  20. package/dist/update.js +13 -1
  21. package/package.json +2 -23
  22. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  23. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  24. package/runtime/marketplace/plugin/.mcp.json +1 -0
  25. package/runtime/marketplace/plugin/MEMORY.md +78 -0
  26. package/runtime/marketplace/plugin/METRO.md +26 -0
  27. package/runtime/marketplace/plugin/STAGE.md +42 -0
  28. package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
  29. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  30. package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
  31. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  32. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  33. package/runtime/node_modules/@metro-labs/core/package.json +51 -0
  34. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  35. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
  36. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  37. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  38. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +22 -26
  39. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  40. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  41. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +14 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
  44. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
  45. package/runtime/node_modules/@metro-labs/core/src/stations/mail-text.ts +72 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/mail-trust.ts +29 -0
  47. package/runtime/node_modules/@metro-labs/core/src/stations/mail.ts +212 -0
  48. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
  49. package/runtime/node_modules/@metro-labs/core/src/stations/oauth.ts +126 -0
  50. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  51. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  52. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
  53. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  54. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
  55. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
  56. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  57. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  58. package/runtime/node_modules/@metro-labs/daemon/package.json +49 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/crontab.ts +44 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +39 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +43 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +91 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
  82. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  83. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +133 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  85. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  86. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +223 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
  93. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
  94. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  95. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
  96. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  97. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +75 -45
  98. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
  99. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  100. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  101. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +380 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory-routine.ts +114 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +295 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +346 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/claude/usage-probe.ts +127 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  115. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
  116. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  119. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  120. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
  121. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
  122. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  123. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-api.ts +116 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-crypto.ts +44 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-store.ts +48 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  134. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  135. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  136. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  137. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
  138. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  139. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +214 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +209 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +155 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +93 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +307 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  155. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  156. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +235 -0
  157. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  158. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +203 -0
  159. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +285 -0
  160. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-lists.ts +57 -0
  161. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +161 -0
  162. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +72 -0
  163. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +104 -0
  164. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +118 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +35 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/gateway/tool-search.ts +126 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage-refresh.ts +102 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +348 -0
  172. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
  173. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  174. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
  175. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  177. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  178. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +34 -80
  179. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  180. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  181. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +88 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  187. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  188. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +71 -0
  190. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +47 -44
  191. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +93 -0
  192. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  193. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  194. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +64 -113
  195. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  196. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  198. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  199. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  200. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  201. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  202. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  203. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  204. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +63 -51
  205. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +209 -0
  206. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +69 -0
  207. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  208. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  209. package/runtime/node_modules/@metro-labs/daemon/src/server/cloudwatch.ts +178 -0
  210. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  211. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  212. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-browser.ts +102 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  217. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  218. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +5 -31
  219. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  220. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
  221. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
  222. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +69 -3
  223. package/runtime/node_modules/@metro-labs/{mcp/src/db → daemon/src/stations}/materialize.ts +60 -179
  224. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +20 -2
  225. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  226. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
  227. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
  228. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  229. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  230. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  231. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  232. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  233. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  234. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  235. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  236. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
  237. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  238. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  239. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  240. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
  241. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
  242. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
  243. package/runtime/node_modules/@metro-labs/discord-bot/package.json +2 -4
  244. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +3 -23
  245. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +19 -187
  246. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +2 -2
  247. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +26 -67
  248. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
  249. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +2 -2
  250. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +8 -14
  251. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +2 -2
  252. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  253. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +12 -13
  254. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +5 -1
  255. package/runtime/node_modules/@metro-labs/gmail/package.json +25 -0
  256. package/runtime/node_modules/@metro-labs/gmail/src/accounts.ts +81 -0
  257. package/runtime/node_modules/@metro-labs/gmail/src/actions.ts +14 -0
  258. package/runtime/node_modules/@metro-labs/gmail/src/api.ts +31 -0
  259. package/runtime/node_modules/@metro-labs/gmail/src/attachments.ts +35 -0
  260. package/runtime/node_modules/@metro-labs/gmail/src/auth.ts +105 -0
  261. package/runtime/node_modules/@metro-labs/gmail/src/config.ts +13 -0
  262. package/runtime/node_modules/@metro-labs/gmail/src/inbound.ts +96 -0
  263. package/runtime/node_modules/@metro-labs/gmail/src/index.ts +12 -0
  264. package/runtime/node_modules/@metro-labs/gmail/src/login.ts +37 -0
  265. package/runtime/node_modules/@metro-labs/gmail/src/message.ts +125 -0
  266. package/runtime/node_modules/@metro-labs/gmail/src/mime.ts +70 -0
  267. package/runtime/node_modules/@metro-labs/gmail/src/outbound.ts +124 -0
  268. package/runtime/node_modules/@metro-labs/gmail/src/query.ts +99 -0
  269. package/runtime/node_modules/@metro-labs/gmail/src/state.ts +24 -0
  270. package/runtime/node_modules/@metro-labs/gmail/src/station.ts +15 -0
  271. package/runtime/node_modules/@metro-labs/gmail/src/trust.ts +42 -0
  272. package/runtime/node_modules/@metro-labs/http/package.json +30 -0
  273. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
  274. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
  275. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  276. package/runtime/node_modules/@metro-labs/http/src/sigv4.ts +90 -0
  277. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  278. package/runtime/node_modules/@metro-labs/outlook/package.json +25 -0
  279. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +78 -0
  280. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
  281. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +81 -0
  282. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +120 -0
  283. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +42 -0
  284. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +23 -0
  285. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +59 -0
  286. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +55 -0
  287. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +96 -0
  288. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  289. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +129 -0
  290. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +31 -0
  291. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +103 -0
  292. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +84 -0
  293. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +28 -0
  294. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  295. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +41 -0
  296. package/runtime/node_modules/@metro-labs/telegram/package.json +1 -3
  297. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +5 -24
  298. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +8 -6
  299. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +1 -1
  300. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +2 -2
  301. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +19 -34
  302. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +3 -3
  303. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +13 -34
  304. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +4 -43
  305. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +2 -3
  306. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +1 -1
  307. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  308. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +2 -2
  309. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +4 -1
  310. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +0 -1
  311. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +1 -1
  312. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +7 -41
  313. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +22 -77
  314. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +2 -2
  315. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +46 -48
  316. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +15 -15
  317. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +6 -82
  318. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +1 -1
  319. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
  320. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +5 -1
  321. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +20 -12
  322. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  323. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +96 -0
  324. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
  325. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  326. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  327. package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
  328. package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
  329. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  330. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  331. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +87 -0
  332. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  333. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  334. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  335. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  336. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  337. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  338. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  339. package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
  340. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
  341. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  342. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
  343. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  344. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  345. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
  346. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
  347. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
  348. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  349. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  350. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  351. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  352. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
  353. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  354. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  355. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
  356. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  357. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  358. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  359. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
  360. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
  361. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
  362. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +40 -53
  363. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +72 -220
  364. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  365. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +27 -0
  366. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +11 -7
  367. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
  368. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
  369. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  370. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  371. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  372. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
  373. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +57 -33
  374. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  375. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  376. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  377. package/runtime/node_modules/@metro-labs/xmtp/src/resolve.ts +44 -0
  378. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  379. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  380. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +7 -2
  381. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +22 -58
  382. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +55 -48
  383. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  384. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  385. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
  386. package/runtime/runtime.json +1 -1
  387. package/runtime/server.ts +1 -0
  388. package/runtime/stations.json +38 -0
  389. package/dist/prompt.js +0 -61
  390. package/dist/store.js +0 -70
  391. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +0 -195
  392. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +0 -292
  393. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +0 -197
  394. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -5
  395. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  396. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  397. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  398. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -341
  399. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  400. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -320
  401. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  402. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  403. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  404. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  405. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  406. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  407. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  408. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  409. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  410. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  411. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  412. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  413. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  414. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  415. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  416. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  417. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  418. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  419. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  420. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  421. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  422. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +0 -71
  423. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  424. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  425. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  426. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  427. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
  428. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  429. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  430. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  431. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  432. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  433. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  434. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  435. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  436. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  437. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  438. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  439. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  440. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  441. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
  442. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
  443. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
  444. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
  445. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -3
  446. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  447. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
  448. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  449. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  450. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  451. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  452. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  453. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  454. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  455. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  456. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  457. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
@@ -1,13 +1,14 @@
1
1
  import type { Reaction } from '@xmtp/content-type-reaction';
2
2
  import type { Reply } from '@xmtp/content-type-reply';
3
- import { errMsg } from '@metro-labs/mcp/log';
3
+ import { errMsg } from '@metro-labs/core/log';
4
4
  import { transcribeAndEmit } from './transcribe.js';
5
5
  import {
6
6
  saveInlineAttachment,
7
7
  saveRemoteAttachment,
8
8
  type RemoteEntry,
9
9
  } from './attachments.js';
10
- import { emitInbound, emitAttachmentSaved } from './emit-core.js';
10
+ import { reportAttachment } from '@metro-labs/core/stations/train-events';
11
+ import { sentByMe } from './wire.js';
11
12
 
12
13
  export interface EnvelopeCtx {
13
14
  accountId: string;
@@ -150,6 +151,7 @@ function replyPayload(
150
151
  ? c.content
151
152
  : `[reply with ${c.contentType?.typeId ?? 'unknown'}]`,
152
153
  event: { type: 'reply', replyTo: c.reference },
154
+ ...(sentByMe(c.reference) ? { reply_to_self: true } : {}),
153
155
  payload: {
154
156
  contentType: typeId,
155
157
  replyTo: c.reference,
@@ -187,19 +189,12 @@ function inlineAttachmentPayload(
187
189
  ctx.line,
188
190
  ctx.accountId,
189
191
  ctx.baseId,
190
- emitInbound,
191
192
  ).catch((err: unknown) => {
192
193
  process.stderr.write(
193
194
  `xmtp[${ctx.accountId}] voice transcription failed: ${errMsg(err)}\n`,
194
195
  );
195
196
  });
196
- emitAttachmentSaved(
197
- ctx.accountId,
198
- ctx.line,
199
- ctx.baseId,
200
- 0,
201
- saveInlineAttachment(c, ctx.msgId, 0),
202
- );
197
+ reportAttachment(saveInlineAttachment(c, ctx.msgId, 0), { station: 'xmtp', account: ctx.accountId, line: ctx.line, forId: ctx.baseId, index: 0 });
203
198
  return out;
204
199
  }
205
200
 
@@ -210,13 +205,7 @@ function remoteStaticPayload(
210
205
  ctx: EnvelopeCtx,
211
206
  ): Record<string, unknown> {
212
207
  const kind = IMG_RE.test(c.url) ? 'image' : 'file';
213
- emitAttachmentSaved(
214
- ctx.accountId,
215
- ctx.line,
216
- ctx.baseId,
217
- 0,
218
- saveRemoteAttachment(c, ctx.msgId, 0),
219
- );
208
+ reportAttachment(saveRemoteAttachment(c, ctx.msgId, 0), { station: 'xmtp', account: ctx.accountId, line: ctx.line, forId: ctx.baseId, index: 0 });
220
209
  return {
221
210
  ...base,
222
211
  text: `[${kind}: ${c.filename ?? c.url}]`,
@@ -232,13 +221,7 @@ function multiRemoteEnvelope(
232
221
  ): Record<string, unknown> {
233
222
  const m = c as { attachments?: RemoteEntry[] };
234
223
  (Array.isArray(m.attachments) ? m.attachments : []).forEach((r, i) => {
235
- emitAttachmentSaved(
236
- ctx.accountId,
237
- ctx.line,
238
- ctx.baseId,
239
- i,
240
- saveRemoteAttachment(r, ctx.msgId, i),
241
- );
224
+ reportAttachment(saveRemoteAttachment(r, ctx.msgId, i), { station: 'xmtp', account: ctx.accountId, line: ctx.line, forId: ctx.baseId, index: i });
242
225
  });
243
226
  return multiRemotePayload(base, typeId, c);
244
227
  }
@@ -1,12 +1,10 @@
1
1
  import type { Conversation, DecodedMessage } from '@xmtp/node-sdk';
2
- import { lineOf, parseLine } from './accounts.js';
3
- import { emit, mintId, rememberUid, SELF_URI } from './wire.js';
4
- import { fcmPushToAll } from './push.js';
5
- import { emitInbound, emitAttachmentSaved } from './emit-core.js';
2
+ import { lineOf } from './accounts.js';
3
+ import { selfUri } from '@metro-labs/core/stations/train-events';
4
+ import { emit, mintId } from '@metro-labs/core/stations/station-runtime';
5
+ import { rememberSent, rememberUid } from './wire.js';
6
6
  import { typedEnvelope, type EnvelopeCtx } from './emit-payloads.js';
7
- import type { StructuredEvent } from '@metro-labs/mcp/events';
8
-
9
- export { emitInbound, emitAttachmentSaved };
7
+ import type { StructuredEvent } from '@metro-labs/core/events';
10
8
 
11
9
  export function envelope(
12
10
  accountId: string,
@@ -23,6 +21,7 @@ export function envelope(
23
21
  line,
24
22
  from: `metro://xmtp/${accountId}/user/${msg.senderInboxId}`,
25
23
  message_id: msg.id,
24
+ is_private: typeof (conv as unknown as { peerInboxId?: unknown }).peerInboxId === 'function',
26
25
  };
27
26
  rememberUid(base.id, msg.id);
28
27
  if (typeof c === 'string')
@@ -53,26 +52,17 @@ export function emitOutbound(
53
52
  ): void {
54
53
  const uid = mintId();
55
54
  rememberUid(uid, messageId);
55
+ rememberSent(messageId);
56
56
  emit({
57
- kind: 'outbound',
58
57
  id: uid,
59
58
  ts: new Date().toISOString(),
60
59
  station: 'xmtp',
61
60
  line,
62
- from: SELF_URI,
61
+ from: selfUri(),
63
62
  to: line,
64
63
  message_id: messageId,
65
64
  text,
66
- account: accountId,
67
65
  ...(event ? { event } : {}),
68
66
  payload: { account: accountId },
69
67
  });
70
- void (async (): Promise<void> => {
71
- const data: Record<string, string> = { line, messageId };
72
- {
73
- const p = parseLine(line);
74
- if (p) data.convId = p.convId.toLowerCase();
75
- }
76
- await fcmPushToAll(accountId, data);
77
- })().catch(() => undefined);
78
68
  }
@@ -1,7 +1,7 @@
1
1
  import type {
2
2
  MemberOutcome,
3
3
  MemberOutcomeStatus,
4
- } from '@metro-labs/mcp/stations/types';
4
+ } from '@metro-labs/core/stations/types';
5
5
 
6
6
  type Args = Record<string, unknown>;
7
7
 
@@ -1,5 +1,5 @@
1
- import { TrainError } from '@metro-labs/mcp/train-error';
2
- import type { GroupResult } from '@metro-labs/mcp/stations/types';
1
+ import { TrainError } from '@metro-labs/core/train-error';
2
+ import type { GroupResult } from '@metro-labs/core/stations/types';
3
3
  import { accountForCall, convOf, lineOf } from './accounts.js';
4
4
  import {
5
5
  applyMemberOp,
@@ -2,32 +2,46 @@ import { IdentifierKind, type Signer } from '@xmtp/node-sdk';
2
2
  import { privateKeyToAccount } from 'viem/accounts';
3
3
  import { homedir } from 'node:os';
4
4
  import { join } from 'node:path';
5
+ import { SMART_CHAIN_ID, smartAccountFor, type SmartAccount } from './smart.js';
5
6
 
6
7
  export const XMTP_ENV = 'production' as const;
7
8
 
8
9
  export const expandHome = (p: string): string =>
9
10
  p.startsWith('~') ? join(homedir(), p.slice(1)) : p;
10
11
 
11
- export function signerFor(privateKey: string): {
12
+ export interface Identity {
12
13
  signer: Signer;
13
14
  address: string;
14
- } {
15
+ smart: SmartAccount | null;
16
+ }
17
+
18
+ const bytesOf = (sig: string): Uint8Array => {
19
+ const hex = sig.slice(2);
20
+ const out = new Uint8Array(hex.length / 2);
21
+ for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
22
+ return out;
23
+ };
24
+
25
+ export function signerFor(privateKey: string): Identity {
15
26
  const acct = privateKeyToAccount(privateKey as `0x${string}`);
16
27
  const signer: Signer = {
17
28
  type: 'EOA',
18
- getIdentifier: () =>
19
- Promise.resolve({
20
- identifier: acct.address,
21
- identifierKind: IdentifierKind.Ethereum,
22
- }),
23
- signMessage: async (msg: string) => {
24
- const sig = await acct.signMessage({ message: msg });
25
- const hex = sig.slice(2);
26
- const out = new Uint8Array(hex.length / 2);
27
- for (let i = 0; i < out.length; i++)
28
- out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
29
- return out;
30
- },
29
+ getIdentifier: () => Promise.resolve({ identifier: acct.address, identifierKind: IdentifierKind.Ethereum }),
30
+ signMessage: async (msg: string) => bytesOf(await acct.signMessage({ message: msg })),
31
+ };
32
+ return { signer, address: acct.address, smart: null };
33
+ }
34
+
35
+ export async function smartSignerFor(privateKey: string, rpc?: string): Promise<Identity> {
36
+ const smart = await smartAccountFor(privateKey, rpc);
37
+ const signer: Signer = {
38
+ type: 'SCW',
39
+ getIdentifier: () => Promise.resolve({ identifier: smart.address, identifierKind: IdentifierKind.Ethereum }),
40
+ signMessage: async (msg: string) => bytesOf(await smart.signMessage(msg)),
41
+ getChainId: () => BigInt(SMART_CHAIN_ID),
31
42
  };
32
- return { signer, address: acct.address };
43
+ return { signer, address: smart.address, smart };
33
44
  }
45
+
46
+ export const identityFor = (privateKey: string, smart: boolean): Promise<Identity> =>
47
+ smart ? smartSignerFor(privateKey) : Promise.resolve(signerFor(privateKey));
@@ -1,4 +1,4 @@
1
- import { errMsg } from '@metro-labs/mcp/log';
1
+ import { errMsg } from '@metro-labs/core/log';
2
2
  import { ConsentState, type DecodedMessage } from '@xmtp/node-sdk';
3
3
  import {
4
4
  accounts,
@@ -6,10 +6,12 @@ import {
6
6
  loadAccounts,
7
7
  type Account,
8
8
  } from './accounts.js';
9
- import { emitInbound, envelope } from './emit.js';
9
+ import { emitInbound } from '@metro-labs/core/stations/train-events';
10
+ import { announceAccounts } from '@metro-labs/core/stations/train-boot';
11
+ import { envelope } from './emit.js';
10
12
  import { groupNameFor } from './conv-helpers.js';
11
- import { handleControlDm, pushInbound } from './push.js';
12
- import { readCalls } from '@metro-labs/mcp/trains/protocol';
13
+ import { senderFieldsNow } from './sender.js';
14
+ import { readCalls } from '@metro-labs/core/trains/protocol';
13
15
  import { handleCall } from './actions.js';
14
16
 
15
17
  readCalls('xmtp', handleCall);
@@ -59,21 +61,15 @@ async function handleStreamMessage(
59
61
  const { id } = acct.cfg;
60
62
  if (msg.senderInboxId === acct.client.inboxId) return;
61
63
  if (SILENT_TYPES.has(msg.contentType?.typeId ?? '')) return;
62
- if (handleControlDm(id, msg)) return;
63
64
  const conv = await acct.client.conversations.getConversationById(
64
65
  msg.conversationId,
65
66
  );
66
67
  if (!conv) return;
67
68
  const env = envelope(id, msg, conv);
69
+ Object.assign(env, senderFieldsNow(acct, msg.senderInboxId));
68
70
  const name = await groupNameFor(msg.conversationId, conv);
69
- if (name) {
70
- env.line_name = name;
71
- env.lineName = name;
72
- const p = (env.payload ?? {}) as Record<string, unknown>;
73
- env.payload = { ...p, lineName: name };
74
- }
71
+ if (name) env.line_name = name;
75
72
  emitInbound(id, env);
76
- pushInbound(id, env, msg, conv);
77
73
  }
78
74
 
79
75
  async function runAccount(acct: Account): Promise<void> {
@@ -88,7 +84,9 @@ async function runAccount(acct: Account): Promise<void> {
88
84
  });
89
85
  for await (const msg of stream) {
90
86
  if (!msg) continue;
91
- await handleStreamMessage(acct, msg);
87
+ await handleStreamMessage(acct, msg).catch((err: unknown) => {
88
+ process.stderr.write(`xmtp[${id}] one message failed and was skipped: ${errMsg(err)}\n`);
89
+ });
92
90
  }
93
91
  } catch (err) {
94
92
  process.stderr.write(
@@ -109,13 +107,7 @@ for (const cfg of cfgs) {
109
107
  );
110
108
  }
111
109
  }
112
- if (accounts.size === 0) {
113
- process.stderr.write('xmtp: no accounts booted, exiting\n');
114
- process.exit(2);
115
- }
116
- process.stderr.write(
117
- `xmtp train ready — ${accounts.size} account(s): ${[...accounts.keys()].join(', ')}\n`,
118
- );
110
+ announceAccounts('xmtp', accounts.keys());
119
111
 
120
112
  for (const acct of accounts.values())
121
113
  runAccount(acct).catch((err: unknown) => {
@@ -1,3 +1,5 @@
1
+ import { TrainError } from '@metro-labs/core/train-error';
2
+
1
3
  export interface GroupLike {
2
4
  id: string;
3
5
  appData?: string;
@@ -10,8 +12,9 @@ export interface GroupLike {
10
12
 
11
13
  const MAX_LABELS = 16;
12
14
  const MAX_LABEL_LEN = 24;
15
+ const MAX_APP_DATA_BYTES = 8192;
13
16
 
14
- export function cleanLabels(raw: unknown): string[] {
17
+ function cleanLabels(raw: unknown): string[] {
15
18
  if (!Array.isArray(raw)) return [];
16
19
  const out: string[] = [];
17
20
  const seen = new Set<string>();
@@ -27,36 +30,40 @@ export function cleanLabels(raw: unknown): string[] {
27
30
  return out;
28
31
  }
29
32
 
30
- function parseAppDataObject(
33
+ export function readAppDataObject(
31
34
  existingAppData: string | undefined,
32
35
  ): Record<string, unknown> {
33
36
  if (!existingAppData?.trim()) return {};
34
- try {
35
- const p: unknown = JSON.parse(existingAppData);
36
- if (p && typeof p === 'object' && !Array.isArray(p))
37
- return p as Record<string, unknown>;
38
- } catch {
39
- return {};
37
+ const parsed: unknown = JSON.parse(existingAppData);
38
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
39
+ throw new TrainError('INVALID_ARGS', 'Channel appData must be a JSON object');
40
40
  }
41
- return {};
41
+ return parsed as Record<string, unknown>;
42
42
  }
43
43
 
44
- export function labelsBlob(
45
- existingAppData: string | undefined,
46
- labels: string[],
47
- github?: string,
48
- ): string {
49
- const existing = parseAppDataObject(existingAppData);
50
- const blob: Record<string, unknown> = {
51
- ...existing,
52
- v: 1,
53
- labels: cleanLabels(labels),
54
- };
55
- if (typeof github === 'string') {
56
- if (github.trim()) blob.github = github.trim();
57
- else delete blob.github;
58
- }
59
- return JSON.stringify(blob);
44
+ export function normalizeAssigned(value: unknown): string[] {
45
+ if (!Array.isArray(value)) {
46
+ throw new TrainError('INVALID_ARGS', 'assigned must be an array of Ethereum addresses');
47
+ }
48
+ const addresses: string[] = [];
49
+ for (const address of value) {
50
+ if (typeof address !== 'string' || !/^0x[0-9a-fA-F]{40}$/.test(address.trim())) {
51
+ throw new TrainError('INVALID_ARGS', 'assigned must contain valid Ethereum addresses');
52
+ }
53
+ addresses.push(address.trim().toLowerCase());
54
+ }
55
+ return [...new Set(addresses)];
56
+ }
57
+
58
+ export function metadataPatch(value: unknown): Record<string, unknown> {
59
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
60
+ throw new TrainError('INVALID_ARGS', 'metadata must be a JSON object');
61
+ }
62
+ const patch = value as Record<string, unknown>;
63
+ if (Object.hasOwn(patch, 'v')) {
64
+ throw new TrainError('INVALID_ARGS', 'metadata cannot change the reserved v field');
65
+ }
66
+ return patch;
60
67
  }
61
68
 
62
69
  function trimmedString(v: unknown): string | undefined {
@@ -83,13 +90,24 @@ export function readAppData(appData: string | undefined): {
83
90
  }
84
91
  }
85
92
 
93
+ function checkedLabels(value: unknown): string[] {
94
+ if (!Array.isArray(value) || value.some((label: unknown) => typeof label !== 'string')) {
95
+ throw new TrainError('INVALID_ARGS', 'labels must be an array of strings');
96
+ }
97
+ return cleanLabels(value);
98
+ }
99
+
86
100
  function applyMergeKey(
87
101
  merged: Record<string, unknown>,
88
102
  k: string,
89
103
  v: unknown,
90
104
  ): void {
91
105
  if (k === 'labels') {
92
- merged.labels = cleanLabels(v);
106
+ merged.labels = checkedLabels(v);
107
+ return;
108
+ }
109
+ if (k === 'assigned') {
110
+ merged.assigned = normalizeAssigned(v);
93
111
  return;
94
112
  }
95
113
  if (k === 'github') {
@@ -105,19 +123,25 @@ function applyMergeKey(
105
123
  return;
106
124
  }
107
125
  if (v === undefined || v === null) Reflect.deleteProperty(merged, k);
108
- else merged[k] = v;
126
+ else Object.defineProperty(merged, k, { value: v, enumerable: true, configurable: true, writable: true });
109
127
  }
110
128
 
111
129
  export function mergeAppData(
112
130
  existingAppData: string | undefined,
113
131
  patch: Record<string, unknown>,
114
132
  ): { blob: string; merged: Record<string, unknown> } {
115
- const merged: Record<string, unknown> = {
116
- ...parseAppDataObject(existingAppData),
117
- v: 1,
118
- };
119
- for (const [k, v] of Object.entries(patch)) applyMergeKey(merged, k, v);
120
- return { blob: JSON.stringify(merged), merged };
133
+ const current = readAppDataObject(existingAppData);
134
+ const checked = metadataPatch(patch);
135
+ if (Object.hasOwn(checked, 'assigned') && Object.hasOwn(current, 'assigned')) {
136
+ normalizeAssigned(current.assigned);
137
+ }
138
+ const merged: Record<string, unknown> = { v: 1, ...current };
139
+ for (const [k, v] of Object.entries(checked)) applyMergeKey(merged, k, v);
140
+ const blob = JSON.stringify(merged);
141
+ if (Buffer.byteLength(blob, 'utf8') > MAX_APP_DATA_BYTES) {
142
+ throw new TrainError('INVALID_ARGS', `Channel appData exceeds ${MAX_APP_DATA_BYTES} bytes`);
143
+ }
144
+ return { blob, merged };
121
145
  }
122
146
 
123
147
  export function normalizeGithubUrl(url: unknown): string {
@@ -1,4 +1,4 @@
1
- import { TrainError } from '@metro-labs/mcp/train-error';
1
+ import { TrainError } from '@metro-labs/core/train-error';
2
2
 
3
3
  type Args = Record<string, unknown>;
4
4
 
@@ -0,0 +1,61 @@
1
+ import { TrainError } from '@metro-labs/core/train-error';
2
+
3
+ export const STAGE_NAMES_PARENT = 'stage.base.eth';
4
+ export const STAGE_PROXY = 'https://proxy.stage.box';
5
+ const LABEL_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
6
+ const MIN = 6;
7
+ const MAX = 32;
8
+ const HEADERS = { 'content-type': 'application/json', 'x-stage-client': '1' };
9
+
10
+ export const proxyBase = (): string => process.env.METRO_STAGE_PROXY ?? STAGE_PROXY;
11
+
12
+ export const stageNameOf = (label: string): string => `${label}.${STAGE_NAMES_PARENT}`;
13
+
14
+ export function parseLabel(raw: unknown): string {
15
+ const label = typeof raw === 'string' ? raw.trim().toLowerCase() : '';
16
+ if (label.length < MIN) throw new TrainError('bad_request', `a name needs at least ${String(MIN)} characters`);
17
+ if (label.length > MAX) throw new TrainError('bad_request', `a name can have at most ${String(MAX)} characters`);
18
+ if (!LABEL_RE.test(label)) throw new TrainError('bad_request', 'a name is lowercase letters, digits and single inner hyphens');
19
+ return label;
20
+ }
21
+
22
+ export const claimMessage = (label: string, address: string, issuedAt: number): string =>
23
+ `Claim ${stageNameOf(label)} for ${address.toLowerCase()} at ${String(issuedAt)}`;
24
+
25
+ async function ask(path: string, init: RequestInit, fetchImpl: typeof fetch): Promise<Record<string, unknown>> {
26
+ let res: Response;
27
+ try {
28
+ res = await fetchImpl(`${proxyBase()}/names/${path}`, { ...init, headers: HEADERS, signal: AbortSignal.timeout(30_000) });
29
+ } catch (err) {
30
+ throw new TrainError('names_unreachable', `could not reach the stage name service: ${err instanceof Error ? err.message : String(err)}`);
31
+ }
32
+ const body: unknown = await res.json().catch(() => null);
33
+ const record = typeof body === 'object' && body !== null ? (body as Record<string, unknown>) : {};
34
+ if (!res.ok) throw new TrainError('names_refused', typeof record.error === 'string' ? record.error : `the stage name service answered ${String(res.status)}`);
35
+ return record;
36
+ }
37
+
38
+ export async function nameOf(address: string, fetchImpl: typeof fetch = fetch): Promise<string | null> {
39
+ const body = await ask(`status?address=${encodeURIComponent(address)}`, {}, fetchImpl);
40
+ return typeof body.name === 'string' && body.name !== '' ? body.name : null;
41
+ }
42
+
43
+ export async function checkLabel(label: string, fetchImpl: typeof fetch = fetch): Promise<void> {
44
+ const body = await ask(`check?label=${encodeURIComponent(label)}`, {}, fetchImpl);
45
+ if (body.valid !== true) throw new TrainError('bad_request', typeof body.reason === 'string' ? body.reason : `${stageNameOf(label)} is not a valid name`);
46
+ if (body.available !== true) throw new TrainError('name_taken', `${stageNameOf(label)} is already taken`);
47
+ }
48
+
49
+ export async function claimName(
50
+ label: string,
51
+ address: string,
52
+ sign: (message: string) => Promise<string>,
53
+ fetchImpl: typeof fetch = fetch,
54
+ now = Date.now(),
55
+ ): Promise<string> {
56
+ await checkLabel(label, fetchImpl);
57
+ const signature = await sign(claimMessage(label, address, now));
58
+ const body = await ask('claim', { method: 'POST', body: JSON.stringify({ label, address, issuedAt: now, signature }) }, fetchImpl);
59
+ if (typeof body.name !== 'string' || body.name === '') throw new TrainError('names_refused', 'the stage name service issued no name');
60
+ return body.name;
61
+ }
@@ -0,0 +1,153 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { encodeFunctionData, encodePacked, keccak256, namehash, stringToBytes, type Hex } from 'viem';
3
+ import { normalize } from 'viem/ens';
4
+ import { TrainError } from '@metro-labs/core/train-error';
5
+ import {
6
+ assertImage,
7
+ fieldsOf,
8
+ parseProfileChange,
9
+ type ProfileApplied,
10
+ type ProfileAvatar,
11
+ type ProfileChange,
12
+ } from '@metro-labs/core/stations/profile';
13
+ import { accountForCall } from './accounts.js';
14
+ import { claimName, nameOf, parseLabel } from './names.js';
15
+ import { sendSponsored, type SmartAccount } from './smart.js';
16
+ import { respond } from '@metro-labs/core/stations/station-runtime';
17
+
18
+ export const BASENAME_REGISTRY = '0xB94704422c2a1E396835A571837Aa5AE53285a95' as const;
19
+ export const BASENAME_L2_RESOLVER = '0xC6d566A56A1aFf6508b41f6c90ff131615583BCD' as const;
20
+ export const BASENAME_REVERSE_REGISTRAR = '0x79EA96012eEa67A83431F1701B3dFf7e37F9E282' as const;
21
+ export const PINEAPPLE_UPLOAD_URL = 'https://pineapple.fyi/upload';
22
+ const STAMP_CLEAR_URL = 'https://stamp.fyi/clear/';
23
+ const ZERO = '0x0000000000000000000000000000000000000000';
24
+ const TEXT_KEYS = { name: 'name', bio: 'description', avatar: 'avatar' } as const;
25
+
26
+ export const REGISTRY_ABI = [
27
+ { name: 'resolver', type: 'function', stateMutability: 'view', inputs: [{ name: 'node', type: 'bytes32' }], outputs: [{ name: '', type: 'address' }] },
28
+ ] as const;
29
+
30
+ const RESOLVER_ABI = [
31
+ {
32
+ name: 'setText',
33
+ type: 'function',
34
+ stateMutability: 'nonpayable',
35
+ inputs: [{ name: 'node', type: 'bytes32' }, { name: 'key', type: 'string' }, { name: 'value', type: 'string' }],
36
+ outputs: [],
37
+ },
38
+ { name: 'multicall', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'data', type: 'bytes[]' }], outputs: [{ name: 'results', type: 'bytes[]' }] },
39
+ ] as const;
40
+
41
+ const REVERSE_ABI = [
42
+ { name: 'setName', type: 'function', stateMutability: 'nonpayable', inputs: [{ name: 'name', type: 'string' }], outputs: [{ name: '', type: 'bytes32' }] },
43
+ ] as const;
44
+
45
+ export const NAME_ABI = [
46
+ { name: 'name', type: 'function', stateMutability: 'view', inputs: [{ name: 'node', type: 'bytes32' }], outputs: [{ name: '', type: 'string' }] },
47
+ ] as const;
48
+
49
+ const BASE_REVERSE_ROOT = namehash('80002105.reverse');
50
+
51
+ type Args = Record<string, unknown>;
52
+
53
+ const NO_NAME = 'this XMTP account has no name yet, so it has no profile: claim a <label>.stage.base.eth name on its channel page first';
54
+ const NOT_SMART = 'this XMTP account was attached before names existed and cannot hold one; attach XMTP again to get an account that can';
55
+
56
+ export async function pinAvatar(avatar: ProfileAvatar, fetchImpl: typeof fetch = fetch): Promise<string> {
57
+ assertImage(avatar);
58
+ const form = new FormData();
59
+ form.append('file', new Blob([await readFile(avatar.path)], { type: avatar.mime }), avatar.name);
60
+ const res = await fetchImpl(PINEAPPLE_UPLOAD_URL, { method: 'POST', body: form, signal: AbortSignal.timeout(60_000) });
61
+ const body = (await res.json().catch(() => ({}))) as { result?: { cid?: string }; error?: { message?: string } };
62
+ if (!res.ok) throw new TrainError('avatar_upload', `the image upload answered ${String(res.status)}`);
63
+ if (typeof body.error?.message === 'string') throw new TrainError('avatar_upload', body.error.message);
64
+ if (typeof body.result?.cid !== 'string' || body.result.cid === '') throw new TrainError('avatar_upload', 'the image upload returned no CID');
65
+ return `ipfs://${body.result.cid}`;
66
+ }
67
+
68
+ export const encodePrimaryName = (name: string): Hex => encodeFunctionData({ abi: REVERSE_ABI, functionName: 'setName', args: [name] });
69
+
70
+ export const reverseNodeOf = (address: string): Hex =>
71
+ keccak256(encodePacked(['bytes32', 'bytes32'], [BASE_REVERSE_ROOT, keccak256(stringToBytes(address.slice(2).toLowerCase()))]));
72
+
73
+ async function resolverOf(smart: SmartAccount, node: Hex): Promise<Hex> {
74
+ const found: Hex = await smart.publicClient.readContract({ address: BASENAME_REGISTRY, abi: REGISTRY_ABI, functionName: 'resolver', args: [node] });
75
+ return found === ZERO ? BASENAME_L2_RESOLVER : found;
76
+ }
77
+
78
+ export async function primaryNameOf(smart: SmartAccount): Promise<string | null> {
79
+ const node = reverseNodeOf(smart.address);
80
+ const found = await smart.publicClient.readContract({ address: await resolverOf(smart, node), abi: NAME_ABI, functionName: 'name', args: [node] }).catch(() => '');
81
+ return found === '' ? null : found;
82
+ }
83
+
84
+ export async function setPrimaryName(smart: SmartAccount, name: string): Promise<boolean> {
85
+ try {
86
+ await sendSponsored(smart, BASENAME_REVERSE_REGISTRAR, encodePrimaryName(name));
87
+ return true;
88
+ } catch (err) {
89
+ process.stderr.write(`xmtp: the name ${name} is claimed but not set as the primary name of ${smart.address}: ${err instanceof Error ? err.message : String(err)}\n`);
90
+ return false;
91
+ }
92
+ }
93
+
94
+ export function encodeTextRecords(name: string, records: Record<string, string>): Hex {
95
+ const node = namehash(normalize(name));
96
+ const calls = Object.entries(records).map(([key, value]) => encodeFunctionData({ abi: RESOLVER_ABI, functionName: 'setText', args: [node, key, value] }));
97
+ return encodeFunctionData({ abi: RESOLVER_ABI, functionName: 'multicall', args: [calls] });
98
+ }
99
+
100
+ const resolverFor = (smart: SmartAccount, name: string): Promise<Hex> => resolverOf(smart, namehash(normalize(name)));
101
+
102
+ async function recordsFor(change: ProfileChange, fetchImpl: typeof fetch): Promise<Record<string, string>> {
103
+ const records: Record<string, string> = {};
104
+ if (change.name !== undefined) records[TEXT_KEYS.name] = change.name;
105
+ if (change.bio !== undefined) records[TEXT_KEYS.bio] = change.bio;
106
+ if (change.avatar !== undefined) records[TEXT_KEYS.avatar] = await pinAvatar(change.avatar, fetchImpl);
107
+ return records;
108
+ }
109
+
110
+ export async function writeProfile(smart: SmartAccount, name: string, change: ProfileChange, fetchImpl: typeof fetch = fetch): Promise<Hex> {
111
+ const records = await recordsFor(change, fetchImpl);
112
+ const hash = await sendSponsored(smart, await resolverFor(smart, name), encodeTextRecords(name, records));
113
+ const id = smart.address.toLowerCase();
114
+ await Promise.all([fetchImpl(`${STAMP_CLEAR_URL}address/${id}`), fetchImpl(`${STAMP_CLEAR_URL}avatar/eth:${id}`)]).catch(() => undefined);
115
+ return hash;
116
+ }
117
+
118
+ const smartOf = (args: Args): { accountId: string; address: string; smart: SmartAccount } => {
119
+ const acct = accountForCall(args);
120
+ if (acct.smart === null) throw new TrainError('no_name', NOT_SMART);
121
+ return { accountId: acct.cfg.id, address: acct.address, smart: acct.smart };
122
+ };
123
+
124
+ export async function setProfile(id: string, args: Args): Promise<void> {
125
+ const { accountId, address, smart } = smartOf(args);
126
+ const change = parseProfileChange(args);
127
+ const name = await nameOf(address);
128
+ if (name === null) throw new TrainError('no_name', NO_NAME);
129
+ const tx = await writeProfile(smart, name, change);
130
+ const result: ProfileApplied & { name: string; tx: string } = { account: accountId, applied: fieldsOf(change), name, tx };
131
+ respond(id, { result });
132
+ }
133
+
134
+ export async function claimNameAction(id: string, args: Args): Promise<void> {
135
+ const { accountId, address, smart } = smartOf(args);
136
+ const held = await nameOf(address);
137
+ if (held !== null) throw new TrainError('name_held', `this account already holds ${held}`);
138
+ const name = await claimName(parseLabel(args.label), address, (message) => smart.signMessage(message));
139
+ const primary = await setPrimaryName(smart, name);
140
+ respond(id, { result: { account: accountId, name, primary } });
141
+ }
142
+
143
+ export async function ensurePrimaryName(smart: SmartAccount, name: string): Promise<boolean> {
144
+ if ((await primaryNameOf(smart)) === name) return true;
145
+ return setPrimaryName(smart, name);
146
+ }
147
+
148
+ export async function nameAction(id: string, args: Args): Promise<void> {
149
+ const acct = accountForCall(args);
150
+ const name = acct.smart === null ? null : await nameOf(acct.address);
151
+ const primary = acct.smart !== null && name !== null ? await ensurePrimaryName(acct.smart, name) : false;
152
+ respond(id, { result: { account: acct.cfg.id, name, primary, canClaim: acct.smart !== null && name === null } });
153
+ }