mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
perf(channels): read bundled channel metadata directly
This commit is contained in:
114
src/channels/bundled-channel-catalog-read.ts
Normal file
114
src/channels/bundled-channel-catalog-read.ts
Normal file
@@ -0,0 +1,114 @@
|
||||
import fs from "node:fs";
|
||||
import path from "node:path";
|
||||
import { resolveOpenClawPackageRootSync } from "../infra/openclaw-root.js";
|
||||
import type { PluginPackageChannel } from "../plugins/manifest.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
|
||||
type ChannelCatalogEntryLike = {
|
||||
openclaw?: {
|
||||
channel?: PluginPackageChannel;
|
||||
};
|
||||
};
|
||||
|
||||
export type BundledChannelCatalogEntry = {
|
||||
id: string;
|
||||
channel: PluginPackageChannel;
|
||||
aliases: readonly string[];
|
||||
order: number;
|
||||
};
|
||||
|
||||
const OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH = path.join("dist", "channel-catalog.json");
|
||||
|
||||
function listPackageRoots(): string[] {
|
||||
return [
|
||||
resolveOpenClawPackageRootSync({ cwd: process.cwd() }),
|
||||
resolveOpenClawPackageRootSync({ moduleUrl: import.meta.url }),
|
||||
].filter((entry, index, all): entry is string => Boolean(entry) && all.indexOf(entry) === index);
|
||||
}
|
||||
|
||||
function listBundledExtensionPackageJsonPaths(): string[] {
|
||||
for (const packageRoot of listPackageRoots()) {
|
||||
const extensionsRoot = path.join(packageRoot, "extensions");
|
||||
if (!fs.existsSync(extensionsRoot)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
return fs
|
||||
.readdirSync(extensionsRoot, { withFileTypes: true })
|
||||
.filter((entry) => entry.isDirectory())
|
||||
.map((entry) => path.join(extensionsRoot, entry.name, "package.json"))
|
||||
.filter((entry) => fs.existsSync(entry));
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function readBundledExtensionCatalogEntriesSync(): ChannelCatalogEntryLike[] {
|
||||
const entries: ChannelCatalogEntryLike[] = [];
|
||||
for (const packageJsonPath of listBundledExtensionPackageJsonPaths()) {
|
||||
try {
|
||||
const payload = JSON.parse(
|
||||
fs.readFileSync(packageJsonPath, "utf8"),
|
||||
) as ChannelCatalogEntryLike;
|
||||
entries.push(payload);
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return entries;
|
||||
}
|
||||
|
||||
function readOfficialCatalogFileSync(): ChannelCatalogEntryLike[] {
|
||||
for (const packageRoot of listPackageRoots()) {
|
||||
const candidate = path.join(packageRoot, OFFICIAL_CHANNEL_CATALOG_RELATIVE_PATH);
|
||||
if (!fs.existsSync(candidate)) {
|
||||
continue;
|
||||
}
|
||||
try {
|
||||
const payload = JSON.parse(fs.readFileSync(candidate, "utf8")) as {
|
||||
entries?: unknown;
|
||||
};
|
||||
return Array.isArray(payload.entries) ? (payload.entries as ChannelCatalogEntryLike[]) : [];
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function toBundledChannelEntry(entry: ChannelCatalogEntryLike): BundledChannelCatalogEntry | null {
|
||||
const channel = entry.openclaw?.channel;
|
||||
const id = normalizeOptionalLowercaseString(channel?.id);
|
||||
if (!id || !channel) {
|
||||
return null;
|
||||
}
|
||||
const aliases = Array.isArray(channel.aliases)
|
||||
? channel.aliases
|
||||
.map((alias) => normalizeOptionalLowercaseString(alias))
|
||||
.filter((alias): alias is string => Boolean(alias))
|
||||
: [];
|
||||
const order =
|
||||
typeof channel.order === "number" && Number.isFinite(channel.order)
|
||||
? channel.order
|
||||
: Number.MAX_SAFE_INTEGER;
|
||||
return {
|
||||
id,
|
||||
channel,
|
||||
aliases,
|
||||
order,
|
||||
};
|
||||
}
|
||||
|
||||
export function listBundledChannelCatalogEntries(): BundledChannelCatalogEntry[] {
|
||||
const bundledEntries = readBundledExtensionCatalogEntriesSync()
|
||||
.map((entry) => toBundledChannelEntry(entry))
|
||||
.filter((entry): entry is BundledChannelCatalogEntry => Boolean(entry));
|
||||
if (bundledEntries.length > 0) {
|
||||
return bundledEntries;
|
||||
}
|
||||
return readOfficialCatalogFileSync()
|
||||
.map((entry) => toBundledChannelEntry(entry))
|
||||
.filter((entry): entry is BundledChannelCatalogEntry => Boolean(entry));
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
import { listChannelCatalogEntries } from "../plugins/channel-catalog-registry.js";
|
||||
import type { PluginPackageChannel } from "../plugins/manifest.js";
|
||||
import { normalizeOptionalString } from "../shared/string-coerce.js";
|
||||
import { listBundledChannelCatalogEntries } from "./bundled-channel-catalog-read.js";
|
||||
import { CHAT_CHANNEL_ORDER, type ChatChannelId } from "./ids.js";
|
||||
import { resolveChannelExposure } from "./plugins/exposure.js";
|
||||
import type { ChannelMeta } from "./plugins/types.core.js";
|
||||
@@ -65,12 +65,8 @@ function toChatChannelMeta(params: {
|
||||
export function buildChatChannelMetaById(): Record<ChatChannelId, ChatChannelMeta> {
|
||||
const entries = new Map<ChatChannelId, ChatChannelMeta>();
|
||||
|
||||
for (const entry of listChannelCatalogEntries({ origin: "bundled" })) {
|
||||
const channel = entry.channel;
|
||||
if (!channel) {
|
||||
continue;
|
||||
}
|
||||
const rawId = normalizeOptionalString(channel.id);
|
||||
for (const entry of listBundledChannelCatalogEntries()) {
|
||||
const rawId = normalizeOptionalString(entry.id);
|
||||
if (!rawId || !CHAT_CHANNEL_ID_SET.has(rawId)) {
|
||||
continue;
|
||||
}
|
||||
@@ -79,7 +75,7 @@ export function buildChatChannelMetaById(): Record<ChatChannelId, ChatChannelMet
|
||||
id,
|
||||
toChatChannelMeta({
|
||||
id,
|
||||
channel,
|
||||
channel: entry.channel,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { listChannelCatalogEntries } from "../plugins/channel-catalog-registry.js";
|
||||
import { normalizeOptionalLowercaseString } from "../shared/string-coerce.js";
|
||||
import { listBundledChannelCatalogEntries } from "./bundled-channel-catalog-read.js";
|
||||
|
||||
export type ChatChannelId = string;
|
||||
|
||||
@@ -10,23 +10,12 @@ type BundledChatChannelEntry = {
|
||||
};
|
||||
|
||||
function listBundledChatChannelEntries(): BundledChatChannelEntry[] {
|
||||
return listChannelCatalogEntries({ origin: "bundled" })
|
||||
.flatMap(({ channel }) => {
|
||||
const id = normalizeOptionalLowercaseString(channel.id);
|
||||
if (!id) {
|
||||
return [];
|
||||
}
|
||||
const aliases = (channel.aliases ?? [])
|
||||
.map((alias) => normalizeOptionalLowercaseString(alias))
|
||||
.filter((alias): alias is string => Boolean(alias));
|
||||
return [
|
||||
{
|
||||
id,
|
||||
aliases,
|
||||
order: typeof channel.order === "number" ? channel.order : Number.MAX_SAFE_INTEGER,
|
||||
},
|
||||
];
|
||||
})
|
||||
return listBundledChannelCatalogEntries()
|
||||
.map((entry) => ({
|
||||
id: normalizeOptionalLowercaseString(entry.id) ?? entry.id,
|
||||
aliases: entry.aliases,
|
||||
order: entry.order,
|
||||
}))
|
||||
.toSorted(
|
||||
(left, right) =>
|
||||
left.order - right.order || left.id.localeCompare(right.id, "en", { sensitivity: "base" }),
|
||||
|
||||
Reference in New Issue
Block a user