@stage-labs/metro 0.1.0-beta.13 → 0.1.0-beta.130
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.
- package/dist/api.js +14 -0
- package/dist/claude-settings.js +40 -0
- package/dist/claude.js +176 -0
- package/dist/cli.js +101 -0
- package/dist/control.js +74 -0
- package/dist/hold.js +211 -0
- package/dist/local.js +93 -0
- package/dist/mcp-config.js +25 -0
- package/dist/node-name.js +71 -0
- package/dist/onboarding.js +43 -0
- package/dist/plugin.js +120 -0
- package/dist/provider-flags.js +9 -0
- package/dist/route.js +38 -0
- package/dist/runtime-install.js +97 -0
- package/dist/runtime.js +74 -0
- package/dist/serve.js +150 -0
- package/dist/service.js +242 -0
- package/dist/tail.js +119 -0
- package/dist/update.js +45 -0
- package/dist/version.js +97 -0
- package/package.json +14 -27
- package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
- package/runtime/marketplace/plugin/.claude-plugin/plugin.json +18 -0
- package/runtime/marketplace/plugin/.mcp.json +1 -0
- package/runtime/marketplace/plugin/bin/guard.mjs +55 -0
- package/runtime/marketplace/plugin/bin/metro-plugin.mjs +122 -0
- package/runtime/marketplace/plugin/bin/session-start.mjs +31 -0
- package/runtime/marketplace/plugin/hooks/hooks.json +25 -0
- package/runtime/marketplace/plugin/orchestrator.md +16 -0
- package/runtime/marketplace/plugin/skills/refresh/SKILL.md +15 -0
- package/runtime/node_modules/@metro-labs/core/package.json +43 -0
- package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
- package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
- package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
- package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
- package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
- package/runtime/node_modules/@metro-labs/core/src/lines.ts +87 -0
- package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
- package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
- package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
- package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +108 -0
- package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
- package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
- package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/package.json +48 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +70 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +365 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +300 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +122 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +371 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +95 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +207 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +309 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +358 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +169 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +56 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +140 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +253 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +239 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +109 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +310 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +175 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +77 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +166 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +139 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +171 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +155 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +122 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +296 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +286 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +332 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +259 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +231 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +123 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +187 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +229 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +269 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +207 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +65 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +229 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +151 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
- package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/body.ts +24 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +372 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +223 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/threema-callback.ts +103 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +248 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +261 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +96 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
- package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
- package/runtime/node_modules/@metro-labs/http/package.json +33 -0
- package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
- package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
- package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
- package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
- package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
- package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
- package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
- package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
- package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
- package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
- package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
- package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
- package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
- package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +100 -0
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +219 -0
- package/runtime/node_modules/@metro-labs/threema/src/api.ts +130 -0
- package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +132 -0
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +123 -0
- package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/threema/src/index.ts +29 -0
- package/runtime/node_modules/@metro-labs/threema/src/station.ts +10 -0
- package/runtime/node_modules/@metro-labs/threema/src/verify.ts +117 -0
- package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
- package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +190 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +347 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +18 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
- package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
- package/runtime/runtime.json +3 -0
- package/runtime/server.ts +1 -0
- package/runtime/stations.json +39 -0
- package/runtime/trains/.keep +0 -0
- package/LICENSE +0 -21
- package/README.md +0 -252
- package/dist/broker.js +0 -202
- package/dist/cache.js +0 -69
- package/dist/cli/actions.js +0 -206
- package/dist/cli/config.js +0 -185
- package/dist/cli/index.js +0 -188
- package/dist/cli/skill.js +0 -62
- package/dist/cli/tail.js +0 -164
- package/dist/cli/util.js +0 -72
- package/dist/cli/webhook.js +0 -81
- package/dist/codex-rc.js +0 -245
- package/dist/dispatcher.js +0 -128
- package/dist/history.js +0 -121
- package/dist/ipc.js +0 -72
- package/dist/log.js +0 -15
- package/dist/monitor.js +0 -194
- package/dist/paths.js +0 -68
- package/dist/registry.js +0 -48
- package/dist/stations/claude.js +0 -45
- package/dist/stations/codex.js +0 -68
- package/dist/stations/discord.js +0 -216
- package/dist/stations/index.js +0 -129
- package/dist/stations/telegram-md.js +0 -34
- package/dist/stations/telegram-upload.js +0 -113
- package/dist/stations/telegram.js +0 -234
- package/dist/stations/webhook.js +0 -103
- package/dist/tunnel.js +0 -64
- package/dist/webhooks.js +0 -41
- package/docs/broker.md +0 -214
- package/docs/monitor.md +0 -163
- package/docs/uri-scheme.md +0 -124
- package/docs/users.md +0 -226
- package/skills/metro/SKILL.md +0 -256
package/dist/local.js
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
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 localStations(dir = agentsDir()) {
|
|
33
|
+
if (!existsSync(dir))
|
|
34
|
+
return [];
|
|
35
|
+
const out = new Set();
|
|
36
|
+
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
|
37
|
+
if (!entry.isDirectory())
|
|
38
|
+
continue;
|
|
39
|
+
const path = join(dir, entry.name, 'agent.json');
|
|
40
|
+
if (!existsSync(path))
|
|
41
|
+
continue;
|
|
42
|
+
try {
|
|
43
|
+
const file = JSON.parse(readFileSync(path, 'utf8'));
|
|
44
|
+
if (!Array.isArray(file.stations))
|
|
45
|
+
continue;
|
|
46
|
+
for (const s of file.stations) {
|
|
47
|
+
const station = s.station;
|
|
48
|
+
if (typeof station === 'string')
|
|
49
|
+
out.add(station);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return [...out].sort();
|
|
57
|
+
}
|
|
58
|
+
export function pickLocalAgent(agents, wanted) {
|
|
59
|
+
if (wanted !== undefined && wanted !== '') {
|
|
60
|
+
const found = agents.find((a) => a.name === wanted || a.id === wanted);
|
|
61
|
+
if (found === undefined)
|
|
62
|
+
throw new Error(`no local agent named '${wanted}'`);
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
const only = agents[0];
|
|
66
|
+
if (only !== undefined && agents.length === 1)
|
|
67
|
+
return only;
|
|
68
|
+
throw new Error(agents.length === 0
|
|
69
|
+
? 'no agent on this machine yet'
|
|
70
|
+
: `several agents on this machine — name one: ${agents.map((a) => a.name).join(', ')}`);
|
|
71
|
+
}
|
|
72
|
+
export async function localDaemonUp(base = localUrl()) {
|
|
73
|
+
try {
|
|
74
|
+
const res = await fetch(`${base}/api/mode`, { signal: AbortSignal.timeout(PROBE_MS) });
|
|
75
|
+
const body = (await res.json());
|
|
76
|
+
return res.ok && body.mode === 'local';
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export async function localMcpServers(agent, base = localUrl()) {
|
|
83
|
+
const res = await fetch(`${base}/api/cli/mcp`, {
|
|
84
|
+
headers: { authorization: `Bearer ${agent.key}` },
|
|
85
|
+
signal: AbortSignal.timeout(READ_MS),
|
|
86
|
+
});
|
|
87
|
+
if (!res.ok)
|
|
88
|
+
throw new Error(`the local daemon answered ${String(res.status)} for the connectors`);
|
|
89
|
+
const body = (await res.json());
|
|
90
|
+
if (typeof body.json !== 'string')
|
|
91
|
+
throw new Error('the local daemon returned an unexpected response');
|
|
92
|
+
return body.json;
|
|
93
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const METRO_SERVER = 'metro';
|
|
5
|
+
export function metroMcpConfig(agentKey, port) {
|
|
6
|
+
const servers = {
|
|
7
|
+
[METRO_SERVER]: {
|
|
8
|
+
type: 'http',
|
|
9
|
+
url: `http://127.0.0.1:${String(port)}/mcp`,
|
|
10
|
+
headers: { Authorization: `Bearer ${agentKey}` },
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
return `${JSON.stringify({ mcpServers: servers }, null, 2)}\n`;
|
|
14
|
+
}
|
|
15
|
+
export function writeMcpConfig(agentKey, port, base = tmpdir()) {
|
|
16
|
+
const dir = mkdtempSync(join(base, 'metro-claude-'));
|
|
17
|
+
const path = join(dir, 'mcp.json');
|
|
18
|
+
writeFileSync(path, metroMcpConfig(agentKey, port), { mode: 0o600 });
|
|
19
|
+
return {
|
|
20
|
+
path,
|
|
21
|
+
cleanup: () => {
|
|
22
|
+
rmSync(dir, { recursive: true, force: true });
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { randomBytes } from 'node:crypto';
|
|
3
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { agentsDir } from './local.js';
|
|
6
|
+
const NODE_FILE = '.node';
|
|
7
|
+
const ALPHABET = 'abcdefghjkmnpqrstuvwxyz23456789';
|
|
8
|
+
const NAME_RE = /^metro-[a-z0-9](?:[a-z0-9-]{0,38}[a-z0-9])?$/;
|
|
9
|
+
const RENAME_WAIT_MS = 30_000;
|
|
10
|
+
const RENAME_POLL_MS = 500;
|
|
11
|
+
const renameWaitMs = () => Number(process.env.METRO_NODE_RENAME_WAIT_MS) || RENAME_WAIT_MS;
|
|
12
|
+
const pause = (ms) => {
|
|
13
|
+
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
|
14
|
+
};
|
|
15
|
+
const stderrLine = (line) => {
|
|
16
|
+
process.stderr.write(`${line}\n`);
|
|
17
|
+
};
|
|
18
|
+
export function newNodeName() {
|
|
19
|
+
const bytes = randomBytes(6);
|
|
20
|
+
return `metro-${[...bytes].map((b) => ALPHABET[b % ALPHABET.length] ?? 'x').join('')}`;
|
|
21
|
+
}
|
|
22
|
+
export function nodeName(dir = agentsDir()) {
|
|
23
|
+
const path = join(dir, NODE_FILE);
|
|
24
|
+
if (existsSync(path)) {
|
|
25
|
+
const held = readFileSync(path, 'utf8').trim();
|
|
26
|
+
if (NAME_RE.test(held))
|
|
27
|
+
return held;
|
|
28
|
+
}
|
|
29
|
+
const name = newNodeName();
|
|
30
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
31
|
+
writeFileSync(path, `${name}\n`, { mode: 0o600 });
|
|
32
|
+
return name;
|
|
33
|
+
}
|
|
34
|
+
export function currentNodeLabel(bin) {
|
|
35
|
+
const run = spawnSync(bin, ['status', '--json'], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'] });
|
|
36
|
+
if (run.error !== undefined || run.status !== 0)
|
|
37
|
+
return null;
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(run.stdout);
|
|
40
|
+
const dns = typeof parsed.Self?.DNSName === 'string' ? parsed.Self.DNSName : '';
|
|
41
|
+
const label = dns.split('.')[0] ?? '';
|
|
42
|
+
return label === '' ? null : label.toLowerCase();
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function awaitRename(bin, wanted, warn) {
|
|
49
|
+
const wait = renameWaitMs();
|
|
50
|
+
const until = Date.now() + wait;
|
|
51
|
+
while (Date.now() < until) {
|
|
52
|
+
if (currentNodeLabel(bin) === wanted)
|
|
53
|
+
return;
|
|
54
|
+
pause(RENAME_POLL_MS);
|
|
55
|
+
}
|
|
56
|
+
warn(`tailscale still reports the old machine name after ${String(Math.round(wait / 1000))}s; the Funnel address may come up under it until the next restart`);
|
|
57
|
+
}
|
|
58
|
+
export function ensureNodeName(bin, dir = agentsDir(), warn = stderrLine) {
|
|
59
|
+
const wanted = nodeName(dir);
|
|
60
|
+
if (currentNodeLabel(bin) === wanted)
|
|
61
|
+
return wanted;
|
|
62
|
+
const run = spawnSync(bin, ['set', '--hostname', wanted], { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
63
|
+
if (run.error !== undefined || run.status !== 0) {
|
|
64
|
+
const said = run.stderr.trim();
|
|
65
|
+
const reason = said === '' ? (run.error?.message ?? `exit ${String(run.status)}`) : said;
|
|
66
|
+
throw new Error(`could not name this machine ${wanted} on your tailnet (${reason}).\n` +
|
|
67
|
+
`Run it once yourself, then start again: sudo tailscale set --hostname ${wanted}`);
|
|
68
|
+
}
|
|
69
|
+
awaitRename(bin, wanted, warn);
|
|
70
|
+
return wanted;
|
|
71
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const FILE = '.claude.json';
|
|
5
|
+
const FLAG = 'hasCompletedOnboarding';
|
|
6
|
+
const given = (value) => value !== undefined && value.trim() !== '' ? value.trim() : undefined;
|
|
7
|
+
export function claudeConfigPath(env = process.env) {
|
|
8
|
+
return join(given(env.CLAUDE_CONFIG_DIR) ?? given(env.HOME) ?? homedir(), FILE);
|
|
9
|
+
}
|
|
10
|
+
function readConfig(path) {
|
|
11
|
+
if (!existsSync(path))
|
|
12
|
+
return {};
|
|
13
|
+
try {
|
|
14
|
+
const parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
15
|
+
return typeof parsed === 'object' && parsed !== null && !Array.isArray(parsed) ? parsed : null;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function markOnboardingDone(path = claudeConfigPath()) {
|
|
22
|
+
const config = readConfig(path);
|
|
23
|
+
if (config === null)
|
|
24
|
+
return 'unreadable';
|
|
25
|
+
if (config[FLAG] === true)
|
|
26
|
+
return 'already';
|
|
27
|
+
writeFileSync(path, `${JSON.stringify({ ...config, [FLAG]: true }, null, 2)}\n`, { mode: 0o600 });
|
|
28
|
+
return 'marked';
|
|
29
|
+
}
|
|
30
|
+
const FEATURES = 'cachedGrowthBookFeatures';
|
|
31
|
+
const CHANNELS_FLAG = 'tengu_harbor';
|
|
32
|
+
const isRecord = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
33
|
+
export function seedChannels(path = claudeConfigPath()) {
|
|
34
|
+
const config = readConfig(path);
|
|
35
|
+
if (config === null)
|
|
36
|
+
return 'unreadable';
|
|
37
|
+
const features = isRecord(config[FEATURES]) ? config[FEATURES] : {};
|
|
38
|
+
if (features[CHANNELS_FLAG] === true)
|
|
39
|
+
return 'already';
|
|
40
|
+
const next = { ...config, [FEATURES]: { ...features, [CHANNELS_FLAG]: true } };
|
|
41
|
+
writeFileSync(path, `${JSON.stringify(next, null, 2)}\n`, { mode: 0o600 });
|
|
42
|
+
return 'marked';
|
|
43
|
+
}
|
package/dist/plugin.js
ADDED
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { MARKETPLACE, runtimeStore } from './runtime-install.js';
|
|
6
|
+
const MARKETPLACE_REPO = 'bonustrack/metro';
|
|
7
|
+
const MARKETPLACE_NAME = 'metro';
|
|
8
|
+
const PLUGIN_SPEC = 'metro@metro';
|
|
9
|
+
export function marketplaceSource(store = runtimeStore()) {
|
|
10
|
+
const bundled = join(store, MARKETPLACE);
|
|
11
|
+
return existsSync(join(bundled, '.claude-plugin', 'marketplace.json')) ? bundled : MARKETPLACE_REPO;
|
|
12
|
+
}
|
|
13
|
+
const realish = (path) => {
|
|
14
|
+
try {
|
|
15
|
+
return realpathSync(path);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return path;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
export function registeredMarketplacePath(listJson) {
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(listJson);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
if (!Array.isArray(parsed))
|
|
30
|
+
return undefined;
|
|
31
|
+
const mine = parsed.find((m) => typeof m === 'object' && m !== null && m.name === MARKETPLACE_NAME);
|
|
32
|
+
if (mine === undefined)
|
|
33
|
+
return undefined;
|
|
34
|
+
const at = mine.installLocation ?? mine.path;
|
|
35
|
+
return typeof at === 'string' ? at : null;
|
|
36
|
+
}
|
|
37
|
+
export function installPathFrom(raw) {
|
|
38
|
+
let parsed;
|
|
39
|
+
try {
|
|
40
|
+
parsed = JSON.parse(raw);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
if (typeof parsed !== 'object' || parsed === null)
|
|
46
|
+
return null;
|
|
47
|
+
const plugins = parsed.plugins;
|
|
48
|
+
if (typeof plugins !== 'object' || plugins === null)
|
|
49
|
+
return null;
|
|
50
|
+
const entries = plugins[PLUGIN_SPEC];
|
|
51
|
+
if (!Array.isArray(entries))
|
|
52
|
+
return null;
|
|
53
|
+
const first = entries[0];
|
|
54
|
+
return typeof first?.installPath === 'string' ? first.installPath : null;
|
|
55
|
+
}
|
|
56
|
+
export function pluginInstallPath() {
|
|
57
|
+
const registry = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
|
|
58
|
+
let raw;
|
|
59
|
+
try {
|
|
60
|
+
raw = readFileSync(registry, 'utf8');
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
const path = installPathFrom(raw);
|
|
66
|
+
if (path === null)
|
|
67
|
+
return null;
|
|
68
|
+
return existsSync(join(path, 'bin', 'metro-plugin.mjs')) ? path : null;
|
|
69
|
+
}
|
|
70
|
+
function runClaude(args) {
|
|
71
|
+
const res = spawnSync('claude', args, { encoding: 'utf8' });
|
|
72
|
+
if (res.error !== undefined)
|
|
73
|
+
throw new Error('the `claude` command is not on PATH — install Claude Code first');
|
|
74
|
+
const output = `${res.stdout ?? ''}${res.stderr ?? ''}`;
|
|
75
|
+
return { ok: res.status === 0, output };
|
|
76
|
+
}
|
|
77
|
+
const tolerable = (output) => /already|exists|latest version/i.test(output);
|
|
78
|
+
export function syncPluginServers() {
|
|
79
|
+
const path = pluginInstallPath();
|
|
80
|
+
if (path === null)
|
|
81
|
+
return false;
|
|
82
|
+
const script = join(path, 'bin', 'metro-plugin.mjs');
|
|
83
|
+
const refreshed = spawnSync(process.execPath, [script, 'refresh'], {
|
|
84
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
85
|
+
});
|
|
86
|
+
if (refreshed.status !== 0)
|
|
87
|
+
return false;
|
|
88
|
+
runClaude(['plugin', 'update', PLUGIN_SPEC]);
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
function ensureMarketplace(source) {
|
|
92
|
+
const listed = runClaude(['plugin', 'marketplace', 'list', '--json']);
|
|
93
|
+
const at = registeredMarketplacePath(listed.output);
|
|
94
|
+
if (at !== undefined && at !== null && (at === source || realish(at) === realish(source)))
|
|
95
|
+
return { ok: true, output: '' };
|
|
96
|
+
if (at !== undefined)
|
|
97
|
+
runClaude(['plugin', 'marketplace', 'remove', MARKETPLACE_NAME]);
|
|
98
|
+
return runClaude(['plugin', 'marketplace', 'add', source]);
|
|
99
|
+
}
|
|
100
|
+
export async function installPlugin() {
|
|
101
|
+
const source = marketplaceSource();
|
|
102
|
+
const added = ensureMarketplace(source);
|
|
103
|
+
if (!added.ok && !tolerable(added.output)) {
|
|
104
|
+
process.stderr.write(added.output);
|
|
105
|
+
return 1;
|
|
106
|
+
}
|
|
107
|
+
const installed = runClaude(['plugin', 'install', PLUGIN_SPEC, '-y', '--scope', 'user']);
|
|
108
|
+
if (!installed.ok && !tolerable(installed.output)) {
|
|
109
|
+
process.stderr.write(installed.output);
|
|
110
|
+
return 1;
|
|
111
|
+
}
|
|
112
|
+
process.stderr.write(`Claude Code plugin installed from ${source}.\n`);
|
|
113
|
+
if (syncPluginServers()) {
|
|
114
|
+
process.stderr.write('Connector servers loaded — new Claude sessions have them; ' +
|
|
115
|
+
'run /reload-plugins --force in any session already open.\n');
|
|
116
|
+
return 0;
|
|
117
|
+
}
|
|
118
|
+
process.stderr.write('Start the daemon on this machine (metro serve), then run /metro:refresh inside Claude Code.\n');
|
|
119
|
+
return Promise.resolve(0);
|
|
120
|
+
}
|
package/dist/route.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { agentsDir } from './local.js';
|
|
4
|
+
const PROVIDERS = ['bedrock', 'openrouter', 'codex'];
|
|
5
|
+
export function currentRoute(dir = agentsDir()) {
|
|
6
|
+
const path = join(dir, 'model.json');
|
|
7
|
+
if (!existsSync(path))
|
|
8
|
+
return null;
|
|
9
|
+
try {
|
|
10
|
+
const cfg = JSON.parse(readFileSync(path, 'utf8'));
|
|
11
|
+
const provider = PROVIDERS.find((p) => p === cfg.provider);
|
|
12
|
+
if (provider === undefined)
|
|
13
|
+
return null;
|
|
14
|
+
const block = cfg[provider];
|
|
15
|
+
const model = typeof block === 'object' && block !== null ? block.model : undefined;
|
|
16
|
+
return typeof model === 'string' && model !== '' ? `${provider}:${model}` : null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export function routeModelEnv(env, route) {
|
|
23
|
+
if (route === null || (env.ANTHROPIC_MODEL ?? '').trim() !== '')
|
|
24
|
+
return env;
|
|
25
|
+
return { ...env, ANTHROPIC_MODEL: route };
|
|
26
|
+
}
|
|
27
|
+
export function permissionMode(dir = agentsDir()) {
|
|
28
|
+
const path = join(dir, 'claude-setup.json');
|
|
29
|
+
if (!existsSync(path))
|
|
30
|
+
return 'auto';
|
|
31
|
+
try {
|
|
32
|
+
const state = JSON.parse(readFileSync(path, 'utf8'));
|
|
33
|
+
return state.permissionMode === 'bypass' ? 'bypass' : 'auto';
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
return 'auto';
|
|
37
|
+
}
|
|
38
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { copyFileSync, cpSync, existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
import { localStations } from './local.js';
|
|
6
|
+
import { STORE_ENTRY, daemonEntry, findBun, runtimeDir } from './runtime.js';
|
|
7
|
+
export const MANIFEST_FILE = 'stations.json';
|
|
8
|
+
export const MARKETPLACE = 'marketplace';
|
|
9
|
+
const METRO_SOURCES = join('node_modules', '@metro-labs');
|
|
10
|
+
const INSTALL_TIMEOUT_MS = 15 * 60_000;
|
|
11
|
+
export function runtimeStore() {
|
|
12
|
+
const env = process.env.METRO_RUNTIME_STORE?.trim() ?? '';
|
|
13
|
+
return env === '' ? join(homedir(), '.metro', 'runtime') : env;
|
|
14
|
+
}
|
|
15
|
+
const isRecord = (v) => typeof v === 'object' && v !== null;
|
|
16
|
+
const ranges = (raw) => isRecord(raw)
|
|
17
|
+
? Object.fromEntries(Object.entries(raw).filter((e) => typeof e[1] === 'string'))
|
|
18
|
+
: {};
|
|
19
|
+
export function readManifest(path) {
|
|
20
|
+
const raw = JSON.parse(readFileSync(path, 'utf8'));
|
|
21
|
+
if (!isRecord(raw))
|
|
22
|
+
throw new Error(`${path} is not a runtime manifest`);
|
|
23
|
+
const stations = isRecord(raw.stations) ? raw.stations : {};
|
|
24
|
+
return {
|
|
25
|
+
core: ranges(raw.core),
|
|
26
|
+
stations: Object.fromEntries(Object.entries(stations).map(([name, deps]) => [name, ranges(deps)])),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export function dependenciesFor(manifest, stations) {
|
|
30
|
+
const out = { ...manifest.core };
|
|
31
|
+
for (const station of new Set(stations))
|
|
32
|
+
Object.assign(out, manifest.stations[station] ?? {});
|
|
33
|
+
return Object.fromEntries(Object.entries(out).sort(([a], [b]) => a.localeCompare(b)));
|
|
34
|
+
}
|
|
35
|
+
const packageText = (deps) => `${JSON.stringify({ name: 'metro-runtime', private: true, dependencies: deps }, null, 2)}\n`;
|
|
36
|
+
function readOrNull(path) {
|
|
37
|
+
try {
|
|
38
|
+
return readFileSync(path, 'utf8');
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
function copyMarketplace(sources, store) {
|
|
45
|
+
const staged = join(sources, MARKETPLACE);
|
|
46
|
+
if (!existsSync(staged))
|
|
47
|
+
return false;
|
|
48
|
+
cpSync(staged, join(store, MARKETPLACE), { recursive: true });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
function syncSources(sources, store) {
|
|
52
|
+
const shim = join(sources, STORE_ENTRY);
|
|
53
|
+
if (existsSync(shim))
|
|
54
|
+
copyFileSync(shim, join(store, STORE_ENTRY));
|
|
55
|
+
const stamp = readOrNull(join(sources, 'runtime.json'));
|
|
56
|
+
if (stamp !== null && readOrNull(join(store, 'runtime.json')) === stamp)
|
|
57
|
+
return existsSync(join(store, MARKETPLACE)) ? false : copyMarketplace(sources, store);
|
|
58
|
+
rmSync(join(store, METRO_SOURCES), { recursive: true, force: true });
|
|
59
|
+
cpSync(join(sources, METRO_SOURCES), join(store, METRO_SOURCES), { recursive: true });
|
|
60
|
+
rmSync(join(store, MARKETPLACE), { recursive: true, force: true });
|
|
61
|
+
copyMarketplace(sources, store);
|
|
62
|
+
if (stamp !== null)
|
|
63
|
+
writeFileSync(join(store, 'runtime.json'), stamp);
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
export function installDependencies(store, deps, bun, log) {
|
|
67
|
+
const wanted = packageText(deps);
|
|
68
|
+
const marker = join(store, 'node_modules', '.metro-installed');
|
|
69
|
+
if (existsSync(marker) && readOrNull(join(store, 'package.json')) === wanted)
|
|
70
|
+
return false;
|
|
71
|
+
writeFileSync(join(store, 'package.json'), wanted);
|
|
72
|
+
log(`Installing the channel SDKs this machine needs into ${store}`);
|
|
73
|
+
const run = spawnSync(bun, ['install', '--no-summary', '--no-progress'], {
|
|
74
|
+
cwd: store,
|
|
75
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
76
|
+
timeout: INSTALL_TIMEOUT_MS,
|
|
77
|
+
});
|
|
78
|
+
if (run.error !== undefined || run.status !== 0)
|
|
79
|
+
throw new Error(`bun install failed in ${store}: ${run.error?.message ?? `exit ${String(run.status)}`}`);
|
|
80
|
+
writeFileSync(marker, `${new Date().toISOString()}\n`);
|
|
81
|
+
return true;
|
|
82
|
+
}
|
|
83
|
+
export function prepareRuntime(opts = {}) {
|
|
84
|
+
const sources = opts.sources ?? runtimeDir();
|
|
85
|
+
const manifestPath = join(sources, MANIFEST_FILE);
|
|
86
|
+
if (!existsSync(manifestPath))
|
|
87
|
+
return { dir: sources, entry: daemonEntry(sources), trains: join(sources, 'trains'), manifest: null };
|
|
88
|
+
const store = opts.store ?? runtimeStore();
|
|
89
|
+
const log = opts.log ??
|
|
90
|
+
((line) => {
|
|
91
|
+
process.stderr.write(`${line}\n`);
|
|
92
|
+
});
|
|
93
|
+
mkdirSync(join(store, 'trains'), { recursive: true });
|
|
94
|
+
syncSources(sources, store);
|
|
95
|
+
installDependencies(store, dependenciesFor(readManifest(manifestPath), localStations(opts.agents)), opts.bun ?? findBun(), log);
|
|
96
|
+
return { dir: store, entry: daemonEntry(store), trains: join(store, 'trains'), manifest: manifestPath };
|
|
97
|
+
}
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
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 STORE_ENTRY = 'server.ts';
|
|
7
|
+
export const PACKAGE_ENTRY = join('node_modules', '@metro-labs', 'daemon', 'src', 'server.ts');
|
|
8
|
+
export const daemonEntry = (dir) => existsSync(join(dir, STORE_ENTRY))
|
|
9
|
+
? join(dir, STORE_ENTRY)
|
|
10
|
+
: join(dir, PACKAGE_ENTRY);
|
|
11
|
+
export class MissingRuntime extends Error {
|
|
12
|
+
}
|
|
13
|
+
export function runtimeDir() {
|
|
14
|
+
const explicit = process.env.METRO_RUNTIME_DIR?.trim();
|
|
15
|
+
const dir = explicit !== undefined && explicit !== ''
|
|
16
|
+
? explicit
|
|
17
|
+
: join(dirname(dirname(fileURLToPath(import.meta.url))), 'runtime');
|
|
18
|
+
if (!existsSync(daemonEntry(dir)))
|
|
19
|
+
throw new MissingRuntime(`no metro daemon at ${dir}. Reinstall with: npm i -g @stage-labs/metro@beta`);
|
|
20
|
+
return dir;
|
|
21
|
+
}
|
|
22
|
+
class MissingBun extends Error {
|
|
23
|
+
}
|
|
24
|
+
export function assertAgentId(id) {
|
|
25
|
+
if (id === undefined || !AGENT_RE.test(id))
|
|
26
|
+
throw new Error(`'${id ?? ''}' is not an agent id — copy it from the agent's page in the web UI`);
|
|
27
|
+
return id;
|
|
28
|
+
}
|
|
29
|
+
export function findBun() {
|
|
30
|
+
const found = spawnSync('bun', ['--version'], { stdio: 'ignore' });
|
|
31
|
+
if (found.error !== undefined || found.status !== 0)
|
|
32
|
+
throw new MissingBun('metro serve needs Bun, which is not on PATH.\n' +
|
|
33
|
+
'Install it with: curl -fsSL https://bun.sh/install | bash');
|
|
34
|
+
return 'bun';
|
|
35
|
+
}
|
|
36
|
+
export const localPort = () => Number(process.env.METRO_WEBHOOK_PORT) || 8420;
|
|
37
|
+
export const localUrl = () => `http://127.0.0.1:${String(localPort())}`;
|
|
38
|
+
export const RESTART_CODE = 75;
|
|
39
|
+
export const HOLD_CODE = 76;
|
|
40
|
+
function spawnOnce(plan) {
|
|
41
|
+
const child = spawn(plan.command, plan.args, {
|
|
42
|
+
cwd: plan.cwd,
|
|
43
|
+
env: plan.env,
|
|
44
|
+
stdio: ['ignore', 'inherit', 'inherit'],
|
|
45
|
+
});
|
|
46
|
+
const relay = (sig) => () => {
|
|
47
|
+
child.kill(sig);
|
|
48
|
+
};
|
|
49
|
+
const onInt = relay('SIGINT');
|
|
50
|
+
const onTerm = relay('SIGTERM');
|
|
51
|
+
process.on('SIGINT', onInt);
|
|
52
|
+
process.on('SIGTERM', onTerm);
|
|
53
|
+
return new Promise((resolve) => {
|
|
54
|
+
child.on('exit', (code) => {
|
|
55
|
+
process.off('SIGINT', onInt);
|
|
56
|
+
process.off('SIGTERM', onTerm);
|
|
57
|
+
resolve(code ?? 0);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
export async function spawnPlan(plan, hold) {
|
|
62
|
+
for (;;) {
|
|
63
|
+
const code = await spawnOnce(plan());
|
|
64
|
+
if (code === RESTART_CODE) {
|
|
65
|
+
process.stderr.write('metro was updated; restarting the daemon on the new version\n');
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (code !== HOLD_CODE || hold === undefined)
|
|
69
|
+
return code;
|
|
70
|
+
if ((await hold()) === 'exit')
|
|
71
|
+
return 0;
|
|
72
|
+
process.stderr.write('starting the daemon again\n');
|
|
73
|
+
}
|
|
74
|
+
}
|