Files
claude-agent-acp/src/auth-methods.ts
Mark Tkachenko c09543d85a fix: offer Anthropic Console login in remote environments (#1572)
## Problem

In a remote environment (`SSH_CONNECTION`, `SSH_CLIENT`, `SSH_TTY`,
`NO_BROWSER` or `CLAUDE_CODE_REMOTE` set), `initialize` advertised only
`claude-login`, the TUI `claude /login`. A user with API billing had no
Anthropic Console option. With `--hide-claude-auth`, the list was empty
and there was no way to log in at all.

There is a second bug. A `_meta["terminal-auth"]` command reruns the
agent with its own flags, for example `--hide-claude-auth --cli auth
login --console`. The `--cli` passthrough removed only `--cli`, so the
Claude CLI failed with `unknown option '--hide-claude-auth'`.

## Fix

- `terminalAuthMethods` (`src/auth-methods.ts`) now offers
`console-login` in remote environments too. Since Claude Code 2.1.126,
`auth login` accepts the OAuth code pasted into the terminal when the
localhost callback can't be reached. The subscription method stays as
`claude-login` on a remote host and `claude-ai-login` locally.
`--hide-claude-auth` hides it in both cases. The change is in the shared
module, so it applies to v1 and v2.
- New `cliArgs()` (`src/agent-command.ts`) removes `--cli` and
`--hide-claude-auth` before the arguments go to the Claude CLI.

## Tests

- `v1/authorization.test.ts`: an `it.each` over the five remote env vars
× typed/`_meta` terminal auth × with/without `--hide-claude-auth`.
- `v2/acp-v2.test.ts`: a remote host with `--hide-claude-auth` gets
exactly one `console-login` terminal method.
- `agent-command.test.ts`: `cliArgs` under Node, a compiled binary, and
`--cli --version`.

Manually checked in a JetBrains IDE through a local launch overlay with
`SSH_CONNECTION` set and `--hide-claude-auth`. Only Anthropic Console
was offered, the terminal login completed, and the chat worked
afterwards.
2026-11-10 13:40:44 +01:00

84 lines
2.7 KiB
TypeScript

import type { AuthMethod } from "@agentclientprotocol/sdk/unstable";
import { agentCommand } from "./agent-command.js";
import { shouldHideClaudeAuth } from "./hide-claude-auth.js";
/**
* The terminal login methods the agent advertises, in v1 form.
*
* - `terminal`: the client runs `args` against the agent's own command
* (`auth.terminal` in v1, an `auth.terminal` object in v2).
* - `metaTerminal`: the client reads `_meta["terminal-auth"]`, which names
* the command too.
*
* Without either, the client cannot run a login, and the list is empty. On a
* remote machine (SSH, `NO_BROWSER`, `CLAUDE_CODE_REMOTE`, as the SDK's own
* isRemote check) the subscription login runs the TUI's `claude /login`:
* clients such as Tidewave set `NO_BROWSER` to get it. The Anthropic Console
* login is offered there too, because since Claude Code 2.1.126 `auth login`
* accepts the OAuth code pasted into the terminal when the localhost callback
* can't be reached. The Claude subscription method is left out when
* `shouldHideClaudeAuth()` says so.
*/
export function terminalAuthMethods({
terminal,
metaTerminal,
}: {
terminal: boolean;
metaTerminal: boolean;
}): AuthMethod[] {
if (!terminal && !metaTerminal) return [];
const withMeta = (method: AuthMethod & { args: string[] }, label: string): AuthMethod => {
if (!metaTerminal) return method;
const agent = agentCommand();
return {
...method,
_meta: {
"terminal-auth": { command: agent.command, args: [...agent.args, ...method.args], label },
},
};
};
const isRemote = !!(
process.env.NO_BROWSER ||
process.env.SSH_CONNECTION ||
process.env.SSH_CLIENT ||
process.env.SSH_TTY ||
process.env.CLAUDE_CODE_REMOTE
);
const subscriptionLogin = isRemote
? withMeta(
{
description: "Run `claude /login` in the terminal",
name: "Log in with Claude",
id: "claude-login",
type: "terminal",
args: ["--cli"],
},
"Claude Login",
)
: withMeta(
{
description: "Use Claude subscription ",
name: "Claude Subscription",
id: "claude-ai-login",
type: "terminal",
args: ["--cli", "auth", "login", "--claudeai"],
},
"Claude Login",
);
const methods: AuthMethod[] = [];
if (!shouldHideClaudeAuth()) methods.push(subscriptionLogin);
methods.push(
withMeta(
{
description: "Use Anthropic Console (API usage billing)",
name: "Anthropic Console",
id: "console-login",
type: "terminal",
args: ["--cli", "auth", "login", "--console"],
},
"Anthropic Console Login",
),
);
return methods;
}