@stage-labs/metro 0.1.0-beta.21 → 0.1.0-beta.210

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 (436) hide show
  1. package/dist/api.js +9 -101
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +152 -0
  4. package/dist/cli.js +52 -109
  5. package/dist/control.js +47 -75
  6. package/dist/hold.js +210 -0
  7. package/dist/local.js +43 -0
  8. package/dist/mcp-config.js +25 -0
  9. package/dist/node-name.js +71 -0
  10. package/dist/onboarding.js +44 -0
  11. package/dist/provider-flags.js +9 -0
  12. package/dist/route.js +58 -0
  13. package/dist/runtime-install.js +96 -0
  14. package/dist/runtime.js +29 -52
  15. package/dist/serve.js +157 -0
  16. package/dist/service.js +304 -0
  17. package/dist/tail.js +119 -0
  18. package/dist/update.js +13 -1
  19. package/package.json +2 -23
  20. package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
  21. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
  22. package/runtime/marketplace/plugin/.mcp.json +1 -0
  23. package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
  24. package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
  25. package/runtime/marketplace/plugin/bin/policy.mjs +97 -0
  26. package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
  27. package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
  28. package/runtime/marketplace/plugin/orchestrator.md +26 -0
  29. package/runtime/node_modules/@metro-labs/core/package.json +47 -0
  30. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  31. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
  32. package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
  33. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  34. package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +24 -28
  35. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
  36. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
  37. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
  38. package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -0
  39. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +83 -0
  40. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
  41. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
  42. package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +58 -0
  43. package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
  44. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/station-runtime.ts +6 -11
  45. package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
  46. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +84 -0
  47. package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
  48. package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
  49. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  50. package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/agent-fs.ts +134 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +89 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +144 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +37 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +89 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +55 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +142 -0
  73. package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
  74. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
  75. package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
  76. package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
  77. package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +150 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +218 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
  84. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
  85. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
  86. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
  87. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
  88. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +70 -45
  89. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
  90. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
  91. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
  92. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +355 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +284 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +273 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
  104. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
  105. package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
  106. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
  108. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
  109. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
  110. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
  111. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
  112. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +347 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -0
  120. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
  121. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
  122. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
  123. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
  124. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
  125. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
  126. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +233 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +173 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +195 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +128 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +237 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +224 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
  144. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
  145. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +278 -0
  146. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
  147. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
  148. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
  149. package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
  150. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
  151. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
  152. package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
  153. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +14 -0
  154. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
  155. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
  156. package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
  157. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
  158. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
  159. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
  160. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
  161. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +24 -79
  162. package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
  163. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
  164. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-prompt.ts +87 -0
  165. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
  166. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -0
  167. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
  168. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
  169. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -0
  170. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +87 -0
  171. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
  172. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
  173. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
  174. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
  175. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
  176. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
  177. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +68 -119
  178. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
  179. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
  180. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  181. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
  183. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
  184. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
  185. package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -0
  186. package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
  187. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/routes}/http.ts +59 -51
  188. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +205 -0
  189. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +66 -0
  190. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  191. package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
  192. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +39 -0
  193. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
  194. package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
  195. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
  196. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  197. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
  198. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
  199. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
  200. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
  201. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
  202. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
  203. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
  204. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +75 -9
  205. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
  206. package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
  207. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  208. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
  209. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
  210. package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
  211. package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
  212. package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +51 -0
  213. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
  214. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
  215. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
  216. package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
  217. package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
  218. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +9 -0
  219. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
  220. package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
  221. package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
  222. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +147 -0
  223. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
  224. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
  225. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/package.json +3 -5
  226. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +8 -28
  227. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +19 -187
  228. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
  229. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +31 -72
  230. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +2 -2
  231. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
  232. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +15 -21
  233. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/members.ts +2 -2
  234. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -0
  235. package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/send-files.ts +12 -13
  236. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +23 -0
  237. package/runtime/node_modules/@metro-labs/http/package.json +29 -0
  238. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
  239. package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
  240. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  241. package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +149 -0
  242. package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
  243. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +93 -0
  244. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
  245. package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
  246. package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
  247. package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
  248. package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
  249. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +140 -0
  250. package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
  251. package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
  252. package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
  253. package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
  254. package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
  255. package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
  256. package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
  257. package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
  258. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
  259. package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
  260. package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -0
  261. package/runtime/node_modules/@metro-labs/telegram/package.json +3 -2
  262. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +38 -96
  263. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
  264. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
  265. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
  266. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +101 -147
  267. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
  268. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
  269. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
  270. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
  271. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
  272. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +64 -151
  273. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
  274. package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
  275. package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +3 -3
  276. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
  277. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
  278. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  279. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +86 -0
  280. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +240 -0
  281. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  282. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +170 -0
  283. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  284. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +90 -0
  285. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  286. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
  287. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +21 -0
  288. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
  289. package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
  290. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +95 -0
  291. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -0
  292. package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
  293. package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
  294. package/runtime/node_modules/@metro-labs/threema/src/files.ts +47 -0
  295. package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -0
  296. package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
  297. package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
  298. package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
  299. package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
  300. package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
  301. package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
  302. package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -0
  303. package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
  304. package/runtime/node_modules/@metro-labs/webhook/package.json +1 -1
  305. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
  306. package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
  307. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
  308. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
  309. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
  310. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
  311. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
  312. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
  313. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
  314. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
  315. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
  316. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
  317. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
  318. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
  319. package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
  320. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
  321. package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
  322. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
  323. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
  324. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
  325. package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
  326. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
  327. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
  328. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
  329. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +4 -43
  330. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +41 -219
  331. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
  332. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
  333. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
  334. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
  335. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
  336. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
  337. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
  338. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
  339. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +1 -19
  340. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
  341. package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
  342. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
  343. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
  344. package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
  345. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +5 -1
  346. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +4 -51
  347. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
  348. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
  349. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
  350. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
  351. package/runtime/runtime.json +1 -1
  352. package/runtime/server.ts +1 -0
  353. package/runtime/stations.json +37 -0
  354. package/dist/prompt.js +0 -10
  355. package/dist/store.js +0 -70
  356. package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
  357. package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
  358. package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
  359. package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
  360. package/runtime/node_modules/@metro-labs/discord/src/wire.ts +0 -5
  361. package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
  362. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
  363. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
  364. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -335
  365. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
  366. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -322
  367. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
  368. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
  369. package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
  370. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
  371. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
  372. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
  373. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
  374. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
  375. package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
  376. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
  377. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
  378. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
  379. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
  380. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
  381. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
  382. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
  383. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
  384. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
  385. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
  386. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
  387. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
  388. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
  389. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
  390. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
  391. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +0 -364
  392. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
  393. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
  394. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
  395. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
  396. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
  397. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
  398. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
  399. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
  400. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
  401. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
  402. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
  403. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
  404. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
  405. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
  406. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
  407. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
  408. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
  409. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -3
  410. package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
  411. package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
  412. package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
  413. package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
  414. package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
  415. package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
  416. package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
  417. package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
  418. package/runtime/node_modules/@metro-labs/telegram-user/src/normalize.ts +0 -22
  419. package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
  420. package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
  421. package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
  422. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  423. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
  424. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
  425. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
  426. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
  427. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
  428. /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
  429. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
  430. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
  431. /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
  432. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/api-base.ts +0 -0
  433. /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.ts +0 -0
  434. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
  435. /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
  436. /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
@@ -0,0 +1,273 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdirSync, rmSync } from 'node:fs';
3
+ import { existsSync, readFileSync, statSync } from '../agent-user/agent-fs.js';
4
+ import { join } from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { errMsg, log } from '@metro-labs/core/log';
7
+ import { isRecord } from '@metro-labs/core/is-record';
8
+ import { readJson, writeAtomic, writeJson } from '@metro-labs/core/secure-fs';
9
+ import { agentsDir } from '../agents/files.js';
10
+ import { writeHomeText } from '../agent-user/home-fs.js';
11
+ import { claudeDir } from './files.js';
12
+ import { stagedMarketplaceDir } from './plugin-install.js';
13
+ import { readModelConfig, routedConnection, type ModelConfig } from '../gateway/model-config.js';
14
+
15
+ export const PRIVACY_ENV: Record<string, string> = {
16
+ DISABLE_TELEMETRY: '1',
17
+ DISABLE_ERROR_REPORTING: '1',
18
+ CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1',
19
+ CLAUDE_CODE_GB_DISK_CACHE_WHEN_TELEMETRY_OFF: '1',
20
+ };
21
+ export const RETENTION_DAYS = 7;
22
+ const SKILL_NAME = 'metro-orchestrator';
23
+ const STATE_FILE = 'claude-setup.json';
24
+ const GUIDANCE = 'orchestrator.md';
25
+ const SYSTEM_PROMPT_FILE = 'system-prompt.md';
26
+ export const SYSTEM_PROMPT_MAX = 64 * 1024;
27
+
28
+ const WORKER_AGENT = `---
29
+ name: worker
30
+ description: Default execution agent for delegated work. Full tool access and maximum reasoning effort. Use for any substantive task the orchestrator main thread cannot perform itself - reading and writing code, running commands, research, analysis.
31
+ effort: xhigh
32
+ ---
33
+
34
+ You are a worker agent. The main thread that dispatched you is an orchestrator with no file, shell or network access; it can only delegate and relay messages. That means you are where the actual work happens, and the quality of your output is the quality of the result.
35
+
36
+ Work to completion. Do not hand back a partial answer with a suggestion that someone else finish it; there is no one else. If part of the task is genuinely blocked, complete every other part in full and say plainly what you left undone and why. Before you finish, read your own last paragraph: if it is a plan, a question or a promise about work you have not done, do that work now.
37
+
38
+ Keep the change to what the task asks for. A pre-existing bug, a performance problem or behaviour the task does not mention is a follow-up to report at the end, not something to fix now, unless the requested behaviour cannot work without it. Where the task is ambiguous, build the reading its wording and the surrounding code support best, and state that assumption. Commit tests only where the task asks for them or the repository already keeps tests for that kind of change, sized like the neighbouring test files; scratch checks you used to verify yourself need not be kept. This is about extras only: every behaviour the task does ask for, implement completely.
39
+
40
+ Never call AskUserQuestion or ExitPlanMode. They are blocked by policy, because nobody is watching the terminal around the clock and a blocking prompt stalls indefinitely. When you hit an ambiguity: do everything that does not depend on it, then choose the most reasonable interpretation, state the assumption explicitly in your report, and continue.
41
+
42
+ Your final message is a report to the orchestrator, not a message to a human. It gets relayed onward, so make it self-contained and concise. Lead with the outcome. Include concrete evidence, command output, file paths with line numbers, test results, for anything you claim to have verified. Report failures faithfully: if a test failed, say so and show the output; if you skipped a step, say that too.
43
+ `;
44
+
45
+ export interface SetupDeps {
46
+ dir?: string;
47
+ agents?: string;
48
+ guidance?: string;
49
+ env?: NodeJS.ProcessEnv;
50
+ }
51
+
52
+ export type Placed = 'written' | 'present' | 'updated' | 'missing';
53
+ export type SettingsOutcome = 'written' | 'unchanged' | 'unreadable';
54
+
55
+ export interface SetupReport {
56
+ privacy: boolean;
57
+ guard: 'plugin';
58
+ worker: Placed;
59
+ skill: Placed;
60
+ settings: SettingsOutcome;
61
+ }
62
+
63
+ export interface SetupStatus {
64
+ privacy: boolean;
65
+ permissionMode: PermissionMode;
66
+ systemPrompt: string;
67
+ guard: 'plugin';
68
+ worker: boolean;
69
+ skill: boolean;
70
+ privacyApplied: boolean;
71
+ retentionDays: number | null;
72
+ }
73
+
74
+ const statePath = (agents: string): string => join(agents, STATE_FILE);
75
+
76
+ const PERMISSION_MODES = ['auto', 'bypass'] as const;
77
+ export type PermissionMode = (typeof PERMISSION_MODES)[number];
78
+
79
+ function readState(agents: string): Record<string, unknown> {
80
+ const raw = readJson<unknown>(statePath(agents), null);
81
+ return isRecord(raw) ? raw : {};
82
+ }
83
+
84
+ function writeState(agents: string, patch: Record<string, unknown>): void {
85
+ mkdirSync(agents, { recursive: true });
86
+ writeJson(statePath(agents), { ...readState(agents), ...patch });
87
+ }
88
+
89
+ const privacyEnabled = (agents = agentsDir()): boolean => readState(agents).privacy !== false;
90
+
91
+ export function setPrivacy(enabled: boolean, agents = agentsDir()): void {
92
+ writeState(agents, { privacy: enabled });
93
+ }
94
+
95
+ export const isPermissionMode = (value: unknown): value is PermissionMode => PERMISSION_MODES.some((m) => m === value);
96
+
97
+ export function permissionMode(agents = agentsDir()): PermissionMode {
98
+ const mode = readState(agents).permissionMode;
99
+ return isPermissionMode(mode) ? mode : 'auto';
100
+ }
101
+
102
+ export function setPermissionMode(mode: PermissionMode, agents = agentsDir()): void {
103
+ writeState(agents, { permissionMode: mode });
104
+ }
105
+
106
+ const promptPath = (agents: string): string => join(agents, SYSTEM_PROMPT_FILE);
107
+
108
+ export function systemPrompt(agents = agentsDir()): string {
109
+ const path = promptPath(agents);
110
+ if (!existsSync(path)) return '';
111
+ return readFileSync(path, 'utf8').trim();
112
+ }
113
+
114
+ export function setSystemPrompt(text: string, agents = agentsDir()): void {
115
+ const trimmed = text.trim();
116
+ if (trimmed === '') {
117
+ rmSync(promptPath(agents), { force: true });
118
+ return;
119
+ }
120
+ writeAtomic(promptPath(agents), `${trimmed}\n`, 0o600);
121
+ }
122
+
123
+ function guidancePath(env: NodeJS.ProcessEnv = process.env): string {
124
+ const staged = stagedMarketplaceDir(env);
125
+ if (staged !== null) {
126
+ const path = join(staged, 'plugin', GUIDANCE);
127
+ if (existsSync(path)) return path;
128
+ }
129
+ return fileURLToPath(new URL(`../../../../plugin/${GUIDANCE}`, import.meta.url));
130
+ }
131
+
132
+ const PRIOR_WORKER: ReadonlySet<string> = new Set(['08a0cd8710df285d6512245246bb8b658b7cfc7f3e89490514ee5376779eb2ef']);
133
+ const PRIOR_SKILL: ReadonlySet<string> = new Set([
134
+ '0b3d122ed95beff09d579cf912cd4238e1db524c41fce4b314de57d6ff5908ad',
135
+ '36f4fb57f231a119d717b5e3d6ccb654f94960fcca708683f376679aead8df25',
136
+ '3840bc50253e51f431691376cf29a925b8245d87b77b8b20804a159e378a6e67',
137
+ 'e6b59eba825b4568dfa9ace5049fd9568a2e10d731719976ad2657084fd4e5f9',
138
+ ]);
139
+
140
+ const digest = (text: string): string => createHash('sha256').update(text).digest('hex');
141
+
142
+ export function placeFile(path: string, text: string, prior: ReadonlySet<string> = new Set()): Placed {
143
+ if (!existsSync(path)) {
144
+ writeHomeText(path, text, 0o644);
145
+ return 'written';
146
+ }
147
+ const current = readFileSync(path, 'utf8');
148
+ if (current === text || !prior.has(digest(current))) return 'present';
149
+ writeHomeText(path, text, 0o644);
150
+ return 'updated';
151
+ }
152
+
153
+ function readSettings(path: string): Record<string, unknown> | null {
154
+ if (!existsSync(path)) return {};
155
+ try {
156
+ const parsed: unknown = JSON.parse(readFileSync(path, 'utf8'));
157
+ return isRecord(parsed) ? parsed : null;
158
+ } catch {
159
+ return null;
160
+ }
161
+ }
162
+
163
+ function withPrivacy(settings: Record<string, unknown>, enabled: boolean): Record<string, unknown> {
164
+ const current = isRecord(settings.env) ? settings.env : {};
165
+ if (enabled)
166
+ return { ...settings, env: { ...current, ...PRIVACY_ENV }, cleanupPeriodDays: settings.cleanupPeriodDays ?? RETENTION_DAYS };
167
+ const env = Object.fromEntries(Object.entries(current).filter(([key]) => !(key in PRIVACY_ENV)));
168
+ const rest = Object.fromEntries(Object.entries(settings).filter(([key]) => key !== 'env'));
169
+ return Object.keys(env).length === 0 ? rest : { ...rest, env };
170
+ }
171
+
172
+ function mergeSettings(dir: string, change: (settings: Record<string, unknown>) => Record<string, unknown>): SettingsOutcome {
173
+ const path = join(dir, 'settings.json');
174
+ const current = readSettings(path);
175
+ if (current === null) return 'unreadable';
176
+ const next = change(current);
177
+ if (existsSync(path) && JSON.stringify(next) === JSON.stringify(current)) return 'unchanged';
178
+ writeHomeText(path, `${JSON.stringify(next, null, 2)}\n`, existsSync(path) ? statSync(path).mode & 0o777 : 0o644);
179
+ return 'written';
180
+ }
181
+
182
+ function applyPrivacy(dir: string, enabled: boolean): SettingsOutcome {
183
+ return mergeSettings(dir, (current) => withPrivacy(current, enabled));
184
+ }
185
+
186
+ const skillPath = (dir: string): string => join(dir, 'skills', SKILL_NAME, 'SKILL.md');
187
+ const workerPath = (dir: string): string => join(dir, 'agents', 'worker.md');
188
+
189
+ function placeSkill(dir: string, guidance: string): Placed {
190
+ if (!existsSync(guidance)) return 'missing';
191
+ return placeFile(skillPath(dir), readFileSync(guidance, 'utf8'), PRIOR_SKILL);
192
+ }
193
+
194
+ const written = (placed: Placed): boolean => placed === 'written' || placed === 'updated';
195
+
196
+ export function ensureClaudeSetup(deps: SetupDeps = {}): SetupReport {
197
+ const dir = deps.dir ?? claudeDir();
198
+ const agents = deps.agents ?? agentsDir();
199
+ const privacy = privacyEnabled(agents);
200
+ const report: SetupReport = {
201
+ privacy,
202
+ guard: 'plugin',
203
+ worker: placeFile(workerPath(dir), WORKER_AGENT, PRIOR_WORKER),
204
+ skill: placeSkill(dir, deps.guidance ?? guidancePath(deps.env)),
205
+ settings: applyPrivacy(dir, privacy),
206
+ };
207
+ syncAvailableModelsQuietly({ ...deps, dir, agents });
208
+ if (written(report.worker) || written(report.skill) || report.settings === 'written')
209
+ log.info(report, 'claude-setup: applied the Claude Code setup for a metro box');
210
+ if (report.settings === 'unreadable') log.warn({ path: join(dir, 'settings.json') }, 'claude-setup: settings.json is not valid JSON, so the privacy settings were not written');
211
+ return report;
212
+ }
213
+
214
+ export function claudeSetupStatus(deps: SetupDeps = {}): SetupStatus {
215
+ const dir = deps.dir ?? claudeDir();
216
+ const settings = readSettings(join(dir, 'settings.json'));
217
+ const env = settings !== null && isRecord(settings.env) ? settings.env : {};
218
+ const days = settings?.cleanupPeriodDays;
219
+ return {
220
+ privacy: privacyEnabled(deps.agents ?? agentsDir()),
221
+ permissionMode: permissionMode(deps.agents ?? agentsDir()),
222
+ systemPrompt: systemPrompt(deps.agents ?? agentsDir()),
223
+ guard: 'plugin',
224
+ worker: existsSync(workerPath(dir)),
225
+ skill: existsSync(skillPath(dir)),
226
+ privacyApplied: Object.keys(PRIVACY_ENV).every((key) => env[key] === '1'),
227
+ retentionDays: typeof days === 'number' ? days : null,
228
+ };
229
+ }
230
+
231
+ export function tryClaudeSetup(deps: SetupDeps = {}): void {
232
+ try {
233
+ ensureClaudeSetup(deps);
234
+ } catch (err) {
235
+ log.warn({ err: errMsg(err) }, 'claude-setup: could not apply the Claude Code setup');
236
+ }
237
+ }
238
+
239
+ const MODELS_KEY = 'availableModels';
240
+ const ENFORCE_KEY = 'enforceAvailableModels';
241
+
242
+ export function routeOf(cfg: ModelConfig): string | null {
243
+ const conn = routedConnection(cfg);
244
+ if (conn === null || conn.provider === 'anthropic' || conn.model === '') return null;
245
+ return `${conn.provider}:${conn.model}`;
246
+ }
247
+
248
+ function withAvailableModels(settings: Record<string, unknown>, route: string | null, metroWrote: boolean): Record<string, unknown> {
249
+ if (route !== null) return { ...settings, [MODELS_KEY]: [route], [ENFORCE_KEY]: true };
250
+ if (!metroWrote) return settings;
251
+ return Object.fromEntries(Object.entries(settings).filter(([key]) => key !== MODELS_KEY && key !== ENFORCE_KEY));
252
+ }
253
+
254
+ export function syncAvailableModels(cfg: ModelConfig, deps: SetupDeps = {}): SettingsOutcome {
255
+ const dir = deps.dir ?? claudeDir();
256
+ const agents = deps.agents ?? agentsDir();
257
+ const route = routeOf(cfg);
258
+ const state = readJson<unknown>(statePath(agents), null);
259
+ const metroWrote = isRecord(state) && state.modelsByMetro === true;
260
+ return mergeSettings(dir, (current) => {
261
+ mkdirSync(agents, { recursive: true });
262
+ writeJson(statePath(agents), { ...(isRecord(state) ? state : {}), modelsByMetro: route !== null });
263
+ return withAvailableModels(current, route, metroWrote);
264
+ });
265
+ }
266
+
267
+ export function syncAvailableModelsQuietly(deps: SetupDeps = {}, cfg?: ModelConfig): void {
268
+ try {
269
+ syncAvailableModels(cfg ?? readModelConfig(deps.agents ?? agentsDir()), deps);
270
+ } catch (err) {
271
+ log.warn({ err: errMsg(err) }, 'claude-setup: could not sync the model allowlist');
272
+ }
273
+ }
@@ -0,0 +1,120 @@
1
+ import { existsSync, readdirNames, readFileSync, statSync } from '../agent-user/agent-fs.js';
2
+ import { basename, dirname, join } from 'node:path';
3
+ import { ApiError } from '@metro-labs/http/api-error';
4
+ import { claudeDir } from './files.js';
5
+ import { removeHome, writeHomeText } from '../agent-user/home-fs.js';
6
+
7
+ const SKILL_MAX = 256 * 1024;
8
+ const SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
9
+ const FILE = 'SKILL.md';
10
+ const DEFAULT_MODE = 0o644;
11
+ const SUMMARY_MAX = 300;
12
+
13
+ export interface ClaudeSkill {
14
+ id: string;
15
+ name: string;
16
+ title: string;
17
+ description: string;
18
+ path: string;
19
+ editable: boolean;
20
+ updatedAt: string | null;
21
+ }
22
+
23
+ const frontmatterField = (text: string, field: string): string => {
24
+ const head = text.startsWith('---') ? text.slice(3, text.indexOf('\n---', 3)) : '';
25
+ for (const line of head.split('\n')) {
26
+ const at = line.indexOf(':');
27
+ if (at > 0 && line.slice(0, at).trim() === field) return line.slice(at + 1).trim().replace(/^["']|["']$/g, '').slice(0, SUMMARY_MAX);
28
+ }
29
+ return '';
30
+ };
31
+
32
+ function entryOf(name: string, path: string): ClaudeSkill {
33
+ const stat = statSync(path);
34
+ const editable = stat.size <= SKILL_MAX;
35
+ const text = editable ? readFileSync(path, 'utf8') : '';
36
+ return {
37
+ id: `user:${name}`,
38
+ name,
39
+ title: frontmatterField(text, 'name') || name,
40
+ description: frontmatterField(text, 'description'),
41
+ path,
42
+ editable,
43
+ updatedAt: stat.mtime.toISOString(),
44
+ };
45
+ }
46
+
47
+ const userSkillsRoot = (dir: string): string => join(dir, 'skills');
48
+
49
+ function isFolder(path: string): boolean {
50
+ try {
51
+ return statSync(path).isDirectory();
52
+ } catch {
53
+ return false;
54
+ }
55
+ }
56
+
57
+ export function listClaudeSkills(dir = claudeDir()): ClaudeSkill[] {
58
+ const root = userSkillsRoot(dir);
59
+ if (!existsSync(root)) return [];
60
+ const out: ClaudeSkill[] = [];
61
+ for (const name of readdirNames(root)) {
62
+ if (!SKILL_NAME_RE.test(name) || !isFolder(join(root, name))) continue;
63
+ const path = join(root, name, FILE);
64
+ if (existsSync(path)) out.push(entryOf(name, path));
65
+ }
66
+ return out.sort((a, b) => (b.updatedAt ?? '').localeCompare(a.updatedAt ?? '') || a.name.localeCompare(b.name));
67
+ }
68
+
69
+ function found(id: string, dir: string): ClaudeSkill {
70
+ const skill = listClaudeSkills(dir).find((entry) => entry.id === id);
71
+ if (skill === undefined) throw new ApiError('no such skill', 404);
72
+ return skill;
73
+ }
74
+
75
+ export function readClaudeSkill(id: string, dir = claudeDir()): ClaudeSkill & { text: string } {
76
+ const skill = found(id, dir);
77
+ return { ...skill, text: skill.editable ? readFileSync(skill.path, 'utf8') : '' };
78
+ }
79
+
80
+ function assertText(text: string): void {
81
+ if (text.length > SKILL_MAX) throw new ApiError('that is more text than a skill may hold', 413);
82
+ if (text.trim() === '') throw new ApiError('a skill needs some text', 400);
83
+ }
84
+
85
+ export function writeClaudeSkill(
86
+ id: string,
87
+ text: string,
88
+ seenAt: string | null | undefined,
89
+ dir = claudeDir(),
90
+ ): ClaudeSkill {
91
+ const skill = found(id, dir);
92
+ if (!skill.editable) throw new ApiError('that skill is too large to edit here', 409);
93
+ assertText(text);
94
+ if (seenAt !== undefined && seenAt !== skill.updatedAt)
95
+ throw new ApiError('that skill changed on disk since you opened it; reload it before saving', 409);
96
+ writeHomeText(skill.path, text, DEFAULT_MODE);
97
+ return entryOf(skill.name, skill.path);
98
+ }
99
+
100
+ const skillTemplate = (name: string): string =>
101
+ ['---', `name: ${name}`, 'description: what this skill does, and when Claude should reach for it', '---', '', `# ${name}`, '', 'Write the instructions here.', ''].join('\n');
102
+
103
+ export function createClaudeSkill(name: string, text: string | undefined, dir = claudeDir()): ClaudeSkill {
104
+ if (typeof name !== 'string' || !SKILL_NAME_RE.test(name))
105
+ throw new ApiError('a skill name is lowercase letters, digits and dashes, up to 64 characters', 400);
106
+ const path = join(userSkillsRoot(dir), name, FILE);
107
+ if (existsSync(path)) throw new ApiError('a skill by that name already lives there', 409);
108
+ const body = text === undefined || text.trim() === '' ? skillTemplate(name) : text;
109
+ assertText(body);
110
+ writeHomeText(path, body, DEFAULT_MODE);
111
+ return entryOf(name, path);
112
+ }
113
+
114
+ export function deleteClaudeSkill(id: string, dir = claudeDir()): string {
115
+ const skill = found(id, dir);
116
+ const folder = dirname(skill.path);
117
+ if (basename(folder) !== skill.name) throw new ApiError('that skill does not live in its own folder', 409);
118
+ removeHome(folder, true);
119
+ return id;
120
+ }
@@ -0,0 +1,113 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { ApiError } from '@metro-labs/http/api-error';
5
+ import { errMsg, log } from '@metro-labs/core/log';
6
+ import { isRecord } from '@metro-labs/core/is-record';
7
+ import { sessionRunning, stopSession, type SessionDeps } from './session.js';
8
+ import { agentUser, asAgent, claudeBin } from '../agent-user/user.js';
9
+
10
+ const LATEST_URL = 'https://registry.npmjs.org/@anthropic-ai/claude-code/latest';
11
+ const VERSION_RE = /(\d+)\.(\d+)\.(\d+)/;
12
+ const CACHE_MS = 10 * 60_000;
13
+ const INSTALL_TIMEOUT_MS = 5 * 60_000;
14
+ const TAIL = 400;
15
+
16
+ export interface VersionDeps {
17
+ claude?: string;
18
+ fetchImpl?: typeof fetch;
19
+ session?: SessionDeps;
20
+ now?: () => number;
21
+ }
22
+
23
+ export interface ClaudeVersion {
24
+ installed: string | null;
25
+ latest: string | null;
26
+ newer: boolean;
27
+ }
28
+
29
+ let cached: { latest: string | null; at: number } | null = null;
30
+
31
+ function candidates(deps: VersionDeps): string[] {
32
+ if (deps.claude !== undefined) return [deps.claude];
33
+ return agentUser() === null ? ['claude', join(homedir(), '.local', 'bin', 'claude')] : [claudeBin()];
34
+ }
35
+
36
+ const parse = (text: string): string | null => {
37
+ const m = VERSION_RE.exec(text);
38
+ return m === null ? null : `${m[1] ?? ''}.${m[2] ?? ''}.${m[3] ?? ''}`;
39
+ };
40
+
41
+ function probe(bin: string): string | null {
42
+ const run = spawnSync(...asAgent(bin, ['--version']), { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
43
+ return run.error === undefined && run.status === 0 && typeof run.stdout === 'string' ? parse(run.stdout) : null;
44
+ }
45
+
46
+ function located(deps: VersionDeps): { bin: string; installed: string } | null {
47
+ for (const bin of candidates(deps)) {
48
+ const installed = probe(bin);
49
+ if (installed !== null) return { bin, installed };
50
+ }
51
+ return null;
52
+ }
53
+
54
+ export function newerThan(a: string, b: string): boolean {
55
+ const left = a.split('.').map(Number);
56
+ const right = b.split('.').map(Number);
57
+ for (let i = 0; i < 3; i += 1) {
58
+ const d = (left[i] ?? 0) - (right[i] ?? 0);
59
+ if (d !== 0) return d > 0;
60
+ }
61
+ return false;
62
+ }
63
+
64
+ async function latestClaudeVersion(deps: VersionDeps = {}): Promise<string | null> {
65
+ const now = (deps.now ?? Date.now)();
66
+ if (cached !== null && now - cached.at < CACHE_MS) return cached.latest;
67
+ const fetchImpl = deps.fetchImpl ?? fetch;
68
+ let latest: string | null = null;
69
+ try {
70
+ const res = await fetchImpl(LATEST_URL, { headers: { accept: 'application/json' }, redirect: 'manual' });
71
+ const body: unknown = res.ok ? await res.json() : null;
72
+ latest = isRecord(body) && typeof body.version === 'string' ? parse(body.version) : null;
73
+ } catch (err) {
74
+ log.warn({ err: errMsg(err) }, 'claude-version: could not read the latest version from npm');
75
+ }
76
+ cached = { latest, at: now };
77
+ return latest;
78
+ }
79
+
80
+ export function forgetClaudeVersion(): void {
81
+ cached = null;
82
+ }
83
+
84
+ export async function claudeVersion(deps: VersionDeps = {}): Promise<ClaudeVersion> {
85
+ const installed = located(deps)?.installed ?? null;
86
+ const latest = await latestClaudeVersion(deps);
87
+ return { installed, latest, newer: installed !== null && latest !== null && newerThan(latest, installed) };
88
+ }
89
+
90
+ function install(bin: string): void {
91
+ const run = spawnSync(...asAgent(bin, ['install', 'latest']), { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'], timeout: INSTALL_TIMEOUT_MS });
92
+ if (run.error !== undefined) throw new ApiError(`claude install latest failed: ${errMsg(run.error)}`, 502);
93
+ if (run.status === 0) return;
94
+ const said = `${run.stderr ?? ''}${run.stdout ?? ''}`.trim().slice(-TAIL);
95
+ throw new ApiError(`claude install latest failed: ${said}`, 502);
96
+ }
97
+
98
+ function restartIfChanged(changed: boolean, session: SessionDeps): boolean {
99
+ if (!changed || !sessionRunning(session.tmux ?? 'tmux')) return false;
100
+ stopSession(session);
101
+ return true;
102
+ }
103
+
104
+ export async function updateClaude(deps: VersionDeps = {}): Promise<ClaudeVersion & { restarted: boolean }> {
105
+ const found = located(deps);
106
+ if (found === null) throw new ApiError('Claude Code is not installed on this machine', 404);
107
+ install(found.bin);
108
+ forgetClaudeVersion();
109
+ const after = await claudeVersion(deps);
110
+ const restarted = restartIfChanged(after.installed !== null && after.installed !== found.installed, deps.session ?? {});
111
+ log.info({ from: found.installed, to: after.installed, restarted }, 'claude-version: Claude Code updated');
112
+ return { ...after, restarted };
113
+ }