mirror of
https://github.com/sst/opencode.git
synced 2025-08-31 10:17:26 +00:00
support anthropic console login flow
This commit is contained in:
parent
535d79b64c
commit
2cdb37c32b
2 changed files with 74 additions and 14 deletions
|
@ -4,9 +4,13 @@ import { Auth } from "./index"
|
|||
export namespace AuthAnthropic {
|
||||
const CLIENT_ID = "9d1c250a-e61b-44d9-88ed-5944d1962f5e"
|
||||
|
||||
export async function authorize() {
|
||||
export async function authorize(mode: "max" | "console") {
|
||||
const pkce = await generatePKCE()
|
||||
const url = new URL("https://claude.ai/oauth/authorize", import.meta.url)
|
||||
|
||||
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")
|
||||
|
@ -39,12 +43,11 @@ export namespace AuthAnthropic {
|
|||
})
|
||||
if (!result.ok) throw new ExchangeFailed()
|
||||
const json = await result.json()
|
||||
await Auth.set("anthropic", {
|
||||
type: "oauth",
|
||||
return {
|
||||
refresh: json.refresh_token as string,
|
||||
access: json.access_token as string,
|
||||
expires: Date.now() + json.expires_in * 1000,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export async function access() {
|
||||
|
|
|
@ -132,20 +132,24 @@ export const AuthLoginCommand = cmd({
|
|||
options: [
|
||||
{
|
||||
label: "Claude Pro/Max",
|
||||
value: "oauth",
|
||||
value: "max",
|
||||
},
|
||||
{
|
||||
label: "API Key",
|
||||
label: "Create API Key",
|
||||
value: "console",
|
||||
},
|
||||
{
|
||||
label: "Manually enter API Key",
|
||||
value: "api",
|
||||
},
|
||||
],
|
||||
})
|
||||
if (prompts.isCancel(method)) throw new UI.CancelledError()
|
||||
|
||||
if (method === "oauth") {
|
||||
if (method === "max") {
|
||||
// some weird bug where program exits without this
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
const { url, verifier } = await AuthAnthropic.authorize()
|
||||
const { url, verifier } = await AuthAnthropic.authorize("max")
|
||||
prompts.note("Trying to open browser...")
|
||||
try {
|
||||
await open(url)
|
||||
|
@ -162,13 +166,66 @@ export const AuthLoginCommand = cmd({
|
|||
})
|
||||
if (prompts.isCancel(code)) throw new UI.CancelledError()
|
||||
|
||||
await AuthAnthropic.exchange(code, verifier)
|
||||
.then(() => {
|
||||
prompts.log.success("Login successful")
|
||||
try {
|
||||
const credentials = await AuthAnthropic.exchange(code, verifier)
|
||||
await Auth.set("anthropic", {
|
||||
type: "oauth",
|
||||
refresh: credentials.refresh,
|
||||
access: credentials.access,
|
||||
expires: credentials.expires,
|
||||
})
|
||||
.catch(() => {
|
||||
prompts.log.error("Invalid code")
|
||||
prompts.log.success("Login successful")
|
||||
} catch {
|
||||
prompts.log.error("Invalid code")
|
||||
}
|
||||
prompts.outro("Done")
|
||||
return
|
||||
}
|
||||
|
||||
if (method === "console") {
|
||||
// some weird bug where program exits without this
|
||||
await new Promise((resolve) => setTimeout(resolve, 10))
|
||||
const { url, verifier } = await AuthAnthropic.authorize("console")
|
||||
prompts.note("Trying to open browser...")
|
||||
try {
|
||||
await open(url)
|
||||
} catch (e) {
|
||||
prompts.log.error(
|
||||
"Failed to open browser perhaps you are running without a display or X server, please open the following URL in your browser:",
|
||||
)
|
||||
}
|
||||
prompts.log.info(url)
|
||||
|
||||
const code = await prompts.text({
|
||||
message: "Paste the authorization code here: ",
|
||||
validate: (x) => (x.length > 0 ? undefined : "Required"),
|
||||
})
|
||||
if (prompts.isCancel(code)) throw new UI.CancelledError()
|
||||
|
||||
try {
|
||||
const credentials = await AuthAnthropic.exchange(code, verifier)
|
||||
const accessToken = credentials.access
|
||||
const response = await fetch("https://api.anthropic.com/api/oauth/claude_cli/create_api_key", {
|
||||
method: "POST",
|
||||
headers: {
|
||||
Authorization: `Bearer ${accessToken}`,
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
Accept: "application/json, text/plain, */*",
|
||||
},
|
||||
})
|
||||
if (!response.ok) {
|
||||
throw new Error("Failed to create API key")
|
||||
}
|
||||
const json = await response.json()
|
||||
await Auth.set("anthropic", {
|
||||
type: "api",
|
||||
key: json.raw_key,
|
||||
})
|
||||
|
||||
prompts.log.success("Login successful - API key created and saved")
|
||||
} catch (error) {
|
||||
prompts.log.error("Invalid code or failed to create API key")
|
||||
}
|
||||
prompts.outro("Done")
|
||||
return
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue