mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:20:43 +00:00
fix: preserve clawhub install selectors
This commit is contained in:
@@ -16,6 +16,7 @@ Docs: https://docs.openclaw.ai
|
||||
|
||||
### Fixes
|
||||
|
||||
- CLI/plugins: preserve unversioned ClawHub install specs so `plugins update` can follow newer ClawHub releases instead of pinning to the initially resolved version. Fixes #63010; supersedes #58426. Thanks @kangsen1234 and @robinspt.
|
||||
- Gateway/models: move local-provider pricing opt-outs, OpenRouter/LiteLLM aliases, and proxy passthrough pricing lookup into plugin manifest metadata so core no longer carries extension-specific pricing tables. Thanks @codex.
|
||||
- CLI/update: honor `OPENCLAW_NO_AUTO_UPDATE=1` as a gateway startup kill-switch for configured background package auto-updates, so operators can hold a deliberate downgrade during incident recovery without editing config first. Fixes #72715. Thanks @Xivi08.
|
||||
- Agents/Claude CLI: force live-session launches to include `--output-format stream-json` whenever OpenClaw adds `--input-format stream-json`, so new Claude CLI sessions no longer fail immediately while reusable sessions keep working. Fixes #72206. Thanks @kwangwonkoh and @Xivi08.
|
||||
|
||||
@@ -138,6 +138,7 @@ openclaw plugins install npm:@scope/plugin-name@1.0.1
|
||||
```
|
||||
|
||||
OpenClaw downloads the package archive from ClawHub, checks the advertised plugin API / minimum gateway compatibility, then installs it through the normal archive path. Recorded installs keep their ClawHub source metadata for later updates.
|
||||
Unversioned ClawHub installs keep an unversioned recorded spec so `openclaw plugins update` can follow newer ClawHub releases; explicit version or tag selectors such as `clawhub:pkg@1.2.3` and `clawhub:pkg@beta` remain pinned to that selector.
|
||||
|
||||
#### Marketplace shorthand
|
||||
|
||||
|
||||
@@ -479,8 +479,6 @@ vi.mock("../plugins/clawhub.js", () => ({
|
||||
installPluginFromClawHub,
|
||||
...args,
|
||||
)) as (typeof import("../plugins/clawhub.js"))["installPluginFromClawHub"],
|
||||
formatClawHubSpecifier: ({ name, version }: { name: string; version?: string }) =>
|
||||
`clawhub:${name}${version ? `@${version}` : ""}`,
|
||||
}));
|
||||
|
||||
vi.mock("../infra/clawhub.js", () => ({
|
||||
|
||||
@@ -408,8 +408,9 @@ describe("plugins cli install", () => {
|
||||
expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
|
||||
demo: expect.objectContaining({
|
||||
source: "clawhub",
|
||||
spec: "clawhub:demo@1.2.3",
|
||||
spec: "clawhub:demo",
|
||||
installPath: cliInstallPath("demo"),
|
||||
version: "1.2.3",
|
||||
clawhubPackage: "demo",
|
||||
clawhubFamily: "code-plugin",
|
||||
clawhubChannel: "official",
|
||||
@@ -491,6 +492,48 @@ describe("plugins cli install", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("keeps explicit ClawHub versions pinned in install records", async () => {
|
||||
const cfg = {
|
||||
plugins: {
|
||||
entries: {},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const enabledCfg = createEnabledPluginConfig("demo");
|
||||
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
parseClawHubPluginSpec.mockReturnValue({ name: "demo", version: "1.2.3" });
|
||||
installPluginFromClawHub.mockResolvedValue(
|
||||
createClawHubInstallResult({
|
||||
pluginId: "demo",
|
||||
packageName: "demo",
|
||||
version: "1.2.3",
|
||||
channel: "official",
|
||||
}),
|
||||
);
|
||||
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
|
||||
applyExclusiveSlotSelection.mockReturnValue({
|
||||
config: enabledCfg,
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "install", "clawhub:demo@1.2.3"]);
|
||||
|
||||
expect(installPluginFromClawHub).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "clawhub:demo@1.2.3",
|
||||
}),
|
||||
);
|
||||
expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
|
||||
demo: expect.objectContaining({
|
||||
source: "clawhub",
|
||||
spec: "clawhub:demo@1.2.3",
|
||||
installPath: cliInstallPath("demo"),
|
||||
version: "1.2.3",
|
||||
clawhubPackage: "demo",
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it("prefers ClawHub before npm for bare plugin specs", async () => {
|
||||
const cfg = {
|
||||
plugins: {
|
||||
@@ -524,14 +567,54 @@ describe("plugins cli install", () => {
|
||||
expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
|
||||
demo: expect.objectContaining({
|
||||
source: "clawhub",
|
||||
spec: "clawhub:demo@1.2.3",
|
||||
spec: "clawhub:demo",
|
||||
installPath: cliInstallPath("demo"),
|
||||
version: "1.2.3",
|
||||
clawhubPackage: "demo",
|
||||
}),
|
||||
});
|
||||
expect(writeConfigFile).toHaveBeenCalledWith(enabledCfg);
|
||||
});
|
||||
|
||||
it("keeps explicit bare ClawHub selectors in install records", async () => {
|
||||
const cfg = {
|
||||
plugins: {
|
||||
entries: {},
|
||||
},
|
||||
} as OpenClawConfig;
|
||||
const enabledCfg = createEnabledPluginConfig("demo");
|
||||
loadConfig.mockReturnValue(cfg);
|
||||
installPluginFromClawHub.mockResolvedValue(
|
||||
createClawHubInstallResult({
|
||||
pluginId: "demo",
|
||||
packageName: "demo",
|
||||
version: "1.2.3-beta.1",
|
||||
channel: "community",
|
||||
}),
|
||||
);
|
||||
enablePluginInConfig.mockReturnValue({ config: enabledCfg });
|
||||
applyExclusiveSlotSelection.mockReturnValue({
|
||||
config: enabledCfg,
|
||||
warnings: [],
|
||||
});
|
||||
|
||||
await runPluginsCommand(["plugins", "install", "demo@beta"]);
|
||||
|
||||
expect(installPluginFromClawHub).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
spec: "clawhub:demo@beta",
|
||||
}),
|
||||
);
|
||||
expect(writePersistedInstalledPluginIndexInstallRecords).toHaveBeenCalledWith({
|
||||
demo: expect.objectContaining({
|
||||
source: "clawhub",
|
||||
spec: "clawhub:demo@beta",
|
||||
version: "1.2.3-beta.1",
|
||||
clawhubPackage: "demo",
|
||||
}),
|
||||
});
|
||||
});
|
||||
|
||||
it("falls back to npm when ClawHub does not have the package", async () => {
|
||||
primeNpmPluginFallback();
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import { resolveArchiveKind } from "../infra/archive.js";
|
||||
import { parseClawHubPluginSpec } from "../infra/clawhub.js";
|
||||
import { formatErrorMessage } from "../infra/errors.js";
|
||||
import { type BundledPluginSource, findBundledPluginSource } from "../plugins/bundled-sources.js";
|
||||
import { formatClawHubSpecifier, installPluginFromClawHub } from "../plugins/clawhub.js";
|
||||
import { installPluginFromClawHub } from "../plugins/clawhub.js";
|
||||
import type { InstallSafetyOverrides } from "../plugins/install-security-scan.js";
|
||||
import {
|
||||
PLUGIN_INSTALL_ERROR_CODE,
|
||||
@@ -672,10 +672,7 @@ export async function runPluginInstallCommand(params: {
|
||||
pluginId: result.pluginId,
|
||||
install: {
|
||||
source: "clawhub",
|
||||
spec: formatClawHubSpecifier({
|
||||
name: result.clawhub.clawhubPackage,
|
||||
version: result.clawhub.version,
|
||||
}),
|
||||
spec: raw,
|
||||
installPath: result.targetDir,
|
||||
version: result.version,
|
||||
integrity: result.clawhub.integrity,
|
||||
@@ -704,10 +701,7 @@ export async function runPluginInstallCommand(params: {
|
||||
pluginId: clawhubResult.pluginId,
|
||||
install: {
|
||||
source: "clawhub",
|
||||
spec: formatClawHubSpecifier({
|
||||
name: clawhubResult.clawhub.clawhubPackage,
|
||||
version: clawhubResult.clawhub.version,
|
||||
}),
|
||||
spec: preferredClawHubSpec,
|
||||
installPath: clawhubResult.targetDir,
|
||||
version: clawhubResult.version,
|
||||
integrity: clawhubResult.clawhub.integrity,
|
||||
|
||||
Reference in New Issue
Block a user