|
1 | 1 | import type { Hooks, PluginInput } from "@opencode-ai/plugin" |
2 | 2 | import type { Model } from "@opencode-ai/sdk/v2" |
| 3 | +import { Global } from "@/global" |
3 | 4 | import { Installation } from "@/installation" |
4 | 5 | import { iife } from "@/util/iife" |
| 6 | +import { Filesystem } from "@/util/filesystem" |
| 7 | +import { Hash } from "@/util/hash" |
5 | 8 | import { Log } from "../../util/log" |
6 | 9 | import { setTimeout as sleep } from "node:timers/promises" |
| 10 | +import path from "path" |
7 | 11 | import { CopilotModels } from "./models" |
8 | 12 |
|
9 | 13 | const log = Log.create({ service: "plugin.copilot" }) |
| 14 | +const ttl = 60 * 60 * 1000 |
| 15 | + |
| 16 | +function cachefile(url: string) { |
| 17 | + return path.join(Global.Path.cache, `copilot-models-${Hash.fast(url)}.json`) |
| 18 | +} |
| 19 | + |
| 20 | +function fresh(file: string) { |
| 21 | + return Date.now() - Number(Filesystem.stat(file)?.mtimeMs ?? 0) < ttl |
| 22 | +} |
10 | 23 |
|
11 | 24 | const CLIENT_ID = "Ov23li8tweQw6odWQebz" |
12 | 25 | // Add a small safety buffer when polling to avoid hitting the server |
@@ -47,17 +60,29 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> { |
47 | 60 | return Object.fromEntries(Object.entries(provider.models).map(([id, model]) => [id, fix(model)])) |
48 | 61 | } |
49 | 62 |
|
50 | | - return CopilotModels.get( |
51 | | - base(ctx.auth.enterpriseUrl), |
52 | | - { |
53 | | - Authorization: `Bearer ${ctx.auth.refresh}`, |
54 | | - "User-Agent": `opencode/${Installation.VERSION}`, |
55 | | - }, |
56 | | - provider.models, |
57 | | - ).catch((error) => { |
58 | | - log.error("failed to fetch copilot models", { error }) |
59 | | - return Object.fromEntries(Object.entries(provider.models).map(([id, model]) => [id, fix(model)])) |
60 | | - }) |
| 63 | + const url = base(ctx.auth.enterpriseUrl) |
| 64 | + const file = cachefile(url) |
| 65 | + const headers = { |
| 66 | + Authorization: `Bearer ${ctx.auth.refresh}`, |
| 67 | + "User-Agent": `opencode/${Installation.VERSION}`, |
| 68 | + } |
| 69 | + |
| 70 | + if (fresh(file)) { |
| 71 | + const cached = await Bun.file(file) |
| 72 | + .json() |
| 73 | + .catch(() => undefined) |
| 74 | + if (cached) return cached as Record<string, Model> |
| 75 | + } |
| 76 | + |
| 77 | + return CopilotModels.get(url, headers, provider.models) |
| 78 | + .then(async (result) => { |
| 79 | + await Bun.write(file, JSON.stringify(result)) |
| 80 | + return result |
| 81 | + }) |
| 82 | + .catch((error) => { |
| 83 | + log.error("failed to fetch copilot models", { error }) |
| 84 | + return Object.fromEntries(Object.entries(provider.models).map(([id, model]) => [id, fix(model)])) |
| 85 | + }) |
61 | 86 | }, |
62 | 87 | }, |
63 | 88 | auth: { |
|
0 commit comments