@stage-labs/metro 0.1.0-beta.2 → 0.1.0-beta.200
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 +11 -0
- package/dist/claude-settings.js +40 -0
- package/dist/claude.js +163 -0
- package/dist/cli.js +77 -567
- package/dist/control.js +74 -0
- package/dist/hold.js +211 -0
- package/dist/local.js +43 -0
- package/dist/mcp-config.js +25 -0
- package/dist/node-name.js +71 -0
- package/dist/onboarding.js +44 -0
- package/dist/provider-flags.js +9 -0
- package/dist/route.js +58 -0
- package/dist/runtime-install.js +97 -0
- package/dist/runtime.js +74 -0
- package/dist/serve.js +158 -0
- package/dist/service.js +243 -0
- package/dist/tail.js +102 -144
- package/dist/update.js +45 -0
- package/dist/version.js +97 -0
- package/package.json +14 -23
- package/runtime/marketplace/.claude-plugin/marketplace.json +12 -0
- package/runtime/marketplace/plugin/.claude-plugin/plugin.json +17 -0
- package/runtime/marketplace/plugin/.mcp.json +1 -0
- package/runtime/marketplace/plugin/bin/guard.mjs +71 -0
- package/runtime/marketplace/plugin/bin/metro-plugin.mjs +30 -0
- package/runtime/marketplace/plugin/bin/policy.mjs +97 -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 +24 -0
- package/runtime/node_modules/@metro-labs/core/package.json +47 -0
- package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
- package/runtime/node_modules/@metro-labs/core/src/events.ts +119 -0
- package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
- package/runtime/node_modules/@metro-labs/core/src/ids.ts +24 -0
- package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
- package/runtime/node_modules/@metro-labs/core/src/lines.ts +91 -0
- package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
- package/runtime/node_modules/@metro-labs/core/src/protocol.ts +227 -0
- package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +74 -0
- package/runtime/node_modules/@metro-labs/core/src/station-names.ts +13 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/account-files.ts +41 -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/profile.ts +58 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/sender-profile.ts +64 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +61 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/train-boot.ts +42 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +89 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +123 -0
- package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
- package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -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/agent-user/agent-fs.ts +134 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files-api.ts +12 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +119 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/home-fs.ts +113 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +100 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +27 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +252 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +61 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +87 -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-lookups.ts +55 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-routes.ts +76 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +357 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +30 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +59 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +131 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +132 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +305 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +168 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +35 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +128 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +86 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/api.ts +55 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +149 -0
- package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +221 -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 +21 -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 +26 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +12 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +370 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +78 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +13 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +1 -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 +355 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +359 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +82 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/onboarding.ts +60 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +149 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +75 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +283 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +109 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +271 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +306 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +176 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +72 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/health.ts +18 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +159 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +135 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +158 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +166 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +131 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/reach.ts +27 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-policy.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +29 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +348 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +358 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +106 -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-reaper.ts +174 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +123 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +278 -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 +233 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +208 -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 +173 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +194 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +137 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +77 -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 +237 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +118 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +274 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +160 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +46 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-setup.ts +150 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +139 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +223 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +199 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +299 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +193 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +70 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/pkce.ts +36 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +18 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/subscription.ts +120 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +15 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +156 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +226 -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 +90 -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 +137 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -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-prompt.ts +87 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +79 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +83 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +79 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +60 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-tool.ts +96 -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/read-tool.ts +87 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +46 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-slot.ts +48 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +202 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +88 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +225 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +333 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +61 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +43 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +126 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +165 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/privilege.ts +48 -0
- package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +132 -0
- package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +316 -0
- package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +118 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/bearer.ts +35 -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 +365 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +71 -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 +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +58 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/owner.ts +26 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +90 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +128 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +134 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +225 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +288 -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 +265 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +51 -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/stations/webhook-endpoints.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +84 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +124 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +15 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/api.ts +45 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/config.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +48 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +96 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/install.ts +56 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/paths.ts +26 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +148 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +130 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +61 -0
- package/runtime/node_modules/@metro-labs/discord-bot/package.json +26 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +117 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +240 -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 +174 -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 +122 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +68 -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 +22 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +1 -0
- package/runtime/node_modules/@metro-labs/http/package.json +29 -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 +138 -0
- package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -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/workos-token.ts +149 -0
- package/runtime/node_modules/@metro-labs/outlook/package.json +25 -0
- package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +107 -0
- package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +20 -0
- package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +97 -0
- package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +150 -0
- package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +53 -0
- package/runtime/node_modules/@metro-labs/outlook/src/config.ts +36 -0
- package/runtime/node_modules/@metro-labs/outlook/src/format.ts +141 -0
- package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +60 -0
- package/runtime/node_modules/@metro-labs/outlook/src/html.ts +72 -0
- package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +104 -0
- package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
- package/runtime/node_modules/@metro-labs/outlook/src/login.ts +150 -0
- package/runtime/node_modules/@metro-labs/outlook/src/me.ts +41 -0
- package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +116 -0
- package/runtime/node_modules/@metro-labs/outlook/src/query.ts +119 -0
- package/runtime/node_modules/@metro-labs/outlook/src/state.ts +43 -0
- package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
- package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +70 -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 +256 -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 +128 -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 +57 -0
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +7 -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/profile.ts +70 -0
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +27 -0
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +6 -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 +119 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +246 -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 +173 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +103 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +95 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +65 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +20 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -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 +1 -0
- package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +106 -0
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +134 -0
- package/runtime/node_modules/@metro-labs/threema/src/api.ts +152 -0
- package/runtime/node_modules/@metro-labs/threema/src/crypto.ts +100 -0
- package/runtime/node_modules/@metro-labs/threema/src/files.ts +58 -0
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +169 -0
- package/runtime/node_modules/@metro-labs/threema/src/groups.ts +96 -0
- package/runtime/node_modules/@metro-labs/threema/src/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +89 -0
- package/runtime/node_modules/@metro-labs/threema/src/index.ts +24 -0
- package/runtime/node_modules/@metro-labs/threema/src/messages.ts +264 -0
- package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +160 -0
- package/runtime/node_modules/@metro-labs/threema/src/station.ts +13 -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 +213 -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 +388 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +83 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +40 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +7 -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/names.ts +28 -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 +24 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +94 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +5 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +1 -0
- package/runtime/node_modules/@metro-labs/xmtp/package.json +34 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +114 -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 +101 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +118 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +244 -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-payloads.ts +316 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +69 -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 +47 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +122 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +139 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +61 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +153 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +112 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/smart.ts +72 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +79 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +198 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +88 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +98 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +51 -0
- package/runtime/runtime.json +3 -0
- package/runtime/server.ts +1 -0
- package/runtime/stations.json +37 -0
- package/runtime/trains/.keep +0 -0
- package/LICENSE +0 -21
- package/README.md +0 -78
- package/dist/channels/discord.js +0 -148
- package/dist/channels/telegram.js +0 -137
- package/dist/lib/address.js +0 -21
- package/dist/lib/codex-rc.js +0 -274
- package/dist/lib/dotenv.js +0 -31
- package/dist/log.js +0 -12
- package/dist/paths.js +0 -45
- package/skills/metro/SKILL.md +0 -122
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { stationForLine } from '../stations/registry.js';
|
|
2
|
+
import type {
|
|
3
|
+
MemberCapability,
|
|
4
|
+
MemberList,
|
|
5
|
+
MetroMember,
|
|
6
|
+
ToolResult,
|
|
7
|
+
} from '@metro-labs/core/stations/types';
|
|
8
|
+
import { makeCtx, okJson, toErr } from './ctx.js';
|
|
9
|
+
import { str } from '@metro-labs/core/str';
|
|
10
|
+
|
|
11
|
+
export interface MemberListResult {
|
|
12
|
+
line: string;
|
|
13
|
+
station: string;
|
|
14
|
+
memberCount: number;
|
|
15
|
+
members: MetroMember[];
|
|
16
|
+
capability: MemberCapability;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function wrapMemberList(
|
|
20
|
+
line: string,
|
|
21
|
+
station: string,
|
|
22
|
+
list: MemberList,
|
|
23
|
+
): MemberListResult {
|
|
24
|
+
const members = Array.isArray(list.members) ? list.members : [];
|
|
25
|
+
return {
|
|
26
|
+
line,
|
|
27
|
+
station,
|
|
28
|
+
memberCount: members.length,
|
|
29
|
+
members,
|
|
30
|
+
capability: list.capability,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function unsupportedMembers(
|
|
35
|
+
line: string,
|
|
36
|
+
station: string,
|
|
37
|
+
reason: string,
|
|
38
|
+
): MemberListResult {
|
|
39
|
+
return {
|
|
40
|
+
line,
|
|
41
|
+
station,
|
|
42
|
+
memberCount: 0,
|
|
43
|
+
members: [],
|
|
44
|
+
capability: { supported: false, complete: false, reason },
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export async function dispatchListMembers(
|
|
49
|
+
a: Record<string, unknown>,
|
|
50
|
+
): Promise<ToolResult> {
|
|
51
|
+
const line = str(a.line);
|
|
52
|
+
if (!line) return okJson(unsupportedMembers('', '', 'list_members requires `line`'));
|
|
53
|
+
const station = stationForLine(line);
|
|
54
|
+
if (!station)
|
|
55
|
+
return okJson(unsupportedMembers(line, '', `no station for line ${line}`));
|
|
56
|
+
if (!station.hasAccounts || !station.hasTrain)
|
|
57
|
+
return okJson(
|
|
58
|
+
unsupportedMembers(
|
|
59
|
+
line,
|
|
60
|
+
station.name,
|
|
61
|
+
`${station.name} lines have no member roster`,
|
|
62
|
+
),
|
|
63
|
+
);
|
|
64
|
+
const args: Record<string, unknown> = { line };
|
|
65
|
+
if (typeof a.limit === 'number') args.limit = a.limit;
|
|
66
|
+
try {
|
|
67
|
+
const { result } = await makeCtx(station.name).call('listMembers', args);
|
|
68
|
+
return okJson(wrapMemberList(line, station.name, result as MemberList));
|
|
69
|
+
} catch (e) {
|
|
70
|
+
return toErr('list_members', e);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { readPreview, shownValue } from '../approvals/preview.js';
|
|
2
|
+
import { connectorToolOf } from '../connectors/gates.js';
|
|
3
|
+
|
|
4
|
+
export interface PermissionParams {
|
|
5
|
+
request_id: string;
|
|
6
|
+
tool_name: string;
|
|
7
|
+
description: string;
|
|
8
|
+
input_preview: string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const METRO_TOOL = /^mcp__metro__(.+)$/;
|
|
12
|
+
const FIELD_MAX = 300;
|
|
13
|
+
const PROMPT_MAX = 1000;
|
|
14
|
+
const RAW_MAX = 600;
|
|
15
|
+
const LEAD = ['text', 'emoji', 'name', 'bio', 'question', 'message_id', 'query', 'from'];
|
|
16
|
+
const PLACE = new Set(['line', 'account', 'station']);
|
|
17
|
+
|
|
18
|
+
const shorten = (text: string, max = FIELD_MAX): string => {
|
|
19
|
+
const shown = shownValue(text);
|
|
20
|
+
return shown.length > max ? `${shown.slice(0, max)}…` : shown;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function valueText(value: unknown): string | undefined {
|
|
24
|
+
if (typeof value === 'string') return value.trim() === '' ? undefined : value;
|
|
25
|
+
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
|
|
26
|
+
if (Array.isArray(value)) return value.length > 0 ? `${String(value.length)} item(s)` : undefined;
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function channelOf(input: Record<string, unknown>): string | undefined {
|
|
31
|
+
const line = typeof input.line === 'string' ? input.line : '';
|
|
32
|
+
const parts = line.split('/');
|
|
33
|
+
if (line.startsWith('metro://') && parts[2] !== undefined) {
|
|
34
|
+
const where = parts.slice(4).join('/');
|
|
35
|
+
return where === '' ? parts[2] : `${parts[2]} · ${where}`;
|
|
36
|
+
}
|
|
37
|
+
return typeof input.station === 'string' && input.station !== '' ? input.station : undefined;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function metroLines(tool: string, input: Record<string, unknown>): string[] {
|
|
41
|
+
const lines = [`Approval needed: ${tool}`];
|
|
42
|
+
const channel = channelOf(input);
|
|
43
|
+
if (channel !== undefined) lines.push(`Channel: ${channel}`);
|
|
44
|
+
const keys = [...LEAD, ...Object.keys(input).filter((k) => !LEAD.includes(k))];
|
|
45
|
+
for (const key of keys) {
|
|
46
|
+
if (PLACE.has(key)) continue;
|
|
47
|
+
const text = key === 'attachments' && Array.isArray(input.attachments) ? `${String(input.attachments.length)} file(s)` : valueText(input[key]);
|
|
48
|
+
if (text !== undefined) lines.push(`${key === 'text' ? 'Text' : key}: ${key === 'text' ? `"${shorten(text)}"` : shorten(text)}`);
|
|
49
|
+
}
|
|
50
|
+
return lines;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const ARGS_MAX = 8;
|
|
54
|
+
|
|
55
|
+
function argText(value: unknown): string | undefined {
|
|
56
|
+
const plain = valueText(value);
|
|
57
|
+
if (plain !== undefined || value === null || typeof value !== 'object') return plain;
|
|
58
|
+
return JSON.stringify(value);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function connectorLines(name: string, tool: string, input: Record<string, unknown> | undefined): string[] {
|
|
62
|
+
const lines = [`Approval needed: ${tool}`, `Connector: ${name}`];
|
|
63
|
+
const args = Object.entries(input ?? {}).flatMap(([key, value]) => {
|
|
64
|
+
const text = argText(value);
|
|
65
|
+
return text === undefined ? [] : [`${key}: ${shorten(text)}`];
|
|
66
|
+
});
|
|
67
|
+
lines.push(...args.slice(0, ARGS_MAX));
|
|
68
|
+
if (args.length > ARGS_MAX) lines.push(`(${String(args.length - ARGS_MAX)} more)`);
|
|
69
|
+
return lines;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function bodyLines(params: PermissionParams): string[] {
|
|
73
|
+
const metro = METRO_TOOL.exec(params.tool_name)?.[1];
|
|
74
|
+
const { input } = readPreview(params.input_preview);
|
|
75
|
+
const raw = params.input_preview.trim() === '' ? [] : [shorten(params.input_preview, RAW_MAX)];
|
|
76
|
+
if (metro !== undefined) return input === undefined ? [`Approval needed: ${metro}`, ...raw] : metroLines(metro, input);
|
|
77
|
+
const connector = connectorToolOf(params.tool_name);
|
|
78
|
+
if (connector !== undefined) return connectorLines(connector.gate.name, connector.tool, input);
|
|
79
|
+
return [`Claude wants to run ${params.tool_name}: ${shorten(params.description)}`, '', ...raw];
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function promptBody(params: PermissionParams): string {
|
|
83
|
+
const answer = `Reply "yes ${params.request_id}" or "no ${params.request_id}"`;
|
|
84
|
+
const body = bodyLines(params).join('\n');
|
|
85
|
+
const room = PROMPT_MAX - answer.length - 3;
|
|
86
|
+
return `${body.length > room ? `${body.slice(0, room)}…` : body}\n\n${answer}`;
|
|
87
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
import type { InboundRelay } from '../channels/inbound.js';
|
|
4
|
+
import { holdPrompt, type Behavior } from '../approvals/pending.js';
|
|
5
|
+
import { approversForLine } from '../agents/map.js';
|
|
6
|
+
import { metroCall } from './ctx.js';
|
|
7
|
+
import { promptBody } from './permission-prompt.js';
|
|
8
|
+
|
|
9
|
+
const PermissionRequestSchema = z.object({
|
|
10
|
+
method: z.literal('notifications/claude/channel/permission_request'),
|
|
11
|
+
params: z.object({
|
|
12
|
+
request_id: z.string(),
|
|
13
|
+
tool_name: z.string(),
|
|
14
|
+
description: z.string(),
|
|
15
|
+
input_preview: z.string(),
|
|
16
|
+
}),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
type PermissionRequest = z.infer<typeof PermissionRequestSchema>;
|
|
20
|
+
|
|
21
|
+
const trainOf = (line: string): string => line.split('/')[2] ?? '';
|
|
22
|
+
|
|
23
|
+
async function metroSend(line: string, text: string): Promise<void> {
|
|
24
|
+
await metroCall(trainOf(line), 'send', { line, text });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface PermissionRelayDeps {
|
|
28
|
+
mcp: Server;
|
|
29
|
+
relay: InboundRelay;
|
|
30
|
+
inScope: (line: string) => boolean;
|
|
31
|
+
log: (...a: unknown[]) => void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function relayLine(deps: PermissionRelayDeps, requestId: string): string | undefined {
|
|
35
|
+
const line = deps.relay.knownLine;
|
|
36
|
+
if (!line) {
|
|
37
|
+
deps.log('permission_request: no known line, held for the page only', requestId);
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
if (!deps.inScope(line)) {
|
|
41
|
+
deps.log('permission_request: known line is outside the agent scope, held for the page only', requestId);
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
if (approversForLine(line).length === 0) {
|
|
45
|
+
deps.log('permission_request: nobody on that chat may approve, held for the page only', requestId);
|
|
46
|
+
return undefined;
|
|
47
|
+
}
|
|
48
|
+
return line;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function registerPermissionRelay(deps: PermissionRelayDeps): void {
|
|
52
|
+
const { mcp, log } = deps;
|
|
53
|
+
const answer = (requestId: string) => async (behavior: Behavior): Promise<void> => {
|
|
54
|
+
await mcp.notification({ method: 'notifications/claude/channel/permission', params: { request_id: requestId, behavior } });
|
|
55
|
+
};
|
|
56
|
+
mcp.setNotificationHandler(PermissionRequestSchema as never, async (n: PermissionRequest) => {
|
|
57
|
+
const { params } = n;
|
|
58
|
+
const line = relayLine(deps, params.request_id);
|
|
59
|
+
holdPrompt(
|
|
60
|
+
{
|
|
61
|
+
requestId: params.request_id,
|
|
62
|
+
tool: params.tool_name,
|
|
63
|
+
description: params.description,
|
|
64
|
+
preview: params.input_preview,
|
|
65
|
+
line,
|
|
66
|
+
at: Date.now(),
|
|
67
|
+
},
|
|
68
|
+
mcp,
|
|
69
|
+
answer(params.request_id),
|
|
70
|
+
line === undefined ? undefined : async (text) => metroSend(line, text),
|
|
71
|
+
);
|
|
72
|
+
if (line === undefined) return;
|
|
73
|
+
try {
|
|
74
|
+
await metroSend(line, promptBody(params));
|
|
75
|
+
} catch (e) {
|
|
76
|
+
log('relay send failed', e);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import { str } from '@metro-labs/core/str';
|
|
2
|
+
import type { ToolResult } from '@metro-labs/core/stations/types';
|
|
3
|
+
import { accountFromLine, knownAccounts } from '../agents/map.js';
|
|
4
|
+
import { policyFor, storedPolicy, strictest, type Access, type PolicyTarget } from '../policy/policy.js';
|
|
5
|
+
import { errResult } from './ctx.js';
|
|
6
|
+
import { takeGrant } from '../approvals/pending.js';
|
|
7
|
+
import { NEEDS_APPROVAL } from '../approvals/needs.js';
|
|
8
|
+
import { connectorToolOf } from '../connectors/gates.js';
|
|
9
|
+
import { profileScopeLine } from './profile-lookup.js';
|
|
10
|
+
import { stationOfAccount } from './read-tool.js';
|
|
11
|
+
import { stationForTool, toolGroupOf } from './tool-catalog.js';
|
|
12
|
+
|
|
13
|
+
export type ChannelTarget = Extract<PolicyTarget, { kind: 'channel' }>;
|
|
14
|
+
|
|
15
|
+
export const UNGATED = new Set<string>(['list_accounts', 'create_upload']);
|
|
16
|
+
|
|
17
|
+
function fromLine(line: string, override: string | undefined): ChannelTarget[] {
|
|
18
|
+
const acct = accountFromLine(line);
|
|
19
|
+
return acct === undefined ? [] : [{ kind: 'channel', station: acct.station, account: override ?? acct.accountId }];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function channelTargets(name: string, a: Record<string, unknown>): ChannelTarget[] {
|
|
23
|
+
if (UNGATED.has(name)) return [];
|
|
24
|
+
if (name === 'get_profile') {
|
|
25
|
+
const line = profileScopeLine(a);
|
|
26
|
+
return line === undefined ? [] : fromLine(line, undefined);
|
|
27
|
+
}
|
|
28
|
+
const account = str(a.account);
|
|
29
|
+
const line = str(a.line);
|
|
30
|
+
if (line) return fromLine(line, account || undefined);
|
|
31
|
+
const station = stationForTool(name, a) ?? (account ? stationOfAccount(account) : undefined);
|
|
32
|
+
if (station === undefined) return [];
|
|
33
|
+
if (account) return [{ kind: 'channel', station, account }];
|
|
34
|
+
return knownAccounts()
|
|
35
|
+
.filter((k) => k.station === station)
|
|
36
|
+
.map((k) => ({ kind: 'channel', station, account: k.id }));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface PolicyVerdict {
|
|
40
|
+
access: Access;
|
|
41
|
+
target?: ChannelTarget;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function channelDecision(name: string, a: Record<string, unknown>): PolicyVerdict {
|
|
45
|
+
const tool = { name, group: toolGroupOf(name) };
|
|
46
|
+
let verdict: PolicyVerdict = { access: 'allow' };
|
|
47
|
+
for (const target of channelTargets(name, a)) {
|
|
48
|
+
const access = policyFor(target, tool);
|
|
49
|
+
if (strictest([verdict.access, access]) !== verdict.access) verdict = { access, target };
|
|
50
|
+
}
|
|
51
|
+
return verdict;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isChannelTool = (name: string) => (tool: string): boolean => tool.endsWith(`__${name}`) && connectorToolOf(tool) === undefined;
|
|
55
|
+
|
|
56
|
+
export function policyGate(name: string, a: Record<string, unknown>): ToolResult | undefined {
|
|
57
|
+
const { access, target } = channelDecision(name, a);
|
|
58
|
+
if (access === 'allow' || target === undefined) return undefined;
|
|
59
|
+
if (access === 'deny') return errResult(`Blocked by the owner's policy for ${target.station} (${name}).`);
|
|
60
|
+
return takeGrant(isChannelTool(name), a) ? undefined : errResult(NEEDS_APPROVAL(target.station, name));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface EffectivePolicy {
|
|
64
|
+
read: Access;
|
|
65
|
+
write: Access;
|
|
66
|
+
tools: Record<string, Access>;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function effectivePolicy(target: ChannelTarget): EffectivePolicy {
|
|
70
|
+
const stored = storedPolicy(target);
|
|
71
|
+
return { read: stored?.read ?? 'allow', write: stored?.write ?? 'allow', tools: { ...stored?.tools } };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function withEffectivePolicy(byStation: Record<string, unknown[]>): Record<string, unknown[]> {
|
|
75
|
+
const out: Record<string, unknown[]> = {};
|
|
76
|
+
for (const [station, rows] of Object.entries(byStation))
|
|
77
|
+
out[station] = rows.map((row) => {
|
|
78
|
+
if (typeof row !== 'object' || row === null || Array.isArray(row)) return row;
|
|
79
|
+
const id = (row as { id?: unknown }).id;
|
|
80
|
+
return typeof id === 'string' ? { ...row, policy: effectivePolicy({ kind: 'channel', station, account: id }) } : row;
|
|
81
|
+
});
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { writeSecure } from '@metro-labs/core/secure-fs';
|
|
4
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
5
|
+
import type { ToolGroup } from '@metro-labs/core/stations/types';
|
|
6
|
+
import { agentsDir } from '../agents/files.js';
|
|
7
|
+
import { knownAccounts } from '../agents/map.js';
|
|
8
|
+
import { onPoliciesChanged, storedPolicies, type ToolPolicy } from '../policy/policy.js';
|
|
9
|
+
import { UNGATED } from './policy-gate.js';
|
|
10
|
+
import { connectorGates } from '../connectors/gates.js';
|
|
11
|
+
import { stationToolOwners, TOOL_DEFS, toolGroupOf } from './tool-catalog.js';
|
|
12
|
+
|
|
13
|
+
export interface PolicySnapshot {
|
|
14
|
+
version: 1;
|
|
15
|
+
tools: Record<string, ToolGroup>;
|
|
16
|
+
owners: Record<string, string>;
|
|
17
|
+
ungated: string[];
|
|
18
|
+
accounts: Record<string, ToolPolicy>;
|
|
19
|
+
stations: Record<string, string[]>;
|
|
20
|
+
connectors: Record<string, SnapshotConnector>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface SnapshotConnector {
|
|
24
|
+
id: string;
|
|
25
|
+
name: string;
|
|
26
|
+
policy: ToolPolicy;
|
|
27
|
+
tools: Record<string, ToolGroup>;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const FILE = 'policy.json';
|
|
31
|
+
|
|
32
|
+
export const policySnapshotPath = (dir = agentsDir()): string => join(dir, FILE);
|
|
33
|
+
|
|
34
|
+
export function policySnapshot(): PolicySnapshot {
|
|
35
|
+
const accounts: Record<string, ToolPolicy> = {};
|
|
36
|
+
for (const { target, policy } of storedPolicies())
|
|
37
|
+
if (target.kind === 'channel') accounts[`${target.station}/${target.account}`] = policy;
|
|
38
|
+
const stations: Record<string, string[]> = {};
|
|
39
|
+
for (const known of knownAccounts()) (stations[known.station] ??= []).push(known.id);
|
|
40
|
+
return {
|
|
41
|
+
version: 1,
|
|
42
|
+
tools: Object.fromEntries(TOOL_DEFS.map((def) => [def.name, toolGroupOf(def.name)])),
|
|
43
|
+
owners: stationToolOwners(),
|
|
44
|
+
ungated: [...UNGATED],
|
|
45
|
+
accounts,
|
|
46
|
+
stations,
|
|
47
|
+
connectors: Object.fromEntries(
|
|
48
|
+
connectorGates()
|
|
49
|
+
.filter((gate) => gate.server !== '')
|
|
50
|
+
.map((gate) => [gate.server, { id: gate.id, name: gate.name, policy: gate.policy, tools: gate.tools }]),
|
|
51
|
+
),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function current(path: string): string | undefined {
|
|
56
|
+
try {
|
|
57
|
+
return readFileSync(path, 'utf8');
|
|
58
|
+
} catch {
|
|
59
|
+
return undefined;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function writePolicySnapshot(path = policySnapshotPath()): void {
|
|
64
|
+
const text = `${JSON.stringify(policySnapshot(), null, 2)}\n`;
|
|
65
|
+
if (current(path) === text) return;
|
|
66
|
+
try {
|
|
67
|
+
writeSecure(path, text);
|
|
68
|
+
log.info({ path }, 'policy: snapshot for the plugin hook written');
|
|
69
|
+
} catch (err) {
|
|
70
|
+
log.warn({ path, err: errMsg(err) }, 'policy: the snapshot for the plugin hook could not be written');
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function watchPolicySnapshot(): () => void {
|
|
75
|
+
writePolicySnapshot();
|
|
76
|
+
return onPoliciesChanged(() => {
|
|
77
|
+
writePolicySnapshot();
|
|
78
|
+
});
|
|
79
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import type { ToolDef } from './tool-def.js';
|
|
2
|
+
import { Line } from '@metro-labs/core/lines';
|
|
3
|
+
import type { Station, ToolResult } from '@metro-labs/core/stations/types';
|
|
4
|
+
import { str } from '@metro-labs/core/str';
|
|
5
|
+
import { stationByName } from '../stations/registry.js';
|
|
6
|
+
import { errResult, makeCtx, okJson, toErr } from './ctx.js';
|
|
7
|
+
|
|
8
|
+
export const GET_PROFILE_TOOL: ToolDef = {
|
|
9
|
+
name: 'get_profile',
|
|
10
|
+
group: 'read',
|
|
11
|
+
description:
|
|
12
|
+
'Who is this person? Give the `from` attribute of one of their messages verbatim ' +
|
|
13
|
+
'(`metro://<station>/<account>/user/<id>`) and get what their network says about them: ' +
|
|
14
|
+
'{id, name, display_name, about, avatar, address}. XMTP answers the Basename ' +
|
|
15
|
+
'(<label>.stage.base.eth), its name, description and avatar records, and the wallet address; ' +
|
|
16
|
+
'WhatsApp the phone number, the name they last used, the about line and the picture; Telegram the handle, the name and the bio; Discord ' +
|
|
17
|
+
'the username, the global name and the avatar. Fields the network does not have are absent. ' +
|
|
18
|
+
'The answer is cached for ten minutes, so ask when you want to know someone, not on every message.',
|
|
19
|
+
inputSchema: {
|
|
20
|
+
type: 'object',
|
|
21
|
+
properties: {
|
|
22
|
+
from: { type: 'string', description: 'The `from` of a message, exactly as received.' },
|
|
23
|
+
},
|
|
24
|
+
required: ['from'],
|
|
25
|
+
},
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export interface ProfileTarget {
|
|
29
|
+
station: Station;
|
|
30
|
+
account: string;
|
|
31
|
+
user: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function profileTarget(a: Record<string, unknown>): ProfileTarget | { refused: string } {
|
|
35
|
+
const from = str(a.from);
|
|
36
|
+
if (!from) return { refused: 'get_profile requires `from`' };
|
|
37
|
+
const parsed = Line.parse(from);
|
|
38
|
+
const at = parsed?.path.indexOf('user') ?? -1;
|
|
39
|
+
if (parsed === null || at < 0 || at + 1 >= parsed.path.length) return { refused: `${from} is not a person: expected metro://<station>/<account>/user/<id>` };
|
|
40
|
+
const station = stationByName(parsed.station);
|
|
41
|
+
if (!station) return { refused: `no station named ${parsed.station}` };
|
|
42
|
+
if (station.readsProfiles !== true) return { refused: `${station.name} has no profile metro can read` };
|
|
43
|
+
return { station, account: at === 0 ? 'default' : parsed.path.slice(0, at).join('/'), user: parsed.path.slice(at + 1).join('/') };
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const profileScopeLine = (a: Record<string, unknown>): string | undefined => {
|
|
47
|
+
const target = profileTarget(a);
|
|
48
|
+
return 'refused' in target ? undefined : `metro://${target.station.name}/${target.account}/user/${target.user}`;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export async function dispatchGetProfile(a: Record<string, unknown>): Promise<ToolResult> {
|
|
52
|
+
const target = profileTarget(a);
|
|
53
|
+
if ('refused' in target) return errResult(target.refused);
|
|
54
|
+
try {
|
|
55
|
+
const { result } = await makeCtx(target.station.name).call('profile', { account: target.account, user: target.user });
|
|
56
|
+
return okJson(result);
|
|
57
|
+
} catch (e) {
|
|
58
|
+
return toErr('get_profile', e);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import type { ToolDef } from './tool-def.js';
|
|
2
|
+
import type { CanonicalAttachment, Station, ToolResult } from '@metro-labs/core/stations/types';
|
|
3
|
+
import { PROFILE_FIELDS, type ProfileField } from '@metro-labs/core/stations/profile';
|
|
4
|
+
import { str } from '@metro-labs/core/str';
|
|
5
|
+
import { STATIONS, stationByName } from '../stations/registry.js';
|
|
6
|
+
import { cleanupAttachments, resolveAttachments, type ResolvedAttachment } from '../stations/attach-resolve.js';
|
|
7
|
+
import { errResult, makeCtx, okJson, toErr } from './ctx.js';
|
|
8
|
+
import { allowedAgents, currentIdentity } from './request-identity.js';
|
|
9
|
+
|
|
10
|
+
const SOURCE = { type: 'string' };
|
|
11
|
+
|
|
12
|
+
export const SET_PROFILE_TOOL: ToolDef = {
|
|
13
|
+
name: 'set_profile',
|
|
14
|
+
group: 'write',
|
|
15
|
+
description:
|
|
16
|
+
'Change how one of your accounts presents itself on its network: the display name, the bio ' +
|
|
17
|
+
'(about line) and the avatar. Args: station (required), account? (which account of that station; ' +
|
|
18
|
+
'needed only when you have several), name?, bio?, avatar? (an image given like an attachment: ' +
|
|
19
|
+
'exactly one of upload, url, path, data, plus mime and name). At least one of name, bio, avatar. ' +
|
|
20
|
+
'Each station takes what its network allows (`list_accounts` reports the fields under ' +
|
|
21
|
+
'`profiles`); a field the station cannot set is refused by name, never dropped. Discord changes ' +
|
|
22
|
+
'the bot username and app description (Discord allows two username changes an hour); Telegram ' +
|
|
23
|
+
'bots set their name, description and photo; a Telegram user account sets its first name, bio ' +
|
|
24
|
+
'(70 chars) and photo; WhatsApp sets the profile name, about and picture; XMTP writes the ' +
|
|
25
|
+
'name, description and avatar records of the account\'s <label>.stage.base.eth name on Base, ' +
|
|
26
|
+
'so it needs a name first (claimed on the channel page, never by you). Visible to everyone ' +
|
|
27
|
+
'who talks to the account, so do it when asked, not on your own. Returns {account, applied}.',
|
|
28
|
+
inputSchema: {
|
|
29
|
+
type: 'object',
|
|
30
|
+
properties: {
|
|
31
|
+
station: { type: 'string', description: 'discord-bot | telegram-bot | telegram | whatsapp | xmtp' },
|
|
32
|
+
account: { type: 'string', description: 'The account id on that station, from list_accounts.' },
|
|
33
|
+
name: { type: 'string', description: 'The display name.' },
|
|
34
|
+
bio: { type: 'string', description: 'The bio or about line.' },
|
|
35
|
+
avatar: {
|
|
36
|
+
type: 'object',
|
|
37
|
+
description: 'The new picture, as an attachment source.',
|
|
38
|
+
properties: { upload: SOURCE, url: SOURCE, path: SOURCE, data: SOURCE, mime: SOURCE, name: SOURCE },
|
|
39
|
+
},
|
|
40
|
+
},
|
|
41
|
+
required: ['station'],
|
|
42
|
+
},
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export const profileCapabilities = (): Record<string, ProfileField[]> => {
|
|
46
|
+
const out: Record<string, ProfileField[]> = {};
|
|
47
|
+
for (const s of STATIONS) if (s.profileFields !== undefined) out[s.name] = [...s.profileFields];
|
|
48
|
+
return out;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const asked = (a: Record<string, unknown>): ProfileField[] => PROFILE_FIELDS.filter((f) => a[f] !== undefined);
|
|
52
|
+
|
|
53
|
+
type Target = { station: Station } | { refused: string };
|
|
54
|
+
|
|
55
|
+
function targetOf(a: Record<string, unknown>): Target {
|
|
56
|
+
const name = str(a.station);
|
|
57
|
+
if (!name) return { refused: 'set_profile requires `station`' };
|
|
58
|
+
const station = stationByName(name);
|
|
59
|
+
if (!station) return { refused: `no station named ${name}` };
|
|
60
|
+
const fields = station.profileFields;
|
|
61
|
+
if (fields === undefined) return { refused: `${name} accounts have no profile metro can set` };
|
|
62
|
+
const wanted = asked(a);
|
|
63
|
+
if (wanted.length === 0) return { refused: 'set_profile needs at least one of name, bio, avatar' };
|
|
64
|
+
const refused = wanted.filter((f) => !fields.has(f));
|
|
65
|
+
if (refused.length > 0) return { refused: `${name} cannot set ${refused.join(', ')}; it takes ${[...fields].join(', ')}` };
|
|
66
|
+
return { station };
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function trainArgs(a: Record<string, unknown>, picture: ResolvedAttachment | undefined): Record<string, unknown> {
|
|
70
|
+
const args: Record<string, unknown> = {};
|
|
71
|
+
if (typeof a.account === 'string') args.account = a.account;
|
|
72
|
+
if (typeof a.name === 'string') args.name = a.name;
|
|
73
|
+
if (typeof a.bio === 'string') args.bio = a.bio;
|
|
74
|
+
if (picture !== undefined) args.avatar = { path: picture.path, mime: picture.mime, name: picture.name };
|
|
75
|
+
return args;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export async function dispatchSetProfile(a: Record<string, unknown>): Promise<ToolResult> {
|
|
79
|
+
const target = targetOf(a);
|
|
80
|
+
if ('refused' in target) return errResult(target.refused);
|
|
81
|
+
const avatar = a.avatar === undefined ? [] : [a.avatar as CanonicalAttachment];
|
|
82
|
+
let resolved: ResolvedAttachment[];
|
|
83
|
+
try {
|
|
84
|
+
resolved = await resolveAttachments(avatar, { allowed: allowedAgents(currentIdentity()) });
|
|
85
|
+
} catch (e) {
|
|
86
|
+
return toErr('set_profile', e);
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const { result } = await makeCtx(target.station.name).call('set_profile', trainArgs(a, resolved[0]));
|
|
90
|
+
return okJson(result);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
return toErr('set_profile', e);
|
|
93
|
+
} finally {
|
|
94
|
+
await cleanupAttachments(resolved);
|
|
95
|
+
}
|
|
96
|
+
}
|