@openclaw/matrix 2026.3.1 → 2026.3.7
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/CHANGELOG.md +18 -0
- package/index.ts +7 -2
- package/package.json +2 -1
- package/src/actions.ts +1 -1
- package/src/channel.directory.test.ts +1 -1
- package/src/channel.ts +46 -58
- package/src/config-schema.test.ts +26 -0
- package/src/config-schema.ts +3 -2
- package/src/directory-live.ts +1 -1
- package/src/group-mentions.ts +1 -1
- package/src/matrix/accounts.ts +9 -44
- package/src/matrix/client/config.ts +55 -29
- package/src/matrix/client/create-client.ts +7 -5
- package/src/matrix/client/logging.ts +17 -7
- package/src/matrix/client/shared.ts +3 -1
- package/src/matrix/client-bootstrap.ts +2 -1
- package/src/matrix/deps.test.ts +74 -0
- package/src/matrix/deps.ts +67 -1
- package/src/matrix/monitor/access-policy.ts +6 -7
- package/src/matrix/monitor/allowlist.ts +9 -16
- package/src/matrix/monitor/auto-join.ts +3 -2
- package/src/matrix/monitor/events.test.ts +1 -1
- package/src/matrix/monitor/events.ts +1 -1
- package/src/matrix/monitor/handler.body-for-agent.test.ts +1 -1
- package/src/matrix/monitor/handler.ts +33 -36
- package/src/matrix/monitor/index.ts +6 -7
- package/src/matrix/monitor/location.ts +1 -1
- package/src/matrix/monitor/media.test.ts +1 -1
- package/src/matrix/monitor/replies.test.ts +1 -1
- package/src/matrix/monitor/replies.ts +1 -1
- package/src/matrix/monitor/rooms.ts +1 -1
- package/src/matrix/poll-types.ts +1 -1
- package/src/matrix/probe.ts +1 -1
- package/src/matrix/sdk-runtime.ts +18 -0
- package/src/matrix/send/client.ts +8 -6
- package/src/matrix/send/types.ts +1 -0
- package/src/matrix/send-queue.ts +7 -23
- package/src/matrix/send.test.ts +90 -2
- package/src/matrix/send.ts +6 -4
- package/src/onboarding.ts +52 -36
- package/src/outbound.test.ts +159 -0
- package/src/outbound.ts +7 -4
- package/src/resolve-targets.test.ts +1 -1
- package/src/resolve-targets.ts +39 -40
- package/src/runtime.ts +1 -1
- package/src/secret-input.ts +13 -0
- package/src/tool-actions.ts +1 -1
- package/src/types.ts +2 -2
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { ensureMatrixCryptoRuntime } from "./deps.js";
|
|
3
|
+
|
|
4
|
+
const logStub = vi.fn();
|
|
5
|
+
|
|
6
|
+
describe("ensureMatrixCryptoRuntime", () => {
|
|
7
|
+
it("returns immediately when matrix SDK loads", async () => {
|
|
8
|
+
const runCommand = vi.fn();
|
|
9
|
+
const requireFn = vi.fn(() => ({}));
|
|
10
|
+
|
|
11
|
+
await ensureMatrixCryptoRuntime({
|
|
12
|
+
log: logStub,
|
|
13
|
+
requireFn,
|
|
14
|
+
runCommand,
|
|
15
|
+
resolveFn: () => "/tmp/download-lib.js",
|
|
16
|
+
nodeExecutable: "/usr/bin/node",
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
expect(requireFn).toHaveBeenCalledTimes(1);
|
|
20
|
+
expect(runCommand).not.toHaveBeenCalled();
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("bootstraps missing crypto runtime and retries matrix SDK load", async () => {
|
|
24
|
+
let bootstrapped = false;
|
|
25
|
+
const requireFn = vi.fn(() => {
|
|
26
|
+
if (!bootstrapped) {
|
|
27
|
+
throw new Error(
|
|
28
|
+
"Cannot find module '@matrix-org/matrix-sdk-crypto-nodejs-linux-x64-gnu' (required by matrix sdk)",
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
return {};
|
|
32
|
+
});
|
|
33
|
+
const runCommand = vi.fn(async () => {
|
|
34
|
+
bootstrapped = true;
|
|
35
|
+
return { code: 0, stdout: "", stderr: "" };
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
await ensureMatrixCryptoRuntime({
|
|
39
|
+
log: logStub,
|
|
40
|
+
requireFn,
|
|
41
|
+
runCommand,
|
|
42
|
+
resolveFn: () => "/tmp/download-lib.js",
|
|
43
|
+
nodeExecutable: "/usr/bin/node",
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
expect(runCommand).toHaveBeenCalledWith({
|
|
47
|
+
argv: ["/usr/bin/node", "/tmp/download-lib.js"],
|
|
48
|
+
cwd: "/tmp",
|
|
49
|
+
timeoutMs: 300_000,
|
|
50
|
+
env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
|
|
51
|
+
});
|
|
52
|
+
expect(requireFn).toHaveBeenCalledTimes(2);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it("rethrows non-crypto module errors without bootstrapping", async () => {
|
|
56
|
+
const runCommand = vi.fn();
|
|
57
|
+
const requireFn = vi.fn(() => {
|
|
58
|
+
throw new Error("Cannot find module '@vector-im/matrix-bot-sdk'");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
await expect(
|
|
62
|
+
ensureMatrixCryptoRuntime({
|
|
63
|
+
log: logStub,
|
|
64
|
+
requireFn,
|
|
65
|
+
runCommand,
|
|
66
|
+
resolveFn: () => "/tmp/download-lib.js",
|
|
67
|
+
nodeExecutable: "/usr/bin/node",
|
|
68
|
+
}),
|
|
69
|
+
).rejects.toThrow("Cannot find module '@vector-im/matrix-bot-sdk'");
|
|
70
|
+
|
|
71
|
+
expect(runCommand).not.toHaveBeenCalled();
|
|
72
|
+
expect(requireFn).toHaveBeenCalledTimes(1);
|
|
73
|
+
});
|
|
74
|
+
});
|
package/src/matrix/deps.ts
CHANGED
|
@@ -2,9 +2,30 @@ import fs from "node:fs";
|
|
|
2
2
|
import { createRequire } from "node:module";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { fileURLToPath } from "node:url";
|
|
5
|
-
import { runPluginCommandWithTimeout, type RuntimeEnv } from "openclaw/plugin-sdk";
|
|
5
|
+
import { runPluginCommandWithTimeout, type RuntimeEnv } from "openclaw/plugin-sdk/matrix";
|
|
6
6
|
|
|
7
7
|
const MATRIX_SDK_PACKAGE = "@vector-im/matrix-bot-sdk";
|
|
8
|
+
const MATRIX_CRYPTO_DOWNLOAD_HELPER = "@matrix-org/matrix-sdk-crypto-nodejs/download-lib.js";
|
|
9
|
+
|
|
10
|
+
function formatCommandError(result: { stderr: string; stdout: string }): string {
|
|
11
|
+
const stderr = result.stderr.trim();
|
|
12
|
+
if (stderr) {
|
|
13
|
+
return stderr;
|
|
14
|
+
}
|
|
15
|
+
const stdout = result.stdout.trim();
|
|
16
|
+
if (stdout) {
|
|
17
|
+
return stdout;
|
|
18
|
+
}
|
|
19
|
+
return "unknown error";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function isMissingMatrixCryptoRuntimeError(err: unknown): boolean {
|
|
23
|
+
const message = err instanceof Error ? err.message : String(err ?? "");
|
|
24
|
+
return (
|
|
25
|
+
message.includes("Cannot find module") &&
|
|
26
|
+
message.includes("@matrix-org/matrix-sdk-crypto-nodejs-")
|
|
27
|
+
);
|
|
28
|
+
}
|
|
8
29
|
|
|
9
30
|
export function isMatrixSdkAvailable(): boolean {
|
|
10
31
|
try {
|
|
@@ -21,6 +42,51 @@ function resolvePluginRoot(): string {
|
|
|
21
42
|
return path.resolve(currentDir, "..", "..");
|
|
22
43
|
}
|
|
23
44
|
|
|
45
|
+
export async function ensureMatrixCryptoRuntime(
|
|
46
|
+
params: {
|
|
47
|
+
log?: (message: string) => void;
|
|
48
|
+
requireFn?: (id: string) => unknown;
|
|
49
|
+
resolveFn?: (id: string) => string;
|
|
50
|
+
runCommand?: typeof runPluginCommandWithTimeout;
|
|
51
|
+
nodeExecutable?: string;
|
|
52
|
+
} = {},
|
|
53
|
+
): Promise<void> {
|
|
54
|
+
const req = createRequire(import.meta.url);
|
|
55
|
+
const requireFn = params.requireFn ?? ((id: string) => req(id));
|
|
56
|
+
const resolveFn = params.resolveFn ?? ((id: string) => req.resolve(id));
|
|
57
|
+
const runCommand = params.runCommand ?? runPluginCommandWithTimeout;
|
|
58
|
+
const nodeExecutable = params.nodeExecutable ?? process.execPath;
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
requireFn(MATRIX_SDK_PACKAGE);
|
|
62
|
+
return;
|
|
63
|
+
} catch (err) {
|
|
64
|
+
if (!isMissingMatrixCryptoRuntimeError(err)) {
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const scriptPath = resolveFn(MATRIX_CRYPTO_DOWNLOAD_HELPER);
|
|
70
|
+
params.log?.("matrix: crypto runtime missing; downloading platform library…");
|
|
71
|
+
const result = await runCommand({
|
|
72
|
+
argv: [nodeExecutable, scriptPath],
|
|
73
|
+
cwd: path.dirname(scriptPath),
|
|
74
|
+
timeoutMs: 300_000,
|
|
75
|
+
env: { COREPACK_ENABLE_DOWNLOAD_PROMPT: "0" },
|
|
76
|
+
});
|
|
77
|
+
if (result.code !== 0) {
|
|
78
|
+
throw new Error(`Matrix crypto runtime bootstrap failed: ${formatCommandError(result)}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
try {
|
|
82
|
+
requireFn(MATRIX_SDK_PACKAGE);
|
|
83
|
+
} catch (err) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Matrix crypto runtime remains unavailable after bootstrap: ${err instanceof Error ? err.message : String(err)}`,
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
24
90
|
export async function ensureMatrixSdkInstalled(params: {
|
|
25
91
|
runtime: RuntimeEnv;
|
|
26
92
|
confirm?: (message: string) => Promise<boolean>;
|
|
@@ -3,7 +3,8 @@ import {
|
|
|
3
3
|
issuePairingChallenge,
|
|
4
4
|
readStoreAllowFromForDmPolicy,
|
|
5
5
|
resolveDmGroupAccessWithLists,
|
|
6
|
-
|
|
6
|
+
resolveSenderScopedGroupPolicy,
|
|
7
|
+
} from "openclaw/plugin-sdk/matrix";
|
|
7
8
|
import {
|
|
8
9
|
normalizeMatrixAllowList,
|
|
9
10
|
resolveMatrixAllowListMatch,
|
|
@@ -32,12 +33,10 @@ export async function resolveMatrixAccessState(params: {
|
|
|
32
33
|
})
|
|
33
34
|
: [];
|
|
34
35
|
const normalizedGroupAllowFrom = normalizeMatrixAllowList(params.groupAllowFrom);
|
|
35
|
-
const senderGroupPolicy =
|
|
36
|
-
params.groupPolicy
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
? "allowlist"
|
|
40
|
-
: "open";
|
|
36
|
+
const senderGroupPolicy = resolveSenderScopedGroupPolicy({
|
|
37
|
+
groupPolicy: params.groupPolicy,
|
|
38
|
+
groupAllowFrom: normalizedGroupAllowFrom,
|
|
39
|
+
});
|
|
41
40
|
const access = resolveDmGroupAccessWithLists({
|
|
42
41
|
isGroup: !params.isDirectMessage,
|
|
43
42
|
dmPolicy: params.dmPolicy,
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
normalizeStringEntries,
|
|
3
|
+
resolveAllowlistMatchByCandidates,
|
|
4
|
+
type AllowlistMatch,
|
|
5
|
+
} from "openclaw/plugin-sdk/matrix";
|
|
2
6
|
|
|
3
7
|
function normalizeAllowList(list?: Array<string | number>) {
|
|
4
|
-
return (list
|
|
8
|
+
return normalizeStringEntries(list);
|
|
5
9
|
}
|
|
6
10
|
|
|
7
11
|
function normalizeMatrixUser(raw?: string | null): string {
|
|
@@ -65,6 +69,7 @@ export function normalizeMatrixAllowList(list?: Array<string | number>) {
|
|
|
65
69
|
export type MatrixAllowListMatch = AllowlistMatch<
|
|
66
70
|
"wildcard" | "id" | "prefixed-id" | "prefixed-user"
|
|
67
71
|
>;
|
|
72
|
+
type MatrixAllowListSource = Exclude<MatrixAllowListMatch["matchSource"], undefined>;
|
|
68
73
|
|
|
69
74
|
export function resolveMatrixAllowListMatch(params: {
|
|
70
75
|
allowList: string[];
|
|
@@ -78,24 +83,12 @@ export function resolveMatrixAllowListMatch(params: {
|
|
|
78
83
|
return { allowed: true, matchKey: "*", matchSource: "wildcard" };
|
|
79
84
|
}
|
|
80
85
|
const userId = normalizeMatrixUser(params.userId);
|
|
81
|
-
const candidates: Array<{ value?: string; source:
|
|
86
|
+
const candidates: Array<{ value?: string; source: MatrixAllowListSource }> = [
|
|
82
87
|
{ value: userId, source: "id" },
|
|
83
88
|
{ value: userId ? `matrix:${userId}` : "", source: "prefixed-id" },
|
|
84
89
|
{ value: userId ? `user:${userId}` : "", source: "prefixed-user" },
|
|
85
90
|
];
|
|
86
|
-
|
|
87
|
-
if (!candidate.value) {
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
if (allowList.includes(candidate.value)) {
|
|
91
|
-
return {
|
|
92
|
-
allowed: true,
|
|
93
|
-
matchKey: candidate.value,
|
|
94
|
-
matchSource: candidate.source,
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return { allowed: false };
|
|
91
|
+
return resolveAllowlistMatchByCandidates({ allowList, candidates });
|
|
99
92
|
}
|
|
100
93
|
|
|
101
94
|
export function resolveMatrixAllowListMatches(params: { allowList: string[]; userId?: string }) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import {
|
|
3
|
-
import type { RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { RuntimeEnv } from "openclaw/plugin-sdk/matrix";
|
|
4
3
|
import { getMatrixRuntime } from "../../runtime.js";
|
|
5
4
|
import type { CoreConfig } from "../../types.js";
|
|
5
|
+
import { loadMatrixSdk } from "../sdk-runtime.js";
|
|
6
6
|
|
|
7
7
|
export function registerMatrixAutoJoin(params: {
|
|
8
8
|
client: MatrixClient;
|
|
@@ -26,6 +26,7 @@ export function registerMatrixAutoJoin(params: {
|
|
|
26
26
|
|
|
27
27
|
if (autoJoin === "always") {
|
|
28
28
|
// Use the built-in autojoin mixin for "always" mode
|
|
29
|
+
const { AutojoinRoomsMixin } = loadMatrixSdk();
|
|
29
30
|
AutojoinRoomsMixin.setupOnClient(client);
|
|
30
31
|
logVerbose("matrix: auto-join enabled for all invites");
|
|
31
32
|
return;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
import type { MatrixAuth } from "../client.js";
|
|
5
5
|
import { registerMatrixMonitorEvents } from "./events.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { PluginRuntime, RuntimeLogger } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import type { MatrixAuth } from "../client.js";
|
|
4
4
|
import { sendReadReceiptMatrix } from "../send.js";
|
|
5
5
|
import type { MatrixRawEvent } from "./types.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import type { PluginRuntime, RuntimeEnv, RuntimeLogger } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { PluginRuntime, RuntimeEnv, RuntimeLogger } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import { describe, expect, it, vi } from "vitest";
|
|
4
4
|
import { createMatrixRoomMessageHandler } from "./handler.js";
|
|
5
5
|
import { EventType, type MatrixRawEvent } from "./types.js";
|
|
@@ -4,14 +4,17 @@ import {
|
|
|
4
4
|
createScopedPairingAccess,
|
|
5
5
|
createReplyPrefixOptions,
|
|
6
6
|
createTypingCallbacks,
|
|
7
|
+
dispatchReplyFromConfigWithSettledDispatcher,
|
|
8
|
+
evaluateGroupRouteAccessForPolicy,
|
|
7
9
|
formatAllowlistMatchMeta,
|
|
8
10
|
logInboundDrop,
|
|
9
11
|
logTypingFailure,
|
|
12
|
+
resolveInboundSessionEnvelopeContext,
|
|
10
13
|
resolveControlCommandGate,
|
|
11
14
|
type PluginRuntime,
|
|
12
15
|
type RuntimeEnv,
|
|
13
16
|
type RuntimeLogger,
|
|
14
|
-
} from "openclaw/plugin-sdk";
|
|
17
|
+
} from "openclaw/plugin-sdk/matrix";
|
|
15
18
|
import type { CoreConfig, MatrixRoomConfig, ReplyToMode } from "../../types.js";
|
|
16
19
|
import { fetchEventSummary } from "../actions/summary.js";
|
|
17
20
|
import {
|
|
@@ -192,10 +195,6 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|
|
192
195
|
});
|
|
193
196
|
const isRoom = !isDirectMessage;
|
|
194
197
|
|
|
195
|
-
if (isRoom && groupPolicy === "disabled") {
|
|
196
|
-
return;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
198
|
const roomConfigInfo = isRoom
|
|
200
199
|
? resolveMatrixRoomConfig({
|
|
201
200
|
rooms: roomsConfig,
|
|
@@ -211,17 +210,21 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|
|
211
210
|
}`
|
|
212
211
|
: "matchKey=none matchSource=none";
|
|
213
212
|
|
|
214
|
-
if (isRoom
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
213
|
+
if (isRoom) {
|
|
214
|
+
const routeAccess = evaluateGroupRouteAccessForPolicy({
|
|
215
|
+
groupPolicy,
|
|
216
|
+
routeAllowlistConfigured: Boolean(roomConfigInfo?.allowlistConfigured),
|
|
217
|
+
routeMatched: Boolean(roomConfig),
|
|
218
|
+
routeEnabled: roomConfigInfo?.allowed ?? true,
|
|
219
|
+
});
|
|
220
|
+
if (!routeAccess.allowed) {
|
|
221
|
+
if (routeAccess.reason === "route_disabled") {
|
|
222
|
+
logVerboseMessage(`matrix: room disabled room=${roomId} (${roomMatchMeta})`);
|
|
223
|
+
} else if (routeAccess.reason === "empty_allowlist") {
|
|
224
|
+
logVerboseMessage(`matrix: drop room message (no allowlist, ${roomMatchMeta})`);
|
|
225
|
+
} else if (routeAccess.reason === "route_not_allowlisted") {
|
|
226
|
+
logVerboseMessage(`matrix: drop room message (not in allowlist, ${roomMatchMeta})`);
|
|
227
|
+
}
|
|
225
228
|
return;
|
|
226
229
|
}
|
|
227
230
|
}
|
|
@@ -484,14 +487,12 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|
|
484
487
|
const textWithId = threadRootId
|
|
485
488
|
? `${bodyText}\n[matrix event id: ${messageId} room: ${roomId} thread: ${threadRootId}]`
|
|
486
489
|
: `${bodyText}\n[matrix event id: ${messageId} room: ${roomId}]`;
|
|
487
|
-
const storePath
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
sessionKey: route.sessionKey,
|
|
494
|
-
});
|
|
490
|
+
const { storePath, envelopeOptions, previousTimestamp } =
|
|
491
|
+
resolveInboundSessionEnvelopeContext({
|
|
492
|
+
cfg,
|
|
493
|
+
agentId: route.agentId,
|
|
494
|
+
sessionKey: route.sessionKey,
|
|
495
|
+
});
|
|
495
496
|
const body = core.channel.reply.formatInboundEnvelope({
|
|
496
497
|
channel: "Matrix",
|
|
497
498
|
from: envelopeFrom,
|
|
@@ -655,22 +656,18 @@ export function createMatrixRoomMessageHandler(params: MatrixMonitorHandlerParam
|
|
|
655
656
|
},
|
|
656
657
|
});
|
|
657
658
|
|
|
658
|
-
const { queuedFinal, counts } = await
|
|
659
|
+
const { queuedFinal, counts } = await dispatchReplyFromConfigWithSettledDispatcher({
|
|
660
|
+
cfg,
|
|
661
|
+
ctxPayload,
|
|
659
662
|
dispatcher,
|
|
660
663
|
onSettled: () => {
|
|
661
664
|
markDispatchIdle();
|
|
662
665
|
},
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
replyOptions: {
|
|
669
|
-
...replyOptions,
|
|
670
|
-
skillFilter: roomConfig?.skills,
|
|
671
|
-
onModelSelected,
|
|
672
|
-
},
|
|
673
|
-
}),
|
|
666
|
+
replyOptions: {
|
|
667
|
+
...replyOptions,
|
|
668
|
+
skillFilter: roomConfig?.skills,
|
|
669
|
+
onModelSelected,
|
|
670
|
+
},
|
|
674
671
|
});
|
|
675
672
|
if (!queuedFinal) {
|
|
676
673
|
return;
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import {
|
|
2
|
-
createLoggerBackedRuntime,
|
|
3
2
|
GROUP_POLICY_BLOCKED_LABEL,
|
|
4
3
|
mergeAllowlist,
|
|
4
|
+
resolveRuntimeEnv,
|
|
5
5
|
resolveAllowlistProviderRuntimeGroupPolicy,
|
|
6
6
|
resolveDefaultGroupPolicy,
|
|
7
7
|
summarizeMapping,
|
|
8
8
|
warnMissingProviderGroupPolicyFallbackOnce,
|
|
9
9
|
type RuntimeEnv,
|
|
10
|
-
} from "openclaw/plugin-sdk";
|
|
10
|
+
} from "openclaw/plugin-sdk/matrix";
|
|
11
11
|
import { resolveMatrixTargets } from "../../resolve-targets.js";
|
|
12
12
|
import { getMatrixRuntime } from "../../runtime.js";
|
|
13
13
|
import type { CoreConfig, MatrixConfig, MatrixRoomConfig, ReplyToMode } from "../../types.js";
|
|
@@ -241,11 +241,10 @@ export async function monitorMatrixProvider(opts: MonitorMatrixOpts = {}): Promi
|
|
|
241
241
|
}
|
|
242
242
|
|
|
243
243
|
const logger = core.logging.getChildLogger({ module: "matrix-auto-reply" });
|
|
244
|
-
const runtime: RuntimeEnv =
|
|
245
|
-
opts.runtime
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
});
|
|
244
|
+
const runtime: RuntimeEnv = resolveRuntimeEnv({
|
|
245
|
+
runtime: opts.runtime,
|
|
246
|
+
logger,
|
|
247
|
+
});
|
|
249
248
|
const logVerboseMessage = (message: string) => {
|
|
250
249
|
if (!core.logging.shouldLogVerbose()) {
|
|
251
250
|
return;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PluginRuntime } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { PluginRuntime } from "openclaw/plugin-sdk/matrix";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { setMatrixRuntime } from "../../runtime.js";
|
|
4
4
|
import { downloadMatrixMedia } from "./media.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
4
4
|
|
|
5
5
|
const sendMessageMatrixMock = vi.hoisted(() => vi.fn().mockResolvedValue({ messageId: "mx-1" }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
2
|
+
import type { MarkdownTableMode, ReplyPayload, RuntimeEnv } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import { getMatrixRuntime } from "../../runtime.js";
|
|
4
4
|
import { sendMessageMatrix } from "../send.js";
|
|
5
5
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "openclaw/plugin-sdk";
|
|
1
|
+
import { buildChannelKeyCandidates, resolveChannelEntryMatch } from "openclaw/plugin-sdk/matrix";
|
|
2
2
|
import type { MatrixRoomConfig } from "../../types.js";
|
|
3
3
|
|
|
4
4
|
export type MatrixRoomConfigResolved = {
|
package/src/matrix/poll-types.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - m.poll.end - Closes a poll
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import type { PollInput } from "openclaw/plugin-sdk";
|
|
10
|
+
import type { PollInput } from "openclaw/plugin-sdk/matrix";
|
|
11
11
|
|
|
12
12
|
export const M_POLL_START = "m.poll.start" as const;
|
|
13
13
|
export const M_POLL_RESPONSE = "m.poll.response" as const;
|
package/src/matrix/probe.ts
CHANGED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
type MatrixSdkRuntime = typeof import("@vector-im/matrix-bot-sdk");
|
|
4
|
+
|
|
5
|
+
let cachedMatrixSdkRuntime: MatrixSdkRuntime | null = null;
|
|
6
|
+
|
|
7
|
+
export function loadMatrixSdk(): MatrixSdkRuntime {
|
|
8
|
+
if (cachedMatrixSdkRuntime) {
|
|
9
|
+
return cachedMatrixSdkRuntime;
|
|
10
|
+
}
|
|
11
|
+
const req = createRequire(import.meta.url);
|
|
12
|
+
cachedMatrixSdkRuntime = req("@vector-im/matrix-bot-sdk") as MatrixSdkRuntime;
|
|
13
|
+
return cachedMatrixSdkRuntime;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function getMatrixLogService() {
|
|
17
|
+
return loadMatrixSdk().LogService;
|
|
18
|
+
}
|
|
@@ -32,19 +32,19 @@ function findAccountConfig(
|
|
|
32
32
|
return undefined;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
export function resolveMediaMaxBytes(accountId?: string): number | undefined {
|
|
36
|
-
const
|
|
35
|
+
export function resolveMediaMaxBytes(accountId?: string, cfg?: CoreConfig): number | undefined {
|
|
36
|
+
const resolvedCfg = cfg ?? (getCore().config.loadConfig() as CoreConfig);
|
|
37
37
|
// Check account-specific config first (case-insensitive key matching)
|
|
38
38
|
const accountConfig = findAccountConfig(
|
|
39
|
-
|
|
39
|
+
resolvedCfg.channels?.matrix?.accounts as Record<string, unknown> | undefined,
|
|
40
40
|
accountId ?? "",
|
|
41
41
|
);
|
|
42
42
|
if (typeof accountConfig?.mediaMaxMb === "number") {
|
|
43
43
|
return (accountConfig.mediaMaxMb as number) * 1024 * 1024;
|
|
44
44
|
}
|
|
45
45
|
// Fall back to top-level config
|
|
46
|
-
if (typeof
|
|
47
|
-
return
|
|
46
|
+
if (typeof resolvedCfg.channels?.matrix?.mediaMaxMb === "number") {
|
|
47
|
+
return resolvedCfg.channels.matrix.mediaMaxMb * 1024 * 1024;
|
|
48
48
|
}
|
|
49
49
|
return undefined;
|
|
50
50
|
}
|
|
@@ -53,6 +53,7 @@ export async function resolveMatrixClient(opts: {
|
|
|
53
53
|
client?: MatrixClient;
|
|
54
54
|
timeoutMs?: number;
|
|
55
55
|
accountId?: string;
|
|
56
|
+
cfg?: CoreConfig;
|
|
56
57
|
}): Promise<{ client: MatrixClient; stopOnDone: boolean }> {
|
|
57
58
|
ensureNodeRuntime();
|
|
58
59
|
if (opts.client) {
|
|
@@ -84,10 +85,11 @@ export async function resolveMatrixClient(opts: {
|
|
|
84
85
|
const client = await resolveSharedMatrixClient({
|
|
85
86
|
timeoutMs: opts.timeoutMs,
|
|
86
87
|
accountId,
|
|
88
|
+
cfg: opts.cfg,
|
|
87
89
|
});
|
|
88
90
|
return { client, stopOnDone: false };
|
|
89
91
|
}
|
|
90
|
-
const auth = await resolveMatrixAuth({ accountId });
|
|
92
|
+
const auth = await resolveMatrixAuth({ accountId, cfg: opts.cfg });
|
|
91
93
|
const client = await createPreparedMatrixClient({
|
|
92
94
|
auth,
|
|
93
95
|
timeoutMs: opts.timeoutMs,
|
package/src/matrix/send/types.ts
CHANGED
package/src/matrix/send-queue.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { KeyedAsyncQueue } from "openclaw/plugin-sdk/keyed-async-queue";
|
|
2
|
+
|
|
1
3
|
export const DEFAULT_SEND_GAP_MS = 150;
|
|
2
4
|
|
|
3
5
|
type MatrixSendQueueOptions = {
|
|
@@ -6,37 +8,19 @@ type MatrixSendQueueOptions = {
|
|
|
6
8
|
};
|
|
7
9
|
|
|
8
10
|
// Serialize sends per room to preserve Matrix delivery order.
|
|
9
|
-
const roomQueues = new
|
|
11
|
+
const roomQueues = new KeyedAsyncQueue();
|
|
10
12
|
|
|
11
|
-
export
|
|
13
|
+
export function enqueueSend<T>(
|
|
12
14
|
roomId: string,
|
|
13
15
|
fn: () => Promise<T>,
|
|
14
16
|
options?: MatrixSendQueueOptions,
|
|
15
17
|
): Promise<T> {
|
|
16
18
|
const gapMs = options?.gapMs ?? DEFAULT_SEND_GAP_MS;
|
|
17
19
|
const delayFn = options?.delayFn ?? delay;
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
.catch(() => {})
|
|
22
|
-
.then(async () => {
|
|
23
|
-
await delayFn(gapMs);
|
|
24
|
-
return await fn();
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
const queueMarker = next.then(
|
|
28
|
-
() => {},
|
|
29
|
-
() => {},
|
|
30
|
-
);
|
|
31
|
-
roomQueues.set(roomId, queueMarker);
|
|
32
|
-
|
|
33
|
-
queueMarker.finally(() => {
|
|
34
|
-
if (roomQueues.get(roomId) === queueMarker) {
|
|
35
|
-
roomQueues.delete(roomId);
|
|
36
|
-
}
|
|
20
|
+
return roomQueues.enqueue(roomId, async () => {
|
|
21
|
+
await delayFn(gapMs);
|
|
22
|
+
return await fn();
|
|
37
23
|
});
|
|
38
|
-
|
|
39
|
-
return await next;
|
|
40
24
|
}
|
|
41
25
|
|
|
42
26
|
function delay(ms: number): Promise<void> {
|