@stage-labs/metro 0.1.0-beta.7 → 0.1.0-beta.71
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/bedrock-eventstream.js +120 -0
- package/dist/bedrock-proxy.js +260 -0
- package/dist/bedrock-upstream.js +72 -0
- package/dist/bedrock.js +37 -0
- package/dist/claude-settings.js +40 -0
- package/dist/claude.js +107 -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 +51 -0
- package/dist/plugin.js +80 -0
- package/dist/provider-flags.js +9 -0
- package/dist/runtime-install.js +84 -0
- package/dist/runtime.js +70 -0
- package/dist/serve.js +147 -0
- package/dist/service.js +224 -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/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/mcp/package.json +64 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/addressed.ts +25 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/dedupe.ts +24 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/inbound.ts +377 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/media-note.ts +85 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/pending.ts +75 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/relay.ts +129 -0
- package/runtime/node_modules/@metro-labs/mcp/src/channels/webhook-note.ts +51 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/account-api.ts +311 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/agent-api.ts +302 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-error.ts +8 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/api-http.ts +95 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-grant.ts +99 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-owner.ts +32 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-serve.ts +128 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/attach-session.ts +280 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/boot.ts +204 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/bundle-api.ts +111 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/claude-api.ts +97 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/claude-files.ts +329 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connect-hint.ts +39 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-api.ts +307 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-json.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth-routes.ts +157 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-oauth.ts +127 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-tools.ts +98 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-url.ts +70 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/connector-verify.ts +330 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/control-api.ts +64 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/cors.ts +29 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/crash-guard.ts +49 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/events.ts +120 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/http.ts +390 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity-key.ts +33 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity-registry.ts +13 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity-routes.ts +54 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/identity.ts +7 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/is-record.ts +2 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-cli-api.ts +63 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-mode.ts +187 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/local-owner.ts +14 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/log.ts +42 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/machine-api.ts +61 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/mode-api.ts +32 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/model-api.ts +169 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-client.ts +159 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-discovery.ts +117 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/oauth-pending.ts +65 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/paths.ts +109 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/protocol.ts +226 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/relay.ts +295 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/return-to.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/runtime-deps.ts +82 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/secure-fs.ts +57 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/server-types.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/servers-api.ts +80 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-api.ts +29 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/session-apis.ts +66 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/signed-identity.ts +36 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor-io.ts +97 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/supervisor.ts +320 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/terminal-api.ts +71 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/terminal-tickets.ts +40 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/terminal-ws.ts +114 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-call.ts +22 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/train-error.ts +53 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/tunnel.ts +352 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/update-api.ts +116 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-api.ts +294 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/upload-store.ts +181 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/vault-api.ts +78 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/vault-types.ts +12 -0
- package/runtime/node_modules/@metro-labs/mcp/src/daemon/version.ts +2 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/account-attach.ts +19 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/address.ts +6 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-admin.ts +43 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-map.ts +84 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/agent-scope.ts +64 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/client.ts +32 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/connector-config.ts +144 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/connector-relay.ts +39 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/connectors.ts +69 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/file-admin.ts +329 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/file-source.ts +155 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/ids.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/key-map.ts +45 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/local-connectors.ts +277 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/materialize.ts +253 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/schema.ts +33 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/servers.ts +83 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/stations.ts +12 -0
- package/runtime/node_modules/@metro-labs/mcp/src/db/vault.ts +109 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/bedrock.ts +252 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/codex-auth.ts +231 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/codex-device.ts +91 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/codex-stream.ts +288 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/codex-translate.ts +203 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/codex.ts +211 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/eventstream.ts +123 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/forward.ts +169 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/gateway.ts +193 -0
- package/runtime/node_modules/@metro-labs/mcp/src/gateway/model-config.ts +180 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/accounts.ts +141 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/call-tools.ts +216 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/channel-owner.ts +29 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/ctx.ts +62 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/event-store.ts +91 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/frame-scope.ts +21 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/group-tools.ts +129 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/index.ts +191 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/member-tools.ts +72 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/permission-relay.ts +66 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/raw-get-stream.ts +207 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/request-identity.ts +47 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-registry.ts +172 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session-route.ts +43 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/session.ts +215 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/str.ts +5 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-dispatch.ts +215 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/tool-schemas.ts +400 -0
- package/runtime/node_modules/@metro-labs/mcp/src/mcp/upload-tool.ts +66 -0
- package/runtime/node_modules/@metro-labs/mcp/src/monitor/api.ts +237 -0
- package/runtime/node_modules/@metro-labs/mcp/src/server.ts +2 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/account-store.ts +60 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-inline.ts +100 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-interactive.ts +161 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-resolve.ts +200 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach-xmtp.ts +111 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attach.ts +184 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/attachments.ts +196 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/lines.ts +82 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/messaging-normalize.ts +88 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/registry.ts +35 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/station-runtime.ts +64 -0
- package/runtime/node_modules/@metro-labs/mcp/src/stations/types.ts +107 -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/stations.json +36 -0
- package/runtime/trains/.keep +0 -0
- package/LICENSE +0 -21
- package/README.md +0 -193
- 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 -159
- package/dist/cli/skill.js +0 -62
- package/dist/cli/util.js +0 -72
- package/dist/codex-rc.js +0 -236
- package/dist/dispatcher.js +0 -95
- package/dist/history.js +0 -84
- package/dist/ipc.js +0 -72
- package/dist/log.js +0 -15
- package/dist/paths.js +0 -68
- package/dist/stations/discord.js +0 -200
- package/dist/stations/index.js +0 -73
- package/dist/stations/telegram-md.js +0 -34
- package/dist/stations/telegram-upload.js +0 -113
- package/dist/stations/telegram.js +0 -213
- package/docs/agents.md +0 -185
- package/docs/uri-scheme.md +0 -71
- package/skills/metro/SKILL.md +0 -241
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { errMsg } from '@metro-labs/mcp/log';
|
|
2
|
+
import { mintId, SELF_URI } from './wire.js';
|
|
3
|
+
|
|
4
|
+
type EmitInbound = (accountId: string, e: Record<string, unknown>) => void;
|
|
5
|
+
|
|
6
|
+
const WHISPER_BIN = process.env.METRO_WHISPER_BIN ?? 'whisper-cli';
|
|
7
|
+
const WHISPER_MODEL =
|
|
8
|
+
process.env.METRO_WHISPER_MODEL ??
|
|
9
|
+
`${process.env.HOME}/.cache/whisper-cpp/ggml-base.bin`;
|
|
10
|
+
const FFMPEG_BIN = process.env.METRO_FFMPEG_BIN ?? 'ffmpeg';
|
|
11
|
+
|
|
12
|
+
export async function transcribeAndEmit(
|
|
13
|
+
audio: Uint8Array,
|
|
14
|
+
line: string,
|
|
15
|
+
accountId: string,
|
|
16
|
+
sourceMsgId: string,
|
|
17
|
+
emitInbound: EmitInbound,
|
|
18
|
+
): Promise<void> {
|
|
19
|
+
const {
|
|
20
|
+
existsSync: ex,
|
|
21
|
+
readFileSync: rf,
|
|
22
|
+
writeFileSync: wf,
|
|
23
|
+
unlinkSync,
|
|
24
|
+
mkdtempSync,
|
|
25
|
+
} = await import('node:fs');
|
|
26
|
+
const { tmpdir } = await import('node:os');
|
|
27
|
+
const path = await import('node:path');
|
|
28
|
+
const j = (...parts: string[]): string => path.join(...parts);
|
|
29
|
+
const { spawn } = await import('node:child_process');
|
|
30
|
+
if (!ex(WHISPER_MODEL)) return;
|
|
31
|
+
const dir = mkdtempSync(j(tmpdir(), 'xmtp-tx-'));
|
|
32
|
+
const inFile = j(dir, 'in.m4a');
|
|
33
|
+
const wav = j(dir, 'in.wav');
|
|
34
|
+
const out = j(dir, 'in');
|
|
35
|
+
const run = (bin: string, args: string[]): Promise<void> =>
|
|
36
|
+
new Promise((res, rej) => {
|
|
37
|
+
const p = spawn(bin, args, { stdio: 'ignore' });
|
|
38
|
+
p.on('error', rej);
|
|
39
|
+
p.on('exit', (c) => {
|
|
40
|
+
if (c === 0) res();
|
|
41
|
+
else rej(new Error(`${bin} ${String(c)}`));
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
try {
|
|
45
|
+
wf(inFile, audio);
|
|
46
|
+
await run(FFMPEG_BIN, [
|
|
47
|
+
'-y',
|
|
48
|
+
'-i',
|
|
49
|
+
inFile,
|
|
50
|
+
'-ar',
|
|
51
|
+
'16000',
|
|
52
|
+
'-ac',
|
|
53
|
+
'1',
|
|
54
|
+
wav,
|
|
55
|
+
]);
|
|
56
|
+
await run(WHISPER_BIN, [
|
|
57
|
+
'-m',
|
|
58
|
+
WHISPER_MODEL,
|
|
59
|
+
'-f',
|
|
60
|
+
wav,
|
|
61
|
+
'--output-txt',
|
|
62
|
+
'-of',
|
|
63
|
+
out,
|
|
64
|
+
]);
|
|
65
|
+
const text = rf(`${out}.txt`, 'utf8').trim();
|
|
66
|
+
if (!text) return;
|
|
67
|
+
emitInbound(accountId, {
|
|
68
|
+
id: mintId(),
|
|
69
|
+
ts: new Date().toISOString(),
|
|
70
|
+
station: 'xmtp',
|
|
71
|
+
line,
|
|
72
|
+
from: SELF_URI,
|
|
73
|
+
text: `🎙️ ${text}`,
|
|
74
|
+
payload: {
|
|
75
|
+
contentType: 'transcript',
|
|
76
|
+
transcribeFor: sourceMsgId,
|
|
77
|
+
transcript: text,
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
} catch (err) {
|
|
81
|
+
process.stderr.write(`xmtp transcribe failed: ${errMsg(err)}\n`);
|
|
82
|
+
} finally {
|
|
83
|
+
for (const f of [inFile, wav, `${out}.txt`]) {
|
|
84
|
+
try {
|
|
85
|
+
unlinkSync(f);
|
|
86
|
+
} catch {
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { dirname } from 'node:path';
|
|
3
|
+
import { Client, type ClientOptions, type Signer } from '@xmtp/node-sdk';
|
|
4
|
+
import { expandHome, signerFor, XMTP_ENV } from './identity.js';
|
|
5
|
+
|
|
6
|
+
export class XmtpVerifyError extends Error {}
|
|
7
|
+
|
|
8
|
+
export interface XmtpVerified {
|
|
9
|
+
inboxId: string;
|
|
10
|
+
address: string;
|
|
11
|
+
installationId: string;
|
|
12
|
+
dbPath: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface RegisteredClient {
|
|
16
|
+
inboxId: string;
|
|
17
|
+
installationId: string;
|
|
18
|
+
isRegistered: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export type CreateXmtpClient = (
|
|
22
|
+
signer: Signer,
|
|
23
|
+
dbPath: string,
|
|
24
|
+
) => Promise<RegisteredClient>;
|
|
25
|
+
|
|
26
|
+
const openInbox: CreateXmtpClient = (signer, dbPath) => {
|
|
27
|
+
const options: ClientOptions = { env: XMTP_ENV, dbPath };
|
|
28
|
+
return Client.create(signer, options);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const reason = (err: unknown): string =>
|
|
32
|
+
err instanceof Error && err.message !== '' ? err.message : 'no reason given';
|
|
33
|
+
|
|
34
|
+
export async function verifyXmtpKey(
|
|
35
|
+
privateKey: string,
|
|
36
|
+
dbPath: string,
|
|
37
|
+
create: CreateXmtpClient = openInbox,
|
|
38
|
+
): Promise<XmtpVerified> {
|
|
39
|
+
let signer: Signer;
|
|
40
|
+
let address: string;
|
|
41
|
+
try {
|
|
42
|
+
({ signer, address } = signerFor(privateKey));
|
|
43
|
+
} catch {
|
|
44
|
+
throw new XmtpVerifyError('that is not a usable XMTP private key');
|
|
45
|
+
}
|
|
46
|
+
const resolved = expandHome(dbPath);
|
|
47
|
+
mkdirSync(dirname(resolved), { recursive: true });
|
|
48
|
+
const client = await create(signer, resolved).catch((err: unknown) => {
|
|
49
|
+
throw new XmtpVerifyError(
|
|
50
|
+
`XMTP would not open an inbox for that key: ${reason(err)}`,
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
if (!client.isRegistered)
|
|
54
|
+
throw new XmtpVerifyError('XMTP did not register an inbox for that key');
|
|
55
|
+
if (client.inboxId === '')
|
|
56
|
+
throw new XmtpVerifyError('XMTP returned no inbox id for that key');
|
|
57
|
+
return {
|
|
58
|
+
inboxId: client.inboxId,
|
|
59
|
+
address,
|
|
60
|
+
installationId: client.installationId,
|
|
61
|
+
dbPath: resolved,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async function runCli(): Promise<void> {
|
|
66
|
+
let out: Record<string, unknown>;
|
|
67
|
+
try {
|
|
68
|
+
const input = JSON.parse(readFileSync(0, 'utf8')) as {
|
|
69
|
+
privateKey?: unknown;
|
|
70
|
+
dbPath?: unknown;
|
|
71
|
+
};
|
|
72
|
+
if (typeof input.privateKey !== 'string' || typeof input.dbPath !== 'string')
|
|
73
|
+
throw new XmtpVerifyError('xmtp verify needs a privateKey and a dbPath');
|
|
74
|
+
out = { ok: true, ...(await verifyXmtpKey(input.privateKey, input.dbPath)) };
|
|
75
|
+
} catch (err) {
|
|
76
|
+
out = { ok: false, error: reason(err) };
|
|
77
|
+
}
|
|
78
|
+
writeFileSync(1, `${JSON.stringify(out)}\n`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (import.meta.main) {
|
|
82
|
+
await runCli();
|
|
83
|
+
process.exit(0);
|
|
84
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
export { emit, respond, mintId } from '@metro-labs/mcp/stations/station-runtime';
|
|
2
|
+
|
|
3
|
+
export const SELF_URI = process.env.METRO_SELF_URI ?? '';
|
|
4
|
+
|
|
5
|
+
const UID_MAP_MAX = 5000;
|
|
6
|
+
const uidToXmtp = new Map<string, string>();
|
|
7
|
+
|
|
8
|
+
const INBOX_ETH_CACHE_MAX = 5000;
|
|
9
|
+
export const inboxEthCache = new Map<string, string>();
|
|
10
|
+
|
|
11
|
+
export function cacheInboxEth(inboxId: string, eth: string): void {
|
|
12
|
+
inboxEthCache.set(inboxId, eth);
|
|
13
|
+
if (inboxEthCache.size > INBOX_ETH_CACHE_MAX) {
|
|
14
|
+
const oldest = inboxEthCache.keys().next().value;
|
|
15
|
+
if (oldest !== undefined) inboxEthCache.delete(oldest);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function rememberUid(
|
|
20
|
+
uid: string | undefined,
|
|
21
|
+
xmtpId: string | undefined,
|
|
22
|
+
): void {
|
|
23
|
+
if (!uid || !xmtpId || !uid.startsWith('msg_')) return;
|
|
24
|
+
uidToXmtp.set(uid, xmtpId);
|
|
25
|
+
if (uidToXmtp.size > UID_MAP_MAX) {
|
|
26
|
+
const oldest = uidToXmtp.keys().next().value;
|
|
27
|
+
if (oldest !== undefined) uidToXmtp.delete(oldest);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function resolveMsgId(rawId: string): string {
|
|
32
|
+
if (!rawId.startsWith('msg_')) return rawId;
|
|
33
|
+
const mapped = uidToXmtp.get(rawId);
|
|
34
|
+
if (mapped) return mapped;
|
|
35
|
+
throw new Error(
|
|
36
|
+
`could not resolve universal id ${rawId} to an xmtp message id ` +
|
|
37
|
+
'(not seen by this train; pass the raw xmtp message_id)',
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const SENT_MAX = 5000;
|
|
42
|
+
const sentIds = new Set<string>();
|
|
43
|
+
|
|
44
|
+
export function rememberSent(xmtpId: string | undefined): void {
|
|
45
|
+
if (!xmtpId) return;
|
|
46
|
+
sentIds.add(xmtpId);
|
|
47
|
+
if (sentIds.size > SENT_MAX) {
|
|
48
|
+
const oldest = sentIds.values().next().value;
|
|
49
|
+
if (oldest !== undefined) sentIds.delete(oldest);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export const sentByMe = (xmtpId: string): boolean => sentIds.has(xmtpId);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"core": {
|
|
3
|
+
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
4
|
+
"@noble/curves": "^1.9.7",
|
|
5
|
+
"pino": "^9.5.0",
|
|
6
|
+
"pino-pretty": "^13.1.3",
|
|
7
|
+
"viem": "2.52.2",
|
|
8
|
+
"ws": "^8.18.0",
|
|
9
|
+
"zod": "^3.23.8"
|
|
10
|
+
},
|
|
11
|
+
"stations": {
|
|
12
|
+
"xmtp": {
|
|
13
|
+
"@xmtp/content-type-primitives": "^3.0.1",
|
|
14
|
+
"@xmtp/content-type-reaction": "^2.0.2",
|
|
15
|
+
"@xmtp/content-type-remote-attachment": "^2.0.4",
|
|
16
|
+
"@xmtp/content-type-reply": "^2.0.3",
|
|
17
|
+
"@xmtp/content-type-wallet-send-calls": "^2.0.0",
|
|
18
|
+
"@xmtp/node-bindings": "1.9.1",
|
|
19
|
+
"@xmtp/node-sdk": "^6.0.0",
|
|
20
|
+
"viem": "^2.52.2"
|
|
21
|
+
},
|
|
22
|
+
"telegram-bot": {},
|
|
23
|
+
"telegram": {
|
|
24
|
+
"@mtcute/bun": "0.30.1"
|
|
25
|
+
},
|
|
26
|
+
"discord-bot": {
|
|
27
|
+
"@discordjs/voice": "^0.19.2",
|
|
28
|
+
"discord.js": "^14.26.4",
|
|
29
|
+
"prism-media": "^1.3.5"
|
|
30
|
+
},
|
|
31
|
+
"whatsapp": {
|
|
32
|
+
"baileys": "7.0.0-rc14"
|
|
33
|
+
},
|
|
34
|
+
"webhook": {}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
File without changes
|
package/LICENSE
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Stage Labs
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
package/README.md
DELETED
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
# Metro
|
|
2
|
-
|
|
3
|
-
> **A live JSON stream of Telegram + Discord messages for your local Claude Code / Codex session.**
|
|
4
|
-
|
|
5
|
-
Metro is a small daemon you launch from inside your agent. It connects to Discord and Telegram, emits each inbound as one JSON line on stdout (which Claude Code's `Monitor` consumes natively, and Codex picks up via an app-server WebSocket push), and exposes a tiny CLI — `metro reply`, `metro send`, `metro edit`, `metro react`, `metro download`, `metro fetch` — for posting back. Cross-agent: any agent can ping any other via `metro send metro://claude/<topic>` and the daemon re-emits it on the stream.
|
|
6
|
-
|
|
7
|
-
```
|
|
8
|
-
[Claude Code session]
|
|
9
|
-
|
|
10
|
-
$ metro & # backgrounded
|
|
11
|
-
$ Monitor( … metro's stdout … )
|
|
12
|
-
|
|
13
|
-
>>> {"kind":"inbound","station":"discord","line":"metro://discord/123…","messageId":"9876",
|
|
14
|
-
"text":"@bot we got a 5xx spike from /v1/sync. Look?",
|
|
15
|
-
"payload":{"channelId":"123…","guildId":"456…","content":"<@…> we got a 5xx spike…",
|
|
16
|
-
"mentions":{"users":["<bot-id>"],"roles":[],"everyone":false},…}}
|
|
17
|
-
|
|
18
|
-
[I'd run git log + read services/sync.ts, then…]
|
|
19
|
-
Bash: metro reply metro://discord/123… 9876 "three deploys in the last 24h…"
|
|
20
|
-
```
|
|
21
|
-
|
|
22
|
-
The agent owns its own streaming, tool calls, and reply timing. Metro is the wire.
|
|
23
|
-
|
|
24
|
-
---
|
|
25
|
-
|
|
26
|
-
## Quickstart
|
|
27
|
-
|
|
28
|
-
```bash
|
|
29
|
-
npm install -g @stage-labs/metro@beta # or: bun add -g @stage-labs/metro@beta
|
|
30
|
-
|
|
31
|
-
metro setup discord <token> # https://discord.com/developers/applications
|
|
32
|
-
metro setup telegram <token> # https://t.me/BotFather
|
|
33
|
-
metro doctor # verify
|
|
34
|
-
metro # run the daemon
|
|
35
|
-
```
|
|
36
|
-
|
|
37
|
-
Requires **Node ≥ 22 or Bun ≥ 1.3**. Metro doesn't launch Claude or Codex — you do, and the agent launches metro. See [`docs/agents.md`](docs/agents.md).
|
|
38
|
-
|
|
39
|
-
In **Discord**: DM the bot, or `@<bot>` in any channel. In **Telegram**: DM, or `@<bot>` in a forum supergroup. Every inbound becomes one JSON line on `metro`'s stdout.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## Architecture
|
|
44
|
-
|
|
45
|
-
```
|
|
46
|
-
Discord gateway ──┐
|
|
47
|
-
Telegram poller ──┤
|
|
48
|
-
│
|
|
49
|
-
├─▶ metro daemon ───▶ stdout (JSON events; Claude Code's Monitor reads here)
|
|
50
|
-
│ ───▶ codex-rc WebSocket (Codex turn/start; opt-in)
|
|
51
|
-
│ ◀── IPC Unix socket (metro send to agent lines)
|
|
52
|
-
│
|
|
53
|
-
agent CLI calls ──┴── REST → Discord / Telegram (metro reply / send / edit / react / download / fetch)
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
- **Inversion of control.** The agent (Claude Code, Codex) launches `metro`, not the other way around. Metro never spawns an agent process.
|
|
57
|
-
- **Single daemon per machine.** Lockfile at `$METRO_STATE_DIR/.tail-lock` enforces singleton.
|
|
58
|
-
- **Codex push (opt-in).** Set `METRO_CODEX_RC=ws://127.0.0.1:8421` and metro pushes each event via JSON-RPC `turn/start` to the Codex app-server. Codex's TUI must be attached with `--remote` to the same URL.
|
|
59
|
-
- **Cross-agent notification.** `metro send metro://claude/<topic>` (or `metro://codex/<topic>`) routes through the daemon's IPC socket; the daemon re-emits on its stdout (and pushes to codex-rc), so the peer agent sees it.
|
|
60
|
-
|
|
61
|
-
---
|
|
62
|
-
|
|
63
|
-
## Stations
|
|
64
|
-
|
|
65
|
-
Each endpoint is a **station** with declared capabilities:
|
|
66
|
-
|
|
67
|
-
| Station | Kind | Modalities | Features | Config |
|
|
68
|
-
|------------|-------|---------------|-------------------------------------------------------|-------------------------------------------------------------|
|
|
69
|
-
| `discord` | chat | text + image | reply, send, edit, react, download, fetch | `DISCORD_BOT_TOKEN` + Message Content Intent |
|
|
70
|
-
| `telegram` | chat | text + image | reply, send, edit, react, download | `TELEGRAM_BOT_TOKEN` |
|
|
71
|
-
| `claude` | agent | text | notify | watches metro stdout via Claude Code's `Monitor` |
|
|
72
|
-
| `codex` | agent | text | notify | set `METRO_CODEX_RC=ws://…` to push |
|
|
73
|
-
|
|
74
|
-
Run `metro stations` to see live config status (`✓` configured, `✗` not, `·` informational).
|
|
75
|
-
|
|
76
|
-
Behaviors worth knowing:
|
|
77
|
-
- **No streaming / no edit machinery in metro.** The agent runs the show; metro is one-shot REST.
|
|
78
|
-
- **No link previews.** Outgoing messages set `link_preview_options.is_disabled` on Telegram and `SUPPRESS_EMBEDS` on Discord.
|
|
79
|
-
- **Image attachments inbound** — `[image]` placeholders surface inline in `text`; the agent calls `metro download` to materialize them. 20 MB cap.
|
|
80
|
-
- **Rich content outbound.** `metro send` / `reply` accept `--image=<path>` (repeatable: albums of up to 10), `--document=<path>` (repeatable), `--voice=<path>` (single voice message — Telegram renders the voice bubble), and `--buttons='[[{"text":"…","url":"…"}]]'` for inline URL-button keyboards. `metro edit` accepts `--buttons` (pass `'[]'` to clear). 20 MB / file. URL buttons only for now — no callback/interactive components.
|
|
81
|
-
- **Telegram non-forum groups are skipped.** No thread boundary to scope on.
|
|
82
|
-
|
|
83
|
-
---
|
|
84
|
-
|
|
85
|
-
## Lines
|
|
86
|
-
|
|
87
|
-
Every conversational scope is identified by a **Line** — a URI in the form `metro://<station>/<path>`:
|
|
88
|
-
|
|
89
|
-
```
|
|
90
|
-
metro://discord/1234567890123456789
|
|
91
|
-
metro://telegram/-1001234567890 # main chat / DM
|
|
92
|
-
metro://telegram/-1001234567890/42 # forum topic 42
|
|
93
|
-
metro://claude/deploys # agent notification sink
|
|
94
|
-
metro://codex/ci
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
Anyone can post to a line via [`metro send`](#cli) — daemon required only for agent lines. Full grammar in [`docs/uri-scheme.md`](docs/uri-scheme.md).
|
|
98
|
-
|
|
99
|
-
---
|
|
100
|
-
|
|
101
|
-
## CLI
|
|
102
|
-
|
|
103
|
-
```
|
|
104
|
-
metro Run the daemon (emits JSON events on stdout).
|
|
105
|
-
metro setup [telegram|discord <token>] Save token, or show status.
|
|
106
|
-
metro setup clear [telegram|discord|all] Remove tokens.
|
|
107
|
-
metro doctor Health check.
|
|
108
|
-
metro stations List stations + capabilities.
|
|
109
|
-
metro lines List recently-seen conversations.
|
|
110
|
-
metro send <line> <text> [--image=…]… [--document=…]… [--voice=…] [--buttons=…]
|
|
111
|
-
Post a fresh message; --image/--document repeat for albums.
|
|
112
|
-
metro reply <line> <message_id> <text> [--image|--document|--voice|--buttons]
|
|
113
|
-
Threaded reply (same flags as send).
|
|
114
|
-
metro edit <line> <message_id> <text> [--buttons=<json>]
|
|
115
|
-
Edit a previously-sent message (text + URL-button keyboard).
|
|
116
|
-
metro react <line> <message_id> <emoji> Set or clear ('') a reaction.
|
|
117
|
-
metro download <line> <message_id> [--out=<dir>]
|
|
118
|
-
Download image attachments to disk.
|
|
119
|
-
metro fetch <line> [--limit=N] Recent-message lookback (Discord only).
|
|
120
|
-
metro history [--limit=N] [--line=…] [--station=…] [--kind=…] [--from=…] [--text=…] [--since=…]
|
|
121
|
-
Universal message log (every inbound + outbound), newest first.
|
|
122
|
-
metro update Upgrade in place.
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
All commands accept `--json`. `reply` / `send` / `edit` read multi-line `<text>` from stdin if no positional is given.
|
|
126
|
-
|
|
127
|
-
**State files** in `$METRO_STATE_DIR` (default `~/.cache/metro`):
|
|
128
|
-
- `AGENTS.md` — agent skill copied from the package on every start (so the path is stable across upgrades)
|
|
129
|
-
- `history.jsonl` — universal message log (one JSON object per line; append-only). Read with `metro history`. Each entry carries `from` and `to` as universal participant URIs (`metro://<station>/user/<id>`, `metro://claude/<topic>`, `metro://codex/<topic>`) plus a `fromName` display field. The dispatcher auto-detects the consuming agent for `to` on inbound (`$CLAUDECODE` → `metro://claude/agent`; `$METRO_CODEX_RC`/`$CODEX_HOME` → `metro://codex/agent`).
|
|
130
|
-
- `bot-ids.json` — `{discord: "<botUserId>", telegram: "<botUserId>"}` written by the daemon on startup (cached for the few historical lookups that still need a bot identity).
|
|
131
|
-
- `lines.json` — line → last-seen / name cache (read by `metro lines`)
|
|
132
|
-
- `.tail-lock` — dispatcher pid
|
|
133
|
-
- `metro.sock` — daemon IPC socket
|
|
134
|
-
- `telegram-offset.json` — last processed update id
|
|
135
|
-
|
|
136
|
-
---
|
|
137
|
-
|
|
138
|
-
## Configuration
|
|
139
|
-
|
|
140
|
-
| Variable | Default | Description |
|
|
141
|
-
|---|---|---|
|
|
142
|
-
| `TELEGRAM_BOT_TOKEN`, `DISCORD_BOT_TOKEN` | — | Bot tokens. `metro setup` writes them here. |
|
|
143
|
-
| `METRO_CODEX_RC` | — | Codex app-server URL (`ws://…`, `wss://…`, `unix:///…`). When set, the daemon pushes each event via JSON-RPC `turn/start`. |
|
|
144
|
-
| `METRO_CONFIG_DIR` | `~/.config/metro` | Where the global `.env` lives. |
|
|
145
|
-
| `METRO_STATE_DIR` | `~/.cache/metro` | Lockfile, line cache, IPC socket, telegram offset. |
|
|
146
|
-
| `METRO_LOG_LEVEL` | `info` | `trace` / `debug` / `info` / `warn` / `error` / `fatal`. |
|
|
147
|
-
|
|
148
|
-
Precedence: process env → `./.env` → `$METRO_CONFIG_DIR/.env`. Logs go to stderr.
|
|
149
|
-
|
|
150
|
-
---
|
|
151
|
-
|
|
152
|
-
## Develop
|
|
153
|
-
|
|
154
|
-
```bash
|
|
155
|
-
git clone https://github.com/bonustrack/metro && cd metro
|
|
156
|
-
bun install && bun run build
|
|
157
|
-
bun link # makes `metro` resolve to this checkout
|
|
158
|
-
METRO_LOG_LEVEL=debug metro
|
|
159
|
-
|
|
160
|
-
bun run typecheck # ts
|
|
161
|
-
bun run lint # eslint
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
Source map:
|
|
165
|
-
|
|
166
|
-
- [`src/cli/`](src/cli/) — `metro` binary entry ([`index.ts`](src/cli/index.ts)) + admin commands ([`config.ts`](src/cli/config.ts): setup/doctor/update) + shared CLI primitives ([`util.ts`](src/cli/util.ts)).
|
|
167
|
-
- [`src/dispatcher.ts`](src/dispatcher.ts) — the daemon: starts each chat station, emits events on stdout, listens on the IPC socket, optionally pushes to codex-rc.
|
|
168
|
-
- [`src/stations/`](src/stations/) — Line URI scheme + ChatStation interface + listing ([`index.ts`](src/stations/index.ts)), the two chat-station impls (`discord.ts`, `telegram.ts`), plus the Telegram markdown→HTML helper ([`telegram-md.ts`](src/stations/telegram-md.ts)). Agent stations have no impl — they're notification sinks expressed only as URI prefixes.
|
|
169
|
-
- [`src/codex-rc.ts`](src/codex-rc.ts) — Codex app-server WebSocket push client.
|
|
170
|
-
- [`src/ipc.ts`](src/ipc.ts) — Unix-socket IPC between the daemon and one-shot CLI commands.
|
|
171
|
-
- [`src/cache.ts`](src/cache.ts) — in-memory line cache with debounced flush to `lines.json`.
|
|
172
|
-
- [`docs/uri-scheme.md`](docs/uri-scheme.md) specs the Line format; [`docs/agents.md`](docs/agents.md) is the in-context skill for agents.
|
|
173
|
-
|
|
174
|
-
CI runs typecheck + lint + build on every PR via [`.github/workflows/ci.yml`](.github/workflows/ci.yml).
|
|
175
|
-
|
|
176
|
-
---
|
|
177
|
-
|
|
178
|
-
## Caveats
|
|
179
|
-
|
|
180
|
-
- **No allowlist.** Anyone who can DM/`@`-mention your bot can produce events. Run against bots you own.
|
|
181
|
-
- **Telegram bot privacy is on by default**, which can block `@`-mentions in groups. Disable via [@BotFather](https://t.me/BotFather) → Bot Settings → Group Privacy, then kick + re-invite.
|
|
182
|
-
- **Telegram non-forum groups are skipped.** No thread boundary to scope on. DMs and forum topics work normally.
|
|
183
|
-
- **Telegram fetch isn't supported** (bot API doesn't expose history); `metro fetch` returns `[]` on Telegram lines.
|
|
184
|
-
|
|
185
|
-
---
|
|
186
|
-
|
|
187
|
-
## Uninstall
|
|
188
|
-
|
|
189
|
-
```bash
|
|
190
|
-
metro setup clear
|
|
191
|
-
rm -rf ~/.cache/metro
|
|
192
|
-
npm uninstall -g @stage-labs/metro
|
|
193
|
-
```
|
package/dist/cache.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/** Per-machine caches: seen lines (lines.json) + bot ids (bot-ids.json). */
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
import { errMsg, log } from './log.js';
|
|
5
|
-
import { STATE_DIR } from './paths.js';
|
|
6
|
-
import { Line } from './stations/index.js';
|
|
7
|
-
const cacheFile = join(STATE_DIR, 'lines.json');
|
|
8
|
-
const FLUSH_DELAY_MS = 5_000;
|
|
9
|
-
let cache = null;
|
|
10
|
-
let dirty = false;
|
|
11
|
-
let flushTimer = null;
|
|
12
|
-
function read() {
|
|
13
|
-
if (cache)
|
|
14
|
-
return cache;
|
|
15
|
-
if (!existsSync(cacheFile))
|
|
16
|
-
return cache = {};
|
|
17
|
-
try {
|
|
18
|
-
cache = JSON.parse(readFileSync(cacheFile, 'utf8'));
|
|
19
|
-
}
|
|
20
|
-
catch (err) {
|
|
21
|
-
log.warn({ err: errMsg(err), path: cacheFile }, 'lines cache read failed; treating as empty');
|
|
22
|
-
cache = {};
|
|
23
|
-
}
|
|
24
|
-
return cache;
|
|
25
|
-
}
|
|
26
|
-
function flush() {
|
|
27
|
-
if (!dirty || !cache)
|
|
28
|
-
return;
|
|
29
|
-
try {
|
|
30
|
-
writeFileSync(cacheFile, JSON.stringify(cache, null, 2));
|
|
31
|
-
dirty = false;
|
|
32
|
-
}
|
|
33
|
-
catch (err) {
|
|
34
|
-
log.warn({ err: errMsg(err), path: cacheFile }, 'lines cache write failed');
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
process.on('exit', flush);
|
|
38
|
-
export function noteSeen(line, name) {
|
|
39
|
-
const c = read();
|
|
40
|
-
const entry = c[line] ??= { createdAt: new Date().toISOString() };
|
|
41
|
-
entry.lastSeenAt = new Date().toISOString();
|
|
42
|
-
if (name && entry.name !== name)
|
|
43
|
-
entry.name = name;
|
|
44
|
-
dirty = true;
|
|
45
|
-
if (!flushTimer)
|
|
46
|
-
flushTimer = setTimeout(() => { flushTimer = null; flush(); }, FLUSH_DELAY_MS);
|
|
47
|
-
}
|
|
48
|
-
export const listLines = () => Object.entries(read()).map(([line, entry]) => ({ line: line, entry }));
|
|
49
|
-
/** Bot identity cache: `{discord: "<userId>", telegram: "<userId>"}`. Daemon writes after getMe(). */
|
|
50
|
-
const botIdsFile = join(STATE_DIR, 'bot-ids.json');
|
|
51
|
-
const readBotIds = () => {
|
|
52
|
-
try {
|
|
53
|
-
return existsSync(botIdsFile) ? JSON.parse(readFileSync(botIdsFile, 'utf8')) : {};
|
|
54
|
-
}
|
|
55
|
-
catch {
|
|
56
|
-
return {};
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
export function saveBotId(station, id) {
|
|
60
|
-
const cur = readBotIds();
|
|
61
|
-
if (cur[station] === id)
|
|
62
|
-
return;
|
|
63
|
-
cur[station] = id;
|
|
64
|
-
try {
|
|
65
|
-
writeFileSync(botIdsFile, JSON.stringify(cur, null, 2));
|
|
66
|
-
}
|
|
67
|
-
catch (err) {
|
|
68
|
-
log.warn({ err: errMsg(err) }, 'bot-ids cache write failed');
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
/** Resolve the bot's URI for a station. Returns `metro://<station>/bot/<id>` or the placeholder. */
|
|
72
|
-
export function botLine(station) {
|
|
73
|
-
const id = readBotIds()[station];
|
|
74
|
-
return id ? Line.bot(station, id) : `metro://${station}/bot`;
|
|
75
|
-
}
|