@stage-labs/metro 0.1.0-beta.8 → 0.1.0-beta.80

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 (282) hide show
  1. package/dist/api.js +14 -0
  2. package/dist/claude-settings.js +40 -0
  3. package/dist/claude.js +107 -0
  4. package/dist/cli.js +101 -0
  5. package/dist/control.js +74 -0
  6. package/dist/hold.js +211 -0
  7. package/dist/local.js +93 -0
  8. package/dist/node-name.js +71 -0
  9. package/dist/plugin.js +80 -0
  10. package/dist/provider-flags.js +9 -0
  11. package/dist/runtime-install.js +87 -0
  12. package/dist/runtime.js +74 -0
  13. package/dist/serve.js +146 -0
  14. package/dist/service.js +242 -0
  15. package/dist/tail.js +119 -0
  16. package/dist/update.js +45 -0
  17. package/dist/version.js +97 -0
  18. package/package.json +14 -25
  19. package/runtime/node_modules/@metro-labs/core/package.json +43 -0
  20. package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
  21. package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
  22. package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
  23. package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
  24. package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
  25. package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
  26. package/runtime/node_modules/@metro-labs/core/src/lines.ts +82 -0
  27. package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
  28. package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
  29. package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
  30. package/runtime/node_modules/@metro-labs/core/src/station-names.ts +12 -0
  31. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
  32. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
  33. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
  34. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
  35. package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +107 -0
  36. package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
  37. package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
  38. package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
  39. package/runtime/node_modules/@metro-labs/daemon/package.json +47 -0
  40. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
  41. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +311 -0
  42. package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
  43. package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +302 -0
  44. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +111 -0
  45. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +329 -0
  46. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
  47. package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
  48. package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +84 -0
  49. package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
  50. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +176 -0
  51. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
  52. package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
  53. package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
  54. package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
  55. package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
  56. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
  57. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
  58. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
  59. package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
  60. package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
  61. package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
  62. package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +148 -0
  63. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +329 -0
  64. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +167 -0
  65. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
  66. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +307 -0
  67. package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
  68. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +144 -0
  69. package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
  70. package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +69 -0
  71. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +159 -0
  72. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +117 -0
  73. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
  74. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +157 -0
  75. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +127 -0
  76. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
  77. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +295 -0
  78. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +277 -0
  79. package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
  80. package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
  81. package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +330 -0
  82. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
  83. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
  84. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
  85. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
  86. package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +252 -0
  88. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
  89. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
  90. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
  91. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
  92. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +220 -0
  93. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
  94. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +182 -0
  95. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +197 -0
  96. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +195 -0
  97. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +198 -0
  98. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +40 -0
  99. package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
  100. package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +137 -0
  101. package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
  102. package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
  103. package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
  104. package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
  105. package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
  106. package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
  107. package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
  108. package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
  109. package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
  110. package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
  111. package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
  112. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
  113. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
  114. package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
  115. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
  116. package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
  117. package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
  118. package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
  119. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
  120. package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +390 -0
  121. package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
  122. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +187 -0
  123. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
  124. package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
  125. package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
  126. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
  127. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
  128. package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
  129. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
  130. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
  131. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
  132. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
  133. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
  134. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +184 -0
  135. package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +248 -0
  136. package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +35 -0
  137. package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
  138. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
  139. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
  140. package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
  141. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +71 -0
  142. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
  143. package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
  144. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  145. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  146. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  147. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  148. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  149. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
  150. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  151. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  152. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  153. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  154. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  155. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  156. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  157. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  158. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  159. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  160. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  161. package/runtime/node_modules/@metro-labs/http/package.json +33 -0
  162. package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
  163. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
  164. package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
  165. package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
  166. package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
  167. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
  168. package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
  169. package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
  170. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  171. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  172. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  173. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  174. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  175. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
  176. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  177. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  178. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  179. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  180. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  181. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  182. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  183. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  184. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  185. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  186. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  187. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  188. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  189. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  190. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  191. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  192. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
  193. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
  194. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  195. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  196. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  197. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
  198. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  199. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  200. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  201. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  202. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  203. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  204. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  205. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  206. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  207. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  208. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +327 -0
  209. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  210. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
  211. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  212. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  213. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  214. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  215. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  216. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  217. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  218. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  219. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
  220. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  221. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  222. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  223. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  224. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  225. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  226. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  227. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  228. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  229. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  230. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  231. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  232. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  233. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  234. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  235. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
  236. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
  237. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  238. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  239. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  240. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  241. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  242. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  243. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  244. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  245. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  246. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  247. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  248. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  249. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
  250. package/runtime/runtime.json +3 -0
  251. package/runtime/server.ts +1 -0
  252. package/runtime/stations.json +36 -0
  253. package/runtime/trains/.keep +0 -0
  254. package/LICENSE +0 -21
  255. package/README.md +0 -249
  256. package/dist/cache.js +0 -75
  257. package/dist/cli/actions.js +0 -139
  258. package/dist/cli/config.js +0 -185
  259. package/dist/cli/index.js +0 -177
  260. package/dist/cli/skill.js +0 -62
  261. package/dist/cli/util.js +0 -72
  262. package/dist/cli/webhook.js +0 -83
  263. package/dist/codex-rc.js +0 -245
  264. package/dist/dispatcher.js +0 -122
  265. package/dist/history.js +0 -99
  266. package/dist/ipc.js +0 -72
  267. package/dist/log.js +0 -15
  268. package/dist/paths.js +0 -68
  269. package/dist/registry.js +0 -48
  270. package/dist/stations/claude.js +0 -45
  271. package/dist/stations/codex.js +0 -68
  272. package/dist/stations/discord.js +0 -200
  273. package/dist/stations/index.js +0 -133
  274. package/dist/stations/telegram-md.js +0 -34
  275. package/dist/stations/telegram-upload.js +0 -113
  276. package/dist/stations/telegram.js +0 -213
  277. package/dist/stations/webhook.js +0 -100
  278. package/dist/tunnel.js +0 -49
  279. package/dist/webhooks.js +0 -40
  280. package/docs/agents.md +0 -216
  281. package/docs/uri-scheme.md +0 -125
  282. package/skills/metro/SKILL.md +0 -249
@@ -0,0 +1,69 @@
1
+ import type { ConnectorAuth, VerifiedRecord } from './verify.js';
2
+ import { readConfig, signInState, type ConnectorSignIn } from './config.js';
3
+ import type { ConnectorTransport } from '@metro-labs/core/station-names';
4
+
5
+ export interface Connector {
6
+ id: string;
7
+ name: string;
8
+ url: string;
9
+ transport: ConnectorTransport;
10
+ auth: ConnectorAuth['kind'];
11
+ header: string | null;
12
+ secret: string | null;
13
+ bearer: string | null;
14
+ expiresAt: number | null;
15
+ signIn: ConnectorSignIn;
16
+ verified: VerifiedRecord;
17
+ }
18
+
19
+ export interface ConnectorInput {
20
+ name: unknown;
21
+ url: unknown;
22
+ header: unknown;
23
+ value: unknown;
24
+ }
25
+
26
+ export type ConnectorCheck =
27
+ | { id: string; name: string; ok: true; verified: VerifiedRecord }
28
+ | { id: string; name: string; ok: false; reason: string };
29
+
30
+ export interface DeletedConnector {
31
+ id: string;
32
+ name: string;
33
+ }
34
+
35
+ export interface ConnectorLike {
36
+ id: string;
37
+ name: string;
38
+ url: string;
39
+ transport: ConnectorTransport;
40
+ config: unknown;
41
+ }
42
+
43
+ export function connectorFromRow(row: ConnectorLike): Connector {
44
+ const config = readConfig(row.config);
45
+ const auth = config.auth;
46
+ return {
47
+ id: row.id,
48
+ name: row.name,
49
+ url: row.url,
50
+ transport: row.transport,
51
+ auth: auth.kind,
52
+ header: auth.kind === 'header' ? auth.name : null,
53
+ secret: auth.kind === 'header' ? auth.value : null,
54
+ bearer: auth.kind === 'oauth' ? auth.accessToken : null,
55
+ expiresAt: auth.kind === 'oauth' ? (auth.expiresAt ?? null) : null,
56
+ signIn: signInState(config),
57
+ verified: config.verified,
58
+ };
59
+ }
60
+
61
+ export const UNVERIFIED = {
62
+ at: '',
63
+ server: '',
64
+ version: '',
65
+ protocol: '',
66
+ icon: '',
67
+ tools: 0,
68
+ catalog: [],
69
+ };
@@ -0,0 +1,159 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import {
3
+ ConnectorVerifyError,
4
+ type OAuthTokens,
5
+ } from './verify.js';
6
+ import type { OAuthServer } from './oauth-discovery.js';
7
+ import { isRecord } from '@metro-labs/core/is-record';
8
+
9
+ const TOKEN_TIMEOUT_MS = 15_000;
10
+ const CLIENT_NAME = 'Metro';
11
+
12
+ export interface OAuthClient {
13
+ clientId: string;
14
+ clientSecret?: string;
15
+ }
16
+
17
+ function refused(message: string): ConnectorVerifyError {
18
+ return new ConnectorVerifyError(message, 400);
19
+ }
20
+
21
+ export function newVerifier(): string {
22
+ return randomBytes(32).toString('base64url');
23
+ }
24
+
25
+ export function challengeOf(verifier: string): string {
26
+ return createHash('sha256').update(verifier).digest('base64url');
27
+ }
28
+
29
+ export async function registerClient(
30
+ server: OAuthServer,
31
+ redirectUri: string,
32
+ ): Promise<OAuthClient> {
33
+ if (server.registrationEndpoint === null)
34
+ throw refused(
35
+ 'That server requires OAuth but does not accept client registration, so Metro cannot sign in to it.',
36
+ );
37
+ let res: Response;
38
+ try {
39
+ res = await fetch(server.registrationEndpoint, {
40
+ method: 'POST',
41
+ redirect: 'manual',
42
+ signal: AbortSignal.timeout(TOKEN_TIMEOUT_MS),
43
+ headers: { 'content-type': 'application/json', accept: 'application/json' },
44
+ body: JSON.stringify({
45
+ client_name: CLIENT_NAME,
46
+ redirect_uris: [redirectUri],
47
+ grant_types: ['authorization_code', 'refresh_token'],
48
+ response_types: ['code'],
49
+ token_endpoint_auth_method: 'none',
50
+ }),
51
+ });
52
+ } catch {
53
+ throw refused('Metro could not reach that server to register.');
54
+ }
55
+ const body: unknown = await res.json().catch(() => null);
56
+ if (!res.ok || !isRecord(body) || typeof body.client_id !== 'string')
57
+ throw refused('That server refused to register Metro as a client.');
58
+ const secret = body.client_secret;
59
+ return {
60
+ clientId: body.client_id,
61
+ ...(typeof secret === 'string' ? { clientSecret: secret } : {}),
62
+ };
63
+ }
64
+
65
+ export interface AuthorizeInput {
66
+ server: OAuthServer;
67
+ client: OAuthClient;
68
+ redirectUri: string;
69
+ state: string;
70
+ verifier: string;
71
+ resource: string;
72
+ }
73
+
74
+ export function authorizeUrl(input: AuthorizeInput): string {
75
+ const url = new URL(input.server.authorizationEndpoint);
76
+ url.searchParams.set('response_type', 'code');
77
+ url.searchParams.set('client_id', input.client.clientId);
78
+ url.searchParams.set('redirect_uri', input.redirectUri);
79
+ url.searchParams.set('state', input.state);
80
+ url.searchParams.set('code_challenge', challengeOf(input.verifier));
81
+ url.searchParams.set('code_challenge_method', 'S256');
82
+ url.searchParams.set('resource', input.resource);
83
+ return url.toString();
84
+ }
85
+
86
+ function tokensFrom(body: Record<string, unknown>): OAuthTokens {
87
+ const access = body.access_token;
88
+ if (typeof access !== 'string' || access === '')
89
+ throw refused('That server did not return an access token.');
90
+ const refresh = body.refresh_token;
91
+ const expires = body.expires_in;
92
+ return {
93
+ accessToken: access,
94
+ ...(typeof refresh === 'string' ? { refreshToken: refresh } : {}),
95
+ ...(typeof expires === 'number'
96
+ ? { expiresAt: Date.now() + expires * 1000 }
97
+ : {}),
98
+ };
99
+ }
100
+
101
+ async function postToken(
102
+ server: OAuthServer,
103
+ client: OAuthClient,
104
+ form: Record<string, string>,
105
+ ): Promise<OAuthTokens> {
106
+ const params = new URLSearchParams({
107
+ ...form,
108
+ client_id: client.clientId,
109
+ ...(client.clientSecret === undefined
110
+ ? {}
111
+ : { client_secret: client.clientSecret }),
112
+ });
113
+ let res: Response;
114
+ try {
115
+ res = await fetch(server.tokenEndpoint, {
116
+ method: 'POST',
117
+ redirect: 'manual',
118
+ signal: AbortSignal.timeout(TOKEN_TIMEOUT_MS),
119
+ headers: {
120
+ 'content-type': 'application/x-www-form-urlencoded',
121
+ accept: 'application/json',
122
+ },
123
+ body: params.toString(),
124
+ });
125
+ } catch {
126
+ throw refused('Metro could not reach that server to finish signing in.');
127
+ }
128
+ const body: unknown = await res.json().catch(() => null);
129
+ if (!res.ok || !isRecord(body))
130
+ throw refused('That server refused to complete the sign-in.');
131
+ return tokensFrom(body);
132
+ }
133
+
134
+ export function exchangeCode(
135
+ server: OAuthServer,
136
+ client: OAuthClient,
137
+ input: { code: string; redirectUri: string; verifier: string; resource: string },
138
+ ): Promise<OAuthTokens> {
139
+ return postToken(server, client, {
140
+ grant_type: 'authorization_code',
141
+ code: input.code,
142
+ redirect_uri: input.redirectUri,
143
+ code_verifier: input.verifier,
144
+ resource: input.resource,
145
+ });
146
+ }
147
+
148
+ export function refreshTokens(
149
+ server: OAuthServer,
150
+ client: OAuthClient,
151
+ refreshToken: string,
152
+ resource: string,
153
+ ): Promise<OAuthTokens> {
154
+ return postToken(server, client, {
155
+ grant_type: 'refresh_token',
156
+ refresh_token: refreshToken,
157
+ resource,
158
+ });
159
+ }
@@ -0,0 +1,117 @@
1
+ import { ConnectorVerifyError, parseConnectorUrl } from './verify.js';
2
+ import { isRecord } from '@metro-labs/core/is-record';
3
+
4
+ const DISCOVERY_TIMEOUT_MS = 10_000;
5
+
6
+ export interface OAuthServer {
7
+ issuer: string;
8
+ authorizationEndpoint: string;
9
+ tokenEndpoint: string;
10
+ registrationEndpoint: string | null;
11
+ supportsS256: boolean;
12
+ }
13
+
14
+ function refused(message: string): ConnectorVerifyError {
15
+ return new ConnectorVerifyError(message, 400);
16
+ }
17
+
18
+ const str = (value: unknown): string =>
19
+ typeof value === 'string' ? value : '';
20
+
21
+ async function getJson(url: URL): Promise<Record<string, unknown> | null> {
22
+ let res: Response;
23
+ try {
24
+ res = await fetch(url, {
25
+ redirect: 'manual',
26
+ signal: AbortSignal.timeout(DISCOVERY_TIMEOUT_MS),
27
+ headers: { accept: 'application/json' },
28
+ });
29
+ } catch {
30
+ return null;
31
+ }
32
+ if (!res.ok) {
33
+ await res.body?.cancel().catch(() => undefined);
34
+ return null;
35
+ }
36
+ const body: unknown = await res.json().catch(() => null);
37
+ return isRecord(body) ? body : null;
38
+ }
39
+
40
+ function wellKnown(base: URL, suffix: string): URL {
41
+ const url = new URL(base.toString());
42
+ url.pathname = `/.well-known/${suffix}`;
43
+ url.search = '';
44
+ url.hash = '';
45
+ return url;
46
+ }
47
+
48
+ export function resourceMetadataUrls(resource: URL): URL[] {
49
+ const path = resource.pathname.replace(/\/$/, '');
50
+ const urls = [wellKnown(resource, 'oauth-protected-resource')];
51
+ if (path !== '')
52
+ urls.unshift(
53
+ new URL(`/.well-known/oauth-protected-resource${path}`, resource.origin),
54
+ );
55
+ return urls;
56
+ }
57
+
58
+ export async function advertisesOAuth(resource: URL): Promise<boolean> {
59
+ for (const candidate of resourceMetadataUrls(resource)) {
60
+ const body = await getJson(candidate);
61
+ const servers: unknown = body?.authorization_servers;
62
+ const first: unknown = Array.isArray(servers) ? servers[0] : undefined;
63
+ if (typeof first === 'string' && first !== '') return true;
64
+ }
65
+ return false;
66
+ }
67
+
68
+ async function authServerFor(resource: URL): Promise<URL> {
69
+ for (const candidate of resourceMetadataUrls(resource)) {
70
+ const body = await getJson(candidate);
71
+ const servers: unknown = body?.authorization_servers;
72
+ const first: unknown = Array.isArray(servers) ? servers[0] : undefined;
73
+ if (typeof first === 'string' && first !== '')
74
+ return parseConnectorUrl(first);
75
+ }
76
+ return parseConnectorUrl(resource.origin);
77
+ }
78
+
79
+ function toServer(body: Record<string, unknown>, issuer: URL): OAuthServer {
80
+ const authorizationEndpoint = str(body.authorization_endpoint);
81
+ const tokenEndpoint = str(body.token_endpoint);
82
+ if (authorizationEndpoint === '' || tokenEndpoint === '')
83
+ throw refused(
84
+ `${issuer.hostname} advertises OAuth but not where to sign in.`,
85
+ );
86
+ const methods = body.code_challenge_methods_supported;
87
+ const registration = str(body.registration_endpoint);
88
+ return {
89
+ issuer: str(body.issuer) === '' ? issuer.origin : str(body.issuer),
90
+ authorizationEndpoint,
91
+ tokenEndpoint,
92
+ registrationEndpoint: registration === '' ? null : registration,
93
+ supportsS256: Array.isArray(methods) ? methods.includes('S256') : false,
94
+ };
95
+ }
96
+
97
+ export function authServerMetadataUrls(issuer: URL): URL[] {
98
+ const path = issuer.pathname.replace(/\/$/, '');
99
+ const suffixes = ['oauth-authorization-server', 'openid-configuration'];
100
+ const urls = suffixes.map((suffix) => wellKnown(issuer, suffix));
101
+ if (path === '') return urls;
102
+ const aware = suffixes.map(
103
+ (suffix) => new URL(`/.well-known/${suffix}${path}`, issuer.origin),
104
+ );
105
+ return [...aware, new URL(`${path}/.well-known/openid-configuration`, issuer.origin), ...urls];
106
+ }
107
+
108
+ export async function discoverOAuth(resource: URL): Promise<OAuthServer> {
109
+ const issuer = await authServerFor(resource);
110
+ for (const candidate of authServerMetadataUrls(issuer)) {
111
+ const body = await getJson(candidate);
112
+ if (body !== null) return toServer(body, issuer);
113
+ }
114
+ throw refused(
115
+ `${resource.hostname} needs authorization, but publishes no OAuth metadata Metro can use.`,
116
+ );
117
+ }
@@ -0,0 +1,65 @@
1
+ import { randomBytes } from 'node:crypto';
2
+ import type { OAuthClient } from './oauth-client.js';
3
+ import type { OAuthServer } from './oauth-discovery.js';
4
+
5
+ const TTL_MS = 10 * 60_000;
6
+ const SWEEP_MS = 60_000;
7
+ const MAX_PENDING = 100;
8
+
9
+ export interface PendingAuth {
10
+ subject: string;
11
+ name: string;
12
+ url: string;
13
+ resource: string;
14
+ returnTo: string;
15
+ verifier: string;
16
+ server: OAuthServer;
17
+ client: OAuthClient;
18
+ connectorId: string;
19
+ }
20
+
21
+ interface Entry extends PendingAuth {
22
+ expiresAt: number;
23
+ }
24
+
25
+ const pending = new Map<string, Entry>();
26
+ let sweeper: ReturnType<typeof setInterval> | null = null;
27
+
28
+ function sweep(now = Date.now()): void {
29
+ for (const [state, entry] of pending)
30
+ if (entry.expiresAt <= now) pending.delete(state);
31
+ }
32
+
33
+ function ensureSweeper(): void {
34
+ if (sweeper !== null) return;
35
+ sweeper = setInterval(sweep, SWEEP_MS);
36
+ sweeper.unref?.();
37
+ }
38
+
39
+ export function startPending(entry: PendingAuth, now = Date.now()): string {
40
+ sweep(now);
41
+ while (pending.size >= MAX_PENDING) {
42
+ const oldest = pending.keys().next();
43
+ if (oldest.done) break;
44
+ pending.delete(oldest.value);
45
+ }
46
+ const state = randomBytes(32).toString('base64url');
47
+ pending.set(state, { ...entry, expiresAt: now + TTL_MS });
48
+ ensureSweeper();
49
+ return state;
50
+ }
51
+
52
+ export function takePending(
53
+ state: string,
54
+ now = Date.now(),
55
+ ): PendingAuth | undefined {
56
+ const entry = pending.get(state);
57
+ if (entry === undefined) return undefined;
58
+ pending.delete(state);
59
+ return entry.expiresAt > now ? entry : undefined;
60
+ }
61
+
62
+ export function pendingCount(): number {
63
+ sweep();
64
+ return pending.size;
65
+ }
@@ -0,0 +1,157 @@
1
+ import type { IncomingMessage, ServerResponse } from 'node:http';
2
+ import { errMsg, log } from '@metro-labs/core/log';
3
+ import { bodyField, readJsonBody, sendJson, type ApiSession } from '@metro-labs/http/api-http';
4
+ import {
5
+ beginOAuth,
6
+ completeOAuth,
7
+ takePending,
8
+ type PendingAuth,
9
+ } from './oauth.js';
10
+ import { parseConnectorUrl } from './verify.js';
11
+ import type { OAuthAuth } from './verify.js';
12
+ import type { Connector } from './model.js';
13
+
14
+ const asText = (value: unknown): string =>
15
+ typeof value === 'string' ? value : '';
16
+
17
+ export function hostOf(url: string): string {
18
+ try {
19
+ return new URL(url).hostname;
20
+ } catch {
21
+ return '';
22
+ }
23
+ }
24
+
25
+ export interface OAuthRouteDeps {
26
+ createPendingConnector: (
27
+ subject: string,
28
+ project: string,
29
+ input: { name: unknown; url: unknown },
30
+ ) => Promise<Connector>;
31
+ reconnectConnector: (
32
+ subject: string,
33
+ id: string,
34
+ auth: OAuthAuth,
35
+ ) => Promise<Connector>;
36
+ getConnector: (subject: string, id: string) => Promise<Connector>;
37
+ }
38
+
39
+ export async function startOAuth(
40
+ req: IncomingMessage,
41
+ res: ServerResponse,
42
+ deps: OAuthRouteDeps,
43
+ session: ApiSession,
44
+ project: string,
45
+ body: unknown,
46
+ payload: (row: Connector) => Record<string, unknown>,
47
+ ): Promise<void> {
48
+ const url = parseConnectorUrl(bodyField(body, 'url'));
49
+ const row = await deps.createPendingConnector(session.subject, project, {
50
+ name: bodyField(body, 'name'),
51
+ url: bodyField(body, 'url'),
52
+ });
53
+ const authorize = await beginOAuth({
54
+ subject: session.subject,
55
+ name: row.name,
56
+ url,
57
+ returnTo: asText(bodyField(body, 'returnTo')),
58
+ connectorId: row.id,
59
+ });
60
+ log.info(
61
+ { id: row.id, host: url.hostname },
62
+ 'connector-api: stored unconnected, sending the user to sign in',
63
+ );
64
+ sendJson(req, res, 201, { ...payload(row), authorizeUrl: authorize });
65
+ }
66
+
67
+ export async function handleConnect(
68
+ req: IncomingMessage,
69
+ res: ServerResponse,
70
+ deps: OAuthRouteDeps,
71
+ session: ApiSession,
72
+ id: string,
73
+ ): Promise<void> {
74
+ const row = await deps.getConnector(session.subject, id);
75
+ const url = parseConnectorUrl(row.url);
76
+ const body = await readJsonBody(req);
77
+ const authorize = await beginOAuth({
78
+ subject: session.subject,
79
+ name: row.name,
80
+ url,
81
+ returnTo: asText(bodyField(body, 'returnTo')),
82
+ connectorId: row.id,
83
+ });
84
+ log.info(
85
+ { id: row.id, host: url.hostname },
86
+ 'connector-api: sending the user back to sign in',
87
+ );
88
+ sendJson(req, res, 202, { status: 'oauth', authorizeUrl: authorize });
89
+ }
90
+
91
+ function landing(entry: PendingAuth, id?: string): string {
92
+ const target = id ?? entry.connectorId;
93
+ return target === undefined ? '#/connectors' : `#/connector/${target}`;
94
+ }
95
+
96
+ function backTo(entry: PendingAuth, error?: string, id?: string): string {
97
+ const suffix =
98
+ error === undefined
99
+ ? ''
100
+ : `?connector_error=${encodeURIComponent(error)}`;
101
+ return `${entry.returnTo}${suffix}${landing(entry, id)}`;
102
+ }
103
+
104
+ function redirect(res: ServerResponse, location: string): void {
105
+ res.writeHead(302, { location, 'cache-control': 'no-store' }).end();
106
+ }
107
+
108
+ async function saveOAuth(
109
+ deps: OAuthRouteDeps,
110
+ entry: PendingAuth,
111
+ auth: OAuthAuth,
112
+ ): Promise<Connector> {
113
+ return deps.reconnectConnector(entry.subject, entry.connectorId, auth);
114
+ }
115
+
116
+ async function settleCallback(
117
+ res: ServerResponse,
118
+ deps: OAuthRouteDeps,
119
+ entry: PendingAuth,
120
+ code: string,
121
+ ): Promise<void> {
122
+ try {
123
+ const auth = await completeOAuth(entry, code);
124
+ const saved = await saveOAuth(deps, entry, auth);
125
+ log.info(
126
+ { id: saved.id, name: saved.name, host: hostOf(saved.url) },
127
+ 'connector-api: oauth sign-in completed',
128
+ );
129
+ redirect(res, backTo(entry, undefined, saved.id));
130
+ } catch (err) {
131
+ log.warn({ err: errMsg(err) }, 'connector-api: oauth sign-in failed');
132
+ redirect(res, backTo(entry, errMsg(err)));
133
+ }
134
+ }
135
+
136
+ export function handleCallback(
137
+ req: IncomingMessage,
138
+ res: ServerResponse,
139
+ deps: OAuthRouteDeps,
140
+ ): void {
141
+ const query = new URLSearchParams((req.url ?? '').split('?')[1] ?? '');
142
+ const entry = takePending(query.get('state') ?? '');
143
+ if (entry === undefined) {
144
+ sendJson(req, res, 400, { error: 'that sign-in has expired — start it again' });
145
+ return;
146
+ }
147
+ const denied = query.get('error');
148
+ const code = query.get('code') ?? '';
149
+ if (denied !== null || code === '') {
150
+ redirect(res, backTo(entry, denied ?? 'no authorization code came back'));
151
+ return;
152
+ }
153
+ settleCallback(res, deps, entry, code).catch((err: unknown) => {
154
+ log.warn({ err: errMsg(err) }, 'connector-api: oauth callback failed');
155
+ if (!res.headersSent) redirect(res, backTo(entry, 'sign-in failed'));
156
+ });
157
+ }
@@ -0,0 +1,127 @@
1
+ import { publicBaseOrDefault } from '../files/attach-serve.js';
2
+ import {
3
+ ConnectorVerifyError,
4
+ type OAuthAuth,
5
+ type OAuthTokens,
6
+ } from './verify.js';
7
+ import { validateReturnTo } from '@metro-labs/http/return-to';
8
+ import {
9
+ authorizeUrl,
10
+ exchangeCode,
11
+ newVerifier,
12
+ refreshTokens,
13
+ registerClient,
14
+ } from './oauth-client.js';
15
+ import { discoverOAuth } from './oauth-discovery.js';
16
+ import { startPending, takePending, type PendingAuth } from './oauth-pending.js';
17
+
18
+ export { takePending, type PendingAuth };
19
+
20
+ const REFRESH_SKEW_MS = 300_000;
21
+
22
+ const CALLBACK_PATH = '/api/connectors/callback';
23
+
24
+ const callbackUri = (): string =>
25
+ `${publicBaseOrDefault()}${CALLBACK_PATH}`;
26
+
27
+ function refused(message: string): ConnectorVerifyError {
28
+ return new ConnectorVerifyError(message, 400);
29
+ }
30
+
31
+ const resourceOf = (url: URL): string => url.toString();
32
+
33
+ export interface BeginInput {
34
+ connectorId: string;
35
+ subject: string;
36
+ name: string;
37
+ url: URL;
38
+ returnTo: string;
39
+ }
40
+
41
+ export async function beginOAuth(input: BeginInput): Promise<string> {
42
+ if (!validateReturnTo(input.returnTo))
43
+ throw refused('that return address is not one Metro will send you back to');
44
+ const server = await discoverOAuth(input.url);
45
+ const redirectUri = callbackUri();
46
+ const client = await registerClient(server, redirectUri);
47
+ const verifier = newVerifier();
48
+ const resource = resourceOf(input.url);
49
+ const state = startPending({
50
+ subject: input.subject,
51
+ name: input.name,
52
+ url: input.url.toString(),
53
+ resource,
54
+ returnTo: input.returnTo,
55
+ verifier,
56
+ server,
57
+ client,
58
+ connectorId: input.connectorId,
59
+ });
60
+ return authorizeUrl({
61
+ server,
62
+ client,
63
+ redirectUri,
64
+ state,
65
+ verifier,
66
+ resource,
67
+ });
68
+ }
69
+
70
+ function authOf(pendingAuth: PendingAuth, tokens: OAuthTokens): OAuthAuth {
71
+ return {
72
+ kind: 'oauth',
73
+ issuer: pendingAuth.server.issuer,
74
+ tokenEndpoint: pendingAuth.server.tokenEndpoint,
75
+ clientId: pendingAuth.client.clientId,
76
+ ...(pendingAuth.client.clientSecret === undefined
77
+ ? {}
78
+ : { clientSecret: pendingAuth.client.clientSecret }),
79
+ ...tokens,
80
+ };
81
+ }
82
+
83
+ export async function completeOAuth(
84
+ entry: PendingAuth,
85
+ code: string,
86
+ ): Promise<OAuthAuth> {
87
+ const tokens = await exchangeCode(entry.server, entry.client, {
88
+ code,
89
+ redirectUri: callbackUri(),
90
+ verifier: entry.verifier,
91
+ resource: entry.resource,
92
+ });
93
+ return authOf(entry, tokens);
94
+ }
95
+
96
+ export function oauthExpired(auth: OAuthAuth, now = Date.now()): boolean {
97
+ return auth.expiresAt !== undefined && auth.expiresAt - REFRESH_SKEW_MS <= now;
98
+ }
99
+
100
+ export async function refreshOAuth(
101
+ auth: OAuthAuth,
102
+ resource: string,
103
+ ): Promise<OAuthAuth> {
104
+ if (auth.refreshToken === undefined)
105
+ throw refused('that connector needs signing in again');
106
+ const server = {
107
+ issuer: auth.issuer,
108
+ authorizationEndpoint: '',
109
+ tokenEndpoint: auth.tokenEndpoint,
110
+ registrationEndpoint: null,
111
+ supportsS256: true,
112
+ };
113
+ const client = {
114
+ clientId: auth.clientId,
115
+ ...(auth.clientSecret === undefined
116
+ ? {}
117
+ : { clientSecret: auth.clientSecret }),
118
+ };
119
+ const tokens = await refreshTokens(server, client, auth.refreshToken, resource);
120
+ return {
121
+ ...auth,
122
+ ...tokens,
123
+ ...(tokens.refreshToken === undefined
124
+ ? { refreshToken: auth.refreshToken }
125
+ : {}),
126
+ };
127
+ }