fix: local updates for PR #4780

Co-authored-by: jlowin <jlowin@users.noreply.github.com>
This commit is contained in:
Gustavo Madeira Santana
2026-01-30 15:39:05 -05:00
committed by Gustavo Madeira Santana
parent dd4715a2c4
commit f24e3cdae5
8 changed files with 250 additions and 57 deletions

View File

@@ -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;
}

View File

@@ -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);
}
});
});

View File

@@ -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,
);