fix(plugins): warn on orphan install integrity (#71163)

This commit is contained in:
Vincent Koc
2026-04-24 09:01:15 -07:00
committed by GitHub
parent 5dfc1b90e1
commit bbe0234720
5 changed files with 40 additions and 8 deletions

View File

@@ -10,6 +10,7 @@ Docs: https://docs.openclaw.ai
- 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.
- 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.
- 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.
- Plugins/source metadata: warn when `openclaw.install.expectedIntegrity` is present without a valid npm source, keeping orphaned integrity metadata visible without rejecting existing plugins. Thanks @vincentkoc.
- Diagnostics/OTEL: add a lightweight diagnostic trace-context carrier for future span correlation without adding OTEL SDK state to core. Thanks @vincentkoc.
- 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.
- 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.

View File

@@ -889,10 +889,11 @@ normalized install-source facts next to the raw `openclaw.install` block. The
normalized facts identify whether the npm spec is an exact version or floating
selector, whether expected integrity metadata is present, and whether a local
source path is also available. They also warn when `defaultChoice` is invalid
or points at a source that is not available. Consumers should treat
`installSource` as an additive optional field so older hand-built entries and
compatibility shims do not have to synthesize it. This lets onboarding and
diagnostics explain source-plane state without importing plugin runtime.
or points at a source that is not available, and when npm integrity metadata is
present without a valid npm source. Consumers should treat `installSource` as
an additive optional field so older hand-built entries and compatibility shims
do not have to synthesize it. This lets onboarding and diagnostics explain
source-plane state without importing plugin runtime.
Official external npm entries should prefer an exact `npmSpec` plus
`expectedIntegrity`. Bare package names and dist-tags still work for

View File

@@ -597,9 +597,10 @@ closed if the fetched npm artifact no longer matches the pinned release.
Interactive onboarding still offers trusted registry npm specs, including bare
package names and dist-tags, for compatibility. Catalog diagnostics can
distinguish exact, floating, integrity-pinned, missing-integrity, and invalid
default-choice sources. When `expectedIntegrity` is present, install/update
flows enforce it; when it is omitted, the registry resolution is recorded
without an integrity pin.
default-choice sources. They also warn when `expectedIntegrity` is present but
there is no valid npm source it can pin. When `expectedIntegrity` is present,
install/update flows enforce it; when it is omitted, the registry resolution is
recorded without an integrity pin.
Channel plugins should provide `openclaw.setupEntry` when status, channel list,
or SecretRef scans need to identify configured accounts without loading the full

View File

@@ -177,4 +177,29 @@ describe("describePluginInstallSource", () => {
warnings: ["invalid-npm-spec", "default-choice-missing-source"],
});
});
it("warns when integrity metadata has no npm source", () => {
expect(
describePluginInstallSource({
localPath: "extensions/demo",
expectedIntegrity: "sha512-demo",
}),
).toEqual({
local: {
path: "extensions/demo",
},
warnings: ["npm-integrity-without-source"],
});
});
it("warns when integrity metadata is attached to an invalid npm source", () => {
expect(
describePluginInstallSource({
npmSpec: "github:vendor/demo",
expectedIntegrity: "sha512-demo",
}),
).toEqual({
warnings: ["invalid-npm-spec", "npm-integrity-without-source"],
});
});
});

View File

@@ -6,6 +6,7 @@ export type PluginInstallSourceWarning =
| "invalid-npm-spec"
| "invalid-default-choice"
| "default-choice-missing-source"
| "npm-integrity-without-source"
| "npm-spec-floating"
| "npm-spec-missing-integrity";
@@ -56,6 +57,7 @@ export function describePluginInstallSource(
const npmSpec = normalizeOptionalString(install.npmSpec);
const localPath = normalizeOptionalString(install.localPath);
const defaultChoice = resolveDefaultChoice(install.defaultChoice);
const expectedIntegrity = normalizeOptionalString(install.expectedIntegrity);
const warnings: PluginInstallSourceWarning[] = [];
let npm: PluginInstallNpmSourceInfo | undefined;
@@ -67,7 +69,6 @@ export function describePluginInstallSource(
const parsed = parseRegistryNpmSpec(npmSpec);
if (parsed) {
const exactVersion = parsed.selectorKind === "exact-version";
const expectedIntegrity = normalizeOptionalString(install.expectedIntegrity);
const hasIntegrity = Boolean(expectedIntegrity);
if (!exactVersion) {
warnings.push("npm-spec-floating");
@@ -94,6 +95,9 @@ export function describePluginInstallSource(
if (defaultChoice === "local" && !localPath) {
warnings.push("default-choice-missing-source");
}
if (expectedIntegrity && !npm) {
warnings.push("npm-integrity-without-source");
}
return {
...(defaultChoice ? { defaultChoice } : {}),