@stage-labs/metro 0.1.0-beta.5 → 0.1.0-beta.51

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 (253) hide show
  1. package/dist/api.js +14 -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 +94 -0
  8. package/dist/control.js +74 -0
  9. package/dist/local.js +67 -0
  10. package/dist/plugin.js +80 -0
  11. package/dist/runtime.js +50 -0
  12. package/dist/serve.js +109 -0
  13. package/dist/tail.js +119 -0
  14. package/dist/update.js +33 -0
  15. package/dist/version.js +97 -0
  16. package/package.json +34 -23
  17. package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
  18. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
  19. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
  20. package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
  21. package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
  22. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +210 -0
  23. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
  24. package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
  25. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
  26. package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
  27. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
  28. package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
  29. package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
  30. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
  31. package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
  32. package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
  33. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
  34. package/runtime/node_modules/@metro-labs/mcp/package.json +62 -0
  35. package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
  36. package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +367 -0
  37. package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
  38. package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
  39. package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
  40. package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
  41. package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
  42. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +302 -0
  43. package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-connector-api.ts +135 -0
  44. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
  45. package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +102 -0
  46. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
  47. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
  48. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +129 -0
  49. package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +278 -0
  50. package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +189 -0
  51. package/runtime/node_modules/@metro-labs/mcp/src/daemon/bundle-api.ts +112 -0
  52. package/runtime/node_modules/@metro-labs/mcp/src/daemon/claude-api.ts +101 -0
  53. package/runtime/node_modules/@metro-labs/mcp/src/daemon/claude-files.ts +329 -0
  54. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +39 -0
  55. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +311 -0
  56. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
  57. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
  58. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
  59. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +98 -0
  60. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +70 -0
  61. package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +330 -0
  62. package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
  63. package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
  64. package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +117 -0
  65. package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +368 -0
  66. package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +7 -0
  67. package/runtime/node_modules/@metro-labs/mcp/src/daemon/is-record.ts +2 -0
  68. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-cli-api.ts +70 -0
  69. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +195 -0
  70. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-owner.ts +14 -0
  71. package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-secret.ts +21 -0
  72. package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
  73. package/runtime/node_modules/@metro-labs/mcp/src/daemon/mode-api.ts +32 -0
  74. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +159 -0
  75. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +117 -0
  76. package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
  77. package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +109 -0
  78. package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +224 -0
  79. package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +302 -0
  80. package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
  81. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +27 -0
  82. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +52 -0
  83. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-config.ts +22 -0
  84. package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +96 -0
  85. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-auth.ts +73 -0
  86. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-nonces.ts +43 -0
  87. package/runtime/node_modules/@metro-labs/mcp/src/daemon/siwe-routes.ts +89 -0
  88. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
  89. package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +320 -0
  90. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
  91. package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
  92. package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +175 -0
  93. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +295 -0
  94. package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
  95. package/runtime/node_modules/@metro-labs/mcp/src/daemon/vault-api.ts +108 -0
  96. package/runtime/node_modules/@metro-labs/mcp/src/daemon/version.ts +2 -0
  97. package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +19 -0
  98. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +43 -0
  99. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +84 -0
  100. package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
  101. package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +32 -0
  102. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +144 -0
  103. package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +39 -0
  104. package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +77 -0
  105. package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +375 -0
  106. package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +161 -0
  107. package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +21 -0
  108. package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
  109. package/runtime/node_modules/@metro-labs/mcp/src/db/local-connectors.ts +322 -0
  110. package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +253 -0
  111. package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +35 -0
  112. package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +45 -0
  113. package/runtime/node_modules/@metro-labs/mcp/src/db/vault.ts +119 -0
  114. package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +141 -0
  115. package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +203 -0
  116. package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
  117. package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
  118. package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
  119. package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
  120. package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
  121. package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +192 -0
  122. package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
  123. package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
  124. package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
  125. package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +73 -0
  126. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
  127. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
  128. package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +211 -0
  129. package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
  130. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +212 -0
  131. package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +397 -0
  132. package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
  133. package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +240 -0
  134. package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
  135. package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +60 -0
  136. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
  137. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
  138. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
  139. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
  140. package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +182 -0
  141. package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
  142. package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
  143. package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
  144. package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
  145. package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
  146. package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -0
  147. package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
  148. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
  149. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
  150. package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
  151. package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
  152. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +141 -0
  153. package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
  154. package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
  155. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
  156. package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
  157. package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
  158. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
  159. package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
  160. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
  161. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
  162. package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
  163. package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
  164. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
  165. package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
  166. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +119 -0
  167. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
  168. package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
  169. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +172 -0
  170. package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
  171. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
  172. package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
  173. package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
  174. package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +48 -0
  175. package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
  176. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
  177. package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
  178. package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
  179. package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
  180. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
  181. package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
  182. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
  183. package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
  184. package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
  185. package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +318 -0
  186. package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
  187. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +132 -0
  188. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
  189. package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
  190. package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
  191. package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
  192. package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
  193. package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
  194. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
  195. package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
  196. package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +141 -0
  197. package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
  198. package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
  199. package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
  200. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
  201. package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
  202. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
  203. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
  204. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
  205. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
  206. package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
  207. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
  208. package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
  209. package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
  210. package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
  211. package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
  212. package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +333 -0
  213. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +78 -0
  214. package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
  215. package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
  216. package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
  217. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
  218. package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
  219. package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
  220. package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
  221. package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
  222. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
  223. package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
  224. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
  225. package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
  226. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +39 -0
  227. package/runtime/runtime.json +3 -0
  228. package/runtime/trains/.keep +0 -0
  229. package/LICENSE +0 -21
  230. package/README.md +0 -277
  231. package/dist/cli/index.js +0 -236
  232. package/dist/cli/lines.js +0 -40
  233. package/dist/cli/update.js +0 -27
  234. package/dist/dispatcher.js +0 -203
  235. package/dist/helpers/async-queue.js +0 -41
  236. package/dist/helpers/scope-cache.js +0 -67
  237. package/dist/helpers/streaming.js +0 -219
  238. package/dist/helpers/turn.js +0 -62
  239. package/dist/log.js +0 -15
  240. package/dist/paths.js +0 -68
  241. package/dist/stations/claude/index.js +0 -224
  242. package/dist/stations/codex/index.js +0 -226
  243. package/dist/stations/discord/index.js +0 -160
  244. package/dist/stations/github/index.js +0 -135
  245. package/dist/stations/line.js +0 -54
  246. package/dist/stations/listing.js +0 -16
  247. package/dist/stations/send.js +0 -19
  248. package/dist/stations/telegram/files.js +0 -31
  249. package/dist/stations/telegram/format.js +0 -39
  250. package/dist/stations/telegram/index.js +0 -209
  251. package/dist/stations/types.js +0 -2
  252. package/docs/agents.md +0 -92
  253. package/docs/uri-scheme.md +0 -52
@@ -0,0 +1,74 @@
1
+ import { execFileSync } from 'node:child_process';
2
+ import { readFileSync, rmSync } from 'node:fs';
3
+ import { homedir } from 'node:os';
4
+ import { join } from 'node:path';
5
+ export function serveStateDir() {
6
+ const xdg = process.env.XDG_CACHE_HOME?.trim();
7
+ const base = xdg === undefined || xdg === '' ? join(homedir(), '.cache') : xdg;
8
+ return join(base, 'metro', 'serve');
9
+ }
10
+ const serveLockPath = () => join(serveStateDir(), '.tail-lock');
11
+ function isAlive(pid) {
12
+ try {
13
+ process.kill(pid, 0);
14
+ return true;
15
+ }
16
+ catch {
17
+ return false;
18
+ }
19
+ }
20
+ async function stopPid(pid, waitMs) {
21
+ process.kill(pid, 'SIGTERM');
22
+ const until = Date.now() + waitMs;
23
+ while (Date.now() < until) {
24
+ if (!isAlive(pid))
25
+ return;
26
+ await new Promise((r) => setTimeout(r, 100));
27
+ }
28
+ process.kill(pid, 'SIGKILL');
29
+ }
30
+ const lockPath = () => process.env.METRO_STATE_DIR?.trim()
31
+ ? join(process.env.METRO_STATE_DIR.trim(), '.tail-lock')
32
+ : join(homedir(), '.cache', 'metro', '.tail-lock');
33
+ function commandOf(pid) {
34
+ try {
35
+ return execFileSync('ps', ['-o', 'command=', '-p', String(pid)], {
36
+ encoding: 'utf8',
37
+ stdio: ['ignore', 'pipe', 'ignore'],
38
+ });
39
+ }
40
+ catch {
41
+ return '';
42
+ }
43
+ }
44
+ const looksLikeMetro = (command) => command.includes('server.ts') || command.includes('metro');
45
+ function holderOf(lockFile) {
46
+ let pid;
47
+ try {
48
+ pid = Number(readFileSync(lockFile, 'utf8').trim());
49
+ }
50
+ catch {
51
+ return null;
52
+ }
53
+ if (!Number.isInteger(pid) || pid <= 0 || !isAlive(pid))
54
+ return null;
55
+ return looksLikeMetro(commandOf(pid)) ? pid : null;
56
+ }
57
+ export const lockedBy = () => holderOf(lockPath());
58
+ export const serveLockedBy = () => holderOf(serveLockPath());
59
+ export async function stopAll(waitMs = 10_000) {
60
+ const stopped = [];
61
+ const holder = lockedBy();
62
+ if (holder !== null) {
63
+ await stopPid(holder, waitMs);
64
+ rmSync(lockPath(), { force: true });
65
+ stopped.push({ pid: holder, via: 'the machine lock' });
66
+ }
67
+ const served = serveLockedBy();
68
+ if (served !== null && served !== holder) {
69
+ await stopPid(served, waitMs);
70
+ rmSync(serveLockPath(), { force: true });
71
+ stopped.push({ pid: served, via: 'metro serve' });
72
+ }
73
+ return stopped;
74
+ }
package/dist/local.js ADDED
@@ -0,0 +1,67 @@
1
+ import { existsSync, readdirSync, readFileSync } from 'node:fs';
2
+ import { homedir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { localUrl } from './runtime.js';
5
+ const PROBE_MS = 3_000;
6
+ const READ_MS = 20_000;
7
+ export function agentsDir() {
8
+ const explicit = process.env.METRO_AGENTS_DIR?.trim();
9
+ return explicit !== undefined && explicit !== '' ? explicit : join(homedir(), '.metro', 'agents');
10
+ }
11
+ export function localAgents(dir = agentsDir()) {
12
+ if (!existsSync(dir))
13
+ return [];
14
+ const out = [];
15
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
16
+ if (!entry.isDirectory())
17
+ continue;
18
+ const path = join(dir, entry.name, 'agent.json');
19
+ if (!existsSync(path))
20
+ continue;
21
+ try {
22
+ const file = JSON.parse(readFileSync(path, 'utf8'));
23
+ if (typeof file.id === 'string' && typeof file.name === 'string' && typeof file.key === 'string')
24
+ out.push({ id: file.id, name: file.name, key: file.key });
25
+ }
26
+ catch {
27
+ continue;
28
+ }
29
+ }
30
+ return out.sort((a, b) => a.name.localeCompare(b.name));
31
+ }
32
+ export function pickLocalAgent(agents, wanted) {
33
+ if (wanted !== undefined && wanted !== '') {
34
+ const found = agents.find((a) => a.name === wanted || a.id === wanted);
35
+ if (found === undefined)
36
+ throw new Error(`no local agent named '${wanted}'`);
37
+ return found;
38
+ }
39
+ const only = agents[0];
40
+ if (only !== undefined && agents.length === 1)
41
+ return only;
42
+ throw new Error(agents.length === 0
43
+ ? 'no agent on this machine yet'
44
+ : `several agents on this machine — name one: ${agents.map((a) => a.name).join(', ')}`);
45
+ }
46
+ export async function localDaemonUp(base = localUrl()) {
47
+ try {
48
+ const res = await fetch(`${base}/api/mode`, { signal: AbortSignal.timeout(PROBE_MS) });
49
+ const body = (await res.json());
50
+ return res.ok && body.mode === 'local';
51
+ }
52
+ catch {
53
+ return false;
54
+ }
55
+ }
56
+ export async function localMcpServers(agent, base = localUrl()) {
57
+ const res = await fetch(`${base}/api/cli/mcp`, {
58
+ headers: { authorization: `Bearer ${agent.key}` },
59
+ signal: AbortSignal.timeout(READ_MS),
60
+ });
61
+ if (!res.ok)
62
+ throw new Error(`the local daemon answered ${String(res.status)} for the connectors`);
63
+ const body = (await res.json());
64
+ if (typeof body.json !== 'string')
65
+ throw new Error('the local daemon returned an unexpected response');
66
+ return body.json;
67
+ }
package/dist/plugin.js ADDED
@@ -0,0 +1,80 @@
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
+ const MARKETPLACE_REPO = 'bonustrack/metro';
6
+ const PLUGIN_SPEC = 'metro@metro';
7
+ export function installPathFrom(raw) {
8
+ let parsed;
9
+ try {
10
+ parsed = JSON.parse(raw);
11
+ }
12
+ catch {
13
+ return null;
14
+ }
15
+ if (typeof parsed !== 'object' || parsed === null)
16
+ return null;
17
+ const plugins = parsed.plugins;
18
+ if (typeof plugins !== 'object' || plugins === null)
19
+ return null;
20
+ const entries = plugins[PLUGIN_SPEC];
21
+ if (!Array.isArray(entries))
22
+ return null;
23
+ const first = entries[0];
24
+ return typeof first?.installPath === 'string' ? first.installPath : null;
25
+ }
26
+ export function pluginInstallPath() {
27
+ const registry = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
28
+ let raw;
29
+ try {
30
+ raw = readFileSync(registry, 'utf8');
31
+ }
32
+ catch {
33
+ return null;
34
+ }
35
+ const path = installPathFrom(raw);
36
+ if (path === null)
37
+ return null;
38
+ return existsSync(join(path, 'bin', 'metro-plugin.mjs')) ? path : null;
39
+ }
40
+ function runClaude(args) {
41
+ const res = spawnSync('claude', args, { encoding: 'utf8' });
42
+ if (res.error !== undefined)
43
+ throw new Error('the `claude` command is not on PATH — install Claude Code first');
44
+ const output = `${res.stdout ?? ''}${res.stderr ?? ''}`;
45
+ return { ok: res.status === 0, output };
46
+ }
47
+ const tolerable = (output) => /already|exists|latest version/i.test(output);
48
+ export function syncPluginServers() {
49
+ const path = pluginInstallPath();
50
+ if (path === null)
51
+ return false;
52
+ const script = join(path, 'bin', 'metro-plugin.mjs');
53
+ const refreshed = spawnSync(process.execPath, [script, 'refresh'], {
54
+ stdio: ['ignore', 'inherit', 'inherit'],
55
+ });
56
+ if (refreshed.status !== 0)
57
+ return false;
58
+ runClaude(['plugin', 'update', PLUGIN_SPEC]);
59
+ return true;
60
+ }
61
+ export async function installPlugin() {
62
+ const added = runClaude(['plugin', 'marketplace', 'add', MARKETPLACE_REPO]);
63
+ if (!added.ok && !tolerable(added.output)) {
64
+ process.stderr.write(added.output);
65
+ return 1;
66
+ }
67
+ const installed = runClaude(['plugin', 'install', PLUGIN_SPEC]);
68
+ if (!installed.ok && !tolerable(installed.output)) {
69
+ process.stderr.write(installed.output);
70
+ return 1;
71
+ }
72
+ process.stderr.write('Claude Code plugin installed.\n');
73
+ if (syncPluginServers()) {
74
+ process.stderr.write('Connector servers loaded — new Claude sessions have them; ' +
75
+ 'run /reload-plugins in any session already open.\n');
76
+ return 0;
77
+ }
78
+ process.stderr.write('Start the daemon on this machine (metro serve), then run /metro:refresh inside Claude Code.\n');
79
+ return Promise.resolve(0);
80
+ }
@@ -0,0 +1,50 @@
1
+ import { spawn, spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { dirname, join } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ const AGENT_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{10}$/;
6
+ export const SERVER_ENTRY = join('node_modules', '@metro-labs', 'mcp', 'src', 'server.ts');
7
+ export class MissingRuntime extends Error {
8
+ }
9
+ export function runtimeDir() {
10
+ const explicit = process.env.METRO_RUNTIME_DIR?.trim();
11
+ const dir = explicit !== undefined && explicit !== ''
12
+ ? explicit
13
+ : join(dirname(dirname(fileURLToPath(import.meta.url))), 'runtime');
14
+ if (!existsSync(join(dir, SERVER_ENTRY)))
15
+ throw new MissingRuntime(`no metro daemon at ${dir}. Reinstall with: npm i -g @stage-labs/metro@beta`);
16
+ return dir;
17
+ }
18
+ class MissingBun extends Error {
19
+ }
20
+ export function assertAgentId(id) {
21
+ if (id === undefined || !AGENT_RE.test(id))
22
+ throw new Error(`'${id ?? ''}' is not an agent id — copy it from the agent's page in the web UI`);
23
+ return id;
24
+ }
25
+ export function findBun() {
26
+ const found = spawnSync('bun', ['--version'], { stdio: 'ignore' });
27
+ if (found.error !== undefined || found.status !== 0)
28
+ throw new MissingBun('metro serve needs Bun, which is not on PATH.\n' +
29
+ 'Install it with: curl -fsSL https://bun.sh/install | bash');
30
+ return 'bun';
31
+ }
32
+ export const localPort = () => Number(process.env.METRO_WEBHOOK_PORT) || 8420;
33
+ export const localUrl = () => `http://127.0.0.1:${String(localPort())}`;
34
+ export function spawnPlan(plan) {
35
+ const child = spawn(plan.command, plan.args, {
36
+ cwd: plan.cwd,
37
+ env: plan.env,
38
+ stdio: ['ignore', 'inherit', 'inherit'],
39
+ });
40
+ const relay = (sig) => () => {
41
+ child.kill(sig);
42
+ };
43
+ process.on('SIGINT', relay('SIGINT'));
44
+ process.on('SIGTERM', relay('SIGTERM'));
45
+ return new Promise((resolve) => {
46
+ child.on('exit', (code) => {
47
+ resolve(code ?? 0);
48
+ });
49
+ });
50
+ }
package/dist/serve.js ADDED
@@ -0,0 +1,109 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { agentsDir } from './local.js';
5
+ import { currentVersion } from './version.js';
6
+ import { serveLockedBy, serveStateDir } from './control.js';
7
+ import { findBun, localPort, runtimeDir, SERVER_ENTRY, spawnPlan, } from './runtime.js';
8
+ const SCRUBBED = new Set(['METRO_RUN_TOKEN', 'METRO_AGENT', 'DATABASE_URL']);
9
+ const PORT_FLAG = /^--port=(.*)$/;
10
+ const OWNER_FLAG = /^--owner=(.*)$/;
11
+ const ADDRESS = /^0x[0-9a-fA-F]{40}$/;
12
+ const USAGE = 'usage: metro serve [--port <n>] [--tunnel] [--owner <address>]';
13
+ function portOf(raw) {
14
+ const n = Number(raw);
15
+ if (raw === undefined || raw === '' || !Number.isInteger(n) || n < 1 || n > 65535)
16
+ throw new Error(`'${raw ?? ''}' is not a port — ${USAGE}`);
17
+ return n;
18
+ }
19
+ function ownerOf(raw) {
20
+ if (raw === undefined || !ADDRESS.test(raw))
21
+ throw new Error(`'${raw ?? ''}' is not an Ethereum address — ${USAGE}`);
22
+ return raw.toLowerCase();
23
+ }
24
+ export function parseServeArgs(argv) {
25
+ let port = localPort();
26
+ let tunnel = false;
27
+ let owner = null;
28
+ for (let i = 0; i < argv.length; i += 1) {
29
+ const arg = argv[i] ?? '';
30
+ if (arg === '--tunnel') {
31
+ tunnel = true;
32
+ continue;
33
+ }
34
+ const inlineOwner = OWNER_FLAG.exec(arg);
35
+ if (inlineOwner) {
36
+ owner = ownerOf(inlineOwner[1]);
37
+ continue;
38
+ }
39
+ if (arg === '--owner') {
40
+ owner = ownerOf(argv[i + 1]);
41
+ i += 1;
42
+ continue;
43
+ }
44
+ const inline = PORT_FLAG.exec(arg);
45
+ if (inline) {
46
+ port = portOf(inline[1]);
47
+ continue;
48
+ }
49
+ if (arg === '--port' || arg === '-p') {
50
+ port = portOf(argv[i + 1]);
51
+ i += 1;
52
+ continue;
53
+ }
54
+ throw new Error(`unknown argument '${arg}' — ${USAGE}`);
55
+ }
56
+ return { port, tunnel, owner };
57
+ }
58
+ export function requireOwner(owner, dir = agentsDir()) {
59
+ if (owner !== null || existsSync(join(dir, '.owner')))
60
+ return;
61
+ throw new Error('no owner is set for this machine, so no wallet could sign in.\n' +
62
+ 'Pass the wallet that owns it once; it is remembered in ' +
63
+ join(dir, '.owner') +
64
+ ':\n metro serve --owner <address>');
65
+ }
66
+ function findCloudflared() {
67
+ const found = spawnSync('cloudflared', ['--version'], { stdio: 'ignore' });
68
+ if (found.error !== undefined || found.status !== 0)
69
+ throw new Error('metro serve --tunnel needs cloudflared, which is not on PATH.\n' +
70
+ 'Debian/Ubuntu (it is not in the distro repos; this adds Cloudflare\'s signed one):\n' +
71
+ ' sudo mkdir -p --mode=0755 /usr/share/keyrings\n' +
72
+ ' curl -fsSL https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-main.gpg >/dev/null\n' +
73
+ " 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" +
74
+ ' sudo apt-get update && sudo apt-get install cloudflared\n' +
75
+ 'macOS: brew install cloudflared\n' +
76
+ 'Others: https://pkg.cloudflare.com/');
77
+ }
78
+ export function servePlan(opts) {
79
+ const env = Object.fromEntries(Object.entries(process.env).filter(([key]) => !SCRUBBED.has(key)));
80
+ return {
81
+ command: findBun(),
82
+ args: [SERVER_ENTRY],
83
+ cwd: opts.dir,
84
+ env: {
85
+ ...env,
86
+ METRO_MODE: 'local',
87
+ METRO_VERSION: currentVersion(),
88
+ METRO_WEBHOOK_PORT: String(opts.port),
89
+ METRO_HTTP_HOST: process.env.METRO_HTTP_HOST ?? '127.0.0.1',
90
+ METRO_TRAINS_DIR: join(opts.dir, 'trains'),
91
+ METRO_STATE_DIR: process.env.METRO_STATE_DIR ?? serveStateDir(),
92
+ ...(opts.tunnel ? { METRO_TUNNEL: 'quick' } : {}),
93
+ ...(opts.owner === null ? {} : { METRO_OWNER: opts.owner }),
94
+ },
95
+ };
96
+ }
97
+ export function serve(argv) {
98
+ const { port, tunnel, owner } = parseServeArgs(argv);
99
+ const running = serveLockedBy();
100
+ if (running !== null)
101
+ throw new Error(`a metro serve daemon is already running on this machine (pid ${String(running)}). ` +
102
+ 'Stop it first: metro stop');
103
+ requireOwner(owner);
104
+ if (tunnel)
105
+ findCloudflared();
106
+ const plan = servePlan({ dir: runtimeDir(), port, tunnel, owner });
107
+ process.stderr.write(`Starting a metro daemon of your own on http://127.0.0.1:${String(port)}\n`);
108
+ return spawnPlan(plan);
109
+ }
package/dist/tail.js ADDED
@@ -0,0 +1,119 @@
1
+ import { localAgents } from './local.js';
2
+ import { assertAgentId, localUrl } 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 agent = localAgents().find((a) => a.id === agentId);
26
+ if (agent === undefined)
27
+ throw new Error(`no agent ${agentId} on this machine — create or restore it in the web UI of metro serve, or set METRO_AGENT_KEY`);
28
+ return Promise.resolve(agent.key);
29
+ },
30
+ fixed: false,
31
+ };
32
+ }
33
+ const armStall = (control) => setTimeout(() => {
34
+ control.abort();
35
+ }, STALL_MS);
36
+ async function connect(key, signal) {
37
+ try {
38
+ return await fetch(`${localUrl()}/api/tail`, {
39
+ headers: { authorization: `Bearer ${key}` },
40
+ signal,
41
+ });
42
+ }
43
+ catch {
44
+ return null;
45
+ }
46
+ }
47
+ async function pump(body, control, stall) {
48
+ const reader = body.getReader();
49
+ const decoder = new TextDecoder();
50
+ let buffer = '';
51
+ try {
52
+ for (;;) {
53
+ const { done, value } = await reader.read();
54
+ if (done)
55
+ return 'ended';
56
+ clearTimeout(stall.timer);
57
+ stall.timer = armStall(control);
58
+ const out = sseDataLines(buffer, decoder.decode(value, { stream: true }));
59
+ buffer = out.rest;
60
+ for (const event of out.events)
61
+ process.stdout.write(`${event}\n`);
62
+ }
63
+ }
64
+ catch {
65
+ return 'ended';
66
+ }
67
+ }
68
+ async function streamOnce(key) {
69
+ const control = new AbortController();
70
+ const stall = { timer: armStall(control) };
71
+ try {
72
+ const res = await connect(key, control.signal);
73
+ if (res === null)
74
+ return 'unreachable';
75
+ if (res.status === 401)
76
+ return 'unauthorized';
77
+ if (!res.ok || res.body === null)
78
+ return 'unreachable';
79
+ return await pump(res.body, control, stall);
80
+ }
81
+ finally {
82
+ clearTimeout(stall.timer);
83
+ }
84
+ }
85
+ function note(state, message) {
86
+ if (state.last === message)
87
+ return;
88
+ state.last = message;
89
+ process.stderr.write(`metro: ${message}\n`);
90
+ }
91
+ const delay = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
92
+ async function settle(end, source, key, agentId, state) {
93
+ if (end === 'unauthorized') {
94
+ if (source.fixed)
95
+ throw new Error('the key in METRO_AGENT_KEY was refused by the local daemon');
96
+ note(state, 'key refused — fetching the current one');
97
+ return source.resolve();
98
+ }
99
+ if (end === 'unreachable')
100
+ note(state, `no metro daemon on ${localUrl()} — run 'metro serve' (agent ${agentId})`);
101
+ else
102
+ note(state, 'stream ended — reconnecting');
103
+ return key;
104
+ }
105
+ export async function tailEvents(argv) {
106
+ const agentId = assertAgentId(argv[0]);
107
+ const source = keySource(agentId);
108
+ let key = await source.resolve();
109
+ const state = { last: '' };
110
+ let attempt = 0;
111
+ for (;;) {
112
+ const end = await streamOnce(key);
113
+ if (end === 'ended')
114
+ attempt = 0;
115
+ key = await settle(end, source, key, agentId, state);
116
+ await delay(backoffAt(attempt));
117
+ attempt += 1;
118
+ }
119
+ }
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
+ }