@stage-labs/metro 0.1.0-beta.324 → 0.1.0-beta.326
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/agent.js +1 -1
- package/dist/cli.js +1 -1
- package/dist/lock.js +15 -3
- package/dist/root-release-io.js +1 -2
- package/dist/root-release.js +2 -3
- package/dist/route.js +1 -1
- package/dist/service.js +4 -4
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/runtime/node_modules/@metro-labs/core/src/automation-store.ts +1 -1
- package/runtime/node_modules/@metro-labs/core/src/automation-types.ts +2 -2
- package/runtime/node_modules/@metro-labs/core/src/lines.ts +2 -8
- package/runtime/node_modules/@metro-labs/core/src/protocol.ts +0 -6
- package/runtime/node_modules/@metro-labs/core/src/skill-source.ts +1 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/account-status.ts +1 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/attachments.ts +5 -9
- package/runtime/node_modules/@metro-labs/core/src/stations/channel-directory.ts +1 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/mail-text.ts +4 -4
- package/runtime/node_modules/@metro-labs/core/src/stations/mail.ts +3 -2
- package/runtime/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +1 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/oauth.ts +1 -1
- package/runtime/node_modules/@metro-labs/core/src/stations/profile.ts +2 -2
- package/runtime/node_modules/@metro-labs/core/src/stations/types.ts +2 -2
- package/runtime/node_modules/@metro-labs/core/src/train-error.ts +4 -4
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/files.ts +2 -6
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/schedules.ts +6 -6
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/user.ts +0 -24
- package/runtime/node_modules/@metro-labs/daemon/src/agent-user/view.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/agents/bundle.ts +3 -4
- package/runtime/node_modules/@metro-labs/daemon/src/agents/file-admin.ts +4 -9
- package/runtime/node_modules/@metro-labs/daemon/src/agents/files.ts +1 -6
- package/runtime/node_modules/@metro-labs/daemon/src/channels/inbound.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/channels/pending.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/claude/files.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/claude/skills.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-grant.ts +3 -2
- package/runtime/node_modules/@metro-labs/daemon/src/files/attach-serve.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/files/upload-api.ts +3 -7
- package/runtime/node_modules/@metro-labs/daemon/src/gateway/model-api.ts +2 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/accounts.ts +10 -19
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/call-tools.ts +1 -5
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/event-store.ts +3 -4
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/group-tools.ts +1 -2
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/index.ts +2 -4
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/policy-gate.ts +5 -6
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/server-approvals.ts +2 -5
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/session.ts +1 -5
- package/runtime/node_modules/@metro-labs/daemon/src/mcp/tool-catalog.ts +2 -4
- package/runtime/node_modules/@metro-labs/daemon/src/mcp-server/announced.ts +1 -1
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/helper-script.ts +0 -1
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/install.ts +2 -0
- package/runtime/node_modules/@metro-labs/daemon/src/metro-user/tailscale-guard.ts +26 -0
- package/runtime/node_modules/@metro-labs/daemon/src/monitor/api.ts +1 -3
- package/runtime/node_modules/@metro-labs/daemon/src/routes/gmail-attach.ts +3 -3
- package/runtime/node_modules/@metro-labs/daemon/src/routes/http.ts +6 -21
- package/runtime/node_modules/@metro-labs/daemon/src/routes/local-mode.ts +1 -5
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach-resolve.ts +6 -6
- package/runtime/node_modules/@metro-labs/daemon/src/stations/attach.ts +0 -1
- package/runtime/node_modules/@metro-labs/daemon/src/stations/materialize.ts +12 -61
- package/runtime/node_modules/@metro-labs/daemon/src/stations/supervisor.ts +13 -25
- package/runtime/node_modules/@metro-labs/daemon/src/stations/threema-callbacks.ts +5 -5
- package/runtime/node_modules/@metro-labs/daemon/src/stations/webhook-endpoints.ts +2 -7
- package/runtime/node_modules/@metro-labs/daemon/src/voice/call.ts +2 -6
- package/runtime/node_modules/@metro-labs/daemon/src/voice/calls.ts +12 -12
- package/runtime/node_modules/@metro-labs/discord-bot/src/actions.ts +5 -23
- package/runtime/node_modules/@metro-labs/discord-bot/src/attachments.ts +1 -7
- package/runtime/node_modules/@metro-labs/discord-bot/src/channels.ts +2 -2
- package/runtime/node_modules/@metro-labs/discord-bot/src/group.ts +10 -35
- package/runtime/node_modules/@metro-labs/discord-bot/src/index.ts +5 -11
- package/runtime/node_modules/@metro-labs/discord-bot/src/members.ts +6 -34
- package/runtime/node_modules/@metro-labs/discord-bot/src/profile.ts +11 -14
- package/runtime/node_modules/@metro-labs/discord-bot/src/send-files.ts +1 -3
- package/runtime/node_modules/@metro-labs/discord-bot/src/verify.ts +2 -3
- package/runtime/node_modules/@metro-labs/gmail/src/accounts.ts +3 -3
- package/runtime/node_modules/@metro-labs/gmail/src/auth.ts +8 -8
- package/runtime/node_modules/@metro-labs/gmail/src/managed.ts +3 -2
- package/runtime/node_modules/@metro-labs/http/src/agent-signature.ts +1 -1
- package/runtime/node_modules/@metro-labs/http/src/api-http.ts +3 -4
- package/runtime/node_modules/@metro-labs/http/src/connector-discovery.ts +0 -113
- package/runtime/node_modules/@metro-labs/http/src/connector-input.ts +1 -1
- package/runtime/node_modules/@metro-labs/http/src/connector-mcp.ts +12 -13
- package/runtime/node_modules/@metro-labs/http/src/connector-oauth.ts +1 -1
- package/runtime/node_modules/@metro-labs/http/src/connector-policy.ts +1 -6
- package/runtime/node_modules/@metro-labs/http/src/connector-public-url.ts +4 -2
- package/runtime/node_modules/@metro-labs/http/src/connector-tools.ts +1 -11
- package/runtime/node_modules/@metro-labs/http/src/connector-url.ts +0 -6
- package/runtime/node_modules/@metro-labs/http/src/mcp-server-protocol.ts +1 -1
- package/runtime/node_modules/@metro-labs/http/src/mcp-server-tools.ts +0 -2
- package/runtime/node_modules/@metro-labs/http/src/mode-api.ts +1 -1
- package/runtime/node_modules/@metro-labs/http/src/return-to.ts +2 -2
- package/runtime/node_modules/@metro-labs/http/src/workos-token.ts +6 -7
- package/runtime/node_modules/@metro-labs/inference/package.json +1 -0
- package/runtime/node_modules/@metro-labs/inference/src/attempts.ts +3 -3
- package/runtime/node_modules/@metro-labs/inference/src/bedrock.ts +1 -1
- package/runtime/node_modules/@metro-labs/inference/src/codex-translate.ts +2 -2
- package/runtime/node_modules/@metro-labs/inference/src/delivered.ts +1 -1
- package/runtime/node_modules/@metro-labs/inference/src/effort.ts +0 -6
- package/runtime/node_modules/@metro-labs/inference/src/fallback.ts +7 -5
- package/runtime/node_modules/@metro-labs/inference/src/forward.ts +5 -5
- package/runtime/node_modules/@metro-labs/inference/src/measure.ts +2 -3
- package/runtime/node_modules/@metro-labs/inference/src/named.ts +3 -3
- package/runtime/node_modules/@metro-labs/inference/src/probes.ts +98 -0
- package/runtime/node_modules/@metro-labs/inference/src/usage.ts +5 -5
- package/runtime/node_modules/@metro-labs/outlook/src/accounts.ts +6 -8
- package/runtime/node_modules/@metro-labs/outlook/src/auth.ts +2 -1
- package/runtime/node_modules/@metro-labs/outlook/src/inbound.ts +3 -5
- package/runtime/node_modules/@metro-labs/outlook/src/me.ts +6 -6
- package/runtime/node_modules/@metro-labs/outlook/src/outbound.ts +10 -13
- package/runtime/node_modules/@metro-labs/telegram/src/actions.ts +7 -26
- package/runtime/node_modules/@metro-labs/telegram/src/attachments.ts +1 -1
- package/runtime/node_modules/@metro-labs/telegram/src/login.ts +3 -6
- package/runtime/node_modules/@metro-labs/telegram/src/profile.ts +3 -2
- package/runtime/node_modules/@metro-labs/telegram-bot/src/actions.ts +6 -20
- package/runtime/node_modules/@metro-labs/telegram-bot/src/attachments.ts +1 -7
- package/runtime/node_modules/@metro-labs/telegram-bot/src/format.ts +0 -2
- package/runtime/node_modules/@metro-labs/telegram-bot/src/media-actions.ts +1 -16
- package/runtime/node_modules/@metro-labs/telegram-bot/src/members.ts +1 -8
- package/runtime/node_modules/@metro-labs/telegram-bot/src/profile.ts +2 -1
- package/runtime/node_modules/@metro-labs/threema/src/actions.ts +1 -8
- package/runtime/node_modules/@metro-labs/threema/src/chats.ts +4 -3
- package/runtime/node_modules/@metro-labs/threema/src/format.ts +15 -17
- package/runtime/node_modules/@metro-labs/threema/src/groups.ts +2 -4
- package/runtime/node_modules/@metro-labs/threema/src/inbound.ts +2 -1
- package/runtime/node_modules/@metro-labs/threema/src/messages.ts +2 -1
- package/runtime/node_modules/@metro-labs/threema/src/outbound.ts +7 -7
- package/runtime/node_modules/@metro-labs/whatsapp/src/actions.ts +1 -3
- package/runtime/node_modules/@metro-labs/whatsapp/src/attachments.ts +0 -2
- package/runtime/node_modules/@metro-labs/whatsapp/src/client.ts +1 -1
- package/runtime/node_modules/@metro-labs/whatsapp/src/login.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/accounts.ts +7 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +4 -10
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-meta.ts +4 -5
- package/runtime/node_modules/@metro-labs/xmtp/src/actions.ts +14 -26
- package/runtime/node_modules/@metro-labs/xmtp/src/attachments.ts +1 -7
- package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +17 -58
- package/runtime/node_modules/@metro-labs/xmtp/src/conv-helpers.ts +1 -6
- package/runtime/node_modules/@metro-labs/xmtp/src/emit-payloads.ts +10 -31
- package/runtime/node_modules/@metro-labs/xmtp/src/frames.ts +12 -14
- package/runtime/node_modules/@metro-labs/xmtp/src/group.ts +2 -3
- package/runtime/node_modules/@metro-labs/xmtp/src/identity.ts +3 -9
- package/runtime/node_modules/@metro-labs/xmtp/src/labels.ts +8 -9
- package/runtime/node_modules/@metro-labs/xmtp/src/names.ts +4 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/network.ts +2 -3
- package/runtime/node_modules/@metro-labs/xmtp/src/profile.ts +2 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/read-attachments.ts +2 -2
- package/runtime/node_modules/@metro-labs/xmtp/src/sender.ts +2 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/transcribe.ts +23 -46
- package/runtime/node_modules/@metro-labs/xmtp/src/wallet.ts +5 -4
- package/runtime/node_modules/@metro-labs/xmtp/src/wire.ts +12 -15
- package/runtime/runtime.json +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/automation-store.ts +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/automation-types.ts +2 -2
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/lines.ts +2 -8
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/protocol.ts +0 -6
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/skill-source.ts +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/account-status.ts +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/attachments.ts +5 -9
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/channel-directory.ts +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/mail-text.ts +4 -4
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/mail.ts +3 -2
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/messaging-normalize.ts +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/oauth.ts +1 -1
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/profile.ts +2 -2
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/stations/types.ts +2 -2
- package/runtime/sdk-runner/node_modules/@metro-labs/core/src/train-error.ts +4 -4
- package/runtime/sdk-runner/runner.json +1 -1
- package/runtime/sdk-runner/src/approvals.ts +2 -2
- package/runtime/sdk-runner/src/automation-command.ts +2 -2
- package/runtime/sdk-runner/src/calls.ts +0 -6
- package/runtime/sdk-runner/src/errors.ts +1 -1
- package/runtime/sdk-runner/src/link.ts +1 -1
- package/runtime/sdk-runner/src/runner.ts +2 -2
- package/runtime/sdk-runner/src/session-watch.ts +5 -34
- package/runtime/sdk-runner/src/task-state.ts +1 -1
- package/runtime/sdk-runner/src/tool-proxy.ts +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/member-args.ts +0 -40
|
@@ -242,16 +242,6 @@ function pollPayload(
|
|
|
242
242
|
};
|
|
243
243
|
}
|
|
244
244
|
|
|
245
|
-
function labeledPayload(
|
|
246
|
-
base: Record<string, unknown>,
|
|
247
|
-
typeId: string,
|
|
248
|
-
text: string,
|
|
249
|
-
key: string,
|
|
250
|
-
c: unknown,
|
|
251
|
-
): Record<string, unknown> {
|
|
252
|
-
return { ...base, text, payload: { contentType: typeId, [key]: c } };
|
|
253
|
-
}
|
|
254
|
-
|
|
255
245
|
function attachmentEnvelope(
|
|
256
246
|
base: Record<string, unknown>,
|
|
257
247
|
typeId: string,
|
|
@@ -275,32 +265,21 @@ function attachmentEnvelope(
|
|
|
275
265
|
return undefined;
|
|
276
266
|
}
|
|
277
267
|
|
|
268
|
+
const REF_TEXT = new Map([
|
|
269
|
+
['walletSendCalls', 'Payment request'],
|
|
270
|
+
['transactionReference', 'Transaction'],
|
|
271
|
+
['signatureRequest', 'Signature request'],
|
|
272
|
+
['signatureReference', 'Signature'],
|
|
273
|
+
]);
|
|
274
|
+
|
|
278
275
|
function refEnvelope(
|
|
279
276
|
base: Record<string, unknown>,
|
|
280
277
|
typeId: string | undefined,
|
|
281
278
|
c: object,
|
|
282
279
|
): Record<string, unknown> | undefined {
|
|
283
|
-
if (typeId ===
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
typeId,
|
|
287
|
-
'Payment request',
|
|
288
|
-
'walletSendCalls',
|
|
289
|
-
c,
|
|
290
|
-
);
|
|
291
|
-
if (typeId === 'transactionReference')
|
|
292
|
-
return labeledPayload(
|
|
293
|
-
base,
|
|
294
|
-
typeId,
|
|
295
|
-
'Transaction',
|
|
296
|
-
'transactionReference',
|
|
297
|
-
c,
|
|
298
|
-
);
|
|
299
|
-
if (typeId === 'signatureRequest')
|
|
300
|
-
return labeledPayload(base, typeId, 'Signature request', 'signatureRequest', c);
|
|
301
|
-
if (typeId === 'signatureReference')
|
|
302
|
-
return labeledPayload(base, typeId, 'Signature', 'signatureReference', c);
|
|
303
|
-
return undefined;
|
|
280
|
+
if (typeId === undefined) return undefined;
|
|
281
|
+
const text = REF_TEXT.get(typeId);
|
|
282
|
+
return text === undefined ? undefined : { ...base, text, payload: { contentType: typeId, [typeId]: c } };
|
|
304
283
|
}
|
|
305
284
|
|
|
306
285
|
function frameEnvelope(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
1
2
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
2
3
|
import type { FrameActionContent, FrameContent } from './codecs.js';
|
|
3
4
|
|
|
@@ -13,9 +14,6 @@ const TEXT_TYPES = new Set(['Text', 'Caption', 'Markdown']);
|
|
|
13
14
|
|
|
14
15
|
type Node = Record<string, unknown>;
|
|
15
16
|
|
|
16
|
-
export const isNode = (v: unknown): v is Node =>
|
|
17
|
-
typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
18
|
-
|
|
19
17
|
export const bad = (message: string): TrainError => new TrainError('INVALID_ARGS', message);
|
|
20
18
|
|
|
21
19
|
function plain(value: unknown): string {
|
|
@@ -30,8 +28,8 @@ function plain(value: unknown): string {
|
|
|
30
28
|
|
|
31
29
|
function childrenOf(node: Node): Node[] {
|
|
32
30
|
const c = node.children;
|
|
33
|
-
if (Array.isArray(c)) return c.filter(
|
|
34
|
-
return
|
|
31
|
+
if (Array.isArray(c)) return c.filter(isRecord);
|
|
32
|
+
return isRecord(c) ? [c] : [];
|
|
35
33
|
}
|
|
36
34
|
|
|
37
35
|
export function frameSummary(widget: Node): { title?: string; description?: string } {
|
|
@@ -73,7 +71,7 @@ function checkSize(value: unknown, name: string): void {
|
|
|
73
71
|
|
|
74
72
|
function parseWidget(raw: unknown, name = 'frame widget'): Node {
|
|
75
73
|
const widget = parseJson(raw, name);
|
|
76
|
-
if (!
|
|
74
|
+
if (!isRecord(widget) || typeof widget.type !== 'string' || widget.type === '')
|
|
77
75
|
throw bad(`${name} must be a ChatKit widget object with a \`type\` (Card, ListView or Basic)`);
|
|
78
76
|
checkSize(widget, name);
|
|
79
77
|
return widget;
|
|
@@ -82,7 +80,7 @@ function parseWidget(raw: unknown, name = 'frame widget'): Node {
|
|
|
82
80
|
function parseScreen(id: string, raw: unknown): { screen: Node; widget: Node } {
|
|
83
81
|
const name = `frame screen "${id}"`;
|
|
84
82
|
const given = parseJson(raw, name);
|
|
85
|
-
if (!
|
|
83
|
+
if (!isRecord(given) || given.type !== undefined || given.widget === undefined) {
|
|
86
84
|
const widget = parseWidget(given, name);
|
|
87
85
|
return { screen: widget, widget };
|
|
88
86
|
}
|
|
@@ -97,9 +95,9 @@ function openTargets(root: unknown): string[] {
|
|
|
97
95
|
while (stack.length > 0) {
|
|
98
96
|
const value = stack.pop();
|
|
99
97
|
if (Array.isArray(value)) stack.push(...(value as unknown[]));
|
|
100
|
-
else if (
|
|
98
|
+
else if (isRecord(value)) {
|
|
101
99
|
if (value.type === FRAME_OPEN)
|
|
102
|
-
targets.push(
|
|
100
|
+
targets.push(isRecord(value.payload) && typeof value.payload.screen === 'string' ? value.payload.screen : '');
|
|
103
101
|
stack.push(...Object.values(value));
|
|
104
102
|
}
|
|
105
103
|
}
|
|
@@ -130,7 +128,7 @@ interface FrameBody {
|
|
|
130
128
|
|
|
131
129
|
function parseScreens(raw: unknown, start: unknown): FrameBody {
|
|
132
130
|
const given = parseJson(raw, 'frame screens');
|
|
133
|
-
if (!
|
|
131
|
+
if (!isRecord(given))
|
|
134
132
|
throw bad('frame screens must be an object of screen id to widget: {"home": {…}, "story": {"title": "…", "widget": {…}}}');
|
|
135
133
|
const ids = screenIds(given);
|
|
136
134
|
const parsed = ids.map((id) => [id, parseScreen(id, given[id])] as const);
|
|
@@ -153,7 +151,7 @@ function frameBody(args: Node): FrameBody {
|
|
|
153
151
|
|
|
154
152
|
function sourcePart(raw: unknown): Pick<FrameContent, 'source'> {
|
|
155
153
|
if (raw === undefined) return {};
|
|
156
|
-
const url =
|
|
154
|
+
const url = isRecord(raw) && typeof raw.url === 'string' ? raw.url.trim() : '';
|
|
157
155
|
if (url.length > MAX_SOURCE_URL)
|
|
158
156
|
throw bad(`frame source url is ${url.length} characters; the limit is ${MAX_SOURCE_URL}`);
|
|
159
157
|
if (!URL.canParse(url) || new URL(url).protocol !== 'https:')
|
|
@@ -163,7 +161,7 @@ function sourcePart(raw: unknown): Pick<FrameContent, 'source'> {
|
|
|
163
161
|
|
|
164
162
|
export function buildFrameContent(raw: unknown): { frame: FrameContent; title: string } {
|
|
165
163
|
const args = parseJson(raw, 'frame');
|
|
166
|
-
if (!
|
|
164
|
+
if (!isRecord(args))
|
|
167
165
|
throw bad('frame must be an object: {widget, title?, description?} or {screens, start, title?, description?}');
|
|
168
166
|
const body = frameBody(args);
|
|
169
167
|
const derived = frameSummary(body.first);
|
|
@@ -180,8 +178,8 @@ export function buildFrameContent(raw: unknown): { frame: FrameContent; title: s
|
|
|
180
178
|
|
|
181
179
|
export function asFrameAction(c: object): FrameActionContent | undefined {
|
|
182
180
|
const v = c as Partial<FrameActionContent>;
|
|
183
|
-
if (typeof v.frameId !== 'string' || !
|
|
184
|
-
const payload =
|
|
181
|
+
if (typeof v.frameId !== 'string' || !isRecord(v.action) || typeof v.action.type !== 'string') return undefined;
|
|
182
|
+
const payload = isRecord(v.action.payload) ? v.action.payload : undefined;
|
|
185
183
|
return {
|
|
186
184
|
frameId: v.frameId,
|
|
187
185
|
action: payload ? { type: v.action.type, payload } : { type: v.action.type },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
2
2
|
import type { GroupResult } from '@metro-labs/core/stations/types';
|
|
3
|
-
import { accountForCall,
|
|
3
|
+
import { accountForCall, lineOf, openConv } from './accounts.js';
|
|
4
4
|
import {
|
|
5
5
|
applyMemberOp,
|
|
6
6
|
createGroupWithMembers,
|
|
@@ -38,8 +38,7 @@ async function memberOp(
|
|
|
38
38
|
const { addrs, inboxes } = splitMembers(args);
|
|
39
39
|
if (!addrs.length && !inboxes.length)
|
|
40
40
|
throw new TrainError('INVALID_ARGS', `${verb} requires members`);
|
|
41
|
-
const { conv } = await
|
|
42
|
-
if (!conv) throw new TrainError('NOT_FOUND', `conversation not found for ${line}`);
|
|
41
|
+
const { conv } = await openConv(line);
|
|
43
42
|
await applyMemberOp(conv, addrs, inboxes, mode);
|
|
44
43
|
return {
|
|
45
44
|
capability: { supported: true },
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { IdentifierKind, type Signer } from '@xmtp/node-sdk';
|
|
2
|
+
import { hexToBytes } from 'viem';
|
|
2
3
|
import { privateKeyToAccount } from 'viem/accounts';
|
|
3
4
|
import { homedir } from 'node:os';
|
|
4
5
|
import { join } from 'node:path';
|
|
@@ -15,19 +16,12 @@ export interface Identity {
|
|
|
15
16
|
smart: SmartAccount | null;
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
const bytesOf = (sig: string): Uint8Array => {
|
|
19
|
-
const hex = sig.slice(2);
|
|
20
|
-
const out = new Uint8Array(hex.length / 2);
|
|
21
|
-
for (let i = 0; i < out.length; i++) out[i] = parseInt(hex.slice(i * 2, i * 2 + 2), 16);
|
|
22
|
-
return out;
|
|
23
|
-
};
|
|
24
|
-
|
|
25
19
|
export function signerFor(privateKey: string): Identity {
|
|
26
20
|
const acct = privateKeyToAccount(privateKey as `0x${string}`);
|
|
27
21
|
const signer: Signer = {
|
|
28
22
|
type: 'EOA',
|
|
29
23
|
getIdentifier: () => Promise.resolve({ identifier: acct.address, identifierKind: IdentifierKind.Ethereum }),
|
|
30
|
-
signMessage: async (msg: string) =>
|
|
24
|
+
signMessage: async (msg: string) => hexToBytes(await acct.signMessage({ message: msg })),
|
|
31
25
|
};
|
|
32
26
|
return { signer, address: acct.address, smart: null };
|
|
33
27
|
}
|
|
@@ -37,7 +31,7 @@ export async function smartSignerFor(privateKey: string, rpc?: string): Promise<
|
|
|
37
31
|
const signer: Signer = {
|
|
38
32
|
type: 'SCW',
|
|
39
33
|
getIdentifier: () => Promise.resolve({ identifier: smart.address, identifierKind: IdentifierKind.Ethereum }),
|
|
40
|
-
signMessage: async (msg: string) =>
|
|
34
|
+
signMessage: async (msg: string) => hexToBytes(await smart.signMessage(msg)),
|
|
41
35
|
getChainId: () => BigInt(SMART_CHAIN_ID),
|
|
42
36
|
};
|
|
43
37
|
return { signer, address: smart.address, smart };
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
1
2
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
2
3
|
|
|
3
4
|
export interface GroupLike {
|
|
@@ -41,10 +42,10 @@ export function readAppDataObject(
|
|
|
41
42
|
): Record<string, unknown> {
|
|
42
43
|
if (!existingAppData?.trim()) return {};
|
|
43
44
|
const parsed: unknown = JSON.parse(existingAppData);
|
|
44
|
-
if (!
|
|
45
|
+
if (!isRecord(parsed)) {
|
|
45
46
|
throw new TrainError('INVALID_ARGS', 'Channel appData must be a JSON object');
|
|
46
47
|
}
|
|
47
|
-
return parsed
|
|
48
|
+
return parsed;
|
|
48
49
|
}
|
|
49
50
|
|
|
50
51
|
export function normalizeAssigned(value: unknown): string[] {
|
|
@@ -62,14 +63,13 @@ export function normalizeAssigned(value: unknown): string[] {
|
|
|
62
63
|
}
|
|
63
64
|
|
|
64
65
|
export function metadataPatch(value: unknown): Record<string, unknown> {
|
|
65
|
-
if (!
|
|
66
|
+
if (!isRecord(value)) {
|
|
66
67
|
throw new TrainError('INVALID_ARGS', 'metadata must be a JSON object');
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
-
if (Object.hasOwn(patch, 'v')) {
|
|
69
|
+
if (Object.hasOwn(value, 'v')) {
|
|
70
70
|
throw new TrainError('INVALID_ARGS', 'metadata cannot change the reserved v field');
|
|
71
71
|
}
|
|
72
|
-
return
|
|
72
|
+
return value;
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
function trimmedString(v: unknown): string | undefined {
|
|
@@ -94,9 +94,8 @@ export function readAppData(appData: string | undefined): {
|
|
|
94
94
|
} {
|
|
95
95
|
if (!appData?.trim()) return { labels: [] };
|
|
96
96
|
try {
|
|
97
|
-
const
|
|
98
|
-
if (!
|
|
99
|
-
const rec = p as Record<string, unknown>;
|
|
97
|
+
const rec: unknown = JSON.parse(appData);
|
|
98
|
+
if (!isRecord(rec)) return { labels: [] };
|
|
100
99
|
return {
|
|
101
100
|
labels: cleanLabels(rec.labels),
|
|
102
101
|
category: cleanMetadataText(rec.category),
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
2
|
+
import { errMsg } from '@metro-labs/core/log';
|
|
1
3
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
2
4
|
|
|
3
5
|
export const STAGE_NAMES_PARENT = 'stage.base.eth';
|
|
@@ -27,10 +29,10 @@ async function ask(path: string, init: RequestInit, fetchImpl: typeof fetch): Pr
|
|
|
27
29
|
try {
|
|
28
30
|
res = await fetchImpl(`${proxyBase()}/names/${path}`, { ...init, headers: HEADERS, signal: AbortSignal.timeout(30_000) });
|
|
29
31
|
} catch (err) {
|
|
30
|
-
throw new TrainError('names_unreachable', `could not reach the stage name service: ${
|
|
32
|
+
throw new TrainError('names_unreachable', `could not reach the stage name service: ${errMsg(err)}`);
|
|
31
33
|
}
|
|
32
34
|
const body: unknown = await res.json().catch(() => null);
|
|
33
|
-
const record =
|
|
35
|
+
const record = isRecord(body) ? body : {};
|
|
34
36
|
if (!res.ok) throw new TrainError('names_refused', typeof record.error === 'string' ? record.error : `the stage name service answered ${String(res.status)}`);
|
|
35
37
|
return record;
|
|
36
38
|
}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
1
2
|
import { errMsg } from '@metro-labs/core/log';
|
|
2
3
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
3
4
|
|
|
4
5
|
const RATE_DELAY_MS = 60_000;
|
|
5
6
|
const MAX_DELAY_MS = 300_000;
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
return typeof value === 'object' && value !== null ? value as Record<string, unknown> : {};
|
|
9
|
-
}
|
|
8
|
+
const recordOf = (value: unknown): Record<string, unknown> => (isRecord(value) ? value : {});
|
|
10
9
|
|
|
11
10
|
function retryAfter(error: unknown, now: number): number {
|
|
12
11
|
const fields = recordOf(error);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
2
|
import { encodeFunctionData, encodePacked, keccak256, namehash, stringToBytes, type Hex } from 'viem';
|
|
3
3
|
import { normalize } from 'viem/ens';
|
|
4
|
+
import { errMsg } from '@metro-labs/core/log';
|
|
4
5
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
5
6
|
import {
|
|
6
7
|
assertImage,
|
|
@@ -93,7 +94,7 @@ export async function setUpName(smart: SmartAccount, name: string): Promise<bool
|
|
|
93
94
|
await sendSponsoredCalls(smart, nameSetupCalls(name, smart.address, resolver));
|
|
94
95
|
return true;
|
|
95
96
|
} catch (err) {
|
|
96
|
-
process.stderr.write(`xmtp: the name ${name} is claimed but its address and primary name are not set for ${smart.address}: ${
|
|
97
|
+
process.stderr.write(`xmtp: the name ${name} is claimed but its address and primary name are not set for ${smart.address}: ${errMsg(err)}\n`);
|
|
97
98
|
return false;
|
|
98
99
|
}
|
|
99
100
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { DecodedMessage } from '@xmtp/node-sdk';
|
|
2
|
-
import { kindOf } from '@metro-labs/core/stations/attachments';
|
|
3
|
-
import { saveInlineAttachment, saveRemoteAttachment, type RemoteEntry
|
|
2
|
+
import { kindOf, type SavedAttachment } from '@metro-labs/core/stations/attachments';
|
|
3
|
+
import { saveInlineAttachment, saveRemoteAttachment, type RemoteEntry } from './attachments.js';
|
|
4
4
|
|
|
5
5
|
interface ReadAttachment {
|
|
6
6
|
name?: string;
|
|
@@ -3,6 +3,7 @@ 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
5
|
import { respond } from '@metro-labs/core/stations/station-runtime';
|
|
6
|
+
import { errMsg } from '@metro-labs/core/log';
|
|
6
7
|
import { TrainError } from '@metro-labs/core/train-error';
|
|
7
8
|
import { accountForCall, accounts, type Account } from './accounts.js';
|
|
8
9
|
import { resolveAddresses } from './conv-helpers.js';
|
|
@@ -85,7 +86,7 @@ const senders = makeProfileCache<SenderProfile>(
|
|
|
85
86
|
const address = (await resolveAddresses(acct, [inboxId]))[inboxId];
|
|
86
87
|
return address === undefined ? null : profileOf(address);
|
|
87
88
|
},
|
|
88
|
-
{ onError: (k, err) => process.stderr.write(`xmtp: could not resolve the sender ${k}: ${
|
|
89
|
+
{ onError: (k, err) => process.stderr.write(`xmtp: could not resolve the sender ${k}: ${errMsg(err)}\n`) },
|
|
89
90
|
);
|
|
90
91
|
|
|
91
92
|
export const senderFields = (p: SenderProfile | null): Record<string, string> =>
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import { existsSync, mkdtempSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { tmpdir } from 'node:os';
|
|
4
|
+
import { join } from 'node:path';
|
|
1
5
|
import { errMsg } from '@metro-labs/core/log';
|
|
2
6
|
import { emitInbound, selfUri } from '@metro-labs/core/stations/train-events';
|
|
3
7
|
import { mintId } from '@metro-labs/core/stations/station-runtime';
|
|
@@ -8,59 +12,32 @@ const WHISPER_MODEL =
|
|
|
8
12
|
`${process.env.HOME}/.cache/whisper-cpp/ggml-base.bin`;
|
|
9
13
|
const FFMPEG_BIN = process.env.METRO_FFMPEG_BIN ?? 'ffmpeg';
|
|
10
14
|
|
|
15
|
+
const run = (bin: string, args: string[]): Promise<void> =>
|
|
16
|
+
new Promise((res, rej) => {
|
|
17
|
+
const p = spawn(bin, args, { stdio: 'ignore' });
|
|
18
|
+
p.on('error', rej);
|
|
19
|
+
p.on('exit', (c) => {
|
|
20
|
+
if (c === 0) res();
|
|
21
|
+
else rej(new Error(`${bin} ${String(c)}`));
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
11
25
|
export async function transcribeAndEmit(
|
|
12
26
|
audio: Uint8Array,
|
|
13
27
|
line: string,
|
|
14
28
|
accountId: string,
|
|
15
29
|
sourceMsgId: string,
|
|
16
30
|
): Promise<void> {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
mkdtempSync,
|
|
23
|
-
} = await import('node:fs');
|
|
24
|
-
const { tmpdir } = await import('node:os');
|
|
25
|
-
const path = await import('node:path');
|
|
26
|
-
const j = (...parts: string[]): string => path.join(...parts);
|
|
27
|
-
const { spawn } = await import('node:child_process');
|
|
28
|
-
if (!ex(WHISPER_MODEL)) return;
|
|
29
|
-
const dir = mkdtempSync(j(tmpdir(), 'xmtp-tx-'));
|
|
30
|
-
const inFile = j(dir, 'in.m4a');
|
|
31
|
-
const wav = j(dir, 'in.wav');
|
|
32
|
-
const out = j(dir, 'in');
|
|
33
|
-
const run = (bin: string, args: string[]): Promise<void> =>
|
|
34
|
-
new Promise((res, rej) => {
|
|
35
|
-
const p = spawn(bin, args, { stdio: 'ignore' });
|
|
36
|
-
p.on('error', rej);
|
|
37
|
-
p.on('exit', (c) => {
|
|
38
|
-
if (c === 0) res();
|
|
39
|
-
else rej(new Error(`${bin} ${String(c)}`));
|
|
40
|
-
});
|
|
41
|
-
});
|
|
31
|
+
if (!existsSync(WHISPER_MODEL)) return;
|
|
32
|
+
const dir = mkdtempSync(join(tmpdir(), 'xmtp-tx-'));
|
|
33
|
+
const inFile = join(dir, 'in.m4a');
|
|
34
|
+
const wav = join(dir, 'in.wav');
|
|
35
|
+
const out = join(dir, 'in');
|
|
42
36
|
try {
|
|
43
|
-
|
|
44
|
-
await run(FFMPEG_BIN, [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
inFile,
|
|
48
|
-
'-ar',
|
|
49
|
-
'16000',
|
|
50
|
-
'-ac',
|
|
51
|
-
'1',
|
|
52
|
-
wav,
|
|
53
|
-
]);
|
|
54
|
-
await run(WHISPER_BIN, [
|
|
55
|
-
'-m',
|
|
56
|
-
WHISPER_MODEL,
|
|
57
|
-
'-f',
|
|
58
|
-
wav,
|
|
59
|
-
'--output-txt',
|
|
60
|
-
'-of',
|
|
61
|
-
out,
|
|
62
|
-
]);
|
|
63
|
-
const text = rf(`${out}.txt`, 'utf8').trim();
|
|
37
|
+
writeFileSync(inFile, audio);
|
|
38
|
+
await run(FFMPEG_BIN, ['-y', '-i', inFile, '-ar', '16000', '-ac', '1', wav]);
|
|
39
|
+
await run(WHISPER_BIN, ['-m', WHISPER_MODEL, '-f', wav, '--output-txt', '-of', out]);
|
|
40
|
+
const text = readFileSync(`${out}.txt`, 'utf8').trim();
|
|
64
41
|
if (!text) return;
|
|
65
42
|
emitInbound(accountId, {
|
|
66
43
|
id: mintId(),
|
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
TransactionReferenceCodec,
|
|
6
6
|
WalletSendCallsCodec,
|
|
7
7
|
} from './codecs.js';
|
|
8
|
-
import {
|
|
8
|
+
import { isRecord } from '@metro-labs/core/is-record';
|
|
9
|
+
import { bad, parseJson } from './frames.js';
|
|
9
10
|
|
|
10
11
|
const WALLET_TYPES: Record<string, { codec: { encode(c: Record<string, unknown>): EncodedContent }; needs: string[] }> = {
|
|
11
12
|
walletSendCalls: { codec: new WalletSendCallsCodec(), needs: ['version', 'chainId', 'from', 'calls'] },
|
|
@@ -16,11 +17,11 @@ const WALLET_TYPES: Record<string, { codec: { encode(c: Record<string, unknown>)
|
|
|
16
17
|
|
|
17
18
|
export function buildWalletContent(raw: unknown): { encoded: EncodedContent; summary: string } {
|
|
18
19
|
const args = parseJson(raw, 'wallet');
|
|
19
|
-
const type =
|
|
20
|
+
const type = isRecord(args) && typeof args.type === 'string' ? args.type : '';
|
|
20
21
|
const spec = Object.hasOwn(WALLET_TYPES, type) ? WALLET_TYPES[type] : undefined;
|
|
21
|
-
if (!
|
|
22
|
+
if (!isRecord(args) || !spec) throw bad(`wallet type must be one of ${Object.keys(WALLET_TYPES).join(', ')}`);
|
|
22
23
|
const content = parseJson(args.content, 'wallet content');
|
|
23
|
-
if (!
|
|
24
|
+
if (!isRecord(content)) throw bad(`wallet content must be the ${type} JSON object`);
|
|
24
25
|
const missing = spec.needs.filter((key) => content[key] === undefined);
|
|
25
26
|
if (missing.length > 0) throw bad(`${type} content needs ${missing.join(', ')}`);
|
|
26
27
|
const encoded = spec.codec.encode(content);
|
|
@@ -4,12 +4,18 @@ const uidToXmtp = new Map<string, string>();
|
|
|
4
4
|
const INBOX_ETH_CACHE_MAX = 5000;
|
|
5
5
|
export const inboxEthCache = new Map<string, string>();
|
|
6
6
|
|
|
7
|
+
const SENT_MAX = 5000;
|
|
8
|
+
const sentIds = new Set<string>();
|
|
9
|
+
|
|
10
|
+
function dropOldest(held: Map<string, string> | Set<string>, max: number): void {
|
|
11
|
+
if (held.size <= max) return;
|
|
12
|
+
const oldest = held.keys().next().value;
|
|
13
|
+
if (oldest !== undefined) held.delete(oldest);
|
|
14
|
+
}
|
|
15
|
+
|
|
7
16
|
export function cacheInboxEth(inboxId: string, eth: string): void {
|
|
8
17
|
inboxEthCache.set(inboxId, eth);
|
|
9
|
-
|
|
10
|
-
const oldest = inboxEthCache.keys().next().value;
|
|
11
|
-
if (oldest !== undefined) inboxEthCache.delete(oldest);
|
|
12
|
-
}
|
|
18
|
+
dropOldest(inboxEthCache, INBOX_ETH_CACHE_MAX);
|
|
13
19
|
}
|
|
14
20
|
|
|
15
21
|
export function rememberUid(
|
|
@@ -18,10 +24,7 @@ export function rememberUid(
|
|
|
18
24
|
): void {
|
|
19
25
|
if (!uid || !xmtpId || !uid.startsWith('msg_')) return;
|
|
20
26
|
uidToXmtp.set(uid, xmtpId);
|
|
21
|
-
|
|
22
|
-
const oldest = uidToXmtp.keys().next().value;
|
|
23
|
-
if (oldest !== undefined) uidToXmtp.delete(oldest);
|
|
24
|
-
}
|
|
27
|
+
dropOldest(uidToXmtp, UID_MAP_MAX);
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
export function resolveMsgId(rawId: string): string {
|
|
@@ -34,16 +37,10 @@ export function resolveMsgId(rawId: string): string {
|
|
|
34
37
|
);
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
const SENT_MAX = 5000;
|
|
38
|
-
const sentIds = new Set<string>();
|
|
39
|
-
|
|
40
40
|
export function rememberSent(xmtpId: string | undefined): void {
|
|
41
41
|
if (!xmtpId) return;
|
|
42
42
|
sentIds.add(xmtpId);
|
|
43
|
-
|
|
44
|
-
const oldest = sentIds.values().next().value;
|
|
45
|
-
if (oldest !== undefined) sentIds.delete(oldest);
|
|
46
|
-
}
|
|
43
|
+
dropOldest(sentIds, SENT_MAX);
|
|
47
44
|
}
|
|
48
45
|
|
|
49
46
|
export const sentByMe = (xmtpId: string): boolean => sentIds.has(xmtpId);
|
package/runtime/runtime.json
CHANGED
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from './automation-types.js';
|
|
9
9
|
import { isRecord } from './is-record.js';
|
|
10
10
|
|
|
11
|
-
export type { AutomationOutcome, AutomationRequest, AutomationResolution, AutomationStatus
|
|
11
|
+
export type { AutomationOutcome, AutomationRequest, AutomationResolution, AutomationStatus } from './automation-types.js';
|
|
12
12
|
|
|
13
13
|
function payload<T extends { uuid: string }>(row: unknown, parse: (value: unknown) => T): T {
|
|
14
14
|
try {
|
|
@@ -5,7 +5,7 @@ export const AUTOMATION_PROMPT_MAX = 64 * 1024;
|
|
|
5
5
|
const PROMPT_JSON_MAX = 128 * 1024 + 2;
|
|
6
6
|
export const AUTOMATION_REQUEST_MAX = 512;
|
|
7
7
|
export const AUTOMATION_RETENTION_MS = 7 * 24 * 60 * 60 * 1000;
|
|
8
|
-
|
|
8
|
+
const AUTOMATION_ROUTINE_RE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
9
9
|
const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/;
|
|
10
10
|
const STATES = ['dispatched', 'consumed', 'awaiting-completion', 'completed', 'failed', 'interrupted', 'coalesced', 'cancelled'] as const;
|
|
11
11
|
const DETAILS = ['sdk-completed', 'sdk-failed', 'runner-interrupted', 'newer-slot', 'owner-cancelled', 'worker-blocked', 'worker-completed'] as const;
|
|
@@ -29,7 +29,7 @@ const ERRORS = {
|
|
|
29
29
|
|
|
30
30
|
export type AutomationUuid = `${string}-${string}-${string}-${string}-${string}`;
|
|
31
31
|
export type AutomationState = (typeof STATES)[number];
|
|
32
|
-
|
|
32
|
+
type AutomationDetail = (typeof DETAILS)[number];
|
|
33
33
|
export type AutomationOutcome = 'completed' | 'blocked';
|
|
34
34
|
export type AutomationErrorCode = keyof typeof ERRORS;
|
|
35
35
|
|
|
@@ -2,8 +2,6 @@ export type Line = string & { readonly __line: unique symbol };
|
|
|
2
2
|
export const asLine = (s: string): Line => s as Line;
|
|
3
3
|
|
|
4
4
|
const PREFIX = 'metro://';
|
|
5
|
-
const build = (station: string, ...seg: (string | number)[]): Line =>
|
|
6
|
-
asLine(`${PREFIX}${station}/${seg.map(String).join('/')}`);
|
|
7
5
|
|
|
8
6
|
function parseAccountScoped(
|
|
9
7
|
line: Line | string,
|
|
@@ -42,11 +40,7 @@ function parseTelegramLine(
|
|
|
42
40
|
}
|
|
43
41
|
|
|
44
42
|
export const Line = {
|
|
45
|
-
|
|
46
|
-
build('claude', orgId, sessionId),
|
|
47
|
-
webhook: (endpointId: string): Line => build('webhook', endpointId),
|
|
48
|
-
user: (station: string, id: string | number): Line =>
|
|
49
|
-
build(station, 'user', id),
|
|
43
|
+
webhook: (endpointId: string): Line => asLine(`${PREFIX}webhook/${endpointId}`),
|
|
50
44
|
|
|
51
45
|
parse(line: Line | string): { station: string; path: string[] } | null {
|
|
52
46
|
if (!line.startsWith(PREFIX)) return null;
|
|
@@ -68,7 +62,7 @@ export const Line = {
|
|
|
68
62
|
parseDiscord: (line: Line | string) =>
|
|
69
63
|
parseAccountScoped(line, 'discord-bot', isSnowflake),
|
|
70
64
|
|
|
71
|
-
parseTelegram:
|
|
65
|
+
parseTelegram: parseTelegramLine,
|
|
72
66
|
|
|
73
67
|
parseThreema: (line: Line | string) =>
|
|
74
68
|
parseAccountScoped(line, 'threema', isThreemaResource),
|
|
@@ -5,12 +5,6 @@ import { coerceErrorInfo, type TrainErrorInfo } from './train-error.js';
|
|
|
5
5
|
import type { StructuredEvent } from './events.js';
|
|
6
6
|
import type { CallMsg } from './stations/station-runtime.js';
|
|
7
7
|
|
|
8
|
-
export {
|
|
9
|
-
TrainError,
|
|
10
|
-
serializeTrainError,
|
|
11
|
-
type TrainErrorInfo,
|
|
12
|
-
} from './train-error.js';
|
|
13
|
-
|
|
14
8
|
const CALL_TIMEOUT_MS = 60_000;
|
|
15
9
|
|
|
16
10
|
export interface Pending {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isRecord } from '../is-record.js';
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
const ACCOUNT_STATES = ['connected', 'connecting', 'logged_out', 'disconnected'] as const;
|
|
4
4
|
|
|
5
5
|
export type AccountState = (typeof ACCOUNT_STATES)[number];
|
|
6
6
|
|
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
import { randomUUID } from 'node:crypto';
|
|
2
|
+
import { mkdir, open, rename, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
1
5
|
export const attachDir = (): string =>
|
|
2
6
|
process.env.METRO_XMTP_ATTACH_DIR ??
|
|
3
7
|
`${process.env.HOME}/.cache/metro/messenger-uploads`;
|
|
@@ -89,14 +93,9 @@ const cachePathFor = async (
|
|
|
89
93
|
index: number,
|
|
90
94
|
meta: { mime?: string; name?: string },
|
|
91
95
|
): Promise<string> => {
|
|
92
|
-
const { mkdir } = await import('node:fs/promises');
|
|
93
|
-
const nodePath = await import('node:path');
|
|
94
96
|
const dir = attachDir();
|
|
95
97
|
await mkdir(dir, { recursive: true });
|
|
96
|
-
return
|
|
97
|
-
dir,
|
|
98
|
-
cacheFileName(messageId, index, meta.name, meta.mime),
|
|
99
|
-
);
|
|
98
|
+
return join(dir, cacheFileName(messageId, index, meta.name, meta.mime));
|
|
100
99
|
};
|
|
101
100
|
|
|
102
101
|
export const saveBufferToCache = async (
|
|
@@ -106,8 +105,6 @@ export const saveBufferToCache = async (
|
|
|
106
105
|
meta: { mime?: string; name?: string },
|
|
107
106
|
): Promise<SavedAttachment> => {
|
|
108
107
|
assertAttachmentSize(data.length);
|
|
109
|
-
const { writeFile, rename, rm } = await import('node:fs/promises');
|
|
110
|
-
const { randomUUID } = await import('node:crypto');
|
|
111
108
|
const path = await cachePathFor(messageId, index, meta);
|
|
112
109
|
const part = `${await cachePathFor(randomUUID(), index, meta)}.part`;
|
|
113
110
|
try {
|
|
@@ -125,7 +122,6 @@ export const saveStreamToCache = async (
|
|
|
125
122
|
index: number,
|
|
126
123
|
meta: { mime?: string; name?: string },
|
|
127
124
|
): Promise<SavedAttachment> => {
|
|
128
|
-
const { open, rename, rm } = await import('node:fs/promises');
|
|
129
125
|
const path = await cachePathFor(messageId, index, meta);
|
|
130
126
|
const part = `${path}.part`;
|
|
131
127
|
const handle = await open(part, 'w');
|
|
@@ -14,7 +14,7 @@ export interface ChannelEntry {
|
|
|
14
14
|
kind: 'direct' | 'group' | 'channel' | 'thread';
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
interface ChannelCapability {
|
|
18
18
|
supported: boolean;
|
|
19
19
|
complete: boolean;
|
|
20
20
|
source: 'remote' | 'local' | 'mixed' | 'unsupported';
|