重命名:agent - skill

This commit is contained in:
jingrow 2026-06-25 03:09:44 +08:00
parent dc9bb60138
commit dfb8f31d2a
52 changed files with 398 additions and 552 deletions

View File

@ -58,7 +58,7 @@ jingrow/
│ │ │ │ ├── auth.ts # 认证API
│ │ │ │ ├── system.ts # 系统API
│ │ │ │ ├── workspace.ts # 工作区API
│ │ │ │ ├── agents.ts # AI代理API
│ │ │ │ ├── skills.ts # AI代理API
│ │ │ │ ├── nodes.ts # AI节点API
│ │ │ │ ├── embedding.ts # 嵌入API
│ │ │ │ ├── localJobs.ts # 本地任务API

View File

@ -315,7 +315,7 @@ const breadcrumbItems = computed(() => {
const map: Record<string, string> = {
Dashboard: t('Dashboard'),
AgentList: t('Agents'),
AgentDetail: t('Agent Detail'),
SkillDetail: t('Skill Detail'),
NodeList: t('Node Management'),
NodeDetail: t('Node Detail'),
LocalJobList: t('Local Jobs'),

View File

@ -177,13 +177,13 @@ const router = createRouter({
},
{
path: 'skill-marketplace',
name: 'AgentMarketplace',
component: () => import('../../views/dev/AgentMarketplace.vue')
name: 'SkillMarketplace',
component: () => import('../../views/dev/SkillMarketplace.vue')
},
{
path: 'skill-marketplace/:name',
name: 'AgentDetail',
component: () => import('../../views/dev/AgentDetail.vue')
name: 'SkillDetail',
component: () => import('../../views/dev/SkillDetail.vue')
},
{
path: 'tool-marketplace',
@ -225,9 +225,9 @@ const router = createRouter({
meta: { requiresAuth: true }
},
{
path: 'my-published-agents',
name: 'MyPublishedAgents',
component: () => import('../../views/dev/MyPublishedAgents.vue'),
path: 'my-published-skills',
name: 'MyPublishedSkills',
component: () => import('../../views/dev/MyPublishedSkills.vue'),
meta: { requiresAuth: true }
},
{

View File

@ -42,7 +42,7 @@ import { Icon } from '@iconify/vue'
// @ts-ignore
import AiSkillFlowBuilder from './AiSkillFlowBuilder.vue'
import { useFlowBuilderStore } from '@/shared/stores/flowBuilder'
import { useSkillStore } from '@/shared/stores/agent'
import { useSkillStore } from '@/shared/stores/skill'
import { slugToPageType } from '@/shared/utils/slug'
import { t } from '@/shared/i18n'
@ -72,7 +72,7 @@ const computedInitialValue = computed(() => {
const flowBuilderRef = ref()
// Computed - 使 propsfallback store
const skillId = computed(() => props.skillId || flowBuilderStore.getAgentId())
const skillId = computed(() => props.skillId || flowBuilderStore.getSkillId())
// URL pagetype
const pagetype = computed(() => {

View File

@ -17,12 +17,12 @@ class AiSkillFlowBuilder {
init() {
// 设置全局智能体信息,供执行器使用
this.setup_global_agent_info();
this.setup_global_skill_info();
this.setup_app();
this.watch_changes();
}
setup_global_agent_info() {
setup_global_skill_info() {
// 设置全局变量,让执行器能够获取到智能体信息
if (this.frm && this.frm.pg && this.frm.pg.name) {
window.current_skill_name = this.frm.pg.name;

View File

@ -32,37 +32,3 @@ export const updateSkillApi = async (name: string, data: Partial<AiSkill>, pageT
throw new Error(error.message || '更新Ai Skill失败')
}
}
import type { AIAgent } from '../types'
import { getRecord, updateRecord } from './common'
// 重新导出类型,供其他模块使用
export type { AIAgent }
export const getAgentDetail = async (name: string, pageType: string): Promise<AIAgent> => {
try {
const result = await getRecord(pageType, name)
if (!result.success) {
throw new Error(result.message || '获取AI Agent详情失败')
}
return result.data
} catch (error: any) {
throw new Error(error.message || '获取AI Agent详情失败')
}
}
export const updateAgentApi = async (name: string, data: Partial<AIAgent>, pageType: string): Promise<AIAgent> => {
try {
const result = await updateRecord(pageType, name, data)
if (!result.success) {
throw new Error(result.message || '更新AI Agent失败')
}
return result.data
} catch (error: any) {
console.error("Error in updateAgentApi:", error)
throw new Error(error.message || '更新AI Agent失败')
}
}

View File

@ -1,90 +0,0 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { getSkillDetail, updateSkillApi, type AiSkill } from '../api/agents'
export const useSkillStore = defineStore('skill', () => {
const currentSkill = ref<AiSkill | null>(null)
const currentPageType = ref<string>('')
// 获取单个Ai Skill
const fetchSkill = async (name: string, pageType: string) => {
try {
const skillData = await getSkillDetail(name, pageType)
currentSkill.value = skillData
currentPageType.value = pageType
} catch (err: any) {
console.error('获取Ai Skill详情失败:', err)
throw err
}
}
// 更新Ai Skill
const updateSkill = async (name: string, data: Partial<AiSkill>, pageType: string) => {
try {
await updateSkillApi(name, data, pageType)
// 更新成功后,手动更新本地状态
if (currentSkill.value && currentSkill.value.name === name) {
currentSkill.value = { ...currentSkill.value, ...data }
}
return { success: true }
} catch (err: any) {
console.error('更新Ai Skill失败:', err)
throw err
}
}
return {
// 状态
currentSkill,
currentPageType,
// 方法
fetchSkill,
updateSkill
}
})
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { getAgentDetail, updateAgentApi, type AIAgent } from '../api/agents'
export const useAgentStore = defineStore('agent', () => {
const currentAgent = ref<AIAgent | null>(null)
const currentPageType = ref<string>('')
// 获取单个AI Agent
const fetchAgent = async (name: string, pageType: string) => {
try {
const agentData = await getAgentDetail(name, pageType)
currentAgent.value = agentData
currentPageType.value = pageType
} catch (err: any) {
console.error('获取AI Agent详情失败:', err)
throw err
}
}
// 更新AI Agent
const updateAgent = async (name: string, data: Partial<AIAgent>, pageType: string) => {
try {
await updateAgentApi(name, data, pageType)
// 更新成功后,手动更新本地状态
if (currentAgent.value && currentAgent.value.name === name) {
currentAgent.value = { ...currentAgent.value, ...data }
}
return { success: true }
} catch (err: any) {
console.error('更新AI Agent失败:', err)
throw err
}
}
return {
// 状态
currentAgent,
currentPageType,
// 方法
fetchAgent,
updateAgent
}
})

View File

@ -18,7 +18,7 @@ export const useFlowBuilderStore = defineStore('flowBuilder', () => {
flowData.value = data
}
const setAgentId = (id: string) => {
const setSkillId = (id: string) => {
skillId.value = id
}
@ -47,7 +47,7 @@ export const useFlowBuilderStore = defineStore('flowBuilder', () => {
return flowData.value
}
const getAgentId = () => {
const getSkillId = () => {
return skillId.value
}
@ -59,7 +59,7 @@ export const useFlowBuilderStore = defineStore('flowBuilder', () => {
// Actions
setFlowData,
setAgentId,
setSkillId,
activateFlowBuilder,
deactivateFlowBuilder,
updateFlowData,
@ -67,6 +67,6 @@ export const useFlowBuilderStore = defineStore('flowBuilder', () => {
// Getters
hasFlowData,
getFlowData,
getAgentId
getSkillId
}
})

View File

@ -0,0 +1,45 @@
import { defineStore } from 'pinia'
import { ref } from 'vue'
import { getSkillDetail, updateSkillApi, type AiSkill } from '../api/skills'
export const useSkillStore = defineStore('skill', () => {
const currentSkill = ref<AiSkill | null>(null)
const currentPageType = ref<string>('')
// 获取单个Ai Skill
const fetchSkill = async (name: string, pageType: string) => {
try {
const skillData = await getSkillDetail(name, pageType)
currentSkill.value = skillData
currentPageType.value = pageType
} catch (err: any) {
console.error('获取Ai Skill详情失败:', err)
throw err
}
}
// 更新Ai Skill
const updateSkill = async (name: string, data: Partial<AiSkill>, pageType: string) => {
try {
await updateSkillApi(name, data, pageType)
// 更新成功后,手动更新本地状态
if (currentSkill.value && currentSkill.value.name === name) {
currentSkill.value = { ...currentSkill.value, ...data }
}
return { success: true }
} catch (err: any) {
console.error('更新Ai Skill失败:', err)
throw err
}
}
return {
// 状态
currentSkill,
currentPageType,
// 方法
fetchSkill,
updateSkill
}
})

View File

@ -1,5 +1,5 @@
// 全局类型定义
export * from './agent'
export * from './skill'
export * from './flow'
export * from './node'
export * from './common'

View File

@ -1,4 +1,4 @@
// 智能体相关类型定义
// 智能体(Skill)相关类型定义
export interface AiSkill {
name: string
skill_name: string

View File

@ -9,7 +9,7 @@
<!-- 原来的4个统计 -->
<n-grid-item>
<n-card>
<n-statistic :label="t('Total Skills')" :value="stats.agents" />
<n-statistic :label="t('Total Skills')" :value="stats.skills" />
</n-card>
</n-grid-item>
<n-grid-item>
@ -71,7 +71,7 @@ import { t } from '../shared/i18n'
import { getCount, getLocalJobCount } from '../shared/api/common'
const stats = reactive({
agents: 0,
skills: 0,
nodes: 0,
taskQueue: 0,
scheduledTasks: 0,
@ -87,9 +87,9 @@ const loadStats = async () => {
try {
// 4
//
const agentsResult = await getCount('Local Ai Skill')
if (agentsResult.success) {
stats.agents = agentsResult.count || 0
const skillsResult = await getCount('Local Ai Skill')
if (skillsResult.success) {
stats.skills = skillsResult.count || 0
}
//

View File

@ -1,10 +1,10 @@
<template>
<div class="my-published-agents">
<div class="my-published-skills">
<div class="page-header">
<div class="header-content">
<div class="header-text">
<h1>{{ t('My Published Agents') }}</h1>
<p>{{ t('Manage your published agents in the marketplace') }}</p>
<h1>{{ t('My Published Skills') }}</h1>
<p>{{ t('Manage your published skills in the marketplace') }}</p>
</div>
</div>
</div>
@ -14,17 +14,17 @@
<div class="search-bar">
<n-input
v-model:value="searchQuery"
:placeholder="t('Search agents...')"
:placeholder="t('Search skills...')"
clearable
size="large"
@keyup.enter="loadAgents"
@keyup.enter="loadSkills"
class="search-input"
>
<template #prefix>
<n-icon><Icon icon="tabler:search" /></n-icon>
</template>
</n-input>
<n-button type="primary" size="large" @click="loadAgents" class="search-button">
<n-button type="primary" size="large" @click="loadSkills" class="search-button">
<template #icon>
<n-icon><Icon icon="tabler:search" /></n-icon>
</template>
@ -33,10 +33,9 @@
</div>
</div>
<div class="agents-section" v-if="!loading && agents.length > 0">
<!-- 排序控件 -->
<div class="agents-header">
<div class="agents-title">
<div class="skills-section" v-if="!loading && skills.length > 0">
<div class="skills-header">
<div class="skills-title">
</div>
<div class="sort-controls">
<n-select
@ -44,59 +43,57 @@
:options="sortOptions"
:placeholder="t('Sort by')"
style="width: 150px"
@update:value="loadAgents"
@update:value="loadSkills"
/>
</div>
</div>
<div class="agents-grid">
<div v-for="agent in agents" :key="agent.name" class="skill-card">
<!-- 智能体图标 -->
<div class="skill-icon" @click="viewAgentDetail(agent)">
<div class="skills-grid">
<div v-for="skill in skills" :key="skill.name" class="skill-card">
<div class="skill-icon" @click="viewSkillDetail(skill)">
<Icon
v-if="agent.icon"
:icon="agent.icon"
v-if="skill.icon"
:icon="skill.icon"
:width="64"
:height="64"
:style="{ color: agent.color || '#6b7280' }"
:style="{ color: skill.color || '#6b7280' }"
/>
<div v-else class="skill-icon-placeholder">
<n-icon size="64"><Icon icon="hugeicons:robotic" /></n-icon>
</div>
</div>
<!-- 智能体信息 -->
<div class="skill-content">
<div class="skill-header">
<div class="skill-title-section">
<h3 @click="viewAgentDetail(agent)" class="clickable-title">{{ agent.title || agent.skill_name || agent.name }}</h3>
<h3 @click="viewSkillDetail(skill)" class="clickable-title">{{ skill.title || skill.skill_name || skill.name }}</h3>
<div class="skill-meta">
<div class="skill-team" v-if="agent.team">
<div class="skill-team" v-if="skill.team">
<n-icon><Icon icon="tabler:users" /></n-icon>
<span>{{ agent.team }}</span>
<span>{{ skill.team }}</span>
</div>
<span v-if="agent.status" class="status-badge" :class="getStatusClass(agent.status)">
{{ t(agent.status) }}
<span v-if="skill.status" class="status-badge" :class="getStatusClass(skill.status)">
{{ t(skill.status) }}
</span>
</div>
</div>
<div class="skill-name" v-if="agent.skill_name">
{{ agent.skill_name }}
<div class="skill-name" v-if="skill.skill_name">
{{ skill.skill_name }}
</div>
</div>
<div class="skill-description" v-if="agent.description">
{{ truncateText(agent.description, 80) }}
<div class="skill-description" v-if="skill.description">
{{ truncateText(skill.description, 80) }}
</div>
</div>
<div class="skill-actions">
<n-button type="default" @click="viewAgentDetail(agent)">
<n-button type="default" @click="viewSkillDetail(skill)">
{{ t('View Details') }}
</n-button>
<n-button
type="error"
@click="deleteAgent(agent)"
@click="deleteSkill(skill)"
>
{{ t('Delete') }}
</n-button>
@ -104,7 +101,6 @@
</div>
</div>
<!-- 分页 -->
<div class="pagination-container">
<n-pagination
v-model:page="page"
@ -113,7 +109,7 @@
show-size-picker
:page-sizes="[20, 50, 100]"
:page-size="pageSize"
@update:page="loadAgents"
@update:page="loadSkills"
@update:page-size="handlePageSizeChange"
/>
</div>
@ -121,12 +117,12 @@
<div v-if="loading" class="loading">
<n-spin size="large">
<template #description>{{ t('Loading agents...') }}</template>
<template #description>{{ t('Loading skills...') }}</template>
</n-spin>
</div>
<div v-if="!loading && agents.length === 0" class="empty">
<n-empty :description="t('No agents found')">
<div v-if="!loading && skills.length === 0" class="empty">
<n-empty :description="t('No skills found')">
<template #icon>
<n-icon><Icon icon="hugeicons:robotic" /></n-icon>
</template>
@ -150,13 +146,12 @@ const router = useRouter()
const searchQuery = ref('')
const loading = ref(false)
const agents = ref<any[]>([])
const skills = ref<any[]>([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(parseInt(localStorage.getItem('itemsPerPage') || '20'))
const sortBy = ref('creation desc')
//
const sortOptions = computed(() => [
{ label: t('Latest'), value: 'creation desc' },
{ label: t('Oldest'), value: 'creation asc' },
@ -165,10 +160,9 @@ const sortOptions = computed(() => [
{ label: t('Most Popular'), value: 'modified desc' }
])
//
const pageCount = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
async function loadAgents() {
async function loadSkills() {
loading.value = true
try {
const params = new URLSearchParams({
@ -178,22 +172,20 @@ async function loadAgents() {
sort_by: sortBy.value
})
const response = await axios.get(`/jingrow/my-published-agents?${params}`)
const response = await axios.get(`/jingrow/my-published-skills?${params}`)
const data = response.data
// API
if (data.items) {
agents.value = data.items
skills.value = data.items
total.value = data.total || 0
} else {
// API
agents.value = data || []
total.value = agents.value.length
skills.value = data || []
total.value = skills.value.length
}
} catch (error) {
console.error('Failed to load agents:', error)
message.error(t('Failed to load agents'))
agents.value = []
console.error('Failed to load skills:', error)
message.error(t('Failed to load skills'))
skills.value = []
total.value = 0
} finally {
loading.value = false
@ -204,14 +196,13 @@ function handlePageSizeChange(newPageSize: number) {
pageSize.value = newPageSize
page.value = 1
localStorage.setItem('itemsPerPage', newPageSize.toString())
loadAgents()
loadSkills()
}
function viewAgentDetail(agent: any) {
//
function viewSkillDetail(skill: any) {
router.push({
path: `/skill-marketplace/${agent.name}`,
query: { returnTo: '/my-published-agents' }
path: `/skill-marketplace/${skill.name}`,
query: { returnTo: '/my-published-skills' }
})
}
@ -223,23 +214,20 @@ function truncateText(text: string, maxLength: number): string {
function getStatusClass(status: string): string {
if (!status) return ''
//
return status.toLowerCase().replace(/\s+/g, '-')
}
async function deleteAgent(agent: any) {
// 使name
const recordName = agent.name
async function deleteSkill(skill: any) {
const recordName = skill.name
if (!recordName) {
message.error(t('Agent name does not exist'))
message.error(t('Skill name does not exist'))
return
}
//
const agentTitle = agent.title || agent.skill_name || recordName
const skillTitle = skill.title || skill.skill_name || recordName
dialog.warning({
title: t('Confirm Delete'),
content: t('Are you sure you want to delete agent "{0}"? This action cannot be undone.').replace('{0}', agentTitle),
content: t('Are you sure you want to delete skill "{0}"? This action cannot be undone.').replace('{0}', skillTitle),
positiveText: t('Confirm Delete'),
negativeText: t('Cancel'),
onPositiveClick: async () => {
@ -250,21 +238,19 @@ async function deleteAgent(agent: any) {
async function performDelete(skillName: string) {
try {
// API
const response = await axios.post('/jingrow/delete-published-agent', {
const response = await axios.post('/jingrow/delete-published-skill', {
name: skillName
})
if (response.data && response.data.success) {
message.success(response.data.message || t('Agent deleted successfully'))
//
loadAgents()
message.success(response.data.message || t('Skill deleted successfully'))
loadSkills()
} else {
const errorMsg = response.data?.message || response.data?.error || t('Delete failed')
message.error(errorMsg)
}
} catch (error: any) {
console.error('Delete agent error:', error)
console.error('Delete skill error:', error)
const errorMsg = error.response?.data?.detail ||
error.response?.data?.message ||
error.message ||
@ -274,32 +260,29 @@ async function performDelete(skillName: string) {
}
onMounted(() => {
loadAgents()
loadSkills()
})
//
watch([searchQuery, sortBy], () => {
page.value = 1 //
loadAgents()
page.value = 1
loadSkills()
}, { deep: true })
//
watch([page], () => {
loadAgents()
loadSkills()
})
//
watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
if (newValue) {
pageSize.value = parseInt(newValue)
page.value = 1 //
loadAgents()
page.value = 1
loadSkills()
}
})
</script>
<style scoped>
.my-published-agents {
.my-published-skills {
padding: 24px;
}
@ -332,18 +315,18 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
margin-bottom: 32px;
}
.agents-section {
.skills-section {
margin-bottom: 32px;
}
.agents-header {
.skills-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 24px;
}
.agents-title h2 {
.skills-title h2 {
margin: 0;
font-size: 20px;
font-weight: 600;
@ -404,7 +387,7 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.agents-grid {
.skills-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
@ -598,7 +581,6 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
min-height: 300px;
}
/* 响应式设计 */
@media (max-width: 768px) {
.header-content {
flex-direction: column;
@ -625,7 +607,7 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
width: 100%;
}
.agents-grid {
.skills-grid {
grid-template-columns: 1fr;
gap: 16px;
}
@ -639,4 +621,3 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
}
}
</style>

View File

@ -3,7 +3,7 @@
<div class="page-header">
<div class="header-content">
<div class="header-text">
<h1>{{ agent?.title || agent?.skill_name || t('Agent Details') }}</h1>
<h1>{{ skill?.title || skill?.skill_name || t('Skill Details') }}</h1>
</div>
<div class="header-actions">
<n-button @click="goBack" size="medium">
@ -13,16 +13,16 @@
{{ t('Back') }}
</n-button>
<n-button
:type="isCurrentAgentInstalled ? 'warning' : 'primary'"
@click="installAgent"
:type="isCurrentSkillInstalled ? 'warning' : 'primary'"
@click="installSkill"
size="medium"
>
<template #icon>
<n-icon>
<Icon :icon="isCurrentAgentInstalled ? 'tabler:check' : 'tabler:download'" />
<Icon :icon="isCurrentSkillInstalled ? 'tabler:check' : 'tabler:download'" />
</n-icon>
</template>
{{ isCurrentAgentInstalled ? t('Installed') : t('Install') }}
{{ isCurrentSkillInstalled ? t('Installed') : t('Install') }}
</n-button>
</div>
</div>
@ -31,7 +31,7 @@
<div v-if="loading" class="loading-container">
<n-spin size="large">
<template #description>
{{ t('Loading agent details...') }}
{{ t('Loading skill details...') }}
</template>
</n-spin>
</div>
@ -44,21 +44,18 @@
</n-empty>
</div>
<div v-else-if="agent" class="skill-content">
<!-- 整体卡片布局 -->
<div v-else-if="skill" class="skill-content">
<div class="skill-card">
<!-- 上部分智能体信息 -->
<div class="skill-info-section">
<div class="skill-content-layout">
<!-- 左侧智能体图标 -->
<div class="skill-image-section">
<div class="skill-image">
<div v-if="agent.icon" class="skill-icon-container">
<div v-if="skill.icon" class="skill-icon-container">
<Icon
:icon="agent.icon"
:icon="skill.icon"
:width="120"
:height="120"
:style="{ color: agent.color || '#6b7280' }"
:style="{ color: skill.color || '#6b7280' }"
/>
</div>
<div v-else class="placeholder-image">
@ -67,60 +64,57 @@
</div>
</div>
<!-- 右侧智能体信息 -->
<div class="skill-info-content">
<div class="skill-header">
<h2 class="skill-title">{{ agent.title || agent.skill_name || t('Untitled Agent') }}</h2>
<div v-if="agent.subtitle" class="skill-subtitle">{{ agent.subtitle }}</div>
<h2 class="skill-title">{{ skill.title || skill.skill_name || t('Untitled Skill') }}</h2>
<div v-if="skill.subtitle" class="skill-subtitle">{{ skill.subtitle }}</div>
</div>
<div class="info-list">
<div v-if="agent.skill_name" class="info-item">
<span class="label">{{ t('Agent Name') }}:</span>
<span class="value">{{ agent.skill_name }}</span>
<div v-if="skill.skill_name" class="info-item">
<span class="label">{{ t('Skill Name') }}:</span>
<span class="value">{{ skill.skill_name }}</span>
</div>
<div v-if="agent.status" class="info-item">
<div v-if="skill.status" class="info-item">
<span class="label">{{ t('Status') }}:</span>
<span class="value">{{ agent.status }}</span>
<span class="value">{{ skill.status }}</span>
</div>
<div v-if="agent.trigger_mode" class="info-item">
<div v-if="skill.trigger_mode" class="info-item">
<span class="label">{{ t('Trigger Mode') }}:</span>
<span class="value">{{ agent.trigger_mode }}</span>
<span class="value">{{ skill.trigger_mode }}</span>
</div>
<div v-if="agent.creation" class="info-item">
<div v-if="skill.creation" class="info-item">
<span class="label">{{ t('Created') }}:</span>
<span class="value">{{ formatDate(agent.creation) }}</span>
<span class="value">{{ formatDate(skill.creation) }}</span>
</div>
<div v-if="agent.modified" class="info-item">
<div v-if="skill.modified" class="info-item">
<span class="label">{{ t('Last Updated') }}:</span>
<span class="value">{{ formatDate(agent.modified) }}</span>
<span class="value">{{ formatDate(skill.modified) }}</span>
</div>
</div>
</div>
</div>
</div>
<!-- 下部分描述内容 -->
<div v-if="agent.description" class="description-section">
<div v-if="skill.description" class="description-section">
<h3>{{ t('Description') }}</h3>
<div class="description-content" v-html="agent.description"></div>
<div class="description-content" v-html="skill.description"></div>
</div>
</div>
</div>
<!-- 安装进度弹窗 -->
<InstallProgressModal
v-model="showProgressModal"
:progress="installProgress"
:message="installMessage"
:status="installStatus"
:installing="installing"
:title="t('Installing Agent')"
:title="t('Installing Skill')"
/>
</div>
</template>
@ -141,36 +135,33 @@ const dialog = useDialog()
const loading = ref(true)
const error = ref('')
const agent = ref<any>(null)
const skill = ref<any>(null)
//
const installing = ref(false)
const installProgress = ref(0)
const installMessage = ref('')
const installStatus = ref<'success' | 'error' | 'info'>('info')
const showProgressModal = ref(false)
//
const installedAgentNames = ref<Set<string>>(new Set())
const installedSkillNames = ref<Set<string>>(new Set())
const skillName = computed(() => route.params.name as string)
//
const isCurrentAgentInstalled = computed(() => {
if (!agent.value) return false
return isAgentInstalled(agent.value.skill_name || agent.value.name || '')
const isCurrentSkillInstalled = computed(() => {
if (!skill.value) return false
return isSkillInstalled(skill.value.skill_name || skill.value.name || '')
})
async function loadAgentDetail() {
async function loadSkillDetail() {
loading.value = true
error.value = ''
try {
const response = await axios.get(`/jingrow/skill-marketplace/${skillName.value}`)
agent.value = response.data
skill.value = response.data
} catch (err: any) {
console.error('Failed to load agent detail:', err)
error.value = err.response?.data?.detail || t('Failed to load agent details')
console.error('Failed to load skill detail:', err)
error.value = err.response?.data?.detail || t('Failed to load skill details')
} finally {
loading.value = false
}
@ -186,39 +177,34 @@ function formatDate(dateString: string): string {
}
function goBack() {
//
const returnTo = route.query.returnTo as string
if (returnTo) {
router.push(returnTo)
return
}
//
if (window.history.length > 1) {
router.back()
} else {
//
router.push('/skill-marketplace')
}
}
async function installAgent() {
if (!agent.value?.skill_flow && !agent.value?.skill_name) {
message.error(t('Agent flow data or name is missing'))
async function installSkill() {
if (!skill.value?.skill_flow && !skill.value?.skill_name) {
message.error(t('Skill flow data or name is missing'))
return
}
//
try {
const skillNameValue = agent.value.skill_name || agent.value.name
const skillNameValue = skill.value.skill_name || skill.value.name
if (skillNameValue) {
const checkResponse = await axios.get(`/jingrow/check-agent/${skillNameValue}`)
const checkResponse = await axios.get(`/jingrow/check-skill/${skillNameValue}`)
if (checkResponse.data.exists) {
//
dialog.warning({
title: t('Agent already exists'),
content: t('Agent "{0}" is already installed, do you want to overwrite?').replace('{0}', skillNameValue),
title: t('Skill already exists'),
content: t('Skill "{0}" is already installed, do you want to overwrite?').replace('{0}', skillNameValue),
positiveText: t('Confirm Overwrite'),
negativeText: t('Cancel'),
onPositiveClick: () => {
@ -229,7 +215,7 @@ async function installAgent() {
}
}
} catch (error) {
console.error('Check agent exists error:', error)
console.error('Check skill exists error:', error)
}
performInstall()
@ -243,27 +229,24 @@ async function performInstall() {
installStatus.value = 'info'
showProgressModal.value = true
//
let skillFlow = agent.value.skill_flow
let skillNameValue = agent.value.skill_name || agent.value.name
let skillFlow = skill.value.skill_flow
let skillNameValue = skill.value.skill_name || skill.value.name
if (!skillFlow) {
throw new Error(t('Agent flow data is missing'))
throw new Error(t('Skill flow data is missing'))
}
installProgress.value = 30
installMessage.value = t('Installing agent...')
installMessage.value = t('Installing skill...')
// skill_flow JSON
if (typeof skillFlow === 'string') {
try {
skillFlow = JSON.parse(skillFlow)
} catch (e) {
//
}
}
const response = await axios.post('/jingrow/install-agent', {
const response = await axios.post('/jingrow/install-skill', {
skill_name: skillNameValue,
skill_flow: skillFlow
}, {
@ -272,18 +255,15 @@ async function performInstall() {
}
})
//
installProgress.value = 100
if (response.data.success) {
//
installing.value = false
installStatus.value = 'success'
installMessage.value = t('Agent installed successfully!')
message.success(t('Agent installed successfully'))
installMessage.value = t('Skill installed successfully!')
message.success(t('Skill installed successfully'))
//
loadInstalledAgents()
loadInstalledSkills()
setTimeout(() => {
showProgressModal.value = false
@ -292,7 +272,7 @@ async function performInstall() {
throw new Error(response.data.error || t('安装失败'))
}
} catch (error: any) {
console.error('Install agent error:', error)
console.error('Install skill error:', error)
installing.value = false
installStatus.value = 'error'
installMessage.value = error.response?.data?.detail || error.message || t('Installation failed')
@ -304,32 +284,29 @@ async function performInstall() {
}
}
//
async function loadInstalledAgents() {
async function loadInstalledSkills() {
try {
const response = await axios.get('/jingrow/installed-skill-names')
if (response.data.success) {
const agents = response.data.agents || []
installedAgentNames.value = new Set(agents.map((a: string) => a.toLowerCase()))
const skills = response.data.skills || []
installedSkillNames.value = new Set(skills.map((a: string) => a.toLowerCase()))
}
} catch (error) {
console.error('Load installed agents error:', error)
console.error('Load installed skills error:', error)
}
}
//
function isAgentInstalled(skillNameValue: string): boolean {
function isSkillInstalled(skillNameValue: string): boolean {
if (!skillNameValue) return false
return installedAgentNames.value.has(skillNameValue.toLowerCase())
return installedSkillNames.value.has(skillNameValue.toLowerCase())
}
onMounted(() => {
loadAgentDetail()
loadInstalledAgents()
loadSkillDetail()
loadInstalledSkills()
//
window.addEventListener('installedAgentsUpdated', () => {
loadInstalledAgents()
window.addEventListener('installedSkillsUpdated', () => {
loadInstalledSkills()
})
})
</script>
@ -382,7 +359,6 @@ onMounted(() => {
gap: 32px;
}
/* 整体卡片布局 */
.skill-card {
background: white;
border-radius: 8px;
@ -393,8 +369,6 @@ onMounted(() => {
gap: 24px;
}
/* 上部分:智能体信息 */
.skill-content-layout {
display: grid;
grid-template-columns: 1fr 2fr;
@ -493,7 +467,6 @@ onMounted(() => {
font-style: italic;
}
/* 下部分:描述内容 */
.description-section {
padding-top: 24px;
border-top: 1px solid #e5e7eb;
@ -598,4 +571,3 @@ onMounted(() => {
}
}
</style>

View File

@ -3,8 +3,8 @@
<div class="page-header">
<div class="header-content">
<div class="header-text">
<h1>{{ t('Agent Marketplace') }}</h1>
<p>{{ t('Browse and install agents from Jingrow Agent Marketplace') }}</p>
<h1>{{ t('Skill Marketplace') }}</h1>
<p>{{ t('Browse and install skills from Jingrow Skill Marketplace') }}</p>
</div>
</div>
</div>
@ -14,17 +14,17 @@
<div class="search-bar">
<n-input
v-model:value="searchQuery"
:placeholder="t('Search agents...')"
:placeholder="t('Search skills...')"
clearable
size="large"
@keyup.enter="loadAgents"
@keyup.enter="loadSkills"
class="search-input"
>
<template #prefix>
<n-icon><Icon icon="tabler:search" /></n-icon>
</template>
</n-input>
<n-button type="primary" size="large" @click="loadAgents" class="search-button">
<n-button type="primary" size="large" @click="loadSkills" class="search-button">
<template #icon>
<n-icon><Icon icon="tabler:search" /></n-icon>
</template>
@ -33,10 +33,9 @@
</div>
</div>
<div class="agents-section" v-if="!loading && agents.length > 0">
<!-- 排序控件 -->
<div class="agents-header">
<div class="agents-title">
<div class="skills-section" v-if="!loading && skills.length > 0">
<div class="skills-header">
<div class="skills-title">
</div>
<div class="sort-controls">
<n-select
@ -44,58 +43,56 @@
:options="sortOptions"
:placeholder="t('Sort by')"
style="width: 150px"
@update:value="loadAgents"
@update:value="loadSkills"
/>
</div>
</div>
<div class="agents-grid">
<div v-for="agent in agents" :key="agent.name" class="skill-card">
<!-- 智能体图标 -->
<div class="skill-icon" @click="viewAgentDetail(agent)">
<div class="skills-grid">
<div v-for="skill in skills" :key="skill.name" class="skill-card">
<div class="skill-icon" @click="viewSkillDetail(skill)">
<Icon
v-if="agent.icon"
:icon="agent.icon"
v-if="skill.icon"
:icon="skill.icon"
:width="48"
:height="48"
:style="{ color: agent.color || '#6b7280' }"
:style="{ color: skill.color || '#6b7280' }"
/>
<div v-else class="skill-icon-placeholder">
<n-icon size="48"><Icon icon="hugeicons:robotic" /></n-icon>
</div>
</div>
<!-- 智能体信息 -->
<div class="skill-content">
<div class="skill-header">
<div class="skill-title-section">
<h3 @click="viewAgentDetail(agent)" class="clickable-title">{{ agent.title || agent.skill_name || agent.name }}</h3>
<div class="skill-name" v-if="agent.skill_name">
{{ agent.skill_name }}
<h3 @click="viewSkillDetail(skill)" class="clickable-title">{{ skill.title || skill.skill_name || skill.name }}</h3>
<div class="skill-name" v-if="skill.skill_name">
{{ skill.skill_name }}
</div>
</div>
</div>
<div class="skill-description" v-if="agent.description">
{{ truncateText(agent.description, 80) }}
<div class="skill-description" v-if="skill.description">
{{ truncateText(skill.description, 80) }}
</div>
</div>
<div class="skill-actions">
<n-button type="default" @click="viewAgentDetail(agent)">
<n-button type="default" @click="viewSkillDetail(skill)">
{{ t('View Details') }}
</n-button>
<n-button
v-if="isAgentInstalled(agent.skill_name || agent.name)"
v-if="isSkillInstalled(skill.skill_name || skill.name)"
type="warning"
@click="installAgent(agent)"
@click="installSkill(skill)"
>
{{ t('Installed') }}
</n-button>
<n-button
v-else
type="primary"
@click="installAgent(agent)"
@click="installSkill(skill)"
>
{{ t('Install') }}
</n-button>
@ -103,7 +100,6 @@
</div>
</div>
<!-- 分页 -->
<div class="pagination-container">
<n-pagination
v-model:page="page"
@ -112,7 +108,7 @@
show-size-picker
:page-sizes="[20, 50, 100]"
:page-size="pageSize"
@update:page="loadAgents"
@update:page="loadSkills"
@update:page-size="handlePageSizeChange"
/>
</div>
@ -120,12 +116,12 @@
<div v-if="loading" class="loading">
<n-spin size="large">
<template #description>{{ t('Loading agents...') }}</template>
<template #description>{{ t('Loading skills...') }}</template>
</n-spin>
</div>
<div v-if="!loading && agents.length === 0" class="empty">
<n-empty :description="t('No agents found')">
<div v-if="!loading && skills.length === 0" class="empty">
<n-empty :description="t('No skills found')">
<template #icon>
<n-icon><Icon icon="hugeicons:robotic" /></n-icon>
</template>
@ -133,14 +129,13 @@
</div>
</div>
<!-- 安装进度弹窗 -->
<InstallProgressModal
v-model="showProgressModal"
:progress="installProgress"
:message="installMessage"
:status="installStatus"
:installing="installing"
:title="t('Installing Agent')"
:title="t('Installing Skill')"
/>
</div>
</template>
@ -160,23 +155,20 @@ const router = useRouter()
const searchQuery = ref('')
const loading = ref(false)
const agents = ref<any[]>([])
const skills = ref<any[]>([])
const total = ref(0)
const page = ref(1)
const pageSize = ref(parseInt(localStorage.getItem('itemsPerPage') || '20'))
const sortBy = ref('creation desc')
//
const installing = ref(false)
const installProgress = ref(0)
const installMessage = ref('')
const installStatus = ref<'success' | 'error' | 'info'>('info')
const showProgressModal = ref(false)
//
const installedAgentNames = ref<Set<string>>(new Set())
const installedSkillNames = ref<Set<string>>(new Set())
//
const sortOptions = computed(() => [
{ label: t('Latest'), value: 'creation desc' },
{ label: t('Oldest'), value: 'creation asc' },
@ -185,10 +177,9 @@ const sortOptions = computed(() => [
{ label: t('Most Popular'), value: 'modified desc' }
])
//
const pageCount = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
async function loadAgents() {
async function loadSkills() {
loading.value = true
try {
const params = new URLSearchParams({
@ -201,19 +192,17 @@ async function loadAgents() {
const response = await axios.get(`/jingrow/skill-marketplace?${params}`)
const data = response.data
// API
if (data.items) {
agents.value = data.items
skills.value = data.items
total.value = data.total || 0
} else {
// API
agents.value = data || []
total.value = agents.value.length
skills.value = data || []
total.value = skills.value.length
}
} catch (error) {
console.error('Failed to load agents:', error)
message.error(t('Failed to load agents'))
agents.value = []
console.error('Failed to load skills:', error)
message.error(t('Failed to load skills'))
skills.value = []
total.value = 0
} finally {
loading.value = false
@ -224,51 +213,48 @@ function handlePageSizeChange(newPageSize: number) {
pageSize.value = newPageSize
page.value = 1
localStorage.setItem('itemsPerPage', newPageSize.toString())
loadAgents()
loadSkills()
}
function viewAgentDetail(agent: any) {
//
function viewSkillDetail(skill: any) {
router.push({
path: `/skill-marketplace/${agent.name}`,
path: `/skill-marketplace/${skill.name}`,
query: { returnTo: '/skill-marketplace' }
})
}
async function installAgent(agent: any) {
if (!agent.skill_flow && !agent.skill_name) {
message.error(t('Agent flow data or name is missing'))
async function installSkill(skill: any) {
if (!skill.skill_flow && !skill.skill_name) {
message.error(t('Skill flow data or name is missing'))
return
}
//
try {
const skillName = agent.skill_name || agent.name
const skillName = skill.skill_name || skill.name
if (skillName) {
const checkResponse = await axios.get(`/jingrow/check-agent/${skillName}`)
const checkResponse = await axios.get(`/jingrow/check-skill/${skillName}`)
if (checkResponse.data.exists) {
//
dialog.warning({
title: t('Agent already exists'),
content: t('Agent "{0}" is already installed, do you want to overwrite?').replace('{0}', skillName),
title: t('Skill already exists'),
content: t('Skill "{0}" is already installed, do you want to overwrite?').replace('{0}', skillName),
positiveText: t('Confirm Overwrite'),
negativeText: t('Cancel'),
onPositiveClick: () => {
performInstall(agent)
performInstall(skill)
}
})
return
}
}
} catch (error) {
console.error('Check agent exists error:', error)
console.error('Check skill exists error:', error)
}
performInstall(agent)
performInstall(skill)
}
async function performInstall(agent: any) {
async function performInstall(skill: any) {
try {
installing.value = true
installProgress.value = 0
@ -276,41 +262,38 @@ async function performInstall(agent: any) {
installStatus.value = 'info'
showProgressModal.value = true
//
let skillFlow = agent.skill_flow
let skillName = agent.skill_name || agent.name
let skillFlow = skill.skill_flow
let skillName = skill.skill_name || skill.name
if (!skillFlow) {
installMessage.value = t('Fetching agent details...')
installMessage.value = t('Fetching skill details...')
installProgress.value = 20
try {
const detailResponse = await axios.get(`/jingrow/skill-marketplace/${agent.name}`)
const detailResponse = await axios.get(`/jingrow/skill-marketplace/${skill.name}`)
const detail = detailResponse.data
skillFlow = detail.skill_flow
skillName = detail.skill_name || skillName
} catch (error) {
throw new Error(t('Failed to fetch agent details'))
throw new Error(t('Failed to fetch skill details'))
}
}
if (!skillFlow) {
throw new Error(t('Agent flow data is missing'))
throw new Error(t('Skill flow data is missing'))
}
installProgress.value = 40
installMessage.value = t('Installing agent...')
installMessage.value = t('Installing skill...')
// skill_flow JSON
if (typeof skillFlow === 'string') {
try {
skillFlow = JSON.parse(skillFlow)
} catch (e) {
//
}
}
const response = await axios.post('/jingrow/install-agent', {
const response = await axios.post('/jingrow/install-skill', {
skill_name: skillName,
skill_flow: skillFlow
}, {
@ -319,18 +302,15 @@ async function performInstall(agent: any) {
}
})
//
installProgress.value = 100
if (response.data.success) {
//
installing.value = false
installStatus.value = 'success'
installMessage.value = t('Agent installed successfully!')
message.success(t('Agent installed successfully'))
installMessage.value = t('Skill installed successfully!')
message.success(t('Skill installed successfully'))
//
loadInstalledAgents()
loadInstalledSkills()
setTimeout(() => {
showProgressModal.value = false
@ -339,7 +319,7 @@ async function performInstall(agent: any) {
throw new Error(response.data.error || t('Installation failed'))
}
} catch (error: any) {
console.error('Install agent error:', error)
console.error('Install skill error:', error)
installing.value = false
installStatus.value = 'error'
installMessage.value = error.response?.data?.detail || error.message || t('Installation failed')
@ -357,52 +337,46 @@ function truncateText(text: string, maxLength: number): string {
return text.substring(0, maxLength) + '...'
}
//
async function loadInstalledAgents() {
async function loadInstalledSkills() {
try {
const response = await axios.get('/jingrow/installed-skill-names')
if (response.data.success) {
const agents = response.data.agents || []
installedAgentNames.value = new Set(agents.map((a: string) => a.toLowerCase()))
const skills = response.data.skills || []
installedSkillNames.value = new Set(skills.map((a: string) => a.toLowerCase()))
}
} catch (error) {
console.error('Load installed agents error:', error)
console.error('Load installed skills error:', error)
}
}
//
function isAgentInstalled(skillName: string): boolean {
function isSkillInstalled(skillName: string): boolean {
if (!skillName) return false
return installedAgentNames.value.has(skillName.toLowerCase())
return installedSkillNames.value.has(skillName.toLowerCase())
}
onMounted(() => {
loadAgents()
loadInstalledAgents()
loadSkills()
loadInstalledSkills()
//
window.addEventListener('installedAgentsUpdated', () => {
loadInstalledAgents()
window.addEventListener('installedSkillsUpdated', () => {
loadInstalledSkills()
})
})
//
watch([sortBy], () => {
page.value = 1 //
loadAgents()
page.value = 1
loadSkills()
}, { deep: true })
//
watch([page], () => {
loadAgents()
loadSkills()
})
//
watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
if (newValue) {
pageSize.value = parseInt(newValue)
page.value = 1 //
loadAgents()
page.value = 1
loadSkills()
}
})
</script>
@ -441,11 +415,11 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
margin-bottom: 32px;
}
.agents-section {
.skills-section {
margin-bottom: 32px;
}
.agents-header {
.skills-header {
display: flex;
justify-content: space-between;
align-items: center;
@ -506,7 +480,7 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.3);
}
.agents-grid {
.skills-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));
gap: 24px;
@ -619,10 +593,9 @@ watch(() => localStorage.getItem('itemsPerPage'), (newValue) => {
}
@media (max-width: 768px) {
.agents-grid {
.skills-grid {
grid-template-columns: 1fr;
gap: 16px;
}
}
</style>

View File

@ -3,8 +3,8 @@
<div v-if="!hasValidParams" class="empty-state">
<div class="empty-content">
<h3>{{ t('Flow Builder') }}</h3>
<p>{{ t('Please enter from Agents page to open Flow Builder') }}</p>
<n-button type="primary" @click="goToAgents">{{ t('View Agents') }}</n-button>
<p>{{ t('Please enter from Skills page to open Flow Builder') }}</p>
<n-button type="primary" @click="goToSkills">{{ t('View Skills') }}</n-button>
</div>
</div>
<FlowBuilderContainer
@ -21,7 +21,7 @@ import { computed, onMounted } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { useMessage, NButton } from 'naive-ui'
import FlowBuilderContainer from '@/core/features/flows/FlowBuilderContainer.vue'
import { useSkillStore } from '@/shared/stores/agent'
import { useSkillStore } from '@/shared/stores/skill'
import { useFlowBuilderStore } from '@/shared/stores/flowBuilder'
import { slugToPageType } from '@/shared/utils/slug'
import { t } from '@/shared/i18n'
@ -34,7 +34,7 @@ const flowBuilderStore = useFlowBuilderStore()
// - URL
const name = computed(() => {
return (route.query.id as string) || (route.query.name as string) || flowBuilderStore.getAgentId()
return (route.query.id as string) || (route.query.name as string) || flowBuilderStore.getSkillId()
})
const pagetype = computed(() => {
@ -52,13 +52,13 @@ onMounted(async () => {
const urlName = (route.query.id as string) || (route.query.name as string)
const urlPageType = pagetype.value
if (urlName && !flowBuilderStore.getAgentId()) {
if (urlName && !flowBuilderStore.getSkillId()) {
try {
//
await skillStore.fetchSkill(urlName, urlPageType)
const agent = skillStore.currentSkill
if (agent?.skill_flow) {
let flowData = agent.skill_flow
const skill = skillStore.currentSkill
if (skill?.skill_flow) {
let flowData = skill.skill_flow
if (typeof flowData === 'string') {
flowData = JSON.parse(flowData)
}
@ -76,7 +76,7 @@ const handleSave = async (flowData: any) => {
if (name.value) {
//
await skillStore.updateSkill(name.value, { skill_flow: flowData }, pagetype.value)
message.success(t('Agent flow saved successfully'))
message.success(t('Skill flow saved successfully'))
// store
flowBuilderStore.deactivateFlowBuilder()
} else {
@ -88,8 +88,8 @@ const handleSave = async (flowData: any) => {
}
}
const goToAgents = () => {
router.push('/agents')
const goToSkills = () => {
router.push('/skills')
}
</script>

View File

@ -370,7 +370,7 @@ async function performPublish() {
skill_flow: flowData // axios JSON
}
const response = await axios.post('/jingrow/agent/publish', publishData, {
const response = await axios.post('/jingrow/skill/publish', publishData, {
headers: {
'Content-Type': 'application/json'
}

View File

@ -294,7 +294,7 @@ async function performPublish() {
skill_flow: flowData // axios JSON
}
const response = await axios.post('/jingrow/agent/publish', publishData, {
const response = await axios.post('/jingrow/skill/publish', publishData, {
headers: {
'Content-Type': 'application/json'
}

View File

@ -45,7 +45,7 @@
"fieldname": "action_type",
"fieldtype": "Select",
"label": "Action Type",
"options": "\ncreate_pagetype\ncreate_record\nupdate_record\nread_record\nexecute_agent\nsearch_records\nhttp_request",
"options": "\ncreate_pagetype\ncreate_record\nupdate_record\nread_record\nexecute_skill\nsearch_records\nhttp_request",
"reqd": 1
},
{

View File

@ -18,7 +18,7 @@ class AiChatAction(Page):
action_input: DF.JSON | None
action_output: DF.JSON | None
action_status: DF.Literal["pending", "running", "completed", "failed"]
action_type: DF.Literal["", "create_pagetype", "create_record", "update_record", "read_record", "execute_agent", "search_records", "http_request"]
action_type: DF.Literal["", "create_pagetype", "create_record", "update_record", "read_record", "execute_skill", "search_records", "http_request"]
chat: DF.Link
completed_at: DF.Datetime | None
error_message: DF.Text | None

View File

@ -9,7 +9,7 @@
"progress",
"section_break_xcls",
"column_break_glmw",
"skill_name"
"skill_name",
"ai_repeat",
"enabled",
"column_break_bzai",
@ -20,8 +20,8 @@
"target_module",
"condition",
"trigger_time",
"skill_flow_tab"
"skill_flow"
"skill_flow_tab",
"skill_flow",
"排除关键词_tab",
"exclude_prompts"
],

View File

@ -3,7 +3,7 @@
"creation": "2026-06-24 10:00:00.000000",
"engine": "InnoDB",
"field_order": [
"skill_name"
"skill_name",
"status",
"column_break_basic",
"start_time",

View File

@ -9,7 +9,7 @@
"progress",
"section_break_xcls",
"column_break_glmw",
"skill_name"
"skill_name",
"ai_repeat",
"enabled",
"column_break_bzai",
@ -20,8 +20,8 @@
"target_module",
"condition",
"trigger_time",
"skill_flow_tab"
"skill_flow"
"skill_flow_tab",
"skill_flow",
"排除关键词_tab",
"exclude_prompts"
],

View File

@ -50,7 +50,7 @@ class LocalAiSkill(Page):
"""
skill_id = self.name
cron = getattr(self, "trigger_time", None) or getattr(self, "cron_format", None)
method_name = f"jingrow/agents/execute_scheduled_skill:{skill_id}"
method_name = f"jingrow/skills/execute_scheduled_skill:{skill_id}"
# 检查是否存在对应的定时任务
job = jingrow.db.exists("Local Scheduled Job", {"method": method_name})

View File

@ -102,18 +102,17 @@ def execute(params: dict, context=None) -> dict:
if not template:
return {"success": False, "error": "AI 内容生成节点缺少模板配置 (ai_user_content)"}
# 构建模板上下文,注入 Agent 字段
template_context = inputs.copy()
agent_data = {}
skill_data = {}
if ctx and "flow_id" in ctx and ctx["flow_id"]:
try:
agent = jingrow.get_pg("Ai Skill", ctx["flow_id"])
if agent:
agent_data = agent.as_dict()
skill = jingrow.get_pg("Ai Skill", ctx["flow_id"])
if skill:
skill_data = skill.as_dict()
except Exception as e:
jingrow.log_error("ai_content_generation: agent data error", str(e))
jingrow.log_error("ai_content_generation: skill data error", str(e))
for key, value in agent_data.items():
for key, value in skill_data.items():
if key not in template_context or not template_context.get(key):
template_context[key] = value

View File

@ -198,15 +198,15 @@ def execute(params: dict, context=None) -> dict:
# 构建模板上下文
template_context = inputs.copy()
agent_data = {}
skill_data = {}
if ctx and "flow_id" in ctx and ctx["flow_id"]:
try:
agent = jingrow.get_pg("Ai Skill", ctx["flow_id"])
if agent:
agent_data = agent.as_dict()
skill = jingrow.get_pg("Ai Skill", ctx["flow_id"])
if skill:
skill_data = skill.as_dict()
except Exception as e:
jingrow.log_error("ai_image_generation: agent data error", str(e))
for key, value in agent_data.items():
jingrow.log_error("ai_image_generation: skill data error", str(e))
for key, value in skill_data.items():
if key not in template_context or not template_context.get(key):
template_context[key] = value

View File

@ -137,7 +137,7 @@ class ToolRegistry:
return result
@classmethod
def get_agent_definitions(cls) -> list:
def get_skill_definitions(cls) -> list:
"""返回 Agent 流程可用的 Tool 列表(含 UI 元数据)
用于 Agent 流程编辑器渲染节点面板

View File

@ -57,10 +57,10 @@ def get_local_ai_skills_list(page: int = 1, page_size: int = 10, fields: List =
if filters:
query_kwargs["filters"] = filters
agents = jingrow.get_list("Local Ai Skill", **query_kwargs)
skills = jingrow.get_list("Local Ai Skill", **query_kwargs)
return {
"items": agents or [],
"items": skills or [],
"total": total,
"page": page,
"page_size": page_size
@ -81,7 +81,7 @@ def get_local_ai_skill_detail(name: str) -> Optional[Dict]:
获取单个AI智能体详情
"""
try:
agents = jingrow.get_list(
skills = jingrow.get_list(
pagetype='Local Ai Skill',
fields=['name', 'skill_name', 'status', 'enabled', 'trigger_mode',
'progress', 'ai_repeat', 'creation', 'modified', 'modified_by',
@ -91,7 +91,7 @@ def get_local_ai_skill_detail(name: str) -> Optional[Dict]:
limit_page_length=1
)
if not agents:
if not skills:
return {
'success': False,
'message': 'Local Ai Skill不存在',
@ -101,7 +101,7 @@ def get_local_ai_skill_detail(name: str) -> Optional[Dict]:
return {
'success': True,
'message': '获取成功',
'data': agents[0]
'data': skills[0]
}
except Exception as e:
@ -186,19 +186,19 @@ def get_available_node_types() -> List[str]:
def update_local_ai_skill(name: str, data: Dict[str, Any]) -> Dict[str, Any]:
try:
agent = jingrow.get_pg('Local Ai Skill', name)
skill = jingrow.get_pg('Local Ai Skill', name)
for field, value in data.items():
if hasattr(agent, field):
field_type = getattr(agent.meta, 'get_field', lambda x: None)(field)
if hasattr(skill, field):
field_type = getattr(skill.meta, 'get_field', lambda x: None)(field)
if field_type and field_type.fieldtype == 'JSON' and isinstance(value, dict):
# JSON 字段保持格式化
import json
setattr(agent, field, json.dumps(value, ensure_ascii=False, indent=2))
setattr(skill, field, json.dumps(value, ensure_ascii=False, indent=2))
else:
setattr(agent, field, value)
setattr(skill, field, value)
# 保存更新
agent.save()
skill.save()
jingrow.db.commit()
return {
@ -220,14 +220,14 @@ def get_jflow_dashboard_stats() -> Dict[str, Any]:
"""
try:
# 获取AI智能体统计
agents_result = get_local_ai_skills_list(page=1, page_size=1000)
agents = agents_result.get('items', [])
skills_result = get_local_ai_skills_list(page=1, page_size=1000)
skills = skills_result.get('items', [])
stats = {
'total_agents': len(agents),
'enabled_agents': len([a for a in agents if a.get('enabled', False)]),
'active_agents': len([a for a in agents if a.get('status') == '进行中']),
'completed_agents': len([a for a in agents if a.get('status') == '已完成']),
'total_skills': len(skills),
'enabled_skills': len([a for a in skills if a.get('enabled', False)]),
'active_skills': len([a for a in skills if a.get('status') == '进行中']),
'completed_skills': len([a for a in skills if a.get('status') == '已完成']),
'total_nodes': len(get_available_node_types()),
'total_executions': 0, # 这里可以添加执行记录统计
'active_flows': 0 # 这里可以添加流程统计
@ -238,10 +238,10 @@ def get_jflow_dashboard_stats() -> Dict[str, Any]:
except Exception as e:
jingrow.log_error("获取仪表板统计失败", str(e))
return {
'total_agents': 0,
'enabled_agents': 0,
'active_agents': 0,
'completed_agents': 0,
'total_skills': 0,
'enabled_skills': 0,
'active_skills': 0,
'completed_skills': 0,
'total_nodes': 0,
'total_executions': 0,
'active_flows': 0

View File

@ -362,7 +362,7 @@ def get_directory_size(*path):
def _get_directory_size(*path):
folder = os.path.abspath(jingrow.get_site_path(*path))
# Copied as is from agent
# Copied as is from skill
total_size = os.path.getsize(folder)
for item in os.listdir(folder):
itempath = os.path.join(folder, item)

View File

@ -83,7 +83,7 @@ website_route_rules = [
{"from_route": "/publish-app", "to_route": "app"},
{"from_route": "/my-published-apps", "to_route": "app"},
{"from_route": "/my-published-nodes", "to_route": "app"},
{"from_route": "/my-published-agents", "to_route": "app"},
{"from_route": "/my-published-skills", "to_route": "app"},
{"from_route": "/my-published-tools", "to_route": "app"},
{"from_route": "/page/<path:page_path>", "to_route": "app"},
{"from_route": "/mindmap", "to_route": "app"},

View File

@ -18656,7 +18656,7 @@ msgstr "智能体 \"{0}\" 已安装,是否覆盖安装?"
msgid "Agent Detail"
msgstr "智能体详情"
msgid "Agent Marketplace"
msgid "Skill Marketplace"
msgstr "智能体市场"
msgid "Agent Name"
@ -18815,7 +18815,7 @@ msgstr "应用标题是必填项"
msgid "Applications"
msgstr "应用列表"
msgid "Are you sure to delete this agent?"
msgid "Are you sure to delete this skill?"
msgstr "确定要删除这个智能体吗?"
msgid "Are you sure to reset menus to default? This will overwrite current settings."
@ -18824,7 +18824,7 @@ msgstr "确定恢复默认菜单?当前自定义将被覆盖。"
msgid "Are you sure you want to delete"
msgstr "确定要删除"
msgid "Are you sure you want to delete agent \"{0}\"? This action cannot be undone."
msgid "Are you sure you want to delete skill \"{0}\"? This action cannot be undone."
msgstr "确定要删除智能体 \"{0}\" 吗?此操作不可恢复。"
msgid "Are you sure you want to delete all recent activities? This action cannot be undone."
@ -18836,7 +18836,7 @@ msgstr "确定删除菜单"
msgid "Are you sure you want to delete node \"{0}\"? This action cannot be undone."
msgstr "确定要删除节点 \"{0}\" 吗?此操作不可恢复。"
msgid "Are you sure you want to delete the selected agents? This action cannot be undone."
msgid "Are you sure you want to delete the selected skills? This action cannot be undone."
msgstr "确定要删除选中的智能体吗?此操作无法撤销。"
msgid "Are you sure you want to delete the selected jobs? This action cannot be undone."
@ -18869,7 +18869,7 @@ msgstr "确定要删除 {count} 条选中的记录吗?此操作无法撤销。
msgid "Are you sure you want to hide default tool"
msgstr "确定要隐藏默认工具"
msgid "Are you sure you want to publish agent \"{0}\" to the marketplace?"
msgid "Are you sure you want to publish skill \"{0}\" to the marketplace?"
msgstr "确定要将智能体 \"{0}\" 发布到市场吗?"
msgid "Are you sure you want to publish node \"{0}\" to the marketplace?"
@ -18971,7 +18971,7 @@ msgstr "布尔值"
msgid "Brief description"
msgstr "简要描述"
msgid "Browse and install agents from Jingrow Agent Marketplace"
msgid "Browse and install skills from Jingrow Skill Marketplace"
msgstr "浏览并安装来自 Jingrow 智能体市场的智能体"
msgid "Browse and install applications from Jingrow App Marketplace"
@ -19154,7 +19154,7 @@ msgstr "创建页面类型模板文件"
msgid "Create a modern, efficient template app"
msgstr "创建一个现代化、高效的应用模板"
msgid "Create a new AI agent"
msgid "Create a new AI skill"
msgstr "创建新的AI智能体"
msgid "Create a new application for the marketplace"
@ -19169,7 +19169,7 @@ msgstr "创建失败"
msgid "Create your account"
msgstr "创建您的账户"
msgid "Created a new agent"
msgid "Created a new skill"
msgstr "创建了新的智能体"
msgid "Created successfully"
@ -19475,7 +19475,7 @@ msgstr "下载失败"
msgid "Failed to export app package"
msgstr "导出应用安装包失败"
msgid "Failed to fetch agent details"
msgid "Failed to fetch skill details"
msgstr "获取智能体详情失败"
msgid "Failed to install app"
@ -19484,13 +19484,13 @@ msgstr "安装应用失败"
msgid "Failed to load Schema Editor"
msgstr "加载 Schema 编辑器失败"
msgid "Failed to load agent detail"
msgid "Failed to load skill detail"
msgstr "获取智能体详情失败"
msgid "Failed to load agent details"
msgid "Failed to load skill details"
msgstr "加载智能体详情失败"
msgid "Failed to load agents"
msgid "Failed to load skills"
msgstr "加载智能体失败"
msgid "Failed to load app details"
@ -19640,7 +19640,7 @@ msgstr "Feather"
msgid "Feedback & Suggestions"
msgstr "反馈建议"
msgid "Fetching agent details..."
msgid "Fetching skill details..."
msgstr "正在获取智能体详情..."
msgid "Field ID"
@ -19805,7 +19805,7 @@ msgstr "正在安装应用"
msgid "Installing Node"
msgstr "正在安装节点"
msgid "Installing agent..."
msgid "Installing skill..."
msgstr "正在安装智能体..."
msgid "Installing extension package..."
@ -19838,7 +19838,7 @@ msgstr "内部路径:/example"
msgid "Invalid JSON, please fix before saving"
msgstr "JSON 格式错误,请修正后再保存"
msgid "Invalid agent flow data format"
msgid "Invalid skill flow data format"
msgstr "智能体流程数据格式无效"
msgid "Is Featured"
@ -20012,10 +20012,10 @@ msgstr "让我们通过几个简单的步骤来设置您的系统"
msgid "Load failed"
msgstr "加载失败"
msgid "Loading agent details..."
msgid "Loading skill details..."
msgstr "加载智能体详情中..."
msgid "Loading agents..."
msgid "Loading skills..."
msgstr "加载智能体中..."
msgid "Loading application details..."
@ -20078,7 +20078,7 @@ msgstr "管理智能体"
msgid "Manage Nodes"
msgstr "管理节点"
msgid "Manage and configure your AI agents workflows"
msgid "Manage and configure your AI skills workflows"
msgstr "管理和配置您的AI智能体工作流"
msgid "Manage and configure your AI node components"
@ -20096,7 +20096,7 @@ msgstr "管理导航菜单项"
msgid "Manage your locally installed applications"
msgstr "管理您本地安装的应用"
msgid "Manage your published agents in the marketplace"
msgid "Manage your published skills in the marketplace"
msgstr "管理您在市场中发布的智能体"
msgid "Manage your published applications in the marketplace"
@ -20198,7 +20198,7 @@ msgstr "暂无工具"
msgid "No activities to delete"
msgstr "没有活动可删除"
msgid "No agents found"
msgid "No skills found"
msgstr "未找到智能体"
msgid "No applications found"
@ -20474,10 +20474,10 @@ msgstr "请输入有效的图片URL"
msgid "Please enter a valid phone number"
msgstr "请输入有效的手机号码"
msgid "Please enter agent description"
msgid "Please enter skill description"
msgstr "请输入智能体描述"
msgid "Please enter agent name"
msgid "Please enter skill name"
msgstr "请输入智能体名称"
msgid "Please enter an image URL"
@ -20522,7 +20522,7 @@ msgstr "请先登录后才能下载"
msgid "Please navigate to the path manually"
msgstr "请手动导航到该路径"
msgid "Please save the agent first"
msgid "Please save the skill first"
msgstr "请先保存智能体"
msgid "Please save the node first"
@ -20615,7 +20615,7 @@ msgstr "发布失败"
msgid "Publish failed, please check permission and server logs"
msgstr "发布失败,请检查权限和服务器日志"
msgid "Publish to Agent Marketplace"
msgid "Publish to Skill Marketplace"
msgstr "发布到智能体市场"
msgid "Publish to Marketplace"
@ -20798,7 +20798,7 @@ msgstr "定时任务"
msgid "Schema saved successfully"
msgstr "Schema 保存成功"
msgid "Search agents..."
msgid "Search skills..."
msgstr "搜索智能体..."
msgid "Search applications..."
@ -21287,7 +21287,7 @@ msgstr "查看结果"
msgid "View Workflow"
msgstr "查看工作流"
msgid "View and manage agent settings"
msgid "View and manage skill settings"
msgstr "查看和管理智能体配置"
msgid "View and manage scheduled job settings"
@ -22912,7 +22912,7 @@ msgstr "更新记录"
msgid "read_record"
msgstr "读取记录"
msgid "execute_agent"
msgid "execute_skill"
msgstr "执行智能体"
msgid "search_records"
@ -22963,7 +22963,7 @@ msgstr "skill_name不能为空"
msgid "Ai Agent {0} does not exist"
msgstr "Ai Agent {0} 不存在"
msgid "The agent execution task has been queued. Please check the execution status later."
msgid "The skill execution task has been queued. Please check the execution status later."
msgstr "智能体执行任务已加入队列,请稍后查看执行状态"
msgid "Ai Agent Execution Log creation failed"

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
.skill-marketplace[data-v-14e257e5]{padding:24px}.page-header[data-v-14e257e5]{margin-bottom:32px}.header-content[data-v-14e257e5]{display:flex;justify-content:space-between;align-items:flex-start}.header-text h1[data-v-14e257e5]{margin:0 0 8px;font-size:28px;font-weight:700;color:#1a1a1a}.header-text p[data-v-14e257e5]{margin:0;color:#666;font-size:16px}.search-container[data-v-14e257e5]{display:flex;justify-content:center;margin-bottom:32px}.agents-section[data-v-14e257e5]{margin-bottom:32px}.agents-header[data-v-14e257e5]{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.sort-controls[data-v-14e257e5]{display:flex;align-items:center;gap:12px}.pagination-container[data-v-14e257e5]{display:flex;justify-content:center;margin-top:32px;padding:20px 0}.search-bar[data-v-14e257e5]{display:flex;gap:16px;align-items:center;max-width:600px;width:100%;padding:20px;background:linear-gradient(135deg,#f8fafc,#f1f5f9);border-radius:20px;border:1px solid #e2e8f0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.search-input[data-v-14e257e5]{flex:1;min-width:0}.search-input .n-input[data-v-14e257e5]{border-radius:12px;border:1px solid #d1d5db;transition:all .2s ease}.search-input .n-input[data-v-14e257e5]:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.search-button[data-v-14e257e5]{border-radius:12px;font-weight:600;padding:0 24px;transition:all .2s ease}.search-button[data-v-14e257e5]:hover{transform:translateY(-1px);box-shadow:0 4px 12px #3b82f64d}.agents-grid[data-v-14e257e5]{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:24px}.skill-card[data-v-14e257e5]{border:1px solid #e5e7eb;border-radius:16px;background:#fff;overflow:hidden;transition:all .3s ease;box-shadow:0 1px 3px #0000001a}.skill-card[data-v-14e257e5]:hover{transform:translateY(-4px);box-shadow:0 8px 25px #00000026;border-color:#d1d5db}.skill-icon[data-v-14e257e5]{display:flex;align-items:center;justify-content:center;width:100%;height:120px;background:linear-gradient(135deg,#f3f4f6,#e5e7eb);cursor:pointer;padding:20px}.skill-icon-placeholder[data-v-14e257e5]{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:#9ca3af}.skill-content[data-v-14e257e5]{padding:20px}.skill-header[data-v-14e257e5]{margin-bottom:12px}.skill-title-section[data-v-14e257e5]{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.skill-title-section h3[data-v-14e257e5]{margin:0;font-size:18px;font-weight:600;color:#1f2937;line-height:1.2;flex:1;min-width:0}.clickable-title[data-v-14e257e5]{cursor:pointer;transition:color .2s ease}.clickable-title[data-v-14e257e5]:hover{color:#10b981}.skill-name[data-v-14e257e5]{color:#6b7280;font-size:11px;font-weight:500;font-family:SF Mono,Monaco,Menlo,Ubuntu Mono,monospace;background:#f3f4f6;border:1px solid #d1d5db;border-radius:8px;padding:4px 10px;display:inline-block;white-space:nowrap}.skill-description[data-v-14e257e5]{color:#6b7280;font-size:14px;line-height:1.5;margin-bottom:12px}.skill-actions[data-v-14e257e5]{padding:0 20px 20px;display:flex;gap:12px}.skill-actions .n-button[data-v-14e257e5]{flex:1}.loading[data-v-14e257e5],.empty[data-v-14e257e5]{display:flex;justify-content:center;align-items:center;min-height:300px}@media(max-width:768px){.agents-grid[data-v-14e257e5]{grid-template-columns:1fr;gap:16px}}
.skill-marketplace[data-v-14e257e5]{padding:24px}.page-header[data-v-14e257e5]{margin-bottom:32px}.header-content[data-v-14e257e5]{display:flex;justify-content:space-between;align-items:flex-start}.header-text h1[data-v-14e257e5]{margin:0 0 8px;font-size:28px;font-weight:700;color:#1a1a1a}.header-text p[data-v-14e257e5]{margin:0;color:#666;font-size:16px}.search-container[data-v-14e257e5]{display:flex;justify-content:center;margin-bottom:32px}.skills-section[data-v-14e257e5]{margin-bottom:32px}.skills-header[data-v-14e257e5]{display:flex;justify-content:space-between;align-items:center;margin-bottom:24px}.sort-controls[data-v-14e257e5]{display:flex;align-items:center;gap:12px}.pagination-container[data-v-14e257e5]{display:flex;justify-content:center;margin-top:32px;padding:20px 0}.search-bar[data-v-14e257e5]{display:flex;gap:16px;align-items:center;max-width:600px;width:100%;padding:20px;background:linear-gradient(135deg,#f8fafc,#f1f5f9);border-radius:20px;border:1px solid #e2e8f0;box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.search-input[data-v-14e257e5]{flex:1;min-width:0}.search-input .n-input[data-v-14e257e5]{border-radius:12px;border:1px solid #d1d5db;transition:all .2s ease}.search-input .n-input[data-v-14e257e5]:focus-within{border-color:#3b82f6;box-shadow:0 0 0 3px #3b82f61a}.search-button[data-v-14e257e5]{border-radius:12px;font-weight:600;padding:0 24px;transition:all .2s ease}.search-button[data-v-14e257e5]:hover{transform:translateY(-1px);box-shadow:0 4px 12px #3b82f64d}.skills-grid[data-v-14e257e5]{display:grid;grid-template-columns:repeat(auto-fill,minmax(320px,1fr));gap:24px}.skill-card[data-v-14e257e5]{border:1px solid #e5e7eb;border-radius:16px;background:#fff;overflow:hidden;transition:all .3s ease;box-shadow:0 1px 3px #0000001a}.skill-card[data-v-14e257e5]:hover{transform:translateY(-4px);box-shadow:0 8px 25px #00000026;border-color:#d1d5db}.skill-icon[data-v-14e257e5]{display:flex;align-items:center;justify-content:center;width:100%;height:120px;background:linear-gradient(135deg,#f3f4f6,#e5e7eb);cursor:pointer;padding:20px}.skill-icon-placeholder[data-v-14e257e5]{display:flex;align-items:center;justify-content:center;width:100%;height:100%;color:#9ca3af}.skill-content[data-v-14e257e5]{padding:20px}.skill-header[data-v-14e257e5]{margin-bottom:12px}.skill-title-section[data-v-14e257e5]{display:flex;align-items:center;gap:12px;flex-wrap:wrap}.skill-title-section h3[data-v-14e257e5]{margin:0;font-size:18px;font-weight:600;color:#1f2937;line-height:1.2;flex:1;min-width:0}.clickable-title[data-v-14e257e5]{cursor:pointer;transition:color .2s ease}.clickable-title[data-v-14e257e5]:hover{color:#10b981}.skill-name[data-v-14e257e5]{color:#6b7280;font-size:11px;font-weight:500;font-family:SF Mono,Monaco,Menlo,Ubuntu Mono,monospace;background:#f3f4f6;border:1px solid #d1d5db;border-radius:8px;padding:4px 10px;display:inline-block;white-space:nowrap}.skill-description[data-v-14e257e5]{color:#6b7280;font-size:14px;line-height:1.5;margin-bottom:12px}.skill-actions[data-v-14e257e5]{padding:0 20px 20px;display:flex;gap:12px}.skill-actions .n-button[data-v-14e257e5]{flex:1}.loading[data-v-14e257e5],.empty[data-v-14e257e5]{display:flex;justify-content:center;align-items:center;min-height:300px}@media(max-width:768px){.skills-grid[data-v-14e257e5]{grid-template-columns:1fr;gap:16px}}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as k,o as m,A as B,J as h,P as N,j as e,t,B as l,C as a,av as T,Q as R,g as x,aw as u,ax as o,ay as n}from"./index-CBewG1aH.js";import{getCount as c,getLocalJobCount as C}from"./common-CaS8RUp0.js";import{_ as D}from"./_plugin-vue_export-helper-DlAUqK2U.js";const Q={class:"dashboard-page"},y={class:"page-header"},A={class:"page-title"},S=k({__name:"Dashboard",setup(L){const s=R({agents:0,nodes:0,taskQueue:0,scheduledTasks:0,knowledgeBase:0,note:0,event:0,todo:0,file:0}),w=async()=>{try{const d=await c("Local Ai Agent");d.success&&(s.agents=d.count||0);const i=await c("Local Ai Node");i.success&&(s.nodes=i.count||0);const f=await C();f.success&&(s.taskQueue=f.count||0);const r=await c("Local Scheduled Job");r.success&&(s.scheduledTasks=r.count||0);const _=await c("Knowledge Base");_.success&&(s.knowledgeBase=_.count||0);const v=await c("Note");v.success&&(s.note=v.count||0);const b=await c("Event");b.success&&(s.event=b.count||0);const g=await c("ToDo");g.success&&(s.todo=g.count||0);const p=await c("File");p.success&&(s.file=p.count||0)}catch(d){console.error("加载统计数据失败:",d)}};return m(()=>{w()}),(d,i)=>(x(),B("div",Q,[h("div",y,[h("h1",A,N(e(t)("Dashboard")),1)]),l(e(T),{cols:4,"x-gap":16,"y-gap":16,responsive:"screen","item-responsive":!0,class:"stats-grid"},{default:a(()=>[l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Total Agents"),value:s.agents},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Total Nodes"),value:s.nodes},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Task Queue"),value:s.taskQueue},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Scheduled Tasks"),value:s.scheduledTasks},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Knowledge Base"),value:s.knowledgeBase},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Note"),value:s.note},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Event"),value:s.event},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("ToDo"),value:s.todo},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("File"),value:s.file},null,8,["label","value"])]),_:1})]),_:1})]),_:1})]))}}),G=D(S,[["__scopeId","data-v-d1f0aa95"]]);export{G as default};
import{d as k,o as m,A as B,J as h,P as N,j as e,t,B as l,C as a,av as T,Q as R,g as x,aw as u,ax as o,ay as n}from"./index-CBewG1aH.js";import{getCount as c,getLocalJobCount as C}from"./common-CaS8RUp0.js";import{_ as D}from"./_plugin-vue_export-helper-DlAUqK2U.js";const Q={class:"dashboard-page"},y={class:"page-header"},A={class:"page-title"},S=k({__name:"Dashboard",setup(L){const s=R({skills:0,nodes:0,taskQueue:0,scheduledTasks:0,knowledgeBase:0,note:0,event:0,todo:0,file:0}),w=async()=>{try{const d=await c("Local Ai Agent");d.success&&(s.skills=d.count||0);const i=await c("Local Ai Node");i.success&&(s.nodes=i.count||0);const f=await C();f.success&&(s.taskQueue=f.count||0);const r=await c("Local Scheduled Job");r.success&&(s.scheduledTasks=r.count||0);const _=await c("Knowledge Base");_.success&&(s.knowledgeBase=_.count||0);const v=await c("Note");v.success&&(s.note=v.count||0);const b=await c("Event");b.success&&(s.event=b.count||0);const g=await c("ToDo");g.success&&(s.todo=g.count||0);const p=await c("File");p.success&&(s.file=p.count||0)}catch(d){console.error("加载统计数据失败:",d)}};return m(()=>{w()}),(d,i)=>(x(),B("div",Q,[h("div",y,[h("h1",A,N(e(t)("Dashboard")),1)]),l(e(T),{cols:4,"x-gap":16,"y-gap":16,responsive:"screen","item-responsive":!0,class:"stats-grid"},{default:a(()=>[l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Total Agents"),value:s.skills},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Total Nodes"),value:s.nodes},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Task Queue"),value:s.taskQueue},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Scheduled Tasks"),value:s.scheduledTasks},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Knowledge Base"),value:s.knowledgeBase},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Note"),value:s.note},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("Event"),value:s.event},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("ToDo"),value:s.todo},null,8,["label","value"])]),_:1})]),_:1}),l(e(u),null,{default:a(()=>[l(e(o),null,{default:a(()=>[l(e(n),{label:e(t)("File"),value:s.file},null,8,["label","value"])]),_:1})]),_:1})]),_:1})]))}}),G=D(S,[["__scopeId","data-v-d1f0aa95"]]);export{G as default};

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
import{d as h,a0 as F,o as A,A as _,J as p,P as d,j as s,t as a,B as S,C as k,as as q,i as x,b as C,c as g,g as m,_ as D,e as N,aD as P}from"./index-CBewG1aH.js";import{u as T,F as V}from"./FlowBuilderContainer-DsK2pBHP.js";import{useFlowBuilderStore as b}from"./flowBuilder-CXOkXb8n.js";import{_ as I}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./iconify-gC035N5m.js";import"./AIAgentFlowBuilder-hFRPYtmD.js";import"./vue-flow-core-B32lO9yC.js";import"./nodeMetadata-CLrm-0CB.js";import"./common-CaS8RUp0.js";import"./NodePropertyModal-DutSkaSe.js";import"./SchemaFormRenderer-BoLivC3r.js";import"./nodes-B80fFdtY.js";import"./NodePalette-B_CHerrO.js";import"./ExecutionResults-CMv5OhfQ.js";const J={class:"flow-builder-page"},M={key:0,class:"empty-state"},R={class:"empty-content"},j=h({__name:"FlowBuilder",setup(E){const y=N(),o=C(),i=F(),c=T(),t=b(),r=g(()=>o.query.id||o.query.name||t.getAgentId()),f=g(()=>{const e=o.query.pagetype;return e?P(e):""}),w=g(()=>r.value||t.hasFlowData());A(async()=>{const e=o.query.id||o.query.name,n=f.value;if(e&&!t.getAgentId())try{await c.fetchAgent(e,n);const l=c.currentAgent;if(l?.skill_flow){let u=l.skill_flow;typeof u=="string"&&(u=JSON.parse(u)),t.activateFlowBuilder(u,e)}}catch(l){console.error("恢复智能体数据失败:",l)}});const v=async e=>{try{r.value?(await c.updateAgent(r.value,{skill_flow:e},f.value),i.success(a("Agent flow saved successfully")),t.deactivateFlowBuilder()):i.success(a("Flow saved successfully"))}catch(n){i.error(a("Save failed")+": "+(n?.message||""))}},B=()=>{y.push("/agents")};return(e,n)=>(m(),_("div",J,[w.value?(m(),x(V,{key:1,"initial-value":s(t).getFlowData(),"skill-id":r.value,onSave:v},null,8,["initial-value","skill-id"])):(m(),_("div",M,[p("div",R,[p("h3",null,d(s(a)("Flow Builder")),1),p("p",null,d(s(a)("Please enter from Agents page to open Flow Builder")),1),S(s(q),{type:"primary",onClick:B},{default:k(()=>[D(d(s(a)("View Agents")),1)]),_:1})])]))]))}}),te=I(j,[["__scopeId","data-v-bffeb9f8"]]);export{te as default};
import{d as h,a0 as F,o as A,A as _,J as p,P as d,j as s,t as a,B as S,C as k,as as q,i as x,b as C,c as g,g as m,_ as D,e as N,aD as P}from"./index-CBewG1aH.js";import{u as T,F as V}from"./FlowBuilderContainer-DsK2pBHP.js";import{useFlowBuilderStore as b}from"./flowBuilder-CXOkXb8n.js";import{_ as I}from"./_plugin-vue_export-helper-DlAUqK2U.js";import"./iconify-gC035N5m.js";import"./AIAgentFlowBuilder-hFRPYtmD.js";import"./vue-flow-core-B32lO9yC.js";import"./nodeMetadata-CLrm-0CB.js";import"./common-CaS8RUp0.js";import"./NodePropertyModal-DutSkaSe.js";import"./SchemaFormRenderer-BoLivC3r.js";import"./nodes-B80fFdtY.js";import"./NodePalette-B_CHerrO.js";import"./ExecutionResults-CMv5OhfQ.js";const J={class:"flow-builder-page"},M={key:0,class:"empty-state"},R={class:"empty-content"},j=h({__name:"FlowBuilder",setup(E){const y=N(),o=C(),i=F(),c=T(),t=b(),r=g(()=>o.query.id||o.query.name||t.getAgentId()),f=g(()=>{const e=o.query.pagetype;return e?P(e):""}),w=g(()=>r.value||t.hasFlowData());A(async()=>{const e=o.query.id||o.query.name,n=f.value;if(e&&!t.getAgentId())try{await c.fetchAgent(e,n);const l=c.currentAgent;if(l?.skill_flow){let u=l.skill_flow;typeof u=="string"&&(u=JSON.parse(u)),t.activateFlowBuilder(u,e)}}catch(l){console.error("恢复智能体数据失败:",l)}});const v=async e=>{try{r.value?(await c.updateAgent(r.value,{skill_flow:e},f.value),i.success(a("Agent flow saved successfully")),t.deactivateFlowBuilder()):i.success(a("Flow saved successfully"))}catch(n){i.error(a("Save failed")+": "+(n?.message||""))}},B=()=>{y.push("/skills")};return(e,n)=>(m(),_("div",J,[w.value?(m(),x(V,{key:1,"initial-value":s(t).getFlowData(),"skill-id":r.value,onSave:v},null,8,["initial-value","skill-id"])):(m(),_("div",M,[p("div",R,[p("h3",null,d(s(a)("Flow Builder")),1),p("p",null,d(s(a)("Please enter from Agents page to open Flow Builder")),1),S(s(q),{type:"primary",onClick:B},{default:k(()=>[D(d(s(a)("View Agents")),1)]),_:1})])]))]))}}),te=I(j,[["__scopeId","data-v-bffeb9f8"]]);export{te as default};

View File

@ -1 +1 @@
import{y as V,r as _,d as E,a0 as N,w as R,A as j,J as w,P as A,B as c,C as d,j as n,aB as x,c as m,g as T,as as B,_ as I,t as u,aC as b,e as z,aD as O}from"./index-CBewG1aH.js";import{I as F}from"./iconify-gC035N5m.js";import P from"./AIAgentFlowBuilder-hFRPYtmD.js";import{useFlowBuilderStore as q}from"./flowBuilder-CXOkXb8n.js";import{getRecord as M,updateRecord as $}from"./common-CaS8RUp0.js";import{_ as J}from"./_plugin-vue_export-helper-DlAUqK2U.js";const G=async(a,l)=>{try{const e=await M(l,a);if(!e.success)throw new Error(e.message||"获取AI Agent详情失败");return e.data}catch(e){throw new Error(e.message||"获取AI Agent详情失败")}},H=async(a,l,e)=>{try{const o=await $(e,a,l);if(!o.success)throw new Error(o.message||"更新AI Agent失败");return o.data}catch(o){throw console.error("Error in updateAgentApi:",o),new Error(o.message||"更新AI Agent失败")}},K=V("agent",()=>{const a=_(null),l=_("");return{currentAgent:a,currentPageType:l,fetchAgent:async(i,s)=>{try{const r=await G(i,s);a.value=r,l.value=s}catch(r){throw console.error("获取AI Agent详情失败:",r),r}},updateAgent:async(i,s,r)=>{try{return await H(i,s,r),a.value&&a.value.name===i&&(a.value={...a.value,...s}),{success:!0}}catch(g){throw console.error("更新AI Agent失败:",g),g}}}}),L={class:"flow-builder"},Q={class:"flow-builder-header"},U={class:"header-left"},W={class:"title"},X={class:"header-right"},Y={class:"flow-builder-content"},Z=E({__name:"FlowBuilderContainer",props:{initialValue:{},skillId:{}},emits:["save"],setup(a,{emit:l}){const e=a,o=l,i=z(),s=N(),r=K(),g=q(),f=_(""),S=m(()=>{if(e.initialValue&&Object.keys(e.initialValue).length>0)return e.initialValue;const t=g.getFlowData();return t&&Object.keys(t).length>0?t:{}}),h=_(),p=m(()=>e.skillId||g.getAgentId()),y=m(()=>{const v=i.currentRoute.value.query.pagetype;return v?O(v):""}),k=m(()=>f.value?`${f.value}`:u("Flow Builder"));R(p,async t=>{if(t)try{await r.fetchAgent(t,y.value),f.value=r.currentAgent?.skill_name||r.currentAgent?.name||""}catch(v){console.error("获取智能体信息失败:",v),f.value=""}else f.value=""},{immediate:!0});const C=async()=>{try{if(!h.value){s.error(u("Flow Builder is not initialized"));return}const t=h.value.getFlowData();p.value?(await r.updateAgent(p.value,{skill_flow:t},y.value),s.success(u("Flow saved successfully"))):(o("save",t),s.success(u("Flow saved successfully")))}catch(t){s.error(u("Save failed")+": "+(t.message||u("An error occurred during save")))}},D=()=>{g.deactivateFlowBuilder(),i.back()};return(t,v)=>(T(),j("div",L,[w("div",Q,[w("div",U,[w("h2",W,A(k.value),1)]),w("div",X,[c(n(x),null,{default:d(()=>[c(n(B),{size:"small",onClick:D},{icon:d(()=>[c(n(b),null,{default:d(()=>[c(n(F),{icon:"tabler:arrow-left"})]),_:1})]),default:d(()=>[I(" "+A(n(u)("Back")),1)]),_:1}),c(n(B),{size:"small",type:"primary",onClick:C,class:"save-btn-brand"},{icon:d(()=>[c(n(b),null,{default:d(()=>[c(n(F),{icon:"tabler:check"})]),_:1})]),default:d(()=>[I(" "+A(n(u)("Save")),1)]),_:1})]),_:1})])]),w("div",Y,[c(P,{ref_key:"flowBuilderRef",ref:h,"initial-value":S.value,"skill-id":p.value},null,8,["initial-value","skill-id"])])]))}}),ee=J(Z,[["__scopeId","data-v-c9b4f7b6"]]),le=Object.freeze(Object.defineProperty({__proto__:null,default:ee},Symbol.toStringTag,{value:"Module"}));export{ee as F,le as a,K as u};
import{y as V,r as _,d as E,a0 as N,w as R,A as j,J as w,P as A,B as c,C as d,j as n,aB as x,c as m,g as T,as as B,_ as I,t as u,aC as b,e as z,aD as O}from"./index-CBewG1aH.js";import{I as F}from"./iconify-gC035N5m.js";import P from"./AIAgentFlowBuilder-hFRPYtmD.js";import{useFlowBuilderStore as q}from"./flowBuilder-CXOkXb8n.js";import{getRecord as M,updateRecord as $}from"./common-CaS8RUp0.js";import{_ as J}from"./_plugin-vue_export-helper-DlAUqK2U.js";const G=async(a,l)=>{try{const e=await M(l,a);if(!e.success)throw new Error(e.message||"获取AI Agent详情失败");return e.data}catch(e){throw new Error(e.message||"获取AI Agent详情失败")}},H=async(a,l,e)=>{try{const o=await $(e,a,l);if(!o.success)throw new Error(o.message||"更新AI Agent失败");return o.data}catch(o){throw console.error("Error in updateAgentApi:",o),new Error(o.message||"更新AI Agent失败")}},K=V("skill",()=>{const a=_(null),l=_("");return{currentAgent:a,currentPageType:l,fetchAgent:async(i,s)=>{try{const r=await G(i,s);a.value=r,l.value=s}catch(r){throw console.error("获取AI Agent详情失败:",r),r}},updateAgent:async(i,s,r)=>{try{return await H(i,s,r),a.value&&a.value.name===i&&(a.value={...a.value,...s}),{success:!0}}catch(g){throw console.error("更新AI Agent失败:",g),g}}}}),L={class:"flow-builder"},Q={class:"flow-builder-header"},U={class:"header-left"},W={class:"title"},X={class:"header-right"},Y={class:"flow-builder-content"},Z=E({__name:"FlowBuilderContainer",props:{initialValue:{},skillId:{}},emits:["save"],setup(a,{emit:l}){const e=a,o=l,i=z(),s=N(),r=K(),g=q(),f=_(""),S=m(()=>{if(e.initialValue&&Object.keys(e.initialValue).length>0)return e.initialValue;const t=g.getFlowData();return t&&Object.keys(t).length>0?t:{}}),h=_(),p=m(()=>e.skillId||g.getAgentId()),y=m(()=>{const v=i.currentRoute.value.query.pagetype;return v?O(v):""}),k=m(()=>f.value?`${f.value}`:u("Flow Builder"));R(p,async t=>{if(t)try{await r.fetchAgent(t,y.value),f.value=r.currentAgent?.skill_name||r.currentAgent?.name||""}catch(v){console.error("获取智能体信息失败:",v),f.value=""}else f.value=""},{immediate:!0});const C=async()=>{try{if(!h.value){s.error(u("Flow Builder is not initialized"));return}const t=h.value.getFlowData();p.value?(await r.updateAgent(p.value,{skill_flow:t},y.value),s.success(u("Flow saved successfully"))):(o("save",t),s.success(u("Flow saved successfully")))}catch(t){s.error(u("Save failed")+": "+(t.message||u("An error occurred during save")))}},D=()=>{g.deactivateFlowBuilder(),i.back()};return(t,v)=>(T(),j("div",L,[w("div",Q,[w("div",U,[w("h2",W,A(k.value),1)]),w("div",X,[c(n(x),null,{default:d(()=>[c(n(B),{size:"small",onClick:D},{icon:d(()=>[c(n(b),null,{default:d(()=>[c(n(F),{icon:"tabler:arrow-left"})]),_:1})]),default:d(()=>[I(" "+A(n(u)("Back")),1)]),_:1}),c(n(B),{size:"small",type:"primary",onClick:C,class:"save-btn-brand"},{icon:d(()=>[c(n(b),null,{default:d(()=>[c(n(F),{icon:"tabler:check"})]),_:1})]),default:d(()=>[I(" "+A(n(u)("Save")),1)]),_:1})]),_:1})])]),w("div",Y,[c(P,{ref_key:"flowBuilderRef",ref:h,"initial-value":S.value,"skill-id":p.value},null,8,["initial-value","skill-id"])])]))}}),ee=J(Z,[["__scopeId","data-v-c9b4f7b6"]]),le=Object.freeze(Object.defineProperty({__proto__:null,default:ee},Symbol.toStringTag,{value:"Module"}));export{ee as F,le as a,K as u};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/flowBuilder-CXOkXb8n.js","assets/index-CBewG1aH.js","assets/index-Cl-AM8AM.css"])))=>i.map(i=>d[i]);
import{d as g,a0 as p,g as y,A as b,J as a,af as l,H as u,c as v,r as B,a as C,aN as E,a2 as F}from"./index-CBewG1aH.js";import{api as S}from"./common-CaS8RUp0.js";import{_}from"./_plugin-vue_export-helper-DlAUqK2U.js";const k=["title"],M=["title"],N=["title","disabled"],R=["title"],$=["title"],D=g({__name:"ai_skill_list_actions",props:{context:{}},setup(n){const e=n,f=v(()=>(e.context.viewMode||"list")==="card"?"actions-container card-actions":"actions-container col-actions"),r=p(),c=B(!1);async function x(){try{c.value=!0;const o=e.context.row.name;if(!o){r.error(e.context.t("Agent name is required"));return}const t=await S.call("jingrow.ai.pagetype.ai_skill.ai_skill.execute_agent_by_name",{skill_name:o}),i=t?.message||t;i?.success?r.success(e.context.t("Submitted successfully")):r.error(i?.error||e.context.t("Execution failed"))}catch(o){r.error(o?.message||e.context.t("Execution failed"))}finally{c.value=!1}}async function w(){try{const o=e.context.row.name;let t={};if(e.context.row.skill_flow!==void 0&&e.context.row.skill_flow!==null){const s=e.context.row.skill_flow;if(t=s,typeof s=="string")try{t=JSON.parse(s)}catch{t={}}}else try{const d=((await C.get(`/api/data/${encodeURIComponent(e.context.entity)}/${encodeURIComponent(o)}`)).data?.data||{}).skill_flow??{};if(t=d,typeof d=="string")try{t=JSON.parse(d)}catch{t={}}}catch(s){console.error("获取智能体数据失败:",s),r.error(e.context.t("Failed to load agent data"));return}const i=E(e.context.entity),{useFlowBuilderStore:m}=await F(async()=>{const{useFlowBuilderStore:s}=await import("./flowBuilder-CXOkXb8n.js");return{useFlowBuilderStore:s}},__vite__mapDeps([0,1,2]));m().activateFlowBuilder(t,o),e.context.router.push({name:"FlowBuilder",query:{pagetype:i,id:o}})}catch(o){console.error("打开流程编排失败:",o),r.error(o?.message||e.context.t("Failed to open flow builder"))}}return(o,t)=>(y(),b("div",{class:u(f.value)},[a("button",{class:"action-btn",onClick:t[0]||(t[0]=l(i=>n.context.openDetail(n.context.row.name),["stop"])),title:n.context.t("View")},[...t[3]||(t[3]=[a("i",{class:"fa fa-eye"},null,-1)])],8,k),a("button",{class:"action-btn",onClick:t[1]||(t[1]=l(i=>n.context.editRecord(n.context.row),["stop"])),title:n.context.t("Edit")},[...t[4]||(t[4]=[a("i",{class:"fa fa-edit"},null,-1)])],8,M),a("button",{class:"action-btn execute-btn",onClick:l(x,["stop"]),title:n.context.t("Execute"),disabled:c.value},[a("i",{class:u(c.value?"fa fa-spinner fa-spin":"fa fa-play")},null,2)],8,N),a("button",{class:"action-btn flow-builder-btn",onClick:l(w,["stop"]),title:n.context.t("Flow Builder")},[...t[5]||(t[5]=[a("i",{class:"fa fa-sitemap"},null,-1)])],8,R),a("button",{class:"action-btn delete-btn",onClick:t[2]||(t[2]=l(i=>n.context.deleteRecord(n.context.row.name),["stop"])),title:n.context.t("Delete")},[...t[6]||(t[6]=[a("i",{class:"fa fa-trash"},null,-1)])],8,$)],2))}}),V=_(D,[["__scopeId","data-v-1aa3dda6"]]);export{V as default};
import{d as g,a0 as p,g as y,A as b,J as a,af as l,H as u,c as v,r as B,a as C,aN as E,a2 as F}from"./index-CBewG1aH.js";import{api as S}from"./common-CaS8RUp0.js";import{_}from"./_plugin-vue_export-helper-DlAUqK2U.js";const k=["title"],M=["title"],N=["title","disabled"],R=["title"],$=["title"],D=g({__name:"ai_skill_list_actions",props:{context:{}},setup(n){const e=n,f=v(()=>(e.context.viewMode||"list")==="card"?"actions-container card-actions":"actions-container col-actions"),r=p(),c=B(!1);async function x(){try{c.value=!0;const o=e.context.row.name;if(!o){r.error(e.context.t("Agent name is required"));return}const t=await S.call("jingrow.ai.pagetype.ai_skill.ai_skill.execute_skill_by_name",{skill_name:o}),i=t?.message||t;i?.success?r.success(e.context.t("Submitted successfully")):r.error(i?.error||e.context.t("Execution failed"))}catch(o){r.error(o?.message||e.context.t("Execution failed"))}finally{c.value=!1}}async function w(){try{const o=e.context.row.name;let t={};if(e.context.row.skill_flow!==void 0&&e.context.row.skill_flow!==null){const s=e.context.row.skill_flow;if(t=s,typeof s=="string")try{t=JSON.parse(s)}catch{t={}}}else try{const d=((await C.get(`/api/data/${encodeURIComponent(e.context.entity)}/${encodeURIComponent(o)}`)).data?.data||{}).skill_flow??{};if(t=d,typeof d=="string")try{t=JSON.parse(d)}catch{t={}}}catch(s){console.error("获取智能体数据失败:",s),r.error(e.context.t("Failed to load skill data"));return}const i=E(e.context.entity),{useFlowBuilderStore:m}=await F(async()=>{const{useFlowBuilderStore:s}=await import("./flowBuilder-CXOkXb8n.js");return{useFlowBuilderStore:s}},__vite__mapDeps([0,1,2]));m().activateFlowBuilder(t,o),e.context.router.push({name:"FlowBuilder",query:{pagetype:i,id:o}})}catch(o){console.error("打开流程编排失败:",o),r.error(o?.message||e.context.t("Failed to open flow builder"))}}return(o,t)=>(y(),b("div",{class:u(f.value)},[a("button",{class:"action-btn",onClick:t[0]||(t[0]=l(i=>n.context.openDetail(n.context.row.name),["stop"])),title:n.context.t("View")},[...t[3]||(t[3]=[a("i",{class:"fa fa-eye"},null,-1)])],8,k),a("button",{class:"action-btn",onClick:t[1]||(t[1]=l(i=>n.context.editRecord(n.context.row),["stop"])),title:n.context.t("Edit")},[...t[4]||(t[4]=[a("i",{class:"fa fa-edit"},null,-1)])],8,M),a("button",{class:"action-btn execute-btn",onClick:l(x,["stop"]),title:n.context.t("Execute"),disabled:c.value},[a("i",{class:u(c.value?"fa fa-spinner fa-spin":"fa fa-play")},null,2)],8,N),a("button",{class:"action-btn flow-builder-btn",onClick:l(w,["stop"]),title:n.context.t("Flow Builder")},[...t[5]||(t[5]=[a("i",{class:"fa fa-sitemap"},null,-1)])],8,R),a("button",{class:"action-btn delete-btn",onClick:t[2]||(t[2]=l(i=>n.context.deleteRecord(n.context.row.name),["stop"])),title:n.context.t("Delete")},[...t[6]||(t[6]=[a("i",{class:"fa fa-trash"},null,-1)])],8,$)],2))}}),V=_(D,[["__scopeId","data-v-1aa3dda6"]]);export{V as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,2 +1,2 @@
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/flowBuilder-CXOkXb8n.js","assets/index-CBewG1aH.js","assets/index-Cl-AM8AM.css"])))=>i.map(i=>d[i]);
import{d as g,a0 as p,g as y,A as b,J as a,af as c,H as u,c as v,r as B,a as C,aN as E,a2 as F}from"./index-CBewG1aH.js";import{api as S}from"./common-CaS8RUp0.js";import{_ as k}from"./_plugin-vue_export-helper-DlAUqK2U.js";const _=["title"],M=["title"],N=["title","disabled"],R=["title"],$=["title"],D=g({__name:"local_ai_skill_list_actions",props:{context:{}},setup(n){const e=n,f=v(()=>(e.context.viewMode||"list")==="card"?"actions-container card-actions":"actions-container col-actions"),i=p(),r=B(!1);async function x(){try{r.value=!0;const o=e.context.row.name;if(!o){i.error(e.context.t("Agent name is required"));return}const t=await S.call("jingrow.ai.utils.jlocal.execute_local_ai_skill",{name:o}),l=t?.message||t;l?.success?i.success(e.context.t("Submitted successfully")):i.error(l?.error||e.context.t("Execution failed"))}catch(o){i.error(o?.message||e.context.t("Execution failed"))}finally{r.value=!1}}async function w(){try{const o=e.context.row.name;let t={};if(e.context.row.skill_flow!==void 0&&e.context.row.skill_flow!==null){const s=e.context.row.skill_flow;if(t=s,typeof s=="string")try{t=JSON.parse(s)}catch{t={}}}else try{const d=((await C.get(`/api/data/${encodeURIComponent(e.context.entity)}/${encodeURIComponent(o)}`)).data?.data||{}).skill_flow??{};if(t=d,typeof d=="string")try{t=JSON.parse(d)}catch{t={}}}catch(s){console.error("获取智能体数据失败:",s),i.error(e.context.t("Failed to load agent data"));return}const l=E(e.context.entity),{useFlowBuilderStore:m}=await F(async()=>{const{useFlowBuilderStore:s}=await import("./flowBuilder-CXOkXb8n.js");return{useFlowBuilderStore:s}},__vite__mapDeps([0,1,2]));m().activateFlowBuilder(t,o),e.context.router.push({name:"FlowBuilder",query:{pagetype:l,id:o}})}catch(o){console.error("打开流程编排失败:",o),i.error(o?.message||e.context.t("Failed to open flow builder"))}}return(o,t)=>(y(),b("div",{class:u(f.value)},[a("button",{class:"action-btn",onClick:t[0]||(t[0]=c(l=>n.context.openDetail(n.context.row.name),["stop"])),title:n.context.t("View")},[...t[3]||(t[3]=[a("i",{class:"fa fa-eye"},null,-1)])],8,_),a("button",{class:"action-btn",onClick:t[1]||(t[1]=c(l=>n.context.editRecord(n.context.row),["stop"])),title:n.context.t("Edit")},[...t[4]||(t[4]=[a("i",{class:"fa fa-edit"},null,-1)])],8,M),a("button",{class:"action-btn execute-btn",onClick:c(x,["stop"]),title:n.context.t("Execute"),disabled:r.value},[a("i",{class:u(r.value?"fa fa-spinner fa-spin":"fa fa-play")},null,2)],8,N),a("button",{class:"action-btn flow-builder-btn",onClick:c(w,["stop"]),title:n.context.t("Flow Builder")},[...t[5]||(t[5]=[a("i",{class:"fa fa-sitemap"},null,-1)])],8,R),a("button",{class:"action-btn delete-btn",onClick:t[2]||(t[2]=c(l=>n.context.deleteRecord(n.context.row.name),["stop"])),title:n.context.t("Delete")},[...t[6]||(t[6]=[a("i",{class:"fa fa-trash"},null,-1)])],8,$)],2))}}),V=k(D,[["__scopeId","data-v-f2238862"]]);export{V as default};
import{d as g,a0 as p,g as y,A as b,J as a,af as c,H as u,c as v,r as B,a as C,aN as E,a2 as F}from"./index-CBewG1aH.js";import{api as S}from"./common-CaS8RUp0.js";import{_ as k}from"./_plugin-vue_export-helper-DlAUqK2U.js";const _=["title"],M=["title"],N=["title","disabled"],R=["title"],$=["title"],D=g({__name:"local_ai_skill_list_actions",props:{context:{}},setup(n){const e=n,f=v(()=>(e.context.viewMode||"list")==="card"?"actions-container card-actions":"actions-container col-actions"),i=p(),r=B(!1);async function x(){try{r.value=!0;const o=e.context.row.name;if(!o){i.error(e.context.t("Agent name is required"));return}const t=await S.call("jingrow.ai.utils.jlocal.execute_local_ai_skill",{name:o}),l=t?.message||t;l?.success?i.success(e.context.t("Submitted successfully")):i.error(l?.error||e.context.t("Execution failed"))}catch(o){i.error(o?.message||e.context.t("Execution failed"))}finally{r.value=!1}}async function w(){try{const o=e.context.row.name;let t={};if(e.context.row.skill_flow!==void 0&&e.context.row.skill_flow!==null){const s=e.context.row.skill_flow;if(t=s,typeof s=="string")try{t=JSON.parse(s)}catch{t={}}}else try{const d=((await C.get(`/api/data/${encodeURIComponent(e.context.entity)}/${encodeURIComponent(o)}`)).data?.data||{}).skill_flow??{};if(t=d,typeof d=="string")try{t=JSON.parse(d)}catch{t={}}}catch(s){console.error("获取智能体数据失败:",s),i.error(e.context.t("Failed to load skill data"));return}const l=E(e.context.entity),{useFlowBuilderStore:m}=await F(async()=>{const{useFlowBuilderStore:s}=await import("./flowBuilder-CXOkXb8n.js");return{useFlowBuilderStore:s}},__vite__mapDeps([0,1,2]));m().activateFlowBuilder(t,o),e.context.router.push({name:"FlowBuilder",query:{pagetype:l,id:o}})}catch(o){console.error("打开流程编排失败:",o),i.error(o?.message||e.context.t("Failed to open flow builder"))}}return(o,t)=>(y(),b("div",{class:u(f.value)},[a("button",{class:"action-btn",onClick:t[0]||(t[0]=c(l=>n.context.openDetail(n.context.row.name),["stop"])),title:n.context.t("View")},[...t[3]||(t[3]=[a("i",{class:"fa fa-eye"},null,-1)])],8,_),a("button",{class:"action-btn",onClick:t[1]||(t[1]=c(l=>n.context.editRecord(n.context.row),["stop"])),title:n.context.t("Edit")},[...t[4]||(t[4]=[a("i",{class:"fa fa-edit"},null,-1)])],8,M),a("button",{class:"action-btn execute-btn",onClick:c(x,["stop"]),title:n.context.t("Execute"),disabled:r.value},[a("i",{class:u(r.value?"fa fa-spinner fa-spin":"fa fa-play")},null,2)],8,N),a("button",{class:"action-btn flow-builder-btn",onClick:c(w,["stop"]),title:n.context.t("Flow Builder")},[...t[5]||(t[5]=[a("i",{class:"fa fa-sitemap"},null,-1)])],8,R),a("button",{class:"action-btn delete-btn",onClick:t[2]||(t[2]=c(l=>n.context.deleteRecord(n.context.row.name),["stop"])),title:n.context.t("Delete")},[...t[6]||(t[6]=[a("i",{class:"fa fa-trash"},null,-1)])],8,$)],2))}}),V=k(D,[["__scopeId","data-v-f2238862"]]);export{V as default};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -632,8 +632,8 @@
</filter>
</defs>
</symbol>
<symbol id="es-line-agent" fill="none" viewBox="0 0 16 16">
<g class="es-line-agent" filter="url(#a)">
<symbol id="es-line-skill" fill="none" viewBox="0 0 16 16">
<g class="es-line-skill" filter="url(#a)">
<path fill="var(--icon-stroke)" fill-rule="evenodd" d="M9.625 4.49a1.625 1.625 0 1 1-3.25 0 1.625 1.625 0 0 1 3.25 0Zm1 0a2.625 2.625 0 1 1-5.25 0 2.625 2.625 0 0 1 5.25 0Zm-7.294 6.996a3.347 3.347 0 0 1 3.267-2.621h2.805a3.347 3.347 0 0 1 3.267 2.62l.093.418a1.424 1.424 0 0 1-1.39 1.732H4.628a1.423 1.423 0 0 1-1.39-1.732l.093-.417Zm3.267-3.621a4.347 4.347 0 0 0-4.243 3.404l-.093.417a2.424 2.424 0 0 0 2.366 2.95h6.745a2.424 2.424 0 0 0 2.366-2.95l-.093-.417a4.347 4.347 0 0 0-4.243-3.404H6.598Z" class="Union" clip-rule="evenodd"/>
</g>
<defs>
@ -1468,8 +1468,8 @@
<path fill="var(--icon-stroke)" fill-rule="evenodd" d="M1 8a7 7 0 1 1 14 0A7 7 0 0 1 1 8Zm6.13 2.684 3.387-2.237a.521.521 0 0 0 .26-.447.521.521 0 0 0-.26-.447L7.131 5.316a.67.67 0 0 0-.645-.06.623.623 0 0 0-.375.507v4.474a.623.623 0 0 0 .375.507.67.67 0 0 0 .645-.06Z" class="Union" clip-rule="evenodd"/>
</g>
</symbol>
<symbol id="es-solid-agent" viewBox="0 0 16 16">
<g class="es-solid-agent" filter="url(#a)">
<symbol id="es-solid-skill" viewBox="0 0 16 16">
<g class="es-solid-skill" filter="url(#a)">
<path fill="var(--icon-stroke)" fill-rule="evenodd" d="M8 7.115a2.625 2.625 0 1 0 0-5.25 2.625 2.625 0 0 0 0 5.25Zm-1.402.75a4.347 4.347 0 0 0-4.243 3.404l-.093.417a2.424 2.424 0 0 0 2.366 2.95h6.745a2.424 2.424 0 0 0 2.366-2.95l-.093-.417a4.347 4.347 0 0 0-4.243-3.404h-.319L8 10.184l-1.084-2.32h-.318Z" class="Union" clip-rule="evenodd"/>
</g>
<defs>
@ -1520,8 +1520,8 @@
<path fill="var(--icon-stroke)" fill-rule="evenodd" d="M5 2.623V3.95a.5.5 0 0 0 1 0V2.623h7.502a1.5 1.5 0 0 1 1.5 1.5V5.91a.5.5 0 0 1-.4.49 1.669 1.669 0 0 0 0 3.268.5.5 0 0 1 .4.49v1.719a1.5 1.5 0 0 1-1.5 1.5H6V12.05a.5.5 0 1 0-1 0v1.327H2.498a1.5 1.5 0 0 1-1.5-1.5v-1.719a.5.5 0 0 1 .4-.49 1.669 1.669 0 0 0 0-3.268.5.5 0 0 1-.4-.49V4.123a1.5 1.5 0 0 1 1.5-1.5H5ZM6 6.65a.5.5 0 1 0-1 0v2.7a.5.5 0 0 0 1 0v-2.7Z" class="Subtract" clip-rule="evenodd"/>
</g>
</symbol>
<symbol id="es-solid-agent" viewBox="0 0 16 16">
<g class="es-solid-agent" filter="url(#a)">
<symbol id="es-solid-skill" viewBox="0 0 16 16">
<g class="es-solid-skill" filter="url(#a)">
<path fill="var(--icon-stroke)" fill-rule="evenodd" d="M8 7.115a2.625 2.625 0 1 0 0-5.25 2.625 2.625 0 0 0 0 5.25Zm-5.645 4.154a4.347 4.347 0 0 1 4.243-3.404h2.805a4.347 4.347 0 0 1 4.243 3.404l.093.417a2.424 2.424 0 0 1-2.366 2.95H4.628a2.424 2.424 0 0 1-2.366-2.95l.093-.417Z" class="Union" clip-rule="evenodd"/>
</g>
<defs>

Before

Width:  |  Height:  |  Size: 198 KiB

After

Width:  |  Height:  |  Size: 198 KiB

View File

@ -17,12 +17,12 @@ class AiSkillFlowBuilder {
init() {
// 设置全局智能体信息,供执行器使用
this.setup_global_agent_info();
this.setup_global_skill_info();
this.setup_app();
this.watch_changes();
}
setup_global_agent_info() {
setup_global_skill_info() {
// 设置全局变量,让执行器能够获取到智能体信息
if (this.frm && this.frm.pg && this.frm.pg.name) {
window.current_skill_name = this.frm.pg.name;