Files
zn-ai/src/stores/skills.ts
duanshuwen e77c815a86 feat: add new stores for cron, locale, providers, script, shared data, skills, and user info
- Implemented `cron` store to manage scheduled tasks with CRUD operations.
- Created `locale` store for language settings with persistence and system language detection.
- Added `providers` store to handle provider accounts and configurations with API interactions.
- Developed `script` store for managing automation scripts, including recording and execution.
- Introduced `sharedStore` for managing shared data across components.
- Established `skills` store for fetching, installing, and managing skills from a marketplace.
- Created `userinfo` store for user authentication and session management.

chore: update path aliases from `@store` to `@stores` in TypeScript configuration and Vite config
2026-04-15 21:49:25 +08:00

124 lines
3.6 KiB
TypeScript

import { defineStore } from 'pinia';
import type { Skill, MarketplaceSkill } from '@src/lib/skills-types';
import {
apiFetchSkills,
apiSearchSkills,
apiInstallSkill,
apiUninstallSkill,
apiUpdateSkillConfig,
} from '@src/lib/skills-api';
const INSTALL_ERROR_CODES = new Set(['installTimeoutError', 'installRateLimitError']);
const FETCH_ERROR_CODES = new Set(['fetchTimeoutError', 'fetchRateLimitError', 'timeoutError', 'rateLimitError']);
const SEARCH_ERROR_CODES = new Set(['searchTimeoutError', 'searchRateLimitError', 'timeoutError', 'rateLimitError']);
export const useSkillsStore = defineStore('skills', {
state: () => ({
skills: [] as Skill[],
searchResults: [] as MarketplaceSkill[],
loading: false,
searching: false,
searchError: null as string | null,
installing: {} as Record<string, boolean>,
error: null as string | null,
}),
getters: {
sourceStats: (state) => {
const safeSkills = Array.isArray(state.skills) ? state.skills : [];
return {
all: safeSkills.length,
builtIn: safeSkills.filter(s => s.isBundled).length,
marketplace: safeSkills.filter(s => !s.isBundled).length,
};
},
},
actions: {
async fetchSkills() {
if (this.skills.length === 0) {
this.loading = true;
}
this.error = null;
try {
this.skills = await apiFetchSkills();
} catch (error) {
console.error('Failed to fetch skills:', error);
const msg = error instanceof Error ? error.message : String(error);
this.error = FETCH_ERROR_CODES.has(msg) ? msg : msg;
} finally {
this.loading = false;
}
},
async searchSkills(query: string) {
this.searching = true;
this.searchError = null;
try {
this.searchResults = await apiSearchSkills(query);
} catch (error) {
const msg = error instanceof Error ? error.message : String(error);
this.searchError = SEARCH_ERROR_CODES.has(msg) ? msg : msg;
} finally {
this.searching = false;
}
},
async installSkill(slug: string, version?: string) {
this.installing[slug] = true;
try {
await apiInstallSkill(slug, version);
await this.fetchSkills();
} finally {
delete this.installing[slug];
}
},
async uninstallSkill(slug: string) {
this.installing[slug] = true;
try {
await apiUninstallSkill(slug);
await this.fetchSkills();
} finally {
delete this.installing[slug];
}
},
async enableSkill(skillId: string) {
const skill = this.skills.find(s => s.id === skillId);
if (!skill) return;
try {
await apiUpdateSkillConfig(skillId, { enabled: true });
skill.enabled = true;
} catch (error) {
console.error('Failed to enable skill:', error);
throw error;
}
},
async disableSkill(skillId: string) {
const skill = this.skills.find(s => s.id === skillId);
if (!skill) return;
if (skill.isCore) {
throw new Error('Cannot disable core skill');
}
try {
await apiUpdateSkillConfig(skillId, { enabled: false });
skill.enabled = false;
} catch (error) {
console.error('Failed to disable skill:', error);
throw error;
}
},
updateSkill(skillId: string, updates: Partial<Skill>) {
const skill = this.skills.find(s => s.id === skillId);
if (skill) {
Object.assign(skill, updates);
}
},
},
});
export { INSTALL_ERROR_CODES, FETCH_ERROR_CODES, SEARCH_ERROR_CODES };