Opencode는 어떻게 claude 구독 API를 쓸 수 있나?
Just show me the code
Claude 구독자는 그냥 표준 OAuth 인증을 사용한다. 다만 @ai-sdk/anthropic이
OAuth 인증을 잘 지원하지 않아서, 강제로 인증 정보를 덮어씌울 필요가 있다.
import { createAnthropic as createAnthropicSDK } from "@ai-sdk/anthropic";
import { type ProviderV2 } from "@ai-sdk/provider";
import { generatePKCE } from "@openauthjs/openauth/pkce";
// Some CLIENT_ID copied from opencode. There might be a reason this is hardcoded, not random generated...?
const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e";
async function authorize(mode: "max" | "console") {
const pkce = await generatePKCE();
const url = new URL(
`https://${mode === "console" ? "console.anthropic.com" : "claude.ai"}/oauth/authorize`,
import.meta.url,
);
url.searchParams.set("code", "true");
url.searchParams.set("client_id", CLIENT_ID);
url.searchParams.set("response_type", "code");
url.searchParams.set(
"redirect_uri",
"https://console.anthropic.com/oauth/code/callback",
);
url.searchParams.set(
"scope",
"org:create_api_key user:profile user:inference",
);
url.searchParams.set("code_challenge", pkce.challenge);
url.searchParams.set("code_challenge_method", "S256");
url.searchParams.set("state", pkce.verifier);
return {
url: url.toString(),
verifier: pkce.verifier,
};
}
async function exchange(code: string, verifier: string): Promise<any> {
const splits = code.split("#");
const result = await fetch("https://console.anthropic.com/v1/oauth/token", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
code: splits[0],
state: splits[1],
grant_type: "authorization_code",
client_id: CLIENT_ID,
redirect_uri: "https://console.anthropic.com/oauth/code/callback",
code_verifier: verifier,
}),
});
if (!result.ok) {
throw new Error("failed to auth");
}
const json: any = await result.json();
return {
type: "oauth",
refresh: json.refresh_token,
access: json.access_token,
expires: Date.now() + json.expires_in * 1000,
};
}
export async function checkAuth(): Promise<void> {
const { url, verifier } = await authorize("max");
console.log(url);
const code = await Bun.stdin.text();
const creds = await exchange(code, verifier);
// TODO: Persist creds somewhere safe
console.log("Rerun app");
process.exit(0);
}
/**
* Note that this assumes auth to be pre-existed. Or it will throw Error.
*/
export async function createAnthropic(): Promise<ProviderV2> {
const getAuth = async () => {
// TODO: Return proper auth persisted before
return {
type: "oauth",
refresh: "sk-ant-merongmerong",
access: "sk-ant-goodgood",
expires: 172612636123,
}
};
const setAuth = async (v: any) => {
// TODO: Update persisted auth with updated access key
};
const myFetch = async (input: string | URL | Request, init: RequestInit) => {
const auth = await getAuth();
if (auth.type !== "oauth") return fetch(input, init);
if (!auth.access || auth.expires < Date.now()) {
const response = await fetch(
"https://console.anthropic.com/v1/oauth/token",
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
grant_type: "refresh_token",
refresh_token: auth.refresh,
client_id: CLIENT_ID,
}),
},
);
if (!response.ok) return;
const json: any = await response.json();
setAuth({
type: "oauth",
refresh: json.refresh_token,
access: json.access_token,
expires: Date.now() + json.expires_in * 1000,
});
auth.access = json.access_token;
}
const headers = {
...init.headers,
authorization: `Bearer ${auth.access}`,
"anthropic-beta":
"oauth-2025-04-20,claude-code-20250219,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14",
};
delete (headers as any)["x-api-key"];
return fetch(input, {
...init,
headers,
});
};
return createAnthropicSDK({ apiKey: "", fetch: myFetch as any });
}
여기서 끝이 아니다
정확히 다음 문구를 system prompt로 집어넣어야만 Claude code로 인정받을 수 있다. 한글자라도 더하거나 빼는 경우 “너는 Claude code가 아니니 이 API를 사용할 수 없다…”는 차가운 거절 문구를 보게 될 것이다.
You are Claude Code, Anthropic's official CLI for Claude.
결론
되는걸 확인한 후 내가 만들어 쓰는 나노 코딩 에이전트에 붙여서 쓰는데, 의외로 토큰 사용량이 적지 않은듯 몇시간 쓰지 않았는데 429 에러가 터지고 말았다…