mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 05:40:44 +00:00
fix: reject agent-scoped model default writes
This commit is contained in:
@@ -22,6 +22,7 @@ Docs: https://docs.openclaw.ai
|
||||
### Fixes
|
||||
|
||||
- Control UI/chat: keep live replies visible when a raw session alias such as `main` sends the chat turn but Gateway emits events under the canonical session key for the same run. Fixes #73716. Thanks @teebes.
|
||||
- CLI/models: reject `--agent` on `openclaw models set` and `set-image` instead of silently writing agent-scoped requests to global model defaults. Fixes #68391. Thanks @derrickabellard.
|
||||
- CLI: stop treating the legacy singular `openclaw tool ...` token as a plugin id under restrictive `plugins.allow`, so it falls through as a normal unknown/reserved command instead of suggesting a stale allowlist entry. Fixes #64732. Thanks @efe-arv, @SweetSophia, and @hashtag1974.
|
||||
- Media: write inbound media buffers through same-directory temp files before rename, so failed disk writes do not leave zero-byte artifacts for later voice transcription. Fixes #55966. Thanks @OpenCodeEngineer.
|
||||
- TTS/Telegram: keep trusted local audio generated by the TTS tool queued for voice-note delivery even when the run-level built-in tool list omits the raw `tts` name. Fixes #74752. Thanks @Loveworld3033 and @andyliu.
|
||||
|
||||
@@ -5,6 +5,8 @@ import { registerModelsCli } from "./models-cli.js";
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
modelsStatusCommand: vi.fn().mockResolvedValue(undefined),
|
||||
modelsSetCommand: vi.fn().mockResolvedValue(undefined),
|
||||
modelsSetImageCommand: vi.fn().mockResolvedValue(undefined),
|
||||
noopAsync: vi.fn(async () => undefined),
|
||||
modelsAuthAddCommand: vi.fn().mockResolvedValue(undefined),
|
||||
modelsAuthLoginCommand: vi.fn().mockResolvedValue(undefined),
|
||||
@@ -17,6 +19,8 @@ const {
|
||||
modelsAuthLoginCommand,
|
||||
modelsAuthPasteTokenCommand,
|
||||
modelsAuthSetupTokenCommand,
|
||||
modelsSetCommand,
|
||||
modelsSetImageCommand,
|
||||
modelsStatusCommand,
|
||||
} = mocks;
|
||||
|
||||
@@ -58,10 +62,10 @@ vi.mock("../commands/models/scan.js", () => ({
|
||||
modelsScanCommand: mocks.noopAsync,
|
||||
}));
|
||||
vi.mock("../commands/models/set.js", () => ({
|
||||
modelsSetCommand: mocks.noopAsync,
|
||||
modelsSetCommand: mocks.modelsSetCommand,
|
||||
}));
|
||||
vi.mock("../commands/models/set-image.js", () => ({
|
||||
modelsSetImageCommand: mocks.noopAsync,
|
||||
modelsSetImageCommand: mocks.modelsSetImageCommand,
|
||||
}));
|
||||
|
||||
describe("models cli", () => {
|
||||
@@ -70,6 +74,8 @@ describe("models cli", () => {
|
||||
modelsAuthLoginCommand.mockClear();
|
||||
modelsAuthPasteTokenCommand.mockClear();
|
||||
modelsAuthSetupTokenCommand.mockClear();
|
||||
modelsSetCommand.mockClear();
|
||||
modelsSetImageCommand.mockClear();
|
||||
modelsStatusCommand.mockClear();
|
||||
});
|
||||
|
||||
@@ -162,6 +168,23 @@ describe("models cli", () => {
|
||||
expect(command).toHaveBeenCalledWith(expect.objectContaining(expected), expect.any(Object));
|
||||
});
|
||||
|
||||
it.each([
|
||||
{
|
||||
label: "set",
|
||||
args: ["models", "--agent", "poe", "set", "anthropic/claude-sonnet-4-6"],
|
||||
command: modelsSetCommand,
|
||||
},
|
||||
{
|
||||
label: "set-image",
|
||||
args: ["models", "--agent", "poe", "set-image", "openai/gpt-image-1"],
|
||||
command: modelsSetImageCommand,
|
||||
},
|
||||
])("rejects parent --agent for models $label", async ({ args, command }) => {
|
||||
await expect(runModelsCommand(args)).rejects.toThrow("does not support `--agent`");
|
||||
|
||||
expect(command).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows help for models auth without error exit", async () => {
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
|
||||
@@ -8,6 +8,16 @@ function runModelsCommand(action: () => Promise<void>) {
|
||||
return runCommandWithRuntime(defaultRuntime, action);
|
||||
}
|
||||
|
||||
function rejectAgentScopedModelWrite(command: Command, commandName: "set" | "set-image"): void {
|
||||
const agent = resolveOptionFromCommand<string>(command, "agent");
|
||||
if (!agent) {
|
||||
return;
|
||||
}
|
||||
throw new Error(
|
||||
`\`openclaw models ${commandName}\` does not support \`--agent\`; it only updates global model defaults. Remove \`--agent\` or use agent config to set a per-agent model override.`,
|
||||
);
|
||||
}
|
||||
|
||||
export function registerModelsCli(program: Command) {
|
||||
const models = program
|
||||
.command("models")
|
||||
@@ -94,7 +104,8 @@ export function registerModelsCli(program: Command) {
|
||||
.command("set")
|
||||
.description("Set the default model")
|
||||
.argument("<model>", "Model id or alias")
|
||||
.action(async (model: string) => {
|
||||
.action(async (model: string, _opts: unknown, command: Command) => {
|
||||
rejectAgentScopedModelWrite(command, "set");
|
||||
await runModelsCommand(async () => {
|
||||
const { modelsSetCommand } = await import("../commands/models/set.js");
|
||||
await modelsSetCommand(model, defaultRuntime);
|
||||
@@ -105,7 +116,8 @@ export function registerModelsCli(program: Command) {
|
||||
.command("set-image")
|
||||
.description("Set the image model")
|
||||
.argument("<model>", "Model id or alias")
|
||||
.action(async (model: string) => {
|
||||
.action(async (model: string, _opts: unknown, command: Command) => {
|
||||
rejectAgentScopedModelWrite(command, "set-image");
|
||||
await runModelsCommand(async () => {
|
||||
const { modelsSetImageCommand } = await import("../commands/models/set-image.js");
|
||||
await modelsSetImageCommand(model, defaultRuntime);
|
||||
|
||||
Reference in New Issue
Block a user