@stage-labs/metro 0.1.0-beta.23 → 0.1.0-beta.231
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 +9 -101
- package/dist/background.js +123 -0
- package/dist/claude-settings.js +40 -0
- package/dist/claude.js +197 -0
- package/dist/cli.js +52 -110
- package/dist/control.js +19 -103
- package/dist/hold.js +210 -0
- package/dist/local.js +43 -0
- package/dist/mcp-config.js +25 -0
- package/dist/memory.js +222 -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 +96 -0
- package/dist/runtime.js +29 -52
- package/dist/serve.js +157 -0
- package/dist/service.js +304 -0
- package/dist/tail.js +119 -0
- package/dist/update.js +13 -1
- package/package.json +2 -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/MEMORY.md +78 -0
- package/runtime/marketplace/plugin/METRO.md +26 -0
- package/runtime/marketplace/plugin/STAGE.md +42 -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/node_modules/@metro-labs/core/package.json +51 -0
- package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/events.ts +11 -7
- package/runtime/node_modules/@metro-labs/{mcp/src/db → core/src}/ids.ts +5 -0
- package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/stations → core/src}/lines.ts +22 -26
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/protocol.ts +7 -4
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/secure-fs.ts +17 -0
- package/runtime/node_modules/@metro-labs/core/src/station-names.ts +14 -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 +83 -0
- package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/attachments.ts +0 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/mail-text.ts +72 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/mail-trust.ts +29 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/mail.ts +212 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +16 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/oauth.ts +126 -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/{mcp → core}/src/stations/station-runtime.ts +6 -11
- 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 +84 -0
- package/runtime/node_modules/@metro-labs/{mcp → core}/src/stations/types.ts +18 -1
- package/runtime/node_modules/@metro-labs/core/src/tickets.ts +37 -0
- package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/package.json +49 -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/crontab.ts +44 -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 +89 -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 +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +80 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +99 -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 +144 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +43 -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 +142 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/db/key-map.ts → daemon/src/agents/keys.ts} +1 -11
- package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +133 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/policy-route.ts +27 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/db/agent-scope.ts → daemon/src/agents/scope.ts} +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/agents/sender-cards.ts +94 -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/pending.ts +150 -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 +223 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +36 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/crash-guard.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +21 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/boot}/paths.ts +6 -34
- package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +26 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/inbound.ts +75 -45
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/media-note.ts +11 -7
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/pending.ts +4 -1
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/relay.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +380 -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-routine.ts +114 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +32 -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 +295 -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 +346 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +120 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/usage-probe.ts +127 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +113 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-api.ts → daemon/src/connectors/api.ts} +79 -113
- package/runtime/node_modules/@metro-labs/{mcp/src/db/connector-config.ts → daemon/src/connectors/config.ts} +67 -41
- 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/{mcp/src/daemon → daemon/src/connectors}/oauth-client.ts +14 -17
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/connectors}/oauth-discovery.ts +33 -20
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth-routes.ts → daemon/src/connectors/oauth-routes.ts} +30 -32
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/connector-oauth.ts → daemon/src/connectors/oauth.ts} +61 -22
- 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.ts +347 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +385 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +81 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-api.ts +116 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-crypto.ts +44 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/transfer-store.ts +48 -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/{mcp/src/daemon → daemon/src/files}/attach-grant.ts +4 -4
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-owner.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-reaper.ts +174 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/attach-serve.ts +12 -13
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-api.ts +10 -27
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/files}/upload-store.ts +4 -4
- 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 +214 -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 +209 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-version.ts +44 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +155 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +93 -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 +307 -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 +235 -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 +203 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +285 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-lists.ts +57 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +161 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +72 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +104 -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 +118 -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 +35 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/tool-search.ts +126 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage-refresh.ts +102 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +348 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/accounts.ts +28 -17
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/attachment-schema.ts +91 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/call-tools.ts +49 -20
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/ctx.ts +4 -4
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/event-store.ts +15 -16
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/group-tools.ts +2 -2
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/index.ts +34 -80
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/instructions.ts +19 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/member-tools.ts +2 -2
- 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 +88 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +82 -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/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 +71 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +47 -44
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +93 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-def.ts +9 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +64 -113
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/upload-tool.ts +6 -11
- 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/install.ts +37 -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/{mcp/src/daemon → daemon/src/routes}/http.ts +63 -51
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +209 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +69 -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/cloudwatch.ts +178 -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-browser.ts +102 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-driver.ts +42 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +5 -31
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-resolve.ts +38 -13
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/attach-session.ts +32 -22
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-xmtp.ts +8 -9
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach.ts +69 -3
- package/runtime/node_modules/@metro-labs/{mcp/src/db → daemon/src/stations}/materialize.ts +60 -179
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +20 -2
- package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor-io.ts +3 -3
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/supervisor.ts +6 -9
- package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +32 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon → daemon/src/stations}/train-call.ts +1 -1
- 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 +9 -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 +147 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +44 -0
- package/runtime/node_modules/@metro-labs/discord-bot/package.json +2 -4
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +3 -23
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +19 -187
- package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +2 -2
- package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +26 -67
- package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
- package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +2 -2
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +8 -14
- package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +2 -2
- 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 +12 -13
- package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +5 -1
- package/runtime/node_modules/@metro-labs/gmail/package.json +25 -0
- package/runtime/node_modules/@metro-labs/gmail/src/accounts.ts +81 -0
- package/runtime/node_modules/@metro-labs/gmail/src/actions.ts +14 -0
- package/runtime/node_modules/@metro-labs/gmail/src/api.ts +31 -0
- package/runtime/node_modules/@metro-labs/gmail/src/attachments.ts +35 -0
- package/runtime/node_modules/@metro-labs/gmail/src/auth.ts +105 -0
- package/runtime/node_modules/@metro-labs/gmail/src/config.ts +13 -0
- package/runtime/node_modules/@metro-labs/gmail/src/inbound.ts +96 -0
- package/runtime/node_modules/@metro-labs/gmail/src/index.ts +12 -0
- package/runtime/node_modules/@metro-labs/gmail/src/login.ts +37 -0
- package/runtime/node_modules/@metro-labs/gmail/src/message.ts +125 -0
- package/runtime/node_modules/@metro-labs/gmail/src/mime.ts +70 -0
- package/runtime/node_modules/@metro-labs/gmail/src/outbound.ts +124 -0
- package/runtime/node_modules/@metro-labs/gmail/src/query.ts +99 -0
- package/runtime/node_modules/@metro-labs/gmail/src/state.ts +24 -0
- package/runtime/node_modules/@metro-labs/gmail/src/station.ts +15 -0
- package/runtime/node_modules/@metro-labs/gmail/src/trust.ts +42 -0
- package/runtime/node_modules/@metro-labs/http/package.json +30 -0
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +135 -0
- package/runtime/node_modules/@metro-labs/{mcp/src/daemon/session-api.ts → http/src/mode-api.ts} +13 -9
- package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
- package/runtime/node_modules/@metro-labs/http/src/sigv4.ts +90 -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 +78 -0
- package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -0
- package/runtime/node_modules/@metro-labs/outlook/src/attachments.ts +81 -0
- package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +120 -0
- package/runtime/node_modules/@metro-labs/outlook/src/browser.ts +42 -0
- package/runtime/node_modules/@metro-labs/outlook/src/config.ts +23 -0
- package/runtime/node_modules/@metro-labs/outlook/src/format.ts +59 -0
- package/runtime/node_modules/@metro-labs/outlook/src/graph.ts +55 -0
- package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +96 -0
- package/runtime/node_modules/@metro-labs/outlook/src/index.ts +12 -0
- package/runtime/node_modules/@metro-labs/outlook/src/login.ts +129 -0
- package/runtime/node_modules/@metro-labs/outlook/src/me.ts +31 -0
- package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +103 -0
- package/runtime/node_modules/@metro-labs/outlook/src/query.ts +84 -0
- package/runtime/node_modules/@metro-labs/outlook/src/state.ts +28 -0
- package/runtime/node_modules/@metro-labs/outlook/src/station.ts +15 -0
- package/runtime/node_modules/@metro-labs/outlook/src/trust.ts +41 -0
- package/runtime/node_modules/@metro-labs/telegram/package.json +1 -3
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +5 -24
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +8 -6
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/client.ts +2 -2
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +19 -34
- package/runtime/node_modules/@metro-labs/telegram/src/group.ts +3 -3
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +13 -34
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +4 -43
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +2 -3
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +2 -2
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +4 -1
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +0 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/package.json +1 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +7 -41
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +22 -77
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +2 -2
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +46 -48
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +15 -15
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +6 -82
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +85 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +5 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +20 -12
- package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +96 -0
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +124 -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 +47 -0
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +164 -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 +87 -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 +1 -1
- package/runtime/node_modules/@metro-labs/webhook/src/station.ts +5 -8
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +2 -5
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +6 -28
- package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +54 -11
- package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +2 -2
- package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +86 -4
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +7 -58
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +11 -28
- package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +4 -37
- package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/names.ts +75 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +44 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/resolve.ts +52 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +16 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +5 -9
- package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +0 -1
- package/runtime/node_modules/@metro-labs/xmtp/package.json +3 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +10 -20
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +15 -204
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +40 -53
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +72 -220
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
- package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +27 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +11 -7
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +7 -24
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +8 -18
- package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +30 -16
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +12 -20
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +57 -33
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +1 -1
- 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/resolve.ts +44 -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 +7 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +22 -58
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +55 -48
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +4 -6
- package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +19 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +14 -4
- package/runtime/runtime.json +1 -1
- package/runtime/server.ts +1 -0
- package/runtime/stations.json +38 -0
- package/dist/prompt.js +0 -61
- package/dist/store.js +0 -70
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +0 -195
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +0 -292
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +0 -197
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -5
- package/runtime/node_modules/@metro-labs/mcp/package.json +0 -66
- package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +0 -24
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +0 -311
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +0 -341
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +0 -129
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +0 -320
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair-api.ts +0 -111
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cli-pair.ts +0 -20
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/collection-api.ts +0 -202
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +0 -31
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +0 -100
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +0 -57
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +0 -333
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-auth.ts +0 -164
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/google-oauth.ts +0 -208
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +0 -79
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +0 -65
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/pair-codes.ts +0 -58
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/project-api.ts +0 -214
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-api.ts +0 -125
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/run-pair.ts +0 -18
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-source.ts +0 -105
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +0 -41
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session.ts +0 -218
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +0 -169
- package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +0 -91
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +0 -215
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +0 -71
- package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +0 -37
- package/runtime/node_modules/@metro-labs/mcp/src/db/connector-collections.ts +0 -218
- package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +0 -329
- package/runtime/node_modules/@metro-labs/mcp/src/db/projects.ts +0 -300
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtime-admin.ts +0 -31
- package/runtime/node_modules/@metro-labs/mcp/src/db/runtimes.ts +0 -105
- package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +0 -131
- package/runtime/node_modules/@metro-labs/mcp/src/db/token-carry.ts +0 -45
- package/runtime/node_modules/@metro-labs/mcp/src/db/users.ts +0 -57
- package/runtime/node_modules/@metro-labs/mcp/src/db/whatsapp-login.ts +0 -18
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +0 -29
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +0 -21
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +0 -66
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +0 -73
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +0 -172
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +0 -43
- package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +0 -240
- package/runtime/node_modules/@metro-labs/mcp/src/server.ts +0 -2
- package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +0 -80
- package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +0 -88
- package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -3
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +0 -87
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +0 -55
- package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +0 -349
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/log.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/mcp → core/src}/str.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → core/src}/train-error.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/channels/webhook-note.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/raw-get-stream.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/api-error.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{mcp/src/daemon → http/src}/cors.ts +0 -0
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: stage
|
|
3
|
+
description: How to use Stage channels (XMTP groups, https://stage.box) through metro. Create a channel, rename it, add or remove members, set labels and assignees, read its info, and send, reply, react, unreact, delete and read messages. Works only when this agent has an XMTP account in metro.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Stage (https://stage.box) is a messenger built on XMTP. A Stage channel is an XMTP group. You use it with the metro tools below (`mcp__metro__<name>`).
|
|
7
|
+
|
|
8
|
+
**XMTP only.** All of this needs an XMTP account connected to this agent in metro. Check with `list_accounts`: it must show an account under `xmtp`. Many agents have none. Then none of this works: say so plainly and do not try another channel. The owner can add an XMTP channel on https://metro.box.
|
|
9
|
+
|
|
10
|
+
**Lines and links.** A channel's line is `metro://xmtp/<account>/<channel id>`. Pass it verbatim as `line`. Its link is `https://stage.box/#/channel/<channel id>`, the last part of the line. Stage shows Markdown, so `[#Launch plan](https://stage.box/#/channel/<channel id>)` is a clickable link. A `message_id` comes from the inbound message or from `read`.
|
|
11
|
+
|
|
12
|
+
**Channels.**
|
|
13
|
+
- Create: `create_group {"station": "xmtp", "name": "Launch plan", "members": ["0x…"]}`. Members are 0x addresses or XMTP inbox ids. It returns the new `line` and `id`.
|
|
14
|
+
- Rename: `set_channel_metadata {"line": "…", "name": "New title"}`. No tool sets the description or the picture.
|
|
15
|
+
- Add or remove people: `add_members {"line": "…", "members": ["0x…"]}`. `remove_members` takes the same shape.
|
|
16
|
+
- Leave: `close_channel {"line": "…", "removeSelf": true}`.
|
|
17
|
+
- Read: `group_info {"line": "…"}` gives the name, `labels`, `assigned`, `appData` and the members (`inboxId`, `address`). `list_members {"line": "…"}` gives the members only.
|
|
18
|
+
- Direct chat: `dm {"address": "0x…"}` opens a 1:1 chat and returns its line.
|
|
19
|
+
- With several XMTP accounts, add `"account": "<id>"` to `create_group` and `dm`.
|
|
20
|
+
|
|
21
|
+
**Labels and assignees.** They live in the channel's metadata, and Stage shows them on the channel.
|
|
22
|
+
- Labels: `set_channel_metadata {"line": "…", "metadata": {"labels": ["🚧 In progress", "Metro"]}}`. At most 16 labels of 24 characters each.
|
|
23
|
+
- Assignees: `set_channel_metadata {"line": "…", "metadata": {"assigned": ["0x…"]}}`. Only 0x addresses of current members.
|
|
24
|
+
- Each key you send replaces that whole list. Keys you leave out stay as they are. To add one label or one assignee, read `group_info` first and send the old list plus the new one. `[]` clears a list. Read `group_info` again to check it saved.
|
|
25
|
+
|
|
26
|
+
**Task status labels.** When a channel is a task, keep exactly one of these status labels, plus any project labels:
|
|
27
|
+
- `🗒️ Backlog`
|
|
28
|
+
- `🎯 To-do`
|
|
29
|
+
- `🚧 In progress`
|
|
30
|
+
- `🔍 In review`
|
|
31
|
+
- `✅ Done`
|
|
32
|
+
- `🚫 Blocked`
|
|
33
|
+
|
|
34
|
+
Move it from Backlog or To-do to In progress, then In review, then Done. Use Blocked while you wait on someone. To change it, read `group_info`, swap the old status for the new one in `labels`, keep the other labels, and send the whole list with `set_channel_metadata` `metadata.labels`.
|
|
35
|
+
|
|
36
|
+
**Messages.**
|
|
37
|
+
- Send: `send {"line": "…", "text": "Hello"}`. Reply with a quote: `reply {"line": "…", "message_id": "…", "text": "…"}`.
|
|
38
|
+
- React: `react {"line": "…", "message_id": "…", "emoji": "👍"}`. `unreact` takes the same shape.
|
|
39
|
+
- Delete one of your own messages: `delete {"line": "…", "message_id": "…"}`. XMTP has no edit and no typing.
|
|
40
|
+
- History: `read {"line": "…", "limit": 20}`.
|
|
41
|
+
- Poll: `ask {"line": "…", "question": "Ship today?", "options": ["Yes", "No"]}`.
|
|
42
|
+
- File: `create_upload {"name": "report.pdf"}`, run the `curl` line it returns in a shell, then `send {"line": "…", "attachments": [{"upload": "<upload_id>"}]}`. Files that are not images are refused over about 190 KiB.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { policyVerdict } from './policy.mjs';
|
|
4
|
+
|
|
5
|
+
const BLOCKING = new Set(['AskUserQuestion', 'ExitPlanMode', 'EnterPlanMode']);
|
|
6
|
+
const ORCHESTRATION = new Set([
|
|
7
|
+
'Agent',
|
|
8
|
+
'Workflow',
|
|
9
|
+
'ToolSearch',
|
|
10
|
+
'Skill',
|
|
11
|
+
'ScheduleWakeup',
|
|
12
|
+
'Monitor',
|
|
13
|
+
'CronCreate',
|
|
14
|
+
'CronList',
|
|
15
|
+
'CronDelete',
|
|
16
|
+
'TaskCreate',
|
|
17
|
+
'TaskGet',
|
|
18
|
+
'TaskList',
|
|
19
|
+
'TaskOutput',
|
|
20
|
+
'TaskStop',
|
|
21
|
+
'TaskUpdate',
|
|
22
|
+
]);
|
|
23
|
+
const IMAGE = /\.(png|jpe?g|gif|webp|bmp)$/i;
|
|
24
|
+
const MCP = /^mcp__/;
|
|
25
|
+
const METRO_MCP = /^mcp__metro__/;
|
|
26
|
+
|
|
27
|
+
function answer(decision, reason) {
|
|
28
|
+
process.stdout.write(
|
|
29
|
+
`${JSON.stringify({ hookSpecificOutput: { hookEventName: 'PreToolUse', permissionDecision: decision, permissionDecisionReason: reason } })}\n`,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function ownerPolicy(tool, input, subagent) {
|
|
34
|
+
const verdict = policyVerdict(tool, input);
|
|
35
|
+
if (verdict === null) return null;
|
|
36
|
+
const { where } = verdict;
|
|
37
|
+
if (verdict.access === 'deny') return { decision: 'deny', reason: `Blocked by the owner's policy for ${verdict.owner} (${verdict.tool}).` };
|
|
38
|
+
if (subagent) return { decision: 'ask', reason: `The owner asked to approve ${where}.` };
|
|
39
|
+
return {
|
|
40
|
+
decision: 'deny',
|
|
41
|
+
reason: `${where} needs the owner's approval, and waiting for it here would stop the whole session. Run this exact call from a background worker (Agent, run_in_background: true): the worker waits for the owner's answer while you keep answering.`,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function verdict(payload, input) {
|
|
46
|
+
const tool = typeof payload.tool_name === 'string' ? payload.tool_name : '';
|
|
47
|
+
if (BLOCKING.has(tool))
|
|
48
|
+
return `${tool} blocks the session waiting on the terminal, and nobody is watching it. Decide with your best judgement and state the assumption, or ask over chat with the metro tools and keep working in the meantime.`;
|
|
49
|
+
if (typeof payload.agent_id === 'string' && payload.agent_id !== '') return null;
|
|
50
|
+
if (tool === 'Agent' && input.run_in_background === false)
|
|
51
|
+
return 'A foreground subagent blinds the main thread: it cannot see or acknowledge inbound metro messages until the agent finishes. Re-issue the identical Agent call with run_in_background: true, then wait for the task notification.';
|
|
52
|
+
if (ORCHESTRATION.has(tool)) return null;
|
|
53
|
+
if (METRO_MCP.test(tool)) return null;
|
|
54
|
+
if (MCP.test(tool))
|
|
55
|
+
return `A connector call is work, not orchestration: ${tool} reads somebody's files, mail or records, and its answer can be long enough to crowd out the chat you are here to follow. Only metro's own tools run on this thread. Delegate it to a subagent with the Agent tool (run_in_background: true; the worker reaches every connector) and relay what it finds.`;
|
|
56
|
+
if (tool === 'Read' && typeof input.file_path === 'string' && IMAGE.test(input.file_path)) return null;
|
|
57
|
+
return `The main thread is orchestrator-only, so ${tool === '' ? 'this tool' : tool} is not available here. Delegate the work to a subagent with the Agent tool (run_in_background: true; the worker agent has full tool access) or to a Workflow, and report the result over metro.`;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
let payload = {};
|
|
61
|
+
try {
|
|
62
|
+
payload = JSON.parse(readFileSync(0, 'utf8'));
|
|
63
|
+
} catch {
|
|
64
|
+
payload = {};
|
|
65
|
+
}
|
|
66
|
+
const shaped = payload !== null && typeof payload === 'object' ? payload : {};
|
|
67
|
+
const input = shaped.tool_input !== null && typeof shaped.tool_input === 'object' ? shaped.tool_input : {};
|
|
68
|
+
const reason = verdict(shaped, input);
|
|
69
|
+
const policy = reason === null && typeof shaped.tool_name === 'string' ? ownerPolicy(shaped.tool_name, input, typeof shaped.agent_id === 'string' && shaped.agent_id !== '') : null;
|
|
70
|
+
if (reason !== null) answer('deny', reason);
|
|
71
|
+
else if (policy !== null) answer(policy.decision, policy.reason);
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
|
|
6
|
+
const agentsDir = () => process.env.METRO_AGENTS_DIR || join(homedir(), '.metro', 'agents');
|
|
7
|
+
|
|
8
|
+
function readAgent() {
|
|
9
|
+
const path = join(agentsDir(), 'agent.json');
|
|
10
|
+
let parsed;
|
|
11
|
+
try {
|
|
12
|
+
parsed = JSON.parse(readFileSync(path, 'utf8'));
|
|
13
|
+
} catch {
|
|
14
|
+
throw new Error(`no agent on this machine yet (${path})`);
|
|
15
|
+
}
|
|
16
|
+
if (typeof parsed?.key !== 'string') throw new Error(`no agent key in ${path}`);
|
|
17
|
+
return parsed;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (process.argv[2] !== 'headers') {
|
|
21
|
+
process.stderr.write('usage: metro-plugin.mjs headers\n');
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
process.stdout.write(`${JSON.stringify({ Authorization: `Bearer ${readAgent().key}` })}\n`);
|
|
27
|
+
} catch (err) {
|
|
28
|
+
process.stderr.write(`metro: ${err.message}\n`);
|
|
29
|
+
process.stdout.write('{}\n');
|
|
30
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
const METRO_TOOL = /^mcp__metro__(.+)$/;
|
|
6
|
+
const RANK = { allow: 0, ask: 1, deny: 2 };
|
|
7
|
+
const LINE_FIELD = { get_profile: 'from' };
|
|
8
|
+
const STATION_ARG = new Set(['create_group', 'set_profile']);
|
|
9
|
+
|
|
10
|
+
const isRecord = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
11
|
+
const text = (v) => (typeof v === 'string' ? v : '');
|
|
12
|
+
const agentsDir = () => process.env.METRO_AGENTS_DIR?.trim() || join(homedir(), '.metro', 'agents');
|
|
13
|
+
|
|
14
|
+
function readSnapshot() {
|
|
15
|
+
try {
|
|
16
|
+
const parsed = JSON.parse(readFileSync(join(agentsDir(), 'policy.json'), 'utf8'));
|
|
17
|
+
return isRecord(parsed) && parsed.version === 1 ? parsed : null;
|
|
18
|
+
} catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const fromLine = (line, override) => {
|
|
24
|
+
const parts = line.split('/');
|
|
25
|
+
return line.startsWith('metro://') && parts[2] && parts[3] ? [{ station: parts[2], account: override || parts[3] }] : [];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function stationOfAccount(snap, account) {
|
|
29
|
+
const hits = Object.entries(isRecord(snap.stations) ? snap.stations : {}).filter(
|
|
30
|
+
([, ids]) => Array.isArray(ids) && ids.includes(account),
|
|
31
|
+
);
|
|
32
|
+
return hits.length === 1 ? hits[0][0] : undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function targets(snap, name, input) {
|
|
36
|
+
if (Array.isArray(snap.ungated) && snap.ungated.includes(name)) return [];
|
|
37
|
+
const account = text(input.account);
|
|
38
|
+
const line = text(input[LINE_FIELD[name] ?? 'line']);
|
|
39
|
+
if (line) return fromLine(line, name in LINE_FIELD ? '' : account);
|
|
40
|
+
const owners = isRecord(snap.owners) ? snap.owners : {};
|
|
41
|
+
const station = (STATION_ARG.has(name) ? text(input.station) : text(owners[name])) || (account ? stationOfAccount(snap, account) : undefined);
|
|
42
|
+
if (!station) return [];
|
|
43
|
+
if (account) return [{ station, account }];
|
|
44
|
+
const ids = isRecord(snap.stations) && Array.isArray(snap.stations[station]) ? snap.stations[station] : [];
|
|
45
|
+
return ids.map((id) => ({ station, account: id }));
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function decide(policy, name, group) {
|
|
49
|
+
if (!isRecord(policy)) return 'allow';
|
|
50
|
+
const byTool = isRecord(policy.tools) ? policy.tools[name] : undefined;
|
|
51
|
+
const access = byTool ?? policy[group] ?? 'allow';
|
|
52
|
+
return access in RANK ? access : 'allow';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const normalized = (name) => name.replace(/[^a-zA-Z0-9_-]/g, '_');
|
|
56
|
+
|
|
57
|
+
function connectorCall(snap, toolName) {
|
|
58
|
+
for (const [server, entry] of Object.entries(isRecord(snap.connectors) ? snap.connectors : {})) {
|
|
59
|
+
if (!isRecord(entry)) continue;
|
|
60
|
+
const name = text(entry.name);
|
|
61
|
+
const prefixes = [`mcp__plugin_metro_${server}__`, `mcp__${server}__`, `mcp__${normalized(`metro.box ${name}`)}__`];
|
|
62
|
+
const prefix = prefixes.find((p) => toolName.startsWith(p) && toolName.length > p.length);
|
|
63
|
+
if (prefix === undefined) continue;
|
|
64
|
+
const called = toolName.slice(prefix.length);
|
|
65
|
+
const tools = isRecord(entry.tools) ? entry.tools : {};
|
|
66
|
+
const policy = isRecord(entry.policy) ? entry.policy : {};
|
|
67
|
+
const known = [...Object.keys(tools), ...Object.keys(isRecord(policy.tools) ? policy.tools : {})];
|
|
68
|
+
const tool = known.find((k) => k === called) ?? known.find((k) => normalized(k) === called) ?? called;
|
|
69
|
+
return { name: name || server, tool, group: tools[tool] === 'read' ? 'read' : 'write', policy };
|
|
70
|
+
}
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function connectorVerdict(snap, toolName) {
|
|
75
|
+
const call = connectorCall(snap, toolName);
|
|
76
|
+
if (call === null) return null;
|
|
77
|
+
const access = decide(call.policy, call.tool, call.group);
|
|
78
|
+
return access === 'allow' ? null : { access, tool: call.tool, owner: call.name, where: `${call.tool} on ${call.name}` };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function policyVerdict(toolName, input) {
|
|
82
|
+
const name = METRO_TOOL.exec(toolName)?.[1];
|
|
83
|
+
if (name === undefined && !toolName.startsWith('mcp__')) return null;
|
|
84
|
+
const snap = readSnapshot();
|
|
85
|
+
if (snap === null) return null;
|
|
86
|
+
if (name === undefined) return connectorVerdict(snap, toolName);
|
|
87
|
+
const tools = isRecord(snap.tools) ? snap.tools : {};
|
|
88
|
+
const group = tools[name] === 'read' ? 'read' : 'write';
|
|
89
|
+
const accounts = isRecord(snap.accounts) ? snap.accounts : {};
|
|
90
|
+
let verdict = null;
|
|
91
|
+
for (const target of targets(snap, name, input)) {
|
|
92
|
+
const access = decide(accounts[`${target.station}/${target.account}`], name, group);
|
|
93
|
+
if (access !== 'allow' && (verdict === null || RANK[access] > RANK[verdict.access]))
|
|
94
|
+
verdict = { access, tool: name, owner: target.station, where: `${name} on ${target.station} (${target.account})` };
|
|
95
|
+
}
|
|
96
|
+
return verdict;
|
|
97
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import { dirname, join } from 'node:path';
|
|
5
|
+
import { fileURLToPath } from 'node:url';
|
|
6
|
+
|
|
7
|
+
const given = (value) => (typeof value === 'string' && value.trim() !== '' ? value.trim() : undefined);
|
|
8
|
+
const claudeDir = given(process.env.CLAUDE_CONFIG_DIR) ?? join(given(process.env.HOME) ?? homedir(), '.claude');
|
|
9
|
+
const SKILL = join(claudeDir, 'skills', 'metro', 'SKILL.md');
|
|
10
|
+
const FALLBACK = join(dirname(dirname(fileURLToPath(import.meta.url))), 'METRO.md');
|
|
11
|
+
|
|
12
|
+
const body = (text) => text.replace(/^---[\s\S]*?\n---\n/, '').trim();
|
|
13
|
+
|
|
14
|
+
function guidance() {
|
|
15
|
+
for (const path of [SKILL, FALLBACK]) {
|
|
16
|
+
if (!existsSync(path)) continue;
|
|
17
|
+
try {
|
|
18
|
+
const text = body(readFileSync(path, 'utf8'));
|
|
19
|
+
if (text !== '') return text;
|
|
20
|
+
} catch {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
return '';
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const text = guidance();
|
|
28
|
+
if (text !== '')
|
|
29
|
+
process.stdout.write(
|
|
30
|
+
`${JSON.stringify({ hookSpecificOutput: { hookEventName: 'SessionStart', additionalContext: `This machine runs as a metro agent. Standing rules, kept in the metro skill:\n\n${text}` } })}\n`,
|
|
31
|
+
);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PreToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"hooks": [
|
|
6
|
+
{
|
|
7
|
+
"type": "command",
|
|
8
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/bin/guard.mjs\"",
|
|
9
|
+
"statusMessage": "Checking the orchestrator boundary"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"SessionStart": [
|
|
15
|
+
{
|
|
16
|
+
"hooks": [
|
|
17
|
+
{
|
|
18
|
+
"type": "command",
|
|
19
|
+
"command": "node \"${CLAUDE_PLUGIN_ROOT}/bin/session-start.mjs\""
|
|
20
|
+
}
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@metro-labs/core",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"private": true,
|
|
5
|
+
"description": "What every metro process shares: the logger, the event bus and its types, ids, secure file writes, the train protocol, station types and attachment handling. No HTTP, no policy.",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"exports": {
|
|
8
|
+
"./log": "./src/log.ts",
|
|
9
|
+
"./events": "./src/events.ts",
|
|
10
|
+
"./is-record": "./src/is-record.ts",
|
|
11
|
+
"./secure-fs": "./src/secure-fs.ts",
|
|
12
|
+
"./version": "./src/version.ts",
|
|
13
|
+
"./train-error": "./src/train-error.ts",
|
|
14
|
+
"./trains/protocol": "./src/protocol.ts",
|
|
15
|
+
"./ids": "./src/ids.ts",
|
|
16
|
+
"./tickets": "./src/tickets.ts",
|
|
17
|
+
"./station-names": "./src/station-names.ts",
|
|
18
|
+
"./str": "./src/str.ts",
|
|
19
|
+
"./lines": "./src/lines.ts",
|
|
20
|
+
"./endpoints": "./src/endpoints.ts",
|
|
21
|
+
"./stations/types": "./src/stations/types.ts",
|
|
22
|
+
"./stations/account-files": "./src/stations/account-files.ts",
|
|
23
|
+
"./stations/station-runtime": "./src/stations/station-runtime.ts",
|
|
24
|
+
"./stations/account-store": "./src/stations/account-store.ts",
|
|
25
|
+
"./stations/attachments": "./src/stations/attachments.ts",
|
|
26
|
+
"./stations/mail": "./src/stations/mail.ts",
|
|
27
|
+
"./stations/mail-text": "./src/stations/mail-text.ts",
|
|
28
|
+
"./stations/mail-trust": "./src/stations/mail-trust.ts",
|
|
29
|
+
"./stations/oauth": "./src/stations/oauth.ts",
|
|
30
|
+
"./stations/messaging-normalize": "./src/stations/messaging-normalize.ts",
|
|
31
|
+
"./stations/profile": "./src/stations/profile.ts",
|
|
32
|
+
"./stations/sender-profile": "./src/stations/sender-profile.ts",
|
|
33
|
+
"./stations/train-events": "./src/stations/train-events.ts",
|
|
34
|
+
"./stations/train-boot": "./src/stations/train-boot.ts"
|
|
35
|
+
},
|
|
36
|
+
"scripts": {
|
|
37
|
+
"build": "tsc",
|
|
38
|
+
"test": "tsc --noEmit && METRO_STATE_DIR=\"$(mktemp -d /tmp/metro-test.XXXXXX)\" bun test test/"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"pino": "^9.5.0",
|
|
42
|
+
"pino-pretty": "^13.1.3"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@stage-labs/config": "0.1.0-beta.2",
|
|
46
|
+
"eslint": "^10.3.0",
|
|
47
|
+
"knip": "^6.15.0",
|
|
48
|
+
"madge": "^8.0.0",
|
|
49
|
+
"typescript": "^5"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { Line } from './lines.js';
|
|
2
|
+
import { errMsg, log } from './log.js';
|
|
3
3
|
|
|
4
|
-
export
|
|
4
|
+
export const userSelf = (): Line =>
|
|
5
|
+
(process.env.METRO_FROM ?? 'metro://user') as Line;
|
|
5
6
|
|
|
6
7
|
export type StructuredEvent =
|
|
7
8
|
| { type: 'msg' }
|
|
@@ -24,6 +25,10 @@ export interface MetroEvent {
|
|
|
24
25
|
text?: string;
|
|
25
26
|
messageId?: string;
|
|
26
27
|
replyTo?: string;
|
|
28
|
+
isPrivate?: boolean;
|
|
29
|
+
mentionsSelf?: boolean;
|
|
30
|
+
replyToSelf?: boolean;
|
|
31
|
+
senderVerified?: boolean;
|
|
27
32
|
payload?: unknown;
|
|
28
33
|
display?: string;
|
|
29
34
|
event?: StructuredEvent;
|
|
@@ -71,8 +76,7 @@ export function formatDisplay(e: MetroEvent): string {
|
|
|
71
76
|
return `${headerFor('📩', [e.station, e.fromName ?? e.from, e.lineName])}\n> ${body}`;
|
|
72
77
|
}
|
|
73
78
|
|
|
74
|
-
export
|
|
75
|
-
`msg_${randomBytes(6).toString('base64url')}`;
|
|
79
|
+
export { mintId } from './ids.js';
|
|
76
80
|
|
|
77
81
|
export type BusListener = (event: MetroEvent, busSeq: number) => void;
|
|
78
82
|
|
|
@@ -81,7 +85,7 @@ export interface BufferedEvent {
|
|
|
81
85
|
event: MetroEvent;
|
|
82
86
|
}
|
|
83
87
|
|
|
84
|
-
|
|
88
|
+
const BUS_BUFFER_MAX = 500;
|
|
85
89
|
|
|
86
90
|
const listeners = new Set<BusListener>();
|
|
87
91
|
const buffer: BufferedEvent[] = [];
|
|
@@ -96,7 +100,7 @@ export function publishEvent(event: MetroEvent): void {
|
|
|
96
100
|
try {
|
|
97
101
|
fn(event, seq);
|
|
98
102
|
} catch (err) {
|
|
99
|
-
|
|
103
|
+
log.error({ busSeq: seq, err: errMsg(err) }, 'bus: a listener threw');
|
|
100
104
|
}
|
|
101
105
|
}
|
|
102
106
|
}
|
|
@@ -5,6 +5,9 @@ const REROLLS = 16;
|
|
|
5
5
|
|
|
6
6
|
export const ID_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{10}$/;
|
|
7
7
|
|
|
8
|
+
export const mintId = (): string =>
|
|
9
|
+
`msg_${randomBytes(6).toString('base64url')}`;
|
|
10
|
+
|
|
8
11
|
export function newId(): string {
|
|
9
12
|
for (let attempt = 0; attempt < REROLLS; attempt += 1) {
|
|
10
13
|
const id = randomBytes(ID_BYTES).toString('base64url');
|
|
@@ -17,3 +20,5 @@ export function parseId(raw: unknown): string | null {
|
|
|
17
20
|
if (typeof raw !== 'string') return null;
|
|
18
21
|
return ID_RE.test(raw) ? raw : null;
|
|
19
22
|
}
|
|
23
|
+
|
|
24
|
+
export const AGENT_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{1,31}$/;
|
|
@@ -5,50 +5,40 @@ const PREFIX = 'metro://';
|
|
|
5
5
|
const build = (station: string, ...seg: (string | number)[]): Line =>
|
|
6
6
|
asLine(`${PREFIX}${station}/${seg.map(String).join('/')}`);
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
function parseAccountScoped(
|
|
9
9
|
line: Line | string,
|
|
10
10
|
station: string,
|
|
11
11
|
validate?: (resource: string) => boolean,
|
|
12
12
|
): { accountId: string; resource: string } | null {
|
|
13
13
|
const p = Line.parse(line);
|
|
14
|
-
if (p?.station !== station) return null;
|
|
15
|
-
|
|
16
|
-
if (
|
|
17
|
-
accountId = p.path[0];
|
|
18
|
-
resource = p.path[1];
|
|
19
|
-
} else if (p.path.length === 1 && p.path[0] !== undefined) {
|
|
20
|
-
accountId = 'default';
|
|
21
|
-
resource = p.path[0];
|
|
22
|
-
} else return null;
|
|
14
|
+
if (p?.station !== station || p.path.length !== 2) return null;
|
|
15
|
+
const [accountId, resource] = p.path;
|
|
16
|
+
if (accountId === undefined || resource === undefined) return null;
|
|
23
17
|
if (validate && !validate(resource)) return null;
|
|
24
18
|
return { accountId, resource };
|
|
25
19
|
}
|
|
26
20
|
|
|
27
21
|
const isSnowflake = (s: string): boolean => /^\d+$/.test(s);
|
|
22
|
+
const isThreemaResource = (s: string): boolean =>
|
|
23
|
+
/^(?:[A-Z0-9]{8}|\*[A-Z0-9]{7})(?:-[0-9a-f]{16})?$/i.test(s);
|
|
24
|
+
const isMailResource = (s: string): boolean => /^[A-Za-z0-9=_%.@+-]+$/.test(s);
|
|
28
25
|
const isSignedInt = (s: string): boolean => /^-?\d+$/.test(s);
|
|
29
26
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return { accountId: 'default', rest: path };
|
|
35
|
-
}
|
|
27
|
+
const topicPart = (topicId: string | undefined): { topicId?: number } | null => {
|
|
28
|
+
if (topicId === undefined) return {};
|
|
29
|
+
return isSnowflake(topicId) ? { topicId: Number(topicId) } : null;
|
|
30
|
+
};
|
|
36
31
|
|
|
37
32
|
function parseTelegramLine(
|
|
38
33
|
line: Line | string,
|
|
39
34
|
): { accountId: string; chatId: number; topicId?: number } | null {
|
|
40
35
|
const p = Line.parse(line);
|
|
41
36
|
if (p?.station !== 'telegram-bot') return null;
|
|
42
|
-
const
|
|
43
|
-
|
|
44
|
-
if (
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
return {
|
|
48
|
-
accountId,
|
|
49
|
-
chatId: Number(chatId),
|
|
50
|
-
...(topicId !== undefined ? { topicId: Number(topicId) } : {}),
|
|
51
|
-
};
|
|
37
|
+
const [accountId, chatId, topicId, ...extra] = p.path;
|
|
38
|
+
if (accountId === undefined || chatId === undefined || extra.length > 0) return null;
|
|
39
|
+
if (!isSignedInt(chatId)) return null;
|
|
40
|
+
const topic = topicPart(topicId);
|
|
41
|
+
return topic === null ? null : { accountId, chatId: Number(chatId), ...topic };
|
|
52
42
|
}
|
|
53
43
|
|
|
54
44
|
export const Line = {
|
|
@@ -79,4 +69,10 @@ export const Line = {
|
|
|
79
69
|
parseAccountScoped(line, 'discord-bot', isSnowflake),
|
|
80
70
|
|
|
81
71
|
parseTelegram: (line: Line | string) => parseTelegramLine(line),
|
|
72
|
+
|
|
73
|
+
parseThreema: (line: Line | string) =>
|
|
74
|
+
parseAccountScoped(line, 'threema', isThreemaResource),
|
|
75
|
+
|
|
76
|
+
parseMail: (line: Line | string, station: 'outlook' | 'gmail') =>
|
|
77
|
+
parseAccountScoped(line, station, isMailResource),
|
|
82
78
|
};
|
|
@@ -3,7 +3,7 @@ import { join, parse as parsePath } from 'node:path';
|
|
|
3
3
|
import { errMsg, log } from './log.js';
|
|
4
4
|
import { coerceErrorInfo, type TrainErrorInfo } from './train-error.js';
|
|
5
5
|
import type { StructuredEvent } from './events.js';
|
|
6
|
-
import type { CallMsg } from '
|
|
6
|
+
import type { CallMsg } from './stations/station-runtime.js';
|
|
7
7
|
|
|
8
8
|
export {
|
|
9
9
|
TrainError,
|
|
@@ -11,7 +11,7 @@ export {
|
|
|
11
11
|
type TrainErrorInfo,
|
|
12
12
|
} from './train-error.js';
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
const CALL_TIMEOUT_MS = 60_000;
|
|
15
15
|
|
|
16
16
|
export interface Pending {
|
|
17
17
|
resolve: (r: TrainCallResponse) => void;
|
|
@@ -24,7 +24,7 @@ export interface CallTarget {
|
|
|
24
24
|
proc: ({ stdin?: unknown } & Record<string, unknown>) | null;
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
const STDOUT_LINE_MAX = 4 * 1024 * 1024;
|
|
28
28
|
|
|
29
29
|
export type TrainEvent = {
|
|
30
30
|
station?: string;
|
|
@@ -37,6 +37,9 @@ export type TrainEvent = {
|
|
|
37
37
|
message_id?: string;
|
|
38
38
|
reply_to?: string;
|
|
39
39
|
is_private?: boolean;
|
|
40
|
+
mentions_self?: boolean;
|
|
41
|
+
reply_to_self?: boolean;
|
|
42
|
+
sender_verified?: boolean;
|
|
40
43
|
text?: string;
|
|
41
44
|
emoji?: string;
|
|
42
45
|
payload?: unknown;
|
|
@@ -149,7 +152,7 @@ export function readCalls(
|
|
|
149
152
|
});
|
|
150
153
|
}
|
|
151
154
|
|
|
152
|
-
|
|
155
|
+
function encodeCall(id: string, action: string, args: unknown): string {
|
|
153
156
|
return JSON.stringify({ op: 'call', id, action, args }) + '\n';
|
|
154
157
|
}
|
|
155
158
|
|
|
@@ -38,6 +38,23 @@ export function writeSecure(path: string, data: string): void {
|
|
|
38
38
|
chmodIfExists(path, 0o600);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
+
function modeOf(path: string): number | null {
|
|
42
|
+
try {
|
|
43
|
+
return statSync(path).mode & 0o777;
|
|
44
|
+
} catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function writeAtomic(path: string, data: string, mode?: number): void {
|
|
50
|
+
const chosen = mode ?? modeOf(path) ?? 0o644;
|
|
51
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
52
|
+
const tmp = `${path}.metro-${String(process.pid)}.tmp`;
|
|
53
|
+
writeFileSync(tmp, data, { mode: chosen });
|
|
54
|
+
chmodSync(tmp, chosen);
|
|
55
|
+
renameSync(tmp, path);
|
|
56
|
+
}
|
|
57
|
+
|
|
41
58
|
export function readJson<T>(
|
|
42
59
|
path: string,
|
|
43
60
|
fallback: T,
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { readdirSync, rmSync } from 'node:fs';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { log } from '../log.js';
|
|
5
|
+
|
|
6
|
+
export interface AccountFiles {
|
|
7
|
+
path: (accountId: string) => string;
|
|
8
|
+
forget: (accountId: string) => void;
|
|
9
|
+
forgetExcept: (keptAccountIds: readonly string[]) => void;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const safeSegment = (s: string): string => s.replace(/[^A-Za-z0-9_-]/g, '_');
|
|
13
|
+
|
|
14
|
+
function listed(dir: string): string[] {
|
|
15
|
+
try {
|
|
16
|
+
return readdirSync(dir);
|
|
17
|
+
} catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function accountFiles(dirEnv: string, prefix: string): AccountFiles {
|
|
23
|
+
const dir = (): string => process.env[dirEnv] ?? join(homedir(), '.metro');
|
|
24
|
+
const name = (accountId: string): string => `${prefix}${safeSegment(accountId)}.json`;
|
|
25
|
+
const remove = (file: string, reason: string): void => {
|
|
26
|
+
rmSync(join(dir(), file), { force: true });
|
|
27
|
+
log.info({ file: join(dir(), file) }, reason);
|
|
28
|
+
};
|
|
29
|
+
return {
|
|
30
|
+
path: (accountId) => join(dir(), name(accountId)),
|
|
31
|
+
forget: (accountId) => {
|
|
32
|
+
remove(name(accountId), 'station: removed the files of a detached account');
|
|
33
|
+
},
|
|
34
|
+
forgetExcept: (keptAccountIds) => {
|
|
35
|
+
const kept = new Set(keptAccountIds.map(name));
|
|
36
|
+
for (const file of listed(dir()))
|
|
37
|
+
if (file.startsWith(prefix) && file.endsWith('.json') && !kept.has(file))
|
|
38
|
+
remove(file, 'station: removed the files of an account that is no longer attached');
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|