mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-18 04:31:10 +00:00
perf(test): trim bundled channel bootstrap
This commit is contained in:
@@ -3,7 +3,7 @@ import {
|
||||
loadBundledEntryExportSync,
|
||||
} from "openclaw/plugin-sdk/channel-entry-contract";
|
||||
import type { PluginRuntime } from "./api.js";
|
||||
import type { ResolvedNostrAccount } from "./src/types.js";
|
||||
import type { ResolvedNostrAccount } from "./api.js";
|
||||
|
||||
function createNostrProfileHttpHandler() {
|
||||
return loadBundledEntryExportSync<
|
||||
|
||||
@@ -7,6 +7,7 @@ import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js";
|
||||
|
||||
afterEach(() => {
|
||||
vi.resetModules();
|
||||
vi.doUnmock("../../plugins/bundled-plugin-metadata.js");
|
||||
vi.doUnmock("../../plugins/discovery.js");
|
||||
vi.doUnmock("../../plugins/manifest-registry.js");
|
||||
vi.doUnmock("../../infra/boundary-file-read.js");
|
||||
@@ -19,18 +20,14 @@ describe("bundled channel entry shape guards", () => {
|
||||
.map((plugin) => plugin.rootDir);
|
||||
|
||||
it("treats missing bundled discovery results as empty", async () => {
|
||||
vi.doMock("../../plugins/discovery.js", () => ({
|
||||
discoverOpenClawPlugins: () => ({
|
||||
candidates: [],
|
||||
diagnostics: [],
|
||||
}),
|
||||
}));
|
||||
vi.doMock("../../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: () => ({
|
||||
plugins: [],
|
||||
diagnostics: [],
|
||||
}),
|
||||
}));
|
||||
vi.doMock("../../plugins/bundled-plugin-metadata.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../../plugins/bundled-plugin-metadata.js")>();
|
||||
return {
|
||||
...actual,
|
||||
listBundledPluginMetadata: () => [],
|
||||
};
|
||||
});
|
||||
|
||||
const bundled = await importFreshModule<typeof import("./bundled.js")>(
|
||||
import.meta.url,
|
||||
@@ -181,32 +178,28 @@ describe("bundled channel entry shape guards", () => {
|
||||
const modulePath = path.join(pluginDir, "index.js");
|
||||
fs.writeFileSync(modulePath, "export {};\n", "utf8");
|
||||
|
||||
vi.doMock("../../plugins/discovery.js", () => ({
|
||||
discoverOpenClawPlugins: () => ({
|
||||
candidates: [
|
||||
vi.doMock("../../plugins/bundled-plugin-metadata.js", async (importOriginal) => {
|
||||
const actual =
|
||||
await importOriginal<typeof import("../../plugins/bundled-plugin-metadata.js")>();
|
||||
return {
|
||||
...actual,
|
||||
listBundledPluginMetadata: () => [
|
||||
{
|
||||
rootDir: pluginDir,
|
||||
source: modulePath,
|
||||
packageManifest: { extensions: ["./index.js"] },
|
||||
dirName: "alpha",
|
||||
idHint: "alpha",
|
||||
source: {
|
||||
source: "./index.js",
|
||||
built: "./index.js",
|
||||
},
|
||||
manifest: {
|
||||
id: "alpha",
|
||||
channels: ["alpha"],
|
||||
},
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
}),
|
||||
}));
|
||||
vi.doMock("../../plugins/manifest-registry.js", () => ({
|
||||
loadPluginManifestRegistry: () => ({
|
||||
plugins: [
|
||||
{
|
||||
id: "alpha",
|
||||
rootDir: pluginDir,
|
||||
origin: "bundled",
|
||||
channels: ["alpha"],
|
||||
source: modulePath,
|
||||
},
|
||||
],
|
||||
diagnostics: [],
|
||||
}),
|
||||
}));
|
||||
resolveBundledPluginGeneratedPath: () => modulePath,
|
||||
};
|
||||
});
|
||||
vi.doMock("../../infra/boundary-file-read.js", () => ({
|
||||
openBoundaryFileSync: ({ absolutePath }: { absolutePath: string }) => ({
|
||||
ok: true,
|
||||
@@ -214,6 +207,9 @@ describe("bundled channel entry shape guards", () => {
|
||||
fd: fs.openSync(absolutePath, "r"),
|
||||
}),
|
||||
}));
|
||||
vi.doMock("../../plugins/channel-catalog-registry.js", () => ({
|
||||
listChannelCatalogEntries: () => [],
|
||||
}));
|
||||
|
||||
let reentered = false;
|
||||
vi.doMock("jiti", () => ({
|
||||
|
||||
@@ -1,16 +1,17 @@
|
||||
import path from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import { createSubsystemLogger } from "../../logging/subsystem.js";
|
||||
import type {
|
||||
BundledChannelEntryContract,
|
||||
BundledChannelSetupEntryContract,
|
||||
} from "../../plugin-sdk/channel-entry-contract.js";
|
||||
import { loadPluginManifestRegistry } from "../../plugins/manifest-registry.js";
|
||||
import type { PluginRuntime } from "../../plugins/runtime/types.js";
|
||||
import {
|
||||
isJavaScriptModulePath,
|
||||
loadChannelPluginModule,
|
||||
resolveCompiledBundledModulePath,
|
||||
} from "./module-loader.js";
|
||||
listBundledPluginMetadata,
|
||||
resolveBundledPluginGeneratedPath,
|
||||
type BundledPluginMetadata,
|
||||
} from "../../plugins/bundled-plugin-metadata.js";
|
||||
import type { PluginRuntime } from "../../plugins/runtime/types.js";
|
||||
import { isJavaScriptModulePath, loadChannelPluginModule } from "./module-loader.js";
|
||||
import type { ChannelId, ChannelPlugin } from "./types.js";
|
||||
|
||||
type GeneratedBundledChannelEntry = {
|
||||
@@ -20,6 +21,7 @@ type GeneratedBundledChannelEntry = {
|
||||
};
|
||||
|
||||
const log = createSubsystemLogger("channels");
|
||||
const OPENCLAW_PACKAGE_ROOT = path.resolve(fileURLToPath(new URL("../../..", import.meta.url)));
|
||||
|
||||
function resolveChannelPluginModuleEntry(
|
||||
moduleExport: unknown,
|
||||
@@ -71,53 +73,85 @@ function resolveChannelSetupModuleEntry(
|
||||
return record as BundledChannelSetupEntryContract;
|
||||
}
|
||||
|
||||
function resolveBundledChannelBoundaryRoot(params: {
|
||||
metadata: BundledPluginMetadata;
|
||||
modulePath: string;
|
||||
}): string {
|
||||
const distRoot = path.resolve(
|
||||
OPENCLAW_PACKAGE_ROOT,
|
||||
"dist",
|
||||
"extensions",
|
||||
params.metadata.dirName,
|
||||
);
|
||||
if (params.modulePath === distRoot || params.modulePath.startsWith(`${distRoot}${path.sep}`)) {
|
||||
return distRoot;
|
||||
}
|
||||
return path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions", params.metadata.dirName);
|
||||
}
|
||||
|
||||
function loadGeneratedBundledChannelModule(params: {
|
||||
metadata: BundledPluginMetadata;
|
||||
entry: BundledPluginMetadata["source"] | BundledPluginMetadata["setupSource"];
|
||||
}): unknown {
|
||||
const modulePath = resolveBundledPluginGeneratedPath(OPENCLAW_PACKAGE_ROOT, params.entry);
|
||||
if (!modulePath) {
|
||||
throw new Error(`missing generated module for bundled channel ${params.metadata.manifest.id}`);
|
||||
}
|
||||
return loadChannelPluginModule({
|
||||
modulePath,
|
||||
rootDir: resolveBundledChannelBoundaryRoot({
|
||||
metadata: params.metadata,
|
||||
modulePath,
|
||||
}),
|
||||
boundaryRootDir: resolveBundledChannelBoundaryRoot({
|
||||
metadata: params.metadata,
|
||||
modulePath,
|
||||
}),
|
||||
shouldTryNativeRequire: (safePath) =>
|
||||
safePath.includes(`${path.sep}dist${path.sep}`) && isJavaScriptModulePath(safePath),
|
||||
});
|
||||
}
|
||||
|
||||
function loadGeneratedBundledChannelEntries(): readonly GeneratedBundledChannelEntry[] {
|
||||
const manifestRegistry = loadPluginManifestRegistry({ cache: false, config: {} });
|
||||
const entries: GeneratedBundledChannelEntry[] = [];
|
||||
|
||||
for (const manifest of manifestRegistry.plugins) {
|
||||
if (manifest.origin !== "bundled" || manifest.channels.length === 0) {
|
||||
for (const metadata of listBundledPluginMetadata({
|
||||
includeChannelConfigs: false,
|
||||
includeSyntheticChannelConfigs: false,
|
||||
})) {
|
||||
if ((metadata.manifest.channels?.length ?? 0) === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
const sourcePath = resolveCompiledBundledModulePath(manifest.source);
|
||||
const entry = resolveChannelPluginModuleEntry(
|
||||
loadChannelPluginModule({
|
||||
modulePath: sourcePath,
|
||||
rootDir: manifest.rootDir,
|
||||
boundaryRootDir: resolveCompiledBundledModulePath(manifest.rootDir),
|
||||
shouldTryNativeRequire: (safePath) =>
|
||||
safePath.includes(`${path.sep}dist${path.sep}`) && isJavaScriptModulePath(safePath),
|
||||
loadGeneratedBundledChannelModule({
|
||||
metadata,
|
||||
entry: metadata.source,
|
||||
}),
|
||||
);
|
||||
if (!entry) {
|
||||
log.warn(
|
||||
`[channels] bundled channel entry ${manifest.id} missing bundled-channel-entry contract from ${sourcePath}; skipping`,
|
||||
`[channels] bundled channel entry ${metadata.manifest.id} missing bundled-channel-entry contract; skipping`,
|
||||
);
|
||||
continue;
|
||||
}
|
||||
const setupEntry = manifest.setupSource
|
||||
const setupEntry = metadata.setupSource
|
||||
? resolveChannelSetupModuleEntry(
|
||||
loadChannelPluginModule({
|
||||
modulePath: resolveCompiledBundledModulePath(manifest.setupSource),
|
||||
rootDir: manifest.rootDir,
|
||||
boundaryRootDir: resolveCompiledBundledModulePath(manifest.rootDir),
|
||||
shouldTryNativeRequire: (safePath) =>
|
||||
safePath.includes(`${path.sep}dist${path.sep}`) && isJavaScriptModulePath(safePath),
|
||||
loadGeneratedBundledChannelModule({
|
||||
metadata,
|
||||
entry: metadata.setupSource,
|
||||
}),
|
||||
)
|
||||
: null;
|
||||
entries.push({
|
||||
id: manifest.id,
|
||||
id: metadata.manifest.id,
|
||||
entry,
|
||||
...(setupEntry ? { setupEntry } : {}),
|
||||
});
|
||||
} catch (error) {
|
||||
const detail = error instanceof Error ? error.message : String(error);
|
||||
log.warn(
|
||||
`[channels] failed to load bundled channel ${manifest.id} from ${manifest.source}: ${detail}`,
|
||||
);
|
||||
log.warn(`[channels] failed to load bundled channel ${metadata.manifest.id}: ${detail}`);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createJiti } from "jiti";
|
||||
import JSON5 from "json5";
|
||||
import { resolveConfigPath } from "../config/paths.js";
|
||||
import { applyPluginAutoEnable } from "../config/plugin-auto-enable.js";
|
||||
import { configMayNeedPluginAutoEnable } from "../config/plugin-auto-enable.shared.js";
|
||||
import { getRuntimeConfigSnapshot } from "../config/runtime-snapshot.js";
|
||||
import type { OpenClawConfig } from "../config/types.js";
|
||||
import { openBoundaryFileSync } from "../infra/boundary-file-read.js";
|
||||
@@ -45,6 +46,15 @@ const loadedFacadeModules = new Map<string, unknown>();
|
||||
const loadedFacadePluginIds = new Set<string>();
|
||||
const OPENCLAW_SOURCE_EXTENSIONS_ROOT = path.resolve(OPENCLAW_PACKAGE_ROOT, "extensions");
|
||||
let cachedBoundaryRawConfig: OpenClawConfig | undefined;
|
||||
let cachedBoundaryResolvedConfigKey: string | undefined;
|
||||
let cachedBoundaryConfigFileState:
|
||||
| {
|
||||
configPath: string;
|
||||
mtimeMs: number;
|
||||
size: number;
|
||||
rawConfig: OpenClawConfig;
|
||||
}
|
||||
| undefined;
|
||||
let cachedBoundaryResolvedConfig:
|
||||
| {
|
||||
rawConfig: OpenClawConfig;
|
||||
@@ -216,36 +226,72 @@ function getJiti(modulePath: string) {
|
||||
return loader;
|
||||
}
|
||||
|
||||
function readFacadeBoundaryConfigSafely(): OpenClawConfig {
|
||||
function readFacadeBoundaryConfigSafely(): {
|
||||
rawConfig: OpenClawConfig;
|
||||
cacheKey?: string;
|
||||
} {
|
||||
try {
|
||||
const runtimeSnapshot = getRuntimeConfigSnapshot();
|
||||
if (runtimeSnapshot) {
|
||||
return runtimeSnapshot;
|
||||
return { rawConfig: runtimeSnapshot };
|
||||
}
|
||||
const configPath = resolveConfigPath();
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return EMPTY_FACADE_BOUNDARY_CONFIG;
|
||||
return { rawConfig: EMPTY_FACADE_BOUNDARY_CONFIG, cacheKey: `missing:${configPath}` };
|
||||
}
|
||||
const stat = fs.statSync(configPath);
|
||||
if (
|
||||
cachedBoundaryConfigFileState &&
|
||||
cachedBoundaryConfigFileState.configPath === configPath &&
|
||||
cachedBoundaryConfigFileState.mtimeMs === stat.mtimeMs &&
|
||||
cachedBoundaryConfigFileState.size === stat.size
|
||||
) {
|
||||
return {
|
||||
rawConfig: cachedBoundaryConfigFileState.rawConfig,
|
||||
cacheKey: `file:${configPath}:${stat.mtimeMs}:${stat.size}`,
|
||||
};
|
||||
}
|
||||
const raw = fs.readFileSync(configPath, "utf8");
|
||||
const parsed = JSON5.parse(raw);
|
||||
return parsed && typeof parsed === "object"
|
||||
? (parsed as OpenClawConfig)
|
||||
: EMPTY_FACADE_BOUNDARY_CONFIG;
|
||||
const rawConfig =
|
||||
parsed && typeof parsed === "object"
|
||||
? (parsed as OpenClawConfig)
|
||||
: EMPTY_FACADE_BOUNDARY_CONFIG;
|
||||
cachedBoundaryConfigFileState = {
|
||||
configPath,
|
||||
mtimeMs: stat.mtimeMs,
|
||||
size: stat.size,
|
||||
rawConfig,
|
||||
};
|
||||
return {
|
||||
rawConfig,
|
||||
cacheKey: `file:${configPath}:${stat.mtimeMs}:${stat.size}`,
|
||||
};
|
||||
} catch {
|
||||
return EMPTY_FACADE_BOUNDARY_CONFIG;
|
||||
return { rawConfig: EMPTY_FACADE_BOUNDARY_CONFIG };
|
||||
}
|
||||
}
|
||||
|
||||
function getFacadeBoundaryResolvedConfig() {
|
||||
const rawConfig = readFacadeBoundaryConfigSafely();
|
||||
if (cachedBoundaryResolvedConfig && cachedBoundaryRawConfig === rawConfig) {
|
||||
const readResult = readFacadeBoundaryConfigSafely();
|
||||
const { rawConfig } = readResult;
|
||||
if (
|
||||
cachedBoundaryResolvedConfig &&
|
||||
((readResult.cacheKey && cachedBoundaryResolvedConfigKey === readResult.cacheKey) ||
|
||||
(!readResult.cacheKey && cachedBoundaryRawConfig === rawConfig))
|
||||
) {
|
||||
return cachedBoundaryResolvedConfig;
|
||||
}
|
||||
|
||||
const autoEnabled = applyPluginAutoEnable({
|
||||
config: rawConfig,
|
||||
env: process.env,
|
||||
});
|
||||
const autoEnabled = configMayNeedPluginAutoEnable(rawConfig, process.env)
|
||||
? applyPluginAutoEnable({
|
||||
config: rawConfig,
|
||||
env: process.env,
|
||||
})
|
||||
: {
|
||||
config: rawConfig,
|
||||
autoEnabledReasons: {} as Record<string, string[]>,
|
||||
};
|
||||
const config = autoEnabled.config;
|
||||
const resolved = {
|
||||
rawConfig,
|
||||
@@ -255,6 +301,7 @@ function getFacadeBoundaryResolvedConfig() {
|
||||
autoEnabledReasons: autoEnabled.autoEnabledReasons,
|
||||
};
|
||||
cachedBoundaryRawConfig = rawConfig;
|
||||
cachedBoundaryResolvedConfigKey = readResult.cacheKey;
|
||||
cachedBoundaryResolvedConfig = resolved;
|
||||
return resolved;
|
||||
}
|
||||
@@ -569,6 +616,8 @@ export function resetFacadeRuntimeStateForTest(): void {
|
||||
loadedFacadePluginIds.clear();
|
||||
jitiLoaders.clear();
|
||||
cachedBoundaryRawConfig = undefined;
|
||||
cachedBoundaryResolvedConfigKey = undefined;
|
||||
cachedBoundaryConfigFileState = undefined;
|
||||
cachedBoundaryResolvedConfig = undefined;
|
||||
cachedManifestRegistry = undefined;
|
||||
cachedFacadeModuleLocationsByKey.clear();
|
||||
|
||||
Reference in New Issue
Block a user