diff --git a/apps/marketing/app/globals.css b/apps/marketing/app/globals.css
index 9297910a..334ee661 100644
--- a/apps/marketing/app/globals.css
+++ b/apps/marketing/app/globals.css
@@ -163,17 +163,6 @@
}
}
-/* Fractal noise texture overlay */
-body::after {
- content: "";
- position: fixed;
- inset: 0;
- z-index: 9999;
- pointer-events: none;
- opacity: 0.035;
- background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 512 512' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noiseFilter'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.65' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noiseFilter)'/%3E%3C/svg%3E");
-}
-
/* Heading scale */
h1 {
font-size: clamp(2.25rem, 5vw, 3.75rem);
diff --git a/apps/marketing/components/DynamicBackground.tsx b/apps/marketing/components/DynamicBackground.tsx
index 9fe15a64..d3a62af4 100644
--- a/apps/marketing/components/DynamicBackground.tsx
+++ b/apps/marketing/components/DynamicBackground.tsx
@@ -1,136 +1,57 @@
-"use client";
-
-import { motion } from "framer-motion";
-
-/**
- * Full-page animated background with floating gradient orbs,
- * a subtle dot grid, and moving aurora streaks — all on deep black.
- */
export function DynamicBackground() {
return (
- {/* ── Dot grid ─────────────────────────────────────── */}
-
+
- {/* ── Gradient orbs ────────────────────────────────── */}
- {/* Top-left — cool purple */}
-
-
- {/* Center-right — deep blue */}
-
- {/* Bottom-center — subtle warm accent */}
-
- {/* Mid-left — faint teal whisper */}
-
- {/* ── Aurora streaks ───────────────────────────────── */}
-
-
- {/* ── Vignette — darkens edges toward pure black ──── */}
+ {/* Edge control */}
diff --git a/apps/marketing/components/GetStarted.tsx b/apps/marketing/components/GetStarted.tsx
index 4f50e8d3..7a21d199 100644
--- a/apps/marketing/components/GetStarted.tsx
+++ b/apps/marketing/components/GetStarted.tsx
@@ -5,14 +5,6 @@ import { LINKS } from "./links";
export function GetStarted() {
return (
- {/* Section glow */}
-
Start building.
One command. Zero config.
diff --git a/apps/marketing/components/Hero.tsx b/apps/marketing/components/Hero.tsx
index 002171d0..ec2d91fc 100644
--- a/apps/marketing/components/Hero.tsx
+++ b/apps/marketing/components/Hero.tsx
@@ -9,14 +9,10 @@ import { OKCodeMockup } from "./OKCodeMockup";
export function Hero() {
return (
- {/* Ambient glow — amplified for true black */}
@@ -89,17 +85,6 @@ export function Hero() {
>
-
- {/* Subtle reflection glow under frame */}
-
diff --git a/apps/server/src/doctor.ts b/apps/server/src/doctor.ts
index 2330e300..1456032b 100644
--- a/apps/server/src/doctor.ts
+++ b/apps/server/src/doctor.ts
@@ -87,9 +87,7 @@ const doctorProgram = Effect.gen(function* () {
console.log("No providers are ready. Set up at least one provider to start coding:");
console.log("");
console.log(" Codex: npm install -g @openai/codex && codex login");
- console.log(
- " Claude Code: npm install -g @anthropic-ai/claude-code && claude auth login (or set ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN)",
- );
+ console.log(" Claude Code: npm install -g @anthropic-ai/claude-code && claude auth login");
console.log(" Copilot: npm install -g @github/copilot && copilot login");
} else if (readyCount === statuses.length) {
console.log("All providers are ready.");
diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts
index 547c8a1b..4699647c 100644
--- a/apps/server/src/provider/Layers/ClaudeAdapter.test.ts
+++ b/apps/server/src/provider/Layers/ClaudeAdapter.test.ts
@@ -132,7 +132,6 @@ class FakeClaudeQuery implements AsyncIterable {
function makeHarness(config?: {
readonly nativeEventLogPath?: string;
readonly nativeEventLogger?: ClaudeAdapterLiveOptions["nativeEventLogger"];
- readonly readAuthTokenFromHelperCommand?: ClaudeAdapterLiveOptions["readAuthTokenFromHelperCommand"];
readonly cwd?: string;
readonly baseDir?: string;
}) {
@@ -149,11 +148,6 @@ function makeHarness(config?: {
createInput = input;
return query;
},
- ...(config?.readAuthTokenFromHelperCommand
- ? {
- readAuthTokenFromHelperCommand: config.readAuthTokenFromHelperCommand,
- }
- : {}),
...(config?.nativeEventLogger
? {
nativeEventLogger: config.nativeEventLogger,
@@ -358,64 +352,8 @@ describe("ClaudeAdapterLive", () => {
);
});
- it.effect("sources ANTHROPIC_AUTH_TOKEN from the configured helper command", () => {
- const helperCommand = "op read op://shared/anthropic/token --no-newline";
- const helperToken = "helper-token";
- let helperCall: {
- readonly command: string;
- readonly options?: { readonly cwd?: string };
- } | null = null;
- const helper: NonNullable = (
- command,
- options,
- ) => {
- helperCall = {
- command,
- ...(options?.cwd ? { options: { cwd: options.cwd } } : {}),
- };
- return helperToken;
- };
- const harness = makeHarness({
- readAuthTokenFromHelperCommand: helper,
- });
-
- return Effect.gen(function* () {
- const adapter = yield* ClaudeAdapter;
- yield* adapter.startSession({
- threadId: THREAD_ID,
- provider: "claudeAgent",
- cwd: THREAD_CWD,
- runtimeMode: "full-access",
- env: {
- ANTHROPIC_AUTH_TOKEN: "",
- },
- providerOptions: {
- claudeAgent: {
- authTokenHelperCommand: helperCommand,
- },
- },
- });
-
- const createInput = harness.getLastCreateQueryInput();
- assert.equal(createInput?.options.env?.ANTHROPIC_AUTH_TOKEN, helperToken);
- assert.equal(helperCall?.command, helperCommand);
- assert.equal(helperCall?.options?.cwd, THREAD_CWD);
- }).pipe(
- Effect.provideService(Random.Random, makeDeterministicRandomService()),
- Effect.provide(harness.layer),
- );
- });
-
- it.effect("prefers an explicit ANTHROPIC_AUTH_TOKEN over the helper command", () => {
- let helperCallCount = 0;
- const helper: NonNullable = () => {
- helperCallCount += 1;
- return "helper-token";
- };
- const harness = makeHarness({
- readAuthTokenFromHelperCommand: helper,
- });
-
+ it.effect("strips Anthropic credential env vars before starting Claude Code", () => {
+ const harness = makeHarness();
return Effect.gen(function* () {
const adapter = yield* ClaudeAdapter;
yield* adapter.startSession({
@@ -424,18 +362,14 @@ describe("ClaudeAdapterLive", () => {
cwd: THREAD_CWD,
runtimeMode: "full-access",
env: {
+ ANTHROPIC_API_KEY: "api-key",
ANTHROPIC_AUTH_TOKEN: "env-token",
},
- providerOptions: {
- claudeAgent: {
- authTokenHelperCommand: "op read op://shared/anthropic/token --no-newline",
- },
- },
});
const createInput = harness.getLastCreateQueryInput();
- assert.equal(createInput?.options.env?.ANTHROPIC_AUTH_TOKEN, "env-token");
- assert.equal(helperCallCount, 0);
+ assert.equal(createInput?.options.env?.ANTHROPIC_API_KEY, undefined);
+ assert.equal(createInput?.options.env?.ANTHROPIC_AUTH_TOKEN, undefined);
}).pipe(
Effect.provideService(Random.Random, makeDeterministicRandomService()),
Effect.provide(harness.layer),
diff --git a/apps/server/src/provider/Layers/ClaudeAdapter.ts b/apps/server/src/provider/Layers/ClaudeAdapter.ts
index edf7dce5..c2c44933 100644
--- a/apps/server/src/provider/Layers/ClaudeAdapter.ts
+++ b/apps/server/src/provider/Layers/ClaudeAdapter.ts
@@ -75,7 +75,6 @@ import {
extractTextAttachmentContents,
} from "../../attachmentText.ts";
import { ServerConfig } from "../../config.ts";
-import { readClaudeAuthTokenFromHelperCommand } from "../claudeAuthTokenHelper.ts";
import {
ProviderAdapterProcessError,
ProviderAdapterRequestError,
@@ -187,7 +186,6 @@ export interface ClaudeAdapterLiveOptions {
readonly prompt: AsyncIterable;
readonly options: ClaudeQueryOptions;
}) => ClaudeQueryRuntime;
- readonly readAuthTokenFromHelperCommand?: typeof readClaudeAuthTokenFromHelperCommand;
readonly nativeEventLogPath?: string;
readonly nativeEventLogger?: EventNdjsonLogger;
}
@@ -211,12 +209,6 @@ function toError(cause: unknown, fallback: string): Error {
return cause instanceof Error ? cause : new Error(toMessage(cause, fallback));
}
-function nonEmptyTrimmed(value: string | undefined): string | undefined {
- if (!value) return undefined;
- const trimmed = value.trim();
- return trimmed.length > 0 ? trimmed : undefined;
-}
-
function normalizeClaudeStreamMessages(cause: Cause.Cause): ReadonlyArray {
const errors = Cause.prettyErrors(cause)
.map((error) => error.message.trim())
@@ -263,7 +255,7 @@ function isClaudeAuthCause(cause: Cause.Cause): boolean {
}
function claudeAuthFailureMessage(): string {
- return "Claude Code is not configured with a supported Anthropic credential. Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN and try again.";
+ return "Claude Code must be authenticated with `claude auth login` before starting a session. API key and auth token credentials are not supported.";
}
function messageFromClaudeStreamCause(cause: Cause.Cause, fallback: string): string {
@@ -1025,8 +1017,6 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) {
stream: "native",
})
: undefined);
- const readAuthTokenFromHelperCommand =
- options?.readAuthTokenFromHelperCommand ?? readClaudeAuthTokenFromHelperCommand;
const createQuery =
options?.createQuery ??
@@ -2832,28 +2822,11 @@ function makeClaudeAdapter(options?: ClaudeAdapterLiveOptions) {
};
const runtimeEnv = input.env ? compactNodeProcessEnv(input.env) : undefined;
const baseEnv = mergeNodeProcessEnv(process.env, runtimeEnv);
- const explicitAuthToken = nonEmptyTrimmed(baseEnv.ANTHROPIC_AUTH_TOKEN);
- const helperCommand = providerOptions?.authTokenHelperCommand;
- let authToken = explicitAuthToken;
- if (!authToken && helperCommand) {
- authToken = yield* Effect.try({
- try: () =>
- readAuthTokenFromHelperCommand(helperCommand, {
- ...(input.cwd ? { cwd: input.cwd } : {}),
- env: baseEnv,
- }),
- catch: (cause) =>
- new ProviderAdapterProcessError({
- provider: PROVIDER,
- threadId,
- detail: `Failed to resolve Claude auth token from helper command: ${toMessage(cause, "unknown error")}`,
- cause,
- }),
- });
- }
- const queryEnv = authToken
- ? mergeNodeProcessEnv(baseEnv, { ANTHROPIC_AUTH_TOKEN: authToken })
- : baseEnv;
+ const {
+ ANTHROPIC_API_KEY: _anthropicApiKey,
+ ANTHROPIC_AUTH_TOKEN: _anthropicAuthToken,
+ ...queryEnv
+ } = baseEnv;
const queryOptions: ClaudeQueryOptions = {
...(input.cwd ? { cwd: input.cwd } : {}),
diff --git a/apps/server/src/provider/Layers/ProviderHealth.test.ts b/apps/server/src/provider/Layers/ProviderHealth.test.ts
index 6a342c35..a617f8b2 100644
--- a/apps/server/src/provider/Layers/ProviderHealth.test.ts
+++ b/apps/server/src/provider/Layers/ProviderHealth.test.ts
@@ -470,13 +470,17 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => {
// ── checkClaudeProviderStatus tests ──────────────────────────
describe("checkClaudeProviderStatus", () => {
- it.effect("returns ready when claude is installed and authenticated", () =>
+ it.effect("rejects Claude API-key auth and requires CLI login", () =>
Effect.gen(function* () {
const status = yield* checkClaudeProviderStatus;
assert.strictEqual(status.provider, "claudeAgent");
- assert.strictEqual(status.status, "ready");
+ assert.strictEqual(status.status, "error");
assert.strictEqual(status.available, true);
- assert.strictEqual(status.authStatus, "authenticated");
+ assert.strictEqual(status.authStatus, "unauthenticated");
+ assert.strictEqual(
+ status.message,
+ "Claude authentication status reported unsupported credential type 'apiKey'. Run `claude auth login` and try again. API key and auth token credentials are not supported.",
+ );
}).pipe(
Effect.provide(
mockSpawnerLayer((args) => {
@@ -535,7 +539,7 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => {
assert.strictEqual(status.authStatus, "unauthenticated");
assert.strictEqual(
status.message,
- "Claude is not configured with a supported Anthropic credential. Run `claude auth login`, or set ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN, and try again.",
+ "Claude Code must be authenticated with `claude auth login` before starting a session. Run `claude auth login` and try again. API key and auth token credentials are not supported.",
);
}).pipe(
Effect.provide(
@@ -561,7 +565,7 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => {
assert.strictEqual(status.status, "ready");
assert.strictEqual(status.available, true);
assert.strictEqual(status.authStatus, "authenticated");
- assert.strictEqual(status.message, "Claude Code CLI is ready via Claude.ai login.");
+ assert.strictEqual(status.message, "Claude Code CLI is ready via `claude auth login`.");
}).pipe(
Effect.provide(
mockSpawnerLayer((args) => {
@@ -626,10 +630,10 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => {
// ── parseClaudeAuthStatusFromOutput pure tests ────────────────────
describe("parseClaudeAuthStatusFromOutput", () => {
- it("exit code 0 with no auth markers is ready", () => {
+ it("exit code 0 with no auth markers is unknown", () => {
const parsed = parseClaudeAuthStatusFromOutput({ stdout: "OK\n", stderr: "", code: 0 });
- assert.strictEqual(parsed.status, "ready");
- assert.strictEqual(parsed.authStatus, "authenticated");
+ assert.strictEqual(parsed.status, "warning");
+ assert.strictEqual(parsed.authStatus, "unknown");
});
it("JSON with loggedIn=true is authenticated", () => {
@@ -640,7 +644,7 @@ it.layer(NodeServices.layer)("ProviderHealth", (it) => {
});
assert.strictEqual(parsed.status, "ready");
assert.strictEqual(parsed.authStatus, "authenticated");
- assert.strictEqual(parsed.message, "Claude Code CLI is ready via Claude.ai login.");
+ assert.strictEqual(parsed.message, "Claude Code CLI is ready via `claude auth login`.");
});
it("JSON with loggedIn=false is unauthenticated", () => {
diff --git a/apps/server/src/provider/Layers/ProviderHealth.ts b/apps/server/src/provider/Layers/ProviderHealth.ts
index f5f7c399..97f3861b 100644
--- a/apps/server/src/provider/Layers/ProviderHealth.ts
+++ b/apps/server/src/provider/Layers/ProviderHealth.ts
@@ -160,9 +160,8 @@ function hasGeminiHeadlessAuthEnv(): boolean {
}
const CLAUDE_CLI_AUTH_METHODS = new Set(["claude.ai", "oauth"]);
-const CLAUDE_SUPPORTED_AUTH_METHODS = new Set(["apiKey", "authToken", "claude.ai", "oauth"]);
const CLAUDE_AUTH_GUIDANCE =
- "Run `claude auth login`, or set ANTHROPIC_API_KEY / ANTHROPIC_AUTH_TOKEN, and try again.";
+ "Run `claude auth login` and try again. API key and auth token credentials are not supported.";
export function parseAuthStatusFromOutput(result: CommandResult): {
readonly status: ServerProviderStatusState;
@@ -660,14 +659,14 @@ export function parseClaudeAuthStatusFromOutput(result: CommandResult): {
lowerOutput.includes("not logged in") ||
lowerOutput.includes("login required") ||
lowerOutput.includes("authentication required") ||
- lowerOutput.includes("oauth authentication is currently not supported") ||
lowerOutput.includes("run `claude login`") ||
- lowerOutput.includes("run claude login")
+ lowerOutput.includes("run claude login") ||
+ lowerOutput.includes("api key and auth token credentials are not supported")
) {
return {
status: "error",
authStatus: "unauthenticated",
- message: `Claude is not configured with a supported Anthropic credential. ${CLAUDE_AUTH_GUIDANCE}`,
+ message: `Claude Code must be authenticated with \`claude auth login\` before starting a session. ${CLAUDE_AUTH_GUIDANCE}`,
};
}
@@ -700,35 +699,39 @@ export function parseClaudeAuthStatusFromOutput(result: CommandResult): {
const authMethod = parsedAuth.authMethod?.trim();
const normalizedAuthMethod = authMethod?.toLowerCase();
if (parsedAuth.auth === true) {
- if (authMethod && !CLAUDE_SUPPORTED_AUTH_METHODS.has(authMethod)) {
+ if (normalizedAuthMethod && !CLAUDE_CLI_AUTH_METHODS.has(normalizedAuthMethod)) {
return {
- status: "warning",
- authStatus: "unknown",
- message: `Claude authentication status reported an unsupported credential type '${authMethod}'. ${CLAUDE_AUTH_GUIDANCE}`,
+ status: "error",
+ authStatus: "unauthenticated",
+ message: `Claude authentication status reported unsupported credential type '${authMethod}'. ${CLAUDE_AUTH_GUIDANCE}`,
};
}
if (normalizedAuthMethod && CLAUDE_CLI_AUTH_METHODS.has(normalizedAuthMethod)) {
return {
status: "ready",
authStatus: "authenticated",
- message: "Claude Code CLI is ready via Claude.ai login.",
+ message: "Claude Code CLI is ready via `claude auth login`.",
};
}
- return { status: "ready", authStatus: "authenticated" };
+ return {
+ status: "warning",
+ authStatus: "unknown",
+ message: "Could not verify Claude CLI authentication method from status output.",
+ };
}
if (parsedAuth.auth === false) {
return {
status: "error",
authStatus: "unauthenticated",
- message: `Claude is not configured with a supported Anthropic credential. ${CLAUDE_AUTH_GUIDANCE}`,
+ message: `Claude Code must be authenticated with \`claude auth login\` before starting a session. ${CLAUDE_AUTH_GUIDANCE}`,
};
}
if (parsedAuth.attemptedJsonParse) {
- if (authMethod && !CLAUDE_SUPPORTED_AUTH_METHODS.has(authMethod)) {
+ if (normalizedAuthMethod && !CLAUDE_CLI_AUTH_METHODS.has(normalizedAuthMethod)) {
return {
status: "error",
authStatus: "unauthenticated",
- message: `Claude authentication status reported an unsupported credential type '${authMethod}'. ${CLAUDE_AUTH_GUIDANCE}`,
+ message: `Claude authentication status reported unsupported credential type '${authMethod}'. ${CLAUDE_AUTH_GUIDANCE}`,
};
}
return {
@@ -738,10 +741,6 @@ export function parseClaudeAuthStatusFromOutput(result: CommandResult): {
"Could not verify Claude authentication status from JSON output (missing auth marker).",
};
}
- if (result.code === 0) {
- return { status: "ready", authStatus: "authenticated" };
- }
-
const detail = detailFromResult(result);
return {
status: "warning",
diff --git a/apps/server/src/provider/claudeAuthTokenHelper.test.ts b/apps/server/src/provider/claudeAuthTokenHelper.test.ts
deleted file mode 100644
index 73da41ed..00000000
--- a/apps/server/src/provider/claudeAuthTokenHelper.test.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-import { describe, expect, it, vi } from "vitest";
-
-import { readClaudeAuthTokenFromHelperCommand } from "./claudeAuthTokenHelper";
-
-describe("readClaudeAuthTokenFromHelperCommand", () => {
- it("runs the helper command through a shell and trims the token", () => {
- const spawnSync = vi.fn(() => ({
- pid: 123,
- output: ["", " token-from-helper \n", ""],
- error: undefined,
- status: 0,
- signal: null,
- stdout: " token-from-helper \n",
- stderr: "",
- })) as unknown as typeof import("node:child_process").spawnSync;
-
- const token = readClaudeAuthTokenFromHelperCommand("op read op://shared/anthropic/token", {
- cwd: "/tmp/project",
- env: { PATH: "/usr/bin" },
- platform: "linux",
- spawnSync,
- timeoutMs: 1234,
- maxBuffer: 2048,
- });
-
- expect(token).toBe("token-from-helper");
- expect(spawnSync).toHaveBeenCalledWith(
- "/bin/sh",
- ["-lc", "op read op://shared/anthropic/token"],
- expect.objectContaining({
- cwd: "/tmp/project",
- encoding: "utf8",
- env: { PATH: "/usr/bin" },
- maxBuffer: 2048,
- timeout: 1234,
- }),
- );
- });
-
- it("fails when the helper command exits non-zero", () => {
- const spawnSync = vi.fn(() => ({
- pid: 123,
- output: ["", "ignored-token\n", ""],
- error: undefined,
- status: 1,
- signal: null,
- stdout: "ignored-token\n",
- stderr: "secret manager unavailable",
- })) as unknown as typeof import("node:child_process").spawnSync;
-
- expect(() =>
- readClaudeAuthTokenFromHelperCommand("pass show anthropic/token", {
- spawnSync,
- platform: "linux",
- }),
- ).toThrow("secret manager unavailable");
- });
-});
diff --git a/apps/server/src/provider/claudeAuthTokenHelper.ts b/apps/server/src/provider/claudeAuthTokenHelper.ts
deleted file mode 100644
index 2283e293..00000000
--- a/apps/server/src/provider/claudeAuthTokenHelper.ts
+++ /dev/null
@@ -1,100 +0,0 @@
-import { spawnSync, type SpawnSyncReturns } from "node:child_process";
-
-const DEFAULT_HELPER_TIMEOUT_MS = 5_000;
-const DEFAULT_HELPER_MAX_BUFFER_BYTES = 64 * 1024;
-
-export interface ClaudeAuthTokenHelperExecutionOptions {
- readonly cwd?: string;
- readonly env?: NodeJS.ProcessEnv;
- readonly maxBuffer?: number;
- readonly platform?: NodeJS.Platform;
- readonly spawnSync?: (
- command: string,
- args: ReadonlyArray,
- options: {
- readonly cwd?: string;
- readonly encoding: "utf8";
- readonly env?: NodeJS.ProcessEnv;
- readonly maxBuffer: number;
- readonly timeout: number;
- },
- ) => SpawnSyncReturns;
- readonly timeoutMs?: number;
-}
-
-function trimSingleLineOutput(value: string): string {
- const trimmed = value.trim();
- if (trimmed.length === 0) {
- return "";
- }
- if (trimmed.includes("\n") || trimmed.includes("\r")) {
- throw new Error("Claude auth token helper command must print a single-line token.");
- }
- return trimmed;
-}
-
-function shellCommandForPlatform(platform: NodeJS.Platform): {
- readonly command: string;
- readonly args: readonly string[];
-} {
- if (platform === "win32") {
- return {
- command: "cmd.exe",
- args: ["/d", "/s", "/c"],
- };
- }
-
- return {
- command: "/bin/sh",
- args: ["-lc"],
- };
-}
-
-function formatHelperFailureMessage(result: SpawnSyncReturns): string {
- if (result.error instanceof Error) {
- return result.error.message;
- }
-
- const stderr = result.stderr.trim();
- if (stderr.length > 0) {
- return stderr;
- }
-
- if (result.status === null) {
- return "Timed out while running Claude auth token helper command.";
- }
-
- return `Claude auth token helper command exited with code ${result.status}.`;
-}
-
-export function readClaudeAuthTokenFromHelperCommand(
- helperCommand: string,
- options?: ClaudeAuthTokenHelperExecutionOptions,
-): string {
- const command = helperCommand.trim();
- if (command.length === 0) {
- throw new Error("Claude auth token helper command is empty.");
- }
-
- const platform = options?.platform ?? process.platform;
- const shell = shellCommandForPlatform(platform);
- const spawn = options?.spawnSync ?? spawnSync;
- const result = spawn(shell.command, [...shell.args, command], {
- encoding: "utf8",
- maxBuffer: options?.maxBuffer ?? DEFAULT_HELPER_MAX_BUFFER_BYTES,
- timeout: options?.timeoutMs ?? DEFAULT_HELPER_TIMEOUT_MS,
- ...(options?.cwd ? { cwd: options.cwd } : {}),
- ...(options?.env ? { env: options.env } : {}),
- }) as SpawnSyncReturns;
-
- if (result.error || result.status !== 0) {
- throw new Error(formatHelperFailureMessage(result));
- }
-
- const token = trimSingleLineOutput(result.stdout);
- if (token.length === 0) {
- throw new Error("Claude auth token helper command returned no output.");
- }
-
- return token;
-}
diff --git a/apps/server/src/sme/authValidation.ts b/apps/server/src/sme/authValidation.ts
index 9d5e5bcc..2afa8ad8 100644
--- a/apps/server/src/sme/authValidation.ts
+++ b/apps/server/src/sme/authValidation.ts
@@ -21,7 +21,7 @@ import {
const OPENAI_MODEL_PROVIDERS = new Set(["openai"]);
const CLAUDE_SME_MISSING_CREDENTIALS_MESSAGE =
- "Claude SME Chat uses direct Anthropic credentials, not the Claude CLI login. Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN, or configure `authTokenHelperCommand` in Settings.";
+ "Claude SME Chat uses direct Anthropic credentials, not the Claude CLI login. Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN.";
export function getAllowedSmeAuthMethods(provider: ProviderKind): readonly SmeAuthMethod[] {
switch (provider) {
@@ -116,7 +116,7 @@ export function resolveClaudeSmeSetup(input: {
ok: false,
severity: "error",
message:
- "Claude SME Chat is set to Auth Token, but no ANTHROPIC_AUTH_TOKEN or auth token helper command is configured.",
+ "Claude SME Chat is set to Auth Token, but no ANTHROPIC_AUTH_TOKEN is configured.",
resolvedAuthMethod: "authToken",
resolvedAccountType: "unknown",
},
diff --git a/apps/server/src/sme/backends/anthropic.ts b/apps/server/src/sme/backends/anthropic.ts
index e9ea30c3..3d6a2e7c 100644
--- a/apps/server/src/sme/backends/anthropic.ts
+++ b/apps/server/src/sme/backends/anthropic.ts
@@ -3,7 +3,6 @@ import type { MessageParam } from "@anthropic-ai/sdk/resources";
import type { SmeMessageEvent } from "@okcode/contracts";
import { Effect } from "effect";
-import { readClaudeAuthTokenFromHelperCommand } from "../../provider/claudeAuthTokenHelper.ts";
import { SmeChatError } from "../Services/SmeChatService.ts";
import type { ProviderStartOptions } from "@okcode/contracts";
@@ -32,18 +31,11 @@ export function resolveAnthropicClientOptions(
const env = input?.env ?? process.env;
const explicitApiKey = nonEmptyTrimmed(env.ANTHROPIC_API_KEY);
const explicitAuthToken = nonEmptyTrimmed(env.ANTHROPIC_AUTH_TOKEN);
- const helperCommand = nonEmptyTrimmed(input?.providerOptions?.authTokenHelperCommand);
-
- let authToken = explicitAuthToken;
- if (!authToken && helperCommand) {
- authToken = readClaudeAuthTokenFromHelperCommand(helperCommand, { env });
- }
-
const baseURL = nonEmptyTrimmed(env.ANTHROPIC_BASE_URL ?? env.ANTHROPIC_API_BASE_URL);
return {
- apiKey: authToken ? null : (explicitApiKey ?? null),
- authToken: authToken ?? null,
+ apiKey: explicitAuthToken ? null : (explicitApiKey ?? null),
+ authToken: explicitAuthToken ?? null,
...(baseURL ? { baseURL } : {}),
};
}
diff --git a/apps/web/src/appSettings.test.ts b/apps/web/src/appSettings.test.ts
index 41c453d1..b65c8c1a 100644
--- a/apps/web/src/appSettings.test.ts
+++ b/apps/web/src/appSettings.test.ts
@@ -26,7 +26,6 @@ describe("AppSettingsSchema", () => {
expect(settings.showNotificationDetails).toBe(false);
expect(settings.includeDiagnosticsTipsInCopy).toBe(false);
expect(settings.browserPreviewStartPageUrl).toBe("");
- expect(settings.claudeAuthTokenHelperCommand).toBe("");
});
it("defaults sidebar appearance controls", () => {
@@ -64,11 +63,10 @@ describe("AppSettingsSchema", () => {
});
describe("getProviderStartOptions", () => {
- it("includes the Claude auth token helper command when configured", () => {
+ it("includes the Claude binary path when configured", () => {
expect(
getProviderStartOptions({
- claudeBinaryPath: "",
- claudeAuthTokenHelperCommand: "op read op://shared/anthropic/token --no-newline",
+ claudeBinaryPath: "/usr/local/bin/claude",
codexBinaryPath: "",
codexHomePath: "",
copilotBinaryPath: "",
@@ -78,7 +76,7 @@ describe("getProviderStartOptions", () => {
}),
).toEqual({
claudeAgent: {
- authTokenHelperCommand: "op read op://shared/anthropic/token --no-newline",
+ binaryPath: "/usr/local/bin/claude",
},
});
});
diff --git a/apps/web/src/appSettings.ts b/apps/web/src/appSettings.ts
index 9580e6a7..23a2c50f 100644
--- a/apps/web/src/appSettings.ts
+++ b/apps/web/src/appSettings.ts
@@ -91,9 +91,6 @@ export const AppSettingsSchema = Schema.Struct({
claudeBinaryPath: Schema.String.check(Schema.isMaxLength(4096)).pipe(withDefaults(() => "")),
copilotBinaryPath: Schema.String.check(Schema.isMaxLength(4096)).pipe(withDefaults(() => "")),
copilotConfigDir: Schema.String.check(Schema.isMaxLength(4096)).pipe(withDefaults(() => "")),
- claudeAuthTokenHelperCommand: Schema.String.check(Schema.isMaxLength(4096)).pipe(
- withDefaults(() => ""),
- ),
codexBinaryPath: Schema.String.check(Schema.isMaxLength(4096)).pipe(withDefaults(() => "")),
codexHomePath: Schema.String.check(Schema.isMaxLength(4096)).pipe(withDefaults(() => "")),
backgroundImageUrl: Schema.String.check(Schema.isMaxLength(4096)).pipe(withDefaults(() => "")),
@@ -265,7 +262,6 @@ function normalizeAppSettings(settings: AppSettings): AppSettings {
...settings,
backgroundImageUrl: settings.backgroundImageUrl.trim(),
browserPreviewStartPageUrl: settings.browserPreviewStartPageUrl.trim(),
- claudeAuthTokenHelperCommand: settings.claudeAuthTokenHelperCommand.trim(),
backgroundImageOpacity: clampBackgroundOpacity(settings.backgroundImageOpacity),
sidebarOpacity: clampOpacity(settings.sidebarOpacity),
sidebarProjectRowHeight: clampSidebarProjectRowHeight(settings.sidebarProjectRowHeight),
@@ -389,7 +385,6 @@ export function getProviderStartOptions(
| "claudeBinaryPath"
| "copilotBinaryPath"
| "copilotConfigDir"
- | "claudeAuthTokenHelperCommand"
| "codexBinaryPath"
| "codexHomePath"
| "openclawGatewayUrl"
@@ -405,13 +400,10 @@ export function getProviderStartOptions(
},
}
: {}),
- ...(settings.claudeBinaryPath || settings.claudeAuthTokenHelperCommand
+ ...(settings.claudeBinaryPath
? {
claudeAgent: {
- ...(settings.claudeBinaryPath ? { binaryPath: settings.claudeBinaryPath } : {}),
- ...(settings.claudeAuthTokenHelperCommand
- ? { authTokenHelperCommand: settings.claudeAuthTokenHelperCommand }
- : {}),
+ binaryPath: settings.claudeBinaryPath,
},
}
: {}),
diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx
index af2682c3..e9b30026 100644
--- a/apps/web/src/components/ChatView.tsx
+++ b/apps/web/src/components/ChatView.tsx
@@ -867,9 +867,8 @@ export default function ChatView({ threadId, onMinimize }: ChatViewProps) {
getSelectableThreadProviders({
statuses: providerStatuses,
openclawGatewayUrl: settings.openclawGatewayUrl,
- claudeAuthTokenHelperCommand: settings.claudeAuthTokenHelperCommand,
}),
- [providerStatuses, settings.claudeAuthTokenHelperCommand, settings.openclawGatewayUrl],
+ [providerStatuses, settings.openclawGatewayUrl],
);
const hasThreadStarted = Boolean(
activeThread &&
diff --git a/apps/web/src/components/chat/ProviderSetupCard.tsx b/apps/web/src/components/chat/ProviderSetupCard.tsx
index 811a0fa1..30c6e17f 100644
--- a/apps/web/src/components/chat/ProviderSetupCard.tsx
+++ b/apps/web/src/components/chat/ProviderSetupCard.tsx
@@ -27,9 +27,9 @@ const PROVIDER_CONFIG = {
},
claudeAgent: {
installCmd: "npm install -g @anthropic-ai/claude-code",
- authCmd: "set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN",
+ authCmd: "claude auth login",
verifyCmd: "claude auth status",
- note: "You can also configure a Claude auth token helper command or one-click secret-manager preset in Settings.",
+ note: "Claude Code must be installed and signed in through the CLI before it can be used.",
},
copilot: {
installCmd: "npm install -g @github/copilot",
diff --git a/apps/web/src/components/chat/threadError.test.ts b/apps/web/src/components/chat/threadError.test.ts
index 1c0b71cf..ad1ae94b 100644
--- a/apps/web/src/components/chat/threadError.test.ts
+++ b/apps/web/src/components/chat/threadError.test.ts
@@ -48,7 +48,7 @@ describe("humanizeThreadError", () => {
).toBe(true);
expect(
isAuthenticationThreadError(
- "Claude is not configured with a supported Anthropic credential. Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN and try again.",
+ "Claude Code must be authenticated with `claude auth login` before starting a session. API key and auth token credentials are not supported.",
),
).toBe(true);
});
@@ -81,11 +81,9 @@ describe("humanizeThreadError", () => {
).toContain("Troubleshooting:");
expect(
buildThreadErrorDiagnosticsCopy(
- "Claude Code is signed in with OAuth, which is not supported here. Set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN and try again.",
+ "Claude Code must be authenticated with `claude auth login` before starting a session. API key and auth token credentials are not supported.",
{ includeTips: true },
),
- ).toContain(
- "Set `ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN` in the runtime environment and retry the turn.",
- );
+ ).toContain("Run `claude auth login` and retry the turn.");
});
});
diff --git a/apps/web/src/components/chat/threadError.ts b/apps/web/src/components/chat/threadError.ts
index 27d71959..72e6398d 100644
--- a/apps/web/src/components/chat/threadError.ts
+++ b/apps/web/src/components/chat/threadError.ts
@@ -38,7 +38,7 @@ function extractWorktreeDetail(error: string): string | null {
function getProviderLoginCommand(error: string): string | null {
const lower = error.toLowerCase();
if (lower.includes("claude")) {
- return "`ANTHROPIC_API_KEY` or `ANTHROPIC_AUTH_TOKEN`";
+ return "`claude auth login`";
}
if (lower.includes("codex")) {
return "`codex login`";
diff --git a/apps/web/src/i18n/messages/en.json b/apps/web/src/i18n/messages/en.json
index 389bd976..0a5aecc9 100644
--- a/apps/web/src/i18n/messages/en.json
+++ b/apps/web/src/i18n/messages/en.json
@@ -133,7 +133,7 @@
"settings.advanced.keybindings.opening": "Opening...",
"settings.advanced.keybindings.resolvingPath": "Resolving keybindings path...",
"settings.advanced.keybindings.title": "Keybindings",
- "settings.advanced.providerInstalls.claude.binaryDescription": "Leave blank to use claude from your PATH. Claude Code needs ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN in the runtime environment.",
+ "settings.advanced.providerInstalls.claude.binaryDescription": "Leave blank to use claude from your PATH. Authentication uses claude auth login.",
"settings.advanced.providerInstalls.claude.binaryPathLabel": "Claude Code binary path",
"settings.advanced.providerInstalls.claude.binaryPlaceholder": "Claude Code binary path",
"settings.advanced.providerInstalls.codex.binaryDescription": "Leave blank to use codex from your PATH. Authentication normally uses codex login unless your Codex config points at a custom model provider.",
diff --git a/apps/web/src/i18n/messages/es.json b/apps/web/src/i18n/messages/es.json
index 794d8d74..52e9fea2 100644
--- a/apps/web/src/i18n/messages/es.json
+++ b/apps/web/src/i18n/messages/es.json
@@ -133,7 +133,7 @@
"settings.advanced.keybindings.opening": "Abriendo...",
"settings.advanced.keybindings.resolvingPath": "Resolviendo la ruta de keybindings...",
"settings.advanced.keybindings.title": "Atajos",
- "settings.advanced.providerInstalls.claude.binaryDescription": "Déjalo vacío para usar claude desde tu PATH. Claude Code necesita ANTHROPIC_API_KEY o ANTHROPIC_AUTH_TOKEN en el entorno de ejecución.",
+ "settings.advanced.providerInstalls.claude.binaryDescription": "Déjalo vacío para usar claude desde tu PATH. La autenticación usa claude auth login.",
"settings.advanced.providerInstalls.claude.binaryPathLabel": "Ruta del binario de Claude Code",
"settings.advanced.providerInstalls.claude.binaryPlaceholder": "Ruta del binario de Claude Code",
"settings.advanced.providerInstalls.codex.binaryDescription": "Déjalo vacío para usar codex desde tu PATH. La autenticación normalmente usa codex login, salvo que tu configuración de Codex apunte a un proveedor de modelos personalizado.",
diff --git a/apps/web/src/i18n/messages/fr.json b/apps/web/src/i18n/messages/fr.json
index 93d790ee..49e3a153 100644
--- a/apps/web/src/i18n/messages/fr.json
+++ b/apps/web/src/i18n/messages/fr.json
@@ -133,7 +133,7 @@
"settings.advanced.keybindings.opening": "Ouverture...",
"settings.advanced.keybindings.resolvingPath": "Résolution du chemin de keybindings...",
"settings.advanced.keybindings.title": "Raccourcis",
- "settings.advanced.providerInstalls.claude.binaryDescription": "Laissez vide pour utiliser claude depuis votre PATH. Claude Code a besoin de ANTHROPIC_API_KEY ou ANTHROPIC_AUTH_TOKEN dans l'environnement d'exécution.",
+ "settings.advanced.providerInstalls.claude.binaryDescription": "Laissez vide pour utiliser claude depuis votre PATH. L'authentification utilise claude auth login.",
"settings.advanced.providerInstalls.claude.binaryPathLabel": "Chemin du binaire Claude Code",
"settings.advanced.providerInstalls.claude.binaryPlaceholder": "Chemin du binaire Claude Code",
"settings.advanced.providerInstalls.codex.binaryDescription": "Laissez vide pour utiliser codex depuis votre PATH. L'authentification utilise normalement codex login, sauf si votre configuration Codex pointe vers un fournisseur de modèle personnalisé.",
diff --git a/apps/web/src/i18n/messages/zh-CN.json b/apps/web/src/i18n/messages/zh-CN.json
index c8d33010..dc2db436 100644
--- a/apps/web/src/i18n/messages/zh-CN.json
+++ b/apps/web/src/i18n/messages/zh-CN.json
@@ -133,7 +133,7 @@
"settings.advanced.keybindings.opening": "打开中...",
"settings.advanced.keybindings.resolvingPath": "正在解析 keybindings 路径...",
"settings.advanced.keybindings.title": "快捷键",
- "settings.advanced.providerInstalls.claude.binaryDescription": "留空则使用 PATH 中的 claude。Claude Code 需要在运行环境中提供 ANTHROPIC_API_KEY 或 ANTHROPIC_AUTH_TOKEN。",
+ "settings.advanced.providerInstalls.claude.binaryDescription": "留空则使用 PATH 中的 claude。认证使用 claude auth login。",
"settings.advanced.providerInstalls.claude.binaryPathLabel": "Claude Code 二进制路径",
"settings.advanced.providerInstalls.claude.binaryPlaceholder": "Claude Code 二进制路径",
"settings.advanced.providerInstalls.codex.binaryDescription": "留空则使用 PATH 中的 codex。认证通常使用 codex login,除非你的 Codex 配置指向了自定义模型提供方。",
diff --git a/apps/web/src/lib/claudeAuthTokenHelperPresets.test.ts b/apps/web/src/lib/claudeAuthTokenHelperPresets.test.ts
deleted file mode 100644
index c332685f..00000000
--- a/apps/web/src/lib/claudeAuthTokenHelperPresets.test.ts
+++ /dev/null
@@ -1,25 +0,0 @@
-import { describe, expect, it } from "vitest";
-
-import { CLAUDE_AUTH_TOKEN_HELPER_PRESETS } from "./claudeAuthTokenHelperPresets";
-
-describe("CLAUDE_AUTH_TOKEN_HELPER_PRESETS", () => {
- it("includes presets for common secret managers", () => {
- expect(CLAUDE_AUTH_TOKEN_HELPER_PRESETS).toEqual([
- {
- label: "1Password",
- description: "Prefill an `op read` command for a 1Password secret.",
- command: 'op read "op://Private/Anthropic/Claude Code/token" --no-newline',
- },
- {
- label: "pass",
- description: "Prefill a `pass show` command for the `pass` password store.",
- command: "pass show anthropic/claude-code",
- },
- {
- label: "Doppler",
- description: "Prefill a `doppler secrets get` command for a linked project.",
- command: "doppler secrets get ANTHROPIC_AUTH_TOKEN --plain",
- },
- ]);
- });
-});
diff --git a/apps/web/src/lib/claudeAuthTokenHelperPresets.ts b/apps/web/src/lib/claudeAuthTokenHelperPresets.ts
deleted file mode 100644
index 97be2191..00000000
--- a/apps/web/src/lib/claudeAuthTokenHelperPresets.ts
+++ /dev/null
@@ -1,23 +0,0 @@
-export type ClaudeAuthTokenHelperPreset = {
- readonly label: string;
- readonly description: string;
- readonly command: string;
-};
-
-export const CLAUDE_AUTH_TOKEN_HELPER_PRESETS: readonly ClaudeAuthTokenHelperPreset[] = [
- {
- label: "1Password",
- description: "Prefill an `op read` command for a 1Password secret.",
- command: 'op read "op://Private/Anthropic/Claude Code/token" --no-newline',
- },
- {
- label: "pass",
- description: "Prefill a `pass show` command for the `pass` password store.",
- command: "pass show anthropic/claude-code",
- },
- {
- label: "Doppler",
- description: "Prefill a `doppler secrets get` command for a linked project.",
- command: "doppler secrets get ANTHROPIC_AUTH_TOKEN --plain",
- },
-] as const;
diff --git a/apps/web/src/lib/providerAvailability.test.ts b/apps/web/src/lib/providerAvailability.test.ts
index af996aab..53c4fb68 100644
--- a/apps/web/src/lib/providerAvailability.test.ts
+++ b/apps/web/src/lib/providerAvailability.test.ts
@@ -49,16 +49,6 @@ describe("providerAvailability", () => {
).toBe(false);
});
- it("allows Claude when a local auth token helper is configured", () => {
- expect(
- isProviderReadyForThreadSelection({
- provider: "claudeAgent",
- statuses: [makeStatus("claudeAgent", { status: "error", authStatus: "unauthenticated" })],
- claudeAuthTokenHelperCommand: "op read op://shared/anthropic/token --no-newline",
- }),
- ).toBe(true);
- });
-
it("treats configured OpenClaw as selectable even when server auth state is unknown", () => {
expect(
isProviderReadyForThreadSelection({
@@ -77,9 +67,8 @@ describe("providerAvailability", () => {
makeStatus("codex"),
makeStatus("claudeAgent", { status: "error", authStatus: "unauthenticated" }),
],
- claudeAuthTokenHelperCommand: "op read op://shared/anthropic/token --no-newline",
}),
- ).toEqual(["codex", "claudeAgent", "openclaw"]);
+ ).toEqual(["codex", "openclaw"]);
});
it("falls back to the first selectable provider when the preferred one is unavailable", () => {
diff --git a/apps/web/src/lib/providerAvailability.ts b/apps/web/src/lib/providerAvailability.ts
index c0d86da8..64017e8a 100644
--- a/apps/web/src/lib/providerAvailability.ts
+++ b/apps/web/src/lib/providerAvailability.ts
@@ -31,7 +31,6 @@ export function isProviderReadyForThreadSelection(input: {
provider: ProviderKind;
statuses: ReadonlyArray;
openclawGatewayUrl?: string | null | undefined;
- claudeAuthTokenHelperCommand?: string | null | undefined;
}): boolean {
const status = getProviderStatusByKind(input.statuses, input.provider);
@@ -46,15 +45,6 @@ export function isProviderReadyForThreadSelection(input: {
return false;
}
- if (
- input.provider === "claudeAgent" &&
- (input.claudeAuthTokenHelperCommand ?? "").trim().length > 0 &&
- status.available &&
- (status.authStatus ?? status.auth?.status) === "unauthenticated"
- ) {
- return true;
- }
-
const authStatus = status.authStatus ?? status.auth?.status;
return Boolean(status.available && status.status === "ready" && authStatus !== "unauthenticated");
}
@@ -62,14 +52,12 @@ export function isProviderReadyForThreadSelection(input: {
export function getSelectableThreadProviders(input: {
statuses: ReadonlyArray;
openclawGatewayUrl?: string | null | undefined;
- claudeAuthTokenHelperCommand?: string | null | undefined;
}): ProviderKind[] {
return THREAD_PROVIDER_ORDER.filter((provider) =>
isProviderReadyForThreadSelection({
provider,
statuses: input.statuses,
openclawGatewayUrl: input.openclawGatewayUrl,
- claudeAuthTokenHelperCommand: input.claudeAuthTokenHelperCommand,
}),
);
}
diff --git a/apps/web/src/routes/_chat.settings.tsx b/apps/web/src/routes/_chat.settings.tsx
index 5cc88014..85c3afc7 100644
--- a/apps/web/src/routes/_chat.settings.tsx
+++ b/apps/web/src/routes/_chat.settings.tsx
@@ -106,7 +106,6 @@ import {
type CustomThemeData,
} from "../lib/customTheme";
import { openUrlInAppBrowser } from "../lib/openUrlInAppBrowser";
-import { CLAUDE_AUTH_TOKEN_HELPER_PRESETS } from "../lib/claudeAuthTokenHelperPresets";
import {
getSelectableThreadProviders,
isProviderReadyForThreadSelection,
@@ -361,9 +360,8 @@ const INSTALL_PROVIDER_SETTINGS: readonly InstallProviderSettings[] = [
binaryPlaceholder: "Claude Code binary path",
binaryDescription: (
<>
- Leave blank to use claude from your PATH. Claude Code sessions need an{" "}
- ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN in the runtime
- environment. You can also source the token automatically with a helper command.
+ Leave blank to use claude from your PATH. Authentication uses{" "}
+ claude auth login.
>
),
},
@@ -386,9 +384,9 @@ const PROVIDER_AUTH_GUIDES: Record<
},
claudeAgent: {
installCmd: "npm install -g @anthropic-ai/claude-code",
- authCmd: "set ANTHROPIC_API_KEY or ANTHROPIC_AUTH_TOKEN",
+ authCmd: "claude auth login",
verifyCmd: "claude auth status",
- note: "Claude Code must be installed and configured with an Anthropic API key or auth token before it appears in the thread picker. Use Environment to add a Claude auth token in one click, or configure a helper command in the Claude install panel.",
+ note: "Claude Code must be installed and signed in through the CLI before it appears in the thread picker.",
},
gemini: {
installCmd: "npm install -g @google/gemini-cli",
@@ -412,26 +410,15 @@ function getAuthenticationBadgeCopy(input: {
status: ServerProviderStatus | null;
provider: ProviderKind;
openclawGatewayUrl: string;
- hasClaudeAuthTokenHelper: boolean;
}): {
tone: "success" | "warning" | "error";
label: string;
} {
- if (
- input.provider === "claudeAgent" &&
- input.hasClaudeAuthTokenHelper &&
- input.status?.available === true &&
- input.status?.authStatus === "unauthenticated"
- ) {
- return { tone: "success", label: "Token helper configured" };
- }
-
if (
isProviderReadyForThreadSelection({
provider: input.provider,
statuses: input.status ? [input.status] : [],
openclawGatewayUrl: input.openclawGatewayUrl,
- claudeAuthTokenHelperCommand: input.hasClaudeAuthTokenHelper ? "configured" : undefined,
})
) {
return { tone: "success", label: "Available in thread picker" };
@@ -456,21 +443,16 @@ function AuthenticationStatusCard({
provider,
status,
openclawGatewayUrl,
- claudeAuthTokenHelperCommand,
}: {
provider: ProviderKind;
status: ServerProviderStatus | null;
openclawGatewayUrl: string;
- claudeAuthTokenHelperCommand: string;
}) {
const guide = PROVIDER_AUTH_GUIDES[provider];
- const hasClaudeAuthTokenHelper =
- provider === "claudeAgent" && claudeAuthTokenHelperCommand.trim().length > 0;
const badge = getAuthenticationBadgeCopy({
status,
provider,
openclawGatewayUrl,
- hasClaudeAuthTokenHelper,
});
const badgeClassName =
badge.tone === "success"
@@ -480,23 +462,13 @@ function AuthenticationStatusCard({
: "border-amber-500/25 bg-amber-500/10 text-amber-700 dark:text-amber-300";
const heading =
status !== null
- ? hasClaudeAuthTokenHelper &&
- status.provider === "claudeAgent" &&
- status.available &&
- status.authStatus === "unauthenticated"
- ? "Claude Code will source its auth token automatically"
- : getProviderStatusHeading(status)
+ ? getProviderStatusHeading(status)
: provider === "openclaw" && openclawGatewayUrl.trim().length > 0
? "OpenClaw gateway is configured locally"
: `${getProviderStatusLabelName(provider)} needs configuration`;
const description =
status !== null
- ? hasClaudeAuthTokenHelper &&
- status.provider === "claudeAgent" &&
- status.available &&
- status.authStatus === "unauthenticated"
- ? "The configured helper command will run locally at session start and print ANTHROPIC_AUTH_TOKEN to stdout."
- : getProviderStatusDescription(status)
+ ? getProviderStatusDescription(status)
: provider === "openclaw" && openclawGatewayUrl.trim().length > 0
? "OpenClaw is configured in local settings. Use Test Connection below to verify the gateway before starting a thread."
: guide.note;
@@ -832,7 +804,7 @@ function SettingsRouteView() {
const [openKeybindingsError, setOpenKeybindingsError] = useState(null);
const [openInstallProviders, setOpenInstallProviders] = useState>({
codex: Boolean(settings.codexBinaryPath || settings.codexHomePath),
- claudeAgent: Boolean(settings.claudeBinaryPath || settings.claudeAuthTokenHelperCommand),
+ claudeAgent: Boolean(settings.claudeBinaryPath),
gemini: false,
openclaw: Boolean(settings.openclawGatewayUrl || settings.openclawPassword),
copilot: Boolean(settings.copilotBinaryPath || settings.copilotConfigDir),
@@ -912,11 +884,6 @@ function SettingsRouteView() {
const codexBinaryPath = settings.codexBinaryPath;
const codexHomePath = settings.codexHomePath;
const claudeBinaryPath = settings.claudeBinaryPath;
- const claudeAuthTokenHelperCommand = settings.claudeAuthTokenHelperCommand;
- const selectedClaudeAuthTokenHelperPreset =
- CLAUDE_AUTH_TOKEN_HELPER_PRESETS.find(
- (preset) => preset.command === claudeAuthTokenHelperCommand,
- )?.label ?? "";
const savedOpenclawGatewayUrl = openclawGatewayConfigQuery.data?.gatewayUrl ?? "";
const savedOpenclawHasSharedSecret = openclawGatewayConfigQuery.data?.hasSharedSecret ?? false;
const effectiveOpenclawGatewayUrl = openclawGatewayDraft ?? savedOpenclawGatewayUrl;
@@ -926,7 +893,6 @@ function SettingsRouteView() {
const selectableProviders = getSelectableThreadProviders({
statuses: providerStatuses,
openclawGatewayUrl: effectiveOpenclawGatewayUrl,
- claudeAuthTokenHelperCommand: settings.claudeAuthTokenHelperCommand,
});
const canImportLegacyOpenclawSettings =
openclawGatewayConfigQuery.isSuccess &&
@@ -967,7 +933,6 @@ function SettingsRouteView() {
: savedCustomModelRows.slice(0, 5);
const isInstallSettingsDirty =
settings.claudeBinaryPath !== defaults.claudeBinaryPath ||
- settings.claudeAuthTokenHelperCommand !== defaults.claudeAuthTokenHelperCommand ||
settings.codexBinaryPath !== defaults.codexBinaryPath ||
settings.codexHomePath !== defaults.codexHomePath;
const isOpenClawSettingsDirty =
@@ -2652,7 +2617,6 @@ function SettingsRouteView() {
null
}
openclawGatewayUrl={effectiveOpenclawGatewayUrl}
- claudeAuthTokenHelperCommand={settings.claudeAuthTokenHelperCommand}
/>
))}
@@ -2669,7 +2633,6 @@ function SettingsRouteView() {
onClick={() => {
updateSettings({
claudeBinaryPath: defaults.claudeBinaryPath,
- claudeAuthTokenHelperCommand: defaults.claudeAuthTokenHelperCommand,
codexBinaryPath: defaults.codexBinaryPath,
codexHomePath: defaults.codexHomePath,
});
@@ -2693,9 +2656,7 @@ function SettingsRouteView() {
providerSettings.provider === "codex"
? settings.codexBinaryPath !== defaults.codexBinaryPath ||
settings.codexHomePath !== defaults.codexHomePath
- : settings.claudeBinaryPath !== defaults.claudeBinaryPath ||
- settings.claudeAuthTokenHelperCommand !==
- defaults.claudeAuthTokenHelperCommand;
+ : settings.claudeBinaryPath !== defaults.claudeBinaryPath;
const binaryPathValue =
providerSettings.binaryPathKey === "claudeBinaryPath"
? claudeBinaryPath
@@ -2769,85 +2730,6 @@ function SettingsRouteView() {
{providerSettings.binaryDescription}
-
- {providerSettings.provider === "claudeAgent" ? (
-
- ) : null}
-
{providerSettings.homePathKey ? (