mirror of
https://fastgit.cc/github.com/openclaw/openclaw
synced 2026-05-01 06:36:23 +08:00
fix(plugin-sdk): keep test contracts publishable
This commit is contained in:
@@ -28,6 +28,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- Plugin SDK/testing: lazy-load TypeScript from the plugin test-contract runtime and add release checks for critical SDK contract entrypoint imports and bundle size, so published packages fail preflight before shipping ESM-incompatible or oversized contract helpers. Thanks @vincentkoc.
|
||||
- CLI/browser: preserve parent flags while lazy-loading browser subcommands, so `openclaw browser --json open` and `openclaw browser --json tabs` keep machine-readable output after reparsing. Fixes #74574. Thanks @devintegeritsm.
|
||||
- Plugins/runtime-deps: add `openclaw plugins deps` inspection and repair with script-free package-manager defaults shared across plugin installers, so operators can repair missing bundled runtime deps without corrupting JSON output or blocking unrelated conflict-free deps. Thanks @vincentkoc.
|
||||
- Agents/output: strip internal `[tool calls omitted]` replay placeholders from user-facing replies while preserving visible reply whitespace. Fixes #74573. Thanks @blaspat.
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { execFileSync, execSync } from "node:child_process";
|
||||
import {
|
||||
existsSync,
|
||||
lstatSync,
|
||||
mkdtempSync,
|
||||
mkdirSync,
|
||||
realpathSync,
|
||||
@@ -116,6 +117,15 @@ const appcastPath = resolve("appcast.xml");
|
||||
const laneBuildMin = 1_000_000_000;
|
||||
const laneFloorAdoptionDateKey = 20260227;
|
||||
const SAFE_UNIX_SMOKE_PATH = "/usr/bin:/bin";
|
||||
export const MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES = 2 * 1024 * 1024;
|
||||
export const CRITICAL_PLUGIN_SDK_SIZE_CHECK_SPECIFIERS = [
|
||||
"openclaw/plugin-sdk/agent-runtime-test-contracts",
|
||||
"openclaw/plugin-sdk/plugin-test-contracts",
|
||||
"openclaw/plugin-sdk/provider-test-contracts",
|
||||
] as const;
|
||||
export const CRITICAL_PLUGIN_SDK_IMPORT_SMOKE_SPECIFIERS = [
|
||||
"openclaw/plugin-sdk/plugin-test-contracts",
|
||||
] as const;
|
||||
export const PACKED_CLI_SMOKE_COMMANDS = [
|
||||
["--help"],
|
||||
["onboard", "--help"],
|
||||
@@ -843,6 +853,44 @@ async function checkPluginSdkExports() {
|
||||
}
|
||||
}
|
||||
|
||||
export function collectCriticalPluginSdkEntrypointSizeErrors(rootDir = process.cwd()): string[] {
|
||||
const errors: string[] = [];
|
||||
for (const specifier of CRITICAL_PLUGIN_SDK_SIZE_CHECK_SPECIFIERS) {
|
||||
const subpath = specifier.slice("openclaw/plugin-sdk/".length);
|
||||
const relativePath = `dist/plugin-sdk/${subpath}.js`;
|
||||
const filePath = resolve(rootDir, relativePath);
|
||||
if (!existsSync(filePath)) {
|
||||
errors.push(`${relativePath} is missing.`);
|
||||
continue;
|
||||
}
|
||||
const stat = lstatSync(filePath);
|
||||
if (!stat.isFile()) {
|
||||
errors.push(`${relativePath} is not a file.`);
|
||||
continue;
|
||||
}
|
||||
if (stat.size > MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES) {
|
||||
errors.push(
|
||||
`${relativePath} is ${stat.size} bytes, exceeding ${MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES} bytes. Keep public SDK test-contract entrypoints lazy and avoid bundling compiler/runtime internals.`,
|
||||
);
|
||||
}
|
||||
}
|
||||
return errors;
|
||||
}
|
||||
|
||||
function runCriticalPluginSdkEntrypointImportSmoke() {
|
||||
const script = [
|
||||
`const specifiers = ${JSON.stringify(CRITICAL_PLUGIN_SDK_IMPORT_SMOKE_SPECIFIERS)};`,
|
||||
`const importModule = new Function("specifier", "return imp" + "ort(specifier)");`,
|
||||
"for (const specifier of specifiers) {",
|
||||
" await importModule(specifier);",
|
||||
"}",
|
||||
].join("\n");
|
||||
execFileSync(process.execPath, ["--input-type=module", "--eval", script], {
|
||||
cwd: process.cwd(),
|
||||
stdio: "inherit",
|
||||
});
|
||||
}
|
||||
|
||||
async function main() {
|
||||
checkAppcastSparkleVersions();
|
||||
checkCliBootstrapExternalImports({
|
||||
@@ -851,6 +899,15 @@ async function main() {
|
||||
},
|
||||
});
|
||||
await checkPluginSdkExports();
|
||||
const criticalPluginSdkEntrypointErrors = collectCriticalPluginSdkEntrypointSizeErrors();
|
||||
if (criticalPluginSdkEntrypointErrors.length > 0) {
|
||||
console.error("release-check: critical plugin-sdk entrypoint validation failed:");
|
||||
for (const error of criticalPluginSdkEntrypointErrors) {
|
||||
console.error(` - ${error}`);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
runCriticalPluginSdkEntrypointImportSmoke();
|
||||
checkBundledExtensionMetadata();
|
||||
await writePackageDistInventory(process.cwd());
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { createRequire } from "node:module";
|
||||
import path from "node:path";
|
||||
import ts from "typescript";
|
||||
|
||||
const nodeRequire = createRequire(import.meta.url);
|
||||
|
||||
function loadTypeScript(): typeof import("typescript") {
|
||||
return nodeRequire("typescript") as typeof import("typescript");
|
||||
}
|
||||
|
||||
const JITI_EXTENSIONS = [
|
||||
".ts",
|
||||
@@ -79,6 +85,7 @@ function resolveLocalModulePath(filePath: string, specifier: string): string | n
|
||||
}
|
||||
|
||||
function collectSourceModuleRefs(filePath: string): SourceModuleRef[] {
|
||||
const ts = loadTypeScript();
|
||||
const sourceText = readFileSync(filePath, "utf8");
|
||||
const sourceFile = ts.createSourceFile(filePath, sourceText, ts.ScriptTarget.Latest, true);
|
||||
const refs: SourceModuleRef[] = [];
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
collectAppcastSparkleVersionErrors,
|
||||
collectBundledExtensionManifestErrors,
|
||||
collectBundledPluginRootRuntimeMirrorErrors,
|
||||
collectCriticalPluginSdkEntrypointSizeErrors,
|
||||
collectDeclaredRootRuntimeDependencyMetadataErrors,
|
||||
collectForbiddenPackContentPaths,
|
||||
collectInstalledBundledPluginRuntimeDepErrors,
|
||||
@@ -21,6 +22,7 @@ import {
|
||||
collectPackUnpackedSizeErrors,
|
||||
createPackedCliSmokeEnv,
|
||||
createPackedBundledPluginPostinstallEnv,
|
||||
MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES,
|
||||
PACKED_CLI_SMOKE_COMMANDS,
|
||||
packageNameFromSpecifier,
|
||||
resolveMissingPackBuildHint,
|
||||
@@ -729,6 +731,30 @@ describe("collectPackUnpackedSizeErrors", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("collectCriticalPluginSdkEntrypointSizeErrors", () => {
|
||||
it("flags oversized plugin SDK test-contract entrypoints before publish", () => {
|
||||
const root = mkdtempSync(join(tmpdir(), "release-check-critical-sdk-"));
|
||||
try {
|
||||
const pluginSdkDir = join(root, "dist", "plugin-sdk");
|
||||
mkdirSync(pluginSdkDir, { recursive: true });
|
||||
writeFileSync(join(pluginSdkDir, "agent-runtime-test-contracts.js"), "export {};\n");
|
||||
writeFileSync(join(pluginSdkDir, "provider-test-contracts.js"), "export {};\n");
|
||||
writeFileSync(
|
||||
join(pluginSdkDir, "plugin-test-contracts.js"),
|
||||
"x".repeat(MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES + 1),
|
||||
);
|
||||
|
||||
expect(collectCriticalPluginSdkEntrypointSizeErrors(root)).toEqual([
|
||||
`dist/plugin-sdk/plugin-test-contracts.js is ${
|
||||
MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES + 1
|
||||
} bytes, exceeding ${MAX_CRITICAL_PLUGIN_SDK_ENTRYPOINT_BYTES} bytes. Keep public SDK test-contract entrypoints lazy and avoid bundling compiler/runtime internals.`,
|
||||
]);
|
||||
} finally {
|
||||
rmSync(root, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe("createPackedBundledPluginPostinstallEnv", () => {
|
||||
it("keeps packed postinstall on the lazy bundled dependency path", () => {
|
||||
expect(createPackedBundledPluginPostinstallEnv({ PATH: "/usr/bin" })).toEqual({
|
||||
|
||||
Reference in New Issue
Block a user