mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-23 16:08:12 +00:00
refactor: simplify manifest registry duplicate detection
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import type { NextFunction, Request, Response } from "express";
|
||||
import type { NextFunction, Request, RequestHandler, Response } from "express";
|
||||
import { isLoopbackHost } from "../gateway/net.js";
|
||||
|
||||
function firstHeader(value: string | string[] | undefined): string {
|
||||
@@ -54,7 +54,7 @@ export function shouldRejectBrowserMutation(params: {
|
||||
return false;
|
||||
}
|
||||
|
||||
export function browserMutationGuardMiddleware() {
|
||||
export function browserMutationGuardMiddleware(): RequestHandler {
|
||||
return (req: Request, res: Response, next: NextFunction) => {
|
||||
// OPTIONS is used for CORS preflight. Even if cross-origin, the preflight isn't mutating.
|
||||
const method = (req.method || "").trim().toUpperCase();
|
||||
|
||||
@@ -11,17 +11,25 @@ type SeenIdEntry = {
|
||||
recordIndex: number;
|
||||
};
|
||||
|
||||
function pluginOriginRank(origin: PluginOrigin): number {
|
||||
// Precedence: config > workspace > global > bundled
|
||||
switch (origin) {
|
||||
case "config":
|
||||
return 0;
|
||||
case "workspace":
|
||||
return 1;
|
||||
case "global":
|
||||
return 2;
|
||||
case "bundled":
|
||||
return 3;
|
||||
// Precedence: config > workspace > global > bundled
|
||||
const PLUGIN_ORIGIN_RANK: Readonly<Record<PluginOrigin, number>> = {
|
||||
config: 0,
|
||||
workspace: 1,
|
||||
global: 2,
|
||||
bundled: 3,
|
||||
};
|
||||
|
||||
function safeRealpathSync(rootDir: string, cache: Map<string, string>): string | null {
|
||||
const cached = cache.get(rootDir);
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
try {
|
||||
const resolved = fs.realpathSync(rootDir);
|
||||
cache.set(rootDir, resolved);
|
||||
return resolved;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,6 +166,7 @@ export function loadPluginManifestRegistry(params: {
|
||||
const candidates: PluginCandidate[] = discovery.candidates;
|
||||
const records: PluginManifestRecord[] = [];
|
||||
const seenIds = new Map<string, SeenIdEntry>();
|
||||
const realpathCache = new Map<string, string>();
|
||||
|
||||
for (const candidate of candidates) {
|
||||
const manifestRes = loadPluginManifest(candidate.rootDir);
|
||||
@@ -191,17 +200,13 @@ export function loadPluginManifestRegistry(params: {
|
||||
// Check whether both candidates point to the same physical directory
|
||||
// (e.g. via symlinks or different path representations). If so, this
|
||||
// is a false-positive duplicate and can be silently skipped.
|
||||
let samePlugin = false;
|
||||
try {
|
||||
samePlugin =
|
||||
fs.realpathSync(existing.candidate.rootDir) === fs.realpathSync(candidate.rootDir);
|
||||
} catch {
|
||||
// If either path is inaccessible, fall through to duplicate warning
|
||||
}
|
||||
const existingReal = safeRealpathSync(existing.candidate.rootDir, realpathCache);
|
||||
const candidateReal = safeRealpathSync(candidate.rootDir, realpathCache);
|
||||
const samePlugin = Boolean(existingReal && candidateReal && existingReal === candidateReal);
|
||||
if (samePlugin) {
|
||||
// Prefer higher-precedence origins even if candidates are passed in
|
||||
// an unexpected order (config > workspace > global > bundled).
|
||||
if (pluginOriginRank(candidate.origin) < pluginOriginRank(existing.candidate.origin)) {
|
||||
if (PLUGIN_ORIGIN_RANK[candidate.origin] < PLUGIN_ORIGIN_RANK[existing.candidate.origin]) {
|
||||
records[existing.recordIndex] = buildRecord({
|
||||
manifest,
|
||||
candidate,
|
||||
|
||||
Reference in New Issue
Block a user