@stage-labs/metro 0.1.0-beta.21 → 0.1.0-beta.211
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/claude-settings.js +40 -0
- package/dist/claude.js +152 -0
- package/dist/cli.js +52 -109
- package/dist/control.js +47 -75
- package/dist/hold.js +210 -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 +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/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 +26 -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/{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 +24 -28
- 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 +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 +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/messaging-normalize.ts +16 -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 +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 +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 +51 -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 +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +89 -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/{mcp/src/db/agent-map.ts → daemon/src/agents/map.ts} +58 -1
- 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 +218 -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 +70 -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 +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 +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 +313 -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 +273 -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/{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/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 +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 +195 -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 +128 -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 +224 -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 +278 -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 +14 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +244 -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 +24 -79
- 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 +79 -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 +48 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/session.ts +33 -42
- 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/{mcp → daemon}/src/mcp/tool-dispatch.ts +53 -40
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/mcp/tool-schemas.ts +68 -119
- 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 +59 -51
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +205 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +66 -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/{mcp → daemon}/src/stations/attach-inline.ts +29 -1
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/attach-interactive.ts +9 -36
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-outlook.ts +63 -0
- 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 +75 -9
- package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +265 -0
- package/runtime/node_modules/@metro-labs/{mcp → daemon}/src/stations/registry.ts +22 -6
- 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 → discord-bot}/package.json +3 -5
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/accounts.ts +8 -28
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/actions.ts +19 -187
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/attachments.ts +3 -3
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/format.ts +31 -72
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group-actions.ts +2 -2
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/group.ts +2 -2
- package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/index.ts +15 -21
- package/runtime/node_modules/@metro-labs/{discord → 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 → discord-bot}/src/send-files.ts +12 -13
- package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +23 -0
- package/runtime/node_modules/@metro-labs/http/package.json +29 -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/workos-token.ts +149 -0
- package/runtime/node_modules/@metro-labs/{telegram-user → outlook}/package.json +2 -5
- package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +93 -0
- package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +14 -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 +140 -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 +3 -2
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +38 -96
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +214 -253
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +67 -108
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/client.ts +6 -6
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +101 -147
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/group.ts +4 -4
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/history.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +57 -0
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +6 -102
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/login.ts +20 -20
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +64 -151
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +61 -33
- package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +70 -0
- package/runtime/node_modules/@metro-labs/{telegram-user → telegram}/src/reactions.ts +3 -3
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +11 -1
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +5 -47
- package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +86 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +240 -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 +170 -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 +90 -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 +85 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +21 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +56 -0
- package/runtime/node_modules/@metro-labs/threema/package.json +26 -0
- package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +95 -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 +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 +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 +4 -43
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +41 -219
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +3 -3
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +3 -3
- 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 +1 -19
- 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/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 +5 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +4 -51
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +30 -44
- 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 +37 -0
- package/dist/prompt.js +0 -10
- package/dist/store.js +0 -70
- package/runtime/node_modules/@metro-labs/discord/src/station.ts +0 -19
- package/runtime/node_modules/@metro-labs/discord/src/voice-speak.ts +0 -195
- package/runtime/node_modules/@metro-labs/discord/src/voice-transcribe.ts +0 -292
- package/runtime/node_modules/@metro-labs/discord/src/voice.ts +0 -197
- package/runtime/node_modules/@metro-labs/discord/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 -335
- 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 -322
- 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/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/materialize.ts +0 -364
- 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/wire.ts +0 -3
- package/runtime/node_modules/@metro-labs/telegram-user/src/accounts.ts +0 -81
- package/runtime/node_modules/@metro-labs/telegram-user/src/actions.ts +0 -254
- package/runtime/node_modules/@metro-labs/telegram-user/src/attachments.ts +0 -87
- package/runtime/node_modules/@metro-labs/telegram-user/src/format.ts +0 -141
- package/runtime/node_modules/@metro-labs/telegram-user/src/inbound.ts +0 -78
- package/runtime/node_modules/@metro-labs/telegram-user/src/index.ts +0 -46
- package/runtime/node_modules/@metro-labs/telegram-user/src/media-actions.ts +0 -80
- package/runtime/node_modules/@metro-labs/telegram-user/src/members.ts +0 -75
- package/runtime/node_modules/@metro-labs/telegram-user/src/normalize.ts +0 -22
- package/runtime/node_modules/@metro-labs/telegram-user/src/station.ts +0 -24
- package/runtime/node_modules/@metro-labs/telegram-user/src/types.ts +0 -7
- package/runtime/node_modules/@metro-labs/telegram-user/src/wire.ts +0 -1
- 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/{discord → discord-bot}/src/api-base.ts +0 -0
- /package/runtime/node_modules/@metro-labs/{discord → discord-bot}/src/verify.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
- /package/runtime/node_modules/@metro-labs/{telegram → telegram-bot}/src/verify.ts +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
3
|
+
import { tokenMatches } from '../stations/webhook-endpoints.js';
|
|
4
|
+
import {
|
|
5
|
+
findThreemaCallback,
|
|
6
|
+
type ThreemaCallback,
|
|
7
|
+
} from '../stations/threema-callbacks.js';
|
|
8
|
+
import { forwardTrainCall, type TrainCallBackend } from '../stations/train-call.js';
|
|
9
|
+
import { BodyTooLargeError, readBody } from './body.js';
|
|
10
|
+
|
|
11
|
+
const PREFIX = '/api/threema/';
|
|
12
|
+
const CALLBACK_PATH = /^\/api\/threema\/([0-9]{17,20})\/([A-Za-z0-9_-]{32,128})$/;
|
|
13
|
+
const BODY_MAX = 64 * 1024;
|
|
14
|
+
const REQUIRED = ['from', 'to', 'messageId', 'date', 'nonce', 'box', 'mac'] as const;
|
|
15
|
+
|
|
16
|
+
function callbackTarget(path: string): ThreemaCallback | null {
|
|
17
|
+
const m = CALLBACK_PATH.exec(path);
|
|
18
|
+
const callbackId = m?.[1];
|
|
19
|
+
const token = m?.[2];
|
|
20
|
+
if (callbackId === undefined || token === undefined) return null;
|
|
21
|
+
const row = findThreemaCallback(callbackId);
|
|
22
|
+
return row !== undefined && tokenMatches(row.callbackToken, token) ? row : null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function parseFields(raw: Buffer): Record<string, string> | null {
|
|
26
|
+
const params = new URLSearchParams(raw.toString('utf8'));
|
|
27
|
+
const out: Record<string, string> = {};
|
|
28
|
+
for (const key of REQUIRED) {
|
|
29
|
+
const value = params.get(key)?.trim() ?? '';
|
|
30
|
+
if (value === '') return null;
|
|
31
|
+
out[key] = value;
|
|
32
|
+
}
|
|
33
|
+
const nickname = params.get('nickname')?.trim() ?? '';
|
|
34
|
+
if (nickname !== '') out.nickname = nickname;
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function deliver(
|
|
39
|
+
res: ServerResponse,
|
|
40
|
+
target: ThreemaCallback,
|
|
41
|
+
fields: Record<string, string>,
|
|
42
|
+
forward: TrainCallBackend,
|
|
43
|
+
): Promise<void> {
|
|
44
|
+
let response;
|
|
45
|
+
try {
|
|
46
|
+
response = await forward('threema', 'callback', { account: target.id, ...fields });
|
|
47
|
+
} catch (err) {
|
|
48
|
+
log.warn(
|
|
49
|
+
{ account: target.id, err: errMsg(err) },
|
|
50
|
+
'threema: the callback could not reach the train, so Threema is asked to retry',
|
|
51
|
+
);
|
|
52
|
+
res.writeHead(503).end('threema train unavailable');
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (response.error !== undefined) {
|
|
56
|
+
log.warn({ account: target.id, err: response.error }, 'threema: callback refused');
|
|
57
|
+
res.writeHead(400).end('refused');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
res.writeHead(200).end('ok');
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function readFields(
|
|
64
|
+
req: IncomingMessage,
|
|
65
|
+
res: ServerResponse,
|
|
66
|
+
): Promise<Record<string, string> | null> {
|
|
67
|
+
let raw: Buffer;
|
|
68
|
+
try {
|
|
69
|
+
raw = await readBody(req, BODY_MAX);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
if (!(err instanceof BodyTooLargeError)) throw err;
|
|
72
|
+
res.writeHead(413).end('payload too large');
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
const fields = parseFields(raw);
|
|
76
|
+
if (fields === null) res.writeHead(400).end('missing callback fields');
|
|
77
|
+
return fields;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function handleThreemaCallback(
|
|
81
|
+
req: IncomingMessage,
|
|
82
|
+
res: ServerResponse,
|
|
83
|
+
forward: TrainCallBackend = forwardTrainCall,
|
|
84
|
+
): Promise<boolean> {
|
|
85
|
+
const path = (req.url ?? '').split('?')[0] ?? '';
|
|
86
|
+
if (!path.startsWith(PREFIX)) return false;
|
|
87
|
+
const target = callbackTarget(path);
|
|
88
|
+
if (target === null) {
|
|
89
|
+
res.writeHead(404).end();
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (req.method === 'GET') {
|
|
93
|
+
res.writeHead(200).end(`metro threema callback ${target.callbackId} ready\n`);
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (req.method !== 'POST') {
|
|
97
|
+
res.writeHead(405).end();
|
|
98
|
+
return true;
|
|
99
|
+
}
|
|
100
|
+
const fields = await readFields(req, res);
|
|
101
|
+
if (fields !== null) await deliver(res, target, fields, forward);
|
|
102
|
+
return true;
|
|
103
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
3
|
+
import { sessionRoute } from '@metro-labs/http/api-http';
|
|
4
|
+
import { log } from '@metro-labs/core/log';
|
|
5
|
+
|
|
6
|
+
const STOP = '/api/stop';
|
|
7
|
+
const RESTART = '/api/restart';
|
|
8
|
+
const EXIT_DELAY_MS = 500;
|
|
9
|
+
|
|
10
|
+
export interface ControlApiDeps {
|
|
11
|
+
restart: () => void;
|
|
12
|
+
stop: () => void;
|
|
13
|
+
served?: () => boolean;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Action {
|
|
17
|
+
run: () => void;
|
|
18
|
+
body: Record<string, boolean>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const servedByCli = (): boolean => (process.env.METRO_CLI_BIN?.trim() ?? '') !== '';
|
|
22
|
+
|
|
23
|
+
function actionFor(path: string, deps: ControlApiDeps): Action {
|
|
24
|
+
return path === STOP ? { run: deps.stop, body: { stopping: true } } : { run: deps.restart, body: { restarting: true } };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function handleControlRequest(req: IncomingMessage, res: ServerResponse, deps: ControlApiDeps): boolean {
|
|
28
|
+
return sessionRoute(req, res, { methods: { [STOP]: ['POST'], [RESTART]: ['POST'] }, admin: true, label: 'control-api' }, async (session, path) => {
|
|
29
|
+
const action = actionFor(path, deps);
|
|
30
|
+
if (!(deps.served ?? servedByCli)())
|
|
31
|
+
throw new ApiError(
|
|
32
|
+
'this daemon was not started by metro serve, so nothing on the machine would bring it back: use the shell instead',
|
|
33
|
+
400,
|
|
34
|
+
);
|
|
35
|
+
log.info({ path, subject: session.subject }, 'control-api: requested from the page');
|
|
36
|
+
setTimeout(action.run, EXIT_DELAY_MS).unref();
|
|
37
|
+
return Promise.resolve(action.body);
|
|
38
|
+
});
|
|
39
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { hostname, homedir } from 'node:os';
|
|
3
|
+
import { statfs } from 'node:fs/promises';
|
|
4
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
5
|
+
import { sessionRoute } from '@metro-labs/http/api-http';
|
|
6
|
+
import { publicBaseUrl, webhookPort } from '../files/attach-serve.js';
|
|
7
|
+
import { claudeDir } from '../claude/files.js';
|
|
8
|
+
import { METRO_VERSION } from '@metro-labs/core/version';
|
|
9
|
+
import { agentsDir } from '../agents/files.js';
|
|
10
|
+
import { localOwner } from '../agents/file-admin.js';
|
|
11
|
+
|
|
12
|
+
const PATH = '/api/server';
|
|
13
|
+
|
|
14
|
+
export interface MachineApiDeps {
|
|
15
|
+
startedAt?: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const bootedAt = new Date(Date.now() - process.uptime() * 1000).toISOString();
|
|
19
|
+
|
|
20
|
+
interface Disk {
|
|
21
|
+
path: string;
|
|
22
|
+
totalBytes: number;
|
|
23
|
+
freeBytes: number;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async function diskInfo(path = homedir()): Promise<Disk | null> {
|
|
27
|
+
try {
|
|
28
|
+
const s = await statfs(path);
|
|
29
|
+
return { path, totalBytes: s.bsize * s.blocks, freeBytes: s.bsize * s.bavail };
|
|
30
|
+
} catch (err) {
|
|
31
|
+
log.warn({ err: errMsg(err), path }, 'machine: could not read the disk');
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export async function machineInfo(startedAt = bootedAt): Promise<Record<string, unknown>> {
|
|
37
|
+
const store = process.env.METRO_RUNTIME_STORE?.trim() ?? '';
|
|
38
|
+
return {
|
|
39
|
+
disk: await diskInfo(),
|
|
40
|
+
version: METRO_VERSION,
|
|
41
|
+
owner: localOwner(),
|
|
42
|
+
hostname: hostname(),
|
|
43
|
+
platform: process.platform,
|
|
44
|
+
arch: process.arch,
|
|
45
|
+
port: webhookPort(),
|
|
46
|
+
publicUrl: publicBaseUrl(),
|
|
47
|
+
uptimeSeconds: Math.round(process.uptime()),
|
|
48
|
+
startedAt,
|
|
49
|
+
bun: process.versions.bun ?? null,
|
|
50
|
+
agentsDir: agentsDir(),
|
|
51
|
+
claudeDir: claudeDir(),
|
|
52
|
+
runtimeStore: store === '' ? null : store,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function handleMachineRequest(req: IncomingMessage, res: ServerResponse, deps: MachineApiDeps): boolean {
|
|
57
|
+
return sessionRoute(req, res, { methods: { [PATH]: ['GET'] }, admin: false, label: 'machine-api' }, () => machineInfo(deps.startedAt));
|
|
58
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
3
|
+
import { readJsonBody, sessionRoute, type ApiSession } from '@metro-labs/http/api-http';
|
|
4
|
+
import { isOrganizationId } from '@metro-labs/http/workos-token';
|
|
5
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
6
|
+
import { log } from '@metro-labs/core/log';
|
|
7
|
+
|
|
8
|
+
const PATH = '/api/owner';
|
|
9
|
+
|
|
10
|
+
export interface OwnerApiDeps {
|
|
11
|
+
setOwner: (owner: string) => string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
async function move(req: IncomingMessage, session: ApiSession, deps: OwnerApiDeps): Promise<{ owner: string }> {
|
|
15
|
+
const body = await readJsonBody(req);
|
|
16
|
+
const owner = isRecord(body) && typeof body.owner === 'string' ? body.owner.trim() : '';
|
|
17
|
+
if (!isOrganizationId(owner)) throw new ApiError('owner must be an organization id', 400);
|
|
18
|
+
if (owner === session.subject) throw new ApiError('this machine already belongs to that organization', 409);
|
|
19
|
+
const saved = deps.setOwner(owner);
|
|
20
|
+
log.info({ from: session.subject, to: saved }, 'owner: this machine moved to another organization');
|
|
21
|
+
return { owner: saved };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function handleOwnerRequest(req: IncomingMessage, res: ServerResponse, deps: OwnerApiDeps): boolean {
|
|
25
|
+
return sessionRoute(req, res, { methods: { [PATH]: ['POST'] }, admin: true, label: 'owner-api' }, (session) => move(req, session, deps));
|
|
26
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
import { log } from '@metro-labs/core/log';
|
|
4
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
5
|
+
import { sessionRoute } from '@metro-labs/http/api-http';
|
|
6
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
7
|
+
import { METRO_VERSION } from '@metro-labs/core/version';
|
|
8
|
+
|
|
9
|
+
const PATH = '/api/update';
|
|
10
|
+
const CHECK_TTL_MS = 10 * 60_000;
|
|
11
|
+
const CHECK_TIMEOUT_MS = 30_000;
|
|
12
|
+
const UPDATE_TIMEOUT_MS = 5 * 60_000;
|
|
13
|
+
const RESTART_DELAY_MS = 500;
|
|
14
|
+
const OUTPUT_TAIL = 800;
|
|
15
|
+
|
|
16
|
+
interface UpdateCheck {
|
|
17
|
+
current: string;
|
|
18
|
+
latest: string;
|
|
19
|
+
newer: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UpdateResult {
|
|
23
|
+
updated: boolean;
|
|
24
|
+
version: string;
|
|
25
|
+
restarting: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface UpdateApiDeps {
|
|
29
|
+
restart: () => void;
|
|
30
|
+
cliBin?: () => string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const cliBinFromEnv = (): string => process.env.METRO_CLI_BIN?.trim() ?? '';
|
|
34
|
+
|
|
35
|
+
function runCli(bin: string, args: string[], timeout: number): Promise<{ stdout: string; stderr: string }> {
|
|
36
|
+
return new Promise((resolve, reject) => {
|
|
37
|
+
execFile(process.execPath, [bin, ...args], { timeout, maxBuffer: 4 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
38
|
+
if (err === null) resolve({ stdout, stderr });
|
|
39
|
+
else reject(new ApiError(`metro update failed: ${(stderr || err.message).trim().slice(-OUTPUT_TAIL)}`, 502));
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
let cached: { at: number; check: UpdateCheck } | null = null;
|
|
45
|
+
|
|
46
|
+
function parseCheck(raw: string): UpdateCheck {
|
|
47
|
+
let body: unknown;
|
|
48
|
+
try {
|
|
49
|
+
body = JSON.parse(raw.trim().split('\n').at(-1) ?? '');
|
|
50
|
+
} catch {
|
|
51
|
+
throw new ApiError('metro update --check returned no report', 502);
|
|
52
|
+
}
|
|
53
|
+
if (!isRecord(body) || typeof body.current !== 'string' || typeof body.latest !== 'string')
|
|
54
|
+
throw new ApiError('metro update --check returned an unexpected report', 502);
|
|
55
|
+
return { current: body.current, latest: body.latest, newer: body.newer === true };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function check(bin: string, now = Date.now()): Promise<UpdateCheck> {
|
|
59
|
+
if (cached !== null && now - cached.at < CHECK_TTL_MS) return cached.check;
|
|
60
|
+
const out = await runCli(bin, ['update', '--check'], CHECK_TIMEOUT_MS);
|
|
61
|
+
cached = { at: now, check: parseCheck(out.stdout) };
|
|
62
|
+
return cached.check;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function apply(bin: string, deps: UpdateApiDeps): Promise<UpdateResult> {
|
|
66
|
+
const before = await check(bin);
|
|
67
|
+
if (!before.newer) return { updated: false, version: before.current, restarting: false };
|
|
68
|
+
const out = await runCli(bin, ['update'], UPDATE_TIMEOUT_MS);
|
|
69
|
+
cached = null;
|
|
70
|
+
log.info({ from: before.current, to: before.latest, output: out.stderr.trim().slice(-OUTPUT_TAIL) }, 'update-api: metro updated, restarting');
|
|
71
|
+
setTimeout(() => {
|
|
72
|
+
deps.restart();
|
|
73
|
+
}, RESTART_DELAY_MS).unref();
|
|
74
|
+
return { updated: true, version: before.latest, restarting: true };
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function binOrThrow(deps: UpdateApiDeps): string {
|
|
78
|
+
const bin = (deps.cliBin ?? cliBinFromEnv)();
|
|
79
|
+
if (bin === '')
|
|
80
|
+
throw new ApiError('this daemon was not started by metro serve, so it cannot update itself: run metro update on the machine', 400);
|
|
81
|
+
return bin;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export function handleUpdateRequest(req: IncomingMessage, res: ServerResponse, deps: UpdateApiDeps): boolean {
|
|
85
|
+
return sessionRoute(req, res, { methods: { [PATH]: ['GET', 'POST'] }, admin: ['POST'], label: 'update-api' }, async () => {
|
|
86
|
+
const bin = binOrThrow(deps);
|
|
87
|
+
if (req.method === 'GET') return { ...(await check(bin)), running: METRO_VERSION };
|
|
88
|
+
return apply(bin, deps);
|
|
89
|
+
});
|
|
90
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export type AttachStep = 'code' | 'password' | 'scan' | 'pair' | 'device' | 'browser';
|
|
2
|
+
|
|
3
|
+
export interface AttachPrompt {
|
|
4
|
+
step: AttachStep;
|
|
5
|
+
prompt: string;
|
|
6
|
+
qr?: string;
|
|
7
|
+
pairingCode?: string;
|
|
8
|
+
userCode?: string;
|
|
9
|
+
verificationUri?: string;
|
|
10
|
+
authorizeUrl?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface AttachOutcome {
|
|
14
|
+
config: Record<string, unknown>;
|
|
15
|
+
identity: Record<string, string>;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface DriverHooks {
|
|
19
|
+
prompt: (p: AttachPrompt) => void;
|
|
20
|
+
done: (o: AttachOutcome) => void;
|
|
21
|
+
fail: (message: string) => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface StepInput {
|
|
25
|
+
code?: unknown;
|
|
26
|
+
password?: unknown;
|
|
27
|
+
state?: unknown;
|
|
28
|
+
mode?: unknown;
|
|
29
|
+
error?: unknown;
|
|
30
|
+
errorDescription?: unknown;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface AttachDriver {
|
|
34
|
+
submit: (input: StepInput) => Promise<void>;
|
|
35
|
+
cancel: () => Promise<void>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface StartedAttach {
|
|
39
|
+
driver: AttachDriver;
|
|
40
|
+
prompt: AttachPrompt;
|
|
41
|
+
expiresAt?: number;
|
|
42
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
1
|
+
import { mkdtemp, open, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { assertAttachmentSize } from '@metro-labs/core/stations/attachments';
|
|
2
3
|
import { tmpdir } from 'node:os';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
|
|
@@ -98,3 +99,30 @@ export async function writeInlineTemp(
|
|
|
98
99
|
|
|
99
100
|
export const removeInlineTemp = (dir: string): Promise<void> =>
|
|
100
101
|
rm(dir, { recursive: true, force: true });
|
|
102
|
+
|
|
103
|
+
export async function streamToTemp(
|
|
104
|
+
body: ReadableStream<Uint8Array>,
|
|
105
|
+
name: string | undefined,
|
|
106
|
+
): Promise<InlineTemp & { bytes: number }> {
|
|
107
|
+
const dir = await mkdtemp(join(tmpdir(), INLINE_TEMP_PREFIX));
|
|
108
|
+
const path = join(dir, safeFileName(name));
|
|
109
|
+
const handle = await open(path, 'wx', 0o600);
|
|
110
|
+
const reader = body.getReader();
|
|
111
|
+
let bytes = 0;
|
|
112
|
+
try {
|
|
113
|
+
for (;;) {
|
|
114
|
+
const { done, value } = await reader.read();
|
|
115
|
+
if (done) break;
|
|
116
|
+
bytes += value.length;
|
|
117
|
+
assertAttachmentSize(bytes);
|
|
118
|
+
await handle.write(value);
|
|
119
|
+
}
|
|
120
|
+
} catch (err) {
|
|
121
|
+
await reader.cancel().catch(() => undefined);
|
|
122
|
+
await handle.close();
|
|
123
|
+
await removeInlineTemp(dir);
|
|
124
|
+
throw err;
|
|
125
|
+
}
|
|
126
|
+
await handle.close();
|
|
127
|
+
return { dir, path, bytes };
|
|
128
|
+
}
|
|
@@ -1,39 +1,11 @@
|
|
|
1
1
|
import { StationAttachError } from './attach.js';
|
|
2
|
+
import type { DriverHooks, StartedAttach } from './attach-driver.js';
|
|
3
|
+
import { startOutlook } from './attach-outlook.js';
|
|
2
4
|
|
|
3
|
-
export const INTERACTIVE_STATIONS = ['telegram
|
|
5
|
+
export const INTERACTIVE_STATIONS = ['telegram', 'whatsapp', 'outlook'] as const;
|
|
4
6
|
|
|
5
7
|
export type InteractiveStation = (typeof INTERACTIVE_STATIONS)[number];
|
|
6
8
|
|
|
7
|
-
export type AttachStep = 'code' | 'password' | 'scan' | 'pair';
|
|
8
|
-
|
|
9
|
-
export interface AttachPrompt {
|
|
10
|
-
step: AttachStep;
|
|
11
|
-
prompt: string;
|
|
12
|
-
qr?: string;
|
|
13
|
-
pairingCode?: string;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface AttachOutcome {
|
|
17
|
-
config: Record<string, unknown>;
|
|
18
|
-
identity: Record<string, string>;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface DriverHooks {
|
|
22
|
-
prompt: (p: AttachPrompt) => void;
|
|
23
|
-
done: (o: AttachOutcome) => void;
|
|
24
|
-
fail: (message: string) => void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface AttachDriver {
|
|
28
|
-
submit: (input: { code?: unknown; password?: unknown }) => Promise<void>;
|
|
29
|
-
cancel: () => Promise<void>;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface StartedAttach {
|
|
33
|
-
driver: AttachDriver;
|
|
34
|
-
prompt: AttachPrompt;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
9
|
const CODE_PROMPT =
|
|
38
10
|
'Telegram sent a login code to that number. Enter it here.';
|
|
39
11
|
const PASSWORD_PROMPT =
|
|
@@ -61,12 +33,12 @@ async function startTelegramUser(
|
|
|
61
33
|
input: Record<string, unknown>,
|
|
62
34
|
hooks: DriverHooks,
|
|
63
35
|
): Promise<StartedAttach> {
|
|
64
|
-
const {
|
|
65
|
-
await import('@metro-labs/telegram
|
|
66
|
-
const isOwn = (e: unknown): boolean => e instanceof
|
|
36
|
+
const { TelegramLogin, TelegramLoginError, validateCredentials } =
|
|
37
|
+
await import('@metro-labs/telegram/login');
|
|
38
|
+
const isOwn = (e: unknown): boolean => e instanceof TelegramLoginError;
|
|
67
39
|
let login;
|
|
68
40
|
try {
|
|
69
|
-
login = new
|
|
41
|
+
login = new TelegramLogin(
|
|
70
42
|
validateCredentials({
|
|
71
43
|
apiId: input.apiId,
|
|
72
44
|
apiHash: input.apiHash,
|
|
@@ -155,7 +127,8 @@ export async function startInteractiveAttach(
|
|
|
155
127
|
input: Record<string, unknown>,
|
|
156
128
|
hooks: DriverHooks,
|
|
157
129
|
): Promise<StartedAttach> {
|
|
158
|
-
|
|
130
|
+
if (station === 'outlook') return startOutlook(input, hooks);
|
|
131
|
+
return station === 'telegram'
|
|
159
132
|
? startTelegramUser(input, hooks)
|
|
160
133
|
: startWhatsapp(input, hooks);
|
|
161
134
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { OutlookLogin } from '@metro-labs/outlook/login';
|
|
2
|
+
import { StationAttachError } from './attach.js';
|
|
3
|
+
import type { DriverHooks, StartedAttach, StepInput } from './attach-driver.js';
|
|
4
|
+
|
|
5
|
+
const BROWSER_PROMPT =
|
|
6
|
+
'Sign in with Microsoft in a new tab, with the mailbox this agent should read. This page updates on its own once you are done.';
|
|
7
|
+
const DEVICE_PROMPT =
|
|
8
|
+
'Open the Microsoft sign-in page, type this code, and sign in with the mailbox this agent should read.';
|
|
9
|
+
const OTHER_ATTEMPT = 'This sign-in link belongs to another attempt. Start again from the Channels page.';
|
|
10
|
+
const BROWSER_TTL_MS = 15 * 60_000;
|
|
11
|
+
|
|
12
|
+
const text = (v: unknown): string => (typeof v === 'string' ? v.trim() : '');
|
|
13
|
+
|
|
14
|
+
export async function startOutlook(input: Record<string, unknown>, hooks: DriverHooks): Promise<StartedAttach> {
|
|
15
|
+
const { OutlookBrowserLogin, OutlookLogin: DeviceLogin, OutlookLoginError, failureOf, parseMailbox } = await import('@metro-labs/outlook/login');
|
|
16
|
+
const said = (err: unknown, fallback: string): string =>
|
|
17
|
+
err instanceof OutlookLoginError && err.message !== '' ? err.message : fallback;
|
|
18
|
+
const { browser, mailbox } = (() => {
|
|
19
|
+
try {
|
|
20
|
+
const wanted = parseMailbox(input.mailbox);
|
|
21
|
+
return { browser: new OutlookBrowserLogin({ mailbox: wanted }), mailbox: wanted };
|
|
22
|
+
} catch (err) {
|
|
23
|
+
throw new StationAttachError(said(err, 'Microsoft refused to start the sign-in'), 400);
|
|
24
|
+
}
|
|
25
|
+
})();
|
|
26
|
+
let device: OutlookLogin | null = null;
|
|
27
|
+
|
|
28
|
+
const toDevice = async (): Promise<void> => {
|
|
29
|
+
if (device !== null) return;
|
|
30
|
+
const login = new DeviceLogin({ onDone: hooks.done, onFailed: hooks.fail }, { mailbox });
|
|
31
|
+
const code = await login.start().catch((err: unknown) => {
|
|
32
|
+
throw new StationAttachError(said(err, 'Microsoft refused to start the sign-in'), 400);
|
|
33
|
+
});
|
|
34
|
+
device = login;
|
|
35
|
+
hooks.prompt({ step: 'device', prompt: DEVICE_PROMPT, userCode: code.userCode, verificationUri: code.verificationUri });
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const finish = async (input: StepInput): Promise<void> => {
|
|
39
|
+
const state = text(input.state);
|
|
40
|
+
if (state !== browser.state) throw new StationAttachError(OTHER_ATTEMPT, 400);
|
|
41
|
+
const error = text(input.error);
|
|
42
|
+
if (error !== '') {
|
|
43
|
+
hooks.fail(failureOf({ error, error_description: text(input.errorDescription) }));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const code = text(input.code);
|
|
47
|
+
if (code === '') throw new StationAttachError('Microsoft did not send a sign-in code back', 400);
|
|
48
|
+
try {
|
|
49
|
+
hooks.done(await browser.finish(code, state));
|
|
50
|
+
} catch (err) {
|
|
51
|
+
hooks.fail(said(err, 'Metro could not finish the Microsoft sign-in.'));
|
|
52
|
+
}
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
prompt: { step: 'browser', prompt: BROWSER_PROMPT, authorizeUrl: browser.authorizeUrl },
|
|
57
|
+
expiresAt: Date.now() + BROWSER_TTL_MS,
|
|
58
|
+
driver: {
|
|
59
|
+
cancel: () => device?.cancel() ?? Promise.resolve(),
|
|
60
|
+
submit: (input) => (input.mode === 'device' ? toDevice() : finish(input)),
|
|
61
|
+
},
|
|
62
|
+
};
|
|
63
|
+
}
|
|
@@ -2,18 +2,20 @@ import {
|
|
|
2
2
|
assertAttachmentSize,
|
|
3
3
|
assertContentLength,
|
|
4
4
|
guessMime,
|
|
5
|
-
|
|
6
|
-
} from './attachments.js';
|
|
5
|
+
} from '@metro-labs/core/stations/attachments';
|
|
7
6
|
import {
|
|
8
7
|
assertInlineTotal,
|
|
9
8
|
decodeInline,
|
|
10
9
|
removeInlineTemp,
|
|
11
10
|
splitInlineData,
|
|
11
|
+
streamToTemp,
|
|
12
12
|
writeInlineTemp,
|
|
13
13
|
} from './attach-inline.js';
|
|
14
14
|
import { realpathSync } from 'node:fs';
|
|
15
|
-
import {
|
|
16
|
-
import
|
|
15
|
+
import { agentCommand, agentUser, type AgentUser } from '../agent-user/user.js';
|
|
16
|
+
import { log } from '@metro-labs/core/log';
|
|
17
|
+
import { readUpload, UPLOAD_TTL_MS } from '../files/upload-store.js';
|
|
18
|
+
import type { CanonicalAttachment } from '@metro-labs/core/stations/types';
|
|
17
19
|
|
|
18
20
|
export interface ResolvedAttachment {
|
|
19
21
|
path: string;
|
|
@@ -38,8 +40,6 @@ const basenameOf = (src: string): string =>
|
|
|
38
40
|
|
|
39
41
|
const isHttpUrl = (src: string): boolean => /^https?:\/\//i.test(src);
|
|
40
42
|
|
|
41
|
-
const mintId = (): string => Math.random().toString(36).slice(2, 12);
|
|
42
|
-
|
|
43
43
|
const HIDDEN_SEGMENT = /(^|\/)\.[^/]/;
|
|
44
44
|
|
|
45
45
|
function assertReadablePath(path: string): void {
|
|
@@ -57,11 +57,38 @@ function assertReadablePath(path: string): void {
|
|
|
57
57
|
);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
async function fromPathAs(
|
|
61
|
+
user: AgentUser,
|
|
62
|
+
a: CanonicalAttachment,
|
|
63
|
+
path: string,
|
|
64
|
+
): Promise<ResolvedAttachment> {
|
|
65
|
+
const proc = Bun.spawn(agentCommand(['cat', '--', path]), { stdout: 'pipe', stderr: 'pipe' });
|
|
66
|
+
const name = a.name ?? basenameOf(path) ?? 'attachment';
|
|
67
|
+
let saved: Awaited<ReturnType<typeof streamToTemp>>;
|
|
68
|
+
try {
|
|
69
|
+
saved = await streamToTemp(proc.stdout, name);
|
|
70
|
+
} catch (err) {
|
|
71
|
+
proc.kill();
|
|
72
|
+
throw err;
|
|
73
|
+
}
|
|
74
|
+
if ((await proc.exited) !== 0) {
|
|
75
|
+
await removeInlineTemp(saved.dir);
|
|
76
|
+
throw new Error(
|
|
77
|
+
`attachment path '${path}' cannot be read by the ${user.name} user Claude Code runs as. ` +
|
|
78
|
+
'`path` is read on the daemon machine with the agent\'s own rights; pass `upload` instead.',
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
assertAttachmentSize(saved.bytes);
|
|
82
|
+
return { path: saved.path, mime: a.mime ?? guessMime(path), name, bytes: saved.bytes, temp: saved.dir };
|
|
83
|
+
}
|
|
84
|
+
|
|
60
85
|
async function fromPath(
|
|
61
86
|
a: CanonicalAttachment,
|
|
62
87
|
path: string,
|
|
63
88
|
): Promise<ResolvedAttachment> {
|
|
64
89
|
assertReadablePath(path);
|
|
90
|
+
const user = agentUser();
|
|
91
|
+
if (user !== null) return fromPathAs(user, a, path);
|
|
65
92
|
const file = Bun.file(path);
|
|
66
93
|
if (!(await file.exists()))
|
|
67
94
|
throw new Error(
|
|
@@ -92,15 +119,12 @@ async function fromUrl(
|
|
|
92
119
|
if (!res.ok)
|
|
93
120
|
throw new Error(`attachment fetch failed ${res.status} for '${url}'`);
|
|
94
121
|
assertContentLength(res.headers.get('content-length'));
|
|
95
|
-
const data = new Uint8Array(await res.arrayBuffer());
|
|
96
122
|
const mime =
|
|
97
123
|
a.mime ?? res.headers.get('content-type')?.split(';')[0] ?? guessMime(url);
|
|
98
124
|
const name = a.name ?? basenameOf(url) ?? 'attachment';
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
});
|
|
103
|
-
return { path: saved.path, mime, name, bytes: data.length };
|
|
125
|
+
if (res.body === null) throw new Error(`attachment fetch returned no body for '${url}'`);
|
|
126
|
+
const { dir, path, bytes } = await streamToTemp(res.body, name);
|
|
127
|
+
return { path, mime, name, bytes, temp: dir };
|
|
104
128
|
}
|
|
105
129
|
|
|
106
130
|
async function fromData(
|
|
@@ -151,7 +175,7 @@ const sourcesOf = (a: CanonicalAttachment): string[] =>
|
|
|
151
175
|
return typeof v === 'string' && v.length > 0;
|
|
152
176
|
});
|
|
153
177
|
|
|
154
|
-
|
|
178
|
+
async function resolveAttachment(
|
|
155
179
|
a: CanonicalAttachment,
|
|
156
180
|
budget: InlineBudget = { used: 0 },
|
|
157
181
|
opts: ResolveOptions = {},
|
|
@@ -167,6 +191,7 @@ export async function resolveAttachment(
|
|
|
167
191
|
.map((s) => `\`${s}\``)
|
|
168
192
|
.join(', ')}); pass exactly one of \`upload\`, \`data\`, \`url\` or \`path\``,
|
|
169
193
|
);
|
|
194
|
+
log.info({ source: sources[0] }, 'send: attachment source');
|
|
170
195
|
if (a.upload) return fromUpload(a, a.upload, opts.allowed);
|
|
171
196
|
if (a.data) return fromData(a, a.data, budget);
|
|
172
197
|
if (a.path) return fromPath(a, a.path);
|