@stage-labs/metro 0.1.0-beta.7 → 0.1.0-beta.70
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 +106 -0
- package/dist/claude.js +15 -0
- package/dist/cli.js +101 -0
- package/dist/control.js +74 -0
- package/dist/hold.js +197 -0
- package/dist/local.js +93 -0
- package/dist/node-name.js +51 -0
- package/dist/plugin.js +80 -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 +384 -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 +184 -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/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 +61 -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/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
package/dist/cli/actions.js
DELETED
|
@@ -1,139 +0,0 @@
|
|
|
1
|
-
/** CLI action handlers: send/reply/edit/react/download/fetch + helpers. */
|
|
2
|
-
import { mkdirSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { errMsg } from '../log.js';
|
|
6
|
-
import { DiscordStation } from '../stations/discord.js';
|
|
7
|
-
import { TelegramStation } from '../stations/telegram.js';
|
|
8
|
-
import { ipcCall } from '../ipc.js';
|
|
9
|
-
import { agentSelf, appendHistory, lookupEntry, mintId, resolvePlatformId, } from '../history.js';
|
|
10
|
-
import { asLine, Line } from '../stations/index.js';
|
|
11
|
-
import { loadMetroEnv } from '../paths.js';
|
|
12
|
-
import { emit, flagList, flagOne, isJson, need, resolveText, writeJson, } from './util.js';
|
|
13
|
-
export function chatStationOf(line) {
|
|
14
|
-
const s = Line.station(line);
|
|
15
|
-
if (s === 'discord')
|
|
16
|
-
return new DiscordStation();
|
|
17
|
-
if (s === 'telegram')
|
|
18
|
-
return new TelegramStation();
|
|
19
|
-
throw new Error(`no chat station for line "${line}" (try metro://{discord|telegram}/...)`);
|
|
20
|
-
}
|
|
21
|
-
function parseButtons(f) {
|
|
22
|
-
const raw = flagOne(f, 'buttons');
|
|
23
|
-
if (raw === undefined)
|
|
24
|
-
return undefined;
|
|
25
|
-
try {
|
|
26
|
-
return JSON.parse(raw);
|
|
27
|
-
}
|
|
28
|
-
catch (err) {
|
|
29
|
-
throw new Error(`--buttons must be JSON like '[[{"text":"…","url":"…"}]]': ${errMsg(err)}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
function richOpts(f) {
|
|
33
|
-
const opts = {};
|
|
34
|
-
const images = flagList(f, 'image');
|
|
35
|
-
if (images.length)
|
|
36
|
-
opts.images = images;
|
|
37
|
-
const documents = flagList(f, 'document');
|
|
38
|
-
if (documents.length)
|
|
39
|
-
opts.documents = documents;
|
|
40
|
-
const voice = flagOne(f, 'voice');
|
|
41
|
-
if (voice)
|
|
42
|
-
opts.voice = voice;
|
|
43
|
-
const buttons = parseButtons(f);
|
|
44
|
-
if (buttons)
|
|
45
|
-
opts.buttons = buttons;
|
|
46
|
-
return opts;
|
|
47
|
-
}
|
|
48
|
-
/** Append an outbound action to history.jsonl; `to` = the original sender when replying/reacting. */
|
|
49
|
-
function logOutbound(f, e) {
|
|
50
|
-
const id = mintId();
|
|
51
|
-
const fromOverride = flagOne(f, 'from');
|
|
52
|
-
appendHistory({
|
|
53
|
-
id, ts: new Date().toISOString(), station: Line.station(e.line) ?? '?',
|
|
54
|
-
from: fromOverride ? asLine(fromOverride) : agentSelf(), to: e.to ?? e.line, ...e,
|
|
55
|
-
});
|
|
56
|
-
return id;
|
|
57
|
-
}
|
|
58
|
-
export async function cmdSend(p, f) {
|
|
59
|
-
need(p, 1, 'metro send <line> <text> [--image=<path>]… [--document=<path>]… [--voice=<path>] [--buttons=<json>]');
|
|
60
|
-
loadMetroEnv();
|
|
61
|
-
const text = await resolveText(p, 1), line = asLine(p[0]);
|
|
62
|
-
if (Line.isAgent(line)) {
|
|
63
|
-
const from = flagOne(f, 'from');
|
|
64
|
-
const resp = await ipcCall({ op: 'notify', line, from, text });
|
|
65
|
-
if (!resp.ok)
|
|
66
|
-
throw new Error(resp.error);
|
|
67
|
-
return emit(f, `notified ${line}`, { ok: true, line, id: null, messageId: null });
|
|
68
|
-
}
|
|
69
|
-
const messageId = await chatStationOf(line).send(line, text, richOpts(f));
|
|
70
|
-
const id = logOutbound(f, { kind: 'outbound', line, text, messageId });
|
|
71
|
-
emit(f, `sent ${id} (${messageId}) to ${line}`, { ok: true, line, id, messageId });
|
|
72
|
-
}
|
|
73
|
-
export async function cmdReply(p, f) {
|
|
74
|
-
need(p, 2, 'metro reply <line> <message_id> <text> [--image=… --document=… --voice=… --buttons=…]');
|
|
75
|
-
loadMetroEnv();
|
|
76
|
-
const [to, replyToArg] = p, text = await resolveText(p, 2), line = asLine(to);
|
|
77
|
-
const replyTo = resolvePlatformId(replyToArg);
|
|
78
|
-
const messageId = await chatStationOf(line).send(line, text, { ...richOpts(f), replyTo });
|
|
79
|
-
const id = logOutbound(f, { kind: 'outbound', line, text, messageId, replyTo: replyToArg, to: lookupEntry(replyToArg)?.from });
|
|
80
|
-
emit(f, `replied ${id} (${messageId}) to ${line}#${replyTo}`, { ok: true, line, id, replyTo: replyToArg, messageId });
|
|
81
|
-
}
|
|
82
|
-
export async function cmdEdit(p, f) {
|
|
83
|
-
need(p, 2, 'metro edit <line> <message_id> <text> [--buttons=<json>]');
|
|
84
|
-
loadMetroEnv();
|
|
85
|
-
const [to, msgArg] = p, text = await resolveText(p, 2), line = asLine(to);
|
|
86
|
-
const platformId = resolvePlatformId(msgArg);
|
|
87
|
-
const buttons = parseButtons(f);
|
|
88
|
-
await chatStationOf(line).edit(line, platformId, text, buttons ? { buttons } : undefined);
|
|
89
|
-
/** Carry forward the original recipient if we have a row for this message. */
|
|
90
|
-
const id = logOutbound(f, { kind: 'edit', line, text, messageId: platformId, replyTo: msgArg, to: lookupEntry(msgArg)?.to });
|
|
91
|
-
emit(f, `edited ${line}#${platformId} (${id})`, { ok: true, line, id, messageId: platformId });
|
|
92
|
-
}
|
|
93
|
-
export async function cmdReact(p, f) {
|
|
94
|
-
need(p, 2, 'metro react <line> <message_id> <emoji> (empty emoji clears)');
|
|
95
|
-
loadMetroEnv();
|
|
96
|
-
const [to, msgArg, emoji = ''] = p, line = asLine(to);
|
|
97
|
-
const platformId = resolvePlatformId(msgArg);
|
|
98
|
-
await chatStationOf(line).react(line, platformId, emoji);
|
|
99
|
-
const id = logOutbound(f, { kind: 'react', line, messageId: platformId, emoji, to: lookupEntry(msgArg)?.from });
|
|
100
|
-
const human = emoji ? `reacted ${emoji} on ${line}#${platformId}` : `cleared reaction on ${line}#${platformId}`;
|
|
101
|
-
emit(f, human, { ok: true, line, id, messageId: platformId, emoji });
|
|
102
|
-
}
|
|
103
|
-
export async function cmdDownload(p, f) {
|
|
104
|
-
need(p, 2, 'metro download <line> <message_id> [--out=<dir>]');
|
|
105
|
-
loadMetroEnv();
|
|
106
|
-
const [to, msgArg] = p, line = asLine(to);
|
|
107
|
-
const messageId = resolvePlatformId(msgArg);
|
|
108
|
-
const outDir = typeof f.out === 'string' ? f.out : join(tmpdir(), 'metro-downloads');
|
|
109
|
-
mkdirSync(outDir, { recursive: true });
|
|
110
|
-
/** Telegram has no get-message-by-id REST endpoint — daemon holds the in-memory snapshot. */
|
|
111
|
-
let files;
|
|
112
|
-
if (Line.station(line) === 'telegram') {
|
|
113
|
-
const resp = await ipcCall({ op: 'download', line, messageId, outDir });
|
|
114
|
-
if (!resp.ok)
|
|
115
|
-
throw new Error(resp.error);
|
|
116
|
-
files = 'files' in resp ? resp.files : [];
|
|
117
|
-
}
|
|
118
|
-
else {
|
|
119
|
-
files = await chatStationOf(line).download(line, messageId, outDir);
|
|
120
|
-
}
|
|
121
|
-
if (isJson(f))
|
|
122
|
-
return writeJson({ ok: true, line, files });
|
|
123
|
-
if (!files.length)
|
|
124
|
-
process.stdout.write(`(no image attachments on ${line}#${messageId})\n`);
|
|
125
|
-
for (const file of files)
|
|
126
|
-
process.stdout.write(file.path + '\n');
|
|
127
|
-
}
|
|
128
|
-
export async function cmdFetch(p, f) {
|
|
129
|
-
need(p, 1, 'metro fetch <line> [--limit=N]');
|
|
130
|
-
loadMetroEnv();
|
|
131
|
-
const line = asLine(p[0]);
|
|
132
|
-
const messages = await chatStationOf(line).fetch(line, Number(flagOne(f, 'limit')) || 20);
|
|
133
|
-
if (isJson(f))
|
|
134
|
-
return writeJson({ ok: true, line, messages });
|
|
135
|
-
if (!messages.length)
|
|
136
|
-
process.stdout.write(`(no messages on ${line})\n`);
|
|
137
|
-
for (const m of messages)
|
|
138
|
-
process.stdout.write(`${m.timestamp} ${m.author}: ${m.text}\n`);
|
|
139
|
-
}
|
package/dist/cli/config.js
DELETED
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
/** Setup / doctor / update — config-side commands consumed by cli.ts. */
|
|
2
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import pkg from '../../package.json' with { type: 'json' };
|
|
6
|
-
import { DiscordStation } from '../stations/discord.js';
|
|
7
|
-
import { TelegramStation } from '../stations/telegram.js';
|
|
8
|
-
import { errMsg } from '../log.js';
|
|
9
|
-
import { CONFIG_ENV_FILE, configuredPlatforms, loadMetroEnv, readDotenv, STATE_DIR, writeDotenv, } from '../paths.js';
|
|
10
|
-
import { emit, exitErr, isJson, writeJson } from './util.js';
|
|
11
|
-
import { cmdSetupSkill, skillStatus } from './skill.js';
|
|
12
|
-
const TOKEN_KEYS = { telegram: 'TELEGRAM_BOT_TOKEN', discord: 'DISCORD_BOT_TOKEN' };
|
|
13
|
-
const stationFor = (p) => p === 'telegram' ? new TelegramStation() : new DiscordStation();
|
|
14
|
-
const maskToken = (t) => !t ? '' : t.length <= 8 ? '••••' : `${t.slice(0, 6)}…${t.slice(-2)}`;
|
|
15
|
-
/** Apply token across CONFIG_ENV_FILE (always set/cleared) AND cwd/.env (only if it exists). */
|
|
16
|
-
function applyToken(key, value) {
|
|
17
|
-
const out = [];
|
|
18
|
-
for (const path of [CONFIG_ENV_FILE, join(process.cwd(), '.env')]) {
|
|
19
|
-
if (path !== CONFIG_ENV_FILE && !existsSync(path))
|
|
20
|
-
continue;
|
|
21
|
-
const env = readDotenv(path);
|
|
22
|
-
if (value === null) {
|
|
23
|
-
if (!(key in env))
|
|
24
|
-
continue;
|
|
25
|
-
delete env[key];
|
|
26
|
-
}
|
|
27
|
-
else
|
|
28
|
-
env[key] = value;
|
|
29
|
-
writeDotenv(path, env);
|
|
30
|
-
out.push(path);
|
|
31
|
-
}
|
|
32
|
-
return out;
|
|
33
|
-
}
|
|
34
|
-
async function cmdSetupStatus(f) {
|
|
35
|
-
loadMetroEnv();
|
|
36
|
-
const tg = process.env.TELEGRAM_BOT_TOKEN ?? '', dc = process.env.DISCORD_BOT_TOKEN ?? '';
|
|
37
|
-
if (isJson(f))
|
|
38
|
-
return writeJson({
|
|
39
|
-
version: pkg.version, config_env_file: CONFIG_ENV_FILE,
|
|
40
|
-
tokens: { telegram: { set: !!tg, masked: maskToken(tg) }, discord: { set: !!dc, masked: maskToken(dc) } },
|
|
41
|
-
});
|
|
42
|
-
const cfgState = existsSync(CONFIG_ENV_FILE) ? '' : ' (not yet written)';
|
|
43
|
-
const getStarted = !tg && !dc
|
|
44
|
-
? 'Get started:\n 1. metro setup telegram <token> # https://t.me/BotFather'
|
|
45
|
-
+ '\n metro setup discord <token> # https://discord.com/developers/applications'
|
|
46
|
-
+ '\n 2. metro doctor\n 3. metro\n'
|
|
47
|
-
: 'Run `metro` to start the dispatcher, or `metro doctor` to verify.\n';
|
|
48
|
-
process.stdout.write(`metro ${pkg.version}\n\nconfig: ${CONFIG_ENV_FILE}${cfgState}\n\n`
|
|
49
|
-
+ ` TELEGRAM_BOT_TOKEN ${tg ? `set (${maskToken(tg)})` : 'not set'}\n`
|
|
50
|
-
+ ` DISCORD_BOT_TOKEN ${dc ? `set (${maskToken(dc)})` : 'not set'}\n\n${getStarted}`);
|
|
51
|
-
}
|
|
52
|
-
export async function cmdSetup(p, f) {
|
|
53
|
-
const [sub, value] = p;
|
|
54
|
-
if (!sub)
|
|
55
|
-
return cmdSetupStatus(f);
|
|
56
|
-
if (sub === 'skill')
|
|
57
|
-
return cmdSetupSkill(p.slice(1), f);
|
|
58
|
-
if (sub === 'telegram' || sub === 'discord') {
|
|
59
|
-
if (!value)
|
|
60
|
-
throw new Error(`metro setup ${sub} <token> — token is required`);
|
|
61
|
-
const trimmed = value.trim();
|
|
62
|
-
let identity;
|
|
63
|
-
if (!f['no-validate']) {
|
|
64
|
-
process.env[TOKEN_KEYS[sub]] = trimmed;
|
|
65
|
-
try {
|
|
66
|
-
const me = await stationFor(sub).getMe();
|
|
67
|
-
identity = sub === 'telegram' ? `@${me.username}` : me.username;
|
|
68
|
-
}
|
|
69
|
-
catch (err) {
|
|
70
|
-
delete process.env[TOKEN_KEYS[sub]];
|
|
71
|
-
throw exitErr(`token rejected by ${sub}: ${errMsg(err)} (use --no-validate to save anyway)`, 3);
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
const paths = applyToken(TOKEN_KEYS[sub], trimmed);
|
|
75
|
-
const verified = identity ? ` (verified as ${identity})` : '';
|
|
76
|
-
emit(f, `saved ${TOKEN_KEYS[sub]}${verified} to ${paths.join(', ')}\nrestart metro for the new token to take effect.`, { ok: true, saved: TOKEN_KEYS[sub], paths, verified_as: identity ?? null });
|
|
77
|
-
return;
|
|
78
|
-
}
|
|
79
|
-
if (sub === 'clear') {
|
|
80
|
-
const target = value ?? 'all';
|
|
81
|
-
if (target !== 'all' && target !== 'telegram' && target !== 'discord')
|
|
82
|
-
throw new Error(`metro setup clear <telegram|discord|all> — got '${target}'`);
|
|
83
|
-
const keys = target === 'all' ? Object.values(TOKEN_KEYS) : [TOKEN_KEYS[target]];
|
|
84
|
-
const paths = new Set();
|
|
85
|
-
for (const k of keys)
|
|
86
|
-
for (const path of applyToken(k, null))
|
|
87
|
-
paths.add(path);
|
|
88
|
-
const label = target === 'all' ? 'all metro tokens' : TOKEN_KEYS[target];
|
|
89
|
-
emit(f, `cleared ${label} from ${[...paths].join(', ') || '(no files had it)'}\nrestart metro for changes to take effect.`, { ok: true, cleared: target, paths: [...paths] });
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
throw new Error(`unknown setup subcommand '${sub}' (try: telegram, discord, clear, skill)`);
|
|
93
|
-
}
|
|
94
|
-
function tokenSource(key) {
|
|
95
|
-
const val = process.env[key];
|
|
96
|
-
if (!val)
|
|
97
|
-
return '';
|
|
98
|
-
for (const path of [join(process.cwd(), '.env'), CONFIG_ENV_FILE]) {
|
|
99
|
-
if (existsSync(path) && readDotenv(path)[key] === val)
|
|
100
|
-
return path;
|
|
101
|
-
}
|
|
102
|
-
return 'process env';
|
|
103
|
-
}
|
|
104
|
-
export async function cmdDoctor(_, f) {
|
|
105
|
-
loadMetroEnv();
|
|
106
|
-
const cfg = configuredPlatforms();
|
|
107
|
-
const checks = [];
|
|
108
|
-
const sources = [];
|
|
109
|
-
for (const p of ['telegram', 'discord']) {
|
|
110
|
-
if (!cfg[p]) {
|
|
111
|
-
checks.push({ name: p, ok: null, detail: 'not configured' });
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
sources.push(`${p}←${tokenSource(TOKEN_KEYS[p])}`);
|
|
115
|
-
try {
|
|
116
|
-
const me = await stationFor(p).getMe();
|
|
117
|
-
checks.push({ name: p, ok: true, detail: `getMe → ${p === 'telegram' ? '@' : ''}${me.username}` });
|
|
118
|
-
}
|
|
119
|
-
catch (err) {
|
|
120
|
-
checks.push({ name: p, ok: false, detail: errMsg(err) });
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
checks.unshift({
|
|
124
|
-
name: 'tokens', ok: cfg.telegram || cfg.discord,
|
|
125
|
-
detail: sources.length ? sources.join(', ') : 'no platform configured — run `metro setup telegram|discord <token>`',
|
|
126
|
-
});
|
|
127
|
-
const lockFile = join(STATE_DIR, '.tail-lock');
|
|
128
|
-
if (!existsSync(lockFile))
|
|
129
|
-
checks.push({ name: 'dispatcher', ok: null, detail: 'not running' });
|
|
130
|
-
else
|
|
131
|
-
try {
|
|
132
|
-
const pid = Number(readFileSync(lockFile, 'utf8').trim());
|
|
133
|
-
if (!Number.isInteger(pid) || pid <= 0)
|
|
134
|
-
throw new Error('invalid pid');
|
|
135
|
-
process.kill(pid, 0);
|
|
136
|
-
checks.push({ name: 'dispatcher', ok: true, detail: `running (pid ${pid})` });
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
checks.push({ name: 'dispatcher', ok: null, detail: 'stale lockfile (auto-reclaims)' });
|
|
140
|
-
}
|
|
141
|
-
checks.push({
|
|
142
|
-
name: 'codex-rc', ok: null,
|
|
143
|
-
detail: process.env.METRO_CODEX_RC
|
|
144
|
-
? `push enabled → ${process.env.METRO_CODEX_RC}`
|
|
145
|
-
: 'not configured (set METRO_CODEX_RC=ws://… to enable Codex push)',
|
|
146
|
-
});
|
|
147
|
-
const sk = skillStatus();
|
|
148
|
-
const installed = Object.entries(sk).filter(([, ok]) => ok).map(([r]) => r);
|
|
149
|
-
checks.push({
|
|
150
|
-
name: 'skill', ok: installed.length ? true : null,
|
|
151
|
-
detail: installed.length ? `installed for ${installed.join(', ')}` : 'not installed (run `metro setup skill`)',
|
|
152
|
-
});
|
|
153
|
-
if (isJson(f))
|
|
154
|
-
return writeJson({ checks });
|
|
155
|
-
process.stdout.write('metro doctor\n\n');
|
|
156
|
-
for (const c of checks) {
|
|
157
|
-
const mark = c.ok === true ? '✓' : c.ok === false ? '✗' : '–';
|
|
158
|
-
process.stdout.write(` ${mark} ${c.name.padEnd(15)} ${c.detail}\n`);
|
|
159
|
-
}
|
|
160
|
-
process.stdout.write('\n');
|
|
161
|
-
if (checks.some(c => c.ok === false))
|
|
162
|
-
throw exitErr('one or more checks failed', 3);
|
|
163
|
-
}
|
|
164
|
-
export async function cmdUpdate(_, f) {
|
|
165
|
-
const tag = pkg.version.includes('-') ? 'beta' : 'latest';
|
|
166
|
-
const res = await fetch('https://registry.npmjs.org/@stage-labs/metro', { signal: AbortSignal.timeout(15_000) });
|
|
167
|
-
if (!res.ok)
|
|
168
|
-
throw new Error(`npm registry: ${res.status}`);
|
|
169
|
-
const latest = (await res.json())['dist-tags']?.[tag];
|
|
170
|
-
if (!latest)
|
|
171
|
-
throw new Error(`no '${tag}' dist-tag for @stage-labs/metro`);
|
|
172
|
-
if (latest === pkg.version) {
|
|
173
|
-
return emit(f, `already on ${pkg.version} (latest ${tag})`, { ok: true, current: pkg.version, latest, upgraded: false });
|
|
174
|
-
}
|
|
175
|
-
const argv1 = process.argv[1] ?? '', spec = `@stage-labs/metro@${tag}`;
|
|
176
|
-
const argv = argv1.includes('/.bun/') || argv1.includes('\\bun\\') ? ['bun', 'add', '-g', spec]
|
|
177
|
-
: argv1.includes('/pnpm/') || argv1.includes('\\pnpm\\') ? ['pnpm', 'add', '-g', spec]
|
|
178
|
-
: ['npm', 'install', '-g', spec];
|
|
179
|
-
emit(f, `metro ${pkg.version} → ${latest}\n$ ${argv.join(' ')}`, { ok: true, current: pkg.version, latest, command: argv.join(' ') });
|
|
180
|
-
await new Promise((resolve, reject) => {
|
|
181
|
-
const child = spawn(argv[0], argv.slice(1), { stdio: isJson(f) ? 'ignore' : 'inherit' });
|
|
182
|
-
child.on('exit', code => code === 0 ? resolve() : reject(new Error(`${argv[0]} exited ${code}`)));
|
|
183
|
-
child.on('error', reject);
|
|
184
|
-
});
|
|
185
|
-
}
|
package/dist/cli/index.js
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/** Metro CLI entry: parses argv, dispatches to subcommands, owns action + info commands. */
|
|
3
|
-
import pkg from '../../package.json' with { type: 'json' };
|
|
4
|
-
import { errMsg } from '../log.js';
|
|
5
|
-
import { listLines } from '../cache.js';
|
|
6
|
-
import { fmtCapabilities, listStations } from '../stations/index.js';
|
|
7
|
-
import { loadMetroEnv } from '../paths.js';
|
|
8
|
-
import { readHistory } from '../history.js';
|
|
9
|
-
import { cmdDoctor, cmdSetup, cmdUpdate } from './config.js';
|
|
10
|
-
import { cmdDownload, cmdEdit, cmdFetch, cmdReact, cmdReply, cmdSend, } from './actions.js';
|
|
11
|
-
import { flagOne, isJson, parseArgs, writeJson, } from './util.js';
|
|
12
|
-
const USAGE = `metro — Telegram + Discord stream for your Claude Code / Codex agent
|
|
13
|
-
|
|
14
|
-
Usage:
|
|
15
|
-
metro Run the dispatcher (emits JSON events on stdout).
|
|
16
|
-
metro setup [telegram|discord <token>] Save token, or show status with no args.
|
|
17
|
-
metro setup clear [telegram|discord|all] Remove tokens.
|
|
18
|
-
metro setup skill [clear] Install the metro skill into ~/.claude / ~/.codex.
|
|
19
|
-
metro doctor Health check.
|
|
20
|
-
metro stations List stations + capabilities.
|
|
21
|
-
metro lines List recently-seen conversations.
|
|
22
|
-
metro send <line> <text> [--image=<path>]… [--document=<path>]… [--voice=<path>] [--buttons=<json>]
|
|
23
|
-
Post a fresh message; repeat --image/--document for multi-file albums.
|
|
24
|
-
metro reply <line> <message_id> <text> [--image=… --document=… --voice=… --buttons=…]
|
|
25
|
-
Threaded reply (same flags as send).
|
|
26
|
-
metro edit <line> <message_id> <text> [--buttons=<json>]
|
|
27
|
-
Edit a previously-sent message (text + buttons).
|
|
28
|
-
metro react <line> <message_id> <emoji> Set or clear ('') a reaction.
|
|
29
|
-
metro download <line> <message_id> [--out=<dir>]
|
|
30
|
-
Download image attachments to disk.
|
|
31
|
-
metro fetch <line> [--limit=N] Recent-message lookback (Discord only).
|
|
32
|
-
metro history [--limit=N] [--line=…] [--station=…] [--kind=…] [--from=…] [--text=…] [--since=…]
|
|
33
|
-
Read the universal message log (newest first).
|
|
34
|
-
metro update Upgrade in place.
|
|
35
|
-
metro --version | --help
|
|
36
|
-
|
|
37
|
-
Lines: metro://<station>/<path>. See docs/uri-scheme.md.
|
|
38
|
-
Multi-line --text: pipe on stdin in place of the positional arg.
|
|
39
|
-
Exit codes: 0 success · 1 usage · 2 config · 3 upstream
|
|
40
|
-
`;
|
|
41
|
-
async function cmdStations(_, f) {
|
|
42
|
-
loadMetroEnv();
|
|
43
|
-
const rows = listStations();
|
|
44
|
-
if (isJson(f))
|
|
45
|
-
return writeJson({ stations: rows });
|
|
46
|
-
process.stdout.write('metro stations\n\n');
|
|
47
|
-
for (const s of rows) {
|
|
48
|
-
const mark = s.configured === true ? '✓' : s.configured === false ? '✗' : '·';
|
|
49
|
-
process.stdout.write(` ${mark} ${s.name.padEnd(10)} ${s.kind.padEnd(6)} ${fmtCapabilities(s.capabilities)}\n ${s.detail}\n`);
|
|
50
|
-
}
|
|
51
|
-
process.stdout.write('\n');
|
|
52
|
-
}
|
|
53
|
-
async function cmdLines(_, f) {
|
|
54
|
-
loadMetroEnv();
|
|
55
|
-
const rows = listLines()
|
|
56
|
-
.map(({ line, entry }) => ({ line, name: entry.name ?? null, lastSeenAt: entry.lastSeenAt ?? null }))
|
|
57
|
-
.sort((a, b) => (b.lastSeenAt ?? '').localeCompare(a.lastSeenAt ?? ''));
|
|
58
|
-
if (isJson(f))
|
|
59
|
-
return writeJson({ lines: rows });
|
|
60
|
-
if (!rows.length)
|
|
61
|
-
return void process.stdout.write('metro lines\n\n (none yet — start the dispatcher and send a message)\n\n');
|
|
62
|
-
const widest = Math.max(...rows.map(r => r.line.length));
|
|
63
|
-
process.stdout.write('metro lines\n\n');
|
|
64
|
-
for (const r of rows) {
|
|
65
|
-
const when = r.lastSeenAt ? humanAgo(r.lastSeenAt) : '—';
|
|
66
|
-
const tag = r.name ? ` ${r.name.slice(0, 40)}${r.name.length > 40 ? '…' : ''}` : '';
|
|
67
|
-
process.stdout.write(` ${when.padEnd(10)} ${r.line.padEnd(widest)}${tag}\n`);
|
|
68
|
-
}
|
|
69
|
-
process.stdout.write('\n');
|
|
70
|
-
}
|
|
71
|
-
function humanAgo(iso) {
|
|
72
|
-
const sec = Math.max(0, Math.floor((Date.now() - new Date(iso).getTime()) / 1000));
|
|
73
|
-
if (sec < 60)
|
|
74
|
-
return `${sec}s ago`;
|
|
75
|
-
if (sec < 3600)
|
|
76
|
-
return `${Math.floor(sec / 60)}m ago`;
|
|
77
|
-
if (sec < 86400)
|
|
78
|
-
return `${Math.floor(sec / 3600)}h ago`;
|
|
79
|
-
return `${Math.floor(sec / 86400)}d ago`;
|
|
80
|
-
}
|
|
81
|
-
async function cmdHistory(_, f) {
|
|
82
|
-
loadMetroEnv();
|
|
83
|
-
const since = flagOne(f, 'since');
|
|
84
|
-
const entries = readHistory({
|
|
85
|
-
line: flagOne(f, 'line'),
|
|
86
|
-
station: flagOne(f, 'station'),
|
|
87
|
-
kind: flagOne(f, 'kind'),
|
|
88
|
-
from: flagOne(f, 'from'),
|
|
89
|
-
textContains: flagOne(f, 'text'),
|
|
90
|
-
since: since ? new Date(since) : undefined,
|
|
91
|
-
limit: Number(flagOne(f, 'limit')) || 50,
|
|
92
|
-
});
|
|
93
|
-
if (isJson(f))
|
|
94
|
-
return writeJson({ entries });
|
|
95
|
-
if (!entries.length) {
|
|
96
|
-
process.stdout.write('(no matching history entries)\n');
|
|
97
|
-
return;
|
|
98
|
-
}
|
|
99
|
-
process.stdout.write('time id kind from → to body\n');
|
|
100
|
-
for (const e of entries.reverse()) {
|
|
101
|
-
const ts = e.ts.slice(11, 19);
|
|
102
|
-
const from = pad(fmtActor(e.from, e.fromName), 24);
|
|
103
|
-
const to = pad(fmtActor(e.to), 24);
|
|
104
|
-
const body = e.text ?? (e.emoji ? `[react ${e.emoji}]` : '');
|
|
105
|
-
const text = body.length > 60 ? body.slice(0, 59) + '…' : body;
|
|
106
|
-
process.stdout.write(`${ts} ${e.id.padEnd(12)} ${e.kind.padEnd(12)} ${from} → ${to} ${text}\n`);
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
/** Compact display: fromName if known; else `station:@<id>` (user), `station:bot`, or `station:<id>`. */
|
|
110
|
-
function fmtActor(uri, name) {
|
|
111
|
-
if (name)
|
|
112
|
-
return name;
|
|
113
|
-
const m = uri.match(/^metro:\/\/([^/]+)(?:\/(?:(user|bot)\/)?(.*))?$/);
|
|
114
|
-
if (!m)
|
|
115
|
-
return uri;
|
|
116
|
-
const [, station, kind, rest] = m;
|
|
117
|
-
if (kind === 'bot')
|
|
118
|
-
return `${station}:bot`;
|
|
119
|
-
if (kind === 'user')
|
|
120
|
-
return `${station}:@${shortId(rest ?? '')}`;
|
|
121
|
-
return rest ? `${station}:${shortId(rest)}` : station;
|
|
122
|
-
}
|
|
123
|
-
const shortId = (s) => s.length <= 12 ? s : `${s.slice(0, 5)}…${s.slice(-4)}`;
|
|
124
|
-
const pad = (s, n) => (s.length > n ? `${s.slice(0, n - 1)}…` : s.padEnd(n));
|
|
125
|
-
const COMMANDS = {
|
|
126
|
-
setup: cmdSetup, doctor: cmdDoctor, stations: cmdStations, lines: cmdLines,
|
|
127
|
-
send: cmdSend, reply: cmdReply, edit: cmdEdit, react: cmdReact,
|
|
128
|
-
download: cmdDownload, fetch: cmdFetch,
|
|
129
|
-
history: cmdHistory, update: cmdUpdate,
|
|
130
|
-
};
|
|
131
|
-
async function main() {
|
|
132
|
-
const cmd = process.argv[2];
|
|
133
|
-
if (cmd === '--version' || cmd === '-v')
|
|
134
|
-
return void process.stdout.write(`${pkg.version}\n`);
|
|
135
|
-
if (cmd === '--help' || cmd === '-h')
|
|
136
|
-
return void process.stdout.write(USAGE);
|
|
137
|
-
if (!cmd) {
|
|
138
|
-
await import('../dispatcher.js');
|
|
139
|
-
return;
|
|
140
|
-
}
|
|
141
|
-
const handler = COMMANDS[cmd];
|
|
142
|
-
if (!handler) {
|
|
143
|
-
process.stderr.write(`unknown command '${cmd}'\n\n${USAGE}`);
|
|
144
|
-
process.exit(1);
|
|
145
|
-
}
|
|
146
|
-
const { positional, flags } = parseArgs(process.argv.slice(3));
|
|
147
|
-
try {
|
|
148
|
-
await handler(positional, flags);
|
|
149
|
-
}
|
|
150
|
-
catch (err) {
|
|
151
|
-
const code = err.code;
|
|
152
|
-
if (isJson(flags))
|
|
153
|
-
writeJson({ ok: false, error: errMsg(err), code: code ?? 1 });
|
|
154
|
-
else
|
|
155
|
-
process.stderr.write(`error: ${errMsg(err)}\n`);
|
|
156
|
-
process.exit(typeof code === 'number' ? code : 1);
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
await main();
|
package/dist/cli/skill.js
DELETED
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
/** `metro setup skill` — install the bundled SKILL.md into each detected agent runtime. */
|
|
2
|
-
import { copyFileSync, existsSync, mkdirSync, unlinkSync } from 'node:fs';
|
|
3
|
-
import { homedir } from 'node:os';
|
|
4
|
-
import { dirname, join } from 'node:path';
|
|
5
|
-
import { fileURLToPath } from 'node:url';
|
|
6
|
-
import { errMsg } from '../log.js';
|
|
7
|
-
import { emit, exitErr } from './util.js';
|
|
8
|
-
const RUNTIME_DIRS = {
|
|
9
|
-
'claude-code': join(homedir(), '.claude', 'skills', 'metro'),
|
|
10
|
-
codex: join(homedir(), '.codex', 'skills', 'metro'),
|
|
11
|
-
};
|
|
12
|
-
/** dist/cli/skill.js → <package-root>/skills/metro/SKILL.md */
|
|
13
|
-
const bundledPath = () => join(dirname(fileURLToPath(import.meta.url)), '..', '..', 'skills', 'metro', 'SKILL.md');
|
|
14
|
-
const dest = (r) => join(RUNTIME_DIRS[r], 'SKILL.md');
|
|
15
|
-
export const skillStatus = () => ({
|
|
16
|
-
'claude-code': existsSync(dest('claude-code')),
|
|
17
|
-
codex: existsSync(dest('codex')),
|
|
18
|
-
});
|
|
19
|
-
export async function cmdSetupSkill(p, f) {
|
|
20
|
-
const [sub] = p;
|
|
21
|
-
if (sub === 'clear')
|
|
22
|
-
return clear(f);
|
|
23
|
-
if (sub && sub !== 'install')
|
|
24
|
-
throw exitErr(`unknown skill subcommand '${sub}' (try: install, clear)`, 1);
|
|
25
|
-
return install(f);
|
|
26
|
-
}
|
|
27
|
-
function install(f) {
|
|
28
|
-
const src = bundledPath();
|
|
29
|
-
if (!existsSync(src))
|
|
30
|
-
throw exitErr(`bundled SKILL.md missing at ${src} (broken install?)`, 2);
|
|
31
|
-
const installed = [];
|
|
32
|
-
for (const r of Object.keys(RUNTIME_DIRS)) {
|
|
33
|
-
if (!existsSync(join(homedir(), r === 'claude-code' ? '.claude' : '.codex')))
|
|
34
|
-
continue;
|
|
35
|
-
try {
|
|
36
|
-
mkdirSync(RUNTIME_DIRS[r], { recursive: true });
|
|
37
|
-
copyFileSync(src, dest(r));
|
|
38
|
-
installed.push(dest(r));
|
|
39
|
-
}
|
|
40
|
-
catch (err) {
|
|
41
|
-
throw exitErr(`failed to install skill for ${r}: ${errMsg(err)}`, 2);
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
if (!installed.length) {
|
|
45
|
-
throw exitErr('no agent runtime detected (~/.claude or ~/.codex). Install one and rerun.', 2);
|
|
46
|
-
}
|
|
47
|
-
emit(f, `installed metro skill → ${installed.join(', ')}`, { ok: true, installed });
|
|
48
|
-
}
|
|
49
|
-
function clear(f) {
|
|
50
|
-
const removed = [];
|
|
51
|
-
for (const r of Object.keys(RUNTIME_DIRS)) {
|
|
52
|
-
const path = dest(r);
|
|
53
|
-
if (existsSync(path)) {
|
|
54
|
-
try {
|
|
55
|
-
unlinkSync(path);
|
|
56
|
-
removed.push(path);
|
|
57
|
-
}
|
|
58
|
-
catch { /* ignore */ }
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
emit(f, removed.length ? `removed metro skill from ${removed.join(', ')}` : 'no installed skill found', { ok: true, removed });
|
|
62
|
-
}
|
package/dist/cli/util.js
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
/** Shared CLI primitives consumed by index.ts + config.ts. */
|
|
2
|
-
export const exitErr = (msg, code) => Object.assign(new Error(msg), { code });
|
|
3
|
-
export const isJson = (f) => f.json === true;
|
|
4
|
-
export const writeJson = (obj) => void process.stdout.write(JSON.stringify(obj) + '\n');
|
|
5
|
-
export const emit = (f, human, structured) => isJson(f) ? writeJson(structured) : void process.stdout.write(human + '\n');
|
|
6
|
-
export const need = (positional, min, usage) => {
|
|
7
|
-
if (positional.length < min)
|
|
8
|
-
throw exitErr(`usage: ${usage}`, 1);
|
|
9
|
-
};
|
|
10
|
-
/** Return the last string value for `key` (or undefined). */
|
|
11
|
-
export function flagOne(f, key) {
|
|
12
|
-
const v = f[key];
|
|
13
|
-
if (typeof v === 'string')
|
|
14
|
-
return v;
|
|
15
|
-
if (Array.isArray(v) && v.length)
|
|
16
|
-
return v[v.length - 1];
|
|
17
|
-
return undefined;
|
|
18
|
-
}
|
|
19
|
-
/** Return all string values for `key`, also splitting comma-separated entries. */
|
|
20
|
-
export function flagList(f, key) {
|
|
21
|
-
const v = f[key];
|
|
22
|
-
const raw = typeof v === 'string' ? [v] : Array.isArray(v) ? v : [];
|
|
23
|
-
return raw.flatMap(s => s.split(',').map(p => p.trim()).filter(Boolean));
|
|
24
|
-
}
|
|
25
|
-
export function parseArgs(argv) {
|
|
26
|
-
const positional = [], flags = {};
|
|
27
|
-
const add = (k, val) => {
|
|
28
|
-
const cur = flags[k];
|
|
29
|
-
if (cur === undefined) {
|
|
30
|
-
flags[k] = val;
|
|
31
|
-
return;
|
|
32
|
-
}
|
|
33
|
-
if (typeof val === 'boolean') {
|
|
34
|
-
flags[k] = val;
|
|
35
|
-
return;
|
|
36
|
-
}
|
|
37
|
-
flags[k] = Array.isArray(cur) ? [...cur, val] : [cur, val];
|
|
38
|
-
};
|
|
39
|
-
for (let i = 0; i < argv.length; i++) {
|
|
40
|
-
const a = argv[i];
|
|
41
|
-
if (!a.startsWith('--')) {
|
|
42
|
-
positional.push(a);
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
const eq = a.indexOf('=');
|
|
46
|
-
if (eq !== -1) {
|
|
47
|
-
add(a.slice(2, eq), a.slice(eq + 1));
|
|
48
|
-
continue;
|
|
49
|
-
}
|
|
50
|
-
const next = argv[i + 1];
|
|
51
|
-
if (next !== undefined && !next.startsWith('--')) {
|
|
52
|
-
add(a.slice(2), next);
|
|
53
|
-
i++;
|
|
54
|
-
}
|
|
55
|
-
else
|
|
56
|
-
add(a.slice(2), true);
|
|
57
|
-
}
|
|
58
|
-
return { positional, flags };
|
|
59
|
-
}
|
|
60
|
-
export async function resolveText(positional, from) {
|
|
61
|
-
if (positional.length > from)
|
|
62
|
-
return positional.slice(from).join(' ');
|
|
63
|
-
if (process.stdin.isTTY)
|
|
64
|
-
throw exitErr('text is required (or pipe text on stdin)', 1);
|
|
65
|
-
const chunks = [];
|
|
66
|
-
for await (const c of process.stdin)
|
|
67
|
-
chunks.push(c);
|
|
68
|
-
const stdin = Buffer.concat(chunks).toString('utf8').replace(/\n$/, '');
|
|
69
|
-
if (!stdin)
|
|
70
|
-
throw exitErr('text is required (or pipe text on stdin)', 1);
|
|
71
|
-
return stdin;
|
|
72
|
-
}
|