@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
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2026.3.7
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Version alignment with core OpenClaw release numbers.
|
|
8
|
+
|
|
9
|
+
## 2026.3.3
|
|
10
|
+
|
|
11
|
+
### Changes
|
|
12
|
+
|
|
13
|
+
- Version alignment with core OpenClaw release numbers.
|
|
14
|
+
|
|
15
|
+
## 2026.3.2
|
|
16
|
+
|
|
17
|
+
### Changes
|
|
18
|
+
|
|
19
|
+
- Version alignment with core OpenClaw release numbers.
|
|
20
|
+
|
|
3
21
|
## 2026.3.1
|
|
4
22
|
|
|
5
23
|
### Changes
|
package/index.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import type { OpenClawPluginApi } from "openclaw/plugin-sdk";
|
|
2
|
-
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { OpenClawPluginApi } from "openclaw/plugin-sdk/matrix";
|
|
2
|
+
import { emptyPluginConfigSchema } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import { matrixPlugin } from "./src/channel.js";
|
|
4
|
+
import { ensureMatrixCryptoRuntime } from "./src/matrix/deps.js";
|
|
4
5
|
import { setMatrixRuntime } from "./src/runtime.js";
|
|
5
6
|
|
|
6
7
|
const plugin = {
|
|
@@ -10,6 +11,10 @@ const plugin = {
|
|
|
10
11
|
configSchema: emptyPluginConfigSchema(),
|
|
11
12
|
register(api: OpenClawPluginApi) {
|
|
12
13
|
setMatrixRuntime(api.runtime);
|
|
14
|
+
void ensureMatrixCryptoRuntime({ log: api.logger.info }).catch((err) => {
|
|
15
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
16
|
+
api.logger.warn?.(`matrix: crypto runtime bootstrap failed: ${message}`);
|
|
17
|
+
});
|
|
13
18
|
api.registerChannel({ plugin: matrixPlugin });
|
|
14
19
|
},
|
|
15
20
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/matrix",
|
|
3
|
-
"version": "2026.3.
|
|
3
|
+
"version": "2026.3.7",
|
|
4
4
|
"description": "OpenClaw Matrix channel plugin",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"dependencies": {
|
|
7
|
+
"@mariozechner/pi-agent-core": "0.55.3",
|
|
7
8
|
"@matrix-org/matrix-sdk-crypto-nodejs": "^0.4.0",
|
|
8
9
|
"@vector-im/matrix-bot-sdk": "0.8.0-element.3",
|
|
9
10
|
"markdown-it": "14.1.1",
|
package/src/actions.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
type ChannelMessageActionContext,
|
|
7
7
|
type ChannelMessageActionName,
|
|
8
8
|
type ChannelToolSend,
|
|
9
|
-
} from "openclaw/plugin-sdk";
|
|
9
|
+
} from "openclaw/plugin-sdk/matrix";
|
|
10
10
|
import { resolveMatrixAccount } from "./matrix/accounts.js";
|
|
11
11
|
import { handleMatrixAction } from "./tool-actions.js";
|
|
12
12
|
import type { CoreConfig } from "./types.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { PluginRuntime, RuntimeEnv } from "openclaw/plugin-sdk/matrix";
|
|
2
2
|
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
3
3
|
import { matrixPlugin } from "./channel.js";
|
|
4
4
|
import { setMatrixRuntime } from "./runtime.js";
|
package/src/channel.ts
CHANGED
|
@@ -1,16 +1,21 @@
|
|
|
1
|
+
import {
|
|
2
|
+
buildAccountScopedDmSecurityPolicy,
|
|
3
|
+
buildOpenGroupPolicyWarning,
|
|
4
|
+
collectAllowlistProviderGroupPolicyWarnings,
|
|
5
|
+
createScopedAccountConfigAccessors,
|
|
6
|
+
} from "openclaw/plugin-sdk/compat";
|
|
1
7
|
import {
|
|
2
8
|
applyAccountNameToChannelSection,
|
|
3
9
|
buildChannelConfigSchema,
|
|
10
|
+
buildProbeChannelStatusSummary,
|
|
11
|
+
collectStatusIssuesFromLastError,
|
|
4
12
|
DEFAULT_ACCOUNT_ID,
|
|
5
13
|
deleteAccountFromConfigSection,
|
|
6
|
-
formatPairingApproveHint,
|
|
7
14
|
normalizeAccountId,
|
|
8
15
|
PAIRING_APPROVED_MESSAGE,
|
|
9
|
-
resolveAllowlistProviderRuntimeGroupPolicy,
|
|
10
|
-
resolveDefaultGroupPolicy,
|
|
11
16
|
setAccountEnabledInConfigSection,
|
|
12
17
|
type ChannelPlugin,
|
|
13
|
-
} from "openclaw/plugin-sdk";
|
|
18
|
+
} from "openclaw/plugin-sdk/matrix";
|
|
14
19
|
import { matrixMessageActions } from "./actions.js";
|
|
15
20
|
import { MatrixConfigSchema } from "./config-schema.js";
|
|
16
21
|
import { listMatrixDirectoryGroupsLive, listMatrixDirectoryPeersLive } from "./directory-live.js";
|
|
@@ -32,6 +37,7 @@ import { sendMessageMatrix } from "./matrix/send.js";
|
|
|
32
37
|
import { matrixOnboardingAdapter } from "./onboarding.js";
|
|
33
38
|
import { matrixOutbound } from "./outbound.js";
|
|
34
39
|
import { resolveMatrixTargets } from "./resolve-targets.js";
|
|
40
|
+
import { normalizeSecretInputString } from "./secret-input.js";
|
|
35
41
|
import type { CoreConfig } from "./types.js";
|
|
36
42
|
|
|
37
43
|
// Mutex for serializing account startup (workaround for concurrent dynamic import race condition)
|
|
@@ -93,6 +99,13 @@ function buildMatrixConfigUpdate(
|
|
|
93
99
|
};
|
|
94
100
|
}
|
|
95
101
|
|
|
102
|
+
const matrixConfigAccessors = createScopedAccountConfigAccessors({
|
|
103
|
+
resolveAccount: ({ cfg, accountId }) =>
|
|
104
|
+
resolveMatrixAccountConfig({ cfg: cfg as CoreConfig, accountId }),
|
|
105
|
+
resolveAllowFrom: (account) => account.dm?.allowFrom,
|
|
106
|
+
formatAllowFrom: (allowFrom) => normalizeMatrixAllowList(allowFrom),
|
|
107
|
+
});
|
|
108
|
+
|
|
96
109
|
export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
|
|
97
110
|
id: "matrix",
|
|
98
111
|
meta,
|
|
@@ -148,41 +161,38 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
|
|
|
148
161
|
configured: account.configured,
|
|
149
162
|
baseUrl: account.homeserver,
|
|
150
163
|
}),
|
|
151
|
-
|
|
152
|
-
const matrixConfig = resolveMatrixAccountConfig({ cfg: cfg as CoreConfig, accountId });
|
|
153
|
-
return (matrixConfig.dm?.allowFrom ?? []).map((entry: string | number) => String(entry));
|
|
154
|
-
},
|
|
155
|
-
formatAllowFrom: ({ allowFrom }) => normalizeMatrixAllowList(allowFrom),
|
|
164
|
+
...matrixConfigAccessors,
|
|
156
165
|
},
|
|
157
166
|
security: {
|
|
158
|
-
resolveDmPolicy: ({ account }) => {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
policy: account.config.dm?.policy ?? "pairing",
|
|
167
|
+
resolveDmPolicy: ({ cfg, accountId, account }) => {
|
|
168
|
+
return buildAccountScopedDmSecurityPolicy({
|
|
169
|
+
cfg: cfg as CoreConfig,
|
|
170
|
+
channelKey: "matrix",
|
|
171
|
+
accountId,
|
|
172
|
+
fallbackAccountId: account.accountId ?? DEFAULT_ACCOUNT_ID,
|
|
173
|
+
policy: account.config.dm?.policy,
|
|
166
174
|
allowFrom: account.config.dm?.allowFrom ?? [],
|
|
167
|
-
|
|
168
|
-
allowFromPath: `${prefix}.allowFrom`,
|
|
169
|
-
approveHint: formatPairingApproveHint("matrix"),
|
|
175
|
+
allowFromPathSuffix: "dm.",
|
|
170
176
|
normalizeEntry: (raw) => normalizeMatrixUserId(raw),
|
|
171
|
-
};
|
|
177
|
+
});
|
|
172
178
|
},
|
|
173
179
|
collectWarnings: ({ account, cfg }) => {
|
|
174
|
-
|
|
175
|
-
|
|
180
|
+
return collectAllowlistProviderGroupPolicyWarnings({
|
|
181
|
+
cfg: cfg as CoreConfig,
|
|
176
182
|
providerConfigPresent: (cfg as CoreConfig).channels?.matrix !== undefined,
|
|
177
|
-
|
|
178
|
-
|
|
183
|
+
configuredGroupPolicy: account.config.groupPolicy,
|
|
184
|
+
collect: (groupPolicy) =>
|
|
185
|
+
groupPolicy === "open"
|
|
186
|
+
? [
|
|
187
|
+
buildOpenGroupPolicyWarning({
|
|
188
|
+
surface: "Matrix rooms",
|
|
189
|
+
openBehavior: "allows any room to trigger (mention-gated)",
|
|
190
|
+
remediation:
|
|
191
|
+
'Set channels.matrix.groupPolicy="allowlist" + channels.matrix.groups (and optionally channels.matrix.groupAllowFrom) to restrict rooms',
|
|
192
|
+
}),
|
|
193
|
+
]
|
|
194
|
+
: [],
|
|
179
195
|
});
|
|
180
|
-
if (groupPolicy !== "open") {
|
|
181
|
-
return [];
|
|
182
|
-
}
|
|
183
|
-
return [
|
|
184
|
-
'- Matrix rooms: groupPolicy="open" allows any room to trigger (mention-gated). Set channels.matrix.groupPolicy="allowlist" + channels.matrix.groups (and optionally channels.matrix.groupAllowFrom) to restrict rooms.',
|
|
185
|
-
];
|
|
186
196
|
},
|
|
187
197
|
},
|
|
188
198
|
groups: {
|
|
@@ -325,7 +335,7 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
|
|
|
325
335
|
return "Matrix requires --homeserver";
|
|
326
336
|
}
|
|
327
337
|
const accessToken = input.accessToken?.trim();
|
|
328
|
-
const password = input.password
|
|
338
|
+
const password = normalizeSecretInputString(input.password);
|
|
329
339
|
const userId = input.userId?.trim();
|
|
330
340
|
if (!accessToken && !password) {
|
|
331
341
|
return "Matrix requires --access-token or --password";
|
|
@@ -363,7 +373,7 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
|
|
|
363
373
|
homeserver: input.homeserver?.trim(),
|
|
364
374
|
userId: input.userId?.trim(),
|
|
365
375
|
accessToken: input.accessToken?.trim(),
|
|
366
|
-
password: input.password
|
|
376
|
+
password: normalizeSecretInputString(input.password),
|
|
367
377
|
deviceName: input.deviceName?.trim(),
|
|
368
378
|
initialSyncLimit: input.initialSyncLimit,
|
|
369
379
|
});
|
|
@@ -378,31 +388,9 @@ export const matrixPlugin: ChannelPlugin<ResolvedMatrixAccount> = {
|
|
|
378
388
|
lastStopAt: null,
|
|
379
389
|
lastError: null,
|
|
380
390
|
},
|
|
381
|
-
collectStatusIssues: (accounts) =>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
if (!lastError) {
|
|
385
|
-
return [];
|
|
386
|
-
}
|
|
387
|
-
return [
|
|
388
|
-
{
|
|
389
|
-
channel: "matrix",
|
|
390
|
-
accountId: account.accountId,
|
|
391
|
-
kind: "runtime",
|
|
392
|
-
message: `Channel error: ${lastError}`,
|
|
393
|
-
},
|
|
394
|
-
];
|
|
395
|
-
}),
|
|
396
|
-
buildChannelSummary: ({ snapshot }) => ({
|
|
397
|
-
configured: snapshot.configured ?? false,
|
|
398
|
-
baseUrl: snapshot.baseUrl ?? null,
|
|
399
|
-
running: snapshot.running ?? false,
|
|
400
|
-
lastStartAt: snapshot.lastStartAt ?? null,
|
|
401
|
-
lastStopAt: snapshot.lastStopAt ?? null,
|
|
402
|
-
lastError: snapshot.lastError ?? null,
|
|
403
|
-
probe: snapshot.probe,
|
|
404
|
-
lastProbeAt: snapshot.lastProbeAt ?? null,
|
|
405
|
-
}),
|
|
391
|
+
collectStatusIssues: (accounts) => collectStatusIssuesFromLastError("matrix", accounts),
|
|
392
|
+
buildChannelSummary: ({ snapshot }) =>
|
|
393
|
+
buildProbeChannelStatusSummary(snapshot, { baseUrl: snapshot.baseUrl ?? null }),
|
|
406
394
|
probeAccount: async ({ account, timeoutMs, cfg }) => {
|
|
407
395
|
try {
|
|
408
396
|
const auth = await resolveMatrixAuth({
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { MatrixConfigSchema } from "./config-schema.js";
|
|
3
|
+
|
|
4
|
+
describe("MatrixConfigSchema SecretInput", () => {
|
|
5
|
+
it("accepts SecretRef password at top-level", () => {
|
|
6
|
+
const result = MatrixConfigSchema.safeParse({
|
|
7
|
+
homeserver: "https://matrix.example.org",
|
|
8
|
+
userId: "@bot:example.org",
|
|
9
|
+
password: { source: "env", provider: "default", id: "MATRIX_PASSWORD" },
|
|
10
|
+
});
|
|
11
|
+
expect(result.success).toBe(true);
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it("accepts SecretRef password on account", () => {
|
|
15
|
+
const result = MatrixConfigSchema.safeParse({
|
|
16
|
+
accounts: {
|
|
17
|
+
work: {
|
|
18
|
+
homeserver: "https://matrix.example.org",
|
|
19
|
+
userId: "@bot:example.org",
|
|
20
|
+
password: { source: "env", provider: "default", id: "MATRIX_WORK_PASSWORD" },
|
|
21
|
+
},
|
|
22
|
+
},
|
|
23
|
+
});
|
|
24
|
+
expect(result.success).toBe(true);
|
|
25
|
+
});
|
|
26
|
+
});
|
package/src/config-schema.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk";
|
|
1
|
+
import { MarkdownConfigSchema, ToolPolicySchema } from "openclaw/plugin-sdk/matrix";
|
|
2
2
|
import { z } from "zod";
|
|
3
|
+
import { buildSecretInputSchema } from "./secret-input.js";
|
|
3
4
|
|
|
4
5
|
const allowFromEntry = z.union([z.string(), z.number()]);
|
|
5
6
|
|
|
@@ -43,7 +44,7 @@ export const MatrixConfigSchema = z.object({
|
|
|
43
44
|
homeserver: z.string().optional(),
|
|
44
45
|
userId: z.string().optional(),
|
|
45
46
|
accessToken: z.string().optional(),
|
|
46
|
-
password:
|
|
47
|
+
password: buildSecretInputSchema().optional(),
|
|
47
48
|
deviceName: z.string().optional(),
|
|
48
49
|
initialSyncLimit: z.number().optional(),
|
|
49
50
|
encryption: z.boolean().optional(),
|
package/src/directory-live.ts
CHANGED
package/src/group-mentions.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk";
|
|
1
|
+
import type { ChannelGroupContext, GroupToolPolicyConfig } from "openclaw/plugin-sdk/matrix";
|
|
2
2
|
import { resolveMatrixAccountConfig } from "./matrix/accounts.js";
|
|
3
3
|
import { resolveMatrixRoomConfig } from "./matrix/monitor/rooms.js";
|
|
4
4
|
import type { CoreConfig } from "./types.js";
|
package/src/matrix/accounts.ts
CHANGED
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
normalizeOptionalAccountId,
|
|
5
|
-
} from "openclaw/plugin-sdk/account-id";
|
|
1
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
2
|
+
import { createAccountListHelpers } from "openclaw/plugin-sdk/matrix";
|
|
3
|
+
import { hasConfiguredSecretInput } from "../secret-input.js";
|
|
6
4
|
import type { CoreConfig, MatrixConfig } from "../types.js";
|
|
7
5
|
import { resolveMatrixConfigForAccount } from "./client.js";
|
|
8
6
|
import { credentialsMatchConfig, loadMatrixCredentials } from "./credentials.js";
|
|
@@ -34,44 +32,11 @@ export type ResolvedMatrixAccount = {
|
|
|
34
32
|
config: MatrixConfig;
|
|
35
33
|
};
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
// Normalize and de-duplicate keys so listing and resolution use the same semantics
|
|
43
|
-
return [
|
|
44
|
-
...new Set(
|
|
45
|
-
Object.keys(accounts)
|
|
46
|
-
.filter(Boolean)
|
|
47
|
-
.map((id) => normalizeAccountId(id)),
|
|
48
|
-
),
|
|
49
|
-
];
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function listMatrixAccountIds(cfg: CoreConfig): string[] {
|
|
53
|
-
const ids = listConfiguredAccountIds(cfg);
|
|
54
|
-
if (ids.length === 0) {
|
|
55
|
-
// Fall back to default if no accounts configured (legacy top-level config)
|
|
56
|
-
return [DEFAULT_ACCOUNT_ID];
|
|
57
|
-
}
|
|
58
|
-
return ids.toSorted((a, b) => a.localeCompare(b));
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function resolveDefaultMatrixAccountId(cfg: CoreConfig): string {
|
|
62
|
-
const preferred = normalizeOptionalAccountId(cfg.channels?.matrix?.defaultAccount);
|
|
63
|
-
if (
|
|
64
|
-
preferred &&
|
|
65
|
-
listMatrixAccountIds(cfg).some((accountId) => normalizeAccountId(accountId) === preferred)
|
|
66
|
-
) {
|
|
67
|
-
return preferred;
|
|
68
|
-
}
|
|
69
|
-
const ids = listMatrixAccountIds(cfg);
|
|
70
|
-
if (ids.includes(DEFAULT_ACCOUNT_ID)) {
|
|
71
|
-
return DEFAULT_ACCOUNT_ID;
|
|
72
|
-
}
|
|
73
|
-
return ids[0] ?? DEFAULT_ACCOUNT_ID;
|
|
74
|
-
}
|
|
35
|
+
const {
|
|
36
|
+
listAccountIds: listMatrixAccountIds,
|
|
37
|
+
resolveDefaultAccountId: resolveDefaultMatrixAccountId,
|
|
38
|
+
} = createAccountListHelpers("matrix", { normalizeAccountId });
|
|
39
|
+
export { listMatrixAccountIds, resolveDefaultMatrixAccountId };
|
|
75
40
|
|
|
76
41
|
function resolveAccountConfig(cfg: CoreConfig, accountId: string): MatrixConfig | undefined {
|
|
77
42
|
const accounts = cfg.channels?.matrix?.accounts;
|
|
@@ -106,7 +71,7 @@ export function resolveMatrixAccount(params: {
|
|
|
106
71
|
const hasUserId = Boolean(resolved.userId);
|
|
107
72
|
const hasAccessToken = Boolean(resolved.accessToken);
|
|
108
73
|
const hasPassword = Boolean(resolved.password);
|
|
109
|
-
const hasPasswordAuth = hasUserId && hasPassword;
|
|
74
|
+
const hasPasswordAuth = hasUserId && (hasPassword || hasConfiguredSecretInput(base.password));
|
|
110
75
|
const stored = loadMatrixCredentials(process.env, accountId);
|
|
111
76
|
const hasStored =
|
|
112
77
|
stored && resolved.homeserver
|
|
@@ -1,12 +1,17 @@
|
|
|
1
|
-
import { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
1
|
import { DEFAULT_ACCOUNT_ID, normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
2
|
+
import { fetchWithSsrFGuard } from "openclaw/plugin-sdk/matrix";
|
|
3
3
|
import { getMatrixRuntime } from "../../runtime.js";
|
|
4
|
+
import {
|
|
5
|
+
normalizeResolvedSecretInputString,
|
|
6
|
+
normalizeSecretInputString,
|
|
7
|
+
} from "../../secret-input.js";
|
|
4
8
|
import type { CoreConfig } from "../../types.js";
|
|
9
|
+
import { loadMatrixSdk } from "../sdk-runtime.js";
|
|
5
10
|
import { ensureMatrixSdkLoggingConfigured } from "./logging.js";
|
|
6
11
|
import type { MatrixAuth, MatrixResolvedConfig } from "./types.js";
|
|
7
12
|
|
|
8
|
-
function clean(value
|
|
9
|
-
return value
|
|
13
|
+
function clean(value: unknown, path: string): string {
|
|
14
|
+
return normalizeResolvedSecretInputString({ value, path }) ?? "";
|
|
10
15
|
}
|
|
11
16
|
|
|
12
17
|
/** Shallow-merge known nested config sub-objects so partial overrides inherit base values. */
|
|
@@ -52,11 +57,23 @@ export function resolveMatrixConfigForAccount(
|
|
|
52
57
|
// nested object inheritance (dm, actions, groups) so partial overrides work.
|
|
53
58
|
const matrix = accountConfig ? deepMergeConfig(matrixBase, accountConfig) : matrixBase;
|
|
54
59
|
|
|
55
|
-
const homeserver =
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
+
const homeserver =
|
|
61
|
+
clean(matrix.homeserver, "channels.matrix.homeserver") ||
|
|
62
|
+
clean(env.MATRIX_HOMESERVER, "MATRIX_HOMESERVER");
|
|
63
|
+
const userId =
|
|
64
|
+
clean(matrix.userId, "channels.matrix.userId") || clean(env.MATRIX_USER_ID, "MATRIX_USER_ID");
|
|
65
|
+
const accessToken =
|
|
66
|
+
clean(matrix.accessToken, "channels.matrix.accessToken") ||
|
|
67
|
+
clean(env.MATRIX_ACCESS_TOKEN, "MATRIX_ACCESS_TOKEN") ||
|
|
68
|
+
undefined;
|
|
69
|
+
const password =
|
|
70
|
+
clean(matrix.password, "channels.matrix.password") ||
|
|
71
|
+
clean(env.MATRIX_PASSWORD, "MATRIX_PASSWORD") ||
|
|
72
|
+
undefined;
|
|
73
|
+
const deviceName =
|
|
74
|
+
clean(matrix.deviceName, "channels.matrix.deviceName") ||
|
|
75
|
+
clean(env.MATRIX_DEVICE_NAME, "MATRIX_DEVICE_NAME") ||
|
|
76
|
+
undefined;
|
|
60
77
|
const initialSyncLimit =
|
|
61
78
|
typeof matrix.initialSyncLimit === "number"
|
|
62
79
|
? Math.max(0, Math.floor(matrix.initialSyncLimit))
|
|
@@ -119,6 +136,7 @@ export async function resolveMatrixAuth(params?: {
|
|
|
119
136
|
if (!userId) {
|
|
120
137
|
// Fetch userId from access token via whoami
|
|
121
138
|
ensureMatrixSdkLoggingConfigured();
|
|
139
|
+
const { MatrixClient } = loadMatrixSdk();
|
|
122
140
|
const tempClient = new MatrixClient(resolved.homeserver, resolved.accessToken);
|
|
123
141
|
const whoami = await tempClient.getUserId();
|
|
124
142
|
userId = whoami;
|
|
@@ -167,28 +185,36 @@ export async function resolveMatrixAuth(params?: {
|
|
|
167
185
|
);
|
|
168
186
|
}
|
|
169
187
|
|
|
170
|
-
// Login with password using HTTP API
|
|
171
|
-
const loginResponse = await
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
188
|
+
// Login with password using HTTP API.
|
|
189
|
+
const { response: loginResponse, release: releaseLoginResponse } = await fetchWithSsrFGuard({
|
|
190
|
+
url: `${resolved.homeserver}/_matrix/client/v3/login`,
|
|
191
|
+
init: {
|
|
192
|
+
method: "POST",
|
|
193
|
+
headers: { "Content-Type": "application/json" },
|
|
194
|
+
body: JSON.stringify({
|
|
195
|
+
type: "m.login.password",
|
|
196
|
+
identifier: { type: "m.id.user", user: resolved.userId },
|
|
197
|
+
password: resolved.password,
|
|
198
|
+
initial_device_display_name: resolved.deviceName ?? "OpenClaw Gateway",
|
|
199
|
+
}),
|
|
200
|
+
},
|
|
201
|
+
auditContext: "matrix.login",
|
|
180
202
|
});
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
203
|
+
const login = await (async () => {
|
|
204
|
+
try {
|
|
205
|
+
if (!loginResponse.ok) {
|
|
206
|
+
const errorText = await loginResponse.text();
|
|
207
|
+
throw new Error(`Matrix login failed: ${errorText}`);
|
|
208
|
+
}
|
|
209
|
+
return (await loginResponse.json()) as {
|
|
210
|
+
access_token?: string;
|
|
211
|
+
user_id?: string;
|
|
212
|
+
device_id?: string;
|
|
213
|
+
};
|
|
214
|
+
} finally {
|
|
215
|
+
await releaseLoginResponse();
|
|
216
|
+
}
|
|
217
|
+
})();
|
|
192
218
|
|
|
193
219
|
const accessToken = login.access_token?.trim();
|
|
194
220
|
if (!accessToken) {
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
|
-
import type {
|
|
3
|
-
|
|
4
|
-
|
|
2
|
+
import type {
|
|
3
|
+
IStorageProvider,
|
|
4
|
+
ICryptoStorageProvider,
|
|
5
5
|
MatrixClient,
|
|
6
|
-
SimpleFsStorageProvider,
|
|
7
|
-
RustSdkCryptoStorageProvider,
|
|
8
6
|
} from "@vector-im/matrix-bot-sdk";
|
|
7
|
+
import { loadMatrixSdk } from "../sdk-runtime.js";
|
|
9
8
|
import { ensureMatrixSdkLoggingConfigured } from "./logging.js";
|
|
10
9
|
import {
|
|
11
10
|
maybeMigrateLegacyStorage,
|
|
@@ -14,6 +13,7 @@ import {
|
|
|
14
13
|
} from "./storage.js";
|
|
15
14
|
|
|
16
15
|
function sanitizeUserIdList(input: unknown, label: string): string[] {
|
|
16
|
+
const LogService = loadMatrixSdk().LogService;
|
|
17
17
|
if (input == null) {
|
|
18
18
|
return [];
|
|
19
19
|
}
|
|
@@ -44,6 +44,8 @@ export async function createMatrixClient(params: {
|
|
|
44
44
|
localTimeoutMs?: number;
|
|
45
45
|
accountId?: string | null;
|
|
46
46
|
}): Promise<MatrixClient> {
|
|
47
|
+
const { MatrixClient, SimpleFsStorageProvider, RustSdkCryptoStorageProvider, LogService } =
|
|
48
|
+
loadMatrixSdk();
|
|
47
49
|
ensureMatrixSdkLoggingConfigured();
|
|
48
50
|
const env = process.env;
|
|
49
51
|
|
|
@@ -1,7 +1,15 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { loadMatrixSdk } from "../sdk-runtime.js";
|
|
2
2
|
|
|
3
3
|
let matrixSdkLoggingConfigured = false;
|
|
4
|
-
|
|
4
|
+
let matrixSdkBaseLogger:
|
|
5
|
+
| {
|
|
6
|
+
trace: (module: string, ...messageOrObject: unknown[]) => void;
|
|
7
|
+
debug: (module: string, ...messageOrObject: unknown[]) => void;
|
|
8
|
+
info: (module: string, ...messageOrObject: unknown[]) => void;
|
|
9
|
+
warn: (module: string, ...messageOrObject: unknown[]) => void;
|
|
10
|
+
error: (module: string, ...messageOrObject: unknown[]) => void;
|
|
11
|
+
}
|
|
12
|
+
| undefined;
|
|
5
13
|
|
|
6
14
|
function shouldSuppressMatrixHttpNotFound(module: string, messageOrObject: unknown[]): boolean {
|
|
7
15
|
if (module !== "MatrixHttpClient") {
|
|
@@ -19,18 +27,20 @@ export function ensureMatrixSdkLoggingConfigured(): void {
|
|
|
19
27
|
if (matrixSdkLoggingConfigured) {
|
|
20
28
|
return;
|
|
21
29
|
}
|
|
30
|
+
const { ConsoleLogger, LogService } = loadMatrixSdk();
|
|
31
|
+
matrixSdkBaseLogger = new ConsoleLogger();
|
|
22
32
|
matrixSdkLoggingConfigured = true;
|
|
23
33
|
|
|
24
34
|
LogService.setLogger({
|
|
25
|
-
trace: (module, ...messageOrObject) => matrixSdkBaseLogger
|
|
26
|
-
debug: (module, ...messageOrObject) => matrixSdkBaseLogger
|
|
27
|
-
info: (module, ...messageOrObject) => matrixSdkBaseLogger
|
|
28
|
-
warn: (module, ...messageOrObject) => matrixSdkBaseLogger
|
|
35
|
+
trace: (module, ...messageOrObject) => matrixSdkBaseLogger?.trace(module, ...messageOrObject),
|
|
36
|
+
debug: (module, ...messageOrObject) => matrixSdkBaseLogger?.debug(module, ...messageOrObject),
|
|
37
|
+
info: (module, ...messageOrObject) => matrixSdkBaseLogger?.info(module, ...messageOrObject),
|
|
38
|
+
warn: (module, ...messageOrObject) => matrixSdkBaseLogger?.warn(module, ...messageOrObject),
|
|
29
39
|
error: (module, ...messageOrObject) => {
|
|
30
40
|
if (shouldSuppressMatrixHttpNotFound(module, messageOrObject)) {
|
|
31
41
|
return;
|
|
32
42
|
}
|
|
33
|
-
matrixSdkBaseLogger
|
|
43
|
+
matrixSdkBaseLogger?.error(module, ...messageOrObject);
|
|
34
44
|
},
|
|
35
45
|
});
|
|
36
46
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { MatrixClient } from "@vector-im/matrix-bot-sdk";
|
|
2
|
-
import { LogService } from "@vector-im/matrix-bot-sdk";
|
|
3
2
|
import { normalizeAccountId } from "openclaw/plugin-sdk/account-id";
|
|
4
3
|
import type { CoreConfig } from "../../types.js";
|
|
4
|
+
import { getMatrixLogService } from "../sdk-runtime.js";
|
|
5
5
|
import { resolveMatrixAuth } from "./config.js";
|
|
6
6
|
import { createMatrixClient } from "./create-client.js";
|
|
7
7
|
import { startMatrixClientWithGrace } from "./startup.js";
|
|
@@ -81,6 +81,7 @@ async function ensureSharedClientStarted(params: {
|
|
|
81
81
|
params.state.cryptoReady = true;
|
|
82
82
|
}
|
|
83
83
|
} catch (err) {
|
|
84
|
+
const LogService = getMatrixLogService();
|
|
84
85
|
LogService.warn("MatrixClientLite", "Failed to prepare crypto:", err);
|
|
85
86
|
}
|
|
86
87
|
}
|
|
@@ -89,6 +90,7 @@ async function ensureSharedClientStarted(params: {
|
|
|
89
90
|
client,
|
|
90
91
|
onError: (err: unknown) => {
|
|
91
92
|
params.state.started = false;
|
|
93
|
+
const LogService = getMatrixLogService();
|
|
92
94
|
LogService.error("MatrixClientLite", "client.start() error:", err);
|
|
93
95
|
},
|
|
94
96
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { LogService } from "@vector-im/matrix-bot-sdk";
|
|
2
1
|
import { createMatrixClient } from "./client/create-client.js";
|
|
3
2
|
import { startMatrixClientWithGrace } from "./client/startup.js";
|
|
3
|
+
import { getMatrixLogService } from "./sdk-runtime.js";
|
|
4
4
|
|
|
5
5
|
type MatrixClientBootstrapAuth = {
|
|
6
6
|
homeserver: string;
|
|
@@ -39,6 +39,7 @@ export async function createPreparedMatrixClient(opts: {
|
|
|
39
39
|
await startMatrixClientWithGrace({
|
|
40
40
|
client,
|
|
41
41
|
onError: (err: unknown) => {
|
|
42
|
+
const LogService = getMatrixLogService();
|
|
42
43
|
LogService.error("MatrixClientBootstrap", "client.start() error:", err);
|
|
43
44
|
},
|
|
44
45
|
});
|