mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-23 14:45:46 +00:00
Plugins: reserve context engine ownership
This commit is contained in:
@@ -231,18 +231,36 @@ describe("Registry tests", () => {
|
||||
expect(Array.isArray(ids)).toBe(true);
|
||||
});
|
||||
|
||||
it("registering the same id overwrites the previous factory", () => {
|
||||
it("registering the same id with the same owner refreshes the factory", () => {
|
||||
const factory1 = () => new MockContextEngine();
|
||||
const factory2 = () => new MockContextEngine();
|
||||
|
||||
registerContextEngine("reg-overwrite", factory1);
|
||||
expect(registerContextEngine("reg-overwrite", factory1, { owner: "owner-a" })).toEqual({
|
||||
ok: true,
|
||||
});
|
||||
expect(getContextEngineFactory("reg-overwrite")).toBe(factory1);
|
||||
|
||||
registerContextEngine("reg-overwrite", factory2);
|
||||
expect(registerContextEngine("reg-overwrite", factory2, { owner: "owner-a" })).toEqual({
|
||||
ok: true,
|
||||
});
|
||||
expect(getContextEngineFactory("reg-overwrite")).toBe(factory2);
|
||||
expect(getContextEngineFactory("reg-overwrite")).not.toBe(factory1);
|
||||
});
|
||||
|
||||
it("rejects context engine registrations from a different owner", () => {
|
||||
const factory1 = () => new MockContextEngine();
|
||||
const factory2 = () => new MockContextEngine();
|
||||
|
||||
expect(registerContextEngine("reg-owner-guard", factory1, { owner: "owner-a" })).toEqual({
|
||||
ok: true,
|
||||
});
|
||||
expect(registerContextEngine("reg-owner-guard", factory2, { owner: "owner-b" })).toEqual({
|
||||
ok: false,
|
||||
existingOwner: "owner-a",
|
||||
});
|
||||
expect(getContextEngineFactory("reg-owner-guard")).toBe(factory1);
|
||||
});
|
||||
|
||||
it("shares registered engines across duplicate module copies", async () => {
|
||||
const registryUrl = new URL("./registry.ts", import.meta.url).href;
|
||||
const suffix = Date.now().toString(36);
|
||||
|
||||
@@ -7,6 +7,7 @@ import type { ContextEngine } from "./types.js";
|
||||
* Supports async creation for engines that need DB connections etc.
|
||||
*/
|
||||
export type ContextEngineFactory = () => ContextEngine | Promise<ContextEngine>;
|
||||
export type ContextEngineRegistrationResult = { ok: true } | { ok: false; existingOwner: string };
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Registry (module-level singleton)
|
||||
@@ -15,7 +16,13 @@ export type ContextEngineFactory = () => ContextEngine | Promise<ContextEngine>;
|
||||
const CONTEXT_ENGINE_REGISTRY_STATE = Symbol.for("openclaw.contextEngineRegistryState");
|
||||
|
||||
type ContextEngineRegistryState = {
|
||||
engines: Map<string, ContextEngineFactory>;
|
||||
engines: Map<
|
||||
string,
|
||||
{
|
||||
factory: ContextEngineFactory;
|
||||
owner: string;
|
||||
}
|
||||
>;
|
||||
};
|
||||
|
||||
// Keep context-engine registrations process-global so duplicated dist chunks
|
||||
@@ -26,7 +33,7 @@ function getContextEngineRegistryState(): ContextEngineRegistryState {
|
||||
};
|
||||
if (!globalState[CONTEXT_ENGINE_REGISTRY_STATE]) {
|
||||
globalState[CONTEXT_ENGINE_REGISTRY_STATE] = {
|
||||
engines: new Map<string, ContextEngineFactory>(),
|
||||
engines: new Map(),
|
||||
};
|
||||
}
|
||||
return globalState[CONTEXT_ENGINE_REGISTRY_STATE];
|
||||
@@ -35,15 +42,26 @@ function getContextEngineRegistryState(): ContextEngineRegistryState {
|
||||
/**
|
||||
* Register a context engine implementation under the given id.
|
||||
*/
|
||||
export function registerContextEngine(id: string, factory: ContextEngineFactory): void {
|
||||
getContextEngineRegistryState().engines.set(id, factory);
|
||||
export function registerContextEngine(
|
||||
id: string,
|
||||
factory: ContextEngineFactory,
|
||||
opts?: { owner?: string },
|
||||
): ContextEngineRegistrationResult {
|
||||
const owner = opts?.owner?.trim() || "core";
|
||||
const registry = getContextEngineRegistryState().engines;
|
||||
const existing = registry.get(id);
|
||||
if (existing && existing.owner !== owner) {
|
||||
return { ok: false, existingOwner: existing.owner };
|
||||
}
|
||||
registry.set(id, { factory, owner });
|
||||
return { ok: true };
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the factory for a registered engine, or undefined.
|
||||
*/
|
||||
export function getContextEngineFactory(id: string): ContextEngineFactory | undefined {
|
||||
return getContextEngineRegistryState().engines.get(id);
|
||||
return getContextEngineRegistryState().engines.get(id)?.factory;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -73,13 +91,13 @@ export async function resolveContextEngine(config?: OpenClawConfig): Promise<Con
|
||||
? slotValue.trim()
|
||||
: defaultSlotIdForKey("contextEngine");
|
||||
|
||||
const factory = getContextEngineRegistryState().engines.get(engineId);
|
||||
if (!factory) {
|
||||
const entry = getContextEngineRegistryState().engines.get(engineId);
|
||||
if (!entry) {
|
||||
throw new Error(
|
||||
`Context engine "${engineId}" is not registered. ` +
|
||||
`Available engines: ${listContextEngineIds().join(", ") || "(none)"}`,
|
||||
);
|
||||
}
|
||||
|
||||
return factory();
|
||||
return entry.factory();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user