Compare commits

...

3 Commits

Author SHA1 Message Date
Dax Raad 1c58023df9 improve anthropic oauth token caching and authentication handling
🤖 Generated with [opencode](https://opencode.ai)

Co-Authored-By: opencode <noreply@opencode.ai>
2025-06-17 13:23:15 -04:00
Dax Raad 4e0aa58b7e ignore: fix 2025-06-17 13:04:26 -04:00
Dax Raad 23ee34b35f state 2025-06-17 12:29:28 -04:00
8 changed files with 32 additions and 10 deletions
+3
View File
@@ -45,6 +45,7 @@
"zod-openapi": "4.2.4",
},
"devDependencies": {
"@ai-sdk/anthropic": "1.2.12",
"@tsconfig/bun": "1.0.7",
"@types/bun": "latest",
"@types/turndown": "5.0.5",
@@ -99,6 +100,8 @@
"zod": "3.24.2",
},
"packages": {
"@ai-sdk/anthropic": ["@ai-sdk/anthropic@1.2.12", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ=="],
"@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="],
"@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="],
+2 -1
View File
@@ -20,7 +20,8 @@
"@types/turndown": "5.0.5",
"@types/yargs": "17.0.33",
"typescript": "catalog:",
"zod-to-json-schema": "3.24.5"
"zod-to-json-schema": "3.24.5",
"@ai-sdk/anthropic": "1.2.12"
},
"dependencies": {
"@clack/prompts": "0.11.0",
+2
View File
@@ -18,6 +18,7 @@ export namespace App {
data: z.string(),
root: z.string(),
cwd: z.string(),
state: z.string(),
}),
time: z.object({
initialized: z.number().optional(),
@@ -68,6 +69,7 @@ export namespace App {
git: git !== undefined,
path: {
config: Global.Path.config,
state: Global.Path.state,
data,
root: git ?? input.cwd,
cwd: input.cwd,
+3
View File
@@ -48,6 +48,7 @@ export namespace AuthAnthropic {
await Auth.set("anthropic", {
type: "oauth",
refresh: json.refresh_token as string,
access: json.access_token as string,
expires: Date.now() + json.expires_in * 1000,
})
}
@@ -55,6 +56,7 @@ export namespace AuthAnthropic {
export async function access() {
const info = await Auth.get("anthropic")
if (!info || info.type !== "oauth") return
if (info.access && info.expires > Date.now()) return info.access
const response = await fetch(
"https://console.anthropic.com/v1/oauth/token",
{
@@ -74,6 +76,7 @@ export namespace AuthAnthropic {
await Auth.set("anthropic", {
type: "oauth",
refresh: json.refresh_token as string,
access: json.access_token as string,
expires: Date.now() + json.expires_in * 1000,
})
return json.access_token as string
+1
View File
@@ -7,6 +7,7 @@ export namespace Auth {
export const Oauth = z.object({
type: z.literal("oauth"),
refresh: z.string(),
access: z.string(),
expires: z.number(),
})
+2 -2
View File
@@ -5,10 +5,10 @@ import path from "path"
export namespace GlobalConfig {
export const Info = z.object({
autoupdate: z.boolean().optional(),
autoshare: z.boolean().optional(),
provider: z.string().optional(),
model: z.string().optional(),
autoupdate: z.boolean().optional(),
autoshare: z.boolean().optional(),
disabled_providers: z.array(z.string()).optional(),
})
export type Info = z.infer<typeof Info>
+7 -4
View File
@@ -1,5 +1,5 @@
import fs from "fs/promises"
import { xdgData, xdgCache, xdgConfig } from "xdg-basedir"
import { xdgData, xdgCache, xdgConfig, xdgState } from "xdg-basedir"
import path from "path"
const app = "opencode"
@@ -7,6 +7,7 @@ const app = "opencode"
const data = path.join(xdgData!, app)
const cache = path.join(xdgCache!, app)
const config = path.join(xdgConfig!, app)
const state = path.join(xdgState!, app)
export namespace Global {
export const Path = {
@@ -15,12 +16,14 @@ export namespace Global {
providers: path.join(config, "providers"),
cache,
config,
state,
} as const
}
await Promise.all([
fs.mkdir(data, { recursive: true }),
fs.mkdir(config, { recursive: true }),
fs.mkdir(cache, { recursive: true }),
fs.mkdir(Global.Path.data, { recursive: true }),
fs.mkdir(Global.Path.config, { recursive: true }),
fs.mkdir(Global.Path.cache, { recursive: true }),
fs.mkdir(Global.Path.providers, { recursive: true }),
fs.mkdir(Global.Path.state, { recursive: true }),
])
+12 -3
View File
@@ -50,9 +50,18 @@ export namespace Provider {
}
return {
apiKey: "",
headers: {
authorization: `Bearer ${access}`,
"anthropic-beta": "oauth-2025-04-20",
async fetch(input: any, init: any) {
const access = await AuthAnthropic.access()
const headers = {
...init.headers,
authorization: `Bearer ${access}`,
"anthropic-beta": "oauth-2025-04-20",
}
delete headers["x-api-key"]
return fetch(input, {
...init,
headers,
})
},
}
},