@archipelagolab/lobi 1.0.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 +164 -0
- package/ENDOFFILE +0 -0
- package/EOF +0 -0
- package/LICENSE +21 -0
- package/SPEC-SUPPORT.md +116 -0
- package/YAMLEND +0 -0
- package/api.ts +18 -0
- package/archipelagolab-lobi-1.0.0.tgz +0 -0
- package/auth-presence.ts +56 -0
- package/channel-plugin-api.ts +3 -0
- package/cli-metadata.ts +11 -0
- package/contract-api.ts +17 -0
- package/docs/CHECKLIST.md +83 -0
- package/docs/FORK_SDK_GUIDE.md +279 -0
- package/helper-api.ts +3 -0
- package/index.test.ts +61 -0
- package/index.ts +65 -0
- package/openclaw.plugin.json +23 -0
- package/package.json +52 -0
- package/plugin-entry.handlers.runtime.ts +1 -0
- package/runtime-api.ts +54 -0
- package/runtime-heavy-api.ts +1 -0
- package/scripts/migrate-to-lobi.sh +72 -0
- package/secret-contract-api.ts +5 -0
- package/setup-entry.ts +13 -0
- package/src/account-selection.test.ts +124 -0
- package/src/account-selection.ts +226 -0
- package/src/actions.account-propagation.test.ts +251 -0
- package/src/actions.test.ts +251 -0
- package/src/actions.ts +336 -0
- package/src/approval-auth.test.ts +23 -0
- package/src/approval-auth.ts +25 -0
- package/src/approval-handler.runtime.test.ts +46 -0
- package/src/approval-handler.runtime.ts +400 -0
- package/src/approval-ids.ts +6 -0
- package/src/approval-native.test.ts +329 -0
- package/src/approval-native.ts +336 -0
- package/src/approval-reactions.test.ts +107 -0
- package/src/approval-reactions.ts +158 -0
- package/src/auth-precedence.ts +61 -0
- package/src/channel-account-paths.ts +92 -0
- package/src/channel.account-paths.test.ts +102 -0
- package/src/channel.directory.test.ts +601 -0
- package/src/channel.resolve.test.ts +38 -0
- package/src/channel.runtime.ts +16 -0
- package/src/channel.setup.test.ts +269 -0
- package/src/channel.ts +570 -0
- package/src/cli-metadata.ts +19 -0
- package/src/cli.test.ts +1015 -0
- package/src/cli.ts +1198 -0
- package/src/config-adapter.ts +41 -0
- package/src/config-schema.test.ts +90 -0
- package/src/config-schema.ts +114 -0
- package/src/directory-live.test.ts +200 -0
- package/src/directory-live.ts +238 -0
- package/src/doctor-contract.ts +287 -0
- package/src/doctor.test.ts +440 -0
- package/src/doctor.ts +262 -0
- package/src/env-vars.ts +92 -0
- package/src/exec-approval-resolver.test.ts +68 -0
- package/src/exec-approval-resolver.ts +23 -0
- package/src/exec-approvals.test.ts +483 -0
- package/src/exec-approvals.ts +290 -0
- package/src/group-mentions.ts +41 -0
- package/src/legacy-crypto-inspector-availability.test.ts +81 -0
- package/src/legacy-crypto-inspector-availability.ts +60 -0
- package/src/legacy-crypto.test.ts +234 -0
- package/src/legacy-crypto.ts +549 -0
- package/src/legacy-state.test.ts +86 -0
- package/src/legacy-state.ts +156 -0
- package/src/matrix/account-config.ts +150 -0
- package/src/matrix/accounts.readiness.test.ts +27 -0
- package/src/matrix/accounts.test.ts +757 -0
- package/src/matrix/accounts.ts +194 -0
- package/src/matrix/actions/client.test.ts +215 -0
- package/src/matrix/actions/client.ts +31 -0
- package/src/matrix/actions/devices.test.ts +114 -0
- package/src/matrix/actions/devices.ts +34 -0
- package/src/matrix/actions/limits.test.ts +15 -0
- package/src/matrix/actions/limits.ts +6 -0
- package/src/matrix/actions/messages.test.ts +289 -0
- package/src/matrix/actions/messages.ts +123 -0
- package/src/matrix/actions/pins.test.ts +74 -0
- package/src/matrix/actions/pins.ts +64 -0
- package/src/matrix/actions/polls.test.ts +71 -0
- package/src/matrix/actions/polls.ts +109 -0
- package/src/matrix/actions/profile.test.ts +109 -0
- package/src/matrix/actions/profile.ts +37 -0
- package/src/matrix/actions/reactions.test.ts +135 -0
- package/src/matrix/actions/reactions.ts +59 -0
- package/src/matrix/actions/room.test.ts +79 -0
- package/src/matrix/actions/room.ts +71 -0
- package/src/matrix/actions/summary.test.ts +87 -0
- package/src/matrix/actions/summary.ts +88 -0
- package/src/matrix/actions/types.ts +82 -0
- package/src/matrix/actions/verification.test.ts +105 -0
- package/src/matrix/actions/verification.ts +237 -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 +115 -0
- package/src/matrix/client/config-runtime-api.ts +14 -0
- package/src/matrix/client/config-secret-input.runtime.ts +1 -0
- package/src/matrix/client/config.ts +982 -0
- package/src/matrix/client/create-client.test.ts +115 -0
- package/src/matrix/client/create-client.ts +101 -0
- package/src/matrix/client/env-auth.ts +6 -0
- package/src/matrix/client/file-sync-store.test.ts +265 -0
- package/src/matrix/client/file-sync-store.ts +289 -0
- package/src/matrix/client/logging.ts +123 -0
- package/src/matrix/client/migration-snapshot.runtime.ts +1 -0
- package/src/matrix/client/private-network-host.ts +56 -0
- package/src/matrix/client/runtime.ts +4 -0
- package/src/matrix/client/shared.test.ts +344 -0
- package/src/matrix/client/shared.ts +306 -0
- package/src/matrix/client/storage.test.ts +634 -0
- package/src/matrix/client/storage.ts +544 -0
- package/src/matrix/client/types.ts +50 -0
- package/src/matrix/client-bootstrap.test.ts +84 -0
- package/src/matrix/client-bootstrap.ts +164 -0
- package/src/matrix/client-resolver.test-helpers.ts +147 -0
- package/src/matrix/client.test.ts +1521 -0
- package/src/matrix/client.ts +23 -0
- package/src/matrix/config-paths.ts +31 -0
- package/src/matrix/config-update.test.ts +237 -0
- package/src/matrix/config-update.ts +291 -0
- package/src/matrix/credentials-read.ts +206 -0
- package/src/matrix/credentials-write.runtime.ts +26 -0
- package/src/matrix/credentials.test.ts +501 -0
- package/src/matrix/credentials.ts +95 -0
- package/src/matrix/deps.test.ts +74 -0
- package/src/matrix/deps.ts +225 -0
- package/src/matrix/device-health.test.ts +45 -0
- package/src/matrix/device-health.ts +31 -0
- package/src/matrix/direct-management.test.ts +350 -0
- package/src/matrix/direct-management.ts +347 -0
- package/src/matrix/direct-room.test.ts +61 -0
- package/src/matrix/direct-room.ts +128 -0
- package/src/matrix/draft-stream.test.ts +406 -0
- package/src/matrix/draft-stream.ts +216 -0
- package/src/matrix/encryption-guidance.ts +27 -0
- package/src/matrix/errors.ts +21 -0
- package/src/matrix/format.test.ts +340 -0
- package/src/matrix/format.ts +428 -0
- package/src/matrix/legacy-crypto-inspector.ts +95 -0
- package/src/matrix/media-errors.ts +20 -0
- package/src/matrix/media-text.ts +169 -0
- package/src/matrix/monitor/access-state.test.ts +45 -0
- package/src/matrix/monitor/access-state.ts +77 -0
- package/src/matrix/monitor/ack-config.test.ts +57 -0
- package/src/matrix/monitor/ack-config.ts +26 -0
- package/src/matrix/monitor/allowlist.test.ts +45 -0
- package/src/matrix/monitor/allowlist.ts +94 -0
- package/src/matrix/monitor/auto-join.test.ts +203 -0
- package/src/matrix/monitor/auto-join.ts +86 -0
- package/src/matrix/monitor/config.test.ts +197 -0
- package/src/matrix/monitor/config.ts +303 -0
- package/src/matrix/monitor/context-summary.ts +43 -0
- package/src/matrix/monitor/direct.test.ts +529 -0
- package/src/matrix/monitor/direct.ts +270 -0
- package/src/matrix/monitor/events.test.ts +1524 -0
- package/src/matrix/monitor/events.ts +213 -0
- package/src/matrix/monitor/handler.body-for-agent.test.ts +396 -0
- package/src/matrix/monitor/handler.group-history.test.ts +648 -0
- package/src/matrix/monitor/handler.media-failure.test.ts +267 -0
- package/src/matrix/monitor/handler.test-helpers.ts +308 -0
- package/src/matrix/monitor/handler.test.ts +2952 -0
- package/src/matrix/monitor/handler.thread-root-media.test.ts +82 -0
- package/src/matrix/monitor/handler.ts +1679 -0
- package/src/matrix/monitor/inbound-dedupe.test.ts +146 -0
- package/src/matrix/monitor/inbound-dedupe.ts +267 -0
- package/src/matrix/monitor/index.test.ts +920 -0
- package/src/matrix/monitor/index.ts +434 -0
- package/src/matrix/monitor/legacy-crypto-restore.test.ts +206 -0
- package/src/matrix/monitor/legacy-crypto-restore.ts +139 -0
- package/src/matrix/monitor/location.ts +100 -0
- package/src/matrix/monitor/media.test.ts +159 -0
- package/src/matrix/monitor/media.ts +119 -0
- package/src/matrix/monitor/mentions.test.ts +289 -0
- package/src/matrix/monitor/mentions.ts +177 -0
- package/src/matrix/monitor/reaction-events.test.ts +326 -0
- package/src/matrix/monitor/reaction-events.ts +187 -0
- package/src/matrix/monitor/recent-invite.test.ts +92 -0
- package/src/matrix/monitor/recent-invite.ts +30 -0
- package/src/matrix/monitor/replies.test.ts +265 -0
- package/src/matrix/monitor/replies.ts +136 -0
- package/src/matrix/monitor/reply-context.test.ts +276 -0
- package/src/matrix/monitor/reply-context.ts +92 -0
- package/src/matrix/monitor/room-history.test.ts +258 -0
- package/src/matrix/monitor/room-history.ts +301 -0
- package/src/matrix/monitor/room-info.test.ts +201 -0
- package/src/matrix/monitor/room-info.ts +126 -0
- package/src/matrix/monitor/rooms.test.ts +121 -0
- package/src/matrix/monitor/rooms.ts +52 -0
- package/src/matrix/monitor/route.test.ts +255 -0
- package/src/matrix/monitor/route.ts +178 -0
- package/src/matrix/monitor/runtime-api.ts +31 -0
- package/src/matrix/monitor/startup-verification.test.ts +294 -0
- package/src/matrix/monitor/startup-verification.ts +237 -0
- package/src/matrix/monitor/startup.test.ts +257 -0
- package/src/matrix/monitor/startup.ts +218 -0
- package/src/matrix/monitor/status.ts +111 -0
- package/src/matrix/monitor/sync-lifecycle.test.ts +224 -0
- package/src/matrix/monitor/sync-lifecycle.ts +91 -0
- package/src/matrix/monitor/task-runner.ts +38 -0
- package/src/matrix/monitor/thread-context.test.ts +149 -0
- package/src/matrix/monitor/thread-context.ts +108 -0
- package/src/matrix/monitor/threads.test.ts +68 -0
- package/src/matrix/monitor/threads.ts +85 -0
- package/src/matrix/monitor/types.ts +30 -0
- package/src/matrix/monitor/verification-events.ts +627 -0
- package/src/matrix/monitor/verification-utils.test.ts +47 -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.test.ts +205 -0
- package/src/matrix/poll-types.ts +433 -0
- package/src/matrix/probe.runtime.ts +4 -0
- package/src/matrix/probe.test.ts +154 -0
- package/src/matrix/probe.ts +96 -0
- package/src/matrix/profile.test.ts +154 -0
- package/src/matrix/profile.ts +184 -0
- package/src/matrix/reaction-common.test.ts +96 -0
- package/src/matrix/reaction-common.ts +147 -0
- package/src/matrix/sdk/crypto-bootstrap.test.ts +505 -0
- package/src/matrix/sdk/crypto-bootstrap.ts +341 -0
- package/src/matrix/sdk/crypto-facade.test.ts +197 -0
- package/src/matrix/sdk/crypto-facade.ts +207 -0
- package/src/matrix/sdk/crypto-node.runtime.test.ts +27 -0
- package/src/matrix/sdk/crypto-node.runtime.ts +9 -0
- package/src/matrix/sdk/crypto-runtime.ts +11 -0
- package/src/matrix/sdk/decrypt-bridge.ts +356 -0
- package/src/matrix/sdk/event-helpers.test.ts +60 -0
- package/src/matrix/sdk/event-helpers.ts +71 -0
- package/src/matrix/sdk/http-client.test.ts +134 -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.lock-order.test.ts +108 -0
- package/src/matrix/sdk/idb-persistence.test-helpers.ts +88 -0
- package/src/matrix/sdk/idb-persistence.test.ts +149 -0
- package/src/matrix/sdk/idb-persistence.ts +283 -0
- package/src/matrix/sdk/logger.test.ts +25 -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.test.ts +385 -0
- package/src/matrix/sdk/recovery-key-store.ts +430 -0
- package/src/matrix/sdk/transport.test.ts +161 -0
- package/src/matrix/sdk/transport.ts +344 -0
- package/src/matrix/sdk/types.ts +236 -0
- package/src/matrix/sdk/verification-manager.test.ts +509 -0
- package/src/matrix/sdk/verification-manager.ts +694 -0
- package/src/matrix/sdk/verification-status.ts +23 -0
- package/src/matrix/sdk.test.ts +2568 -0
- package/src/matrix/sdk.ts +1789 -0
- package/src/matrix/send/client.test.ts +174 -0
- package/src/matrix/send/client.ts +90 -0
- package/src/matrix/send/formatting.ts +189 -0
- package/src/matrix/send/media.ts +244 -0
- package/src/matrix/send/targets.test.ts +254 -0
- package/src/matrix/send/targets.ts +104 -0
- package/src/matrix/send/types.ts +134 -0
- package/src/matrix/send.test.ts +958 -0
- package/src/matrix/send.ts +609 -0
- package/src/matrix/session-store-metadata.ts +108 -0
- package/src/matrix/startup-abort.ts +44 -0
- package/src/matrix/sync-state.ts +27 -0
- package/src/matrix/target-ids.ts +102 -0
- package/src/matrix/thread-bindings-shared.ts +201 -0
- package/src/matrix/thread-bindings.test.ts +673 -0
- package/src/matrix/thread-bindings.ts +577 -0
- package/src/matrix-migration.runtime.ts +9 -0
- package/src/migration-config.test.ts +228 -0
- package/src/migration-config.ts +243 -0
- package/src/migration-snapshot-backup.ts +117 -0
- package/src/migration-snapshot.test.ts +184 -0
- package/src/migration-snapshot.ts +55 -0
- package/src/onboarding.resolve.test.ts +55 -0
- package/src/onboarding.test-harness.ts +158 -0
- package/src/onboarding.test.ts +665 -0
- package/src/onboarding.ts +773 -0
- package/src/outbound.test.ts +173 -0
- package/src/outbound.ts +78 -0
- package/src/plugin-entry.runtime.js +159 -0
- package/src/plugin-entry.runtime.test.ts +108 -0
- package/src/plugin-entry.runtime.ts +68 -0
- package/src/profile-update.ts +68 -0
- package/src/record-shared.ts +3 -0
- package/src/resolve-targets.test.ts +178 -0
- package/src/resolve-targets.ts +175 -0
- package/src/resolver.ts +21 -0
- package/src/runtime-api.ts +144 -0
- package/src/runtime.ts +7 -0
- package/src/secret-contract.ts +174 -0
- package/src/session-route.test.ts +315 -0
- package/src/session-route.ts +113 -0
- package/src/setup-bootstrap.ts +94 -0
- package/src/setup-config.ts +222 -0
- package/src/setup-contract.ts +89 -0
- package/src/setup-core.test.ts +326 -0
- package/src/setup-core.ts +50 -0
- package/src/setup-surface.ts +4 -0
- package/src/startup-maintenance.test.ts +227 -0
- package/src/startup-maintenance.ts +114 -0
- package/src/storage-paths.ts +92 -0
- package/src/test-helpers.ts +42 -0
- package/src/test-mocks.ts +55 -0
- package/src/test-runtime.ts +72 -0
- package/src/test-support/monitor-route-test-support.ts +8 -0
- package/src/tool-actions.runtime.ts +1 -0
- package/src/tool-actions.test.ts +422 -0
- package/src/tool-actions.ts +498 -0
- package/src/types.ts +230 -0
- package/test-api.ts +2 -0
- package/thread-bindings-runtime.ts +4 -0
- package/tsconfig.json +16 -0
|
@@ -0,0 +1,549 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import os from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
5
|
+
import { writeJsonFileAtomically as writeJsonFileAtomicallyImpl } from "openclaw/plugin-sdk/json-store";
|
|
6
|
+
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
|
|
7
|
+
import { resolveConfiguredMatrixAccountIds } from "./account-selection.js";
|
|
8
|
+
import { isMatrixLegacyCryptoInspectorAvailable } from "./legacy-crypto-inspector-availability.js";
|
|
9
|
+
import { formatMatrixErrorMessage } from "./matrix/errors.js";
|
|
10
|
+
import {
|
|
11
|
+
resolveLegacyMatrixFlatStoreTarget,
|
|
12
|
+
resolveMatrixMigrationAccountTarget,
|
|
13
|
+
} from "./migration-config.js";
|
|
14
|
+
import { resolveMatrixLegacyFlatStoragePaths } from "./storage-paths.js";
|
|
15
|
+
|
|
16
|
+
const MATRIX_LEGACY_CRYPTO_INSPECTOR_UNAVAILABLE_MESSAGE =
|
|
17
|
+
"Legacy Matrix encrypted state was detected, but the Matrix crypto inspector is unavailable.";
|
|
18
|
+
|
|
19
|
+
type MatrixLegacyCryptoCounts = {
|
|
20
|
+
total: number;
|
|
21
|
+
backedUp: number;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
type MatrixLegacyCryptoSummary = {
|
|
25
|
+
deviceId: string | null;
|
|
26
|
+
roomKeyCounts: MatrixLegacyCryptoCounts | null;
|
|
27
|
+
backupVersion: string | null;
|
|
28
|
+
decryptionKeyBase64: string | null;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
type MatrixLegacyCryptoMigrationState = {
|
|
32
|
+
version: 1;
|
|
33
|
+
source: "matrix-bot-sdk-rust";
|
|
34
|
+
accountId: string;
|
|
35
|
+
deviceId: string | null;
|
|
36
|
+
roomKeyCounts: MatrixLegacyCryptoCounts | null;
|
|
37
|
+
backupVersion: string | null;
|
|
38
|
+
decryptionKeyImported: boolean;
|
|
39
|
+
restoreStatus: "pending" | "completed" | "manual-action-required";
|
|
40
|
+
detectedAt: string;
|
|
41
|
+
restoredAt?: string;
|
|
42
|
+
importedCount?: number;
|
|
43
|
+
totalCount?: number;
|
|
44
|
+
lastError?: string | null;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
type MatrixLegacyCryptoPlan = {
|
|
48
|
+
accountId: string;
|
|
49
|
+
rootDir: string;
|
|
50
|
+
recoveryKeyPath: string;
|
|
51
|
+
statePath: string;
|
|
52
|
+
legacyCryptoPath: string;
|
|
53
|
+
homeserver: string;
|
|
54
|
+
userId: string;
|
|
55
|
+
accessToken: string;
|
|
56
|
+
deviceId: string | null;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
type MatrixLegacyCryptoDetection = {
|
|
60
|
+
inspectorAvailable: boolean;
|
|
61
|
+
plans: MatrixLegacyCryptoPlan[];
|
|
62
|
+
warnings: string[];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
type MatrixLegacyCryptoPreparationResult = {
|
|
66
|
+
migrated: boolean;
|
|
67
|
+
changes: string[];
|
|
68
|
+
warnings: string[];
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
type MatrixLegacyCryptoPrepareDeps = {
|
|
72
|
+
inspectLegacyStore: MatrixLegacyCryptoInspector;
|
|
73
|
+
writeJsonFileAtomically: typeof writeJsonFileAtomicallyImpl;
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
type MatrixLegacyCryptoInspectorParams = {
|
|
77
|
+
cryptoRootDir: string;
|
|
78
|
+
userId: string;
|
|
79
|
+
deviceId: string;
|
|
80
|
+
log?: (message: string) => void;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type MatrixLegacyCryptoInspectorResult = {
|
|
84
|
+
deviceId: string | null;
|
|
85
|
+
roomKeyCounts: {
|
|
86
|
+
total: number;
|
|
87
|
+
backedUp: number;
|
|
88
|
+
} | null;
|
|
89
|
+
backupVersion: string | null;
|
|
90
|
+
decryptionKeyBase64: string | null;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
type MatrixLegacyCryptoInspector = (
|
|
94
|
+
params: MatrixLegacyCryptoInspectorParams,
|
|
95
|
+
) => Promise<MatrixLegacyCryptoInspectorResult>;
|
|
96
|
+
|
|
97
|
+
type MatrixLegacyBotSdkMetadata = {
|
|
98
|
+
deviceId: string | null;
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
type MatrixStoredRecoveryKey = {
|
|
102
|
+
version: 1;
|
|
103
|
+
createdAt: string;
|
|
104
|
+
keyId?: string | null;
|
|
105
|
+
encodedPrivateKey?: string;
|
|
106
|
+
privateKeyBase64: string;
|
|
107
|
+
keyInfo?: {
|
|
108
|
+
passphrase?: unknown;
|
|
109
|
+
name?: string;
|
|
110
|
+
};
|
|
111
|
+
};
|
|
112
|
+
|
|
113
|
+
async function loadMatrixLegacyCryptoInspector(): Promise<MatrixLegacyCryptoInspector> {
|
|
114
|
+
const module = await import("./matrix/legacy-crypto-inspector.js");
|
|
115
|
+
return module.inspectLegacyMatrixCryptoStore as MatrixLegacyCryptoInspector;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function detectLegacyBotSdkCryptoStore(cryptoRootDir: string): {
|
|
119
|
+
detected: boolean;
|
|
120
|
+
warning?: string;
|
|
121
|
+
} {
|
|
122
|
+
try {
|
|
123
|
+
const stat = fs.statSync(cryptoRootDir);
|
|
124
|
+
if (!stat.isDirectory()) {
|
|
125
|
+
return {
|
|
126
|
+
detected: false,
|
|
127
|
+
warning:
|
|
128
|
+
`Legacy Matrix encrypted state path exists but is not a directory: ${cryptoRootDir}. ` +
|
|
129
|
+
"OpenClaw skipped automatic crypto migration for that path.",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
} catch (err) {
|
|
133
|
+
return {
|
|
134
|
+
detected: false,
|
|
135
|
+
warning:
|
|
136
|
+
`Failed reading legacy Matrix encrypted state path (${cryptoRootDir}): ${String(err)}. ` +
|
|
137
|
+
"OpenClaw skipped automatic crypto migration for that path.",
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
try {
|
|
142
|
+
return {
|
|
143
|
+
detected:
|
|
144
|
+
fs.existsSync(path.join(cryptoRootDir, "bot-sdk.json")) ||
|
|
145
|
+
fs.existsSync(path.join(cryptoRootDir, "matrix-sdk-crypto.sqlite3")) ||
|
|
146
|
+
fs
|
|
147
|
+
.readdirSync(cryptoRootDir, { withFileTypes: true })
|
|
148
|
+
.some(
|
|
149
|
+
(entry) =>
|
|
150
|
+
entry.isDirectory() &&
|
|
151
|
+
fs.existsSync(path.join(cryptoRootDir, entry.name, "matrix-sdk-crypto.sqlite3")),
|
|
152
|
+
),
|
|
153
|
+
};
|
|
154
|
+
} catch (err) {
|
|
155
|
+
return {
|
|
156
|
+
detected: false,
|
|
157
|
+
warning:
|
|
158
|
+
`Failed scanning legacy Matrix encrypted state path (${cryptoRootDir}): ${String(err)}. ` +
|
|
159
|
+
"OpenClaw skipped automatic crypto migration for that path.",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function resolveMatrixAccountIds(cfg: OpenClawConfig): string[] {
|
|
165
|
+
return resolveConfiguredMatrixAccountIds(cfg);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function resolveLegacyMatrixFlatStorePlan(params: {
|
|
169
|
+
cfg: OpenClawConfig;
|
|
170
|
+
env: NodeJS.ProcessEnv;
|
|
171
|
+
}): MatrixLegacyCryptoPlan | { warning: string } | null {
|
|
172
|
+
const legacy = resolveMatrixLegacyFlatStoragePaths(resolveStateDir(params.env, os.homedir));
|
|
173
|
+
if (!fs.existsSync(legacy.cryptoPath)) {
|
|
174
|
+
return null;
|
|
175
|
+
}
|
|
176
|
+
const legacyStore = detectLegacyBotSdkCryptoStore(legacy.cryptoPath);
|
|
177
|
+
if (legacyStore.warning) {
|
|
178
|
+
return { warning: legacyStore.warning };
|
|
179
|
+
}
|
|
180
|
+
if (!legacyStore.detected) {
|
|
181
|
+
return null;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const target = resolveLegacyMatrixFlatStoreTarget({
|
|
185
|
+
cfg: params.cfg,
|
|
186
|
+
env: params.env,
|
|
187
|
+
detectedPath: legacy.cryptoPath,
|
|
188
|
+
detectedKind: "encrypted state",
|
|
189
|
+
});
|
|
190
|
+
if ("warning" in target) {
|
|
191
|
+
return target;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const metadata = loadLegacyBotSdkMetadata(legacy.cryptoPath);
|
|
195
|
+
return {
|
|
196
|
+
accountId: target.accountId,
|
|
197
|
+
rootDir: target.rootDir,
|
|
198
|
+
recoveryKeyPath: path.join(target.rootDir, "recovery-key.json"),
|
|
199
|
+
statePath: path.join(target.rootDir, "legacy-crypto-migration.json"),
|
|
200
|
+
legacyCryptoPath: legacy.cryptoPath,
|
|
201
|
+
homeserver: target.homeserver,
|
|
202
|
+
userId: target.userId,
|
|
203
|
+
accessToken: target.accessToken,
|
|
204
|
+
deviceId: metadata.deviceId ?? target.storedDeviceId,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function loadLegacyBotSdkMetadata(cryptoRootDir: string): MatrixLegacyBotSdkMetadata {
|
|
209
|
+
const metadataPath = path.join(cryptoRootDir, "bot-sdk.json");
|
|
210
|
+
const fallback: MatrixLegacyBotSdkMetadata = { deviceId: null };
|
|
211
|
+
try {
|
|
212
|
+
if (!fs.existsSync(metadataPath)) {
|
|
213
|
+
return fallback;
|
|
214
|
+
}
|
|
215
|
+
const parsed = JSON.parse(fs.readFileSync(metadataPath, "utf8")) as {
|
|
216
|
+
deviceId?: unknown;
|
|
217
|
+
};
|
|
218
|
+
return {
|
|
219
|
+
deviceId:
|
|
220
|
+
typeof parsed.deviceId === "string" && parsed.deviceId.trim() ? parsed.deviceId : null,
|
|
221
|
+
};
|
|
222
|
+
} catch {
|
|
223
|
+
return fallback;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function resolveMatrixLegacyCryptoPlans(params: {
|
|
228
|
+
cfg: OpenClawConfig;
|
|
229
|
+
env: NodeJS.ProcessEnv;
|
|
230
|
+
}): Omit<MatrixLegacyCryptoDetection, "inspectorAvailable"> {
|
|
231
|
+
const warnings: string[] = [];
|
|
232
|
+
const plans: MatrixLegacyCryptoPlan[] = [];
|
|
233
|
+
|
|
234
|
+
const flatPlan = resolveLegacyMatrixFlatStorePlan(params);
|
|
235
|
+
if (flatPlan) {
|
|
236
|
+
if ("warning" in flatPlan) {
|
|
237
|
+
warnings.push(flatPlan.warning);
|
|
238
|
+
} else {
|
|
239
|
+
plans.push(flatPlan);
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
for (const accountId of resolveMatrixAccountIds(params.cfg)) {
|
|
244
|
+
const target = resolveMatrixMigrationAccountTarget({
|
|
245
|
+
cfg: params.cfg,
|
|
246
|
+
env: params.env,
|
|
247
|
+
accountId,
|
|
248
|
+
});
|
|
249
|
+
if (!target) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
const legacyCryptoPath = path.join(target.rootDir, "crypto");
|
|
253
|
+
if (!fs.existsSync(legacyCryptoPath)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
const detectedStore = detectLegacyBotSdkCryptoStore(legacyCryptoPath);
|
|
257
|
+
if (detectedStore.warning) {
|
|
258
|
+
warnings.push(detectedStore.warning);
|
|
259
|
+
continue;
|
|
260
|
+
}
|
|
261
|
+
if (!detectedStore.detected) {
|
|
262
|
+
continue;
|
|
263
|
+
}
|
|
264
|
+
if (
|
|
265
|
+
plans.some(
|
|
266
|
+
(plan) =>
|
|
267
|
+
plan.accountId === accountId &&
|
|
268
|
+
path.resolve(plan.legacyCryptoPath) === path.resolve(legacyCryptoPath),
|
|
269
|
+
)
|
|
270
|
+
) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
const metadata = loadLegacyBotSdkMetadata(legacyCryptoPath);
|
|
274
|
+
plans.push({
|
|
275
|
+
accountId: target.accountId,
|
|
276
|
+
rootDir: target.rootDir,
|
|
277
|
+
recoveryKeyPath: path.join(target.rootDir, "recovery-key.json"),
|
|
278
|
+
statePath: path.join(target.rootDir, "legacy-crypto-migration.json"),
|
|
279
|
+
legacyCryptoPath,
|
|
280
|
+
homeserver: target.homeserver,
|
|
281
|
+
userId: target.userId,
|
|
282
|
+
accessToken: target.accessToken,
|
|
283
|
+
deviceId: metadata.deviceId ?? target.storedDeviceId,
|
|
284
|
+
});
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
return { plans, warnings };
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function loadStoredRecoveryKey(filePath: string): MatrixStoredRecoveryKey | null {
|
|
291
|
+
try {
|
|
292
|
+
if (!fs.existsSync(filePath)) {
|
|
293
|
+
return null;
|
|
294
|
+
}
|
|
295
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8")) as MatrixStoredRecoveryKey;
|
|
296
|
+
} catch {
|
|
297
|
+
return null;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
function loadLegacyCryptoMigrationState(filePath: string): MatrixLegacyCryptoMigrationState | null {
|
|
302
|
+
try {
|
|
303
|
+
if (!fs.existsSync(filePath)) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8")) as MatrixLegacyCryptoMigrationState;
|
|
307
|
+
} catch {
|
|
308
|
+
return null;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
async function persistLegacyMigrationState(params: {
|
|
313
|
+
filePath: string;
|
|
314
|
+
state: MatrixLegacyCryptoMigrationState;
|
|
315
|
+
writeJsonFileAtomically: typeof writeJsonFileAtomicallyImpl;
|
|
316
|
+
}): Promise<void> {
|
|
317
|
+
await params.writeJsonFileAtomically(params.filePath, params.state);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
export function detectLegacyMatrixCrypto(params: {
|
|
321
|
+
cfg: OpenClawConfig;
|
|
322
|
+
env?: NodeJS.ProcessEnv;
|
|
323
|
+
}): MatrixLegacyCryptoDetection {
|
|
324
|
+
const detection = resolveMatrixLegacyCryptoPlans({
|
|
325
|
+
cfg: params.cfg,
|
|
326
|
+
env: params.env ?? process.env,
|
|
327
|
+
});
|
|
328
|
+
const inspectorAvailable =
|
|
329
|
+
detection.plans.length === 0 || isMatrixLegacyCryptoInspectorAvailable();
|
|
330
|
+
if (!inspectorAvailable && detection.plans.length > 0) {
|
|
331
|
+
return {
|
|
332
|
+
inspectorAvailable,
|
|
333
|
+
plans: detection.plans,
|
|
334
|
+
warnings: [...detection.warnings, MATRIX_LEGACY_CRYPTO_INSPECTOR_UNAVAILABLE_MESSAGE],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
return {
|
|
338
|
+
inspectorAvailable,
|
|
339
|
+
plans: detection.plans,
|
|
340
|
+
warnings: detection.warnings,
|
|
341
|
+
};
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
export async function autoPrepareLegacyMatrixCrypto(params: {
|
|
345
|
+
cfg: OpenClawConfig;
|
|
346
|
+
env?: NodeJS.ProcessEnv;
|
|
347
|
+
log?: { info?: (message: string) => void; warn?: (message: string) => void };
|
|
348
|
+
deps?: Partial<MatrixLegacyCryptoPrepareDeps>;
|
|
349
|
+
}): Promise<MatrixLegacyCryptoPreparationResult> {
|
|
350
|
+
const env = params.env ?? process.env;
|
|
351
|
+
const detection = params.deps?.inspectLegacyStore
|
|
352
|
+
? resolveMatrixLegacyCryptoPlans({ cfg: params.cfg, env })
|
|
353
|
+
: detectLegacyMatrixCrypto({ cfg: params.cfg, env });
|
|
354
|
+
const inspectorAvailable =
|
|
355
|
+
"inspectorAvailable" in detection ? detection.inspectorAvailable : true;
|
|
356
|
+
const warnings = [...detection.warnings];
|
|
357
|
+
const changes: string[] = [];
|
|
358
|
+
const writeJsonFileAtomically =
|
|
359
|
+
params.deps?.writeJsonFileAtomically ?? writeJsonFileAtomicallyImpl;
|
|
360
|
+
if (detection.plans.length === 0) {
|
|
361
|
+
if (warnings.length > 0) {
|
|
362
|
+
params.log?.warn?.(
|
|
363
|
+
`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
return {
|
|
367
|
+
migrated: false,
|
|
368
|
+
changes,
|
|
369
|
+
warnings,
|
|
370
|
+
};
|
|
371
|
+
}
|
|
372
|
+
if (!params.deps?.inspectLegacyStore && !inspectorAvailable) {
|
|
373
|
+
if (warnings.length > 0) {
|
|
374
|
+
params.log?.warn?.(
|
|
375
|
+
`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
return {
|
|
379
|
+
migrated: false,
|
|
380
|
+
changes,
|
|
381
|
+
warnings,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
let inspectLegacyStore = params.deps?.inspectLegacyStore;
|
|
386
|
+
if (!inspectLegacyStore) {
|
|
387
|
+
try {
|
|
388
|
+
inspectLegacyStore = await loadMatrixLegacyCryptoInspector();
|
|
389
|
+
} catch (err) {
|
|
390
|
+
const message = formatMatrixErrorMessage(err);
|
|
391
|
+
if (!warnings.includes(message)) {
|
|
392
|
+
warnings.push(message);
|
|
393
|
+
}
|
|
394
|
+
if (warnings.length > 0) {
|
|
395
|
+
params.log?.warn?.(
|
|
396
|
+
`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`,
|
|
397
|
+
);
|
|
398
|
+
}
|
|
399
|
+
return {
|
|
400
|
+
migrated: false,
|
|
401
|
+
changes,
|
|
402
|
+
warnings,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
if (!inspectLegacyStore) {
|
|
407
|
+
return {
|
|
408
|
+
migrated: false,
|
|
409
|
+
changes,
|
|
410
|
+
warnings,
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
for (const plan of detection.plans) {
|
|
415
|
+
const existingState = loadLegacyCryptoMigrationState(plan.statePath);
|
|
416
|
+
if (existingState?.version === 1) {
|
|
417
|
+
continue;
|
|
418
|
+
}
|
|
419
|
+
if (!plan.deviceId) {
|
|
420
|
+
warnings.push(
|
|
421
|
+
`Legacy Matrix encrypted state detected at ${plan.legacyCryptoPath}, but no device ID was found for account "${plan.accountId}". ` +
|
|
422
|
+
`OpenClaw will continue, but old encrypted history cannot be recovered automatically.`,
|
|
423
|
+
);
|
|
424
|
+
continue;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
let summary: MatrixLegacyCryptoSummary;
|
|
428
|
+
try {
|
|
429
|
+
summary = await inspectLegacyStore({
|
|
430
|
+
cryptoRootDir: plan.legacyCryptoPath,
|
|
431
|
+
userId: plan.userId,
|
|
432
|
+
deviceId: plan.deviceId,
|
|
433
|
+
log: params.log?.info,
|
|
434
|
+
});
|
|
435
|
+
} catch (err) {
|
|
436
|
+
warnings.push(
|
|
437
|
+
`Failed inspecting legacy Matrix encrypted state for account "${plan.accountId}" (${plan.legacyCryptoPath}): ${String(err)}`,
|
|
438
|
+
);
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
let decryptionKeyImported = false;
|
|
443
|
+
if (summary.decryptionKeyBase64) {
|
|
444
|
+
const existingRecoveryKey = loadStoredRecoveryKey(plan.recoveryKeyPath);
|
|
445
|
+
if (
|
|
446
|
+
existingRecoveryKey?.privateKeyBase64 &&
|
|
447
|
+
existingRecoveryKey.privateKeyBase64 !== summary.decryptionKeyBase64
|
|
448
|
+
) {
|
|
449
|
+
warnings.push(
|
|
450
|
+
`Legacy Matrix backup key was found for account "${plan.accountId}", but ${plan.recoveryKeyPath} already contains a different recovery key. Leaving the existing file unchanged.`,
|
|
451
|
+
);
|
|
452
|
+
} else if (!existingRecoveryKey?.privateKeyBase64) {
|
|
453
|
+
const payload: MatrixStoredRecoveryKey = {
|
|
454
|
+
version: 1,
|
|
455
|
+
createdAt: new Date().toISOString(),
|
|
456
|
+
keyId: null,
|
|
457
|
+
privateKeyBase64: summary.decryptionKeyBase64,
|
|
458
|
+
};
|
|
459
|
+
try {
|
|
460
|
+
await writeJsonFileAtomically(plan.recoveryKeyPath, payload);
|
|
461
|
+
changes.push(
|
|
462
|
+
`Imported Matrix legacy backup key for account "${plan.accountId}": ${plan.recoveryKeyPath}`,
|
|
463
|
+
);
|
|
464
|
+
decryptionKeyImported = true;
|
|
465
|
+
} catch (err) {
|
|
466
|
+
warnings.push(
|
|
467
|
+
`Failed writing Matrix recovery key for account "${plan.accountId}" (${plan.recoveryKeyPath}): ${String(err)}`,
|
|
468
|
+
);
|
|
469
|
+
}
|
|
470
|
+
} else {
|
|
471
|
+
decryptionKeyImported = true;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
const localOnlyKeys =
|
|
476
|
+
summary.roomKeyCounts && summary.roomKeyCounts.total > summary.roomKeyCounts.backedUp
|
|
477
|
+
? summary.roomKeyCounts.total - summary.roomKeyCounts.backedUp
|
|
478
|
+
: 0;
|
|
479
|
+
if (localOnlyKeys > 0) {
|
|
480
|
+
warnings.push(
|
|
481
|
+
`Legacy Matrix encrypted state for account "${plan.accountId}" contains ${localOnlyKeys} room key(s) that were never backed up. ` +
|
|
482
|
+
"Backed-up keys can be restored automatically, but local-only encrypted history may remain unavailable after upgrade.",
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
if (!summary.decryptionKeyBase64 && (summary.roomKeyCounts?.backedUp ?? 0) > 0) {
|
|
486
|
+
warnings.push(
|
|
487
|
+
`Legacy Matrix encrypted state for account "${plan.accountId}" has backed-up room keys, but no local backup decryption key was found. ` +
|
|
488
|
+
`Ask the operator to run "openclaw matrix verify backup restore --recovery-key <key>" after upgrade if they have the recovery key.`,
|
|
489
|
+
);
|
|
490
|
+
}
|
|
491
|
+
if (!summary.decryptionKeyBase64 && (summary.roomKeyCounts?.total ?? 0) > 0) {
|
|
492
|
+
warnings.push(
|
|
493
|
+
`Legacy Matrix encrypted state for account "${plan.accountId}" cannot be fully converted automatically because the old rust crypto store does not expose all local room keys for export.`,
|
|
494
|
+
);
|
|
495
|
+
}
|
|
496
|
+
// If recovery-key persistence failed, leave the migration state absent so the next startup can retry.
|
|
497
|
+
if (
|
|
498
|
+
summary.decryptionKeyBase64 &&
|
|
499
|
+
!decryptionKeyImported &&
|
|
500
|
+
!loadStoredRecoveryKey(plan.recoveryKeyPath)
|
|
501
|
+
) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
const state: MatrixLegacyCryptoMigrationState = {
|
|
506
|
+
version: 1,
|
|
507
|
+
source: "matrix-bot-sdk-rust",
|
|
508
|
+
accountId: plan.accountId,
|
|
509
|
+
deviceId: summary.deviceId,
|
|
510
|
+
roomKeyCounts: summary.roomKeyCounts,
|
|
511
|
+
backupVersion: summary.backupVersion,
|
|
512
|
+
decryptionKeyImported,
|
|
513
|
+
restoreStatus: decryptionKeyImported ? "pending" : "manual-action-required",
|
|
514
|
+
detectedAt: new Date().toISOString(),
|
|
515
|
+
lastError: null,
|
|
516
|
+
};
|
|
517
|
+
try {
|
|
518
|
+
await persistLegacyMigrationState({
|
|
519
|
+
filePath: plan.statePath,
|
|
520
|
+
state,
|
|
521
|
+
writeJsonFileAtomically,
|
|
522
|
+
});
|
|
523
|
+
changes.push(
|
|
524
|
+
`Prepared Matrix legacy encrypted-state migration for account "${plan.accountId}": ${plan.statePath}`,
|
|
525
|
+
);
|
|
526
|
+
} catch (err) {
|
|
527
|
+
warnings.push(
|
|
528
|
+
`Failed writing Matrix legacy encrypted-state migration record for account "${plan.accountId}" (${plan.statePath}): ${String(err)}`,
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
if (changes.length > 0) {
|
|
534
|
+
params.log?.info?.(
|
|
535
|
+
`matrix: prepared encrypted-state upgrade.\n${changes.map((entry) => `- ${entry}`).join("\n")}`,
|
|
536
|
+
);
|
|
537
|
+
}
|
|
538
|
+
if (warnings.length > 0) {
|
|
539
|
+
params.log?.warn?.(
|
|
540
|
+
`matrix: legacy encrypted-state warnings:\n${warnings.map((entry) => `- ${entry}`).join("\n")}`,
|
|
541
|
+
);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
migrated: changes.length > 0,
|
|
546
|
+
changes,
|
|
547
|
+
warnings,
|
|
548
|
+
};
|
|
549
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
4
|
+
import { describe, expect, it } from "vitest";
|
|
5
|
+
import { withTempHome } from "../../../test/helpers/temp-home.js";
|
|
6
|
+
import { autoMigrateLegacyMatrixState, detectLegacyMatrixState } from "./legacy-state.js";
|
|
7
|
+
|
|
8
|
+
function writeFile(filePath: string, value: string) {
|
|
9
|
+
fs.mkdirSync(path.dirname(filePath), { recursive: true });
|
|
10
|
+
fs.writeFileSync(filePath, value, "utf-8");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
describe("matrix legacy state migration", () => {
|
|
14
|
+
it("migrates the flat legacy Matrix store into account-scoped storage", async () => {
|
|
15
|
+
await withTempHome(async (home) => {
|
|
16
|
+
const stateDir = path.join(home, ".openclaw");
|
|
17
|
+
writeFile(path.join(stateDir, "matrix", "bot-storage.json"), '{"next_batch":"s1"}');
|
|
18
|
+
writeFile(path.join(stateDir, "matrix", "crypto", "store.db"), "crypto");
|
|
19
|
+
|
|
20
|
+
const cfg: OpenClawConfig = {
|
|
21
|
+
channels: {
|
|
22
|
+
matrix: {
|
|
23
|
+
homeserver: "https://matrix.example.org",
|
|
24
|
+
userId: "@bot:example.org",
|
|
25
|
+
accessToken: "tok-123",
|
|
26
|
+
},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const detection = detectLegacyMatrixState({ cfg, env: process.env });
|
|
31
|
+
expect(detection && "warning" in detection).toBe(false);
|
|
32
|
+
if (!detection || "warning" in detection) {
|
|
33
|
+
throw new Error("expected a migratable Matrix legacy state plan");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const result = await autoMigrateLegacyMatrixState({ cfg, env: process.env });
|
|
37
|
+
expect(result.migrated).toBe(true);
|
|
38
|
+
expect(result.warnings).toEqual([]);
|
|
39
|
+
expect(fs.existsSync(path.join(stateDir, "matrix", "bot-storage.json"))).toBe(false);
|
|
40
|
+
expect(fs.existsSync(path.join(stateDir, "matrix", "crypto"))).toBe(false);
|
|
41
|
+
expect(fs.existsSync(detection.targetStoragePath)).toBe(true);
|
|
42
|
+
expect(fs.existsSync(path.join(detection.targetCryptoPath, "store.db"))).toBe(true);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("uses cached Matrix credentials when the config no longer stores an access token", async () => {
|
|
47
|
+
await withTempHome(async (home) => {
|
|
48
|
+
const stateDir = path.join(home, ".openclaw");
|
|
49
|
+
writeFile(path.join(stateDir, "matrix", "bot-storage.json"), '{"next_batch":"s1"}');
|
|
50
|
+
writeFile(
|
|
51
|
+
path.join(stateDir, "credentials", "matrix", "credentials.json"),
|
|
52
|
+
JSON.stringify(
|
|
53
|
+
{
|
|
54
|
+
homeserver: "https://matrix.example.org",
|
|
55
|
+
userId: "@bot:example.org",
|
|
56
|
+
accessToken: "tok-from-cache",
|
|
57
|
+
},
|
|
58
|
+
null,
|
|
59
|
+
2,
|
|
60
|
+
),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const cfg: OpenClawConfig = {
|
|
64
|
+
channels: {
|
|
65
|
+
matrix: {
|
|
66
|
+
homeserver: "https://matrix.example.org",
|
|
67
|
+
userId: "@bot:example.org",
|
|
68
|
+
password: "secret",
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const detection = detectLegacyMatrixState({ cfg, env: process.env });
|
|
74
|
+
expect(detection && "warning" in detection).toBe(false);
|
|
75
|
+
if (!detection || "warning" in detection) {
|
|
76
|
+
throw new Error("expected cached credentials to make Matrix migration resolvable");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
expect(detection.targetRootDir).toContain("matrix.example.org__bot_example.org");
|
|
80
|
+
|
|
81
|
+
const result = await autoMigrateLegacyMatrixState({ cfg, env: process.env });
|
|
82
|
+
expect(result.migrated).toBe(true);
|
|
83
|
+
expect(fs.existsSync(detection.targetStoragePath)).toBe(true);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
});
|