Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/web/src/appSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,20 @@ describe("getProviderStartOptions", () => {
},
});
});

it("does not emit Claude start options without a Claude binary path", () => {
expect(
getProviderStartOptions({
claudeBinaryPath: "",
codexBinaryPath: "",
codexHomePath: "",
copilotBinaryPath: "",
copilotConfigDir: "",
openclawGatewayUrl: "",
openclawPassword: "",
}),
).toBeUndefined();
});
});

describe("resolveBrowserPreviewStartPageUrl", () => {
Expand Down
14 changes: 3 additions & 11 deletions apps/web/src/appSettings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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(() => "")),
Expand Down Expand Up @@ -392,9 +389,7 @@ export function getProviderStartOptions(
| "codexHomePath"
| "openclawGatewayUrl"
| "openclawPassword"
> & {
claudeAuthTokenHelperCommand?: AppSettings["claudeAuthTokenHelperCommand"];
},
>,
): ProviderStartOptions | undefined {
const providerOptions: ProviderStartOptions = {
...(settings.codexBinaryPath || settings.codexHomePath
Expand All @@ -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,
},
}
: {}),
Expand Down
1 change: 0 additions & 1 deletion apps/web/src/components/settings/SettingsRouteContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@ export function SettingsRouteContextProvider({ children }: { children: ReactNode
currentGitTextGenerationModel !== defaultGitTextGenerationModel;
const isInstallSettingsDirty =
settings.claudeBinaryPath !== defaults.claudeBinaryPath ||
settings.claudeAuthTokenHelperCommand !== defaults.claudeAuthTokenHelperCommand ||
settings.copilotBinaryPath !== defaults.copilotBinaryPath ||
settings.copilotConfigDir !== defaults.copilotConfigDir ||
settings.codexBinaryPath !== defaults.codexBinaryPath ||
Expand Down
23 changes: 0 additions & 23 deletions apps/web/src/lib/claudeAuthTokenHelperPresets.ts

This file was deleted.

8 changes: 3 additions & 5 deletions apps/web/src/lib/providerAvailability.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ describe("providerAvailability", () => {
).toBe(false);
});

it("blocks claudeAgent when status is error even if claudeAuthTokenHelperCommand is set and available is true", () => {
it("blocks claudeAgent when status is error", () => {
expect(
isProviderReadyForThreadSelection({
provider: "claudeAgent",
Expand All @@ -60,12 +60,11 @@ describe("providerAvailability", () => {
authStatus: "unauthenticated",
}),
],
claudeAuthTokenHelperCommand: "my-token-helper",
}),
).toBe(false);
});

it("allows claudeAgent with claudeAuthTokenHelperCommand when status is ready but auth is unauthenticated", () => {
it("blocks unauthenticated claudeAgent even when the status is otherwise ready", () => {
expect(
isProviderReadyForThreadSelection({
provider: "claudeAgent",
Expand All @@ -76,9 +75,8 @@ describe("providerAvailability", () => {
authStatus: "unauthenticated",
}),
],
claudeAuthTokenHelperCommand: "my-token-helper",
}),
).toBe(true);
).toBe(false);
});

it("treats configured OpenClaw as selectable even when server auth state is unknown", () => {
Expand Down
13 changes: 0 additions & 13 deletions apps/web/src/lib/providerAvailability.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ export function isProviderReadyForThreadSelection(input: {
provider: ProviderKind;
statuses: ReadonlyArray<ServerProviderStatus>;
openclawGatewayUrl?: string | null | undefined;
claudeAuthTokenHelperCommand?: string | null | undefined;
}): boolean {
const status = getProviderStatusByKind(input.statuses, input.provider);

Expand All @@ -46,31 +45,19 @@ export function isProviderReadyForThreadSelection(input: {
return false;
}

if (
input.provider === "claudeAgent" &&
(input.claudeAuthTokenHelperCommand ?? "").trim().length > 0 &&
status.available &&
status.status === "ready" &&
(status.authStatus ?? status.auth?.status) === "unauthenticated"
) {
return true;
}

const authStatus = status.authStatus ?? status.auth?.status;
return Boolean(status.available && status.status === "ready" && authStatus !== "unauthenticated");
}

export function getSelectableThreadProviders(input: {
statuses: ReadonlyArray<ServerProviderStatus>;
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,
}),
);
}
Expand Down
91 changes: 2 additions & 89 deletions apps/web/src/routes/_chat.settings.index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ import {
getProviderStatusDescription,
getProviderStatusHeading,
} from "../components/chat/providerStatusPresentation";
import { CLAUDE_AUTH_TOKEN_HELPER_PRESETS } from "../lib/claudeAuthTokenHelperPresets";
import {
INSTALL_PROVIDER_SETTINGS,
PROVIDER_AUTH_GUIDES,
Expand Down Expand Up @@ -236,7 +235,6 @@ function getAuthenticationBadgeCopy(input: {
status: ServerProviderStatus | null;
provider: ProviderKind;
openclawGatewayUrl: string;
claudeAuthTokenHelperCommand: string;
}): {
tone: "success" | "warning" | "error";
label: string;
Expand All @@ -246,7 +244,6 @@ function getAuthenticationBadgeCopy(input: {
provider: input.provider,
statuses: input.status ? [input.status] : [],
openclawGatewayUrl: input.openclawGatewayUrl,
claudeAuthTokenHelperCommand: input.claudeAuthTokenHelperCommand,
})
) {
return { tone: "success", label: "Available in thread picker" };
Expand All @@ -271,19 +268,16 @@ function AuthenticationStatusCard({
provider,
status,
openclawGatewayUrl,
claudeAuthTokenHelperCommand,
}: {
provider: ProviderKind;
status: ServerProviderStatus | null;
openclawGatewayUrl: string;
claudeAuthTokenHelperCommand: string;
}) {
const guide = PROVIDER_AUTH_GUIDES[provider];
const badge = getAuthenticationBadgeCopy({
status,
provider,
openclawGatewayUrl,
claudeAuthTokenHelperCommand,
});
const badgeClassName =
badge.tone === "success"
Expand Down Expand Up @@ -419,7 +413,7 @@ function SettingsRouteView() {
const [openKeybindingsError, setOpenKeybindingsError] = useState<string | null>(null);
const [openInstallProviders, setOpenInstallProviders] = useState<Record<ProviderKind, boolean>>({
codex: Boolean(settings.codexBinaryPath || settings.codexHomePath),
claudeAgent: Boolean(settings.claudeBinaryPath || settings.claudeAuthTokenHelperCommand),
claudeAgent: Boolean(settings.claudeBinaryPath),
gemini: false,
copilot: Boolean(settings.copilotBinaryPath || settings.copilotConfigDir),
openclaw: Boolean(settings.openclawGatewayUrl || settings.openclawPassword),
Expand Down Expand Up @@ -474,18 +468,12 @@ 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 keybindingsConfigPath = serverConfigQuery.data?.keybindingsConfigPath ?? null;
const availableEditors = serverConfigQuery.data?.availableEditors;
const providerStatuses = serverConfigQuery.data?.providers ?? [];
const selectableProviders = getSelectableThreadProviders({
statuses: providerStatuses,
openclawGatewayUrl: settings.openclawGatewayUrl,
claudeAuthTokenHelperCommand,
});

const gitTextGenerationModelOptions = getAppModelOptions(
Expand Down Expand Up @@ -527,7 +515,6 @@ function SettingsRouteView() {
: savedCustomModelRows.slice(0, 5);
const isInstallSettingsDirty =
settings.claudeBinaryPath !== defaults.claudeBinaryPath ||
settings.claudeAuthTokenHelperCommand !== defaults.claudeAuthTokenHelperCommand ||
settings.copilotBinaryPath !== defaults.copilotBinaryPath ||
settings.copilotConfigDir !== defaults.copilotConfigDir ||
settings.codexBinaryPath !== defaults.codexBinaryPath ||
Expand Down Expand Up @@ -1361,7 +1348,6 @@ function SettingsRouteView() {
provider={provider}
status={providerStatuses.find((status) => status.provider === provider) ?? null}
openclawGatewayUrl={settings.openclawGatewayUrl}
claudeAuthTokenHelperCommand={claudeAuthTokenHelperCommand}
/>
))}
</div>
Expand All @@ -1378,7 +1364,6 @@ function SettingsRouteView() {
onClick={() => {
updateSettings({
claudeBinaryPath: defaults.claudeBinaryPath,
claudeAuthTokenHelperCommand: defaults.claudeAuthTokenHelperCommand,
codexBinaryPath: defaults.codexBinaryPath,
codexHomePath: defaults.codexHomePath,
copilotBinaryPath: defaults.copilotBinaryPath,
Expand All @@ -1405,9 +1390,7 @@ function SettingsRouteView() {
? settings.codexBinaryPath !== defaults.codexBinaryPath ||
settings.codexHomePath !== defaults.codexHomePath
: providerSettings.provider === "claudeAgent"
? settings.claudeBinaryPath !== defaults.claudeBinaryPath ||
settings.claudeAuthTokenHelperCommand !==
defaults.claudeAuthTokenHelperCommand
? settings.claudeBinaryPath !== defaults.claudeBinaryPath
: settings.copilotBinaryPath !== defaults.copilotBinaryPath ||
settings.copilotConfigDir !== defaults.copilotConfigDir;
const binaryPathValue =
Expand Down Expand Up @@ -1494,76 +1477,6 @@ function SettingsRouteView() {
</span>
</label>

{providerSettings.provider === "claudeAgent" ? (
<label
htmlFor="provider-install-claude-auth-token-helper"
className="block"
>
<span className="block text-xs font-medium text-foreground">
Claude auth token helper command
</span>
<div className="mt-1 flex flex-col gap-2 sm:flex-row">
<Input
id="provider-install-claude-auth-token-helper"
className="sm:flex-1"
value={claudeAuthTokenHelperCommand}
onChange={(event) =>
updateSettings({
claudeAuthTokenHelperCommand: event.target.value,
})
}
placeholder="op read op://shared/anthropic/token --no-newline"
spellCheck={false}
/>
<Select
value={selectedClaudeAuthTokenHelperPreset}
onValueChange={(value) => {
const preset =
CLAUDE_AUTH_TOKEN_HELPER_PRESETS.find(
(entry) => entry.label === value,
) ?? null;
if (!preset) return;
updateSettings({
claudeAuthTokenHelperCommand: preset.command,
});
}}
>
<SelectTrigger
className="w-full sm:w-44"
aria-label="Claude auth token helper preset"
>
<SelectValue placeholder="Secret manager" />
</SelectTrigger>
<SelectPopup align="end" alignItemWithTrigger={false}>
{CLAUDE_AUTH_TOKEN_HELPER_PRESETS.map((preset) => (
<SelectItem
hideIndicator
key={preset.label}
value={preset.label}
title={preset.description}
>
<div className="flex min-w-0 flex-col">
<span className="truncate">{preset.label}</span>
<span className="truncate text-[11px] text-muted-foreground">
{preset.command}
</span>
</div>
</SelectItem>
))}
</SelectPopup>
</Select>
</div>
<span className="mt-1 block text-xs text-muted-foreground">
Runs locally before Claude sessions start. The command should
print the token to stdout so OK Code can set
ANTHROPIC_AUTH_TOKEN automatically.
</span>
<span className="mt-1 block text-[11px] text-muted-foreground">
Choose a secret manager to prefill the command.
</span>
</label>
) : null}

{homePathKey ? (
<label
htmlFor={`provider-install-${homePathKey}`}
Expand Down
1 change: 0 additions & 1 deletion packages/contracts/src/orchestration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,6 @@ export const CodexProviderStartOptions = Schema.Struct({
export const ClaudeProviderStartOptions = Schema.Struct({
binaryPath: Schema.optional(TrimmedNonEmptyString),
permissionMode: Schema.optional(TrimmedNonEmptyString),
authTokenHelperCommand: Schema.optional(TrimmedNonEmptyString),
maxThinkingTokens: Schema.optional(NonNegativeInt),
});

Expand Down
Loading