|
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 = 5 * 60 * 1000 |
10 | 15 |
|
11 | | -const modelsCache = new Map<string, Record<string, Model>>() |
| 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 | +} |
12 | 23 |
|
13 | 24 | const CLIENT_ID = "Ov23li8tweQw6odWQebz" |
14 | 25 | // Add a small safety buffer when polling to avoid hitting the server |
@@ -50,19 +61,19 @@ export async function CopilotAuthPlugin(input: PluginInput): Promise<Hooks> { |
50 | 61 | } |
51 | 62 |
|
52 | 63 | const url = base(ctx.auth.enterpriseUrl) |
53 | | - const key = `${url}:${ctx.auth.refresh}` |
| 64 | + const file = cachefile(url) |
54 | 65 | const headers = { |
55 | 66 | Authorization: `Bearer ${ctx.auth.refresh}`, |
56 | 67 | "User-Agent": `opencode/${Installation.VERSION}`, |
57 | 68 | } |
58 | 69 |
|
59 | | - // Fire-and-forget background refresh (same pattern as models.dev) |
60 | | - CopilotModels.get(url, headers, provider.models) |
61 | | - .then((result) => modelsCache.set(key, result)) |
62 | | - .catch((error) => log.error("failed to fetch copilot models", { error })) |
| 70 | + if (!fresh(file)) { |
| 71 | + CopilotModels.get(url, headers, provider.models) |
| 72 | + .then((result) => Filesystem.write(file, JSON.stringify(result))) |
| 73 | + .catch((error) => log.error("failed to fetch copilot models", { error })) |
| 74 | + } |
63 | 75 |
|
64 | | - // Return cached result immediately, falling back to static models |
65 | | - const cached = modelsCache.get(key) |
| 76 | + const cached = await Filesystem.readJson<Record<string, Model>>(file).catch(() => undefined) |
66 | 77 | if (cached) return cached |
67 | 78 | return Object.fromEntries(Object.entries(provider.models).map(([id, model]) => [id, fix(model)])) |
68 | 79 | }, |
|
0 commit comments