fix(cli): prefer built doctor plugin runtimes

This commit is contained in:
Gustavo Madeira Santana
2026-04-21 17:42:40 -04:00
parent 4712692f4c
commit 47e5d44805
4 changed files with 43 additions and 25 deletions

View File

@@ -2,6 +2,12 @@
Docs: https://docs.openclaw.ai
## Unreleased
### Changes
- CLI/doctor plugins: lazy-load doctor plugin paths and prefer installed plugin `dist/*` runtime entries over source-adjacent JavaScript fallbacks, keeping cold doctor startup on built plugin artifacts. (#69840) Thanks @gumadeiras.
## 2026.4.21
### Changes

View File

@@ -424,6 +424,8 @@ describe("discoverOpenClawPlugins", () => {
});
writePluginEntry(path.join(pluginDir, "src", "index.ts"));
writePluginEntry(path.join(pluginDir, "src", "setup-entry.ts"));
writePluginEntry(path.join(pluginDir, "src", "index.js"));
writePluginEntry(path.join(pluginDir, "src", "setup-entry.js"));
writePluginEntry(path.join(pluginDir, "dist", "index.js"));
writePluginEntry(path.join(pluginDir, "dist", "setup-entry.js"));

View File

@@ -635,8 +635,8 @@ function listBuiltRuntimeEntryCandidates(entryPath: string): string[] {
`${basePath}.cjs`,
];
const candidates = [
...withJavaScriptExtensions(withoutExtension),
...withJavaScriptExtensions(distWithoutExtension),
...withJavaScriptExtensions(withoutExtension),
];
return [...new Set(candidates)].filter((candidate) => candidate !== normalized);
}

View File

@@ -63,6 +63,7 @@ describe("doctor-contract-registry getJiti", () => {
it("prefers doctor-contract-api over the broader contract-api surface", () => {
const pluginRoot = makeTempDir();
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("darwin");
fs.writeFileSync(
path.join(pluginRoot, "doctor-contract-api.cjs"),
"module.exports = { legacyConfigRules: [{ path: ['plugins', 'entries', 'demo', 'doctor'], message: 'doctor contract' }] };\n",
@@ -78,22 +79,27 @@ describe("doctor-contract-registry getJiti", () => {
diagnostics: [],
});
expect(
listPluginDoctorLegacyConfigRules({
workspaceDir: pluginRoot,
env: {},
}),
).toEqual([
{
path: ["plugins", "entries", "demo", "doctor"],
message: "doctor contract",
},
]);
expect(mocks.createJiti).not.toHaveBeenCalled();
try {
expect(
listPluginDoctorLegacyConfigRules({
workspaceDir: pluginRoot,
env: {},
}),
).toEqual([
{
path: ["plugins", "entries", "demo", "doctor"],
message: "doctor contract",
},
]);
expect(mocks.createJiti).not.toHaveBeenCalled();
} finally {
platformSpy.mockRestore();
}
});
it("uses native require for compatible JavaScript contract modules", () => {
const pluginRoot = makeTempDir();
const platformSpy = vi.spyOn(process, "platform", "get").mockReturnValue("darwin");
fs.writeFileSync(
path.join(pluginRoot, "doctor-contract-api.cjs"),
"module.exports = { legacyConfigRules: [{ path: ['plugins', 'entries', 'demo', 'legacy'], message: 'legacy demo key' }] };\n",
@@ -104,18 +110,22 @@ describe("doctor-contract-registry getJiti", () => {
diagnostics: [],
});
expect(
listPluginDoctorLegacyConfigRules({
workspaceDir: pluginRoot,
env: {},
}),
).toEqual([
{
path: ["plugins", "entries", "demo", "legacy"],
message: "legacy demo key",
},
]);
expect(mocks.createJiti).not.toHaveBeenCalled();
try {
expect(
listPluginDoctorLegacyConfigRules({
workspaceDir: pluginRoot,
env: {},
}),
).toEqual([
{
path: ["plugins", "entries", "demo", "legacy"],
message: "legacy demo key",
},
]);
expect(mocks.createJiti).not.toHaveBeenCalled();
} finally {
platformSpy.mockRestore();
}
});
it("narrows touched-path doctor ids for scoped dry-run validation", () => {