@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,433 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matrix Poll Types (MSC3381)
|
|
3
|
+
*
|
|
4
|
+
* Defines types for Matrix poll events:
|
|
5
|
+
* - m.poll.start - Creates a new poll
|
|
6
|
+
* - m.poll.response - Records a vote
|
|
7
|
+
* - m.poll.end - Closes a poll
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
|
11
|
+
import { normalizePollInput, type PollInput } from "../runtime-api.js";
|
|
12
|
+
|
|
13
|
+
export const M_POLL_START = "m.poll.start" as const;
|
|
14
|
+
export const M_POLL_RESPONSE = "m.poll.response" as const;
|
|
15
|
+
export const M_POLL_END = "m.poll.end" as const;
|
|
16
|
+
|
|
17
|
+
export const ORG_POLL_START = "org.matrix.msc3381.poll.start" as const;
|
|
18
|
+
export const ORG_POLL_RESPONSE = "org.matrix.msc3381.poll.response" as const;
|
|
19
|
+
export const ORG_POLL_END = "org.matrix.msc3381.poll.end" as const;
|
|
20
|
+
|
|
21
|
+
export const POLL_EVENT_TYPES = [
|
|
22
|
+
M_POLL_START,
|
|
23
|
+
M_POLL_RESPONSE,
|
|
24
|
+
M_POLL_END,
|
|
25
|
+
ORG_POLL_START,
|
|
26
|
+
ORG_POLL_RESPONSE,
|
|
27
|
+
ORG_POLL_END,
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
export const POLL_START_TYPES = [M_POLL_START, ORG_POLL_START];
|
|
31
|
+
export const POLL_RESPONSE_TYPES = [M_POLL_RESPONSE, ORG_POLL_RESPONSE];
|
|
32
|
+
export const POLL_END_TYPES = [M_POLL_END, ORG_POLL_END];
|
|
33
|
+
|
|
34
|
+
export type PollKind = "m.poll.disclosed" | "m.poll.undisclosed";
|
|
35
|
+
|
|
36
|
+
export type TextContent = {
|
|
37
|
+
"m.text"?: string;
|
|
38
|
+
"org.matrix.msc1767.text"?: string;
|
|
39
|
+
body?: string;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export type PollAnswer = {
|
|
43
|
+
id: string;
|
|
44
|
+
} & TextContent;
|
|
45
|
+
|
|
46
|
+
export type PollParsedAnswer = {
|
|
47
|
+
id: string;
|
|
48
|
+
text: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export type PollStartSubtype = {
|
|
52
|
+
question: TextContent;
|
|
53
|
+
kind?: PollKind;
|
|
54
|
+
max_selections?: number;
|
|
55
|
+
answers: PollAnswer[];
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
export type LegacyPollStartContent = {
|
|
59
|
+
"m.poll"?: PollStartSubtype;
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
export type PollStartContent = {
|
|
63
|
+
[M_POLL_START]?: PollStartSubtype;
|
|
64
|
+
[ORG_POLL_START]?: PollStartSubtype;
|
|
65
|
+
"m.poll"?: PollStartSubtype;
|
|
66
|
+
"m.text"?: string;
|
|
67
|
+
"org.matrix.msc1767.text"?: string;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
export type PollSummary = {
|
|
71
|
+
eventId: string;
|
|
72
|
+
roomId: string;
|
|
73
|
+
sender: string;
|
|
74
|
+
senderName: string;
|
|
75
|
+
question: string;
|
|
76
|
+
answers: string[];
|
|
77
|
+
kind: PollKind;
|
|
78
|
+
maxSelections: number;
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export type PollResultsSummary = PollSummary & {
|
|
82
|
+
entries: Array<{
|
|
83
|
+
id: string;
|
|
84
|
+
text: string;
|
|
85
|
+
votes: number;
|
|
86
|
+
}>;
|
|
87
|
+
totalVotes: number;
|
|
88
|
+
closed: boolean;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
export type ParsedPollStart = {
|
|
92
|
+
question: string;
|
|
93
|
+
answers: PollParsedAnswer[];
|
|
94
|
+
kind: PollKind;
|
|
95
|
+
maxSelections: number;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
export type PollResponseSubtype = {
|
|
99
|
+
answers: string[];
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
export type PollResponseContent = {
|
|
103
|
+
[M_POLL_RESPONSE]?: PollResponseSubtype;
|
|
104
|
+
[ORG_POLL_RESPONSE]?: PollResponseSubtype;
|
|
105
|
+
"m.relates_to": {
|
|
106
|
+
rel_type: "m.reference";
|
|
107
|
+
event_id: string;
|
|
108
|
+
};
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
export function isPollStartType(eventType: string): boolean {
|
|
112
|
+
return (POLL_START_TYPES as readonly string[]).includes(eventType);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function isPollResponseType(eventType: string): boolean {
|
|
116
|
+
return (POLL_RESPONSE_TYPES as readonly string[]).includes(eventType);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function isPollEndType(eventType: string): boolean {
|
|
120
|
+
return (POLL_END_TYPES as readonly string[]).includes(eventType);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export function isPollEventType(eventType: string): boolean {
|
|
124
|
+
return (POLL_EVENT_TYPES as readonly string[]).includes(eventType);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getTextContent(text?: TextContent): string {
|
|
128
|
+
if (!text) {
|
|
129
|
+
return "";
|
|
130
|
+
}
|
|
131
|
+
return text["m.text"] ?? text["org.matrix.msc1767.text"] ?? text.body ?? "";
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export function parsePollStart(content: PollStartContent): ParsedPollStart | null {
|
|
135
|
+
const poll =
|
|
136
|
+
(content as Record<string, PollStartSubtype | undefined>)[M_POLL_START] ??
|
|
137
|
+
(content as Record<string, PollStartSubtype | undefined>)[ORG_POLL_START] ??
|
|
138
|
+
(content as Record<string, PollStartSubtype | undefined>)["m.poll"];
|
|
139
|
+
if (!poll) {
|
|
140
|
+
return null;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const question = getTextContent(poll.question).trim();
|
|
144
|
+
if (!question) {
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const answers = poll.answers
|
|
149
|
+
.map((answer) => ({
|
|
150
|
+
id: answer.id,
|
|
151
|
+
text: getTextContent(answer).trim(),
|
|
152
|
+
}))
|
|
153
|
+
.filter((answer) => answer.id.trim().length > 0 && answer.text.length > 0);
|
|
154
|
+
if (answers.length === 0) {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const maxSelectionsRaw = poll.max_selections;
|
|
159
|
+
const maxSelections =
|
|
160
|
+
typeof maxSelectionsRaw === "number" && Number.isFinite(maxSelectionsRaw)
|
|
161
|
+
? Math.floor(maxSelectionsRaw)
|
|
162
|
+
: 1;
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
question,
|
|
166
|
+
answers,
|
|
167
|
+
kind: poll.kind ?? "m.poll.disclosed",
|
|
168
|
+
maxSelections: Math.min(Math.max(maxSelections, 1), answers.length),
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
export function parsePollStartContent(content: PollStartContent): PollSummary | null {
|
|
173
|
+
const parsed = parsePollStart(content);
|
|
174
|
+
if (!parsed) {
|
|
175
|
+
return null;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return {
|
|
179
|
+
eventId: "",
|
|
180
|
+
roomId: "",
|
|
181
|
+
sender: "",
|
|
182
|
+
senderName: "",
|
|
183
|
+
question: parsed.question,
|
|
184
|
+
answers: parsed.answers.map((answer) => answer.text),
|
|
185
|
+
kind: parsed.kind,
|
|
186
|
+
maxSelections: parsed.maxSelections,
|
|
187
|
+
};
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export function formatPollAsText(summary: PollSummary): string {
|
|
191
|
+
const lines = [
|
|
192
|
+
"[Poll]",
|
|
193
|
+
summary.question,
|
|
194
|
+
"",
|
|
195
|
+
...summary.answers.map((answer, idx) => `${idx + 1}. ${answer}`),
|
|
196
|
+
];
|
|
197
|
+
return lines.join("\n");
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export function resolvePollReferenceEventId(content: unknown): string | null {
|
|
201
|
+
if (!content || typeof content !== "object") {
|
|
202
|
+
return null;
|
|
203
|
+
}
|
|
204
|
+
const relates = (content as { "m.relates_to"?: { event_id?: unknown } })["m.relates_to"];
|
|
205
|
+
if (!relates || typeof relates.event_id !== "string") {
|
|
206
|
+
return null;
|
|
207
|
+
}
|
|
208
|
+
const eventId = relates.event_id.trim();
|
|
209
|
+
return eventId.length > 0 ? eventId : null;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
export function parsePollResponseAnswerIds(content: unknown): string[] | null {
|
|
213
|
+
if (!content || typeof content !== "object") {
|
|
214
|
+
return null;
|
|
215
|
+
}
|
|
216
|
+
const response =
|
|
217
|
+
(content as Record<string, PollResponseSubtype | undefined>)[M_POLL_RESPONSE] ??
|
|
218
|
+
(content as Record<string, PollResponseSubtype | undefined>)[ORG_POLL_RESPONSE];
|
|
219
|
+
if (!response || !Array.isArray(response.answers)) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
return response.answers.filter((answer): answer is string => typeof answer === "string");
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
export function buildPollResultsSummary(params: {
|
|
226
|
+
pollEventId: string;
|
|
227
|
+
roomId: string;
|
|
228
|
+
sender: string;
|
|
229
|
+
senderName: string;
|
|
230
|
+
content: PollStartContent;
|
|
231
|
+
relationEvents: Array<{
|
|
232
|
+
event_id?: string;
|
|
233
|
+
sender?: string;
|
|
234
|
+
type?: string;
|
|
235
|
+
origin_server_ts?: number;
|
|
236
|
+
content?: Record<string, unknown>;
|
|
237
|
+
unsigned?: {
|
|
238
|
+
redacted_because?: unknown;
|
|
239
|
+
};
|
|
240
|
+
}>;
|
|
241
|
+
}): PollResultsSummary | null {
|
|
242
|
+
const parsed = parsePollStart(params.content);
|
|
243
|
+
if (!parsed) {
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
let pollClosedAt = Number.POSITIVE_INFINITY;
|
|
248
|
+
for (const event of params.relationEvents) {
|
|
249
|
+
if (event.unsigned?.redacted_because) {
|
|
250
|
+
continue;
|
|
251
|
+
}
|
|
252
|
+
if (!isPollEndType(typeof event.type === "string" ? event.type : "")) {
|
|
253
|
+
continue;
|
|
254
|
+
}
|
|
255
|
+
if (event.sender !== params.sender) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const ts =
|
|
259
|
+
typeof event.origin_server_ts === "number" && Number.isFinite(event.origin_server_ts)
|
|
260
|
+
? event.origin_server_ts
|
|
261
|
+
: Number.POSITIVE_INFINITY;
|
|
262
|
+
if (ts < pollClosedAt) {
|
|
263
|
+
pollClosedAt = ts;
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const answerIds = new Set(parsed.answers.map((answer) => answer.id));
|
|
268
|
+
const latestVoteBySender = new Map<
|
|
269
|
+
string,
|
|
270
|
+
{
|
|
271
|
+
ts: number;
|
|
272
|
+
eventId: string;
|
|
273
|
+
answerIds: string[];
|
|
274
|
+
}
|
|
275
|
+
>();
|
|
276
|
+
|
|
277
|
+
const orderedRelationEvents = [...params.relationEvents].toSorted((left, right) => {
|
|
278
|
+
const leftTs =
|
|
279
|
+
typeof left.origin_server_ts === "number" && Number.isFinite(left.origin_server_ts)
|
|
280
|
+
? left.origin_server_ts
|
|
281
|
+
: Number.POSITIVE_INFINITY;
|
|
282
|
+
const rightTs =
|
|
283
|
+
typeof right.origin_server_ts === "number" && Number.isFinite(right.origin_server_ts)
|
|
284
|
+
? right.origin_server_ts
|
|
285
|
+
: Number.POSITIVE_INFINITY;
|
|
286
|
+
if (leftTs !== rightTs) {
|
|
287
|
+
return leftTs - rightTs;
|
|
288
|
+
}
|
|
289
|
+
return (left.event_id ?? "").localeCompare(right.event_id ?? "");
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
for (const event of orderedRelationEvents) {
|
|
293
|
+
if (event.unsigned?.redacted_because) {
|
|
294
|
+
continue;
|
|
295
|
+
}
|
|
296
|
+
if (!isPollResponseType(typeof event.type === "string" ? event.type : "")) {
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
299
|
+
const senderId = normalizeOptionalString(event.sender) ?? "";
|
|
300
|
+
if (!senderId) {
|
|
301
|
+
continue;
|
|
302
|
+
}
|
|
303
|
+
const eventTs =
|
|
304
|
+
typeof event.origin_server_ts === "number" && Number.isFinite(event.origin_server_ts)
|
|
305
|
+
? event.origin_server_ts
|
|
306
|
+
: Number.POSITIVE_INFINITY;
|
|
307
|
+
if (eventTs > pollClosedAt) {
|
|
308
|
+
continue;
|
|
309
|
+
}
|
|
310
|
+
const rawAnswers = parsePollResponseAnswerIds(event.content) ?? [];
|
|
311
|
+
const normalizedAnswers = Array.from(
|
|
312
|
+
new Set(
|
|
313
|
+
rawAnswers
|
|
314
|
+
.map((answerId) => normalizeOptionalString(answerId) ?? "")
|
|
315
|
+
.filter((answerId) => answerIds.has(answerId))
|
|
316
|
+
.slice(0, parsed.maxSelections),
|
|
317
|
+
),
|
|
318
|
+
);
|
|
319
|
+
latestVoteBySender.set(senderId, {
|
|
320
|
+
ts: eventTs,
|
|
321
|
+
eventId: typeof event.event_id === "string" ? event.event_id : "",
|
|
322
|
+
answerIds: normalizedAnswers,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
const voteCounts = new Map<string, number>(
|
|
327
|
+
parsed.answers.map((answer): [string, number] => [answer.id, 0]),
|
|
328
|
+
);
|
|
329
|
+
let totalVotes = 0;
|
|
330
|
+
for (const latestVote of latestVoteBySender.values()) {
|
|
331
|
+
if (latestVote.answerIds.length === 0) {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
totalVotes += 1;
|
|
335
|
+
for (const answerId of latestVote.answerIds) {
|
|
336
|
+
voteCounts.set(answerId, (voteCounts.get(answerId) ?? 0) + 1);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return {
|
|
341
|
+
eventId: params.pollEventId,
|
|
342
|
+
roomId: params.roomId,
|
|
343
|
+
sender: params.sender,
|
|
344
|
+
senderName: params.senderName,
|
|
345
|
+
question: parsed.question,
|
|
346
|
+
answers: parsed.answers.map((answer) => answer.text),
|
|
347
|
+
kind: parsed.kind,
|
|
348
|
+
maxSelections: parsed.maxSelections,
|
|
349
|
+
entries: parsed.answers.map((answer) => ({
|
|
350
|
+
id: answer.id,
|
|
351
|
+
text: answer.text,
|
|
352
|
+
votes: voteCounts.get(answer.id) ?? 0,
|
|
353
|
+
})),
|
|
354
|
+
totalVotes,
|
|
355
|
+
closed: Number.isFinite(pollClosedAt),
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
export function formatPollResultsAsText(summary: PollResultsSummary): string {
|
|
360
|
+
const lines = [summary.closed ? "[Poll closed]" : "[Poll]", summary.question, ""];
|
|
361
|
+
const revealResults = summary.kind === "m.poll.disclosed" || summary.closed;
|
|
362
|
+
for (const [index, entry] of summary.entries.entries()) {
|
|
363
|
+
if (!revealResults) {
|
|
364
|
+
lines.push(`${index + 1}. ${entry.text}`);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
lines.push(`${index + 1}. ${entry.text} (${entry.votes} vote${entry.votes === 1 ? "" : "s"})`);
|
|
368
|
+
}
|
|
369
|
+
lines.push("");
|
|
370
|
+
if (!revealResults) {
|
|
371
|
+
lines.push("Responses are hidden until the poll closes.");
|
|
372
|
+
} else {
|
|
373
|
+
lines.push(`Total voters: ${summary.totalVotes}`);
|
|
374
|
+
}
|
|
375
|
+
return lines.join("\n");
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function buildTextContent(body: string): TextContent {
|
|
379
|
+
return {
|
|
380
|
+
"m.text": body,
|
|
381
|
+
"org.matrix.msc1767.text": body,
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
function buildPollFallbackText(question: string, answers: string[]): string {
|
|
386
|
+
if (answers.length === 0) {
|
|
387
|
+
return question;
|
|
388
|
+
}
|
|
389
|
+
return `${question}\n${answers.map((answer, idx) => `${idx + 1}. ${answer}`).join("\n")}`;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
export function buildPollStartContent(poll: PollInput): PollStartContent {
|
|
393
|
+
const normalized = normalizePollInput(poll);
|
|
394
|
+
const answers = normalized.options.map((option, idx) => ({
|
|
395
|
+
id: `answer${idx + 1}`,
|
|
396
|
+
...buildTextContent(option),
|
|
397
|
+
}));
|
|
398
|
+
|
|
399
|
+
const isMultiple = normalized.maxSelections > 1;
|
|
400
|
+
const fallbackText = buildPollFallbackText(
|
|
401
|
+
normalized.question,
|
|
402
|
+
answers.map((answer) => getTextContent(answer)),
|
|
403
|
+
);
|
|
404
|
+
|
|
405
|
+
return {
|
|
406
|
+
[M_POLL_START]: {
|
|
407
|
+
question: buildTextContent(normalized.question),
|
|
408
|
+
kind: isMultiple ? "m.poll.undisclosed" : "m.poll.disclosed",
|
|
409
|
+
max_selections: normalized.maxSelections,
|
|
410
|
+
answers,
|
|
411
|
+
},
|
|
412
|
+
"m.text": fallbackText,
|
|
413
|
+
"org.matrix.msc1767.text": fallbackText,
|
|
414
|
+
};
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function buildPollResponseContent(
|
|
418
|
+
pollEventId: string,
|
|
419
|
+
answerIds: string[],
|
|
420
|
+
): PollResponseContent {
|
|
421
|
+
return {
|
|
422
|
+
[M_POLL_RESPONSE]: {
|
|
423
|
+
answers: answerIds,
|
|
424
|
+
},
|
|
425
|
+
[ORG_POLL_RESPONSE]: {
|
|
426
|
+
answers: answerIds,
|
|
427
|
+
},
|
|
428
|
+
"m.relates_to": {
|
|
429
|
+
rel_type: "m.reference",
|
|
430
|
+
event_id: pollEventId,
|
|
431
|
+
},
|
|
432
|
+
};
|
|
433
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
|
|
3
|
+
const createMatrixClientMock = vi.fn();
|
|
4
|
+
const isBunRuntimeMock = vi.fn(() => false);
|
|
5
|
+
|
|
6
|
+
vi.mock("./probe.runtime.js", () => ({
|
|
7
|
+
createMatrixClient: (...args: unknown[]) => createMatrixClientMock(...args),
|
|
8
|
+
}));
|
|
9
|
+
|
|
10
|
+
vi.mock("./client/runtime.js", () => ({
|
|
11
|
+
isBunRuntime: () => isBunRuntimeMock(),
|
|
12
|
+
}));
|
|
13
|
+
|
|
14
|
+
import { probeMatrix } from "./probe.js";
|
|
15
|
+
|
|
16
|
+
describe("probeMatrix", () => {
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
vi.clearAllMocks();
|
|
19
|
+
isBunRuntimeMock.mockReturnValue(false);
|
|
20
|
+
createMatrixClientMock.mockResolvedValue({
|
|
21
|
+
getUserId: vi.fn(async () => "@bot:example.org"),
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it("passes undefined userId when not provided", async () => {
|
|
26
|
+
const result = await probeMatrix({
|
|
27
|
+
homeserver: "https://matrix.example.org",
|
|
28
|
+
accessToken: "tok",
|
|
29
|
+
timeoutMs: 1234,
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
expect(result.ok).toBe(true);
|
|
33
|
+
expect(createMatrixClientMock).toHaveBeenCalledWith({
|
|
34
|
+
homeserver: "https://matrix.example.org",
|
|
35
|
+
userId: undefined,
|
|
36
|
+
accessToken: "tok",
|
|
37
|
+
persistStorage: false,
|
|
38
|
+
localTimeoutMs: 1234,
|
|
39
|
+
});
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("trims provided userId before client creation", async () => {
|
|
43
|
+
await probeMatrix({
|
|
44
|
+
homeserver: "https://matrix.example.org",
|
|
45
|
+
accessToken: "tok",
|
|
46
|
+
userId: " @bot:example.org ",
|
|
47
|
+
timeoutMs: 500,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(createMatrixClientMock).toHaveBeenCalledWith({
|
|
51
|
+
homeserver: "https://matrix.example.org",
|
|
52
|
+
userId: "@bot:example.org",
|
|
53
|
+
accessToken: "tok",
|
|
54
|
+
persistStorage: false,
|
|
55
|
+
localTimeoutMs: 500,
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("passes accountId through to client creation", async () => {
|
|
60
|
+
await probeMatrix({
|
|
61
|
+
homeserver: "https://matrix.example.org",
|
|
62
|
+
accessToken: "tok",
|
|
63
|
+
userId: "@bot:example.org",
|
|
64
|
+
timeoutMs: 500,
|
|
65
|
+
accountId: "ops",
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
expect(createMatrixClientMock).toHaveBeenCalledWith({
|
|
69
|
+
homeserver: "https://matrix.example.org",
|
|
70
|
+
userId: "@bot:example.org",
|
|
71
|
+
accessToken: "tok",
|
|
72
|
+
persistStorage: false,
|
|
73
|
+
localTimeoutMs: 500,
|
|
74
|
+
accountId: "ops",
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it("passes dispatcherPolicy through to client creation", async () => {
|
|
79
|
+
await probeMatrix({
|
|
80
|
+
homeserver: "https://matrix.example.org",
|
|
81
|
+
accessToken: "tok",
|
|
82
|
+
timeoutMs: 500,
|
|
83
|
+
dispatcherPolicy: {
|
|
84
|
+
mode: "explicit-proxy",
|
|
85
|
+
proxyUrl: "http://127.0.0.1:7890",
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
expect(createMatrixClientMock).toHaveBeenCalledWith({
|
|
90
|
+
homeserver: "https://matrix.example.org",
|
|
91
|
+
userId: undefined,
|
|
92
|
+
accessToken: "tok",
|
|
93
|
+
persistStorage: false,
|
|
94
|
+
localTimeoutMs: 500,
|
|
95
|
+
dispatcherPolicy: {
|
|
96
|
+
mode: "explicit-proxy",
|
|
97
|
+
proxyUrl: "http://127.0.0.1:7890",
|
|
98
|
+
},
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("passes deviceId through to client creation (#61317)", async () => {
|
|
103
|
+
await probeMatrix({
|
|
104
|
+
homeserver: "https://matrix.example.org",
|
|
105
|
+
accessToken: "tok",
|
|
106
|
+
userId: "@bot:example.org",
|
|
107
|
+
deviceId: "ABCDEF",
|
|
108
|
+
timeoutMs: 500,
|
|
109
|
+
accountId: "ops",
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
expect(createMatrixClientMock).toHaveBeenCalledWith({
|
|
113
|
+
homeserver: "https://matrix.example.org",
|
|
114
|
+
userId: "@bot:example.org",
|
|
115
|
+
accessToken: "tok",
|
|
116
|
+
deviceId: "ABCDEF",
|
|
117
|
+
persistStorage: false,
|
|
118
|
+
localTimeoutMs: 500,
|
|
119
|
+
accountId: "ops",
|
|
120
|
+
});
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("omits deviceId when not provided", async () => {
|
|
124
|
+
await probeMatrix({
|
|
125
|
+
homeserver: "https://matrix.example.org",
|
|
126
|
+
accessToken: "tok",
|
|
127
|
+
timeoutMs: 500,
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(createMatrixClientMock).toHaveBeenCalledWith({
|
|
131
|
+
homeserver: "https://matrix.example.org",
|
|
132
|
+
userId: undefined,
|
|
133
|
+
accessToken: "tok",
|
|
134
|
+
deviceId: undefined,
|
|
135
|
+
persistStorage: false,
|
|
136
|
+
localTimeoutMs: 500,
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it("returns client validation errors for insecure public http homeservers", async () => {
|
|
141
|
+
createMatrixClientMock.mockRejectedValue(
|
|
142
|
+
new Error("Matrix homeserver must use https:// unless it targets a private or loopback host"),
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
const result = await probeMatrix({
|
|
146
|
+
homeserver: "http://matrix.example.org",
|
|
147
|
+
accessToken: "tok",
|
|
148
|
+
timeoutMs: 500,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
expect(result.ok).toBe(false);
|
|
152
|
+
expect(result.error).toContain("Matrix homeserver must use https://");
|
|
153
|
+
});
|
|
154
|
+
});
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { formatErrorMessage, type PinnedDispatcherPolicy } from "openclaw/plugin-sdk/infra-runtime";
|
|
2
|
+
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
|
3
|
+
import type { SsrFPolicy } from "../runtime-api.js";
|
|
4
|
+
import type { BaseProbeResult } from "../runtime-api.js";
|
|
5
|
+
import { isBunRuntime } from "./client/runtime.js";
|
|
6
|
+
|
|
7
|
+
type MatrixProbeRuntimeDeps = Pick<typeof import("./probe.runtime.js"), "createMatrixClient">;
|
|
8
|
+
|
|
9
|
+
let matrixProbeRuntimeDepsPromise: Promise<MatrixProbeRuntimeDeps> | undefined;
|
|
10
|
+
|
|
11
|
+
async function loadMatrixProbeRuntimeDeps(): Promise<MatrixProbeRuntimeDeps> {
|
|
12
|
+
matrixProbeRuntimeDepsPromise ??= import("./probe.runtime.js").then((runtimeModule) => ({
|
|
13
|
+
createMatrixClient: runtimeModule.createMatrixClient,
|
|
14
|
+
}));
|
|
15
|
+
return await matrixProbeRuntimeDepsPromise;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export type MatrixProbe = BaseProbeResult & {
|
|
19
|
+
status?: number | null;
|
|
20
|
+
elapsedMs: number;
|
|
21
|
+
userId?: string | null;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export async function probeMatrix(params: {
|
|
25
|
+
homeserver: string;
|
|
26
|
+
accessToken: string;
|
|
27
|
+
userId?: string;
|
|
28
|
+
deviceId?: string;
|
|
29
|
+
timeoutMs?: number;
|
|
30
|
+
accountId?: string | null;
|
|
31
|
+
allowPrivateNetwork?: boolean;
|
|
32
|
+
ssrfPolicy?: SsrFPolicy;
|
|
33
|
+
dispatcherPolicy?: PinnedDispatcherPolicy;
|
|
34
|
+
}): Promise<MatrixProbe> {
|
|
35
|
+
const started = Date.now();
|
|
36
|
+
const result: MatrixProbe = {
|
|
37
|
+
ok: false,
|
|
38
|
+
status: null,
|
|
39
|
+
error: null,
|
|
40
|
+
elapsedMs: 0,
|
|
41
|
+
};
|
|
42
|
+
if (isBunRuntime()) {
|
|
43
|
+
return {
|
|
44
|
+
...result,
|
|
45
|
+
error: "Matrix probe requires Node (bun runtime not supported)",
|
|
46
|
+
elapsedMs: Date.now() - started,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
if (!params.homeserver?.trim()) {
|
|
50
|
+
return {
|
|
51
|
+
...result,
|
|
52
|
+
error: "missing homeserver",
|
|
53
|
+
elapsedMs: Date.now() - started,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
if (!params.accessToken?.trim()) {
|
|
57
|
+
return {
|
|
58
|
+
...result,
|
|
59
|
+
error: "missing access token",
|
|
60
|
+
elapsedMs: Date.now() - started,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const { createMatrixClient } = await loadMatrixProbeRuntimeDeps();
|
|
65
|
+
const inputUserId = normalizeOptionalString(params.userId);
|
|
66
|
+
const client = await createMatrixClient({
|
|
67
|
+
homeserver: params.homeserver,
|
|
68
|
+
userId: inputUserId,
|
|
69
|
+
accessToken: params.accessToken,
|
|
70
|
+
deviceId: params.deviceId,
|
|
71
|
+
persistStorage: false,
|
|
72
|
+
localTimeoutMs: params.timeoutMs,
|
|
73
|
+
accountId: params.accountId,
|
|
74
|
+
allowPrivateNetwork: params.allowPrivateNetwork,
|
|
75
|
+
ssrfPolicy: params.ssrfPolicy,
|
|
76
|
+
dispatcherPolicy: params.dispatcherPolicy,
|
|
77
|
+
});
|
|
78
|
+
// The client wrapper resolves user ID via whoami when needed.
|
|
79
|
+
const userId = await client.getUserId();
|
|
80
|
+
result.ok = true;
|
|
81
|
+
result.userId = userId ?? null;
|
|
82
|
+
|
|
83
|
+
result.elapsedMs = Date.now() - started;
|
|
84
|
+
return result;
|
|
85
|
+
} catch (err) {
|
|
86
|
+
return {
|
|
87
|
+
...result,
|
|
88
|
+
status:
|
|
89
|
+
typeof err === "object" && err && "statusCode" in err
|
|
90
|
+
? Number((err as { statusCode?: number }).statusCode)
|
|
91
|
+
: result.status,
|
|
92
|
+
error: formatErrorMessage(err),
|
|
93
|
+
elapsedMs: Date.now() - started,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
}
|