mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-21 14:11:26 +00:00
refactor: add xai plugin-sdk boundary canary (#61548)
* docs: plan real plugin-sdk workspace rollout * build: add xai plugin-sdk boundary canary * build: generate plugin-sdk package types * build: hide plugin-sdk core export * build: alias scoped plugin-sdk runtime imports * build: repair plugin-sdk boundary drift * fix(plugins): remove duplicated plugin-sdk entrypoints * test(plugins): make tsc boundary canary portable --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
This commit is contained in:
@@ -15,6 +15,11 @@ export type {
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
export { describeFailoverError, isFailoverError } from "../agents/failover-error.js";
|
||||
export {
|
||||
buildNoCapabilityModelConfiguredMessage,
|
||||
resolveCapabilityModelCandidates,
|
||||
throwCapabilityGenerationFailure,
|
||||
} from "../media-generation/runtime-shared.js";
|
||||
export {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
|
||||
@@ -20,6 +20,11 @@ export type {
|
||||
export type { OpenClawConfig } from "../config/config.js";
|
||||
|
||||
export { describeFailoverError, isFailoverError } from "../agents/failover-error.js";
|
||||
export {
|
||||
buildNoCapabilityModelConfiguredMessage,
|
||||
resolveCapabilityModelCandidates,
|
||||
throwCapabilityGenerationFailure,
|
||||
} from "../media-generation/runtime-shared.js";
|
||||
export {
|
||||
resolveAgentModelFallbackValues,
|
||||
resolveAgentModelPrimaryValue,
|
||||
|
||||
@@ -33,7 +33,11 @@ function applyVitestCapabilityAliasOverrides(params: {
|
||||
return params.aliasMap;
|
||||
}
|
||||
|
||||
const { ["openclaw/plugin-sdk"]: _ignoredRootAlias, ...scopedAliasMap } = params.aliasMap;
|
||||
const {
|
||||
["openclaw/plugin-sdk"]: _ignoredLegacyRootAlias,
|
||||
["@openclaw/plugin-sdk"]: _ignoredScopedRootAlias,
|
||||
...scopedAliasMap
|
||||
} = params.aliasMap;
|
||||
return {
|
||||
...scopedAliasMap,
|
||||
// Capability contract loads only need a narrow SDK slice. Keep those
|
||||
@@ -42,18 +46,33 @@ function applyVitestCapabilityAliasOverrides(params: {
|
||||
"openclaw/plugin-sdk/llm-task": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/llm-task.ts", import.meta.url),
|
||||
),
|
||||
"@openclaw/plugin-sdk/llm-task": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/llm-task.ts", import.meta.url),
|
||||
),
|
||||
"openclaw/plugin-sdk/config-runtime": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/config-runtime.ts", import.meta.url),
|
||||
),
|
||||
"@openclaw/plugin-sdk/config-runtime": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/config-runtime.ts", import.meta.url),
|
||||
),
|
||||
"openclaw/plugin-sdk/media-runtime": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/media-runtime.ts", import.meta.url),
|
||||
),
|
||||
"@openclaw/plugin-sdk/media-runtime": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/media-runtime.ts", import.meta.url),
|
||||
),
|
||||
"openclaw/plugin-sdk/provider-onboard": fileURLToPath(
|
||||
new URL("../plugin-sdk/provider-onboard.ts", import.meta.url),
|
||||
),
|
||||
"@openclaw/plugin-sdk/provider-onboard": fileURLToPath(
|
||||
new URL("../plugin-sdk/provider-onboard.ts", import.meta.url),
|
||||
),
|
||||
"openclaw/plugin-sdk/speech-core": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/speech-core.ts", import.meta.url),
|
||||
),
|
||||
"@openclaw/plugin-sdk/speech-core": fileURLToPath(
|
||||
new URL("./capability-runtime-vitest-shims/speech-core.ts", import.meta.url),
|
||||
),
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
const REPO_ROOT = resolve(import.meta.dirname, "../../..");
|
||||
|
||||
type TsConfigJson = {
|
||||
extends?: unknown;
|
||||
compilerOptions?: {
|
||||
paths?: unknown;
|
||||
rootDir?: unknown;
|
||||
outDir?: unknown;
|
||||
declaration?: unknown;
|
||||
emitDeclarationOnly?: unknown;
|
||||
};
|
||||
include?: unknown;
|
||||
exclude?: unknown;
|
||||
};
|
||||
|
||||
type PackageJson = {
|
||||
name?: unknown;
|
||||
exports?: Record<string, { types?: unknown; default?: unknown }>;
|
||||
devDependencies?: Record<string, string>;
|
||||
};
|
||||
|
||||
function readJsonFile<T>(relativePath: string): T {
|
||||
return JSON.parse(readFileSync(resolve(REPO_ROOT, relativePath), "utf8")) as T;
|
||||
}
|
||||
|
||||
describe("opt-in extension package boundaries", () => {
|
||||
it("keeps the opt-in extension base on real package resolution", () => {
|
||||
const tsconfig = readJsonFile<TsConfigJson>("extensions/tsconfig.package-boundary.base.json");
|
||||
expect(tsconfig.extends).toBe("../tsconfig.json");
|
||||
expect(tsconfig.compilerOptions?.paths).toEqual({
|
||||
"@openclaw/plugin-sdk/*": ["packages/plugin-sdk/dist/packages/plugin-sdk/src/*.d.ts"],
|
||||
});
|
||||
});
|
||||
|
||||
it("roots xai inside its own package and depends on the package sdk", () => {
|
||||
const tsconfig = readJsonFile<TsConfigJson>("extensions/xai/tsconfig.json");
|
||||
expect(tsconfig.extends).toBe("../tsconfig.package-boundary.base.json");
|
||||
expect(tsconfig.compilerOptions?.rootDir).toBe(".");
|
||||
expect(tsconfig.include).toEqual(["./*.ts", "./src/**/*.ts"]);
|
||||
expect(tsconfig.exclude).toEqual(["./**/*.test.ts", "./dist/**", "./node_modules/**"]);
|
||||
|
||||
const packageJson = readJsonFile<PackageJson>("extensions/xai/package.json");
|
||||
expect(packageJson.devDependencies?.["@openclaw/plugin-sdk"]).toBe("workspace:*");
|
||||
});
|
||||
|
||||
it("keeps plugin-sdk package types generated from the package build, not a hand-maintained types bridge", () => {
|
||||
const tsconfig = readJsonFile<TsConfigJson>("packages/plugin-sdk/tsconfig.json");
|
||||
expect(tsconfig.extends).toBe("../../tsconfig.json");
|
||||
expect(tsconfig.compilerOptions?.declaration).toBe(true);
|
||||
expect(tsconfig.compilerOptions?.emitDeclarationOnly).toBe(true);
|
||||
expect(tsconfig.compilerOptions?.outDir).toBe("dist");
|
||||
expect(tsconfig.compilerOptions?.rootDir).toBe("../..");
|
||||
expect(tsconfig.include).toEqual([
|
||||
"src/**/*.ts",
|
||||
"../../src/types/**/*.d.ts",
|
||||
"../../packages/memory-host-sdk/src/**/*.ts",
|
||||
]);
|
||||
|
||||
const packageJson = readJsonFile<PackageJson>("packages/plugin-sdk/package.json");
|
||||
expect(packageJson.name).toBe("@openclaw/plugin-sdk");
|
||||
expect(packageJson.exports?.["./core"]).toBeUndefined();
|
||||
expect(packageJson.exports?.["./plugin-entry"]?.types).toBe(
|
||||
"./dist/packages/plugin-sdk/src/plugin-entry.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./provider-http"]?.types).toBe(
|
||||
"./dist/packages/plugin-sdk/src/provider-http.d.ts",
|
||||
);
|
||||
expect(packageJson.exports?.["./video-generation"]?.types).toBe(
|
||||
"./dist/packages/plugin-sdk/src/video-generation.d.ts",
|
||||
);
|
||||
expect(existsSync(resolve(REPO_ROOT, "packages/plugin-sdk/types/plugin-entry.d.ts"))).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -134,7 +134,12 @@ export function getPluginBoundaryJiti(
|
||||
modulePath,
|
||||
});
|
||||
const aliasMap = {
|
||||
...(pluginSdkAlias ? { "openclaw/plugin-sdk": pluginSdkAlias } : {}),
|
||||
...(pluginSdkAlias
|
||||
? {
|
||||
"openclaw/plugin-sdk": pluginSdkAlias,
|
||||
"@openclaw/plugin-sdk": pluginSdkAlias,
|
||||
}
|
||||
: {}),
|
||||
...resolvePluginSdkScopedAliasMap({ modulePath }),
|
||||
};
|
||||
const loader = createJiti(import.meta.url, {
|
||||
|
||||
@@ -221,10 +221,16 @@ function expectPluginSdkAliasTargets(
|
||||
expect(fs.realpathSync(aliases["openclaw/plugin-sdk"] ?? "")).toBe(
|
||||
fs.realpathSync(params.rootAliasPath),
|
||||
);
|
||||
expect(fs.realpathSync(aliases["@openclaw/plugin-sdk"] ?? "")).toBe(
|
||||
fs.realpathSync(params.rootAliasPath),
|
||||
);
|
||||
if (params.channelRuntimePath) {
|
||||
expect(fs.realpathSync(aliases["openclaw/plugin-sdk/channel-runtime"] ?? "")).toBe(
|
||||
fs.realpathSync(params.channelRuntimePath),
|
||||
);
|
||||
expect(fs.realpathSync(aliases["@openclaw/plugin-sdk/channel-runtime"] ?? "")).toBe(
|
||||
fs.realpathSync(params.channelRuntimePath),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -715,7 +721,7 @@ describe("plugin sdk alias helpers", () => {
|
||||
fs.writeFileSync(jitiBaseFile, "export {};\n", "utf-8");
|
||||
fs.writeFileSync(
|
||||
path.join(copiedSourceDir, "channel.runtime.ts"),
|
||||
`import { resolveOutboundSendDep } from "openclaw/plugin-sdk/infra-runtime";
|
||||
`import { resolveOutboundSendDep } from "@openclaw/plugin-sdk/infra-runtime";
|
||||
|
||||
export const syntheticRuntimeMarker = {
|
||||
resolveOutboundSendDep,
|
||||
@@ -745,6 +751,7 @@ export const syntheticRuntimeMarker = {
|
||||
const withAlias = createJiti(jitiBaseUrl, {
|
||||
...buildPluginLoaderJitiOptions({
|
||||
"openclaw/plugin-sdk/infra-runtime": copiedChannelRuntimeShim,
|
||||
"@openclaw/plugin-sdk/infra-runtime": copiedChannelRuntimeShim,
|
||||
}),
|
||||
tryNative: false,
|
||||
});
|
||||
|
||||
@@ -250,6 +250,7 @@ export function resolvePluginSdkAliasFile(params: {
|
||||
|
||||
const cachedPluginSdkExportedSubpaths = new Map<string, string[]>();
|
||||
const cachedPluginSdkScopedAliasMaps = new Map<string, Record<string, string>>();
|
||||
const PLUGIN_SDK_PACKAGE_NAMES = ["openclaw/plugin-sdk", "@openclaw/plugin-sdk"] as const;
|
||||
|
||||
export function listPluginSdkExportedSubpaths(
|
||||
params: {
|
||||
@@ -318,7 +319,9 @@ export function resolvePluginSdkScopedAliasMap(
|
||||
for (const kind of orderedKinds) {
|
||||
const candidate = candidateMap[kind];
|
||||
if (fs.existsSync(candidate)) {
|
||||
aliasMap[`openclaw/plugin-sdk/${subpath}`] = candidate;
|
||||
for (const packageName of PLUGIN_SDK_PACKAGE_NAMES) {
|
||||
aliasMap[`${packageName}/${subpath}`] = candidate;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -376,7 +379,12 @@ export function buildPluginLoaderAliasMap(
|
||||
? { "openclaw/extension-api": normalizeJitiAliasTargetPath(extensionApiAlias) }
|
||||
: {}),
|
||||
...(pluginSdkAlias
|
||||
? { "openclaw/plugin-sdk": normalizeJitiAliasTargetPath(pluginSdkAlias) }
|
||||
? Object.fromEntries(
|
||||
PLUGIN_SDK_PACKAGE_NAMES.map((packageName) => [
|
||||
packageName,
|
||||
normalizeJitiAliasTargetPath(pluginSdkAlias),
|
||||
]),
|
||||
)
|
||||
: {}),
|
||||
...Object.fromEntries(
|
||||
Object.entries(
|
||||
|
||||
Reference in New Issue
Block a user