mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-28 05:16:23 -06:00
refactor(plugins): unify plugin entry ordering (#128170)
This commit is contained in:
@@ -7,20 +7,9 @@ import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveEnabledBundledManifestContractPlugins } from "./bundled-manifest-contract-plugins.js";
|
||||
import { loadBundledDocumentExtractorEntriesFromDir } from "./document-extractor-public-artifacts.js";
|
||||
import type { PluginDocumentExtractorEntry } from "./document-extractor-types.js";
|
||||
import { sortPluginEntriesForAutoDetect } from "./plugin-entry-order.js";
|
||||
import { createPluginIdScopeSet } from "./plugin-scope.js";
|
||||
|
||||
function compareExtractors(
|
||||
left: PluginDocumentExtractorEntry,
|
||||
right: PluginDocumentExtractorEntry,
|
||||
): number {
|
||||
const leftOrder = left.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
const rightOrder = right.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
if (leftOrder !== rightOrder) {
|
||||
return leftOrder - rightOrder;
|
||||
}
|
||||
return left.id.localeCompare(right.id) || left.pluginId.localeCompare(right.pluginId);
|
||||
}
|
||||
|
||||
function resolveExplicitAllowedDocumentExtractorPluginIds(params: {
|
||||
config?: OpenClawConfig;
|
||||
onlyPluginIds?: readonly string[];
|
||||
@@ -82,5 +71,5 @@ export function resolvePluginDocumentExtractors(params?: {
|
||||
cause: loadErrors.length === 1 ? loadErrors[0] : new AggregateError(loadErrors),
|
||||
});
|
||||
}
|
||||
return extractors.toSorted(compareExtractors);
|
||||
return sortPluginEntriesForAutoDetect(extractors);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { sortPluginEntriesById, sortPluginEntriesForAutoDetect } from "./plugin-entry-order.js";
|
||||
|
||||
type TestEntry = {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
autoDetectOrder?: number;
|
||||
};
|
||||
|
||||
function toKeys(entries: readonly TestEntry[]): string[] {
|
||||
return entries.map((entry) => `${entry.id}:${entry.pluginId}`);
|
||||
}
|
||||
|
||||
describe("plugin entry order", () => {
|
||||
it("sorts identities without mutating the input", () => {
|
||||
const entries = [
|
||||
{ id: "beta", pluginId: "plugin-b" },
|
||||
{ id: "alpha", pluginId: "plugin-z" },
|
||||
{ id: "alpha", pluginId: "plugin-a" },
|
||||
] satisfies TestEntry[];
|
||||
|
||||
expect(toKeys(sortPluginEntriesById(entries))).toEqual([
|
||||
"alpha:plugin-a",
|
||||
"alpha:plugin-z",
|
||||
"beta:plugin-b",
|
||||
]);
|
||||
expect(toKeys(entries)).toEqual(["beta:plugin-b", "alpha:plugin-z", "alpha:plugin-a"]);
|
||||
});
|
||||
|
||||
it("sorts auto-detect priorities before deterministic identity ties", () => {
|
||||
const entries = [
|
||||
{ id: "unordered", pluginId: "plugin-u" },
|
||||
{ id: "beta", pluginId: "plugin-b", autoDetectOrder: 10 },
|
||||
{ id: "alpha", pluginId: "plugin-z", autoDetectOrder: 10 },
|
||||
{ id: "alpha", pluginId: "plugin-a", autoDetectOrder: 10 },
|
||||
{ id: "first", pluginId: "plugin-f", autoDetectOrder: 1 },
|
||||
] satisfies TestEntry[];
|
||||
|
||||
expect(toKeys(sortPluginEntriesForAutoDetect(entries))).toEqual([
|
||||
"first:plugin-f",
|
||||
"alpha:plugin-a",
|
||||
"alpha:plugin-z",
|
||||
"beta:plugin-b",
|
||||
"unordered:plugin-u",
|
||||
]);
|
||||
expect(toKeys(entries)).toEqual([
|
||||
"unordered:plugin-u",
|
||||
"beta:plugin-b",
|
||||
"alpha:plugin-z",
|
||||
"alpha:plugin-a",
|
||||
"first:plugin-f",
|
||||
]);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,30 @@
|
||||
type PluginEntryIdentity = {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
};
|
||||
|
||||
type PluginAutoDetectEntry = PluginEntryIdentity & {
|
||||
autoDetectOrder?: number;
|
||||
};
|
||||
|
||||
function comparePluginEntryIdentity(left: PluginEntryIdentity, right: PluginEntryIdentity): number {
|
||||
return left.id.localeCompare(right.id) || left.pluginId.localeCompare(right.pluginId);
|
||||
}
|
||||
|
||||
export function sortPluginEntriesById<T extends PluginEntryIdentity>(entries: readonly T[]): T[] {
|
||||
return entries.toSorted(comparePluginEntryIdentity);
|
||||
}
|
||||
|
||||
/** Sorts auto-detect candidates by priority, then stable plugin identity. */
|
||||
export function sortPluginEntriesForAutoDetect<T extends PluginAutoDetectEntry>(
|
||||
entries: readonly T[],
|
||||
): T[] {
|
||||
return entries.toSorted((left, right) => {
|
||||
const leftOrder = left.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
const rightOrder = right.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
if (leftOrder !== rightOrder) {
|
||||
return leftOrder - rightOrder;
|
||||
}
|
||||
return comparePluginEntryIdentity(left, right);
|
||||
});
|
||||
}
|
||||
@@ -1,21 +1,10 @@
|
||||
// Runtime bridge for web content extractors supplied by plugins.
|
||||
import type { OpenClawConfig } from "../config/types.openclaw.js";
|
||||
import { resolveEnabledBundledManifestContractPlugins } from "./bundled-manifest-contract-plugins.js";
|
||||
import { sortPluginEntriesForAutoDetect } from "./plugin-entry-order.js";
|
||||
import { loadBundledWebContentExtractorEntriesFromDir } from "./web-content-extractor-public-artifacts.js";
|
||||
import type { PluginWebContentExtractorEntry } from "./web-content-extractor-types.js";
|
||||
|
||||
function compareExtractors(
|
||||
left: PluginWebContentExtractorEntry,
|
||||
right: PluginWebContentExtractorEntry,
|
||||
): number {
|
||||
const leftOrder = left.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
const rightOrder = right.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
if (leftOrder !== rightOrder) {
|
||||
return leftOrder - rightOrder;
|
||||
}
|
||||
return left.id.localeCompare(right.id) || left.pluginId.localeCompare(right.pluginId);
|
||||
}
|
||||
|
||||
export function resolvePluginWebContentExtractors(params?: {
|
||||
config?: OpenClawConfig;
|
||||
workspaceDir?: string;
|
||||
@@ -38,5 +27,5 @@ export function resolvePluginWebContentExtractors(params?: {
|
||||
extractors.push(...loaded);
|
||||
}
|
||||
}
|
||||
return extractors.toSorted(compareExtractors);
|
||||
return sortPluginEntriesForAutoDetect(extractors);
|
||||
}
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
// Shares web fetch provider loading helpers across provider plugins.
|
||||
import type { PluginLoadOptions } from "./loader.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { sortPluginEntriesById, sortPluginEntriesForAutoDetect } from "./plugin-entry-order.js";
|
||||
import type { PluginWebFetchProviderEntry } from "./types.js";
|
||||
import {
|
||||
resolveBundledWebProviderResolutionConfig,
|
||||
sortPluginProviders,
|
||||
sortPluginProvidersForAutoDetect,
|
||||
} from "./web-provider-resolution-shared.js";
|
||||
import { resolveBundledWebProviderResolutionConfig } from "./web-provider-resolution-shared.js";
|
||||
|
||||
export function sortWebFetchProviders(
|
||||
providers: PluginWebFetchProviderEntry[],
|
||||
): PluginWebFetchProviderEntry[] {
|
||||
return sortPluginProviders(providers);
|
||||
return sortPluginEntriesById(providers);
|
||||
}
|
||||
|
||||
export function sortWebFetchProvidersForAutoDetect(
|
||||
providers: PluginWebFetchProviderEntry[],
|
||||
): PluginWebFetchProviderEntry[] {
|
||||
return sortPluginProvidersForAutoDetect(providers);
|
||||
return sortPluginEntriesForAutoDetect(providers);
|
||||
}
|
||||
|
||||
export function resolveBundledWebFetchResolutionConfig(params: {
|
||||
|
||||
@@ -15,39 +15,6 @@ type WebProviderCandidateResolution = {
|
||||
manifestRecords?: readonly PluginManifestRecord[];
|
||||
};
|
||||
|
||||
type WebProviderSortEntry = {
|
||||
id: string;
|
||||
pluginId: string;
|
||||
autoDetectOrder?: number;
|
||||
};
|
||||
|
||||
function comparePluginProvidersAlphabetically(
|
||||
left: Pick<WebProviderSortEntry, "id" | "pluginId">,
|
||||
right: Pick<WebProviderSortEntry, "id" | "pluginId">,
|
||||
): number {
|
||||
return left.id.localeCompare(right.id) || left.pluginId.localeCompare(right.pluginId);
|
||||
}
|
||||
|
||||
export function sortPluginProviders<T extends Pick<WebProviderSortEntry, "id" | "pluginId">>(
|
||||
providers: T[],
|
||||
): T[] {
|
||||
return providers.toSorted(comparePluginProvidersAlphabetically);
|
||||
}
|
||||
|
||||
/** Sorts provider candidates for auto-detect while keeping equal priorities deterministic. */
|
||||
export function sortPluginProvidersForAutoDetect<T extends WebProviderSortEntry>(
|
||||
providers: T[],
|
||||
): T[] {
|
||||
return providers.toSorted((left, right) => {
|
||||
const leftOrder = left.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
const rightOrder = right.autoDetectOrder ?? Number.MAX_SAFE_INTEGER;
|
||||
if (leftOrder !== rightOrder) {
|
||||
return leftOrder - rightOrder;
|
||||
}
|
||||
return comparePluginProvidersAlphabetically(left, right);
|
||||
});
|
||||
}
|
||||
|
||||
function pluginManifestDeclaresProviderConfig(
|
||||
record: PluginManifestRecord,
|
||||
configKey: WebProviderConfigKey,
|
||||
|
||||
@@ -1,23 +1,20 @@
|
||||
// Shares web-search provider loading helpers across runtime paths.
|
||||
import type { PluginLoadOptions } from "./loader.js";
|
||||
import type { PluginManifestRecord } from "./manifest-registry.js";
|
||||
import { sortPluginEntriesById, sortPluginEntriesForAutoDetect } from "./plugin-entry-order.js";
|
||||
import type { PluginWebSearchProviderEntry } from "./types.js";
|
||||
import {
|
||||
resolveBundledWebProviderResolutionConfig,
|
||||
sortPluginProviders,
|
||||
sortPluginProvidersForAutoDetect,
|
||||
} from "./web-provider-resolution-shared.js";
|
||||
import { resolveBundledWebProviderResolutionConfig } from "./web-provider-resolution-shared.js";
|
||||
|
||||
export function sortWebSearchProviders(
|
||||
providers: PluginWebSearchProviderEntry[],
|
||||
): PluginWebSearchProviderEntry[] {
|
||||
return sortPluginProviders(providers);
|
||||
return sortPluginEntriesById(providers);
|
||||
}
|
||||
|
||||
export function sortWebSearchProvidersForAutoDetect(
|
||||
providers: PluginWebSearchProviderEntry[],
|
||||
): PluginWebSearchProviderEntry[] {
|
||||
return sortPluginProvidersForAutoDetect(providers);
|
||||
return sortPluginEntriesForAutoDetect(providers);
|
||||
}
|
||||
|
||||
export function resolveBundledWebSearchResolutionConfig(params: {
|
||||
|
||||
Reference in New Issue
Block a user