@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,290 @@
|
|
|
1
|
+
import { resolveApprovalApprovers } from "openclaw/plugin-sdk/approval-auth-runtime";
|
|
2
|
+
import {
|
|
3
|
+
createChannelExecApprovalProfile,
|
|
4
|
+
getExecApprovalReplyMetadata,
|
|
5
|
+
isChannelExecApprovalClientEnabledFromConfig,
|
|
6
|
+
isChannelExecApprovalTargetRecipient,
|
|
7
|
+
matchesApprovalRequestFilters,
|
|
8
|
+
} from "openclaw/plugin-sdk/approval-client-runtime";
|
|
9
|
+
import { resolveApprovalRequestChannelAccountId } from "openclaw/plugin-sdk/approval-native-runtime";
|
|
10
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
11
|
+
import type { ExecApprovalRequest, PluginApprovalRequest } from "openclaw/plugin-sdk/infra-runtime";
|
|
12
|
+
import type { ReplyPayload } from "openclaw/plugin-sdk/reply-runtime";
|
|
13
|
+
import { normalizeAccountId } from "openclaw/plugin-sdk/routing";
|
|
14
|
+
import { normalizeLowercaseStringOrEmpty } from "openclaw/plugin-sdk/text-runtime";
|
|
15
|
+
import { getMatrixApprovalAuthApprovers } from "./approval-auth.js";
|
|
16
|
+
import { normalizeMatrixApproverId } from "./approval-ids.js";
|
|
17
|
+
import { listMatrixAccountIds, resolveMatrixAccount } from "./matrix/accounts.js";
|
|
18
|
+
import type { CoreConfig } from "./types.js";
|
|
19
|
+
|
|
20
|
+
type ApprovalRequest = ExecApprovalRequest | PluginApprovalRequest;
|
|
21
|
+
type ApprovalKind = "exec" | "plugin";
|
|
22
|
+
|
|
23
|
+
export { normalizeMatrixApproverId };
|
|
24
|
+
|
|
25
|
+
function normalizeMatrixExecApproverId(value: string | number): string | undefined {
|
|
26
|
+
const normalized = normalizeMatrixApproverId(value);
|
|
27
|
+
return normalized === "*" ? undefined : normalized;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveMatrixExecApprovalConfig(params: {
|
|
31
|
+
cfg: OpenClawConfig;
|
|
32
|
+
accountId?: string | null;
|
|
33
|
+
}) {
|
|
34
|
+
const account = resolveMatrixAccount(params);
|
|
35
|
+
const config = account.config.execApprovals;
|
|
36
|
+
if (!config) {
|
|
37
|
+
return undefined;
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
...config,
|
|
41
|
+
enabled: account.enabled && account.configured ? config.enabled : false,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function countMatrixExecApprovalEligibleAccounts(params: {
|
|
46
|
+
cfg: OpenClawConfig;
|
|
47
|
+
request: ApprovalRequest;
|
|
48
|
+
approvalKind: ApprovalKind;
|
|
49
|
+
}): number {
|
|
50
|
+
return listMatrixAccountIds(params.cfg).filter((accountId) => {
|
|
51
|
+
const account = resolveMatrixAccount({ cfg: params.cfg, accountId });
|
|
52
|
+
if (!account.enabled || !account.configured) {
|
|
53
|
+
return false;
|
|
54
|
+
}
|
|
55
|
+
const config = resolveMatrixExecApprovalConfig({
|
|
56
|
+
cfg: params.cfg,
|
|
57
|
+
accountId,
|
|
58
|
+
});
|
|
59
|
+
const filters = config?.enabled
|
|
60
|
+
? {
|
|
61
|
+
agentFilter: config.agentFilter,
|
|
62
|
+
sessionFilter: config.sessionFilter,
|
|
63
|
+
}
|
|
64
|
+
: {
|
|
65
|
+
agentFilter: undefined,
|
|
66
|
+
sessionFilter: undefined,
|
|
67
|
+
};
|
|
68
|
+
return (
|
|
69
|
+
isChannelExecApprovalClientEnabledFromConfig({
|
|
70
|
+
enabled: config?.enabled,
|
|
71
|
+
approverCount: getMatrixApprovalApprovers({
|
|
72
|
+
cfg: params.cfg,
|
|
73
|
+
accountId,
|
|
74
|
+
approvalKind: params.approvalKind,
|
|
75
|
+
}).length,
|
|
76
|
+
}) &&
|
|
77
|
+
matchesApprovalRequestFilters({
|
|
78
|
+
request: params.request.request,
|
|
79
|
+
agentFilter: filters.agentFilter,
|
|
80
|
+
sessionFilter: filters.sessionFilter,
|
|
81
|
+
})
|
|
82
|
+
);
|
|
83
|
+
}).length;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function matchesMatrixRequestAccount(params: {
|
|
87
|
+
cfg: OpenClawConfig;
|
|
88
|
+
accountId?: string | null;
|
|
89
|
+
request: ApprovalRequest;
|
|
90
|
+
approvalKind: ApprovalKind;
|
|
91
|
+
}): boolean {
|
|
92
|
+
const turnSourceChannel = normalizeLowercaseStringOrEmpty(
|
|
93
|
+
params.request.request.turnSourceChannel,
|
|
94
|
+
);
|
|
95
|
+
const boundAccountId = resolveApprovalRequestChannelAccountId({
|
|
96
|
+
cfg: params.cfg,
|
|
97
|
+
request: params.request,
|
|
98
|
+
channel: "matrix",
|
|
99
|
+
});
|
|
100
|
+
if (turnSourceChannel && turnSourceChannel !== "matrix" && !boundAccountId) {
|
|
101
|
+
return (
|
|
102
|
+
countMatrixExecApprovalEligibleAccounts({
|
|
103
|
+
cfg: params.cfg,
|
|
104
|
+
request: params.request,
|
|
105
|
+
approvalKind: params.approvalKind,
|
|
106
|
+
}) <= 1
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
return (
|
|
110
|
+
!boundAccountId ||
|
|
111
|
+
!params.accountId ||
|
|
112
|
+
normalizeAccountId(boundAccountId) === normalizeAccountId(params.accountId)
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function getMatrixExecApprovalApprovers(params: {
|
|
117
|
+
cfg: OpenClawConfig;
|
|
118
|
+
accountId?: string | null;
|
|
119
|
+
}): string[] {
|
|
120
|
+
const account = resolveMatrixAccount(params).config;
|
|
121
|
+
return resolveApprovalApprovers({
|
|
122
|
+
explicit: account.execApprovals?.approvers,
|
|
123
|
+
allowFrom: account.dm?.allowFrom,
|
|
124
|
+
normalizeApprover: normalizeMatrixExecApproverId,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function resolveMatrixApprovalKind(request: ApprovalRequest): ApprovalKind {
|
|
129
|
+
return request.id.startsWith("plugin:") ? "plugin" : "exec";
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export function getMatrixApprovalApprovers(params: {
|
|
133
|
+
cfg: OpenClawConfig;
|
|
134
|
+
accountId?: string | null;
|
|
135
|
+
approvalKind: ApprovalKind;
|
|
136
|
+
}): string[] {
|
|
137
|
+
if (params.approvalKind === "plugin") {
|
|
138
|
+
return getMatrixApprovalAuthApprovers({
|
|
139
|
+
cfg: params.cfg as CoreConfig,
|
|
140
|
+
accountId: params.accountId,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return getMatrixExecApprovalApprovers(params);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export function isMatrixExecApprovalTargetRecipient(params: {
|
|
147
|
+
cfg: OpenClawConfig;
|
|
148
|
+
senderId?: string | null;
|
|
149
|
+
accountId?: string | null;
|
|
150
|
+
}): boolean {
|
|
151
|
+
return isChannelExecApprovalTargetRecipient({
|
|
152
|
+
...params,
|
|
153
|
+
channel: "matrix",
|
|
154
|
+
normalizeSenderId: normalizeMatrixApproverId,
|
|
155
|
+
matchTarget: ({ target, normalizedSenderId }) =>
|
|
156
|
+
normalizeMatrixApproverId(target.to) === normalizedSenderId,
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const matrixExecApprovalProfile = createChannelExecApprovalProfile({
|
|
161
|
+
resolveConfig: resolveMatrixExecApprovalConfig,
|
|
162
|
+
resolveApprovers: getMatrixExecApprovalApprovers,
|
|
163
|
+
normalizeSenderId: normalizeMatrixApproverId,
|
|
164
|
+
isTargetRecipient: isMatrixExecApprovalTargetRecipient,
|
|
165
|
+
matchesRequestAccount: (params) =>
|
|
166
|
+
matchesMatrixRequestAccount({
|
|
167
|
+
...params,
|
|
168
|
+
approvalKind: "exec",
|
|
169
|
+
}),
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
export const isMatrixExecApprovalClientEnabled = matrixExecApprovalProfile.isClientEnabled;
|
|
173
|
+
export const isMatrixExecApprovalApprover = matrixExecApprovalProfile.isApprover;
|
|
174
|
+
export const isMatrixExecApprovalAuthorizedSender = matrixExecApprovalProfile.isAuthorizedSender;
|
|
175
|
+
export const resolveMatrixExecApprovalTarget = matrixExecApprovalProfile.resolveTarget;
|
|
176
|
+
export const shouldHandleMatrixExecApprovalRequest = matrixExecApprovalProfile.shouldHandleRequest;
|
|
177
|
+
|
|
178
|
+
export function isMatrixApprovalClientEnabled(params: {
|
|
179
|
+
cfg: OpenClawConfig;
|
|
180
|
+
accountId?: string | null;
|
|
181
|
+
approvalKind: ApprovalKind;
|
|
182
|
+
}): boolean {
|
|
183
|
+
if (params.approvalKind === "exec") {
|
|
184
|
+
return isMatrixExecApprovalClientEnabled(params);
|
|
185
|
+
}
|
|
186
|
+
const config = resolveMatrixExecApprovalConfig(params);
|
|
187
|
+
return isChannelExecApprovalClientEnabledFromConfig({
|
|
188
|
+
enabled: config?.enabled,
|
|
189
|
+
approverCount: getMatrixApprovalApprovers(params).length,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
export function isMatrixAnyApprovalClientEnabled(params: {
|
|
194
|
+
cfg: OpenClawConfig;
|
|
195
|
+
accountId?: string | null;
|
|
196
|
+
}): boolean {
|
|
197
|
+
return (
|
|
198
|
+
isMatrixApprovalClientEnabled({
|
|
199
|
+
...params,
|
|
200
|
+
approvalKind: "exec",
|
|
201
|
+
}) ||
|
|
202
|
+
isMatrixApprovalClientEnabled({
|
|
203
|
+
...params,
|
|
204
|
+
approvalKind: "plugin",
|
|
205
|
+
})
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function shouldHandleMatrixApprovalRequest(params: {
|
|
210
|
+
cfg: OpenClawConfig;
|
|
211
|
+
accountId?: string | null;
|
|
212
|
+
request: ApprovalRequest;
|
|
213
|
+
}): boolean {
|
|
214
|
+
const approvalKind = resolveMatrixApprovalKind(params.request);
|
|
215
|
+
if (
|
|
216
|
+
!matchesMatrixRequestAccount({
|
|
217
|
+
...params,
|
|
218
|
+
approvalKind,
|
|
219
|
+
})
|
|
220
|
+
) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
const config = resolveMatrixExecApprovalConfig(params);
|
|
224
|
+
if (
|
|
225
|
+
!isChannelExecApprovalClientEnabledFromConfig({
|
|
226
|
+
enabled: config?.enabled,
|
|
227
|
+
approverCount: getMatrixApprovalApprovers({
|
|
228
|
+
...params,
|
|
229
|
+
approvalKind,
|
|
230
|
+
}).length,
|
|
231
|
+
})
|
|
232
|
+
) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
return matchesApprovalRequestFilters({
|
|
236
|
+
request: params.request.request,
|
|
237
|
+
agentFilter: config?.agentFilter,
|
|
238
|
+
sessionFilter: config?.sessionFilter,
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function buildFilterCheckRequest(params: {
|
|
243
|
+
metadata: NonNullable<ReturnType<typeof getExecApprovalReplyMetadata>>;
|
|
244
|
+
}): ApprovalRequest {
|
|
245
|
+
if (params.metadata.approvalKind === "plugin") {
|
|
246
|
+
return {
|
|
247
|
+
id: params.metadata.approvalId,
|
|
248
|
+
request: {
|
|
249
|
+
title: "Plugin Approval Required",
|
|
250
|
+
description: "",
|
|
251
|
+
agentId: params.metadata.agentId ?? null,
|
|
252
|
+
sessionKey: params.metadata.sessionKey ?? null,
|
|
253
|
+
},
|
|
254
|
+
createdAtMs: 0,
|
|
255
|
+
expiresAtMs: 0,
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
return {
|
|
259
|
+
id: params.metadata.approvalId,
|
|
260
|
+
request: {
|
|
261
|
+
command: "",
|
|
262
|
+
agentId: params.metadata.agentId ?? null,
|
|
263
|
+
sessionKey: params.metadata.sessionKey ?? null,
|
|
264
|
+
},
|
|
265
|
+
createdAtMs: 0,
|
|
266
|
+
expiresAtMs: 0,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function shouldSuppressLocalMatrixExecApprovalPrompt(params: {
|
|
271
|
+
cfg: OpenClawConfig;
|
|
272
|
+
accountId?: string | null;
|
|
273
|
+
payload: ReplyPayload;
|
|
274
|
+
}): boolean {
|
|
275
|
+
if (!matrixExecApprovalProfile.shouldSuppressLocalPrompt(params)) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
const metadata = getExecApprovalReplyMetadata(params.payload);
|
|
279
|
+
if (!metadata) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
const request = buildFilterCheckRequest({
|
|
283
|
+
metadata,
|
|
284
|
+
});
|
|
285
|
+
return shouldHandleMatrixApprovalRequest({
|
|
286
|
+
cfg: params.cfg,
|
|
287
|
+
accountId: params.accountId,
|
|
288
|
+
request,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { resolveMatrixAccountConfig } from "./matrix/accounts.js";
|
|
2
|
+
import { resolveMatrixRoomConfig } from "./matrix/monitor/rooms.js";
|
|
3
|
+
import { normalizeMatrixResolvableTarget } from "./matrix/target-ids.js";
|
|
4
|
+
import type { ChannelGroupContext, GroupToolPolicyConfig } from "./runtime-api.js";
|
|
5
|
+
import type { CoreConfig } from "./types.js";
|
|
6
|
+
|
|
7
|
+
function resolveMatrixRoomConfigForGroup(params: ChannelGroupContext) {
|
|
8
|
+
const roomId = normalizeMatrixResolvableTarget(params.groupId?.trim() ?? "");
|
|
9
|
+
const groupChannel = params.groupChannel?.trim() ?? "";
|
|
10
|
+
const aliases = groupChannel ? [normalizeMatrixResolvableTarget(groupChannel)] : [];
|
|
11
|
+
const cfg = params.cfg as CoreConfig;
|
|
12
|
+
const matrixConfig = resolveMatrixAccountConfig({ cfg, accountId: params.accountId });
|
|
13
|
+
return resolveMatrixRoomConfig({
|
|
14
|
+
rooms: matrixConfig.groups ?? matrixConfig.rooms,
|
|
15
|
+
roomId,
|
|
16
|
+
aliases,
|
|
17
|
+
}).config;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function resolveMatrixGroupRequireMention(params: ChannelGroupContext): boolean {
|
|
21
|
+
const resolved = resolveMatrixRoomConfigForGroup(params);
|
|
22
|
+
if (resolved) {
|
|
23
|
+
if (resolved.autoReply === true) {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
if (resolved.autoReply === false) {
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
if (typeof resolved.requireMention === "boolean") {
|
|
30
|
+
return resolved.requireMention;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function resolveMatrixGroupToolPolicy(
|
|
37
|
+
params: ChannelGroupContext,
|
|
38
|
+
): GroupToolPolicyConfig | undefined {
|
|
39
|
+
const resolved = resolveMatrixRoomConfigForGroup(params);
|
|
40
|
+
return resolved?.tools;
|
|
41
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const availabilityState = vi.hoisted(() => ({
|
|
4
|
+
currentFilePath: "/virtual/dist/matrix-migration.runtime.js",
|
|
5
|
+
existingPaths: new Set<string>(),
|
|
6
|
+
dirEntries: [] as Array<{ name: string; isFile: () => boolean }>,
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
vi.mock("node:fs", async () => {
|
|
10
|
+
const { mockNodeBuiltinModule } = await import("../../../test/helpers/node-builtin-mocks.js");
|
|
11
|
+
return mockNodeBuiltinModule(
|
|
12
|
+
() => vi.importActual<typeof import("node:fs")>("node:fs"),
|
|
13
|
+
{
|
|
14
|
+
existsSync: (candidate: unknown) => availabilityState.existingPaths.has(String(candidate)),
|
|
15
|
+
readdirSync: () => availabilityState.dirEntries as never,
|
|
16
|
+
},
|
|
17
|
+
{ mirrorToDefault: true },
|
|
18
|
+
);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
vi.mock("node:url", async () => {
|
|
22
|
+
const actual = await vi.importActual<typeof import("node:url")>("node:url");
|
|
23
|
+
return {
|
|
24
|
+
...actual,
|
|
25
|
+
fileURLToPath: () => availabilityState.currentFilePath,
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const { isMatrixLegacyCryptoInspectorAvailable } =
|
|
30
|
+
await import("./legacy-crypto-inspector-availability.js");
|
|
31
|
+
|
|
32
|
+
describe("isMatrixLegacyCryptoInspectorAvailable", () => {
|
|
33
|
+
beforeEach(() => {
|
|
34
|
+
availabilityState.currentFilePath = "/virtual/dist/matrix-migration.runtime.js";
|
|
35
|
+
availabilityState.existingPaths.clear();
|
|
36
|
+
availabilityState.dirEntries = [];
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
it("detects the source inspector module directly", () => {
|
|
40
|
+
availabilityState.currentFilePath =
|
|
41
|
+
"/virtual/extensions/matrix/src/legacy-crypto-inspector-availability.js";
|
|
42
|
+
availabilityState.existingPaths.add(
|
|
43
|
+
"/virtual/extensions/matrix/src/matrix/legacy-crypto-inspector.ts",
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
expect(isMatrixLegacyCryptoInspectorAvailable()).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("detects hashed built inspector chunks", () => {
|
|
50
|
+
availabilityState.dirEntries = [
|
|
51
|
+
{
|
|
52
|
+
name: "legacy-crypto-inspector-TPlLnFSE.js",
|
|
53
|
+
isFile: () => true,
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
expect(isMatrixLegacyCryptoInspectorAvailable()).toBe(true);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("does not confuse the availability helper artifact with the real inspector", () => {
|
|
61
|
+
availabilityState.dirEntries = [
|
|
62
|
+
{
|
|
63
|
+
name: "legacy-crypto-inspector-availability.js",
|
|
64
|
+
isFile: () => true,
|
|
65
|
+
},
|
|
66
|
+
];
|
|
67
|
+
|
|
68
|
+
expect(isMatrixLegacyCryptoInspectorAvailable()).toBe(false);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("does not confuse hashed availability helper chunks with the real inspector", () => {
|
|
72
|
+
availabilityState.dirEntries = [
|
|
73
|
+
{
|
|
74
|
+
name: "legacy-crypto-inspector-availability-TPlLnFSE.js",
|
|
75
|
+
isFile: () => true,
|
|
76
|
+
},
|
|
77
|
+
];
|
|
78
|
+
|
|
79
|
+
expect(isMatrixLegacyCryptoInspectorAvailable()).toBe(false);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
const LEGACY_CRYPTO_INSPECTOR_FILE = "legacy-crypto-inspector.js";
|
|
6
|
+
const LEGACY_CRYPTO_INSPECTOR_CHUNK_PREFIX = "legacy-crypto-inspector-";
|
|
7
|
+
const LEGACY_CRYPTO_INSPECTOR_HELPER_CHUNK_PREFIX = "availability-";
|
|
8
|
+
const JAVASCRIPT_MODULE_SUFFIX = ".js";
|
|
9
|
+
|
|
10
|
+
function isLegacyCryptoInspectorArtifactName(name: string): boolean {
|
|
11
|
+
if (name === LEGACY_CRYPTO_INSPECTOR_FILE) {
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
if (
|
|
15
|
+
!name.startsWith(LEGACY_CRYPTO_INSPECTOR_CHUNK_PREFIX) ||
|
|
16
|
+
!name.endsWith(JAVASCRIPT_MODULE_SUFFIX)
|
|
17
|
+
) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
const chunkSuffix = name.slice(
|
|
21
|
+
LEGACY_CRYPTO_INSPECTOR_CHUNK_PREFIX.length,
|
|
22
|
+
-JAVASCRIPT_MODULE_SUFFIX.length,
|
|
23
|
+
);
|
|
24
|
+
return (
|
|
25
|
+
chunkSuffix.length > 0 &&
|
|
26
|
+
chunkSuffix !== "availability" &&
|
|
27
|
+
!chunkSuffix.startsWith(LEGACY_CRYPTO_INSPECTOR_HELPER_CHUNK_PREFIX)
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function hasSourceInspectorArtifact(currentDir: string): boolean {
|
|
32
|
+
return [
|
|
33
|
+
path.resolve(currentDir, "matrix", "legacy-crypto-inspector.ts"),
|
|
34
|
+
path.resolve(currentDir, "matrix", "legacy-crypto-inspector.js"),
|
|
35
|
+
].some((candidate) => fs.existsSync(candidate));
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function hasBuiltInspectorArtifact(currentDir: string): boolean {
|
|
39
|
+
if (fs.existsSync(path.join(currentDir, "legacy-crypto-inspector.js"))) {
|
|
40
|
+
return true;
|
|
41
|
+
}
|
|
42
|
+
if (fs.existsSync(path.join(currentDir, "extensions", "matrix", "legacy-crypto-inspector.js"))) {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
return fs
|
|
46
|
+
.readdirSync(currentDir, { withFileTypes: true })
|
|
47
|
+
.some((entry) => entry.isFile() && isLegacyCryptoInspectorArtifactName(entry.name));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function isMatrixLegacyCryptoInspectorAvailable(): boolean {
|
|
51
|
+
const currentDir = path.dirname(fileURLToPath(import.meta.url));
|
|
52
|
+
if (hasSourceInspectorArtifact(currentDir)) {
|
|
53
|
+
return true;
|
|
54
|
+
}
|
|
55
|
+
try {
|
|
56
|
+
return hasBuiltInspectorArtifact(currentDir);
|
|
57
|
+
} catch {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
|
|
4
|
+
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
5
|
+
import { withTempHome } from "../../../test/helpers/temp-home.js";
|
|
6
|
+
|
|
7
|
+
const legacyCryptoInspectorAvailability = vi.hoisted(() => ({
|
|
8
|
+
available: true,
|
|
9
|
+
}));
|
|
10
|
+
|
|
11
|
+
vi.mock("./legacy-crypto-inspector-availability.js", () => ({
|
|
12
|
+
isMatrixLegacyCryptoInspectorAvailable: () => legacyCryptoInspectorAvailability.available,
|
|
13
|
+
}));
|
|
14
|
+
|
|
15
|
+
import { autoPrepareLegacyMatrixCrypto, detectLegacyMatrixCrypto } from "./legacy-crypto.js";
|
|
16
|
+
import { resolveMatrixAccountStorageRoot } from "./storage-paths.js";
|
|
17
|
+
import {
|
|
18
|
+
MATRIX_DEFAULT_ACCESS_TOKEN,
|
|
19
|
+
MATRIX_DEFAULT_DEVICE_ID,
|
|
20
|
+
MATRIX_DEFAULT_USER_ID,
|
|
21
|
+
LOBI_OPS_ACCESS_TOKEN,
|
|
22
|
+
MATRIX_OPS_ACCOUNT_ID,
|
|
23
|
+
LOBI_OPS_DEVICE_ID,
|
|
24
|
+
MATRIX_OPS_USER_ID,
|
|
25
|
+
MATRIX_TEST_HOMESERVER,
|
|
26
|
+
writeFile,
|
|
27
|
+
writeMatrixCredentials,
|
|
28
|
+
} from "./test-helpers.js";
|
|
29
|
+
|
|
30
|
+
function createDefaultMatrixConfig(): OpenClawConfig {
|
|
31
|
+
return {
|
|
32
|
+
channels: {
|
|
33
|
+
matrix: {
|
|
34
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
35
|
+
userId: MATRIX_DEFAULT_USER_ID,
|
|
36
|
+
accessToken: MATRIX_DEFAULT_ACCESS_TOKEN,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function writeDefaultLegacyCryptoFixture(home: string) {
|
|
43
|
+
const stateDir = path.join(home, ".openclaw");
|
|
44
|
+
const cfg = createDefaultMatrixConfig();
|
|
45
|
+
const { rootDir } = resolveMatrixAccountStorageRoot({
|
|
46
|
+
stateDir,
|
|
47
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
48
|
+
userId: MATRIX_DEFAULT_USER_ID,
|
|
49
|
+
accessToken: MATRIX_DEFAULT_ACCESS_TOKEN,
|
|
50
|
+
});
|
|
51
|
+
writeFile(
|
|
52
|
+
path.join(rootDir, "crypto", "bot-sdk.json"),
|
|
53
|
+
JSON.stringify({ deviceId: MATRIX_DEFAULT_DEVICE_ID }),
|
|
54
|
+
);
|
|
55
|
+
return { cfg, rootDir };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function createOpsLegacyCryptoFixture(params: {
|
|
59
|
+
home: string;
|
|
60
|
+
accessToken?: string;
|
|
61
|
+
includeStoredCredentials?: boolean;
|
|
62
|
+
}) {
|
|
63
|
+
const stateDir = path.join(params.home, ".openclaw");
|
|
64
|
+
writeFile(
|
|
65
|
+
path.join(stateDir, "matrix", "crypto", "bot-sdk.json"),
|
|
66
|
+
JSON.stringify({ deviceId: LOBI_OPS_DEVICE_ID }),
|
|
67
|
+
);
|
|
68
|
+
if (params.includeStoredCredentials) {
|
|
69
|
+
writeMatrixCredentials(stateDir, {
|
|
70
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
71
|
+
accessToken: params.accessToken ?? LOBI_OPS_ACCESS_TOKEN,
|
|
72
|
+
deviceId: LOBI_OPS_DEVICE_ID,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
const { rootDir } = resolveMatrixAccountStorageRoot({
|
|
76
|
+
stateDir,
|
|
77
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
78
|
+
userId: MATRIX_OPS_USER_ID,
|
|
79
|
+
accessToken: params.accessToken ?? LOBI_OPS_ACCESS_TOKEN,
|
|
80
|
+
accountId: MATRIX_OPS_ACCOUNT_ID,
|
|
81
|
+
});
|
|
82
|
+
return { rootDir };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
describe("matrix legacy encrypted-state migration", () => {
|
|
86
|
+
afterEach(() => {
|
|
87
|
+
legacyCryptoInspectorAvailability.available = true;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it("extracts a saved backup key into the new recovery-key path", async () => {
|
|
91
|
+
await withTempHome(async (home) => {
|
|
92
|
+
const { cfg, rootDir } = writeDefaultLegacyCryptoFixture(home);
|
|
93
|
+
|
|
94
|
+
const detection = detectLegacyMatrixCrypto({ cfg, env: process.env });
|
|
95
|
+
expect(detection.inspectorAvailable).toBe(true);
|
|
96
|
+
expect(detection.warnings).toEqual([]);
|
|
97
|
+
expect(detection.plans).toHaveLength(1);
|
|
98
|
+
|
|
99
|
+
const result = await autoPrepareLegacyMatrixCrypto({
|
|
100
|
+
cfg,
|
|
101
|
+
env: process.env,
|
|
102
|
+
deps: {
|
|
103
|
+
inspectLegacyStore: async () => ({
|
|
104
|
+
deviceId: MATRIX_DEFAULT_DEVICE_ID,
|
|
105
|
+
roomKeyCounts: { total: 12, backedUp: 12 },
|
|
106
|
+
backupVersion: "1",
|
|
107
|
+
decryptionKeyBase64: "YWJjZA==",
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(result.migrated).toBe(true);
|
|
113
|
+
expect(result.warnings).toEqual([]);
|
|
114
|
+
|
|
115
|
+
const recovery = JSON.parse(
|
|
116
|
+
fs.readFileSync(path.join(rootDir, "recovery-key.json"), "utf8"),
|
|
117
|
+
) as {
|
|
118
|
+
privateKeyBase64: string;
|
|
119
|
+
};
|
|
120
|
+
expect(recovery.privateKeyBase64).toBe("YWJjZA==");
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it("skips migration when no legacy Matrix plans exist", async () => {
|
|
125
|
+
await withTempHome(async () => {
|
|
126
|
+
const result = await autoPrepareLegacyMatrixCrypto({
|
|
127
|
+
cfg: createDefaultMatrixConfig(),
|
|
128
|
+
env: process.env,
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
expect(result).toEqual({
|
|
132
|
+
migrated: false,
|
|
133
|
+
changes: [],
|
|
134
|
+
warnings: [],
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
it("warns when legacy local-only room keys cannot be recovered automatically", async () => {
|
|
140
|
+
await withTempHome(async (home) => {
|
|
141
|
+
const { cfg, rootDir } = writeDefaultLegacyCryptoFixture(home);
|
|
142
|
+
|
|
143
|
+
const result = await autoPrepareLegacyMatrixCrypto({
|
|
144
|
+
cfg,
|
|
145
|
+
env: process.env,
|
|
146
|
+
deps: {
|
|
147
|
+
inspectLegacyStore: async () => ({
|
|
148
|
+
deviceId: MATRIX_DEFAULT_DEVICE_ID,
|
|
149
|
+
roomKeyCounts: { total: 15, backedUp: 10 },
|
|
150
|
+
backupVersion: null,
|
|
151
|
+
decryptionKeyBase64: null,
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
expect(result.migrated).toBe(true);
|
|
157
|
+
expect(result.warnings).toContain(
|
|
158
|
+
'Legacy Matrix encrypted state for account "default" contains 5 room key(s) that were never backed up. Backed-up keys can be restored automatically, but local-only encrypted history may remain unavailable after upgrade.',
|
|
159
|
+
);
|
|
160
|
+
expect(result.warnings).toContain(
|
|
161
|
+
'Legacy Matrix encrypted state for account "default" cannot be fully converted automatically because the old rust crypto store does not expose all local room keys for export.',
|
|
162
|
+
);
|
|
163
|
+
const state = JSON.parse(
|
|
164
|
+
fs.readFileSync(path.join(rootDir, "legacy-crypto-migration.json"), "utf8"),
|
|
165
|
+
) as { restoreStatus: string };
|
|
166
|
+
expect(state.restoreStatus).toBe("manual-action-required");
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("prefers stored credentials for named accounts when config is token-only", async () => {
|
|
171
|
+
await withTempHome(async (home) => {
|
|
172
|
+
const { rootDir } = createOpsLegacyCryptoFixture({
|
|
173
|
+
home,
|
|
174
|
+
includeStoredCredentials: true,
|
|
175
|
+
});
|
|
176
|
+
const cfg: OpenClawConfig = {
|
|
177
|
+
channels: {
|
|
178
|
+
matrix: {
|
|
179
|
+
accounts: {
|
|
180
|
+
ops: {
|
|
181
|
+
homeserver: MATRIX_TEST_HOMESERVER,
|
|
182
|
+
accessToken: LOBI_OPS_ACCESS_TOKEN,
|
|
183
|
+
},
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
const result = await autoPrepareLegacyMatrixCrypto({
|
|
190
|
+
cfg,
|
|
191
|
+
env: process.env,
|
|
192
|
+
deps: {
|
|
193
|
+
inspectLegacyStore: async () => ({
|
|
194
|
+
deviceId: LOBI_OPS_DEVICE_ID,
|
|
195
|
+
roomKeyCounts: { total: 1, backedUp: 1 },
|
|
196
|
+
backupVersion: "1",
|
|
197
|
+
decryptionKeyBase64: "b3Bz",
|
|
198
|
+
}),
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(result.migrated).toBe(true);
|
|
203
|
+
expect(fs.existsSync(path.join(rootDir, "recovery-key.json"))).toBe(true);
|
|
204
|
+
});
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it("stays warning-only when the legacy crypto inspector artifact is unavailable", async () => {
|
|
208
|
+
legacyCryptoInspectorAvailability.available = false;
|
|
209
|
+
|
|
210
|
+
await withTempHome(async (home) => {
|
|
211
|
+
const { cfg } = writeDefaultLegacyCryptoFixture(home);
|
|
212
|
+
|
|
213
|
+
const detection = detectLegacyMatrixCrypto({ cfg, env: process.env });
|
|
214
|
+
expect(detection.inspectorAvailable).toBe(false);
|
|
215
|
+
expect(detection.plans).toHaveLength(1);
|
|
216
|
+
expect(detection.warnings).toContain(
|
|
217
|
+
"Legacy Matrix encrypted state was detected, but the Matrix crypto inspector is unavailable.",
|
|
218
|
+
);
|
|
219
|
+
|
|
220
|
+
const result = await autoPrepareLegacyMatrixCrypto({
|
|
221
|
+
cfg,
|
|
222
|
+
env: process.env,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
expect(result).toEqual({
|
|
226
|
+
migrated: false,
|
|
227
|
+
changes: [],
|
|
228
|
+
warnings: [
|
|
229
|
+
"Legacy Matrix encrypted state was detected, but the Matrix crypto inspector is unavailable.",
|
|
230
|
+
],
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
});
|