matrix: align bundled channel metadata

This commit is contained in:
Gustavo Madeira Santana 2026-04-05 20:16:57 -04:00
parent 89c8a1c36a
commit 4031e4b92d
8 changed files with 134 additions and 4 deletions

View File

@ -0,0 +1,59 @@
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import type { OpenClawConfig } from "openclaw/plugin-sdk/config-runtime";
import { resolveStateDir } from "openclaw/plugin-sdk/state-paths";
import {
resolveMatrixCredentialsDir,
resolveMatrixCredentialsFilename,
} from "./src/storage-paths.js";
type MatrixAuthPresenceParams =
| {
cfg: OpenClawConfig;
env?: NodeJS.ProcessEnv;
}
| OpenClawConfig;
function listMatrixCredentialPaths(
_cfg: OpenClawConfig,
env: NodeJS.ProcessEnv = process.env,
): readonly string[] {
const credentialsDir = resolveMatrixCredentialsDir(resolveStateDir(env, os.homedir));
const paths = new Set<string>([
resolveMatrixCredentialsFilename(),
resolveMatrixCredentialsFilename("default"),
]);
try {
const entries = fs.readdirSync(credentialsDir, { withFileTypes: true });
for (const entry of entries) {
if (entry.isFile() && /^credentials(?:-[a-z0-9._-]+)?\.json$/i.test(entry.name)) {
paths.add(entry.name);
}
}
} catch {
// Missing credentials directories mean no persisted Matrix auth state.
}
return [...paths].map((filename) => path.join(credentialsDir, filename));
}
export function hasAnyMatrixAuth(
params: MatrixAuthPresenceParams,
env: NodeJS.ProcessEnv = process.env,
): boolean {
const cfg =
params && typeof params === "object" && "cfg" in params
? params.cfg
: (params as OpenClawConfig);
const resolvedEnv =
params && typeof params === "object" && "cfg" in params ? (params.env ?? env) : env;
return listMatrixCredentialPaths(cfg, resolvedEnv).some((filePath) => {
try {
return fs.existsSync(filePath);
} catch {
return false;
}
});
}

View File

@ -0,0 +1,3 @@
// Keep bundled channel entry imports narrow so bootstrap/discovery paths do
// not drag Matrix setup and onboarding helpers into lightweight plugin loads.
export { matrixPlugin } from "./src/channel.js";

View File

@ -8,7 +8,7 @@ export default defineBundledChannelEntry({
description: "Matrix channel plugin (matrix-js-sdk)",
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
specifier: "./channel-plugin-api.js",
exportName: "matrixPlugin",
},
runtime: {

View File

@ -35,7 +35,11 @@
"docsLabel": "matrix",
"blurb": "open protocol; install the plugin to enable.",
"order": 70,
"quickstartAllowFrom": true
"quickstartAllowFrom": true,
"persistedAuthState": {
"specifier": "./auth-presence",
"exportName": "hasAnyMatrixAuth"
}
},
"install": {
"npmSpec": "@openclaw/matrix",

View File

@ -3,7 +3,7 @@ import { defineBundledChannelSetupEntry } from "openclaw/plugin-sdk/channel-entr
export default defineBundledChannelSetupEntry({
importMetaUrl: import.meta.url,
plugin: {
specifier: "./api.js",
specifier: "./channel-plugin-api.js",
exportName: "matrixPlugin",
},
});

View File

@ -71,4 +71,26 @@ describe("config presence", () => {
expectedConfigured: true,
});
});
it("detects persisted Matrix credentials without config or env", () => {
const stateDir = makeTempStateDir();
fs.mkdirSync(path.join(stateDir, "credentials", "matrix"), { recursive: true });
fs.writeFileSync(
path.join(stateDir, "credentials", "matrix", "credentials.json"),
JSON.stringify({
homeserver: "https://matrix.example.org",
userId: "@bot:example.org",
accessToken: "token",
}),
"utf8",
);
const env = { OPENCLAW_STATE_DIR: stateDir } as NodeJS.ProcessEnv;
expectPotentialConfiguredChannelCase({
cfg: {},
env,
expectedIds: ["matrix"],
expectedConfigured: true,
});
});
});

View File

@ -1,6 +1,26 @@
import { describe, expect, it } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, describe, expect, it } from "vitest";
import { isChannelConfigured } from "./channel-configured.js";
const tempDirs: string[] = [];
function makeTempStateDir() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "openclaw-channel-configured-"));
tempDirs.push(dir);
return dir;
}
afterEach(() => {
while (tempDirs.length > 0) {
const dir = tempDirs.pop();
if (dir) {
fs.rmSync(dir, { recursive: true, force: true });
}
}
});
describe("isChannelConfigured", () => {
it("detects Telegram env configuration through the package metadata seam", () => {
expect(isChannelConfigured({}, "telegram", { TELEGRAM_BOT_TOKEN: "token" })).toBe(true);
@ -39,4 +59,20 @@ describe("isChannelConfigured", () => {
),
).toBe(true);
});
it("detects persisted Matrix credentials through package metadata", () => {
const stateDir = makeTempStateDir();
fs.mkdirSync(path.join(stateDir, "credentials", "matrix"), { recursive: true });
fs.writeFileSync(
path.join(stateDir, "credentials", "matrix", "credentials-ops.json"),
JSON.stringify({
homeserver: "https://matrix.example.org",
userId: "@ops:example.org",
accessToken: "token",
}),
"utf8",
);
expect(isChannelConfigured({}, "matrix", { OPENCLAW_STATE_DIR: stateDir })).toBe(true);
});
});

View File

@ -105,6 +105,12 @@ describe("bundled plugin metadata", () => {
specifier: "./auth-presence",
exportName: "hasAnyWhatsAppAuth",
});
const matrix = listBundledPluginMetadata().find((entry) => entry.dirName === "matrix");
expect(matrix?.packageManifest?.channel?.persistedAuthState).toEqual({
specifier: "./auth-presence",
exportName: "hasAnyMatrixAuth",
});
});
it("keeps bundled configured-state metadata on channel package manifests", () => {