fix: guide config users to plugin commands

This commit is contained in:
Shakker
2026-04-25 23:28:57 +01:00
parent ebbefd6903
commit df7348e586
2 changed files with 29 additions and 21 deletions

View File

@@ -274,26 +274,6 @@ describe("config cli", () => {
});
it("rejects plugin install record config updates", async () => {
const resolved = {
plugins: {
installs: {
"openclaw-web-search": {
source: "npm",
spec: "@ollama/openclaw-web-search",
installPath: "/tmp/openclaw-web-search",
version: "0.2.2",
resolvedName: "@ollama/openclaw-web-search",
resolvedVersion: "0.2.2",
resolvedSpec: "@ollama/openclaw-web-search@0.2.2",
integrity: "sha512-test",
resolvedAt: "2026-04-22T10:33:58.083Z",
installedAt: "2026-04-22T10:33:58.240Z",
},
},
},
} as unknown as OpenClawConfig;
setSnapshot(resolved, resolved);
await expect(
runConfigCommand([
"config",
@@ -306,7 +286,12 @@ describe("config cli", () => {
).rejects.toThrow("__exit__:1");
expect(mockWriteConfigFile).not.toHaveBeenCalled();
expect(mockError).toHaveBeenCalledWith(expect.stringContaining("Unrecognized key"));
expect(mockError).toHaveBeenCalledWith(
expect.stringContaining("openclaw plugins install <spec>"),
);
expect(mockError).toHaveBeenCalledWith(
expect.stringContaining("openclaw plugins update <plugin-id>"),
);
});
it("rejects protected model map replacement unless explicitly requested", async () => {

View File

@@ -75,6 +75,7 @@ type ConfigSetOperation = {
const GATEWAY_AUTH_MODE_PATH: PathSegment[] = ["gateway", "auth", "mode"];
const SECRET_PROVIDER_PATH_PREFIX: PathSegment[] = ["secrets", "providers"];
const PLUGIN_INSTALL_RECORD_PATH_PREFIX: PathSegment[] = ["plugins", "installs"];
const CONFIG_SET_EXAMPLE_VALUE = formatCliCommand(
"openclaw config set gateway.port 19001 --strict-json",
);
@@ -1088,6 +1089,21 @@ function selectDryRunRefsForResolution(params: { refs: SecretRef[]; allowExecInD
return { refsToResolve, skippedExecRefs };
}
function pathStartsWith(path: readonly PathSegment[], prefix: readonly PathSegment[]): boolean {
return prefix.every((segment, index) => path[index] === segment);
}
function formatPluginInstallConfigSetError(): string {
return [
"plugins.installs is managed by the plugin index and cannot be edited with config set.",
"",
"Use plugin commands instead:",
` ${formatCliCommand("openclaw plugins install <spec>")}`,
` ${formatCliCommand("openclaw plugins update <plugin-id>")}`,
` ${formatCliCommand("openclaw plugins uninstall <plugin-id>")}`,
].join("\n");
}
function collectDryRunSchemaErrors(params: {
config: OpenClawConfig;
operations: ReadonlyArray<ConfigSetOperation>;
@@ -1192,6 +1208,13 @@ export async function runConfigSet(opts: {
value: opts.value,
opts: opts.cliOptions,
});
if (
operations.some((operation) =>
pathStartsWith(operation.requestedPath, PLUGIN_INSTALL_RECORD_PATH_PREFIX),
)
) {
throw new Error(formatPluginInstallConfigSetError());
}
const snapshot = await loadValidConfig(runtime);
// Use snapshot.resolved (config after $include and ${ENV} resolution, but BEFORE runtime defaults)
// instead of snapshot.config (runtime-merged with defaults).