mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-14 10:41:23 +00:00
fix: tighten Matrix invite auto-join onboarding
This commit is contained in:
@@ -62,6 +62,7 @@ What the Matrix wizard actually asks for:
|
||||
- whether to enable E2EE
|
||||
- whether to configure Matrix room access now
|
||||
- whether to configure Matrix invite auto-join now
|
||||
- when invite auto-join is enabled, whether it should be `allowlist`, `always`, or `off`
|
||||
|
||||
Wizard behavior that matters:
|
||||
|
||||
@@ -70,6 +71,7 @@ Wizard behavior that matters:
|
||||
- DM allowlist prompts accept full `@user:server` values immediately. Display names only work when live directory lookup finds one exact match; otherwise the wizard asks you to retry with a full Matrix ID.
|
||||
- Room allowlist prompts accept room IDs and aliases directly. They can also resolve joined-room names live, but unresolved names are only kept as typed during setup and are ignored later by runtime allowlist resolution. Prefer `!room:server` or `#alias:server`.
|
||||
- The wizard now shows an explicit warning before the invite auto-join step because `channels.matrix.autoJoin` defaults to `off`; agents will not join invited rooms or fresh DM-style invites unless you set it.
|
||||
- In invite auto-join allowlist mode, use only stable invite targets: `!roomId:server`, `#alias:server`, or `*`. Plain room names are rejected.
|
||||
- Runtime room/session identity uses the stable Matrix room ID. Room-declared aliases are only used as lookup inputs, not as the long-term session key or stable group identity.
|
||||
- To resolve room names before saving them, use `openclaw channels resolve --channel matrix "Project Room"`.
|
||||
|
||||
@@ -79,6 +81,8 @@ Wizard behavior that matters:
|
||||
If you leave it unset, the bot will not join invited rooms or fresh DM-style invites, so it will not appear in new groups or invited DMs unless you join manually first.
|
||||
|
||||
Set `autoJoin: "allowlist"` together with `autoJoinAllowlist` to restrict which invites it accepts, or set `autoJoin: "always"` if you want it to join every invite.
|
||||
|
||||
In `allowlist` mode, `autoJoinAllowlist` only accepts `!roomId:server`, `#alias:server`, or `*`.
|
||||
</Warning>
|
||||
|
||||
Allowlist example:
|
||||
|
||||
@@ -470,8 +470,10 @@ describe("matrix onboarding", () => {
|
||||
|
||||
it("clears Matrix invite auto-join allowlists when switching auto-join off", async () => {
|
||||
installMatrixTestRuntime();
|
||||
const notes: string[] = [];
|
||||
|
||||
const prompter = createMatrixWizardPrompter({
|
||||
notes,
|
||||
select: {
|
||||
"Matrix already configured. What do you want to do?": "update",
|
||||
"Matrix invite auto-join": "off",
|
||||
@@ -484,6 +486,7 @@ describe("matrix onboarding", () => {
|
||||
"Matrix credentials already configured. Keep them?": true,
|
||||
"Enable end-to-end encryption (E2EE)?": false,
|
||||
"Configure Matrix rooms access?": false,
|
||||
"Configure Matrix invite auto-join?": true,
|
||||
"Update Matrix invite auto-join?": true,
|
||||
},
|
||||
});
|
||||
@@ -510,6 +513,68 @@ describe("matrix onboarding", () => {
|
||||
|
||||
expect(result.cfg.channels?.matrix?.autoJoin).toBe("off");
|
||||
expect(result.cfg.channels?.matrix?.autoJoinAllowlist).toBeUndefined();
|
||||
expect(notes.join("\n")).toContain("Matrix invite auto-join remains off.");
|
||||
expect(notes.join("\n")).toContain(
|
||||
"Agents will not join invited rooms or fresh DM-style invites until you change autoJoin.",
|
||||
);
|
||||
});
|
||||
|
||||
it("re-prompts Matrix invite auto-join allowlists until entries are stable invite targets", async () => {
|
||||
installMatrixTestRuntime();
|
||||
const notes: string[] = [];
|
||||
let inviteAllowlistPrompts = 0;
|
||||
|
||||
const prompter = createMatrixWizardPrompter({
|
||||
notes,
|
||||
select: {
|
||||
"Matrix already configured. What do you want to do?": "update",
|
||||
"Matrix invite auto-join": "allowlist",
|
||||
},
|
||||
text: {
|
||||
"Matrix homeserver URL": "https://matrix.example.org",
|
||||
"Matrix device name (optional)": "OpenClaw Gateway",
|
||||
},
|
||||
confirm: {
|
||||
"Matrix credentials already configured. Keep them?": true,
|
||||
"Enable end-to-end encryption (E2EE)?": false,
|
||||
"Configure Matrix rooms access?": false,
|
||||
"Configure Matrix invite auto-join?": true,
|
||||
"Update Matrix invite auto-join?": true,
|
||||
},
|
||||
onText: async (message) => {
|
||||
if (message === "Matrix invite auto-join allowlist (comma-separated)") {
|
||||
inviteAllowlistPrompts += 1;
|
||||
return inviteAllowlistPrompts === 1 ? "Project Room" : "#ops:example.org";
|
||||
}
|
||||
throw new Error(`unexpected text prompt: ${message}`);
|
||||
},
|
||||
});
|
||||
|
||||
const result = await runMatrixInteractiveConfigure({
|
||||
cfg: {
|
||||
channels: {
|
||||
matrix: {
|
||||
homeserver: "https://matrix.example.org",
|
||||
accessToken: "matrix-token",
|
||||
},
|
||||
},
|
||||
} as CoreConfig,
|
||||
prompter,
|
||||
configured: true,
|
||||
});
|
||||
|
||||
expect(result).not.toBe("skip");
|
||||
if (result === "skip") {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(inviteAllowlistPrompts).toBe(2);
|
||||
expect(result.cfg.channels?.matrix?.autoJoin).toBe("allowlist");
|
||||
expect(result.cfg.channels?.matrix?.autoJoinAllowlist).toEqual(["#ops:example.org"]);
|
||||
expect(notes.join("\n")).toContain(
|
||||
"Use only stable Matrix invite targets for auto-join: !roomId:server, #alias:server, or *.",
|
||||
);
|
||||
expect(notes.join("\n")).toContain("Invalid: Project Room");
|
||||
});
|
||||
|
||||
it("reports account-scoped DM config keys for named accounts", () => {
|
||||
|
||||
@@ -53,6 +53,18 @@ function isMatrixInviteAutoJoinPolicy(value: string): value is MatrixInviteAutoJ
|
||||
return value === "allowlist" || value === "always" || value === "off";
|
||||
}
|
||||
|
||||
function isMatrixInviteAutoJoinTarget(entry: string): boolean {
|
||||
return (
|
||||
entry === "*" ||
|
||||
(entry.startsWith("!") && entry.includes(":")) ||
|
||||
(entry.startsWith("#") && entry.includes(":"))
|
||||
);
|
||||
}
|
||||
|
||||
function normalizeMatrixInviteAutoJoinTargets(entries: string[]): string[] {
|
||||
return [...new Set(entries.map((entry) => entry.trim()).filter(Boolean))];
|
||||
}
|
||||
|
||||
function resolveMatrixOnboardingAccountId(cfg: CoreConfig, accountId?: string): string {
|
||||
return normalizeAccountId(
|
||||
accountId?.trim() || resolveDefaultMatrixAccountId(cfg) || DEFAULT_ACCOUNT_ID,
|
||||
@@ -248,23 +260,49 @@ async function configureMatrixInviteAutoJoin(params: {
|
||||
}
|
||||
const policy = selectedPolicy;
|
||||
|
||||
if (policy !== "allowlist") {
|
||||
if (policy === "off") {
|
||||
await params.prompter.note(
|
||||
[
|
||||
"Matrix invite auto-join remains off.",
|
||||
"Agents will not join invited rooms or fresh DM-style invites until you change autoJoin.",
|
||||
].join("\n"),
|
||||
"Matrix invite auto-join",
|
||||
);
|
||||
return setMatrixAutoJoin(params.cfg, policy, [], accountId);
|
||||
}
|
||||
|
||||
const rawAllowlist = String(
|
||||
await params.prompter.text({
|
||||
message: "Matrix invite auto-join allowlist (comma-separated)",
|
||||
placeholder: "!roomId:server, #alias:server, *",
|
||||
initialValue: currentAllowlist[0] ? currentAllowlist.join(", ") : undefined,
|
||||
validate: (value) => {
|
||||
const entries = splitSetupEntries(String(value ?? ""));
|
||||
return entries.length > 0 ? undefined : "Required";
|
||||
},
|
||||
}),
|
||||
);
|
||||
const allowlist = splitSetupEntries(rawAllowlist);
|
||||
return setMatrixAutoJoin(params.cfg, "allowlist", allowlist, accountId);
|
||||
if (policy === "always") {
|
||||
return setMatrixAutoJoin(params.cfg, policy, [], accountId);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
const rawAllowlist = String(
|
||||
await params.prompter.text({
|
||||
message: "Matrix invite auto-join allowlist (comma-separated)",
|
||||
placeholder: "!roomId:server, #alias:server, *",
|
||||
initialValue: currentAllowlist[0] ? currentAllowlist.join(", ") : undefined,
|
||||
validate: (value) => {
|
||||
const entries = splitSetupEntries(String(value ?? ""));
|
||||
return entries.length > 0 ? undefined : "Required";
|
||||
},
|
||||
}),
|
||||
);
|
||||
const allowlist = normalizeMatrixInviteAutoJoinTargets(splitSetupEntries(rawAllowlist));
|
||||
const invalidEntries = allowlist.filter((entry) => !isMatrixInviteAutoJoinTarget(entry));
|
||||
if (allowlist.length === 0 || invalidEntries.length > 0) {
|
||||
await params.prompter.note(
|
||||
[
|
||||
"Use only stable Matrix invite targets for auto-join: !roomId:server, #alias:server, or *.",
|
||||
invalidEntries.length > 0 ? `Invalid: ${invalidEntries.join(", ")}` : undefined,
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("\n"),
|
||||
"Matrix invite auto-join",
|
||||
);
|
||||
continue;
|
||||
}
|
||||
return setMatrixAutoJoin(params.cfg, "allowlist", allowlist, accountId);
|
||||
}
|
||||
}
|
||||
|
||||
async function configureMatrixAccessPrompts(params: {
|
||||
|
||||
Reference in New Issue
Block a user