@openclaw/matrix 2026.3.12 → 2026.5.9-beta.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.
Files changed (205) hide show
  1. package/dist/account-config-D2W-V1eQ.js +96 -0
  2. package/dist/account-selection-BWwIruri.js +158 -0
  3. package/dist/accounts-Bm90Rzvp.js +130 -0
  4. package/dist/active-client-uhlxdhEy.js +20 -0
  5. package/dist/allowlist-sTzpCn5d.js +68 -0
  6. package/dist/api.js +12 -0
  7. package/dist/approval-handler.runtime-DWTQfd4m.js +370 -0
  8. package/dist/approval-ids-DoC2z7tR.js +7 -0
  9. package/dist/approval-reaction-auth-DbcA1gGd.js +27 -0
  10. package/dist/approval-reactions-o2_tuH8D.js +162 -0
  11. package/dist/async-lock-uQfhfQIY.js +19 -0
  12. package/dist/auth-presence.js +26 -0
  13. package/dist/backup-health-Cabu_WQC.js +60 -0
  14. package/dist/channel-DJNir3Rb.js +1116 -0
  15. package/dist/channel-plugin-api.js +2 -0
  16. package/dist/channel.runtime-BQu0hTih.js +246 -0
  17. package/dist/cli-BmfTmg7x.js +1340 -0
  18. package/dist/cli-metadata-B-PCEzrA.js +22 -0
  19. package/dist/cli-metadata.js +2 -0
  20. package/dist/client-DkcXnm0X.js +25 -0
  21. package/dist/client-_hckQNGW.js +31 -0
  22. package/dist/client-bootstrap-Rb8oHvhH.js +114 -0
  23. package/dist/config--5-S2Akv.js +452 -0
  24. package/dist/config-paths-nsVaysCu.js +19 -0
  25. package/dist/config-schema-nPLpEgHl.js +200 -0
  26. package/dist/config-secret-input.runtime-DiKFehsE.js +2 -0
  27. package/dist/config-update-wZX-HLMn.js +143 -0
  28. package/dist/contract-api.js +9 -0
  29. package/dist/create-client-DCnqDaqd.js +64 -0
  30. package/dist/credentials-DV6fWXhC.js +56 -0
  31. package/dist/credentials-read-cmHgousK.js +112 -0
  32. package/dist/credentials-write.runtime-zniTq-Gr.js +17 -0
  33. package/dist/crypto-node.runtime-pihzdpY7.js +12 -0
  34. package/dist/crypto-runtime-ZI0zAtn3.js +1214 -0
  35. package/dist/deps-C6WqKY7m.js +235 -0
  36. package/dist/device-health-UVYpbA_W.js +16 -0
  37. package/dist/direct-management-DMMMgtTB.js +249 -0
  38. package/dist/direct-room-XkutHjES.js +76 -0
  39. package/dist/directory-live-DmOtMhyr.js +150 -0
  40. package/dist/doctor-C4__7c-U.js +153 -0
  41. package/dist/doctor-contract-D4-64QuJ.js +246 -0
  42. package/dist/doctor-contract-api.js +2 -0
  43. package/dist/draft-stream-BE2QevQQ.js +144 -0
  44. package/dist/encryption-guidance-BPi3A_m3.js +15 -0
  45. package/dist/env-auth-BJqGI8M6.js +63 -0
  46. package/dist/env-vars-C7uQCTKn.js +63 -0
  47. package/dist/errors-CTcpEDq-.js +17 -0
  48. package/dist/exec-approval-resolver-Bza9Dhlm.js +15 -0
  49. package/dist/exec-approvals-Crnh543m.js +196 -0
  50. package/dist/helper-api.js +4 -0
  51. package/dist/http-client-C7AeVJay.js +319 -0
  52. package/dist/index.js +46 -0
  53. package/dist/legacy-crypto-inspector-poDWldgy.js +41 -0
  54. package/dist/legacy-crypto-restore-Biw-w2ng.js +85 -0
  55. package/dist/logger-CnZRVrux.js +78 -0
  56. package/dist/logging-DZHSPP5N.js +99 -0
  57. package/dist/matrix-migration.runtime-WY6ffcrf.js +525 -0
  58. package/dist/media-text-DU6nWZuj.js +146 -0
  59. package/dist/messages-BpihMh82.js +140 -0
  60. package/dist/migration-snapshot-backup-DaCHTp8C.js +69 -0
  61. package/dist/migration-snapshot.runtime-CKHE3xF9.js +2 -0
  62. package/dist/monitor-C_81r_Ck.js +4125 -0
  63. package/dist/plugin-entry.handlers.runtime.js +51 -0
  64. package/dist/probe.runtime-BvAzYAIe.js +3 -0
  65. package/dist/profile-BlHu0wDX.js +111 -0
  66. package/dist/profile-update-DjeBNgIV.js +69 -0
  67. package/dist/reaction-common-ejrL19w-.js +71 -0
  68. package/dist/reaction-events-CiARZfjk.js +121 -0
  69. package/dist/record-shared-CHWJCTWf.js +2 -0
  70. package/dist/recovery-key-store-BTJ6jz5v.js +294 -0
  71. package/dist/resolve-targets-YtJnw1Tb.js +140 -0
  72. package/dist/resolver.runtime-D9piiGEl.js +5 -0
  73. package/dist/rolldown-runtime-DUslC3ob.js +14 -0
  74. package/dist/route-D6rg-iXN.js +161 -0
  75. package/dist/runtime-C6X4h_SJ.js +6 -0
  76. package/dist/runtime-Dog86njy.js +8 -0
  77. package/dist/runtime-api-BXWBFIqm.js +25 -0
  78. package/dist/runtime-api.js +25 -0
  79. package/dist/runtime-heavy-api.js +3 -0
  80. package/dist/runtime-setter-api.js +2 -0
  81. package/dist/sdk-B2vZA27-.js +1416 -0
  82. package/dist/secret-contract-DcrJWCQI.js +120 -0
  83. package/dist/secret-contract-api.js +2 -0
  84. package/dist/send-Bo0DU1ca.js +1200 -0
  85. package/dist/session-store-metadata-DI5SCofx.js +77 -0
  86. package/dist/setup-bootstrap-ImenBsMt.js +62 -0
  87. package/dist/setup-core-CfZy05oW.js +116 -0
  88. package/dist/setup-dm-policy-2-r1FrQh.js +194 -0
  89. package/dist/setup-entry.js +19 -0
  90. package/dist/setup-plugin-api.js +44 -0
  91. package/dist/setup-surface-CqT_o61M.js +540 -0
  92. package/dist/shared-CpMoYKm1.js +195 -0
  93. package/dist/startup-abort-56edvmbM.js +32 -0
  94. package/dist/startup-verification-Demyp0bP.js +132 -0
  95. package/dist/storage-paths-BJLdnCjV.js +52 -0
  96. package/dist/storage-tC3ujLiW.js +281 -0
  97. package/dist/subagent-hooks-DQbyqq9V.js +149 -0
  98. package/dist/subagent-hooks-api.js +23 -0
  99. package/dist/sync-state-C_beeevA.js +12 -0
  100. package/dist/target-ids-80nQ2gql.js +77 -0
  101. package/dist/test-api.js +4 -0
  102. package/dist/thread-binding-api-Cq_E-E1K.js +17 -0
  103. package/dist/thread-binding-api.js +2 -0
  104. package/dist/thread-bindings-B9mesxXk.js +352 -0
  105. package/dist/thread-bindings-runtime.js +2 -0
  106. package/dist/thread-bindings-shared-DK-d-oYX.js +97 -0
  107. package/dist/timeout-abort-signal-CtaIaP1v.js +2 -0
  108. package/dist/tool-actions.runtime-BIH49vRr.js +532 -0
  109. package/dist/url-validation-DiK9j7jz.js +36 -0
  110. package/dist/verification-CZ2rDeHL.js +345 -0
  111. package/openclaw.plugin.json +788 -1
  112. package/package.json +82 -16
  113. package/CHANGELOG.md +0 -98
  114. package/index.ts +0 -22
  115. package/src/actions.ts +0 -195
  116. package/src/channel.directory.test.ts +0 -154
  117. package/src/channel.ts +0 -461
  118. package/src/config-schema.test.ts +0 -26
  119. package/src/config-schema.ts +0 -62
  120. package/src/directory-live.test.ts +0 -85
  121. package/src/directory-live.ts +0 -209
  122. package/src/group-mentions.ts +0 -52
  123. package/src/matrix/accounts.test.ts +0 -131
  124. package/src/matrix/accounts.ts +0 -114
  125. package/src/matrix/actions/client.ts +0 -47
  126. package/src/matrix/actions/limits.test.ts +0 -15
  127. package/src/matrix/actions/limits.ts +0 -6
  128. package/src/matrix/actions/messages.ts +0 -126
  129. package/src/matrix/actions/pins.test.ts +0 -74
  130. package/src/matrix/actions/pins.ts +0 -84
  131. package/src/matrix/actions/reactions.test.ts +0 -109
  132. package/src/matrix/actions/reactions.ts +0 -102
  133. package/src/matrix/actions/room.ts +0 -85
  134. package/src/matrix/actions/summary.ts +0 -75
  135. package/src/matrix/actions/types.ts +0 -85
  136. package/src/matrix/actions.ts +0 -15
  137. package/src/matrix/active-client.ts +0 -32
  138. package/src/matrix/client/config.ts +0 -245
  139. package/src/matrix/client/create-client.ts +0 -125
  140. package/src/matrix/client/logging.ts +0 -46
  141. package/src/matrix/client/runtime.ts +0 -4
  142. package/src/matrix/client/shared.test.ts +0 -85
  143. package/src/matrix/client/shared.ts +0 -210
  144. package/src/matrix/client/startup.test.ts +0 -49
  145. package/src/matrix/client/startup.ts +0 -29
  146. package/src/matrix/client/storage.ts +0 -131
  147. package/src/matrix/client/types.ts +0 -34
  148. package/src/matrix/client-bootstrap.ts +0 -47
  149. package/src/matrix/client.test.ts +0 -56
  150. package/src/matrix/client.ts +0 -14
  151. package/src/matrix/credentials.ts +0 -125
  152. package/src/matrix/deps.test.ts +0 -74
  153. package/src/matrix/deps.ts +0 -126
  154. package/src/matrix/format.test.ts +0 -33
  155. package/src/matrix/format.ts +0 -22
  156. package/src/matrix/index.ts +0 -11
  157. package/src/matrix/monitor/access-policy.ts +0 -126
  158. package/src/matrix/monitor/allowlist.test.ts +0 -45
  159. package/src/matrix/monitor/allowlist.ts +0 -100
  160. package/src/matrix/monitor/auto-join.ts +0 -72
  161. package/src/matrix/monitor/direct.test.ts +0 -400
  162. package/src/matrix/monitor/direct.ts +0 -152
  163. package/src/matrix/monitor/events.test.ts +0 -172
  164. package/src/matrix/monitor/events.ts +0 -168
  165. package/src/matrix/monitor/handler.body-for-agent.test.ts +0 -196
  166. package/src/matrix/monitor/handler.ts +0 -767
  167. package/src/matrix/monitor/inbound-body.test.ts +0 -73
  168. package/src/matrix/monitor/inbound-body.ts +0 -28
  169. package/src/matrix/monitor/index.test.ts +0 -18
  170. package/src/matrix/monitor/index.ts +0 -414
  171. package/src/matrix/monitor/location.ts +0 -100
  172. package/src/matrix/monitor/media.test.ts +0 -86
  173. package/src/matrix/monitor/media.ts +0 -118
  174. package/src/matrix/monitor/mentions.test.ts +0 -154
  175. package/src/matrix/monitor/mentions.ts +0 -62
  176. package/src/matrix/monitor/replies.test.ts +0 -184
  177. package/src/matrix/monitor/replies.ts +0 -124
  178. package/src/matrix/monitor/room-info.ts +0 -55
  179. package/src/matrix/monitor/rooms.test.ts +0 -124
  180. package/src/matrix/monitor/rooms.ts +0 -47
  181. package/src/matrix/monitor/threads.ts +0 -68
  182. package/src/matrix/monitor/types.ts +0 -39
  183. package/src/matrix/poll-types.test.ts +0 -21
  184. package/src/matrix/poll-types.ts +0 -167
  185. package/src/matrix/probe.ts +0 -69
  186. package/src/matrix/sdk-runtime.ts +0 -18
  187. package/src/matrix/send/client.ts +0 -99
  188. package/src/matrix/send/formatting.ts +0 -93
  189. package/src/matrix/send/media.ts +0 -230
  190. package/src/matrix/send/targets.test.ts +0 -98
  191. package/src/matrix/send/targets.ts +0 -150
  192. package/src/matrix/send/types.ts +0 -110
  193. package/src/matrix/send-queue.test.ts +0 -154
  194. package/src/matrix/send-queue.ts +0 -28
  195. package/src/matrix/send.test.ts +0 -326
  196. package/src/matrix/send.ts +0 -267
  197. package/src/onboarding.ts +0 -462
  198. package/src/outbound.test.ts +0 -159
  199. package/src/outbound.ts +0 -58
  200. package/src/resolve-targets.test.ts +0 -67
  201. package/src/resolve-targets.ts +0 -125
  202. package/src/runtime.ts +0 -6
  203. package/src/secret-input.ts +0 -13
  204. package/src/tool-actions.ts +0 -164
  205. package/src/types.ts +0 -118
@@ -1,209 +0,0 @@
1
- import type { ChannelDirectoryEntry } from "openclaw/plugin-sdk/matrix";
2
- import { resolveMatrixAuth } from "./matrix/client.js";
3
-
4
- type MatrixUserResult = {
5
- user_id?: string;
6
- display_name?: string;
7
- };
8
-
9
- type MatrixUserDirectoryResponse = {
10
- results?: MatrixUserResult[];
11
- };
12
-
13
- type MatrixJoinedRoomsResponse = {
14
- joined_rooms?: string[];
15
- };
16
-
17
- type MatrixRoomNameState = {
18
- name?: string;
19
- };
20
-
21
- type MatrixAliasLookup = {
22
- room_id?: string;
23
- };
24
-
25
- type MatrixDirectoryLiveParams = {
26
- cfg: unknown;
27
- accountId?: string | null;
28
- query?: string | null;
29
- limit?: number | null;
30
- };
31
-
32
- type MatrixResolvedAuth = Awaited<ReturnType<typeof resolveMatrixAuth>>;
33
-
34
- async function fetchMatrixJson<T>(params: {
35
- homeserver: string;
36
- path: string;
37
- accessToken: string;
38
- method?: "GET" | "POST";
39
- body?: unknown;
40
- }): Promise<T> {
41
- const res = await fetch(`${params.homeserver}${params.path}`, {
42
- method: params.method ?? "GET",
43
- headers: {
44
- Authorization: `Bearer ${params.accessToken}`,
45
- "Content-Type": "application/json",
46
- },
47
- body: params.body ? JSON.stringify(params.body) : undefined,
48
- });
49
- if (!res.ok) {
50
- const text = await res.text().catch(() => "");
51
- throw new Error(`Matrix API ${params.path} failed (${res.status}): ${text || "unknown error"}`);
52
- }
53
- return (await res.json()) as T;
54
- }
55
-
56
- function normalizeQuery(value?: string | null): string {
57
- return value?.trim().toLowerCase() ?? "";
58
- }
59
-
60
- function resolveMatrixDirectoryLimit(limit?: number | null): number {
61
- return typeof limit === "number" && limit > 0 ? limit : 20;
62
- }
63
-
64
- async function resolveMatrixDirectoryContext(
65
- params: MatrixDirectoryLiveParams,
66
- ): Promise<{ query: string; auth: MatrixResolvedAuth } | null> {
67
- const query = normalizeQuery(params.query);
68
- if (!query) {
69
- return null;
70
- }
71
- const auth = await resolveMatrixAuth({ cfg: params.cfg as never, accountId: params.accountId });
72
- return { query, auth };
73
- }
74
-
75
- function createGroupDirectoryEntry(params: {
76
- id: string;
77
- name: string;
78
- handle?: string;
79
- }): ChannelDirectoryEntry {
80
- return {
81
- kind: "group",
82
- id: params.id,
83
- name: params.name,
84
- handle: params.handle,
85
- } satisfies ChannelDirectoryEntry;
86
- }
87
-
88
- export async function listMatrixDirectoryPeersLive(
89
- params: MatrixDirectoryLiveParams,
90
- ): Promise<ChannelDirectoryEntry[]> {
91
- const context = await resolveMatrixDirectoryContext(params);
92
- if (!context) {
93
- return [];
94
- }
95
- const { query, auth } = context;
96
- const res = await fetchMatrixJson<MatrixUserDirectoryResponse>({
97
- homeserver: auth.homeserver,
98
- accessToken: auth.accessToken,
99
- path: "/_matrix/client/v3/user_directory/search",
100
- method: "POST",
101
- body: {
102
- search_term: query,
103
- limit: resolveMatrixDirectoryLimit(params.limit),
104
- },
105
- });
106
- const results = res.results ?? [];
107
- return results
108
- .map((entry) => {
109
- const userId = entry.user_id?.trim();
110
- if (!userId) {
111
- return null;
112
- }
113
- return {
114
- kind: "user",
115
- id: userId,
116
- name: entry.display_name?.trim() || undefined,
117
- handle: entry.display_name ? `@${entry.display_name.trim()}` : undefined,
118
- raw: entry,
119
- } satisfies ChannelDirectoryEntry;
120
- })
121
- .filter(Boolean) as ChannelDirectoryEntry[];
122
- }
123
-
124
- async function resolveMatrixRoomAlias(
125
- homeserver: string,
126
- accessToken: string,
127
- alias: string,
128
- ): Promise<string | null> {
129
- try {
130
- const res = await fetchMatrixJson<MatrixAliasLookup>({
131
- homeserver,
132
- accessToken,
133
- path: `/_matrix/client/v3/directory/room/${encodeURIComponent(alias)}`,
134
- });
135
- return res.room_id?.trim() || null;
136
- } catch {
137
- return null;
138
- }
139
- }
140
-
141
- async function fetchMatrixRoomName(
142
- homeserver: string,
143
- accessToken: string,
144
- roomId: string,
145
- ): Promise<string | null> {
146
- try {
147
- const res = await fetchMatrixJson<MatrixRoomNameState>({
148
- homeserver,
149
- accessToken,
150
- path: `/_matrix/client/v3/rooms/${encodeURIComponent(roomId)}/state/m.room.name`,
151
- });
152
- return res.name?.trim() || null;
153
- } catch {
154
- return null;
155
- }
156
- }
157
-
158
- export async function listMatrixDirectoryGroupsLive(
159
- params: MatrixDirectoryLiveParams,
160
- ): Promise<ChannelDirectoryEntry[]> {
161
- const context = await resolveMatrixDirectoryContext(params);
162
- if (!context) {
163
- return [];
164
- }
165
- const { query, auth } = context;
166
- const limit = resolveMatrixDirectoryLimit(params.limit);
167
-
168
- if (query.startsWith("#")) {
169
- const roomId = await resolveMatrixRoomAlias(auth.homeserver, auth.accessToken, query);
170
- if (!roomId) {
171
- return [];
172
- }
173
- return [createGroupDirectoryEntry({ id: roomId, name: query, handle: query })];
174
- }
175
-
176
- if (query.startsWith("!")) {
177
- const originalId = params.query?.trim() ?? query;
178
- return [createGroupDirectoryEntry({ id: originalId, name: originalId })];
179
- }
180
-
181
- const joined = await fetchMatrixJson<MatrixJoinedRoomsResponse>({
182
- homeserver: auth.homeserver,
183
- accessToken: auth.accessToken,
184
- path: "/_matrix/client/v3/joined_rooms",
185
- });
186
- const rooms = joined.joined_rooms ?? [];
187
- const results: ChannelDirectoryEntry[] = [];
188
-
189
- for (const roomId of rooms) {
190
- const name = await fetchMatrixRoomName(auth.homeserver, auth.accessToken, roomId);
191
- if (!name) {
192
- continue;
193
- }
194
- if (!name.toLowerCase().includes(query)) {
195
- continue;
196
- }
197
- results.push({
198
- kind: "group",
199
- id: roomId,
200
- name,
201
- handle: `#${name}`,
202
- });
203
- if (results.length >= limit) {
204
- break;
205
- }
206
- }
207
-
208
- return results;
209
- }
@@ -1,52 +0,0 @@
1
- import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk/matrix";
2
- import { resolveMatrixAccountConfig } from "./matrix/accounts.js";
3
- import { resolveMatrixRoomConfig } from "./matrix/monitor/rooms.js";
4
- import type { CoreConfig } from "./types.js";
5
-
6
- function stripLeadingPrefixCaseInsensitive(value: string, prefix: string): string {
7
- return value.toLowerCase().startsWith(prefix.toLowerCase())
8
- ? value.slice(prefix.length).trim()
9
- : value;
10
- }
11
-
12
- function resolveMatrixRoomConfigForGroup(params: ChannelGroupContext) {
13
- const rawGroupId = params.groupId?.trim() ?? "";
14
- let roomId = rawGroupId;
15
- roomId = stripLeadingPrefixCaseInsensitive(roomId, "matrix:");
16
- roomId = stripLeadingPrefixCaseInsensitive(roomId, "channel:");
17
- roomId = stripLeadingPrefixCaseInsensitive(roomId, "room:");
18
-
19
- const groupChannel = params.groupChannel?.trim() ?? "";
20
- const aliases = groupChannel ? [groupChannel] : [];
21
- const cfg = params.cfg as CoreConfig;
22
- const matrixConfig = resolveMatrixAccountConfig({ cfg, accountId: params.accountId });
23
- return resolveMatrixRoomConfig({
24
- rooms: matrixConfig.groups ?? matrixConfig.rooms,
25
- roomId,
26
- aliases,
27
- name: groupChannel || undefined,
28
- }).config;
29
- }
30
-
31
- export function resolveMatrixGroupRequireMention(params: ChannelGroupContext): boolean {
32
- const resolved = resolveMatrixRoomConfigForGroup(params);
33
- if (resolved) {
34
- if (resolved.autoReply === true) {
35
- return false;
36
- }
37
- if (resolved.autoReply === false) {
38
- return true;
39
- }
40
- if (typeof resolved.requireMention === "boolean") {
41
- return resolved.requireMention;
42
- }
43
- }
44
- return true;
45
- }
46
-
47
- export function resolveMatrixGroupToolPolicy(
48
- params: ChannelGroupContext,
49
- ): GroupToolPolicyConfig | undefined {
50
- const resolved = resolveMatrixRoomConfigForGroup(params);
51
- return resolved?.tools;
52
- }
@@ -1,131 +0,0 @@
1
- import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
2
- import type { CoreConfig } from "../types.js";
3
- import { resolveDefaultMatrixAccountId, resolveMatrixAccount } from "./accounts.js";
4
-
5
- vi.mock("./credentials.js", () => ({
6
- loadMatrixCredentials: () => null,
7
- credentialsMatchConfig: () => false,
8
- }));
9
-
10
- const envKeys = [
11
- "MATRIX_HOMESERVER",
12
- "MATRIX_USER_ID",
13
- "MATRIX_ACCESS_TOKEN",
14
- "MATRIX_PASSWORD",
15
- "MATRIX_DEVICE_NAME",
16
- ];
17
-
18
- describe("resolveMatrixAccount", () => {
19
- let prevEnv: Record<string, string | undefined> = {};
20
-
21
- beforeEach(() => {
22
- prevEnv = {};
23
- for (const key of envKeys) {
24
- prevEnv[key] = process.env[key];
25
- delete process.env[key];
26
- }
27
- });
28
-
29
- afterEach(() => {
30
- for (const key of envKeys) {
31
- const value = prevEnv[key];
32
- if (value === undefined) {
33
- delete process.env[key];
34
- } else {
35
- process.env[key] = value;
36
- }
37
- }
38
- });
39
-
40
- it("treats access-token-only config as configured", () => {
41
- const cfg: CoreConfig = {
42
- channels: {
43
- matrix: {
44
- homeserver: "https://matrix.example.org",
45
- accessToken: "tok-access",
46
- },
47
- },
48
- };
49
-
50
- const account = resolveMatrixAccount({ cfg });
51
- expect(account.configured).toBe(true);
52
- });
53
-
54
- it("requires userId + password when no access token is set", () => {
55
- const cfg: CoreConfig = {
56
- channels: {
57
- matrix: {
58
- homeserver: "https://matrix.example.org",
59
- userId: "@bot:example.org",
60
- },
61
- },
62
- };
63
-
64
- const account = resolveMatrixAccount({ cfg });
65
- expect(account.configured).toBe(false);
66
- });
67
-
68
- it("marks password auth as configured when userId is present", () => {
69
- const cfg: CoreConfig = {
70
- channels: {
71
- matrix: {
72
- homeserver: "https://matrix.example.org",
73
- userId: "@bot:example.org",
74
- password: "secret",
75
- },
76
- },
77
- };
78
-
79
- const account = resolveMatrixAccount({ cfg });
80
- expect(account.configured).toBe(true);
81
- });
82
- });
83
-
84
- describe("resolveDefaultMatrixAccountId", () => {
85
- it("prefers channels.matrix.defaultAccount when it matches a configured account", () => {
86
- const cfg: CoreConfig = {
87
- channels: {
88
- matrix: {
89
- defaultAccount: "alerts",
90
- accounts: {
91
- default: { homeserver: "https://matrix.example.org", accessToken: "tok-default" },
92
- alerts: { homeserver: "https://matrix.example.org", accessToken: "tok-alerts" },
93
- },
94
- },
95
- },
96
- };
97
-
98
- expect(resolveDefaultMatrixAccountId(cfg)).toBe("alerts");
99
- });
100
-
101
- it("normalizes channels.matrix.defaultAccount before lookup", () => {
102
- const cfg: CoreConfig = {
103
- channels: {
104
- matrix: {
105
- defaultAccount: "Team Alerts",
106
- accounts: {
107
- "team-alerts": { homeserver: "https://matrix.example.org", accessToken: "tok-alerts" },
108
- },
109
- },
110
- },
111
- };
112
-
113
- expect(resolveDefaultMatrixAccountId(cfg)).toBe("team-alerts");
114
- });
115
-
116
- it("falls back when channels.matrix.defaultAccount is not configured", () => {
117
- const cfg: CoreConfig = {
118
- channels: {
119
- matrix: {
120
- defaultAccount: "missing",
121
- accounts: {
122
- default: { homeserver: "https://matrix.example.org", accessToken: "tok-default" },
123
- alerts: { homeserver: "https://matrix.example.org", accessToken: "tok-alerts" },
124
- },
125
- },
126
- },
127
- };
128
-
129
- expect(resolveDefaultMatrixAccountId(cfg)).toBe("default");
130
- });
131
- });
@@ -1,114 +0,0 @@
1
- import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
2
- import { createAccountListHelpers } from "openclaw/plugin-sdk/matrix";
3
- import { hasConfiguredSecretInput } from "../secret-input.js";
4
- import type { CoreConfig, MatrixConfig } from "../types.js";
5
- import { resolveMatrixConfigForAccount } from "./client.js";
6
- import { credentialsMatchConfig, loadMatrixCredentials } from "./credentials.js";
7
-
8
- /** Merge account config with top-level defaults, preserving nested objects. */
9
- function mergeAccountConfig(base: MatrixConfig, account: MatrixConfig): MatrixConfig {
10
- const merged = { ...base, ...account };
11
- // Deep-merge known nested objects so partial overrides inherit base fields
12
- for (const key of ["dm", "actions"] as const) {
13
- const b = base[key];
14
- const o = account[key];
15
- if (typeof b === "object" && b != null && typeof o === "object" && o != null) {
16
- (merged as Record<string, unknown>)[key] = { ...b, ...o };
17
- }
18
- }
19
- // Don't propagate the accounts map into the merged per-account config
20
- delete (merged as Record<string, unknown>).accounts;
21
- delete (merged as Record<string, unknown>).defaultAccount;
22
- return merged;
23
- }
24
-
25
- export type ResolvedMatrixAccount = {
26
- accountId: string;
27
- enabled: boolean;
28
- name?: string;
29
- configured: boolean;
30
- homeserver?: string;
31
- userId?: string;
32
- config: MatrixConfig;
33
- };
34
-
35
- const {
36
- listAccountIds: listMatrixAccountIds,
37
- resolveDefaultAccountId: resolveDefaultMatrixAccountId,
38
- } = createAccountListHelpers("matrix", { normalizeAccountId });
39
- export { listMatrixAccountIds, resolveDefaultMatrixAccountId };
40
-
41
- function resolveAccountConfig(cfg: CoreConfig, accountId: string): MatrixConfig | undefined {
42
- const accounts = cfg.channels?.matrix?.accounts;
43
- if (!accounts || typeof accounts !== "object") {
44
- return undefined;
45
- }
46
- // Direct lookup first (fast path for already-normalized keys)
47
- if (accounts[accountId]) {
48
- return accounts[accountId] as MatrixConfig;
49
- }
50
- // Fall back to case-insensitive match (user may have mixed-case keys in config)
51
- const normalized = normalizeAccountId(accountId);
52
- for (const key of Object.keys(accounts)) {
53
- if (normalizeAccountId(key) === normalized) {
54
- return accounts[key] as MatrixConfig;
55
- }
56
- }
57
- return undefined;
58
- }
59
-
60
- export function resolveMatrixAccount(params: {
61
- cfg: CoreConfig;
62
- accountId?: string | null;
63
- }): ResolvedMatrixAccount {
64
- const accountId = normalizeAccountId(params.accountId);
65
- const matrixBase = params.cfg.channels?.matrix ?? {};
66
- const base = resolveMatrixAccountConfig({ cfg: params.cfg, accountId });
67
- const enabled = base.enabled !== false && matrixBase.enabled !== false;
68
-
69
- const resolved = resolveMatrixConfigForAccount(params.cfg, accountId, process.env);
70
- const hasHomeserver = Boolean(resolved.homeserver);
71
- const hasUserId = Boolean(resolved.userId);
72
- const hasAccessToken = Boolean(resolved.accessToken);
73
- const hasPassword = Boolean(resolved.password);
74
- const hasPasswordAuth = hasUserId && (hasPassword || hasConfiguredSecretInput(base.password));
75
- const stored = loadMatrixCredentials(process.env, accountId);
76
- const hasStored =
77
- stored && resolved.homeserver
78
- ? credentialsMatchConfig(stored, {
79
- homeserver: resolved.homeserver,
80
- userId: resolved.userId || "",
81
- })
82
- : false;
83
- const configured = hasHomeserver && (hasAccessToken || hasPasswordAuth || Boolean(hasStored));
84
- return {
85
- accountId,
86
- enabled,
87
- name: base.name?.trim() || undefined,
88
- configured,
89
- homeserver: resolved.homeserver || undefined,
90
- userId: resolved.userId || undefined,
91
- config: base,
92
- };
93
- }
94
-
95
- export function resolveMatrixAccountConfig(params: {
96
- cfg: CoreConfig;
97
- accountId?: string | null;
98
- }): MatrixConfig {
99
- const accountId = normalizeAccountId(params.accountId);
100
- const matrixBase = params.cfg.channels?.matrix ?? {};
101
- const accountConfig = resolveAccountConfig(params.cfg, accountId);
102
- if (!accountConfig) {
103
- return matrixBase;
104
- }
105
- // Merge account-specific config with top-level defaults so settings like
106
- // groupPolicy and blockStreaming inherit when not overridden.
107
- return mergeAccountConfig(matrixBase, accountConfig);
108
- }
109
-
110
- export function listEnabledMatrixAccounts(cfg: CoreConfig): ResolvedMatrixAccount[] {
111
- return listMatrixAccountIds(cfg)
112
- .map((accountId) => resolveMatrixAccount({ cfg, accountId }))
113
- .filter((account) => account.enabled);
114
- }
@@ -1,47 +0,0 @@
1
- import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
2
- import { getMatrixRuntime } from "../../runtime.js";
3
- import type { CoreConfig } from "../../types.js";
4
- import { getActiveMatrixClient } from "../active-client.js";
5
- import { createPreparedMatrixClient } from "../client-bootstrap.js";
6
- import { isBunRuntime, resolveMatrixAuth, resolveSharedMatrixClient } from "../client.js";
7
- import type { MatrixActionClient, MatrixActionClientOpts } from "./types.js";
8
-
9
- export function ensureNodeRuntime() {
10
- if (isBunRuntime()) {
11
- throw new Error("Matrix support requires Node (bun runtime not supported)");
12
- }
13
- }
14
-
15
- export async function resolveActionClient(
16
- opts: MatrixActionClientOpts = {},
17
- ): Promise<MatrixActionClient> {
18
- ensureNodeRuntime();
19
- if (opts.client) {
20
- return { client: opts.client, stopOnDone: false };
21
- }
22
- // Normalize accountId early to ensure consistent keying across all lookups
23
- const accountId = normalizeAccountId(opts.accountId);
24
- const active = getActiveMatrixClient(accountId);
25
- if (active) {
26
- return { client: active, stopOnDone: false };
27
- }
28
- const shouldShareClient = Boolean(process.env.OPENCLAW_GATEWAY_PORT);
29
- if (shouldShareClient) {
30
- const client = await resolveSharedMatrixClient({
31
- cfg: getMatrixRuntime().config.loadConfig() as CoreConfig,
32
- timeoutMs: opts.timeoutMs,
33
- accountId,
34
- });
35
- return { client, stopOnDone: false };
36
- }
37
- const auth = await resolveMatrixAuth({
38
- cfg: getMatrixRuntime().config.loadConfig() as CoreConfig,
39
- accountId,
40
- });
41
- const client = await createPreparedMatrixClient({
42
- auth,
43
- timeoutMs: opts.timeoutMs,
44
- accountId,
45
- });
46
- return { client, stopOnDone: true };
47
- }
@@ -1,15 +0,0 @@
1
- import { describe, expect, it } from "vitest";
2
- import { resolveMatrixActionLimit } from "./limits.js";
3
-
4
- describe("resolveMatrixActionLimit", () => {
5
- it("uses fallback for non-finite values", () => {
6
- expect(resolveMatrixActionLimit(undefined, 20)).toBe(20);
7
- expect(resolveMatrixActionLimit(Number.NaN, 20)).toBe(20);
8
- });
9
-
10
- it("normalizes finite numbers to positive integers", () => {
11
- expect(resolveMatrixActionLimit(7.9, 20)).toBe(7);
12
- expect(resolveMatrixActionLimit(0, 20)).toBe(1);
13
- expect(resolveMatrixActionLimit(-3, 20)).toBe(1);
14
- });
15
- });
@@ -1,6 +0,0 @@
1
- export function resolveMatrixActionLimit(raw: unknown, fallback: number): number {
2
- if (typeof raw !== "number" || !Number.isFinite(raw)) {
3
- return fallback;
4
- }
5
- return Math.max(1, Math.floor(raw));
6
- }
@@ -1,126 +0,0 @@
1
- import { resolveMatrixRoomId, sendMessageMatrix } from "../send.js";
2
- import { resolveActionClient } from "./client.js";
3
- import { resolveMatrixActionLimit } from "./limits.js";
4
- import { summarizeMatrixRawEvent } from "./summary.js";
5
- import {
6
- EventType,
7
- MsgType,
8
- RelationType,
9
- type MatrixActionClientOpts,
10
- type MatrixMessageSummary,
11
- type MatrixRawEvent,
12
- type RoomMessageEventContent,
13
- } from "./types.js";
14
-
15
- export async function sendMatrixMessage(
16
- to: string,
17
- content: string,
18
- opts: MatrixActionClientOpts & {
19
- mediaUrl?: string;
20
- replyToId?: string;
21
- threadId?: string;
22
- } = {},
23
- ) {
24
- return await sendMessageMatrix(to, content, {
25
- mediaUrl: opts.mediaUrl,
26
- replyToId: opts.replyToId,
27
- threadId: opts.threadId,
28
- client: opts.client,
29
- timeoutMs: opts.timeoutMs,
30
- });
31
- }
32
-
33
- export async function editMatrixMessage(
34
- roomId: string,
35
- messageId: string,
36
- content: string,
37
- opts: MatrixActionClientOpts = {},
38
- ) {
39
- const trimmed = content.trim();
40
- if (!trimmed) {
41
- throw new Error("Matrix edit requires content");
42
- }
43
- const { client, stopOnDone } = await resolveActionClient(opts);
44
- try {
45
- const resolvedRoom = await resolveMatrixRoomId(client, roomId);
46
- const newContent = {
47
- msgtype: MsgType.Text,
48
- body: trimmed,
49
- } satisfies RoomMessageEventContent;
50
- const payload: RoomMessageEventContent = {
51
- msgtype: MsgType.Text,
52
- body: `* ${trimmed}`,
53
- "m.new_content": newContent,
54
- "m.relates_to": {
55
- rel_type: RelationType.Replace,
56
- event_id: messageId,
57
- },
58
- };
59
- const eventId = await client.sendMessage(resolvedRoom, payload);
60
- return { eventId: eventId ?? null };
61
- } finally {
62
- if (stopOnDone) {
63
- client.stop();
64
- }
65
- }
66
- }
67
-
68
- export async function deleteMatrixMessage(
69
- roomId: string,
70
- messageId: string,
71
- opts: MatrixActionClientOpts & { reason?: string } = {},
72
- ) {
73
- const { client, stopOnDone } = await resolveActionClient(opts);
74
- try {
75
- const resolvedRoom = await resolveMatrixRoomId(client, roomId);
76
- await client.redactEvent(resolvedRoom, messageId, opts.reason);
77
- } finally {
78
- if (stopOnDone) {
79
- client.stop();
80
- }
81
- }
82
- }
83
-
84
- export async function readMatrixMessages(
85
- roomId: string,
86
- opts: MatrixActionClientOpts & {
87
- limit?: number;
88
- before?: string;
89
- after?: string;
90
- } = {},
91
- ): Promise<{
92
- messages: MatrixMessageSummary[];
93
- nextBatch?: string | null;
94
- prevBatch?: string | null;
95
- }> {
96
- const { client, stopOnDone } = await resolveActionClient(opts);
97
- try {
98
- const resolvedRoom = await resolveMatrixRoomId(client, roomId);
99
- const limit = resolveMatrixActionLimit(opts.limit, 20);
100
- const token = opts.before?.trim() || opts.after?.trim() || undefined;
101
- const dir = opts.after ? "f" : "b";
102
- // @vector-im/matrix-bot-sdk uses doRequest for room messages
103
- const res = (await client.doRequest(
104
- "GET",
105
- `/_matrix/client/v3/rooms/${encodeURIComponent(resolvedRoom)}/messages`,
106
- {
107
- dir,
108
- limit,
109
- from: token,
110
- },
111
- )) as { chunk: MatrixRawEvent[]; start?: string; end?: string };
112
- const messages = res.chunk
113
- .filter((event) => event.type === EventType.RoomMessage)
114
- .filter((event) => !event.unsigned?.redacted_because)
115
- .map(summarizeMatrixRawEvent);
116
- return {
117
- messages,
118
- nextBatch: res.end ?? null,
119
- prevBatch: res.start ?? null,
120
- };
121
- } finally {
122
- if (stopOnDone) {
123
- client.stop();
124
- }
125
- }
126
- }