@stage-labs/metro 0.1.0-beta.234 → 0.1.0-beta.235
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/package.json +1 -1
- package/runtime/marketplace/plugin/.claude-plugin/plugin.json +1 -1
- package/runtime/node_modules/@metro-labs/xmtp/src/actions-conv.ts +21 -19
- package/runtime/node_modules/@metro-labs/xmtp/src/codecs.ts +9 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/delete-requests.ts +34 -0
- package/runtime/node_modules/@metro-labs/xmtp/src/index.ts +1 -0
- package/runtime/runtime.json +1 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "metro",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.235",
|
|
4
4
|
"description": "Metro for Claude Code, installed by the metro daemon on this machine: every connector the agent holds as its own MCP server relayed through the daemon, the orchestrator-only guard on the main thread, and the standing rules loaded at each session start. No vendor credential ever sits in a config file.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Bonustrack",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IdentifierKind } from '@xmtp/node-sdk';
|
|
1
|
+
import { IdentifierKind, type DecodedMessage } from '@xmtp/node-sdk';
|
|
2
2
|
import { accountForCall, convOf, lineOf, parseLine } from './accounts.js';
|
|
3
3
|
import { respond } from '@metro-labs/core/stations/station-runtime';
|
|
4
4
|
import { resolveMsgId } from './wire.js';
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
groupRemoveMembers,
|
|
15
15
|
} from './group.js';
|
|
16
16
|
import { updateChannelMeta } from './actions-meta.js';
|
|
17
|
+
import { deletedByRequests, isDeleteRequest, superAdminCheck } from './delete-requests.js';
|
|
17
18
|
|
|
18
19
|
type Args = Record<string, unknown>;
|
|
19
20
|
type Handler = (id: string, args: Args) => Promise<void>;
|
|
@@ -42,6 +43,17 @@ function upTo<T extends { id: string }>(all: T[], before: string | undefined): T
|
|
|
42
43
|
return all.slice(0, at);
|
|
43
44
|
}
|
|
44
45
|
|
|
46
|
+
function textOf(m: DecodedMessage): string {
|
|
47
|
+
try {
|
|
48
|
+
const cc: unknown = m.content;
|
|
49
|
+
return typeof cc === 'string' ? cc : `[${m.contentType?.typeId ?? 'unknown'}]`;
|
|
50
|
+
} catch {
|
|
51
|
+
return `[${m.contentType?.typeId ?? 'unknown'}]`;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const DELETED = { text: '[deletedMessage]', contentType: 'deletedMessage' };
|
|
56
|
+
|
|
45
57
|
async function read(id: string, args: Args): Promise<void> {
|
|
46
58
|
const { line, limit, before } = args as { line: string; limit?: number; before?: string };
|
|
47
59
|
const { conv } = await convOf(line);
|
|
@@ -50,28 +62,18 @@ async function read(id: string, args: Args): Promise<void> {
|
|
|
50
62
|
const lim = Math.min(Math.max(1, limit ?? 20), 200);
|
|
51
63
|
await conv.sync().catch(() => undefined);
|
|
52
64
|
const all = await conv.messages();
|
|
53
|
-
const
|
|
65
|
+
const deleted = deletedByRequests(all, superAdminCheck(conv));
|
|
66
|
+
const slice = upTo(all.filter((m) => !isDeleteRequest(m)), before).slice(-lim);
|
|
54
67
|
const parsed = parseLine(line);
|
|
55
68
|
if (!parsed)
|
|
56
69
|
throw new TrainError('NOT_FOUND', `could not parse line ${line}`);
|
|
57
70
|
const acctId = parsed.accountId;
|
|
58
|
-
const messages = slice.map((m) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
} catch {
|
|
65
|
-
text = `[${m.contentType?.typeId ?? 'unknown'}]`;
|
|
66
|
-
}
|
|
67
|
-
return {
|
|
68
|
-
id: m.id,
|
|
69
|
-
ts: new Date(Number(m.sentAtNs / 1_000_000n)).toISOString(),
|
|
70
|
-
from: `metro://xmtp/${acctId}/user/${m.senderInboxId}`,
|
|
71
|
-
text,
|
|
72
|
-
contentType: m.contentType?.typeId ?? 'unknown',
|
|
73
|
-
};
|
|
74
|
-
});
|
|
71
|
+
const messages = slice.map((m) => ({
|
|
72
|
+
id: m.id,
|
|
73
|
+
ts: new Date(Number(m.sentAtNs / 1_000_000n)).toISOString(),
|
|
74
|
+
from: `metro://xmtp/${acctId}/user/${m.senderInboxId}`,
|
|
75
|
+
...(deleted.has(m.id) ? DELETED : { text: textOf(m), contentType: m.contentType?.typeId ?? 'unknown' }),
|
|
76
|
+
}));
|
|
75
77
|
respond(id, { result: { line, count: messages.length, messages } });
|
|
76
78
|
}
|
|
77
79
|
|
|
@@ -200,6 +200,14 @@ export const frameActionText = (c: FrameActionContent): string => {
|
|
|
200
200
|
};
|
|
201
201
|
const FrameActionCodec = makeJsonCodec<FrameActionContent>(ContentTypeFrameAction, frameActionText);
|
|
202
202
|
|
|
203
|
+
export const ContentTypeDeleteRequest: ContentTypeId = {
|
|
204
|
+
authorityId: 'stage.box',
|
|
205
|
+
typeId: 'deleteRequest',
|
|
206
|
+
versionMajor: 1,
|
|
207
|
+
versionMinor: 0,
|
|
208
|
+
};
|
|
209
|
+
const DeleteRequestCodec = makeJsonCodec<{ messageId: string }>(ContentTypeDeleteRequest, () => 'Message deleted');
|
|
210
|
+
|
|
203
211
|
const ContentTypeDeleteMessage: ContentTypeId = {
|
|
204
212
|
authorityId: 'xmtp.org',
|
|
205
213
|
typeId: 'deleteMessage',
|
|
@@ -233,4 +241,5 @@ export const CODECS = (): ContentCodec[] => [
|
|
|
233
241
|
new SignatureReferenceCodec(),
|
|
234
242
|
new FrameCodec(),
|
|
235
243
|
new FrameActionCodec(),
|
|
244
|
+
new DeleteRequestCodec(),
|
|
236
245
|
];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { DecodedMessage } from '@xmtp/node-sdk';
|
|
2
|
+
import { ContentTypeDeleteRequest } from './codecs.js';
|
|
3
|
+
|
|
4
|
+
type Listed = Pick<DecodedMessage, 'id' | 'senderInboxId' | 'contentType' | 'content'>;
|
|
5
|
+
|
|
6
|
+
export const isDeleteRequest = (m: Pick<Listed, 'contentType'>): boolean =>
|
|
7
|
+
m.contentType?.authorityId === ContentTypeDeleteRequest.authorityId &&
|
|
8
|
+
m.contentType.typeId === ContentTypeDeleteRequest.typeId;
|
|
9
|
+
|
|
10
|
+
function targetOf(m: Listed): string | undefined {
|
|
11
|
+
if (!isDeleteRequest(m)) return undefined;
|
|
12
|
+
const id = (m.content as { messageId?: unknown } | undefined)?.messageId;
|
|
13
|
+
return typeof id === 'string' && id ? id : undefined;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function superAdminCheck(conv: unknown): (inboxId: string) => boolean {
|
|
17
|
+
const group = conv as { isSuperAdmin?: (inboxId: string) => boolean };
|
|
18
|
+
return (inboxId) => typeof group.isSuperAdmin === 'function' && group.isSuperAdmin(inboxId);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function deletedByRequests(
|
|
22
|
+
messages: Listed[],
|
|
23
|
+
isSuperAdmin: (inboxId: string) => boolean,
|
|
24
|
+
): Set<string> {
|
|
25
|
+
const senders = new Map(messages.map((m) => [m.id, m.senderInboxId]));
|
|
26
|
+
const deleted = new Set<string>();
|
|
27
|
+
for (const m of messages) {
|
|
28
|
+
const target = targetOf(m);
|
|
29
|
+
const sender = target === undefined ? undefined : senders.get(target);
|
|
30
|
+
if (target !== undefined && sender !== undefined && (sender === m.senderInboxId || isSuperAdmin(m.senderInboxId)))
|
|
31
|
+
deleted.add(target);
|
|
32
|
+
}
|
|
33
|
+
return deleted;
|
|
34
|
+
}
|
package/runtime/runtime.json
CHANGED