feat(auth): sync OAuth from Claude/Codex CLIs
Add source profiles anthropic:claude-cli and openai-codex:codex-cli; surface them in onboarding/configure. Co-authored-by: pepicrft <pepicrft@users.noreply.github.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import {
|
||||
resolveAgentDir,
|
||||
resolveAgentWorkspaceDir,
|
||||
} from "../agents/agent-scope.js";
|
||||
import { ensureAuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
@@ -21,6 +22,7 @@ import { resolveDefaultWhatsAppAccountId } from "../web/accounts.js";
|
||||
import { createClackPrompter } from "../wizard/clack-prompter.js";
|
||||
import { WizardCancelledError } from "../wizard/prompts.js";
|
||||
import { applyAuthChoice, warnIfModelConfigLooksOff } from "./auth-choice.js";
|
||||
import { buildAuthChoiceOptions } from "./auth-choice-options.js";
|
||||
import { ensureWorkspaceAndSessions, moveToTrash } from "./onboard-helpers.js";
|
||||
import { setupProviders } from "./onboard-providers.js";
|
||||
import type { AuthChoice, ProviderChoice } from "./onboard-types.js";
|
||||
@@ -458,19 +460,13 @@ export async function agentsAddCommand(
|
||||
initialValue: false,
|
||||
});
|
||||
if (wantsAuth) {
|
||||
const authStore = ensureAuthProfileStore(agentDir);
|
||||
const authChoice = (await prompter.select({
|
||||
message: "Model/auth choice",
|
||||
options: [
|
||||
{ value: "oauth", label: "Anthropic OAuth (Claude Pro/Max)" },
|
||||
{ value: "openai-codex", label: "OpenAI Codex (ChatGPT OAuth)" },
|
||||
{
|
||||
value: "antigravity",
|
||||
label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)",
|
||||
},
|
||||
{ value: "apiKey", label: "Anthropic API key" },
|
||||
{ value: "minimax", label: "Minimax M2.1 (LM Studio)" },
|
||||
{ value: "skip", label: "Skip for now" },
|
||||
],
|
||||
options: buildAuthChoiceOptions({
|
||||
store: authStore,
|
||||
includeSkip: true,
|
||||
}),
|
||||
})) as AuthChoice;
|
||||
|
||||
const authResult = await applyAuthChoice({
|
||||
|
||||
49
src/commands/auth-choice-options.ts
Normal file
49
src/commands/auth-choice-options.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import type { AuthProfileStore } from "../agents/auth-profiles.js";
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
CODEX_CLI_PROFILE_ID,
|
||||
} from "../agents/auth-profiles.js";
|
||||
import type { AuthChoice } from "./onboard-types.js";
|
||||
|
||||
export type AuthChoiceOption = { value: AuthChoice; label: string };
|
||||
|
||||
export function buildAuthChoiceOptions(params: {
|
||||
store: AuthProfileStore;
|
||||
includeSkip: boolean;
|
||||
}): AuthChoiceOption[] {
|
||||
const options: AuthChoiceOption[] = [];
|
||||
|
||||
const claudeCli = params.store.profiles[CLAUDE_CLI_PROFILE_ID];
|
||||
if (claudeCli?.type === "oauth") {
|
||||
options.push({
|
||||
value: "claude-cli",
|
||||
label: "Anthropic OAuth (Claude CLI)",
|
||||
});
|
||||
}
|
||||
|
||||
options.push({ value: "oauth", label: "Anthropic OAuth (Claude Pro/Max)" });
|
||||
|
||||
const codexCli = params.store.profiles[CODEX_CLI_PROFILE_ID];
|
||||
if (codexCli?.type === "oauth") {
|
||||
options.push({
|
||||
value: "codex-cli",
|
||||
label: "OpenAI Codex OAuth (Codex CLI)",
|
||||
});
|
||||
}
|
||||
|
||||
options.push({
|
||||
value: "openai-codex",
|
||||
label: "OpenAI Codex (ChatGPT OAuth)",
|
||||
});
|
||||
options.push({
|
||||
value: "antigravity",
|
||||
label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)",
|
||||
});
|
||||
options.push({ value: "apiKey", label: "Anthropic API key" });
|
||||
options.push({ value: "minimax", label: "Minimax M2.1 (LM Studio)" });
|
||||
if (params.includeSkip) {
|
||||
options.push({ value: "skip", label: "Skip for now" });
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
@@ -6,6 +6,8 @@ import {
|
||||
} from "@mariozechner/pi-ai";
|
||||
import { resolveAgentConfig } from "../agents/agent-scope.js";
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
CODEX_CLI_PROFILE_ID,
|
||||
ensureAuthProfileStore,
|
||||
listProfilesForProvider,
|
||||
} from "../agents/auth-profiles.js";
|
||||
@@ -165,6 +167,20 @@ export async function applyAuthChoice(params: {
|
||||
"OAuth help",
|
||||
);
|
||||
}
|
||||
} else if (params.authChoice === "claude-cli") {
|
||||
const store = ensureAuthProfileStore(params.agentDir);
|
||||
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||
await params.prompter.note(
|
||||
"No Claude CLI credentials found at ~/.claude/.credentials.json.",
|
||||
"Claude CLI OAuth",
|
||||
);
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: CLAUDE_CLI_PROFILE_ID,
|
||||
provider: "anthropic",
|
||||
mode: "oauth",
|
||||
});
|
||||
} else if (params.authChoice === "openai-codex") {
|
||||
const isRemote = isRemoteEnvironment();
|
||||
await params.prompter.note(
|
||||
@@ -250,6 +266,33 @@ export async function applyAuthChoice(params: {
|
||||
"OAuth help",
|
||||
);
|
||||
}
|
||||
} else if (params.authChoice === "codex-cli") {
|
||||
const store = ensureAuthProfileStore(params.agentDir);
|
||||
if (!store.profiles[CODEX_CLI_PROFILE_ID]) {
|
||||
await params.prompter.note(
|
||||
"No Codex CLI credentials found at ~/.codex/auth.json.",
|
||||
"Codex CLI OAuth",
|
||||
);
|
||||
return { config: nextConfig, agentModelOverride };
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: CODEX_CLI_PROFILE_ID,
|
||||
provider: "openai-codex",
|
||||
mode: "oauth",
|
||||
});
|
||||
if (params.setDefaultModel) {
|
||||
const applied = applyOpenAICodexModelDefault(nextConfig);
|
||||
nextConfig = applied.next;
|
||||
if (applied.changed) {
|
||||
await params.prompter.note(
|
||||
`Default model set to ${OPENAI_CODEX_DEFAULT_MODEL}`,
|
||||
"Model configured",
|
||||
);
|
||||
}
|
||||
} else {
|
||||
agentModelOverride = OPENAI_CODEX_DEFAULT_MODEL;
|
||||
await noteAgentModel(OPENAI_CODEX_DEFAULT_MODEL);
|
||||
}
|
||||
} else if (params.authChoice === "antigravity") {
|
||||
const isRemote = isRemoteEnvironment();
|
||||
await params.prompter.note(
|
||||
|
||||
@@ -16,6 +16,11 @@ import {
|
||||
type OAuthCredentials,
|
||||
type OAuthProvider,
|
||||
} from "@mariozechner/pi-ai";
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
CODEX_CLI_PROFILE_ID,
|
||||
ensureAuthProfileStore,
|
||||
} from "../agents/auth-profiles.js";
|
||||
import type { ClawdbotConfig } from "../config/config.js";
|
||||
import {
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
@@ -35,6 +40,7 @@ import {
|
||||
isRemoteEnvironment,
|
||||
loginAntigravityVpsAware,
|
||||
} from "./antigravity-oauth.js";
|
||||
import { buildAuthChoiceOptions } from "./auth-choice-options.js";
|
||||
import {
|
||||
DEFAULT_GATEWAY_DAEMON_RUNTIME,
|
||||
GATEWAY_DAEMON_RUNTIME_OPTIONS,
|
||||
@@ -254,20 +260,21 @@ async function promptAuthConfig(
|
||||
const authChoice = guardCancel(
|
||||
await select({
|
||||
message: "Model/auth choice",
|
||||
options: [
|
||||
{ value: "oauth", label: "Anthropic OAuth (Claude Pro/Max)" },
|
||||
{ value: "openai-codex", label: "OpenAI Codex (ChatGPT OAuth)" },
|
||||
{
|
||||
value: "antigravity",
|
||||
label: "Google Antigravity (Claude Opus 4.5, Gemini 3, etc.)",
|
||||
},
|
||||
{ value: "apiKey", label: "Anthropic API key" },
|
||||
{ value: "minimax", label: "Minimax M2.1 (LM Studio)" },
|
||||
{ value: "skip", label: "Skip for now" },
|
||||
],
|
||||
options: buildAuthChoiceOptions({
|
||||
store: ensureAuthProfileStore(),
|
||||
includeSkip: true,
|
||||
}),
|
||||
}),
|
||||
runtime,
|
||||
) as "oauth" | "openai-codex" | "antigravity" | "apiKey" | "minimax" | "skip";
|
||||
) as
|
||||
| "oauth"
|
||||
| "claude-cli"
|
||||
| "openai-codex"
|
||||
| "codex-cli"
|
||||
| "antigravity"
|
||||
| "apiKey"
|
||||
| "minimax"
|
||||
| "skip";
|
||||
|
||||
let next = cfg;
|
||||
|
||||
@@ -312,6 +319,12 @@ async function promptAuthConfig(
|
||||
runtime.error(String(err));
|
||||
note("Trouble with OAuth? See https://docs.clawd.bot/start/faq", "OAuth");
|
||||
}
|
||||
} else if (authChoice === "claude-cli") {
|
||||
next = applyAuthProfileConfig(next, {
|
||||
profileId: CLAUDE_CLI_PROFILE_ID,
|
||||
provider: "anthropic",
|
||||
mode: "oauth",
|
||||
});
|
||||
} else if (authChoice === "openai-codex") {
|
||||
const isRemote = isRemoteEnvironment();
|
||||
note(
|
||||
@@ -386,6 +399,20 @@ async function promptAuthConfig(
|
||||
runtime.error(String(err));
|
||||
note("Trouble with OAuth? See https://docs.clawd.bot/start/faq", "OAuth");
|
||||
}
|
||||
} else if (authChoice === "codex-cli") {
|
||||
next = applyAuthProfileConfig(next, {
|
||||
profileId: CODEX_CLI_PROFILE_ID,
|
||||
provider: "openai-codex",
|
||||
mode: "oauth",
|
||||
});
|
||||
const applied = applyOpenAICodexModelDefault(next);
|
||||
next = applied.next;
|
||||
if (applied.changed) {
|
||||
note(
|
||||
`Default model set to ${OPENAI_CODEX_DEFAULT_MODEL}`,
|
||||
"Model configured",
|
||||
);
|
||||
}
|
||||
} else if (authChoice === "antigravity") {
|
||||
const isRemote = isRemoteEnvironment();
|
||||
note(
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import path from "node:path";
|
||||
|
||||
import {
|
||||
CLAUDE_CLI_PROFILE_ID,
|
||||
CODEX_CLI_PROFILE_ID,
|
||||
ensureAuthProfileStore,
|
||||
} from "../agents/auth-profiles.js";
|
||||
import {
|
||||
type ClawdbotConfig,
|
||||
CONFIG_PATH_CLAWDBOT,
|
||||
@@ -30,6 +34,7 @@ import {
|
||||
randomToken,
|
||||
} from "./onboard-helpers.js";
|
||||
import type { AuthChoice, OnboardOptions } from "./onboard-types.js";
|
||||
import { applyOpenAICodexModelDefault } from "./openai-codex-model-default.js";
|
||||
import { ensureSystemdUserLingerNonInteractive } from "./systemd-linger.js";
|
||||
|
||||
export async function runNonInteractiveOnboarding(
|
||||
@@ -112,6 +117,33 @@ export async function runNonInteractiveOnboarding(
|
||||
provider: "anthropic",
|
||||
mode: "api_key",
|
||||
});
|
||||
} else if (authChoice === "claude-cli") {
|
||||
const store = ensureAuthProfileStore();
|
||||
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||
runtime.error(
|
||||
"No Claude CLI credentials found at ~/.claude/.credentials.json",
|
||||
);
|
||||
runtime.exit(1);
|
||||
return;
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: CLAUDE_CLI_PROFILE_ID,
|
||||
provider: "anthropic",
|
||||
mode: "oauth",
|
||||
});
|
||||
} else if (authChoice === "codex-cli") {
|
||||
const store = ensureAuthProfileStore();
|
||||
if (!store.profiles[CODEX_CLI_PROFILE_ID]) {
|
||||
runtime.error("No Codex CLI credentials found at ~/.codex/auth.json");
|
||||
runtime.exit(1);
|
||||
return;
|
||||
}
|
||||
nextConfig = applyAuthProfileConfig(nextConfig, {
|
||||
profileId: CODEX_CLI_PROFILE_ID,
|
||||
provider: "openai-codex",
|
||||
mode: "oauth",
|
||||
});
|
||||
nextConfig = applyOpenAICodexModelDefault(nextConfig).next;
|
||||
} else if (authChoice === "minimax") {
|
||||
nextConfig = applyMinimaxConfig(nextConfig);
|
||||
} else if (
|
||||
|
||||
@@ -3,7 +3,9 @@ import type { GatewayDaemonRuntime } from "./daemon-runtime.js";
|
||||
export type OnboardMode = "local" | "remote";
|
||||
export type AuthChoice =
|
||||
| "oauth"
|
||||
| "claude-cli"
|
||||
| "openai-codex"
|
||||
| "codex-cli"
|
||||
| "antigravity"
|
||||
| "apiKey"
|
||||
| "minimax"
|
||||
|
||||
Reference in New Issue
Block a user