@@ -11,7 +11,7 @@ describe("external CLI credential sync", () => {
|
||||
try {
|
||||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
// Create Claude CLI credentials
|
||||
// Create Claude Code CLI credentials
|
||||
const claudeDir = path.join(tempHome, ".claude");
|
||||
fs.mkdirSync(claudeDir, { recursive: true });
|
||||
const claudeCreds = {
|
||||
|
||||
@@ -6,13 +6,13 @@ import { withTempHome } from "../../test/helpers/temp-home.js";
|
||||
import { CLAUDE_CLI_PROFILE_ID, ensureAuthProfileStore } from "./auth-profiles.js";
|
||||
|
||||
describe("external CLI credential sync", () => {
|
||||
it("syncs Claude CLI OAuth credentials into anthropic:claude-cli", async () => {
|
||||
it("syncs Claude Code CLI OAuth credentials into anthropic:claude-cli", async () => {
|
||||
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-sync-"));
|
||||
try {
|
||||
// Create a temp home with Claude CLI credentials
|
||||
// Create a temp home with Claude Code CLI credentials
|
||||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
// Create Claude CLI credentials with refreshToken (OAuth)
|
||||
// Create Claude Code CLI credentials with refreshToken (OAuth)
|
||||
const claudeDir = path.join(tempHome, ".claude");
|
||||
fs.mkdirSync(claudeDir, { recursive: true });
|
||||
const claudeCreds = {
|
||||
@@ -59,12 +59,12 @@ describe("external CLI credential sync", () => {
|
||||
fs.rmSync(agentDir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
it("syncs Claude CLI credentials without refreshToken as token type", async () => {
|
||||
it("syncs Claude Code CLI credentials without refreshToken as token type", async () => {
|
||||
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-token-sync-"));
|
||||
try {
|
||||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
// Create Claude CLI credentials WITHOUT refreshToken (fallback to token type)
|
||||
// Create Claude Code CLI credentials WITHOUT refreshToken (fallback to token type)
|
||||
const claudeDir = path.join(tempHome, ".claude");
|
||||
fs.mkdirSync(claudeDir, { recursive: true });
|
||||
const claudeCreds = {
|
||||
|
||||
@@ -10,12 +10,12 @@ import {
|
||||
} from "./auth-profiles.js";
|
||||
|
||||
describe("external CLI credential sync", () => {
|
||||
it("upgrades token to oauth when Claude CLI gets refreshToken", async () => {
|
||||
it("upgrades token to oauth when Claude Code CLI gets refreshToken", async () => {
|
||||
const agentDir = fs.mkdtempSync(path.join(os.tmpdir(), "clawdbot-cli-upgrade-"));
|
||||
try {
|
||||
await withTempHome(
|
||||
async (tempHome) => {
|
||||
// Create Claude CLI credentials with refreshToken
|
||||
// Create Claude Code CLI credentials with refreshToken
|
||||
const claudeDir = path.join(tempHome, ".claude");
|
||||
fs.mkdirSync(claudeDir, { recursive: true });
|
||||
fs.writeFileSync(
|
||||
|
||||
@@ -53,7 +53,7 @@ function isExternalProfileFresh(cred: AuthProfileCredential | undefined, now: nu
|
||||
}
|
||||
|
||||
/**
|
||||
* Sync OAuth credentials from external CLI tools (Claude CLI, Codex CLI) into the store.
|
||||
* Sync OAuth credentials from external CLI tools (Claude Code CLI, Codex CLI) into the store.
|
||||
* This allows clawdbot to use the same credentials as these tools without requiring
|
||||
* separate authentication, and keeps credentials in sync when CLI tools refresh tokens.
|
||||
*
|
||||
@@ -66,7 +66,7 @@ export function syncExternalCliCredentials(
|
||||
let mutated = false;
|
||||
const now = Date.now();
|
||||
|
||||
// Sync from Claude CLI (supports both OAuth and Token credentials)
|
||||
// Sync from Claude Code CLI (supports both OAuth and Token credentials)
|
||||
const existingClaude = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
||||
const shouldSyncClaude =
|
||||
!existingClaude ||
|
||||
|
||||
@@ -66,7 +66,7 @@ async function refreshOAuthTokenWithLock(params: {
|
||||
};
|
||||
saveAuthProfileStore(store, params.agentDir);
|
||||
|
||||
// Sync refreshed credentials back to Claude CLI if this is the claude-cli profile
|
||||
// Sync refreshed credentials back to Claude Code CLI if this is the claude-cli profile
|
||||
// This ensures Claude Code continues to work after ClawdBot refreshes the token
|
||||
if (params.profileId === CLAUDE_CLI_PROFILE_ID && cred.provider === "anthropic") {
|
||||
writeClaudeCliCredentials(result.newCredentials);
|
||||
|
||||
@@ -111,7 +111,7 @@ describe("cli credentials", () => {
|
||||
expect(updated.claudeAiOauth?.expiresAt).toBeTypeOf("number");
|
||||
});
|
||||
|
||||
it("caches Claude CLI credentials within the TTL window", async () => {
|
||||
it("caches Claude Code CLI credentials within the TTL window", async () => {
|
||||
execSyncMock.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
claudeAiOauth: {
|
||||
@@ -142,7 +142,7 @@ describe("cli credentials", () => {
|
||||
expect(execSyncMock).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("refreshes Claude CLI credentials after the TTL window", async () => {
|
||||
it("refreshes Claude Code CLI credentials after the TTL window", async () => {
|
||||
execSyncMock.mockImplementation(() =>
|
||||
JSON.stringify({
|
||||
claudeAiOauth: {
|
||||
|
||||
@@ -15,7 +15,7 @@ describe("buildAuthChoiceOptions", () => {
|
||||
|
||||
expect(options.find((opt) => opt.value === "github-copilot")).toBeDefined();
|
||||
});
|
||||
it("includes Claude CLI option on macOS even when missing", () => {
|
||||
it("includes Claude Code CLI option on macOS even when missing", () => {
|
||||
const store: AuthProfileStore = { version: 1, profiles: {} };
|
||||
const options = buildAuthChoiceOptions({
|
||||
store,
|
||||
@@ -29,7 +29,7 @@ describe("buildAuthChoiceOptions", () => {
|
||||
expect(claudeCli?.hint).toBe("requires Keychain access");
|
||||
});
|
||||
|
||||
it("skips missing Claude CLI option off macOS", () => {
|
||||
it("skips missing Claude Code CLI option off macOS", () => {
|
||||
const store: AuthProfileStore = { version: 1, profiles: {} };
|
||||
const options = buildAuthChoiceOptions({
|
||||
store,
|
||||
@@ -41,7 +41,7 @@ describe("buildAuthChoiceOptions", () => {
|
||||
expect(options.find((opt) => opt.value === "claude-cli")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("uses token hint when Claude CLI credentials exist", () => {
|
||||
it("uses token hint when Claude Code CLI credentials exist", () => {
|
||||
const store: AuthProfileStore = {
|
||||
version: 1,
|
||||
profiles: {
|
||||
|
||||
@@ -28,10 +28,10 @@ export async function applyAuthChoiceAnthropic(
|
||||
'Choose "Always Allow" so the launchd gateway can start without prompts.',
|
||||
'If you choose "Allow" or "Deny", each restart will block on a Keychain alert.',
|
||||
].join("\n"),
|
||||
"Claude CLI Keychain",
|
||||
"Claude Code CLI Keychain",
|
||||
);
|
||||
const proceed = await params.prompter.confirm({
|
||||
message: "Check Keychain for Claude CLI credentials now?",
|
||||
message: "Check Keychain for Claude Code CLI credentials now?",
|
||||
initialValue: true,
|
||||
});
|
||||
if (!proceed) return { config: nextConfig };
|
||||
@@ -74,9 +74,9 @@ export async function applyAuthChoiceAnthropic(
|
||||
if (!refreshed.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||
await params.prompter.note(
|
||||
process.platform === "darwin"
|
||||
? 'No Claude CLI credentials found in Keychain ("Claude Code-credentials") or ~/.claude/.credentials.json.'
|
||||
: "No Claude CLI credentials found at ~/.claude/.credentials.json.",
|
||||
"Claude CLI OAuth",
|
||||
? 'No Claude Code CLI credentials found in Keychain ("Claude Code-credentials") or ~/.claude/.credentials.json.'
|
||||
: "No Claude Code CLI credentials found at ~/.claude/.credentials.json.",
|
||||
"Claude Code CLI OAuth",
|
||||
);
|
||||
return { config: nextConfig };
|
||||
}
|
||||
@@ -137,7 +137,7 @@ export async function applyAuthChoiceAnthropic(
|
||||
});
|
||||
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||
await params.prompter.note(
|
||||
`No Claude CLI credentials found after setup-token. Expected ${CLAUDE_CLI_PROFILE_ID}.`,
|
||||
`No Claude Code CLI credentials found after setup-token. Expected ${CLAUDE_CLI_PROFILE_ID}.`,
|
||||
"Anthropic setup-token",
|
||||
);
|
||||
return { config: nextConfig };
|
||||
|
||||
@@ -83,7 +83,7 @@ export async function modelsAuthSetupTokenCommand(
|
||||
const synced = store.profiles[CLAUDE_CLI_PROFILE_ID];
|
||||
if (!synced) {
|
||||
throw new Error(
|
||||
`No Claude CLI credentials found after setup-token. Expected auth profile ${CLAUDE_CLI_PROFILE_ID}.`,
|
||||
`No Claude Code CLI credentials found after setup-token. Expected auth profile ${CLAUDE_CLI_PROFILE_ID}.`,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -261,8 +261,8 @@ export async function applyNonInteractiveAuthChoice(params: {
|
||||
if (!store.profiles[CLAUDE_CLI_PROFILE_ID]) {
|
||||
runtime.error(
|
||||
process.platform === "darwin"
|
||||
? 'No Claude CLI credentials found. Run interactive onboarding to approve Keychain access for "Claude Code-credentials".'
|
||||
: "No Claude CLI credentials found at ~/.claude/.credentials.json",
|
||||
? 'No Claude Code CLI credentials found. Run interactive onboarding to approve Keychain access for "Claude Code-credentials".'
|
||||
: "No Claude Code CLI credentials found at ~/.claude/.credentials.json",
|
||||
);
|
||||
runtime.exit(1);
|
||||
return null;
|
||||
|
||||
@@ -111,7 +111,7 @@ async function resolveOAuthToken(params: {
|
||||
provider: params.provider,
|
||||
});
|
||||
|
||||
// Claude CLI creds are the only Anthropic tokens that reliably include the
|
||||
// Claude Code CLI creds are the only Anthropic tokens that reliably include the
|
||||
// `user:profile` scope required for the OAuth usage endpoint.
|
||||
const candidates = params.provider === "anthropic" ? [CLAUDE_CLI_PROFILE_ID, ...order] : order;
|
||||
const deduped: string[] = [];
|
||||
|
||||
@@ -126,7 +126,7 @@ export async function fetchClaudeUsage(
|
||||
// ignore parse errors
|
||||
}
|
||||
|
||||
// Claude CLI setup-token yields tokens that can be used for inference, but may not
|
||||
// Claude Code CLI setup-token yields tokens that can be used for inference, but may not
|
||||
// include user:profile scope required by the OAuth usage endpoint. When a claude.ai
|
||||
// browser sessionKey is available, fall back to the web API.
|
||||
if (res.status === 403 && message?.includes("scope requirement user:profile")) {
|
||||
|
||||
Reference in New Issue
Block a user