mirror of
https://github.com/zhom/donutbrowser.git
synced 2026-10-21 06:51:24 +02:00
feat: leaner MCP and clients
This commit is contained in:
1 parent
38ab1f1a2d
commit
9ed9baf5d2
26 files changed
+2656
-360
No files matched your search
@@ -11,6 +11,7 @@ import {
|
||||
LuEyeOff,
|
||||
LuPlug,
|
||||
LuRefreshCw,
|
||||
LuSlidersHorizontal,
|
||||
LuTrash2,
|
||||
} from "react-icons/lu";
|
||||
import {
|
||||
@@ -29,6 +30,7 @@ import {
|
||||
AnimatedTabsTrigger,
|
||||
} from "@/components/ui/animated-tabs";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { CodeSnippet } from "@/components/ui/code-snippet";
|
||||
import { ConfirmationMark } from "@/components/ui/confirmation-mark";
|
||||
import { CopyToClipboard } from "@/components/ui/copy-to-clipboard";
|
||||
@@ -41,6 +43,11 @@ import {
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { OperationFlow } from "@/components/ui/operation-flow";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import {
|
||||
FeatureIcon,
|
||||
SECTION_LABEL_CLASS,
|
||||
@@ -65,6 +72,7 @@ import { translateBackendError } from "@/lib/backend-errors";
|
||||
import { canUseRemoteControl } from "@/lib/entitlements";
|
||||
import {
|
||||
type AgentCategory,
|
||||
CORE_TOOLSET,
|
||||
credentialPrefixOf,
|
||||
FX_AGENT_ID,
|
||||
fxExportLine,
|
||||
@@ -72,6 +80,7 @@ import {
|
||||
type McpRemoteCredential,
|
||||
type McpRemoteCredentialRotation,
|
||||
type McpRemoteStatus,
|
||||
OPTIONAL_TOOLSETS,
|
||||
REMOTE_MCP_URL,
|
||||
} from "@/lib/mcp";
|
||||
import { showErrorToast, showSuccessToast } from "@/lib/toast-utils";
|
||||
@@ -83,6 +92,7 @@ interface AppSettings {
|
||||
api_token?: string;
|
||||
mcp_remote_enabled: boolean;
|
||||
mcp_remote_key?: string | null;
|
||||
mcp_toolsets?: string[];
|
||||
}
|
||||
|
||||
type IntegrationsTab = "api" | "remote";
|
||||
@@ -128,6 +138,9 @@ export function IntegrationsDialog({
|
||||
const [showApiToken, setShowApiToken] = useState(false);
|
||||
const [isApiStarting, setIsApiStarting] = useState(false);
|
||||
const [agents, setAgents] = useState<McpAgentInfo[]>([]);
|
||||
const [showAllClients, setShowAllClients] = useState(false);
|
||||
const [isAddingAll, setIsAddingAll] = useState(false);
|
||||
const [isSavingToolsets, setIsSavingToolsets] = useState(false);
|
||||
const [busyAgentIds, setBusyAgentIds] = useState<Set<string>>(new Set());
|
||||
const [apiPortDraft, setApiPortDraft] = useState<string>("10108");
|
||||
const [remote, setRemote] = useState<McpRemoteStatus | null>(null);
|
||||
@@ -442,6 +455,63 @@ export function IntegrationsDialog({
|
||||
}
|
||||
};
|
||||
|
||||
/** Add Donut to several clients in one go, with one toast for all of them. */
|
||||
const handleAddAll = async (targets: McpAgentInfo[]) => {
|
||||
setIsAddingAll(true);
|
||||
for (const agent of targets) markAgentBusy(agent.id, true);
|
||||
const failed: string[] = [];
|
||||
try {
|
||||
await ensureCredential();
|
||||
for (const agent of targets) {
|
||||
try {
|
||||
await invoke("add_mcp_to_agent", { agentId: agent.id });
|
||||
} catch (e) {
|
||||
console.error(`Failed to add MCP to ${agent.id}:`, e);
|
||||
failed.push(agent.display_name);
|
||||
}
|
||||
}
|
||||
const added = targets.length - failed.length;
|
||||
if (added > 0) {
|
||||
showSuccessToast(t("integrations.mcp.addedToMany", { count: added }));
|
||||
}
|
||||
if (failed.length > 0) {
|
||||
showErrorToast(
|
||||
t("integrations.mcp.addFailed", { names: failed.join(", ") }),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorToast(translateBackendError(t, e));
|
||||
} finally {
|
||||
for (const agent of targets) markAgentBusy(agent.id, false);
|
||||
setIsAddingAll(false);
|
||||
void loadCredential();
|
||||
void loadSettings();
|
||||
void loadAgents();
|
||||
}
|
||||
};
|
||||
|
||||
/** Save the toolsets; the backend rewrites every connected client. */
|
||||
const handleSetToolsets = async (toolsets: string[]) => {
|
||||
setIsSavingToolsets(true);
|
||||
try {
|
||||
const failed = await invoke<string[]>("set_mcp_toolsets", { toolsets });
|
||||
setSettings((current) => ({ ...current, mcp_toolsets: toolsets }));
|
||||
if (failed.length > 0) {
|
||||
const names = failed.map(
|
||||
(id) => agents.find((agent) => agent.id === id)?.display_name ?? id,
|
||||
);
|
||||
showErrorToast(
|
||||
t("integrations.mcp.toolsets.failed", { names: names.join(", ") }),
|
||||
);
|
||||
}
|
||||
} catch (e) {
|
||||
showErrorToast(translateBackendError(t, e));
|
||||
} finally {
|
||||
setIsSavingToolsets(false);
|
||||
void loadSettings();
|
||||
}
|
||||
};
|
||||
|
||||
const handleRemoveAgent = async (agent: McpAgentInfo) => {
|
||||
markAgentBusy(agent.id, true);
|
||||
try {
|
||||
@@ -566,121 +636,185 @@ export function IntegrationsDialog({
|
||||
const shownTab: IntegrationsTab =
|
||||
activeTab === "remote" && !remoteTabShown ? "api" : activeTab;
|
||||
|
||||
const clientsGrid = (
|
||||
<section className="flex flex-col gap-2">
|
||||
<h3 className={SECTION_LABEL_CLASS}>
|
||||
{t("integrations.mcp.clientsLabel")}
|
||||
</h3>
|
||||
<div className="grid grid-cols-1 gap-2 @2xl:grid-cols-2">
|
||||
{agents.map((agent) => {
|
||||
const busy = busyAgentIds.has(agent.id);
|
||||
return (
|
||||
<div
|
||||
key={agent.id}
|
||||
className={cn(
|
||||
"flex flex-col justify-center gap-2 rounded-xl px-3 py-2.5 transition-colors duration-200",
|
||||
agent.connected ? "bg-foreground/5" : "bg-foreground/3",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<AgentIcon id={agent.id} category={agent.category} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium">
|
||||
{agent.display_name}
|
||||
</p>
|
||||
<p className="text-[10px] tracking-wide text-muted-foreground uppercase">
|
||||
{categoryLabel(t, agent.category)}
|
||||
</p>
|
||||
</div>
|
||||
{agent.connected ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium text-foreground">
|
||||
<ConfirmationMark />
|
||||
{t("appFeedback.configured")}
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="subtle"
|
||||
size="icon"
|
||||
className="size-8 rounded-lg hover:text-destructive-text"
|
||||
disabled={busy}
|
||||
onClick={() => void handleRemoveAgent(agent)}
|
||||
aria-label={t("integrations.mcp.removeAriaLabel", {
|
||||
name: agent.display_name,
|
||||
})}
|
||||
>
|
||||
<LuTrash2 className="size-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="soft"
|
||||
className="h-8 rounded-lg text-xs"
|
||||
disabled={busy}
|
||||
aria-busy={busy}
|
||||
onClick={() => void handleAddAgent(agent)}
|
||||
>
|
||||
{t("integrations.mcp.add")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{agent.connected && (
|
||||
<details data-slot="client-route" className="text-xs">
|
||||
<summary className="w-fit cursor-pointer rounded-sm py-1 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring">
|
||||
{t("appFeedback.routeDetails")}
|
||||
</summary>
|
||||
<OperationFlow
|
||||
label={t("appFeedback.routeDetails")}
|
||||
active={remote?.connected ? 2 : 1}
|
||||
steps={[
|
||||
{
|
||||
id: "client",
|
||||
label: agent.display_name,
|
||||
detail: t("appFeedback.configured"),
|
||||
},
|
||||
{
|
||||
id: "endpoint",
|
||||
label: t("integrations.tabRemote"),
|
||||
detail: (
|
||||
<span className="break-all">{REMOTE_MCP_URL}</span>
|
||||
),
|
||||
},
|
||||
{
|
||||
id: "device",
|
||||
label: t("appFeedback.thisDevice"),
|
||||
detail: t(
|
||||
remote?.connected
|
||||
? "appFeedback.reachable"
|
||||
: "appFeedback.notVerified",
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<p className="pt-1 leading-relaxed text-muted-foreground">
|
||||
{t("appFeedback.remoteProbeScope")}
|
||||
</p>
|
||||
</details>
|
||||
)}
|
||||
{agent.connected && agent.id === FX_AGENT_ID && (
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("integrations.remote.fxHint")}
|
||||
</p>
|
||||
{fxLine && (
|
||||
<CopyToClipboard
|
||||
variant="subtle"
|
||||
className="size-8 shrink-0 rounded-lg"
|
||||
text={fxLine}
|
||||
successMessage={t("integrations.remote.fxExportCopied")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
const renderAgent = (agent: McpAgentInfo) => {
|
||||
const busy = busyAgentIds.has(agent.id);
|
||||
return (
|
||||
<div
|
||||
key={agent.id}
|
||||
data-slot="mcp-client"
|
||||
data-agent-id={agent.id}
|
||||
className={cn(
|
||||
"flex flex-col justify-center gap-2 rounded-xl px-3 py-2.5 transition-colors duration-200",
|
||||
agent.connected ? "bg-foreground/5" : "bg-foreground/3",
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<AgentIcon id={agent.id} category={agent.category} />
|
||||
<div className="min-w-0 flex-1">
|
||||
<p className="truncate text-sm font-medium">{agent.display_name}</p>
|
||||
<p className="text-[10px] tracking-wide text-muted-foreground uppercase">
|
||||
{categoryLabel(t, agent.category)}
|
||||
</p>
|
||||
</div>
|
||||
{agent.connected ? (
|
||||
<div className="flex items-center gap-1">
|
||||
<span className="inline-flex items-center gap-1 text-xs font-medium text-foreground">
|
||||
<ConfirmationMark />
|
||||
{t("appFeedback.configured")}
|
||||
</span>
|
||||
<Button
|
||||
type="button"
|
||||
variant="subtle"
|
||||
size="icon"
|
||||
className="size-8 rounded-lg hover:text-destructive-text"
|
||||
disabled={busy}
|
||||
onClick={() => void handleRemoveAgent(agent)}
|
||||
aria-label={t("integrations.mcp.removeAriaLabel", {
|
||||
name: agent.display_name,
|
||||
})}
|
||||
>
|
||||
<LuTrash2 className="size-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
) : (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="soft"
|
||||
className="h-8 rounded-lg text-xs"
|
||||
disabled={busy}
|
||||
aria-busy={busy}
|
||||
onClick={() => void handleAddAgent(agent)}
|
||||
>
|
||||
{t("integrations.mcp.add")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
{agent.connected && (
|
||||
<details data-slot="client-route" className="text-xs">
|
||||
<summary className="w-fit cursor-pointer rounded-sm py-1 text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-2 focus-visible:outline-ring">
|
||||
{t("appFeedback.routeDetails")}
|
||||
</summary>
|
||||
<OperationFlow
|
||||
label={t("appFeedback.routeDetails")}
|
||||
active={remote?.connected ? 2 : 1}
|
||||
steps={[
|
||||
{
|
||||
id: "client",
|
||||
label: agent.display_name,
|
||||
detail: t("appFeedback.configured"),
|
||||
},
|
||||
{
|
||||
id: "endpoint",
|
||||
label: t("integrations.tabRemote"),
|
||||
detail: <span className="break-all">{REMOTE_MCP_URL}</span>,
|
||||
},
|
||||
{
|
||||
id: "device",
|
||||
label: t("appFeedback.thisDevice"),
|
||||
detail: t(
|
||||
remote?.connected
|
||||
? "appFeedback.reachable"
|
||||
: "appFeedback.notVerified",
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<p className="pt-1 leading-relaxed text-muted-foreground">
|
||||
{t("appFeedback.remoteProbeScope")}
|
||||
</p>
|
||||
</details>
|
||||
)}
|
||||
{agent.connected && agent.id === FX_AGENT_ID && (
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("integrations.remote.fxHint")}
|
||||
</p>
|
||||
{fxLine && (
|
||||
<CopyToClipboard
|
||||
variant="subtle"
|
||||
className="size-8 shrink-0 rounded-lg"
|
||||
text={fxLine}
|
||||
successMessage={t("integrations.remote.fxExportCopied")}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
// The clients on this computer come first; the rest wait behind a toggle,
|
||||
// so the page leads with the apps the person actually has.
|
||||
const onThisComputer = agents
|
||||
.filter((agent) => agent.connected || agent.detected)
|
||||
.sort((a, b) => Number(b.connected) - Number(a.connected));
|
||||
const otherClients = agents.filter(
|
||||
(agent) => !agent.connected && !agent.detected,
|
||||
);
|
||||
const suggestedClients = onThisComputer.filter((agent) => !agent.connected);
|
||||
const clientGridClass = "grid grid-cols-1 gap-2 @2xl:grid-cols-2";
|
||||
|
||||
const clientsGrid = (
|
||||
<section className="flex flex-col gap-2" data-slot="mcp-clients">
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<h3 className={SECTION_LABEL_CLASS}>
|
||||
{t("integrations.mcp.clientsLabel")}
|
||||
</h3>
|
||||
<ToolsetsPicker
|
||||
value={settings.mcp_toolsets ?? []}
|
||||
onChange={(next) => void handleSetToolsets(next)}
|
||||
disabled={isSavingToolsets}
|
||||
/>
|
||||
</div>
|
||||
{onThisComputer.length > 0 ? (
|
||||
<>
|
||||
<div className="flex items-center justify-between gap-2">
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("integrations.mcp.onThisComputer")}
|
||||
</p>
|
||||
{suggestedClients.length > 1 && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="soft"
|
||||
data-slot="mcp-add-all"
|
||||
className="h-7 rounded-lg text-xs"
|
||||
disabled={isAddingAll}
|
||||
aria-busy={isAddingAll}
|
||||
onClick={() => void handleAddAll(suggestedClients)}
|
||||
>
|
||||
{t("integrations.mcp.addToAll", {
|
||||
count: suggestedClients.length,
|
||||
})}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
<div className={clientGridClass}>
|
||||
{onThisComputer.map(renderAgent)}
|
||||
</div>
|
||||
</>
|
||||
) : (
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{t("integrations.mcp.noneFound")}
|
||||
</p>
|
||||
)}
|
||||
{otherClients.length > 0 && onThisComputer.length > 0 && (
|
||||
<Button
|
||||
type="button"
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
data-slot="mcp-show-more"
|
||||
className="h-7 w-fit rounded-lg px-2 text-xs"
|
||||
aria-expanded={showAllClients}
|
||||
onClick={() => setShowAllClients((shown) => !shown)}
|
||||
>
|
||||
{showAllClients
|
||||
? t("integrations.mcp.showFewer")
|
||||
: t("integrations.mcp.showMore", { count: otherClients.length })}
|
||||
</Button>
|
||||
)}
|
||||
{otherClients.length > 0 &&
|
||||
(onThisComputer.length === 0 || showAllClients) && (
|
||||
<div className={clientGridClass}>{otherClients.map(renderAgent)}</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
|
||||
@@ -1102,3 +1236,94 @@ export function IntegrationsDialog({
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
/** Which tool groups agents list, besides the core tools. */
|
||||
function ToolsetsPicker({
|
||||
value,
|
||||
onChange,
|
||||
disabled,
|
||||
}: {
|
||||
value: string[];
|
||||
onChange: (next: string[]) => void;
|
||||
disabled: boolean;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const everything = value.length === 0;
|
||||
const chosen: string[] = everything
|
||||
? [...OPTIONAL_TOOLSETS]
|
||||
: value.filter((name) => name !== CORE_TOOLSET);
|
||||
const label = everything
|
||||
? t("integrations.mcp.toolsets.all")
|
||||
: chosen.length === 0
|
||||
? t("integrations.mcp.toolsets.coreOnly")
|
||||
: t("integrations.mcp.toolsets.some", { count: chosen.length });
|
||||
const toggle = (name: string, on: boolean) => {
|
||||
const next = OPTIONAL_TOOLSETS.filter((set) =>
|
||||
set === name ? on : chosen.includes(set),
|
||||
);
|
||||
onChange(
|
||||
next.length === OPTIONAL_TOOLSETS.length
|
||||
? []
|
||||
: next.length === 0
|
||||
? [CORE_TOOLSET]
|
||||
: next,
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Popover>
|
||||
<PopoverTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
variant="subtle"
|
||||
size="sm"
|
||||
data-slot="mcp-toolsets"
|
||||
className="h-7 gap-1.5 rounded-lg px-2 text-xs"
|
||||
disabled={disabled}
|
||||
>
|
||||
<LuSlidersHorizontal className="size-3.5" aria-hidden="true" />
|
||||
{label}
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent align="end" className="w-80 p-3">
|
||||
<p className="text-sm font-medium">
|
||||
{t("integrations.mcp.toolsets.title")}
|
||||
</p>
|
||||
<p className="mt-1 text-xs leading-relaxed text-muted-foreground">
|
||||
{t("integrations.mcp.toolsets.description")}
|
||||
</p>
|
||||
<div className="mt-2 flex flex-col gap-0.5">
|
||||
{OPTIONAL_TOOLSETS.map((name) => (
|
||||
<div
|
||||
key={name}
|
||||
className="flex items-start gap-2 rounded-md px-1.5 py-1.5 transition-colors hover:bg-foreground/5"
|
||||
>
|
||||
<Checkbox
|
||||
id={`mcp-toolset-${name}`}
|
||||
data-toolset={name}
|
||||
checked={chosen.includes(name)}
|
||||
disabled={disabled}
|
||||
onCheckedChange={(checked) => toggle(name, checked === true)}
|
||||
className="mt-0.5"
|
||||
/>
|
||||
<label
|
||||
htmlFor={`mcp-toolset-${name}`}
|
||||
className="min-w-0 cursor-pointer"
|
||||
>
|
||||
<span className="block text-sm">
|
||||
{t(`integrations.mcp.toolsets.names.${name}.label`)}
|
||||
</span>
|
||||
<span className="block text-xs text-muted-foreground">
|
||||
{t(`integrations.mcp.toolsets.names.${name}.description`)}
|
||||
</span>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
<p className="mt-2 text-xs leading-relaxed text-muted-foreground">
|
||||
{t("integrations.mcp.toolsets.core")}
|
||||
</p>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Editor",
|
||||
"editorExt": "Editor-Erweiterung"
|
||||
},
|
||||
"onThisComputer": "Auf diesem Computer",
|
||||
"addToAll_one": "Zu allen {{count}} hinzufügen",
|
||||
"addToAll_other": "Zu allen {{count}} hinzufügen",
|
||||
"addedToMany_one": "Donut zu {{count}} App hinzugefügt",
|
||||
"addedToMany_other": "Donut zu {{count}} Apps hinzugefügt",
|
||||
"addFailed": "Donut konnte nicht zu {{names}} hinzugefügt werden.",
|
||||
"noneFound": "Auf diesem Computer wurden keine KI-Apps gefunden. Sie können Donut trotzdem zu jeder dieser Apps hinzufügen.",
|
||||
"showMore_one": "{{count}} weiteren Client anzeigen",
|
||||
"showMore_other": "{{count}} weitere Clients anzeigen",
|
||||
"showFewer": "Weniger Clients anzeigen",
|
||||
"toolsets": {
|
||||
"all": "Alle Werkzeuge",
|
||||
"coreOnly": "Nur Kernwerkzeuge",
|
||||
"some_one": "Kern + {{count}} Gruppe",
|
||||
"some_other": "Kern + {{count}} Gruppen",
|
||||
"title": "Werkzeuge, die Agenten sehen",
|
||||
"description": "Ein Agent lädt eine Beschreibung jedes Werkzeugs, das er sieht. Wählen Sie nur die Gruppen, die er braucht – dann startet er schneller und hat mehr Platz zum Arbeiten. Verbundene Apps werden sofort aktualisiert.",
|
||||
"core": "Profile, Browsersteuerung und die Werkzeuge, die Sie auf dem Laufenden halten, sind immer aktiv.",
|
||||
"failed": "{{names}} konnte nicht aktualisiert werden. Entfernen Sie Donut dort und fügen Sie es erneut hinzu.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Organisieren",
|
||||
"description": "Gruppen erstellen und ändern, Profile importieren"
|
||||
},
|
||||
"network": {
|
||||
"label": "Netzwerk",
|
||||
"description": "Proxys, VPNs, IP-Wechsel und DNS-Regeln"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Fingerabdruck",
|
||||
"description": "Das Gerät eines Profils lesen und ändern"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Erweiterungen",
|
||||
"description": "Erweiterungen und Erweiterungsgruppen"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookies",
|
||||
"description": "Cookie-Import und der Cookie-Bot"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Synchronisierung",
|
||||
"description": "Team-Sperren und der Synchronizer"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Remote-Flotte",
|
||||
"description": "Profile auf der Remote-Flotte ausführen"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Rezepte",
|
||||
"description": "Gespeicherte Abläufe, die ein Agent wiederholen kann"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "Lokale API",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "Ein Vorlagenname darf höchstens 60 Zeichen haben.",
|
||||
"templateLimitReached": "Sie können höchstens 100 Vorlagen behalten. Löschen Sie zuerst eine.",
|
||||
"bulkCreateCountInvalid": "Sie können 1 bis {{max}} Profile auf einmal erstellen.",
|
||||
"bulkCreateBusy": "Es werden bereits Profile erstellt. Warten Sie, bis das abgeschlossen ist."
|
||||
"bulkCreateBusy": "Es werden bereits Profile erstellt. Warten Sie, bis das abgeschlossen ist.",
|
||||
"mcpToolsetUnknown": "Diese Werkzeuggruppe existiert nicht."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profile",
|
||||
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Editor",
|
||||
"editorExt": "Editor ext"
|
||||
},
|
||||
"onThisComputer": "On this computer",
|
||||
"addToAll_one": "Add to all {{count}}",
|
||||
"addToAll_other": "Add to all {{count}}",
|
||||
"addedToMany_one": "Added Donut to {{count}} app",
|
||||
"addedToMany_other": "Added Donut to {{count}} apps",
|
||||
"addFailed": "Could not add Donut to {{names}}.",
|
||||
"noneFound": "No AI apps found on this computer. You can still add Donut to any of these.",
|
||||
"showMore_one": "Show {{count}} more client",
|
||||
"showMore_other": "Show {{count}} more clients",
|
||||
"showFewer": "Show fewer clients",
|
||||
"toolsets": {
|
||||
"all": "All tools",
|
||||
"coreOnly": "Core tools only",
|
||||
"some_one": "Core + {{count}} group",
|
||||
"some_other": "Core + {{count}} groups",
|
||||
"title": "Tools agents see",
|
||||
"description": "An agent loads a description of every tool it can see. Choose only the groups it needs, and it starts faster and has more room to work. Connected apps update at once.",
|
||||
"core": "Profiles, browser control and the tools that keep you in the loop are always on.",
|
||||
"failed": "Could not update {{names}}. Remove Donut there and add it again.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Organize",
|
||||
"description": "Create and change groups, import profiles"
|
||||
},
|
||||
"network": {
|
||||
"label": "Network",
|
||||
"description": "Proxies, VPNs, IP rotation and DNS rules"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Fingerprint",
|
||||
"description": "Read and change a profile's device"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Extensions",
|
||||
"description": "Extensions and extension groups"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookies",
|
||||
"description": "Cookie import and the cookie bot"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Sync",
|
||||
"description": "Team locks and the synchronizer"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Remote fleet",
|
||||
"description": "Run profiles on the remote fleet"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Recipes",
|
||||
"description": "Saved flows an agent can repeat"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "Local API",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "A template name can have at most 60 characters.",
|
||||
"templateLimitReached": "You can keep at most 100 templates. Delete one first.",
|
||||
"bulkCreateCountInvalid": "You can create from 1 to {{max}} profiles at a time.",
|
||||
"bulkCreateBusy": "Profiles are already being created. Wait until that is done."
|
||||
"bulkCreateBusy": "Profiles are already being created. Wait until that is done.",
|
||||
"mcpToolsetUnknown": "This tool group does not exist."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profiles",
|
||||
|
||||
@@ -869,6 +869,64 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Editor",
|
||||
"editorExt": "Extensión de editor"
|
||||
},
|
||||
"onThisComputer": "En este ordenador",
|
||||
"addToAll_one": "Añadir a los {{count}}",
|
||||
"addToAll_many": "Añadir a los {{count}}",
|
||||
"addToAll_other": "Añadir a los {{count}}",
|
||||
"addedToMany_one": "Donut añadido a {{count}} app",
|
||||
"addedToMany_many": "Donut añadido a {{count}} apps",
|
||||
"addedToMany_other": "Donut añadido a {{count}} apps",
|
||||
"addFailed": "No se pudo añadir Donut a {{names}}.",
|
||||
"noneFound": "No se encontraron apps de IA en este ordenador. Aun así puedes añadir Donut a cualquiera de estas.",
|
||||
"showMore_one": "Mostrar {{count}} cliente más",
|
||||
"showMore_many": "Mostrar {{count}} clientes más",
|
||||
"showMore_other": "Mostrar {{count}} clientes más",
|
||||
"showFewer": "Mostrar menos clientes",
|
||||
"toolsets": {
|
||||
"all": "Todas las herramientas",
|
||||
"coreOnly": "Solo herramientas básicas",
|
||||
"some_one": "Básicas + {{count}} grupo",
|
||||
"some_many": "Básicas + {{count}} grupos",
|
||||
"some_other": "Básicas + {{count}} grupos",
|
||||
"title": "Herramientas que ven los agentes",
|
||||
"description": "Un agente carga una descripción de cada herramienta que ve. Elige solo los grupos que necesita: arrancará más rápido y tendrá más espacio para trabajar. Las apps conectadas se actualizan al momento.",
|
||||
"core": "Los perfiles, el control del navegador y las herramientas que te mantienen al tanto están siempre activos.",
|
||||
"failed": "No se pudo actualizar {{names}}. Quita Donut ahí y vuelve a añadirlo.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Organizar",
|
||||
"description": "Crear y cambiar grupos, importar perfiles"
|
||||
},
|
||||
"network": {
|
||||
"label": "Red",
|
||||
"description": "Proxies, VPN, rotación de IP y reglas DNS"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Huella",
|
||||
"description": "Leer y cambiar el dispositivo de un perfil"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Extensiones",
|
||||
"description": "Extensiones y grupos de extensiones"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookies",
|
||||
"description": "Importación de cookies y el bot de cookies"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Sincronización",
|
||||
"description": "Bloqueos de equipo y el sincronizador"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Flota remota",
|
||||
"description": "Ejecutar perfiles en la flota remota"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Recetas",
|
||||
"description": "Flujos guardados que un agente puede repetir"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "API local",
|
||||
@@ -2581,7 +2639,8 @@
|
||||
"templateNameTooLong": "El nombre de una plantilla puede tener como máximo 60 caracteres.",
|
||||
"templateLimitReached": "Puedes guardar como máximo 100 plantillas. Elimina una primero.",
|
||||
"bulkCreateCountInvalid": "Puedes crear de 1 a {{max}} perfiles a la vez.",
|
||||
"bulkCreateBusy": "Ya se están creando perfiles. Espera a que termine."
|
||||
"bulkCreateBusy": "Ya se están creando perfiles. Espera a que termine.",
|
||||
"mcpToolsetUnknown": "Este grupo de herramientas no existe."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Perfiles",
|
||||
|
||||
@@ -869,6 +869,64 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Éditeur",
|
||||
"editorExt": "Ext. d'éditeur"
|
||||
},
|
||||
"onThisComputer": "Sur cet ordinateur",
|
||||
"addToAll_one": "Ajouter aux {{count}}",
|
||||
"addToAll_many": "Ajouter aux {{count}}",
|
||||
"addToAll_other": "Ajouter aux {{count}}",
|
||||
"addedToMany_one": "Donut ajouté à {{count}} app",
|
||||
"addedToMany_many": "Donut ajouté à {{count}} apps",
|
||||
"addedToMany_other": "Donut ajouté à {{count}} apps",
|
||||
"addFailed": "Impossible d'ajouter Donut à {{names}}.",
|
||||
"noneFound": "Aucune app d'IA trouvée sur cet ordinateur. Vous pouvez quand même ajouter Donut à l'une d'elles.",
|
||||
"showMore_one": "Afficher {{count}} autre client",
|
||||
"showMore_many": "Afficher {{count}} autres clients",
|
||||
"showMore_other": "Afficher {{count}} autres clients",
|
||||
"showFewer": "Afficher moins de clients",
|
||||
"toolsets": {
|
||||
"all": "Tous les outils",
|
||||
"coreOnly": "Outils de base uniquement",
|
||||
"some_one": "Base + {{count}} groupe",
|
||||
"some_many": "Base + {{count}} groupes",
|
||||
"some_other": "Base + {{count}} groupes",
|
||||
"title": "Outils visibles par les agents",
|
||||
"description": "Un agent charge une description de chaque outil qu'il voit. Ne choisissez que les groupes dont il a besoin : il démarre plus vite et a plus de place pour travailler. Les apps connectées sont mises à jour aussitôt.",
|
||||
"core": "Les profils, le contrôle du navigateur et les outils qui vous tiennent informé sont toujours actifs.",
|
||||
"failed": "Impossible de mettre à jour {{names}}. Retirez Donut de cette app et ajoutez-le à nouveau.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Organiser",
|
||||
"description": "Créer et modifier des groupes, importer des profils"
|
||||
},
|
||||
"network": {
|
||||
"label": "Réseau",
|
||||
"description": "Proxys, VPN, rotation d'IP et règles DNS"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Empreinte",
|
||||
"description": "Lire et modifier l'appareil d'un profil"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Extensions",
|
||||
"description": "Extensions et groupes d'extensions"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookies",
|
||||
"description": "Import de cookies et le bot de cookies"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Synchronisation",
|
||||
"description": "Verrous d'équipe et le synchroniseur"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Flotte distante",
|
||||
"description": "Lancer des profils sur la flotte distante"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Recettes",
|
||||
"description": "Flux enregistrés qu'un agent peut répéter"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "API locale",
|
||||
@@ -2581,7 +2639,8 @@
|
||||
"templateNameTooLong": "Le nom d'un modèle peut contenir au plus 60 caractères.",
|
||||
"templateLimitReached": "Vous pouvez garder au plus 100 modèles. Supprimez-en un d'abord.",
|
||||
"bulkCreateCountInvalid": "Vous pouvez créer de 1 à {{max}} profils à la fois.",
|
||||
"bulkCreateBusy": "Des profils sont déjà en cours de création. Attendez la fin."
|
||||
"bulkCreateBusy": "Des profils sont déjà en cours de création. Attendez la fin.",
|
||||
"mcpToolsetUnknown": "Ce groupe d'outils n'existe pas."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profils",
|
||||
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "CLI",
|
||||
"editor": "エディタ",
|
||||
"editorExt": "エディタ拡張"
|
||||
},
|
||||
"onThisComputer": "このコンピューター",
|
||||
"addToAll_one": "{{count}} 件すべてに追加",
|
||||
"addToAll_other": "{{count}} 件すべてに追加",
|
||||
"addedToMany_one": "{{count}} 件のアプリに Donut を追加しました",
|
||||
"addedToMany_other": "{{count}} 件のアプリに Donut を追加しました",
|
||||
"addFailed": "{{names}} に Donut を追加できませんでした。",
|
||||
"noneFound": "このコンピューターに AI アプリが見つかりません。以下のどのアプリにも Donut を追加できます。",
|
||||
"showMore_one": "ほかのクライアントを {{count}} 件表示",
|
||||
"showMore_other": "ほかのクライアントを {{count}} 件表示",
|
||||
"showFewer": "表示するクライアントを減らす",
|
||||
"toolsets": {
|
||||
"all": "すべてのツール",
|
||||
"coreOnly": "基本ツールのみ",
|
||||
"some_one": "基本 + {{count}} グループ",
|
||||
"some_other": "基本 + {{count}} グループ",
|
||||
"title": "エージェントに見せるツール",
|
||||
"description": "エージェントは見えるすべてのツールの説明を読み込みます。必要なグループだけを選ぶと、起動が速くなり作業の余裕も増えます。接続済みのアプリはすぐに更新されます。",
|
||||
"core": "プロファイル、ブラウザー操作、あなたと連携するためのツールは常に有効です。",
|
||||
"failed": "{{names}} を更新できませんでした。そのアプリから Donut を削除して、もう一度追加してください。",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "整理",
|
||||
"description": "グループの作成と変更、プロファイルのインポート"
|
||||
},
|
||||
"network": {
|
||||
"label": "ネットワーク",
|
||||
"description": "プロキシ、VPN、IP の切り替え、DNS ルール"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "フィンガープリント",
|
||||
"description": "プロファイルのデバイスの確認と変更"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "拡張機能",
|
||||
"description": "拡張機能と拡張機能グループ"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookie",
|
||||
"description": "Cookie のインポートと Cookie ボット"
|
||||
},
|
||||
"sync": {
|
||||
"label": "同期",
|
||||
"description": "チームロックとシンクロナイザー"
|
||||
},
|
||||
"remote": {
|
||||
"label": "リモートフリート",
|
||||
"description": "リモートフリートでプロファイルを実行"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "レシピ",
|
||||
"description": "エージェントが繰り返せる保存済みのフロー"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "ローカル API",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "テンプレート名は60文字以内にしてください。",
|
||||
"templateLimitReached": "保存できるテンプレートは最大100件です。先に1件削除してください。",
|
||||
"bulkCreateCountInvalid": "一度に作成できるプロファイルは1〜{{max}}件です。",
|
||||
"bulkCreateBusy": "既にプロファイルを作成中です。完了するまでお待ちください。"
|
||||
"bulkCreateBusy": "既にプロファイルを作成中です。完了するまでお待ちください。",
|
||||
"mcpToolsetUnknown": "このツールグループは存在しません。"
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "プロファイル",
|
||||
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "CLI",
|
||||
"editor": "에디터",
|
||||
"editorExt": "에디터 확장"
|
||||
},
|
||||
"onThisComputer": "이 컴퓨터",
|
||||
"addToAll_one": "{{count}}개 모두에 추가",
|
||||
"addToAll_other": "{{count}}개 모두에 추가",
|
||||
"addedToMany_one": "앱 {{count}}개에 Donut을 추가했습니다",
|
||||
"addedToMany_other": "앱 {{count}}개에 Donut을 추가했습니다",
|
||||
"addFailed": "{{names}}에 Donut을 추가하지 못했습니다.",
|
||||
"noneFound": "이 컴퓨터에서 AI 앱을 찾지 못했습니다. 아래 앱 중 어디에나 Donut을 추가할 수 있습니다.",
|
||||
"showMore_one": "클라이언트 {{count}}개 더 보기",
|
||||
"showMore_other": "클라이언트 {{count}}개 더 보기",
|
||||
"showFewer": "클라이언트 적게 보기",
|
||||
"toolsets": {
|
||||
"all": "모든 도구",
|
||||
"coreOnly": "기본 도구만",
|
||||
"some_one": "기본 + {{count}}개 그룹",
|
||||
"some_other": "기본 + {{count}}개 그룹",
|
||||
"title": "에이전트가 보는 도구",
|
||||
"description": "에이전트는 볼 수 있는 모든 도구의 설명을 불러옵니다. 필요한 그룹만 고르면 더 빨리 시작하고 작업할 여유도 늘어납니다. 연결된 앱은 바로 업데이트됩니다.",
|
||||
"core": "프로필, 브라우저 제어, 사용자와 함께 작업하는 도구는 항상 켜져 있습니다.",
|
||||
"failed": "{{names}}을(를) 업데이트하지 못했습니다. 그곳에서 Donut을 제거한 뒤 다시 추가하세요.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "정리",
|
||||
"description": "그룹 만들기 및 변경, 프로필 가져오기"
|
||||
},
|
||||
"network": {
|
||||
"label": "네트워크",
|
||||
"description": "프록시, VPN, IP 교체, DNS 규칙"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "핑거프린트",
|
||||
"description": "프로필의 기기 확인 및 변경"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "확장 프로그램",
|
||||
"description": "확장 프로그램과 확장 프로그램 그룹"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "쿠키",
|
||||
"description": "쿠키 가져오기와 쿠키 봇"
|
||||
},
|
||||
"sync": {
|
||||
"label": "동기화",
|
||||
"description": "팀 잠금과 동기화 도구"
|
||||
},
|
||||
"remote": {
|
||||
"label": "원격 플릿",
|
||||
"description": "원격 플릿에서 프로필 실행"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "레시피",
|
||||
"description": "에이전트가 반복할 수 있는 저장된 흐름"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "로컬 API",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "템플릿 이름은 최대 60자까지 가능합니다.",
|
||||
"templateLimitReached": "템플릿은 최대 100개까지 보관할 수 있습니다. 먼저 하나를 삭제하세요.",
|
||||
"bulkCreateCountInvalid": "한 번에 1개부터 {{max}}개까지 프로필을 만들 수 있습니다.",
|
||||
"bulkCreateBusy": "이미 프로필을 만드는 중입니다. 끝날 때까지 기다리세요."
|
||||
"bulkCreateBusy": "이미 프로필을 만드는 중입니다. 끝날 때까지 기다리세요.",
|
||||
"mcpToolsetUnknown": "이 도구 그룹은 없습니다."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "프로필",
|
||||
|
||||
@@ -869,6 +869,64 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Editor",
|
||||
"editorExt": "Extensão de editor"
|
||||
},
|
||||
"onThisComputer": "Neste computador",
|
||||
"addToAll_one": "Adicionar aos {{count}}",
|
||||
"addToAll_many": "Adicionar aos {{count}}",
|
||||
"addToAll_other": "Adicionar aos {{count}}",
|
||||
"addedToMany_one": "Donut adicionado a {{count}} app",
|
||||
"addedToMany_many": "Donut adicionado a {{count}} apps",
|
||||
"addedToMany_other": "Donut adicionado a {{count}} apps",
|
||||
"addFailed": "Não foi possível adicionar o Donut a {{names}}.",
|
||||
"noneFound": "Nenhum app de IA encontrado neste computador. Você ainda pode adicionar o Donut a qualquer um destes.",
|
||||
"showMore_one": "Mostrar mais {{count}} cliente",
|
||||
"showMore_many": "Mostrar mais {{count}} clientes",
|
||||
"showMore_other": "Mostrar mais {{count}} clientes",
|
||||
"showFewer": "Mostrar menos clientes",
|
||||
"toolsets": {
|
||||
"all": "Todas as ferramentas",
|
||||
"coreOnly": "Só ferramentas básicas",
|
||||
"some_one": "Básicas + {{count}} grupo",
|
||||
"some_many": "Básicas + {{count}} grupos",
|
||||
"some_other": "Básicas + {{count}} grupos",
|
||||
"title": "Ferramentas que os agentes veem",
|
||||
"description": "Um agente carrega uma descrição de cada ferramenta que vê. Escolha só os grupos de que ele precisa: ele começa mais rápido e tem mais espaço para trabalhar. Os apps conectados são atualizados na hora.",
|
||||
"core": "Perfis, controle do navegador e as ferramentas que mantêm você informado ficam sempre ativos.",
|
||||
"failed": "Não foi possível atualizar {{names}}. Remova o Donut lá e adicione-o de novo.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Organizar",
|
||||
"description": "Criar e alterar grupos, importar perfis"
|
||||
},
|
||||
"network": {
|
||||
"label": "Rede",
|
||||
"description": "Proxies, VPNs, rotação de IP e regras de DNS"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Impressão digital",
|
||||
"description": "Ler e alterar o dispositivo de um perfil"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Extensões",
|
||||
"description": "Extensões e grupos de extensões"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookies",
|
||||
"description": "Importação de cookies e o bot de cookies"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Sincronização",
|
||||
"description": "Bloqueios de equipe e o sincronizador"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Frota remota",
|
||||
"description": "Executar perfis na frota remota"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Receitas",
|
||||
"description": "Fluxos salvos que um agente pode repetir"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "API local",
|
||||
@@ -2581,7 +2639,8 @@
|
||||
"templateNameTooLong": "O nome de um modelo pode ter no máximo 60 caracteres.",
|
||||
"templateLimitReached": "Você pode manter no máximo 100 modelos. Exclua um primeiro.",
|
||||
"bulkCreateCountInvalid": "Você pode criar de 1 a {{max}} perfis de cada vez.",
|
||||
"bulkCreateBusy": "Já há perfis sendo criados. Aguarde até terminar."
|
||||
"bulkCreateBusy": "Já há perfis sendo criados. Aguarde até terminar.",
|
||||
"mcpToolsetUnknown": "Este grupo de ferramentas não existe."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Perfis",
|
||||
|
||||
@@ -874,6 +874,68 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Редактор",
|
||||
"editorExt": "Расширение редактора"
|
||||
},
|
||||
"onThisComputer": "На этом компьютере",
|
||||
"addToAll_one": "Добавить во все {{count}}",
|
||||
"addToAll_few": "Добавить во все {{count}}",
|
||||
"addToAll_many": "Добавить во все {{count}}",
|
||||
"addToAll_other": "Добавить во все {{count}}",
|
||||
"addedToMany_one": "Donut добавлен в {{count}} приложение",
|
||||
"addedToMany_few": "Donut добавлен в {{count}} приложения",
|
||||
"addedToMany_many": "Donut добавлен в {{count}} приложений",
|
||||
"addedToMany_other": "Donut добавлен в {{count}} приложений",
|
||||
"addFailed": "Не удалось добавить Donut в {{names}}.",
|
||||
"noneFound": "На этом компьютере не найдено ИИ-приложений. Donut всё равно можно добавить в любое из них.",
|
||||
"showMore_one": "Показать ещё {{count}} клиент",
|
||||
"showMore_few": "Показать ещё {{count}} клиента",
|
||||
"showMore_many": "Показать ещё {{count}} клиентов",
|
||||
"showMore_other": "Показать ещё {{count}} клиентов",
|
||||
"showFewer": "Показать меньше клиентов",
|
||||
"toolsets": {
|
||||
"all": "Все инструменты",
|
||||
"coreOnly": "Только основные инструменты",
|
||||
"some_one": "Основные + {{count}} группа",
|
||||
"some_few": "Основные + {{count}} группы",
|
||||
"some_many": "Основные + {{count}} групп",
|
||||
"some_other": "Основные + {{count}} групп",
|
||||
"title": "Инструменты, которые видят агенты",
|
||||
"description": "Агент загружает описание каждого инструмента, который видит. Выберите только нужные группы — он будет запускаться быстрее и получит больше места для работы. Подключённые приложения обновятся сразу.",
|
||||
"core": "Профили, управление браузером и инструменты, которые держат вас в курсе, включены всегда.",
|
||||
"failed": "Не удалось обновить {{names}}. Удалите там Donut и добавьте его снова.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Организация",
|
||||
"description": "Создание и изменение групп, импорт профилей"
|
||||
},
|
||||
"network": {
|
||||
"label": "Сеть",
|
||||
"description": "Прокси, VPN, смена IP и правила DNS"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Отпечаток",
|
||||
"description": "Просмотр и изменение устройства профиля"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Расширения",
|
||||
"description": "Расширения и группы расширений"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookie",
|
||||
"description": "Импорт cookie и cookie-бот"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Синхронизация",
|
||||
"description": "Командные блокировки и синхронизатор"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Удалённый парк",
|
||||
"description": "Запуск профилей в удалённом парке"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Рецепты",
|
||||
"description": "Сохранённые сценарии, которые агент может повторить"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "Локальный API",
|
||||
@@ -2597,7 +2659,8 @@
|
||||
"templateNameTooLong": "Название шаблона может содержать не более 60 символов.",
|
||||
"templateLimitReached": "Можно хранить не более 100 шаблонов. Сначала удалите один.",
|
||||
"bulkCreateCountInvalid": "За один раз можно создать от 1 до {{max}} профилей.",
|
||||
"bulkCreateBusy": "Профили уже создаются. Дождитесь окончания."
|
||||
"bulkCreateBusy": "Профили уже создаются. Дождитесь окончания.",
|
||||
"mcpToolsetUnknown": "Такой группы инструментов нет."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Профили",
|
||||
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "Komut satırı",
|
||||
"editor": "Editör",
|
||||
"editorExt": "Editör uzantısı"
|
||||
},
|
||||
"onThisComputer": "Bu bilgisayarda",
|
||||
"addToAll_one": "{{count}} uygulamanın hepsine ekle",
|
||||
"addToAll_other": "{{count}} uygulamanın hepsine ekle",
|
||||
"addedToMany_one": "Donut {{count}} uygulamaya eklendi",
|
||||
"addedToMany_other": "Donut {{count}} uygulamaya eklendi",
|
||||
"addFailed": "Donut şuraya eklenemedi: {{names}}.",
|
||||
"noneFound": "Bu bilgisayarda yapay zeka uygulaması bulunamadı. Donut'u yine de bunlardan herhangi birine ekleyebilirsiniz.",
|
||||
"showMore_one": "{{count}} istemci daha göster",
|
||||
"showMore_other": "{{count}} istemci daha göster",
|
||||
"showFewer": "Daha az istemci göster",
|
||||
"toolsets": {
|
||||
"all": "Tüm araçlar",
|
||||
"coreOnly": "Yalnızca temel araçlar",
|
||||
"some_one": "Temel + {{count}} grup",
|
||||
"some_other": "Temel + {{count}} grup",
|
||||
"title": "Ajanların gördüğü araçlar",
|
||||
"description": "Bir ajan, gördüğü her aracın açıklamasını yükler. Yalnızca ihtiyaç duyduğu grupları seçin; daha hızlı başlar ve çalışmak için daha fazla yeri olur. Bağlı uygulamalar hemen güncellenir.",
|
||||
"core": "Profiller, tarayıcı denetimi ve sizi bilgilendiren araçlar her zaman açıktır.",
|
||||
"failed": "{{names}} güncellenemedi. Donut'u oradan kaldırıp yeniden ekleyin.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Düzenle",
|
||||
"description": "Grup oluşturma ve değiştirme, profil içe aktarma"
|
||||
},
|
||||
"network": {
|
||||
"label": "Ağ",
|
||||
"description": "Proxy'ler, VPN'ler, IP değiştirme ve DNS kuralları"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Parmak izi",
|
||||
"description": "Bir profilin cihazını okuma ve değiştirme"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Uzantılar",
|
||||
"description": "Uzantılar ve uzantı grupları"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Çerezler",
|
||||
"description": "Çerez içe aktarma ve çerez botu"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Eşitleme",
|
||||
"description": "Ekip kilitleri ve eşitleyici"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Uzak filo",
|
||||
"description": "Profilleri uzak filoda çalıştırma"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Tarifler",
|
||||
"description": "Bir ajanın tekrarlayabileceği kayıtlı akışlar"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "Yerel API",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "Şablon adı en fazla 60 karakter olabilir.",
|
||||
"templateLimitReached": "En fazla 100 şablon tutabilirsiniz. Önce birini silin.",
|
||||
"bulkCreateCountInvalid": "Bir seferde 1 ile {{max}} arasında profil oluşturabilirsiniz.",
|
||||
"bulkCreateBusy": "Profiller zaten oluşturuluyor. Bitmesini bekleyin."
|
||||
"bulkCreateBusy": "Profiller zaten oluşturuluyor. Bitmesini bekleyin.",
|
||||
"mcpToolsetUnknown": "Bu araç grubu yok."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profiller",
|
||||
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "CLI",
|
||||
"editor": "Trình soạn thảo",
|
||||
"editorExt": "Tiện ích editor"
|
||||
},
|
||||
"onThisComputer": "Trên máy tính này",
|
||||
"addToAll_one": "Thêm vào cả {{count}}",
|
||||
"addToAll_other": "Thêm vào cả {{count}}",
|
||||
"addedToMany_one": "Đã thêm Donut vào {{count}} ứng dụng",
|
||||
"addedToMany_other": "Đã thêm Donut vào {{count}} ứng dụng",
|
||||
"addFailed": "Không thể thêm Donut vào {{names}}.",
|
||||
"noneFound": "Không tìm thấy ứng dụng AI nào trên máy tính này. Bạn vẫn có thể thêm Donut vào bất kỳ ứng dụng nào dưới đây.",
|
||||
"showMore_one": "Hiện thêm {{count}} ứng dụng khách",
|
||||
"showMore_other": "Hiện thêm {{count}} ứng dụng khách",
|
||||
"showFewer": "Hiện ít ứng dụng khách hơn",
|
||||
"toolsets": {
|
||||
"all": "Tất cả công cụ",
|
||||
"coreOnly": "Chỉ công cụ cơ bản",
|
||||
"some_one": "Cơ bản + {{count}} nhóm",
|
||||
"some_other": "Cơ bản + {{count}} nhóm",
|
||||
"title": "Công cụ mà tác tử thấy",
|
||||
"description": "Tác tử tải mô tả của mọi công cụ mà nó thấy. Chỉ chọn những nhóm nó cần để nó khởi động nhanh hơn và có thêm chỗ để làm việc. Các ứng dụng đã kết nối được cập nhật ngay.",
|
||||
"core": "Profile, điều khiển trình duyệt và các công cụ giữ bạn trong vòng làm việc luôn được bật.",
|
||||
"failed": "Không thể cập nhật {{names}}. Hãy gỡ Donut ở đó rồi thêm lại.",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "Sắp xếp",
|
||||
"description": "Tạo và đổi nhóm, nhập profile"
|
||||
},
|
||||
"network": {
|
||||
"label": "Mạng",
|
||||
"description": "Proxy, VPN, đổi IP và quy tắc DNS"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "Dấu vân tay",
|
||||
"description": "Xem và đổi thiết bị của profile"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "Tiện ích",
|
||||
"description": "Tiện ích và nhóm tiện ích"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookie",
|
||||
"description": "Nhập cookie và bot cookie"
|
||||
},
|
||||
"sync": {
|
||||
"label": "Đồng bộ",
|
||||
"description": "Khóa nhóm và bộ đồng bộ"
|
||||
},
|
||||
"remote": {
|
||||
"label": "Đội máy từ xa",
|
||||
"description": "Chạy profile trên đội máy từ xa"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "Công thức",
|
||||
"description": "Các luồng đã lưu mà tác tử có thể lặp lại"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "API cục bộ",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "Tên mẫu có tối đa 60 ký tự.",
|
||||
"templateLimitReached": "Bạn chỉ có thể giữ tối đa 100 mẫu. Hãy xóa một mẫu trước.",
|
||||
"bulkCreateCountInvalid": "Mỗi lần bạn có thể tạo từ 1 đến {{max}} profile.",
|
||||
"bulkCreateBusy": "Đang có profile được tạo. Hãy chờ đến khi xong."
|
||||
"bulkCreateBusy": "Đang có profile được tạo. Hãy chờ đến khi xong.",
|
||||
"mcpToolsetUnknown": "Nhóm công cụ này không tồn tại."
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "Profile",
|
||||
|
||||
@@ -864,6 +864,60 @@
|
||||
"cli": "CLI",
|
||||
"editor": "编辑器",
|
||||
"editorExt": "编辑器扩展"
|
||||
},
|
||||
"onThisComputer": "此电脑上",
|
||||
"addToAll_one": "全部添加({{count}} 个)",
|
||||
"addToAll_other": "全部添加({{count}} 个)",
|
||||
"addedToMany_one": "已将 Donut 添加到 {{count}} 个应用",
|
||||
"addedToMany_other": "已将 Donut 添加到 {{count}} 个应用",
|
||||
"addFailed": "无法将 Donut 添加到 {{names}}。",
|
||||
"noneFound": "此电脑上未找到 AI 应用。你仍可以将 Donut 添加到以下任一应用。",
|
||||
"showMore_one": "再显示 {{count}} 个客户端",
|
||||
"showMore_other": "再显示 {{count}} 个客户端",
|
||||
"showFewer": "少显示一些客户端",
|
||||
"toolsets": {
|
||||
"all": "全部工具",
|
||||
"coreOnly": "仅基础工具",
|
||||
"some_one": "基础 + {{count}} 个分组",
|
||||
"some_other": "基础 + {{count}} 个分组",
|
||||
"title": "代理可见的工具",
|
||||
"description": "代理会加载它能看到的每个工具的说明。只选择它需要的分组,它就能更快启动,也有更多空间工作。已连接的应用会立即更新。",
|
||||
"core": "配置文件、浏览器控制以及让你随时掌握进展的工具始终开启。",
|
||||
"failed": "无法更新 {{names}}。请在其中移除 Donut 后重新添加。",
|
||||
"names": {
|
||||
"organize": {
|
||||
"label": "整理",
|
||||
"description": "创建和修改分组,导入配置文件"
|
||||
},
|
||||
"network": {
|
||||
"label": "网络",
|
||||
"description": "代理、VPN、IP 轮换和 DNS 规则"
|
||||
},
|
||||
"fingerprint": {
|
||||
"label": "指纹",
|
||||
"description": "查看和修改配置文件的设备"
|
||||
},
|
||||
"extensions": {
|
||||
"label": "扩展",
|
||||
"description": "扩展和扩展分组"
|
||||
},
|
||||
"cookies": {
|
||||
"label": "Cookie",
|
||||
"description": "Cookie 导入和 Cookie 机器人"
|
||||
},
|
||||
"sync": {
|
||||
"label": "同步",
|
||||
"description": "团队锁和同步器"
|
||||
},
|
||||
"remote": {
|
||||
"label": "远程机群",
|
||||
"description": "在远程机群上运行配置文件"
|
||||
},
|
||||
"recipes": {
|
||||
"label": "配方",
|
||||
"description": "代理可以重复执行的已保存流程"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tabApi": "本地 API",
|
||||
@@ -2565,7 +2619,8 @@
|
||||
"templateNameTooLong": "模板名称最多 60 个字符。",
|
||||
"templateLimitReached": "最多可保留 100 个模板。请先删除一个。",
|
||||
"bulkCreateCountInvalid": "每次可以创建 1 到 {{max}} 个配置文件。",
|
||||
"bulkCreateBusy": "已在创建配置文件。请等待完成。"
|
||||
"bulkCreateBusy": "已在创建配置文件。请等待完成。",
|
||||
"mcpToolsetUnknown": "该工具分组不存在。"
|
||||
},
|
||||
"rail": {
|
||||
"profiles": "配置文件",
|
||||
|
||||
@@ -121,6 +121,7 @@ export type BackendErrorCode =
|
||||
| "WAYFERN_TERMS_REQUIRED"
|
||||
| "API_PORT_UNAVAILABLE"
|
||||
| "MCP_AGENT_UNKNOWN"
|
||||
| "MCP_TOOLSET_UNKNOWN"
|
||||
| "MCP_AGENT_INSTALL_FAILED"
|
||||
| "MCP_AGENT_REMOVE_FAILED"
|
||||
| "MCP_REMOTE_REQUIRES_SIGN_IN"
|
||||
@@ -635,6 +636,8 @@ export function translateBackendError(t: TFunction, err: unknown): string {
|
||||
return t("backendErrors.mcpRemoteKeyLimit");
|
||||
case "MCP_REMOTE_KEY_UNAVAILABLE":
|
||||
return t("backendErrors.mcpRemoteKeyUnavailable");
|
||||
case "MCP_TOOLSET_UNKNOWN":
|
||||
return t("backendErrors.mcpToolsetUnknown");
|
||||
case "MCP_AGENT_UNKNOWN":
|
||||
return t("backendErrors.mcpAgentUnknown");
|
||||
case "MCP_AGENT_INSTALL_FAILED":
|
||||
|
||||
@@ -16,6 +16,23 @@ export function fxExportLine(key: string | null | undefined): string | null {
|
||||
|
||||
export type AgentCategory = "desktop-app" | "cli" | "editor" | "editor-ext";
|
||||
|
||||
/**
|
||||
* The tool groups an agent can list besides the core tools, in display
|
||||
* order. The backend checks every name; `["core"]` is the core tools only and
|
||||
* an empty list is every tool.
|
||||
*/
|
||||
export const OPTIONAL_TOOLSETS = [
|
||||
"organize",
|
||||
"network",
|
||||
"fingerprint",
|
||||
"extensions",
|
||||
"cookies",
|
||||
"sync",
|
||||
"remote",
|
||||
"recipes",
|
||||
] as const;
|
||||
export const CORE_TOOLSET = "core";
|
||||
|
||||
export interface McpAgentInfo {
|
||||
id: string;
|
||||
display_name: string;
|
||||
|
||||
Reference in new issue
Block a user