@stage-labs/metro 0.1.0-beta.13 → 0.1.0-beta.131
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/seed.ts +33 -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 +209 -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
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import { join } from 'node:path';
|
|
2
|
+
import { readJson, writeSecure } from '@metro-labs/core/secure-fs';
|
|
3
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
4
|
+
import {
|
|
5
|
+
connectorUrlText,
|
|
6
|
+
ConnectorVerifyError,
|
|
7
|
+
parseConnectorUrl,
|
|
8
|
+
verifyRemoteMcp,
|
|
9
|
+
type ConnectorAuth,
|
|
10
|
+
type OAuthAuth,
|
|
11
|
+
} from './verify.js';
|
|
12
|
+
import { oauthExpired, refreshOAuth } from './oauth.js';
|
|
13
|
+
import { advertisesOAuth } from './oauth-discovery.js';
|
|
14
|
+
import {
|
|
15
|
+
ConnectorError,
|
|
16
|
+
connectorAuth,
|
|
17
|
+
connectorClient,
|
|
18
|
+
connectorName,
|
|
19
|
+
readConfig,
|
|
20
|
+
stamp,
|
|
21
|
+
type ConnectorConfig,
|
|
22
|
+
} from './config.js';
|
|
23
|
+
import {
|
|
24
|
+
bearerHeaders,
|
|
25
|
+
fixedTarget,
|
|
26
|
+
unrefreshedTarget,
|
|
27
|
+
type RelayTarget,
|
|
28
|
+
} from './relay-target.js';
|
|
29
|
+
import {
|
|
30
|
+
connectorFromRow,
|
|
31
|
+
UNVERIFIED,
|
|
32
|
+
type Connector,
|
|
33
|
+
type ConnectorCheck,
|
|
34
|
+
type ConnectorInput,
|
|
35
|
+
type DeletedConnector,
|
|
36
|
+
type PendingConnectorInput,
|
|
37
|
+
} from './model.js';
|
|
38
|
+
import { agentsDir } from '../agents/files.js';
|
|
39
|
+
import { assertLocalOwner, LOCAL_PROJECT_ID } from '../agents/file-admin.js';
|
|
40
|
+
import { newId } from '@metro-labs/core/ids';
|
|
41
|
+
import type { LoadedConnector } from '../stations/materialize.js';
|
|
42
|
+
|
|
43
|
+
export interface LocalConnectorRow {
|
|
44
|
+
id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
url: string;
|
|
47
|
+
transport: 'http';
|
|
48
|
+
config: ConnectorConfig;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const FILE = 'connectors.json';
|
|
52
|
+
const filePath = (dir: string): string => join(dir, FILE);
|
|
53
|
+
|
|
54
|
+
const isRow = (v: unknown): v is { id: string; name: string; url: string; config?: unknown } =>
|
|
55
|
+
typeof v === 'object' &&
|
|
56
|
+
v !== null &&
|
|
57
|
+
typeof (v as { id?: unknown }).id === 'string' &&
|
|
58
|
+
typeof (v as { name?: unknown }).name === 'string' &&
|
|
59
|
+
typeof (v as { url?: unknown }).url === 'string';
|
|
60
|
+
|
|
61
|
+
const asList = (value: unknown): unknown[] =>
|
|
62
|
+
Array.isArray(value) ? value.map((item: unknown) => item) : [];
|
|
63
|
+
|
|
64
|
+
export function readLocalConnectors(dir = agentsDir()): LocalConnectorRow[] {
|
|
65
|
+
const raw = readJson<{ connectors?: unknown }>(filePath(dir), {}, {
|
|
66
|
+
warn: 'connectors.json: malformed, ignoring',
|
|
67
|
+
});
|
|
68
|
+
return asList(raw.connectors).filter(isRow).map((r) => ({
|
|
69
|
+
id: r.id,
|
|
70
|
+
name: r.name,
|
|
71
|
+
url: r.url,
|
|
72
|
+
transport: 'http' as const,
|
|
73
|
+
config: readConfig(r.config),
|
|
74
|
+
}));
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function writeRows(dir: string, rows: LocalConnectorRow[]): void {
|
|
78
|
+
writeSecure(filePath(dir), `${JSON.stringify({ version: 1, connectors: rows }, null, 2)}\n`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const missing = (): ConnectorError => new ConnectorError('no such connector', 404);
|
|
82
|
+
|
|
83
|
+
function ownedRows(subject: string, project: string, dir: string): LocalConnectorRow[] {
|
|
84
|
+
assertLocalOwner(subject, dir);
|
|
85
|
+
if (project !== LOCAL_PROJECT_ID) throw new ConnectorError('no such project', 404);
|
|
86
|
+
return readLocalConnectors(dir);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function rowOrThrow(subject: string, id: string, dir: string): LocalConnectorRow {
|
|
90
|
+
assertLocalOwner(subject, dir);
|
|
91
|
+
const row = readLocalConnectors(dir).find((r) => r.id === id);
|
|
92
|
+
if (row === undefined) throw missing();
|
|
93
|
+
return row;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function replace(dir: string, row: LocalConnectorRow): Connector {
|
|
97
|
+
writeRows(dir, readLocalConnectors(dir).map((r) => (r.id === row.id ? row : r)));
|
|
98
|
+
return connectorFromRow(row);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
async function oauthCapable(url: URL, auth: ConnectorAuth): Promise<boolean> {
|
|
102
|
+
if (auth.kind === 'oauth') return true;
|
|
103
|
+
if (auth.kind === 'header') return false;
|
|
104
|
+
return advertisesOAuth(url);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function localImportConnectors(
|
|
108
|
+
rows: LoadedConnector[],
|
|
109
|
+
dir = agentsDir(),
|
|
110
|
+
mode: 'append' | 'overwrite' = 'overwrite',
|
|
111
|
+
): number {
|
|
112
|
+
const current = readLocalConnectors(dir);
|
|
113
|
+
const fresh =
|
|
114
|
+
mode === 'overwrite'
|
|
115
|
+
? rows
|
|
116
|
+
: rows.filter((row) => !current.some((r) => r.id === row.id || r.name === row.name));
|
|
117
|
+
const imported: LocalConnectorRow[] = fresh.map((row) => ({
|
|
118
|
+
id: row.id,
|
|
119
|
+
name: row.name,
|
|
120
|
+
url: row.url,
|
|
121
|
+
transport: 'http',
|
|
122
|
+
config: readConfig(row.config),
|
|
123
|
+
}));
|
|
124
|
+
const ids = new Set(imported.map((r) => r.id));
|
|
125
|
+
const names = new Set(imported.map((r) => r.name));
|
|
126
|
+
writeRows(dir, [...current.filter((r) => !ids.has(r.id) && !names.has(r.name)), ...imported]);
|
|
127
|
+
return imported.length;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export async function localListConnectors(
|
|
131
|
+
subject: string,
|
|
132
|
+
project: string,
|
|
133
|
+
dir = agentsDir(),
|
|
134
|
+
): Promise<Connector[]> {
|
|
135
|
+
return Promise.resolve(ownedRows(subject, project, dir).map(connectorFromRow));
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export async function localGetConnector(subject: string, id: string, dir = agentsDir()): Promise<Connector> {
|
|
139
|
+
return Promise.resolve(connectorFromRow(rowOrThrow(subject, id, dir)));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function assertNameFree(name: string, exceptId: string | null, dir: string): void {
|
|
143
|
+
if (readLocalConnectors(dir).some((r) => r.id !== exceptId && r.name === name))
|
|
144
|
+
throw new ConnectorError(`a connector named '${name}' already exists on this daemon`, 409);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function insert(dir: string, name: string, url: URL, config: ConnectorConfig): Connector {
|
|
148
|
+
assertNameFree(name, null, dir);
|
|
149
|
+
const row: LocalConnectorRow = { id: newId(), name, url: connectorUrlText(url), transport: 'http', config };
|
|
150
|
+
writeRows(dir, [...readLocalConnectors(dir), row]);
|
|
151
|
+
return connectorFromRow(row);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
export async function localCreateConnector(
|
|
155
|
+
subject: string,
|
|
156
|
+
project: string,
|
|
157
|
+
input: ConnectorInput,
|
|
158
|
+
dir = agentsDir(),
|
|
159
|
+
): Promise<Connector> {
|
|
160
|
+
ownedRows(subject, project, dir);
|
|
161
|
+
const name = connectorName(input.name);
|
|
162
|
+
const url = parseConnectorUrl(input.url);
|
|
163
|
+
const auth = connectorAuth(input.header, input.value);
|
|
164
|
+
const client = connectorClient(input.clientId, input.clientSecret);
|
|
165
|
+
const verified = stamp(await verifyRemoteMcp(url, auth));
|
|
166
|
+
return insert(dir, name, url, {
|
|
167
|
+
auth,
|
|
168
|
+
createdAt: new Date().toISOString(),
|
|
169
|
+
verified,
|
|
170
|
+
oauth: await oauthCapable(url, auth),
|
|
171
|
+
client,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function localCreatePendingConnector(
|
|
176
|
+
subject: string,
|
|
177
|
+
project: string,
|
|
178
|
+
input: PendingConnectorInput,
|
|
179
|
+
dir = agentsDir(),
|
|
180
|
+
): Promise<Connector> {
|
|
181
|
+
ownedRows(subject, project, dir);
|
|
182
|
+
const name = connectorName(input.name);
|
|
183
|
+
const url = parseConnectorUrl(input.url);
|
|
184
|
+
const client = connectorClient(input.clientId, input.clientSecret);
|
|
185
|
+
return Promise.resolve(
|
|
186
|
+
insert(dir, name, url, { auth: { kind: 'none' }, createdAt: new Date().toISOString(), verified: UNVERIFIED, oauth: true, client }),
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export async function localReconnectConnector(
|
|
191
|
+
subject: string,
|
|
192
|
+
id: string,
|
|
193
|
+
auth: OAuthAuth,
|
|
194
|
+
dir = agentsDir(),
|
|
195
|
+
): Promise<Connector> {
|
|
196
|
+
const row = rowOrThrow(subject, id, dir);
|
|
197
|
+
const verified = stamp(await verifyRemoteMcp(parseConnectorUrl(row.url), auth));
|
|
198
|
+
return replace(dir, { ...row, config: { ...row.config, auth, verified, oauth: true } });
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
export async function localDisconnectConnector(subject: string, id: string, dir = agentsDir()): Promise<Connector> {
|
|
202
|
+
const row = rowOrThrow(subject, id, dir);
|
|
203
|
+
if (row.config.auth.kind !== 'oauth') throw new ConnectorError('that connector is not signed in', 400);
|
|
204
|
+
return Promise.resolve(replace(dir, { ...row, config: { ...row.config, auth: { kind: 'none' }, oauth: true } }));
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function freshAuth(auth: ConnectorAuth, resource: string): Promise<ConnectorAuth> {
|
|
208
|
+
if (auth.kind !== 'oauth' || !oauthExpired(auth)) return auth;
|
|
209
|
+
return refreshOAuth(auth, resource);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export async function localVerifyConnector(subject: string, id: string, dir = agentsDir()): Promise<ConnectorCheck> {
|
|
213
|
+
const row = rowOrThrow(subject, id, dir);
|
|
214
|
+
try {
|
|
215
|
+
const url = parseConnectorUrl(row.url);
|
|
216
|
+
const auth = await freshAuth(row.config.auth, url.toString());
|
|
217
|
+
const verified = stamp(await verifyRemoteMcp(url, auth));
|
|
218
|
+
const oauth = row.config.oauth || (await oauthCapable(url, auth));
|
|
219
|
+
replace(dir, { ...row, config: { ...row.config, auth, verified, oauth } });
|
|
220
|
+
return { id: row.id, name: row.name, ok: true, verified };
|
|
221
|
+
} catch (err) {
|
|
222
|
+
if (!(err instanceof ConnectorVerifyError)) throw err;
|
|
223
|
+
return { id: row.id, name: row.name, ok: false, reason: err.message };
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export async function localRenameConnector(subject: string, id: string, raw: string, dir = agentsDir()): Promise<Connector> {
|
|
228
|
+
const name = connectorName(raw);
|
|
229
|
+
const row = rowOrThrow(subject, id, dir);
|
|
230
|
+
assertNameFree(name, id, dir);
|
|
231
|
+
return Promise.resolve(replace(dir, { ...row, name }));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function localDeleteConnector(subject: string, id: string, dir = agentsDir()): Promise<DeletedConnector> {
|
|
235
|
+
const row = rowOrThrow(subject, id, dir);
|
|
236
|
+
writeRows(dir, readLocalConnectors(dir).filter((r) => r.id !== id));
|
|
237
|
+
return Promise.resolve({ id: row.id, name: row.name });
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
const byIds = (ids: string[], dir: string): LocalConnectorRow[] =>
|
|
241
|
+
readLocalConnectors(dir).filter((r) => ids.includes(r.id));
|
|
242
|
+
|
|
243
|
+
export async function localConnectorNamesByIds(ids: string[], dir = agentsDir()): Promise<{ id: string; name: string }[]> {
|
|
244
|
+
return Promise.resolve(byIds(ids, dir).map((r) => ({ id: r.id, name: r.name })));
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const inflight = new Map<string, Promise<OAuthAuth>>();
|
|
248
|
+
|
|
249
|
+
function refreshOnce(row: LocalConnectorRow, auth: OAuthAuth, dir: string): Promise<OAuthAuth> {
|
|
250
|
+
const running = inflight.get(row.id);
|
|
251
|
+
if (running !== undefined) return running;
|
|
252
|
+
const job = refreshOAuth(auth, parseConnectorUrl(row.url).toString())
|
|
253
|
+
.then((fresh) => {
|
|
254
|
+
replace(dir, { ...row, config: { ...row.config, auth: fresh } });
|
|
255
|
+
return fresh;
|
|
256
|
+
})
|
|
257
|
+
.finally(() => {
|
|
258
|
+
inflight.delete(row.id);
|
|
259
|
+
});
|
|
260
|
+
inflight.set(row.id, job);
|
|
261
|
+
return job;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
async function oauthTarget(row: LocalConnectorRow, auth: OAuthAuth, force: boolean, dir: string): Promise<RelayTarget> {
|
|
265
|
+
if (!force && !oauthExpired(auth)) return { kind: 'ok', url: row.url, headers: bearerHeaders(auth.accessToken) };
|
|
266
|
+
try {
|
|
267
|
+
const fresh = await refreshOnce(row, auth, dir);
|
|
268
|
+
return { kind: 'ok', url: row.url, headers: bearerHeaders(fresh.accessToken) };
|
|
269
|
+
} catch (err) {
|
|
270
|
+
log.warn({ id: row.id, err: errMsg(err) }, 'local relay: token refresh failed');
|
|
271
|
+
return unrefreshedTarget(row.url, auth, force);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export async function localRelayTarget(
|
|
276
|
+
connectorId: string,
|
|
277
|
+
force: boolean,
|
|
278
|
+
dir = agentsDir(),
|
|
279
|
+
): Promise<RelayTarget> {
|
|
280
|
+
const row = readLocalConnectors(dir).find((r) => r.id === connectorId);
|
|
281
|
+
if (row === undefined) return { kind: 'missing' };
|
|
282
|
+
parseConnectorUrl(row.url);
|
|
283
|
+
const auth = row.config.auth;
|
|
284
|
+
if (auth.kind === 'oauth') return oauthTarget(row, auth, force, dir);
|
|
285
|
+
return fixedTarget(row.url, auth, force);
|
|
286
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
2
|
+
|
|
3
|
+
export const TOOL_KINDS = ['read', 'write'] as const;
|
|
4
|
+
|
|
5
|
+
export type ToolKind = (typeof TOOL_KINDS)[number];
|
|
6
|
+
|
|
7
|
+
export interface ToolInfo {
|
|
8
|
+
name: string;
|
|
9
|
+
title: string;
|
|
10
|
+
description: string;
|
|
11
|
+
kind: ToolKind;
|
|
12
|
+
annotated: boolean;
|
|
13
|
+
destructive: boolean;
|
|
14
|
+
idempotent: boolean;
|
|
15
|
+
openWorld: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const MAX_TOOLS = 500;
|
|
19
|
+
const MAX_DESCRIPTION = 400;
|
|
20
|
+
|
|
21
|
+
const str = (value: unknown, cap: number): string =>
|
|
22
|
+
typeof value === 'string' ? value.slice(0, cap) : '';
|
|
23
|
+
|
|
24
|
+
const flag = (value: unknown, fallback: boolean): boolean =>
|
|
25
|
+
typeof value === 'boolean' ? value : fallback;
|
|
26
|
+
|
|
27
|
+
export function classifyTool(annotations: unknown): ToolKind {
|
|
28
|
+
return isRecord(annotations) && annotations.readOnlyHint === true
|
|
29
|
+
? 'read'
|
|
30
|
+
: 'write';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isDestructive(annotations: unknown): boolean {
|
|
34
|
+
if (!isRecord(annotations)) return true;
|
|
35
|
+
if (annotations.readOnlyHint === true) return false;
|
|
36
|
+
return annotations.destructiveHint !== false;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function toToolInfo(raw: unknown): ToolInfo | null {
|
|
40
|
+
if (!isRecord(raw)) return null;
|
|
41
|
+
const name = str(raw.name, 200);
|
|
42
|
+
if (name === '') return null;
|
|
43
|
+
const annotations = isRecord(raw.annotations) ? raw.annotations : undefined;
|
|
44
|
+
return {
|
|
45
|
+
name,
|
|
46
|
+
title: str(annotations?.title ?? raw.title, 200),
|
|
47
|
+
description: str(raw.description, MAX_DESCRIPTION),
|
|
48
|
+
kind: classifyTool(raw.annotations),
|
|
49
|
+
annotated: annotations !== undefined,
|
|
50
|
+
destructive: isDestructive(raw.annotations),
|
|
51
|
+
idempotent: flag(annotations?.idempotentHint, false),
|
|
52
|
+
openWorld: flag(annotations?.openWorldHint, true),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function toToolList(raw: unknown): ToolInfo[] {
|
|
57
|
+
if (!Array.isArray(raw)) return [];
|
|
58
|
+
const out: ToolInfo[] = [];
|
|
59
|
+
for (const entry of raw) {
|
|
60
|
+
if (out.length >= MAX_TOOLS) break;
|
|
61
|
+
const tool = toToolInfo(entry);
|
|
62
|
+
if (tool !== null) out.push(tool);
|
|
63
|
+
}
|
|
64
|
+
return out;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function toStoredTool(raw: unknown): ToolInfo | null {
|
|
68
|
+
if (!isRecord(raw)) return null;
|
|
69
|
+
const name = str(raw.name, 200);
|
|
70
|
+
if (name === '') return null;
|
|
71
|
+
return {
|
|
72
|
+
name,
|
|
73
|
+
title: str(raw.title, 200),
|
|
74
|
+
description: str(raw.description, MAX_DESCRIPTION),
|
|
75
|
+
kind: raw.kind === 'read' ? 'read' : 'write',
|
|
76
|
+
annotated: raw.annotated === true,
|
|
77
|
+
destructive: raw.destructive !== false,
|
|
78
|
+
idempotent: raw.idempotent === true,
|
|
79
|
+
openWorld: raw.openWorld !== false,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export function readStoredTools(raw: unknown): ToolInfo[] {
|
|
84
|
+
if (!Array.isArray(raw)) return [];
|
|
85
|
+
const out: ToolInfo[] = [];
|
|
86
|
+
for (const entry of raw) {
|
|
87
|
+
if (out.length >= MAX_TOOLS) break;
|
|
88
|
+
const tool = toStoredTool(entry);
|
|
89
|
+
if (tool !== null) out.push(tool);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export function countByKind(tools: ToolInfo[]): Record<ToolKind, number> {
|
|
95
|
+
const counts: Record<ToolKind, number> = { read: 0, write: 0 };
|
|
96
|
+
for (const tool of tools) counts[tool.kind] += 1;
|
|
97
|
+
return counts;
|
|
98
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
2
|
+
|
|
3
|
+
export class ConnectorVerifyError extends ApiError {}
|
|
4
|
+
|
|
5
|
+
export class ConnectorUnauthorized extends ConnectorVerifyError {}
|
|
6
|
+
|
|
7
|
+
export class ConnectorNotMcp extends ConnectorVerifyError {}
|
|
8
|
+
|
|
9
|
+
export const POLICY_MESSAGE =
|
|
10
|
+
'Metro connects from its own server, so it cannot reach a URL on your machine. localhost and private addresses are not usable as connectors.';
|
|
11
|
+
|
|
12
|
+
let localAllowed = false;
|
|
13
|
+
|
|
14
|
+
export function allowLocalConnectors(on: boolean): void {
|
|
15
|
+
localAllowed = on;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const IPV4 = /^\d{1,3}(?:\.\d{1,3}){3}$/;
|
|
19
|
+
const PRIVATE_SUFFIX = /(?:^|\.)(?:localhost|local|internal|flycast)$/;
|
|
20
|
+
|
|
21
|
+
export function refused(message: string): ConnectorVerifyError {
|
|
22
|
+
return new ConnectorVerifyError(message, 400);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function hostAllowed(host: string): boolean {
|
|
26
|
+
if (host === '' || host.startsWith('[')) return false;
|
|
27
|
+
if (IPV4.test(host) || !host.includes('.')) return false;
|
|
28
|
+
return !PRIVATE_SUFFIX.test(host);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function schemeAllowed(url: URL): boolean {
|
|
32
|
+
return url.protocol === 'https:' || (localAllowed && url.protocol === 'http:');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function refuseShape(url: URL): void {
|
|
36
|
+
if (!schemeAllowed(url)) throw refused('a connector url must start with https://');
|
|
37
|
+
if (url.username !== '' || url.password !== '')
|
|
38
|
+
throw refused('a connector url must not carry a user:password');
|
|
39
|
+
if (url.hash !== '')
|
|
40
|
+
throw refused('a connector url must not carry a #fragment');
|
|
41
|
+
if (!localAllowed && !hostAllowed(url.hostname.toLowerCase())) throw refused(POLICY_MESSAGE);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseConnectorUrl(raw: unknown): URL {
|
|
45
|
+
const text = typeof raw === 'string' ? raw.trim() : '';
|
|
46
|
+
if (text === '') throw refused('a connector url is required');
|
|
47
|
+
let url: URL;
|
|
48
|
+
try {
|
|
49
|
+
url = new URL(text);
|
|
50
|
+
} catch {
|
|
51
|
+
throw refused('that is not a valid url');
|
|
52
|
+
}
|
|
53
|
+
refuseShape(url);
|
|
54
|
+
return url;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function connectorUrlText(url: URL): string {
|
|
58
|
+
const text = url.toString();
|
|
59
|
+
if (url.pathname !== '/' || url.search !== '') return text;
|
|
60
|
+
return text.endsWith('/') ? text.slice(0, -1) : text;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function safeIconSrc(raw: unknown): string {
|
|
64
|
+
if (typeof raw !== 'string' || raw === '') return '';
|
|
65
|
+
try {
|
|
66
|
+
return new URL(raw).protocol === 'https:' ? raw.slice(0, 500) : '';
|
|
67
|
+
} catch {
|
|
68
|
+
return '';
|
|
69
|
+
}
|
|
70
|
+
}
|