Files
claude-agent-acp/src/agent-command.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

34 lines
1.5 KiB
TypeScript

import { HIDE_CLAUDE_AUTH_FLAG } from "./hide-claude-auth.js";
/**
* The command that starts this agent again with the flags it was started with,
* for a client to run in a terminal (e.g. `terminal-auth` login).
*
* Under Node, `process.argv` is `[node, script, ...flags]`, so the command is
* `node script ...flags`. In a `bun build --compile` executable it is
* `["bun", "/$bunfs/root/<name>", ...flags]` (`B:\~BUN\root\<name>` on
* Windows): `process.execPath` is the executable itself and `argv[1]` names a
* file inside it, which must not be passed back as an argument.
*/
export function agentCommand(argv: readonly string[] = process.argv): {
command: string;
args: string[];
} {
const [, script, ...flags] = argv;
if (script === undefined) return { command: process.execPath, args: [] };
const compiled = script.includes("$bunfs") || script.includes("~BUN");
return { command: process.execPath, args: compiled ? flags : [script, ...flags] };
}
/**
* The arguments for the Claude CLI under `--cli`: the flags this process was
* started with, minus the ones the adapter owns. The terminal-auth command
* reruns the agent with its own flags (`--hide-claude-auth --cli auth login`),
* and the CLI rejects the unknown option `--hide-claude-auth`. `argv.slice(2)`
* is the flags under both Node and a compiled executable (see
* {@link agentCommand}).
*/
export function cliArgs(argv: readonly string[] = process.argv): string[] {
return argv.slice(2).filter((arg) => arg !== "--cli" && arg !== HIDE_CLAUDE_AUTH_FLAG);
}