mirror of
https://github.com/agentclientprotocol/claude-agent-acp.git
synced 2026-10-27 04:30:59 +00:00
fix: pass --settings before the auth status subcommand (#1392)
`runCliAuthProbe` builds `["auth", "status", "--json"]` and, when the
session has a settings
override, appends `--settings <json>`. `--settings` is a global option
of the Claude Code CLI;
the `auth status` subcommand only accepts `--json` and `--text` (see
`claude auth status --help`).
So every probe with an override fails with `error: unknown option
'--settings'` and an empty
stdout. The catch branch logs `claude auth status failed: …` on every
call and returns
`undefined`. For sessions that pass `_meta.claudeCode.options.settings`,
the session-creation and
per-prompt refreshes therefore never update the auth status. Only the
initialize probe, which has
no override, still works.
This moves `--settings <json>` in front of the subcommand. Without an
override the argv is
unchanged.
**Reproduce** (no credentials needed; an empty config dir answers "not
logged in"):
```sh
d=$(mktemp -d)
env -i HOME="$d" PATH="$PATH" CLAUDE_CONFIG_DIR="$d/.claude" \
claude auth status --json --settings '{}'
# error: unknown option '--settings'
env -i HOME="$d" PATH="$PATH" CLAUDE_CONFIG_DIR="$d/.claude" \
claude --settings '{}' auth status --json
# {"loggedIn": false, "authMethod": "none", ...}
```
The option is applied in the global position, not just parsed:
`claude --settings '{"apiKeyHelper":"/bin/echo rt-fake-key"}' auth
status --json` reports
`"loggedIn": true, "authMethod": "api_key_helper"`.
Tested with 10 native Claude Code binaries (linux-x64), each in an empty
`HOME`:
| Claude Code | `auth status --json --settings '{}'` | `--settings '{}'
auth status --json` |
|---|---|---|
| 2.1.286 | unknown option | JSON |
| 2.1.294 | unknown option | JSON |
| 2.1.295, 2.1.296, 2.1.299 | unknown option | JSON |
| 2.1.300, 2.1.301, 2.1.302 | unknown option | JSON |
| 2.1.303 (the SDK's `claudeCodeVersion`) | unknown option | JSON |
| 2.1.304 | unknown option | JSON |
The existing test in `auth-status.test.ts` pinned the old order; it now
expects `--settings`
first. Its mock and `statusCalls()` matched probes by `args[1] ===
"status"`, which a leading
global option shifts, so both now locate the subcommand instead.
`npm run build`, `format:check`, `lint` and `test:run` pass, apart from
one test that fails
identically on `main` without this change.
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -3430,7 +3430,9 @@ export class ClaudeAcpAgent {
|
||||
try {
|
||||
const cliPath = await claudeCliPath();
|
||||
const args = ["auth", "status", "--json"];
|
||||
if (context?.override) args.push("--settings", JSON.stringify(context.override));
|
||||
// `--settings` is a global CLI option: after the `auth status` subcommand the CLI rejects
|
||||
// it with "unknown option '--settings'", so it has to come first.
|
||||
if (context?.override) args.unshift("--settings", JSON.stringify(context.override));
|
||||
({ stdout } = await execFileAsync(cliPath, args, {
|
||||
timeout: AUTH_STATUS_PROBE_TIMEOUT_MS,
|
||||
cwd: context?.manager.getCwd() ?? process.cwd(),
|
||||
|
||||
@@ -22,6 +22,11 @@ vi.mock("@anthropic-ai/claude-agent-sdk", async () => ({
|
||||
/** Shared spy for both `claude auth status --json` and `claude auth logout`.
|
||||
* Tests set `execFileResults` per invoked subcommand. */
|
||||
const execFileSpy = vi.hoisted(() => vi.fn());
|
||||
/** `auth status` probes, wherever global options such as `--settings` put the subcommand. */
|
||||
function isStatusProbe(args: string[]): boolean {
|
||||
const auth = args.indexOf("auth");
|
||||
return auth >= 0 && args[auth + 1] === "status";
|
||||
}
|
||||
|
||||
vi.mock("node:child_process", async () => {
|
||||
const actual = await vi.importActual<typeof import("node:child_process")>("node:child_process");
|
||||
@@ -409,7 +414,7 @@ describe("auth status over ACP", () => {
|
||||
execFileSpy.mockImplementation((...invocation: unknown[]) => {
|
||||
const args = invocation[1] as string[];
|
||||
const cb = invocation[invocation.length - 1] as (...a: unknown[]) => void;
|
||||
if (args[1] === "status") {
|
||||
if (isStatusProbe(args)) {
|
||||
const stdout = statusStdout;
|
||||
const fails = statusFails;
|
||||
const answer = () => {
|
||||
@@ -451,7 +456,7 @@ describe("auth status over ACP", () => {
|
||||
});
|
||||
|
||||
function statusCalls() {
|
||||
return execFileSpy.mock.calls.filter((call) => (call[1] as string[])[1] === "status");
|
||||
return execFileSpy.mock.calls.filter((call) => isStatusProbe(call[1] as string[]));
|
||||
}
|
||||
|
||||
function updates() {
|
||||
@@ -542,11 +547,11 @@ describe("auth status over ACP", () => {
|
||||
env: { ANTHROPIC_BASE_URL: "https://example.invalid/override" },
|
||||
});
|
||||
expect(statusCalls().at(-1)?.[1]).toEqual([
|
||||
"--settings",
|
||||
JSON.stringify({ apiKeyHelper: "central proxy start" }),
|
||||
"auth",
|
||||
"status",
|
||||
"--json",
|
||||
"--settings",
|
||||
JSON.stringify({ apiKeyHelper: "central proxy start" }),
|
||||
]);
|
||||
await promptOnce(sessionId);
|
||||
await flushStatus();
|
||||
|
||||
Reference in New Issue
Block a user