Skip to content

Commit a63b879

Browse files
committed
fix(plugin-sdk): bundle zod subpath artifact
1 parent 5d55717 commit a63b879

4 files changed

Lines changed: 84 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ Docs: https://docs.openclaw.ai
112112
### Fixes
113113

114114
- Gate Slack startup user allowlist resolution [AI]. (#77898) Thanks @pgondhi987.
115+
- Plugin SDK: bundle the `openclaw/plugin-sdk/zod` runtime export into the published package so pnpm global installs do not need a package-local `zod` symlink before bundled channel plugins can register. Fixes #78398. Thanks @ggzeng.
115116
- OpenAI/Codex: suppress stale `openai-codex` GPT-5.1/5.2/5.3 model refs that ChatGPT/Codex OAuth accounts now reject, keeping model lists, config validation, and forward-compat resolution on current 5.4/5.5 routes. Fixes #67158. Thanks @drpau.
116117
- Google Meet/Voice Call: wait longer before playing PIN-derived Twilio DTMF for Meet dial-in prompts and retire stale delegated phone sessions instead of reusing completed calls.
117118
- PDF/Codex: include extraction-fallback instructions for `openai-codex/*` PDF tool requests so Codex Responses receives its required system prompt. Fixes #77872. Thanks @anyech.

scripts/openclaw-npm-postpublish-verify.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ export function collectInstalledPackageErrors(params: {
119119
}
120120

121121
errors.push(...collectInstalledContextEngineRuntimeErrors(params.packageRoot));
122+
errors.push(...collectInstalledPluginSdkZodArtifactErrors(params.packageRoot));
122123
errors.push(...collectInstalledRootDependencyManifestErrors(params.packageRoot));
123124

124125
return errors;
@@ -204,6 +205,39 @@ export function collectInstalledContextEngineRuntimeErrors(packageRoot: string):
204205
return errors;
205206
}
206207

208+
export function collectInstalledPluginSdkZodArtifactErrors(packageRoot: string): string[] {
209+
const relativePath = "dist/plugin-sdk/zod.js";
210+
const artifactPath = join(packageRoot, relativePath);
211+
if (!existsSync(artifactPath)) {
212+
return [`installed package is missing required plugin SDK artifact: ${relativePath}`];
213+
}
214+
const artifactStat = lstatSync(artifactPath);
215+
if (!artifactStat.isFile() || artifactStat.size > MAX_INSTALLED_ROOT_DIST_JS_BYTES) {
216+
return [
217+
`installed package plugin SDK artifact '${relativePath}' is invalid or exceeds ${MAX_INSTALLED_ROOT_DIST_JS_BYTES} bytes.`,
218+
];
219+
}
220+
221+
const source = readFileSync(artifactPath, "utf8");
222+
const parsedSpecifiers = extractJavaScriptImportSpecifiers(source);
223+
if (!parsedSpecifiers.ok) {
224+
return [
225+
`installed package plugin SDK artifact '${relativePath}' could not be parsed for runtime dependency verification: ${parsedSpecifiers.error}.`,
226+
];
227+
}
228+
229+
const bareZodSpecifiers = [...parsedSpecifiers.specifiers]
230+
.filter((specifier) => specifier === "zod" || specifier.startsWith("zod/"))
231+
.toSorted((left, right) => left.localeCompare(right));
232+
if (bareZodSpecifiers.length === 0) {
233+
return [];
234+
}
235+
236+
return [
237+
`installed package plugin SDK artifact '${relativePath}' must be self-contained but imports ${bareZodSpecifiers.join(", ")}.`,
238+
];
239+
}
240+
207241
function listInstalledRootDistJavaScriptFiles(packageRoot: string): string[] {
208242
return listDistJavaScriptFiles(packageRoot, {
209243
skipRelativePath: (relativePath) => relativePath.startsWith("extensions/"),

test/openclaw-npm-postpublish-verify.test.ts

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import {
77
buildPublishedInstallScenarios,
88
collectInstalledBundledRuntimeSidecarPaths,
99
collectInstalledContextEngineRuntimeErrors,
10+
collectInstalledPluginSdkZodArtifactErrors,
1011
collectInstalledRootDependencyManifestErrors,
1112
collectInstalledPackageErrors,
1213
normalizeInstalledBinaryVersion,
@@ -146,6 +147,48 @@ describe("collectInstalledContextEngineRuntimeErrors", () => {
146147
});
147148
});
148149

150+
describe("collectInstalledPluginSdkZodArtifactErrors", () => {
151+
function makeInstalledPackageRoot(): string {
152+
return mkdtempSync(join(tmpdir(), "openclaw-postpublish-zod-sdk-"));
153+
}
154+
155+
it("rejects plugin-sdk zod artifacts with a bare zod export", () => {
156+
const packageRoot = makeInstalledPackageRoot();
157+
158+
try {
159+
mkdirSync(join(packageRoot, "dist", "plugin-sdk"), { recursive: true });
160+
writeFileSync(
161+
join(packageRoot, "dist", "plugin-sdk", "zod.js"),
162+
'import "../zod-D2c0iocA.js";\nexport * from "zod";\n',
163+
"utf8",
164+
);
165+
166+
expect(collectInstalledPluginSdkZodArtifactErrors(packageRoot)).toEqual([
167+
"installed package plugin SDK artifact 'dist/plugin-sdk/zod.js' must be self-contained but imports zod.",
168+
]);
169+
} finally {
170+
rmSync(packageRoot, { recursive: true, force: true });
171+
}
172+
});
173+
174+
it("accepts plugin-sdk zod artifacts that only import package-local chunks", () => {
175+
const packageRoot = makeInstalledPackageRoot();
176+
177+
try {
178+
mkdirSync(join(packageRoot, "dist", "plugin-sdk"), { recursive: true });
179+
writeFileSync(
180+
join(packageRoot, "dist", "plugin-sdk", "zod.js"),
181+
'export { z } from "../zod-D2c0iocA.js";\n',
182+
"utf8",
183+
);
184+
185+
expect(collectInstalledPluginSdkZodArtifactErrors(packageRoot)).toEqual([]);
186+
} finally {
187+
rmSync(packageRoot, { recursive: true, force: true });
188+
}
189+
});
190+
});
191+
149192
describe("normalizeInstalledBinaryVersion", () => {
150193
it("accepts decorated CLI version output", () => {
151194
expect(normalizeInstalledBinaryVersion("OpenClaw 2026.4.8 (9ece252)")).toBe("2026.4.8");

tsdown.config.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,12 @@ function shouldNeverBundleDependency(id: string): boolean {
175175
}
176176

177177
function shouldAlwaysBundleDependency(id: string): boolean {
178-
return id === "@openclaw/fs-safe" || id.startsWith("@openclaw/fs-safe/");
178+
return (
179+
id === "@openclaw/fs-safe" ||
180+
id.startsWith("@openclaw/fs-safe/") ||
181+
id === "zod" ||
182+
id.startsWith("zod/")
183+
);
179184
}
180185

181186
function listBundledPluginEntrySources(

0 commit comments

Comments
 (0)