fix(plugin-sdk): restore root diagnostic compat
This commit is contained in:
parent
50ce9ac1c6
commit
dbc9d3dd70
@ -42,7 +42,7 @@ const exportedNames = exportMatch[1]
|
|||||||
|
|
||||||
const exportSet = new Set(exportedNames);
|
const exportSet = new Set(exportedNames);
|
||||||
|
|
||||||
const requiredRuntimeShimEntries = ["root-alias.cjs"];
|
const requiredRuntimeShimEntries = ["compat.js", "root-alias.cjs"];
|
||||||
|
|
||||||
// Critical functions that channel extension plugins import from openclaw/plugin-sdk.
|
// Critical functions that channel extension plugins import from openclaw/plugin-sdk.
|
||||||
// If any of these are missing, plugins will fail at runtime with:
|
// If any of these are missing, plugins will fail at runtime with:
|
||||||
@ -65,6 +65,7 @@ const requiredExports = [
|
|||||||
"resolveChannelMediaMaxBytes",
|
"resolveChannelMediaMaxBytes",
|
||||||
"warnMissingProviderGroupPolicyFallbackOnce",
|
"warnMissingProviderGroupPolicyFallbackOnce",
|
||||||
"emptyPluginConfigSchema",
|
"emptyPluginConfigSchema",
|
||||||
|
"onDiagnosticEvent",
|
||||||
"normalizePluginHttpPath",
|
"normalizePluginHttpPath",
|
||||||
"registerPluginHttpRoute",
|
"registerPluginHttpRoute",
|
||||||
"DEFAULT_ACCOUNT_ID",
|
"DEFAULT_ACCOUNT_ID",
|
||||||
|
|||||||
@ -21,6 +21,7 @@ const requiredPathGroups = [
|
|||||||
["dist/index.js", "dist/index.mjs"],
|
["dist/index.js", "dist/index.mjs"],
|
||||||
["dist/entry.js", "dist/entry.mjs"],
|
["dist/entry.js", "dist/entry.mjs"],
|
||||||
...listPluginSdkDistArtifacts(),
|
...listPluginSdkDistArtifacts(),
|
||||||
|
"dist/plugin-sdk/compat.js",
|
||||||
"dist/plugin-sdk/root-alias.cjs",
|
"dist/plugin-sdk/root-alias.cjs",
|
||||||
"dist/build-info.json",
|
"dist/build-info.json",
|
||||||
];
|
];
|
||||||
@ -228,6 +229,7 @@ const requiredPluginSdkExports = [
|
|||||||
"resolveChannelMediaMaxBytes",
|
"resolveChannelMediaMaxBytes",
|
||||||
"warnMissingProviderGroupPolicyFallbackOnce",
|
"warnMissingProviderGroupPolicyFallbackOnce",
|
||||||
"emptyPluginConfigSchema",
|
"emptyPluginConfigSchema",
|
||||||
|
"onDiagnosticEvent",
|
||||||
"normalizePluginHttpPath",
|
"normalizePluginHttpPath",
|
||||||
"registerPluginHttpRoute",
|
"registerPluginHttpRoute",
|
||||||
"DEFAULT_ACCOUNT_ID",
|
"DEFAULT_ACCOUNT_ID",
|
||||||
|
|||||||
@ -36,6 +36,7 @@ describe("tsdown config", () => {
|
|||||||
expect.arrayContaining([
|
expect.arrayContaining([
|
||||||
"index",
|
"index",
|
||||||
"plugins/runtime/index",
|
"plugins/runtime/index",
|
||||||
|
"plugin-sdk/compat",
|
||||||
"plugin-sdk/index",
|
"plugin-sdk/index",
|
||||||
"extensions/openai/index",
|
"extensions/openai/index",
|
||||||
"bundled/boot-md/handler",
|
"bundled/boot-md/handler",
|
||||||
|
|||||||
@ -20,6 +20,8 @@ if (shouldWarnCompatImport) {
|
|||||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||||
export { resolveControlCommandGate } from "../channels/command-gating.js";
|
export { resolveControlCommandGate } from "../channels/command-gating.js";
|
||||||
export { delegateCompactionToRuntime } from "../context-engine/delegate.js";
|
export { delegateCompactionToRuntime } from "../context-engine/delegate.js";
|
||||||
|
export type { DiagnosticEventPayload } from "../infra/diagnostic-events.js";
|
||||||
|
export { onDiagnosticEvent } from "../infra/diagnostic-events.js";
|
||||||
|
|
||||||
export { createAccountStatusSink } from "./channel-lifecycle.js";
|
export { createAccountStatusSink } from "./channel-lifecycle.js";
|
||||||
export { createPluginRuntimeStore } from "./runtime-store.js";
|
export { createPluginRuntimeStore } from "./runtime-store.js";
|
||||||
|
|||||||
@ -50,9 +50,11 @@ describe("plugin-sdk exports", () => {
|
|||||||
it("keeps the root runtime surface intentionally small", () => {
|
it("keeps the root runtime surface intentionally small", () => {
|
||||||
expect(typeof sdk.emptyPluginConfigSchema).toBe("function");
|
expect(typeof sdk.emptyPluginConfigSchema).toBe("function");
|
||||||
expect(typeof sdk.delegateCompactionToRuntime).toBe("function");
|
expect(typeof sdk.delegateCompactionToRuntime).toBe("function");
|
||||||
|
expect(typeof sdk.onDiagnosticEvent).toBe("function");
|
||||||
expect(Object.prototype.hasOwnProperty.call(sdk, "resolveControlCommandGate")).toBe(false);
|
expect(Object.prototype.hasOwnProperty.call(sdk, "resolveControlCommandGate")).toBe(false);
|
||||||
expect(Object.prototype.hasOwnProperty.call(sdk, "buildAgentSessionKey")).toBe(false);
|
expect(Object.prototype.hasOwnProperty.call(sdk, "buildAgentSessionKey")).toBe(false);
|
||||||
expect(Object.prototype.hasOwnProperty.call(sdk, "isDangerousNameMatchingEnabled")).toBe(false);
|
expect(Object.prototype.hasOwnProperty.call(sdk, "isDangerousNameMatchingEnabled")).toBe(false);
|
||||||
|
expect(Object.prototype.hasOwnProperty.call(sdk, "emitDiagnosticEvent")).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("keeps package.json plugin-sdk exports synced with the manifest", async () => {
|
it("keeps package.json plugin-sdk exports synced with the manifest", async () => {
|
||||||
|
|||||||
@ -64,7 +64,9 @@ export type { HookEntry } from "../hooks/types.js";
|
|||||||
export type { ReplyPayload } from "../auto-reply/types.js";
|
export type { ReplyPayload } from "../auto-reply/types.js";
|
||||||
export type { WizardPrompter } from "../wizard/prompts.js";
|
export type { WizardPrompter } from "../wizard/prompts.js";
|
||||||
export type { ContextEngineFactory } from "../context-engine/registry.js";
|
export type { ContextEngineFactory } from "../context-engine/registry.js";
|
||||||
|
export type { DiagnosticEventPayload } from "../infra/diagnostic-events.js";
|
||||||
|
|
||||||
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
export { emptyPluginConfigSchema } from "../plugins/config-schema.js";
|
||||||
export { registerContextEngine } from "../context-engine/registry.js";
|
export { registerContextEngine } from "../context-engine/registry.js";
|
||||||
export { delegateCompactionToRuntime } from "../context-engine/delegate.js";
|
export { delegateCompactionToRuntime } from "../context-engine/delegate.js";
|
||||||
|
export { onDiagnosticEvent } from "../infra/diagnostic-events.js";
|
||||||
|
|||||||
@ -5,6 +5,7 @@ const fs = require("node:fs");
|
|||||||
|
|
||||||
let monolithicSdk = null;
|
let monolithicSdk = null;
|
||||||
const jitiLoaders = new Map();
|
const jitiLoaders = new Map();
|
||||||
|
const pluginSdkSubpathsCache = new Map();
|
||||||
|
|
||||||
function emptyPluginConfigSchema() {
|
function emptyPluginConfigSchema() {
|
||||||
function error(message) {
|
function error(message) {
|
||||||
@ -61,6 +62,49 @@ function resolveControlCommandGate(params) {
|
|||||||
return { commandAuthorized, shouldBlock };
|
return { commandAuthorized, shouldBlock };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getPackageRoot() {
|
||||||
|
return path.resolve(__dirname, "..", "..");
|
||||||
|
}
|
||||||
|
|
||||||
|
function listPluginSdkExportedSubpaths() {
|
||||||
|
const packageRoot = getPackageRoot();
|
||||||
|
if (pluginSdkSubpathsCache.has(packageRoot)) {
|
||||||
|
return pluginSdkSubpathsCache.get(packageRoot);
|
||||||
|
}
|
||||||
|
|
||||||
|
let subpaths = [];
|
||||||
|
try {
|
||||||
|
const packageJsonPath = path.join(packageRoot, "package.json");
|
||||||
|
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
||||||
|
subpaths = Object.keys(packageJson.exports ?? {})
|
||||||
|
.filter((key) => key.startsWith("./plugin-sdk/"))
|
||||||
|
.map((key) => key.slice("./plugin-sdk/".length));
|
||||||
|
} catch {
|
||||||
|
subpaths = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
pluginSdkSubpathsCache.set(packageRoot, subpaths);
|
||||||
|
return subpaths;
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPluginSdkAliasMap(useDist) {
|
||||||
|
const packageRoot = getPackageRoot();
|
||||||
|
const pluginSdkDir = path.join(packageRoot, useDist ? "dist" : "src", "plugin-sdk");
|
||||||
|
const ext = useDist ? ".js" : ".ts";
|
||||||
|
const aliasMap = {
|
||||||
|
"openclaw/plugin-sdk": __filename,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const subpath of listPluginSdkExportedSubpaths()) {
|
||||||
|
const candidate = path.join(pluginSdkDir, `${subpath}${ext}`);
|
||||||
|
if (fs.existsSync(candidate)) {
|
||||||
|
aliasMap[`openclaw/plugin-sdk/${subpath}`] = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return aliasMap;
|
||||||
|
}
|
||||||
|
|
||||||
function getJiti(tryNative) {
|
function getJiti(tryNative) {
|
||||||
if (jitiLoaders.has(tryNative)) {
|
if (jitiLoaders.has(tryNative)) {
|
||||||
return jitiLoaders.get(tryNative);
|
return jitiLoaders.get(tryNative);
|
||||||
@ -68,6 +112,7 @@ function getJiti(tryNative) {
|
|||||||
|
|
||||||
const { createJiti } = require("jiti");
|
const { createJiti } = require("jiti");
|
||||||
const jitiLoader = createJiti(__filename, {
|
const jitiLoader = createJiti(__filename, {
|
||||||
|
alias: buildPluginSdkAliasMap(tryNative),
|
||||||
interopDefault: true,
|
interopDefault: true,
|
||||||
// Prefer Node's native sync ESM loader for built dist/plugin-sdk/*.js files
|
// Prefer Node's native sync ESM loader for built dist/plugin-sdk/*.js files
|
||||||
// so local plugins do not create a second transpiled OpenClaw core graph.
|
// so local plugins do not create a second transpiled OpenClaw core graph.
|
||||||
|
|||||||
@ -48,6 +48,12 @@ function loadRootAliasWithStubs(options?: {
|
|||||||
}
|
}
|
||||||
if (id === "node:fs") {
|
if (id === "node:fs") {
|
||||||
return {
|
return {
|
||||||
|
readFileSync: () =>
|
||||||
|
JSON.stringify({
|
||||||
|
exports: {
|
||||||
|
"./plugin-sdk/group-access": { default: "./dist/plugin-sdk/group-access.js" },
|
||||||
|
},
|
||||||
|
}),
|
||||||
existsSync: () => options?.distExists ?? false,
|
existsSync: () => options?.distExists ?? false,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -164,8 +170,23 @@ describe("plugin-sdk root alias", () => {
|
|||||||
expect("delegateCompactionToRuntime" in lazyRootSdk).toBe(true);
|
expect("delegateCompactionToRuntime" in lazyRootSdk).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("forwards onDiagnosticEvent through the compat-backed root alias", () => {
|
||||||
|
const onDiagnosticEvent = () => () => undefined;
|
||||||
|
const lazyModule = loadRootAliasWithStubs({
|
||||||
|
monolithicExports: {
|
||||||
|
onDiagnosticEvent,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const lazyRootSdk = lazyModule.moduleExports;
|
||||||
|
|
||||||
|
expect(typeof lazyRootSdk.onDiagnosticEvent).toBe("function");
|
||||||
|
expect(lazyRootSdk.onDiagnosticEvent).toBe(onDiagnosticEvent);
|
||||||
|
expect("onDiagnosticEvent" in lazyRootSdk).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
it("loads legacy root exports through the merged root wrapper", { timeout: 240_000 }, () => {
|
it("loads legacy root exports through the merged root wrapper", { timeout: 240_000 }, () => {
|
||||||
expect(typeof rootSdk.resolveControlCommandGate).toBe("function");
|
expect(typeof rootSdk.resolveControlCommandGate).toBe("function");
|
||||||
|
expect(typeof rootSdk.onDiagnosticEvent).toBe("function");
|
||||||
expect(typeof rootSdk.default).toBe("object");
|
expect(typeof rootSdk.default).toBe("object");
|
||||||
expect(rootSdk.default).toBe(rootSdk);
|
expect(rootSdk.default).toBe(rootSdk);
|
||||||
expect(rootSdk.__esModule).toBe(true);
|
expect(rootSdk.__esModule).toBe(true);
|
||||||
@ -173,9 +194,12 @@ describe("plugin-sdk root alias", () => {
|
|||||||
|
|
||||||
it("preserves reflection semantics for lazily resolved exports", { timeout: 240_000 }, () => {
|
it("preserves reflection semantics for lazily resolved exports", { timeout: 240_000 }, () => {
|
||||||
expect("resolveControlCommandGate" in rootSdk).toBe(true);
|
expect("resolveControlCommandGate" in rootSdk).toBe(true);
|
||||||
|
expect("onDiagnosticEvent" in rootSdk).toBe(true);
|
||||||
const keys = Object.keys(rootSdk);
|
const keys = Object.keys(rootSdk);
|
||||||
expect(keys).toContain("resolveControlCommandGate");
|
expect(keys).toContain("resolveControlCommandGate");
|
||||||
|
expect(keys).toContain("onDiagnosticEvent");
|
||||||
const descriptor = Object.getOwnPropertyDescriptor(rootSdk, "resolveControlCommandGate");
|
const descriptor = Object.getOwnPropertyDescriptor(rootSdk, "resolveControlCommandGate");
|
||||||
expect(descriptor).toBeDefined();
|
expect(descriptor).toBeDefined();
|
||||||
|
expect(Object.getOwnPropertyDescriptor(rootSdk, "onDiagnosticEvent")).toBeDefined();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -186,6 +186,8 @@ const coreDistEntries = buildCoreDistEntries();
|
|||||||
function buildUnifiedDistEntries(): Record<string, string> {
|
function buildUnifiedDistEntries(): Record<string, string> {
|
||||||
return {
|
return {
|
||||||
...coreDistEntries,
|
...coreDistEntries,
|
||||||
|
// Internal compat artifact for the root-alias.cjs lazy loader.
|
||||||
|
"plugin-sdk/compat": "src/plugin-sdk/compat.ts",
|
||||||
...Object.fromEntries(
|
...Object.fromEntries(
|
||||||
Object.entries(buildPluginSdkEntrySources()).map(([entry, source]) => [
|
Object.entries(buildPluginSdkEntrySources()).map(([entry, source]) => [
|
||||||
`plugin-sdk/${entry}`,
|
`plugin-sdk/${entry}`,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user