mirror of
https://wget.la/https://github.com/Wxw-Gu/WechatExplorer
synced 2026-08-18 12:07:01 +08:00
144 lines
4.4 KiB
TypeScript
144 lines
4.4 KiB
TypeScript
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) }
|
||
}
|
||
}
|
||
}
|