mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 08:00:42 +00:00
* fix(config): resolve CLI command aliases against parent plugin in plugins.allow (#64748) The CLI allow guard checked command names (e.g. 'wiki') directly against plugins.allow, missing the parent plugin ('memory-wiki'). Additionally, memory-wiki did not declare 'wiki' as a commandAlias, so doctor --fix would remove it as stale. - Add commandAliases entry for 'wiki' in memory-wiki plugin manifest - Check parent plugin ID in the CLI fallback allow guard - Add tests for both allow and deny cases * fix(cli): inject manifest registry for alias diagnostics * Update CHANGELOG.md --------- Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
189 lines
5.9 KiB
TypeScript
189 lines
5.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
rewriteUpdateFlagArgv,
|
|
resolveMissingPluginCommandMessage,
|
|
shouldEnsureCliPath,
|
|
shouldUseRootHelpFastPath,
|
|
} from "./run-main.js";
|
|
|
|
const memoryWikiCommandAliasRegistry = {
|
|
plugins: [
|
|
{
|
|
id: "memory-wiki",
|
|
commandAliases: [{ name: "wiki" }],
|
|
},
|
|
],
|
|
};
|
|
|
|
describe("rewriteUpdateFlagArgv", () => {
|
|
it("leaves argv unchanged when --update is absent", () => {
|
|
const argv = ["node", "entry.js", "status"];
|
|
expect(rewriteUpdateFlagArgv(argv)).toBe(argv);
|
|
});
|
|
|
|
it("rewrites --update into the update command", () => {
|
|
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--update"])).toEqual([
|
|
"node",
|
|
"entry.js",
|
|
"update",
|
|
]);
|
|
});
|
|
|
|
it("preserves global flags that appear before --update", () => {
|
|
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--profile", "p", "--update"])).toEqual([
|
|
"node",
|
|
"entry.js",
|
|
"--profile",
|
|
"p",
|
|
"update",
|
|
]);
|
|
});
|
|
|
|
it("keeps update options after the rewritten command", () => {
|
|
expect(rewriteUpdateFlagArgv(["node", "entry.js", "--update", "--json"])).toEqual([
|
|
"node",
|
|
"entry.js",
|
|
"update",
|
|
"--json",
|
|
]);
|
|
});
|
|
});
|
|
|
|
describe("shouldEnsureCliPath", () => {
|
|
it("skips path bootstrap for help/version invocations", () => {
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "--help"])).toBe(false);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "-V"])).toBe(false);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "-v"])).toBe(false);
|
|
});
|
|
|
|
it("skips path bootstrap for read-only fast paths", () => {
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "status"])).toBe(false);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "--log-level", "debug", "status"])).toBe(false);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "sessions", "--json"])).toBe(false);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "config", "get", "update"])).toBe(false);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "models", "status", "--json"])).toBe(false);
|
|
});
|
|
|
|
it("keeps path bootstrap for mutating or unknown commands", () => {
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "message", "send"])).toBe(true);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "voicecall", "status"])).toBe(true);
|
|
expect(shouldEnsureCliPath(["node", "openclaw", "acp", "-v"])).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe("shouldUseRootHelpFastPath", () => {
|
|
it("uses the fast path for root help only", () => {
|
|
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--help"])).toBe(true);
|
|
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--profile", "work", "-h"])).toBe(true);
|
|
expect(shouldUseRootHelpFastPath(["node", "openclaw", "status", "--help"])).toBe(false);
|
|
expect(shouldUseRootHelpFastPath(["node", "openclaw", "--help", "status"])).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("resolveMissingPluginCommandMessage", () => {
|
|
it("explains plugins.allow misses for a bundled plugin command", () => {
|
|
expect(
|
|
resolveMissingPluginCommandMessage("browser", {
|
|
plugins: {
|
|
allow: ["telegram"],
|
|
},
|
|
}),
|
|
).toContain('`plugins.allow` excludes "browser"');
|
|
});
|
|
|
|
it("explains explicit bundled plugin disablement", () => {
|
|
expect(
|
|
resolveMissingPluginCommandMessage("browser", {
|
|
plugins: {
|
|
entries: {
|
|
browser: {
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
}),
|
|
).toContain("plugins.entries.browser.enabled=false");
|
|
});
|
|
|
|
it("returns null when the bundled plugin command is already allowed", () => {
|
|
expect(
|
|
resolveMissingPluginCommandMessage("browser", {
|
|
plugins: {
|
|
allow: ["browser"],
|
|
},
|
|
}),
|
|
).toBeNull();
|
|
});
|
|
|
|
it("explains that dreaming is a runtime slash command, not a CLI command", () => {
|
|
const message = resolveMissingPluginCommandMessage("dreaming", {});
|
|
expect(message).toContain("runtime slash command");
|
|
expect(message).toContain("/dreaming");
|
|
expect(message).toContain("memory-core");
|
|
expect(message).toContain("openclaw memory");
|
|
});
|
|
|
|
it("returns the runtime command message even when plugins.allow is set", () => {
|
|
const message = resolveMissingPluginCommandMessage("dreaming", {
|
|
plugins: {
|
|
allow: ["memory-core"],
|
|
},
|
|
});
|
|
expect(message).toContain("runtime slash command");
|
|
expect(message).not.toContain("plugins.allow");
|
|
});
|
|
|
|
it("points command names in plugins.allow at their parent plugin", () => {
|
|
const message = resolveMissingPluginCommandMessage("dreaming", {
|
|
plugins: {
|
|
allow: ["dreaming"],
|
|
},
|
|
});
|
|
expect(message).toContain('"dreaming" is not a plugin');
|
|
expect(message).toContain('"memory-core"');
|
|
expect(message).toContain("plugins.allow");
|
|
});
|
|
|
|
it("explains parent plugin disablement for runtime command aliases", () => {
|
|
const message = resolveMissingPluginCommandMessage("dreaming", {
|
|
plugins: {
|
|
entries: {
|
|
"memory-core": {
|
|
enabled: false,
|
|
},
|
|
},
|
|
},
|
|
});
|
|
expect(message).toContain("plugins.entries.memory-core.enabled=false");
|
|
expect(message).not.toContain("runtime slash command");
|
|
});
|
|
|
|
it("allows CLI commands when their parent plugin is in plugins.allow", () => {
|
|
const message = resolveMissingPluginCommandMessage(
|
|
"wiki",
|
|
{
|
|
plugins: {
|
|
allow: ["memory-wiki"],
|
|
},
|
|
},
|
|
{ registry: memoryWikiCommandAliasRegistry },
|
|
);
|
|
expect(message).toBeNull();
|
|
});
|
|
|
|
it("blocks CLI commands when parent plugin is NOT in plugins.allow", () => {
|
|
const message = resolveMissingPluginCommandMessage(
|
|
"wiki",
|
|
{
|
|
plugins: {
|
|
allow: ["telegram"],
|
|
},
|
|
},
|
|
{ registry: memoryWikiCommandAliasRegistry },
|
|
);
|
|
expect(message).not.toBeNull();
|
|
expect(message).toContain('"memory-wiki"');
|
|
expect(message).toContain("plugins.allow");
|
|
});
|
|
});
|