style(format): 格式化代码
This commit is contained in:
parent
ba7c1299a5
commit
ee624513f1
@ -302,7 +302,9 @@ class LightRagKB(KnowledgeBase):
|
||||
),
|
||||
)
|
||||
|
||||
async def index_file(self, db_id: str, file_id: str, operator_id: str | None = None, params: dict | None = None) -> dict:
|
||||
async def index_file(
|
||||
self, db_id: str, file_id: str, operator_id: str | None = None, params: dict | None = None
|
||||
) -> dict:
|
||||
"""
|
||||
Index parsed file (Status: INDEXING -> INDEXED/ERROR_INDEXING)
|
||||
|
||||
@ -395,7 +397,7 @@ class LightRagKB(KnowledgeBase):
|
||||
|
||||
logger.info(
|
||||
f"Indexed file {file_id} into LightRAG with {len(chunks)} chunks, "
|
||||
f"chunk_preset_id={processing_params.get('chunk_preset_id')}"
|
||||
f"chunk_preset_id={params.get('chunk_preset_id')}"
|
||||
)
|
||||
|
||||
# Update status
|
||||
|
||||
@ -284,7 +284,9 @@ class MilvusKB(KnowledgeBase):
|
||||
"""将文本分割成块"""
|
||||
return chunk_markdown(text, file_id, filename, params)
|
||||
|
||||
async def index_file(self, db_id: str, file_id: str, operator_id: str | None = None, params: dict | None = None) -> dict:
|
||||
async def index_file(
|
||||
self, db_id: str, file_id: str, operator_id: str | None = None, params: dict | None = None
|
||||
) -> dict:
|
||||
"""
|
||||
Index parsed file (Status: INDEXING -> INDEXED/ERROR_INDEXING)
|
||||
|
||||
|
||||
@ -415,7 +415,9 @@ class KnowledgeBaseManager:
|
||||
kb_instance = await self._get_kb_for_database(db_id)
|
||||
return await kb_instance.parse_file(db_id, file_id, operator_id)
|
||||
|
||||
async def index_file(self, db_id: str, file_id: str, operator_id: str | None = None, params: dict | None = None) -> dict:
|
||||
async def index_file(
|
||||
self, db_id: str, file_id: str, operator_id: str | None = None, params: dict | None = None
|
||||
) -> dict:
|
||||
"""Index parsed file"""
|
||||
kb_instance = await self._get_kb_for_database(db_id)
|
||||
return await kb_instance.index_file(db_id, file_id, operator_id, params=params)
|
||||
|
||||
@ -263,6 +263,7 @@ async def save_messages_from_langgraph_state(
|
||||
elif msg_type == "tool":
|
||||
await _save_tool_message(conv_repo, msg_dict)
|
||||
|
||||
|
||||
def _extract_interrupt_info(state) -> Any | None:
|
||||
"""从 LangGraph state 中提取中断信息"""
|
||||
if hasattr(state, "tasks") and state.tasks:
|
||||
@ -1086,7 +1087,7 @@ async def stream_agent_resume(
|
||||
except Exception as e:
|
||||
logger.error(f"Error saving messages from LangGraph state: {e}")
|
||||
logger.error(traceback.format_exc())
|
||||
yield make_chunk(status="warning", message=f"消息保存失败: {e}", meta=meta)
|
||||
yield make_resume_chunk(status="warning", message=f"消息保存失败: {e}", meta=meta)
|
||||
|
||||
yield make_resume_chunk(status="finished", meta=meta)
|
||||
|
||||
|
||||
@ -7,6 +7,7 @@ from typing import Any
|
||||
|
||||
import httpx
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
from yuxi.config.builtin_providers import BUILTIN_PROVIDERS
|
||||
from yuxi.repositories.model_provider_repository import (
|
||||
create_model_provider,
|
||||
delete_model_provider,
|
||||
@ -14,7 +15,6 @@ from yuxi.repositories.model_provider_repository import (
|
||||
list_model_providers,
|
||||
update_model_provider,
|
||||
)
|
||||
from yuxi.config.builtin_providers import BUILTIN_PROVIDERS
|
||||
from yuxi.services.model_cache import is_v2_spec_format
|
||||
from yuxi.storage.postgres.models_business import ModelProvider
|
||||
|
||||
|
||||
@ -11,7 +11,8 @@ export default defineConfig({
|
||||
ignoreDeadLinks: [
|
||||
/localhost/,
|
||||
/CONTRIBUTING$/,
|
||||
/docker-compose\.yml$/
|
||||
/docker-compose\.yml$/,
|
||||
/^\.\/intro\//
|
||||
],
|
||||
markdown: {
|
||||
config: (md) => {
|
||||
|
||||
@ -27,7 +27,9 @@
|
||||
cursor: pointer;
|
||||
padding: 4px 8px;
|
||||
border-radius: 6px;
|
||||
transition: color 0.15s, background 0.15s;
|
||||
transition:
|
||||
color 0.15s,
|
||||
background 0.15s;
|
||||
|
||||
&:hover {
|
||||
color: var(--gray-700);
|
||||
@ -90,4 +92,4 @@
|
||||
min-height: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -86,7 +86,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Provider 标签样式
|
||||
.provider-tag {
|
||||
padding: 2px 4px;
|
||||
|
||||
@ -101,7 +101,12 @@ const emit = defineEmits(['update:value', 'change'])
|
||||
|
||||
const v2Models = ref({})
|
||||
const v1ModelStatuses = reactive({})
|
||||
const { statusMap: v2ModelStatuses, getStatusIcon: getV2StatusIcon, getStatusClass: getV2StatusClass, getStatusTooltip: getV2StatusTooltip, checkV2Status, checkV2Statuses } = useModelStatus()
|
||||
const {
|
||||
getStatusIcon: getV2StatusIcon,
|
||||
getStatusClass: getV2StatusClass,
|
||||
getStatusTooltip: getV2StatusTooltip,
|
||||
checkV2Statuses
|
||||
} = useModelStatus()
|
||||
const state = reactive({
|
||||
checkingStatus: false
|
||||
})
|
||||
|
||||
@ -530,12 +530,33 @@ const deleteDatabase = () => {
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-600);
|
||||
|
||||
&.tag-purple { background: #f3e8ff; color: #7c3aed; }
|
||||
&.tag-blue { background: var(--color-info-50); color: var(--color-info-700); }
|
||||
&.tag-red { background: var(--color-error-50); color: var(--color-error-700); }
|
||||
&.tag-green { background: var(--color-success-50); color: var(--color-success-700); }
|
||||
&.tag-gold { background: #fef3c7; color: #d97706; }
|
||||
&.tag-cyan { background: #cffafe; color: #0891b2; }
|
||||
&.tag-orange { background: #fff7ed; color: #ea580c; }
|
||||
&.tag-purple {
|
||||
background: #f3e8ff;
|
||||
color: #7c3aed;
|
||||
}
|
||||
&.tag-blue {
|
||||
background: var(--color-info-50);
|
||||
color: var(--color-info-700);
|
||||
}
|
||||
&.tag-red {
|
||||
background: var(--color-error-50);
|
||||
color: var(--color-error-700);
|
||||
}
|
||||
&.tag-green {
|
||||
background: var(--color-success-50);
|
||||
color: var(--color-success-700);
|
||||
}
|
||||
&.tag-gold {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
&.tag-cyan {
|
||||
background: #cffafe;
|
||||
color: #0891b2;
|
||||
}
|
||||
&.tag-orange {
|
||||
background: #fff7ed;
|
||||
color: #ea580c;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@ -125,7 +125,7 @@ const refreshCache = async () => {
|
||||
}
|
||||
|
||||
// 状态管理
|
||||
const { statusMap: modelStatusMap, getStatusIcon, getStatusTooltip } = useModelStatus()
|
||||
useModelStatus()
|
||||
const state = reactive({
|
||||
currentModelStatus: null,
|
||||
checkingStatus: false,
|
||||
@ -197,7 +197,6 @@ const handleSelectV2Model = (spec) => {
|
||||
<style lang="less" scoped>
|
||||
@import '@/assets/css/model-selector-common.less';
|
||||
|
||||
|
||||
// 状态检查按钮
|
||||
.status-check-button {
|
||||
font-size: 12px;
|
||||
|
||||
@ -55,11 +55,7 @@
|
||||
@click="selectTool(tool)"
|
||||
>
|
||||
<div class="item-header">
|
||||
<component
|
||||
:is="getToolIcon(tool.id) || Wrench"
|
||||
:size="16"
|
||||
class="item-icon"
|
||||
/>
|
||||
<component :is="getToolIcon(tool.id) || Wrench" :size="16" class="item-icon" />
|
||||
<span class="item-name">{{ tool.name }}</span>
|
||||
</div>
|
||||
<div class="item-details item-details-inline">
|
||||
|
||||
@ -12,7 +12,9 @@
|
||||
</div>
|
||||
<div class="extension-card-info">
|
||||
<span class="extension-card-name" :title="title">{{ title }}</span>
|
||||
<span v-if="subtitle" class="extension-card-subtitle" :title="subtitle">{{ subtitle }}</span>
|
||||
<span v-if="subtitle" class="extension-card-subtitle" :title="subtitle">{{
|
||||
subtitle
|
||||
}}</span>
|
||||
</div>
|
||||
<div class="extension-card-status">
|
||||
<slot name="status" />
|
||||
@ -23,17 +25,17 @@
|
||||
class="card-action-btn"
|
||||
:class="`card-action-btn--${actionVariant || 'primary'}`"
|
||||
@click.stop="$emit('actionClick')"
|
||||
>{{ actionLabel }}</button>
|
||||
>
|
||||
{{ actionLabel }}
|
||||
</button>
|
||||
<template v-else-if="status">
|
||||
<span
|
||||
v-if="status.label"
|
||||
class="card-status-tag"
|
||||
:class="`card-status-tag--${status.level || 'info'}`"
|
||||
>{{ status.label }}</span>
|
||||
<span
|
||||
class="card-status-dot"
|
||||
:class="`card-status-dot--${statusDotColor}`"
|
||||
></span>
|
||||
>{{ status.label }}</span
|
||||
>
|
||||
<span class="card-status-dot" :class="`card-status-dot--${statusDotColor}`"></span>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
@ -52,7 +54,10 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="$slots.tags || (normalizedTags && normalizedTags.length > 0)" class="extension-card-tags">
|
||||
<div
|
||||
v-if="$slots.tags || (normalizedTags && normalizedTags.length > 0)"
|
||||
class="extension-card-tags"
|
||||
>
|
||||
<slot name="tags">
|
||||
<span
|
||||
v-for="(tag, idx) in normalizedTags"
|
||||
@ -60,7 +65,8 @@
|
||||
class="card-tag"
|
||||
:class="tag.color ? `tag-${tag.color}` : ''"
|
||||
:style="tag.bgColor ? { backgroundColor: tag.bgColor } : {}"
|
||||
>{{ tag.name }}</span>
|
||||
>{{ tag.name }}</span
|
||||
>
|
||||
</slot>
|
||||
</div>
|
||||
|
||||
@ -116,7 +122,9 @@ const normalizedTags = computed(() => {
|
||||
border: 1px solid var(--gray-150);
|
||||
background: linear-gradient(45deg, var(--gray-0) 0%, var(--gray-25) 100%);
|
||||
cursor: pointer;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
transition:
|
||||
border-color 0.2s ease,
|
||||
box-shadow 0.2s ease;
|
||||
overflow: hidden;
|
||||
|
||||
&:hover {
|
||||
@ -206,7 +214,6 @@ const normalizedTags = computed(() => {
|
||||
gap: 6px;
|
||||
}
|
||||
|
||||
|
||||
.info-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -260,13 +267,34 @@ const normalizedTags = computed(() => {
|
||||
background: var(--gray-100);
|
||||
color: var(--gray-600);
|
||||
|
||||
&.tag-purple { background: #f3e8ff; color: #7c3aed; }
|
||||
&.tag-blue { background: var(--color-info-50); color: var(--color-info-700); }
|
||||
&.tag-red { background: var(--color-error-50); color: var(--color-error-700); }
|
||||
&.tag-green { background: var(--color-success-50); color: var(--color-success-700); }
|
||||
&.tag-gold { background: #fef3c7; color: #d97706; }
|
||||
&.tag-cyan { background: #cffafe; color: #0891b2; }
|
||||
&.tag-orange { background: #fff7ed; color: #ea580c; }
|
||||
&.tag-purple {
|
||||
background: #f3e8ff;
|
||||
color: #7c3aed;
|
||||
}
|
||||
&.tag-blue {
|
||||
background: var(--color-info-50);
|
||||
color: var(--color-info-700);
|
||||
}
|
||||
&.tag-red {
|
||||
background: var(--color-error-50);
|
||||
color: var(--color-error-700);
|
||||
}
|
||||
&.tag-green {
|
||||
background: var(--color-success-50);
|
||||
color: var(--color-success-700);
|
||||
}
|
||||
&.tag-gold {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
&.tag-cyan {
|
||||
background: #cffafe;
|
||||
color: #0891b2;
|
||||
}
|
||||
&.tag-orange {
|
||||
background: #fff7ed;
|
||||
color: #ea580c;
|
||||
}
|
||||
}
|
||||
|
||||
.card-action-btn {
|
||||
@ -282,13 +310,17 @@ const normalizedTags = computed(() => {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
line-height: 1;
|
||||
transition: background-color 0.18s ease, border-color 0.18s ease, color 0.18s ease;
|
||||
transition:
|
||||
background-color 0.18s ease,
|
||||
border-color 0.18s ease,
|
||||
color 0.18s ease;
|
||||
cursor: pointer;
|
||||
appearance: none;
|
||||
background: var(--main-50);
|
||||
color: var(--main-700);
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover,
|
||||
&:focus {
|
||||
outline: none;
|
||||
border-color: var(--main-200);
|
||||
background: var(--main-50);
|
||||
@ -299,7 +331,8 @@ const normalizedTags = computed(() => {
|
||||
background: var(--color-error-50);
|
||||
color: var(--color-error-700);
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover,
|
||||
&:focus {
|
||||
border-color: var(--color-error-200);
|
||||
background: var(--color-error-50);
|
||||
color: var(--color-error-800);
|
||||
@ -319,7 +352,8 @@ const normalizedTags = computed(() => {
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
|
||||
&--success, &--on {
|
||||
&--success,
|
||||
&--on {
|
||||
background: var(--color-success-50);
|
||||
color: var(--color-success-700);
|
||||
}
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
<template>
|
||||
<div class="extension-card-grid" :style="{ gridTemplateColumns: `repeat(auto-fill, minmax(${minWidth}px, 1fr))` }">
|
||||
<div
|
||||
class="extension-card-grid"
|
||||
:style="{ gridTemplateColumns: `repeat(auto-fill, minmax(${minWidth}px, 1fr))` }"
|
||||
>
|
||||
<slot />
|
||||
<div v-if="!$slots.default && items.length === 0" class="extension-card-grid-empty">
|
||||
<a-empty :image="false" :description="emptyText" />
|
||||
|
||||
@ -28,4 +28,4 @@ defineProps({
|
||||
max-width: 720px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -76,4 +76,4 @@ defineProps({
|
||||
background-color: transparent;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -14,8 +14,14 @@
|
||||
</template>
|
||||
</PageShoulder>
|
||||
|
||||
<div v-if="filteredEnabledServers.length === 0 && filteredDisabledServers.length === 0" class="extension-card-grid-empty-state">
|
||||
<a-empty :image="false" :description="searchQuery ? '无匹配 MCP' : '暂无 MCP,点击上方按钮添加'" />
|
||||
<div
|
||||
v-if="filteredEnabledServers.length === 0 && filteredDisabledServers.length === 0"
|
||||
class="extension-card-grid-empty-state"
|
||||
>
|
||||
<a-empty
|
||||
:image="false"
|
||||
:description="searchQuery ? '无匹配 MCP' : '暂无 MCP,点击上方按钮添加'"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
@ -89,7 +95,10 @@ const formModalVisible = ref(false)
|
||||
|
||||
const filteredServers = computed(() => {
|
||||
const sorted = [...servers.value].sort((a, b) =>
|
||||
String(a.name || '').localeCompare(String(b.name || ''), 'zh-Hans-CN', { sensitivity: 'base', numeric: true })
|
||||
String(a.name || '').localeCompare(String(b.name || ''), 'zh-Hans-CN', {
|
||||
sensitivity: 'base',
|
||||
numeric: true
|
||||
})
|
||||
)
|
||||
if (!searchQuery.value) return sorted
|
||||
const q = searchQuery.value.toLowerCase()
|
||||
@ -98,8 +107,12 @@ const filteredServers = computed(() => {
|
||||
)
|
||||
})
|
||||
|
||||
const filteredEnabledServers = computed(() => filteredServers.value.filter((item) => !!item.enabled))
|
||||
const filteredDisabledServers = computed(() => filteredServers.value.filter((item) => !item.enabled))
|
||||
const filteredEnabledServers = computed(() =>
|
||||
filteredServers.value.filter((item) => !!item.enabled)
|
||||
)
|
||||
const filteredDisabledServers = computed(() =>
|
||||
filteredServers.value.filter((item) => !item.enabled)
|
||||
)
|
||||
|
||||
const getTransportColor = (transport) => {
|
||||
const colors = { sse: 'orange', stdio: 'green', streamable_http: 'blue' }
|
||||
@ -109,7 +122,8 @@ const getTransportColor = (transport) => {
|
||||
const mcpTags = (server) => {
|
||||
const tags = []
|
||||
if (server.created_by === 'system') tags.push({ name: '内置' })
|
||||
if (server.transport) tags.push({ name: server.transport, color: getTransportColor(server.transport) })
|
||||
if (server.transport)
|
||||
tags.push({ name: server.transport, color: getTransportColor(server.transport) })
|
||||
return tags
|
||||
}
|
||||
|
||||
@ -168,4 +182,4 @@ defineExpose({ fetchServers, loading })
|
||||
font-size: 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -40,7 +40,9 @@
|
||||
:class="[
|
||||
'lucide-icon-btn',
|
||||
'extension-panel-action',
|
||||
server?.enabled === false ? 'extension-panel-action-primary' : 'extension-panel-action-danger'
|
||||
server?.enabled === false
|
||||
? 'extension-panel-action-primary'
|
||||
: 'extension-panel-action-danger'
|
||||
]"
|
||||
>
|
||||
<Plus v-if="server?.enabled === false" :size="14" />
|
||||
@ -68,21 +70,31 @@
|
||||
<div class="info-item">
|
||||
<label>传输类型</label>
|
||||
<span>
|
||||
<a-tag :color="getTransportColor(server.transport)">{{ server.transport }}</a-tag>
|
||||
<a-tag :color="getTransportColor(server.transport)">{{
|
||||
server.transport
|
||||
}}</a-tag>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item" v-if="Array.isArray(server.tags) && server.tags.length > 0">
|
||||
<div
|
||||
class="info-item"
|
||||
v-if="Array.isArray(server.tags) && server.tags.length > 0"
|
||||
>
|
||||
<label>标签</label>
|
||||
<span>
|
||||
<a-tag v-for="tag in server.tags" :key="tag">{{ tag }}</a-tag>
|
||||
</span>
|
||||
</div>
|
||||
<template v-if="server.transport === 'streamable_http' || server.transport === 'sse'">
|
||||
<template
|
||||
v-if="server.transport === 'streamable_http' || server.transport === 'sse'"
|
||||
>
|
||||
<div class="info-item" v-if="server.url">
|
||||
<label>MCP URL</label>
|
||||
<span class="code-inline text-break-all">{{ server.url }}</span>
|
||||
</div>
|
||||
<div class="info-item" v-if="server.headers && Object.keys(server.headers).length > 0">
|
||||
<div
|
||||
class="info-item"
|
||||
v-if="server.headers && Object.keys(server.headers).length > 0"
|
||||
>
|
||||
<label>请求头</label>
|
||||
<pre class="code-pre">{{ JSON.stringify(server.headers, null, 2) }}</pre>
|
||||
</div>
|
||||
@ -103,7 +115,9 @@
|
||||
<div class="info-item" v-if="server.args && server.args.length > 0">
|
||||
<label>参数</label>
|
||||
<span>
|
||||
<a-tag v-for="(arg, index) in server.args" :key="index" size="small">{{ arg }}</a-tag>
|
||||
<a-tag v-for="(arg, index) in server.args" :key="index" size="small">{{
|
||||
arg
|
||||
}}</a-tag>
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item" v-if="server.env && Object.keys(server.env).length > 0">
|
||||
@ -184,7 +198,10 @@
|
||||
<div class="tool-description" v-if="tool.description">
|
||||
{{ tool.description }}
|
||||
</div>
|
||||
<a-collapse v-if="tool.parameters && Object.keys(tool.parameters).length > 0" ghost>
|
||||
<a-collapse
|
||||
v-if="tool.parameters && Object.keys(tool.parameters).length > 0"
|
||||
ghost
|
||||
>
|
||||
<a-collapse-panel key="params" header="参数">
|
||||
<div class="params-list">
|
||||
<div
|
||||
@ -194,10 +211,16 @@
|
||||
>
|
||||
<div class="param-header">
|
||||
<span class="param-name">{{ paramName }}</span>
|
||||
<span class="param-required" v-if="tool.required?.includes(paramName)">必填</span>
|
||||
<span
|
||||
class="param-required"
|
||||
v-if="tool.required?.includes(paramName)"
|
||||
>必填</span
|
||||
>
|
||||
<span class="param-type">{{ param.type || 'any' }}</span>
|
||||
</div>
|
||||
<div class="param-desc" v-if="param.description">{{ param.description }}</div>
|
||||
<div class="param-desc" v-if="param.description">
|
||||
{{ param.description }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
@ -272,7 +295,9 @@ const filteredTools = computed(() => {
|
||||
if (!toolSearchText.value) return tools.value
|
||||
const search = toolSearchText.value.toLowerCase()
|
||||
return tools.value.filter(
|
||||
(t) => t.name.toLowerCase().includes(search) || (t.description && t.description.toLowerCase().includes(search))
|
||||
(t) =>
|
||||
t.name.toLowerCase().includes(search) ||
|
||||
(t.description && t.description.toLowerCase().includes(search))
|
||||
)
|
||||
})
|
||||
|
||||
@ -458,4 +483,4 @@ onMounted(() => {
|
||||
padding: 16px var(--page-padding);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -18,7 +18,11 @@
|
||||
|
||||
<a-form v-if="formMode === 'form'" layout="vertical" class="extension-form">
|
||||
<a-form-item label="MCP 名称" required class="form-item">
|
||||
<a-input v-model:value="form.name" placeholder="请输入 MCP 名称(唯一标识)" :disabled="editMode" />
|
||||
<a-input
|
||||
v-model:value="form.name"
|
||||
placeholder="请输入 MCP 名称(唯一标识)"
|
||||
:disabled="editMode"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="描述" class="form-item">
|
||||
<a-input v-model:value="form.description" placeholder="请输入 MCP 描述" />
|
||||
@ -44,17 +48,31 @@
|
||||
<a-input v-model:value="form.url" placeholder="https://example.com/mcp" />
|
||||
</a-form-item>
|
||||
<a-form-item label="HTTP 请求头" class="form-item">
|
||||
<a-textarea v-model:value="form.headersText" placeholder='JSON 格式,如:{"Authorization": "Bearer xxx"}' :rows="3" />
|
||||
<a-textarea
|
||||
v-model:value="form.headersText"
|
||||
placeholder='JSON 格式,如:{"Authorization": "Bearer xxx"}'
|
||||
:rows="3"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-row :gutter="16">
|
||||
<a-col :span="12">
|
||||
<a-form-item label="HTTP 超时(秒)" class="form-item">
|
||||
<a-input-number v-model:value="form.timeout" :min="1" :max="300" style="width: 100%" />
|
||||
<a-input-number
|
||||
v-model:value="form.timeout"
|
||||
:min="1"
|
||||
:max="300"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
<a-col :span="12">
|
||||
<a-form-item label="SSE 读取超时(秒)" class="form-item">
|
||||
<a-input-number v-model:value="form.sse_read_timeout" :min="1" :max="300" style="width: 100%" />
|
||||
<a-input-number
|
||||
v-model:value="form.sse_read_timeout"
|
||||
:min="1"
|
||||
:max="300"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-col>
|
||||
</a-row>
|
||||
@ -64,14 +82,24 @@
|
||||
<a-input v-model:value="form.command" placeholder="例如:npx 或 /path/to/server" />
|
||||
</a-form-item>
|
||||
<a-form-item label="参数" class="form-item">
|
||||
<a-select v-model:value="form.args" mode="tags" placeholder="输入参数后回车添加,如:-m" style="width: 100%" />
|
||||
<a-select
|
||||
v-model:value="form.args"
|
||||
mode="tags"
|
||||
placeholder="输入参数后回车添加,如:-m"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="环境变量" class="form-item">
|
||||
<McpEnvEditor v-model="form.env" />
|
||||
</a-form-item>
|
||||
</template>
|
||||
<a-form-item label="标签" class="form-item">
|
||||
<a-select v-model:value="form.tags" mode="tags" placeholder="输入标签后回车添加" style="width: 100%" />
|
||||
<a-select
|
||||
v-model:value="form.tags"
|
||||
mode="tags"
|
||||
placeholder="输入标签后回车添加"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
|
||||
@ -79,7 +107,7 @@
|
||||
<a-textarea
|
||||
v-model:value="jsonContent"
|
||||
:rows="15"
|
||||
placeholder='请输入 JSON 配置'
|
||||
placeholder="请输入 JSON 配置"
|
||||
class="json-textarea"
|
||||
/>
|
||||
<div class="json-actions">
|
||||
@ -129,7 +157,10 @@ const form = reactive({
|
||||
})
|
||||
|
||||
const isStdioTransport = computed(
|
||||
() => String(form.transport || '').trim().toLowerCase() === 'stdio'
|
||||
() =>
|
||||
String(form.transport || '')
|
||||
.trim()
|
||||
.toLowerCase() === 'stdio'
|
||||
)
|
||||
|
||||
watch(
|
||||
@ -310,4 +341,4 @@ const handleFormSubmit = async () => {
|
||||
gap: 8px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -2,7 +2,11 @@
|
||||
<div class="skill-cards-page extension-page-root">
|
||||
<PageShoulder search-placeholder="搜索技能..." v-model:search="searchQuery">
|
||||
<template #actions>
|
||||
<a-button @click="handleOpenRemoteInstall" :disabled="loading || importing" class="lucide-icon-btn">
|
||||
<a-button
|
||||
@click="handleOpenRemoteInstall"
|
||||
:disabled="loading || importing"
|
||||
class="lucide-icon-btn"
|
||||
>
|
||||
<Computer :size="14" />
|
||||
<span>远程安装</span>
|
||||
</a-button>
|
||||
@ -26,12 +30,17 @@
|
||||
</template>
|
||||
</PageShoulder>
|
||||
|
||||
<div v-if="filteredInstalledSkills.length === 0 && filteredUninstalledBuiltinSkills.length === 0" class="extension-card-grid-empty-state">
|
||||
<div
|
||||
v-if="filteredInstalledSkills.length === 0 && filteredUninstalledBuiltinSkills.length === 0"
|
||||
class="extension-card-grid-empty-state"
|
||||
>
|
||||
<a-empty :image="false" description="无匹配技能" />
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div v-if="filteredInstalledSkills.length" class="extension-section-header">已添加 Skills</div>
|
||||
<div v-if="filteredInstalledSkills.length" class="extension-section-header">
|
||||
已添加 Skills
|
||||
</div>
|
||||
<ExtensionCardGrid>
|
||||
<ExtensionCard
|
||||
v-for="skill in filteredInstalledSkills"
|
||||
@ -46,7 +55,9 @@
|
||||
</ExtensionCard>
|
||||
</ExtensionCardGrid>
|
||||
|
||||
<div v-if="filteredUninstalledBuiltinSkills.length" class="extension-section-header">可添加 Skills</div>
|
||||
<div v-if="filteredUninstalledBuiltinSkills.length" class="extension-section-header">
|
||||
可添加 Skills
|
||||
</div>
|
||||
<ExtensionCardGrid v-if="filteredUninstalledBuiltinSkills.length">
|
||||
<ExtensionCard
|
||||
v-for="skill in filteredUninstalledBuiltinSkills"
|
||||
@ -102,13 +113,24 @@
|
||||
/>
|
||||
</a-form-item>
|
||||
<div class="remote-install-actions">
|
||||
<a-button :loading="listingRemoteSkills" :disabled="installingRemoteSkill" @click="handleListRemoteSkills">
|
||||
<a-button
|
||||
:loading="listingRemoteSkills"
|
||||
:disabled="installingRemoteSkill"
|
||||
@click="handleListRemoteSkills"
|
||||
>
|
||||
查看可安装 Skills
|
||||
</a-button>
|
||||
<a-button type="primary" :loading="installingRemoteSkill" :disabled="listingRemoteSkills" @click="handleInstallRemoteSkill">
|
||||
<a-button
|
||||
type="primary"
|
||||
:loading="installingRemoteSkill"
|
||||
:disabled="listingRemoteSkills"
|
||||
@click="handleInstallRemoteSkill"
|
||||
>
|
||||
安装
|
||||
</a-button>
|
||||
<span v-if="remoteInstallStatusText" class="remote-install-status">{{ remoteInstallStatusText }}</span>
|
||||
<span v-if="remoteInstallStatusText" class="remote-install-status">{{
|
||||
remoteInstallStatusText
|
||||
}}</span>
|
||||
</div>
|
||||
<div v-if="remoteSkillOptions.length" class="remote-skill-summary">
|
||||
共发现 {{ remoteSkillOptions.length }} 个 skills,可按输入内容筛选候选项。
|
||||
@ -174,9 +196,10 @@ const installedSkillCards = computed(() => {
|
||||
...skill,
|
||||
sourceType: 'builtin',
|
||||
sourceLabel: '内置',
|
||||
status: skill.status === 'update_available'
|
||||
? { label: '更新可用', level: 'warning' }
|
||||
: { label: '已安装', level: 'success' }
|
||||
status:
|
||||
skill.status === 'update_available'
|
||||
? { label: '更新可用', level: 'warning' }
|
||||
: { label: '已安装', level: 'success' }
|
||||
}
|
||||
])
|
||||
)
|
||||
@ -194,7 +217,9 @@ const installedSkillCards = computed(() => {
|
||||
const filteredInstalledSkills = computed(() => installedSkillCards.value.filter(matchesSearch))
|
||||
|
||||
const filteredUninstalledBuiltinSkills = computed(() => {
|
||||
return (builtinSkills.value || []).filter((skill) => skill.status === 'not_installed' && matchesSearch(skill))
|
||||
return (builtinSkills.value || []).filter(
|
||||
(skill) => skill.status === 'not_installed' && matchesSearch(skill)
|
||||
)
|
||||
})
|
||||
|
||||
const filteredRemoteSkillOptions = computed(() =>
|
||||
@ -208,7 +233,8 @@ const remoteInstallStatusText = computed(() => {
|
||||
if (!remoteInstallProgress.visible || !remoteInstallProgress.total) return ''
|
||||
const progressText = `[${remoteInstallProgress.completed}/${remoteInstallProgress.total}]`
|
||||
const currentSkill = remoteInstallProgress.currentSkill || ''
|
||||
const failedText = remoteInstallProgress.failed > 0 ? `, ${remoteInstallProgress.failed} failed` : ''
|
||||
const failedText =
|
||||
remoteInstallProgress.failed > 0 ? `, ${remoteInstallProgress.failed} failed` : ''
|
||||
return `${progressText} ${currentSkill}${failedText}`.trim()
|
||||
})
|
||||
|
||||
@ -401,12 +427,16 @@ onMounted(() => {
|
||||
fetchSkills()
|
||||
})
|
||||
|
||||
defineExpose({ fetchSkills, handleImportUpload, openRemoteInstallModal: handleOpenRemoteInstall, loading })
|
||||
defineExpose({
|
||||
fetchSkills,
|
||||
handleImportUpload,
|
||||
openRemoteInstallModal: handleOpenRemoteInstall,
|
||||
loading
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@import '@/assets/css/extensions.less';
|
||||
|
||||
</style>
|
||||
|
||||
<style lang="less" scoped>
|
||||
@ -469,4 +499,4 @@ defineExpose({ fetchSkills, handleImportUpload, openRemoteInstallModal: handleOp
|
||||
color: var(--gray-500);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -19,7 +19,11 @@
|
||||
</div>
|
||||
<div class="detail-actions">
|
||||
<a-space :size="8">
|
||||
<span v-if="currentSkillStatusLabel" class="panel-status-chip" :class="{ warning: currentSkillStatusTone === 'warning' }">
|
||||
<span
|
||||
v-if="currentSkillStatusLabel"
|
||||
class="panel-status-chip"
|
||||
:class="{ warning: currentSkillStatusTone === 'warning' }"
|
||||
>
|
||||
{{ currentSkillStatusLabel }}
|
||||
</span>
|
||||
<button
|
||||
@ -66,7 +70,9 @@
|
||||
<div v-if="!isInstalledSkill" class="builtin-uninstalled-state">
|
||||
<h3>{{ currentSkill.description }}</h3>
|
||||
<p>版本 {{ currentSkill.version }}</p>
|
||||
<a-button type="primary" @click="handleInstallBuiltin(currentSkill)">安装内置 Skill</a-button>
|
||||
<a-button type="primary" @click="handleInstallBuiltin(currentSkill)"
|
||||
>安装内置 Skill</a-button
|
||||
>
|
||||
</div>
|
||||
|
||||
<a-tabs v-else v-model:activeKey="activeTab" class="minimal-tabs">
|
||||
@ -79,9 +85,15 @@
|
||||
<div class="tree-header">
|
||||
<span class="label">项目结构</span>
|
||||
<div class="tree-actions">
|
||||
<a-tooltip v-if="!isBuiltinInstalledSkill" title="新建文件"><button @click="openCreateModal(false)"><FilePlus :size="14" /></button></a-tooltip>
|
||||
<a-tooltip v-if="!isBuiltinInstalledSkill" title="新建目录"><button @click="openCreateModal(true)"><FolderPlus :size="14" /></button></a-tooltip>
|
||||
<a-tooltip title="刷新"><button @click="reloadTree"><RotateCw :size="14" /></button></a-tooltip>
|
||||
<a-tooltip v-if="!isBuiltinInstalledSkill" title="新建文件"
|
||||
><button @click="openCreateModal(false)"><FilePlus :size="14" /></button
|
||||
></a-tooltip>
|
||||
<a-tooltip v-if="!isBuiltinInstalledSkill" title="新建目录"
|
||||
><button @click="openCreateModal(true)"><FolderPlus :size="14" /></button
|
||||
></a-tooltip>
|
||||
<a-tooltip title="刷新"
|
||||
><button @click="reloadTree"><RotateCw :size="14" /></button
|
||||
></a-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tree-content">
|
||||
@ -127,7 +139,11 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="editor-main">
|
||||
<a-empty v-if="!selectedPath || selectedIsDir" description="选择文件以开始编辑" class="mt-40" />
|
||||
<a-empty
|
||||
v-if="!selectedPath || selectedIsDir"
|
||||
description="选择文件以开始编辑"
|
||||
class="mt-40"
|
||||
/>
|
||||
<template v-else>
|
||||
<MdPreview
|
||||
v-if="viewMode === 'preview'"
|
||||
@ -172,13 +188,34 @@
|
||||
<div class="config-form">
|
||||
<a-form layout="vertical">
|
||||
<a-form-item label="工具依赖 (Tools)">
|
||||
<a-select v-model:value="dependencyForm.tool_dependencies" mode="multiple" :options="toolDependencyOptions" placeholder="选择工具..." allow-clear show-search />
|
||||
<a-select
|
||||
v-model:value="dependencyForm.tool_dependencies"
|
||||
mode="multiple"
|
||||
:options="toolDependencyOptions"
|
||||
placeholder="选择工具..."
|
||||
allow-clear
|
||||
show-search
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="MCP 依赖 (Model Context Protocol)">
|
||||
<a-select v-model:value="dependencyForm.mcp_dependencies" mode="multiple" :options="mcpDependencyOptions" placeholder="选择 MCP 服务..." allow-clear show-search />
|
||||
<a-select
|
||||
v-model:value="dependencyForm.mcp_dependencies"
|
||||
mode="multiple"
|
||||
:options="mcpDependencyOptions"
|
||||
placeholder="选择 MCP 服务..."
|
||||
allow-clear
|
||||
show-search
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="Skill 依赖">
|
||||
<a-select v-model:value="dependencyForm.skill_dependencies" mode="multiple" :options="skillDependencyOptions" placeholder="选择 Skill..." allow-clear show-search />
|
||||
<a-select
|
||||
v-model:value="dependencyForm.skill_dependencies"
|
||||
mode="multiple"
|
||||
:options="skillDependencyOptions"
|
||||
placeholder="选择 Skill..."
|
||||
allow-clear
|
||||
show-search
|
||||
/>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</div>
|
||||
@ -270,11 +307,17 @@ const dependencyForm = reactive({
|
||||
})
|
||||
|
||||
const isInstalledSkill = computed(() => {
|
||||
return !!(currentSkill.value && (currentSkill.value.installed_record || currentSkill.value.dir_path))
|
||||
return !!(
|
||||
currentSkill.value &&
|
||||
(currentSkill.value.installed_record || currentSkill.value.dir_path)
|
||||
)
|
||||
})
|
||||
|
||||
const isBuiltinInstalledSkill = computed(() => {
|
||||
return !!(isInstalledSkill.value && (currentSkill.value?.is_builtin || currentSkill.value?.installed_record))
|
||||
return !!(
|
||||
isInstalledSkill.value &&
|
||||
(currentSkill.value?.is_builtin || currentSkill.value?.installed_record)
|
||||
)
|
||||
})
|
||||
|
||||
const currentSkillStatusLabel = computed(() => {
|
||||
@ -312,7 +355,9 @@ const mcpDependencyOptions = computed(() =>
|
||||
(dependencyOptions.mcps || []).map((i) => ({ label: i, value: i }))
|
||||
)
|
||||
const skillDependencyOptions = computed(() =>
|
||||
(dependencyOptions.skills || []).filter((s) => s !== currentSkill.value?.slug).map((i) => ({ label: i, value: i }))
|
||||
(dependencyOptions.skills || [])
|
||||
.filter((s) => s !== currentSkill.value?.slug)
|
||||
.map((i) => ({ label: i, value: i }))
|
||||
)
|
||||
|
||||
const goBack = () => {
|
||||
@ -451,7 +496,13 @@ const handleTreeSelect = async (keys, info) => {
|
||||
}
|
||||
|
||||
const saveCurrentFile = async () => {
|
||||
if (!currentSkill.value || !selectedPath.value || selectedIsDir.value || isBuiltinInstalledSkill.value) return
|
||||
if (
|
||||
!currentSkill.value ||
|
||||
!selectedPath.value ||
|
||||
selectedIsDir.value ||
|
||||
isBuiltinInstalledSkill.value
|
||||
)
|
||||
return
|
||||
savingFile.value = true
|
||||
try {
|
||||
await skillApi.updateSkillFile(currentSkill.value.slug, {
|
||||
@ -521,7 +572,11 @@ const handleUpdateBuiltin = async (record) => {
|
||||
const confirmDeleteSkill = () => {
|
||||
const target = currentSkill.value
|
||||
if (!target) return
|
||||
const isBuiltinTarget = !!(target?.is_builtin || target?.installed_record || target?.sourceType === 'builtin')
|
||||
const isBuiltinTarget = !!(
|
||||
target?.is_builtin ||
|
||||
target?.installed_record ||
|
||||
target?.sourceType === 'builtin'
|
||||
)
|
||||
const actionText = isBuiltinTarget ? '卸载' : '删除'
|
||||
Modal.confirm({
|
||||
title: `确认${actionText}技能「${target.slug}」?`,
|
||||
@ -840,4 +895,4 @@ onMounted(() => {
|
||||
.pt-12 {
|
||||
padding-top: 12px;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -14,7 +14,10 @@
|
||||
</template>
|
||||
</PageShoulder>
|
||||
|
||||
<div v-if="filteredEnabledSubAgents.length === 0 && filteredDisabledSubAgents.length === 0" class="extension-card-grid-empty-state">
|
||||
<div
|
||||
v-if="filteredEnabledSubAgents.length === 0 && filteredDisabledSubAgents.length === 0"
|
||||
class="extension-card-grid-empty-state"
|
||||
>
|
||||
<a-empty :image="false" :description="searchQuery ? '无匹配 SubAgent' : '暂无 SubAgent'" />
|
||||
</div>
|
||||
|
||||
@ -27,7 +30,7 @@
|
||||
:title="agent.name"
|
||||
:description="agent.description || '暂无描述'"
|
||||
:default-icon="BotIcon"
|
||||
:tags="agent.is_builtin ? [{ name: '内置' }] : [{name: '添加'}]"
|
||||
:tags="agent.is_builtin ? [{ name: '内置' }] : [{ name: '添加' }]"
|
||||
:status="{ label: '已添加', level: 'success' }"
|
||||
@click="navigateToDetail(agent)"
|
||||
>
|
||||
@ -42,7 +45,7 @@
|
||||
:title="agent.name"
|
||||
:description="agent.description || '暂无描述'"
|
||||
:default-icon="BotIcon"
|
||||
:tags="agent.is_builtin ? [{ name: '内置' }] : [{name: '添加'}]"
|
||||
:tags="agent.is_builtin ? [{ name: '内置' }] : [{ name: '添加' }]"
|
||||
action-label="添加"
|
||||
@click="navigateToDetail(agent)"
|
||||
@action-click="handleSetAgentEnabled(agent, true)"
|
||||
@ -88,7 +91,9 @@
|
||||
class="model-selector-full"
|
||||
@select-model="handleModelSelect"
|
||||
/>
|
||||
<a-button v-if="form.model" type="link" size="small" @click="form.model = ''">清空</a-button>
|
||||
<a-button v-if="form.model" type="link" size="small" @click="form.model = ''"
|
||||
>清空</a-button
|
||||
>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@ -259,4 +264,4 @@ defineExpose({ fetchSubAgents, loading })
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -140,7 +140,11 @@
|
||||
>
|
||||
<a-form layout="vertical" class="extension-form">
|
||||
<a-form-item label="名称" required class="form-item">
|
||||
<a-input v-model:value="form.name" placeholder="请输入 SubAgent 名称(唯一标识)" disabled />
|
||||
<a-input
|
||||
v-model:value="form.name"
|
||||
placeholder="请输入 SubAgent 名称(唯一标识)"
|
||||
disabled
|
||||
/>
|
||||
</a-form-item>
|
||||
<a-form-item label="描述" class="form-item">
|
||||
<a-input v-model:value="form.description" placeholder="请输入 SubAgent 描述" />
|
||||
@ -166,7 +170,9 @@
|
||||
class="model-selector-full"
|
||||
@select-model="handleModelSelect"
|
||||
/>
|
||||
<a-button v-if="form.model" type="link" size="small" @click="form.model = ''">清空</a-button>
|
||||
<a-button v-if="form.model" type="link" size="small" @click="form.model = ''"
|
||||
>清空</a-button
|
||||
>
|
||||
</div>
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
@ -353,4 +359,4 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -219,4 +219,4 @@ defineExpose({ fetchTools, loading })
|
||||
.config-guide {
|
||||
white-space: pre-line;
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -114,7 +114,9 @@ function emitChange(item) {
|
||||
line-height: 1;
|
||||
text-decoration: none;
|
||||
cursor: pointer;
|
||||
transition: background-color 0.2s ease, color 0.2s ease;
|
||||
transition:
|
||||
background-color 0.2s ease,
|
||||
color 0.2s ease;
|
||||
|
||||
&:hover {
|
||||
color: var(--gray-900);
|
||||
|
||||
@ -23,7 +23,8 @@ export function useModelStatus() {
|
||||
const getStatusTooltip = (key) => {
|
||||
const status = statusMap[key]
|
||||
if (!status) return '状态未知'
|
||||
const text = { available: '可用', unavailable: '不可用', error: '错误' }[status.status] || '未知'
|
||||
const text =
|
||||
{ available: '可用', unavailable: '不可用', error: '错误' }[status.status] || '未知'
|
||||
return `${text}: ${status.message || '无详细信息'}`
|
||||
}
|
||||
|
||||
@ -44,5 +45,12 @@ export function useModelStatus() {
|
||||
}
|
||||
}
|
||||
|
||||
return { statusMap, getStatusIcon, getStatusClass, getStatusTooltip, checkV2Status, checkV2Statuses }
|
||||
}
|
||||
return {
|
||||
statusMap,
|
||||
getStatusIcon,
|
||||
getStatusClass,
|
||||
getStatusTooltip,
|
||||
checkV2Status,
|
||||
checkV2Statuses
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,4 +29,4 @@ export const modelIcons = {
|
||||
zhipu: `${ICON_BASE}/zhipu-color.svg`,
|
||||
zhipuai: `${ICON_BASE}/zhipu-color.svg`,
|
||||
'zhipuai-coding-plan': `${ICON_BASE}/zhipu-color.svg`
|
||||
}
|
||||
}
|
||||
|
||||
@ -53,9 +53,11 @@ const extensionTabs = [
|
||||
]
|
||||
|
||||
const isDetailPage = computed(() => {
|
||||
return route.path.startsWith('/extensions/mcp/') ||
|
||||
return (
|
||||
route.path.startsWith('/extensions/mcp/') ||
|
||||
route.path.startsWith('/extensions/subagent/') ||
|
||||
route.path.startsWith('/extensions/skill/')
|
||||
)
|
||||
})
|
||||
|
||||
const activeChildLoading = computed(() => {
|
||||
@ -91,4 +93,4 @@ watch(
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -256,7 +256,6 @@ function getProviderInfo(provider) {
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
function getProviderStatus(provider) {
|
||||
if (!provider.is_enabled) return { label: '未启用', level: 'info' }
|
||||
if (provider.credential_status === 'warning') return { label: '凭证缺失', level: 'warning' }
|
||||
|
||||
Loading…
Reference in New Issue
Block a user