Răsfoiți Sursa

feat: 文件管理的常用目录不再列出会话目录

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Jerry Liang 1 săptămână în urmă
părinte
comite
8b29d11c30

+ 3 - 8
apps/server/src/services/file-manager.ts

@@ -3,7 +3,7 @@
 // 路径以"位置 + 相对路径"表示;解析符号链接后必须仍在该位置之内
 import fs from 'node:fs'
 import path from 'node:path'
-import { desc, eq } from 'drizzle-orm'
+import { eq } from 'drizzle-orm'
 import { zipSync } from 'fflate'
 import type { AppSettings, FsEntryDto, FsRootDto, FsShortcutDto, KnowledgeRootDto } from '@agent-server/shared'
 import { config } from '../config'
@@ -34,15 +34,10 @@ export class FileManagerService {
     return this.rootList().map(r => ({ id: r.id, name: r.name, kind: r.kind, writable: r.writable, description: r.description, exists: fs.existsSync(r.path) }))
   }
 
-  /** 常用目录:工作区根目录、最近的会话、各资料库、服务目录中的常用子目录 */
+  /** 常用目录:各资料库、服务目录中的常用子目录(会话工作区在"位置"里,不重复列出) */
   shortcuts(): FsShortcutDto[] {
     const s = this.settings()
-    const out: FsShortcutDto[] = [{ label: '全部会话工作区', rootId: 'workspaces', path: '', icon: 'folder-library' }]
-    const recent = this.db.select({ id: sessions.id, title: sessions.title, dir: sessions.workspaceDir }).from(sessions).orderBy(desc(sessions.updatedAt)).limit(8).all()
-    for (const r of recent) {
-      if (!isWithin(r.dir, s.paths.workspaceRoot)) continue
-      out.push({ label: r.title || '新会话', rootId: 'workspaces', path: path.relative(s.paths.workspaceRoot, r.dir), icon: 'comment-discussion' })
-    }
+    const out: FsShortcutDto[] = []
     for (const k of this.knowledge()) if (k.enabled) out.push({ label: k.name, rootId: `kb:${k.id}`, path: '', icon: 'library' })
     const server = realpathLoose(config.dataDir)
     const dirs: [string, string, string][] = [

+ 3 - 3
apps/server/test/file-manager.test.ts

@@ -30,12 +30,12 @@ beforeAll(async () => {
 const kbRoot = () => app.fileManager.roots().find(r => r.kind === 'knowledge')!.id
 
 describe('后台文件管理', () => {
-  it('位置:会话工作区与资料库可写,整个服务目录只读;常用目录包含最近的会话与资料库', () => {
+  it('位置:会话工作区与资料库可写,整个服务目录只读;常用目录包含资料库与服务目录的常用子目录,不含会话', () => {
     const roots = app.fileManager.roots()
     expect(roots.map(r => [r.id.split(':')[0], r.writable])).toEqual([['workspaces', true], ['kb', true], ['server', false]])
     const sc = app.fileManager.shortcuts()
-    expect(sc[0]).toMatchObject({ rootId: 'workspaces', path: '' })
-    expect(sc.some(s => s.rootId === 'workspaces' && s.path === sid)).toBe(true)
+    expect(sc.some(s => s.rootId === 'workspaces')).toBe(false)
+    expect(sc).toEqual(expect.arrayContaining([expect.objectContaining({ label: '服务数据', rootId: 'server', path: 'data' })]))
     expect(sc.some(s => s.label === '账单')).toBe(true)
   })
 

+ 1 - 1
docs/design/09-admin-ui.md

@@ -26,7 +26,7 @@
 | 🗂 会话 | 全部会话(所有平台/使用者) | 只读回放(时间线 + 原始事件 + 文件),可"在 Playground 中继续" |
 | 🧩 Profiles | Profile 列表 | 表单编辑:引擎、模型、提示词(Monaco 编辑器)、权限、工具、技能、资料库、**文件访问策略**(模式、可读/可写/禁止路径,右侧显示合并后的生效预览,见 06 §4.3);版本历史 |
 | 📚 资料库 | 资料目录列表 | 名称、路径(带目录选择器)、说明、读写属性、启用;可见性检测结果;被哪些 Profile 使用;索引状态(M3) |
-| 🗃 文件管理 | 位置 + 常用目录(全部会话工作区、最近 8 个会话、各资料库、本地技能目录、已安装技能、Python 运行环境、服务数据、日志) | 以管理员身份浏览与管理文件:会话工作区与资料库可写(新建文件/目录、上传与拖放、重命名/移动、删除、目录打包下载);**整个服务目录**(`/srv/agent-server`:程序、运行环境、技能、数据、日志、引擎 home)只读浏览。文本文件用 Monaco 编辑或查看(Ctrl/⌘+S 保存,打开后被改动则拒绝覆盖),图片预览,其他文件下载。登录凭证与密钥(内置全局禁止项中的 `~/.claude/.credentials.json`、`~/.codex/auth.json`、`~/.ssh` 等)不显示;数据库及其备份能看到但不能读取或下载(含 MCP 服务密钥等只写不读的配置)。路径不能越出位置,外部符号链接不跟随;会话目录本身不能在这里删除或移动;修改会话文件后同步该会话的文件记录。实现:`apps/server/src/services/file-manager.ts`、`/admin/v1/fs/*` |
+| 🗃 文件管理 | 位置 + 常用目录(各资料库、本地技能目录、已安装技能、Python 运行环境、服务数据、日志) | 以管理员身份浏览与管理文件:会话工作区与资料库可写(新建文件/目录、上传与拖放、重命名/移动、删除、目录打包下载);**整个服务目录**(`/srv/agent-server`:程序、运行环境、技能、数据、日志、引擎 home)只读浏览。文本文件用 Monaco 编辑或查看(Ctrl/⌘+S 保存,打开后被改动则拒绝覆盖),图片预览,其他文件下载。登录凭证与密钥(内置全局禁止项中的 `~/.claude/.credentials.json`、`~/.codex/auth.json`、`~/.ssh` 等)不显示;数据库及其备份能看到但不能读取或下载(含 MCP 服务密钥等只写不读的配置)。路径不能越出位置,外部符号链接不跟随;会话目录本身不能在这里删除或移动;修改会话文件后同步该会话的文件记录。实现:`apps/server/src/services/file-manager.ts`、`/admin/v1/fs/*` |
 | 🔑 引擎状态 | Claude / Codex | 登录状态、账户摘要、额度、CLI 路径与版本、生效的工具清单(用于确认个人插件已被禁用);未登录时提示在终端执行 `claude` / `codex login` |
 | 🗝 API Keys | Key 列表 | 签发(明文只显示一次)、吊销、最近使用时间 |
 | 🔌 MCP 服务 | — | 内置工具(只读展示);第三方服务:添加远程 HTTP 或本地进程(沙箱)服务、测试连接、刷新工具、逐个工具设置策略(直接调用 / 跟随权限模式 / 关闭)、启停、删除;密钥只写不读;显示使用它的 Profile 与连接状态(ADR-0021) |