ForcePilot/web/src/apis/skill_api.js
supreme0597 44d1b20383 feat(skills): 优化远程 Skill 批量安装,单次克隆完成多 skill 安装
新增 install_remote_skills_batch() 函数,利用 npx skills add 原生多 --skill 支持,
将安装 N 个 skill 的仓库克隆次数从 2N 降至 1。

后端:
- remote_skill_install_service.py 新增批处理 + _find_skill_dir()
- skill_router.py 新增 POST /remote/install-batch 端点 (admin 权限)
- 非法 skill 名不阻塞其他合法 skill,结果按请求顺序返回
- CLI 部分失败时仍导入已安装的 skill
- import 失败后 rollback session,不影响后续导入

前端:
- skill_api.js 新增 installRemoteSkillsBatch()
- SkillCardList.vue 改为单次批处理调用

测试:
- 4 个单元测试覆盖正常安装/缺失跳过/部分失败/非法名场景

文档:
- docs/develop-guides/roadmap.md 更新开发记录
- docs/vibe/2026-05-14-remote-skill-batch-install-plan.md 方案文档(含设计决策)
2026-05-14 17:15:38 +08:00

100 lines
2.7 KiB
JavaScript

import { apiAdminGet, apiAdminPost, apiAdminPut, apiAdminDelete } from './base'
const BASE_URL = '/api/system/skills'
export const listSkills = async () => {
return apiAdminGet(BASE_URL)
}
export const importSkillZip = async (file) => {
const formData = new FormData()
formData.append('file', file)
return apiAdminPost(`${BASE_URL}/import`, formData)
}
export const listRemoteSkills = async (source) => {
return apiAdminPost(`${BASE_URL}/remote/list`, { source })
}
export const installRemoteSkill = async (payload) => {
return apiAdminPost(`${BASE_URL}/remote/install`, payload)
}
export const installRemoteSkillsBatch = async (payload) => {
return apiAdminPost(`${BASE_URL}/remote/install-batch`, payload)
}
export const getSkillDependencyOptions = async () => {
return apiAdminGet(`${BASE_URL}/dependency-options`)
}
export const listBuiltinSkills = async () => {
return apiAdminGet(`${BASE_URL}/builtin`)
}
export const installBuiltinSkill = async (slug) => {
return apiAdminPost(`${BASE_URL}/builtin/${encodeURIComponent(slug)}/install`)
}
export const updateBuiltinSkill = async (slug, force = false) => {
return apiAdminPost(`${BASE_URL}/builtin/${encodeURIComponent(slug)}/update`, { force })
}
export const getSkillTree = async (slug) => {
return apiAdminGet(`${BASE_URL}/${encodeURIComponent(slug)}/tree`)
}
export const getSkillFile = async (slug, path) => {
return apiAdminGet(
`${BASE_URL}/${encodeURIComponent(slug)}/file?path=${encodeURIComponent(path)}`
)
}
export const createSkillFile = async (slug, payload) => {
return apiAdminPost(`${BASE_URL}/${encodeURIComponent(slug)}/file`, payload)
}
export const updateSkillFile = async (slug, payload) => {
return apiAdminPut(`${BASE_URL}/${encodeURIComponent(slug)}/file`, payload)
}
export const updateSkillDependencies = async (slug, payload) => {
return apiAdminPut(`${BASE_URL}/${encodeURIComponent(slug)}/dependencies`, payload)
}
export const deleteSkillFile = async (slug, path) => {
return apiAdminDelete(
`${BASE_URL}/${encodeURIComponent(slug)}/file?path=${encodeURIComponent(path)}`
)
}
export const exportSkill = async (slug) => {
return apiAdminGet(`${BASE_URL}/${encodeURIComponent(slug)}/export`, {}, 'blob')
}
export const deleteSkill = async (slug) => {
return apiAdminDelete(`${BASE_URL}/${encodeURIComponent(slug)}`)
}
export const skillApi = {
listSkills,
importSkillZip,
listRemoteSkills,
installRemoteSkill,
installRemoteSkillsBatch,
getSkillDependencyOptions,
listBuiltinSkills,
installBuiltinSkill,
updateBuiltinSkill,
getSkillTree,
getSkillFile,
createSkillFile,
updateSkillFile,
updateSkillDependencies,
deleteSkillFile,
exportSkill,
deleteSkill
}
export default skillApi