mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 07:00:43 +00:00
fix: guide config users to plugin commands
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -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).
|
||||
|
||||
Reference in New Issue
Block a user