mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 05:43:55 +00:00
125 lines
3.8 KiB
TypeScript
125 lines
3.8 KiB
TypeScript
import path from "path"
|
|
import { Global } from "../global"
|
|
import fs from "fs/promises"
|
|
import z from "zod"
|
|
import { NamedError } from "@opencode-ai/util/error"
|
|
import { lazy } from "../util/lazy"
|
|
import { Log } from "../util/log"
|
|
import { ZipReader, BlobReader, BlobWriter } from "@zip.js/zip.js"
|
|
|
|
export namespace Fzf {
|
|
const log = Log.create({ service: "fzf" })
|
|
|
|
const VERSION = "0.62.0"
|
|
const PLATFORM = {
|
|
darwin: { extension: "tar.gz" },
|
|
linux: { extension: "tar.gz" },
|
|
win32: { extension: "zip" },
|
|
} as const
|
|
|
|
export const ExtractionFailedError = NamedError.create(
|
|
"FzfExtractionFailedError",
|
|
z.object({
|
|
filepath: z.string(),
|
|
stderr: z.string(),
|
|
}),
|
|
)
|
|
|
|
export const UnsupportedPlatformError = NamedError.create(
|
|
"FzfUnsupportedPlatformError",
|
|
z.object({
|
|
platform: z.string(),
|
|
}),
|
|
)
|
|
|
|
export const DownloadFailedError = NamedError.create(
|
|
"FzfDownloadFailedError",
|
|
z.object({
|
|
url: z.string(),
|
|
status: z.number(),
|
|
}),
|
|
)
|
|
|
|
const state = lazy(async () => {
|
|
let filepath = Bun.which("fzf")
|
|
if (filepath) {
|
|
log.info("found", { filepath })
|
|
return { filepath }
|
|
}
|
|
filepath = path.join(Global.Path.bin, "fzf" + (process.platform === "win32" ? ".exe" : ""))
|
|
|
|
const file = Bun.file(filepath)
|
|
if (!(await file.exists())) {
|
|
const archMap = { x64: "amd64", arm64: "arm64" } as const
|
|
const arch = archMap[process.arch as keyof typeof archMap] ?? "amd64"
|
|
|
|
const config = PLATFORM[process.platform as keyof typeof PLATFORM]
|
|
if (!config) throw new UnsupportedPlatformError({ platform: process.platform })
|
|
|
|
const version = VERSION
|
|
const platformName = process.platform === "win32" ? "windows" : process.platform
|
|
const filename = `fzf-${version}-${platformName}_${arch}.${config.extension}`
|
|
const url = `https://github.com/junegunn/fzf/releases/download/v${version}/${filename}`
|
|
|
|
const response = await fetch(url)
|
|
if (!response.ok) throw new DownloadFailedError({ url, status: response.status })
|
|
|
|
const buffer = await response.arrayBuffer()
|
|
const archivePath = path.join(Global.Path.bin, filename)
|
|
await Bun.write(archivePath, buffer)
|
|
if (config.extension === "tar.gz") {
|
|
const proc = Bun.spawn(["tar", "-xzf", archivePath, "fzf"], {
|
|
cwd: Global.Path.bin,
|
|
stderr: "pipe",
|
|
stdout: "pipe",
|
|
})
|
|
await proc.exited
|
|
if (proc.exitCode !== 0)
|
|
throw new ExtractionFailedError({
|
|
filepath,
|
|
stderr: await Bun.readableStreamToText(proc.stderr),
|
|
})
|
|
}
|
|
if (config.extension === "zip") {
|
|
const zipFileReader = new ZipReader(new BlobReader(new Blob([await Bun.file(archivePath).arrayBuffer()])))
|
|
const entries = await zipFileReader.getEntries()
|
|
let fzfEntry: any
|
|
for (const entry of entries) {
|
|
if (entry.filename === "fzf.exe") {
|
|
fzfEntry = entry
|
|
break
|
|
}
|
|
}
|
|
|
|
if (!fzfEntry) {
|
|
throw new ExtractionFailedError({
|
|
filepath: archivePath,
|
|
stderr: "fzf.exe not found in zip archive",
|
|
})
|
|
}
|
|
|
|
const fzfBlob = await fzfEntry.getData(new BlobWriter())
|
|
if (!fzfBlob) {
|
|
throw new ExtractionFailedError({
|
|
filepath: archivePath,
|
|
stderr: "Failed to extract fzf.exe from zip archive",
|
|
})
|
|
}
|
|
await Bun.write(filepath, await fzfBlob.arrayBuffer())
|
|
await zipFileReader.close()
|
|
}
|
|
await fs.unlink(archivePath)
|
|
if (process.platform !== "win32") await fs.chmod(filepath, 0o755)
|
|
}
|
|
|
|
return {
|
|
filepath,
|
|
}
|
|
})
|
|
|
|
export async function filepath() {
|
|
const { filepath } = await state()
|
|
return filepath
|
|
}
|
|
}
|