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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (249) hide show
  1. package/dist/api.js +131 -0
  2. package/dist/bedrock-eventstream.js +120 -0
  3. package/dist/bedrock-proxy.js +260 -0
  4. package/dist/bedrock-upstream.js +72 -0
  5. package/dist/bedrock.js +106 -0
  6. package/dist/claude.js +15 -0
  7. package/dist/cli.js +181 -207
  8. package/dist/control.js +173 -0
  9. package/dist/plugin.js +82 -0
  10. package/dist/prompt.js +61 -0
  11. package/dist/runtime.js +117 -0
  12. package/dist/serve.js +77 -0
  13. package/dist/store.js +93 -0
  14. package/dist/tail.js +120 -0
  15. package/dist/update.js +33 -0
  16. package/dist/version.js +97 -0
  17. package/package.json +34 -21
  18. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  19. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +119 -0
  20. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  21. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  22. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  23. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +210 -0
  24. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  25. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  26. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  27. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  28. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  29. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  30. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  31. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  32. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  33. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  34. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  35. package/runtime/node_modules/@metro-labs/mcp/package.json +67 -0
  36. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
  37. package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +367 -0
  38. package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
  39. package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
  40. package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
  41. package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
  42. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
  43. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +334 -0
  44. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-connector-api.ts +150 -0
  45. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-import.ts +76 -0
  46. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-pair.ts +20 -0
  47. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
  48. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +153 -0
  49. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
  50. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
  51. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +133 -0
  52. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +278 -0
  53. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +383 -0
  54. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +143 -0
  55. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +33 -0
  56. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +315 -0
  57. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
  58. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
  59. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
  60. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +100 -0
  61. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +57 -0
  62. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +333 -0
  63. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
  64. package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
  65. package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +117 -0
  66. package/runtime/node_modules/@metro-labs/mcp/src/daemon/hosted-connectors.ts +118 -0
  67. package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +369 -0
  68. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +86 -0
  69. package/runtime/node_modules/@metro-labs/mcp/src/daemon/import-api.ts +67 -0
  70. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-connectors-api.ts +56 -0
  71. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +187 -0
  72. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-secret.ts +21 -0
  73. package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
  74. package/runtime/node_modules/@metro-labs/mcp/src/daemon/mode-api.ts +31 -0
  75. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +162 -0
  76. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +120 -0
  77. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
  78. package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +58 -0
  79. package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +145 -0
  80. package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +214 -0
  81. package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +224 -0
  82. package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +303 -0
  83. package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +139 -0
  84. package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +105 -0
  85. package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
  86. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +27 -0
  87. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +63 -0
  88. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-config.ts +27 -0
  89. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +218 -0
  90. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-auth.ts +82 -0
  91. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-nonces.ts +43 -0
  92. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-routes.ts +89 -0
  93. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
  94. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +320 -0
  95. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
  96. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
  97. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +257 -0
  98. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +295 -0
  99. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
  100. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +91 -0
  101. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +214 -0
  102. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-connectors.ts +143 -0
  103. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +84 -0
  104. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
  105. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +37 -0
  106. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +147 -0
  107. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +126 -0
  108. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +326 -0
  109. package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +319 -0
  110. package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +152 -0
  111. package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +21 -0
  112. package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
  113. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +373 -0
  114. package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +309 -0
  115. package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +24 -0
  116. package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +105 -0
  117. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +118 -0
  118. package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +45 -0
  119. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +72 -0
  120. package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +18 -0
  121. package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +141 -0
  122. package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +203 -0
  123. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
  124. package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
  125. package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
  126. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
  127. package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
  128. package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +192 -0
  129. package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
  130. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
  131. package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
  132. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +73 -0
  133. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
  134. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
  135. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +211 -0
  136. package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
  137. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +212 -0
  138. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +397 -0
  139. package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
  140. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +240 -0
  141. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
  142. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +80 -0
  143. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
  144. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
  145. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
  146. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
  147. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +182 -0
  148. package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
  149. package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
  150. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
  151. package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
  152. package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
  153. package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -0
  154. package/runtime/node_modules/@metro-labs/telegram/package.json +28 -0
  155. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +81 -0
  156. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  157. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  158. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  159. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +141 -0
  160. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  161. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  162. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  163. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  164. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  165. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  166. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  167. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  168. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  169. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  170. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  171. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  172. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  173. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
  174. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  175. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  176. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
  177. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  178. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  179. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  180. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  181. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
  182. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  183. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  184. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  185. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +40 -0
  186. package/runtime/node_modules/@metro-labs/whatsapp/package.json +29 -0
  187. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +67 -0
  188. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  189. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  190. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  191. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  192. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +318 -0
  193. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  194. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +132 -0
  195. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  196. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  197. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  198. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  199. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  200. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  201. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  202. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  203. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +141 -0
  204. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  205. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  206. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  207. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  208. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  209. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +113 -0
  210. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  211. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  212. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  213. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  214. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  215. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  216. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  217. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  218. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  219. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +333 -0
  220. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +78 -0
  221. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  222. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  223. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  224. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  225. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  226. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  227. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  228. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  229. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  230. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  231. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  232. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  233. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +39 -0
  234. package/runtime/runtime.json +3 -0
  235. package/runtime/trains/.keep +0 -0
  236. package/LICENSE +0 -21
  237. package/README.md +0 -117
  238. package/dist/agents/claude.js +0 -207
  239. package/dist/agents/codex.js +0 -207
  240. package/dist/agents/types.js +0 -2
  241. package/dist/channels/discord.js +0 -104
  242. package/dist/channels/telegram.js +0 -189
  243. package/dist/helpers/scope-cache.js +0 -65
  244. package/dist/helpers/streaming.js +0 -209
  245. package/dist/helpers/telegram-format.js +0 -39
  246. package/dist/helpers/turn.js +0 -40
  247. package/dist/log.js +0 -10
  248. package/dist/orchestrator.js +0 -208
  249. package/dist/paths.js +0 -68
package/dist/plugin.js ADDED
@@ -0,0 +1,82 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync, readFileSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ import { readToken } from './store.js';
6
+ const MARKETPLACE_REPO = 'bonustrack/metro';
7
+ const PLUGIN_SPEC = 'metro@metro';
8
+ export function installPathFrom(raw) {
9
+ let parsed;
10
+ try {
11
+ parsed = JSON.parse(raw);
12
+ }
13
+ catch {
14
+ return null;
15
+ }
16
+ if (typeof parsed !== 'object' || parsed === null)
17
+ return null;
18
+ const plugins = parsed.plugins;
19
+ if (typeof plugins !== 'object' || plugins === null)
20
+ return null;
21
+ const entries = plugins[PLUGIN_SPEC];
22
+ if (!Array.isArray(entries))
23
+ return null;
24
+ const first = entries[0];
25
+ return typeof first?.installPath === 'string' ? first.installPath : null;
26
+ }
27
+ export function pluginInstallPath() {
28
+ const registry = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
29
+ let raw;
30
+ try {
31
+ raw = readFileSync(registry, 'utf8');
32
+ }
33
+ catch {
34
+ return null;
35
+ }
36
+ const path = installPathFrom(raw);
37
+ if (path === null)
38
+ return null;
39
+ return existsSync(join(path, 'bin', 'metro-plugin.mjs')) ? path : null;
40
+ }
41
+ function runClaude(args) {
42
+ const res = spawnSync('claude', args, { encoding: 'utf8' });
43
+ if (res.error !== undefined)
44
+ throw new Error('the `claude` command is not on PATH — install Claude Code first');
45
+ const output = `${res.stdout ?? ''}${res.stderr ?? ''}`;
46
+ return { ok: res.status === 0, output };
47
+ }
48
+ const tolerable = (output) => /already|exists|latest version/i.test(output);
49
+ export function syncPluginServers() {
50
+ const path = pluginInstallPath();
51
+ if (path === null || readToken() === null)
52
+ return false;
53
+ const script = join(path, 'bin', 'metro-plugin.mjs');
54
+ const refreshed = spawnSync(process.execPath, [script, 'refresh'], {
55
+ stdio: ['ignore', 'inherit', 'inherit'],
56
+ });
57
+ if (refreshed.status !== 0)
58
+ return false;
59
+ runClaude(['plugin', 'update', PLUGIN_SPEC]);
60
+ return true;
61
+ }
62
+ export async function installPlugin() {
63
+ const added = runClaude(['plugin', 'marketplace', 'add', MARKETPLACE_REPO]);
64
+ if (!added.ok && !tolerable(added.output)) {
65
+ process.stderr.write(added.output);
66
+ return 1;
67
+ }
68
+ const installed = runClaude(['plugin', 'install', PLUGIN_SPEC]);
69
+ if (!installed.ok && !tolerable(installed.output)) {
70
+ process.stderr.write(installed.output);
71
+ return 1;
72
+ }
73
+ process.stderr.write('Claude Code plugin installed.\n');
74
+ if (syncPluginServers()) {
75
+ process.stderr.write('Connector servers loaded — new Claude sessions have them; ' +
76
+ 'run /reload-plugins in any session already open.\n');
77
+ return 0;
78
+ }
79
+ process.stderr.write('Now sign in: metro login (get a code at https://metro.box/#/authorize), ' +
80
+ 'or run /metro:login <code> inside Claude Code.\n');
81
+ return Promise.resolve(0);
82
+ }
package/dist/prompt.js ADDED
@@ -0,0 +1,61 @@
1
+ import { createInterface } from 'node:readline';
2
+ const CTRL_C = '\u0003';
3
+ const BACKSPACE = '\u007f';
4
+ export function askLine(question) {
5
+ const rl = createInterface({ input: process.stdin, output: process.stderr });
6
+ return new Promise((resolve) => {
7
+ rl.question(question, (answer) => {
8
+ rl.close();
9
+ resolve(answer.trim());
10
+ });
11
+ });
12
+ }
13
+ export function feedSecret(value, chunk) {
14
+ let out = value;
15
+ for (const ch of chunk) {
16
+ if (ch === CTRL_C)
17
+ return { kind: 'cancelled' };
18
+ if (ch === '\r' || ch === '\n')
19
+ return { kind: 'done', value: out };
20
+ if (ch === BACKSPACE || ch === '\b') {
21
+ out = out.slice(0, -1);
22
+ continue;
23
+ }
24
+ if (ch >= ' ')
25
+ out += ch;
26
+ }
27
+ return { kind: 'more', value: out };
28
+ }
29
+ export function askSecret(question) {
30
+ const stdin = process.stdin;
31
+ if (!stdin.isTTY)
32
+ return askLine(question);
33
+ process.stderr.write(question);
34
+ stdin.setRawMode(true);
35
+ stdin.resume();
36
+ stdin.setEncoding('utf8');
37
+ return new Promise((resolve, reject) => {
38
+ let value = '';
39
+ const finish = () => {
40
+ stdin.setRawMode(false);
41
+ stdin.pause();
42
+ stdin.off('data', onData);
43
+ process.stderr.write('\n');
44
+ };
45
+ const onData = (chunk) => {
46
+ const step = feedSecret(value, chunk);
47
+ if (step.kind === 'cancelled') {
48
+ finish();
49
+ reject(new Error('cancelled'));
50
+ return;
51
+ }
52
+ if (step.kind === 'done') {
53
+ finish();
54
+ resolve(step.value.trim());
55
+ return;
56
+ }
57
+ value = step.value;
58
+ };
59
+ stdin.on('data', onData);
60
+ });
61
+ }
@@ -0,0 +1,117 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { mkdirSync, readFileSync, writeFileSync, chmodSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { existsSync } from 'node:fs';
6
+ import { homedir } from 'node:os';
7
+ import { metroUrl } from './store.js';
8
+ const AGENT_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{10}$/;
9
+ export const SERVER_ENTRY = join('node_modules', '@metro-labs', 'mcp', 'src', 'server.ts');
10
+ export class MissingRuntime extends Error {
11
+ }
12
+ export function runtimeDir() {
13
+ const explicit = process.env.METRO_RUNTIME_DIR?.trim();
14
+ const dir = explicit !== undefined && explicit !== ''
15
+ ? explicit
16
+ : join(dirname(dirname(fileURLToPath(import.meta.url))), 'runtime');
17
+ if (!existsSync(join(dir, SERVER_ENTRY)))
18
+ throw new MissingRuntime(`no metro daemon at ${dir}. Reinstall with: npm i -g @stage-labs/metro@beta`);
19
+ return dir;
20
+ }
21
+ export class MissingBun extends Error {
22
+ }
23
+ export function assertAgentId(id) {
24
+ if (id === undefined || !AGENT_RE.test(id))
25
+ throw new Error(`'${id ?? ''}' is not an agent id — copy it from the agent's page in the web UI`);
26
+ return id;
27
+ }
28
+ export function findBun() {
29
+ const found = spawnSync('bun', ['--version'], { stdio: 'ignore' });
30
+ if (found.error !== undefined || found.status !== 0)
31
+ throw new MissingBun('metro start needs Bun, which is not on PATH.\n' +
32
+ 'Install it with: curl -fsSL https://bun.sh/install | bash');
33
+ return 'bun';
34
+ }
35
+ function tokenPath(agentId) {
36
+ const xdg = process.env.XDG_CONFIG_HOME?.trim();
37
+ const base = xdg === undefined || xdg === '' ? join(homedir(), '.config') : xdg;
38
+ return join(base, 'metro', `runtime-${agentId}.json`);
39
+ }
40
+ export function readRunToken(agentId) {
41
+ const fromEnv = process.env.METRO_RUN_TOKEN?.trim();
42
+ if (fromEnv !== undefined && fromEnv !== '')
43
+ return fromEnv;
44
+ try {
45
+ const parsed = JSON.parse(readFileSync(tokenPath(agentId), 'utf8'));
46
+ if (typeof parsed.token !== 'string' || parsed.token === '')
47
+ return null;
48
+ return parsed.url === metroUrl() ? parsed.token : null;
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ }
54
+ export function writeRunToken(agentId, token) {
55
+ const path = tokenPath(agentId);
56
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
57
+ writeFileSync(path, JSON.stringify({ token, url: metroUrl() }, null, 2));
58
+ chmodSync(path, 0o600);
59
+ }
60
+ const PROBE_TIMEOUT_MS = 15_000;
61
+ export async function runTokenState(token) {
62
+ let res;
63
+ try {
64
+ res = await fetch(`${metroUrl()}/api/run/config`, {
65
+ headers: { authorization: `Bearer ${token}` },
66
+ signal: AbortSignal.timeout(PROBE_TIMEOUT_MS),
67
+ redirect: 'manual',
68
+ });
69
+ }
70
+ catch {
71
+ return 'unreachable';
72
+ }
73
+ await res.arrayBuffer().catch(() => undefined);
74
+ if (res.ok)
75
+ return 'ok';
76
+ return res.status === 401 || res.status === 403 || res.status === 409
77
+ ? 'stale'
78
+ : 'unreachable';
79
+ }
80
+ export const localPort = () => Number(process.env.METRO_WEBHOOK_PORT) || 8420;
81
+ export const localUrl = () => `http://127.0.0.1:${String(localPort())}`;
82
+ export function daemonPlan(opts) {
83
+ return {
84
+ command: findBun(),
85
+ args: [SERVER_ENTRY],
86
+ cwd: opts.dir,
87
+ env: {
88
+ ...process.env,
89
+ METRO_RUN_TOKEN: opts.token,
90
+ METRO_AGENT: opts.agentId,
91
+ METRO_URL: metroUrl(),
92
+ METRO_HTTP_HOST: process.env.METRO_HTTP_HOST ?? '127.0.0.1',
93
+ METRO_TRAINS_DIR: join(opts.dir, 'trains'),
94
+ },
95
+ };
96
+ }
97
+ export function runDaemon(agentId, plan) {
98
+ process.stderr.write(`Starting metro for ${agentId}\n`);
99
+ return spawnPlan(plan);
100
+ }
101
+ export function spawnPlan(plan) {
102
+ const child = spawn(plan.command, plan.args, {
103
+ cwd: plan.cwd,
104
+ env: plan.env,
105
+ stdio: ['ignore', 'inherit', 'inherit'],
106
+ });
107
+ const relay = (sig) => () => {
108
+ child.kill(sig);
109
+ };
110
+ process.on('SIGINT', relay('SIGINT'));
111
+ process.on('SIGTERM', relay('SIGTERM'));
112
+ return new Promise((resolve) => {
113
+ child.on('exit', (code) => {
114
+ resolve(code ?? 0);
115
+ });
116
+ });
117
+ }
package/dist/serve.js ADDED
@@ -0,0 +1,77 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { join } from 'node:path';
3
+ import { serveLockedBy, serveStateDir } from './control.js';
4
+ import { findBun, localPort, runtimeDir, SERVER_ENTRY, spawnPlan, } from './runtime.js';
5
+ const SCRUBBED = new Set(['METRO_RUN_TOKEN', 'METRO_AGENT', 'DATABASE_URL']);
6
+ const PORT_FLAG = /^--port=(.*)$/;
7
+ const USAGE = 'usage: metro serve [--port <n>] [--tunnel]';
8
+ function portOf(raw) {
9
+ const n = Number(raw);
10
+ if (raw === undefined || raw === '' || !Number.isInteger(n) || n < 1 || n > 65535)
11
+ throw new Error(`'${raw ?? ''}' is not a port — ${USAGE}`);
12
+ return n;
13
+ }
14
+ export function parseServeArgs(argv) {
15
+ let port = localPort();
16
+ let tunnel = false;
17
+ for (let i = 0; i < argv.length; i += 1) {
18
+ const arg = argv[i] ?? '';
19
+ if (arg === '--tunnel') {
20
+ tunnel = true;
21
+ continue;
22
+ }
23
+ const inline = PORT_FLAG.exec(arg);
24
+ if (inline) {
25
+ port = portOf(inline[1]);
26
+ continue;
27
+ }
28
+ if (arg === '--port' || arg === '-p') {
29
+ port = portOf(argv[i + 1]);
30
+ i += 1;
31
+ continue;
32
+ }
33
+ throw new Error(`unknown argument '${arg}' — ${USAGE}`);
34
+ }
35
+ return { port, tunnel };
36
+ }
37
+ export function findCloudflared() {
38
+ const found = spawnSync('cloudflared', ['--version'], { stdio: 'ignore' });
39
+ if (found.error !== undefined || found.status !== 0)
40
+ throw new Error('metro serve --tunnel needs cloudflared, which is not on PATH.\n' +
41
+ 'Debian/Ubuntu (it is not in the distro repos; this adds Cloudflare\'s signed one):\n' +
42
+ ' sudo mkdir -p --mode=0755 /usr/share/keyrings\n' +
43
+ ' curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null\n' +
44
+ " echo 'deb [signed-by=/usr/share/keyrings/cloudflare-main.gpg] https://pkg.cloudflare.com/cloudflared any main' | sudo tee /etc/apt/sources.list.d/cloudflared.list\n" +
45
+ ' sudo apt-get update && sudo apt-get install cloudflared\n' +
46
+ 'macOS: brew install cloudflared\n' +
47
+ 'Others: https://pkg.cloudflare.com/');
48
+ }
49
+ export function servePlan(opts) {
50
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !SCRUBBED.has(key)));
51
+ return {
52
+ command: findBun(),
53
+ args: [SERVER_ENTRY],
54
+ cwd: opts.dir,
55
+ env: {
56
+ ...env,
57
+ METRO_MODE: 'local',
58
+ METRO_WEBHOOK_PORT: String(opts.port),
59
+ METRO_HTTP_HOST: process.env.METRO_HTTP_HOST ?? '127.0.0.1',
60
+ METRO_TRAINS_DIR: join(opts.dir, 'trains'),
61
+ METRO_STATE_DIR: process.env.METRO_STATE_DIR ?? serveStateDir(),
62
+ ...(opts.tunnel ? { METRO_TUNNEL: 'quick' } : {}),
63
+ },
64
+ };
65
+ }
66
+ export function serve(argv) {
67
+ const { port, tunnel } = parseServeArgs(argv);
68
+ const running = serveLockedBy();
69
+ if (running !== null)
70
+ throw new Error(`a metro serve daemon is already running on this machine (pid ${String(running)}). ` +
71
+ 'Stop it first: metro stop');
72
+ if (tunnel)
73
+ findCloudflared();
74
+ const plan = servePlan({ dir: runtimeDir(), port, tunnel });
75
+ process.stderr.write(`Starting a metro daemon of your own on http://127.0.0.1:${String(port)}\n`);
76
+ return spawnPlan(plan);
77
+ }
package/dist/store.js ADDED
@@ -0,0 +1,93 @@
1
+ import { chmodSync, mkdirSync, readdirSync, readFileSync, rmSync, writeFileSync, } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { dirname, join } from 'node:path';
4
+ const DEFAULT_BASE = 'https://mcp.metro.box';
5
+ const DEFAULT_WEB = 'https://metro.box';
6
+ function fromEnv(name, fallback) {
7
+ const raw = process.env[name]?.trim();
8
+ return raw === undefined || raw === '' ? fallback : raw.replace(/\/+$/, '');
9
+ }
10
+ export function metroUrl() {
11
+ return fromEnv('METRO_URL', DEFAULT_BASE);
12
+ }
13
+ export function metroWebUrl() {
14
+ return fromEnv('METRO_UI_URL', DEFAULT_WEB);
15
+ }
16
+ function configDir() {
17
+ const xdg = process.env.XDG_CONFIG_HOME?.trim();
18
+ const base = xdg === undefined || xdg === '' ? join(homedir(), '.config') : xdg;
19
+ return join(base, 'metro');
20
+ }
21
+ export function credentialsPath() {
22
+ return join(configDir(), 'credentials.json');
23
+ }
24
+ function readStoredAt(path) {
25
+ let raw;
26
+ try {
27
+ raw = readFileSync(path, 'utf8');
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ let parsed;
33
+ try {
34
+ parsed = JSON.parse(raw);
35
+ }
36
+ catch {
37
+ return null;
38
+ }
39
+ if (typeof parsed !== 'object' || parsed === null)
40
+ return null;
41
+ const { token, url } = parsed;
42
+ if (typeof token !== 'string' || token === '')
43
+ return null;
44
+ return { token, url: typeof url === 'string' ? url : DEFAULT_BASE };
45
+ }
46
+ const readStored = () => readStoredAt(credentialsPath());
47
+ const RUN_TOKEN_FILE = /^runtime-[A-Za-z0-9_-]{11}\.json$/;
48
+ export function readRunTokens() {
49
+ const fromEnv = process.env.METRO_RUN_TOKEN?.trim();
50
+ if (fromEnv !== undefined && fromEnv !== '')
51
+ return [fromEnv];
52
+ let names;
53
+ try {
54
+ names = readdirSync(configDir());
55
+ }
56
+ catch {
57
+ return [];
58
+ }
59
+ const out = [];
60
+ for (const name of names) {
61
+ if (!RUN_TOKEN_FILE.test(name))
62
+ continue;
63
+ const stored = readStoredAt(join(configDir(), name));
64
+ if (stored !== null && stored.url === metroUrl())
65
+ out.push(stored.token);
66
+ }
67
+ return out;
68
+ }
69
+ export function readToken() {
70
+ const fromEnv = process.env.METRO_TOKEN?.trim();
71
+ if (fromEnv !== undefined && fromEnv !== '')
72
+ return fromEnv;
73
+ const stored = readStored();
74
+ if (stored === null)
75
+ return null;
76
+ return stored.url === metroUrl() ? stored.token : null;
77
+ }
78
+ export function writeToken(token) {
79
+ const path = credentialsPath();
80
+ mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
81
+ writeFileSync(path, `${JSON.stringify({ token, url: metroUrl() })}\n`, {
82
+ mode: 0o600,
83
+ });
84
+ chmodSync(path, 0o600);
85
+ }
86
+ export function clearToken() {
87
+ try {
88
+ rmSync(credentialsPath());
89
+ }
90
+ catch {
91
+ return;
92
+ }
93
+ }
package/dist/tail.js ADDED
@@ -0,0 +1,120 @@
1
+ import { runConfigKey } from './api.js';
2
+ import { assertAgentId, localUrl, readRunToken } from './runtime.js';
3
+ const BACKOFF_MS = [1_000, 2_000, 5_000];
4
+ const STALL_MS = 90_000;
5
+ export const backoffAt = (attempt) => BACKOFF_MS[Math.min(attempt, BACKOFF_MS.length - 1)] ?? 5_000;
6
+ export function sseDataLines(buffer, chunk) {
7
+ const lines = (buffer + chunk).split('\n');
8
+ const rest = lines.pop() ?? '';
9
+ const events = [];
10
+ for (const raw of lines) {
11
+ const line = raw.endsWith('\r') ? raw.slice(0, -1) : raw;
12
+ if (!line.startsWith('data:'))
13
+ continue;
14
+ const value = line.slice(5);
15
+ events.push(value.startsWith(' ') ? value.slice(1) : value);
16
+ }
17
+ return { rest, events };
18
+ }
19
+ function keySource(agentId) {
20
+ const fromEnv = process.env.METRO_AGENT_KEY?.trim();
21
+ if (fromEnv !== undefined && fromEnv !== '')
22
+ return { resolve: () => Promise.resolve(fromEnv), fixed: true };
23
+ return {
24
+ resolve: () => {
25
+ const token = readRunToken(agentId);
26
+ if (token === null)
27
+ throw new Error(`this machine is not authorized for ${agentId} — ` +
28
+ `run 'metro start ${agentId}' once, or set METRO_AGENT_KEY`);
29
+ return runConfigKey(token);
30
+ },
31
+ fixed: false,
32
+ };
33
+ }
34
+ const armStall = (control) => setTimeout(() => {
35
+ control.abort();
36
+ }, STALL_MS);
37
+ async function connect(key, signal) {
38
+ try {
39
+ return await fetch(`${localUrl()}/api/tail`, {
40
+ headers: { authorization: `Bearer ${key}` },
41
+ signal,
42
+ });
43
+ }
44
+ catch {
45
+ return null;
46
+ }
47
+ }
48
+ async function pump(body, control, stall) {
49
+ const reader = body.getReader();
50
+ const decoder = new TextDecoder();
51
+ let buffer = '';
52
+ try {
53
+ for (;;) {
54
+ const { done, value } = await reader.read();
55
+ if (done)
56
+ return 'ended';
57
+ clearTimeout(stall.timer);
58
+ stall.timer = armStall(control);
59
+ const out = sseDataLines(buffer, decoder.decode(value, { stream: true }));
60
+ buffer = out.rest;
61
+ for (const event of out.events)
62
+ process.stdout.write(`${event}\n`);
63
+ }
64
+ }
65
+ catch {
66
+ return 'ended';
67
+ }
68
+ }
69
+ async function streamOnce(key) {
70
+ const control = new AbortController();
71
+ const stall = { timer: armStall(control) };
72
+ try {
73
+ const res = await connect(key, control.signal);
74
+ if (res === null)
75
+ return 'unreachable';
76
+ if (res.status === 401)
77
+ return 'unauthorized';
78
+ if (!res.ok || res.body === null)
79
+ return 'unreachable';
80
+ return await pump(res.body, control, stall);
81
+ }
82
+ finally {
83
+ clearTimeout(stall.timer);
84
+ }
85
+ }
86
+ function note(state, message) {
87
+ if (state.last === message)
88
+ return;
89
+ state.last = message;
90
+ process.stderr.write(`metro: ${message}\n`);
91
+ }
92
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
93
+ async function settle(end, source, key, agentId, state) {
94
+ if (end === 'unauthorized') {
95
+ if (source.fixed)
96
+ throw new Error('the key in METRO_AGENT_KEY was refused by the local daemon');
97
+ note(state, 'key refused — fetching the current one');
98
+ return source.resolve();
99
+ }
100
+ if (end === 'unreachable')
101
+ note(state, `no metro daemon on ${localUrl()} — run 'metro start ${agentId}'`);
102
+ else
103
+ note(state, 'stream ended — reconnecting');
104
+ return key;
105
+ }
106
+ export async function tailEvents(argv) {
107
+ const agentId = assertAgentId(argv[0]);
108
+ const source = keySource(agentId);
109
+ let key = await source.resolve();
110
+ const state = { last: '' };
111
+ let attempt = 0;
112
+ for (;;) {
113
+ const end = await streamOnce(key);
114
+ if (end === 'ended')
115
+ attempt = 0;
116
+ key = await settle(end, source, key, agentId, state);
117
+ await delay(backoffAt(attempt));
118
+ attempt += 1;
119
+ }
120
+ }
package/dist/update.js ADDED
@@ -0,0 +1,33 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { currentVersion, isNewer, PACKAGE_NAME, publishedVersion, } from './version.js';
3
+ export function managerFor(location) {
4
+ return location.includes('/.bun/') ? 'bun' : 'npm';
5
+ }
6
+ export function installArgs(manager, version) {
7
+ const spec = `${PACKAGE_NAME}@${version}`;
8
+ return manager === 'bun'
9
+ ? ['add', '--global', spec]
10
+ : ['install', '--global', spec];
11
+ }
12
+ export async function update() {
13
+ const current = currentVersion();
14
+ const newest = await publishedVersion();
15
+ if (newest === '') {
16
+ process.stderr.write('npm published no version this CLI understands\n');
17
+ return 1;
18
+ }
19
+ if (!isNewer(newest, current)) {
20
+ process.stderr.write(`metro ${current} is the newest published version\n`);
21
+ return 0;
22
+ }
23
+ const manager = managerFor(import.meta.url);
24
+ const args = installArgs(manager, newest);
25
+ process.stderr.write(`Updating metro ${current} to ${newest} with ${manager}\n`);
26
+ const run = spawnSync(manager, args, { stdio: 'inherit' });
27
+ if (run.error !== undefined || run.status !== 0) {
28
+ process.stderr.write(`That did not work. Run it yourself:\n ${manager} ${args.join(' ')}\n`);
29
+ return 1;
30
+ }
31
+ process.stderr.write(`metro is now ${newest}\n`);
32
+ return 0;
33
+ }
@@ -0,0 +1,97 @@
1
+ import { readFileSync } from 'node:fs';
2
+ export const PACKAGE_NAME = '@stage-labs/metro';
3
+ const REGISTRY = 'https://registry.npmjs.org';
4
+ const TIMEOUT_MS = 15_000;
5
+ export function parseVersion(raw) {
6
+ const match = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?$/.exec(raw.trim());
7
+ if (match === null)
8
+ return null;
9
+ const core = [match[1], match[2], match[3]].map((n) => Number(n));
10
+ const pre = match[4] === undefined
11
+ ? []
12
+ : match[4]
13
+ .split('.')
14
+ .map((part) => (/^\d+$/.test(part) ? Number(part) : part));
15
+ return { core, pre };
16
+ }
17
+ function comparePart(left, right) {
18
+ if (left === undefined)
19
+ return -1;
20
+ if (right === undefined)
21
+ return 1;
22
+ if (left === right)
23
+ return 0;
24
+ if (typeof left === 'number' && typeof right === 'number')
25
+ return left < right ? -1 : 1;
26
+ if (typeof left === 'number')
27
+ return -1;
28
+ if (typeof right === 'number')
29
+ return 1;
30
+ return left < right ? -1 : 1;
31
+ }
32
+ function comparePre(a, b) {
33
+ if (a.length === 0 && b.length === 0)
34
+ return 0;
35
+ if (a.length === 0)
36
+ return 1;
37
+ if (b.length === 0)
38
+ return -1;
39
+ for (let i = 0; i < Math.max(a.length, b.length); i += 1) {
40
+ const cmp = comparePart(a[i], b[i]);
41
+ if (cmp !== 0)
42
+ return cmp;
43
+ }
44
+ return 0;
45
+ }
46
+ export function compareVersions(a, b) {
47
+ const left = parseVersion(a);
48
+ const right = parseVersion(b);
49
+ if (left === null || right === null)
50
+ return 0;
51
+ for (let i = 0; i < 3; i += 1) {
52
+ const l = left.core[i] ?? 0;
53
+ const r = right.core[i] ?? 0;
54
+ if (l !== r)
55
+ return l < r ? -1 : 1;
56
+ }
57
+ return comparePre(left.pre, right.pre);
58
+ }
59
+ export function isNewer(candidate, current) {
60
+ return compareVersions(candidate, current) > 0;
61
+ }
62
+ export function currentVersion() {
63
+ const path = new URL('../package.json', import.meta.url);
64
+ const raw = JSON.parse(readFileSync(path, 'utf8'));
65
+ return typeof raw.version === 'string' ? raw.version : '0.0.0';
66
+ }
67
+ export function newestOf(tags) {
68
+ let best = '';
69
+ for (const value of Object.values(tags)) {
70
+ if (typeof value !== 'string' || parseVersion(value) === null)
71
+ continue;
72
+ if (best === '' || isNewer(value, best))
73
+ best = value;
74
+ }
75
+ return best;
76
+ }
77
+ export async function publishedVersion() {
78
+ let res;
79
+ try {
80
+ res = await fetch(`${REGISTRY}/${PACKAGE_NAME.replace('/', '%2F')}`, {
81
+ headers: { accept: 'application/json' },
82
+ signal: AbortSignal.timeout(TIMEOUT_MS),
83
+ });
84
+ }
85
+ catch {
86
+ throw new Error('could not reach the npm registry');
87
+ }
88
+ if (!res.ok)
89
+ throw new Error(`npm answered ${String(res.status)}`);
90
+ const body = await res.json();
91
+ if (typeof body !== 'object' || body === null)
92
+ throw new Error('npm returned an unexpected response');
93
+ const tags = body['dist-tags'];
94
+ if (typeof tags !== 'object' || tags === null)
95
+ throw new Error('npm returned no dist-tags');
96
+ return newestOf(tags);
97
+ }