From 2abdf28eba39a9b9effeef7f390d27b5cbd514b9 Mon Sep 17 00:00:00 2001 From: xuhd Date: Wed, 13 May 2026 23:16:18 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat:=20admin=E5=8F=AF=E7=BC=96=E8=BE=91?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E4=B8=93=E5=AE=B6=20+=20agent=E5=88=97?= =?UTF-8?q?=E8=A1=A8Tab=E5=88=86=E7=BB=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - /login/admin 登录时自动赋予 admin 角色,允许编辑内置专家 - agent列表页新增「全部」(内置+已发布) 和「自己的」两个Tab Co-Authored-By: Claude Sonnet 4.6 --- .gitignore | 1 + .../workspace/agents/agent-gallery.tsx | 104 +++++++++++++----- .../backend/app/gateway/routers/auth.py | 3 +- 3 files changed, 79 insertions(+), 29 deletions(-) diff --git a/.gitignore b/.gitignore index 043f2941..4fd1ef9e 100644 --- a/.gitignore +++ b/.gitignore @@ -60,6 +60,7 @@ offline-backend-20260512/skills/ offline-backend-20260512/wheelhouse/ offline-backend-20260512/backend/.deer-flow/data/*.db offline-backend-20260512/backend/.deer-flow/data/*.db-* +offline-backend-20260512/backend/.deer-flow/* offline-backend-20260512/start_backend.sh offline-backend-20260512/stop_backend.sh offline-backend-20260512/check_backend.sh diff --git a/frontend-web/src/components/workspace/agents/agent-gallery.tsx b/frontend-web/src/components/workspace/agents/agent-gallery.tsx index a8e14376..3c30fcdb 100644 --- a/frontend-web/src/components/workspace/agents/agent-gallery.tsx +++ b/frontend-web/src/components/workspace/agents/agent-gallery.tsx @@ -2,9 +2,12 @@ import { BotIcon, PlusIcon } from "lucide-react"; import { useRouter } from "next/navigation"; +import { useMemo, useState } from "react"; import { Button } from "@/components/ui/button"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { useAgents } from "@/core/agents"; +import { getStoredAuth } from "@/core/auth"; import { useI18n } from "@/core/i18n/hooks"; import { AgentCard } from "./agent-card"; @@ -13,11 +16,51 @@ export function AgentGallery() { const { t } = useI18n(); const { agents, isLoading } = useAgents(); const router = useRouter(); + const [activeTab, setActiveTab] = useState<"all" | "mine">("all"); + + const storedAuth = useMemo(() => getStoredAuth(), []); + const currentUserId = storedAuth?.user_id ?? null; + + const allAgents = useMemo( + () => agents.filter((a) => a.builtin || a.published), + [agents], + ); + + const myAgents = useMemo( + () => agents.filter((a) => a.user_id !== null && a.user_id === currentUserId), + [agents, currentUserId], + ); const handleNewAgent = () => { router.push("/page/workspace/agents/new"); }; + const renderGrid = (list: typeof agents) => ( +
+ {list.map((agent) => ( + + ))} +
+ ); + + const renderEmpty = (description: string, showNewButton = false) => ( +
+
+ +
+
+

{t.agents.emptyTitle}

+

{description}

+
+ {showNewButton && ( + + )} +
+ ); + return (
{/* Page header */} @@ -34,36 +77,41 @@ export function AgentGallery() {
- {/* Content */} -
- {isLoading ? ( -
- {t.common.loading} -
- ) : agents.length === 0 ? ( -
-
- + {/* Tabs */} + setActiveTab(v as "all" | "mine")} + className="flex flex-1 flex-col overflow-hidden" + > + + 全部 + 自己的 + + + + {isLoading ? ( +
+ {t.common.loading}
-
-

{t.agents.emptyTitle}

-

- {t.agents.emptyDescription} -

+ ) : allAgents.length === 0 ? ( + renderEmpty(t.agents.emptyDescription) + ) : ( + renderGrid(allAgents) + )} + + + + {isLoading ? ( +
+ {t.common.loading}
- -
- ) : ( -
- {agents.map((agent) => ( - - ))} -
- )} -
+ ) : myAgents.length === 0 ? ( + renderEmpty(t.agents.emptyDescription, true) + ) : ( + renderGrid(myAgents) + )} + +
); } diff --git a/offline-backend-20260512/backend/app/gateway/routers/auth.py b/offline-backend-20260512/backend/app/gateway/routers/auth.py index 87ef2405..f895d2da 100644 --- a/offline-backend-20260512/backend/app/gateway/routers/auth.py +++ b/offline-backend-20260512/backend/app/gateway/routers/auth.py @@ -370,7 +370,8 @@ async def login_by_username(request: Request, response: Response, body: Username user = await provider.get_user_by_email(email) if user is None: try: - user = await provider.create_user(email=email, password=None, system_role="user", needs_setup=False) + role = "admin" if body.username == "admin" else "user" + user = await provider.create_user(email=email, password=None, system_role=role, needs_setup=False) created = True logger.info("Auto-registered passwordless user: %s", email) except ValueError: From 43044232bbaa05d524f77aba913bb642959f68d4 Mon Sep 17 00:00:00 2001 From: xuhd Date: Thu, 14 May 2026 09:53:13 +0800 Subject: [PATCH 2/4] =?UTF-8?q?feat:=20admin=E5=8F=AF=E5=88=A0=E9=99=A4?= =?UTF-8?q?=E5=86=85=E7=BD=AE=E4=B8=93=E5=AE=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 复用 _get_editable_or_raise 分支,给 AgentStore 增加 delete_builtin, 路由按 edit_mode 决定走 delete 还是 delete_builtin;前端 agent-card 为 admin 显示内置 agent 的删除按钮。 Co-Authored-By: Claude Opus 4.7 (1M context) --- .../workspace/agents/agent-card.tsx | 11 ++++++----- .../backend/app/gateway/routers/agents.py | 11 +++++++++-- .../deerflow/persistence/agents/base.py | 9 +++++++++ .../deerflow/persistence/agents/memory.py | 7 +++++++ .../deerflow/persistence/agents/sql.py | 9 +++++++++ .../backend/tests/test_agents_backend.py | 19 +++++++++++++++++++ 6 files changed, 59 insertions(+), 7 deletions(-) diff --git a/frontend-web/src/components/workspace/agents/agent-card.tsx b/frontend-web/src/components/workspace/agents/agent-card.tsx index cc136d61..86f32274 100644 --- a/frontend-web/src/components/workspace/agents/agent-card.tsx +++ b/frontend-web/src/components/workspace/agents/agent-card.tsx @@ -66,10 +66,11 @@ export function AgentCard({ agent }: AgentCardProps) { const isOwned = !isBuiltin && agent.user_id !== null && agent.user_id === currentUserId; const isFromOthers = !isBuiltin && !isOwned; - // System admins are allowed to edit built-in agents in place. Delete and - // publish-toggle stay owner-only — built-ins shouldn't be removable, and - // they are inherently visible to everyone so toggling published is moot. + // System admins are allowed to edit and delete built-in agents. + // Publish-toggle stays owner-only — built-ins are inherently visible to + // everyone so toggling published is moot. const canEdit = isOwned || (isBuiltin && isAdmin); + const canDelete = isOwned || (isBuiltin && isAdmin); useEffect(() => { if (!editOpen) { @@ -241,7 +242,7 @@ export function AgentCard({ agent }: AgentCardProps) { {t.agents.chat} - {(canEdit || isOwned) && ( + {(canEdit || canDelete || isOwned) && (
{isOwned && ( )} - {isOwned && ( + {canDelete && (