mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
refactor: move doctor capabilities to channel manifests
This commit is contained in:
@@ -43,7 +43,13 @@
|
||||
],
|
||||
"order": 55,
|
||||
"systemImage": "message.badge",
|
||||
"markdownCapable": true
|
||||
"markdownCapable": true,
|
||||
"doctorCapabilities": {
|
||||
"dmAllowFromMode": "nestedOnly",
|
||||
"groupModel": "route",
|
||||
"groupAllowFromFallbackToAllowFrom": false,
|
||||
"warnOnEmptyGroupSenderAllowlist": false
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/googlechat",
|
||||
|
||||
@@ -42,6 +42,12 @@
|
||||
"blurb": "open protocol; install the plugin to enable.",
|
||||
"order": 70,
|
||||
"quickstartAllowFrom": true,
|
||||
"doctorCapabilities": {
|
||||
"dmAllowFromMode": "nestedOnly",
|
||||
"groupModel": "sender",
|
||||
"groupAllowFromFallbackToAllowFrom": false,
|
||||
"warnOnEmptyGroupSenderAllowlist": true
|
||||
},
|
||||
"persistedAuthState": {
|
||||
"specifier": "./auth-presence",
|
||||
"exportName": "hasAnyMatrixAuth"
|
||||
|
||||
@@ -40,7 +40,13 @@
|
||||
"aliases": [
|
||||
"teams"
|
||||
],
|
||||
"order": 60
|
||||
"order": 60,
|
||||
"doctorCapabilities": {
|
||||
"dmAllowFromMode": "topOnly",
|
||||
"groupModel": "hybrid",
|
||||
"groupAllowFromFallbackToAllowFrom": false,
|
||||
"warnOnEmptyGroupSenderAllowlist": true
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/msteams",
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
"zlu"
|
||||
],
|
||||
"order": 85,
|
||||
"quickstartAllowFrom": false
|
||||
"quickstartAllowFrom": false,
|
||||
"doctorCapabilities": {
|
||||
"dmAllowFromMode": "topOnly",
|
||||
"groupModel": "hybrid",
|
||||
"groupAllowFromFallbackToAllowFrom": false,
|
||||
"warnOnEmptyGroupSenderAllowlist": false
|
||||
}
|
||||
},
|
||||
"install": {
|
||||
"npmSpec": "@openclaw/zalouser",
|
||||
|
||||
@@ -2,7 +2,7 @@ import { describe, expect, it } from "vitest";
|
||||
import { getDoctorChannelCapabilities } from "./channel-capabilities.js";
|
||||
|
||||
describe("doctor channel capabilities", () => {
|
||||
it("returns nested route semantics for googlechat before plugin metadata loads", () => {
|
||||
it("returns nested route semantics from googlechat plugin metadata", () => {
|
||||
expect(getDoctorChannelCapabilities("googlechat")).toEqual({
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "route",
|
||||
@@ -11,7 +11,7 @@ describe("doctor channel capabilities", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("returns built-in capability overrides for matrix", () => {
|
||||
it("returns capability overrides from matrix plugin metadata", () => {
|
||||
expect(getDoctorChannelCapabilities("matrix")).toEqual({
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "sender",
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { getBundledChannelPlugin } from "../../channels/plugins/bundled.js";
|
||||
import { getChannelPlugin } from "../../channels/plugins/index.js";
|
||||
import { normalizeAnyChannelId } from "../../channels/registry.js";
|
||||
import { findBundledPackageChannelMetadata } from "../../plugins/bundled-package-channel-metadata.js";
|
||||
import type { PluginPackageChannelDoctorCapabilities } from "../../plugins/manifest.js";
|
||||
import type { AllowFromMode } from "./shared/allow-from-mode.types.js";
|
||||
|
||||
export type DoctorGroupModel = "sender" | "route" | "hybrid";
|
||||
@@ -19,60 +21,46 @@ const DEFAULT_DOCTOR_CHANNEL_CAPABILITIES: DoctorChannelCapabilities = {
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
};
|
||||
|
||||
const STATIC_DOCTOR_CHANNEL_CAPABILITIES: Readonly<Record<string, DoctorChannelCapabilities>> = {
|
||||
googlechat: {
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "route",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
},
|
||||
matrix: {
|
||||
dmAllowFromMode: "nestedOnly",
|
||||
groupModel: "sender",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
},
|
||||
msteams: {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "hybrid",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: true,
|
||||
},
|
||||
zalouser: {
|
||||
dmAllowFromMode: "topOnly",
|
||||
groupModel: "hybrid",
|
||||
groupAllowFromFallbackToAllowFrom: false,
|
||||
warnOnEmptyGroupSenderAllowlist: false,
|
||||
},
|
||||
};
|
||||
function mergeDoctorChannelCapabilities(
|
||||
capabilities?: PluginPackageChannelDoctorCapabilities,
|
||||
): DoctorChannelCapabilities {
|
||||
return {
|
||||
dmAllowFromMode:
|
||||
capabilities?.dmAllowFromMode ?? DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.dmAllowFromMode,
|
||||
groupModel: capabilities?.groupModel ?? DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.groupModel,
|
||||
groupAllowFromFallbackToAllowFrom:
|
||||
capabilities?.groupAllowFromFallbackToAllowFrom ??
|
||||
DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.groupAllowFromFallbackToAllowFrom,
|
||||
warnOnEmptyGroupSenderAllowlist:
|
||||
capabilities?.warnOnEmptyGroupSenderAllowlist ??
|
||||
DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.warnOnEmptyGroupSenderAllowlist,
|
||||
};
|
||||
}
|
||||
|
||||
function getManifestDoctorCapabilities(
|
||||
channelId: string,
|
||||
): PluginPackageChannelDoctorCapabilities | undefined {
|
||||
return findBundledPackageChannelMetadata(channelId)?.doctorCapabilities;
|
||||
}
|
||||
|
||||
export function getDoctorChannelCapabilities(channelName?: string): DoctorChannelCapabilities {
|
||||
if (!channelName) {
|
||||
return DEFAULT_DOCTOR_CHANNEL_CAPABILITIES;
|
||||
}
|
||||
const staticCapabilities = STATIC_DOCTOR_CHANNEL_CAPABILITIES[channelName];
|
||||
if (staticCapabilities) {
|
||||
return staticCapabilities;
|
||||
|
||||
const manifestCapabilities = getManifestDoctorCapabilities(channelName);
|
||||
if (manifestCapabilities) {
|
||||
return mergeDoctorChannelCapabilities(manifestCapabilities);
|
||||
}
|
||||
const registeredChannelId = normalizeAnyChannelId(channelName);
|
||||
if (!registeredChannelId) {
|
||||
|
||||
const channelId = normalizeAnyChannelId(channelName);
|
||||
if (!channelId) {
|
||||
return DEFAULT_DOCTOR_CHANNEL_CAPABILITIES;
|
||||
}
|
||||
const pluginDoctor =
|
||||
getChannelPlugin(registeredChannelId)?.doctor ??
|
||||
getBundledChannelPlugin(registeredChannelId)?.doctor;
|
||||
getChannelPlugin(channelId)?.doctor ?? getBundledChannelPlugin(channelId)?.doctor;
|
||||
if (pluginDoctor) {
|
||||
return {
|
||||
dmAllowFromMode:
|
||||
pluginDoctor.dmAllowFromMode ?? DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.dmAllowFromMode,
|
||||
groupModel: pluginDoctor.groupModel ?? DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.groupModel,
|
||||
groupAllowFromFallbackToAllowFrom:
|
||||
pluginDoctor.groupAllowFromFallbackToAllowFrom ??
|
||||
DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.groupAllowFromFallbackToAllowFrom,
|
||||
warnOnEmptyGroupSenderAllowlist:
|
||||
pluginDoctor.warnOnEmptyGroupSenderAllowlist ??
|
||||
DEFAULT_DOCTOR_CHANNEL_CAPABILITIES.warnOnEmptyGroupSenderAllowlist,
|
||||
};
|
||||
return mergeDoctorChannelCapabilities(pluginDoctor);
|
||||
}
|
||||
return DEFAULT_DOCTOR_CHANNEL_CAPABILITIES;
|
||||
return mergeDoctorChannelCapabilities(getManifestDoctorCapabilities(channelId));
|
||||
}
|
||||
|
||||
58
src/plugins/bundled-package-channel-metadata.ts
Normal file
58
src/plugins/bundled-package-channel-metadata.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { resolveBundledPluginScanDir } from "./bundled-plugin-scan.js";
|
||||
import {
|
||||
getPackageManifestMetadata,
|
||||
type PackageManifest,
|
||||
type PluginPackageChannel,
|
||||
} from "./manifest.js";
|
||||
|
||||
const PACKAGE_ROOT = fileURLToPath(new URL("../..", import.meta.url));
|
||||
const CURRENT_MODULE_PATH = fileURLToPath(import.meta.url);
|
||||
const RUNNING_FROM_BUILT_ARTIFACT =
|
||||
CURRENT_MODULE_PATH.includes(`${path.sep}dist${path.sep}`) ||
|
||||
CURRENT_MODULE_PATH.includes(`${path.sep}dist-runtime${path.sep}`);
|
||||
|
||||
let bundledPackageChannelMetadataCache: readonly PluginPackageChannel[] | undefined;
|
||||
|
||||
function readPackageManifest(pluginDir: string): PackageManifest | undefined {
|
||||
const packagePath = path.join(pluginDir, "package.json");
|
||||
if (!fs.existsSync(packagePath)) {
|
||||
return undefined;
|
||||
}
|
||||
try {
|
||||
return JSON.parse(fs.readFileSync(packagePath, "utf-8")) as PackageManifest;
|
||||
} catch {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
|
||||
function listBundledPackageChannelMetadata(): readonly PluginPackageChannel[] {
|
||||
if (bundledPackageChannelMetadataCache) {
|
||||
return bundledPackageChannelMetadataCache;
|
||||
}
|
||||
const scanDir = resolveBundledPluginScanDir({
|
||||
packageRoot: PACKAGE_ROOT,
|
||||
runningFromBuiltArtifact: RUNNING_FROM_BUILT_ARTIFACT,
|
||||
});
|
||||
if (!scanDir || !fs.existsSync(scanDir)) {
|
||||
bundledPackageChannelMetadataCache = [];
|
||||
return bundledPackageChannelMetadataCache;
|
||||
}
|
||||
bundledPackageChannelMetadataCache = fs
|
||||
.readdirSync(scanDir, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => readPackageManifest(path.join(scanDir, entry.name)))
|
||||
.map((manifest) => getPackageManifestMetadata(manifest)?.channel)
|
||||
.filter((channel): channel is PluginPackageChannel => Boolean(channel?.id));
|
||||
return bundledPackageChannelMetadataCache;
|
||||
}
|
||||
|
||||
export function findBundledPackageChannelMetadata(
|
||||
channelId: string,
|
||||
): PluginPackageChannel | undefined {
|
||||
return listBundledPackageChannelMetadata().find(
|
||||
(channel) => channel.id === channelId || channel.aliases?.includes(channelId),
|
||||
);
|
||||
}
|
||||
@@ -959,6 +959,14 @@ export type PluginPackageChannel = {
|
||||
specifier?: string;
|
||||
exportName?: string;
|
||||
};
|
||||
doctorCapabilities?: PluginPackageChannelDoctorCapabilities;
|
||||
};
|
||||
|
||||
export type PluginPackageChannelDoctorCapabilities = {
|
||||
dmAllowFromMode?: "topOnly" | "topOrNested" | "nestedOnly";
|
||||
groupModel?: "sender" | "route" | "hybrid";
|
||||
groupAllowFromFallbackToAllowFrom?: boolean;
|
||||
warnOnEmptyGroupSenderAllowlist?: boolean;
|
||||
};
|
||||
|
||||
export type PluginPackageInstall = {
|
||||
|
||||
Reference in New Issue
Block a user