mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:10:44 +00:00
fix(plugins): warn on invalid install default choice (#71011)
This commit is contained in:
@@ -9,6 +9,7 @@ Docs: https://docs.openclaw.ai
|
||||
- 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.
|
||||
- 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.
|
||||
- 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.
|
||||
|
||||
@@ -888,10 +888,11 @@ Generated channel catalog entries and provider install catalog entries expose
|
||||
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. 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.
|
||||
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.
|
||||
|
||||
Official external npm entries should prefer an exact `npmSpec` plus
|
||||
`expectedIntegrity`. Bare package names and dist-tags still work for
|
||||
|
||||
@@ -596,9 +596,10 @@ entries should pair exact specs with `expectedIntegrity` so update flows fail
|
||||
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, and missing-integrity sources.
|
||||
When `expectedIntegrity` is present, install/update flows enforce it; when it
|
||||
is omitted, the registry resolution is recorded without an integrity pin.
|
||||
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.
|
||||
|
||||
Channel plugins should provide `openclaw.setupEntry` when status, channel list,
|
||||
or SecretRef scans need to identify configured accounts without loading the full
|
||||
|
||||
@@ -131,4 +131,50 @@ describe("describePluginInstallSource", () => {
|
||||
warnings: ["invalid-npm-spec"],
|
||||
});
|
||||
});
|
||||
|
||||
it("warns when defaultChoice is not a supported install source", () => {
|
||||
expect(
|
||||
describePluginInstallSource({
|
||||
npmSpec: "@vendor/demo@1.2.3",
|
||||
defaultChoice: "registry",
|
||||
} as never),
|
||||
).toEqual({
|
||||
npm: {
|
||||
spec: "@vendor/demo@1.2.3",
|
||||
packageName: "@vendor/demo",
|
||||
selector: "1.2.3",
|
||||
selectorKind: "exact-version",
|
||||
exactVersion: true,
|
||||
pinState: "exact-without-integrity",
|
||||
},
|
||||
warnings: ["invalid-default-choice", "npm-spec-missing-integrity"],
|
||||
});
|
||||
});
|
||||
|
||||
it("warns when defaultChoice points at a missing source", () => {
|
||||
expect(
|
||||
describePluginInstallSource({
|
||||
localPath: "extensions/demo",
|
||||
defaultChoice: "npm",
|
||||
}),
|
||||
).toEqual({
|
||||
defaultChoice: "npm",
|
||||
local: {
|
||||
path: "extensions/demo",
|
||||
},
|
||||
warnings: ["default-choice-missing-source"],
|
||||
});
|
||||
});
|
||||
|
||||
it("warns when defaultChoice points at an invalid npm source", () => {
|
||||
expect(
|
||||
describePluginInstallSource({
|
||||
npmSpec: "github:vendor/demo",
|
||||
defaultChoice: "npm",
|
||||
}),
|
||||
).toEqual({
|
||||
defaultChoice: "npm",
|
||||
warnings: ["invalid-npm-spec", "default-choice-missing-source"],
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,8 @@ import type { PluginPackageInstall } from "./manifest.js";
|
||||
|
||||
export type PluginInstallSourceWarning =
|
||||
| "invalid-npm-spec"
|
||||
| "invalid-default-choice"
|
||||
| "default-choice-missing-source"
|
||||
| "npm-spec-floating"
|
||||
| "npm-spec-missing-integrity";
|
||||
|
||||
@@ -44,18 +46,23 @@ function resolveNpmPinState(params: {
|
||||
return params.hasIntegrity ? "floating-with-integrity" : "floating-without-integrity";
|
||||
}
|
||||
|
||||
function resolveDefaultChoice(value: unknown): PluginPackageInstall["defaultChoice"] | undefined {
|
||||
return value === "npm" || value === "local" ? value : undefined;
|
||||
}
|
||||
|
||||
export function describePluginInstallSource(
|
||||
install: PluginPackageInstall,
|
||||
): PluginInstallSourceInfo {
|
||||
const npmSpec = normalizeOptionalString(install.npmSpec);
|
||||
const localPath = normalizeOptionalString(install.localPath);
|
||||
const defaultChoice =
|
||||
install.defaultChoice === "npm" || install.defaultChoice === "local"
|
||||
? install.defaultChoice
|
||||
: undefined;
|
||||
const defaultChoice = resolveDefaultChoice(install.defaultChoice);
|
||||
const warnings: PluginInstallSourceWarning[] = [];
|
||||
let npm: PluginInstallNpmSourceInfo | undefined;
|
||||
|
||||
if (install.defaultChoice !== undefined && !defaultChoice) {
|
||||
warnings.push("invalid-default-choice");
|
||||
}
|
||||
|
||||
if (npmSpec) {
|
||||
const parsed = parseRegistryNpmSpec(npmSpec);
|
||||
if (parsed) {
|
||||
@@ -81,6 +88,12 @@ export function describePluginInstallSource(
|
||||
warnings.push("invalid-npm-spec");
|
||||
}
|
||||
}
|
||||
if (defaultChoice === "npm" && !npm) {
|
||||
warnings.push("default-choice-missing-source");
|
||||
}
|
||||
if (defaultChoice === "local" && !localPath) {
|
||||
warnings.push("default-choice-missing-source");
|
||||
}
|
||||
|
||||
return {
|
||||
...(defaultChoice ? { defaultChoice } : {}),
|
||||
|
||||
Reference in New Issue
Block a user