@stage-labs/metro 0.1.0-beta.9 → 0.1.0-beta.90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/api.js +14 -0
- package/dist/claude-settings.js +40 -0
- package/dist/claude.js +129 -0
- package/dist/cli.js +101 -0
- package/dist/control.js +74 -0
- package/dist/hold.js +211 -0
- package/dist/local.js +93 -0
- package/dist/node-name.js +71 -0
- package/dist/plugin.js +80 -0
- package/dist/provider-flags.js +9 -0
- package/dist/runtime-install.js +87 -0
- package/dist/runtime.js +74 -0
- package/dist/serve.js +150 -0
- package/dist/service.js +242 -0
- package/dist/tail.js +119 -0
- package/dist/update.js +45 -0
- package/dist/version.js +97 -0
- package/package.json +14 -25
- package/runtime/node_modules/@metro-labs/core/package.json +43 -0
- package/runtime/node_modules/@metro-labs/core/src/address.ts +6 -0
- package/runtime/node_modules/@metro-labs/core/src/endpoints.ts +7 -0
- package/runtime/node_modules/@metro-labs/core/src/events.ts +120 -0
- package/runtime/node_modules/@metro-labs/core/src/identity.ts +7 -0
- package/runtime/node_modules/@metro-labs/core/src/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/core/src/is-record.ts +2 -0
- package/runtime/node_modules/@metro-labs/core/src/lines.ts +82 -0
- package/runtime/node_modules/@metro-labs/core/src/log.ts +42 -0
- package/runtime/node_modules/@metro-labs/core/src/protocol.ts +226 -0
- package/runtime/node_modules/@metro-labs/core/src/secure-fs.ts +57 -0
- package/runtime/node_modules/@metro-labs/core/src/station-names.ts +12 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +60 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +196 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +88 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +64 -0
- package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +107 -0
- package/runtime/node_modules/@metro-labs/core/src/str.ts +5 -0
- package/runtime/node_modules/@metro-labs/core/src/train-error.ts +53 -0
- package/runtime/node_modules/@metro-labs/core/src/version.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/package.json +47 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/account-attach.ts +19 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +368 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/admin.ts +43 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +37 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/api.ts +302 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +111 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +345 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +155 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/keys.ts +45 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/map.ts +88 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/scope.ts +64 -0
- package/runtime/node_modules/@metro-labs/daemon/src/agents/senders.ts +38 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +193 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/crash-guard.ts +49 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/local-owner.ts +14 -0
- package/runtime/node_modules/@metro-labs/daemon/src/boot/paths.ts +106 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/addressed.ts +25 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +24 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +377 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +85 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +75 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/relay.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/channels/webhook-note.ts +51 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/api.ts +176 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +329 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +167 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +117 -0
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +154 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +307 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/cli-api.ts +63 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +144 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/json.ts +21 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/model.ts +69 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-client.ts +159 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +117 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-pending.ts +65 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +157 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth.ts +127 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +97 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +39 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +295 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +277 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/tools.ts +98 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/url.ts +70 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/verify.ts +330 -0
- package/runtime/node_modules/@metro-labs/daemon/src/connectors/watch.ts +54 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +99 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-owner.ts +32 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +121 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +294 -0
- package/runtime/node_modules/@metro-labs/daemon/src/files/upload-store.ts +181 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +252 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +231 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +288 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +203 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +220 -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 +182 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +208 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +195 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +198 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +40 -0
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/served.ts +17 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +139 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +216 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/channel-owner.ts +29 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/ctx.ts +62 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/frame-scope.ts +21 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +129 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +191 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/member-tools.ts +72 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/permission-relay.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/raw-get-stream.ts +207 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/request-identity.ts +47 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-registry.ts +172 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session-route.ts +43 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +215 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-dispatch.ts +215 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-schemas.ts +400 -0
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/upload-tool.ts +66 -0
- package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +237 -0
- package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +389 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +390 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/identity.ts +54 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +198 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +60 -0
- package/runtime/node_modules/@metro-labs/daemon/src/routes/session.ts +29 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/control.ts +64 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +61 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +116 -0
- package/runtime/node_modules/@metro-labs/daemon/src/server.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-inline.ts +100 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-interactive.ts +161 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +202 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +280 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +111 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +184 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +248 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/registry.ts +35 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/runtime-deps.ts +82 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +97 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +320 -0
- package/runtime/node_modules/@metro-labs/daemon/src/stations/train-call.ts +22 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +91 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +123 -0
- package/runtime/node_modules/@metro-labs/daemon/src/terminal/tickets.ts +40 -0
- package/runtime/node_modules/@metro-labs/discord-bot/package.json +28 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +118 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +388 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/api-base.ts +1 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +29 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +220 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +50 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +115 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +127 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +215 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +43 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/station.ts +19 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +82 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-speak.ts +195 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice-transcribe.ts +292 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/voice.ts +197 -0
- package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +5 -0
- package/runtime/node_modules/@metro-labs/http/package.json +33 -0
- package/runtime/node_modules/@metro-labs/http/src/api-error.ts +8 -0
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +95 -0
- package/runtime/node_modules/@metro-labs/http/src/cors.ts +29 -0
- package/runtime/node_modules/@metro-labs/http/src/identity-key.ts +33 -0
- package/runtime/node_modules/@metro-labs/http/src/identity-registry.ts +13 -0
- package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +32 -0
- package/runtime/node_modules/@metro-labs/http/src/return-to.ts +21 -0
- package/runtime/node_modules/@metro-labs/http/src/signed-identity.ts +36 -0
- package/runtime/node_modules/@metro-labs/telegram/package.json +26 -0
- package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +80 -0
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +254 -0
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +87 -0
- package/runtime/node_modules/@metro-labs/telegram/src/client.ts +77 -0
- package/runtime/node_modules/@metro-labs/telegram/src/format.ts +156 -0
- package/runtime/node_modules/@metro-labs/telegram/src/group.ts +261 -0
- package/runtime/node_modules/@metro-labs/telegram/src/history.ts +52 -0
- package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +78 -0
- package/runtime/node_modules/@metro-labs/telegram/src/index.ts +46 -0
- package/runtime/node_modules/@metro-labs/telegram/src/login.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +80 -0
- package/runtime/node_modules/@metro-labs/telegram/src/members.ts +75 -0
- package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +22 -0
- package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +167 -0
- package/runtime/node_modules/@metro-labs/telegram/src/station.ts +24 -0
- package/runtime/node_modules/@metro-labs/telegram/src/types.ts +7 -0
- package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +1 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/package.json +25 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +120 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +295 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +128 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +201 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/index.ts +109 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +166 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +47 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/types.ts +58 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/verify.ts +38 -0
- package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +3 -0
- package/runtime/node_modules/@metro-labs/webhook/package.json +23 -0
- package/runtime/node_modules/@metro-labs/webhook/src/station.ts +37 -0
- package/runtime/node_modules/@metro-labs/whatsapp/package.json +27 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +66 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/ack.ts +101 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +178 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +72 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/auth-state.ts +129 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +327 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/delivery.ts +49 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +138 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +57 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/index.ts +40 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/keys.ts +59 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/logger.ts +60 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +176 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/media.ts +84 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +22 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/outbox.ts +38 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/parse.ts +185 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/station.ts +17 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/token-store.ts +98 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/types.ts +6 -0
- package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +5 -0
- package/runtime/node_modules/@metro-labs/xmtp/package.json +33 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +112 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +110 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +299 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +157 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-push.ts +87 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +375 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +107 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +171 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +239 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-core.ts +55 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +335 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +80 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/group-members.ts +34 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +56 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +33 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +125 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +157 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +40 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/push.ts +349 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/station.ts +13 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +126 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/tools.ts +212 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +90 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/verify.ts +84 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +53 -0
- package/runtime/runtime.json +3 -0
- package/runtime/server.ts +1 -0
- package/runtime/stations.json +36 -0
- package/runtime/trains/.keep +0 -0
- package/LICENSE +0 -21
- package/README.md +0 -249
- package/dist/cache.js +0 -75
- package/dist/cli/actions.js +0 -139
- package/dist/cli/config.js +0 -185
- package/dist/cli/index.js +0 -177
- package/dist/cli/skill.js +0 -62
- package/dist/cli/util.js +0 -72
- package/dist/cli/webhook.js +0 -83
- package/dist/codex-rc.js +0 -245
- package/dist/dispatcher.js +0 -122
- package/dist/history.js +0 -99
- package/dist/ipc.js +0 -72
- package/dist/log.js +0 -15
- package/dist/paths.js +0 -68
- package/dist/registry.js +0 -48
- package/dist/stations/claude.js +0 -45
- package/dist/stations/codex.js +0 -68
- package/dist/stations/discord.js +0 -200
- package/dist/stations/index.js +0 -133
- package/dist/stations/telegram-md.js +0 -34
- package/dist/stations/telegram-upload.js +0 -113
- package/dist/stations/telegram.js +0 -213
- package/dist/stations/webhook.js +0 -100
- package/dist/tunnel.js +0 -66
- package/dist/webhooks.js +0 -40
- package/docs/agents.md +0 -216
- package/docs/uri-scheme.md +0 -125
- package/skills/metro/SKILL.md +0 -249
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { apiFailure, apiSession, cors, sendJson } from '@metro-labs/http/api-http';
|
|
3
|
+
|
|
4
|
+
const PATH = '/api/session';
|
|
5
|
+
|
|
6
|
+
export function handleSessionApiRequest(
|
|
7
|
+
req: IncomingMessage,
|
|
8
|
+
res: ServerResponse,
|
|
9
|
+
): boolean {
|
|
10
|
+
const path = (req.url ?? '').split('?')[0] ?? '';
|
|
11
|
+
if (path !== PATH) return false;
|
|
12
|
+
if (req.method === 'OPTIONS') {
|
|
13
|
+
res.writeHead(204, cors(req)).end();
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
if (req.method !== 'GET') {
|
|
17
|
+
sendJson(req, res, 405, { error: 'method not allowed' });
|
|
18
|
+
return true;
|
|
19
|
+
}
|
|
20
|
+
apiSession(req)
|
|
21
|
+
.then((session) => {
|
|
22
|
+
if (!session) sendJson(req, res, 401, { error: 'unauthorized' });
|
|
23
|
+
else sendJson(req, res, 200, { subject: session.subject });
|
|
24
|
+
})
|
|
25
|
+
.catch((err: unknown) => {
|
|
26
|
+
apiFailure(req, res, err, 'session');
|
|
27
|
+
});
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
3
|
+
import { apiFailure, apiSession, cors, sendJson } from '@metro-labs/http/api-http';
|
|
4
|
+
import { errMsg, 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
|
+
authorize: (subject: string) => void;
|
|
12
|
+
restart: () => void;
|
|
13
|
+
stop: () => void;
|
|
14
|
+
served?: () => boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
interface Action {
|
|
18
|
+
run: () => void;
|
|
19
|
+
body: Record<string, boolean>;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const servedByCli = (): boolean => (process.env.METRO_CLI_BIN?.trim() ?? '') !== '';
|
|
23
|
+
|
|
24
|
+
function actionFor(path: string, deps: ControlApiDeps): Action | null {
|
|
25
|
+
if (path === STOP) return { run: deps.stop, body: { stopping: true } };
|
|
26
|
+
if (path === RESTART) return { run: deps.restart, body: { restarting: true } };
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function handleControlRequest(req: IncomingMessage, res: ServerResponse, deps: ControlApiDeps): boolean {
|
|
31
|
+
const path = (req.url ?? '').split('?')[0] ?? '';
|
|
32
|
+
const action = actionFor(path, deps);
|
|
33
|
+
if (action === null) return false;
|
|
34
|
+
if (req.method === 'OPTIONS') {
|
|
35
|
+
res.writeHead(204, cors(req)).end();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
if (req.method !== 'POST') {
|
|
39
|
+
sendJson(req, res, 405, { error: 'method not allowed' });
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
Promise.resolve()
|
|
43
|
+
.then(async () => {
|
|
44
|
+
const session = await apiSession(req);
|
|
45
|
+
if (!session) throw new ApiError('unauthorized', 401);
|
|
46
|
+
deps.authorize(session.subject);
|
|
47
|
+
if (!(deps.served ?? servedByCli)())
|
|
48
|
+
throw new ApiError(
|
|
49
|
+
'this daemon was not started by metro serve, so nothing on the machine would bring it back: use the shell instead',
|
|
50
|
+
400,
|
|
51
|
+
);
|
|
52
|
+
log.info({ path, subject: session.subject }, 'control-api: requested from the page');
|
|
53
|
+
setTimeout(action.run, EXIT_DELAY_MS).unref();
|
|
54
|
+
sendJson(req, res, 200, action.body);
|
|
55
|
+
})
|
|
56
|
+
.catch((err: unknown) => {
|
|
57
|
+
if (err instanceof ApiError) apiFailure(req, res, err, 'control-api');
|
|
58
|
+
else {
|
|
59
|
+
log.warn({ err: errMsg(err) }, 'control-api: request failed');
|
|
60
|
+
if (!res.headersSent) sendJson(req, res, 500, { error: 'control api failed' });
|
|
61
|
+
}
|
|
62
|
+
});
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
2
|
+
import { hostname } from 'node:os';
|
|
3
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
4
|
+
import { apiFailure, apiSession, cors, sendJson } from '@metro-labs/http/api-http';
|
|
5
|
+
import { publicBaseUrl } from '../files/attach-serve.js';
|
|
6
|
+
import { claudeDir } from '../claude/files.js';
|
|
7
|
+
import { webhookPort } from '../net/tunnel.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
|
+
authorize: (subject: string) => void;
|
|
16
|
+
startedAt?: string;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const bootedAt = new Date(Date.now() - process.uptime() * 1000).toISOString();
|
|
20
|
+
|
|
21
|
+
export function machineInfo(startedAt = bootedAt): Record<string, unknown> {
|
|
22
|
+
const store = process.env.METRO_RUNTIME_STORE?.trim() ?? '';
|
|
23
|
+
return {
|
|
24
|
+
version: METRO_VERSION,
|
|
25
|
+
owner: localOwner(),
|
|
26
|
+
hostname: hostname(),
|
|
27
|
+
platform: process.platform,
|
|
28
|
+
arch: process.arch,
|
|
29
|
+
port: webhookPort(),
|
|
30
|
+
publicUrl: publicBaseUrl(),
|
|
31
|
+
uptimeSeconds: Math.round(process.uptime()),
|
|
32
|
+
startedAt,
|
|
33
|
+
bun: process.versions.bun ?? null,
|
|
34
|
+
agentsDir: agentsDir(),
|
|
35
|
+
claudeDir: claudeDir(),
|
|
36
|
+
runtimeStore: store === '' ? null : store,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function handleMachineRequest(req: IncomingMessage, res: ServerResponse, deps: MachineApiDeps): boolean {
|
|
41
|
+
const path = (req.url ?? '').split('?')[0] ?? '';
|
|
42
|
+
if (path !== PATH) return false;
|
|
43
|
+
if (req.method === 'OPTIONS') {
|
|
44
|
+
res.writeHead(204, cors(req)).end();
|
|
45
|
+
return true;
|
|
46
|
+
}
|
|
47
|
+
if (req.method !== 'GET') {
|
|
48
|
+
sendJson(req, res, 405, { error: 'method not allowed' });
|
|
49
|
+
return true;
|
|
50
|
+
}
|
|
51
|
+
apiSession(req)
|
|
52
|
+
.then((session) => {
|
|
53
|
+
if (!session) throw new ApiError('unauthorized', 401);
|
|
54
|
+
deps.authorize(session.subject);
|
|
55
|
+
sendJson(req, res, 200, machineInfo(deps.startedAt));
|
|
56
|
+
})
|
|
57
|
+
.catch((err: unknown) => {
|
|
58
|
+
apiFailure(req, res, err, 'machine-api');
|
|
59
|
+
});
|
|
60
|
+
return true;
|
|
61
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { execFile } from 'node:child_process';
|
|
2
|
+
import type { IncomingMessage, ServerResponse } from 'node:http';
|
|
3
|
+
import { errMsg, log } from '@metro-labs/core/log';
|
|
4
|
+
import { ApiError } from '@metro-labs/http/api-error';
|
|
5
|
+
import { apiFailure, apiSession, cors, sendJson } 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
|
+
export interface UpdateCheck {
|
|
17
|
+
current: string;
|
|
18
|
+
latest: string;
|
|
19
|
+
newer: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface UpdateResult {
|
|
23
|
+
updated: boolean;
|
|
24
|
+
version: string;
|
|
25
|
+
restarting: boolean;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface UpdateApiDeps {
|
|
29
|
+
authorize: (subject: string) => void;
|
|
30
|
+
restart: () => void;
|
|
31
|
+
cliBin?: () => string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const cliBinFromEnv = (): string => process.env.METRO_CLI_BIN?.trim() ?? '';
|
|
35
|
+
|
|
36
|
+
function runCli(bin: string, args: string[], timeout: number): Promise<{ stdout: string; stderr: string }> {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
execFile(process.execPath, [bin, ...args], { timeout, maxBuffer: 4 * 1024 * 1024 }, (err, stdout, stderr) => {
|
|
39
|
+
if (err === null) resolve({ stdout, stderr });
|
|
40
|
+
else reject(new ApiError(`metro update failed: ${(stderr || err.message).trim().slice(-OUTPUT_TAIL)}`, 502));
|
|
41
|
+
});
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
let cached: { at: number; check: UpdateCheck } | null = null;
|
|
46
|
+
|
|
47
|
+
function parseCheck(raw: string): UpdateCheck {
|
|
48
|
+
let body: unknown;
|
|
49
|
+
try {
|
|
50
|
+
body = JSON.parse(raw.trim().split('\n').at(-1) ?? '');
|
|
51
|
+
} catch {
|
|
52
|
+
throw new ApiError('metro update --check returned no report', 502);
|
|
53
|
+
}
|
|
54
|
+
if (!isRecord(body) || typeof body.current !== 'string' || typeof body.latest !== 'string')
|
|
55
|
+
throw new ApiError('metro update --check returned an unexpected report', 502);
|
|
56
|
+
return { current: body.current, latest: body.latest, newer: body.newer === true };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function check(bin: string, now = Date.now()): Promise<UpdateCheck> {
|
|
60
|
+
if (cached !== null && now - cached.at < CHECK_TTL_MS) return cached.check;
|
|
61
|
+
const out = await runCli(bin, ['update', '--check'], CHECK_TIMEOUT_MS);
|
|
62
|
+
cached = { at: now, check: parseCheck(out.stdout) };
|
|
63
|
+
return cached.check;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function apply(bin: string, deps: UpdateApiDeps): Promise<UpdateResult> {
|
|
67
|
+
const before = await check(bin);
|
|
68
|
+
if (!before.newer) return { updated: false, version: before.current, restarting: false };
|
|
69
|
+
const out = await runCli(bin, ['update'], UPDATE_TIMEOUT_MS);
|
|
70
|
+
cached = null;
|
|
71
|
+
log.info({ from: before.current, to: before.latest, output: out.stderr.trim().slice(-OUTPUT_TAIL) }, 'update-api: metro updated, restarting');
|
|
72
|
+
setTimeout(() => {
|
|
73
|
+
deps.restart();
|
|
74
|
+
}, RESTART_DELAY_MS).unref();
|
|
75
|
+
return { updated: true, version: before.latest, restarting: true };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function binOrThrow(deps: UpdateApiDeps): string {
|
|
79
|
+
const bin = (deps.cliBin ?? cliBinFromEnv)();
|
|
80
|
+
if (bin === '')
|
|
81
|
+
throw new ApiError('this daemon was not started by metro serve, so it cannot update itself: run metro update on the machine', 400);
|
|
82
|
+
return bin;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
export function handleUpdateRequest(req: IncomingMessage, res: ServerResponse, deps: UpdateApiDeps): boolean {
|
|
86
|
+
const path = (req.url ?? '').split('?')[0] ?? '';
|
|
87
|
+
if (path !== PATH) return false;
|
|
88
|
+
if (req.method === 'OPTIONS') {
|
|
89
|
+
res.writeHead(204, cors(req)).end();
|
|
90
|
+
return true;
|
|
91
|
+
}
|
|
92
|
+
if (req.method !== 'GET' && req.method !== 'POST') {
|
|
93
|
+
sendJson(req, res, 405, { error: 'method not allowed' });
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
Promise.resolve()
|
|
97
|
+
.then(async (): Promise<unknown> => {
|
|
98
|
+
const session = await apiSession(req);
|
|
99
|
+
if (!session) throw new ApiError('unauthorized', 401);
|
|
100
|
+
deps.authorize(session.subject);
|
|
101
|
+
const bin = binOrThrow(deps);
|
|
102
|
+
if (req.method === 'GET') return { ...(await check(bin)), running: METRO_VERSION };
|
|
103
|
+
return apply(bin, deps);
|
|
104
|
+
})
|
|
105
|
+
.then((body) => {
|
|
106
|
+
sendJson(req, res, 200, body);
|
|
107
|
+
})
|
|
108
|
+
.catch((err: unknown) => {
|
|
109
|
+
if (err instanceof ApiError) apiFailure(req, res, err, 'update-api');
|
|
110
|
+
else {
|
|
111
|
+
log.warn({ err: errMsg(err) }, 'update-api: request failed');
|
|
112
|
+
if (!res.headersSent) sendJson(req, res, 500, { error: 'update api failed' });
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
export const MAX_INLINE_BYTES = 8 * 1024 * 1024;
|
|
6
|
+
|
|
7
|
+
const INLINE_TEMP_PREFIX = 'metro-inline-';
|
|
8
|
+
|
|
9
|
+
const BASE64_RE = /^[A-Za-z0-9+/_-]*={0,2}$/;
|
|
10
|
+
|
|
11
|
+
const mib = (n: number): string => `${(n / (1024 * 1024)).toFixed(2)} MiB`;
|
|
12
|
+
|
|
13
|
+
const LIMIT_HINT =
|
|
14
|
+
`inline \`data\` is capped at ${mib(MAX_INLINE_BYTES)} (${MAX_INLINE_BYTES} bytes) of decoded ` +
|
|
15
|
+
'bytes per attachment and per send, because the base64 travels inside the MCP request body. ' +
|
|
16
|
+
'Pass `url`, or a `path` already on the daemon host, for anything larger.';
|
|
17
|
+
|
|
18
|
+
function assertInlineSize(bytes: number, label: string): void {
|
|
19
|
+
if (bytes > MAX_INLINE_BYTES)
|
|
20
|
+
throw new Error(`inline attachment ${label} is ${mib(bytes)}; ${LIMIT_HINT}`);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function assertInlineTotal(bytes: number): void {
|
|
24
|
+
if (bytes > MAX_INLINE_BYTES)
|
|
25
|
+
throw new Error(
|
|
26
|
+
`inline attachments total ${mib(bytes)} in one send; ${LIMIT_HINT}`,
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const padOf = (b64: string): number =>
|
|
31
|
+
b64.endsWith('==') ? 2 : b64.endsWith('=') ? 1 : 0;
|
|
32
|
+
|
|
33
|
+
export const decodedLengthOf = (b64: string): number =>
|
|
34
|
+
Math.max(0, Math.floor((b64.length * 3) / 4) - padOf(b64));
|
|
35
|
+
|
|
36
|
+
const malformed = (why: string): Error =>
|
|
37
|
+
new Error(
|
|
38
|
+
`inline \`data\` ${why}; pass the file's bytes base64-encoded ` +
|
|
39
|
+
'(standard or url-safe alphabet, optionally behind a `data:<mime>;base64,` prefix)',
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
const DATA_URL_RE = /^data:([^,]*),/i;
|
|
43
|
+
|
|
44
|
+
export interface InlineSource {
|
|
45
|
+
data: string;
|
|
46
|
+
mime?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function splitInlineData(raw: string): InlineSource {
|
|
50
|
+
const compact = raw.replace(/\s+/g, '');
|
|
51
|
+
const match = DATA_URL_RE.exec(compact);
|
|
52
|
+
if (!match) return { data: raw };
|
|
53
|
+
const params = (match[1] ?? '').split(';');
|
|
54
|
+
if (!params.slice(1).some((p) => p.toLowerCase() === 'base64'))
|
|
55
|
+
throw malformed('is a `data:` url whose payload is not base64');
|
|
56
|
+
const mime = params[0]?.toLowerCase();
|
|
57
|
+
const data = compact.slice(match[0].length);
|
|
58
|
+
return mime === undefined || mime === '' ? { data } : { data, mime };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function decodeInline(raw: string, label: string): Uint8Array {
|
|
62
|
+
const b64 = raw.replace(/\s+/g, '');
|
|
63
|
+
if (!b64) throw malformed('is empty');
|
|
64
|
+
const expected = decodedLengthOf(b64);
|
|
65
|
+
assertInlineSize(expected, label);
|
|
66
|
+
if (!BASE64_RE.test(b64)) throw malformed('is not base64');
|
|
67
|
+
if (b64.length % 4 === 1 || (padOf(b64) > 0 && b64.length % 4 !== 0))
|
|
68
|
+
throw malformed('is not a whole number of base64 quanta');
|
|
69
|
+
const buf = Buffer.from(b64, 'base64');
|
|
70
|
+
if (buf.length !== expected)
|
|
71
|
+
throw malformed(`decoded to ${buf.length} bytes instead of ${expected}`);
|
|
72
|
+
return new Uint8Array(buf);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export const safeFileName = (name: string | undefined): string => {
|
|
76
|
+
const base = (name ?? '').split(/[\\/]/).pop() ?? '';
|
|
77
|
+
const clean = base
|
|
78
|
+
.replace(/[^A-Za-z0-9._-]/g, '_')
|
|
79
|
+
.replace(/^\.+/, '')
|
|
80
|
+
.slice(0, 64);
|
|
81
|
+
return clean || 'attachment';
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
export interface InlineTemp {
|
|
85
|
+
dir: string;
|
|
86
|
+
path: string;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export async function writeInlineTemp(
|
|
90
|
+
data: Uint8Array,
|
|
91
|
+
name: string | undefined,
|
|
92
|
+
): Promise<InlineTemp> {
|
|
93
|
+
const dir = await mkdtemp(join(tmpdir(), INLINE_TEMP_PREFIX));
|
|
94
|
+
const path = join(dir, safeFileName(name));
|
|
95
|
+
await writeFile(path, data, { mode: 0o600 });
|
|
96
|
+
return { dir, path };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export const removeInlineTemp = (dir: string): Promise<void> =>
|
|
100
|
+
rm(dir, { recursive: true, force: true });
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { StationAttachError } from './attach.js';
|
|
2
|
+
|
|
3
|
+
export const INTERACTIVE_STATIONS = ['telegram', 'whatsapp'] as const;
|
|
4
|
+
|
|
5
|
+
export type InteractiveStation = (typeof INTERACTIVE_STATIONS)[number];
|
|
6
|
+
|
|
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
|
+
const CODE_PROMPT =
|
|
38
|
+
'Telegram sent a login code to that number. Enter it here.';
|
|
39
|
+
const PASSWORD_PROMPT =
|
|
40
|
+
'This account has two-step verification. Enter its password.';
|
|
41
|
+
const SCAN_PROMPT =
|
|
42
|
+
'Open WhatsApp on your phone, go to Settings, Linked Devices, Link a Device, and scan this code.';
|
|
43
|
+
const PAIR_PROMPT =
|
|
44
|
+
'Open WhatsApp on your phone, go to Settings, Linked Devices, Link a Device, Link with phone number, and type this code.';
|
|
45
|
+
|
|
46
|
+
export function isInteractiveStation(raw: unknown): raw is InteractiveStation {
|
|
47
|
+
return (
|
|
48
|
+
typeof raw === 'string' &&
|
|
49
|
+
(INTERACTIVE_STATIONS as readonly string[]).includes(raw)
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function refuse(err: unknown, fallback: string): never {
|
|
54
|
+
throw new StationAttachError(
|
|
55
|
+
err instanceof Error && err.message !== '' ? err.message : fallback,
|
|
56
|
+
400,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function startTelegramUser(
|
|
61
|
+
input: Record<string, unknown>,
|
|
62
|
+
hooks: DriverHooks,
|
|
63
|
+
): Promise<StartedAttach> {
|
|
64
|
+
const { TelegramLogin, TelegramLoginError, validateCredentials } =
|
|
65
|
+
await import('@metro-labs/telegram/login');
|
|
66
|
+
const isOwn = (e: unknown): boolean => e instanceof TelegramLoginError;
|
|
67
|
+
let login;
|
|
68
|
+
try {
|
|
69
|
+
login = new TelegramLogin(
|
|
70
|
+
validateCredentials({
|
|
71
|
+
apiId: input.apiId,
|
|
72
|
+
apiHash: input.apiHash,
|
|
73
|
+
phone: input.phone,
|
|
74
|
+
}),
|
|
75
|
+
);
|
|
76
|
+
await login.requestCode();
|
|
77
|
+
} catch (err) {
|
|
78
|
+
await login?.close();
|
|
79
|
+
refuse(isOwn(err) ? err : null, 'Telegram refused to start the sign-in');
|
|
80
|
+
}
|
|
81
|
+
const settled = login;
|
|
82
|
+
return {
|
|
83
|
+
prompt: { step: 'code', prompt: CODE_PROMPT },
|
|
84
|
+
driver: {
|
|
85
|
+
cancel: () => settled.close(),
|
|
86
|
+
submit: async (values) => {
|
|
87
|
+
try {
|
|
88
|
+
if (typeof values.password === 'string') {
|
|
89
|
+
hooks.done(await settled.submitPassword(values.password));
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
if (typeof values.code !== 'string')
|
|
93
|
+
throw new StationAttachError('enter the login code', 400);
|
|
94
|
+
const result = await settled.submitCode(values.code);
|
|
95
|
+
if (result === 'password') {
|
|
96
|
+
hooks.prompt({ step: 'password', prompt: PASSWORD_PROMPT });
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
hooks.done(result);
|
|
100
|
+
} catch (err) {
|
|
101
|
+
if (err instanceof StationAttachError) throw err;
|
|
102
|
+
refuse(isOwn(err) ? err : null, 'Telegram refused the sign-in');
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function startWhatsapp(
|
|
110
|
+
input: Record<string, unknown>,
|
|
111
|
+
hooks: DriverHooks,
|
|
112
|
+
): Promise<StartedAttach> {
|
|
113
|
+
const { WhatsappLogin, WhatsappLoginError, normalizePhone } = await import(
|
|
114
|
+
'@metro-labs/whatsapp/login'
|
|
115
|
+
);
|
|
116
|
+
const isOwn = (e: unknown): boolean => e instanceof WhatsappLoginError;
|
|
117
|
+
let login;
|
|
118
|
+
try {
|
|
119
|
+
login = new WhatsappLogin(normalizePhone(input.phone), {
|
|
120
|
+
onQr: (qr) => {
|
|
121
|
+
hooks.prompt({ step: 'scan', prompt: SCAN_PROMPT, qr });
|
|
122
|
+
},
|
|
123
|
+
onPairingCode: (pairingCode) => {
|
|
124
|
+
hooks.prompt({ step: 'pair', prompt: PAIR_PROMPT, pairingCode });
|
|
125
|
+
},
|
|
126
|
+
onPaired: hooks.done,
|
|
127
|
+
onFailed: hooks.fail,
|
|
128
|
+
});
|
|
129
|
+
await login.start();
|
|
130
|
+
} catch (err) {
|
|
131
|
+
await login?.cancel();
|
|
132
|
+
refuse(isOwn(err) ? err : null, 'WhatsApp refused to start the pairing');
|
|
133
|
+
}
|
|
134
|
+
const settled = login;
|
|
135
|
+
const qrMode = settled.mode === 'qr';
|
|
136
|
+
return {
|
|
137
|
+
prompt: qrMode
|
|
138
|
+
? { step: 'scan', prompt: SCAN_PROMPT }
|
|
139
|
+
: { step: 'pair', prompt: PAIR_PROMPT },
|
|
140
|
+
driver: {
|
|
141
|
+
cancel: () => settled.cancel(),
|
|
142
|
+
submit: () =>
|
|
143
|
+
Promise.reject(
|
|
144
|
+
new StationAttachError(
|
|
145
|
+
'this pairing finishes on your phone, there is nothing to submit here',
|
|
146
|
+
409,
|
|
147
|
+
),
|
|
148
|
+
),
|
|
149
|
+
},
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function startInteractiveAttach(
|
|
154
|
+
station: InteractiveStation,
|
|
155
|
+
input: Record<string, unknown>,
|
|
156
|
+
hooks: DriverHooks,
|
|
157
|
+
): Promise<StartedAttach> {
|
|
158
|
+
return station === 'telegram'
|
|
159
|
+
? startTelegramUser(input, hooks)
|
|
160
|
+
: startWhatsapp(input, hooks);
|
|
161
|
+
}
|