From dc9ce2a1bf1cd5b6c3eae1ef8a18be6d71078b81 Mon Sep 17 00:00:00 2001 From: Neerav Makwana Date: Sun, 26 Apr 2026 00:30:47 -0400 Subject: [PATCH] fix: honor agent for models auth writes (#71933) Honor the parent `models auth --agent ` flag across auth write commands: `add`, `login`, `setup-token`, `paste-token`, and `login-github-copilot`. The auth helpers now resolve the requested configured agent before choosing the auth-profile store and provider workspace, while preserving default-agent behavior when `--agent` is omitted. Validation: - `pnpm test src/cli/models-cli.test.ts src/commands/models/auth.test.ts` - `pnpm test src/commands/models/auth.test.ts` - `pnpm docs:check-mdx` - `pnpm check:changed` - `pnpm check` - `pnpm build` - `pnpm test src/cli/run-main.test.ts` Full `pnpm test` was also run; it failed in unrelated `src/cli/run-main.test.ts` assertions during the full-suite order, while the exact file passes on both latest main and this branch. The PR diff only touches models auth CLI/auth files, docs, and changelog. Fixes #71864. Thanks @neeravmakwana. --- CHANGELOG.md | 1 + docs/cli/models.md | 3 + src/cli/models-cli.test.ts | 65 ++++++++- src/cli/models-cli.ts | 25 +++- src/commands/models/auth.test.ts | 229 +++++++++++++++++++++++++++++-- src/commands/models/auth.ts | 36 +++-- 6 files changed, 323 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e4335f57510..a7bdb43ebea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -80,6 +80,7 @@ Docs: https://docs.openclaw.ai - Plugins/registry: resolve web provider ownership from the installed plugin index instead of broad manifest scans on secret, tool, and pricing paths. Thanks @shakkernerd. - Config/providers: accept `video` and `audio` in configured model `input` values and preserve them in provider catalog entries. Fixes #20721. Thanks @alvinttang. +- Models/auth: honor the parent `--agent` flag for auth write commands (`add`, `login`, `setup-token`, `paste-token`, and the GitHub Copilot shortcut) so OAuth/API-key/token results are written to the requested agent store instead of the default agent. Fixes #71864. (#71933) Thanks @balric-seo. - TTS: strip model-emitted TTS directives from streamed block text before channel delivery, including directives split across adjacent blocks, while preserving the accumulated raw reply for final-mode synthesis. Fixes #38937. diff --git a/docs/cli/models.md b/docs/cli/models.md index 093a0c79a29..88bfd511695 100644 --- a/docs/cli/models.md +++ b/docs/cli/models.md @@ -154,6 +154,9 @@ provider you choose. `models auth login` runs a provider plugin’s auth flow (OAuth/API key). Use `openclaw plugins list` to see which providers are installed. +Use `openclaw models auth --agent ` to write auth results to a +specific configured agent store. The parent `--agent` flag is honored by +`add`, `login`, `setup-token`, `paste-token`, and `login-github-copilot`. Examples: diff --git a/src/cli/models-cli.test.ts b/src/cli/models-cli.test.ts index cae77e98557..30f3f2fc03f 100644 --- a/src/cli/models-cli.test.ts +++ b/src/cli/models-cli.test.ts @@ -6,10 +6,19 @@ import { registerModelsCli } from "./models-cli.js"; const mocks = vi.hoisted(() => ({ modelsStatusCommand: vi.fn().mockResolvedValue(undefined), noopAsync: vi.fn(async () => undefined), + modelsAuthAddCommand: vi.fn().mockResolvedValue(undefined), modelsAuthLoginCommand: vi.fn().mockResolvedValue(undefined), + modelsAuthPasteTokenCommand: vi.fn().mockResolvedValue(undefined), + modelsAuthSetupTokenCommand: vi.fn().mockResolvedValue(undefined), })); -const { modelsStatusCommand, modelsAuthLoginCommand } = mocks; +const { + modelsAuthAddCommand, + modelsAuthLoginCommand, + modelsAuthPasteTokenCommand, + modelsAuthSetupTokenCommand, + modelsStatusCommand, +} = mocks; vi.mock("../commands/models.js", () => ({ modelsStatusCommand: mocks.modelsStatusCommand, @@ -41,10 +50,10 @@ vi.mock("../commands/models/list.js", () => ({ modelsStatusCommand: mocks.modelsStatusCommand, })); vi.mock("../commands/models/auth.js", () => ({ - modelsAuthAddCommand: mocks.noopAsync, + modelsAuthAddCommand: mocks.modelsAuthAddCommand, modelsAuthLoginCommand: mocks.modelsAuthLoginCommand, - modelsAuthPasteTokenCommand: mocks.noopAsync, - modelsAuthSetupTokenCommand: mocks.noopAsync, + modelsAuthPasteTokenCommand: mocks.modelsAuthPasteTokenCommand, + modelsAuthSetupTokenCommand: mocks.modelsAuthSetupTokenCommand, })); vi.mock("../commands/models/auth-order.js", () => ({ modelsAuthOrderClearCommand: mocks.noopAsync, @@ -80,7 +89,10 @@ vi.mock("../commands/models/set-image.js", () => ({ describe("models cli", () => { beforeEach(() => { + modelsAuthAddCommand.mockClear(); modelsAuthLoginCommand.mockClear(); + modelsAuthPasteTokenCommand.mockClear(); + modelsAuthSetupTokenCommand.mockClear(); modelsStatusCommand.mockClear(); }); @@ -108,9 +120,10 @@ describe("models cli", () => { const login = auth?.commands.find((cmd) => cmd.name() === "login-github-copilot"); expect(login).toBeTruthy(); - await program.parseAsync(["models", "auth", "login-github-copilot", "--yes"], { - from: "user", - }); + await program.parseAsync( + ["models", "auth", "--agent", "poe", "login-github-copilot", "--yes"], + { from: "user" }, + ); expect(modelsAuthLoginCommand).toHaveBeenCalledTimes(1); expect(modelsAuthLoginCommand).toHaveBeenCalledWith( @@ -118,6 +131,7 @@ describe("models cli", () => { provider: "github-copilot", method: "device", yes: true, + agent: "poe", }), expect.any(Object), ); @@ -134,6 +148,43 @@ describe("models cli", () => { ); }); + it.each([ + { + label: "add", + args: ["models", "auth", "--agent", "poe", "add"], + command: modelsAuthAddCommand, + expected: { agent: "poe" }, + }, + { + label: "login", + args: ["models", "auth", "--agent", "poe", "login", "--provider", "openai-codex"], + command: modelsAuthLoginCommand, + expected: { agent: "poe", provider: "openai-codex" }, + }, + { + label: "setup-token", + args: ["models", "auth", "--agent", "poe", "setup-token", "--provider", "anthropic"], + command: modelsAuthSetupTokenCommand, + expected: { agent: "poe", provider: "anthropic" }, + }, + { + label: "paste-token", + args: ["models", "auth", "--agent", "poe", "paste-token", "--provider", "anthropic"], + command: modelsAuthPasteTokenCommand, + expected: { agent: "poe", provider: "anthropic" }, + }, + { + label: "login-github-copilot", + args: ["models", "auth", "--agent", "poe", "login-github-copilot", "--yes"], + command: modelsAuthLoginCommand, + expected: { agent: "poe", provider: "github-copilot", method: "device", yes: true }, + }, + ])("passes parent --agent to models auth $label", async ({ args, command, expected }) => { + await runModelsCommand(args); + + expect(command).toHaveBeenCalledWith(expect.objectContaining(expected), expect.any(Object)); + }); + it("shows help for models auth without error exit", async () => { const program = new Command(); program.exitOverride(); diff --git a/src/cli/models-cli.ts b/src/cli/models-cli.ts index 18ff7be01a4..0773a55bb54 100644 --- a/src/cli/models-cli.ts +++ b/src/cli/models-cli.ts @@ -282,7 +282,7 @@ export function registerModelsCli(program: Command) { }); const auth = models.command("auth").description("Manage model auth profiles"); - auth.option("--agent ", "Agent id for auth order get/set/clear"); + auth.option("--agent ", "Agent id for auth commands"); auth.action(() => { auth.help(); }); @@ -290,10 +290,13 @@ export function registerModelsCli(program: Command) { auth .command("add") .description("Interactive auth helper (provider auth or paste token)") - .action(async () => { + .action(async (command) => { + const agent = + resolveOptionFromCommand(command, "agent") ?? + resolveOptionFromCommand(auth, "agent"); await runModelsCommand(async () => { const { modelsAuthAddCommand } = await import("../commands/models/auth.js"); - await modelsAuthAddCommand({}, defaultRuntime); + await modelsAuthAddCommand({ agent }, defaultRuntime); }); }); @@ -303,7 +306,8 @@ export function registerModelsCli(program: Command) { .option("--provider ", "Provider id registered by a plugin") .option("--method ", "Provider auth method id") .option("--set-default", "Apply the provider's default model recommendation", false) - .action(async (opts) => { + .action(async (opts, command) => { + const agent = resolveOptionFromCommand(command, "agent"); await runModelsCommand(async () => { const { modelsAuthLoginCommand } = await import("../commands/models/auth.js"); await modelsAuthLoginCommand( @@ -311,6 +315,7 @@ export function registerModelsCli(program: Command) { provider: opts.provider as string | undefined, method: opts.method as string | undefined, setDefault: Boolean(opts.setDefault), + agent, }, defaultRuntime, ); @@ -322,13 +327,15 @@ export function registerModelsCli(program: Command) { .description("Run a provider CLI to create/sync a token (TTY required)") .option("--provider ", "Provider id") .option("--yes", "Skip confirmation", false) - .action(async (opts) => { + .action(async (opts, command) => { + const agent = resolveOptionFromCommand(command, "agent"); await runModelsCommand(async () => { const { modelsAuthSetupTokenCommand } = await import("../commands/models/auth.js"); await modelsAuthSetupTokenCommand( { provider: opts.provider as string | undefined, yes: Boolean(opts.yes), + agent, }, defaultRuntime, ); @@ -344,7 +351,8 @@ export function registerModelsCli(program: Command) { "--expires-in ", "Optional expiry duration (e.g. 365d, 12h). Stored as absolute expiresAt.", ) - .action(async (opts) => { + .action(async (opts, command) => { + const agent = resolveOptionFromCommand(command, "agent"); await runModelsCommand(async () => { const { modelsAuthPasteTokenCommand } = await import("../commands/models/auth.js"); await modelsAuthPasteTokenCommand( @@ -352,6 +360,7 @@ export function registerModelsCli(program: Command) { provider: opts.provider as string | undefined, profileId: opts.profileId as string | undefined, expiresIn: opts.expiresIn as string | undefined, + agent, }, defaultRuntime, ); @@ -362,7 +371,8 @@ export function registerModelsCli(program: Command) { .command("login-github-copilot") .description("Login to GitHub Copilot via GitHub device flow (TTY required)") .option("--yes", "Overwrite existing profile without prompting", false) - .action(async (opts) => { + .action(async (opts, command) => { + const agent = resolveOptionFromCommand(command, "agent"); await runModelsCommand(async () => { const { modelsAuthLoginCommand } = await import("../commands/models/auth.js"); await modelsAuthLoginCommand( @@ -370,6 +380,7 @@ export function registerModelsCli(program: Command) { provider: "github-copilot", method: "device", yes: Boolean(opts.yes), + agent, }, defaultRuntime, ); diff --git a/src/commands/models/auth.test.ts b/src/commands/models/auth.test.ts index cf3dd1c3e7a..778e93344f0 100644 --- a/src/commands/models/auth.test.ts +++ b/src/commands/models/auth.test.ts @@ -74,11 +74,15 @@ vi.mock("@clack/prompts", () => ({ text: mocks.clackText, })); -vi.mock("../../agents/agent-scope.js", () => ({ - resolveDefaultAgentId: mocks.resolveDefaultAgentId, - resolveAgentDir: mocks.resolveAgentDir, - resolveAgentWorkspaceDir: mocks.resolveAgentWorkspaceDir, -})); +vi.mock("../../agents/agent-scope.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + resolveDefaultAgentId: mocks.resolveDefaultAgentId, + resolveAgentDir: mocks.resolveAgentDir, + resolveAgentWorkspaceDir: mocks.resolveAgentWorkspaceDir, + }; +}); vi.mock("../../agents/workspace.js", () => ({ resolveDefaultAgentWorkspaceDir: mocks.resolveDefaultAgentWorkspaceDir, @@ -92,10 +96,14 @@ vi.mock("../../wizard/clack-prompter.js", () => ({ createClackPrompter: mocks.createClackPrompter, })); -vi.mock("./shared.js", () => ({ - loadValidConfigOrThrow: mocks.loadValidConfigOrThrow, - updateConfig: mocks.updateConfig, -})); +vi.mock("./shared.js", async (importOriginal) => { + const actual = await importOriginal(); + return { + ...actual, + loadValidConfigOrThrow: mocks.loadValidConfigOrThrow, + updateConfig: mocks.updateConfig, + }; +}); vi.mock("../../config/logging.js", () => ({ logConfigUpdated: mocks.logConfigUpdated, @@ -199,8 +207,12 @@ vi.mock("../provider-auth-helpers.js", () => { }; }); -const { modelsAuthLoginCommand, modelsAuthPasteTokenCommand, modelsAuthSetupTokenCommand } = - await import("./auth.js"); +const { + modelsAuthAddCommand, + modelsAuthLoginCommand, + modelsAuthPasteTokenCommand, + modelsAuthSetupTokenCommand, +} = await import("./auth.js"); function createRuntime(): RuntimeEnv { return { @@ -317,6 +329,22 @@ describe("modelsAuthLoginCommand", () => { restoreStdin = null; }); + function useCoderAgentConfig() { + currentConfig = { + agents: { + list: [{ id: "main" }, { id: "coder", workspace: "/tmp/openclaw/workspaces/coder" }], + }, + }; + const originalConfig = currentConfig; + mocks.resolveAgentDir.mockImplementation((_cfg: OpenClawConfig, agentId: string) => + agentId === "coder" ? "/tmp/openclaw/agents/coder" : "/tmp/openclaw/agents/main", + ); + mocks.resolveAgentWorkspaceDir.mockImplementation((_cfg: OpenClawConfig, agentId: string) => + agentId === "coder" ? "/tmp/openclaw/workspaces/coder" : "/tmp/openclaw/workspace", + ); + return originalConfig; + } + it("runs plugin-owned openai-codex login", async () => { const runtime = createRuntime(); const fakeStore = { @@ -372,6 +400,36 @@ describe("modelsAuthLoginCommand", () => { ); }); + it("uses the requested agent store for provider auth login", async () => { + const runtime = createRuntime(); + const coderStore = { + profiles: { + "openai-codex:coder@example.com": { + type: "oauth", + provider: "openai-codex", + }, + }, + usageStats: {}, + }; + const originalConfig = useCoderAgentConfig(); + mocks.loadAuthProfileStoreForRuntime.mockReturnValue(coderStore); + + await modelsAuthLoginCommand({ provider: "openai-codex", agent: "coder" }, runtime); + + expect(mocks.resolveDefaultAgentId).not.toHaveBeenCalled(); + expect(mocks.resolveAgentDir).toHaveBeenCalledWith(originalConfig, "coder"); + expect(mocks.loadAuthProfileStoreForRuntime).toHaveBeenCalledWith("/tmp/openclaw/agents/coder"); + expect(runProviderAuth).toHaveBeenCalledWith( + expect.objectContaining({ + agentDir: "/tmp/openclaw/agents/coder", + workspaceDir: "/tmp/openclaw/workspaces/coder", + }), + ); + expect(mocks.upsertAuthProfile).toHaveBeenCalledWith( + expect.objectContaining({ agentDir: "/tmp/openclaw/agents/coder" }), + ); + }); + it("loads the owning plugin for an explicit provider even in a clean config", async () => { const runtime = createRuntime(); const runClaudeCliMigration = vi.fn().mockResolvedValue({ @@ -418,6 +476,7 @@ describe("modelsAuthLoginCommand", () => { workspaceDir: "/tmp/openclaw/workspace", bundledProviderAllowlistCompat: true, bundledProviderVitestCompat: true, + includeUntrustedWorkspacePlugins: false, providerRefs: ["anthropic"], activate: true, }), @@ -706,6 +765,40 @@ describe("modelsAuthLoginCommand", () => { ); }); + it("writes pasted tokens to the requested agent store", async () => { + const runtime = createRuntime(); + useCoderAgentConfig(); + mocks.clackText.mockResolvedValue("openai-token"); + + await modelsAuthPasteTokenCommand({ provider: "openai", agent: "coder" }, runtime); + + expect(mocks.resolveDefaultAgentId).not.toHaveBeenCalled(); + expect(mocks.upsertAuthProfile).toHaveBeenCalledWith({ + profileId: "openai:manual", + credential: { + type: "token", + provider: "openai", + token: "openai-token", + }, + agentDir: "/tmp/openclaw/agents/coder", + }); + }); + + it("rejects an unknown agent before prompting for pasted tokens", async () => { + const runtime = createRuntime(); + currentConfig = { agents: { list: [{ id: "main" }] } }; + + await expect( + modelsAuthPasteTokenCommand({ provider: "openai", agent: "missing" }, runtime), + ).rejects.toThrow( + 'Unknown agent id "missing". Use "openclaw agents list" to see configured agents.', + ); + + expect(mocks.clackText).not.toHaveBeenCalled(); + expect(mocks.upsertAuthProfile).not.toHaveBeenCalled(); + expect(mocks.updateConfig).not.toHaveBeenCalled(); + }); + it("runs token auth for any token-capable provider plugin", async () => { const runtime = createRuntime(); const runTokenAuth = vi.fn().mockResolvedValue({ @@ -748,4 +841,118 @@ describe("modelsAuthLoginCommand", () => { agentDir: "/tmp/openclaw/agents/main", }); }); + + it("uses the requested agent store for setup-token provider auth", async () => { + const runtime = createRuntime(); + useCoderAgentConfig(); + const runTokenAuth = vi.fn().mockResolvedValue({ + profiles: [ + { + profileId: "moonshot:token", + credential: { + type: "token", + provider: "moonshot", + token: "moonshot-token", + }, + }, + ], + }); + mocks.resolvePluginProviders.mockReturnValue([ + { + id: "moonshot", + label: "Moonshot", + auth: [ + { + id: "setup-token", + label: "setup-token", + kind: "token", + run: runTokenAuth, + }, + ], + }, + ]); + + await modelsAuthSetupTokenCommand({ provider: "moonshot", yes: true, agent: "coder" }, runtime); + + expect(mocks.resolveDefaultAgentId).not.toHaveBeenCalled(); + expect(runTokenAuth).toHaveBeenCalledWith( + expect.objectContaining({ + agentDir: "/tmp/openclaw/agents/coder", + workspaceDir: "/tmp/openclaw/workspaces/coder", + }), + ); + expect(mocks.upsertAuthProfile).toHaveBeenCalledWith( + expect.objectContaining({ agentDir: "/tmp/openclaw/agents/coder" }), + ); + }); + + it("uses the requested agent store for interactive token auth add", async () => { + const runtime = createRuntime(); + useCoderAgentConfig(); + const runTokenAuth = vi.fn().mockResolvedValue({ + profiles: [ + { + profileId: "moonshot:token", + credential: { + type: "token", + provider: "moonshot", + token: "moonshot-token", + }, + }, + ], + }); + mocks.resolvePluginProviders.mockReturnValue([ + { + id: "moonshot", + label: "Moonshot", + auth: [ + { + id: "setup-token", + label: "setup-token", + kind: "token", + run: runTokenAuth, + }, + ], + }, + ]); + mocks.clackSelect.mockResolvedValueOnce("moonshot").mockResolvedValueOnce("setup-token"); + + await modelsAuthAddCommand({ agent: "coder" }, runtime); + + expect(mocks.resolveDefaultAgentId).not.toHaveBeenCalled(); + expect(runTokenAuth).toHaveBeenCalledWith( + expect.objectContaining({ + agentDir: "/tmp/openclaw/agents/coder", + workspaceDir: "/tmp/openclaw/workspaces/coder", + }), + ); + expect(mocks.upsertAuthProfile).toHaveBeenCalledWith( + expect.objectContaining({ agentDir: "/tmp/openclaw/agents/coder" }), + ); + }); + + it("keeps the requested agent store when interactive auth add falls back to paste-token", async () => { + const runtime = createRuntime(); + useCoderAgentConfig(); + mocks.resolvePluginProviders.mockReturnValue([]); + mocks.clackSelect.mockResolvedValue("custom"); + mocks.clackText + .mockResolvedValueOnce("openai") + .mockResolvedValueOnce("openai:manual") + .mockResolvedValueOnce("openai-token"); + mocks.clackConfirm.mockResolvedValue(false); + + await modelsAuthAddCommand({ agent: "coder" }, runtime); + + expect(mocks.resolveDefaultAgentId).not.toHaveBeenCalled(); + expect(mocks.upsertAuthProfile).toHaveBeenCalledWith({ + profileId: "openai:manual", + credential: { + type: "token", + provider: "openai", + token: "openai-token", + }, + agentDir: "/tmp/openclaw/agents/coder", + }); + }); }); diff --git a/src/commands/models/auth.ts b/src/commands/models/auth.ts index 0c751a61e53..40a7ccd2351 100644 --- a/src/commands/models/auth.ts +++ b/src/commands/models/auth.ts @@ -43,7 +43,7 @@ import { pickAuthMethod, resolveProviderMatch, } from "../provider-auth-helpers.js"; -import { loadValidConfigOrThrow, updateConfig } from "./shared.js"; +import { loadValidConfigOrThrow, resolveKnownAgentId, updateConfig } from "./shared.js"; function guardCancel(value: T | symbol): T { if (typeof value === "symbol" || isCancel(value)) { @@ -103,16 +103,20 @@ function listProvidersWithTokenMethods(providers: ProviderPlugin[]): ProviderPlu async function resolveModelsAuthContext(params?: { requestedProvider?: string; + rawAgentId?: string | null; }): Promise { const config = await loadValidConfigOrThrow(); - const defaultAgentId = resolveDefaultAgentId(config); - const agentDir = resolveAgentDir(config, defaultAgentId); + const agentId = + resolveKnownAgentId({ cfg: config, rawAgentId: params?.rawAgentId }) ?? + resolveDefaultAgentId(config); + const agentDir = resolveAgentDir(config, agentId); const workspaceDir = - resolveAgentWorkspaceDir(config, defaultAgentId) ?? resolveDefaultAgentWorkspaceDir(); + resolveAgentWorkspaceDir(config, agentId) ?? resolveDefaultAgentWorkspaceDir(); const providers = resolvePluginProviders({ config, workspaceDir, mode: "setup", + includeUntrustedWorkspacePlugins: false, bundledProviderAllowlistCompat: true, bundledProviderVitestCompat: true, ...(params?.requestedProvider?.trim() @@ -127,9 +131,10 @@ async function resolveModelsAuthContext(params?: { }; } -async function resolveModelsAuthAgentDir(): Promise { +async function resolveModelsAuthAgentDir(rawAgentId?: string | null): Promise { const config = await loadValidConfigOrThrow(); - return resolveAgentDir(config, resolveDefaultAgentId(config)); + const agentId = resolveKnownAgentId({ cfg: config, rawAgentId }) ?? resolveDefaultAgentId(config); + return resolveAgentDir(config, agentId); } function resolveRequestedProviderOrThrow( @@ -321,7 +326,7 @@ async function runProviderAuthMethod(params: { } export async function modelsAuthSetupTokenCommand( - opts: { provider?: string; yes?: boolean }, + opts: { provider?: string; yes?: boolean; agent?: string }, runtime: RuntimeEnv, ) { if (!process.stdin.isTTY) { @@ -330,6 +335,7 @@ export async function modelsAuthSetupTokenCommand( const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext({ requestedProvider: opts.provider, + rawAgentId: opts.agent, }); const tokenProviders = listProvidersWithTokenMethods(providers); if (tokenProviders.length === 0) { @@ -376,10 +382,11 @@ export async function modelsAuthPasteTokenCommand( provider?: string; profileId?: string; expiresIn?: string; + agent?: string; }, runtime: RuntimeEnv, ) { - const agentDir = await resolveModelsAuthAgentDir(); + const agentDir = await resolveModelsAuthAgentDir(opts.agent); const rawProvider = normalizeOptionalString(opts.provider); if (!rawProvider) { throw new Error("Missing --provider."); @@ -435,8 +442,10 @@ export async function modelsAuthPasteTokenCommand( } } -export async function modelsAuthAddCommand(_opts: Record, runtime: RuntimeEnv) { - const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext(); +export async function modelsAuthAddCommand(opts: { agent?: string }, runtime: RuntimeEnv) { + const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext({ + rawAgentId: opts.agent, + }); const tokenProviders = listProvidersWithTokenMethods(providers); const provider = await select({ @@ -528,7 +537,10 @@ export async function modelsAuthAddCommand(_opts: Record, runtime ).trim() : undefined; - await modelsAuthPasteTokenCommand({ provider: providerId, profileId, expiresIn }, runtime); + await modelsAuthPasteTokenCommand( + { provider: providerId, profileId, expiresIn, agent: opts.agent }, + runtime, + ); } type LoginOptions = { @@ -536,6 +548,7 @@ type LoginOptions = { method?: string; setDefault?: boolean; yes?: boolean; + agent?: string; }; /** @@ -588,6 +601,7 @@ export async function modelsAuthLoginCommand(opts: LoginOptions, runtime: Runtim const { config, agentDir, workspaceDir, providers } = await resolveModelsAuthContext({ requestedProvider: opts.provider, + rawAgentId: opts.agent, }); const prompter = createClackPrompter(); const authProviders = listProvidersWithAuthMethods(providers);