Phase B: encrypted archive export bundles (age), power-level-gated export command, encryption-state linter

This commit is contained in:
glass beadian 2026-10-12 12:46:56 +00:00
parent a7ad1f059f
commit 699b9412c1
22 changed files with 2325 additions and 9 deletions

View file

@ -38,6 +38,10 @@ export interface MatrixPort {
joinRoom(roomId: string, signal: AbortSignal): Promise<void>;
downloadMedia(mxcUri: string, signal: AbortSignal): Promise<MediaDownload>;
sendNotice(roomId: string, txnId: string, content: Record<string, unknown>, signal: AbortSignal): Promise<void>;
/** Phase-B export gate: current `m.room.power_levels` for a mapped room. */
roomPowerLevels(roomId: string, signal: AbortSignal): Promise<Record<string, unknown>>;
/** Every room the account has joined (encryption lint input). */
joinedRooms(signal: AbortSignal): Promise<string[]>;
}
const SYNC_TIMEOUT_MS = 30_000;
@ -229,6 +233,21 @@ export class MatrixClient implements MatrixPort {
);
}
async roomPowerLevels(roomId: string, signal: AbortSignal): Promise<Record<string, unknown>> {
const body = record(await this.request(
"GET",
`/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state/m.room.power_levels/`,
{ signal },
));
return body;
}
async joinedRooms(signal: AbortSignal): Promise<string[]> {
const body = record(await this.request("GET", "/_matrix/client/v3/joined_rooms", { signal }));
const joined = body.joined_rooms;
return Array.isArray(joined) ? joined.filter((r): r is string => typeof r === "string") : [];
}
/** Authenticated media endpoint only; the URL is derived from a validated mxc URI, never user-supplied HTTP. */
async downloadMedia(mxcUri: string, signal: AbortSignal): Promise<MediaDownload> {
const { serverName, mediaId } = parseMxc(mxcUri);