@kodelyth/matrix 2026.5.39 → 2026.6.1
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/account-selection-Y50DNJ2l.js +158 -0
- package/dist/active-client-CmFdvPdO.js +20 -0
- package/dist/api.js +12 -0
- package/dist/approval-handler.runtime-BIi4fL0R.js +377 -0
- package/dist/approval-ids-BGHK7PnZ.js +7 -0
- package/dist/approval-reaction-auth-CL0-nCNV.js +27 -0
- package/dist/approval-reactions-nDm2x-K5.js +162 -0
- package/dist/async-lock-SsmtFXtt.js +19 -0
- package/dist/auth-presence.js +26 -0
- package/dist/backup-health-3BHbHxyd.js +60 -0
- package/dist/channel-C0kCyTNB.js +1380 -0
- package/dist/channel-plugin-api.js +2 -0
- package/dist/channel.runtime-CdrdEN-0.js +250 -0
- package/dist/cli-FtY6Nuzw.js +1338 -0
- package/dist/cli-metadata-Dkwua7CB.js +22 -0
- package/dist/cli-metadata.js +2 -0
- package/dist/client-BnohYygh.js +25 -0
- package/dist/client-PhrTwuC4.js +30 -0
- package/dist/client-bootstrap-Mcj8ChJ5.js +114 -0
- package/dist/config-paths-DVvt6vM3.js +114 -0
- package/dist/config-schema-BMGOlhdI.js +308 -0
- package/dist/config-secret-input.runtime-Dv_4Br_f.js +2 -0
- package/dist/contract-api.js +8 -0
- package/dist/create-client-J0htTaRj.js +64 -0
- package/dist/credentials-B7GsBbgQ.js +56 -0
- package/dist/credentials-read-8fE4qoWs.js +112 -0
- package/dist/credentials-write.runtime-BibplB4Y.js +17 -0
- package/dist/crypto-node.runtime-D9qxgRPa.js +12 -0
- package/dist/crypto-runtime-1pKW4O2F.js +1214 -0
- package/dist/deps-DVpDS81G.js +208 -0
- package/dist/device-health-Ct2wDSPG.js +16 -0
- package/dist/directory-live-i3T8uORc.js +150 -0
- package/dist/doctor-contract-BLzYHl_9.js +246 -0
- package/dist/doctor-contract-api.js +2 -0
- package/dist/doctor-diR5gE7D.js +153 -0
- package/dist/draft-stream-HpPJ_VJt.js +143 -0
- package/dist/encryption-guidance-BNEgckrZ.js +15 -0
- package/dist/env-auth-UFiTGkDM.js +63 -0
- package/dist/env-vars-EQKQv-FE.js +63 -0
- package/dist/errors-BETj3zr9.js +17 -0
- package/dist/exec-approval-resolver-BxPorU_t.js +15 -0
- package/dist/helper-api.js +4 -0
- package/dist/http-client-DoQgbQsU.js +331 -0
- package/dist/index.js +46 -0
- package/dist/legacy-crypto-inspector-zK0hDCbt.js +41 -0
- package/dist/legacy-crypto-restore-DSFIXuDo.js +85 -0
- package/dist/logging-Df7aPD1z.js +99 -0
- package/dist/matrix-migration.runtime-BNoT1Prt.js +525 -0
- package/dist/media-text-ZhGA8Pcs.js +146 -0
- package/dist/messages-CRA9WGg0.js +140 -0
- package/dist/migration-snapshot-backup-BR-xD7Ew.js +69 -0
- package/dist/migration-snapshot.runtime-BLcy_Nvw.js +2 -0
- package/dist/monitor-DQm7_13y.js +4331 -0
- package/dist/plugin-entry.handlers.runtime.js +51 -0
- package/dist/probe.runtime-CjJS53Kz.js +3 -0
- package/dist/profile-update-DqkPgZ1P.js +68 -0
- package/dist/reaction-common-CmVLzP-u.js +71 -0
- package/dist/reaction-events-D0nUJuZV.js +121 -0
- package/dist/record-shared-DGvSFn5M.js +2 -0
- package/dist/resolve-targets-ChECUzD2.js +140 -0
- package/dist/resolver.runtime-hdY3n0GO.js +5 -0
- package/dist/rolldown-runtime-DUslC3ob.js +14 -0
- package/dist/route-xRKj_ESW.js +161 -0
- package/dist/runtime-B-Fyrmxo.js +8 -0
- package/dist/runtime-api-BYXXkxq2.js +24 -0
- package/dist/runtime-api.js +25 -0
- package/dist/runtime-heavy-api.js +3 -0
- package/dist/runtime-lwTSy9Yt.js +6 -0
- package/dist/runtime-setter-api.js +2 -0
- package/dist/sdk-Jhq7mLtD.js +1704 -0
- package/dist/secret-contract-DEMcDsjl.js +120 -0
- package/dist/secret-contract-api.js +2 -0
- package/dist/send-CJunc6QM.js +1517 -0
- package/dist/setup-bootstrap-rJ0qZWPe.js +62 -0
- package/dist/setup-core-BEYoXF3J.js +677 -0
- package/dist/setup-entry.js +19 -0
- package/dist/setup-plugin-api.js +43 -0
- package/dist/setup-surface-c28ON6jq.js +537 -0
- package/dist/shared-D6MFMnpG.js +642 -0
- package/dist/startup-abort-B2J3MU_h.js +109 -0
- package/dist/startup-verification-CkD4Cwce.js +132 -0
- package/dist/storage-nyO0DOFE.js +281 -0
- package/dist/storage-paths-BTAketfg.js +52 -0
- package/dist/subagent-hooks-api-Dr_xnMRG.js +170 -0
- package/dist/subagent-hooks-api.js +2 -0
- package/dist/sync-state-Bx0gPaGA.js +12 -0
- package/dist/target-ids-Bsazo8si.js +77 -0
- package/dist/test-api.js +4 -0
- package/dist/thread-binding-api-IGU0-L70.js +17 -0
- package/dist/thread-binding-api.js +2 -0
- package/dist/thread-bindings-FjAZmDUP.js +352 -0
- package/dist/thread-bindings-runtime.js +2 -0
- package/dist/thread-bindings-shared-fvfP7jVs.js +97 -0
- package/dist/timeout-abort-signal-DpSHDHhR.js +2 -0
- package/dist/tool-actions.runtime-Cbo7YcYZ.js +532 -0
- package/dist/url-validation-DlrXNjAE.js +36 -0
- package/dist/verification-7tDPRpJU.js +345 -0
- package/package.json +19 -7
- package/api.js +0 -7
- package/auth-presence.js +0 -7
- package/channel-plugin-api.js +0 -7
- package/cli-metadata.js +0 -7
- package/contract-api.js +0 -7
- package/doctor-contract-api.js +0 -7
- package/helper-api.js +0 -7
- package/index.js +0 -7
- package/plugin-entry.handlers.runtime.js +0 -7
- package/runtime-api.js +0 -7
- package/runtime-heavy-api.js +0 -7
- package/runtime-setter-api.js +0 -7
- package/secret-contract-api.js +0 -7
- package/setup-entry.js +0 -7
- package/setup-plugin-api.js +0 -7
- package/subagent-hooks-api.js +0 -7
- package/test-api.js +0 -7
- package/thread-binding-api.js +0 -7
- package/thread-bindings-runtime.js +0 -7
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { resolveThreadBindingLifecycle } from "klaw/plugin-sdk/thread-bindings-session-runtime";
|
|
2
|
+
//#region extensions/matrix/src/matrix/thread-bindings-shared.ts
|
|
3
|
+
const MANAGERS_BY_ACCOUNT_ID = /* @__PURE__ */ new Map();
|
|
4
|
+
const BINDINGS_BY_ACCOUNT_CONVERSATION = /* @__PURE__ */ new Map();
|
|
5
|
+
function resolveBindingKey(params) {
|
|
6
|
+
return `${params.accountId}:${params.parentConversationId?.trim() || "-"}:${params.conversationId}`;
|
|
7
|
+
}
|
|
8
|
+
function toSessionBindingTargetKind(raw) {
|
|
9
|
+
return raw === "subagent" ? "subagent" : "session";
|
|
10
|
+
}
|
|
11
|
+
function toMatrixBindingTargetKind(raw) {
|
|
12
|
+
return raw === "subagent" ? "subagent" : "acp";
|
|
13
|
+
}
|
|
14
|
+
function resolveEffectiveBindingExpiry(params) {
|
|
15
|
+
return resolveThreadBindingLifecycle(params);
|
|
16
|
+
}
|
|
17
|
+
function toSessionBindingRecord(record, defaults) {
|
|
18
|
+
const lifecycle = resolveEffectiveBindingExpiry({
|
|
19
|
+
record,
|
|
20
|
+
defaultIdleTimeoutMs: defaults.idleTimeoutMs,
|
|
21
|
+
defaultMaxAgeMs: defaults.maxAgeMs
|
|
22
|
+
});
|
|
23
|
+
const idleTimeoutMs = typeof record.idleTimeoutMs === "number" ? record.idleTimeoutMs : defaults.idleTimeoutMs;
|
|
24
|
+
const maxAgeMs = typeof record.maxAgeMs === "number" ? record.maxAgeMs : defaults.maxAgeMs;
|
|
25
|
+
return {
|
|
26
|
+
bindingId: resolveBindingKey(record),
|
|
27
|
+
targetSessionKey: record.targetSessionKey,
|
|
28
|
+
targetKind: toSessionBindingTargetKind(record.targetKind),
|
|
29
|
+
conversation: {
|
|
30
|
+
channel: "matrix",
|
|
31
|
+
accountId: record.accountId,
|
|
32
|
+
conversationId: record.conversationId,
|
|
33
|
+
parentConversationId: record.parentConversationId
|
|
34
|
+
},
|
|
35
|
+
status: "active",
|
|
36
|
+
boundAt: record.boundAt,
|
|
37
|
+
expiresAt: lifecycle.expiresAt,
|
|
38
|
+
metadata: {
|
|
39
|
+
agentId: record.agentId,
|
|
40
|
+
label: record.label,
|
|
41
|
+
boundBy: record.boundBy,
|
|
42
|
+
lastActivityAt: record.lastActivityAt,
|
|
43
|
+
idleTimeoutMs,
|
|
44
|
+
maxAgeMs
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
function setBindingRecord(record) {
|
|
49
|
+
BINDINGS_BY_ACCOUNT_CONVERSATION.set(resolveBindingKey(record), record);
|
|
50
|
+
}
|
|
51
|
+
function removeBindingRecord(record) {
|
|
52
|
+
const key = resolveBindingKey(record);
|
|
53
|
+
const removed = BINDINGS_BY_ACCOUNT_CONVERSATION.get(key) ?? null;
|
|
54
|
+
if (removed) BINDINGS_BY_ACCOUNT_CONVERSATION.delete(key);
|
|
55
|
+
return removed;
|
|
56
|
+
}
|
|
57
|
+
function listBindingsForAccount(accountId) {
|
|
58
|
+
return [...BINDINGS_BY_ACCOUNT_CONVERSATION.values()].filter((entry) => entry.accountId === accountId);
|
|
59
|
+
}
|
|
60
|
+
function listAllBindings() {
|
|
61
|
+
return [...BINDINGS_BY_ACCOUNT_CONVERSATION.values()];
|
|
62
|
+
}
|
|
63
|
+
function getMatrixThreadBindingManagerEntry(accountId) {
|
|
64
|
+
return MANAGERS_BY_ACCOUNT_ID.get(accountId) ?? null;
|
|
65
|
+
}
|
|
66
|
+
function setMatrixThreadBindingManagerEntry(accountId, entry) {
|
|
67
|
+
MANAGERS_BY_ACCOUNT_ID.set(accountId, entry);
|
|
68
|
+
}
|
|
69
|
+
function deleteMatrixThreadBindingManagerEntry(accountId) {
|
|
70
|
+
MANAGERS_BY_ACCOUNT_ID.delete(accountId);
|
|
71
|
+
}
|
|
72
|
+
function getMatrixThreadBindingManager(accountId) {
|
|
73
|
+
return MANAGERS_BY_ACCOUNT_ID.get(accountId)?.manager ?? null;
|
|
74
|
+
}
|
|
75
|
+
function setMatrixThreadBindingIdleTimeoutBySessionKey(params) {
|
|
76
|
+
const manager = MANAGERS_BY_ACCOUNT_ID.get(params.accountId)?.manager;
|
|
77
|
+
if (!manager) return [];
|
|
78
|
+
return manager.setIdleTimeoutBySessionKey(params).map((record) => toSessionBindingRecord(record, {
|
|
79
|
+
idleTimeoutMs: manager.getIdleTimeoutMs(),
|
|
80
|
+
maxAgeMs: manager.getMaxAgeMs()
|
|
81
|
+
}));
|
|
82
|
+
}
|
|
83
|
+
function setMatrixThreadBindingMaxAgeBySessionKey(params) {
|
|
84
|
+
const manager = MANAGERS_BY_ACCOUNT_ID.get(params.accountId)?.manager;
|
|
85
|
+
if (!manager) return [];
|
|
86
|
+
return manager.setMaxAgeBySessionKey(params).map((record) => toSessionBindingRecord(record, {
|
|
87
|
+
idleTimeoutMs: manager.getIdleTimeoutMs(),
|
|
88
|
+
maxAgeMs: manager.getMaxAgeMs()
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
function resetMatrixThreadBindingsForTests() {
|
|
92
|
+
for (const { manager } of MANAGERS_BY_ACCOUNT_ID.values()) manager.stop();
|
|
93
|
+
MANAGERS_BY_ACCOUNT_ID.clear();
|
|
94
|
+
BINDINGS_BY_ACCOUNT_CONVERSATION.clear();
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { listBindingsForAccount as a, resolveBindingKey as c, setMatrixThreadBindingIdleTimeoutBySessionKey as d, setMatrixThreadBindingManagerEntry as f, toSessionBindingRecord as h, listAllBindings as i, resolveEffectiveBindingExpiry as l, toMatrixBindingTargetKind as m, getMatrixThreadBindingManager as n, removeBindingRecord as o, setMatrixThreadBindingMaxAgeBySessionKey as p, getMatrixThreadBindingManagerEntry as r, resetMatrixThreadBindingsForTests as s, deleteMatrixThreadBindingManagerEntry as t, setBindingRecord as u };
|
|
@@ -0,0 +1,532 @@
|
|
|
1
|
+
import { c as resolveMatrixAccountConfig } from "./config-paths-DVvt6vM3.js";
|
|
2
|
+
import "./setup-core-BEYoXF3J.js";
|
|
3
|
+
import { E as parsePollStart, T as isPollStartType, b as buildPollResponseContent, i as reactMatrixMessage, u as resolveMatrixRoomId } from "./send-CJunc6QM.js";
|
|
4
|
+
import { i as buildMatrixReactionRelationsPath, o as selectOwnMatrixReactionEventIds, s as summarizeMatrixReactionEvents } from "./reaction-common-CmVLzP-u.js";
|
|
5
|
+
import { n as withResolvedActionClient, r as withResolvedRoomAction } from "./client-BnohYygh.js";
|
|
6
|
+
import { a as fetchEventSummary, c as resolveMatrixActionLimit, i as sendMatrixMessage, n as editMatrixMessage, o as readPinnedEvents, r as readMatrixMessages, s as EventType, t as deleteMatrixMessage } from "./messages-CRA9WGg0.js";
|
|
7
|
+
import { a as jsonResult, c as readStringArrayParam, l as readStringParam, o as readNumberParam, r as createActionGate, s as readReactionParams } from "./runtime-api-BYXXkxq2.js";
|
|
8
|
+
import { t as applyMatrixProfileUpdate } from "./profile-update-DqkPgZ1P.js";
|
|
9
|
+
import { _ as scanMatrixVerificationQr, a as confirmMatrixVerificationSas, b as verifyMatrixRecoveryKey, c as getMatrixRoomKeyBackupStatus, d as listMatrixVerifications, f as mismatchMatrixVerificationSas, h as restoreMatrixRoomKeyBackup, i as confirmMatrixVerificationReciprocateQr, l as getMatrixVerificationSas, n as bootstrapMatrixVerification, o as generateMatrixVerificationQr, p as requestMatrixVerification, r as cancelMatrixVerification, s as getMatrixEncryptionStatus, t as acceptMatrixVerification, u as getMatrixVerificationStatus, v as startMatrixVerification } from "./verification-7tDPRpJU.js";
|
|
10
|
+
import { normalizeOptionalLowercaseString } from "klaw/plugin-sdk/string-coerce-runtime";
|
|
11
|
+
//#region extensions/matrix/src/matrix/actions/polls.ts
|
|
12
|
+
function normalizeOptionIndexes(indexes) {
|
|
13
|
+
const normalized = indexes.map((index) => Math.trunc(index)).filter((index) => Number.isFinite(index) && index > 0);
|
|
14
|
+
return Array.from(new Set(normalized));
|
|
15
|
+
}
|
|
16
|
+
function normalizeOptionIds(optionIds) {
|
|
17
|
+
return Array.from(new Set(optionIds.map((optionId) => optionId.trim()).filter((optionId) => optionId.length > 0)));
|
|
18
|
+
}
|
|
19
|
+
function resolveSelectedAnswerIds(params) {
|
|
20
|
+
const parsed = parsePollStart(params.pollContent);
|
|
21
|
+
if (!parsed) throw new Error("Matrix poll vote requires a valid poll start event.");
|
|
22
|
+
const selectedById = normalizeOptionIds(params.optionIds ?? []);
|
|
23
|
+
const selectedByIndex = normalizeOptionIndexes(params.optionIndexes ?? []).map((index) => {
|
|
24
|
+
const answer = parsed.answers[index - 1];
|
|
25
|
+
if (!answer) throw new Error(`Matrix poll option index ${index} is out of range for a poll with ${parsed.answers.length} options.`);
|
|
26
|
+
return answer.id;
|
|
27
|
+
});
|
|
28
|
+
const answerIds = normalizeOptionIds([...selectedById, ...selectedByIndex]);
|
|
29
|
+
if (answerIds.length === 0) throw new Error("Matrix poll vote requires at least one poll option id or index.");
|
|
30
|
+
if (answerIds.length > parsed.maxSelections) throw new Error(`Matrix poll allows at most ${parsed.maxSelections} selection${parsed.maxSelections === 1 ? "" : "s"}.`);
|
|
31
|
+
const answerMap = new Map(parsed.answers.map((answer) => [answer.id, answer.text]));
|
|
32
|
+
return {
|
|
33
|
+
answerIds,
|
|
34
|
+
labels: answerIds.map((answerId) => {
|
|
35
|
+
const label = answerMap.get(answerId);
|
|
36
|
+
if (!label) throw new Error(`Matrix poll option id "${answerId}" is not valid for poll ${parsed.question}.`);
|
|
37
|
+
return label;
|
|
38
|
+
}),
|
|
39
|
+
maxSelections: parsed.maxSelections
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
async function voteMatrixPoll(roomId, pollId, opts = {}) {
|
|
43
|
+
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
|
|
44
|
+
const pollEvent = await client.getEvent(resolvedRoom, pollId);
|
|
45
|
+
if (!isPollStartType(typeof pollEvent.type === "string" ? pollEvent.type : "")) throw new Error(`Event ${pollId} is not a Matrix poll start event.`);
|
|
46
|
+
const { answerIds, labels, maxSelections } = resolveSelectedAnswerIds({
|
|
47
|
+
optionIds: [...opts.optionIds ?? [], ...opts.optionId ? [opts.optionId] : []],
|
|
48
|
+
optionIndexes: [...opts.optionIndexes ?? [], ...opts.optionIndex !== void 0 ? [opts.optionIndex] : []],
|
|
49
|
+
pollContent: pollEvent.content
|
|
50
|
+
});
|
|
51
|
+
const content = buildPollResponseContent(pollId, answerIds);
|
|
52
|
+
return {
|
|
53
|
+
eventId: await client.sendEvent(resolvedRoom, "m.poll.response", content) ?? null,
|
|
54
|
+
roomId: resolvedRoom,
|
|
55
|
+
pollId,
|
|
56
|
+
answerIds,
|
|
57
|
+
labels,
|
|
58
|
+
maxSelections
|
|
59
|
+
};
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region extensions/matrix/src/matrix/actions/reactions.ts
|
|
64
|
+
async function listMatrixReactionEvents(client, roomId, messageId, limit) {
|
|
65
|
+
const res = await client.doRequest("GET", buildMatrixReactionRelationsPath(roomId, messageId), {
|
|
66
|
+
dir: "b",
|
|
67
|
+
limit
|
|
68
|
+
});
|
|
69
|
+
return Array.isArray(res.chunk) ? res.chunk : [];
|
|
70
|
+
}
|
|
71
|
+
async function listMatrixReactions(roomId, messageId, opts = {}) {
|
|
72
|
+
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
|
|
73
|
+
return summarizeMatrixReactionEvents(await listMatrixReactionEvents(client, resolvedRoom, messageId, resolveMatrixActionLimit(opts.limit, 100)));
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
async function removeMatrixReactions(roomId, messageId, opts = {}) {
|
|
77
|
+
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
|
|
78
|
+
const chunk = await listMatrixReactionEvents(client, resolvedRoom, messageId, 200);
|
|
79
|
+
const userId = await client.getUserId();
|
|
80
|
+
if (!userId) return { removed: 0 };
|
|
81
|
+
const toRemove = selectOwnMatrixReactionEventIds(chunk, userId, opts.emoji);
|
|
82
|
+
if (toRemove.length === 0) return { removed: 0 };
|
|
83
|
+
await Promise.all(toRemove.map((id) => client.redactEvent(resolvedRoom, id)));
|
|
84
|
+
return { removed: toRemove.length };
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
//#endregion
|
|
88
|
+
//#region extensions/matrix/src/matrix/actions/pins.ts
|
|
89
|
+
async function updateMatrixPins(roomId, opts, update) {
|
|
90
|
+
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
|
|
91
|
+
const next = update(await readPinnedEvents(client, resolvedRoom));
|
|
92
|
+
const payload = { pinned: next };
|
|
93
|
+
await client.sendStateEvent(resolvedRoom, EventType.RoomPinnedEvents, "", payload);
|
|
94
|
+
return { pinned: next };
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
async function pinMatrixMessage(roomId, messageId, opts = {}) {
|
|
98
|
+
return await updateMatrixPins(roomId, opts, (current) => current.includes(messageId) ? current : [...current, messageId]);
|
|
99
|
+
}
|
|
100
|
+
async function unpinMatrixMessage(roomId, messageId, opts = {}) {
|
|
101
|
+
return await updateMatrixPins(roomId, opts, (current) => current.filter((id) => id !== messageId));
|
|
102
|
+
}
|
|
103
|
+
async function listMatrixPins(roomId, opts = {}) {
|
|
104
|
+
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
|
|
105
|
+
const pinned = await readPinnedEvents(client, resolvedRoom);
|
|
106
|
+
return {
|
|
107
|
+
pinned,
|
|
108
|
+
events: (await Promise.all(pinned.map(async (eventId) => {
|
|
109
|
+
try {
|
|
110
|
+
return await fetchEventSummary(client, resolvedRoom, eventId);
|
|
111
|
+
} catch {
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
}))).filter((event) => Boolean(event))
|
|
115
|
+
};
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region extensions/matrix/src/matrix/actions/room.ts
|
|
120
|
+
async function getMatrixMemberInfo(userId, opts = {}) {
|
|
121
|
+
return await withResolvedActionClient(opts, async (client) => {
|
|
122
|
+
const roomId = opts.roomId ? await resolveMatrixRoomId(client, opts.roomId) : void 0;
|
|
123
|
+
const profile = await client.getUserProfile(userId);
|
|
124
|
+
return {
|
|
125
|
+
userId,
|
|
126
|
+
profile: {
|
|
127
|
+
displayName: profile?.displayname ?? null,
|
|
128
|
+
avatarUrl: profile?.avatar_url ?? null
|
|
129
|
+
},
|
|
130
|
+
membership: null,
|
|
131
|
+
powerLevel: null,
|
|
132
|
+
displayName: profile?.displayname ?? null,
|
|
133
|
+
roomId: roomId ?? null
|
|
134
|
+
};
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
async function getMatrixRoomInfo(roomId, opts = {}) {
|
|
138
|
+
return await withResolvedRoomAction(roomId, opts, async (client, resolvedRoom) => {
|
|
139
|
+
let name = null;
|
|
140
|
+
let topic = null;
|
|
141
|
+
let canonicalAlias = null;
|
|
142
|
+
let memberCount = null;
|
|
143
|
+
try {
|
|
144
|
+
const nameState = await client.getRoomStateEvent(resolvedRoom, "m.room.name", "");
|
|
145
|
+
name = typeof nameState?.name === "string" ? nameState.name : null;
|
|
146
|
+
} catch {}
|
|
147
|
+
try {
|
|
148
|
+
const topicState = await client.getRoomStateEvent(resolvedRoom, EventType.RoomTopic, "");
|
|
149
|
+
topic = typeof topicState?.topic === "string" ? topicState.topic : null;
|
|
150
|
+
} catch {}
|
|
151
|
+
try {
|
|
152
|
+
const aliasState = await client.getRoomStateEvent(resolvedRoom, "m.room.canonical_alias", "");
|
|
153
|
+
canonicalAlias = typeof aliasState?.alias === "string" ? aliasState.alias : null;
|
|
154
|
+
} catch {}
|
|
155
|
+
try {
|
|
156
|
+
memberCount = (await client.getJoinedRoomMembers(resolvedRoom)).length;
|
|
157
|
+
} catch {}
|
|
158
|
+
return {
|
|
159
|
+
roomId: resolvedRoom,
|
|
160
|
+
name,
|
|
161
|
+
topic,
|
|
162
|
+
canonicalAlias,
|
|
163
|
+
altAliases: [],
|
|
164
|
+
memberCount
|
|
165
|
+
};
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region extensions/matrix/src/tool-actions.ts
|
|
170
|
+
const messageActions = new Set([
|
|
171
|
+
"sendMessage",
|
|
172
|
+
"editMessage",
|
|
173
|
+
"deleteMessage",
|
|
174
|
+
"readMessages"
|
|
175
|
+
]);
|
|
176
|
+
const reactionActions = new Set(["react", "reactions"]);
|
|
177
|
+
const pinActions = new Set([
|
|
178
|
+
"pinMessage",
|
|
179
|
+
"unpinMessage",
|
|
180
|
+
"listPins"
|
|
181
|
+
]);
|
|
182
|
+
const pollActions = new Set(["pollVote"]);
|
|
183
|
+
const profileActions = new Set(["setProfile"]);
|
|
184
|
+
const verificationActions = new Set([
|
|
185
|
+
"encryptionStatus",
|
|
186
|
+
"verificationList",
|
|
187
|
+
"verificationRequest",
|
|
188
|
+
"verificationAccept",
|
|
189
|
+
"verificationCancel",
|
|
190
|
+
"verificationStart",
|
|
191
|
+
"verificationGenerateQr",
|
|
192
|
+
"verificationScanQr",
|
|
193
|
+
"verificationSas",
|
|
194
|
+
"verificationConfirm",
|
|
195
|
+
"verificationMismatch",
|
|
196
|
+
"verificationConfirmQr",
|
|
197
|
+
"verificationStatus",
|
|
198
|
+
"verificationBootstrap",
|
|
199
|
+
"verificationRecoveryKey",
|
|
200
|
+
"verificationBackupStatus",
|
|
201
|
+
"verificationBackupRestore"
|
|
202
|
+
]);
|
|
203
|
+
function readRoomId(params, required = true) {
|
|
204
|
+
const direct = readStringParam(params, "roomId") ?? readStringParam(params, "channelId");
|
|
205
|
+
if (direct) return direct;
|
|
206
|
+
if (!required) return readStringParam(params, "to") ?? "";
|
|
207
|
+
return readStringParam(params, "to", { required: true });
|
|
208
|
+
}
|
|
209
|
+
function toSnakeCaseKey(key) {
|
|
210
|
+
return normalizeOptionalLowercaseString(key.replace(/([A-Z]+)([A-Z][a-z])/g, "$1_$2").replace(/([a-z0-9])([A-Z])/g, "$1_$2"));
|
|
211
|
+
}
|
|
212
|
+
function readRawParam(params, key) {
|
|
213
|
+
if (Object.hasOwn(params, key)) return params[key];
|
|
214
|
+
const snakeKey = toSnakeCaseKey(key);
|
|
215
|
+
if (snakeKey !== key && Object.hasOwn(params, snakeKey)) return params[snakeKey];
|
|
216
|
+
}
|
|
217
|
+
function readStringAliasParam(params, keys, options = {}) {
|
|
218
|
+
for (const key of keys) {
|
|
219
|
+
const raw = readRawParam(params, key);
|
|
220
|
+
if (typeof raw !== "string") continue;
|
|
221
|
+
const trimmed = raw.trim();
|
|
222
|
+
if (trimmed) return trimmed;
|
|
223
|
+
}
|
|
224
|
+
if (options.required) throw new Error(`${keys[0]} required`);
|
|
225
|
+
}
|
|
226
|
+
function readNumericArrayParam(params, key, options = {}) {
|
|
227
|
+
const { integer = false } = options;
|
|
228
|
+
const raw = readRawParam(params, key);
|
|
229
|
+
if (raw === void 0) return [];
|
|
230
|
+
return (Array.isArray(raw) ? raw : [raw]).map((value) => {
|
|
231
|
+
if (typeof value === "number" && Number.isFinite(value)) return value;
|
|
232
|
+
if (typeof value === "string") {
|
|
233
|
+
const trimmed = value.trim();
|
|
234
|
+
if (!trimmed) return null;
|
|
235
|
+
const parsed = Number(trimmed);
|
|
236
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
237
|
+
}
|
|
238
|
+
return null;
|
|
239
|
+
}).filter((value) => value !== null).map((value) => integer ? Math.trunc(value) : value);
|
|
240
|
+
}
|
|
241
|
+
async function handleMatrixAction(params, cfg, opts = {}) {
|
|
242
|
+
const action = readStringParam(params, "action", { required: true });
|
|
243
|
+
const accountId = readStringParam(params, "accountId") ?? void 0;
|
|
244
|
+
const isActionEnabled = createActionGate(resolveMatrixAccountConfig({
|
|
245
|
+
cfg,
|
|
246
|
+
accountId
|
|
247
|
+
}).actions);
|
|
248
|
+
const clientOpts = {
|
|
249
|
+
cfg,
|
|
250
|
+
...accountId ? { accountId } : {}
|
|
251
|
+
};
|
|
252
|
+
if (reactionActions.has(action)) {
|
|
253
|
+
if (!isActionEnabled("reactions")) throw new Error("Matrix reactions are disabled.");
|
|
254
|
+
const roomId = readRoomId(params);
|
|
255
|
+
const messageId = readStringParam(params, "messageId", { required: true });
|
|
256
|
+
if (action === "react") {
|
|
257
|
+
const { emoji, remove, isEmpty } = readReactionParams(params, { removeErrorMessage: "Emoji is required to remove a Matrix reaction." });
|
|
258
|
+
if (remove || isEmpty) return jsonResult({
|
|
259
|
+
ok: true,
|
|
260
|
+
removed: (await removeMatrixReactions(roomId, messageId, {
|
|
261
|
+
...clientOpts,
|
|
262
|
+
emoji: remove ? emoji : void 0
|
|
263
|
+
})).removed
|
|
264
|
+
});
|
|
265
|
+
await reactMatrixMessage(roomId, messageId, emoji, clientOpts);
|
|
266
|
+
return jsonResult({
|
|
267
|
+
ok: true,
|
|
268
|
+
added: emoji
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
const limit = readNumberParam(params, "limit", { integer: true });
|
|
272
|
+
return jsonResult({
|
|
273
|
+
ok: true,
|
|
274
|
+
reactions: await listMatrixReactions(roomId, messageId, {
|
|
275
|
+
...clientOpts,
|
|
276
|
+
limit: limit ?? void 0
|
|
277
|
+
})
|
|
278
|
+
});
|
|
279
|
+
}
|
|
280
|
+
if (pollActions.has(action)) {
|
|
281
|
+
const roomId = readRoomId(params);
|
|
282
|
+
const pollId = readStringAliasParam(params, ["pollId", "messageId"], { required: true });
|
|
283
|
+
if (!pollId) throw new Error("pollId required");
|
|
284
|
+
const optionId = readStringParam(params, "pollOptionId");
|
|
285
|
+
const optionIndex = readNumberParam(params, "pollOptionIndex", { integer: true });
|
|
286
|
+
const optionIds = [...readStringArrayParam(params, "pollOptionIds") ?? [], ...optionId ? [optionId] : []];
|
|
287
|
+
const optionIndexes = [...readNumericArrayParam(params, "pollOptionIndexes", { integer: true }), ...optionIndex !== void 0 ? [optionIndex] : []];
|
|
288
|
+
return jsonResult({
|
|
289
|
+
ok: true,
|
|
290
|
+
result: await voteMatrixPoll(roomId, pollId, {
|
|
291
|
+
...clientOpts,
|
|
292
|
+
optionIds,
|
|
293
|
+
optionIndexes
|
|
294
|
+
})
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
if (messageActions.has(action)) {
|
|
298
|
+
if (!isActionEnabled("messages")) throw new Error("Matrix messages are disabled.");
|
|
299
|
+
switch (action) {
|
|
300
|
+
case "sendMessage": {
|
|
301
|
+
const to = readStringParam(params, "to", { required: true });
|
|
302
|
+
const mediaUrl = readStringParam(params, "mediaUrl", { trim: false }) ?? readStringParam(params, "media", { trim: false }) ?? readStringParam(params, "filePath", { trim: false }) ?? readStringParam(params, "path", { trim: false });
|
|
303
|
+
const content = readStringParam(params, "content", {
|
|
304
|
+
required: !mediaUrl,
|
|
305
|
+
allowEmpty: true
|
|
306
|
+
});
|
|
307
|
+
const replyToId = readStringParam(params, "replyToId") ?? readStringParam(params, "replyTo");
|
|
308
|
+
const threadId = readStringParam(params, "threadId");
|
|
309
|
+
const audioAsVoice = typeof readRawParam(params, "audioAsVoice") === "boolean" ? readRawParam(params, "audioAsVoice") : typeof readRawParam(params, "asVoice") === "boolean" ? readRawParam(params, "asVoice") : void 0;
|
|
310
|
+
return jsonResult({
|
|
311
|
+
ok: true,
|
|
312
|
+
result: await sendMatrixMessage(to, content, {
|
|
313
|
+
mediaUrl: mediaUrl ?? void 0,
|
|
314
|
+
mediaLocalRoots: opts.mediaLocalRoots,
|
|
315
|
+
replyToId: replyToId ?? void 0,
|
|
316
|
+
threadId: threadId ?? void 0,
|
|
317
|
+
audioAsVoice,
|
|
318
|
+
...clientOpts
|
|
319
|
+
})
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
case "editMessage": return jsonResult({
|
|
323
|
+
ok: true,
|
|
324
|
+
result: await editMatrixMessage(readRoomId(params), readStringParam(params, "messageId", { required: true }), readStringParam(params, "content", { required: true }), clientOpts)
|
|
325
|
+
});
|
|
326
|
+
case "deleteMessage":
|
|
327
|
+
await deleteMatrixMessage(readRoomId(params), readStringParam(params, "messageId", { required: true }), {
|
|
328
|
+
reason: readStringParam(params, "reason") ?? void 0,
|
|
329
|
+
...clientOpts
|
|
330
|
+
});
|
|
331
|
+
return jsonResult({
|
|
332
|
+
ok: true,
|
|
333
|
+
deleted: true
|
|
334
|
+
});
|
|
335
|
+
case "readMessages": {
|
|
336
|
+
const roomId = readRoomId(params);
|
|
337
|
+
const limit = readNumberParam(params, "limit", { integer: true });
|
|
338
|
+
const before = readStringParam(params, "before");
|
|
339
|
+
const after = readStringParam(params, "after");
|
|
340
|
+
return jsonResult({
|
|
341
|
+
ok: true,
|
|
342
|
+
...await readMatrixMessages(roomId, {
|
|
343
|
+
limit: limit ?? void 0,
|
|
344
|
+
before: before ?? void 0,
|
|
345
|
+
after: after ?? void 0,
|
|
346
|
+
...clientOpts
|
|
347
|
+
})
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
default: break;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
if (pinActions.has(action)) {
|
|
354
|
+
if (!isActionEnabled("pins")) throw new Error("Matrix pins are disabled.");
|
|
355
|
+
const roomId = readRoomId(params);
|
|
356
|
+
if (action === "pinMessage") return jsonResult({
|
|
357
|
+
ok: true,
|
|
358
|
+
pinned: (await pinMatrixMessage(roomId, readStringParam(params, "messageId", { required: true }), clientOpts)).pinned
|
|
359
|
+
});
|
|
360
|
+
if (action === "unpinMessage") return jsonResult({
|
|
361
|
+
ok: true,
|
|
362
|
+
pinned: (await unpinMatrixMessage(roomId, readStringParam(params, "messageId", { required: true }), clientOpts)).pinned
|
|
363
|
+
});
|
|
364
|
+
const result = await listMatrixPins(roomId, clientOpts);
|
|
365
|
+
return jsonResult({
|
|
366
|
+
ok: true,
|
|
367
|
+
pinned: result.pinned,
|
|
368
|
+
events: result.events
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
if (profileActions.has(action)) {
|
|
372
|
+
if (!isActionEnabled("profile")) throw new Error("Matrix profile updates are disabled.");
|
|
373
|
+
const avatarPath = readStringParam(params, "avatarPath") ?? readStringParam(params, "path") ?? readStringParam(params, "filePath");
|
|
374
|
+
return jsonResult({
|
|
375
|
+
ok: true,
|
|
376
|
+
...await applyMatrixProfileUpdate({
|
|
377
|
+
cfg,
|
|
378
|
+
account: accountId,
|
|
379
|
+
displayName: readStringParam(params, "displayName") ?? readStringParam(params, "name"),
|
|
380
|
+
avatarUrl: readStringParam(params, "avatarUrl"),
|
|
381
|
+
avatarPath,
|
|
382
|
+
mediaLocalRoots: opts.mediaLocalRoots
|
|
383
|
+
})
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
if (action === "memberInfo") {
|
|
387
|
+
if (!isActionEnabled("memberInfo")) throw new Error("Matrix member info is disabled.");
|
|
388
|
+
return jsonResult({
|
|
389
|
+
ok: true,
|
|
390
|
+
member: await getMatrixMemberInfo(readStringParam(params, "userId", { required: true }), {
|
|
391
|
+
roomId: readStringParam(params, "roomId") ?? readStringParam(params, "channelId") ?? void 0,
|
|
392
|
+
...clientOpts
|
|
393
|
+
})
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
if (action === "channelInfo") {
|
|
397
|
+
if (!isActionEnabled("channelInfo")) throw new Error("Matrix room info is disabled.");
|
|
398
|
+
return jsonResult({
|
|
399
|
+
ok: true,
|
|
400
|
+
room: await getMatrixRoomInfo(readRoomId(params), clientOpts)
|
|
401
|
+
});
|
|
402
|
+
}
|
|
403
|
+
if (verificationActions.has(action)) {
|
|
404
|
+
if (!isActionEnabled("verification")) throw new Error("Matrix verification actions are disabled.");
|
|
405
|
+
const requestId = readStringParam(params, "requestId") ?? readStringParam(params, "verificationId") ?? readStringParam(params, "id");
|
|
406
|
+
if (action === "encryptionStatus") return jsonResult({
|
|
407
|
+
ok: true,
|
|
408
|
+
status: await getMatrixEncryptionStatus({
|
|
409
|
+
includeRecoveryKey: params.includeRecoveryKey === true,
|
|
410
|
+
...clientOpts
|
|
411
|
+
})
|
|
412
|
+
});
|
|
413
|
+
if (action === "verificationStatus") return jsonResult({
|
|
414
|
+
ok: true,
|
|
415
|
+
status: await getMatrixVerificationStatus({
|
|
416
|
+
includeRecoveryKey: params.includeRecoveryKey === true,
|
|
417
|
+
...clientOpts
|
|
418
|
+
})
|
|
419
|
+
});
|
|
420
|
+
if (action === "verificationBootstrap") {
|
|
421
|
+
const result = await bootstrapMatrixVerification({
|
|
422
|
+
recoveryKey: readStringParam(params, "recoveryKey", { trim: false }) ?? readStringParam(params, "key", { trim: false }) ?? void 0,
|
|
423
|
+
forceResetCrossSigning: params.forceResetCrossSigning === true,
|
|
424
|
+
...clientOpts
|
|
425
|
+
});
|
|
426
|
+
return jsonResult({
|
|
427
|
+
ok: result.success,
|
|
428
|
+
result
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
if (action === "verificationRecoveryKey") {
|
|
432
|
+
const result = await verifyMatrixRecoveryKey(readStringParam({ recoveryKey: readStringParam(params, "recoveryKey", { trim: false }) ?? readStringParam(params, "key", { trim: false }) }, "recoveryKey", {
|
|
433
|
+
required: true,
|
|
434
|
+
trim: false
|
|
435
|
+
}), clientOpts);
|
|
436
|
+
return jsonResult({
|
|
437
|
+
ok: result.success,
|
|
438
|
+
result
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
if (action === "verificationBackupStatus") return jsonResult({
|
|
442
|
+
ok: true,
|
|
443
|
+
status: await getMatrixRoomKeyBackupStatus(clientOpts)
|
|
444
|
+
});
|
|
445
|
+
if (action === "verificationBackupRestore") {
|
|
446
|
+
const result = await restoreMatrixRoomKeyBackup({
|
|
447
|
+
recoveryKey: readStringParam(params, "recoveryKey", { trim: false }) ?? readStringParam(params, "key", { trim: false }) ?? void 0,
|
|
448
|
+
...clientOpts
|
|
449
|
+
});
|
|
450
|
+
return jsonResult({
|
|
451
|
+
ok: result.success,
|
|
452
|
+
result
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
if (action === "verificationList") return jsonResult({
|
|
456
|
+
ok: true,
|
|
457
|
+
verifications: await listMatrixVerifications(clientOpts)
|
|
458
|
+
});
|
|
459
|
+
if (action === "verificationRequest") {
|
|
460
|
+
const userId = readStringParam(params, "userId");
|
|
461
|
+
const deviceId = readStringParam(params, "deviceId");
|
|
462
|
+
const roomId = readStringParam(params, "roomId") ?? readStringParam(params, "channelId");
|
|
463
|
+
return jsonResult({
|
|
464
|
+
ok: true,
|
|
465
|
+
verification: await requestMatrixVerification({
|
|
466
|
+
ownUser: typeof params.ownUser === "boolean" ? params.ownUser : void 0,
|
|
467
|
+
userId: userId ?? void 0,
|
|
468
|
+
deviceId: deviceId ?? void 0,
|
|
469
|
+
roomId: roomId ?? void 0,
|
|
470
|
+
...clientOpts
|
|
471
|
+
})
|
|
472
|
+
});
|
|
473
|
+
}
|
|
474
|
+
if (action === "verificationAccept") return jsonResult({
|
|
475
|
+
ok: true,
|
|
476
|
+
verification: await acceptMatrixVerification(readStringParam({ requestId }, "requestId", { required: true }), clientOpts)
|
|
477
|
+
});
|
|
478
|
+
if (action === "verificationCancel") {
|
|
479
|
+
const reason = readStringParam(params, "reason");
|
|
480
|
+
const code = readStringParam(params, "code");
|
|
481
|
+
return jsonResult({
|
|
482
|
+
ok: true,
|
|
483
|
+
verification: await cancelMatrixVerification(readStringParam({ requestId }, "requestId", { required: true }), {
|
|
484
|
+
reason: reason ?? void 0,
|
|
485
|
+
code: code ?? void 0,
|
|
486
|
+
...clientOpts
|
|
487
|
+
})
|
|
488
|
+
});
|
|
489
|
+
}
|
|
490
|
+
if (action === "verificationStart") {
|
|
491
|
+
const method = normalizeOptionalLowercaseString(readStringParam(params, "method"));
|
|
492
|
+
if (method && method !== "sas") throw new Error("Matrix verificationStart only supports method=sas; use verificationGenerateQr/verificationScanQr for QR flows.");
|
|
493
|
+
return jsonResult({
|
|
494
|
+
ok: true,
|
|
495
|
+
verification: await startMatrixVerification(readStringParam({ requestId }, "requestId", { required: true }), {
|
|
496
|
+
method: "sas",
|
|
497
|
+
...clientOpts
|
|
498
|
+
})
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
if (action === "verificationGenerateQr") return jsonResult({
|
|
502
|
+
ok: true,
|
|
503
|
+
...await generateMatrixVerificationQr(readStringParam({ requestId }, "requestId", { required: true }), clientOpts)
|
|
504
|
+
});
|
|
505
|
+
if (action === "verificationScanQr") {
|
|
506
|
+
const qrDataBase64 = readStringParam(params, "qrDataBase64") ?? readStringParam(params, "qrData") ?? readStringParam(params, "qr");
|
|
507
|
+
return jsonResult({
|
|
508
|
+
ok: true,
|
|
509
|
+
verification: await scanMatrixVerificationQr(readStringParam({ requestId }, "requestId", { required: true }), readStringParam({ qrDataBase64 }, "qrDataBase64", { required: true }), clientOpts)
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
if (action === "verificationSas") return jsonResult({
|
|
513
|
+
ok: true,
|
|
514
|
+
sas: await getMatrixVerificationSas(readStringParam({ requestId }, "requestId", { required: true }), clientOpts)
|
|
515
|
+
});
|
|
516
|
+
if (action === "verificationConfirm") return jsonResult({
|
|
517
|
+
ok: true,
|
|
518
|
+
verification: await confirmMatrixVerificationSas(readStringParam({ requestId }, "requestId", { required: true }), clientOpts)
|
|
519
|
+
});
|
|
520
|
+
if (action === "verificationMismatch") return jsonResult({
|
|
521
|
+
ok: true,
|
|
522
|
+
verification: await mismatchMatrixVerificationSas(readStringParam({ requestId }, "requestId", { required: true }), clientOpts)
|
|
523
|
+
});
|
|
524
|
+
if (action === "verificationConfirmQr") return jsonResult({
|
|
525
|
+
ok: true,
|
|
526
|
+
verification: await confirmMatrixVerificationReciprocateQr(readStringParam({ requestId }, "requestId", { required: true }), clientOpts)
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
throw new Error(`Unsupported Matrix action: ${action}`);
|
|
530
|
+
}
|
|
531
|
+
//#endregion
|
|
532
|
+
export { handleMatrixAction };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { assertHttpUrlTargetsPrivateNetwork, isPrivateOrLoopbackHost } from "klaw/plugin-sdk/ssrf-runtime";
|
|
2
|
+
//#region extensions/matrix/src/matrix/client/url-validation.ts
|
|
3
|
+
const MATRIX_HTTP_HOMESERVER_ERROR = "Matrix homeserver must use https:// unless it targets a private or loopback host";
|
|
4
|
+
function cleanString(value, requiredMessage) {
|
|
5
|
+
const trimmed = typeof value === "string" ? value.trim() : "";
|
|
6
|
+
if (!trimmed) throw new Error(requiredMessage);
|
|
7
|
+
return trimmed;
|
|
8
|
+
}
|
|
9
|
+
function validateMatrixHomeserverUrl(homeserver, opts) {
|
|
10
|
+
const trimmed = cleanString(homeserver, "Matrix homeserver is required (matrix.homeserver)");
|
|
11
|
+
let parsed;
|
|
12
|
+
try {
|
|
13
|
+
parsed = new URL(trimmed);
|
|
14
|
+
} catch {
|
|
15
|
+
throw new Error("Matrix homeserver must be a valid http(s) URL");
|
|
16
|
+
}
|
|
17
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") throw new Error("Matrix homeserver must use http:// or https://");
|
|
18
|
+
if (!parsed.hostname) throw new Error("Matrix homeserver must include a hostname");
|
|
19
|
+
if (parsed.username || parsed.password) throw new Error("Matrix homeserver URL must not include embedded credentials");
|
|
20
|
+
if (parsed.search || parsed.hash) throw new Error("Matrix homeserver URL must not include query strings or fragments");
|
|
21
|
+
if (parsed.protocol === "http:" && opts?.allowPrivateNetwork !== true && !isPrivateOrLoopbackHost(parsed.hostname)) throw new Error(MATRIX_HTTP_HOMESERVER_ERROR);
|
|
22
|
+
return trimmed;
|
|
23
|
+
}
|
|
24
|
+
async function resolveValidatedMatrixHomeserverUrl(homeserver, opts) {
|
|
25
|
+
const allowPrivateNetwork = typeof opts?.dangerouslyAllowPrivateNetwork === "boolean" ? opts.dangerouslyAllowPrivateNetwork : opts?.allowPrivateNetwork;
|
|
26
|
+
const normalized = validateMatrixHomeserverUrl(homeserver, { allowPrivateNetwork });
|
|
27
|
+
await assertHttpUrlTargetsPrivateNetwork(normalized, {
|
|
28
|
+
dangerouslyAllowPrivateNetwork: opts?.dangerouslyAllowPrivateNetwork,
|
|
29
|
+
allowPrivateNetwork,
|
|
30
|
+
lookupFn: opts?.lookupFn,
|
|
31
|
+
errorMessage: MATRIX_HTTP_HOMESERVER_ERROR
|
|
32
|
+
});
|
|
33
|
+
return normalized;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { validateMatrixHomeserverUrl as n, isPrivateOrLoopbackHost as r, resolveValidatedMatrixHomeserverUrl as t };
|