mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
fix: local updates for PR #4780
Co-authored-by: jlowin <jlowin@users.noreply.github.com>
This commit is contained in:
committed by
Gustavo Madeira Santana
parent
dd4715a2c4
commit
f24e3cdae5
@@ -27,6 +27,9 @@ When provider usage snapshots are available, the OAuth/token status section incl
|
||||
provider usage headers.
|
||||
Add `--probe` to run live auth probes against each configured provider profile.
|
||||
Probes are real requests (may consume tokens and trigger rate limits).
|
||||
Use `--agent <id>` to inspect a configured agent’s model/auth state. When omitted,
|
||||
the command uses `OPENCLAW_AGENT_DIR`/`PI_CODING_AGENT_DIR` if set, otherwise the
|
||||
configured default agent.
|
||||
|
||||
Notes:
|
||||
- `models set <model-or-alias>` accepts `provider/model` or an alias.
|
||||
@@ -44,6 +47,7 @@ Options:
|
||||
- `--probe-timeout <ms>`
|
||||
- `--probe-concurrency <n>`
|
||||
- `--probe-max-tokens <n>`
|
||||
- `--agent <id>` (configured agent id; overrides `OPENCLAW_AGENT_DIR`/`PI_CODING_AGENT_DIR`)
|
||||
|
||||
## Aliases + fallbacks
|
||||
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import type { Command } from "commander";
|
||||
|
||||
export type ManagerLookupResult<T> = {
|
||||
manager: T | null;
|
||||
error?: string;
|
||||
@@ -46,3 +48,16 @@ export async function runCommandWithRuntime(
|
||||
runtime.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
export function resolveOptionFromCommand<T>(
|
||||
command: Command | undefined,
|
||||
key: string,
|
||||
): T | undefined {
|
||||
let current: Command | null | undefined = command;
|
||||
while (current) {
|
||||
const opts = (current.opts?.() ?? {}) as Record<string, T | undefined>;
|
||||
if (opts[key] !== undefined) return opts[key];
|
||||
current = current.parent ?? undefined;
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const githubCopilotLoginCommand = vi.fn();
|
||||
const modelsStatusCommand = vi.fn().mockResolvedValue(undefined);
|
||||
|
||||
vi.mock("../commands/models.js", async () => {
|
||||
const actual = (await vi.importActual<typeof import("../commands/models.js")>(
|
||||
@@ -10,10 +11,16 @@ vi.mock("../commands/models.js", async () => {
|
||||
return {
|
||||
...actual,
|
||||
githubCopilotLoginCommand,
|
||||
modelsStatusCommand,
|
||||
};
|
||||
});
|
||||
|
||||
describe("models cli", () => {
|
||||
beforeEach(() => {
|
||||
githubCopilotLoginCommand.mockClear();
|
||||
modelsStatusCommand.mockClear();
|
||||
});
|
||||
|
||||
it("registers github-copilot login command", { timeout: 60_000 }, async () => {
|
||||
const { Command } = await import("commander");
|
||||
const { registerModelsCli } = await import("./models-cli.js");
|
||||
@@ -40,4 +47,51 @@ describe("models cli", () => {
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes --agent to models status", async () => {
|
||||
const { Command } = await import("commander");
|
||||
const { registerModelsCli } = await import("./models-cli.js");
|
||||
|
||||
const program = new Command();
|
||||
registerModelsCli(program);
|
||||
|
||||
await program.parseAsync(["models", "status", "--agent", "poe"], { from: "user" });
|
||||
|
||||
expect(modelsStatusCommand).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ agent: "poe" }),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("passes parent --agent to models status", async () => {
|
||||
const { Command } = await import("commander");
|
||||
const { registerModelsCli } = await import("./models-cli.js");
|
||||
|
||||
const program = new Command();
|
||||
registerModelsCli(program);
|
||||
|
||||
await program.parseAsync(["models", "--agent", "poe", "status"], { from: "user" });
|
||||
|
||||
expect(modelsStatusCommand).toHaveBeenCalledWith(
|
||||
expect.objectContaining({ agent: "poe" }),
|
||||
expect.any(Object),
|
||||
);
|
||||
});
|
||||
|
||||
it("shows help for models auth without error exit", async () => {
|
||||
const { Command } = await import("commander");
|
||||
const { registerModelsCli } = await import("./models-cli.js");
|
||||
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerModelsCli(program);
|
||||
|
||||
try {
|
||||
await program.parseAsync(["models", "auth"], { from: "user" });
|
||||
expect.fail("expected help to exit");
|
||||
} catch (err) {
|
||||
const error = err as { exitCode?: number };
|
||||
expect(error.exitCode).toBe(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -29,7 +29,7 @@ import {
|
||||
import { defaultRuntime } from "../runtime.js";
|
||||
import { formatDocsLink } from "../terminal/links.js";
|
||||
import { theme } from "../terminal/theme.js";
|
||||
import { runCommandWithRuntime } from "./cli-utils.js";
|
||||
import { resolveOptionFromCommand, runCommandWithRuntime } from "./cli-utils.js";
|
||||
|
||||
function runModelsCommand(action: () => Promise<void>) {
|
||||
return runCommandWithRuntime(defaultRuntime, action);
|
||||
@@ -41,7 +41,10 @@ export function registerModelsCli(program: Command) {
|
||||
.description("Model discovery, scanning, and configuration")
|
||||
.option("--status-json", "Output JSON (alias for `models status --json`)", false)
|
||||
.option("--status-plain", "Plain output (alias for `models status --plain`)", false)
|
||||
.option("--agent <id>", "Agent id (default: configured default agent)")
|
||||
.option(
|
||||
"--agent <id>",
|
||||
"Agent id to inspect (overrides OPENCLAW_AGENT_DIR/PI_CODING_AGENT_DIR)",
|
||||
)
|
||||
.addHelpText(
|
||||
"after",
|
||||
() =>
|
||||
@@ -86,8 +89,13 @@ export function registerModelsCli(program: Command) {
|
||||
.option("--probe-timeout <ms>", "Per-probe timeout in ms")
|
||||
.option("--probe-concurrency <n>", "Concurrent probes")
|
||||
.option("--probe-max-tokens <n>", "Probe max tokens (best-effort)")
|
||||
.option("--agent <id>", "Agent id (default: configured default agent)")
|
||||
.action(async (opts) => {
|
||||
.option(
|
||||
"--agent <id>",
|
||||
"Agent id to inspect (overrides OPENCLAW_AGENT_DIR/PI_CODING_AGENT_DIR)",
|
||||
)
|
||||
.action(async (opts, command) => {
|
||||
const agent =
|
||||
resolveOptionFromCommand<string>(command, "agent") ?? (opts.agent as string | undefined);
|
||||
await runModelsCommand(async () => {
|
||||
await modelsStatusCommand(
|
||||
{
|
||||
@@ -100,7 +108,7 @@ export function registerModelsCli(program: Command) {
|
||||
probeTimeout: opts.probeTimeout as string | undefined,
|
||||
probeConcurrency: opts.probeConcurrency as string | undefined,
|
||||
probeMaxTokens: opts.probeMaxTokens as string | undefined,
|
||||
agent: opts.agent as string | undefined,
|
||||
agent,
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
@@ -282,6 +290,10 @@ export function registerModelsCli(program: Command) {
|
||||
});
|
||||
|
||||
const auth = models.command("auth").description("Manage model auth profiles");
|
||||
auth.option("--agent <id>", "Agent id for auth order get/set/clear");
|
||||
auth.action(() => {
|
||||
auth.help();
|
||||
});
|
||||
|
||||
auth
|
||||
.command("add")
|
||||
@@ -375,12 +387,14 @@ export function registerModelsCli(program: Command) {
|
||||
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
|
||||
.option("--agent <id>", "Agent id (default: configured default agent)")
|
||||
.option("--json", "Output JSON", false)
|
||||
.action(async (opts) => {
|
||||
.action(async (opts, command) => {
|
||||
const agent =
|
||||
resolveOptionFromCommand<string>(command, "agent") ?? (opts.agent as string | undefined);
|
||||
await runModelsCommand(async () => {
|
||||
await modelsAuthOrderGetCommand(
|
||||
{
|
||||
provider: opts.provider as string,
|
||||
agent: opts.agent as string | undefined,
|
||||
agent,
|
||||
json: Boolean(opts.json),
|
||||
},
|
||||
defaultRuntime,
|
||||
@@ -394,12 +408,14 @@ export function registerModelsCli(program: Command) {
|
||||
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
|
||||
.option("--agent <id>", "Agent id (default: configured default agent)")
|
||||
.argument("<profileIds...>", "Auth profile ids (e.g. anthropic:default)")
|
||||
.action(async (profileIds: string[], opts) => {
|
||||
.action(async (profileIds: string[], opts, command) => {
|
||||
const agent =
|
||||
resolveOptionFromCommand<string>(command, "agent") ?? (opts.agent as string | undefined);
|
||||
await runModelsCommand(async () => {
|
||||
await modelsAuthOrderSetCommand(
|
||||
{
|
||||
provider: opts.provider as string,
|
||||
agent: opts.agent as string | undefined,
|
||||
agent,
|
||||
order: profileIds,
|
||||
},
|
||||
defaultRuntime,
|
||||
@@ -412,12 +428,14 @@ export function registerModelsCli(program: Command) {
|
||||
.description("Clear per-agent auth order override (fall back to config/round-robin)")
|
||||
.requiredOption("--provider <name>", "Provider id (e.g. anthropic)")
|
||||
.option("--agent <id>", "Agent id (default: configured default agent)")
|
||||
.action(async (opts) => {
|
||||
.action(async (opts, command) => {
|
||||
const agent =
|
||||
resolveOptionFromCommand<string>(command, "agent") ?? (opts.agent as string | undefined);
|
||||
await runModelsCommand(async () => {
|
||||
await modelsAuthOrderClearCommand(
|
||||
{
|
||||
provider: opts.provider as string,
|
||||
agent: opts.agent as string | undefined,
|
||||
agent,
|
||||
},
|
||||
defaultRuntime,
|
||||
);
|
||||
|
||||
@@ -6,9 +6,9 @@ import {
|
||||
} from "../../agents/auth-profiles.js";
|
||||
import { normalizeProviderId } from "../../agents/model-selection.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { normalizeAgentId } from "../../routing/session-key.js";
|
||||
import type { RuntimeEnv } from "../../runtime.js";
|
||||
import { shortenHomePath } from "../../utils.js";
|
||||
import { resolveKnownAgentId } from "./shared.js";
|
||||
|
||||
function resolveTargetAgent(
|
||||
cfg: ReturnType<typeof loadConfig>,
|
||||
@@ -17,7 +17,7 @@ function resolveTargetAgent(
|
||||
agentId: string;
|
||||
agentDir: string;
|
||||
} {
|
||||
const agentId = raw?.trim() ? normalizeAgentId(raw.trim()) : resolveDefaultAgentId(cfg);
|
||||
const agentId = resolveKnownAgentId({ cfg, rawAgentId: raw }) ?? resolveDefaultAgentId(cfg);
|
||||
const agentDir = resolveAgentDir(cfg, agentId);
|
||||
return { agentId, agentDir };
|
||||
}
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import path from "node:path";
|
||||
import { resolveAgentDir, resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { resolveOpenClawAgentDir } from "../../agents/agent-paths.js";
|
||||
import {
|
||||
resolveAgentDir,
|
||||
resolveAgentModelFallbacksOverride,
|
||||
resolveAgentModelPrimary,
|
||||
} from "../../agents/agent-scope.js";
|
||||
import {
|
||||
buildAuthHealthSummary,
|
||||
DEFAULT_OAUTH_WARN_MS,
|
||||
@@ -15,6 +20,7 @@ import {
|
||||
buildModelAliasIndex,
|
||||
parseModelRef,
|
||||
resolveConfiguredModelRef,
|
||||
resolveDefaultModelForAgent,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { CONFIG_PATH, loadConfig } from "../../config/config.js";
|
||||
@@ -40,8 +46,12 @@ import {
|
||||
sortProbeResults,
|
||||
type AuthProbeSummary,
|
||||
} from "./list.probe.js";
|
||||
import { normalizeAgentId } from "../../routing/session-key.js";
|
||||
import { DEFAULT_MODEL, DEFAULT_PROVIDER, ensureFlagCompatibility } from "./shared.js";
|
||||
import {
|
||||
DEFAULT_MODEL,
|
||||
DEFAULT_PROVIDER,
|
||||
ensureFlagCompatibility,
|
||||
resolveKnownAgentId,
|
||||
} from "./shared.js";
|
||||
|
||||
export async function modelsStatusCommand(
|
||||
opts: {
|
||||
@@ -63,11 +73,19 @@ export async function modelsStatusCommand(
|
||||
throw new Error("--probe cannot be used with --plain output.");
|
||||
}
|
||||
const cfg = loadConfig();
|
||||
const resolved = resolveConfiguredModelRef({
|
||||
cfg,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
defaultModel: DEFAULT_MODEL,
|
||||
});
|
||||
const agentId = resolveKnownAgentId({ cfg, rawAgentId: opts.agent });
|
||||
const agentDir = agentId ? resolveAgentDir(cfg, agentId) : resolveOpenClawAgentDir();
|
||||
const agentModelPrimary = agentId ? resolveAgentModelPrimary(cfg, agentId) : undefined;
|
||||
const agentFallbacksOverride = agentId
|
||||
? resolveAgentModelFallbacksOverride(cfg, agentId)
|
||||
: undefined;
|
||||
const resolved = agentId
|
||||
? resolveDefaultModelForAgent({ cfg, agentId })
|
||||
: resolveConfiguredModelRef({
|
||||
cfg,
|
||||
defaultProvider: DEFAULT_PROVIDER,
|
||||
defaultModel: DEFAULT_MODEL,
|
||||
});
|
||||
|
||||
const modelConfig = cfg.agents?.defaults?.model as
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
@@ -77,11 +95,13 @@ export async function modelsStatusCommand(
|
||||
| { primary?: string; fallbacks?: string[] }
|
||||
| string
|
||||
| undefined;
|
||||
const rawModel =
|
||||
const rawDefaultsModel =
|
||||
typeof modelConfig === "string" ? modelConfig.trim() : (modelConfig?.primary?.trim() ?? "");
|
||||
const rawModel = agentModelPrimary ?? rawDefaultsModel;
|
||||
const resolvedLabel = `${resolved.provider}/${resolved.model}`;
|
||||
const defaultLabel = rawModel || resolvedLabel;
|
||||
const fallbacks = typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const defaultsFallbacks = typeof modelConfig === "object" ? (modelConfig?.fallbacks ?? []) : [];
|
||||
const fallbacks = agentFallbacksOverride ?? defaultsFallbacks;
|
||||
const imageModel =
|
||||
typeof imageConfig === "string" ? imageConfig.trim() : (imageConfig?.primary?.trim() ?? "");
|
||||
const imageFallbacks = typeof imageConfig === "object" ? (imageConfig?.fallbacks ?? []) : [];
|
||||
@@ -95,10 +115,6 @@ export async function modelsStatusCommand(
|
||||
);
|
||||
const allowed = Object.keys(cfg.agents?.defaults?.models ?? {});
|
||||
|
||||
const agentId = opts.agent?.trim()
|
||||
? normalizeAgentId(opts.agent.trim())
|
||||
: resolveDefaultAgentId(cfg);
|
||||
const agentDir = resolveAgentDir(cfg, agentId);
|
||||
const store = ensureAuthProfileStore(agentDir);
|
||||
const modelsPath = path.join(agentDir, "models.json");
|
||||
|
||||
@@ -300,12 +316,21 @@ export async function modelsStatusCommand(
|
||||
JSON.stringify(
|
||||
{
|
||||
configPath: CONFIG_PATH,
|
||||
...(agentId ? { agentId } : {}),
|
||||
agentDir,
|
||||
defaultModel: defaultLabel,
|
||||
resolvedDefault: resolvedLabel,
|
||||
fallbacks,
|
||||
imageModel: imageModel || null,
|
||||
imageFallbacks,
|
||||
...(agentId
|
||||
? {
|
||||
modelConfig: {
|
||||
defaultSource: agentModelPrimary ? "agent" : "defaults",
|
||||
fallbacksSource: agentFallbacksOverride !== undefined ? "agent" : "defaults",
|
||||
},
|
||||
}
|
||||
: {}),
|
||||
aliases,
|
||||
allowed,
|
||||
auth: {
|
||||
@@ -341,7 +366,10 @@ export async function modelsStatusCommand(
|
||||
}
|
||||
|
||||
const rich = isRich(opts);
|
||||
type ModelConfigSource = "agent" | "defaults";
|
||||
const label = (value: string) => colorize(rich, theme.accent, value.padEnd(14));
|
||||
const labelWithSource = (value: string, source?: ModelConfigSource) =>
|
||||
label(source ? `${value} (${source})` : value);
|
||||
const displayDefault =
|
||||
rawModel && rawModel !== resolvedLabel ? `${resolvedLabel} (from ${rawModel})` : resolvedLabel;
|
||||
|
||||
@@ -356,32 +384,34 @@ export async function modelsStatusCommand(
|
||||
)}`,
|
||||
);
|
||||
runtime.log(
|
||||
`${label("Default")}${colorize(rich, theme.muted, ":")} ${colorize(
|
||||
`${labelWithSource("Default", agentId ? (agentModelPrimary ? "agent" : "defaults") : undefined)}${colorize(
|
||||
rich,
|
||||
theme.success,
|
||||
displayDefault,
|
||||
)}`,
|
||||
theme.muted,
|
||||
":",
|
||||
)} ${colorize(rich, theme.success, displayDefault)}`,
|
||||
);
|
||||
runtime.log(
|
||||
`${label(`Fallbacks (${fallbacks.length || 0})`)}${colorize(rich, theme.muted, ":")} ${colorize(
|
||||
`${labelWithSource(
|
||||
`Fallbacks (${fallbacks.length || 0})`,
|
||||
agentId ? (agentFallbacksOverride !== undefined ? "agent" : "defaults") : undefined,
|
||||
)}${colorize(rich, theme.muted, ":")} ${colorize(
|
||||
rich,
|
||||
fallbacks.length ? theme.warn : theme.muted,
|
||||
fallbacks.length ? fallbacks.join(", ") : "-",
|
||||
)}`,
|
||||
);
|
||||
runtime.log(
|
||||
`${label("Image model")}${colorize(rich, theme.muted, ":")} ${colorize(
|
||||
rich,
|
||||
imageModel ? theme.accentBright : theme.muted,
|
||||
imageModel || "-",
|
||||
)}`,
|
||||
);
|
||||
runtime.log(
|
||||
`${label(`Image fallbacks (${imageFallbacks.length || 0})`)}${colorize(
|
||||
`${labelWithSource("Image model", agentId ? "defaults" : undefined)}${colorize(
|
||||
rich,
|
||||
theme.muted,
|
||||
":",
|
||||
)} ${colorize(
|
||||
)} ${colorize(rich, imageModel ? theme.accentBright : theme.muted, imageModel || "-")}`,
|
||||
);
|
||||
runtime.log(
|
||||
`${labelWithSource(
|
||||
`Image fallbacks (${imageFallbacks.length || 0})`,
|
||||
agentId ? "defaults" : undefined,
|
||||
)}${colorize(rich, theme.muted, ":")} ${colorize(
|
||||
rich,
|
||||
imageFallbacks.length ? theme.accentBright : theme.muted,
|
||||
imageFallbacks.length ? imageFallbacks.join(", ") : "-",
|
||||
|
||||
@@ -29,8 +29,11 @@ const mocks = vi.hoisted(() => {
|
||||
|
||||
return {
|
||||
store,
|
||||
resolveOpenClawAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"),
|
||||
resolveAgentDir: vi.fn().mockReturnValue("/tmp/openclaw-agent"),
|
||||
resolveDefaultAgentId: vi.fn().mockReturnValue("main"),
|
||||
resolveAgentModelPrimary: vi.fn().mockReturnValue(undefined),
|
||||
resolveAgentModelFallbacksOverride: vi.fn().mockReturnValue(undefined),
|
||||
listAgentIds: vi.fn().mockReturnValue(["main", "jeremiah"]),
|
||||
ensureAuthProfileStore: vi.fn().mockReturnValue(store),
|
||||
listProfilesForProvider: vi.fn((s: typeof store, provider: string) => {
|
||||
return Object.entries(s.profiles)
|
||||
@@ -72,18 +75,16 @@ const mocks = vi.hoisted(() => {
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveAgentDir: mocks.resolveAgentDir,
|
||||
resolveDefaultAgentId: mocks.resolveDefaultAgentId,
|
||||
vi.mock("../../agents/agent-paths.js", () => ({
|
||||
resolveOpenClawAgentDir: mocks.resolveOpenClawAgentDir,
|
||||
}));
|
||||
|
||||
vi.mock("../../routing/session-key.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../routing/session-key.js")>();
|
||||
return {
|
||||
...actual,
|
||||
normalizeAgentId: (id: string) => id.toLowerCase().replace(/\s+/g, "-"),
|
||||
};
|
||||
});
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveAgentDir: mocks.resolveAgentDir,
|
||||
resolveAgentModelPrimary: mocks.resolveAgentModelPrimary,
|
||||
resolveAgentModelFallbacksOverride: mocks.resolveAgentModelFallbacksOverride,
|
||||
listAgentIds: mocks.listAgentIds,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/auth-profiles.js", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("../../agents/auth-profiles.js")>();
|
||||
@@ -127,6 +128,7 @@ describe("modelsStatusCommand auth overview", () => {
|
||||
await modelsStatusCommand({ json: true }, runtime as never);
|
||||
const payload = JSON.parse(String((runtime.log as vi.Mock).mock.calls[0][0]));
|
||||
|
||||
expect(mocks.resolveOpenClawAgentDir).toHaveBeenCalled();
|
||||
expect(payload.defaultModel).toBe("anthropic/claude-opus-4-5");
|
||||
expect(payload.auth.storePath).toBe("/tmp/openclaw-agent/auth-profiles.json");
|
||||
expect(payload.auth.shellEnvFallback.enabled).toBe(true);
|
||||
@@ -157,23 +159,74 @@ describe("modelsStatusCommand auth overview", () => {
|
||||
).toBe(true);
|
||||
});
|
||||
|
||||
it("resolves agent dir from --agent flag", async () => {
|
||||
mocks.resolveAgentDir.mockReturnValue("/tmp/openclaw-agent-custom");
|
||||
it("uses agent overrides and reports sources", async () => {
|
||||
const localRuntime = {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
};
|
||||
const originalPrimary = mocks.resolveAgentModelPrimary.getMockImplementation();
|
||||
const originalFallbacks = mocks.resolveAgentModelFallbacksOverride.getMockImplementation();
|
||||
const originalAgentDir = mocks.resolveAgentDir.getMockImplementation();
|
||||
|
||||
mocks.resolveAgentModelPrimary.mockReturnValue("openai/gpt-4");
|
||||
mocks.resolveAgentModelFallbacksOverride.mockReturnValue(["openai/gpt-3.5"]);
|
||||
mocks.resolveAgentDir.mockReturnValue("/tmp/openclaw-agent-custom");
|
||||
|
||||
try {
|
||||
await modelsStatusCommand({ json: true, agent: "jeremiah" }, localRuntime as never);
|
||||
await modelsStatusCommand({ json: true, agent: "Jeremiah" }, localRuntime as never);
|
||||
expect(mocks.resolveAgentDir).toHaveBeenCalledWith(expect.anything(), "jeremiah");
|
||||
const payload = JSON.parse(String((localRuntime.log as vi.Mock).mock.calls[0][0]));
|
||||
expect(payload.agentId).toBe("jeremiah");
|
||||
expect(payload.agentDir).toBe("/tmp/openclaw-agent-custom");
|
||||
expect(payload.defaultModel).toBe("openai/gpt-4");
|
||||
expect(payload.fallbacks).toEqual(["openai/gpt-3.5"]);
|
||||
expect(payload.modelConfig).toEqual({
|
||||
defaultSource: "agent",
|
||||
fallbacksSource: "agent",
|
||||
});
|
||||
} finally {
|
||||
mocks.resolveAgentDir.mockReturnValue("/tmp/openclaw-agent");
|
||||
mocks.resolveAgentModelPrimary.mockImplementation(originalPrimary);
|
||||
mocks.resolveAgentModelFallbacksOverride.mockImplementation(originalFallbacks);
|
||||
mocks.resolveAgentDir.mockImplementation(originalAgentDir);
|
||||
}
|
||||
});
|
||||
|
||||
it("labels defaults when --agent has no overrides", async () => {
|
||||
const localRuntime = {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
};
|
||||
const originalPrimary = mocks.resolveAgentModelPrimary.getMockImplementation();
|
||||
const originalFallbacks = mocks.resolveAgentModelFallbacksOverride.getMockImplementation();
|
||||
|
||||
mocks.resolveAgentModelPrimary.mockReturnValue(undefined);
|
||||
mocks.resolveAgentModelFallbacksOverride.mockReturnValue(undefined);
|
||||
|
||||
try {
|
||||
await modelsStatusCommand({ agent: "main" }, localRuntime as never);
|
||||
const output = (localRuntime.log as vi.Mock).mock.calls
|
||||
.map((call) => String(call[0]))
|
||||
.join("\n");
|
||||
expect(output).toContain("Default (defaults)");
|
||||
expect(output).toContain("Fallbacks (0) (defaults)");
|
||||
} finally {
|
||||
mocks.resolveAgentModelPrimary.mockImplementation(originalPrimary);
|
||||
mocks.resolveAgentModelFallbacksOverride.mockImplementation(originalFallbacks);
|
||||
}
|
||||
});
|
||||
|
||||
it("throws when agent id is unknown", async () => {
|
||||
const localRuntime = {
|
||||
log: vi.fn(),
|
||||
error: vi.fn(),
|
||||
exit: vi.fn(),
|
||||
};
|
||||
await expect(modelsStatusCommand({ agent: "unknown" }, localRuntime as never)).rejects.toThrow(
|
||||
'Unknown agent id "unknown".',
|
||||
);
|
||||
});
|
||||
it("exits non-zero when auth is missing", async () => {
|
||||
const originalProfiles = { ...mocks.store.profiles };
|
||||
mocks.store.profiles = {};
|
||||
|
||||
@@ -5,11 +5,14 @@ import {
|
||||
parseModelRef,
|
||||
resolveModelRefFromString,
|
||||
} from "../../agents/model-selection.js";
|
||||
import { listAgentIds } from "../../agents/agent-scope.js";
|
||||
import { formatCliCommand } from "../../cli/command-format.js";
|
||||
import {
|
||||
type OpenClawConfig,
|
||||
readConfigFileSnapshot,
|
||||
writeConfigFile,
|
||||
} from "../../config/config.js";
|
||||
import { normalizeAgentId } from "../../routing/session-key.js";
|
||||
|
||||
export const ensureFlagCompatibility = (opts: { json?: boolean; plain?: boolean }) => {
|
||||
if (opts.json && opts.plain) {
|
||||
@@ -82,6 +85,22 @@ export function normalizeAlias(alias: string): string {
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
export function resolveKnownAgentId(params: {
|
||||
cfg: OpenClawConfig;
|
||||
rawAgentId?: string | null;
|
||||
}): string | undefined {
|
||||
const raw = params.rawAgentId?.trim();
|
||||
if (!raw) return undefined;
|
||||
const agentId = normalizeAgentId(raw);
|
||||
const knownAgents = listAgentIds(params.cfg);
|
||||
if (!knownAgents.includes(agentId)) {
|
||||
throw new Error(
|
||||
`Unknown agent id "${raw}". Use "${formatCliCommand("openclaw agents list")}" to see configured agents.`,
|
||||
);
|
||||
}
|
||||
return agentId;
|
||||
}
|
||||
|
||||
export { modelKey };
|
||||
export { DEFAULT_MODEL, DEFAULT_PROVIDER };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user