Skip to content

Commit 5dfc1b9

Browse files
authored
fix(plugins): warn on invalid install default choice (#71011)
1 parent 1b997be commit 5dfc1b9

5 files changed

Lines changed: 73 additions & 11 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai
99
- Plugins/activation: expose activation plan reasons and a richer plan API so callers can inspect why a plugin was selected while preserving existing id-list activation behavior. (#70943) Thanks @vincentkoc.
1010
- Plugins/source metadata: expose normalized install-source facts on provider and channel catalogs so onboarding can explain npm pinning, integrity state, and local availability before runtime loads. (#70951) Thanks @vincentkoc.
1111
- Plugins/catalog: pin the official external WeCom channel source to an exact npm release plus dist integrity, with a guard that official external sources stay integrity-pinned. (#70997) Thanks @vincentkoc.
12+
- Plugins/source metadata: warn when `openclaw.install.defaultChoice` is invalid or points at a missing source, keeping catalog diagnostics explicit without breaking existing plugins. Thanks @vincentkoc.
1213
- Diagnostics/OTEL: add a lightweight diagnostic trace-context carrier for future span correlation without adding OTEL SDK state to core. Thanks @vincentkoc.
1314
- Diagnostics/OTEL: attach diagnostic trace context to exported OTEL logs so log records can correlate with future spans without adding retained process state. Thanks @vincentkoc.
1415
- Diagnostics/OTEL: pass immutable per-run diagnostic trace context through agent and tool hook contexts, and parent exported diagnostic spans from validated context without retaining global trace state. Thanks @vincentkoc.

docs/plugins/architecture-internals.md

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -888,10 +888,11 @@ Generated channel catalog entries and provider install catalog entries expose
888888
normalized install-source facts next to the raw `openclaw.install` block. The
889889
normalized facts identify whether the npm spec is an exact version or floating
890890
selector, whether expected integrity metadata is present, and whether a local
891-
source path is also available. Consumers should treat `installSource` as an
892-
additive optional field so older hand-built entries and compatibility shims do
893-
not have to synthesize it. This lets onboarding and diagnostics explain
894-
source-plane state without importing plugin runtime.
891+
source path is also available. They also warn when `defaultChoice` is invalid
892+
or points at a source that is not available. Consumers should treat
893+
`installSource` as an additive optional field so older hand-built entries and
894+
compatibility shims do not have to synthesize it. This lets onboarding and
895+
diagnostics explain source-plane state without importing plugin runtime.
895896

896897
Official external npm entries should prefer an exact `npmSpec` plus
897898
`expectedIntegrity`. Bare package names and dist-tags still work for

docs/plugins/manifest.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -596,9 +596,10 @@ entries should pair exact specs with `expectedIntegrity` so update flows fail
596596
closed if the fetched npm artifact no longer matches the pinned release.
597597
Interactive onboarding still offers trusted registry npm specs, including bare
598598
package names and dist-tags, for compatibility. Catalog diagnostics can
599-
distinguish exact, floating, integrity-pinned, and missing-integrity sources.
600-
When `expectedIntegrity` is present, install/update flows enforce it; when it
601-
is omitted, the registry resolution is recorded without an integrity pin.
599+
distinguish exact, floating, integrity-pinned, missing-integrity, and invalid
600+
default-choice sources. When `expectedIntegrity` is present, install/update
601+
flows enforce it; when it is omitted, the registry resolution is recorded
602+
without an integrity pin.
602603

603604
Channel plugins should provide `openclaw.setupEntry` when status, channel list,
604605
or SecretRef scans need to identify configured accounts without loading the full

src/plugins/install-source-info.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,4 +131,50 @@ describe("describePluginInstallSource", () => {
131131
warnings: ["invalid-npm-spec"],
132132
});
133133
});
134+
135+
it("warns when defaultChoice is not a supported install source", () => {
136+
expect(
137+
describePluginInstallSource({
138+
npmSpec: "@vendor/demo@1.2.3",
139+
defaultChoice: "registry",
140+
} as never),
141+
).toEqual({
142+
npm: {
143+
spec: "@vendor/demo@1.2.3",
144+
packageName: "@vendor/demo",
145+
selector: "1.2.3",
146+
selectorKind: "exact-version",
147+
exactVersion: true,
148+
pinState: "exact-without-integrity",
149+
},
150+
warnings: ["invalid-default-choice", "npm-spec-missing-integrity"],
151+
});
152+
});
153+
154+
it("warns when defaultChoice points at a missing source", () => {
155+
expect(
156+
describePluginInstallSource({
157+
localPath: "extensions/demo",
158+
defaultChoice: "npm",
159+
}),
160+
).toEqual({
161+
defaultChoice: "npm",
162+
local: {
163+
path: "extensions/demo",
164+
},
165+
warnings: ["default-choice-missing-source"],
166+
});
167+
});
168+
169+
it("warns when defaultChoice points at an invalid npm source", () => {
170+
expect(
171+
describePluginInstallSource({
172+
npmSpec: "github:vendor/demo",
173+
defaultChoice: "npm",
174+
}),
175+
).toEqual({
176+
defaultChoice: "npm",
177+
warnings: ["invalid-npm-spec", "default-choice-missing-source"],
178+
});
179+
});
134180
});

src/plugins/install-source-info.ts

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import type { PluginPackageInstall } from "./manifest.js";
44

55
export type PluginInstallSourceWarning =
66
| "invalid-npm-spec"
7+
| "invalid-default-choice"
8+
| "default-choice-missing-source"
79
| "npm-spec-floating"
810
| "npm-spec-missing-integrity";
911

@@ -44,18 +46,23 @@ function resolveNpmPinState(params: {
4446
return params.hasIntegrity ? "floating-with-integrity" : "floating-without-integrity";
4547
}
4648

49+
function resolveDefaultChoice(value: unknown): PluginPackageInstall["defaultChoice"] | undefined {
50+
return value === "npm" || value === "local" ? value : undefined;
51+
}
52+
4753
export function describePluginInstallSource(
4854
install: PluginPackageInstall,
4955
): PluginInstallSourceInfo {
5056
const npmSpec = normalizeOptionalString(install.npmSpec);
5157
const localPath = normalizeOptionalString(install.localPath);
52-
const defaultChoice =
53-
install.defaultChoice === "npm" || install.defaultChoice === "local"
54-
? install.defaultChoice
55-
: undefined;
58+
const defaultChoice = resolveDefaultChoice(install.defaultChoice);
5659
const warnings: PluginInstallSourceWarning[] = [];
5760
let npm: PluginInstallNpmSourceInfo | undefined;
5861

62+
if (install.defaultChoice !== undefined && !defaultChoice) {
63+
warnings.push("invalid-default-choice");
64+
}
65+
5966
if (npmSpec) {
6067
const parsed = parseRegistryNpmSpec(npmSpec);
6168
if (parsed) {
@@ -81,6 +88,12 @@ export function describePluginInstallSource(
8188
warnings.push("invalid-npm-spec");
8289
}
8390
}
91+
if (defaultChoice === "npm" && !npm) {
92+
warnings.push("default-choice-missing-source");
93+
}
94+
if (defaultChoice === "local" && !localPath) {
95+
warnings.push("default-choice-missing-source");
96+
}
8497

8598
return {
8699
...(defaultChoice ? { defaultChoice } : {}),

0 commit comments

Comments
 (0)