@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,283 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { indexedDB as fakeIndexedDB } from "fake-indexeddb";
|
|
4
|
+
import { withFileLock } from "openclaw/plugin-sdk/infra-runtime";
|
|
5
|
+
import { MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS } from "./idb-persistence-lock.js";
|
|
6
|
+
import { LogService } from "./logger.js";
|
|
7
|
+
|
|
8
|
+
// Advisory lock options for IDB snapshot file access. Without locking, the
|
|
9
|
+
// gateway's periodic 60-second persist cycle and CLI crypto commands (e.g.
|
|
10
|
+
// `openclaw matrix verify bootstrap`) can corrupt each other's state.
|
|
11
|
+
// Use a longer stale window than the generic 30s default because snapshot
|
|
12
|
+
// restore and large crypto-store dumps can legitimately hold the lock for
|
|
13
|
+
// longer, and reclaiming a live lock would reintroduce concurrent corruption.
|
|
14
|
+
type IdbStoreSnapshot = {
|
|
15
|
+
name: string;
|
|
16
|
+
keyPath: IDBObjectStoreParameters["keyPath"];
|
|
17
|
+
autoIncrement: boolean;
|
|
18
|
+
indexes: { name: string; keyPath: string | string[]; multiEntry: boolean; unique: boolean }[];
|
|
19
|
+
records: { key: IDBValidKey; value: unknown }[];
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
type IdbDatabaseSnapshot = {
|
|
23
|
+
name: string;
|
|
24
|
+
version: number;
|
|
25
|
+
stores: IdbStoreSnapshot[];
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function isValidIdbIndexSnapshot(value: unknown): value is IdbStoreSnapshot["indexes"][number] {
|
|
29
|
+
if (!value || typeof value !== "object") {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
const candidate = value as Partial<IdbStoreSnapshot["indexes"][number]>;
|
|
33
|
+
return (
|
|
34
|
+
typeof candidate.name === "string" &&
|
|
35
|
+
(typeof candidate.keyPath === "string" ||
|
|
36
|
+
(Array.isArray(candidate.keyPath) &&
|
|
37
|
+
candidate.keyPath.every((entry) => typeof entry === "string"))) &&
|
|
38
|
+
typeof candidate.multiEntry === "boolean" &&
|
|
39
|
+
typeof candidate.unique === "boolean"
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function isValidIdbRecordSnapshot(value: unknown): value is IdbStoreSnapshot["records"][number] {
|
|
44
|
+
if (!value || typeof value !== "object") {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
return "key" in value && "value" in value;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function isValidIdbStoreSnapshot(value: unknown): value is IdbStoreSnapshot {
|
|
51
|
+
if (!value || typeof value !== "object") {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
const candidate = value as Partial<IdbStoreSnapshot>;
|
|
55
|
+
const validKeyPath =
|
|
56
|
+
candidate.keyPath === null ||
|
|
57
|
+
typeof candidate.keyPath === "string" ||
|
|
58
|
+
(Array.isArray(candidate.keyPath) &&
|
|
59
|
+
candidate.keyPath.every((entry) => typeof entry === "string"));
|
|
60
|
+
return (
|
|
61
|
+
typeof candidate.name === "string" &&
|
|
62
|
+
validKeyPath &&
|
|
63
|
+
typeof candidate.autoIncrement === "boolean" &&
|
|
64
|
+
Array.isArray(candidate.indexes) &&
|
|
65
|
+
candidate.indexes.every((entry) => isValidIdbIndexSnapshot(entry)) &&
|
|
66
|
+
Array.isArray(candidate.records) &&
|
|
67
|
+
candidate.records.every((entry) => isValidIdbRecordSnapshot(entry))
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function isValidIdbDatabaseSnapshot(value: unknown): value is IdbDatabaseSnapshot {
|
|
72
|
+
if (!value || typeof value !== "object") {
|
|
73
|
+
return false;
|
|
74
|
+
}
|
|
75
|
+
const candidate = value as Partial<IdbDatabaseSnapshot>;
|
|
76
|
+
return (
|
|
77
|
+
typeof candidate.name === "string" &&
|
|
78
|
+
typeof candidate.version === "number" &&
|
|
79
|
+
Number.isFinite(candidate.version) &&
|
|
80
|
+
candidate.version > 0 &&
|
|
81
|
+
Array.isArray(candidate.stores) &&
|
|
82
|
+
candidate.stores.every((entry) => isValidIdbStoreSnapshot(entry))
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function parseSnapshotPayload(data: string): IdbDatabaseSnapshot[] | null {
|
|
87
|
+
const parsed = JSON.parse(data) as unknown;
|
|
88
|
+
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
if (!parsed.every((entry) => isValidIdbDatabaseSnapshot(entry))) {
|
|
92
|
+
throw new Error("Malformed IndexedDB snapshot payload");
|
|
93
|
+
}
|
|
94
|
+
return parsed;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function idbReq<T>(req: IDBRequest<T>): Promise<T> {
|
|
98
|
+
return new Promise((resolve, reject) => {
|
|
99
|
+
req.addEventListener("success", () => resolve(req.result), { once: true });
|
|
100
|
+
req.addEventListener("error", () => reject(req.error), { once: true });
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
async function dumpIndexedDatabases(databasePrefix?: string): Promise<IdbDatabaseSnapshot[]> {
|
|
105
|
+
const idb = fakeIndexedDB;
|
|
106
|
+
const dbList = await idb.databases();
|
|
107
|
+
const snapshot: IdbDatabaseSnapshot[] = [];
|
|
108
|
+
const expectedPrefix = databasePrefix ? `${databasePrefix}::` : null;
|
|
109
|
+
|
|
110
|
+
for (const { name, version } of dbList) {
|
|
111
|
+
if (!name || !version) {
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
if (expectedPrefix && !name.startsWith(expectedPrefix)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
const db: IDBDatabase = await new Promise((resolve, reject) => {
|
|
118
|
+
const r = idb.open(name, version);
|
|
119
|
+
r.addEventListener("success", () => resolve(r.result), { once: true });
|
|
120
|
+
r.addEventListener("error", () => reject(r.error), { once: true });
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const stores: IdbStoreSnapshot[] = [];
|
|
124
|
+
for (const storeName of db.objectStoreNames) {
|
|
125
|
+
const tx = db.transaction(storeName, "readonly");
|
|
126
|
+
const store = tx.objectStore(storeName);
|
|
127
|
+
const storeInfo: IdbStoreSnapshot = {
|
|
128
|
+
name: storeName,
|
|
129
|
+
keyPath: store.keyPath as IDBObjectStoreParameters["keyPath"],
|
|
130
|
+
autoIncrement: store.autoIncrement,
|
|
131
|
+
indexes: [],
|
|
132
|
+
records: [],
|
|
133
|
+
};
|
|
134
|
+
for (const idxName of store.indexNames) {
|
|
135
|
+
const idx = store.index(idxName);
|
|
136
|
+
storeInfo.indexes.push({
|
|
137
|
+
name: idxName,
|
|
138
|
+
keyPath: idx.keyPath,
|
|
139
|
+
multiEntry: idx.multiEntry,
|
|
140
|
+
unique: idx.unique,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
const keys = await idbReq(store.getAllKeys());
|
|
144
|
+
const values = await idbReq(store.getAll());
|
|
145
|
+
storeInfo.records = keys.map((k, i) => ({ key: k, value: values[i] }));
|
|
146
|
+
stores.push(storeInfo);
|
|
147
|
+
}
|
|
148
|
+
snapshot.push({ name, version, stores });
|
|
149
|
+
db.close();
|
|
150
|
+
}
|
|
151
|
+
return snapshot;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async function restoreIndexedDatabases(snapshot: IdbDatabaseSnapshot[]): Promise<void> {
|
|
155
|
+
const idb = fakeIndexedDB;
|
|
156
|
+
for (const dbSnap of snapshot) {
|
|
157
|
+
await new Promise<void>((resolve, reject) => {
|
|
158
|
+
const r = idb.open(dbSnap.name, dbSnap.version);
|
|
159
|
+
r.addEventListener("upgradeneeded", () => {
|
|
160
|
+
const db = r.result;
|
|
161
|
+
for (const storeSnap of dbSnap.stores) {
|
|
162
|
+
const opts: IDBObjectStoreParameters = {};
|
|
163
|
+
if (storeSnap.keyPath !== null) {
|
|
164
|
+
opts.keyPath = storeSnap.keyPath;
|
|
165
|
+
}
|
|
166
|
+
if (storeSnap.autoIncrement) {
|
|
167
|
+
opts.autoIncrement = true;
|
|
168
|
+
}
|
|
169
|
+
const store = db.createObjectStore(storeSnap.name, opts);
|
|
170
|
+
for (const idx of storeSnap.indexes) {
|
|
171
|
+
store.createIndex(idx.name, idx.keyPath, {
|
|
172
|
+
unique: idx.unique,
|
|
173
|
+
multiEntry: idx.multiEntry,
|
|
174
|
+
});
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
r.addEventListener(
|
|
179
|
+
"success",
|
|
180
|
+
() => {
|
|
181
|
+
void (async () => {
|
|
182
|
+
const db = r.result;
|
|
183
|
+
for (const storeSnap of dbSnap.stores) {
|
|
184
|
+
if (storeSnap.records.length === 0) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const tx = db.transaction(storeSnap.name, "readwrite");
|
|
188
|
+
const store = tx.objectStore(storeSnap.name);
|
|
189
|
+
for (const rec of storeSnap.records) {
|
|
190
|
+
if (storeSnap.keyPath !== null) {
|
|
191
|
+
store.put(rec.value);
|
|
192
|
+
} else {
|
|
193
|
+
store.put(rec.value, rec.key);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
await new Promise<void>((res) => {
|
|
197
|
+
tx.addEventListener("complete", () => res(), { once: true });
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
db.close();
|
|
201
|
+
resolve();
|
|
202
|
+
})().catch(reject);
|
|
203
|
+
},
|
|
204
|
+
{ once: true },
|
|
205
|
+
);
|
|
206
|
+
r.addEventListener("error", () => reject(r.error), { once: true });
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function resolveDefaultIdbSnapshotPath(): string {
|
|
212
|
+
const stateDir =
|
|
213
|
+
process.env.OPENCLAW_STATE_DIR || path.join(process.env.HOME || "/tmp", ".openclaw");
|
|
214
|
+
return path.join(stateDir, "matrix", "crypto-idb-snapshot.json");
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export async function restoreIdbFromDisk(snapshotPath?: string): Promise<boolean> {
|
|
218
|
+
const candidatePaths = snapshotPath ? [snapshotPath] : [resolveDefaultIdbSnapshotPath()];
|
|
219
|
+
for (const resolvedPath of candidatePaths) {
|
|
220
|
+
try {
|
|
221
|
+
const restored = await withFileLock(
|
|
222
|
+
resolvedPath,
|
|
223
|
+
MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS,
|
|
224
|
+
async () => {
|
|
225
|
+
const data = fs.readFileSync(resolvedPath, "utf8");
|
|
226
|
+
const snapshot = parseSnapshotPayload(data);
|
|
227
|
+
if (!snapshot) {
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
await restoreIndexedDatabases(snapshot);
|
|
231
|
+
LogService.info(
|
|
232
|
+
"IdbPersistence",
|
|
233
|
+
`Restored ${snapshot.length} IndexedDB database(s) from ${resolvedPath}`,
|
|
234
|
+
);
|
|
235
|
+
return true;
|
|
236
|
+
},
|
|
237
|
+
);
|
|
238
|
+
if (restored) {
|
|
239
|
+
return true;
|
|
240
|
+
}
|
|
241
|
+
} catch (err) {
|
|
242
|
+
LogService.warn(
|
|
243
|
+
"IdbPersistence",
|
|
244
|
+
`Failed to restore IndexedDB snapshot from ${resolvedPath}:`,
|
|
245
|
+
err,
|
|
246
|
+
);
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export async function persistIdbToDisk(params?: {
|
|
254
|
+
snapshotPath?: string;
|
|
255
|
+
databasePrefix?: string;
|
|
256
|
+
}): Promise<void> {
|
|
257
|
+
const snapshotPath = params?.snapshotPath ?? resolveDefaultIdbSnapshotPath();
|
|
258
|
+
try {
|
|
259
|
+
fs.mkdirSync(path.dirname(snapshotPath), { recursive: true });
|
|
260
|
+
const persistedCount = await withFileLock(
|
|
261
|
+
snapshotPath,
|
|
262
|
+
MATRIX_IDB_SNAPSHOT_LOCK_OPTIONS,
|
|
263
|
+
async () => {
|
|
264
|
+
const snapshot = await dumpIndexedDatabases(params?.databasePrefix);
|
|
265
|
+
if (snapshot.length === 0) {
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
fs.writeFileSync(snapshotPath, JSON.stringify(snapshot));
|
|
269
|
+
fs.chmodSync(snapshotPath, 0o600);
|
|
270
|
+
return snapshot.length;
|
|
271
|
+
},
|
|
272
|
+
);
|
|
273
|
+
if (persistedCount === 0) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
LogService.debug(
|
|
277
|
+
"IdbPersistence",
|
|
278
|
+
`Persisted ${persistedCount} IndexedDB database(s) to ${snapshotPath}`,
|
|
279
|
+
);
|
|
280
|
+
} catch (err) {
|
|
281
|
+
LogService.warn("IdbPersistence", "Failed to persist IndexedDB snapshot:", err);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import { ConsoleLogger, setMatrixConsoleLogging } from "./logger.js";
|
|
3
|
+
|
|
4
|
+
describe("ConsoleLogger", () => {
|
|
5
|
+
afterEach(() => {
|
|
6
|
+
setMatrixConsoleLogging(false);
|
|
7
|
+
vi.restoreAllMocks();
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it("redacts sensitive tokens in emitted log messages", () => {
|
|
11
|
+
setMatrixConsoleLogging(true);
|
|
12
|
+
const spy = vi.spyOn(console, "error").mockImplementation(() => {});
|
|
13
|
+
|
|
14
|
+
new ConsoleLogger().error(
|
|
15
|
+
"MatrixHttpClient",
|
|
16
|
+
"Authorization: Bearer 123456:abcdefghijklmnopqrstuvwxyzABCDEFG",
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
const message = spy.mock.calls[0]?.[0];
|
|
20
|
+
expect(typeof message).toBe("string");
|
|
21
|
+
expect(message).toContain("Authorization: Bearer");
|
|
22
|
+
expect(message).not.toContain("123456:abcdefghijklmnopqrstuvwxyzABCDEFG");
|
|
23
|
+
expect(message).toContain("***");
|
|
24
|
+
});
|
|
25
|
+
});
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { format } from "node:util";
|
|
2
|
+
import { redactSensitiveText } from "openclaw/plugin-sdk/logging-core";
|
|
3
|
+
import type { RuntimeLogger } from "openclaw/plugin-sdk/plugin-runtime";
|
|
4
|
+
import { getMatrixRuntime } from "../../runtime.js";
|
|
5
|
+
|
|
6
|
+
export type Logger = {
|
|
7
|
+
trace: (module: string, ...messageOrObject: unknown[]) => void;
|
|
8
|
+
debug: (module: string, ...messageOrObject: unknown[]) => void;
|
|
9
|
+
info: (module: string, ...messageOrObject: unknown[]) => void;
|
|
10
|
+
warn: (module: string, ...messageOrObject: unknown[]) => void;
|
|
11
|
+
error: (module: string, ...messageOrObject: unknown[]) => void;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function noop(): void {
|
|
15
|
+
// no-op
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
let forceConsoleLogging = false;
|
|
19
|
+
|
|
20
|
+
export function setMatrixConsoleLogging(enabled: boolean): void {
|
|
21
|
+
forceConsoleLogging = enabled;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function resolveRuntimeLogger(module: string): RuntimeLogger | null {
|
|
25
|
+
if (forceConsoleLogging) {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return getMatrixRuntime().logging.getChildLogger({ module: `matrix:${module}` });
|
|
30
|
+
} catch {
|
|
31
|
+
return null;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function formatMessage(module: string, messageOrObject: unknown[]): string {
|
|
36
|
+
if (messageOrObject.length === 0) {
|
|
37
|
+
return `[${module}]`;
|
|
38
|
+
}
|
|
39
|
+
return redactSensitiveText(`[${module}] ${format(...messageOrObject)}`);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export class ConsoleLogger {
|
|
43
|
+
private emit(
|
|
44
|
+
level: "debug" | "info" | "warn" | "error",
|
|
45
|
+
module: string,
|
|
46
|
+
...messageOrObject: unknown[]
|
|
47
|
+
): void {
|
|
48
|
+
const runtimeLogger = resolveRuntimeLogger(module);
|
|
49
|
+
const message = formatMessage(module, messageOrObject);
|
|
50
|
+
if (runtimeLogger) {
|
|
51
|
+
if (level === "debug") {
|
|
52
|
+
runtimeLogger.debug?.(message);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
runtimeLogger[level](message);
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
if (level === "debug") {
|
|
59
|
+
console.debug(message);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
console[level](message);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
trace(module: string, ...messageOrObject: unknown[]): void {
|
|
66
|
+
this.emit("debug", module, ...messageOrObject);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
debug(module: string, ...messageOrObject: unknown[]): void {
|
|
70
|
+
this.emit("debug", module, ...messageOrObject);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
info(module: string, ...messageOrObject: unknown[]): void {
|
|
74
|
+
this.emit("info", module, ...messageOrObject);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
warn(module: string, ...messageOrObject: unknown[]): void {
|
|
78
|
+
this.emit("warn", module, ...messageOrObject);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
error(module: string, ...messageOrObject: unknown[]): void {
|
|
82
|
+
this.emit("error", module, ...messageOrObject);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const defaultLogger = new ConsoleLogger();
|
|
87
|
+
let activeLogger: Logger = defaultLogger;
|
|
88
|
+
|
|
89
|
+
export const LogService = {
|
|
90
|
+
setLogger(logger: Logger): void {
|
|
91
|
+
activeLogger = logger;
|
|
92
|
+
},
|
|
93
|
+
trace(module: string, ...messageOrObject: unknown[]): void {
|
|
94
|
+
activeLogger.trace(module, ...messageOrObject);
|
|
95
|
+
},
|
|
96
|
+
debug(module: string, ...messageOrObject: unknown[]): void {
|
|
97
|
+
activeLogger.debug(module, ...messageOrObject);
|
|
98
|
+
},
|
|
99
|
+
info(module: string, ...messageOrObject: unknown[]): void {
|
|
100
|
+
activeLogger.info(module, ...messageOrObject);
|
|
101
|
+
},
|
|
102
|
+
warn(module: string, ...messageOrObject: unknown[]): void {
|
|
103
|
+
activeLogger.warn(module, ...messageOrObject);
|
|
104
|
+
},
|
|
105
|
+
error(module: string, ...messageOrObject: unknown[]): void {
|
|
106
|
+
activeLogger.error(module, ...messageOrObject);
|
|
107
|
+
},
|
|
108
|
+
};
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readResponseWithLimit as readSharedResponseWithLimit } from "openclaw/plugin-sdk/media-runtime";
|
|
2
|
+
|
|
3
|
+
export async function readResponseWithLimit(
|
|
4
|
+
res: Response,
|
|
5
|
+
maxBytes: number,
|
|
6
|
+
opts?: {
|
|
7
|
+
onOverflow?: (params: { size: number; maxBytes: number; res: Response }) => Error;
|
|
8
|
+
chunkTimeoutMs?: number;
|
|
9
|
+
onIdleTimeout?: (params: { chunkTimeoutMs: number }) => Error;
|
|
10
|
+
},
|
|
11
|
+
): Promise<Buffer> {
|
|
12
|
+
return await readSharedResponseWithLimit(res, maxBytes, {
|
|
13
|
+
...opts,
|
|
14
|
+
onIdleTimeout:
|
|
15
|
+
opts?.onIdleTimeout ??
|
|
16
|
+
(({ chunkTimeoutMs }) =>
|
|
17
|
+
new Error(`Matrix media download stalled: no data received for ${chunkTimeoutMs}ms`)),
|
|
18
|
+
});
|
|
19
|
+
}
|