mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-06 16:59:01 +00:00
149 lines
4.2 KiB
TypeScript
149 lines
4.2 KiB
TypeScript
import { execFileSync } from "node:child_process"
|
|
import { existsSync, readFileSync, readdirSync } from "node:fs"
|
|
import { dirname, extname, join } from "node:path"
|
|
|
|
export function checkAppExists(appName: string): boolean {
|
|
if (process.platform === "win32") return true
|
|
if (process.platform === "linux") return true
|
|
return checkMacosApp(appName)
|
|
}
|
|
|
|
export function resolveAppPath(appName: string): string | null {
|
|
if (process.platform !== "win32") return appName
|
|
return resolveWindowsAppPath(appName)
|
|
}
|
|
|
|
export function wslPath(path: string, mode: "windows" | "linux" | null): string {
|
|
if (process.platform !== "win32") return path
|
|
|
|
const flag = mode === "windows" ? "-w" : "-u"
|
|
try {
|
|
if (path.startsWith("~")) {
|
|
const suffix = path.slice(1)
|
|
const cmd = `wslpath ${flag} \"$HOME${suffix.replace(/\"/g, '\\"')}\"`
|
|
const output = execFileSync("wsl", ["-e", "sh", "-lc", cmd])
|
|
return output.toString().trim()
|
|
}
|
|
|
|
const output = execFileSync("wsl", ["-e", "wslpath", flag, path])
|
|
return output.toString().trim()
|
|
} catch (error) {
|
|
throw new Error(`Failed to run wslpath: ${String(error)}`)
|
|
}
|
|
}
|
|
|
|
function checkMacosApp(appName: string) {
|
|
const locations = [`/Applications/${appName}.app`, `/System/Applications/${appName}.app`]
|
|
|
|
const home = process.env.HOME
|
|
if (home) locations.push(`${home}/Applications/${appName}.app`)
|
|
|
|
if (locations.some((location) => existsSync(location))) return true
|
|
|
|
try {
|
|
execFileSync("which", [appName])
|
|
return true
|
|
} catch {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function resolveWindowsAppPath(appName: string): string | null {
|
|
let output: string
|
|
try {
|
|
output = execFileSync("where", [appName]).toString()
|
|
} catch {
|
|
return null
|
|
}
|
|
|
|
const paths = output
|
|
.split(/\r?\n/)
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0)
|
|
|
|
const hasExt = (path: string, ext: string) => extname(path).toLowerCase() === `.${ext}`
|
|
|
|
const exe = paths.find((path) => hasExt(path, "exe"))
|
|
if (exe) return exe
|
|
|
|
const resolveCmd = (path: string) => {
|
|
const content = readFileSync(path, "utf8")
|
|
for (const token of content.split('"').map((value: string) => value.trim())) {
|
|
const lower = token.toLowerCase()
|
|
if (!lower.includes(".exe")) continue
|
|
|
|
const index = lower.indexOf("%~dp0")
|
|
if (index >= 0) {
|
|
const base = dirname(path)
|
|
const suffix = token.slice(index + 5)
|
|
const resolved = suffix
|
|
.replace(/\//g, "\\")
|
|
.split("\\")
|
|
.filter((part: string) => part && part !== ".")
|
|
.reduce((current: string, part: string) => {
|
|
if (part === "..") return dirname(current)
|
|
return join(current, part)
|
|
}, base)
|
|
|
|
if (existsSync(resolved)) return resolved
|
|
}
|
|
|
|
if (existsSync(token)) return token
|
|
}
|
|
|
|
return null
|
|
}
|
|
|
|
for (const path of paths) {
|
|
if (hasExt(path, "cmd") || hasExt(path, "bat")) {
|
|
const resolved = resolveCmd(path)
|
|
if (resolved) return resolved
|
|
}
|
|
|
|
if (!extname(path)) {
|
|
const cmd = `${path}.cmd`
|
|
if (existsSync(cmd)) {
|
|
const resolved = resolveCmd(cmd)
|
|
if (resolved) return resolved
|
|
}
|
|
|
|
const bat = `${path}.bat`
|
|
if (existsSync(bat)) {
|
|
const resolved = resolveCmd(bat)
|
|
if (resolved) return resolved
|
|
}
|
|
}
|
|
}
|
|
|
|
const key = appName
|
|
.split("")
|
|
.filter((value: string) => /[a-z0-9]/i.test(value))
|
|
.map((value: string) => value.toLowerCase())
|
|
.join("")
|
|
|
|
if (key) {
|
|
for (const path of paths) {
|
|
const dirs = [dirname(path), dirname(dirname(path)), dirname(dirname(dirname(path)))]
|
|
for (const dir of dirs) {
|
|
try {
|
|
for (const entry of readdirSync(dir)) {
|
|
const candidate = join(dir, entry)
|
|
if (!hasExt(candidate, "exe")) continue
|
|
const stem = entry.replace(/\.exe$/i, "")
|
|
const name = stem
|
|
.split("")
|
|
.filter((value: string) => /[a-z0-9]/i.test(value))
|
|
.map((value: string) => value.toLowerCase())
|
|
.join("")
|
|
if (name.includes(key) || key.includes(name)) return candidate
|
|
}
|
|
} catch {
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return paths[0] ?? null
|
|
}
|