Skip to content

Commit 5b8bd63

Browse files
authored
feat(plugins): warn on ignored setup runtime (#71253)
* feat(plugins): warn on ignored setup runtime * fix(plugins): avoid fallback setup runtime diagnostics * refactor(plugins): clarify setup runtime lookup
1 parent 6e985a4 commit 5b8bd63

4 files changed

Lines changed: 87 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ Docs: https://docs.openclaw.ai
1717
- Plugin hooks: expose first-class run, message, sender, session, and trace correlation fields on message hook contexts and run lifecycle events. Thanks @vincentkoc.
1818
- Plugins/setup: include `setup.providers[].envVars` in generic provider auth/env lookups and warn non-bundled plugins that still rely on deprecated `providerAuthEnvVars` compatibility metadata. Thanks @vincentkoc.
1919
- Plugins/setup: surface manifest provider auth choices directly in provider setup flow before falling back to setup runtime or install-catalog choices. Thanks @vincentkoc.
20+
- Plugins/setup: warn when descriptor-only setup plugins still ship ignored setup runtime entries, keeping `setup.requiresRuntime: false` semantics explicit without breaking existing metadata. Thanks @vincentkoc.
2021
- TUI/dependencies: remove direct `cli-highlight` usage from the OpenClaw TUI code-block renderer, keeping themed code coloring without the extra root dependency. Thanks @vincentkoc.
2122
- Diagnostics/OTEL: export run, model-call, and tool-execution diagnostic lifecycle events as OTEL spans without retaining live span state. Thanks @vincentkoc.
2223
- Providers/Anthropic Vertex: move the Vertex SDK runtime behind the bundled provider plugin so core no longer owns that provider-specific dependency. Thanks @vincentkoc.

docs/plugins/manifest.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -337,9 +337,11 @@ on `setup.providers[].envVars`.
337337

338338
Set `requiresRuntime: false` only when those descriptors are sufficient for the
339339
setup surface. OpenClaw treats explicit `false` as a descriptor-only contract
340-
and will not execute `setup-api` for setup lookup. Omitted `requiresRuntime`
341-
keeps legacy fallback behavior so existing plugins that added descriptors
342-
without the flag do not break.
340+
and will not execute `setup-api` or `openclaw.setupEntry` for setup lookup. If
341+
a descriptor-only plugin still ships one of those setup runtime entries,
342+
OpenClaw reports an additive diagnostic and continues ignoring it. Omitted
343+
`requiresRuntime` keeps legacy fallback behavior so existing plugins that added
344+
descriptors without the flag do not break.
343345

344346
Because setup lookup can execute plugin-owned `setup-api` code, normalized
345347
`setup.providers[].id` and `setup.cliBackends[]` values must stay unique across

src/plugins/setup-registry.test.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,42 @@ describe("setup-registry getJiti", () => {
373373

374374
expect(resolvePluginSetupProvider({ provider: "openai", env: {} })).toBeUndefined();
375375
expect(resolvePluginSetupCliBackend({ backend: "codex-cli", env: {} })).toBeUndefined();
376+
expect(resolvePluginSetupRegistry({ env: {} })).toEqual({
377+
providers: [],
378+
cliBackends: [],
379+
configMigrations: [],
380+
autoEnableProbes: [],
381+
diagnostics: [
382+
expect.objectContaining({
383+
pluginId: "openai",
384+
code: "setup-descriptor-runtime-disabled",
385+
}),
386+
],
387+
});
388+
expect(mocks.createJiti).not.toHaveBeenCalled();
389+
});
390+
391+
it("does not report descriptor-only diagnostics for bundled setup-api fallback paths", () => {
392+
const parentDir = makeTempDir();
393+
const pluginRoot = path.join(parentDir, "openai");
394+
fs.mkdirSync(pluginRoot);
395+
expect(fs.existsSync(path.join(process.cwd(), "extensions", "openai", "setup-api.ts"))).toBe(
396+
true,
397+
);
398+
mocks.loadPluginManifestRegistry.mockReturnValue({
399+
plugins: [
400+
{
401+
id: "workspace-openai",
402+
rootDir: pluginRoot,
403+
setup: {
404+
providers: [{ id: "workspace-openai" }],
405+
requiresRuntime: false,
406+
},
407+
},
408+
],
409+
diagnostics: [],
410+
});
411+
376412
expect(resolvePluginSetupRegistry({ env: {} })).toEqual({
377413
providers: [],
378414
cliBackends: [],

src/plugins/setup-registry.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ type SetupAutoEnableProbeEntry = {
4747
};
4848

4949
export type PluginSetupRegistryDiagnosticCode =
50+
| "setup-descriptor-runtime-disabled"
5051
| "setup-descriptor-provider-missing-runtime"
5152
| "setup-descriptor-provider-runtime-undeclared"
5253
| "setup-descriptor-cli-backend-missing-runtime"
@@ -189,7 +190,10 @@ function buildSetupCliBackendCacheKey(params: {
189190
});
190191
}
191192

192-
function resolveSetupApiPath(rootDir: string): string | null {
193+
function resolveSetupApiPath(
194+
rootDir: string,
195+
options?: { includeBundledSourceFallback?: boolean },
196+
): string | null {
193197
const orderedExtensions = RUNNING_FROM_BUILT_ARTIFACT
194198
? SETUP_API_EXTENSIONS
195199
: ([...SETUP_API_EXTENSIONS.slice(3), ...SETUP_API_EXTENSIONS.slice(0, 3)] as const);
@@ -209,6 +213,10 @@ function resolveSetupApiPath(rootDir: string): string | null {
209213
return direct;
210214
}
211215

216+
if (options?.includeBundledSourceFallback === false) {
217+
return null;
218+
}
219+
212220
const bundledExtensionDir = path.basename(rootDir);
213221
const repoRootCandidates = [path.resolve(path.dirname(CURRENT_MODULE_PATH), "..", "..")];
214222
for (const repoRoot of repoRootCandidates) {
@@ -283,14 +291,27 @@ function resolveRegister(mod: OpenClawPluginModule): {
283291
return {};
284292
}
285293

294+
function resolveLoadableSetupRuntimeSource(record: PluginManifestRecord): string | null {
295+
return record.setupSource ?? resolveSetupApiPath(record.rootDir);
296+
}
297+
298+
function resolveDeclaredSetupRuntimeSource(record: PluginManifestRecord): string | null {
299+
return (
300+
record.setupSource ??
301+
resolveSetupApiPath(record.rootDir, {
302+
includeBundledSourceFallback: false,
303+
})
304+
);
305+
}
306+
286307
function resolveSetupRegistration(record: PluginManifestRecord): {
287308
setupSource: string;
288309
register: (api: ReturnType<typeof buildPluginApi>) => void | Promise<void>;
289310
} | null {
290311
if (record.setup?.requiresRuntime === false) {
291312
return null;
292313
}
293-
const setupSource = record.setupSource ?? resolveSetupApiPath(record.rootDir);
314+
const setupSource = resolveLoadableSetupRuntimeSource(record);
294315
if (!setupSource) {
295316
return null;
296317
}
@@ -399,6 +420,21 @@ function mapNormalizedIds(ids: readonly string[]): Map<string, string> {
399420
return mapped;
400421
}
401422

423+
function pushDescriptorRuntimeDisabledDiagnostic(params: {
424+
record: PluginManifestRecord;
425+
diagnostics: PluginSetupRegistryDiagnostic[];
426+
}): void {
427+
if (!resolveDeclaredSetupRuntimeSource(params.record)) {
428+
return;
429+
}
430+
params.diagnostics.push({
431+
pluginId: params.record.id,
432+
code: "setup-descriptor-runtime-disabled",
433+
message:
434+
"setup.requiresRuntime is false, so OpenClaw ignored the plugin setup runtime entry. Remove setup-api/openclaw.setupEntry or set requiresRuntime true if setup lookup still needs plugin code.",
435+
});
436+
}
437+
402438
function pushSetupDescriptorDriftDiagnostics(params: {
403439
record: PluginManifestRecord;
404440
providers: readonly ProviderPlugin[];
@@ -504,6 +540,13 @@ export function resolvePluginSetupRegistry(params?: {
504540
if (selectedPluginIds && !selectedPluginIds.has(record.id)) {
505541
continue;
506542
}
543+
if (record.setup?.requiresRuntime === false) {
544+
pushDescriptorRuntimeDisabledDiagnostic({
545+
record,
546+
diagnostics,
547+
});
548+
continue;
549+
}
507550
const setupRegistration = resolveSetupRegistration(record);
508551
if (!setupRegistration) {
509552
continue;

0 commit comments

Comments
 (0)