Files
WechatExplorer/src/main/services/skill-resource-service.ts
T
2026-08-08 14:54:54 +08:00

144 lines
4.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { app, shell } from 'electron'
import { existsSync, promises as fs } from 'fs'
import { dirname, join } from 'path'
import { isPackagedRuntime } from '../runtime-mode'
const SKILL_RELATIVE_PATH = join('skill', 'wechatexplorer-reader', 'SKILL.md')
const GITHUB_URL =
'https://github.com/Wxw-Gu/WechatExplorer/tree/main/docs/skill/wechatexplorer-reader'
const SKILL_VERSION = 'v1.1'
type SkillResourceSource = 'development' | 'bundled'
interface SkillPathEnvironment {
appPath: string
cwd: string
resourcesPath: string
execPath: string
packaged: boolean
}
interface SkillCandidate {
path: string
source: SkillResourceSource
}
export interface SkillResourceStatus {
available: boolean
version?: string
filePath?: string
directoryPath?: string
source: SkillResourceSource
githubUrl: string
error?: string
}
function currentEnvironment(): SkillPathEnvironment {
return {
appPath: app.getAppPath(),
cwd: process.cwd(),
resourcesPath: process.resourcesPath || '',
execPath: process.execPath,
packaged: isPackagedRuntime()
}
}
function uniqueCandidates(candidates: SkillCandidate[]): SkillCandidate[] {
const seen = new Set<string>()
return candidates.filter((candidate) => {
if (!candidate.path || seen.has(candidate.path)) return false
seen.add(candidate.path)
return true
})
}
export function getSkillCandidates(environment?: SkillPathEnvironment): SkillCandidate[] {
const runtime = environment || currentEnvironment()
const developmentPaths = [
join(runtime.appPath, 'docs', SKILL_RELATIVE_PATH),
join(runtime.cwd, 'docs', SKILL_RELATIVE_PATH),
join(dirname(runtime.appPath), 'docs', SKILL_RELATIVE_PATH)
]
const execDirectory = dirname(runtime.execPath)
const bundledPaths = [
join(runtime.resourcesPath, SKILL_RELATIVE_PATH),
join(runtime.resourcesPath, 'resources', SKILL_RELATIVE_PATH),
join(dirname(runtime.appPath), SKILL_RELATIVE_PATH),
join(execDirectory, 'resources', SKILL_RELATIVE_PATH),
join(dirname(execDirectory), 'Resources', SKILL_RELATIVE_PATH)
]
return uniqueCandidates(
runtime.packaged
? bundledPaths.map((path) => ({ path, source: 'bundled' }))
: [
...developmentPaths.map((path) => ({ path, source: 'development' as const })),
...bundledPaths.map((path) => ({ path, source: 'bundled' as const }))
]
)
}
export function resolveSkillResourceStatus(
environment?: SkillPathEnvironment
): SkillResourceStatus {
const candidates = getSkillCandidates(environment)
const resolved = candidates.find((candidate) => existsSync(candidate.path))
const filePath = resolved?.path || candidates[0].path
const source = resolved?.source || candidates[0].source
const directoryPath = dirname(filePath)
if (!resolved) {
return {
available: false,
source,
githubUrl: GITHUB_URL,
error: `未找到 WechatExplorer Reader Skill 文件(已检查:${candidates.map((item) => item.path).join('')}`
}
}
return {
available: true,
version: SKILL_VERSION,
filePath,
directoryPath,
source,
githubUrl: GITHUB_URL
}
}
function getStatus(): SkillResourceStatus {
return resolveSkillResourceStatus()
}
export const skillResourceService = {
getStatus,
async read(): Promise<{ success: boolean; content?: string; error?: string }> {
const status = this.getStatus()
if (!status.available || !status.filePath) return { success: false, error: status.error }
try {
return { success: true, content: await fs.readFile(status.filePath, 'utf-8') }
} catch (error) {
return { success: false, error: error instanceof Error ? error.message : String(error) }
}
},
async reveal(): Promise<{ success: boolean; error?: string }> {
const status = this.getStatus()
if (!status.available || !status.directoryPath) return { success: false, error: status.error }
try {
const error = await shell.openPath(status.directoryPath)
if (error) return { success: false, error }
return { success: true }
} catch (error) {
return { success: false, error: error instanceof Error ? error.message : String(error) }
}
},
async openGithub(): Promise<{ success: boolean; error?: string }> {
try {
await shell.openExternal(GITHUB_URL)
return { success: true }
} catch (error) {
return { success: false, error: error instanceof Error ? error.message : String(error) }
}
}
}