feat(opencode): Windows PowerShell installer and Desktop key file

CLI and Desktop share %USERPROFILE%\.config\opencode. Persist the API
key in .arka-api-key so GUI apps do not need a login-shell export.
This commit is contained in:
ixoblakp committed 2026-08-27 18:12:48 +03:00
1 parent 3e440b9365
commit cb17ce8d2a
6 files changed
+531 -12

No files matched your search

+36 -3
View File
@@ -6,9 +6,35 @@ const PROVIDER_ID = "Arka-AI"
const API_URL = "https://api.arka-ai.ru/v1"
const CATALOG_URL =
"https://git.arka-ai.ru/arka/public/raw/branch/main/opencode/catalog.json"
const CACHE_FILE = join(homedir(), ".cache", "opencode", "arka-ai-models.json")
const TTL_MS = 24 * 60 * 60 * 1000
function configDir(): string {
const xdg = process.env.XDG_CONFIG_HOME
if (xdg) return join(xdg, "opencode")
return join(homedir(), ".config", "opencode")
}
function cacheDir(): string {
const xdg = process.env.XDG_CACHE_HOME
if (xdg) return join(xdg, "opencode")
return join(homedir(), ".cache", "opencode")
}
const CACHE_FILE = join(cacheDir(), "arka-ai-models.json")
const KEY_FILE = join(configDir(), ".arka-api-key")
function readApiKey(): string | undefined {
const fromEnv = process.env.ARKA_API_KEY?.trim()
if (fromEnv) return fromEnv
try {
if (existsSync(KEY_FILE)) {
const fromFile = readFileSync(KEY_FILE, "utf8").trim()
if (fromFile) return fromFile
}
} catch {}
return undefined
}
// Ids that must always be exposed, even if the filter would drop them.
const FORCED_MODELS = [
"DeepSeek-V4-Pro",
@@ -50,7 +76,7 @@ function loadCache(): Cache | null {
function saveCache(models: ModelMap): void {
try {
mkdirSync(join(homedir(), ".cache", "opencode"), { recursive: true })
mkdirSync(cacheDir(), { recursive: true })
writeFileSync(CACHE_FILE, JSON.stringify({ ts: Date.now(), models }))
} catch {}
}
@@ -99,7 +125,7 @@ async function resolveModels(): Promise<ModelMap | null> {
} catch {}
try {
const key = process.env.ARKA_API_KEY
const key = readApiKey()
const json = await fetchJson(
`${API_URL}/models`,
key ? { Authorization: `Bearer ${key}` } : {},
@@ -115,6 +141,7 @@ async function resolveModels(): Promise<ModelMap | null> {
export default async function ArkaAIProviderPlugin() {
const models = await resolveModels()
const apiKey = readApiKey()
return {
config: (cfg: any) => {
@@ -124,12 +151,18 @@ export default async function ArkaAIProviderPlugin() {
// Static models from opencode.json act as a fallback; the fetched
// catalog is layered on top so the configured default model never
// disappears even if the catalog drops it.
// apiKey from env or ~/.config/opencode/.arka-api-key so Desktop /
// Windows GUI works without a login-shell export.
cfg.provider[PROVIDER_ID] = {
name: "Arka-AI",
api: API_URL,
env: ["ARKA_API_KEY"],
...prev,
models: { ...(prev.models ?? {}), ...(models ?? {}) },
options: {
...(prev.options ?? {}),
...(apiKey ? { apiKey } : {}),
},
}
},
}