openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
540 lines (539 loc) • 21.3 kB
JavaScript
import { a as normalizeLowercaseStringOrEmpty, c as normalizeOptionalString, f as normalizeStringifiedOptionalString } from "./string-coerce-mnp54Vah.js";
import { t as formatDocsLink } from "./links-CsLBrRff.js";
import { s as hasConfiguredSecretInput } from "./types.secrets-_0JOMGE5.js";
import { n as normalizeAccountId } from "./account-id-Df9e41E6.js";
import { o as isPrivateOrLoopbackHost } from "./net-DTe7AQiu.js";
import "./string-coerce-runtime-CEGJWkQ_.js";
import { O as promptAccountId, Q as splitSetupEntries, v as mergeAllowFromEntries } from "./setup-wizard-helpers-Dv-t1nb8.js";
import { t as promptChannelAccessConfig } from "./setup-group-access-3hjQBoEk.js";
import "./setup-DHfdBmUU.js";
import { o as isPrivateNetworkOptInEnabled } from "./ssrf-policy-sZLyyYht.js";
import { n as requiresExplicitMatrixDefaultAccount } from "./account-selection-CqvkLWBU.js";
import { a as resolveMatrixAccountConfig } from "./account-config-C2f9UtTn.js";
import { r as resolveMatrixEnvAuthReadiness } from "./env-auth-B_5QIHM9.js";
import { i as resolveMatrixAccount, r as resolveDefaultMatrixAccountId, t as listMatrixAccountIds } from "./accounts-ChjotiNL.js";
import { r as moveSingleMatrixAccountConfigToNamedAccount, t as resolveMatrixSetupDmAllowFrom } from "./setup-dm-policy-CfftVKVu.js";
import { t as resolveMatrixConfigFieldPath } from "./config-paths-Cz5wYCcV.js";
import { n as updateMatrixAccountConfig } from "./config-update-IQZLcp6g.js";
import { n as validateMatrixHomeserverUrl, t as resolveValidatedMatrixHomeserverUrl } from "./url-validation-Ck23jxmT.js";
import { n as ensureMatrixSdkInstalled, r as isMatrixSdkAvailable } from "./deps-D5b5VIwf.js";
//#region extensions/matrix/src/onboarding.ts
const channel = "matrix";
const matrixInviteAutoJoinOptions = [
{
value: "allowlist",
label: "Allowlist (recommended)"
},
{
value: "always",
label: "Always (join every invite)"
},
{
value: "off",
label: "Off (do not auto-join invites)"
}
];
function isMatrixInviteAutoJoinPolicy(value) {
return value === "allowlist" || value === "always" || value === "off";
}
function isMatrixInviteAutoJoinTarget(entry) {
return entry === "*" || entry.startsWith("!") && entry.includes(":") || entry.startsWith("#") && entry.includes(":");
}
function normalizeMatrixInviteAutoJoinTargets(entries) {
return [...new Set(entries.map((entry) => normalizeOptionalString(entry)).filter((entry) => Boolean(entry)))];
}
function resolveMatrixOnboardingAccountId(cfg, accountId) {
return normalizeAccountId(normalizeOptionalString(accountId) || resolveDefaultMatrixAccountId(cfg) || "default");
}
function setMatrixDmPolicy(cfg, policy, accountId) {
const resolvedAccountId = resolveMatrixOnboardingAccountId(cfg, accountId);
const existing = resolveMatrixAccountConfig({
cfg,
accountId: resolvedAccountId
});
const allowFrom = resolveMatrixSetupDmAllowFrom(policy, existing.dm?.allowFrom);
return updateMatrixAccountConfig(cfg, resolvedAccountId, { dm: {
...existing.dm,
policy,
allowFrom
} });
}
async function noteMatrixAuthHelp(prompter) {
await prompter.note([
"Matrix requires a homeserver URL.",
"Use an access token (recommended) or password login to an existing account.",
"With access token: user ID is fetched automatically.",
"Env vars supported: MATRIX_HOMESERVER, MATRIX_USER_ID, MATRIX_ACCESS_TOKEN, MATRIX_PASSWORD, MATRIX_DEVICE_ID, MATRIX_DEVICE_NAME.",
"Per-account env vars: MATRIX_<ACCOUNT_ID>_HOMESERVER, MATRIX_<ACCOUNT_ID>_USER_ID, MATRIX_<ACCOUNT_ID>_ACCESS_TOKEN, MATRIX_<ACCOUNT_ID>_PASSWORD, MATRIX_<ACCOUNT_ID>_DEVICE_ID, MATRIX_<ACCOUNT_ID>_DEVICE_NAME.",
`Docs: ${formatDocsLink("/channels/matrix", "channels/matrix")}`
].join("\n"), "Matrix setup");
}
function requiresMatrixPrivateNetworkOptIn(homeserver) {
try {
const parsed = new URL(homeserver);
return parsed.protocol === "http:" && !isPrivateOrLoopbackHost(parsed.hostname);
} catch {
return false;
}
}
async function promptMatrixAllowFrom(params) {
const { cfg, prompter } = params;
const accountId = resolveMatrixOnboardingAccountId(cfg, params.accountId);
const existingConfig = resolveMatrixAccountConfig({
cfg,
accountId
});
const existingAllowFrom = existingConfig.dm?.allowFrom ?? [];
const canResolve = resolveMatrixAccount({
cfg,
accountId
}).configured;
const isFullUserId = (value) => value.startsWith("@") && value.includes(":");
while (true) {
const parts = splitSetupEntries(await prompter.text({
message: "Matrix allowFrom (full @user:server; display name only if unique)",
placeholder: "@user:server",
initialValue: existingAllowFrom[0] ? String(existingAllowFrom[0]) : void 0,
validate: (value) => normalizeOptionalString(value) ? void 0 : "Required"
}));
const resolvedIds = [];
const pending = [];
const unresolved = [];
const unresolvedNotes = [];
for (const part of parts) {
if (isFullUserId(part)) {
resolvedIds.push(part);
continue;
}
if (!canResolve) {
unresolved.push(part);
continue;
}
pending.push(part);
}
if (pending.length > 0) {
const { resolveMatrixTargets } = await import("./resolve-targets-BW2PUZsc.js");
const results = await resolveMatrixTargets({
cfg,
accountId,
inputs: pending,
kind: "user"
}).catch(() => []);
for (const result of results) {
if (result?.resolved && result.id) {
resolvedIds.push(result.id);
continue;
}
if (result?.input) {
unresolved.push(result.input);
if (result.note) unresolvedNotes.push(`${result.input}: ${result.note}`);
}
}
}
if (unresolved.length > 0) {
const details = unresolvedNotes.length > 0 ? unresolvedNotes : unresolved;
await prompter.note(`Could not resolve:\n${details.join("\n")}\nUse full @user:server IDs.`, "Matrix allowlist");
continue;
}
const unique = mergeAllowFromEntries(existingAllowFrom, resolvedIds);
return updateMatrixAccountConfig(cfg, accountId, { dm: {
...existingConfig.dm,
policy: "allowlist",
allowFrom: unique
} });
}
}
function setMatrixGroupPolicy(cfg, groupPolicy, accountId) {
return updateMatrixAccountConfig(cfg, resolveMatrixOnboardingAccountId(cfg, accountId), { groupPolicy });
}
function setMatrixGroupRooms(cfg, roomKeys, accountId) {
const groups = Object.fromEntries(roomKeys.map((key) => [key, { enabled: true }]));
return updateMatrixAccountConfig(cfg, resolveMatrixOnboardingAccountId(cfg, accountId), {
groups,
rooms: null
});
}
function setMatrixAutoJoin(cfg, autoJoin, autoJoinAllowlist, accountId) {
return updateMatrixAccountConfig(cfg, resolveMatrixOnboardingAccountId(cfg, accountId), {
autoJoin,
autoJoinAllowlist: autoJoin === "allowlist" ? autoJoinAllowlist : null
});
}
async function configureMatrixInviteAutoJoin(params) {
const accountId = resolveMatrixOnboardingAccountId(params.cfg, params.accountId);
const existingConfig = resolveMatrixAccountConfig({
cfg: params.cfg,
accountId
});
const currentPolicy = existingConfig.autoJoin ?? "off";
const currentAllowlist = (existingConfig.autoJoinAllowlist ?? []).map((entry) => String(entry));
const hasExistingConfig = existingConfig.autoJoin !== void 0 || currentAllowlist.length > 0;
await params.prompter.note([
"WARNING: Matrix invite auto-join defaults to off.",
"OpenClaw agents will not join invited rooms or fresh DM-style invites unless you set autoJoin.",
"Choose \"allowlist\" to restrict joins or \"always\" to join every invite."
].join("\n"), "Matrix invite auto-join");
if (!await params.prompter.confirm({
message: hasExistingConfig ? "Update Matrix invite auto-join?" : "Configure Matrix invite auto-join?",
initialValue: hasExistingConfig ? currentPolicy !== "off" : true
})) return params.cfg;
const selectedPolicy = await params.prompter.select({
message: "Matrix invite auto-join",
options: matrixInviteAutoJoinOptions,
initialValue: currentPolicy
});
if (!isMatrixInviteAutoJoinPolicy(selectedPolicy)) throw new Error(`Unsupported Matrix invite auto-join policy: ${String(selectedPolicy)}`);
const policy = selectedPolicy;
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);
}
if (policy === "always") return setMatrixAutoJoin(params.cfg, policy, [], accountId);
while (true) {
const allowlist = normalizeMatrixInviteAutoJoinTargets(splitSetupEntries(await params.prompter.text({
message: "Matrix invite auto-join allowlist (comma-separated)",
placeholder: "!roomId:server, #alias:server, *",
initialValue: currentAllowlist[0] ? currentAllowlist.join(", ") : void 0,
validate: (value) => {
return splitSetupEntries(value).length > 0 ? void 0 : "Required";
}
})));
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(", ")}` : void 0].filter(Boolean).join("\n"), "Matrix invite auto-join");
continue;
}
return setMatrixAutoJoin(params.cfg, "allowlist", allowlist, accountId);
}
}
async function configureMatrixAccessPrompts(params) {
let next = params.cfg;
if (params.forceAllowFrom) next = await promptMatrixAllowFrom({
cfg: next,
prompter: params.prompter,
accountId: params.accountId
});
const existingAccountConfig = resolveMatrixAccountConfig({
cfg: next,
accountId: params.accountId
});
const existingGroups = existingAccountConfig.groups ?? existingAccountConfig.rooms;
const accessConfig = await promptChannelAccessConfig({
prompter: params.prompter,
label: "Matrix rooms",
currentPolicy: existingAccountConfig.groupPolicy ?? "allowlist",
currentEntries: Object.keys(existingGroups ?? {}),
placeholder: "!roomId:server, #alias:server, Project Room",
updatePrompt: Boolean(existingGroups)
});
if (accessConfig) if (accessConfig.policy !== "allowlist") next = setMatrixGroupPolicy(next, accessConfig.policy, params.accountId);
else {
let roomKeys = accessConfig.entries;
if (accessConfig.entries.length > 0) try {
const resolvedIds = [];
const unresolved = [];
for (const entry of accessConfig.entries) {
const trimmed = normalizeOptionalString(entry) ?? "";
if (!trimmed) continue;
const cleaned = trimmed.replace(/^(room|channel):/i, "").trim();
if (cleaned.startsWith("!") && cleaned.includes(":")) {
resolvedIds.push(cleaned);
continue;
}
const { listMatrixDirectoryGroupsLive } = await import("./directory-live-DrRZfNz-.js");
const matches = await listMatrixDirectoryGroupsLive({
cfg: next,
accountId: params.accountId,
query: trimmed,
limit: 10
});
const best = matches.find((match) => normalizeLowercaseStringOrEmpty(match.name) === normalizeLowercaseStringOrEmpty(trimmed)) ?? matches[0];
if (best?.id) resolvedIds.push(best.id);
else unresolved.push(entry);
}
roomKeys = [...resolvedIds, ...unresolved.map((entry) => normalizeOptionalString(entry)).filter((entry) => Boolean(entry))];
if (resolvedIds.length > 0 || unresolved.length > 0) await params.prompter.note([resolvedIds.length > 0 ? `Resolved: ${resolvedIds.join(", ")}` : void 0, unresolved.length > 0 ? `Unresolved (kept as typed): ${unresolved.join(", ")}` : void 0].filter(Boolean).join("\n"), "Matrix rooms");
} catch (err) {
await params.prompter.note(`Room lookup failed; keeping entries as typed. ${String(err)}`, "Matrix rooms");
}
next = setMatrixGroupPolicy(next, "allowlist", params.accountId);
next = setMatrixGroupRooms(next, roomKeys, params.accountId);
}
return await configureMatrixInviteAutoJoin({
cfg: next,
prompter: params.prompter,
accountId: params.accountId
});
}
const dmPolicy = {
label: "Matrix",
channel,
policyKey: "channels.matrix.dm.policy",
allowFromKey: "channels.matrix.dm.allowFrom",
resolveConfigKeys: (cfg, accountId) => {
const effectiveAccountId = resolveMatrixOnboardingAccountId(cfg, accountId);
return {
policyKey: resolveMatrixConfigFieldPath(cfg, effectiveAccountId, "dm.policy"),
allowFromKey: resolveMatrixConfigFieldPath(cfg, effectiveAccountId, "dm.allowFrom")
};
},
getCurrent: (cfg, accountId) => resolveMatrixAccountConfig({
cfg,
accountId: resolveMatrixOnboardingAccountId(cfg, accountId)
}).dm?.policy ?? "pairing",
setPolicy: (cfg, policy, accountId) => setMatrixDmPolicy(cfg, policy, accountId),
promptAllowFrom: promptMatrixAllowFrom
};
async function runMatrixConfigure(params) {
let next = params.cfg;
await ensureMatrixSdkInstalled({
runtime: params.runtime,
confirm: async (message) => await params.prompter.confirm({
message,
initialValue: true
})
});
const defaultAccountId = resolveDefaultMatrixAccountId(next);
let accountId = defaultAccountId || "default";
if (params.intent === "add-account") {
const enteredName = normalizeStringifiedOptionalString(await params.prompter.text({
message: "Matrix account name",
validate: (value) => normalizeOptionalString(value) ? void 0 : "Required"
})) ?? "";
accountId = normalizeAccountId(enteredName);
if (enteredName !== accountId) await params.prompter.note(`Account id will be "${accountId}".`, "Matrix account");
if (accountId !== "default") next = moveSingleMatrixAccountConfigToNamedAccount(next);
next = updateMatrixAccountConfig(next, accountId, {
name: enteredName,
enabled: true
});
} else {
const override = normalizeOptionalString(params.accountOverrides?.[channel]);
if (override) accountId = normalizeAccountId(override);
else if (params.shouldPromptAccountIds) accountId = await promptAccountId({
cfg: next,
prompter: params.prompter,
label: "Matrix",
currentId: accountId,
listAccountIds: (inputCfg) => listMatrixAccountIds(inputCfg),
defaultAccountId
});
}
const existing = resolveMatrixAccountConfig({
cfg: next,
accountId
});
if (!resolveMatrixAccount({
cfg: next,
accountId
}).configured) await noteMatrixAuthHelp(params.prompter);
const envReadiness = resolveMatrixEnvAuthReadiness(accountId, process.env);
const envReady = envReadiness.ready;
const envHomeserver = envReadiness.homeserver;
const envUserId = envReadiness.userId;
if (envReady && !existing.homeserver && !existing.userId && !existing.accessToken && !existing.password) {
if (await params.prompter.confirm({
message: `Matrix env vars detected (${envReadiness.sourceHint}). Use env values?`,
initialValue: true
})) {
next = updateMatrixAccountConfig(next, accountId, { enabled: true });
next = await configureMatrixAccessPrompts({
cfg: next,
prompter: params.prompter,
forceAllowFrom: params.forceAllowFrom,
accountId
});
return {
cfg: next,
accountId
};
}
}
const homeserver = normalizeStringifiedOptionalString(await params.prompter.text({
message: "Matrix homeserver URL",
initialValue: existing.homeserver ?? envHomeserver,
validate: (value) => {
try {
validateMatrixHomeserverUrl(value, { allowPrivateNetwork: true });
return;
} catch (error) {
return error instanceof Error ? error.message : "Invalid Matrix homeserver URL";
}
}
})) ?? "";
const requiresAllowPrivateNetwork = requiresMatrixPrivateNetworkOptIn(homeserver);
const shouldPromptAllowPrivateNetwork = requiresAllowPrivateNetwork || isPrivateNetworkOptInEnabled(existing);
const allowPrivateNetwork = shouldPromptAllowPrivateNetwork ? await params.prompter.confirm({
message: "Allow private/internal Matrix homeserver traffic for this account?",
initialValue: isPrivateNetworkOptInEnabled(existing) || requiresAllowPrivateNetwork
}) : false;
if (requiresAllowPrivateNetwork && !allowPrivateNetwork) throw new Error("Matrix homeserver requires explicit private-network opt-in");
await resolveValidatedMatrixHomeserverUrl(homeserver, { dangerouslyAllowPrivateNetwork: allowPrivateNetwork });
let accessToken = existing.accessToken;
let password = existing.password;
let userId = existing.userId ?? "";
if (hasConfiguredSecretInput(accessToken) || hasConfiguredSecretInput(password)) {
if (!await params.prompter.confirm({
message: "Matrix credentials already configured. Keep them?",
initialValue: true
})) {
accessToken = void 0;
password = void 0;
userId = "";
}
}
if (!hasConfiguredSecretInput(accessToken) && !hasConfiguredSecretInput(password)) if (await params.prompter.select({
message: "Matrix auth method",
options: [{
value: "token",
label: "Access token (user ID fetched automatically)"
}, {
value: "password",
label: "Password (requires user ID)"
}]
}) === "token") {
accessToken = normalizeStringifiedOptionalString(await params.prompter.text({
message: "Matrix access token",
validate: (value) => normalizeOptionalString(value) ? void 0 : "Required"
})) ?? "";
password = void 0;
userId = "";
} else {
userId = normalizeStringifiedOptionalString(await params.prompter.text({
message: "Matrix user ID",
initialValue: existing.userId ?? envUserId,
validate: (value) => {
const raw = normalizeOptionalString(value) ?? "";
if (!raw) return "Required";
if (!raw.startsWith("@")) return "Matrix user IDs should start with @";
if (!raw.includes(":")) return "Matrix user IDs should include a server (:server)";
}
})) ?? "";
password = normalizeStringifiedOptionalString(await params.prompter.text({
message: "Matrix password",
validate: (value) => normalizeOptionalString(value) ? void 0 : "Required"
})) ?? "";
accessToken = void 0;
}
const deviceName = normalizeStringifiedOptionalString(await params.prompter.text({
message: "Matrix device name (optional)",
initialValue: existing.deviceName ?? "OpenClaw Gateway"
})) ?? "";
const enableEncryption = await params.prompter.confirm({
message: "Enable end-to-end encryption (E2EE)?",
initialValue: existing.encryption ?? false
});
next = updateMatrixAccountConfig(next, accountId, {
enabled: true,
homeserver,
...shouldPromptAllowPrivateNetwork ? { allowPrivateNetwork: allowPrivateNetwork ? true : null } : {},
userId: userId || null,
accessToken: accessToken ?? null,
password: password ?? null,
deviceName: deviceName || null,
encryption: enableEncryption
});
next = await configureMatrixAccessPrompts({
cfg: next,
prompter: params.prompter,
forceAllowFrom: params.forceAllowFrom,
accountId
});
return {
cfg: next,
accountId
};
}
const matrixOnboardingAdapter = {
channel,
getStatus: async ({ cfg, accountOverrides }) => {
const resolvedCfg = cfg;
const sdkReady = isMatrixSdkAvailable();
if (!accountOverrides[channel] && requiresExplicitMatrixDefaultAccount(resolvedCfg)) return {
channel,
configured: false,
statusLines: ["Matrix: set \"channels.matrix.defaultAccount\" to select a named account"],
selectionHint: !sdkReady ? "install Matrix deps" : "set defaultAccount"
};
const configured = resolveMatrixAccount({
cfg: resolvedCfg,
accountId: resolveMatrixOnboardingAccountId(resolvedCfg, accountOverrides[channel])
}).configured;
return {
channel,
configured,
statusLines: [`Matrix: ${configured ? "configured" : "needs homeserver + access token or password"}`],
selectionHint: !sdkReady ? "install Matrix deps" : configured ? "configured" : "needs auth"
};
},
configure: async ({ cfg, runtime, prompter, forceAllowFrom, accountOverrides, shouldPromptAccountIds }) => await runMatrixConfigure({
cfg,
runtime,
prompter,
forceAllowFrom,
accountOverrides,
shouldPromptAccountIds,
intent: "update"
}),
configureInteractive: async ({ cfg, runtime, prompter, forceAllowFrom, accountOverrides, shouldPromptAccountIds, configured }) => {
if (!configured) return await runMatrixConfigure({
cfg,
runtime,
prompter,
forceAllowFrom,
accountOverrides,
shouldPromptAccountIds,
intent: "update"
});
const action = await prompter.select({
message: "Matrix already configured. What do you want to do?",
options: [
{
value: "update",
label: "Modify settings"
},
{
value: "add-account",
label: "Add account"
},
{
value: "skip",
label: "Skip (leave as-is)"
}
],
initialValue: "update"
});
if (action === "skip") return "skip";
return await runMatrixConfigure({
cfg,
runtime,
prompter,
forceAllowFrom,
accountOverrides,
shouldPromptAccountIds,
intent: action === "add-account" ? "add-account" : "update"
});
},
afterConfigWritten: async ({ previousCfg, cfg, accountId, runtime }) => {
const { runMatrixSetupBootstrapAfterConfigWrite } = await import("./setup-bootstrap-CnULAw1Q.js");
await runMatrixSetupBootstrapAfterConfigWrite({
previousCfg,
cfg,
accountId,
runtime
});
},
dmPolicy,
disable: (cfg) => ({
...cfg,
channels: {
...cfg.channels,
matrix: {
...cfg.channels?.["matrix"],
enabled: false
}
}
})
};
//#endregion
export { matrixOnboardingAdapter as t };