@stage-labs/metro 0.1.0-beta.201 → 0.1.0-beta.202

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.
Files changed (163) hide show
  1. package/dist/claude.js +4 -15
  2. package/dist/cli.js +5 -3
  3. package/dist/hold.js +1 -2
  4. package/dist/runtime-install.js +4 -5
  5. package/dist/runtime.js +1 -4
  6. package/dist/serve.js +4 -5
  7. package/dist/service.js +82 -21
  8. package/dist/update.js +1 -1
  9. package/package.json +2 -2
  10. package/runtime/marketplace/plugin/.claude-plugin/plugin.json +1 -1
  11. package/runtime/node_modules/@metro-labs/core/src/events.ts +4 -2
  12. package/runtime/node_modules/@metro-labs/core/src/lines.ts +12 -25
  13. package/runtime/node_modules/@metro-labs/core/src/stations/account-store.ts +45 -22
  14. package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +0 -1
  15. package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +8 -80
  16. package/runtime/node_modules/@metro-labs/core/src/stations/station-runtime.ts +3 -5
  17. package/runtime/node_modules/@metro-labs/core/src/stations/train-events.ts +4 -9
  18. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +9 -39
  19. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/job-env.ts +1 -1
  20. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/provision.ts +9 -28
  21. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedule-detail.ts +0 -1
  22. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules-api.ts +3 -3
  23. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +8 -116
  24. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/unit-files.ts +11 -35
  25. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +7 -9
  26. package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +1 -1
  27. package/runtime/node_modules/@metro-labs/daemon/src/agents/account-lookups.ts +1 -1
  28. package/runtime/node_modules/@metro-labs/daemon/src/agents/accounts-api.ts +1 -1
  29. package/runtime/node_modules/@metro-labs/daemon/src/agents/allowlist.ts +1 -1
  30. package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +3 -3
  31. package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +2 -2
  32. package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +3 -29
  33. package/runtime/node_modules/@metro-labs/daemon/src/approvals/pending.ts +5 -8
  34. package/runtime/node_modules/@metro-labs/daemon/src/approvals/preview.ts +1 -1
  35. package/runtime/node_modules/@metro-labs/daemon/src/boot/boot.ts +5 -8
  36. package/runtime/node_modules/@metro-labs/daemon/src/boot/connect-hint.ts +3 -6
  37. package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +25 -3
  38. package/runtime/node_modules/@metro-labs/daemon/src/channels/media-note.ts +2 -3
  39. package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +2 -2
  40. package/runtime/node_modules/@metro-labs/daemon/src/claude/login.ts +1 -1
  41. package/runtime/node_modules/@metro-labs/daemon/src/claude/memory.ts +6 -56
  42. package/runtime/node_modules/@metro-labs/daemon/src/claude/plugin-install.ts +3 -3
  43. package/runtime/node_modules/@metro-labs/daemon/src/claude/session-files.ts +1 -1
  44. package/runtime/node_modules/@metro-labs/daemon/src/claude/session.ts +7 -6
  45. package/runtime/node_modules/@metro-labs/daemon/src/claude/settings.ts +1 -1
  46. package/runtime/node_modules/@metro-labs/daemon/src/claude/setup.ts +7 -7
  47. package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +4 -4
  48. package/runtime/node_modules/@metro-labs/daemon/src/claude/version.ts +1 -1
  49. package/runtime/node_modules/@metro-labs/daemon/src/connectors/api.ts +2 -4
  50. package/runtime/node_modules/@metro-labs/daemon/src/connectors/config.ts +19 -22
  51. package/runtime/node_modules/@metro-labs/daemon/src/connectors/gates.ts +1 -2
  52. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-discovery.ts +6 -8
  53. package/runtime/node_modules/@metro-labs/daemon/src/connectors/oauth-routes.ts +3 -6
  54. package/runtime/node_modules/@metro-labs/daemon/src/connectors/plugin-sync.ts +1 -1
  55. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay.ts +2 -3
  56. package/runtime/node_modules/@metro-labs/daemon/src/connectors/store.ts +28 -1
  57. package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +1 -1
  58. package/runtime/node_modules/@metro-labs/daemon/src/gateway/bedrock.ts +4 -4
  59. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-auth.ts +1 -1
  60. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-device.ts +1 -1
  61. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-stream.ts +1 -1
  62. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex-translate.ts +3 -2
  63. package/runtime/node_modules/@metro-labs/daemon/src/gateway/codex.ts +1 -1
  64. package/runtime/node_modules/@metro-labs/daemon/src/gateway/effort.ts +2 -2
  65. package/runtime/node_modules/@metro-labs/daemon/src/gateway/eventstream.ts +1 -1
  66. package/runtime/node_modules/@metro-labs/daemon/src/gateway/forward.ts +3 -3
  67. package/runtime/node_modules/@metro-labs/daemon/src/gateway/frames.ts +1 -1
  68. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gateway.ts +3 -3
  69. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-auth.ts +6 -6
  70. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-client.ts +7 -7
  71. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-stream.ts +1 -1
  72. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini-translate.ts +3 -2
  73. package/runtime/node_modules/@metro-labs/daemon/src/gateway/gemini.ts +1 -1
  74. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-config.ts +10 -31
  75. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-signin.ts +1 -1
  76. package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-store.ts +1 -1
  77. package/runtime/node_modules/@metro-labs/daemon/src/gateway/openrouter.ts +2 -2
  78. package/runtime/node_modules/@metro-labs/daemon/src/gateway/provider-models.ts +3 -3
  79. package/runtime/node_modules/@metro-labs/daemon/src/gateway/text.ts +1 -2
  80. package/runtime/node_modules/@metro-labs/daemon/src/gateway/usage.ts +2 -2
  81. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +5 -6
  82. package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-snapshot.ts +3 -3
  83. package/runtime/node_modules/@metro-labs/daemon/src/mcp/profile-lookup.ts +2 -2
  84. package/runtime/node_modules/@metro-labs/daemon/src/mcp/read-tool.ts +2 -2
  85. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +2 -2
  86. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +37 -0
  87. package/runtime/node_modules/@metro-labs/daemon/src/net/tunnel.ts +2 -2
  88. package/runtime/node_modules/@metro-labs/daemon/src/policy/policy.ts +1 -1
  89. package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +1 -4
  90. package/runtime/node_modules/@metro-labs/daemon/src/routes/session-apis.ts +3 -8
  91. package/runtime/node_modules/@metro-labs/daemon/src/server/machine.ts +2 -2
  92. package/runtime/node_modules/@metro-labs/daemon/src/server/update.ts +2 -2
  93. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-session.ts +2 -2
  94. package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-xmtp.ts +6 -7
  95. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor-io.ts +1 -1
  96. package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +2 -2
  97. package/runtime/node_modules/@metro-labs/daemon/src/terminal/api.ts +2 -2
  98. package/runtime/node_modules/@metro-labs/daemon/src/terminal/socket.ts +1 -1
  99. package/runtime/node_modules/@metro-labs/daemon/src/vault/firewall.ts +5 -44
  100. package/runtime/node_modules/@metro-labs/daemon/src/vault/index.ts +4 -4
  101. package/runtime/node_modules/@metro-labs/daemon/src/vault/proxy.ts +6 -7
  102. package/runtime/node_modules/@metro-labs/daemon/src/vault/store.ts +12 -13
  103. package/runtime/node_modules/@metro-labs/daemon/src/vault/trust.ts +5 -22
  104. package/runtime/node_modules/@metro-labs/discord-bot/src/accounts.ts +1 -19
  105. package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +7 -35
  106. package/runtime/node_modules/@metro-labs/discord-bot/src/format.ts +5 -10
  107. package/runtime/node_modules/@metro-labs/discord-bot/src/group-actions.ts +1 -1
  108. package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +1 -2
  109. package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +1 -1
  110. package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +11 -12
  111. package/runtime/node_modules/@metro-labs/http/src/api-http.ts +2 -5
  112. package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +0 -1
  113. package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +1 -15
  114. package/runtime/node_modules/@metro-labs/outlook/src/actions.ts +0 -6
  115. package/runtime/node_modules/@metro-labs/outlook/src/format.ts +0 -1
  116. package/runtime/node_modules/@metro-labs/outlook/src/state.ts +1 -1
  117. package/runtime/node_modules/@metro-labs/telegram/src/accounts.ts +4 -22
  118. package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +2 -2
  119. package/runtime/node_modules/@metro-labs/telegram/src/format.ts +1 -3
  120. package/runtime/node_modules/@metro-labs/telegram/src/inbound.ts +1 -1
  121. package/runtime/node_modules/@metro-labs/telegram/src/media-actions.ts +1 -2
  122. package/runtime/node_modules/@metro-labs/telegram/src/reactions.ts +1 -1
  123. package/runtime/node_modules/@metro-labs/telegram-bot/src/accounts.ts +4 -37
  124. package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +7 -23
  125. package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +1 -4
  126. package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +4 -9
  127. package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +3 -3
  128. package/runtime/node_modules/@metro-labs/threema/src/accounts.ts +0 -11
  129. package/runtime/node_modules/@metro-labs/threema/src/actions.ts +2 -12
  130. package/runtime/node_modules/@metro-labs/threema/src/files.ts +4 -15
  131. package/runtime/node_modules/@metro-labs/threema/src/format.ts +2 -7
  132. package/runtime/node_modules/@metro-labs/whatsapp/package.json +1 -2
  133. package/runtime/node_modules/@metro-labs/whatsapp/src/accounts.ts +5 -26
  134. package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +5 -6
  135. package/runtime/node_modules/@metro-labs/whatsapp/src/format.ts +1 -3
  136. package/runtime/node_modules/@metro-labs/whatsapp/src/inbound.ts +1 -1
  137. package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +1 -12
  138. package/runtime/node_modules/@metro-labs/xmtp/src/actions-close.ts +1 -1
  139. package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +14 -5
  140. package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +1 -1
  141. package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +31 -78
  142. package/runtime/node_modules/@metro-labs/xmtp/src/emit.ts +3 -4
  143. package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +1 -6
  144. package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +1 -1
  145. package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +1 -1
  146. package/runtime/node_modules/@metro-labs/xmtp/src/tools-handlers.ts +1 -1
  147. package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +2 -2
  148. package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +0 -2
  149. package/runtime/runtime.json +1 -1
  150. package/runtime/node_modules/@metro-labs/core/src/identity.ts +0 -7
  151. package/runtime/node_modules/@metro-labs/daemon/src/approvals/needs.ts +0 -2
  152. package/runtime/node_modules/@metro-labs/daemon/src/channels/dedupe.ts +0 -12
  153. package/runtime/node_modules/@metro-labs/daemon/src/channels/react-note.ts +0 -13
  154. package/runtime/node_modules/@metro-labs/daemon/src/channels/reply-id.ts +0 -1
  155. package/runtime/node_modules/@metro-labs/daemon/src/connectors/relay-target.ts +0 -29
  156. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/api.ts +0 -43
  157. package/runtime/node_modules/@metro-labs/daemon/src/metro-user/move.ts +0 -165
  158. package/runtime/node_modules/@metro-labs/discord-bot/src/wire.ts +0 -1
  159. package/runtime/node_modules/@metro-labs/telegram/src/normalize.ts +0 -22
  160. package/runtime/node_modules/@metro-labs/telegram/src/wire.ts +0 -1
  161. package/runtime/node_modules/@metro-labs/telegram-bot/src/wire.ts +0 -1
  162. package/runtime/node_modules/@metro-labs/whatsapp/src/normalize.ts +0 -22
  163. package/runtime/node_modules/@metro-labs/whatsapp/src/wire.ts +0 -1
@@ -1,5 +1,5 @@
1
1
  import type { WAMessage } from 'baileys';
2
- import { emit } from './wire.js';
2
+ import { emit } from '@metro-labs/core/stations/station-runtime';
3
3
  import { reportAttachment } from '@metro-labs/core/stations/train-events';
4
4
  import { lineOf } from './accounts.js';
5
5
  import {
@@ -15,10 +15,6 @@ import {
15
15
  } from '@metro-labs/core/stations/account-store';
16
16
  import { Line } from '@metro-labs/core/lines';
17
17
 
18
- const ACCOUNTS_FILE =
19
- process.env.XMTP_ACCOUNTS_FILE ??
20
- join(homedir(), '.metro', 'xmtp-accounts.json');
21
-
22
18
  export interface AccountConfig {
23
19
  id: string;
24
20
  privateKey?: string;
@@ -28,15 +24,8 @@ export interface AccountConfig {
28
24
 
29
25
  export const { loadAccounts } = makeAccountStore<AccountConfig>({
30
26
  prefix: 'xmtp',
31
- file: ACCOUNTS_FILE,
32
27
  validate(raw, die) {
33
- const seen = new Set<string>();
34
- for (const a of raw) {
35
- if (!a.id) die('account missing id');
36
- if (!a.privateKey) die(`account '${a.id}' needs a privateKey`);
37
- if (seen.has(a.id)) die(`duplicate account id '${a.id}'`);
38
- seen.add(a.id);
39
- }
28
+ for (const a of raw) if (!a.privateKey) die(`account '${a.id}' needs a privateKey`);
40
29
  },
41
30
  });
42
31
 
@@ -1,6 +1,6 @@
1
1
  import { errMsg } from '@metro-labs/core/log';
2
2
  import { accountForCall, convOf, lineOf, type Account } from './accounts.js';
3
- import { respond } from './wire.js';
3
+ import { respond } from '@metro-labs/core/stations/station-runtime';
4
4
  import type { GroupLike } from './labels.js';
5
5
 
6
6
  type Args = Record<string, unknown>;
@@ -1,6 +1,7 @@
1
1
  import { IdentifierKind } from '@xmtp/node-sdk';
2
2
  import { accountForCall, convOf, lineOf, parseLine } from './accounts.js';
3
- import { respond } from './wire.js';
3
+ import { respond } from '@metro-labs/core/stations/station-runtime';
4
+ import { resolveMsgId } from './wire.js';
4
5
  import { TrainError } from '@metro-labs/core/train-error';
5
6
  import {
6
7
  buildGroupInfo,
@@ -33,15 +34,23 @@ async function newDm(id: string, args: Args): Promise<void> {
33
34
  });
34
35
  }
35
36
 
36
- async function query(id: string, args: Args): Promise<void> {
37
- const { line, limit } = args as { line: string; limit?: number };
37
+ function upTo<T extends { id: string }>(all: T[], before: string | undefined): T[] {
38
+ if (!before) return all;
39
+ const target = resolveMsgId(before);
40
+ const at = all.findIndex((m) => m.id === target);
41
+ if (at < 0) throw new TrainError('NOT_FOUND', `message ${before} is not in this conversation`);
42
+ return all.slice(0, at);
43
+ }
44
+
45
+ async function read(id: string, args: Args): Promise<void> {
46
+ const { line, limit, before } = args as { line: string; limit?: number; before?: string };
38
47
  const { conv } = await convOf(line);
39
48
  if (!conv)
40
49
  throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
41
50
  const lim = Math.min(Math.max(1, limit ?? 20), 200);
42
51
  await conv.sync().catch(() => undefined);
43
52
  const all = await conv.messages();
44
- const slice = all.slice(-lim);
53
+ const slice = upTo(all, before).slice(-lim);
45
54
  const parsed = parseLine(line);
46
55
  if (!parsed)
47
56
  throw new TrainError('NOT_FOUND', `could not parse line ${line}`);
@@ -86,7 +95,7 @@ export const convHandlers: Record<string, Handler> = {
86
95
  newDm,
87
96
  updateChannelMeta,
88
97
  closeGroup,
89
- query,
98
+ read,
90
99
  groupInfo,
91
100
  listMembers,
92
101
  groupCreate: async (id, args) => {
@@ -1,5 +1,5 @@
1
1
  import { accountForCall, convOf, lineOf } from './accounts.js';
2
- import { respond } from './wire.js';
2
+ import { respond } from '@metro-labs/core/stations/station-runtime';
3
3
  import { warmGroupName } from './conv-helpers.js';
4
4
  import { mergeAppData, readAppData, type GroupLike } from './labels.js';
5
5
  import { TrainError } from '@metro-labs/core/train-error';
@@ -1,19 +1,19 @@
1
- import { ReactionAction, ReactionSchema } from '@xmtp/node-sdk';
1
+ import { ReactionAction, ReactionSchema, type Conversation } from '@xmtp/node-sdk';
2
2
  import type { Reply } from '@xmtp/node-bindings';
3
3
  import {
4
4
  AttachmentCodec,
5
5
  type Attachment,
6
6
  } from '@xmtp/content-type-remote-attachment';
7
7
  import { convOf } from './accounts.js';
8
- import { resolveMsgId, respond } from './wire.js';
8
+ import { resolveMsgId } from './wire.js';
9
9
  import { emitOutbound } from './emit.js';
10
10
  import { PollCodec, buildPollContent } from './codecs.js';
11
11
  import { convHandlers } from './actions-conv.js';
12
- import { normalizeXmtp } from '@metro-labs/core/stations/messaging-normalize';
12
+ import { messagingAliases } from '@metro-labs/core/stations/messaging-normalize';
13
13
  import { TrainError } from '@metro-labs/core/train-error';
14
14
  import { claimNameAction, nameAction, setProfile } from './profile.js';
15
15
  import { profileAction } from './sender.js';
16
- import { makeStation, type CallMsg } from '@metro-labs/core/stations/station-runtime';
16
+ import { makeStation, respond, type CallMsg } from '@metro-labs/core/stations/station-runtime';
17
17
 
18
18
  type Args = Record<string, unknown>;
19
19
 
@@ -32,68 +32,47 @@ async function send(id: string, args: Args): Promise<void> {
32
32
  }
33
33
 
34
34
  async function ask(id: string, args: Args): Promise<void> {
35
- const { line, pollId } = args as { line: string; pollId?: string };
35
+ const { line } = args as { line: string };
36
36
  const { acct, conv } = await convOf(line);
37
37
  if (!conv) throw noConv(line);
38
- const fallbackId = `poll_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
39
- const mintedId =
40
- pollId ??
41
- (typeof crypto?.randomUUID === 'function'
42
- ? crypto.randomUUID()
43
- : fallbackId);
38
+ const mintedId = crypto.randomUUID();
44
39
  const { poll, title } = buildPollContent(args, mintedId);
45
40
  const sentId = await conv.send(new PollCodec().encode(poll));
46
41
  emitOutbound(acct.cfg.id, line, sentId, `📊 Poll: ${title}`);
47
42
  respond(id, { result: { messageId: sentId, pollId: mintedId } });
48
43
  }
49
44
 
45
+ async function referenceInboxOf(conv: Conversation, xmtpMsgId: string): Promise<string> {
46
+ const recent = await conv.messages({ limit: 200, direction: 1 });
47
+ const inbox = recent.find((m) => m.id === xmtpMsgId)?.senderInboxId;
48
+ if (!inbox)
49
+ throw new TrainError('NOT_FOUND', `could not resolve referenceInboxId for ${xmtpMsgId}`);
50
+ return inbox;
51
+ }
52
+
50
53
  async function react(id: string, args: Args): Promise<void> {
51
- const {
52
- line,
53
- messageId,
54
- emoji,
55
- action: reactAction,
56
- schema: reactSchema,
57
- } = args as {
54
+ const { line, messageId, emoji, action: reactAction } = args as {
58
55
  line: string;
59
56
  messageId: string;
60
57
  emoji: string;
61
58
  action?: 'added' | 'removed';
62
- schema?: string;
63
- referenceInboxId?: string;
64
59
  };
65
60
  const { acct, conv } = await convOf(line);
66
61
  if (!conv) throw noConv(line);
67
62
  const xmtpMsgId = resolveMsgId(messageId);
68
- let refInbox = (args as { referenceInboxId?: string }).referenceInboxId;
69
- if (!refInbox) {
70
- const recent = await conv.messages({ limit: 200, direction: 1 });
71
- refInbox = recent.find((m) => m.id === xmtpMsgId)?.senderInboxId;
72
- if (!refInbox)
73
- throw new TrainError(
74
- 'NOT_FOUND',
75
- `could not resolve referenceInboxId for ${xmtpMsgId}`,
76
- );
77
- }
78
- const schemaEnum =
79
- reactSchema === 'custom'
80
- ? ReactionSchema.Custom
81
- : reactSchema === 'shortcode'
82
- ? ReactionSchema.Shortcode
83
- : ReactionSchema.Unicode;
63
+ const removed = reactAction === 'removed';
84
64
  const sentId = await conv.sendReaction({
85
65
  reference: xmtpMsgId,
86
- referenceInboxId: refInbox,
87
- action:
88
- reactAction === 'removed' ? ReactionAction.Removed : ReactionAction.Added,
66
+ referenceInboxId: await referenceInboxOf(conv, xmtpMsgId),
67
+ action: removed ? ReactionAction.Removed : ReactionAction.Added,
89
68
  content: emoji,
90
- schema: schemaEnum,
69
+ schema: ReactionSchema.Unicode,
91
70
  });
92
71
  emitOutbound(
93
72
  acct.cfg.id,
94
73
  line,
95
74
  sentId,
96
- `[react ${emoji}${reactAction === 'removed' ? ' (removed)' : ''}]`,
75
+ `[react ${emoji}${removed ? ' (removed)' : ''}]`,
97
76
  { type: 'react', emoji, targetId: xmtpMsgId },
98
77
  );
99
78
  respond(id, { result: { messageId: sentId } });
@@ -151,54 +130,28 @@ const IMG_MIME_BY_EXT: Record<string, string> = {
151
130
  webp: 'image/webp',
152
131
  };
153
132
 
154
- async function loadImageBytes(
155
- path: string | undefined,
156
- dataB64: string | undefined,
157
- ): Promise<Uint8Array> {
158
- if (path) {
159
- const { readFileSync } = await import('node:fs');
160
- return new Uint8Array(readFileSync(path));
161
- }
162
- if (dataB64) return new Uint8Array(Buffer.from(dataB64, 'base64'));
163
- throw badArgs('sendImage requires path or dataB64');
164
- }
165
-
166
- function imageMime(
167
- mimeType: string | undefined,
168
- filename: string | undefined,
169
- path: string | undefined,
170
- ): string {
171
- if (mimeType) return mimeType;
172
- const ext = (filename ?? path ?? '').toLowerCase().split('.').pop() ?? '';
133
+ function imageMime(path: string): string {
134
+ const ext = path.toLowerCase().split('.').pop() ?? '';
173
135
  return IMG_MIME_BY_EXT[ext] ?? 'image/png';
174
136
  }
175
137
 
176
- function imageFilename(
177
- filename: string | undefined,
178
- path: string | undefined,
179
- ): string {
180
- if (filename) return filename;
181
- const baseName = path ? path.split('/').pop() : undefined;
138
+ function imageFilename(path: string): string {
139
+ const baseName = path.split('/').pop();
182
140
  return baseName != null && baseName !== '' ? baseName : 'image.png';
183
141
  }
184
142
 
185
143
  async function sendImage(id: string, args: Args): Promise<void> {
186
- const { line, path, dataB64, filename, mimeType } = args as {
187
- line: string;
188
- path?: string;
189
- dataB64?: string;
190
- filename?: string;
191
- mimeType?: string;
192
- };
144
+ const { line, path } = args as { line: string; path?: string };
145
+ if (!path) throw badArgs('sendImage requires path');
193
146
  const { acct, conv } = await convOf(line);
194
147
  if (!conv) throw noConv(line);
195
- const bytes = await loadImageBytes(path, dataB64);
196
- const mime = imageMime(mimeType, filename, path);
197
- const fname = imageFilename(filename, path);
148
+ const { readFileSync } = await import('node:fs');
149
+ const mime = imageMime(path);
150
+ const fname = imageFilename(path);
198
151
  const attachment: Attachment = {
199
152
  filename: fname,
200
153
  mimeType: mime,
201
- data: bytes,
154
+ data: new Uint8Array(readFileSync(path)),
202
155
  };
203
156
  const sentId = await conv.send(new AttachmentCodec().encode(attachment));
204
157
  emitOutbound(acct.cfg.id, line, sentId, `[${mime.split('/')[0]}: ${fname}]`);
@@ -240,5 +193,5 @@ export type { CallMsg };
240
193
 
241
194
  export const handleCall = makeStation({
242
195
  handlers,
243
- normalize: normalizeXmtp,
196
+ normalize: messagingAliases({ action: 'removed' }),
244
197
  });
@@ -1,7 +1,8 @@
1
1
  import type { Conversation, DecodedMessage } from '@xmtp/node-sdk';
2
2
  import { lineOf } from './accounts.js';
3
3
  import { selfUri } from '@metro-labs/core/stations/train-events';
4
- import { emit, mintId, rememberSent, rememberUid } from './wire.js';
4
+ import { emit, mintId } from '@metro-labs/core/stations/station-runtime';
5
+ import { rememberSent, rememberUid } from './wire.js';
5
6
  import { typedEnvelope, type EnvelopeCtx } from './emit-payloads.js';
6
7
  import type { StructuredEvent } from '@metro-labs/core/events';
7
8
 
@@ -53,16 +54,14 @@ export function emitOutbound(
53
54
  rememberUid(uid, messageId);
54
55
  rememberSent(messageId);
55
56
  emit({
56
- kind: 'outbound',
57
57
  id: uid,
58
58
  ts: new Date().toISOString(),
59
59
  station: 'xmtp',
60
60
  line,
61
- from: selfUri('xmtp', accountId),
61
+ from: selfUri(),
62
62
  to: line,
63
63
  message_id: messageId,
64
64
  text,
65
- account: accountId,
66
65
  ...(event ? { event } : {}),
67
66
  payload: { account: accountId },
68
67
  });
@@ -68,12 +68,7 @@ async function handleStreamMessage(
68
68
  const env = envelope(id, msg, conv);
69
69
  Object.assign(env, senderFieldsNow(acct, msg.senderInboxId));
70
70
  const name = await groupNameFor(msg.conversationId, conv);
71
- if (name) {
72
- env.line_name = name;
73
- env.lineName = name;
74
- const p = (env.payload ?? {}) as Record<string, unknown>;
75
- env.payload = { ...p, lineName: name };
76
- }
71
+ if (name) env.line_name = name;
77
72
  emitInbound(id, env);
78
73
  }
79
74
 
@@ -13,7 +13,7 @@ import {
13
13
  import { accountForCall } from './accounts.js';
14
14
  import { claimName, nameOf, parseLabel } from './names.js';
15
15
  import { sendSponsored, type SmartAccount } from './smart.js';
16
- import { respond } from './wire.js';
16
+ import { respond } from '@metro-labs/core/stations/station-runtime';
17
17
 
18
18
  export const BASENAME_REGISTRY = '0xB94704422c2a1E396835A571837Aa5AE53285a95' as const;
19
19
  export const BASENAME_L2_RESOLVER = '0xC6d566A56A1aFf6508b41f6c90ff131615583BCD' as const;
@@ -2,7 +2,7 @@ import { createPublicClient, http, namehash, type Hex } from 'viem';
2
2
  import { base } from 'viem/chains';
3
3
  import { normalize } from 'viem/ens';
4
4
  import { makeProfileCache, type SenderProfile as Profile } from '@metro-labs/core/stations/sender-profile';
5
- import { respond } from './wire.js';
5
+ import { respond } from '@metro-labs/core/stations/station-runtime';
6
6
  import { TrainError } from '@metro-labs/core/train-error';
7
7
  import { accountForCall, accounts, type Account } from './accounts.js';
8
8
  import { resolveAddresses } from './conv-helpers.js';
@@ -65,7 +65,7 @@ export async function xmtpSendAttachments(
65
65
  ): Promise<string[]> {
66
66
  const sent: string[] = [];
67
67
  for (const a of atts) {
68
- const src = a.path ?? a.url ?? '';
68
+ const src = a.path ?? '';
69
69
  if (!src) continue;
70
70
  const mime = a.mime ?? guessMime(src);
71
71
  if (isImageMime(mime) || isImageExt(src)) {
@@ -1,6 +1,6 @@
1
1
  import { errMsg } from '@metro-labs/core/log';
2
2
  import { emitInbound, selfUri } from '@metro-labs/core/stations/train-events';
3
- import { mintId } from './wire.js';
3
+ import { mintId } from '@metro-labs/core/stations/station-runtime';
4
4
 
5
5
  const WHISPER_BIN = process.env.METRO_WHISPER_BIN ?? 'whisper-cli';
6
6
  const WHISPER_MODEL =
@@ -67,7 +67,7 @@ export async function transcribeAndEmit(
67
67
  ts: new Date().toISOString(),
68
68
  station: 'xmtp',
69
69
  line,
70
- from: selfUri('xmtp', accountId),
70
+ from: selfUri(),
71
71
  text: `🎙️ ${text}`,
72
72
  payload: {
73
73
  contentType: 'transcript',
@@ -1,5 +1,3 @@
1
- export { emit, respond, mintId } from '@metro-labs/core/stations/station-runtime';
2
-
3
1
  const UID_MAP_MAX = 5000;
4
2
  const uidToXmtp = new Map<string, string>();
5
3
 
@@ -1,3 +1,3 @@
1
1
  {
2
- "version": "0.1.0-beta.201"
2
+ "version": "0.1.0-beta.202"
3
3
  }
@@ -1,7 +0,0 @@
1
- import { Line } from './lines.js';
2
-
3
- export const userSelf = (): Line =>
4
- (process.env.METRO_FROM ?? 'metro://user') as Line;
5
-
6
- export const daemonSelf = (): Line =>
7
- (process.env.METRO_FROM ?? process.env.METRO_SELF_URI ?? 'metro://user') as Line;
@@ -1,2 +0,0 @@
1
- export const NEEDS_APPROVAL = (where: string, tool: string): string =>
2
- `Needs the owner's approval for ${where} (${tool}). Make this exact call from a background worker so Claude Code asks the owner in chat, and wait for the answer. An approval given only in the terminal does not count.`;
@@ -1,12 +0,0 @@
1
- const accountStrippedLine = (line: string): string => {
2
- const parts = line.split('/');
3
- if (parts.length < 5) return line;
4
- return [parts[0], parts[1], parts[2], ...parts.slice(4)].join('/');
5
- };
6
-
7
- export const dedupeKey = (
8
- station: string,
9
- line: string,
10
- kind: string,
11
- messageId: string,
12
- ): string => `${station} ${accountStrippedLine(line)} ${kind} ${messageId}`;
@@ -1,13 +0,0 @@
1
- const shortId = (id: string): string => (id.length > 10 ? `${id.slice(0, 6)}…` : id);
2
-
3
- export function reactionEmoji(raw: unknown): string {
4
- if (typeof raw === 'string') return raw;
5
- const obj = raw as { name?: string; reaction?: string } | undefined;
6
- return obj?.name ?? obj?.reaction ?? '';
7
- }
8
-
9
- export function reactContent(emoji: string, target: string, removed: boolean): string {
10
- const verb = removed ? 'removed from' : 'reacted to';
11
- const label = removed ? emoji || 'reaction' : emoji || 'reacted';
12
- return `${label} ${verb} message ${shortId(target)}`.trim();
13
- }
@@ -1 +0,0 @@
1
- export const PERMISSION_REPLY_RE = /^\s*(y|yes|n|no)\s+([a-km-z]{5})\s*$/i;
@@ -1,29 +0,0 @@
1
- import { authHeaders, type ConnectorAuth, type OAuthAuth } from './verify.js';
2
-
3
- export type RelayTarget =
4
- | { kind: 'ok'; url: string; headers: Record<string, string> }
5
- | { kind: 'missing' }
6
- | { kind: 'signin' };
7
-
8
- function staleUsable(auth: OAuthAuth, now = Date.now()): boolean {
9
- return auth.expiresAt === undefined || auth.expiresAt > now;
10
- }
11
-
12
- export function unrefreshedTarget(
13
- url: string,
14
- auth: OAuthAuth,
15
- force: boolean,
16
- now = Date.now(),
17
- ): RelayTarget {
18
- if (force) return { kind: 'signin' };
19
- return { kind: 'ok', url, headers: staleUsable(auth, now) ? authHeaders(auth) : {} };
20
- }
21
-
22
- export function fixedTarget(
23
- url: string,
24
- auth: Exclude<ConnectorAuth, OAuthAuth>,
25
- force: boolean,
26
- ): RelayTarget {
27
- if (force) return { kind: 'signin' };
28
- return { kind: 'ok', url, headers: authHeaders(auth) };
29
- }
@@ -1,43 +0,0 @@
1
- import type { IncomingMessage, ServerResponse } from 'node:http';
2
- import { readFileSync } from 'node:fs';
3
- import { sessionRoute } from '@metro-labs/http/api-http';
4
- import { MOVE_LOG, moveState, startMove } from './move.js';
5
- import { rootHelper, runningAsMetro, runningAsRoot } from './privilege.js';
6
- import { HELPER_VERSION } from './helper-script.js';
7
-
8
- const PATH = '/api/server/metro-user';
9
- const LOG_TAIL = 40;
10
-
11
- function logTail(): string {
12
- try {
13
- return readFileSync(MOVE_LOG, 'utf8').split('\n').slice(-LOG_TAIL).join('\n');
14
- } catch {
15
- return '';
16
- }
17
- }
18
-
19
- function helperVersion(): number | null {
20
- if (!runningAsMetro()) return null;
21
- const v = Number(rootHelper(['version']).stdout.trim());
22
- return Number.isInteger(v) ? v : null;
23
- }
24
-
25
- function view(): unknown {
26
- const runningAs = runningAsRoot() ? 'root' : runningAsMetro() ? 'metro' : 'other';
27
- const helper = helperVersion();
28
- return {
29
- runningAs,
30
- canMove: runningAs === 'root',
31
- move: moveState(),
32
- log: logTail(),
33
- helper,
34
- helperCurrent: runningAs !== 'metro' || helper === HELPER_VERSION,
35
- };
36
- }
37
-
38
- export function handleMetroUserRequest(req: IncomingMessage, res: ServerResponse): boolean {
39
- return sessionRoute(req, res, { methods: { [PATH]: ['GET', 'POST'] }, admin: true, label: 'metro-user-api' }, () => {
40
- if (req.method === 'POST') startMove();
41
- return Promise.resolve(view());
42
- });
43
- }