diff --git a/apps/server/README.md b/apps/server/README.md index bf5fd780..d32662ae 100644 --- a/apps/server/README.md +++ b/apps/server/README.md @@ -1,6 +1,6 @@ # @umt/server -服务进程(Deno):Hono HTTP API(前缀 `/api/v1`,OpenAPI 见 `/api/openapi.json`、`/api/docs`)、SSE 事件流与 worker 入口。`--role api|worker|all` 决定本进程承担哪部分,默认 `all` 单进程运行。业务规则不写在这里,只做编排、鉴权、审计与协议适配。 +服务进程(Deno):Hono HTTP API(前缀 `/api/v1`,OpenAPI 见 `/api/openapi.json`、`/api/docs`)、SSE 事件流与 worker 入口。`--role api|worker|worker:agent|all` 决定本进程承担哪部分,默认 `all` 单进程运行(`worker` 只认领 io 组任务,`worker:agent` 只认领 Agent 组;另有内部角色 `--role fsworker`,由 FsProxy 以子进程方式拉起执行单个文件操作,不要手动启动)。业务规则不写在这里,只做编排、鉴权、审计与协议适配。 ## 模块骨架(W2) @@ -12,10 +12,12 @@ src/ worker/index.ts startWorker():引擎 worker + OutboxRelay + 各模块 start() modules/ types.ts ServerModule / ServerContext / OutboxWriter / SecretStore / SettingsReader(共享,不随子任务改) - index.ts SERVER_MODULES 清单(四个模块已登记,子任务不需要再改) + index.ts SERVER_MODULES 清单 auth-settings/ W2-A(provided.ts 提供 secrets / settings 实现) sources-identity/ W2-B acquisition-organize/ W2-C + file-manager/ W5-A:/files/*(浏览、mkdir/rename、move/copy/organize 两阶段、回收站、identify/probe/scrape、download), + 见 docs/runbook/files.md agent/ W2-D:capability-registry.ts(authorize / audit / 幂等键 / 操作账本)、会话 API、runAgent、resolveDecision (见 docs/runbook/agent.md;agent 模块可以 import 其它模块的 service 导出,反向不行) ``` diff --git a/apps/server/test/e2e/e6-w4a-endpoints.test.ts b/apps/server/test/e2e/e10-w4a-endpoints.test.ts similarity index 100% rename from apps/server/test/e2e/e6-w4a-endpoints.test.ts rename to apps/server/test/e2e/e10-w4a-endpoints.test.ts diff --git a/docs/plan/04-v0.1-acceptance.md b/docs/plan/04-v0.1-acceptance.md index 63bc9b74..744234bd 100644 --- a/docs/plan/04-v0.1-acceptance.md +++ b/docs/plan/04-v0.1-acceptance.md @@ -49,7 +49,7 @@ | S6 | AT24(管理标签被移除、qB 任务消失)在服务端没有测试。代码在 `downloads.ts:359-383` 里,只有 domain 层的 `download-state` 单测覆盖 | `__tests__/vertical.test.ts` | 补两个断言:去掉标签后 `managed=false`、历史保留、之后不再暂停也不再清理;任务消失后 `removed`,并且库文件还在 **已修(ceff9cf):新增 E8 `e8-tag-removal`:去标签后 managed=false、完成不入库、清理不触碰,任务消失 → removed 且资产/位置不变、不重下;以 mock qB 调用日志断言** | | S7 | 前端 Agent 页在生产构建里也能用 `?mock=1` 打开 mock 模式(sessionStorage 会记住),可能把演示数据当成真实运行展示,与 AT78 冲突 | `apps/web/app/api/agent.ts:37-48` | 只在 `import.meta.env.DEV` 或设置了 `VITE_AGENT_MOCK` 时启用 **已修(f48e1aa):只在 DEV 构建或后端 `pi.provider = "fake"` 时生效,生产构建忽略并清掉记住的开关(`app/api/agent-mock.test.ts`)** | | S8 | 引擎面板只有 cancel,没有 retry/rerun;§3.1-10 要求 "cancel/retry 操作"(02 backlog 也列了这一项) | `apps/web/app/routes/console/engine*.tsx`,`modules/auth-settings/engine-routes.ts` | 加 `POST /engine/runs/{id}/rerun`,用我们自己的 enqueue、带新幂等键实现,并写审计 **已修(ceff9cf):`POST /engine/runs/{id}/retry`(admin,幂等键 `retry::<序号>`,审计),run 详情页"重跑"按钮;测试 `test/auth-settings/engine-retry.test.ts`** | -| S9 | 账户 TOTP 在控制台没有入口(只有登录页的第二步),02 backlog 里是应修项 | `apps/web/app/routes/console/layout.tsx` | 账户弹层接上 `GET/POST /auth/totp` | +| S9 | 账户 TOTP 在控制台没有入口(只有登录页的第二步),02 backlog 里是应修项 | `apps/web/app/routes/console/layout.tsx` | 账户弹层接上 `GET/POST /auth/totp` **已修(W6-B,见 07 S-3):账户弹层"二次验证"入口,生成密钥(显示 base32 密钥与 otpauth 链接,无二维码)→ 验证码确认开启;已开启时凭当前密码 + 验证码关闭** | | S10 | API 注册表把 `/auth/setup|login|session|logout` 标成 `read`,但实际上前三个免鉴权;`/auth/password`、`/auth/totp` 标成 `admin`,实际要求 admin 会话。按注册表生成的 scope 文档会误导外部集成方 | `packages/contracts/src/api.ts` | EndpointDef 增加 `auth: "public" | "session" | scope`,并加测试断言它与 `PUBLIC_AUTH_PATHS` 一致 **已修(f48e1aa):`EndpointDef.auth` 为 `public`/`session`,`api-registry.test` 断言与 `PUBLIC_AUTH_PATHS` 一致、session 端点拒绝 Bearer** | | S11 | 文档与实际不一致:任务书 §2、§4 还写着 `pnpm ci`;deploy.md 的验证记录写"21 个迁移",现在是 22 个(0017 号空缺);ci 耗时写"约 75 秒",实测约 135 秒;testkit README 没提 emby/browser mock;adapters 和 web 的 README 各只有 3 行 | `docs/plan/00-first-version-plan.md`,`docs/runbook/deploy.md:128`,`packages/testkit/README.md`,`packages/adapters/README.md` | 统一更新;adapters README 列出 8 个子模块的入口,并链接各子目录的 README **已修(f48e1aa):任务书 pnpm→deno task、ci 耗时、deploy.md 迁移数、testkit/adapters/web README;adapters 实际 7 个子模块** | diff --git a/docs/porting/moviepilot-rust-transfer.md b/docs/porting/moviepilot-rust-transfer.md index 4d64ebc7..74003e39 100644 --- a/docs/porting/moviepilot-rust-transfer.md +++ b/docs/porting/moviepilot-rust-transfer.md @@ -164,9 +164,9 @@ MP 做法中明确**不照抄**的:删源失败仍报成功;move 遇 EXDEV 静 遗留: -- **编译二进制**(`deno compile`)里 `Deno.execPath()` 不是 deno CLI,`FS_PROXY_MODE=auto` 回退 in-process 看门狗(能按时报 TIMEOUT、不阻塞任务,但卡死的系统调用线程无法回收)。要在镜像里也用子进程:`main.ts` 加 `--role fsworker` 调 `fsWorkerMain()`,compile 权限放开对自身的 `--allow-run`,再以 `workerCommand` 构造 FsProxy。 -- 规划侧(`planImport` 的源/目标 stat 与同尺寸 sha256)、`reconcile.ts`、文件管理(W5-A)仍直接调进程内 fs,未走 fsproxy。 -- 替换进 `.umt-trash` 的旧文件未登记 `trash_item`(W5-A 的回收站表),到期清理需 W5-A 接上。 +- ~~**编译二进制**里回退 in-process 看门狗~~ **已做(W5-E,3641ab9)**:`main.ts` 在加载其余模块前分派 `--role fsworker`,compile 放开 `--allow-run=/app/umt-server`,FsProxy 在编译二进制里默认 ` --role fsworker`(`stats().workerKind = "self"`),容器演练见 `docs/runbook/deploy.md`。二进制不在 `/app/umt-server` 时仍回退 in-process。 +- 规划侧(`planImport` 的源/目标 stat 与同尺寸 sha256)、`reconcile.ts`、文件管理(W5-A)仍直接调进程内 fs,未走 fsproxy(文件管理的 move/copy/organize 执行与回收站跨卷复制已经走 fsproxy,见 W5-A2 / W6-B)。 +- ~~替换进 `.umt-trash` 的旧文件未登记 `trash_item`~~ **已做(W5-E)**:进 `/.umt-trash///` 并登记 `trash_item`,到期由 `purgeTrash` 清理。 - 根挂死后没有"标 suspect、停止向该根派发"的根级熔断,目前靠根检查超时 → 整轮重试。 ### 基准记录 diff --git a/packages/adapters/README.md b/packages/adapters/README.md index 8b25c532..c84134a4 100644 --- a/packages/adapters/README.md +++ b/packages/adapters/README.md @@ -11,7 +11,7 @@ | [`sites`](src/sites/README.md) | `createSiteAdapter` / `getSiteAdapter`、`listSiteDefinitions`、`siteFetch` | 观众(NexusPHP 配置驱动 spider)与馒头 M-Team API | | [`tmdb`](src/tmdb/README.md) | `createTmdbClient` / `TmdbClient`、`matchTmdb`、`matchTmdbMulti`、`TmdbError` | 多语言搜索、详情、季集;缓存限流;错误信息不带 api_key | | [`emby`](src/emby/README.md) | `createEmbyClient` / `EmbyClient`、`MediaServerRegistry` | Emby / Jellyfin 库存查询,失败返回 `unknown` 而不是"不存在" | -| [`fs`](src/fs/README.md) | `copyWithVerify`、`moveSafe`、`crossDeviceMove`、`checkRoot`、`scanIncremental`、`diskUsage`、`ffprobe` | 校验复制、跨卷移动、库根哨兵、增量扫描 | +| [`fs`](src/fs/README.md) | `copyWithVerify`、`moveSafe`、`crossDeviceMove`、`checkRoot`、`scanIncremental`、`diskUsage`、`ffprobe`、`FsProxy`;另有子路径导出 `@umt/adapters/fsworker`(`fsWorkerMain`,供 `--role fsworker`) | 校验复制、跨卷移动、库根哨兵、增量扫描;FsProxy 把文件操作放进可杀子进程(挂死 → SIGKILL → TIMEOUT) | | [`browser`](src/browser/README.md) | `BrowserProvider` 合同、`createNoneBrowserProvider`、camofox 适配骨架、`redactBrowserUrl` | 浏览器服务接口(M1 只有接口与骨架) | 测试:`deno task test`(vitest,外部服务全部用 `@umt/testkit` 的 mock)。 diff --git a/packages/contracts/README.md b/packages/contracts/README.md index 7f52e0ae..79bb799d 100644 --- a/packages/contracts/README.md +++ b/packages/contracts/README.md @@ -10,7 +10,11 @@ | `settings.ts` | `GlobalSettings`、`DEFAULT_SETTINGS`、`GlobalSettingsUpdate`、`mergeSettings()` | | `capability.ts` / `capabilities-io.ts` | `CapabilityDescriptor`、首批 14 个能力 ID 与目录、各能力 Input/Output schema、`CAPABILITY_IO` | | `events.ts` | SSE 信封、`EventType`、`EVENT_PAYLOADS`、`TypedEvent` 判别联合 | -| `api.ts` | `Api` 端点表(method/path/scope/params/query/body/response)、`buildPath()`、`listEndpoints()` | +| `files.ts` | 文件管理(`/files/*`)的请求/响应:根与条目、dryRun 计划(planId)、move/copy/organize/delete、回收站 `TrashItem`、identify/probe/scrape、download | +| `auth.ts` | 登录、会话探测 `SessionInfo`、改密、TOTP(`TotpStatus`/`TotpAction`)、审计动作枚举 | +| `browser.ts` | BrowserProvider 合同:会话/标签/快照、快照脱敏类别 `SnapshotRedactionKind` | +| `redact.ts` | 凭证脱敏的唯一键表与规则:`SENSITIVE_KEY_PARTS`、`SENSITIVE_QUERY_PARAMS`、`isSensitiveKey`、`isSensitiveQueryParam`、`redactString`、`redactUrl`(server logger、Agent 模型上下文、adapters 的 rss/browser 共用) | +| `api.ts` | `Api` 端点表(method/path/scope/auth/params/query/body/response;`auth:"session"` 表示只接受管理员控制台会话)、`buildPath()`、`listEndpoints()` | 约定: - Row schema 与 `packages/db/src/types.ts` 的列一一对应(`test/db-sync.test.ts` 校验);只在 DB 存在的列(密文、key_hash、change_xid)不对外。