mirror of
https://github.com/eigent-ai/eigent.git
synced 2026-08-22 07:03:33 +00:00
Polish ux and ui flow for new open connector to replace original build in connector
This commit is contained in:
parent
d8d0a9f087
commit
c8aaa6232f
37 changed files with 4850 additions and 3040 deletions
|
|
@ -121,18 +121,49 @@ export interface FetchConnectorProvidersOptions {
|
|||
query?: string;
|
||||
}
|
||||
|
||||
export async function fetchConnectorProviders(
|
||||
export function providerLabel(provider: ConnectorProvider): string {
|
||||
return provider.displayName || provider.service;
|
||||
}
|
||||
|
||||
export function isConnectedProvider(
|
||||
provider: ConnectorProvider | null | undefined
|
||||
): boolean {
|
||||
const connection = provider?.connection;
|
||||
return connection?.configured === true && connection.virtual !== true;
|
||||
}
|
||||
|
||||
export interface FetchConnectorProvidersRequestOptions {
|
||||
/** Skip the short-lived list cache and force a network fetch. */
|
||||
bypassCache?: boolean;
|
||||
}
|
||||
|
||||
const PROVIDERS_LIST_CACHE_TTL_MS = 60_000;
|
||||
|
||||
type ProvidersListCacheEntry = {
|
||||
expiresAt: number;
|
||||
data: ConnectorProvidersResponse;
|
||||
};
|
||||
|
||||
const providersListCache = new Map<string, ProvidersListCacheEntry>();
|
||||
const providersListInflight = new Map<
|
||||
string,
|
||||
Promise<ConnectorProvidersResponse>
|
||||
>();
|
||||
|
||||
function providersListCacheKey(
|
||||
options: FetchConnectorProvidersOptions = {}
|
||||
): Promise<ConnectorProvidersResponse> {
|
||||
const params: Record<string, string | number> = {
|
||||
page: options.page || 1,
|
||||
page_size: options.pageSize || 24,
|
||||
};
|
||||
const query = options.query?.trim();
|
||||
if (query) {
|
||||
params.q = query;
|
||||
}
|
||||
const response = await proxyFetchGet('/api/v1/connectors/providers', params);
|
||||
): string {
|
||||
return [
|
||||
options.page || 1,
|
||||
options.pageSize || 24,
|
||||
options.query?.trim() || '',
|
||||
].join('::');
|
||||
}
|
||||
|
||||
function normalizeProvidersResponse(
|
||||
response: any,
|
||||
options: FetchConnectorProvidersOptions = {}
|
||||
): ConnectorProvidersResponse {
|
||||
const providers = Array.isArray(response?.providers)
|
||||
? response.providers
|
||||
: [];
|
||||
|
|
@ -168,6 +199,88 @@ export async function fetchConnectorProviders(
|
|||
};
|
||||
}
|
||||
|
||||
/** Synchronous cache read for instant UI hydration. */
|
||||
export function getCachedConnectorProviders(
|
||||
options: FetchConnectorProvidersOptions = {}
|
||||
): ConnectorProvidersResponse | null {
|
||||
const entry = providersListCache.get(providersListCacheKey(options));
|
||||
if (!entry || entry.expiresAt <= Date.now()) return null;
|
||||
return entry.data;
|
||||
}
|
||||
|
||||
export function invalidateConnectorProvidersCache(): void {
|
||||
providersListCache.clear();
|
||||
providersListInflight.clear();
|
||||
}
|
||||
|
||||
/** Warm the list cache without waiting for dialog open. */
|
||||
export function prefetchConnectorProviders(
|
||||
options: FetchConnectorProvidersOptions = {}
|
||||
): Promise<ConnectorProvidersResponse> {
|
||||
return fetchConnectorProviders(options);
|
||||
}
|
||||
|
||||
export async function fetchConnectorProviders(
|
||||
options: FetchConnectorProvidersOptions = {},
|
||||
requestOptions: FetchConnectorProvidersRequestOptions = {}
|
||||
): Promise<ConnectorProvidersResponse> {
|
||||
const cacheKey = providersListCacheKey(options);
|
||||
if (!requestOptions.bypassCache) {
|
||||
const cached = getCachedConnectorProviders(options);
|
||||
if (cached) return cached;
|
||||
}
|
||||
// Always coalesce concurrent identical requests, even when bypassing cache.
|
||||
const inflight = providersListInflight.get(cacheKey);
|
||||
if (inflight) return inflight;
|
||||
|
||||
const request = (async () => {
|
||||
const params: Record<string, string | number> = {
|
||||
page: options.page || 1,
|
||||
page_size: options.pageSize || 24,
|
||||
};
|
||||
const query = options.query?.trim();
|
||||
if (query) {
|
||||
params.q = query;
|
||||
}
|
||||
const response = await proxyFetchGet(
|
||||
'/api/v1/connectors/providers',
|
||||
params
|
||||
);
|
||||
const normalized = normalizeProvidersResponse(response, options);
|
||||
providersListCache.set(cacheKey, {
|
||||
expiresAt: Date.now() + PROVIDERS_LIST_CACHE_TTL_MS,
|
||||
data: normalized,
|
||||
});
|
||||
return normalized;
|
||||
})();
|
||||
|
||||
providersListInflight.set(cacheKey, request);
|
||||
try {
|
||||
return await request;
|
||||
} finally {
|
||||
if (providersListInflight.get(cacheKey) === request) {
|
||||
providersListInflight.delete(cacheKey);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Fetch every provider page and return only connected providers. */
|
||||
export async function fetchConnectedProviders(): Promise<ConnectorProvider[]> {
|
||||
const first = await fetchConnectorProviders({ page: 1, pageSize: 100 });
|
||||
let providers = first.providers;
|
||||
for (let page = 2; page <= first.total_pages; page += 1) {
|
||||
const response = await fetchConnectorProviders({
|
||||
page,
|
||||
pageSize: first.page_size,
|
||||
});
|
||||
providers = providers.concat(response.providers);
|
||||
}
|
||||
const unique = new Map(
|
||||
providers.map((provider) => [provider.service, provider])
|
||||
);
|
||||
return Array.from(unique.values()).filter(isConnectedProvider);
|
||||
}
|
||||
|
||||
export async function fetchConnectorProvider(
|
||||
service: string
|
||||
): Promise<ConnectorProviderResponse> {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import {
|
|||
} from '@/api/http';
|
||||
import IntegrationList from '@/components/Dashboard/IntegrationList';
|
||||
import { Badge } from '@/components/ui/badge';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
useIntegrationManagement,
|
||||
type IntegrationItem,
|
||||
|
|
@ -42,6 +43,7 @@ import {
|
|||
useState,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { Checkbox } from '../ui/checkbox';
|
||||
import { Textarea } from '../ui/textarea';
|
||||
import { TooltipSimple } from '../ui/tooltip';
|
||||
|
|
@ -258,6 +260,7 @@ const ToolSelect = forwardRef<
|
|||
const host = useHost();
|
||||
const electronAPI = host?.electronAPI;
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
// state management - remove internal selected state, use parent passed initialSelectedTools
|
||||
const [keyword, setKeyword] = useState<string>('');
|
||||
const { email } = useAuthStore();
|
||||
|
|
@ -783,36 +786,22 @@ const ToolSelect = forwardRef<
|
|||
});
|
||||
}, [integrations, webInstalled, keyword]);
|
||||
|
||||
const webNotConnectedItems = useMemo(() => {
|
||||
const kw = keyword.trim().toLowerCase();
|
||||
return integrations
|
||||
.filter((i: IntegrationItem) => !webInstalled[i.key])
|
||||
.filter((i: IntegrationItem) => {
|
||||
if (!kw) return true;
|
||||
const descStr = typeof i.desc === 'string' ? i.desc.toLowerCase() : '';
|
||||
return (
|
||||
(i.key || '').toLowerCase().includes(kw) ||
|
||||
(i.name || '').toLowerCase().includes(kw) ||
|
||||
descStr.includes(kw)
|
||||
);
|
||||
});
|
||||
}, [integrations, webInstalled, keyword]);
|
||||
|
||||
// Align with the Connectors page: only connected built-ins and enabled
|
||||
// custom MCPs are selectable as agent tools.
|
||||
const ownPicks = useMemo(() => {
|
||||
const kw = keyword.trim().toLowerCase();
|
||||
return userMcpList.filter((opt) => {
|
||||
if (!kw) return true;
|
||||
const name = String(opt.mcp_name || '').toLowerCase();
|
||||
const desc = String(opt.mcp_desc || '').toLowerCase();
|
||||
const key = String(opt.mcp_key || '').toLowerCase();
|
||||
return name.includes(kw) || desc.includes(kw) || key.includes(kw);
|
||||
});
|
||||
return userMcpList
|
||||
.filter((opt) => Number(opt.status) === 1)
|
||||
.filter((opt) => {
|
||||
if (!kw) return true;
|
||||
const name = String(opt.mcp_name || '').toLowerCase();
|
||||
const desc = String(opt.mcp_desc || '').toLowerCase();
|
||||
const key = String(opt.mcp_key || '').toLowerCase();
|
||||
return name.includes(kw) || desc.includes(kw) || key.includes(kw);
|
||||
});
|
||||
}, [userMcpList, keyword]);
|
||||
|
||||
const listHasItems =
|
||||
webConnectedItems.length > 0 ||
|
||||
webNotConnectedItems.length > 0 ||
|
||||
ownPicks.length > 0;
|
||||
const listHasItems = webConnectedItems.length > 0 || ownPicks.length > 0;
|
||||
|
||||
const showSearchPlaceholder =
|
||||
keyword.length === 0 && (initialSelectedTools?.length ?? 0) === 0;
|
||||
|
|
@ -944,29 +933,21 @@ const ToolSelect = forwardRef<
|
|||
</div>
|
||||
</div>
|
||||
)}
|
||||
{webNotConnectedItems.length > 0 && (
|
||||
<div>
|
||||
<div className="px-2 py-1 text-body-sm font-medium text-ds-text-neutral-subtle-default">
|
||||
{t('setting.mcp-sidebar-not-connected')}
|
||||
</div>
|
||||
<IntegrationList
|
||||
className="!space-y-1.5"
|
||||
variant="select"
|
||||
onShowEnvConfig={onShowEnvConfig}
|
||||
addOption={addOption}
|
||||
items={webNotConnectedItems}
|
||||
translationNamespace="layout"
|
||||
selectWithCheckbox
|
||||
isIntegrationSelected={isIntegrationInAgentSelection}
|
||||
onToggleIntegration={handleToggleIntegrationForAgent}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
) : (
|
||||
<p className="break-words px-2 py-2 text-center text-body-md text-ds-text-neutral-muted-default">
|
||||
{t('dashboard.no-results')}
|
||||
</p>
|
||||
<div className="flex flex-col items-center gap-1 px-2 py-2">
|
||||
<p className="break-words text-center text-body-md text-ds-text-neutral-muted-default">
|
||||
{t('dashboard.no-results')}
|
||||
</p>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
buttonContent="text"
|
||||
onClick={() => navigate('/history?tab=connectors')}
|
||||
>
|
||||
{t('chat.input-attach-manage-connectors')}
|
||||
</Button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -12,9 +12,14 @@
|
|||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { fetchConnectedProviders, providerLabel } from '@/api/connectors';
|
||||
import { proxyFetchGet } from '@/api/http';
|
||||
import ellipseIcon from '@/assets/mcp/Ellipse-25.svg';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
useIntegrationManagement,
|
||||
type IntegrationItem,
|
||||
} from '@/hooks/useIntegrationManagement';
|
||||
import { integrationLeadingIconUrl } from '@/lib/connectorIcons';
|
||||
import {
|
||||
RICH_CONNECTOR_STYLE_CLASSES,
|
||||
|
|
@ -24,6 +29,7 @@ import {
|
|||
} from '@/lib/richText';
|
||||
import { skillNameToDirName } from '@/lib/skillToolkit';
|
||||
import { cn } from '@/lib/utils';
|
||||
import { useServerCapabilityStore } from '@/store/serverCapabilityStore';
|
||||
import { useSkillsStore } from '@/store/skillsStore';
|
||||
import { Check, Plus, Wrench } from 'lucide-react';
|
||||
import { Fragment, useEffect, useMemo, useState, type ReactNode } from 'react';
|
||||
|
|
@ -38,6 +44,8 @@ export interface PickerItem {
|
|||
id: string;
|
||||
name: string;
|
||||
token: string;
|
||||
/** Provider icon URL for Open Connector items. */
|
||||
iconUrl?: string;
|
||||
}
|
||||
|
||||
/** A labelled section within a picker (e.g. built-in vs. your own connectors). */
|
||||
|
|
@ -205,9 +213,10 @@ interface WiredPickerPanelProps {
|
|||
const EXCLUDED_BUILTIN_CONNECTORS = ['Search', 'RAG'];
|
||||
|
||||
/**
|
||||
* Full connector list matching the Connectors settings page: built-in
|
||||
* integrations (`/api/v1/config/info`) plus the user's own MCPs
|
||||
* (`/api/v1/mcp/users`), shown as two labelled sections.
|
||||
* Connected connectors only, matching the Connectors page sidebar: connected
|
||||
* Open Connectors (when the Connector Gateway is enabled), connected built-in
|
||||
* integrations (`/api/v1/config/info` + configs), and the user's enabled MCPs
|
||||
* (`/api/v1/mcp/users`), shown as labelled sections.
|
||||
*/
|
||||
export function ConnectorPickerPanel({
|
||||
inputValue,
|
||||
|
|
@ -215,10 +224,27 @@ export function ConnectorPickerPanel({
|
|||
}: WiredPickerPanelProps) {
|
||||
const { t } = useTranslation();
|
||||
const navigate = useNavigate();
|
||||
const [builtIn, setBuiltIn] = useState<PickerItem[]>([]);
|
||||
const [builtInItems, setBuiltInItems] = useState<IntegrationItem[]>([]);
|
||||
const [openItems, setOpenItems] = useState<PickerItem[]>([]);
|
||||
const [yourMcps, setYourMcps] = useState<PickerItem[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
const capabilities = useServerCapabilityStore((state) => state.capabilities);
|
||||
const capabilityStatus = useServerCapabilityStore((state) => state.status);
|
||||
const fetchCapabilities = useServerCapabilityStore(
|
||||
(state) => state.fetchCapabilities
|
||||
);
|
||||
const gatewayEnabled =
|
||||
capabilities.features.connector_gateway.enabled === true;
|
||||
|
||||
// Reuse the Connectors page's connected-state rules (OAuth tokens, config
|
||||
// groups, Google Search defaults) instead of duplicating them here.
|
||||
const { installed } = useIntegrationManagement(builtInItems);
|
||||
|
||||
useEffect(() => {
|
||||
void fetchCapabilities();
|
||||
}, [fetchCapabilities]);
|
||||
|
||||
useEffect(() => {
|
||||
let cancelled = false;
|
||||
Promise.allSettled([
|
||||
|
|
@ -232,13 +258,15 @@ export function ConnectorPickerPanel({
|
|||
infoRes.value &&
|
||||
typeof infoRes.value === 'object'
|
||||
) {
|
||||
setBuiltIn(
|
||||
setBuiltInItems(
|
||||
Object.keys(infoRes.value)
|
||||
.filter((key) => !EXCLUDED_BUILTIN_CONNECTORS.includes(key))
|
||||
.map((key) => ({
|
||||
id: `builtin-${key}`,
|
||||
key,
|
||||
name: key,
|
||||
token: connectorNameToToken(key),
|
||||
desc: '',
|
||||
env_vars: [],
|
||||
onInstall: () => undefined,
|
||||
}))
|
||||
);
|
||||
}
|
||||
|
|
@ -247,11 +275,13 @@ export function ConnectorPickerPanel({
|
|||
? usersRes.value
|
||||
: (usersRes.value?.items ?? []);
|
||||
setYourMcps(
|
||||
list.map((item: { id: number; mcp_name: string }) => ({
|
||||
id: `user-${item.id}`,
|
||||
name: item.mcp_name,
|
||||
token: connectorNameToToken(item.mcp_name),
|
||||
}))
|
||||
list
|
||||
.filter((item: { status?: number }) => Number(item.status) === 1)
|
||||
.map((item: { id: number; mcp_name: string }) => ({
|
||||
id: `user-${item.id}`,
|
||||
name: item.mcp_name,
|
||||
token: connectorNameToToken(item.mcp_name),
|
||||
}))
|
||||
);
|
||||
}
|
||||
})
|
||||
|
|
@ -263,7 +293,46 @@ export function ConnectorPickerPanel({
|
|||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (capabilityStatus !== 'ready' || !gatewayEnabled) {
|
||||
setOpenItems([]);
|
||||
return;
|
||||
}
|
||||
let cancelled = false;
|
||||
fetchConnectedProviders()
|
||||
.then((providers) => {
|
||||
if (cancelled) return;
|
||||
setOpenItems(
|
||||
providers.map((provider) => ({
|
||||
id: `open-${provider.service}`,
|
||||
name: providerLabel(provider),
|
||||
token: connectorNameToToken(providerLabel(provider)),
|
||||
iconUrl: provider.iconUrl || undefined,
|
||||
}))
|
||||
);
|
||||
})
|
||||
.catch(() => {
|
||||
if (!cancelled) setOpenItems([]);
|
||||
});
|
||||
return () => {
|
||||
cancelled = true;
|
||||
};
|
||||
}, [capabilityStatus, gatewayEnabled]);
|
||||
|
||||
const builtIn = useMemo(
|
||||
() =>
|
||||
builtInItems
|
||||
.filter((item) => installed[item.key])
|
||||
.map((item) => ({
|
||||
id: `builtin-${item.key}`,
|
||||
name: item.name,
|
||||
token: connectorNameToToken(item.key),
|
||||
})),
|
||||
[builtInItems, installed]
|
||||
);
|
||||
|
||||
const groups: PickerGroup[] = [
|
||||
{ id: 'open', label: t('connectors.open-connectors'), items: openItems },
|
||||
{
|
||||
id: 'builtin',
|
||||
label: t('setting.mcp-sidebar-built-in'),
|
||||
|
|
@ -289,6 +358,13 @@ export function ConnectorPickerPanel({
|
|||
</span>
|
||||
)}
|
||||
renderLogo={(item) => {
|
||||
if (item.id.startsWith('open-')) {
|
||||
return item.iconUrl ? (
|
||||
<img src={item.iconUrl} alt="" className="h-4 w-4 object-contain" />
|
||||
) : (
|
||||
<img src={ellipseIcon} alt="" className="h-3 w-3" />
|
||||
);
|
||||
}
|
||||
if (!item.id.startsWith('builtin-')) {
|
||||
return (
|
||||
<Wrench size={16} className="text-ds-icon-neutral-muted-default" />
|
||||
|
|
|
|||
132
src/i18n/locales/ar/connectors.json
Normal file
132
src/i18n/locales/ar/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "الموصلات",
|
||||
"search-placeholder": "البحث في موصلاتك",
|
||||
"browse": "تصفح الموصلات",
|
||||
"add-custom": "إضافة موصل مخصص",
|
||||
"retry": "إعادة المحاولة",
|
||||
"your-connectors": "موصلاتك",
|
||||
"no-matching": "لا توجد موصلات مطابقة.",
|
||||
"count-one": "موصل",
|
||||
"count-other": "موصلات",
|
||||
"recommended": "موصلات موصى بها لك.",
|
||||
"no-recommended": "لا توجد موصلات موصى بها متاحة.",
|
||||
"gateway-unavailable": "Open Connectors غير متاح.",
|
||||
"gateway-unavailable-desc": "لم يتم تمكين Connector Gateway في هذا النشر.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "مدمج",
|
||||
"source-local": "محلي",
|
||||
"source-remote": "بعيد",
|
||||
"load-built-in-failed": "تعذر تحميل الموصلات المدمجة",
|
||||
"load-custom-failed": "تعذر تحميل الموصلات المخصصة",
|
||||
"load-open-failed": "تعذر تحميل Open Connectors",
|
||||
"disconnected": "تم قطع اتصال {{name}}",
|
||||
"disconnect-failed": "تعذر قطع اتصال الموصل",
|
||||
"update-failed": "تعذر تحديث الموصل",
|
||||
"status-save-failed-enabled": "تم تمكين الموصل، ولكن تعذر حفظ حالته. حدّث الصفحة وحاول مرة أخرى.",
|
||||
"status-save-failed-disabled": "تم تعطيل الموصل، ولكن تعذر حفظ حالته. حدّث الصفحة وحاول مرة أخرى.",
|
||||
"updated": "تم تحديث الموصل",
|
||||
"save-failed": "تعذر حفظ الموصل",
|
||||
"deleted": "تم حذف الموصل",
|
||||
"delete-failed": "تعذر حذف الموصل",
|
||||
"enable-connector": "تمكين الموصل",
|
||||
"disable-connector": "تعطيل الموصل",
|
||||
"more-actions": "المزيد من الإجراءات",
|
||||
"open": "فتح",
|
||||
"edit": "تعديل",
|
||||
"delete": "حذف",
|
||||
"connected-account": "الحساب المتصل",
|
||||
"supported-actions": "الإجراءات المدعومة",
|
||||
"supported-actions-count": "{{num}} من الإجراءات المدعومة",
|
||||
"show-more": "عرض المزيد",
|
||||
"show-less": "عرض أقل",
|
||||
"provider-website": "موقع المزود",
|
||||
"built-in-title": "موصل توافق محلي",
|
||||
"built-in-desc": "يستخدم هذا الموصل بيئة التكامل المحلية في Eigent. ويُعد Open Connectors السوق الأساسي للتكاملات المستضافة الجديدة.",
|
||||
"configuration": "الإعداد: {{vars}}",
|
||||
"status": "الحالة",
|
||||
"active": "نشط",
|
||||
"disabled": "معطّل",
|
||||
"server-url": "عنوان URL للخادم",
|
||||
"not-configured": "غير معدّ",
|
||||
"command": "الأمر",
|
||||
"arguments": "الوسائط",
|
||||
"requires": "يتطلب {{vars}}",
|
||||
"notion-desc": "اتصل بمساحة عمل Notion.",
|
||||
"google-calendar-desc": "إدارة أحداث Google Calendar وجداوله.",
|
||||
"generic-desc": "ربط {{name}} بـ Eigent.",
|
||||
"google-search": "بحث Google",
|
||||
"google-search-desc": "ربط Google Custom Search بمهام التصفح والبحث.",
|
||||
"notion-install-failed": "تعذر تثبيت Notion",
|
||||
"google-calendar-install-failed": "تعذر تثبيت Google Calendar",
|
||||
"search-connectors": "البحث عن موصلات",
|
||||
"auth-api-key": "مفتاح API",
|
||||
"auth-credential": "بيانات الاعتماد",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "دون مصادقة",
|
||||
"unnamed-action": "إجراء بلا اسم",
|
||||
"try-again": "حاول مرة أخرى",
|
||||
"no-open-found": "لم يتم العثور على موصلات Open Connectors.",
|
||||
"no-built-in-found": "لم يتم العثور على موصلات مدمجة.",
|
||||
"loading": "جارٍ التحميل…",
|
||||
"updating": "جارٍ التحديث…",
|
||||
"loading-more": "جارٍ تحميل المزيد…",
|
||||
"new": "جديد",
|
||||
"installed": "مثبّت",
|
||||
"local-integration": "تكامل محلي",
|
||||
"authentication": "المصادقة",
|
||||
"oauth-title": "الاتصال باستخدام OAuth",
|
||||
"oauth-desc": "سيفتح Eigent صفحة تفويض المزود. ستُكمل نافذة الحوار هذه التثبيت عند اكتمال التفويض.",
|
||||
"oauth-scopes": "الأذونات المطلوبة: {{scopes}}",
|
||||
"no-auth-desc": "لا يتطلب هذا الموصل بيانات اعتماد.",
|
||||
"waiting-authorization": "في انتظار اكتمال التفويض…",
|
||||
"authorization-started": "بدأ التفويض",
|
||||
"installed-toast": "تم تثبيت {{name}}",
|
||||
"no-authorization-url": "لم يُرجع الموصل عنوان URL للتفويض",
|
||||
"authorization-pending": "لا يزال التفويض معلقًا. أكمله في نافذة المزود، ثم حاول مرة أخرى.",
|
||||
"install-failed": "تعذر تثبيت الموصل",
|
||||
"detail-load-failed": "تعذر تحميل تفاصيل الموصل",
|
||||
"authorization-incomplete": "لم يكتمل التفويض بعد. أكمله في نافذة المزود ثم حدّث الحالة مرة أخرى.",
|
||||
"refresh-status-failed": "تعذر تحديث حالة الموصل",
|
||||
"field-required": "الحقل {{field}} مطلوب",
|
||||
"complete-authorization": "أكمل التفويض في نافذة المزود.",
|
||||
"refresh-status": "تحديث الحالة",
|
||||
"built-in-auth-desc": "يفتح التثبيت مسار تفويض المزود في نافذة منفصلة.",
|
||||
"built-in-generic-desc": "تكامل Eigent محلي.",
|
||||
"cancel": "إلغاء",
|
||||
"install": "تثبيت",
|
||||
"installing": "جارٍ التثبيت…",
|
||||
"save": "حفظ",
|
||||
"enter-value": "أدخل {{field}}",
|
||||
"custom-title": "إضافة موصل مخصص",
|
||||
"custom-subtitle": "ثبّت خادم MCP محليًا أو بعيدًا تثق به.",
|
||||
"custom-warning": "يمكن لخوادم MCP المخصصة تنفيذ الأوامر أو الوصول إلى خدمات بعيدة. ثبّت الإعدادات التي تثق بها فقط.",
|
||||
"local-json-desc": "أضف خادم MCP محليًا من خلال تقديم إعداد JSON صالح.",
|
||||
"learn-more": "معرفة المزيد",
|
||||
"connector-name": "اسم الموصل",
|
||||
"remote-name-placeholder": "خادم MCP البعيد الخاص بي",
|
||||
"remote-url": "عنوان URL لخادم MCP البعيد",
|
||||
"remote-url-note": "استخدم HTTPS للخوادم الموجودة خارج شبكة محلية موثوقة.",
|
||||
"invalid-json": "JSON غير صالح: {{message}}",
|
||||
"json-format-error": "خطأ في تنسيق JSON: {{message}}",
|
||||
"parse-failed": "فشل التحليل",
|
||||
"missing-mcp-servers": "يجب أن يحتوي الإعداد على كائن mcpServers",
|
||||
"add-at-least-one": "أضف خادم MCP واحدًا على الأقل",
|
||||
"already-exists": "{{name}} موجود بالفعل",
|
||||
"name-required": "اسم الموصل مطلوب",
|
||||
"invalid-remote-url": "أدخل عنوان URL صالحًا لخادم MCP بعيد",
|
||||
"remote-url-protocol": "يجب أن تستخدم عناوين URL لخوادم MCP البعيدة بروتوكول HTTP أو HTTPS",
|
||||
"remote-missing-id": "تم إنشاء الموصل البعيد دون معرّف",
|
||||
"custom-installed": "الموصلات المخصصة المثبّتة: {{num}}",
|
||||
"install-custom-failed": "تعذر تثبيت الموصل المخصص",
|
||||
"env-key-value": "متغيرات البيئة (مفتاح-قيمة)",
|
||||
"value-placeholder": "القيمة",
|
||||
"configuration-title": "الإعداد",
|
||||
"google-api-key": "مفتاح Google API",
|
||||
"google-api-key-placeholder": "أدخل مفتاح Google API من Google Cloud Console",
|
||||
"google-api-key-note": "تعرّف على كيفية الحصول على مفتاح Google API ← https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "معرّف محرك البحث",
|
||||
"search-engine-id-placeholder": "أدخل معرّف محرك البحث المخصص المرتبط بمفتاح API",
|
||||
"google-search-custom-desc": "اتصل بـ Google Custom Search. يتطلب مفتاح Google API ومعرّف محرك بحث مخصص (CSE).",
|
||||
"google-search-default-desc": "بحث Google مفعّل افتراضيًا. لا يلزم مفتاح API."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/de/connectors.json
Normal file
132
src/i18n/locales/de/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "Konnektoren",
|
||||
"search-placeholder": "Deine Konnektoren durchsuchen",
|
||||
"browse": "Konnektoren durchsuchen",
|
||||
"add-custom": "Benutzerdefinierten hinzufügen",
|
||||
"retry": "Erneut versuchen",
|
||||
"your-connectors": "Deine Konnektoren",
|
||||
"no-matching": "Keine passenden Konnektoren.",
|
||||
"count-one": "Konnektor",
|
||||
"count-other": "Konnektoren",
|
||||
"recommended": "Für dich empfohlene Konnektoren.",
|
||||
"no-recommended": "Keine empfohlenen Konnektoren verfügbar.",
|
||||
"gateway-unavailable": "Open Connectors ist nicht verfügbar.",
|
||||
"gateway-unavailable-desc": "Für diese Bereitstellung ist das Connector Gateway nicht aktiviert.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "Integriert",
|
||||
"source-local": "Lokal",
|
||||
"source-remote": "Remote",
|
||||
"load-built-in-failed": "Integrierte Konnektoren konnten nicht geladen werden",
|
||||
"load-custom-failed": "Benutzerdefinierte Konnektoren konnten nicht geladen werden",
|
||||
"load-open-failed": "Open Connectors konnte nicht geladen werden",
|
||||
"disconnected": "Verbindung zu {{name}} getrennt",
|
||||
"disconnect-failed": "Konnektor konnte nicht getrennt werden",
|
||||
"update-failed": "Konnektor konnte nicht aktualisiert werden",
|
||||
"status-save-failed-enabled": "Der Konnektor wurde aktiviert, sein Status konnte jedoch nicht gespeichert werden. Aktualisiere die Seite und versuche es erneut.",
|
||||
"status-save-failed-disabled": "Der Konnektor wurde deaktiviert, sein Status konnte jedoch nicht gespeichert werden. Aktualisiere die Seite und versuche es erneut.",
|
||||
"updated": "Konnektor aktualisiert",
|
||||
"save-failed": "Konnektor konnte nicht gespeichert werden",
|
||||
"deleted": "Konnektor gelöscht",
|
||||
"delete-failed": "Konnektor konnte nicht gelöscht werden",
|
||||
"enable-connector": "Konnektor aktivieren",
|
||||
"disable-connector": "Konnektor deaktivieren",
|
||||
"more-actions": "Weitere Aktionen",
|
||||
"open": "Öffnen",
|
||||
"edit": "Bearbeiten",
|
||||
"delete": "Löschen",
|
||||
"connected-account": "Verbundenes Konto",
|
||||
"supported-actions": "Unterstützte Aktionen",
|
||||
"supported-actions-count": "{{num}} unterstützte Aktionen",
|
||||
"show-more": "Mehr anzeigen",
|
||||
"show-less": "Weniger anzeigen",
|
||||
"provider-website": "Website des Anbieters",
|
||||
"built-in-title": "Lokaler Kompatibilitätskonnektor",
|
||||
"built-in-desc": "Dieser Konnektor verwendet die lokale Integrationslaufzeit von Eigent. Open Connectors ist der primäre Marktplatz für neue gehostete Integrationen.",
|
||||
"configuration": "Konfiguration: {{vars}}",
|
||||
"status": "Status",
|
||||
"active": "Aktiv",
|
||||
"disabled": "Deaktiviert",
|
||||
"server-url": "Server-URL",
|
||||
"not-configured": "Nicht konfiguriert",
|
||||
"command": "Befehl",
|
||||
"arguments": "Argumente",
|
||||
"requires": "Erfordert {{vars}}",
|
||||
"notion-desc": "Einen Notion-Arbeitsbereich verbinden.",
|
||||
"google-calendar-desc": "Google-Kalendertermine und -zeitpläne verwalten.",
|
||||
"generic-desc": "{{name}} mit Eigent verbinden.",
|
||||
"google-search": "Google-Suche",
|
||||
"google-search-desc": "Google Custom Search für Browser- und Rechercheaufgaben verbinden.",
|
||||
"notion-install-failed": "Notion konnte nicht installiert werden",
|
||||
"google-calendar-install-failed": "Google Kalender konnte nicht installiert werden",
|
||||
"search-connectors": "Konnektoren suchen",
|
||||
"auth-api-key": "API-Schlüssel",
|
||||
"auth-credential": "Anmeldedaten",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "Keine Authentifizierung",
|
||||
"unnamed-action": "Unbenannte Aktion",
|
||||
"try-again": "Erneut versuchen",
|
||||
"no-open-found": "Keine Open Connectors gefunden.",
|
||||
"no-built-in-found": "Keine integrierten Konnektoren gefunden.",
|
||||
"loading": "Wird geladen…",
|
||||
"updating": "Wird aktualisiert…",
|
||||
"loading-more": "Weitere werden geladen…",
|
||||
"new": "Neu",
|
||||
"installed": "Installiert",
|
||||
"local-integration": "Lokale Integration",
|
||||
"authentication": "Authentifizierung",
|
||||
"oauth-title": "Mit OAuth verbinden",
|
||||
"oauth-desc": "Eigent öffnet die Autorisierungsseite des Anbieters. Dieses Dialogfeld schließt die Installation ab, sobald die Autorisierung abgeschlossen ist.",
|
||||
"oauth-scopes": "Angeforderte Berechtigungen: {{scopes}}",
|
||||
"no-auth-desc": "Dieser Konnektor benötigt keine Anmeldedaten.",
|
||||
"waiting-authorization": "Warten auf Abschluss der Autorisierung…",
|
||||
"authorization-started": "Autorisierung gestartet",
|
||||
"installed-toast": "{{name}} installiert",
|
||||
"no-authorization-url": "Der Konnektor hat keine Autorisierungs-URL zurückgegeben",
|
||||
"authorization-pending": "Die Autorisierung steht noch aus. Schließe sie im Anbieterfenster ab und versuche es erneut.",
|
||||
"install-failed": "Konnektor konnte nicht installiert werden",
|
||||
"detail-load-failed": "Konnektordetails konnten nicht geladen werden",
|
||||
"authorization-incomplete": "Die Autorisierung ist noch nicht abgeschlossen. Schließe sie im Anbieterfenster ab und aktualisiere den Status erneut.",
|
||||
"refresh-status-failed": "Konnektorstatus konnte nicht aktualisiert werden",
|
||||
"field-required": "{{field}} ist erforderlich",
|
||||
"complete-authorization": "Schließe die Autorisierung im Anbieterfenster ab.",
|
||||
"refresh-status": "Status aktualisieren",
|
||||
"built-in-auth-desc": "Bei der Installation wird der Autorisierungsvorgang des Anbieters in einem separaten Fenster geöffnet.",
|
||||
"built-in-generic-desc": "Eine lokale Eigent-Integration.",
|
||||
"cancel": "Abbrechen",
|
||||
"install": "Installieren",
|
||||
"installing": "Wird installiert…",
|
||||
"save": "Speichern",
|
||||
"enter-value": "{{field}} eingeben",
|
||||
"custom-title": "Benutzerdefinierten Konnektor hinzufügen",
|
||||
"custom-subtitle": "Installiere einen vertrauenswürdigen lokalen oder Remote-MCP-Server.",
|
||||
"custom-warning": "Benutzerdefinierte MCP-Server können Befehle ausführen oder auf Remote-Dienste zugreifen. Installiere nur Konfigurationen, denen du vertraust.",
|
||||
"local-json-desc": "Füge einen lokalen MCP-Server mit einer gültigen JSON-Konfiguration hinzu.",
|
||||
"learn-more": "Mehr erfahren",
|
||||
"connector-name": "Konnektorname",
|
||||
"remote-name-placeholder": "Mein Remote-MCP",
|
||||
"remote-url": "Remote-MCP-URL",
|
||||
"remote-url-note": "Verwende HTTPS für Server außerhalb eines vertrauenswürdigen lokalen Netzwerks.",
|
||||
"invalid-json": "Ungültiges JSON: {{message}}",
|
||||
"json-format-error": "JSON-Formatfehler: {{message}}",
|
||||
"parse-failed": "Analyse fehlgeschlagen",
|
||||
"missing-mcp-servers": "Die Konfiguration muss ein mcpServers-Objekt enthalten",
|
||||
"add-at-least-one": "Füge mindestens einen MCP-Server hinzu",
|
||||
"already-exists": "{{name}} ist bereits vorhanden",
|
||||
"name-required": "Der Konnektorname ist erforderlich",
|
||||
"invalid-remote-url": "Gib eine gültige Remote-MCP-URL ein",
|
||||
"remote-url-protocol": "Remote-MCP-URLs müssen HTTP oder HTTPS verwenden",
|
||||
"remote-missing-id": "Der Remote-Konnektor wurde ohne ID erstellt",
|
||||
"custom-installed": "Installierte benutzerdefinierte Konnektoren: {{num}}",
|
||||
"install-custom-failed": "Benutzerdefinierter Konnektor konnte nicht installiert werden",
|
||||
"env-key-value": "Umgebungsvariablen (Schlüssel-Wert)",
|
||||
"value-placeholder": "Wert",
|
||||
"configuration-title": "Konfiguration",
|
||||
"google-api-key": "Google-API-Schlüssel",
|
||||
"google-api-key-placeholder": "Google-API-Schlüssel aus der Google Cloud Console eingeben",
|
||||
"google-api-key-note": "So erhältst du deinen Google-API-Schlüssel → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "Suchmaschinen-ID",
|
||||
"search-engine-id-placeholder": "Die mit deinem API-Schlüssel verknüpfte ID der benutzerdefinierten Suchmaschine eingeben",
|
||||
"google-search-custom-desc": "Mit Google Custom Search verbinden. Erfordert einen Google-API-Schlüssel und eine ID der benutzerdefinierten Suchmaschine (CSE).",
|
||||
"google-search-default-desc": "Die Google-Suche ist standardmäßig aktiviert. Kein API-Schlüssel erforderlich."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/en-us/connectors.json
Normal file
132
src/i18n/locales/en-us/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "Connectors",
|
||||
"search-placeholder": "Search your connectors",
|
||||
"browse": "Browse connectors",
|
||||
"add-custom": "Add custom",
|
||||
"retry": "Retry",
|
||||
"your-connectors": "Your connectors",
|
||||
"no-matching": "No matching connectors.",
|
||||
"count-one": "connector",
|
||||
"count-other": "connectors",
|
||||
"recommended": "Recommended connectors for you.",
|
||||
"no-recommended": "No recommended connectors available.",
|
||||
"gateway-unavailable": "Open Connectors is unavailable.",
|
||||
"gateway-unavailable-desc": "This deployment does not have the Connector Gateway enabled.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "Built-in",
|
||||
"source-local": "Local",
|
||||
"source-remote": "Remote",
|
||||
"load-built-in-failed": "Failed to load Built-in connectors",
|
||||
"load-custom-failed": "Failed to load Custom connectors",
|
||||
"load-open-failed": "Failed to load Open Connectors",
|
||||
"disconnected": "{{name}} disconnected",
|
||||
"disconnect-failed": "Failed to disconnect connector",
|
||||
"update-failed": "Failed to update connector",
|
||||
"status-save-failed-enabled": "Connector enabled, but saving its state failed. Refresh and try again.",
|
||||
"status-save-failed-disabled": "Connector disabled, but saving its state failed. Refresh and try again.",
|
||||
"updated": "Connector updated",
|
||||
"save-failed": "Failed to save connector",
|
||||
"deleted": "Connector deleted",
|
||||
"delete-failed": "Failed to delete connector",
|
||||
"enable-connector": "Enable connector",
|
||||
"disable-connector": "Disable connector",
|
||||
"more-actions": "More actions",
|
||||
"open": "Open",
|
||||
"edit": "Edit",
|
||||
"delete": "Delete",
|
||||
"connected-account": "Connected account",
|
||||
"supported-actions": "Supported actions",
|
||||
"supported-actions-count": "{{num}} supported actions",
|
||||
"show-more": "Show more",
|
||||
"show-less": "Show less",
|
||||
"provider-website": "Provider website",
|
||||
"built-in-title": "Local compatibility connector",
|
||||
"built-in-desc": "This connector uses Eigent's local integration runtime. Open Connectors is the primary marketplace for new hosted integrations.",
|
||||
"configuration": "Configuration: {{vars}}",
|
||||
"status": "Status",
|
||||
"active": "Active",
|
||||
"disabled": "Disabled",
|
||||
"server-url": "Server URL",
|
||||
"not-configured": "Not configured",
|
||||
"command": "Command",
|
||||
"arguments": "Arguments",
|
||||
"requires": "Requires {{vars}}",
|
||||
"notion-desc": "Connect a Notion workspace.",
|
||||
"google-calendar-desc": "Manage Google Calendar events and schedules.",
|
||||
"generic-desc": "Connect {{name}} to Eigent.",
|
||||
"google-search": "Google Search",
|
||||
"google-search-desc": "Connect Google Custom Search for browser and research tasks.",
|
||||
"notion-install-failed": "Failed to install Notion",
|
||||
"google-calendar-install-failed": "Failed to install Google Calendar",
|
||||
"search-connectors": "Search connectors",
|
||||
"auth-api-key": "API key",
|
||||
"auth-credential": "Credential",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "No authentication",
|
||||
"unnamed-action": "Unnamed action",
|
||||
"try-again": "Try again",
|
||||
"no-open-found": "No Open Connectors found.",
|
||||
"no-built-in-found": "No Built-in connectors found.",
|
||||
"loading": "Loading…",
|
||||
"updating": "Updating…",
|
||||
"loading-more": "Loading more…",
|
||||
"new": "New",
|
||||
"installed": "Installed",
|
||||
"local-integration": "Local integration",
|
||||
"authentication": "Authentication",
|
||||
"oauth-title": "Connect with OAuth",
|
||||
"oauth-desc": "Eigent will open the provider authorization page. This dialog will finish installation when authorization is complete.",
|
||||
"oauth-scopes": "Requested scopes: {{scopes}}",
|
||||
"no-auth-desc": "This connector does not require credentials.",
|
||||
"waiting-authorization": "Waiting for authorization to complete…",
|
||||
"authorization-started": "Authorization started",
|
||||
"installed-toast": "{{name}} installed",
|
||||
"no-authorization-url": "The connector did not return an authorization URL",
|
||||
"authorization-pending": "Authorization is still pending. Complete it in the provider window, then try again.",
|
||||
"install-failed": "Failed to install connector",
|
||||
"detail-load-failed": "Failed to load connector details",
|
||||
"authorization-incomplete": "Authorization has not completed yet. Finish it in the provider window and refresh again.",
|
||||
"refresh-status-failed": "Failed to refresh connector status",
|
||||
"field-required": "{{field}} is required",
|
||||
"complete-authorization": "Complete authorization in the provider window.",
|
||||
"refresh-status": "Refresh status",
|
||||
"built-in-auth-desc": "Installation opens the provider authorization flow in a separate window.",
|
||||
"built-in-generic-desc": "A local Eigent integration.",
|
||||
"cancel": "Cancel",
|
||||
"install": "Install",
|
||||
"installing": "Installing…",
|
||||
"save": "Save",
|
||||
"enter-value": "Enter {{field}}",
|
||||
"custom-title": "Add custom connector",
|
||||
"custom-subtitle": "Install a local or remote MCP server you trust.",
|
||||
"custom-warning": "Custom MCP servers can execute commands or access remote services. Only install configurations you trust.",
|
||||
"local-json-desc": "Add a local MCP server by providing a valid JSON configuration.",
|
||||
"learn-more": "Learn more",
|
||||
"connector-name": "Connector name",
|
||||
"remote-name-placeholder": "My remote MCP",
|
||||
"remote-url": "Remote MCP URL",
|
||||
"remote-url-note": "Use HTTPS for servers outside a trusted local network.",
|
||||
"invalid-json": "Invalid JSON: {{message}}",
|
||||
"json-format-error": "JSON format error: {{message}}",
|
||||
"parse-failed": "Parsing failed",
|
||||
"missing-mcp-servers": "Configuration must contain an mcpServers object",
|
||||
"add-at-least-one": "Add at least one MCP server",
|
||||
"already-exists": "{{name}} already exists",
|
||||
"name-required": "Connector name is required",
|
||||
"invalid-remote-url": "Enter a valid remote MCP URL",
|
||||
"remote-url-protocol": "Remote MCP URLs must use HTTP or HTTPS",
|
||||
"remote-missing-id": "Remote connector was created without an ID",
|
||||
"custom-installed": "Custom connectors installed: {{num}}",
|
||||
"install-custom-failed": "Failed to install custom connector",
|
||||
"env-key-value": "Env (key-value)",
|
||||
"value-placeholder": "Value",
|
||||
"configuration-title": "Configuration",
|
||||
"google-api-key": "Google API Key",
|
||||
"google-api-key-placeholder": "Enter your Google API key from Google Cloud Console",
|
||||
"google-api-key-note": "Learn how to get your Google API key → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "Search Engine ID",
|
||||
"search-engine-id-placeholder": "Enter the Custom Search Engine ID associated with your API key",
|
||||
"google-search-custom-desc": "Connect to Google Custom Search. Requires a Google API key and a Custom Search Engine (CSE) ID.",
|
||||
"google-search-default-desc": "Google Search is enabled by default. No API key required."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/es/connectors.json
Normal file
132
src/i18n/locales/es/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "Conectores",
|
||||
"search-placeholder": "Busca tus conectores",
|
||||
"browse": "Explorar conectores",
|
||||
"add-custom": "Añadir personalizado",
|
||||
"retry": "Reintentar",
|
||||
"your-connectors": "Tus conectores",
|
||||
"no-matching": "No hay conectores coincidentes.",
|
||||
"count-one": "conector",
|
||||
"count-other": "conectores",
|
||||
"recommended": "Conectores recomendados para ti.",
|
||||
"no-recommended": "No hay conectores recomendados disponibles.",
|
||||
"gateway-unavailable": "Open Connectors no está disponible.",
|
||||
"gateway-unavailable-desc": "Esta implementación no tiene habilitado el Connector Gateway.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "Integrado",
|
||||
"source-local": "Local",
|
||||
"source-remote": "Remoto",
|
||||
"load-built-in-failed": "Error al cargar los conectores integrados",
|
||||
"load-custom-failed": "Error al cargar los conectores personalizados",
|
||||
"load-open-failed": "Error al cargar Open Connectors",
|
||||
"disconnected": "{{name}} desconectado",
|
||||
"disconnect-failed": "Error al desconectar el conector",
|
||||
"update-failed": "Error al actualizar el conector",
|
||||
"status-save-failed-enabled": "Conector habilitado, pero no se pudo guardar su estado. Actualiza e inténtalo de nuevo.",
|
||||
"status-save-failed-disabled": "Conector deshabilitado, pero no se pudo guardar su estado. Actualiza e inténtalo de nuevo.",
|
||||
"updated": "Conector actualizado",
|
||||
"save-failed": "Error al guardar el conector",
|
||||
"deleted": "Conector eliminado",
|
||||
"delete-failed": "Error al eliminar el conector",
|
||||
"enable-connector": "Habilitar conector",
|
||||
"disable-connector": "Deshabilitar conector",
|
||||
"more-actions": "Más acciones",
|
||||
"open": "Abrir",
|
||||
"edit": "Editar",
|
||||
"delete": "Eliminar",
|
||||
"connected-account": "Cuenta conectada",
|
||||
"supported-actions": "Acciones compatibles",
|
||||
"supported-actions-count": "{{num}} acciones compatibles",
|
||||
"show-more": "Mostrar más",
|
||||
"show-less": "Mostrar menos",
|
||||
"provider-website": "Sitio web del proveedor",
|
||||
"built-in-title": "Conector de compatibilidad local",
|
||||
"built-in-desc": "Este conector usa el entorno de integración local de Eigent. Open Connectors es el mercado principal para nuevas integraciones alojadas.",
|
||||
"configuration": "Configuración: {{vars}}",
|
||||
"status": "Estado",
|
||||
"active": "Activo",
|
||||
"disabled": "Deshabilitado",
|
||||
"server-url": "URL del servidor",
|
||||
"not-configured": "Sin configurar",
|
||||
"command": "Comando",
|
||||
"arguments": "Argumentos",
|
||||
"requires": "Requiere {{vars}}",
|
||||
"notion-desc": "Conecta un espacio de trabajo de Notion.",
|
||||
"google-calendar-desc": "Gestiona eventos y horarios de Google Calendar.",
|
||||
"generic-desc": "Conecta {{name}} a Eigent.",
|
||||
"google-search": "Búsqueda de Google",
|
||||
"google-search-desc": "Conecta Google Custom Search para tareas de navegación e investigación.",
|
||||
"notion-install-failed": "Error al instalar Notion",
|
||||
"google-calendar-install-failed": "Error al instalar Google Calendar",
|
||||
"search-connectors": "Buscar conectores",
|
||||
"auth-api-key": "Clave API",
|
||||
"auth-credential": "Credencial",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "Sin autenticación",
|
||||
"unnamed-action": "Acción sin nombre",
|
||||
"try-again": "Intentar de nuevo",
|
||||
"no-open-found": "No se encontraron Open Connectors.",
|
||||
"no-built-in-found": "No se encontraron conectores integrados.",
|
||||
"loading": "Cargando…",
|
||||
"updating": "Actualizando…",
|
||||
"loading-more": "Cargando más…",
|
||||
"new": "Nuevo",
|
||||
"installed": "Instalado",
|
||||
"local-integration": "Integración local",
|
||||
"authentication": "Autenticación",
|
||||
"oauth-title": "Conectar con OAuth",
|
||||
"oauth-desc": "Eigent abrirá la página de autorización del proveedor. Este cuadro de diálogo finalizará la instalación cuando se complete la autorización.",
|
||||
"oauth-scopes": "Permisos solicitados: {{scopes}}",
|
||||
"no-auth-desc": "Este conector no requiere credenciales.",
|
||||
"waiting-authorization": "Esperando a que se complete la autorización…",
|
||||
"authorization-started": "Autorización iniciada",
|
||||
"installed-toast": "{{name}} instalado",
|
||||
"no-authorization-url": "El conector no devolvió una URL de autorización",
|
||||
"authorization-pending": "La autorización sigue pendiente. Complétala en la ventana del proveedor e inténtalo de nuevo.",
|
||||
"install-failed": "Error al instalar el conector",
|
||||
"detail-load-failed": "Error al cargar los detalles del conector",
|
||||
"authorization-incomplete": "La autorización aún no se ha completado. Termínala en la ventana del proveedor y actualiza de nuevo.",
|
||||
"refresh-status-failed": "Error al actualizar el estado del conector",
|
||||
"field-required": "{{field}} es obligatorio",
|
||||
"complete-authorization": "Completa la autorización en la ventana del proveedor.",
|
||||
"refresh-status": "Actualizar estado",
|
||||
"built-in-auth-desc": "La instalación abre el flujo de autorización del proveedor en una ventana aparte.",
|
||||
"built-in-generic-desc": "Una integración local de Eigent.",
|
||||
"cancel": "Cancelar",
|
||||
"install": "Instalar",
|
||||
"installing": "Instalando…",
|
||||
"save": "Guardar",
|
||||
"enter-value": "Introduce {{field}}",
|
||||
"custom-title": "Añadir conector personalizado",
|
||||
"custom-subtitle": "Instala un servidor MCP local o remoto de confianza.",
|
||||
"custom-warning": "Los servidores MCP personalizados pueden ejecutar comandos o acceder a servicios remotos. Instala solo configuraciones de confianza.",
|
||||
"local-json-desc": "Añade un servidor MCP local proporcionando una configuración JSON válida.",
|
||||
"learn-more": "Más información",
|
||||
"connector-name": "Nombre del conector",
|
||||
"remote-name-placeholder": "Mi MCP remoto",
|
||||
"remote-url": "URL del MCP remoto",
|
||||
"remote-url-note": "Usa HTTPS para servidores fuera de una red local de confianza.",
|
||||
"invalid-json": "JSON no válido: {{message}}",
|
||||
"json-format-error": "Error de formato JSON: {{message}}",
|
||||
"parse-failed": "Error al analizar",
|
||||
"missing-mcp-servers": "La configuración debe contener un objeto mcpServers",
|
||||
"add-at-least-one": "Añade al menos un servidor MCP",
|
||||
"already-exists": "{{name}} ya existe",
|
||||
"name-required": "El nombre del conector es obligatorio",
|
||||
"invalid-remote-url": "Introduce una URL de MCP remoto válida",
|
||||
"remote-url-protocol": "Las URL de MCP remoto deben usar HTTP o HTTPS",
|
||||
"remote-missing-id": "El conector remoto se creó sin un ID",
|
||||
"custom-installed": "Conectores personalizados instalados: {{num}}",
|
||||
"install-custom-failed": "Error al instalar el conector personalizado",
|
||||
"env-key-value": "Variables de entorno (clave-valor)",
|
||||
"value-placeholder": "Valor",
|
||||
"configuration-title": "Configuración",
|
||||
"google-api-key": "Clave de API de Google",
|
||||
"google-api-key-placeholder": "Introduce tu clave de API de Google Cloud Console",
|
||||
"google-api-key-note": "Cómo obtener tu clave de API de Google → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "ID del motor de búsqueda",
|
||||
"search-engine-id-placeholder": "Introduce el ID del motor de búsqueda personalizado asociado a tu clave de API",
|
||||
"google-search-custom-desc": "Conecta Google Custom Search. Requiere una clave de API de Google y un ID de motor de búsqueda personalizado (CSE).",
|
||||
"google-search-default-desc": "Google Search está habilitado de forma predeterminada. No se requiere una clave de API."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/fr/connectors.json
Normal file
132
src/i18n/locales/fr/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "Connecteurs",
|
||||
"search-placeholder": "Rechercher dans vos connecteurs",
|
||||
"browse": "Parcourir les connecteurs",
|
||||
"add-custom": "Ajouter un connecteur personnalisé",
|
||||
"retry": "Réessayer",
|
||||
"your-connectors": "Vos connecteurs",
|
||||
"no-matching": "Aucun connecteur correspondant.",
|
||||
"count-one": "connecteur",
|
||||
"count-other": "connecteurs",
|
||||
"recommended": "Connecteurs recommandés pour vous.",
|
||||
"no-recommended": "Aucun connecteur recommandé disponible.",
|
||||
"gateway-unavailable": "Open Connectors n’est pas disponible.",
|
||||
"gateway-unavailable-desc": "Connector Gateway n’est pas activé pour ce déploiement.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "Intégré",
|
||||
"source-local": "Local",
|
||||
"source-remote": "Distant",
|
||||
"load-built-in-failed": "Échec du chargement des connecteurs intégrés",
|
||||
"load-custom-failed": "Échec du chargement des connecteurs personnalisés",
|
||||
"load-open-failed": "Échec du chargement d’Open Connectors",
|
||||
"disconnected": "{{name}} déconnecté",
|
||||
"disconnect-failed": "Échec de la déconnexion du connecteur",
|
||||
"update-failed": "Échec de la mise à jour du connecteur",
|
||||
"status-save-failed-enabled": "Le connecteur a été activé, mais son état n’a pas pu être enregistré. Actualisez la page et réessayez.",
|
||||
"status-save-failed-disabled": "Le connecteur a été désactivé, mais son état n’a pas pu être enregistré. Actualisez la page et réessayez.",
|
||||
"updated": "Connecteur mis à jour",
|
||||
"save-failed": "Échec de l’enregistrement du connecteur",
|
||||
"deleted": "Connecteur supprimé",
|
||||
"delete-failed": "Échec de la suppression du connecteur",
|
||||
"enable-connector": "Activer le connecteur",
|
||||
"disable-connector": "Désactiver le connecteur",
|
||||
"more-actions": "Plus d’actions",
|
||||
"open": "Ouvrir",
|
||||
"edit": "Modifier",
|
||||
"delete": "Supprimer",
|
||||
"connected-account": "Compte connecté",
|
||||
"supported-actions": "Actions prises en charge",
|
||||
"supported-actions-count": "{{num}} actions prises en charge",
|
||||
"show-more": "Afficher plus",
|
||||
"show-less": "Afficher moins",
|
||||
"provider-website": "Site web du fournisseur",
|
||||
"built-in-title": "Connecteur de compatibilité locale",
|
||||
"built-in-desc": "Ce connecteur utilise l’environnement d’intégration local d’Eigent. Open Connectors est la place de marché principale pour les nouvelles intégrations hébergées.",
|
||||
"configuration": "Configuration : {{vars}}",
|
||||
"status": "État",
|
||||
"active": "Actif",
|
||||
"disabled": "Désactivé",
|
||||
"server-url": "URL du serveur",
|
||||
"not-configured": "Non configuré",
|
||||
"command": "Commande",
|
||||
"arguments": "Arguments",
|
||||
"requires": "Nécessite {{vars}}",
|
||||
"notion-desc": "Connecter un espace de travail Notion.",
|
||||
"google-calendar-desc": "Gérer les événements et les calendriers Google Agenda.",
|
||||
"generic-desc": "Connecter {{name}} à Eigent.",
|
||||
"google-search": "Recherche Google",
|
||||
"google-search-desc": "Connecter Google Custom Search pour les tâches de navigation et de recherche.",
|
||||
"notion-install-failed": "Échec de l’installation de Notion",
|
||||
"google-calendar-install-failed": "Échec de l’installation de Google Agenda",
|
||||
"search-connectors": "Rechercher des connecteurs",
|
||||
"auth-api-key": "Clé API",
|
||||
"auth-credential": "Identifiants",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "Aucune authentification",
|
||||
"unnamed-action": "Action sans nom",
|
||||
"try-again": "Réessayer",
|
||||
"no-open-found": "Aucun Open Connector trouvé.",
|
||||
"no-built-in-found": "Aucun connecteur intégré trouvé.",
|
||||
"loading": "Chargement…",
|
||||
"updating": "Mise à jour…",
|
||||
"loading-more": "Chargement d’autres éléments…",
|
||||
"new": "Nouveau",
|
||||
"installed": "Installé",
|
||||
"local-integration": "Intégration locale",
|
||||
"authentication": "Authentification",
|
||||
"oauth-title": "Se connecter avec OAuth",
|
||||
"oauth-desc": "Eigent ouvrira la page d’autorisation du fournisseur. Cette boîte de dialogue terminera l’installation une fois l’autorisation accordée.",
|
||||
"oauth-scopes": "Autorisations demandées : {{scopes}}",
|
||||
"no-auth-desc": "Ce connecteur ne nécessite aucun identifiant.",
|
||||
"waiting-authorization": "En attente de l’autorisation…",
|
||||
"authorization-started": "Autorisation démarrée",
|
||||
"installed-toast": "{{name}} installé",
|
||||
"no-authorization-url": "Le connecteur n’a pas renvoyé d’URL d’autorisation",
|
||||
"authorization-pending": "L’autorisation est toujours en attente. Terminez-la dans la fenêtre du fournisseur, puis réessayez.",
|
||||
"install-failed": "Échec de l’installation du connecteur",
|
||||
"detail-load-failed": "Échec du chargement des détails du connecteur",
|
||||
"authorization-incomplete": "L’autorisation n’est pas encore terminée. Terminez-la dans la fenêtre du fournisseur et actualisez à nouveau l’état.",
|
||||
"refresh-status-failed": "Échec de l’actualisation de l’état du connecteur",
|
||||
"field-required": "{{field}} est requis",
|
||||
"complete-authorization": "Terminez l’autorisation dans la fenêtre du fournisseur.",
|
||||
"refresh-status": "Actualiser l’état",
|
||||
"built-in-auth-desc": "L’installation ouvre le processus d’autorisation du fournisseur dans une fenêtre séparée.",
|
||||
"built-in-generic-desc": "Une intégration Eigent locale.",
|
||||
"cancel": "Annuler",
|
||||
"install": "Installer",
|
||||
"installing": "Installation…",
|
||||
"save": "Enregistrer",
|
||||
"enter-value": "Saisir {{field}}",
|
||||
"custom-title": "Ajouter un connecteur personnalisé",
|
||||
"custom-subtitle": "Installez un serveur MCP local ou distant auquel vous faites confiance.",
|
||||
"custom-warning": "Les serveurs MCP personnalisés peuvent exécuter des commandes ou accéder à des services distants. Installez uniquement des configurations auxquelles vous faites confiance.",
|
||||
"local-json-desc": "Ajoutez un serveur MCP local en fournissant une configuration JSON valide.",
|
||||
"learn-more": "En savoir plus",
|
||||
"connector-name": "Nom du connecteur",
|
||||
"remote-name-placeholder": "Mon MCP distant",
|
||||
"remote-url": "URL du MCP distant",
|
||||
"remote-url-note": "Utilisez HTTPS pour les serveurs situés hors d’un réseau local de confiance.",
|
||||
"invalid-json": "JSON non valide : {{message}}",
|
||||
"json-format-error": "Erreur de format JSON : {{message}}",
|
||||
"parse-failed": "Échec de l’analyse",
|
||||
"missing-mcp-servers": "La configuration doit contenir un objet mcpServers",
|
||||
"add-at-least-one": "Ajoutez au moins un serveur MCP",
|
||||
"already-exists": "{{name}} existe déjà",
|
||||
"name-required": "Le nom du connecteur est requis",
|
||||
"invalid-remote-url": "Saisissez une URL de MCP distant valide",
|
||||
"remote-url-protocol": "Les URL de MCP distant doivent utiliser HTTP ou HTTPS",
|
||||
"remote-missing-id": "Le connecteur distant a été créé sans identifiant",
|
||||
"custom-installed": "Connecteurs personnalisés installés : {{num}}",
|
||||
"install-custom-failed": "Échec de l’installation du connecteur personnalisé",
|
||||
"env-key-value": "Variables d’environnement (clé-valeur)",
|
||||
"value-placeholder": "Valeur",
|
||||
"configuration-title": "Configuration",
|
||||
"google-api-key": "Clé API Google",
|
||||
"google-api-key-placeholder": "Saisissez votre clé API Google depuis Google Cloud Console",
|
||||
"google-api-key-note": "Comment obtenir votre clé API Google → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "Identifiant du moteur de recherche",
|
||||
"search-engine-id-placeholder": "Saisissez l’identifiant du moteur de recherche personnalisé associé à votre clé API",
|
||||
"google-search-custom-desc": "Connectez Google Custom Search. Une clé API Google et un identifiant de moteur de recherche personnalisé (CSE) sont requis.",
|
||||
"google-search-default-desc": "La recherche Google est activée par défaut. Aucune clé API n’est requise."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/it/connectors.json
Normal file
132
src/i18n/locales/it/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "Connettori",
|
||||
"search-placeholder": "Cerca nei tuoi connettori",
|
||||
"browse": "Sfoglia i connettori",
|
||||
"add-custom": "Aggiungi personalizzato",
|
||||
"retry": "Riprova",
|
||||
"your-connectors": "I tuoi connettori",
|
||||
"no-matching": "Nessun connettore corrispondente.",
|
||||
"count-one": "connettore",
|
||||
"count-other": "connettori",
|
||||
"recommended": "Connettori consigliati per te.",
|
||||
"no-recommended": "Nessun connettore consigliato disponibile.",
|
||||
"gateway-unavailable": "Open Connectors non è disponibile.",
|
||||
"gateway-unavailable-desc": "Connector Gateway non è abilitato per questa distribuzione.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "Integrato",
|
||||
"source-local": "Locale",
|
||||
"source-remote": "Remoto",
|
||||
"load-built-in-failed": "Impossibile caricare i connettori integrati",
|
||||
"load-custom-failed": "Impossibile caricare i connettori personalizzati",
|
||||
"load-open-failed": "Impossibile caricare Open Connectors",
|
||||
"disconnected": "{{name}} disconnesso",
|
||||
"disconnect-failed": "Impossibile disconnettere il connettore",
|
||||
"update-failed": "Impossibile aggiornare il connettore",
|
||||
"status-save-failed-enabled": "Il connettore è stato abilitato, ma non è stato possibile salvarne lo stato. Aggiorna la pagina e riprova.",
|
||||
"status-save-failed-disabled": "Il connettore è stato disabilitato, ma non è stato possibile salvarne lo stato. Aggiorna la pagina e riprova.",
|
||||
"updated": "Connettore aggiornato",
|
||||
"save-failed": "Impossibile salvare il connettore",
|
||||
"deleted": "Connettore eliminato",
|
||||
"delete-failed": "Impossibile eliminare il connettore",
|
||||
"enable-connector": "Abilita connettore",
|
||||
"disable-connector": "Disabilita connettore",
|
||||
"more-actions": "Altre azioni",
|
||||
"open": "Apri",
|
||||
"edit": "Modifica",
|
||||
"delete": "Elimina",
|
||||
"connected-account": "Account connesso",
|
||||
"supported-actions": "Azioni supportate",
|
||||
"supported-actions-count": "{{num}} azioni supportate",
|
||||
"show-more": "Mostra altro",
|
||||
"show-less": "Mostra meno",
|
||||
"provider-website": "Sito web del fornitore",
|
||||
"built-in-title": "Connettore di compatibilità locale",
|
||||
"built-in-desc": "Questo connettore utilizza l’ambiente di integrazione locale di Eigent. Open Connectors è il marketplace principale per le nuove integrazioni ospitate.",
|
||||
"configuration": "Configurazione: {{vars}}",
|
||||
"status": "Stato",
|
||||
"active": "Attivo",
|
||||
"disabled": "Disabilitato",
|
||||
"server-url": "URL del server",
|
||||
"not-configured": "Non configurato",
|
||||
"command": "Comando",
|
||||
"arguments": "Argomenti",
|
||||
"requires": "Richiede {{vars}}",
|
||||
"notion-desc": "Connetti uno spazio di lavoro Notion.",
|
||||
"google-calendar-desc": "Gestisci eventi e pianificazioni di Google Calendar.",
|
||||
"generic-desc": "Connetti {{name}} a Eigent.",
|
||||
"google-search": "Ricerca Google",
|
||||
"google-search-desc": "Connetti Google Custom Search per attività di navigazione e ricerca.",
|
||||
"notion-install-failed": "Impossibile installare Notion",
|
||||
"google-calendar-install-failed": "Impossibile installare Google Calendar",
|
||||
"search-connectors": "Cerca connettori",
|
||||
"auth-api-key": "Chiave API",
|
||||
"auth-credential": "Credenziale",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "Nessuna autenticazione",
|
||||
"unnamed-action": "Azione senza nome",
|
||||
"try-again": "Riprova",
|
||||
"no-open-found": "Nessun Open Connector trovato.",
|
||||
"no-built-in-found": "Nessun connettore integrato trovato.",
|
||||
"loading": "Caricamento…",
|
||||
"updating": "Aggiornamento…",
|
||||
"loading-more": "Caricamento di altri elementi…",
|
||||
"new": "Nuovo",
|
||||
"installed": "Installato",
|
||||
"local-integration": "Integrazione locale",
|
||||
"authentication": "Autenticazione",
|
||||
"oauth-title": "Connetti con OAuth",
|
||||
"oauth-desc": "Eigent aprirà la pagina di autorizzazione del fornitore. Questa finestra completerà l’installazione al termine dell’autorizzazione.",
|
||||
"oauth-scopes": "Autorizzazioni richieste: {{scopes}}",
|
||||
"no-auth-desc": "Questo connettore non richiede credenziali.",
|
||||
"waiting-authorization": "In attesa del completamento dell’autorizzazione…",
|
||||
"authorization-started": "Autorizzazione avviata",
|
||||
"installed-toast": "{{name}} installato",
|
||||
"no-authorization-url": "Il connettore non ha restituito un URL di autorizzazione",
|
||||
"authorization-pending": "L’autorizzazione è ancora in sospeso. Completala nella finestra del fornitore, quindi riprova.",
|
||||
"install-failed": "Impossibile installare il connettore",
|
||||
"detail-load-failed": "Impossibile caricare i dettagli del connettore",
|
||||
"authorization-incomplete": "L’autorizzazione non è ancora completa. Completala nella finestra del fornitore e aggiorna nuovamente lo stato.",
|
||||
"refresh-status-failed": "Impossibile aggiornare lo stato del connettore",
|
||||
"field-required": "{{field}} è obbligatorio",
|
||||
"complete-authorization": "Completa l’autorizzazione nella finestra del fornitore.",
|
||||
"refresh-status": "Aggiorna stato",
|
||||
"built-in-auth-desc": "L’installazione apre il flusso di autorizzazione del fornitore in una finestra separata.",
|
||||
"built-in-generic-desc": "Un’integrazione locale di Eigent.",
|
||||
"cancel": "Annulla",
|
||||
"install": "Installa",
|
||||
"installing": "Installazione…",
|
||||
"save": "Salva",
|
||||
"enter-value": "Inserisci {{field}}",
|
||||
"custom-title": "Aggiungi connettore personalizzato",
|
||||
"custom-subtitle": "Installa un server MCP locale o remoto attendibile.",
|
||||
"custom-warning": "I server MCP personalizzati possono eseguire comandi o accedere a servizi remoti. Installa solo configurazioni attendibili.",
|
||||
"local-json-desc": "Aggiungi un server MCP locale fornendo una configurazione JSON valida.",
|
||||
"learn-more": "Scopri di più",
|
||||
"connector-name": "Nome del connettore",
|
||||
"remote-name-placeholder": "Il mio MCP remoto",
|
||||
"remote-url": "URL MCP remoto",
|
||||
"remote-url-note": "Usa HTTPS per i server esterni a una rete locale attendibile.",
|
||||
"invalid-json": "JSON non valido: {{message}}",
|
||||
"json-format-error": "Errore di formato JSON: {{message}}",
|
||||
"parse-failed": "Analisi non riuscita",
|
||||
"missing-mcp-servers": "La configurazione deve contenere un oggetto mcpServers",
|
||||
"add-at-least-one": "Aggiungi almeno un server MCP",
|
||||
"already-exists": "{{name}} esiste già",
|
||||
"name-required": "Il nome del connettore è obbligatorio",
|
||||
"invalid-remote-url": "Inserisci un URL MCP remoto valido",
|
||||
"remote-url-protocol": "Gli URL MCP remoti devono utilizzare HTTP o HTTPS",
|
||||
"remote-missing-id": "Il connettore remoto è stato creato senza ID",
|
||||
"custom-installed": "Connettori personalizzati installati: {{num}}",
|
||||
"install-custom-failed": "Impossibile installare il connettore personalizzato",
|
||||
"env-key-value": "Variabili d’ambiente (chiave-valore)",
|
||||
"value-placeholder": "Valore",
|
||||
"configuration-title": "Configurazione",
|
||||
"google-api-key": "Chiave API Google",
|
||||
"google-api-key-placeholder": "Inserisci la chiave API Google da Google Cloud Console",
|
||||
"google-api-key-note": "Come ottenere la chiave API Google → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "ID del motore di ricerca",
|
||||
"search-engine-id-placeholder": "Inserisci l’ID del motore di ricerca personalizzato associato alla chiave API",
|
||||
"google-search-custom-desc": "Connetti Google Custom Search. Sono richiesti una chiave API Google e un ID del motore di ricerca personalizzato (CSE).",
|
||||
"google-search-default-desc": "La Ricerca Google è abilitata per impostazione predefinita. Non è richiesta alcuna chiave API."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/ja/connectors.json
Normal file
132
src/i18n/locales/ja/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "コネクタ",
|
||||
"search-placeholder": "コネクタを検索",
|
||||
"browse": "コネクタを探す",
|
||||
"add-custom": "カスタムを追加",
|
||||
"retry": "再試行",
|
||||
"your-connectors": "あなたのコネクタ",
|
||||
"no-matching": "一致するコネクタがありません。",
|
||||
"count-one": "件のコネクタ",
|
||||
"count-other": "件のコネクタ",
|
||||
"recommended": "あなたへのおすすめコネクタ。",
|
||||
"no-recommended": "おすすめのコネクタはありません。",
|
||||
"gateway-unavailable": "Open Connectors は利用できません。",
|
||||
"gateway-unavailable-desc": "このデプロイでは Connector Gateway が有効になっていません。",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "組み込み",
|
||||
"source-local": "ローカル",
|
||||
"source-remote": "リモート",
|
||||
"load-built-in-failed": "組み込みコネクタの読み込みに失敗しました",
|
||||
"load-custom-failed": "カスタムコネクタの読み込みに失敗しました",
|
||||
"load-open-failed": "Open Connectors の読み込みに失敗しました",
|
||||
"disconnected": "{{name}} の接続を解除しました",
|
||||
"disconnect-failed": "コネクタの接続解除に失敗しました",
|
||||
"update-failed": "コネクタの更新に失敗しました",
|
||||
"status-save-failed-enabled": "コネクタは有効になりましたが、状態の保存に失敗しました。更新して再試行してください。",
|
||||
"status-save-failed-disabled": "コネクタは無効になりましたが、状態の保存に失敗しました。更新して再試行してください。",
|
||||
"updated": "コネクタを更新しました",
|
||||
"save-failed": "コネクタの保存に失敗しました",
|
||||
"deleted": "コネクタを削除しました",
|
||||
"delete-failed": "コネクタの削除に失敗しました",
|
||||
"enable-connector": "コネクタを有効化",
|
||||
"disable-connector": "コネクタを無効化",
|
||||
"more-actions": "その他の操作",
|
||||
"open": "開く",
|
||||
"edit": "編集",
|
||||
"delete": "削除",
|
||||
"connected-account": "接続中のアカウント",
|
||||
"supported-actions": "対応アクション",
|
||||
"supported-actions-count": "{{num}} 件の対応アクション",
|
||||
"show-more": "もっと見る",
|
||||
"show-less": "折りたたむ",
|
||||
"provider-website": "プロバイダーのウェブサイト",
|
||||
"built-in-title": "ローカル互換コネクタ",
|
||||
"built-in-desc": "このコネクタは Eigent のローカル統合ランタイムを使用します。新しいホスト型統合は Open Connectors が主要なマーケットプレイスです。",
|
||||
"configuration": "設定項目:{{vars}}",
|
||||
"status": "ステータス",
|
||||
"active": "有効",
|
||||
"disabled": "無効",
|
||||
"server-url": "サーバー URL",
|
||||
"not-configured": "未設定",
|
||||
"command": "コマンド",
|
||||
"arguments": "引数",
|
||||
"requires": "{{vars}} が必要です",
|
||||
"notion-desc": "Notion ワークスペースを接続します。",
|
||||
"google-calendar-desc": "Google カレンダーの予定とスケジュールを管理します。",
|
||||
"generic-desc": "{{name}} を Eigent に接続します。",
|
||||
"google-search": "Google 検索",
|
||||
"google-search-desc": "ブラウザやリサーチタスク用に Google カスタム検索を接続します。",
|
||||
"notion-install-failed": "Notion のインストールに失敗しました",
|
||||
"google-calendar-install-failed": "Google カレンダーのインストールに失敗しました",
|
||||
"search-connectors": "コネクタを検索",
|
||||
"auth-api-key": "API キー",
|
||||
"auth-credential": "認証情報",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "認証不要",
|
||||
"unnamed-action": "名称未設定のアクション",
|
||||
"try-again": "再試行",
|
||||
"no-open-found": "Open Connectors が見つかりません。",
|
||||
"no-built-in-found": "組み込みコネクタが見つかりません。",
|
||||
"loading": "読み込み中…",
|
||||
"updating": "更新中…",
|
||||
"loading-more": "さらに読み込み中…",
|
||||
"new": "新着",
|
||||
"installed": "インストール済み",
|
||||
"local-integration": "ローカル統合",
|
||||
"authentication": "認証",
|
||||
"oauth-title": "OAuth で接続",
|
||||
"oauth-desc": "Eigent がプロバイダーの認可ページを開きます。認可が完了すると、このダイアログでインストールが完了します。",
|
||||
"oauth-scopes": "要求されるスコープ:{{scopes}}",
|
||||
"no-auth-desc": "このコネクタに認証情報は不要です。",
|
||||
"waiting-authorization": "認可の完了を待っています…",
|
||||
"authorization-started": "認可を開始しました",
|
||||
"installed-toast": "{{name}} をインストールしました",
|
||||
"no-authorization-url": "コネクタが認可 URL を返しませんでした",
|
||||
"authorization-pending": "認可はまだ完了していません。プロバイダーのウィンドウで完了してから再試行してください。",
|
||||
"install-failed": "コネクタのインストールに失敗しました",
|
||||
"detail-load-failed": "コネクタの詳細の読み込みに失敗しました",
|
||||
"authorization-incomplete": "認可がまだ完了していません。プロバイダーのウィンドウで完了してから再度更新してください。",
|
||||
"refresh-status-failed": "コネクタの状態の更新に失敗しました",
|
||||
"field-required": "{{field}} は必須です",
|
||||
"complete-authorization": "プロバイダーのウィンドウで認可を完了してください。",
|
||||
"refresh-status": "状態を更新",
|
||||
"built-in-auth-desc": "インストール時に別ウィンドウでプロバイダーの認可フローが開きます。",
|
||||
"built-in-generic-desc": "Eigent のローカル統合です。",
|
||||
"cancel": "キャンセル",
|
||||
"install": "インストール",
|
||||
"installing": "インストール中…",
|
||||
"save": "保存",
|
||||
"enter-value": "{{field}} を入力",
|
||||
"custom-title": "カスタムコネクタを追加",
|
||||
"custom-subtitle": "信頼できるローカルまたはリモートの MCP サーバーをインストールします。",
|
||||
"custom-warning": "カスタム MCP サーバーはコマンドの実行やリモートサービスへのアクセスが可能です。信頼できる設定のみをインストールしてください。",
|
||||
"local-json-desc": "有効な JSON 設定を指定してローカル MCP サーバーを追加します。",
|
||||
"learn-more": "詳細",
|
||||
"connector-name": "コネクタ名",
|
||||
"remote-name-placeholder": "マイリモート MCP",
|
||||
"remote-url": "リモート MCP URL",
|
||||
"remote-url-note": "信頼できるローカルネットワーク外のサーバーには HTTPS を使用してください。",
|
||||
"invalid-json": "無効な JSON:{{message}}",
|
||||
"json-format-error": "JSON 形式エラー:{{message}}",
|
||||
"parse-failed": "解析に失敗しました",
|
||||
"missing-mcp-servers": "設定には mcpServers オブジェクトが必要です",
|
||||
"add-at-least-one": "MCP サーバーを少なくとも 1 つ追加してください",
|
||||
"already-exists": "{{name}} は既に存在します",
|
||||
"name-required": "コネクタ名は必須です",
|
||||
"invalid-remote-url": "有効なリモート MCP URL を入力してください",
|
||||
"remote-url-protocol": "リモート MCP URL は HTTP または HTTPS を使用する必要があります",
|
||||
"remote-missing-id": "リモートコネクタが ID なしで作成されました",
|
||||
"custom-installed": "{{num}} 件のカスタムコネクタをインストールしました",
|
||||
"install-custom-failed": "カスタムコネクタのインストールに失敗しました",
|
||||
"env-key-value": "環境変数(キーと値)",
|
||||
"value-placeholder": "値",
|
||||
"configuration-title": "設定",
|
||||
"google-api-key": "Google API キー",
|
||||
"google-api-key-placeholder": "Google Cloud Console の API キーを入力してください",
|
||||
"google-api-key-note": "Google API キーの取得方法 → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "検索エンジン ID",
|
||||
"search-engine-id-placeholder": "API キーに関連付けられたカスタム検索エンジン ID を入力してください",
|
||||
"google-search-custom-desc": "Google カスタム検索に接続します。Google API キーとカスタム検索エンジン(CSE)ID が必要です。",
|
||||
"google-search-default-desc": "Google 検索はデフォルトで有効です。API キーは必要ありません。"
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/ko/connectors.json
Normal file
132
src/i18n/locales/ko/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "커넥터",
|
||||
"search-placeholder": "내 커넥터 검색",
|
||||
"browse": "커넥터 찾아보기",
|
||||
"add-custom": "사용자 지정 추가",
|
||||
"retry": "다시 시도",
|
||||
"your-connectors": "내 커넥터",
|
||||
"no-matching": "일치하는 커넥터가 없습니다.",
|
||||
"count-one": "개 커넥터",
|
||||
"count-other": "개 커넥터",
|
||||
"recommended": "추천 커넥터입니다.",
|
||||
"no-recommended": "사용 가능한 추천 커넥터가 없습니다.",
|
||||
"gateway-unavailable": "Open Connectors를 사용할 수 없습니다.",
|
||||
"gateway-unavailable-desc": "이 배포에서는 Connector Gateway가 활성화되어 있지 않습니다.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "기본 제공",
|
||||
"source-local": "로컬",
|
||||
"source-remote": "원격",
|
||||
"load-built-in-failed": "기본 제공 커넥터를 불러오지 못했습니다",
|
||||
"load-custom-failed": "사용자 지정 커넥터를 불러오지 못했습니다",
|
||||
"load-open-failed": "Open Connectors를 불러오지 못했습니다",
|
||||
"disconnected": "{{name}} 연결이 해제되었습니다",
|
||||
"disconnect-failed": "커넥터 연결을 해제하지 못했습니다",
|
||||
"update-failed": "커넥터를 업데이트하지 못했습니다",
|
||||
"status-save-failed-enabled": "커넥터가 활성화되었지만 상태를 저장하지 못했습니다. 새로 고친 후 다시 시도하세요.",
|
||||
"status-save-failed-disabled": "커넥터가 비활성화되었지만 상태를 저장하지 못했습니다. 새로 고친 후 다시 시도하세요.",
|
||||
"updated": "커넥터가 업데이트되었습니다",
|
||||
"save-failed": "커넥터를 저장하지 못했습니다",
|
||||
"deleted": "커넥터가 삭제되었습니다",
|
||||
"delete-failed": "커넥터를 삭제하지 못했습니다",
|
||||
"enable-connector": "커넥터 활성화",
|
||||
"disable-connector": "커넥터 비활성화",
|
||||
"more-actions": "추가 작업",
|
||||
"open": "열기",
|
||||
"edit": "편집",
|
||||
"delete": "삭제",
|
||||
"connected-account": "연결된 계정",
|
||||
"supported-actions": "지원되는 작업",
|
||||
"supported-actions-count": "지원되는 작업 {{num}}개",
|
||||
"show-more": "더 보기",
|
||||
"show-less": "간략히 보기",
|
||||
"provider-website": "제공업체 웹사이트",
|
||||
"built-in-title": "로컬 호환 커넥터",
|
||||
"built-in-desc": "이 커넥터는 Eigent의 로컬 통합 런타임을 사용합니다. Open Connectors는 새로운 호스팅 통합을 위한 기본 마켓플레이스입니다.",
|
||||
"configuration": "구성: {{vars}}",
|
||||
"status": "상태",
|
||||
"active": "활성",
|
||||
"disabled": "비활성",
|
||||
"server-url": "서버 URL",
|
||||
"not-configured": "구성되지 않음",
|
||||
"command": "명령어",
|
||||
"arguments": "인수",
|
||||
"requires": "{{vars}} 필요",
|
||||
"notion-desc": "Notion 워크스페이스를 연결합니다.",
|
||||
"google-calendar-desc": "Google Calendar 일정과 스케줄을 관리합니다.",
|
||||
"generic-desc": "{{name}}을(를) Eigent에 연결합니다.",
|
||||
"google-search": "Google 검색",
|
||||
"google-search-desc": "브라우저 및 리서치 작업에 Google Custom Search를 연결합니다.",
|
||||
"notion-install-failed": "Notion을 설치하지 못했습니다",
|
||||
"google-calendar-install-failed": "Google Calendar를 설치하지 못했습니다",
|
||||
"search-connectors": "커넥터 검색",
|
||||
"auth-api-key": "API 키",
|
||||
"auth-credential": "자격 증명",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "인증 없음",
|
||||
"unnamed-action": "이름 없는 작업",
|
||||
"try-again": "다시 시도",
|
||||
"no-open-found": "Open Connectors를 찾을 수 없습니다.",
|
||||
"no-built-in-found": "기본 제공 커넥터를 찾을 수 없습니다.",
|
||||
"loading": "불러오는 중…",
|
||||
"updating": "업데이트 중…",
|
||||
"loading-more": "더 불러오는 중…",
|
||||
"new": "신규",
|
||||
"installed": "설치됨",
|
||||
"local-integration": "로컬 통합",
|
||||
"authentication": "인증",
|
||||
"oauth-title": "OAuth로 연결",
|
||||
"oauth-desc": "Eigent가 제공업체의 인증 페이지를 엽니다. 인증이 완료되면 이 대화 상자에서 설치가 완료됩니다.",
|
||||
"oauth-scopes": "요청된 권한: {{scopes}}",
|
||||
"no-auth-desc": "이 커넥터에는 자격 증명이 필요하지 않습니다.",
|
||||
"waiting-authorization": "인증 완료 대기 중…",
|
||||
"authorization-started": "인증이 시작되었습니다",
|
||||
"installed-toast": "{{name}}이(가) 설치되었습니다",
|
||||
"no-authorization-url": "커넥터가 인증 URL을 반환하지 않았습니다",
|
||||
"authorization-pending": "인증이 아직 진행 중입니다. 제공업체 창에서 완료한 후 다시 시도하세요.",
|
||||
"install-failed": "커넥터를 설치하지 못했습니다",
|
||||
"detail-load-failed": "커넥터 세부 정보를 불러오지 못했습니다",
|
||||
"authorization-incomplete": "인증이 아직 완료되지 않았습니다. 제공업체 창에서 완료한 후 상태를 다시 새로 고치세요.",
|
||||
"refresh-status-failed": "커넥터 상태를 새로 고치지 못했습니다",
|
||||
"field-required": "{{field}}은(는) 필수입니다",
|
||||
"complete-authorization": "제공업체 창에서 인증을 완료하세요.",
|
||||
"refresh-status": "상태 새로 고침",
|
||||
"built-in-auth-desc": "설치하면 별도 창에서 제공업체 인증 절차가 열립니다.",
|
||||
"built-in-generic-desc": "Eigent 로컬 통합입니다.",
|
||||
"cancel": "취소",
|
||||
"install": "설치",
|
||||
"installing": "설치 중…",
|
||||
"save": "저장",
|
||||
"enter-value": "{{field}} 입력",
|
||||
"custom-title": "사용자 지정 커넥터 추가",
|
||||
"custom-subtitle": "신뢰할 수 있는 로컬 또는 원격 MCP 서버를 설치합니다.",
|
||||
"custom-warning": "사용자 지정 MCP 서버는 명령을 실행하거나 원격 서비스에 접근할 수 있습니다. 신뢰할 수 있는 구성만 설치하세요.",
|
||||
"local-json-desc": "유효한 JSON 구성을 입력하여 로컬 MCP 서버를 추가합니다.",
|
||||
"learn-more": "자세히 알아보기",
|
||||
"connector-name": "커넥터 이름",
|
||||
"remote-name-placeholder": "내 원격 MCP",
|
||||
"remote-url": "원격 MCP URL",
|
||||
"remote-url-note": "신뢰할 수 있는 로컬 네트워크 외부의 서버에는 HTTPS를 사용하세요.",
|
||||
"invalid-json": "잘못된 JSON: {{message}}",
|
||||
"json-format-error": "JSON 형식 오류: {{message}}",
|
||||
"parse-failed": "구문 분석 실패",
|
||||
"missing-mcp-servers": "구성에 mcpServers 객체가 있어야 합니다",
|
||||
"add-at-least-one": "MCP 서버를 하나 이상 추가하세요",
|
||||
"already-exists": "{{name}}이(가) 이미 있습니다",
|
||||
"name-required": "커넥터 이름은 필수입니다",
|
||||
"invalid-remote-url": "유효한 원격 MCP URL을 입력하세요",
|
||||
"remote-url-protocol": "원격 MCP URL은 HTTP 또는 HTTPS를 사용해야 합니다",
|
||||
"remote-missing-id": "원격 커넥터가 ID 없이 생성되었습니다",
|
||||
"custom-installed": "사용자 지정 커넥터 {{num}}개가 설치되었습니다",
|
||||
"install-custom-failed": "사용자 지정 커넥터를 설치하지 못했습니다",
|
||||
"env-key-value": "환경 변수(키-값)",
|
||||
"value-placeholder": "값",
|
||||
"configuration-title": "구성",
|
||||
"google-api-key": "Google API 키",
|
||||
"google-api-key-placeholder": "Google Cloud Console의 Google API 키를 입력하세요",
|
||||
"google-api-key-note": "Google API 키를 발급받는 방법 → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "검색 엔진 ID",
|
||||
"search-engine-id-placeholder": "API 키와 연결된 Custom Search Engine ID를 입력하세요",
|
||||
"google-search-custom-desc": "Google Custom Search를 연결합니다. Google API 키와 Custom Search Engine(CSE) ID가 필요합니다.",
|
||||
"google-search-default-desc": "Google 검색은 기본적으로 활성화되어 있습니다. API 키가 필요하지 않습니다."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/ru/connectors.json
Normal file
132
src/i18n/locales/ru/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "Коннекторы",
|
||||
"search-placeholder": "Поиск по вашим коннекторам",
|
||||
"browse": "Обзор коннекторов",
|
||||
"add-custom": "Добавить свой",
|
||||
"retry": "Повторить",
|
||||
"your-connectors": "Ваши коннекторы",
|
||||
"no-matching": "Подходящие коннекторы не найдены.",
|
||||
"count-one": "коннектор",
|
||||
"count-other": "коннекторов",
|
||||
"recommended": "Рекомендованные для вас коннекторы.",
|
||||
"no-recommended": "Нет доступных рекомендованных коннекторов.",
|
||||
"gateway-unavailable": "Open Connectors недоступен.",
|
||||
"gateway-unavailable-desc": "Connector Gateway не включён для этого развёртывания.",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "Встроенный",
|
||||
"source-local": "Локальный",
|
||||
"source-remote": "Удалённый",
|
||||
"load-built-in-failed": "Не удалось загрузить встроенные коннекторы",
|
||||
"load-custom-failed": "Не удалось загрузить пользовательские коннекторы",
|
||||
"load-open-failed": "Не удалось загрузить Open Connectors",
|
||||
"disconnected": "{{name}} отключён",
|
||||
"disconnect-failed": "Не удалось отключить коннектор",
|
||||
"update-failed": "Не удалось обновить коннектор",
|
||||
"status-save-failed-enabled": "Коннектор включён, но сохранить его состояние не удалось. Обновите страницу и повторите попытку.",
|
||||
"status-save-failed-disabled": "Коннектор отключён, но сохранить его состояние не удалось. Обновите страницу и повторите попытку.",
|
||||
"updated": "Коннектор обновлён",
|
||||
"save-failed": "Не удалось сохранить коннектор",
|
||||
"deleted": "Коннектор удалён",
|
||||
"delete-failed": "Не удалось удалить коннектор",
|
||||
"enable-connector": "Включить коннектор",
|
||||
"disable-connector": "Отключить коннектор",
|
||||
"more-actions": "Другие действия",
|
||||
"open": "Открыть",
|
||||
"edit": "Изменить",
|
||||
"delete": "Удалить",
|
||||
"connected-account": "Подключённая учётная запись",
|
||||
"supported-actions": "Поддерживаемые действия",
|
||||
"supported-actions-count": "Поддерживаемых действий: {{num}}",
|
||||
"show-more": "Показать больше",
|
||||
"show-less": "Показать меньше",
|
||||
"provider-website": "Сайт поставщика",
|
||||
"built-in-title": "Локальный коннектор совместимости",
|
||||
"built-in-desc": "Этот коннектор использует локальную среду интеграции Eigent. Open Connectors — основной каталог новых облачных интеграций.",
|
||||
"configuration": "Конфигурация: {{vars}}",
|
||||
"status": "Состояние",
|
||||
"active": "Активен",
|
||||
"disabled": "Отключён",
|
||||
"server-url": "URL сервера",
|
||||
"not-configured": "Не настроено",
|
||||
"command": "Команда",
|
||||
"arguments": "Аргументы",
|
||||
"requires": "Требуется: {{vars}}",
|
||||
"notion-desc": "Подключить рабочее пространство Notion.",
|
||||
"google-calendar-desc": "Управлять событиями и расписаниями Google Календаря.",
|
||||
"generic-desc": "Подключить {{name}} к Eigent.",
|
||||
"google-search": "Поиск Google",
|
||||
"google-search-desc": "Подключить Google Custom Search для задач браузера и поиска информации.",
|
||||
"notion-install-failed": "Не удалось установить Notion",
|
||||
"google-calendar-install-failed": "Не удалось установить Google Календарь",
|
||||
"search-connectors": "Поиск коннекторов",
|
||||
"auth-api-key": "Ключ API",
|
||||
"auth-credential": "Учётные данные",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "Без аутентификации",
|
||||
"unnamed-action": "Действие без названия",
|
||||
"try-again": "Повторить",
|
||||
"no-open-found": "Коннекторы Open Connectors не найдены.",
|
||||
"no-built-in-found": "Встроенные коннекторы не найдены.",
|
||||
"loading": "Загрузка…",
|
||||
"updating": "Обновление…",
|
||||
"loading-more": "Загрузка дополнительных элементов…",
|
||||
"new": "Новый",
|
||||
"installed": "Установлен",
|
||||
"local-integration": "Локальная интеграция",
|
||||
"authentication": "Аутентификация",
|
||||
"oauth-title": "Подключение через OAuth",
|
||||
"oauth-desc": "Eigent откроет страницу авторизации поставщика. После завершения авторизации установка будет завершена в этом диалоговом окне.",
|
||||
"oauth-scopes": "Запрашиваемые разрешения: {{scopes}}",
|
||||
"no-auth-desc": "Для этого коннектора не требуются учётные данные.",
|
||||
"waiting-authorization": "Ожидание завершения авторизации…",
|
||||
"authorization-started": "Авторизация начата",
|
||||
"installed-toast": "{{name}} установлен",
|
||||
"no-authorization-url": "Коннектор не вернул URL авторизации",
|
||||
"authorization-pending": "Авторизация ещё не завершена. Завершите её в окне поставщика и повторите попытку.",
|
||||
"install-failed": "Не удалось установить коннектор",
|
||||
"detail-load-failed": "Не удалось загрузить сведения о коннекторе",
|
||||
"authorization-incomplete": "Авторизация ещё не завершена. Завершите её в окне поставщика и снова обновите состояние.",
|
||||
"refresh-status-failed": "Не удалось обновить состояние коннектора",
|
||||
"field-required": "Поле {{field}} обязательно",
|
||||
"complete-authorization": "Завершите авторизацию в окне поставщика.",
|
||||
"refresh-status": "Обновить состояние",
|
||||
"built-in-auth-desc": "При установке процесс авторизации поставщика откроется в отдельном окне.",
|
||||
"built-in-generic-desc": "Локальная интеграция Eigent.",
|
||||
"cancel": "Отмена",
|
||||
"install": "Установить",
|
||||
"installing": "Установка…",
|
||||
"save": "Сохранить",
|
||||
"enter-value": "Введите {{field}}",
|
||||
"custom-title": "Добавить пользовательский коннектор",
|
||||
"custom-subtitle": "Установите доверенный локальный или удалённый MCP-сервер.",
|
||||
"custom-warning": "Пользовательские MCP-серверы могут выполнять команды или обращаться к удалённым службам. Устанавливайте только конфигурации, которым доверяете.",
|
||||
"local-json-desc": "Добавьте локальный MCP-сервер, указав корректную конфигурацию JSON.",
|
||||
"learn-more": "Подробнее",
|
||||
"connector-name": "Название коннектора",
|
||||
"remote-name-placeholder": "Мой удалённый MCP",
|
||||
"remote-url": "URL удалённого MCP",
|
||||
"remote-url-note": "Используйте HTTPS для серверов за пределами доверенной локальной сети.",
|
||||
"invalid-json": "Недопустимый JSON: {{message}}",
|
||||
"json-format-error": "Ошибка формата JSON: {{message}}",
|
||||
"parse-failed": "Не удалось выполнить разбор",
|
||||
"missing-mcp-servers": "Конфигурация должна содержать объект mcpServers",
|
||||
"add-at-least-one": "Добавьте хотя бы один MCP-сервер",
|
||||
"already-exists": "{{name}} уже существует",
|
||||
"name-required": "Необходимо указать название коннектора",
|
||||
"invalid-remote-url": "Введите корректный URL удалённого MCP",
|
||||
"remote-url-protocol": "URL удалённого MCP должен использовать HTTP или HTTPS",
|
||||
"remote-missing-id": "Удалённый коннектор создан без идентификатора",
|
||||
"custom-installed": "Установленные пользовательские коннекторы: {{num}}",
|
||||
"install-custom-failed": "Не удалось установить пользовательский коннектор",
|
||||
"env-key-value": "Переменные среды (ключ-значение)",
|
||||
"value-placeholder": "Значение",
|
||||
"configuration-title": "Конфигурация",
|
||||
"google-api-key": "Ключ API Google",
|
||||
"google-api-key-placeholder": "Введите ключ API Google из Google Cloud Console",
|
||||
"google-api-key-note": "Как получить ключ API Google → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "Идентификатор поисковой системы",
|
||||
"search-engine-id-placeholder": "Введите идентификатор пользовательской поисковой системы, связанный с ключом API",
|
||||
"google-search-custom-desc": "Подключите Google Custom Search. Требуются ключ API Google и идентификатор пользовательской поисковой системы (CSE).",
|
||||
"google-search-default-desc": "Поиск Google включён по умолчанию. Ключ API не требуется."
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/zh-Hans/connectors.json
Normal file
132
src/i18n/locales/zh-Hans/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "连接器",
|
||||
"search-placeholder": "搜索你的连接器",
|
||||
"browse": "浏览连接器",
|
||||
"add-custom": "添加自定义",
|
||||
"retry": "重试",
|
||||
"your-connectors": "你的连接器",
|
||||
"no-matching": "没有匹配的连接器。",
|
||||
"count-one": "个连接器",
|
||||
"count-other": "个连接器",
|
||||
"recommended": "为你推荐的连接器。",
|
||||
"no-recommended": "暂无推荐的连接器。",
|
||||
"gateway-unavailable": "Open Connectors 不可用。",
|
||||
"gateway-unavailable-desc": "当前部署未启用 Connector Gateway。",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "内置",
|
||||
"source-local": "本地",
|
||||
"source-remote": "远程",
|
||||
"load-built-in-failed": "加载内置连接器失败",
|
||||
"load-custom-failed": "加载自定义连接器失败",
|
||||
"load-open-failed": "加载 Open Connectors 失败",
|
||||
"disconnected": "{{name}} 已断开连接",
|
||||
"disconnect-failed": "断开连接器失败",
|
||||
"update-failed": "更新连接器失败",
|
||||
"status-save-failed-enabled": "连接器已启用,但保存状态失败。请刷新后重试。",
|
||||
"status-save-failed-disabled": "连接器已禁用,但保存状态失败。请刷新后重试。",
|
||||
"updated": "连接器已更新",
|
||||
"save-failed": "保存连接器失败",
|
||||
"deleted": "连接器已删除",
|
||||
"delete-failed": "删除连接器失败",
|
||||
"enable-connector": "启用连接器",
|
||||
"disable-connector": "禁用连接器",
|
||||
"more-actions": "更多操作",
|
||||
"open": "打开",
|
||||
"edit": "编辑",
|
||||
"delete": "删除",
|
||||
"connected-account": "已连接账户",
|
||||
"supported-actions": "支持的操作",
|
||||
"supported-actions-count": "支持 {{num}} 项操作",
|
||||
"show-more": "展开更多",
|
||||
"show-less": "收起",
|
||||
"provider-website": "服务商网站",
|
||||
"built-in-title": "本地兼容连接器",
|
||||
"built-in-desc": "此连接器使用 Eigent 的本地集成运行时。Open Connectors 是新托管集成的主要市场。",
|
||||
"configuration": "配置项:{{vars}}",
|
||||
"status": "状态",
|
||||
"active": "已启用",
|
||||
"disabled": "已禁用",
|
||||
"server-url": "服务器 URL",
|
||||
"not-configured": "未配置",
|
||||
"command": "命令",
|
||||
"arguments": "参数",
|
||||
"requires": "需要 {{vars}}",
|
||||
"notion-desc": "连接 Notion 工作区。",
|
||||
"google-calendar-desc": "管理 Google 日历的事件和日程。",
|
||||
"generic-desc": "将 {{name}} 连接到 Eigent。",
|
||||
"google-search": "Google 搜索",
|
||||
"google-search-desc": "连接 Google 自定义搜索,用于浏览器和研究任务。",
|
||||
"notion-install-failed": "安装 Notion 失败",
|
||||
"google-calendar-install-failed": "安装 Google 日历失败",
|
||||
"search-connectors": "搜索连接器",
|
||||
"auth-api-key": "API 密钥",
|
||||
"auth-credential": "凭证",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "无需认证",
|
||||
"unnamed-action": "未命名操作",
|
||||
"try-again": "重试",
|
||||
"no-open-found": "未找到 Open Connectors。",
|
||||
"no-built-in-found": "未找到内置连接器。",
|
||||
"loading": "加载中…",
|
||||
"updating": "更新中…",
|
||||
"loading-more": "加载更多…",
|
||||
"new": "新",
|
||||
"installed": "已安装",
|
||||
"local-integration": "本地集成",
|
||||
"authentication": "认证方式",
|
||||
"oauth-title": "使用 OAuth 连接",
|
||||
"oauth-desc": "Eigent 将打开服务商授权页面。授权完成后,此对话框将自动完成安装。",
|
||||
"oauth-scopes": "请求的权限范围:{{scopes}}",
|
||||
"no-auth-desc": "此连接器无需凭证。",
|
||||
"waiting-authorization": "等待授权完成…",
|
||||
"authorization-started": "授权已开始",
|
||||
"installed-toast": "{{name}} 已安装",
|
||||
"no-authorization-url": "连接器未返回授权 URL",
|
||||
"authorization-pending": "授权仍在进行中。请在服务商窗口中完成授权后重试。",
|
||||
"install-failed": "安装连接器失败",
|
||||
"detail-load-failed": "加载连接器详情失败",
|
||||
"authorization-incomplete": "授权尚未完成。请在服务商窗口中完成授权后再次刷新。",
|
||||
"refresh-status-failed": "刷新连接器状态失败",
|
||||
"field-required": "{{field}} 为必填项",
|
||||
"complete-authorization": "请在服务商窗口中完成授权。",
|
||||
"refresh-status": "刷新状态",
|
||||
"built-in-auth-desc": "安装时将在单独窗口中打开服务商授权流程。",
|
||||
"built-in-generic-desc": "一个 Eigent 本地集成。",
|
||||
"cancel": "取消",
|
||||
"install": "安装",
|
||||
"installing": "安装中…",
|
||||
"save": "保存",
|
||||
"enter-value": "请输入 {{field}}",
|
||||
"custom-title": "添加自定义连接器",
|
||||
"custom-subtitle": "安装你信任的本地或远程 MCP 服务器。",
|
||||
"custom-warning": "自定义 MCP 服务器可以执行命令或访问远程服务。请仅安装你信任的配置。",
|
||||
"local-json-desc": "通过提供有效的 JSON 配置来添加本地 MCP 服务器。",
|
||||
"learn-more": "了解更多",
|
||||
"connector-name": "连接器名称",
|
||||
"remote-name-placeholder": "我的远程 MCP",
|
||||
"remote-url": "远程 MCP URL",
|
||||
"remote-url-note": "对于可信本地网络之外的服务器,请使用 HTTPS。",
|
||||
"invalid-json": "无效的 JSON:{{message}}",
|
||||
"json-format-error": "JSON 格式错误:{{message}}",
|
||||
"parse-failed": "解析失败",
|
||||
"missing-mcp-servers": "配置必须包含 mcpServers 对象",
|
||||
"add-at-least-one": "请至少添加一个 MCP 服务器",
|
||||
"already-exists": "{{name}} 已存在",
|
||||
"name-required": "连接器名称为必填项",
|
||||
"invalid-remote-url": "请输入有效的远程 MCP URL",
|
||||
"remote-url-protocol": "远程 MCP URL 必须使用 HTTP 或 HTTPS",
|
||||
"remote-missing-id": "创建远程连接器时未返回 ID",
|
||||
"custom-installed": "已安装 {{num}} 个自定义连接器",
|
||||
"install-custom-failed": "安装自定义连接器失败",
|
||||
"env-key-value": "环境变量(键值对)",
|
||||
"value-placeholder": "值",
|
||||
"configuration-title": "配置",
|
||||
"google-api-key": "Google API 密钥",
|
||||
"google-api-key-placeholder": "输入 Google Cloud Console 中的 Google API 密钥",
|
||||
"google-api-key-note": "了解如何获取 Google API 密钥 → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "搜索引擎 ID",
|
||||
"search-engine-id-placeholder": "输入与你的 API 密钥关联的自定义搜索引擎 ID",
|
||||
"google-search-custom-desc": "连接 Google 自定义搜索。需要 Google API 密钥和自定义搜索引擎(CSE)ID。",
|
||||
"google-search-default-desc": "Google 搜索默认已启用,无需 API 密钥。"
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
132
src/i18n/locales/zh-Hant/connectors.json
Normal file
132
src/i18n/locales/zh-Hant/connectors.json
Normal file
|
|
@ -0,0 +1,132 @@
|
|||
{
|
||||
"title": "連接器",
|
||||
"search-placeholder": "搜尋你的連接器",
|
||||
"browse": "瀏覽連接器",
|
||||
"add-custom": "新增自訂",
|
||||
"retry": "重試",
|
||||
"your-connectors": "你的連接器",
|
||||
"no-matching": "沒有符合的連接器。",
|
||||
"count-one": "個連接器",
|
||||
"count-other": "個連接器",
|
||||
"recommended": "為你推薦的連接器。",
|
||||
"no-recommended": "暫無推薦的連接器。",
|
||||
"gateway-unavailable": "Open Connectors 無法使用。",
|
||||
"gateway-unavailable-desc": "目前部署未啟用 Connector Gateway。",
|
||||
"open-connectors": "Open Connectors",
|
||||
"source-open": "Open",
|
||||
"source-built-in": "內建",
|
||||
"source-local": "本機",
|
||||
"source-remote": "遠端",
|
||||
"load-built-in-failed": "載入內建連接器失敗",
|
||||
"load-custom-failed": "載入自訂連接器失敗",
|
||||
"load-open-failed": "載入 Open Connectors 失敗",
|
||||
"disconnected": "{{name}} 已中斷連接",
|
||||
"disconnect-failed": "中斷連接器失敗",
|
||||
"update-failed": "更新連接器失敗",
|
||||
"status-save-failed-enabled": "連接器已啟用,但儲存狀態失敗。請重新整理後再試。",
|
||||
"status-save-failed-disabled": "連接器已停用,但儲存狀態失敗。請重新整理後再試。",
|
||||
"updated": "連接器已更新",
|
||||
"save-failed": "儲存連接器失敗",
|
||||
"deleted": "連接器已刪除",
|
||||
"delete-failed": "刪除連接器失敗",
|
||||
"enable-connector": "啟用連接器",
|
||||
"disable-connector": "停用連接器",
|
||||
"more-actions": "更多操作",
|
||||
"open": "開啟",
|
||||
"edit": "編輯",
|
||||
"delete": "刪除",
|
||||
"connected-account": "已連接帳戶",
|
||||
"supported-actions": "支援的操作",
|
||||
"supported-actions-count": "支援 {{num}} 項操作",
|
||||
"show-more": "顯示更多",
|
||||
"show-less": "收合",
|
||||
"provider-website": "服務商網站",
|
||||
"built-in-title": "本機相容連接器",
|
||||
"built-in-desc": "此連接器使用 Eigent 的本機整合執行環境。Open Connectors 是新託管整合的主要市集。",
|
||||
"configuration": "設定項目:{{vars}}",
|
||||
"status": "狀態",
|
||||
"active": "已啟用",
|
||||
"disabled": "已停用",
|
||||
"server-url": "伺服器 URL",
|
||||
"not-configured": "未設定",
|
||||
"command": "指令",
|
||||
"arguments": "參數",
|
||||
"requires": "需要 {{vars}}",
|
||||
"notion-desc": "連接 Notion 工作區。",
|
||||
"google-calendar-desc": "管理 Google 日曆的活動與行程。",
|
||||
"generic-desc": "將 {{name}} 連接到 Eigent。",
|
||||
"google-search": "Google 搜尋",
|
||||
"google-search-desc": "連接 Google 自訂搜尋,用於瀏覽器與研究任務。",
|
||||
"notion-install-failed": "安裝 Notion 失敗",
|
||||
"google-calendar-install-failed": "安裝 Google 日曆失敗",
|
||||
"search-connectors": "搜尋連接器",
|
||||
"auth-api-key": "API 金鑰",
|
||||
"auth-credential": "憑證",
|
||||
"auth-oauth": "OAuth",
|
||||
"auth-none": "無需驗證",
|
||||
"unnamed-action": "未命名操作",
|
||||
"try-again": "再試一次",
|
||||
"no-open-found": "找不到 Open Connectors。",
|
||||
"no-built-in-found": "找不到內建連接器。",
|
||||
"loading": "載入中…",
|
||||
"updating": "更新中…",
|
||||
"loading-more": "載入更多…",
|
||||
"new": "新",
|
||||
"installed": "已安裝",
|
||||
"local-integration": "本機整合",
|
||||
"authentication": "驗證方式",
|
||||
"oauth-title": "使用 OAuth 連接",
|
||||
"oauth-desc": "Eigent 將開啟服務商授權頁面。授權完成後,此對話框將自動完成安裝。",
|
||||
"oauth-scopes": "要求的權限範圍:{{scopes}}",
|
||||
"no-auth-desc": "此連接器無需憑證。",
|
||||
"waiting-authorization": "等待授權完成…",
|
||||
"authorization-started": "授權已開始",
|
||||
"installed-toast": "{{name}} 已安裝",
|
||||
"no-authorization-url": "連接器未回傳授權 URL",
|
||||
"authorization-pending": "授權仍在進行中。請在服務商視窗中完成授權後再試。",
|
||||
"install-failed": "安裝連接器失敗",
|
||||
"detail-load-failed": "載入連接器詳細資訊失敗",
|
||||
"authorization-incomplete": "授權尚未完成。請在服務商視窗中完成授權後再次重新整理。",
|
||||
"refresh-status-failed": "重新整理連接器狀態失敗",
|
||||
"field-required": "{{field}} 為必填欄位",
|
||||
"complete-authorization": "請在服務商視窗中完成授權。",
|
||||
"refresh-status": "重新整理狀態",
|
||||
"built-in-auth-desc": "安裝時將在獨立視窗中開啟服務商授權流程。",
|
||||
"built-in-generic-desc": "一個 Eigent 本機整合。",
|
||||
"cancel": "取消",
|
||||
"install": "安裝",
|
||||
"installing": "安裝中…",
|
||||
"save": "儲存",
|
||||
"enter-value": "請輸入 {{field}}",
|
||||
"custom-title": "新增自訂連接器",
|
||||
"custom-subtitle": "安裝你信任的本機或遠端 MCP 伺服器。",
|
||||
"custom-warning": "自訂 MCP 伺服器可以執行指令或存取遠端服務。請僅安裝你信任的設定。",
|
||||
"local-json-desc": "透過提供有效的 JSON 設定來新增本機 MCP 伺服器。",
|
||||
"learn-more": "瞭解更多",
|
||||
"connector-name": "連接器名稱",
|
||||
"remote-name-placeholder": "我的遠端 MCP",
|
||||
"remote-url": "遠端 MCP URL",
|
||||
"remote-url-note": "對於可信本機網路以外的伺服器,請使用 HTTPS。",
|
||||
"invalid-json": "無效的 JSON:{{message}}",
|
||||
"json-format-error": "JSON 格式錯誤:{{message}}",
|
||||
"parse-failed": "解析失敗",
|
||||
"missing-mcp-servers": "設定必須包含 mcpServers 物件",
|
||||
"add-at-least-one": "請至少新增一個 MCP 伺服器",
|
||||
"already-exists": "{{name}} 已存在",
|
||||
"name-required": "連接器名稱為必填",
|
||||
"invalid-remote-url": "請輸入有效的遠端 MCP URL",
|
||||
"remote-url-protocol": "遠端 MCP URL 必須使用 HTTP 或 HTTPS",
|
||||
"remote-missing-id": "建立遠端連接器時未回傳 ID",
|
||||
"custom-installed": "已安裝 {{num}} 個自訂連接器",
|
||||
"install-custom-failed": "安裝自訂連接器失敗",
|
||||
"env-key-value": "環境變數(鍵值對)",
|
||||
"value-placeholder": "值",
|
||||
"configuration-title": "設定",
|
||||
"google-api-key": "Google API 金鑰",
|
||||
"google-api-key-placeholder": "輸入 Google Cloud Console 中的 Google API 金鑰",
|
||||
"google-api-key-note": "瞭解如何取得 Google API 金鑰 → https://developers.google.com/custom-search/v1/overview",
|
||||
"search-engine-id": "搜尋引擎 ID",
|
||||
"search-engine-id-placeholder": "輸入與你的 API 金鑰相關聯的自訂搜尋引擎 ID",
|
||||
"google-search-custom-desc": "連接 Google 自訂搜尋。需要 Google API 金鑰和自訂搜尋引擎(CSE)ID。",
|
||||
"google-search-default-desc": "Google 搜尋預設已啟用,無需 API 金鑰。"
|
||||
}
|
||||
|
|
@ -14,6 +14,7 @@
|
|||
|
||||
import agents from './agents.json';
|
||||
import chat from './chat.json';
|
||||
import connectors from './connectors.json';
|
||||
import dashboard from './dashboard.json';
|
||||
import layout from './layout.json';
|
||||
import setting from './setting.json';
|
||||
|
|
@ -26,6 +27,7 @@ export default {
|
|||
dashboard,
|
||||
workforce,
|
||||
chat,
|
||||
connectors,
|
||||
setting,
|
||||
update,
|
||||
triggers,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
1361
src/pages/Connectors/components/AddConnectorDialog.tsx
Normal file
1361
src/pages/Connectors/components/AddConnectorDialog.tsx
Normal file
File diff suppressed because it is too large
Load diff
411
src/pages/Connectors/components/AddCustomConnectorDialog.tsx
Normal file
411
src/pages/Connectors/components/AddCustomConnectorDialog.tsx
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { mcpInstall, mcpRemove } from '@/api/brain';
|
||||
import {
|
||||
proxyFetchDelete,
|
||||
proxyFetchGet,
|
||||
proxyFetchPost,
|
||||
proxyFetchPut,
|
||||
} from '@/api/http';
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogContentSection,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
} from '@/components/ui/dialog';
|
||||
import { Input } from '@/components/ui/input';
|
||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||
import loader from '@monaco-editor/loader';
|
||||
import MonacoEditor from '@monaco-editor/react';
|
||||
import { Server, Wrench } from 'lucide-react';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import { useCallback, useEffect, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { toast } from 'sonner';
|
||||
import type { ConnectorInstallHint, MCPUserItem } from './types';
|
||||
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
(globalThis as any).MonacoEnvironment = {
|
||||
// Vite does not bundle Monaco's language workers, so hand every label a
|
||||
// no-op worker: language services (JSON diagnostics etc.) stay disabled,
|
||||
// but the editor itself works and Monaco never throws for a missing
|
||||
// worker. Our own parse errors surface via installCustom's JSON.parse.
|
||||
getWorker() {
|
||||
return new Worker(
|
||||
URL.createObjectURL(
|
||||
new Blob([`self.onmessage = function () {};`], {
|
||||
type: 'application/javascript',
|
||||
})
|
||||
)
|
||||
);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
loader.config({ monaco });
|
||||
|
||||
const LOCAL_MCP_EXAMPLE = `{
|
||||
"mcpServers": {
|
||||
"sequential-thinking": {
|
||||
"command": "npx",
|
||||
"args": [
|
||||
"-y",
|
||||
"@modelcontextprotocol/server-sequential-thinking"
|
||||
]
|
||||
}
|
||||
}
|
||||
}`;
|
||||
|
||||
interface AddCustomConnectorDialogProps {
|
||||
open: boolean;
|
||||
customMcps: MCPUserItem[];
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onInstalled: (hint: ConnectorInstallHint) => void | Promise<void>;
|
||||
}
|
||||
|
||||
export default function AddCustomConnectorDialog({
|
||||
open,
|
||||
customMcps,
|
||||
onOpenChange,
|
||||
onInstalled,
|
||||
}: AddCustomConnectorDialogProps) {
|
||||
const { t } = useTranslation();
|
||||
const [customType, setCustomType] = useState<'local' | 'remote'>('local');
|
||||
const [localJson, setLocalJson] = useState(LOCAL_MCP_EXAMPLE);
|
||||
const [remoteName, setRemoteName] = useState('');
|
||||
const [remoteUrl, setRemoteUrl] = useState('');
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [formError, setFormError] = useState<string | null>(null);
|
||||
const [jsonError, setJsonError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!open) return;
|
||||
setCustomType('local');
|
||||
setRemoteName('');
|
||||
setRemoteUrl('');
|
||||
setFormError(null);
|
||||
setJsonError(null);
|
||||
setSaving(false);
|
||||
try {
|
||||
setLocalJson(JSON.stringify(JSON.parse(LOCAL_MCP_EXAMPLE), null, 4));
|
||||
} catch {
|
||||
setLocalJson(LOCAL_MCP_EXAMPLE);
|
||||
}
|
||||
}, [open]);
|
||||
|
||||
useEffect(() => {
|
||||
setJsonError(null);
|
||||
}, [localJson]);
|
||||
|
||||
const closeDialog = useCallback(() => {
|
||||
onOpenChange(false);
|
||||
}, [onOpenChange]);
|
||||
|
||||
const installCustom = useCallback(async () => {
|
||||
setSaving(true);
|
||||
setFormError(null);
|
||||
try {
|
||||
if (customType === 'local') {
|
||||
let parsed: {
|
||||
mcpServers?: Record<string, Record<string, unknown>>;
|
||||
};
|
||||
try {
|
||||
parsed = JSON.parse(localJson);
|
||||
} catch (error: any) {
|
||||
throw new Error(
|
||||
t('connectors.invalid-json', {
|
||||
message: error?.message || t('connectors.parse-failed'),
|
||||
})
|
||||
);
|
||||
}
|
||||
if (!parsed.mcpServers || typeof parsed.mcpServers !== 'object') {
|
||||
throw new Error(t('connectors.missing-mcp-servers'));
|
||||
}
|
||||
const names = Object.keys(parsed.mcpServers);
|
||||
if (!names.length) {
|
||||
throw new Error(t('connectors.add-at-least-one'));
|
||||
}
|
||||
const duplicate = names.find((name) =>
|
||||
customMcps.some(
|
||||
(item) => item.mcp_name.toLowerCase() === name.toLowerCase()
|
||||
)
|
||||
);
|
||||
if (duplicate) {
|
||||
throw new Error(t('connectors.already-exists', { name: duplicate }));
|
||||
}
|
||||
|
||||
const response = await proxyFetchPost(
|
||||
'/api/v1/mcp/import/local',
|
||||
parsed
|
||||
);
|
||||
if (response?.detail) throw new Error(String(response.detail));
|
||||
const installedNames: string[] = [];
|
||||
try {
|
||||
for (const [name, config] of Object.entries(parsed.mcpServers)) {
|
||||
await mcpInstall(name, config);
|
||||
installedNames.push(name);
|
||||
}
|
||||
} catch (installError) {
|
||||
// The import already created DB records; remove them so a retry
|
||||
// does not fail the duplicate-name check. Cleanup is best-effort.
|
||||
for (const name of installedNames) {
|
||||
try {
|
||||
await mcpRemove(name);
|
||||
} catch {
|
||||
// Preserve the install error.
|
||||
}
|
||||
}
|
||||
try {
|
||||
const users = await proxyFetchGet('/api/v1/mcp/users');
|
||||
const rows: MCPUserItem[] = Array.isArray(users)
|
||||
? users
|
||||
: Array.isArray(users?.items)
|
||||
? users.items
|
||||
: [];
|
||||
const lowerNames = new Set(names.map((n) => n.toLowerCase()));
|
||||
for (const row of rows) {
|
||||
if (lowerNames.has((row.mcp_name || '').toLowerCase())) {
|
||||
await proxyFetchDelete(`/api/v1/mcp/users/${row.id}`);
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
// Preserve the install error.
|
||||
}
|
||||
throw installError;
|
||||
}
|
||||
toast.success(t('connectors.custom-installed', { num: names.length }));
|
||||
await onInstalled({ source: 'custom', key: names[0] });
|
||||
closeDialog();
|
||||
return;
|
||||
}
|
||||
|
||||
const name = remoteName.trim();
|
||||
const url = remoteUrl.trim();
|
||||
if (!name) throw new Error(t('connectors.name-required'));
|
||||
if (
|
||||
customMcps.some(
|
||||
(item) => item.mcp_name.toLowerCase() === name.toLowerCase()
|
||||
)
|
||||
) {
|
||||
throw new Error(t('connectors.already-exists', { name }));
|
||||
}
|
||||
let parsedUrl: URL;
|
||||
try {
|
||||
parsedUrl = new URL(url);
|
||||
} catch {
|
||||
throw new Error(t('connectors.invalid-remote-url'));
|
||||
}
|
||||
if (!['http:', 'https:'].includes(parsedUrl.protocol)) {
|
||||
throw new Error(t('connectors.remote-url-protocol'));
|
||||
}
|
||||
|
||||
const response = await proxyFetchPost('/api/v1/mcp/import/remote', {
|
||||
server_name: name,
|
||||
server_url: url,
|
||||
});
|
||||
if (response?.detail) throw new Error(String(response.detail));
|
||||
const importedId = Number(response?.mcp_user?.id);
|
||||
if (!Number.isInteger(importedId) || importedId <= 0) {
|
||||
throw new Error(t('connectors.remote-missing-id'));
|
||||
}
|
||||
try {
|
||||
await proxyFetchPut(`/api/v1/mcp/users/${importedId}`, {
|
||||
mcp_key: name,
|
||||
mcp_desc: name,
|
||||
});
|
||||
} catch (error) {
|
||||
try {
|
||||
await proxyFetchDelete(`/api/v1/mcp/users/${importedId}`);
|
||||
} catch {
|
||||
// Preserve the update error; cleanup is best-effort.
|
||||
}
|
||||
throw error;
|
||||
}
|
||||
try {
|
||||
await mcpInstall(name, { url });
|
||||
} catch (installError) {
|
||||
try {
|
||||
await proxyFetchDelete(`/api/v1/mcp/users/${importedId}`);
|
||||
} catch {
|
||||
// Preserve the runtime install error; cleanup is best-effort.
|
||||
}
|
||||
throw installError;
|
||||
}
|
||||
toast.success(t('connectors.installed-toast', { name }));
|
||||
await onInstalled({ source: 'custom', key: name });
|
||||
closeDialog();
|
||||
} catch (error: any) {
|
||||
setFormError(error?.message || t('connectors.install-custom-failed'));
|
||||
} finally {
|
||||
setSaving(false);
|
||||
}
|
||||
}, [
|
||||
closeDialog,
|
||||
customMcps,
|
||||
customType,
|
||||
localJson,
|
||||
onInstalled,
|
||||
remoteName,
|
||||
remoteUrl,
|
||||
t,
|
||||
]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(next) => {
|
||||
if (!next) closeDialog();
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
size="lg"
|
||||
showCloseButton
|
||||
onClose={closeDialog}
|
||||
overlayVariant="dimmed"
|
||||
className="h-[min(640px,90vh)] !max-w-[720px]"
|
||||
>
|
||||
<DialogHeader
|
||||
title={t('connectors.custom-title')}
|
||||
subtitle={t('connectors.custom-subtitle')}
|
||||
className="pr-12"
|
||||
/>
|
||||
|
||||
<DialogContentSection className="flex min-h-0 flex-col overflow-hidden p-0">
|
||||
<div className="scrollbar-always-visible min-h-0 flex-1 overflow-y-auto p-5">
|
||||
<div className="mx-auto flex max-w-3xl flex-col gap-5">
|
||||
<Tabs
|
||||
value={customType}
|
||||
onValueChange={(value) => {
|
||||
const next = value as 'local' | 'remote';
|
||||
setCustomType(next);
|
||||
setFormError(null);
|
||||
if (next !== 'local') return;
|
||||
try {
|
||||
setLocalJson(
|
||||
JSON.stringify(JSON.parse(localJson), null, 4)
|
||||
);
|
||||
setJsonError(null);
|
||||
} catch (error: any) {
|
||||
setJsonError(
|
||||
t('connectors.json-format-error', {
|
||||
message: error?.message || String(error),
|
||||
})
|
||||
);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<TabsList appearance="default">
|
||||
<TabsTrigger value="local">
|
||||
<Wrench className="h-3.5 w-3.5" />
|
||||
<span className="!text-body-sm font-bold text-ds-text-neutral-default-default">
|
||||
{t('connectors.source-local')}
|
||||
</span>
|
||||
</TabsTrigger>
|
||||
<TabsTrigger value="remote">
|
||||
<Server className="h-3.5 w-3.5" />
|
||||
<span className="!text-body-sm font-bold text-ds-text-neutral-default-default">
|
||||
{t('connectors.source-remote')}
|
||||
</span>
|
||||
</TabsTrigger>
|
||||
</TabsList>
|
||||
</Tabs>
|
||||
|
||||
<div className="rounded-xl border border-solid border-ds-border-warning-default-default bg-ds-bg-warning-subtle-default p-4 text-body-sm text-ds-text-warning-strong-default">
|
||||
{t('connectors.custom-warning')}
|
||||
</div>
|
||||
|
||||
{customType === 'local' ? (
|
||||
<div className="space-y-2">
|
||||
<span className="block text-body-sm text-ds-text-neutral-muted-default">
|
||||
{t('connectors.local-json-desc')}{' '}
|
||||
<a
|
||||
href="https://modelcontextprotocol.io/docs/getting-started/intro"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ds-text-information-strong-default underline underline-offset-2"
|
||||
>
|
||||
{t('connectors.learn-more')}
|
||||
</a>
|
||||
</span>
|
||||
{jsonError ? (
|
||||
<span className="block text-label-sm text-ds-text-error-strong-default">
|
||||
{jsonError}
|
||||
</span>
|
||||
) : null}
|
||||
<div className="overflow-hidden rounded-xl border border-solid border-ds-border-neutral-strong-default">
|
||||
<MonacoEditor
|
||||
height="300px"
|
||||
width="100%"
|
||||
language="json"
|
||||
theme="vs-dark"
|
||||
value={localJson}
|
||||
onChange={(value) => setLocalJson(value ?? '')}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
scrollBeyondLastLine: false,
|
||||
readOnly: saving,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-4">
|
||||
<Input
|
||||
title={t('connectors.connector-name')}
|
||||
required
|
||||
value={remoteName}
|
||||
onChange={(event) => setRemoteName(event.target.value)}
|
||||
placeholder={t('connectors.remote-name-placeholder')}
|
||||
leadingIcon={<Wrench className="h-4 w-4" />}
|
||||
/>
|
||||
<Input
|
||||
title={t('connectors.remote-url')}
|
||||
required
|
||||
value={remoteUrl}
|
||||
onChange={(event) => setRemoteUrl(event.target.value)}
|
||||
placeholder="https://example.com/mcp"
|
||||
leadingIcon={<Server className="h-4 w-4" />}
|
||||
note={t('connectors.remote-url-note')}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{formError ? (
|
||||
<div className="rounded-xl bg-ds-bg-error-subtle-default p-3 text-body-sm text-ds-text-error-strong-default">
|
||||
{formError}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<DialogFooter
|
||||
showCancelButton
|
||||
cancelButtonText={t('connectors.cancel')}
|
||||
onCancel={closeDialog}
|
||||
showConfirmButton
|
||||
confirmButtonText={
|
||||
saving ? t('connectors.installing') : t('connectors.install')
|
||||
}
|
||||
onConfirm={() => void installCustom()}
|
||||
confirmButtonDisabled={saving}
|
||||
/>
|
||||
</DialogContentSection>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -24,15 +24,14 @@ import { toast } from 'sonner';
|
|||
const FIELDS = [
|
||||
{
|
||||
key: 'GOOGLE_API_KEY',
|
||||
label: 'Google API Key',
|
||||
placeholder: 'Enter your Google API key from Google Cloud Console',
|
||||
note: 'Learn how to get your Google API key → https://developers.google.com/custom-search/v1/overview',
|
||||
labelKey: 'connectors.google-api-key',
|
||||
placeholderKey: 'connectors.google-api-key-placeholder',
|
||||
noteKey: 'connectors.google-api-key-note',
|
||||
},
|
||||
{
|
||||
key: 'SEARCH_ENGINE_ID',
|
||||
label: 'Search Engine ID',
|
||||
placeholder:
|
||||
'Enter the Custom Search Engine ID associated with your API key',
|
||||
labelKey: 'connectors.search-engine-id',
|
||||
placeholderKey: 'connectors.search-engine-id-placeholder',
|
||||
},
|
||||
] as const;
|
||||
|
||||
|
|
@ -99,14 +98,11 @@ export function GoogleSearchPanel({ onConfigured }: GoogleSearchPanelProps) {
|
|||
{requiresApiKey ? (
|
||||
<>
|
||||
<span className="whitespace-pre-wrap text-body-sm text-ds-text-neutral-muted-default">
|
||||
{t('setting.google-search-custom-desc', {
|
||||
defaultValue:
|
||||
'Connect to Google Custom Search. Requires a Google API key and a Custom Search Engine (CSE) ID.',
|
||||
})}
|
||||
{t('connectors.google-search-custom-desc')}
|
||||
</span>
|
||||
|
||||
<div className="text-body-sm font-bold text-ds-text-neutral-default-default">
|
||||
{t('setting.tools', { defaultValue: 'Configuration' })}
|
||||
{t('connectors.configuration-title')}
|
||||
</div>
|
||||
|
||||
<div className="space-y-3">
|
||||
|
|
@ -115,9 +111,9 @@ export function GoogleSearchPanel({ onConfigured }: GoogleSearchPanelProps) {
|
|||
key={field.key}
|
||||
id={field.key}
|
||||
size="default"
|
||||
title={field.label}
|
||||
title={t(field.labelKey)}
|
||||
type={showKeys[field.key] ? 'text' : 'password'}
|
||||
placeholder={field.placeholder}
|
||||
placeholder={t(field.placeholderKey)}
|
||||
value={formData[field.key] || ''}
|
||||
onChange={(e) =>
|
||||
setFormData((prev) => ({
|
||||
|
|
@ -125,7 +121,7 @@ export function GoogleSearchPanel({ onConfigured }: GoogleSearchPanelProps) {
|
|||
[field.key]: e.target.value,
|
||||
}))
|
||||
}
|
||||
note={'note' in field ? field.note : undefined}
|
||||
note={'noteKey' in field ? t(field.noteKey) : undefined}
|
||||
backIcon={<Eye className="h-5 w-5" />}
|
||||
onBackIconClick={() =>
|
||||
setShowKeys((prev) => ({
|
||||
|
|
@ -145,10 +141,7 @@ export function GoogleSearchPanel({ onConfigured }: GoogleSearchPanelProps) {
|
|||
</>
|
||||
) : (
|
||||
<div className="rounded-lg bg-ds-bg-neutral-default-default px-4 py-3 text-body-sm text-ds-text-neutral-muted-default">
|
||||
{t('setting.google-search-default-desc', {
|
||||
defaultValue:
|
||||
'Google Search is enabled by default. No API key required.',
|
||||
})}
|
||||
{t('connectors.google-search-default-desc')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,162 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogContentSection,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
} from '@/components/ui/dialog';
|
||||
import loader from '@monaco-editor/loader';
|
||||
import MonacoEditor from '@monaco-editor/react';
|
||||
import * as monaco from 'monaco-editor';
|
||||
import React, { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
if (typeof globalThis !== 'undefined') {
|
||||
(globalThis as any).MonacoEnvironment = {
|
||||
getWorker(_: string, label: string) {
|
||||
if (['json', 'css', 'html', 'typescript', 'javascript'].includes(label)) {
|
||||
return new Worker(
|
||||
URL.createObjectURL(
|
||||
new Blob(
|
||||
[
|
||||
`
|
||||
self.onmessage = function () {};
|
||||
`,
|
||||
],
|
||||
{ type: 'application/javascript' }
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
loader.config({ monaco }); // put at the top of the MCPAddDialog component file
|
||||
|
||||
interface MCPAddDialogProps {
|
||||
open: boolean;
|
||||
addType: 'local' | 'remote';
|
||||
setAddType: (type: 'local' | 'remote') => void;
|
||||
localJson: string;
|
||||
setLocalJson: (v: string) => void;
|
||||
remoteName: string;
|
||||
setRemoteName: (v: string) => void;
|
||||
remoteUrl: string;
|
||||
setRemoteUrl: (v: string) => void;
|
||||
installing: boolean;
|
||||
onClose: () => void;
|
||||
onInstall: () => void;
|
||||
}
|
||||
|
||||
export default function MCPAddDialog({
|
||||
open,
|
||||
localJson,
|
||||
setLocalJson,
|
||||
installing,
|
||||
onClose,
|
||||
onInstall,
|
||||
}: MCPAddDialogProps) {
|
||||
const [jsonError, setJsonError] = useState<string | null>(null);
|
||||
const { t } = useTranslation();
|
||||
// when the dialog is opened, automatically format the JSON
|
||||
React.useEffect(() => {
|
||||
if (open && localJson) {
|
||||
try {
|
||||
const obj = JSON.parse(localJson);
|
||||
setLocalJson(JSON.stringify(obj, null, 4));
|
||||
setJsonError(null);
|
||||
} catch (e: any) {
|
||||
// do not format invalid JSON, keep the original content
|
||||
setJsonError('JSON format error: ' + (e.message || e.toString()));
|
||||
}
|
||||
} else if (open) {
|
||||
setJsonError(null);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [open]);
|
||||
|
||||
// when localJson changes, clear the error prompt
|
||||
React.useEffect(() => {
|
||||
setJsonError(null);
|
||||
}, [localJson]);
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
onOpenChange={(v) => {
|
||||
if (!v) onClose();
|
||||
}}
|
||||
>
|
||||
<DialogContent
|
||||
size="lg"
|
||||
showCloseButton
|
||||
onClose={onClose}
|
||||
className="p-0"
|
||||
>
|
||||
<DialogHeader title={t('setting.add-your-agent')} />
|
||||
<DialogContentSection>
|
||||
<div className="mb-4 text-body-sm text-ds-text-neutral-muted-default">
|
||||
{t(
|
||||
'setting.add-a-local-mcp-server-by-providing-a-valid-json-configuration'
|
||||
)}
|
||||
<a
|
||||
href="https://modelcontextprotocol.io/docs/getting-started/intro"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className="text-ds-text-status-splitting-strong-default underline"
|
||||
>
|
||||
{t('setting.learn-more')}
|
||||
</a>
|
||||
</div>
|
||||
{jsonError && (
|
||||
<div className="mb-2 text-label-md text-ds-text-status-error-strong-default">
|
||||
{jsonError}
|
||||
</div>
|
||||
)}
|
||||
<div className="rounded-xl border-ds-border-neutral-strong-default overflow-hidden border">
|
||||
<MonacoEditor
|
||||
height="300px"
|
||||
width="100%"
|
||||
language="json"
|
||||
theme="vs-dark"
|
||||
value={localJson}
|
||||
onChange={(v) => {
|
||||
setLocalJson(v ?? '');
|
||||
}}
|
||||
options={{
|
||||
minimap: { enabled: false },
|
||||
fontSize: 14,
|
||||
scrollBeyondLastLine: false,
|
||||
readOnly: installing,
|
||||
automaticLayout: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</DialogContentSection>
|
||||
<DialogFooter
|
||||
showConfirmButton
|
||||
confirmButtonText={
|
||||
installing ? t('setting.installing') : t('setting.install')
|
||||
}
|
||||
onConfirm={onInstall}
|
||||
confirmButtonVariant="primary"
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
|
@ -52,6 +52,7 @@ export default function MCPConfigDialog({
|
|||
const { t } = useTranslation();
|
||||
const formRef = useRef<HTMLFormElement | null>(null);
|
||||
if (!form || !mcp) return null;
|
||||
const isRemote = Number(mcp.type) === 2;
|
||||
return (
|
||||
<Dialog
|
||||
open={open}
|
||||
|
|
@ -64,7 +65,7 @@ export default function MCPConfigDialog({
|
|||
<form
|
||||
ref={formRef}
|
||||
onSubmit={onSave}
|
||||
className="gap-4 p-md flex flex-col"
|
||||
className="flex flex-col gap-4 p-md"
|
||||
>
|
||||
<Input
|
||||
title={t('setting.name')}
|
||||
|
|
@ -81,59 +82,81 @@ export default function MCPConfigDialog({
|
|||
disabled={loading}
|
||||
placeholder={t('setting.description') as string}
|
||||
/>
|
||||
<Input
|
||||
title={t('setting.command')}
|
||||
value={form.command}
|
||||
onChange={(e) => onChange({ ...form, command: e.target.value })}
|
||||
disabled={loading}
|
||||
placeholder={t('setting.command') as string}
|
||||
/>
|
||||
|
||||
<Textarea
|
||||
variant="enhanced"
|
||||
title={t('setting.args-one-per-line')}
|
||||
value={Array.isArray(form.argsArr) ? form.argsArr.join('\n') : ''}
|
||||
onChange={(e) =>
|
||||
onChange({ ...form, argsArr: e.target.value.split(/\r?\n/) })
|
||||
}
|
||||
disabled={loading}
|
||||
placeholder={t('setting.args-one-per-line') as string}
|
||||
rows={Math.max(
|
||||
3,
|
||||
form.argsArr && form.argsArr.length > 0 ? form.argsArr.length : 3
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="mb-1 text-label-sm font-normal block">
|
||||
Env (key-value)
|
||||
</div>
|
||||
{Object.entries(form.env).map(([k, v], idx) => (
|
||||
<div className="mb-2" key={k + idx}>
|
||||
{isRemote ? (
|
||||
<Input
|
||||
title={t('connectors.remote-url')}
|
||||
value={form.server_url}
|
||||
onChange={(e) =>
|
||||
onChange({ ...form, server_url: e.target.value })
|
||||
}
|
||||
disabled={loading}
|
||||
placeholder="https://example.com/mcp"
|
||||
required
|
||||
/>
|
||||
) : (
|
||||
<>
|
||||
<Input
|
||||
title={k}
|
||||
type={showEnvValues[k] ? 'text' : 'password'}
|
||||
value={String(v)}
|
||||
onChange={(e) => {
|
||||
const newEnv = { ...form.env };
|
||||
newEnv[k] = e.target.value;
|
||||
onChange({ ...form, env: newEnv });
|
||||
}}
|
||||
title={t('setting.command')}
|
||||
value={form.command}
|
||||
onChange={(e) => onChange({ ...form, command: e.target.value })}
|
||||
disabled={loading}
|
||||
backIcon={
|
||||
showEnvValues[k] ? (
|
||||
<Eye className="h-5 w-5" />
|
||||
) : (
|
||||
<EyeOff className="h-5 w-5" />
|
||||
)
|
||||
}
|
||||
onBackIconClick={() =>
|
||||
setShowEnvValues((prev) => ({ ...prev, [k]: !prev[k] }))
|
||||
}
|
||||
size="default"
|
||||
placeholder="Value"
|
||||
placeholder={t('setting.command') as string}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
|
||||
<Textarea
|
||||
variant="enhanced"
|
||||
title={t('setting.args-one-per-line')}
|
||||
value={
|
||||
Array.isArray(form.argsArr) ? form.argsArr.join('\n') : ''
|
||||
}
|
||||
onChange={(e) =>
|
||||
onChange({ ...form, argsArr: e.target.value.split(/\r?\n/) })
|
||||
}
|
||||
disabled={loading}
|
||||
placeholder={t('setting.args-one-per-line') as string}
|
||||
rows={Math.max(
|
||||
3,
|
||||
form.argsArr && form.argsArr.length > 0
|
||||
? form.argsArr.length
|
||||
: 3
|
||||
)}
|
||||
/>
|
||||
|
||||
<div className="mb-1 block text-label-sm font-normal">
|
||||
{t('connectors.env-key-value')}
|
||||
</div>
|
||||
{Object.entries(form.env).map(([k, v], idx) => (
|
||||
<div className="mb-2" key={k + idx}>
|
||||
<Input
|
||||
title={k}
|
||||
type={showEnvValues[k] ? 'text' : 'password'}
|
||||
value={String(v)}
|
||||
onChange={(e) => {
|
||||
const newEnv = { ...form.env };
|
||||
newEnv[k] = e.target.value;
|
||||
onChange({ ...form, env: newEnv });
|
||||
}}
|
||||
disabled={loading}
|
||||
backIcon={
|
||||
showEnvValues[k] ? (
|
||||
<Eye className="h-5 w-5" />
|
||||
) : (
|
||||
<EyeOff className="h-5 w-5" />
|
||||
)
|
||||
}
|
||||
onBackIconClick={() =>
|
||||
setShowEnvValues((prev) => ({
|
||||
...prev,
|
||||
[k]: !prev[k],
|
||||
}))
|
||||
}
|
||||
size="default"
|
||||
placeholder={t('connectors.value-placeholder')}
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{errorMsg && (
|
||||
<div className="mb-2 text-label-md text-ds-text-status-error-strong-default">
|
||||
{errorMsg}
|
||||
|
|
|
|||
|
|
@ -1,47 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import MCPListItem from './MCPListItem';
|
||||
import type { MCPUserItem } from './types';
|
||||
|
||||
interface MCPListProps {
|
||||
items: MCPUserItem[];
|
||||
onSetting: (item: MCPUserItem) => void;
|
||||
onDelete: (item: MCPUserItem) => void;
|
||||
onSwitch: (id: number, checked: boolean) => Promise<void>;
|
||||
switchLoading: Record<number, boolean>;
|
||||
}
|
||||
|
||||
export default function MCPList({
|
||||
items,
|
||||
onSetting,
|
||||
onDelete,
|
||||
onSwitch,
|
||||
switchLoading,
|
||||
}: MCPListProps) {
|
||||
return (
|
||||
<div className="pt-4">
|
||||
{items.map((item) => (
|
||||
<MCPListItem
|
||||
key={item.id}
|
||||
item={item}
|
||||
onSetting={onSetting}
|
||||
onDelete={onDelete}
|
||||
onSwitch={onSwitch}
|
||||
loading={!!switchLoading[item.id]}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { TooltipSimple } from '@/components/ui/tooltip';
|
||||
import { capitalizeFirstLetter } from '@/lib';
|
||||
import { CircleAlert, Trash2 } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import type { MCPUserItem } from './types';
|
||||
|
||||
interface MCPListItemProps {
|
||||
item: MCPUserItem;
|
||||
onSetting: (item: MCPUserItem) => void;
|
||||
onDelete: (item: MCPUserItem) => void;
|
||||
onSwitch: (id: number, checked: boolean) => Promise<void>;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export default function MCPListItem({
|
||||
item,
|
||||
onSetting: _onSetting,
|
||||
onDelete,
|
||||
onSwitch: _onSwitch,
|
||||
loading: _loading,
|
||||
}: MCPListItemProps) {
|
||||
const [_showMenu, setShowMenu] = useState(false);
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<div className="mb-4 gap-4 rounded-2xl bg-ds-bg-neutral-strong-default p-4 flex items-center justify-between">
|
||||
<div className="gap-4 flex items-center">
|
||||
<div className="mx-xs h-3 w-3 bg-green-500 rounded-full"></div>
|
||||
<div className="text-base font-bold leading-9 text-ds-text-neutral-default-default">
|
||||
{capitalizeFirstLetter(item.mcp_name)}
|
||||
</div>
|
||||
<div className="flex items-center">
|
||||
<TooltipSimple content={item.mcp_desc}>
|
||||
<CircleAlert className="h-4 w-4 text-ds-icon-neutral-muted-default" />
|
||||
</TooltipSimple>
|
||||
</div>
|
||||
</div>
|
||||
<div className="gap-2 flex items-center">
|
||||
{/* <Switch
|
||||
checked={item.status === 1}
|
||||
disabled={loading}
|
||||
onCheckedChange={(checked) => onSwitch(item.id, checked)}
|
||||
/> */}
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
textWeight="medium"
|
||||
buttonRadius="lg"
|
||||
tone="error"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
onDelete(item);
|
||||
setShowMenu(false);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="h-4 w-4" /> {t('setting.delete')}
|
||||
</Button>
|
||||
{/* <div className="relative">
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs" buttonContent="icon-only"
|
||||
onClick={() => setShowMenu((v) => !v)}
|
||||
disabled={loading}
|
||||
>
|
||||
<Ellipsis className="w-4 h-4 text-ds-icon-neutral-default-default" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent className="w-[98px] p-sm rounded-[12px] bg-dropdown-bg border border-solid border-ds-border-neutral-default-default">
|
||||
<div className="space-y-1">
|
||||
<PopoverClose asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
onSetting(item);
|
||||
setShowMenu(false);
|
||||
}}
|
||||
>
|
||||
<Settings className="w-4 h-4" /> {t("setting.setting")}
|
||||
</Button>
|
||||
</PopoverClose>
|
||||
<PopoverClose asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="w-full !text-ds-text-status-error-strong-default"
|
||||
onClick={() => {
|
||||
onDelete(item);
|
||||
setShowMenu(false);
|
||||
}}
|
||||
>
|
||||
<Trash2 className="w-4 h-4" /> {t("setting.delete")}
|
||||
</Button>
|
||||
</PopoverClose>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
</div> */}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -1,426 +0,0 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import { mcpInstall, mcpRemove } from '@/api/brain';
|
||||
import { proxyFetchDelete, proxyFetchGet, proxyFetchPost } from '@/api/http';
|
||||
import githubIcon from '@/assets/icon/github.svg';
|
||||
import AnthropicIcon from '@/assets/mcp/Anthropic.svg?url';
|
||||
import CamelIcon from '@/assets/mcp/Camel.svg?url';
|
||||
import CommunityIcon from '@/assets/mcp/Community.svg?url';
|
||||
import OfficialIcon from '@/assets/mcp/Official.svg?url';
|
||||
import SearchInput from '@/components/Dashboard/SearchInput';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ToggleGroup, ToggleGroupItem } from '@/components/ui/toggle-group';
|
||||
import { TooltipSimple } from '@/components/ui/tooltip';
|
||||
import { useAuthStore } from '@/store/authStore';
|
||||
import { ChevronLeft, CircleAlert, Store } from 'lucide-react';
|
||||
import { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { MCPEnvDialog } from './MCPEnvDialog';
|
||||
interface MCPItem {
|
||||
id: number;
|
||||
name: string;
|
||||
key: string;
|
||||
description: string;
|
||||
status: number | string;
|
||||
category?: { name: string };
|
||||
home_page?: string;
|
||||
install_command?: {
|
||||
command: string;
|
||||
args: string[];
|
||||
env?: Record<string, string>;
|
||||
};
|
||||
homepage?: string;
|
||||
}
|
||||
|
||||
function useDebounce<T>(value: T, delay: number): T {
|
||||
const [debounced, setDebounced] = useState(value);
|
||||
useEffect(() => {
|
||||
const handler = setTimeout(() => setDebounced(value), delay);
|
||||
return () => clearTimeout(handler);
|
||||
}, [value, delay]);
|
||||
return debounced;
|
||||
}
|
||||
// map category name to svg file name
|
||||
const categoryIconMap: Record<string, string> = {
|
||||
anthropic: 'Anthropic',
|
||||
community: 'Community',
|
||||
official: 'Official',
|
||||
camel: 'Camel',
|
||||
};
|
||||
|
||||
// load all svg files
|
||||
const svgIcons: Record<string, string> = {
|
||||
Anthropic: AnthropicIcon,
|
||||
Community: CommunityIcon,
|
||||
Official: OfficialIcon,
|
||||
Camel: CamelIcon,
|
||||
};
|
||||
|
||||
type MCPMarketProps = {
|
||||
onBack?: () => void;
|
||||
keyword?: string;
|
||||
};
|
||||
|
||||
export default function MCPMarket({
|
||||
onBack,
|
||||
keyword: externalKeyword,
|
||||
}: MCPMarketProps) {
|
||||
const { t } = useTranslation();
|
||||
const { checkAgentTool } = useAuthStore();
|
||||
const [items, setItems] = useState<MCPItem[]>([]);
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const [error, setError] = useState('');
|
||||
const [page, setPage] = useState(1);
|
||||
const [hasMore, setHasMore] = useState(true);
|
||||
const [keyword, setKeyword] = useState('');
|
||||
const effectiveKeyword =
|
||||
externalKeyword !== undefined ? externalKeyword : keyword;
|
||||
const debouncedKeyword = useDebounce(effectiveKeyword, 400);
|
||||
const loader = useRef<HTMLDivElement | null>(null);
|
||||
const [installing, setInstalling] = useState<{ [id: number]: boolean }>({});
|
||||
const [installed, setInstalled] = useState<{ [id: number]: boolean }>({});
|
||||
const [installedIds, setInstalledIds] = useState<number[]>([]);
|
||||
const [mcpCategory, setMcpCategory] = useState<
|
||||
{ id: number; name: string }[]
|
||||
>([]);
|
||||
|
||||
// environment variable configuration
|
||||
const [showEnvConfig, setShowEnvConfig] = useState(false);
|
||||
const [activeMcp, setActiveMcp] = useState<MCPItem | null>(null);
|
||||
|
||||
const [categoryId, setCategoryId] = useState<number | undefined>(undefined);
|
||||
const effectiveCategoryId = categoryId;
|
||||
const [userInstallMcp, setUserInstallMcp] = useState<any | undefined>([]);
|
||||
// get installed MCP list
|
||||
useEffect(() => {
|
||||
proxyFetchGet('/api/v1/mcp/users').then((res) => {
|
||||
let ids: number[] = [];
|
||||
if (Array.isArray(res)) {
|
||||
setUserInstallMcp(res);
|
||||
ids = res.map((item: any) => item.mcp_id);
|
||||
} else if (Array.isArray(res.items)) {
|
||||
setUserInstallMcp(res.items);
|
||||
ids = res.items.map((item: any) => item.mcp_id);
|
||||
}
|
||||
setInstalledIds(ids);
|
||||
});
|
||||
}, []);
|
||||
|
||||
// get MCP categories
|
||||
useEffect(() => {
|
||||
proxyFetchGet('/api/v1/mcp/categories').then((res) => {
|
||||
if (Array.isArray(res)) {
|
||||
setMcpCategory(res);
|
||||
}
|
||||
});
|
||||
}, []);
|
||||
|
||||
// load data
|
||||
const loadData = useCallback(
|
||||
async (pageNum: number, kw: string, catId?: number, pageSize = 20) => {
|
||||
setIsLoading(true);
|
||||
setError('');
|
||||
try {
|
||||
const params: any = { page: pageNum, size: pageSize, keyword: kw };
|
||||
if (catId) params.category_id = catId;
|
||||
const res = await proxyFetchGet('/api/v1/mcps', params);
|
||||
if (res && Array.isArray(res.items)) {
|
||||
// frontend deduplication
|
||||
const all: MCPItem[] =
|
||||
pageNum === 1 ? res.items : [...items, ...res.items];
|
||||
const unique: MCPItem[] = Array.from(
|
||||
new Map(all.map((i: MCPItem) => [i.id, i])).values()
|
||||
);
|
||||
setItems(unique);
|
||||
setHasMore(res.items.length === pageSize);
|
||||
} else {
|
||||
if (pageNum === 1) setItems([]);
|
||||
setHasMore(false);
|
||||
}
|
||||
} catch (err: any) {
|
||||
setError(err?.message || 'Load failed');
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
},
|
||||
[items]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setPage(1);
|
||||
loadData(1, debouncedKeyword, effectiveCategoryId);
|
||||
// eslint-disable-next-line
|
||||
}, [debouncedKeyword, effectiveCategoryId]);
|
||||
|
||||
useEffect(() => {
|
||||
if (page > 1) loadData(page, debouncedKeyword, effectiveCategoryId);
|
||||
// eslint-disable-next-line
|
||||
}, [page]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!hasMore || isLoading) return;
|
||||
const node = loader.current;
|
||||
if (!node) return;
|
||||
const observer = new window.IntersectionObserver(
|
||||
(entries) => {
|
||||
if (entries[0].isIntersecting) {
|
||||
setPage((p) => (isLoading || !hasMore ? p : p + 1));
|
||||
}
|
||||
},
|
||||
{ root: null, rootMargin: '0px', threshold: 0.1 }
|
||||
);
|
||||
observer.observe(node);
|
||||
return () => {
|
||||
observer.disconnect();
|
||||
};
|
||||
}, [hasMore, isLoading]);
|
||||
|
||||
const checkEnv = (id: number) => {
|
||||
const mcp = items.find((mcp) => mcp.id === id);
|
||||
if (mcp && Object.keys(mcp?.install_command?.env || {}).length > 0) {
|
||||
setActiveMcp(mcp);
|
||||
setShowEnvConfig(true);
|
||||
} else {
|
||||
installMcp(id);
|
||||
}
|
||||
};
|
||||
const onConnect = (mcp: MCPItem) => {
|
||||
console.log(mcp);
|
||||
setItems((prev) =>
|
||||
prev.map((item) => (item.id === mcp.id ? { ...item, ...mcp } : item))
|
||||
);
|
||||
installMcp(mcp.id);
|
||||
onClose();
|
||||
};
|
||||
const onClose = () => {
|
||||
setShowEnvConfig(false);
|
||||
setActiveMcp(null);
|
||||
};
|
||||
const installMcp = async (id: number) => {
|
||||
setInstalling((prev) => ({ ...prev, [id]: true }));
|
||||
try {
|
||||
const mcpItem = items.find((item) => item.id === id);
|
||||
const res = await proxyFetchPost('/api/v1/mcp/install?mcp_id=' + id);
|
||||
if (res) {
|
||||
console.log(res);
|
||||
setUserInstallMcp((prev: any) => [...prev, res]);
|
||||
}
|
||||
setInstalled((prev) => ({ ...prev, [id]: true }));
|
||||
setInstalledIds((prev) => [...prev, id]);
|
||||
if (mcpItem?.install_command) {
|
||||
await mcpInstall(mcpItem.key, mcpItem.install_command);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Error installing MCP:', e);
|
||||
} finally {
|
||||
setInstalling((prev) => ({ ...prev, [id]: false }));
|
||||
}
|
||||
};
|
||||
|
||||
const handleBack = () => {
|
||||
if (onBack) onBack();
|
||||
else window.history.back();
|
||||
};
|
||||
|
||||
const handleDelete = async (deleteTarget: MCPItem) => {
|
||||
if (!deleteTarget) return;
|
||||
try {
|
||||
checkAgentTool(deleteTarget.name);
|
||||
console.log(userInstallMcp, deleteTarget);
|
||||
const userMcpRecord = userInstallMcp.find(
|
||||
(item: any) => item.mcp_id === deleteTarget.id
|
||||
);
|
||||
const id = userMcpRecord?.id;
|
||||
if (id === undefined || id === null) {
|
||||
console.warn(
|
||||
'No matching user MCP record found for delete target:',
|
||||
deleteTarget
|
||||
);
|
||||
return;
|
||||
}
|
||||
console.log('deleteTarget', deleteTarget);
|
||||
await proxyFetchDelete(`/api/v1/mcp/users/${id}`);
|
||||
await mcpRemove(deleteTarget.key);
|
||||
setInstalledIds((prev) =>
|
||||
prev.filter((item) => item !== deleteTarget.id)
|
||||
);
|
||||
setInstalled((prev) => ({ ...prev, [deleteTarget.id]: false }));
|
||||
loadData(1, debouncedKeyword, categoryId, page * 20);
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
}
|
||||
};
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center">
|
||||
{externalKeyword === undefined && (
|
||||
<>
|
||||
<div className="text-body top-0 mb-0 max-w-4xl py-2 sticky z-[20] flex w-full items-center justify-between">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={handleBack}
|
||||
className="mr-2"
|
||||
>
|
||||
<ChevronLeft className="h-6 w-6" />
|
||||
</Button>
|
||||
<span className="text-base font-bold leading-12 text-ds-text-neutral-default-default">
|
||||
{t('setting.mcp-market')}
|
||||
</span>
|
||||
</div>
|
||||
<div className="w-40 max-w-4xl">
|
||||
<SearchInput
|
||||
value={keyword}
|
||||
onChange={(e) => setKeyword(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Category toggle row */}
|
||||
<div className="py-2 flex w-full">
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
value={categoryId ? String(categoryId) : 'all'}
|
||||
onValueChange={(val) =>
|
||||
setCategoryId(!val || val === 'all' ? undefined : Number(val))
|
||||
}
|
||||
className="flex flex-wrap"
|
||||
>
|
||||
<ToggleGroupItem value="all">{t('setting.all')}</ToggleGroupItem>
|
||||
{mcpCategory.map((cat) => (
|
||||
<ToggleGroupItem key={cat.id} value={String(cat.id)}>
|
||||
{cat.name}
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
</ToggleGroup>
|
||||
</div>
|
||||
|
||||
{/* list */}
|
||||
<MCPEnvDialog
|
||||
showEnvConfig={showEnvConfig}
|
||||
onClose={onClose}
|
||||
onConnect={onConnect}
|
||||
activeMcp={activeMcp}
|
||||
></MCPEnvDialog>
|
||||
<div className="gap-4 pt-4 flex w-full flex-col">
|
||||
{isLoading && items.length === 0 && (
|
||||
<div className="py-8 text-ds-text-neutral-muted-default text-center">
|
||||
{t('setting.loading')}
|
||||
</div>
|
||||
)}
|
||||
{error && (
|
||||
<div className="py-8 text-ds-text-status-error-strong-default text-center">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
{!isLoading && !error && items.length === 0 && (
|
||||
<div className="py-8 text-ds-text-neutral-muted-default text-center">
|
||||
{t('setting.no-mcp-services')}
|
||||
</div>
|
||||
)}
|
||||
{items.map((item) => (
|
||||
<div
|
||||
key={item.id}
|
||||
className="rounded-2xl bg-ds-bg-neutral-default-default p-4 flex items-center"
|
||||
>
|
||||
{/* Left: Icon */}
|
||||
<div className="mr-4 flex items-center">
|
||||
{(() => {
|
||||
const catName = item.category?.name;
|
||||
const normalizedName = catName?.toLowerCase() || '';
|
||||
const iconKey = normalizedName
|
||||
? categoryIconMap[normalizedName]
|
||||
: undefined;
|
||||
const iconUrl = iconKey ? svgIcons[iconKey] : undefined;
|
||||
return iconUrl ? (
|
||||
<img src={iconUrl} alt={catName} className="h-11 w-9" />
|
||||
) : (
|
||||
<Store className="h-11 w-9 text-ds-icon-neutral-default-default" />
|
||||
);
|
||||
})()}
|
||||
</div>
|
||||
<div className="min-w-0 flex flex-1 flex-col justify-center">
|
||||
<div className="gap-xs pb-1 flex w-full items-center">
|
||||
<div className="gap-xs flex flex-1 items-center">
|
||||
<span className="text-base font-bold leading-9 text-ds-text-neutral-default-default truncate">
|
||||
{item.name}
|
||||
</span>
|
||||
<TooltipSimple content={item.description}>
|
||||
<CircleAlert className="h-4 w-4 text-ds-icon-neutral-muted-default" />
|
||||
</TooltipSimple>
|
||||
</div>
|
||||
<Button
|
||||
variant={
|
||||
!installedIds.includes(item.id) ? 'primary' : 'secondary'
|
||||
}
|
||||
size="sm"
|
||||
onClick={() =>
|
||||
installedIds.includes(item.id)
|
||||
? handleDelete(item)
|
||||
: checkEnv(item.id)
|
||||
}
|
||||
>
|
||||
{installedIds.includes(item.id)
|
||||
? t('setting.uninstall')
|
||||
: installing[item.id]
|
||||
? t('setting.installing')
|
||||
: installed[item.id]
|
||||
? t('setting.uninstall')
|
||||
: t('setting.install')}
|
||||
</Button>
|
||||
</div>
|
||||
{item.home_page &&
|
||||
item.home_page.startsWith('https://github.com/') && (
|
||||
<div className="flex items-center">
|
||||
<img
|
||||
src={githubIcon}
|
||||
alt="github"
|
||||
style={{
|
||||
width: 14.7,
|
||||
height: 14.7,
|
||||
marginRight: 4,
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
}}
|
||||
/>
|
||||
<span className="text-xs font-medium leading-3 items-center justify-center self-stretch">
|
||||
{(() => {
|
||||
const parts = item.home_page.split('/');
|
||||
return parts.length > 4 ? parts[4] : item.home_page;
|
||||
})()}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<div className="mt-1 text-sm text-ds-text-neutral-muted-default break-words whitespace-pre-line">
|
||||
{item.description}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
<div ref={loader} />
|
||||
{isLoading && items.length > 0 && (
|
||||
<div className="py-4 text-ds-text-neutral-muted-default text-center">
|
||||
{t('setting.loading-more')}
|
||||
</div>
|
||||
)}
|
||||
{!hasMore && items.length > 0 && (
|
||||
<div className="py-4 text-ds-text-neutral-muted-default text-center">
|
||||
{t('setting.no-more-mcp-servers')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -32,4 +32,10 @@ export interface MCPConfigForm {
|
|||
command: string;
|
||||
argsArr: string[];
|
||||
env: Record<string, string>;
|
||||
server_url: string;
|
||||
}
|
||||
|
||||
export type ConnectorInstallHint =
|
||||
| { source: 'open'; key: string }
|
||||
| { source: 'builtin'; key: string }
|
||||
| { source: 'custom'; key: string };
|
||||
|
|
|
|||
|
|
@ -13,13 +13,7 @@
|
|||
// ========= Copyright 2025-2026 @ Eigent.ai All Rights Reserved. =========
|
||||
|
||||
import ConnectorGateway from './ConnectorGateway';
|
||||
import MCP from './MCP';
|
||||
|
||||
export default function Connectors() {
|
||||
return (
|
||||
<div className="flex h-auto w-full flex-1 flex-col">
|
||||
<ConnectorGateway />
|
||||
<MCP />
|
||||
</div>
|
||||
);
|
||||
return <ConnectorGateway />;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue