@gakr-gakr/matrix 0.1.0
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 +285 -0
- package/SPEC-SUPPORT.md +116 -0
- package/api.ts +38 -0
- package/auth-presence.ts +56 -0
- package/autobot.plugin.json +28 -0
- package/channel-plugin-api.ts +3 -0
- package/cli-metadata.ts +11 -0
- package/contract-api.ts +17 -0
- package/doctor-contract-api.ts +1 -0
- package/helper-api.ts +3 -0
- package/index.ts +55 -0
- package/package.json +101 -0
- package/plugin-entry.handlers.runtime.ts +1 -0
- package/runtime-api.ts +72 -0
- package/runtime-heavy-api.ts +1 -0
- package/runtime-setter-api.ts +3 -0
- package/secret-contract-api.ts +5 -0
- package/setup-entry.ts +17 -0
- package/setup-plugin-api.ts +3 -0
- package/src/account-selection.ts +223 -0
- package/src/actions.ts +346 -0
- package/src/approval-auth.ts +25 -0
- package/src/approval-handler.runtime.ts +595 -0
- package/src/approval-ids.ts +6 -0
- package/src/approval-native.ts +348 -0
- package/src/approval-reaction-auth.ts +45 -0
- package/src/approval-reactions.ts +313 -0
- package/src/auth-precedence.ts +61 -0
- package/src/channel-account-paths.ts +97 -0
- package/src/channel.runtime.ts +17 -0
- package/src/channel.setup.ts +48 -0
- package/src/channel.ts +667 -0
- package/src/cli-metadata.ts +19 -0
- package/src/cli.ts +2298 -0
- package/src/config-adapter.ts +41 -0
- package/src/config-schema.ts +159 -0
- package/src/config-ui-hints.ts +56 -0
- package/src/directory-live.ts +238 -0
- package/src/doctor-contract.ts +287 -0
- package/src/doctor.ts +262 -0
- package/src/env-vars.ts +92 -0
- package/src/exec-approval-resolver.ts +23 -0
- package/src/exec-approvals.ts +293 -0
- package/src/group-mentions.ts +41 -0
- package/src/legacy-crypto-inspector-availability.ts +60 -0
- package/src/legacy-crypto.ts +531 -0
- package/src/legacy-state.ts +156 -0
- package/src/matrix/account-config.ts +175 -0
- package/src/matrix/accounts.ts +194 -0
- package/src/matrix/actions/client.ts +31 -0
- package/src/matrix/actions/devices.ts +34 -0
- package/src/matrix/actions/limits.ts +6 -0
- package/src/matrix/actions/messages.ts +129 -0
- package/src/matrix/actions/pins.ts +63 -0
- package/src/matrix/actions/polls.ts +109 -0
- package/src/matrix/actions/profile.ts +37 -0
- package/src/matrix/actions/reactions.ts +59 -0
- package/src/matrix/actions/room.ts +71 -0
- package/src/matrix/actions/summary.ts +88 -0
- package/src/matrix/actions/types.ts +63 -0
- package/src/matrix/actions/verification.ts +589 -0
- package/src/matrix/actions.ts +37 -0
- package/src/matrix/active-client.ts +26 -0
- package/src/matrix/async-lock.ts +18 -0
- package/src/matrix/backup-health.ts +124 -0
- package/src/matrix/client/config-runtime-api.ts +9 -0
- package/src/matrix/client/config-secret-input.runtime.ts +1 -0
- package/src/matrix/client/config.ts +853 -0
- package/src/matrix/client/create-client.ts +105 -0
- package/src/matrix/client/env-auth.ts +95 -0
- package/src/matrix/client/file-sync-store.ts +289 -0
- package/src/matrix/client/logging.ts +140 -0
- package/src/matrix/client/migration-snapshot.runtime.ts +1 -0
- package/src/matrix/client/private-network-host.ts +1 -0
- package/src/matrix/client/runtime.ts +4 -0
- package/src/matrix/client/shared.ts +316 -0
- package/src/matrix/client/storage.ts +543 -0
- package/src/matrix/client/types.ts +50 -0
- package/src/matrix/client/url-validation.ts +76 -0
- package/src/matrix/client-bootstrap.ts +173 -0
- package/src/matrix/client.ts +23 -0
- package/src/matrix/config-paths.ts +31 -0
- package/src/matrix/config-update.ts +292 -0
- package/src/matrix/credentials-read.ts +207 -0
- package/src/matrix/credentials-write.runtime.ts +35 -0
- package/src/matrix/credentials.ts +95 -0
- package/src/matrix/deps.ts +309 -0
- package/src/matrix/device-health.ts +31 -0
- package/src/matrix/direct-management.ts +349 -0
- package/src/matrix/direct-room.ts +128 -0
- package/src/matrix/draft-stream.ts +225 -0
- package/src/matrix/encryption-guidance.ts +24 -0
- package/src/matrix/errors.ts +21 -0
- package/src/matrix/format.ts +426 -0
- package/src/matrix/legacy-crypto-inspector.ts +95 -0
- package/src/matrix/media-errors.ts +20 -0
- package/src/matrix/media-text.ts +162 -0
- package/src/matrix/monitor/access-state.ts +145 -0
- package/src/matrix/monitor/ack-config.ts +27 -0
- package/src/matrix/monitor/allowlist.ts +92 -0
- package/src/matrix/monitor/auto-join.ts +86 -0
- package/src/matrix/monitor/config.ts +569 -0
- package/src/matrix/monitor/context-summary.ts +43 -0
- package/src/matrix/monitor/direct.ts +296 -0
- package/src/matrix/monitor/events.ts +397 -0
- package/src/matrix/monitor/handler.ts +2271 -0
- package/src/matrix/monitor/inbound-dedupe.ts +267 -0
- package/src/matrix/monitor/index.ts +540 -0
- package/src/matrix/monitor/legacy-crypto-restore.ts +139 -0
- package/src/matrix/monitor/location.ts +108 -0
- package/src/matrix/monitor/media.ts +119 -0
- package/src/matrix/monitor/mentions.ts +256 -0
- package/src/matrix/monitor/reaction-events.ts +197 -0
- package/src/matrix/monitor/recent-invite.ts +30 -0
- package/src/matrix/monitor/replies.ts +136 -0
- package/src/matrix/monitor/reply-context.ts +92 -0
- package/src/matrix/monitor/room-history.ts +301 -0
- package/src/matrix/monitor/room-info.ts +126 -0
- package/src/matrix/monitor/rooms.ts +52 -0
- package/src/matrix/monitor/route.ts +179 -0
- package/src/matrix/monitor/runtime-api.ts +28 -0
- package/src/matrix/monitor/startup-verification.ts +237 -0
- package/src/matrix/monitor/startup.ts +218 -0
- package/src/matrix/monitor/status.ts +120 -0
- package/src/matrix/monitor/sync-lifecycle.ts +91 -0
- package/src/matrix/monitor/task-runner.ts +38 -0
- package/src/matrix/monitor/test-events.ts +21 -0
- package/src/matrix/monitor/thread-context.ts +108 -0
- package/src/matrix/monitor/threads.ts +85 -0
- package/src/matrix/monitor/types.ts +30 -0
- package/src/matrix/monitor/verification-events.ts +643 -0
- package/src/matrix/monitor/verification-utils.ts +46 -0
- package/src/matrix/outbound-media-runtime.ts +1 -0
- package/src/matrix/poll-summary.ts +110 -0
- package/src/matrix/poll-types.ts +429 -0
- package/src/matrix/probe.runtime.ts +4 -0
- package/src/matrix/probe.ts +97 -0
- package/src/matrix/profile.ts +184 -0
- package/src/matrix/reaction-common.ts +147 -0
- package/src/matrix/sdk/crypto-bootstrap.ts +438 -0
- package/src/matrix/sdk/crypto-facade.ts +242 -0
- package/src/matrix/sdk/crypto-node.runtime.ts +17 -0
- package/src/matrix/sdk/crypto-runtime.ts +14 -0
- package/src/matrix/sdk/decrypt-bridge.ts +410 -0
- package/src/matrix/sdk/event-helpers.ts +83 -0
- package/src/matrix/sdk/http-client.ts +87 -0
- package/src/matrix/sdk/idb-persistence-lock.ts +51 -0
- package/src/matrix/sdk/idb-persistence.ts +286 -0
- package/src/matrix/sdk/logger.ts +108 -0
- package/src/matrix/sdk/read-response-with-limit.ts +19 -0
- package/src/matrix/sdk/recovery-key-store.ts +453 -0
- package/src/matrix/sdk/timeout-abort-signal.ts +1 -0
- package/src/matrix/sdk/transport-runtime-api.ts +18 -0
- package/src/matrix/sdk/transport.ts +352 -0
- package/src/matrix/sdk/types.ts +245 -0
- package/src/matrix/sdk/verification-manager.ts +795 -0
- package/src/matrix/sdk/verification-status.ts +23 -0
- package/src/matrix/sdk.ts +2152 -0
- package/src/matrix/send/client.ts +93 -0
- package/src/matrix/send/formatting.ts +189 -0
- package/src/matrix/send/media.ts +244 -0
- package/src/matrix/send/targets.ts +104 -0
- package/src/matrix/send/types.ts +131 -0
- package/src/matrix/send.ts +660 -0
- package/src/matrix/session-store-metadata.ts +108 -0
- package/src/matrix/startup-abort.ts +44 -0
- package/src/matrix/subagent-hooks.ts +308 -0
- package/src/matrix/sync-state.ts +27 -0
- package/src/matrix/target-ids.ts +79 -0
- package/src/matrix/thread-bindings-shared.ts +206 -0
- package/src/matrix/thread-bindings.ts +580 -0
- package/src/matrix-migration.runtime.ts +9 -0
- package/src/migration-config.ts +243 -0
- package/src/migration-snapshot-backup.ts +116 -0
- package/src/migration-snapshot.ts +53 -0
- package/src/onboarding.ts +775 -0
- package/src/outbound.ts +248 -0
- package/src/plugin-entry.runtime.js +115 -0
- package/src/plugin-entry.runtime.ts +70 -0
- package/src/profile-update.ts +71 -0
- package/src/record-shared.ts +3 -0
- package/src/resolve-targets.ts +175 -0
- package/src/resolver.runtime.ts +5 -0
- package/src/resolver.ts +21 -0
- package/src/runtime-api.ts +106 -0
- package/src/runtime.ts +13 -0
- package/src/secret-contract.ts +174 -0
- package/src/session-route.ts +126 -0
- package/src/setup-bootstrap.ts +102 -0
- package/src/setup-config.ts +222 -0
- package/src/setup-contract.ts +90 -0
- package/src/setup-core.ts +146 -0
- package/src/setup-dm-policy.ts +15 -0
- package/src/setup-surface.ts +4 -0
- package/src/startup-maintenance.ts +114 -0
- package/src/storage-paths.ts +92 -0
- package/src/thread-binding-api.ts +23 -0
- package/src/tool-actions.runtime.ts +1 -0
- package/src/tool-actions.ts +498 -0
- package/src/types.ts +257 -0
- package/subagent-hooks-api.ts +31 -0
- package/test-api.ts +21 -0
- package/thread-binding-api.ts +4 -0
- package/thread-bindings-runtime.ts +4 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { resolveConfiguredAcpBindingRecord } from "autobot/plugin-sdk/acp-binding-resolve-runtime";
|
|
2
|
+
import type { PluginRuntime } from "autobot/plugin-sdk/plugin-runtime";
|
|
3
|
+
import {
|
|
4
|
+
buildAgentSessionKey,
|
|
5
|
+
deriveLastRoutePolicy,
|
|
6
|
+
resolveAgentIdFromSessionKey,
|
|
7
|
+
} from "autobot/plugin-sdk/routing";
|
|
8
|
+
import { getSessionBindingService } from "autobot/plugin-sdk/session-binding-runtime";
|
|
9
|
+
import type { CoreConfig } from "../../types.js";
|
|
10
|
+
import { resolveMatrixThreadSessionKeys } from "./threads.js";
|
|
11
|
+
|
|
12
|
+
type MatrixResolvedRoute = ReturnType<PluginRuntime["channel"]["routing"]["resolveAgentRoute"]>;
|
|
13
|
+
|
|
14
|
+
function resolveMatrixDmSessionKey(params: {
|
|
15
|
+
accountId: string;
|
|
16
|
+
agentId: string;
|
|
17
|
+
roomId: string;
|
|
18
|
+
dmSessionScope?: "per-user" | "per-room";
|
|
19
|
+
fallbackSessionKey: string;
|
|
20
|
+
}): string {
|
|
21
|
+
if (params.dmSessionScope !== "per-room") {
|
|
22
|
+
return params.fallbackSessionKey;
|
|
23
|
+
}
|
|
24
|
+
return buildAgentSessionKey({
|
|
25
|
+
agentId: params.agentId,
|
|
26
|
+
channel: "matrix",
|
|
27
|
+
accountId: params.accountId,
|
|
28
|
+
peer: {
|
|
29
|
+
kind: "channel",
|
|
30
|
+
id: params.roomId,
|
|
31
|
+
},
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function shouldApplyMatrixPerRoomDmSessionScope(params: {
|
|
36
|
+
isDirectMessage: boolean;
|
|
37
|
+
configuredSessionKey?: string;
|
|
38
|
+
}): boolean {
|
|
39
|
+
return params.isDirectMessage && !params.configuredSessionKey;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function resolveMatrixInboundRoute(params: {
|
|
43
|
+
cfg: CoreConfig;
|
|
44
|
+
accountId: string;
|
|
45
|
+
roomId: string;
|
|
46
|
+
senderId: string;
|
|
47
|
+
isDirectMessage: boolean;
|
|
48
|
+
dmSessionScope?: "per-user" | "per-room";
|
|
49
|
+
threadId?: string;
|
|
50
|
+
eventTs?: number;
|
|
51
|
+
resolveAgentRoute: PluginRuntime["channel"]["routing"]["resolveAgentRoute"];
|
|
52
|
+
}): {
|
|
53
|
+
route: MatrixResolvedRoute;
|
|
54
|
+
configuredBinding: ReturnType<typeof resolveConfiguredAcpBindingRecord>;
|
|
55
|
+
runtimeBindingId: string | null;
|
|
56
|
+
} {
|
|
57
|
+
const baseRoute = params.resolveAgentRoute({
|
|
58
|
+
cfg: params.cfg,
|
|
59
|
+
channel: "matrix",
|
|
60
|
+
accountId: params.accountId,
|
|
61
|
+
peer: {
|
|
62
|
+
kind: params.isDirectMessage ? "direct" : "channel",
|
|
63
|
+
id: params.isDirectMessage ? params.senderId : params.roomId,
|
|
64
|
+
},
|
|
65
|
+
// Matrix DMs are still sender-addressed first, but the room ID remains a
|
|
66
|
+
// useful fallback binding key for generic route matching.
|
|
67
|
+
parentPeer: params.isDirectMessage
|
|
68
|
+
? {
|
|
69
|
+
kind: "channel",
|
|
70
|
+
id: params.roomId,
|
|
71
|
+
}
|
|
72
|
+
: undefined,
|
|
73
|
+
});
|
|
74
|
+
const bindingConversationId = params.threadId ?? params.roomId;
|
|
75
|
+
const bindingParentConversationId = params.threadId ? params.roomId : undefined;
|
|
76
|
+
const sessionBindingService = getSessionBindingService();
|
|
77
|
+
const runtimeBinding = sessionBindingService.resolveByConversation({
|
|
78
|
+
channel: "matrix",
|
|
79
|
+
accountId: params.accountId,
|
|
80
|
+
conversationId: bindingConversationId,
|
|
81
|
+
parentConversationId: bindingParentConversationId,
|
|
82
|
+
});
|
|
83
|
+
const boundSessionKey = runtimeBinding?.targetSessionKey?.trim();
|
|
84
|
+
|
|
85
|
+
if (runtimeBinding && boundSessionKey) {
|
|
86
|
+
return {
|
|
87
|
+
route: {
|
|
88
|
+
...baseRoute,
|
|
89
|
+
sessionKey: boundSessionKey,
|
|
90
|
+
agentId: resolveAgentIdFromSessionKey(boundSessionKey) || baseRoute.agentId,
|
|
91
|
+
lastRoutePolicy: deriveLastRoutePolicy({
|
|
92
|
+
sessionKey: boundSessionKey,
|
|
93
|
+
mainSessionKey: baseRoute.mainSessionKey,
|
|
94
|
+
}),
|
|
95
|
+
matchedBy: "binding.channel",
|
|
96
|
+
},
|
|
97
|
+
configuredBinding: null,
|
|
98
|
+
runtimeBindingId: runtimeBinding.bindingId,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const configuredBinding =
|
|
103
|
+
runtimeBinding == null
|
|
104
|
+
? resolveConfiguredAcpBindingRecord({
|
|
105
|
+
cfg: params.cfg,
|
|
106
|
+
channel: "matrix",
|
|
107
|
+
accountId: params.accountId,
|
|
108
|
+
conversationId: bindingConversationId,
|
|
109
|
+
parentConversationId: bindingParentConversationId,
|
|
110
|
+
})
|
|
111
|
+
: null;
|
|
112
|
+
const configuredSessionKey = configuredBinding?.record.targetSessionKey?.trim();
|
|
113
|
+
|
|
114
|
+
const effectiveRoute =
|
|
115
|
+
configuredBinding && configuredSessionKey
|
|
116
|
+
? {
|
|
117
|
+
...baseRoute,
|
|
118
|
+
sessionKey: configuredSessionKey,
|
|
119
|
+
agentId:
|
|
120
|
+
resolveAgentIdFromSessionKey(configuredSessionKey) ||
|
|
121
|
+
configuredBinding.spec.agentId ||
|
|
122
|
+
baseRoute.agentId,
|
|
123
|
+
lastRoutePolicy: deriveLastRoutePolicy({
|
|
124
|
+
sessionKey: configuredSessionKey,
|
|
125
|
+
mainSessionKey: baseRoute.mainSessionKey,
|
|
126
|
+
}),
|
|
127
|
+
matchedBy: "binding.channel" as const,
|
|
128
|
+
}
|
|
129
|
+
: baseRoute;
|
|
130
|
+
|
|
131
|
+
const dmSessionKey = shouldApplyMatrixPerRoomDmSessionScope({
|
|
132
|
+
isDirectMessage: params.isDirectMessage,
|
|
133
|
+
configuredSessionKey,
|
|
134
|
+
})
|
|
135
|
+
? resolveMatrixDmSessionKey({
|
|
136
|
+
accountId: params.accountId,
|
|
137
|
+
agentId: effectiveRoute.agentId,
|
|
138
|
+
roomId: params.roomId,
|
|
139
|
+
dmSessionScope: params.dmSessionScope,
|
|
140
|
+
fallbackSessionKey: effectiveRoute.sessionKey,
|
|
141
|
+
})
|
|
142
|
+
: effectiveRoute.sessionKey;
|
|
143
|
+
const routeWithDmScope =
|
|
144
|
+
dmSessionKey === effectiveRoute.sessionKey
|
|
145
|
+
? effectiveRoute
|
|
146
|
+
: {
|
|
147
|
+
...effectiveRoute,
|
|
148
|
+
sessionKey: dmSessionKey,
|
|
149
|
+
lastRoutePolicy: "session" as const,
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
// When no binding overrides the session key, isolate threads into their own sessions.
|
|
153
|
+
if (!configuredBinding && !configuredSessionKey && params.threadId) {
|
|
154
|
+
const threadKeys = resolveMatrixThreadSessionKeys({
|
|
155
|
+
baseSessionKey: routeWithDmScope.sessionKey,
|
|
156
|
+
threadId: params.threadId,
|
|
157
|
+
parentSessionKey: routeWithDmScope.sessionKey,
|
|
158
|
+
});
|
|
159
|
+
return {
|
|
160
|
+
route: {
|
|
161
|
+
...routeWithDmScope,
|
|
162
|
+
sessionKey: threadKeys.sessionKey,
|
|
163
|
+
mainSessionKey: threadKeys.parentSessionKey ?? routeWithDmScope.sessionKey,
|
|
164
|
+
lastRoutePolicy: deriveLastRoutePolicy({
|
|
165
|
+
sessionKey: threadKeys.sessionKey,
|
|
166
|
+
mainSessionKey: threadKeys.parentSessionKey ?? routeWithDmScope.sessionKey,
|
|
167
|
+
}),
|
|
168
|
+
},
|
|
169
|
+
configuredBinding,
|
|
170
|
+
runtimeBindingId: null,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return {
|
|
175
|
+
route: routeWithDmScope,
|
|
176
|
+
configuredBinding,
|
|
177
|
+
runtimeBindingId: null,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Narrow Matrix monitor helper seam.
|
|
2
|
+
// Keep monitor internals off the broad package runtime-api barrel so monitor
|
|
3
|
+
// tests and shared workers do not pull unrelated Matrix helper surfaces.
|
|
4
|
+
|
|
5
|
+
export type { NormalizedLocation } from "autobot/plugin-sdk/channel-location";
|
|
6
|
+
export type { PluginRuntime, RuntimeLogger } from "autobot/plugin-sdk/plugin-runtime";
|
|
7
|
+
export type { BlockReplyContext, ReplyPayload } from "autobot/plugin-sdk/reply-runtime";
|
|
8
|
+
export type { MarkdownTableMode, AutoBotConfig } from "autobot/plugin-sdk/config-contracts";
|
|
9
|
+
export type { RuntimeEnv } from "autobot/plugin-sdk/runtime";
|
|
10
|
+
export {
|
|
11
|
+
addAllowlistUserEntriesFromConfigEntry,
|
|
12
|
+
buildAllowlistResolutionSummary,
|
|
13
|
+
canonicalizeAllowlistWithResolvedIds,
|
|
14
|
+
formatAllowlistMatchMeta,
|
|
15
|
+
patchAllowlistUsersInConfigEntries,
|
|
16
|
+
summarizeMapping,
|
|
17
|
+
} from "autobot/plugin-sdk/allow-from";
|
|
18
|
+
export {
|
|
19
|
+
createReplyPrefixOptions,
|
|
20
|
+
createTypingCallbacks,
|
|
21
|
+
} from "autobot/plugin-sdk/channel-reply-options-runtime";
|
|
22
|
+
export { formatLocationText, toLocationContext } from "autobot/plugin-sdk/channel-location";
|
|
23
|
+
export { getAgentScopedMediaLocalRoots } from "autobot/plugin-sdk/agent-media-payload";
|
|
24
|
+
export { logInboundDrop, logTypingFailure } from "autobot/plugin-sdk/channel-logging";
|
|
25
|
+
export {
|
|
26
|
+
buildChannelKeyCandidates,
|
|
27
|
+
resolveChannelEntryMatch,
|
|
28
|
+
} from "autobot/plugin-sdk/channel-targets";
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import fs from "node:fs/promises";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { readJsonFileWithFallback, writeJsonFileAtomically } from "autobot/plugin-sdk/json-store";
|
|
4
|
+
import type { MatrixConfig } from "../../types.js";
|
|
5
|
+
import { resolveMatrixStoragePaths } from "../client/storage.js";
|
|
6
|
+
import type { MatrixAuth } from "../client/types.js";
|
|
7
|
+
import { formatMatrixErrorMessage } from "../errors.js";
|
|
8
|
+
import type { MatrixClient, MatrixOwnDeviceVerificationStatus } from "../sdk.js";
|
|
9
|
+
|
|
10
|
+
const STARTUP_VERIFICATION_STATE_FILENAME = "startup-verification.json";
|
|
11
|
+
const DEFAULT_STARTUP_VERIFICATION_MODE = "if-unverified" as const;
|
|
12
|
+
const DEFAULT_STARTUP_VERIFICATION_COOLDOWN_HOURS = 24;
|
|
13
|
+
const DEFAULT_STARTUP_VERIFICATION_FAILURE_COOLDOWN_MS = 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
type MatrixStartupVerificationState = {
|
|
16
|
+
userId?: string | null;
|
|
17
|
+
deviceId?: string | null;
|
|
18
|
+
attemptedAt?: string;
|
|
19
|
+
outcome?: "requested" | "failed";
|
|
20
|
+
requestId?: string;
|
|
21
|
+
transactionId?: string;
|
|
22
|
+
error?: string;
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export type MatrixStartupVerificationOutcome =
|
|
26
|
+
| {
|
|
27
|
+
kind: "disabled" | "verified" | "cooldown" | "pending" | "requested" | "request-failed";
|
|
28
|
+
verification: MatrixOwnDeviceVerificationStatus;
|
|
29
|
+
requestId?: string;
|
|
30
|
+
transactionId?: string;
|
|
31
|
+
error?: string;
|
|
32
|
+
retryAfterMs?: number;
|
|
33
|
+
}
|
|
34
|
+
| {
|
|
35
|
+
kind: "unsupported";
|
|
36
|
+
verification?: undefined;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
function normalizeCooldownHours(value: number | undefined): number {
|
|
40
|
+
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
41
|
+
return DEFAULT_STARTUP_VERIFICATION_COOLDOWN_HOURS;
|
|
42
|
+
}
|
|
43
|
+
return Math.max(0, value);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function resolveStartupVerificationStatePath(params: {
|
|
47
|
+
auth: MatrixAuth;
|
|
48
|
+
env?: NodeJS.ProcessEnv;
|
|
49
|
+
}): string {
|
|
50
|
+
const storagePaths = resolveMatrixStoragePaths({
|
|
51
|
+
homeserver: params.auth.homeserver,
|
|
52
|
+
userId: params.auth.userId,
|
|
53
|
+
accessToken: params.auth.accessToken,
|
|
54
|
+
accountId: params.auth.accountId,
|
|
55
|
+
deviceId: params.auth.deviceId,
|
|
56
|
+
env: params.env,
|
|
57
|
+
});
|
|
58
|
+
return path.join(storagePaths.rootDir, STARTUP_VERIFICATION_STATE_FILENAME);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function readStartupVerificationState(
|
|
62
|
+
filePath: string,
|
|
63
|
+
): Promise<MatrixStartupVerificationState | null> {
|
|
64
|
+
const { value } = await readJsonFileWithFallback<MatrixStartupVerificationState | null>(
|
|
65
|
+
filePath,
|
|
66
|
+
null,
|
|
67
|
+
);
|
|
68
|
+
return value && typeof value === "object" ? value : null;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async function clearStartupVerificationState(filePath: string): Promise<void> {
|
|
72
|
+
await fs.rm(filePath, { force: true }).catch(() => {});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function resolveStateCooldownMs(
|
|
76
|
+
state: MatrixStartupVerificationState | null,
|
|
77
|
+
cooldownMs: number,
|
|
78
|
+
): number {
|
|
79
|
+
if (state?.outcome === "failed") {
|
|
80
|
+
return Math.min(cooldownMs, DEFAULT_STARTUP_VERIFICATION_FAILURE_COOLDOWN_MS);
|
|
81
|
+
}
|
|
82
|
+
return cooldownMs;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function resolveRetryAfterMs(params: {
|
|
86
|
+
attemptedAt?: string;
|
|
87
|
+
cooldownMs: number;
|
|
88
|
+
nowMs: number;
|
|
89
|
+
}): number | undefined {
|
|
90
|
+
const attemptedAtMs = Date.parse(params.attemptedAt ?? "");
|
|
91
|
+
if (!Number.isFinite(attemptedAtMs)) {
|
|
92
|
+
return undefined;
|
|
93
|
+
}
|
|
94
|
+
const remaining = attemptedAtMs + params.cooldownMs - params.nowMs;
|
|
95
|
+
return remaining > 0 ? remaining : undefined;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function shouldHonorCooldown(params: {
|
|
99
|
+
state: MatrixStartupVerificationState | null;
|
|
100
|
+
verification: MatrixOwnDeviceVerificationStatus;
|
|
101
|
+
stateCooldownMs: number;
|
|
102
|
+
nowMs: number;
|
|
103
|
+
}): boolean {
|
|
104
|
+
if (!params.state || params.stateCooldownMs <= 0) {
|
|
105
|
+
return false;
|
|
106
|
+
}
|
|
107
|
+
if (
|
|
108
|
+
params.state.userId &&
|
|
109
|
+
params.verification.userId &&
|
|
110
|
+
params.state.userId !== params.verification.userId
|
|
111
|
+
) {
|
|
112
|
+
return false;
|
|
113
|
+
}
|
|
114
|
+
if (
|
|
115
|
+
params.state.deviceId &&
|
|
116
|
+
params.verification.deviceId &&
|
|
117
|
+
params.state.deviceId !== params.verification.deviceId
|
|
118
|
+
) {
|
|
119
|
+
return false;
|
|
120
|
+
}
|
|
121
|
+
return (
|
|
122
|
+
resolveRetryAfterMs({
|
|
123
|
+
attemptedAt: params.state.attemptedAt,
|
|
124
|
+
cooldownMs: params.stateCooldownMs,
|
|
125
|
+
nowMs: params.nowMs,
|
|
126
|
+
}) !== undefined
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function hasPendingSelfVerification(
|
|
131
|
+
verifications: Array<{
|
|
132
|
+
isSelfVerification: boolean;
|
|
133
|
+
completed: boolean;
|
|
134
|
+
pending: boolean;
|
|
135
|
+
}>,
|
|
136
|
+
): boolean {
|
|
137
|
+
return verifications.some(
|
|
138
|
+
(entry) => entry.isSelfVerification && !entry.completed && entry.pending,
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export async function ensureMatrixStartupVerification(params: {
|
|
143
|
+
client: Pick<MatrixClient, "crypto" | "getOwnDeviceVerificationStatus">;
|
|
144
|
+
auth: MatrixAuth;
|
|
145
|
+
accountConfig: Pick<MatrixConfig, "startupVerification" | "startupVerificationCooldownHours">;
|
|
146
|
+
env?: NodeJS.ProcessEnv;
|
|
147
|
+
nowMs?: number;
|
|
148
|
+
stateFilePath?: string;
|
|
149
|
+
}): Promise<MatrixStartupVerificationOutcome> {
|
|
150
|
+
if (params.auth.encryption !== true || !params.client.crypto) {
|
|
151
|
+
return { kind: "unsupported" };
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const verification = await params.client.getOwnDeviceVerificationStatus();
|
|
155
|
+
const statePath =
|
|
156
|
+
params.stateFilePath ??
|
|
157
|
+
resolveStartupVerificationStatePath({
|
|
158
|
+
auth: params.auth,
|
|
159
|
+
env: params.env,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
if (verification.verified) {
|
|
163
|
+
await clearStartupVerificationState(statePath);
|
|
164
|
+
return {
|
|
165
|
+
kind: "verified",
|
|
166
|
+
verification,
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const mode = params.accountConfig.startupVerification ?? DEFAULT_STARTUP_VERIFICATION_MODE;
|
|
171
|
+
if (mode === "off") {
|
|
172
|
+
await clearStartupVerificationState(statePath);
|
|
173
|
+
return {
|
|
174
|
+
kind: "disabled",
|
|
175
|
+
verification,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const verifications = await params.client.crypto.listVerifications().catch(() => []);
|
|
180
|
+
if (hasPendingSelfVerification(verifications)) {
|
|
181
|
+
return {
|
|
182
|
+
kind: "pending",
|
|
183
|
+
verification,
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const cooldownHours = normalizeCooldownHours(
|
|
188
|
+
params.accountConfig.startupVerificationCooldownHours,
|
|
189
|
+
);
|
|
190
|
+
const cooldownMs = cooldownHours * 60 * 60 * 1000;
|
|
191
|
+
const nowMs = params.nowMs ?? Date.now();
|
|
192
|
+
const state = await readStartupVerificationState(statePath);
|
|
193
|
+
const stateCooldownMs = resolveStateCooldownMs(state, cooldownMs);
|
|
194
|
+
if (shouldHonorCooldown({ state, verification, stateCooldownMs, nowMs })) {
|
|
195
|
+
return {
|
|
196
|
+
kind: "cooldown",
|
|
197
|
+
verification,
|
|
198
|
+
retryAfterMs: resolveRetryAfterMs({
|
|
199
|
+
attemptedAt: state?.attemptedAt,
|
|
200
|
+
cooldownMs: stateCooldownMs,
|
|
201
|
+
nowMs,
|
|
202
|
+
}),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
try {
|
|
207
|
+
const request = await params.client.crypto.requestVerification({ ownUser: true });
|
|
208
|
+
await writeJsonFileAtomically(statePath, {
|
|
209
|
+
userId: verification.userId,
|
|
210
|
+
deviceId: verification.deviceId,
|
|
211
|
+
attemptedAt: new Date(nowMs).toISOString(),
|
|
212
|
+
outcome: "requested",
|
|
213
|
+
requestId: request.id,
|
|
214
|
+
transactionId: request.transactionId,
|
|
215
|
+
} satisfies MatrixStartupVerificationState);
|
|
216
|
+
return {
|
|
217
|
+
kind: "requested",
|
|
218
|
+
verification,
|
|
219
|
+
requestId: request.id,
|
|
220
|
+
transactionId: request.transactionId ?? undefined,
|
|
221
|
+
};
|
|
222
|
+
} catch (err) {
|
|
223
|
+
const error = formatMatrixErrorMessage(err);
|
|
224
|
+
await writeJsonFileAtomically(statePath, {
|
|
225
|
+
userId: verification.userId,
|
|
226
|
+
deviceId: verification.deviceId,
|
|
227
|
+
attemptedAt: new Date(nowMs).toISOString(),
|
|
228
|
+
outcome: "failed",
|
|
229
|
+
error,
|
|
230
|
+
} satisfies MatrixStartupVerificationState).catch(() => {});
|
|
231
|
+
return {
|
|
232
|
+
kind: "request-failed",
|
|
233
|
+
verification,
|
|
234
|
+
error,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
}
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
import type { RuntimeLogger } from "../../runtime-api.js";
|
|
2
|
+
import type { CoreConfig, MatrixConfig } from "../../types.js";
|
|
3
|
+
import type { MatrixAuth } from "../client.js";
|
|
4
|
+
import type { MatrixClient } from "../sdk.js";
|
|
5
|
+
import { isMatrixStartupAbortError, throwIfMatrixStartupAborted } from "../startup-abort.js";
|
|
6
|
+
|
|
7
|
+
type MatrixStartupClient = Pick<
|
|
8
|
+
MatrixClient,
|
|
9
|
+
| "crypto"
|
|
10
|
+
| "getOwnDeviceVerificationStatus"
|
|
11
|
+
| "getUserProfile"
|
|
12
|
+
| "listOwnDevices"
|
|
13
|
+
| "restoreRoomKeyBackup"
|
|
14
|
+
| "setAvatarUrl"
|
|
15
|
+
| "setDisplayName"
|
|
16
|
+
| "uploadContent"
|
|
17
|
+
>;
|
|
18
|
+
|
|
19
|
+
export type MatrixStartupMaintenanceDeps = {
|
|
20
|
+
updateMatrixAccountConfig: typeof import("../config-update.js").updateMatrixAccountConfig;
|
|
21
|
+
summarizeMatrixDeviceHealth: typeof import("../device-health.js").summarizeMatrixDeviceHealth;
|
|
22
|
+
syncMatrixOwnProfile: typeof import("../profile.js").syncMatrixOwnProfile;
|
|
23
|
+
maybeRestoreLegacyMatrixBackup: typeof import("./legacy-crypto-restore.js").maybeRestoreLegacyMatrixBackup;
|
|
24
|
+
ensureMatrixStartupVerification: typeof import("./startup-verification.js").ensureMatrixStartupVerification;
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
let matrixStartupMaintenanceDepsPromise: Promise<MatrixStartupMaintenanceDeps> | undefined;
|
|
28
|
+
|
|
29
|
+
async function loadMatrixStartupMaintenanceDeps(): Promise<MatrixStartupMaintenanceDeps> {
|
|
30
|
+
matrixStartupMaintenanceDepsPromise ??= Promise.all([
|
|
31
|
+
import("../config-update.js"),
|
|
32
|
+
import("../device-health.js"),
|
|
33
|
+
import("../profile.js"),
|
|
34
|
+
import("./legacy-crypto-restore.js"),
|
|
35
|
+
import("./startup-verification.js"),
|
|
36
|
+
]).then(
|
|
37
|
+
([
|
|
38
|
+
configUpdateModule,
|
|
39
|
+
deviceHealthModule,
|
|
40
|
+
profileModule,
|
|
41
|
+
legacyCryptoRestoreModule,
|
|
42
|
+
startupVerificationModule,
|
|
43
|
+
]) => ({
|
|
44
|
+
updateMatrixAccountConfig: configUpdateModule.updateMatrixAccountConfig,
|
|
45
|
+
summarizeMatrixDeviceHealth: deviceHealthModule.summarizeMatrixDeviceHealth,
|
|
46
|
+
syncMatrixOwnProfile: profileModule.syncMatrixOwnProfile,
|
|
47
|
+
maybeRestoreLegacyMatrixBackup: legacyCryptoRestoreModule.maybeRestoreLegacyMatrixBackup,
|
|
48
|
+
ensureMatrixStartupVerification: startupVerificationModule.ensureMatrixStartupVerification,
|
|
49
|
+
}),
|
|
50
|
+
);
|
|
51
|
+
return await matrixStartupMaintenanceDepsPromise;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export async function runMatrixStartupMaintenance(
|
|
55
|
+
params: {
|
|
56
|
+
client: MatrixStartupClient;
|
|
57
|
+
auth: MatrixAuth;
|
|
58
|
+
accountId: string;
|
|
59
|
+
effectiveAccountId: string;
|
|
60
|
+
accountConfig: MatrixConfig;
|
|
61
|
+
logger: RuntimeLogger;
|
|
62
|
+
logVerboseMessage: (message: string) => void;
|
|
63
|
+
getRuntimeConfig: () => CoreConfig;
|
|
64
|
+
replaceConfigFile: (cfg: never) => Promise<void>;
|
|
65
|
+
loadWebMedia: (
|
|
66
|
+
url: string,
|
|
67
|
+
maxBytes: number,
|
|
68
|
+
) => Promise<{ buffer: Buffer; contentType?: string; fileName?: string }>;
|
|
69
|
+
env?: NodeJS.ProcessEnv;
|
|
70
|
+
abortSignal?: AbortSignal;
|
|
71
|
+
},
|
|
72
|
+
deps?: MatrixStartupMaintenanceDeps,
|
|
73
|
+
): Promise<void> {
|
|
74
|
+
const runtimeDeps = deps ?? (await loadMatrixStartupMaintenanceDeps());
|
|
75
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
76
|
+
try {
|
|
77
|
+
const profileSync = await runtimeDeps.syncMatrixOwnProfile({
|
|
78
|
+
client: params.client,
|
|
79
|
+
userId: params.auth.userId,
|
|
80
|
+
displayName: params.accountConfig.name,
|
|
81
|
+
avatarUrl: params.accountConfig.avatarUrl,
|
|
82
|
+
loadAvatarFromUrl: async (url, maxBytes) => await params.loadWebMedia(url, maxBytes),
|
|
83
|
+
});
|
|
84
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
85
|
+
if (profileSync.displayNameUpdated) {
|
|
86
|
+
params.logger.info(`matrix: profile display name updated for ${params.auth.userId}`);
|
|
87
|
+
}
|
|
88
|
+
if (profileSync.avatarUpdated) {
|
|
89
|
+
params.logger.info(`matrix: profile avatar updated for ${params.auth.userId}`);
|
|
90
|
+
}
|
|
91
|
+
if (
|
|
92
|
+
profileSync.convertedAvatarFromHttp &&
|
|
93
|
+
profileSync.resolvedAvatarUrl &&
|
|
94
|
+
params.accountConfig.avatarUrl !== profileSync.resolvedAvatarUrl
|
|
95
|
+
) {
|
|
96
|
+
const latestCfg = params.getRuntimeConfig();
|
|
97
|
+
const updatedCfg = runtimeDeps.updateMatrixAccountConfig(latestCfg, params.accountId, {
|
|
98
|
+
avatarUrl: profileSync.resolvedAvatarUrl,
|
|
99
|
+
});
|
|
100
|
+
await params.replaceConfigFile(updatedCfg as never);
|
|
101
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
102
|
+
params.logVerboseMessage(
|
|
103
|
+
`matrix: persisted converted avatar URL for account ${params.accountId} (${profileSync.resolvedAvatarUrl})`,
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (isMatrixStartupAbortError(err)) {
|
|
108
|
+
throw err;
|
|
109
|
+
}
|
|
110
|
+
params.logger.warn("matrix: failed to sync profile from config", { error: String(err) });
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!(params.auth.encryption && params.client.crypto)) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
119
|
+
const deviceHealth = runtimeDeps.summarizeMatrixDeviceHealth(
|
|
120
|
+
await params.client.listOwnDevices(),
|
|
121
|
+
);
|
|
122
|
+
if (deviceHealth.staleAutoBotDevices.length > 0) {
|
|
123
|
+
params.logger.warn(
|
|
124
|
+
`matrix: stale AutoBot devices detected for ${params.auth.userId}: ${deviceHealth.staleAutoBotDevices.map((device) => device.deviceId).join(", ")}. Run 'autobot matrix devices prune-stale --account ${params.effectiveAccountId}' to keep encrypted-room trust healthy.`,
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
} catch (err) {
|
|
128
|
+
if (isMatrixStartupAbortError(err)) {
|
|
129
|
+
throw err;
|
|
130
|
+
}
|
|
131
|
+
params.logger.debug?.("Failed to inspect matrix device hygiene (non-fatal)", {
|
|
132
|
+
error: String(err),
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
try {
|
|
137
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
138
|
+
const startupVerification = await runtimeDeps.ensureMatrixStartupVerification({
|
|
139
|
+
client: params.client,
|
|
140
|
+
auth: params.auth,
|
|
141
|
+
accountConfig: params.accountConfig,
|
|
142
|
+
env: params.env,
|
|
143
|
+
});
|
|
144
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
145
|
+
if (startupVerification.kind === "verified") {
|
|
146
|
+
params.logger.info("matrix: device is verified by its owner and ready for encrypted rooms");
|
|
147
|
+
} else if (
|
|
148
|
+
startupVerification.kind === "disabled" ||
|
|
149
|
+
startupVerification.kind === "cooldown" ||
|
|
150
|
+
startupVerification.kind === "pending" ||
|
|
151
|
+
startupVerification.kind === "request-failed"
|
|
152
|
+
) {
|
|
153
|
+
params.logger.info(
|
|
154
|
+
"matrix: device not verified — run 'autobot matrix verify device <key>' to enable E2EE",
|
|
155
|
+
);
|
|
156
|
+
if (startupVerification.kind === "pending") {
|
|
157
|
+
params.logger.info(
|
|
158
|
+
"matrix: startup verification request is already pending; finish it in another Matrix client",
|
|
159
|
+
);
|
|
160
|
+
} else if (startupVerification.kind === "cooldown") {
|
|
161
|
+
params.logVerboseMessage(
|
|
162
|
+
`matrix: skipped startup verification request due to cooldown (retryAfterMs=${startupVerification.retryAfterMs ?? 0})`,
|
|
163
|
+
);
|
|
164
|
+
} else if (startupVerification.kind === "request-failed") {
|
|
165
|
+
params.logger.debug?.("Matrix startup verification request failed (non-fatal)", {
|
|
166
|
+
error: startupVerification.error ?? "unknown",
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
} else if (startupVerification.kind === "requested") {
|
|
170
|
+
params.logger.info(
|
|
171
|
+
"matrix: device not verified — requested verification in another Matrix client",
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
} catch (err) {
|
|
175
|
+
if (isMatrixStartupAbortError(err)) {
|
|
176
|
+
throw err;
|
|
177
|
+
}
|
|
178
|
+
params.logger.debug?.("Failed to resolve matrix verification status (non-fatal)", {
|
|
179
|
+
error: String(err),
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
try {
|
|
184
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
185
|
+
const legacyCryptoRestore = await runtimeDeps.maybeRestoreLegacyMatrixBackup({
|
|
186
|
+
client: params.client,
|
|
187
|
+
auth: params.auth,
|
|
188
|
+
env: params.env,
|
|
189
|
+
});
|
|
190
|
+
throwIfMatrixStartupAborted(params.abortSignal);
|
|
191
|
+
if (legacyCryptoRestore.kind === "restored") {
|
|
192
|
+
params.logger.info(
|
|
193
|
+
`matrix: restored ${legacyCryptoRestore.imported}/${legacyCryptoRestore.total} room key(s) from legacy encrypted-state backup`,
|
|
194
|
+
);
|
|
195
|
+
if (legacyCryptoRestore.localOnlyKeys > 0) {
|
|
196
|
+
params.logger.warn(
|
|
197
|
+
`matrix: ${legacyCryptoRestore.localOnlyKeys} legacy local-only room key(s) were never backed up and could not be restored automatically`,
|
|
198
|
+
);
|
|
199
|
+
}
|
|
200
|
+
} else if (legacyCryptoRestore.kind === "failed") {
|
|
201
|
+
params.logger.warn(
|
|
202
|
+
`matrix: failed restoring room keys from legacy encrypted-state backup: ${legacyCryptoRestore.error}`,
|
|
203
|
+
);
|
|
204
|
+
if (legacyCryptoRestore.localOnlyKeys > 0) {
|
|
205
|
+
params.logger.warn(
|
|
206
|
+
`matrix: ${legacyCryptoRestore.localOnlyKeys} legacy local-only room key(s) were never backed up and may remain unavailable until manually recovered`,
|
|
207
|
+
);
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
} catch (err) {
|
|
211
|
+
if (isMatrixStartupAbortError(err)) {
|
|
212
|
+
throw err;
|
|
213
|
+
}
|
|
214
|
+
params.logger.warn("matrix: failed restoring legacy encrypted-state backup", {
|
|
215
|
+
error: String(err),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
}
|