feat(contracts): file manager API schemas and /files endpoint registry
Adds packages/contracts/src/files.ts (roots/list/stat, mkdir/rename, move/copy/organize two-phase dryRun->planId, delete with reference checks and trash, identify/scrape/probe) and registers Api.files in api.ts. Implementation lands in a follow-up commit (W5-A). Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
3665daaf07
commit
b86f75c011
2 files changed
+702
No files matched your search
@@ -7,6 +7,7 @@ import { type ApiScope, CursorQuery, cursorPage, HealthStatus, Id, IdParam } fro
|
||||
import * as content from "./content.ts";
|
||||
import * as events from "./events.ts";
|
||||
import * as execution from "./execution.ts";
|
||||
import * as files from "./files.ts";
|
||||
import * as identity from "./identity.ts";
|
||||
import * as instances from "./instances.ts";
|
||||
import * as ops from "./ops.ts";
|
||||
@@ -1019,6 +1020,129 @@ export const Api = {
|
||||
}),
|
||||
},
|
||||
|
||||
// ── 文件管理(docs/plan/06-file-manager-spec.md;只浏览已登记根,写操作两段式 dryRun → planId) ──
|
||||
files: {
|
||||
roots: ep({
|
||||
method: "get",
|
||||
path: "/files/roots",
|
||||
scope: "organize.read",
|
||||
summary: "可浏览的根(库根 + 下载根),含实时健康与空间",
|
||||
response: files.FileRootList,
|
||||
}),
|
||||
list: ep({
|
||||
method: "get",
|
||||
path: "/files",
|
||||
scope: "organize.read",
|
||||
summary: "列目录(分页/排序/过滤/截断,行内业务状态);根离线 503",
|
||||
query: files.FilesListQuery,
|
||||
response: files.FilesListResponse,
|
||||
}),
|
||||
stat: ep({
|
||||
method: "get",
|
||||
path: "/files/stat",
|
||||
scope: "organize.read",
|
||||
summary: "单项详情:realpath/inode/nlink/sidecars/路径绑定",
|
||||
query: files.FileStatQuery,
|
||||
response: files.FileStat,
|
||||
}),
|
||||
mkdir: ep({
|
||||
method: "post",
|
||||
path: "/files/mkdir",
|
||||
scope: "organize.execute",
|
||||
summary: "新建目录(非递归,已存在 409,仅 writable 根)",
|
||||
body: files.FilesMkdirRequest,
|
||||
response: files.FilesMkdirResult,
|
||||
}),
|
||||
rename: ep({
|
||||
method: "post",
|
||||
path: "/files/rename",
|
||||
scope: "organize.execute",
|
||||
summary: "同目录改名(不覆盖;同步 asset_location 与 path_binding 修订;做种中 409)",
|
||||
body: files.FilesRenameRequest,
|
||||
response: files.FilesRenameResult,
|
||||
}),
|
||||
move: ep({
|
||||
method: "post",
|
||||
path: "/files/move",
|
||||
scope: "organize.execute",
|
||||
summary: "移动/复制到目录:dryRun 返回 planId,执行回传 planId(源变更 409 plan_stale)",
|
||||
body: files.FilesMoveRequest,
|
||||
response: files.FilesTransferResult,
|
||||
}),
|
||||
copy: ep({
|
||||
method: "post",
|
||||
path: "/files/copy",
|
||||
scope: "organize.execute",
|
||||
summary: "复制到目录(同 /files/move mode=copy)",
|
||||
body: files.FilesCopyRequest,
|
||||
response: files.FilesTransferResult,
|
||||
}),
|
||||
organize: ep({
|
||||
method: "post",
|
||||
path: "/files/organize",
|
||||
scope: "organize.execute",
|
||||
summary: "按作品分组整理入库(每组走 planImport,与 /organization-records 同实现)",
|
||||
body: files.FilesOrganizeRequest,
|
||||
response: files.FilesOrganizeResult,
|
||||
}),
|
||||
delete: ep({
|
||||
method: "post",
|
||||
path: "/files/delete",
|
||||
scope: "organize.execute",
|
||||
summary: "删除(默认进回收站;引用检查:做种/资产/绑定;permanent 仅 admin 会话)",
|
||||
body: files.FilesDeleteRequest,
|
||||
response: files.FilesDeleteResult,
|
||||
}),
|
||||
trash: ep({
|
||||
method: "get",
|
||||
path: "/files/trash",
|
||||
scope: "organize.read",
|
||||
summary: "回收站列表",
|
||||
query: files.FilesTrashQuery,
|
||||
response: files.FilesTrashList,
|
||||
}),
|
||||
trashRestore: ep({
|
||||
method: "post",
|
||||
path: "/files/trash/restore",
|
||||
scope: "organize.execute",
|
||||
summary: "从回收站还原(原位被占的项失败 conflict)",
|
||||
body: files.FilesTrashRestoreRequest,
|
||||
response: files.FilesTrashRestoreResult,
|
||||
}),
|
||||
trashPurge: ep({
|
||||
method: "post",
|
||||
path: "/files/trash/purge",
|
||||
scope: "admin",
|
||||
summary: "清空回收站(缺省只清过期项;服务端另要求 admin 控制台会话,API Key 403)",
|
||||
body: files.FilesTrashPurgeRequest,
|
||||
response: files.FilesTrashPurgeResult,
|
||||
}),
|
||||
identify: ep({
|
||||
method: "post",
|
||||
path: "/files/identify",
|
||||
scope: "organize.read",
|
||||
summary: "识别(只读):解析 + 本地/TMDB 召回前 5 候选,不落库",
|
||||
body: files.FilesIdentifyRequest,
|
||||
response: files.FilesIdentifyResult,
|
||||
}),
|
||||
scrape: ep({
|
||||
method: "post",
|
||||
path: "/files/scrape",
|
||||
scope: "organize.execute",
|
||||
summary: "刮削已登记作品(字段级刷新,遵守字段锁;NFO 投影 W5)",
|
||||
body: files.FilesScrapeRequest,
|
||||
response: files.FilesScrapeResult,
|
||||
}),
|
||||
probe: ep({
|
||||
method: "post",
|
||||
path: "/files/probe",
|
||||
scope: "organize.read",
|
||||
summary: "ffprobe 摘要(≤20 项;已登记资产写回 media_info)",
|
||||
body: files.FilesProbeRequest,
|
||||
response: files.FilesProbeResult,
|
||||
}),
|
||||
},
|
||||
|
||||
// ── 能力 ──
|
||||
capabilities: {
|
||||
list: ep({
|
||||
@@ -1291,3 +1415,5 @@ export function listEndpoints(): EndpointDef[] {
|
||||
|
||||
export type EndpointBody<E extends EndpointDef> = E["body"] extends z.ZodType ? z.input<E["body"]> : never;
|
||||
export type EndpointResponse<E extends EndpointDef> = z.infer<E["response"]>;
|
||||
|
||||
export * from "./files.ts";
|
||||
Reference in new issue
Block a user