diff --git a/apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts b/apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts index 93a5a4c4..55e57b28 100644 --- a/apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts +++ b/apps/server/src/modules/acquisition-organize/__tests__/organize-guard.test.ts @@ -72,7 +72,7 @@ describe.skipIf(!hasPg)("手动整理守卫:路径 / Move 授权 / 做种义务" for (const dryRun of [true, false]) { const r = await post({ sourcePaths: [outFile], mediaEntityId: movie, action: "copy", dryRun }); expect(r.status).toBe(400); - expect(r.body.error.message).toContain("不在已登记的库根或下载器路径映射之下"); + expect(r.body.error.message).toContain("不在已登记的媒体库目录或下载器路径映射之下"); } // `..` 即使规范化后仍在根内也拒绝 const inside = await file("Dots.2020.1080p.BluRay-X.mkv"); diff --git a/apps/server/src/modules/acquisition-organize/capabilities.ts b/apps/server/src/modules/acquisition-organize/capabilities.ts index 0684a4ed..f5e1da62 100644 --- a/apps/server/src/modules/acquisition-organize/capabilities.ts +++ b/apps/server/src/modules/acquisition-organize/capabilities.ts @@ -77,7 +77,7 @@ export function registerAoCapabilities(registry: CapabilityRegistry, deps: () => return { items } as never; }); - // AG-3:订阅摘要(分组计数 + 条目),版本目标只给总数 / 已满足数,细节用 inventory.query + // AG-3:订阅摘要(分组计数 + 条目),收录版本只给总数 / 已满足数,细节用 inventory.query registry.register(capabilitySpec("subscription.list"), async (input) => { const db = deps().ctx.db; let q = db @@ -253,7 +253,7 @@ export function registerAoCapabilities(registry: CapabilityRegistry, deps: () => const d = deps(); const db = d.ctx.db; const slot = await db.selectFrom("acquisition_slot").selectAll().where("id", "=", input.slotId).executeTakeFirst(); - if (!slot) throw new CapabilityError("not_found", "版本目标不存在"); + if (!slot) throw new CapabilityError("not_found", "收录版本不存在"); const rel = await db .selectFrom("source_release") .select(["id", "media_entity_id"]) @@ -285,7 +285,7 @@ export function registerAoCapabilities(registry: CapabilityRegistry, deps: () => for (const r of c.rejected ?? []) rejected.push({ code: r.code, message: r.message }); for (const r of c.reasons ?? []) rejected.push({ code: r.split(":")[0] ?? r, message: r }); if (rejected.length === 0) - rejected.push({ code: "not_planned", message: "未生成计划:版本目标正忙或条件不满足" }); + rejected.push({ code: "not_planned", message: "未生成计划:收录版本正忙或条件不满足" }); } const fresh = await db .selectFrom("acquisition_slot") @@ -324,8 +324,8 @@ export function registerAoCapabilities(registry: CapabilityRegistry, deps: () => .where("id", "=", plan.slot_id) .forUpdate() .executeTakeFirstOrThrow(); - if (slot.revision + 1 !== plan.expected_revision) return "版本目标已变化,重新生成计划"; - if (BUSY.includes(slot.status)) return `版本目标正忙(${slot.status})`; + if (slot.revision + 1 !== plan.expected_revision) return "收录版本已变化,重新生成计划"; + if (BUSY.includes(slot.status)) return `收录版本正忙(${slot.status})`; let st = { status: slot.status, hasAsset: slot.current_asset_id !== null }; if (st.status === "satisfied") st = transition(st, { type: "upgrade_found" }); st = transition(st, { type: "reserve" }); @@ -348,7 +348,7 @@ export function registerAoCapabilities(registry: CapabilityRegistry, deps: () => return null; }) .catch((err) => { - if ((err as { code?: string }).code === "23505") return "版本目标已被其他计划预留"; + if ((err as { code?: string }).code === "23505") return "收录版本已被其他计划预留"; throw err; }); if (ok) throw new CapabilityError("conflict", ok); diff --git a/apps/server/src/modules/acquisition-organize/index.ts b/apps/server/src/modules/acquisition-organize/index.ts index 7ec51daf..3812616b 100644 --- a/apps/server/src/modules/acquisition-organize/index.ts +++ b/apps/server/src/modules/acquisition-organize/index.ts @@ -186,8 +186,8 @@ export function createAcquisitionOrganizeModule(overrides: AoOverrides = {}): Se registerScheduledJob(ctx, { key: "library.reconcile", defaults: { - name: "资产对账", - description: "核对媒体库文件与资产记录", + name: "文件核对", + description: "检查媒体库里的文件是否还在", group: "organize", schedule: every(reconcileMs), }, diff --git a/apps/server/src/modules/acquisition-organize/routes/assets.ts b/apps/server/src/modules/acquisition-organize/routes/assets.ts index 006ca3f3..d437c9c7 100644 --- a/apps/server/src/modules/acquisition-organize/routes/assets.ts +++ b/apps/server/src/modules/acquisition-organize/routes/assets.ts @@ -30,19 +30,19 @@ const READ_ONLY: ReadOnly[] = [ { ...Api.mediaAssets, table: "media_asset", - what: "资产", + what: "文件", filters: { mediaEntityId: "media_entity_id", status: "status" }, }, { ...Api.assetUnitCoverage, table: "asset_unit_coverage", - what: "资产覆盖", + what: "文件覆盖", filters: { mediaAssetId: "media_asset_id", contentUnitId: "content_unit_id" }, }, { ...Api.assetLocations, table: "asset_location", - what: "资产位置", + what: "文件位置", filters: { mediaAssetId: "media_asset_id", status: "status" }, }, { diff --git a/apps/server/src/modules/acquisition-organize/routes/dashboard.ts b/apps/server/src/modules/acquisition-organize/routes/dashboard.ts index 0c8eb4ad..6311b391 100644 --- a/apps/server/src/modules/acquisition-organize/routes/dashboard.ts +++ b/apps/server/src/modules/acquisition-organize/routes/dashboard.ts @@ -143,7 +143,7 @@ export function registerDashboardRoutes(app: ModuleApp, d: AoDeps) { const notes: string[] = []; if (!v.snapshot) notes.push(v.path ? "磁盘用量读取失败,已暂停新增下载" : "实例未配置保存路径,无法统计"); if (dv.unknownSizeTaskIds.length) notes.push(`${dv.unknownSizeTaskIds.length} 个在途任务体积未知`); - if (dv.unmanagedGrowthRisk) notes.push("有未纳管任务仍在增长,暂停纳管任务后磁盘仍会增长"); + if (dv.unmanagedGrowthRisk) notes.push("有外部任务仍在增长,暂停本工具的任务后磁盘仍会增长"); const pct = usedPercent(v); volumes.push({ volumeKey: v.volumeKey, diff --git a/apps/server/src/modules/acquisition-organize/routes/downloads.ts b/apps/server/src/modules/acquisition-organize/routes/downloads.ts index 321fbcc1..e523c1da 100644 --- a/apps/server/src/modules/acquisition-organize/routes/downloads.ts +++ b/apps/server/src/modules/acquisition-organize/routes/downloads.ts @@ -110,7 +110,7 @@ export function registerDownloadRoutes(app: ModuleApp, d: AoDeps) { method: "post", path: "/library/reconcile", scope: "admin", - summary: "入队一轮资产对账(库根哨兵 + 增量 stat;不删记录、不补下载);返回任务 id,进度见任务页", + summary: "入队一轮资产对账(媒体库目录哨兵 + 增量 stat;不删记录、不补下载);返回任务 id,进度见任务页", body: z.object({ libraryRootId: z.uuid().optional() }), response: accepted, }, diff --git a/apps/server/src/modules/acquisition-organize/routes/naming.ts b/apps/server/src/modules/acquisition-organize/routes/naming.ts index 41cf8777..73594878 100644 --- a/apps/server/src/modules/acquisition-organize/routes/naming.ts +++ b/apps/server/src/modules/acquisition-organize/routes/naming.ts @@ -49,7 +49,7 @@ export function registerNamingRoutes(app: ModuleApp, d: AoDeps) { }; const rootRow = async (id: string) => { const r = await db().selectFrom("library_root").selectAll().where("id", "=", id).executeTakeFirst(); - if (!r) throw notFound("库根"); + if (!r) throw notFound("媒体库目录"); return r; }; const audit = ( @@ -209,7 +209,7 @@ async function loadSample(d: AoDeps, input: NamingPreviewInput): Promise { const r = await sql<{ n: number }>` select (select count(*) from acquisition_slot where rule_group_ids @> ${JSON.stringify([id])}::jsonb) @@ -117,7 +117,7 @@ async function checkIdsExist(exec: Trx | AoDeps["ctx"]["db"], ids: readonly stri if (missing.length) throw new ApiException("bad_request", `规则组不存在:${missing.join(", ")}`); } -/** 版本目标 / 订阅引用的规则组 id 校验(供 slots 路由复用) */ +/** 收录版本 / 订阅引用的规则组 id 校验(供 slots 路由复用) */ export const assertRuleGroupIds = checkIdsExist; const TRACKED_FIELDS = [ @@ -528,7 +528,7 @@ export function registerRuleGroupRoutes(app: ModuleApp, d: AoDeps) { .executeTakeFirst(); if (!cur) notFound("规则组"); const refs = await referenceCount(trx, cur!.id); - if (refs > 0) conflict(`规则组被 ${refs} 个版本目标或订阅引用,先解除引用`); + if (refs > 0) conflict(`规则组被 ${refs} 个收录版本或订阅引用,先解除引用`); const row = await trx .updateTable("rule_group") .set({ @@ -597,7 +597,7 @@ export function registerRuleGroupRoutes(app: ModuleApp, d: AoDeps) { ); } -/** 规则组改动后重评引用它的版本目标(显式引用;默认链改动由周期重评覆盖) */ +/** 规则组改动后重评引用它的收录版本(显式引用;默认链改动由周期重评覆盖) */ async function kickReevaluate(d: AoDeps, groupId: string) { try { const slots = await d.ctx.db diff --git a/apps/server/src/modules/acquisition-organize/routes/rules.ts b/apps/server/src/modules/acquisition-organize/routes/rules.ts index dab2b3b5..3712dbb2 100644 --- a/apps/server/src/modules/acquisition-organize/routes/rules.ts +++ b/apps/server/src/modules/acquisition-organize/routes/rules.ts @@ -136,7 +136,7 @@ export function registerRuleRoutes(app: ModuleApp, d: AoDeps) { Api.acquisitionSlots.get, async (_c, { params }) => { const r = await db().selectFrom("acquisition_slot").selectAll().where("id", "=", params.id).executeTakeFirst(); - return toApi(r ?? notFound("版本目标")); + return toApi(r ?? notFound("收录版本")); }, { tag: TAG }, ); @@ -171,7 +171,7 @@ export function registerRuleRoutes(app: ModuleApp, d: AoDeps) { .onConflict((oc) => oc.doNothing()) .returningAll() .executeTakeFirst(); - if (!r) conflict("该作品或内容单元下已有同名版本目标"); + if (!r) conflict("该作品或内容单元下已有同名收录版本"); await kickEvaluate(d, { acquisitionSlotId: r!.id }); return toApi(r!); }, @@ -208,7 +208,7 @@ export function registerRuleRoutes(app: ModuleApp, d: AoDeps) { .select("revision") .where("id", "=", params.id) .executeTakeFirst(); - if (!exists) notFound("版本目标"); + if (!exists) notFound("收录版本"); conflict(`revision 不匹配(当前 ${exists!.revision})`); } await kickEvaluate(d, { acquisitionSlotId: params.id }); diff --git a/apps/server/src/modules/acquisition-organize/routes/subscriptions.ts b/apps/server/src/modules/acquisition-organize/routes/subscriptions.ts index fa396220..02e86142 100644 --- a/apps/server/src/modules/acquisition-organize/routes/subscriptions.ts +++ b/apps/server/src/modules/acquisition-organize/routes/subscriptions.ts @@ -49,7 +49,7 @@ import { actorOf, conflict, errMessage, jsonb, notFound, toApi, toApiList } from import { mount, paginate } from "./helpers.ts"; // W9-E 订阅 API(docs/design/subscriptions.md §1–§3、§5):总览(分组/排序/筛选)、覆盖矩阵、表单字段规范化、 -// 暂停/恢复/重新订阅、批量、手动补全某版本、版本目标排序;主动搜索、进度、总集数、完成历史沿用 W5-C。 +// 暂停/恢复/重新订阅、批量、手动补全某版本、收录版本排序;主动搜索、进度、总集数、完成历史沿用 W5-C。 const TAG = "subscriptions"; const OVERVIEW_MAX = 2000; @@ -106,12 +106,12 @@ async function assertNoDuplicate( if (hit) conflict(`该作品同一季已有订阅:${hit.id}`); } -/** 没有收录策略模板、表单给了质量预设时,新订阅按这个模板建一个版本目标(预设随后写入) */ +/** 没有收录策略模板、表单给了质量预设时,新订阅按这个模板建一个收录版本(预设随后写入) */ const DEFAULT_SLOT_TEMPLATE: SlotTemplate = { name: "默认版本" }; /** * 保存后应用表单的派生写入:包含 / 排除 → 订阅私有规则组;质量预设(MP resolution / quality / effect)→ - * 该订阅自己的版本目标;MP total_episode → 季总集数;best_version → 版本目标升级策略。 + * 该订阅自己的收录版本;MP total_episode → 季总集数;best_version → 收录版本升级策略。 */ async function applyDerived( exec: Exec, @@ -616,7 +616,7 @@ export function registerSubscriptionRoutes(app: ModuleApp, d: AoDeps) { { tag: TAG }, ); - // ── 版本目标:手动补全 / 排序 ── + // ── 收录版本:手动补全 / 排序 ── mount( app, Api.subscriptions.fillSlot, @@ -632,7 +632,7 @@ export function registerSubscriptionRoutes(app: ModuleApp, d: AoDeps) { .execute() : []; const slot = slotsOfSub(slots, sub).find((s) => s.id === params.slotId); - if (!slot) notFound("版本目标"); + if (!slot) notFound("收录版本"); invalidateCoverage(sub.id); try { return await d.ctx.engine.enqueue( @@ -668,7 +668,7 @@ export function registerSubscriptionRoutes(app: ModuleApp, d: AoDeps) { new Set(want).size !== want.length || !want.every((id) => mine.some((s) => s.id === id)) ) - throw new ApiException("bad_request", "slotIds 必须恰好是该订阅的全部版本目标"); + throw new ApiException("bad_request", "slotIds 必须恰好是该订阅的全部收录版本"); for (const [i, id] of want.entries()) await trx.updateTable("acquisition_slot").set({ position: i }).where("id", "=", id).execute(); return (await trx.selectFrom("acquisition_slot").selectAll().where("id", "in", want).execute()).sort( diff --git a/apps/server/src/modules/acquisition-organize/services/acquisition.ts b/apps/server/src/modules/acquisition-organize/services/acquisition.ts index fb627f7a..d9fa24fa 100644 --- a/apps/server/src/modules/acquisition-organize/services/acquisition.ts +++ b/apps/server/src/modules/acquisition-organize/services/acquisition.ts @@ -200,7 +200,7 @@ export async function createSlotsFromTemplates( await exec .insertInto("acquisition_slot") .values({ - // 同一事务建的版本目标 created_at 相同:按模板顺序给 position,第一个为主版本 + // 同一事务建的收录版本 created_at 相同:按模板顺序给 position,第一个为主版本 position: i, media_entity_id: input.entityId, subscription_id: input.subscriptionId, @@ -556,7 +556,7 @@ async function evaluateForSlot(c: Ctx, p: SlotInput): Promise<{ planId: string | } const spec = slotSpecFromRow(toApi(slot)); const ctx = contextOf(rel, now); - // 规则组:版本目标 ruleGroupIds → 订阅 ruleGroupIds → 订阅默认链;开升级叠加洗版默认链 + // 规则组:收录版本 ruleGroupIds → 订阅 ruleGroupIds → 订阅默认链;开升级叠加洗版默认链 c.rules ??= loadRuleCatalog(db); const chain = resolveRuleChain(await c.rules, { kind: "acquisition", @@ -568,7 +568,7 @@ async function evaluateForSlot(c: Ctx, p: SlotInput): Promise<{ planId: string | siteId: rel.site_id, subscriptionId: sub?.id, }); - // 规则组的结构化硬条件(订阅私有组的包含 / 排除)并入版本目标硬条件 + // 规则组的结构化硬条件(订阅私有组的包含 / 排除)并入收录版本硬条件 spec.hard = mergeHard(spec.hard, chain.hard); if (chain.groups.length) { spec.rules = { groups: chain.groups, atoms: chain.atoms }; @@ -811,7 +811,7 @@ async function unitQuality( exec: Exec, slot: AcquisitionSlotRow, keys: readonly string[], - /** 版本目标引用了带层的规则组时,按规则组重算该单元现有资产的 pri(覆盖入库时按 soft.tiers 记下的值) */ + /** 收录版本引用了带层的规则组时,按规则组重算该单元现有资产的 pri(覆盖入库时按 soft.tiers 记下的值) */ priOf?: (assetId: string) => Promise, ): Promise> { const rows = await exec diff --git a/apps/server/src/modules/acquisition-organize/services/coverage.ts b/apps/server/src/modules/acquisition-organize/services/coverage.ts index 20368fb0..2deb3b8d 100644 --- a/apps/server/src/modules/acquisition-organize/services/coverage.ts +++ b/apps/server/src/modules/acquisition-organize/services/coverage.ts @@ -25,9 +25,9 @@ import { defaultChainIds, loadRuleCatalog, type RuleCatalog, resolveRuleChain } import { INFLIGHT_PLAN_STATUSES, rangeOf, subParams } from "./subscriptions.ts"; // W9-E 覆盖矩阵与订阅总览(docs/design/subscriptions.md §1、§2.1、§5): -// 一批订阅一次取齐 版本目标 / 资产 / 覆盖 / 分集账本 / 在途计划 / 内容单元 / 延后(固定 8 条查询,与订阅数无关), -// 在内存里按版本目标算每格状态(domain coverageRow)。覆盖口径与 slotCoveredUnits 相同: -// 版本目标覆盖 = 该目标计划产出的资产 + 当前资产 + 分集账本资产(作品只有一个版本目标时,作品下全部资产都算)。 +// 一批订阅一次取齐 收录版本 / 资产 / 覆盖 / 分集账本 / 在途计划 / 内容单元 / 延后(固定 8 条查询,与订阅数无关), +// 在内存里按收录版本算每格状态(domain coverageRow)。覆盖口径与 slotCoveredUnits 相同: +// 收录版本覆盖 = 该目标计划产出的资产 + 当前资产 + 分集账本资产(作品只有一个收录版本时,作品下全部资产都算)。 const MAX_EPISODES = 2000; const COVERAGE_TTL_MS = 30_000; @@ -111,11 +111,11 @@ export function subscriptionAutoFill( }; } -/** 版本目标排序:position → created_at → id;第一个为主版本 */ +/** 收录版本排序:position → created_at → id;第一个为主版本 */ export const slotOrder = (a: AcquisitionSlotRow, b: AcquisitionSlotRow) => a.position - b.position || a.created_at.getTime() - b.created_at.getTime() || a.id.localeCompare(b.id); -/** 订阅关联的版本目标:同作品、属于该订阅或不属于任何订阅 */ +/** 订阅关联的收录版本:同作品、属于该订阅或不属于任何订阅 */ export const slotsOfSub = (all: readonly AcquisitionSlotRow[], sub: Pick) => all .filter( @@ -287,7 +287,7 @@ export async function buildSubscriptionModels( if (p?.episode !== undefined || /^d\d{8}$/.test(k)) put(k, { assetId: id, quality: q, pri }); } } - // 分集账本优先(质量与 pri 按版本目标记) + // 分集账本优先(质量与 pri 按收录版本记) for (const r of ledger) { const f = { assetId: r.media_asset_id, quality: qualityOf(r.quality), pri: r.pri }; if (r.kind === "episode" && r.episode_number !== null) @@ -542,7 +542,7 @@ export async function buildSubscriptionModels( upgrade: m.upgrade, }); if (isMovie) { - // 电影:该版本目标不要求的内容版本不算缺 + // 电影:该收录版本不要求的内容版本不算缺 for (const c of r.cells) if (c.state === "missing" && !m.requiredEditions.includes(c.unitKey.slice("edition:".length))) { c.state = "skipped"; diff --git a/apps/server/src/modules/acquisition-organize/services/naming.ts b/apps/server/src/modules/acquisition-organize/services/naming.ts index 42f3fd84..502ec0b0 100644 --- a/apps/server/src/modules/acquisition-organize/services/naming.ts +++ b/apps/server/src/modules/acquisition-organize/services/naming.ts @@ -106,7 +106,7 @@ export async function namingByVersion(exec: Exec, version: number): Promise { throw new PathGuardError(`源路径不存在或不可访问:${p}`, p); } if (!roots.some((root) => within(r, root))) - throw new PathGuardError(`源路径不在已登记的库根或下载器路径映射之下:${p}`, p); + throw new PathGuardError(`源路径不在已登记的媒体库目录或下载器路径映射之下:${p}`, p); return r; }, }; diff --git a/apps/server/src/modules/acquisition-organize/services/organize.ts b/apps/server/src/modules/acquisition-organize/services/organize.ts index 0f8483df..c8a9f1c4 100644 --- a/apps/server/src/modules/acquisition-organize/services/organize.ts +++ b/apps/server/src/modules/acquisition-organize/services/organize.ts @@ -393,7 +393,7 @@ export async function planImport(d: AoDeps, req: ImportRequest): Promise24h;失败吞掉)。copy 路径在 copyWithVerify 内还会再清一次 if (fsr.partialMaxAgeMs > 0) diff --git a/apps/server/src/modules/acquisition-organize/services/rules.ts b/apps/server/src/modules/acquisition-organize/services/rules.ts index 3f750587..b635004a 100644 --- a/apps/server/src/modules/acquisition-organize/services/rules.ts +++ b/apps/server/src/modules/acquisition-organize/services/rules.ts @@ -35,7 +35,7 @@ import { ApiException } from "../../../http/errors.ts"; import { hashJson, toApi } from "../util.ts"; import type { AoDeps, Exec } from "./common.ts"; -// 规则组(W9-D):目录加载、引用链解析(版本目标 → 订阅 → 默认链,开升级叠加洗版默认)、指纹、输入规范化、试算。 +// 规则组(W9-D):目录加载、引用链解析(收录版本 → 订阅 → 默认链,开升级叠加洗版默认)、指纹、输入规范化、试算。 export interface RuleCatalog { /** 未删除的全部规则组(含停用,解析链时再过滤) */ @@ -132,17 +132,17 @@ export function defaultChainIds(cat: RuleCatalog, kind: RuleGroupKind): string[] } export interface ResolveInput extends RuleTarget { - /** 版本目标 rule_group_ids(非空时优先) */ + /** 收录版本 rule_group_ids(非空时优先) */ slotRuleGroupIds?: readonly string[] | undefined; /** 订阅 params.ruleGroupIds */ subRuleGroupIds?: readonly string[] | undefined; kind: "acquisition" | "search"; - /** 版本目标开了升级:叠加洗版默认链 */ + /** 收录版本开了升级:叠加洗版默认链 */ upgrade?: boolean | undefined; } /** - * 引用链:订阅私有组 → (版本目标 ruleGroupIds || 订阅 ruleGroupIds || 该类默认链)→ 开升级时叠加洗版默认链。 + * 引用链:订阅私有组 → (收录版本 ruleGroupIds || 订阅 ruleGroupIds || 该类默认链)→ 开升级时叠加洗版默认链。 * 按 scope 过滤、跳过停用/已删;pri 取链上最后一个带层的组(对齐 MP 多组串联)。 */ export function resolveRuleChain( @@ -190,7 +190,7 @@ export function chainHard(rows: readonly Pick[]): SlotHard return { ...(inc.length ? { include: inc } : {}), ...(exclude.length ? { exclude } : {}) }; } -/** 版本目标硬条件 + 规则组硬条件:两边的 include 都要满足,exclude 取并集 */ +/** 收录版本硬条件 + 规则组硬条件:两边的 include 都要满足,exclude 取并集 */ export function mergeHard(base: SlotHardConditions, extra: SlotHardConditions): SlotHardConditions { if (!extra.include?.length && !extra.exclude?.length) return base; const out: SlotHardConditions = { ...base }; diff --git a/apps/server/src/modules/acquisition-organize/services/subscription-calendar.ts b/apps/server/src/modules/acquisition-organize/services/subscription-calendar.ts index d2c372f7..ef37a01c 100644 --- a/apps/server/src/modules/acquisition-organize/services/subscription-calendar.ts +++ b/apps/server/src/modules/acquisition-organize/services/subscription-calendar.ts @@ -11,7 +11,7 @@ import { TRACKING_SUB_STATUSES } from "./subscriptions.ts"; // 订阅日历(docs/design/subscriptions.md §4):事件 = 追踪中订阅范围内集的播出日期(集内容单元 air_date, // 由 refreshMetadata 按 TMDB 季集数据写入),同日同季聚合;电影取上映日期(元数据 releaseDate)。 -// 状态取主版本那一行的格(没有版本目标时取"库内任一版本"):全已入库 have / 部分 partial / 都没有 missing / +// 状态取主版本那一行的格(没有收录版本时取"库内任一版本"):全已入库 have / 部分 partial / 都没有 missing / // 播出日期在今天之后 upcoming。范围内没有播出日期的集列进 unknown(界面显示"播出日期未知")。 // 与覆盖矩阵同一套模型(buildSubscriptionModels),口径一致。 diff --git a/apps/server/src/modules/acquisition-organize/services/subscription-reset.ts b/apps/server/src/modules/acquisition-organize/services/subscription-reset.ts index f16b3f9c..ac2d0a4e 100644 --- a/apps/server/src/modules/acquisition-organize/services/subscription-reset.ts +++ b/apps/server/src/modules/acquisition-organize/services/subscription-reset.ts @@ -8,7 +8,7 @@ import { INFLIGHT_PLAN_STATUSES } from "./subscriptions.ts"; // 订阅重置(差距 #24,对照 MP GET /subscribe/reset:清 note / episode_priority,lack = total,state = R)。 // 我们的"note"是在途计划:已预留未提交的、以及下载任务已不在下载器里的已提交计划都释放(计划 cancelled, -// Slot 走 release 迁移);下载器里仍在跑的计划保留(否则会重复下载)。清掉版本目标的延后记录,标记立即重评。 +// Slot 走 release 迁移);下载器里仍在跑的计划保留(否则会重复下载)。清掉收录版本的延后记录,标记立即重评。 // 已入库资产、分集账本、总集数(含锁定)不动。 /** 仍算"活着"的下载任务状态:任务在下载器里,重置不能释放它的计划 */ diff --git a/apps/server/src/modules/acquisition-organize/services/subscriptions-params.ts b/apps/server/src/modules/acquisition-organize/services/subscriptions-params.ts index 499b6b9f..a24ba6d2 100644 --- a/apps/server/src/modules/acquisition-organize/services/subscriptions-params.ts +++ b/apps/server/src/modules/acquisition-organize/services/subscriptions-params.ts @@ -19,10 +19,10 @@ import { allOfAny } from "./rules.ts"; // total_episode (+ manual_total_episode) → 保存后写季总集数(manual 锁定) // include / exclude(字符串或数组)→ include[] / exclude[](正则白名单校验) // keyword / keywords(字符串)→ keywords[] filter_groups(组名或 id)→ ruleGroupIds -// best_version / best_version_full → 订阅版本目标的升级策略(按集 / 整季;电影 = 单片) +// best_version / best_version_full → 订阅收录版本的升级策略(按集 / 整季;电影 = 单片) // autoFillVersions{enabled, budget{perDay, bytes}} / autoFill:boolean → autoFill + fillBudget{unitsPerDay, bytesPerDay} // resolution / quality(resource_type) / effect(MP 标签或正则)→ resolution / source / effect 质量预设 -// 保存后:include / exclude(+ 预设里只能用正则表达的 UHD / Atmos)同步为订阅私有规则组;质量预设写到该订阅自己的版本目标。 +// 保存后:include / exclude(+ 预设里只能用正则表达的 UHD / Atmos)同步为订阅私有规则组;质量预设写到该订阅自己的收录版本。 const UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i; @@ -32,7 +32,7 @@ export interface NormalizedParams { params: SubscriptionParams; /** MP total_episode:保存后写入订阅季的总集数 */ episodeTotal?: { total: number; locked: boolean }; - /** MP best_version:写到订阅版本目标的升级策略 */ + /** MP best_version:写到订阅收录版本的升级策略 */ upgrade?: UpgradeStrategy; /** 被改写的兼容字段(调试/审计用) */ mapped: string[]; @@ -141,7 +141,7 @@ export function parseMpEffect(v: string): SubscriptionEffect | null { return null; } -/** 质量预设 → 版本目标硬条件 / 软偏好;只能用正则表达的(UHD、Atmos)进私有规则组的包含 */ +/** 质量预设 → 收录版本硬条件 / 软偏好;只能用正则表达的(UHD、Atmos)进私有规则组的包含 */ export interface PresetConditions { hard: Record; soft: Record; @@ -214,8 +214,8 @@ export function presetConditions(p: Pick JSON.stringify(a) === JSON.stringify(b); /** - * 质量预设写到该订阅自己的版本目标:新预设给出的字段覆盖;旧预设给过、新预设不再给的字段, - * 只有当版本目标上的值仍等于旧预设生成的值时才删掉(手改过的保留)。有变化 revision+1。 + * 质量预设写到该订阅自己的收录版本:新预设给出的字段覆盖;旧预设给过、新预设不再给的字段, + * 只有当收录版本上的值仍等于旧预设生成的值时才删掉(手改过的保留)。有变化 revision+1。 */ export async function applySlotPreset( exec: Exec, @@ -461,7 +461,7 @@ export async function normalizeSubscriptionParams( // ── 库根存在性与正则白名单 ── if (p.libraryRootId) { const r = await exec.selectFrom("library_root").select("id").where("id", "=", p.libraryRootId).executeTakeFirst(); - if (!r) bad(`库根不存在:${p.libraryRootId}`); + if (!r) bad(`媒体库目录不存在:${p.libraryRootId}`); } for (const k of ["include", "exclude"] as const) for (const pat of p[k] ?? []) { @@ -481,7 +481,7 @@ export async function normalizeSubscriptionParams( return out; } -/** best_version 等 → 订阅版本目标的升级策略(保留其余升级参数;revision+1) */ +/** best_version 等 → 订阅收录版本的升级策略(保留其余升级参数;revision+1) */ export async function applySlotUpgrade( exec: Exec, input: { subscriptionId: string; entityId: string; strategy: UpgradeStrategy }, diff --git a/apps/server/src/modules/acquisition-organize/services/subscriptions.ts b/apps/server/src/modules/acquisition-organize/services/subscriptions.ts index 323a797b..74ee39a2 100644 --- a/apps/server/src/modules/acquisition-organize/services/subscriptions.ts +++ b/apps/server/src/modules/acquisition-organize/services/subscriptions.ts @@ -709,7 +709,7 @@ export async function sweepMetadataRefresh(d: AoDeps): Promise { } /** - * 订阅指定的库根(params.libraryRootId):下载计划所属版本目标的订阅优先,否则取该作品最近的未取消订阅。 + * 订阅指定的库根(params.libraryRootId):下载计划所属收录版本的订阅优先,否则取该作品最近的未取消订阅。 * 整理时只在作品没有路径绑定时使用(首次建绑定)。 */ export async function subscriptionLibraryRoot( diff --git a/apps/server/src/modules/agent/authorize.ts b/apps/server/src/modules/agent/authorize.ts index ea48f859..905cfb52 100644 --- a/apps/server/src/modules/agent/authorize.ts +++ b/apps/server/src/modules/agent/authorize.ts @@ -204,13 +204,13 @@ export function createAuthorizer(deps: { if (d.id === "agent.spawn") { return runId && (ctx.source === "agent" || ctx.source === "task") ? { allowed: true } - : { allowed: false, reason: "只有 Agent 会话可以派生子会话" }; + : { allowed: false, reason: "只有智能体会话可以派生子会话" }; } // agent.propose_options 只登记一个待用户选择的事项(无外部副作用);能否问用户由处理器校验 if (d.id === "agent.propose_options") { return runId && (ctx.source === "agent" || ctx.source === "task") ? { allowed: true } - : { allowed: false, reason: "只有 Agent 会话可以让用户选方案" }; + : { allowed: false, reason: "只有智能体会话可以让用户选方案" }; } // metadata.enrich 只存补全建议(不写作品表);次数由每日预算约束,应用建议另走控制台会话 if (d.id === "metadata.enrich") return { allowed: true }; diff --git a/apps/server/src/modules/agent/capabilities-download.ts b/apps/server/src/modules/agent/capabilities-download.ts index b63dfcde..2e881660 100644 --- a/apps/server/src/modules/agent/capabilities-download.ts +++ b/apps/server/src/modules/agent/capabilities-download.ts @@ -63,7 +63,7 @@ export function registerDownloadRecoveryCapabilities( .executeTakeFirst(); if (!dt) throw new CapabilityError("not_found", "下载任务不存在"); if (!dt.managed || dt.removed_at || dt.status === "removed") - throw new CapabilityError("precondition", "未纳管或已移除的下载任务不做自动恢复"); + throw new CapabilityError("precondition", "外部或已移除的下载任务不做自动恢复"); const status = dt.status as DownloadStatus; if (input.action === "recheck" && !RECHECK_FROM.includes(status)) throw new CapabilityError("precondition", `当前状态 ${status} 不需要重新校验`); diff --git a/apps/server/src/modules/agent/capabilities-metadata.ts b/apps/server/src/modules/agent/capabilities-metadata.ts index e047d235..9e104e77 100644 --- a/apps/server/src/modules/agent/capabilities-metadata.ts +++ b/apps/server/src/modules/agent/capabilities-metadata.ts @@ -59,7 +59,8 @@ export async function upstreamComplete( sessionId: `umt-enrich-${randomUUID()}`, }, ); - if (res.stopReason === "error" || res.stopReason === "aborted") throw new Error(res.errorMessage ?? "上游返回错误"); + if (res.stopReason === "error" || res.stopReason === "aborted") + throw new Error(res.errorMessage ?? "模型服务返回错误"); const text = res.content .filter((b): b is { type: "text"; text: string } => b.type === "text") .map((b) => b.text) @@ -87,7 +88,7 @@ export function registerMetadataCapabilities( const agent = await ctx.settings.get("agent").catch(() => null); const ws = { ...DEFAULT_WEB_SEARCH, ...(agent?.webSearch ?? {}) }; if (ws.provider === "none" || !ws.apiKeySecretRef || !registry.has("web.search")) - throw new CapabilityError("precondition", "网页搜索未开启,在设置 → Agent → 网页搜索里开启后再补全"); + throw new CapabilityError("precondition", "网页搜索未开启,在设置 → 智能体 → 网页搜索里开启后再补全"); // 内部调用 web.* 用本次补全自己的调用方身份:白名单与 Agent 会话的互不覆盖 const sub = (scope: string) => ({ actorId: scope, diff --git a/apps/server/src/modules/agent/challenge.ts b/apps/server/src/modules/agent/challenge.ts index 2ec1b1d7..595850a8 100644 --- a/apps/server/src/modules/agent/challenge.ts +++ b/apps/server/src/modules/agent/challenge.ts @@ -149,7 +149,7 @@ async function solveChallenge(env: AgentEnv, registry: CapabilityRegistry, input reason = "页面要求二次验证 / 短信 / 本人设备确认,不自动处理"; } else if (r.kind === "no_vision") { status = "needs_human"; - reason = "当前 Agent 上游模型不支持图片输入,不能做视觉自动化;换支持视觉的模型或人工处理"; + reason = "当前模型不支持图片输入,不能做视觉自动化;换支持视觉的模型或人工处理"; } else if (r.kind === "no_upstream") { status = "needs_human"; reason = r.message; @@ -230,7 +230,7 @@ async function runChild( childRunId: null, steps: 0, outcome: null, - message: "未配置 Agent 上游,不能做视觉自动化", + message: "未配置模型服务,不能做视觉自动化", }; const fake = upstream.row.provider === "fake"; const s = await ctx.settings.all(); diff --git a/apps/server/src/modules/agent/fake-scripts.ts b/apps/server/src/modules/agent/fake-scripts.ts index ac6f335f..3a065ed8 100644 --- a/apps/server/src/modules/agent/fake-scripts.ts +++ b/apps/server/src/modules/agent/fake-scripts.ts @@ -79,7 +79,7 @@ export const DEMO_SCRIPTS: FakeScript[] = [ finalText: (results) => { const r = results.find((x) => x.capabilityId === "inventory.query"); const n = (r?.json as { items?: unknown[] } | undefined)?.items?.length ?? 0; - return `[fake] 演示模式:库存查询返回 ${n} 个作品。演示模型不提交下载;要真实执行,在设置里配置 Agent 上游。`; + return `[fake] 演示模式:库存查询返回 ${n} 个作品。演示模型不提交下载;要真实执行,在设置里配置模型服务。`; }, }, ]; diff --git a/apps/server/src/modules/agent/interaction.ts b/apps/server/src/modules/agent/interaction.ts index 0dc0defe..27724a38 100644 --- a/apps/server/src/modules/agent/interaction.ts +++ b/apps/server/src/modules/agent/interaction.ts @@ -76,9 +76,9 @@ const isUuid = (s: string) => /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}- async function runOf(ctx: ServerContext, c: HandlerContext | InvokeContext): Promise { const id = c.agentRunId && isUuid(c.agentRunId) ? c.agentRunId : null; - if (!id) throw new CapabilityError("precondition", "只有 Agent 会话可以使用这个能力"); + if (!id) throw new CapabilityError("precondition", "只有智能体会话可以使用这个能力"); const run = await ctx.db.selectFrom("agent_run").selectAll().where("id", "=", id).executeTakeFirst(); - if (!run) throw new CapabilityError("not_found", "Agent 会话不存在"); + if (!run) throw new CapabilityError("not_found", "智能体会话不存在"); return run; } @@ -405,7 +405,7 @@ export async function inlineApproval( const summary = await planSummary(db, plan.planType, plan.planId); const decision = await insertDecision(sctx, { kind: "approval_required", - title: `Agent 请求审批:${CAPABILITY_CATALOG[d.id as KnownCapabilityId]?.title ?? d.id}`, + title: `智能体请求审批:${CAPABILITY_CATALOG[d.id as KnownCapabilityId]?.title ?? d.id}`, summary, runId, dedupeKey: `${APPROVAL_KEY}:${runId}:${inv}`, @@ -625,7 +625,7 @@ export function registerInteractionCapabilities(registry: CapabilityRegistry, ct const stepId = await currentStepId(ctx, live, run.id); d = await insertDecision(ctx, { kind: "other", - title: `Agent 等你选方案:${clip(input.question, 80)}`, + title: `智能体等你选方案:${clip(input.question, 80)}`, summary: input.options.map((o) => `${o.id}. ${o.title}`).join(";"), runId: run.id, dedupeKey, diff --git a/apps/server/src/modules/agent/routes.ts b/apps/server/src/modules/agent/routes.ts index 6ae8ed0f..1e54f450 100644 --- a/apps/server/src/modules/agent/routes.ts +++ b/apps/server/src/modules/agent/routes.ts @@ -139,7 +139,7 @@ export function registerAgentRoutes( route(app, Api.agentRuns.get, async ({ params, query }) => { const run = await db().selectFrom("agent_run").selectAll().where("id", "=", params.id).executeTakeFirst(); - if (!run) throw new ApiException("not_found", "Agent 运行不存在"); + if (!run) throw new ApiException("not_found", "智能体运行不存在"); const events = await db() .selectFrom("agent_run_event") .selectAll() @@ -222,9 +222,9 @@ export function registerAgentRoutes( route(app, Api.agentRuns.choose, async ({ params, body, actorId }) => { const run = await db().selectFrom("agent_run").selectAll().where("id", "=", params.id).executeTakeFirst(); - if (!run) throw new ApiException("not_found", "Agent 运行不存在"); + if (!run) throw new ApiException("not_found", "智能体运行不存在"); if (body.optionId === null && !body.reason?.trim()) - throw new ApiException("validation_failed", "都不要时写一句原因,Agent 按它调整方案"); + throw new ApiException("validation_failed", "都不要时写一句原因,智能体按它调整方案"); const r = await answerOptions(ctx, run.id, body, actorId); if (r === "not_found") throw new ApiException("not_found", "这个会话没有这项方案选择"); if (r === "bad_option") throw new ApiException("validation_failed", "没有这个方案"); @@ -427,7 +427,7 @@ export function registerAgentRoutes( let slotIds: string[]; if (body.slotId) { const s = await db().selectFrom("acquisition_slot").select("id").where("id", "=", body.slotId).executeTakeFirst(); - if (!s) throw new ApiException("not_found", "版本目标不存在"); + if (!s) throw new ApiException("not_found", "收录版本不存在"); slotIds = [s.id]; } else { const entityId = body.entityId ?? rel.media_entity_id; @@ -444,7 +444,7 @@ export function registerAgentRoutes( } const rejected: { slotId: string | null; code: string; message: string }[] = []; if (slotIds.length === 0) - rejected.push({ slotId: null, code: "no_slot", message: "这个作品没有版本目标,先在订阅里新建" }); + rejected.push({ slotId: null, code: "no_slot", message: "这个作品没有收录版本,先在订阅里新建" }); const invokeCtx = { actorId, taskId: `http:${c.get("requestId") ?? "req"}`, diff --git a/apps/server/src/modules/agent/runner.ts b/apps/server/src/modules/agent/runner.ts index 2b7be3ec..19ee3d1a 100644 --- a/apps/server/src/modules/agent/runner.ts +++ b/apps/server/src/modules/agent/runner.ts @@ -288,7 +288,7 @@ function systemPromptFor( constraints.push("本任务由后台事件触发,只做只读调查:收集证据,给出结论与建议,不要反复重试同一操作。"); const opts = sessionOptions(run); return buildSystemPrompt({ - role: run.trigger === "event" ? "媒体库异常调查员" : "媒体库自动化 Agent", + role: run.trigger === "event" ? "媒体库异常调查员" : "媒体库自动化智能体", goal: run.goal, constraints, // 自动化优先:用户会话默认执行,先出步骤清单;有取舍时给方案;"先看方案再执行"是会话选项 @@ -326,8 +326,8 @@ async function dailyBudgetBlock(env: AgentEnv, s: GlobalSettings, run: AgentRunR from agent_run where started_at >= ${day} and id <> ${run.id}`.execute(env.ctx.db); const n = r.rows[0]?.n ?? 0; const cost = r.rows[0]?.cost ?? 0; - if (n >= s.budget.agentMaxRunsPerDay) return `今日 Agent 运行次数已达上限 ${s.budget.agentMaxRunsPerDay}`; - if (cost >= s.budget.agentMaxCostUsdPerDay) return `今日 Agent 费用已达上限 $${s.budget.agentMaxCostUsdPerDay}`; + if (n >= s.budget.agentMaxRunsPerDay) return `今日智能体运行次数已达上限 ${s.budget.agentMaxRunsPerDay}`; + if (cost >= s.budget.agentMaxCostUsdPerDay) return `今日智能体费用已达上限 $${s.budget.agentMaxCostUsdPerDay}`; return null; } @@ -357,7 +357,7 @@ export async function drive( let settings: AgentSettings; try { const resolved = await resolveRunUpstream(ctx, run.upstream_id); - if (!resolved) throw new Error("未配置 Agent 上游(设置 → Agent → 上游)"); + if (!resolved) throw new Error("未配置模型服务(设置 → 智能体 → 模型服务)"); upstream = resolved; fake = upstream.row.provider === "fake"; settings = agentSettings(s, (run.budget ?? {}) as AgentBudget, fake, upstream); diff --git a/apps/server/src/modules/agent/service.ts b/apps/server/src/modules/agent/service.ts index f2352186..43a5bf76 100644 --- a/apps/server/src/modules/agent/service.ts +++ b/apps/server/src/modules/agent/service.ts @@ -152,7 +152,7 @@ async function enqueueRun(ctx: ServerContext, run: AgentRunRow, key: string) { /** 追加用户消息:运行中由当前会话在本轮结束后消费;已结束/等待中则唤醒续跑(新的 runAgent run) */ export async function appendMessage(ctx: ServerContext, runId: string, text: string, by: string): Promise { const run = await ctx.db.selectFrom("agent_run").selectAll().where("id", "=", runId).executeTakeFirst(); - if (!run) throw new AgentRunError("not_found", "Agent 运行不存在"); + if (!run) throw new AgentRunError("not_found", "智能体运行不存在"); const msg = await ctx.db .insertInto("agent_run_message") .values({ run_id: runId, text, created_by: by }) @@ -173,7 +173,7 @@ export async function appendMessage(ctx: ServerContext, runId: string, text: str /** 中止:只写取消标志,由会话监视器中止模型并走 finalize(不取消引擎 run,保证终态落库) */ export async function abortRun(ctx: ServerContext, runId: string, reason: string | undefined, by: string) { const run = await ctx.db.selectFrom("agent_run").selectAll().where("id", "=", runId).executeTakeFirst(); - if (!run) throw new AgentRunError("not_found", "Agent 运行不存在"); + if (!run) throw new AgentRunError("not_found", "智能体运行不存在"); if (TERMINAL.includes(run.status)) return run; const now = new Date(); const row = await ctx.db @@ -353,7 +353,7 @@ export async function finalize( // 同根因(同一待处理事项 / 同一定时巡检 / 同一目标模板)超预算合并到已有事项,不重复新建 await writeDecision(ctx, { kind: "other", - title: "Agent 运行超出预算", + title: "智能体运行超出预算", summary: res.message ?? "超出预算", subjectId: runId, dedupeKey: await budgetDedupeKey(ctx, run), diff --git a/apps/server/src/modules/agent/subagents.ts b/apps/server/src/modules/agent/subagents.ts index 027f28c1..9ae30e59 100644 --- a/apps/server/src/modules/agent/subagents.ts +++ b/apps/server/src/modules/agent/subagents.ts @@ -707,9 +707,9 @@ export interface SpawnDeps { async function parentOf(ctx: ServerContext, c: HandlerContext): Promise { const id = c.agentRunId; - if (!id) throw new CapabilityError("precondition", "只有 Agent 会话可以派生 / 等待子会话"); + if (!id) throw new CapabilityError("precondition", "只有智能体会话可以派生 / 等待子会话"); const run = await ctx.db.selectFrom("agent_run").selectAll().where("id", "=", id).executeTakeFirst(); - if (!run) throw new CapabilityError("not_found", "Agent 会话不存在"); + if (!run) throw new CapabilityError("not_found", "智能体会话不存在"); return run; } @@ -719,7 +719,7 @@ export function registerSubagentCapabilities(registry: CapabilityRegistry, ctx: const parent = await parentOf(ctx, c); const s = await ctx.settings.all(); const cfg = subagentSettings(s); - if (!cfg.enabled) throw new CapabilityError("precondition", "子会话未开启(设置 → Agent → 允许子会话)"); + if (!cfg.enabled) throw new CapabilityError("precondition", "子会话未开启(设置 → 智能体 → 允许子会话)"); if (parent.cancel_requested_at || TERMINAL.includes(parent.status)) throw new CapabilityError("precondition", "本会话已取消或已结束,不能派生子会话"); const dedupeKey = `subagent:${c.idempotencyKey ?? `${parent.id}:${Date.now()}`}`; diff --git a/apps/server/src/modules/auth-settings/instances-routes.ts b/apps/server/src/modules/auth-settings/instances-routes.ts index 9b6619e3..1ccea786 100644 --- a/apps/server/src/modules/auth-settings/instances-routes.ts +++ b/apps/server/src/modules/auth-settings/instances-routes.ts @@ -62,7 +62,7 @@ const test = (path: string, summary: string, tag: string) => ({ type Table = "library_root" | "downloader_instance" | "media_server_instance" | "path_mapping"; const NOUN: Record = { - library_root: "库根", + library_root: "媒体库目录", downloader_instance: "下载器实例", media_server_instance: "媒体服务器实例", path_mapping: "路径映射", @@ -71,7 +71,7 @@ const JSON_COLS = ["settings", "version_info", "health_detail", "verify_detail"] const errMessage = (e: unknown) => redactString(String((e as Error)?.message ?? e)).slice(0, 500); -const LR = crudDefs("/library-roots", "库根", { +const LR = crudDefs("/library-roots", "媒体库目录", { row: LibraryRoot, create: LibraryRootCreate, update: LibraryRootUpdate, @@ -181,7 +181,7 @@ export function registerInstanceRoutes(app: ModuleApp, ctx: ServerContext) { } catch (e) { throw new ApiException( "precondition_failed", - `库根不可写(需要目录存在且可写入哨兵文件 ${name}):${errMessage(e)}`, + `媒体库目录不可写(需要目录存在且可写入标记文件 ${name}):${errMessage(e)}`, ); } return name; @@ -215,7 +215,7 @@ export function registerInstanceRoutes(app: ModuleApp, ctx: ServerContext) { remove("library_root", "library-roots", "/library-roots/{id}", "library_root.delete"); endpoint( app, - test("/library-roots/{id}/test", "检查库根在线(目录 + 哨兵文件)", "library-roots"), + test("/library-roots/{id}/test", "检查媒体库目录在线(目录 + 哨兵文件)", "library-roots"), async ({ params, actor }) => { const row = await getRow("library_root", params.id); const h = await checkRoot(String(row.local_path), (row.sentinel_file as string | null) ?? DEFAULT_ROOT_SENTINEL); diff --git a/apps/server/src/modules/browser/__tests__/browser-api.test.ts b/apps/server/src/modules/browser/__tests__/browser-api.test.ts index dc3aefb0..813cc6bc 100644 --- a/apps/server/src/modules/browser/__tests__/browser-api.test.ts +++ b/apps/server/src/modules/browser/__tests__/browser-api.test.ts @@ -149,7 +149,7 @@ describe.skipIf(!up)("浏览器 HTTP 端点", () => { expect(files).toHaveLength(1); expect(new Uint8Array(await readFile(join(h.stagingDir, files[0]!)))).toEqual(h.torrent.bytes); - // 识别到作品后再下载:走同一流程(无版本目标 → no_slot);同一文件第二次捕获标 duplicate、不提交 + // 识别到作品后再下载:走同一流程(无收录版本 → no_slot);同一文件第二次捕获标 duplicate、不提交 const entity = await h.db .insertInto("media_entity") .values({ kind: "movie", title: "奥本海默", year: 2023, status: "active" }) diff --git a/apps/server/src/modules/browser/service.ts b/apps/server/src/modules/browser/service.ts index 4d411839..e56242d5 100644 --- a/apps/server/src/modules/browser/service.ts +++ b/apps/server/src/modules/browser/service.ts @@ -1579,7 +1579,7 @@ export class BrowserService { const give = opts.wantImage && !blocked && (!sensitive || opts.sensitiveAllowed); let note: string | null = null; if (blocked) note = "页面要求二次验证 / 短信 / 本人设备确认:不自动处理,会话已转等待处理,由人在原站视图接管"; - else if (!opts.wantImage) note = "当前上游模型不支持图片输入,截图没有交给模型;下面是页面的文字快照"; + else if (!opts.wantImage) note = "当前模型不支持图片输入,截图没有交给模型;下面是页面的文字快照"; else if (sensitive && !opts.sensitiveAllowed) note = "登录页 / 含密码框的截图属于敏感截图,默认不交给模型(需要 browser.sensitive_screenshot 持续授权并传 allowSensitive);下面是已脱敏的文字快照"; @@ -1801,7 +1801,7 @@ const REASON_TEXT: Record = { challenge_unsolved: "自动处理页面挑战未成功", login_required: "登录已失效:接管后在画面里登录一次,交还后全站生效", lease_expired: "人工接管超时未交还", - agent_request: "Agent 请求人工处理", + agent_request: "智能体请求人工处理", human_released_waiting: "人工交还时选择继续等待", }; diff --git a/apps/server/src/modules/file-manager/organize.ts b/apps/server/src/modules/file-manager/organize.ts index 867ad2aa..0975f712 100644 --- a/apps/server/src/modules/file-manager/organize.ts +++ b/apps/server/src/modules/file-manager/organize.ts @@ -231,7 +231,7 @@ export async function organizeFiles( if (it.targetDir) { const t = await resolveIn(d, it.targetDir.root, it.targetDir.path); if (!t.lst?.isDirectory() || t.isSymlink || !t.def.libraryRootId) - throw new FilesError("bad_request", `目标目录必须是库根下已存在的目录:${it.targetDir.path || "/"}`); + throw new FilesError("bad_request", `目标目录必须是媒体库目录下已存在的目录:${it.targetDir.path || "/"}`); if (!(await isWritableTarget(t.def, t.rootReal))) throw new FilesError("not_writable", `目标根 ${t.def.name} 不可写`); targetDir = join(t.def.hostPath, t.rel); @@ -264,10 +264,10 @@ export async function organizeFiles( if (slotId) { const s = slots.get(slotId); if (!s || s.media_entity_id !== entityId) { - warnings.push(`${r.rel}:版本目标 ${slotId} 不属于该作品,未应用`); + warnings.push(`${r.rel}:收录版本 ${slotId} 不属于该作品,未应用`); slotId = null; } else if (BUSY_SLOT.has(s.status)) { - warnings.push(`${r.rel}:版本目标正在获取中(${s.status}),不改其状态`); + warnings.push(`${r.rel}:收录版本正在获取中(${s.status}),不改其状态`); slotId = null; } } diff --git a/apps/server/src/modules/file-manager/probe.ts b/apps/server/src/modules/file-manager/probe.ts index f33e9a82..590e2c29 100644 --- a/apps/server/src/modules/file-manager/probe.ts +++ b/apps/server/src/modules/file-manager/probe.ts @@ -120,7 +120,7 @@ export async function scrape( .where("media_entity_id", "=", e.id) .where("status", "=", "active") .executeTakeFirst(); - if (!registered) throw new FilesError("bad_request", "作品没有已登记资产。先识别并整理入库,再刮削"); + if (!registered) throw new FilesError("bad_request", "作品没有已入库文件。先识别并整理入库,再刮削"); const { runId } = await d.ctx.engine.enqueue( "refreshMetadata", { mediaEntityId: e.id }, diff --git a/apps/server/src/modules/file-manager/transfer.ts b/apps/server/src/modules/file-manager/transfer.ts index 9c026cf0..742ee55f 100644 --- a/apps/server/src/modules/file-manager/transfer.ts +++ b/apps/server/src/modules/file-manager/transfer.ts @@ -95,7 +95,7 @@ export async function transfer( const t = await resolveIn(d, body.target.root, body.target.path); if (!t.lst?.isDirectory() || t.isSymlink) throw new FilesError("bad_request", "目标必须是已存在的目录"); if (!t.def.libraryRootId || !(await isWritableTarget(t.def, t.rootReal))) - throw new FilesError("not_writable", `目标根 ${t.def.name} 不可写,只能选 managed 库根`); + throw new FilesError("not_writable", `目标根 ${t.def.name} 不可写,只能选 可写的媒体库目录`); const sources: Resolved[] = []; for (const it of body.items) { diff --git a/apps/server/src/modules/file-manager/trash.ts b/apps/server/src/modules/file-manager/trash.ts index 2b00e995..2806eb17 100644 --- a/apps/server/src/modules/file-manager/trash.ts +++ b/apps/server/src/modules/file-manager/trash.ts @@ -157,7 +157,7 @@ const BLOCK_ERR: Record< > = { download_active: ["download_active", "属于未完成或做种义务未满足的下载任务"], is_root: ["bad_request", "不能删除根目录或只读根下的内容"], - needs_retire: ["needs_retire", "含已登记资产,需勾选「同时退役资产」"], + needs_retire: ["needs_retire", "含已入库文件,需勾选「同时从媒体库移除」"], needs_unbind: ["needs_unbind", "是作品/季的绑定目录,需勾选「解除绑定」"], root_unavailable: ["root_unavailable", "根不可用"], not_found: ["not_found", "不存在"], @@ -276,8 +276,8 @@ export async function deleteFiles( for (const s of slots) await upsertDecision(d.ao, trx, { kind: "other", - title: `版本目标的文件已删除:${s.name}`, - summary: "满足该版本目标的资产已在文件管理中删除,版本目标改为需关注。确认是否重新获取", + title: `收录版本的文件已删除:${s.name}`, + summary: "满足该收录版本的文件已在文件管理中删除,收录版本改为需关注。确认是否重新获取", subjectType: "acquisition_slot", subjectId: s.id, dedupeKey: `files_delete_slot:${s.id}`, @@ -509,7 +509,7 @@ export async function restoreTrash( throw new FilesError("bad_request", "还原目标必须是已存在的目录"); // "还原到…"只能进可写的 managed 库根(与 move/copy 的目标规则一致;register_only / 下载根拒绝,验收 07 S-5) if (target && !(await isWritableTarget(target.def, target.rootReal))) - throw new FilesError("not_writable", `还原目标根 ${target.def.name} 不可写,只能选 managed 库根`); + throw new FilesError("not_writable", `还原目标根 ${target.def.name} 不可写,只能选 可写的媒体库目录`); for (const id of body.ids) { try { const row = await d.ctx.db.selectFrom("trash_item").selectAll().where("id", "=", id).executeTakeFirst(); diff --git a/apps/server/src/modules/mcp/catalog.ts b/apps/server/src/modules/mcp/catalog.ts index 3c514cae..ccc13c50 100644 --- a/apps/server/src/modules/mcp/catalog.ts +++ b/apps/server/src/modules/mcp/catalog.ts @@ -21,7 +21,7 @@ export const MCP_RESOURCES: readonly McpResourceDef[] = [ uri: "umt://settings/summary", name: "settings-summary", title: "设置概要", - description: "自动化总开关、只读对账、预算与 Agent 运行上限。不含凭证。", + description: "自动化总开关、只读对账、预算与智能体运行上限。不含凭证。", mimeType: "application/json", async load(ctx) { const s = await ctx.settings.all(); diff --git a/apps/server/src/modules/mcp/config-store.ts b/apps/server/src/modules/mcp/config-store.ts index eb731e8b..d495ea18 100644 --- a/apps/server/src/modules/mcp/config-store.ts +++ b/apps/server/src/modules/mcp/config-store.ts @@ -48,7 +48,7 @@ export function createMcpConfigStore(db: () => Kysely): McpConfigStore { /** 这个 Key 能不能连 MCP:总开关开着,且在允许列表里(列表为 null 表示不限) */ export function keyAllowed(cfg: McpServerConfig, principal: { kind: string; id: string }): string | null { - if (!cfg.enabled) return "MCP 服务端已停用。在设置 → Agent → 工具与 MCP 里启用"; + if (!cfg.enabled) return "MCP 服务端已停用。在设置 → 智能体 → 工具与 MCP 里启用"; if (principal.kind === "api_key" && cfg.allowedApiKeyIds && !cfg.allowedApiKeyIds.includes(principal.id)) { return "这个 API Key 不在 MCP 允许列表里"; } diff --git a/apps/server/src/modules/mcp/server.ts b/apps/server/src/modules/mcp/server.ts index c607b415..3caf4033 100644 --- a/apps/server/src/modules/mcp/server.ts +++ b/apps/server/src/modules/mcp/server.ts @@ -102,7 +102,7 @@ export function createMcpServer( prompts: { listChanged: false }, }, instructions: - "工具与控制台、内置 Agent 共用同一套能力、授权与审计。只读工具可以直接调用;有副作用的工具需要持续授权或单次审批,缺授权时返回 unauthorized,不要重复尝试。资源里有设置概要、待处理事项与订阅总览。", + "工具与控制台、内置智能体共用同一套能力、授权与审计。只读工具可以直接调用;有副作用的工具需要持续授权或单次审批,缺授权时返回 unauthorized,不要重复尝试。资源里有设置概要、待处理事项与订阅总览。", }, ); const limiter = sharedInvokeLimiter(); diff --git a/apps/server/src/modules/pi/index.ts b/apps/server/src/modules/pi/index.ts index cce60b12..3225108f 100644 --- a/apps/server/src/modules/pi/index.ts +++ b/apps/server/src/modules/pi/index.ts @@ -73,8 +73,8 @@ export function createPiModule( registerScheduledJob(ctx, { key: "pi.token_refresh", defaults: { - name: "上游登录刷新", - description: "订阅登录即将过期的 Agent 上游,提前刷新 token", + name: "模型服务登录刷新", + description: "订阅登录即将过期的模型服务,提前刷新 token", group: "maintain", schedule: { type: "interval", everyMs: interval }, }, diff --git a/apps/server/src/modules/pi/models.ts b/apps/server/src/modules/pi/models.ts index f9524774..448643fb 100644 --- a/apps/server/src/modules/pi/models.ts +++ b/apps/server/src/modules/pi/models.ts @@ -92,12 +92,12 @@ function keyOf(cred: UpstreamCredential | undefined): string | null { /** 按 provider / 认证方式挑模型列表接口 */ function planFor(spec: UpstreamSpec, cred: UpstreamCredential | undefined): Plan { const extra = spec.extra ?? {}; - if (spec.provider === "fake") return { kind: "unsupported", reason: "Fake 上游没有远程模型列表" }; + if (spec.provider === "fake") return { kind: "unsupported", reason: "Fake 模型服务没有远程模型列表" }; if (spec.provider === "radius") return { kind: "pi" }; const custom = spec.provider === "openai-compatible"; // 订阅登录只有 OpenRouter 换到的是普通 API Key,其它订阅 token 不能调公开的模型列表接口 if (spec.authKind === "oauth" && spec.provider !== "openrouter") - return { kind: "unsupported", reason: "订阅登录的上游没有公开的模型列表接口,用内置模型表" }; + return { kind: "unsupported", reason: "订阅登录的模型服务没有公开的模型列表接口,用内置模型表" }; const key = keyOf(cred); if (spec.authKind !== "none" && !key && !custom) throw new FetchError("auth", "还没有配置 API Key"); @@ -232,7 +232,7 @@ function normalizeGoogle(item: Record): Remote | null { function parseList(shape: Shape, body: unknown): Remote[] { const b = obj(body); const raw = Array.isArray(body) ? body : Array.isArray(b.data) ? b.data : Array.isArray(b.models) ? b.models : null; - if (!raw) throw new FetchError("other", "上游返回的不是模型列表"); + if (!raw) throw new FetchError("other", "模型服务返回的不是模型列表"); const norm = shape === "anthropic" ? normalizeAnthropic : shape === "google" ? normalizeGoogle : normalizeOpenAI; const out: Remote[] = []; const seen = new Set(); @@ -376,7 +376,7 @@ async function fetchRemote( if (plan.kind === "unsupported") throw new FetchError("unsupported", plan.reason); if (plan.kind === "http") return await fetchList(plan.endpoint); // Radius:pi-ai 自带的动态刷新 - if (!client) throw new FetchError("config", "上游配置不完整"); + if (!client) throw new FetchError("config", "模型服务配置不完整"); const r = await client.listModels({ refresh: true }); if (!r.refreshed) throw toFetchError(new Error(r.refreshError ?? "刷新失败")); return r.items.map(fromInfo); diff --git a/apps/server/src/modules/pi/oauth.ts b/apps/server/src/modules/pi/oauth.ts index 26afd5f5..04aab222 100644 --- a/apps/server/src/modules/pi/oauth.ts +++ b/apps/server/src/modules/pi/oauth.ts @@ -105,8 +105,8 @@ export class OAuthSessions { async start(row: PiUpstreamRow, req: { mode?: PiOAuthMode; enterpriseDomain?: string }): Promise { const info = providerInfo(row.provider); if (row.auth_kind !== "oauth" || !info?.oauth) - throw new ApiException("precondition_failed", "这个上游不是订阅登录类型"); - if (!row.enabled) throw new ApiException("precondition_failed", "上游已停用"); + throw new ApiException("precondition_failed", "这个模型服务不是订阅登录类型"); + if (!row.enabled) throw new ApiException("precondition_failed", "模型服务已停用"); const mode = req.mode ?? info.oauth.modes[0]!; if (!info.oauth.modes.includes(mode)) throw new ApiException( @@ -270,6 +270,6 @@ export class OAuthSessions { export async function requireRow(ctx: ServerContext, id: string): Promise { const row = await getRow(ctx, id); - if (!row) throw new ApiException("not_found", "上游不存在"); + if (!row) throw new ApiException("not_found", "模型服务不存在"); return row; } diff --git a/apps/server/src/modules/pi/refresh.ts b/apps/server/src/modules/pi/refresh.ts index db35dd04..15f0b6cd 100644 --- a/apps/server/src/modules/pi/refresh.ts +++ b/apps/server/src/modules/pi/refresh.ts @@ -24,13 +24,13 @@ const DECISION_KEY = (id: string) => `pi_upstream_expired:${id}`; /** 与其它模块的 upsertDecision 同一写法(跨模块不 import):同键未关闭时只累加 attempts */ async function upsertExpiredDecision(ctx: ServerContext, row: PiUpstreamRow, message: string) { - const title = `Agent 上游「${row.name}」登录已失效,需要重新登录`; + const title = `模型服务「${row.name}」登录已失效,需要重新登录`; await ctx.db.transaction().execute(async (trx) => { const r = await sql<{ id: string; inserted: boolean }>` insert into decision (kind, severity, title, summary, subject_type, subject_id, dedupe_key, evidence, options) values ('other', 'warning', ${title}, ${message}, 'pi_upstream', ${row.id}, ${DECISION_KEY(row.id)}, ${JSON.stringify({ provider: row.provider, upstreamId: row.id, error: message })}::jsonb, - ${JSON.stringify([{ key: "relogin", label: "重新登录", description: "设置 → Agent → 上游" }])}::jsonb) + ${JSON.stringify([{ key: "relogin", label: "重新登录", description: "设置 → 智能体 → 模型服务" }])}::jsonb) on conflict (dedupe_key) where status in ('open', 'investigating', 'awaiting_user') do update set attempts = decision.attempts + 1, evidence = excluded.evidence, summary = excluded.summary, updated_at = now() @@ -102,7 +102,7 @@ export async function refreshPiTokens(ctx: ServerContext, opts: RefreshOptions = if (r.ok) report.refreshed += 1; else { report.failed.push({ upstreamId: row.id, message: r.message }); - ctx.logger.warn({ upstreamId: row.id, provider: row.provider, message: r.message }, "Agent 上游 token 刷新失败"); + ctx.logger.warn({ upstreamId: row.id, provider: row.provider, message: r.message }, "模型服务 token 刷新失败"); } } return report; diff --git a/apps/server/src/modules/pi/service.ts b/apps/server/src/modules/pi/service.ts index c1ec85ce..3ffd541b 100644 --- a/apps/server/src/modules/pi/service.ts +++ b/apps/server/src/modules/pi/service.ts @@ -116,7 +116,7 @@ export async function createUpstream( .execute(); } catch (err) { if (secretRef) await ctx.secrets.remove(secretRef).catch(() => {}); - if (isUnique(err)) throw new ApiException("conflict", `上游名称「${body.name}」已存在`); + if (isUnique(err)) throw new ApiException("conflict", `模型服务名称「${body.name}」已存在`); throw err; } if (body.makeDefault || !(await defaultUpstreamId(ctx))) await setDefaultUpstream(ctx, id); @@ -140,7 +140,7 @@ export async function updateUpstream( const defaultModel = body.defaultModel !== undefined ? body.defaultModel : row.default_model; validateShape({ provider: row.provider, authKind, baseUrl, defaultModel }); if (body.apiKey && authKind !== "api_key") - throw new ApiException("validation_failed", "只有 api_key 型上游可以填 apiKey"); + throw new ApiException("validation_failed", "只有 api_key 型模型服务可以填 apiKey"); const kindChanged = authKind !== row.auth_kind; const store = dbCredentialStore(ctx, row.id); @@ -167,7 +167,7 @@ export async function updateUpstream( .where("id", "=", row.id) .execute() .catch((err) => { - if (isUnique(err)) throw new ApiException("conflict", `上游名称「${body.name}」已存在`); + if (isUnique(err)) throw new ApiException("conflict", `模型服务名称「${body.name}」已存在`); throw err; }); if (typeof body.apiKey === "string") { @@ -217,10 +217,11 @@ export async function resolveRunUpstream(ctx: ServerContext, upstreamId?: string const id = upstreamId ?? (await defaultUpstreamId(ctx)); if (!id) return null; const row = await getRow(ctx, id); - if (!row) throw new Error(upstreamId ? `指定的上游 ${upstreamId} 不存在` : "默认上游已不存在,在设置里重新选择"); - if (!row.enabled) throw new Error(`上游「${row.name}」已停用`); - if (row.status === "expired") throw new Error(`上游「${row.name}」登录已失效,在设置里重新登录`); + if (!row) + throw new Error(upstreamId ? `指定的模型服务 ${upstreamId} 不存在` : "默认模型服务已不存在,在设置里重新选择"); + if (!row.enabled) throw new Error(`模型服务「${row.name}」已停用`); + if (row.status === "expired") throw new Error(`模型服务「${row.name}」登录已失效,在设置里重新登录`); if (row.status === "auth_required") - throw new Error(`上游「${row.name}」${row.auth_kind === "oauth" ? "未登录" : "未配置 API Key"}`); + throw new Error(`模型服务「${row.name}」${row.auth_kind === "oauth" ? "未登录" : "未配置 API Key"}`); return { row, spec: { ...specOf(row), extra: parseExtra(row.extra) }, credentials: dbCredentialStore(ctx, row.id) }; } diff --git a/apps/server/src/modules/pi/store.ts b/apps/server/src/modules/pi/store.ts index 746de61c..3b68274b 100644 --- a/apps/server/src/modules/pi/store.ts +++ b/apps/server/src/modules/pi/store.ts @@ -59,7 +59,7 @@ async function readSecret(ctx: Ctx, row: Pick> { const out = new Map(); if (ids.length === 0) return out; diff --git a/apps/server/src/modules/sources-identity/index.ts b/apps/server/src/modules/sources-identity/index.ts index 8fc18caf..770f0562 100644 --- a/apps/server/src/modules/sources-identity/index.ts +++ b/apps/server/src/modules/sources-identity/index.ts @@ -63,7 +63,7 @@ export function createSourcesIdentityModule(overrides: RuntimeOverrides = {}): S }); job("feeds.poll", "Feed 轮询", "扫描到期的 Feed 并入队抓取(每个 Feed 仍按自己的轮询间隔)", tickFeeds); job("sites.snapshot", "站点快照", "扫描到周期的站点并入队快照", tickSnapshots); - job("identity.review", "待识别复查", "到期的待识别条目重新识别", tickReviews); + job("identity.review", "未识别复查", "到期的未识别条目重新识别", tickReviews); // W9-A 站点中心:用户数据每 6 小时、健康检测每小时(可在定时任务页关闭或改周期) registerScheduledJob(ctx, { key: "sites.user_data", diff --git a/apps/server/src/modules/sources-identity/routes/enrich.ts b/apps/server/src/modules/sources-identity/routes/enrich.ts index cefdb7cf..4659183f 100644 --- a/apps/server/src/modules/sources-identity/routes/enrich.ts +++ b/apps/server/src/modules/sources-identity/routes/enrich.ts @@ -79,7 +79,7 @@ export function enrichRoutes(app: ModuleApp, rt: () => SourcesRuntime): void { path: "/identity-pool/{id}/enrich/apply", scope: "admin", auth: "session", - summary: "应用元数据补全建议:建本地临时作品(或并入同名作品)并关联待识别资源(只接受控制台管理员会话)", + summary: "应用元数据补全建议:建本地临时作品(或并入同名作品)并关联未识别资源(只接受控制台管理员会话)", tags: T_ID, params: IdParam, body: EnrichApplyInput, diff --git a/apps/server/src/modules/sources-identity/routes/identity.ts b/apps/server/src/modules/sources-identity/routes/identity.ts index 7fca1f93..77b7fccf 100644 --- a/apps/server/src/modules/sources-identity/routes/identity.ts +++ b/apps/server/src/modules/sources-identity/routes/identity.ts @@ -542,7 +542,7 @@ export function identityRoutes(app: ModuleApp, rt: () => SourcesRuntime) { method: "get", path: "/identity-pool", scope: "read", - summary: "待识别池(可按原始标题检索;候选不建正式作品)", + summary: "未识别资源(可按原始标题检索;候选不建正式作品)", tags: T_ID, query: IdentityPoolQuery, response: cursorPage(IdentityPoolItem), @@ -619,7 +619,7 @@ export function identityRoutes(app: ModuleApp, rt: () => SourcesRuntime) { method: "post", path: "/identity-pool/{id}/assign", scope: "organize.execute", - summary: "为待识别资源手动指定候选 / 本地作品 / 外部身份", + summary: "为未识别资源手动指定候选 / 本地作品 / 外部身份", tags: T_ID, params: IdParam, body: ReleaseIdentityAssign, @@ -631,7 +631,7 @@ export function identityRoutes(app: ModuleApp, rt: () => SourcesRuntime) { .select(["source_release_id"]) .where("id", "=", params.id) .executeTakeFirst(); - if (!pool) throw new ApiException("not_found", "待识别条目不存在"); + if (!pool) throw new ApiException("not_found", "未识别条目不存在"); return assignReleaseIdentity(rt(), pool.source_release_id, body, actor); }, ); @@ -641,7 +641,7 @@ export function identityRoutes(app: ModuleApp, rt: () => SourcesRuntime) { method: "post", path: "/identity-pool/{id}/dismiss", scope: "organize.execute", - summary: "忽略待识别资源(不再自动复查)", + summary: "忽略未识别资源(不再自动复查)", tags: T_ID, params: IdParam, body: z.object({ note: z.string().max(2000).optional() }), @@ -666,7 +666,7 @@ export function identityRoutes(app: ModuleApp, rt: () => SourcesRuntime) { .select(["source_release_id", "attempts"]) .where("id", "=", params.id) .executeTakeFirst(); - if (!pool) throw new ApiException("not_found", "待识别条目不存在"); + if (!pool) throw new ApiException("not_found", "未识别条目不存在"); const r = await rt().ctx.engine.enqueue( "resolveObservation", // 用户点"重新识别" = 手动触发:即使不相关也查 TMDB(惰性识别的 c 路径) diff --git a/apps/server/src/modules/sources-identity/service/enrich.ts b/apps/server/src/modules/sources-identity/service/enrich.ts index 0953d40a..513a576f 100644 --- a/apps/server/src/modules/sources-identity/service/enrich.ts +++ b/apps/server/src/modules/sources-identity/service/enrich.ts @@ -43,7 +43,7 @@ const MAX_SEARCHES = 2; const SITE_FILTER_DOMAINS = 6; /** 交给模型的每页正文上限 */ const PAGE_CHARS_FOR_MODEL = 6000; -export const ENRICH_NOTICE = "字段来自网页与 Agent 抽取,不是 TMDB 数据;应用前核对出处"; +export const ENRICH_NOTICE = "字段来自网页与智能体抽取,不是 TMDB 数据;应用前核对出处"; export class EnrichError extends Error { constructor( @@ -411,7 +411,7 @@ async function resolveQuery(ctx: Pick, input: MetadataEnric .select(["p.id", "p.query", "r.title as release_title"]) .where("p.id", "=", input.poolEntryId) .executeTakeFirst(); - if (!p) throw new EnrichError("not_found", "待识别条目不存在"); + if (!p) throw new EnrichError("not_found", "未识别条目不存在"); const q = (p.query ?? {}) as { title?: string | null; titleCn?: string | null; year?: number | null }; const title = (q.titleCn || q.title || "").trim(); if (!title) throw new EnrichError("precondition", "这条资源没有解析出标题,无法补全"); @@ -737,7 +737,7 @@ export async function applyEnrichment( .select(["id", "source_release_id", "status"]) .where("id", "=", poolEntryId) .executeTakeFirst(); - if (!pool) throw new ApiException("not_found", "待识别条目不存在"); + if (!pool) throw new ApiException("not_found", "未识别条目不存在"); const row = await ctx.db .selectFrom("metadata_enrichment") .selectAll() diff --git a/apps/server/src/modules/sources-identity/service/identity-admin.ts b/apps/server/src/modules/sources-identity/service/identity-admin.ts index 76ffec59..cdd27b6c 100644 --- a/apps/server/src/modules/sources-identity/service/identity-admin.ts +++ b/apps/server/src/modules/sources-identity/service/identity-admin.ts @@ -44,9 +44,12 @@ async function entityForExternal( const hit = await entityByExternal(exec, ref); if (hit) return { entityId: hit, created: false }; if (ref.provider !== "tmdb") - throw new ApiException("bad_request", `未登记的外部身份 ${ref.provider}:${ref.externalId},只支持按 TMDB 建档`); + throw new ApiException( + "bad_request", + `未登记的外部身份 ${ref.provider}:${ref.externalId},只支持按 TMDB 加入媒体库`, + ); const tmdb = await rt.tmdb(); - if (!tmdb) throw new ApiException("precondition_failed", "未配置 TMDB,无法按外部身份建档"); + if (!tmdb) throw new ApiException("precondition_failed", "未配置 TMDB,无法按外部编号加入媒体库"); let detail: Awaited>; try { detail = await tmdb.details(ref.entityType, ref.externalId); @@ -475,7 +478,7 @@ export async function dismissPoolEntry(rt: SourcesRuntime, poolId: string, actor .where("id", "=", poolId) .returningAll() .executeTakeFirst(); - if (!row) throw new ApiException("not_found", "待识别条目不存在"); + if (!row) throw new ApiException("not_found", "未识别条目不存在"); await trx .updateTable("source_release") .set({ resolution_status: "rejected" }) diff --git a/apps/server/src/modules/sources-identity/service/identity-decisions.ts b/apps/server/src/modules/sources-identity/service/identity-decisions.ts index 0dd5473f..3547fec5 100644 --- a/apps/server/src/modules/sources-identity/service/identity-decisions.ts +++ b/apps/server/src/modules/sources-identity/service/identity-decisions.ts @@ -25,7 +25,7 @@ function manualIdentityOptions(sourceReleaseId: string): DecisionOption[] { { key: "manual_tmdb", label: "手动指定 TMDB ID", - description: "按 TMDB 电影或剧集 ID 建档并关联,加人工锁", + description: "按 TMDB 电影或剧集 ID 加入媒体库并关联,加人工锁", input: { sourceReleaseId, requires: "tmdb" }, }, { @@ -79,7 +79,7 @@ function titleFor( one: string, work: string | null, ) { - const what = kind === "identity_ambiguous" ? "待确认身份" : "待识别"; + const what = kind === "identity_ambiguous" ? "没识别出作品" : "未识别"; if (n <= 1) return `${what}:${one}`; return `来源「${siteName}」的 ${n} 条资源${what}${work ? `:${work}` : ""}`; } diff --git a/apps/server/src/modules/system/retention.ts b/apps/server/src/modules/system/retention.ts index 93185f4d..1c0bf0a6 100644 --- a/apps/server/src/modules/system/retention.ts +++ b/apps/server/src/modules/system/retention.ts @@ -167,8 +167,8 @@ export const RETENTION_JOBS: RetentionJob[] = [ { target: "agent_event", jobId: "storage.retention.agent_event", - name: "Agent 事件清理", - description: "删除超过保留期、会话已结束的 Agent 事件流与消息(agent_run_event / agent_run_message;会话本身保留)", + name: "智能体事件清理", + description: "删除超过保留期、会话已结束的智能体事件流与消息(agent_run_event / agent_run_message;会话本身保留)", everyMs: 6 * HOUR, retention: (s) => `${s.retentionDays.agentEvent} 天`, async run(c) { @@ -182,7 +182,7 @@ export const RETENTION_JOBS: RetentionJob[] = [ sql`created_at < ${cutoff} and status = 'consumed' and ${ended}`, c.batch, ); - return `Agent 事件 ${ev.deleted}、消息 ${msg.deleted} 行已删除${ev.truncated || msg.truncated ? ",未删完,下次继续" : ""}`; + return `智能体事件 ${ev.deleted}、消息 ${msg.deleted} 行已删除${ev.truncated || msg.truncated ? ",未删完,下次继续" : ""}`; }, }, { @@ -230,8 +230,8 @@ export const RETENTION_JOBS: RetentionJob[] = [ { target: "identity_pool", jobId: "storage.retention.identity_pool", - name: "待识别池归档", - description: "待识别池里 unmatched 条目超过保留期没有新证据就归档(不删除,仍可按状态筛出)", + name: "未识别资源归档", + description: "未识别资源里 unmatched 条目超过保留期没有新证据就归档(不删除,仍可按状态筛出)", everyMs: 6 * HOUR, retention: (s) => `${s.retentionDays.identityPoolArchive} 天`, async run(c) { @@ -246,7 +246,7 @@ export const RETENTION_JOBS: RetentionJob[] = [ archived += n; if (n < size) break; } - return `归档 ${archived} 条待识别条目`; + return `归档 ${archived} 条未识别条目`; }, }, { diff --git a/apps/server/src/modules/websearch/service.ts b/apps/server/src/modules/websearch/service.ts index 5187a61f..d8b46333 100644 --- a/apps/server/src/modules/websearch/service.ts +++ b/apps/server/src/modules/websearch/service.ts @@ -88,7 +88,7 @@ export class WebSearchService { private async client(s: WebSearchSettings) { if (s.provider === "none") - throw new WebSearchServiceError("disabled", "网页搜索未开启,在设置 → Agent → 网页搜索里开启"); + throw new WebSearchServiceError("disabled", "网页搜索未开启,在设置 → 智能体 → 网页搜索里开启"); if (!s.apiKeySecretRef) throw new WebSearchServiceError("auth", "未保存搜索服务 API Key"); const apiKey = await this.ctx.secrets.resolve(s.apiKeySecretRef); return createBraveClient({ @@ -175,7 +175,7 @@ export class WebSearchService { async fetch(url: string, o: { scopeKey: string; signal?: AbortSignal }): Promise { const s = await this.settings(); if (s.provider === "none") - throw new WebSearchServiceError("disabled", "网页搜索未开启,在设置 → Agent → 网页搜索里开启"); + throw new WebSearchServiceError("disabled", "网页搜索未开启,在设置 → 智能体 → 网页搜索里开启"); const want = canonicalUrl(url.trim()); const allow = await pgCacheFor(this.ctx.db).get<{ urls: string[] }>(WEBSEARCH_ALLOW_REGION, o.scopeKey); if (!want || !allow?.urls.includes(want)) diff --git a/apps/server/test/bootstrap.test.ts b/apps/server/test/bootstrap.test.ts index 736db048..64357cac 100644 --- a/apps/server/test/bootstrap.test.ts +++ b/apps/server/test/bootstrap.test.ts @@ -158,7 +158,7 @@ describe.skipIf(!hasPg)("bootstrap + worker(真实 PG)", () => { decisionId, kind: "identity_unresolved", severity: "info", - title: "待识别", + title: "未识别", }); }); const deadline = Date.now() + 20_000; diff --git a/apps/server/test/e2e/e12-interaction.test.ts b/apps/server/test/e2e/e12-interaction.test.ts index f6f22e45..31845c93 100644 --- a/apps/server/test/e2e/e12-interaction.test.ts +++ b/apps/server/test/e2e/e12-interaction.test.ts @@ -93,7 +93,7 @@ const optionsScript = (goal: string): FakeScript => ({ }, }); -// 审批:两个目标各用一个版本目标(拒绝与批准互不影响);ID 在 beforeAll 里填 +// 审批:两个目标各用一个收录版本(拒绝与批准互不影响);ID 在 beforeAll 里填 const T: Record<"approve" | "reject", { slotId: string; releaseId: string }> = { approve: { slotId: ZERO, releaseId: ZERO }, reject: { slotId: ZERO, releaseId: ZERO }, diff --git a/apps/server/test/e2e/e15-agent-quality.test.ts b/apps/server/test/e2e/e15-agent-quality.test.ts index 0dfa6e40..449fbf87 100644 --- a/apps/server/test/e2e/e15-agent-quality.test.ts +++ b/apps/server/test/e2e/e15-agent-quality.test.ts @@ -15,7 +15,7 @@ type Any = any; const hasPg = await pgAvailable(); const HASH = "ab".repeat(20); const SURVEY = "盘点订阅、待处理与下载"; -const LOOP = (at: string) => `巡检库存(${at}):逐个作品查看版本目标`; +const LOOP = (at: string) => `巡检库存(${at}):逐个作品查看收录版本`; describe.skipIf(!hasPg)("E15 Agent 质量(fake 模型 + 真实能力)", () => { const scripts: FakeScript[] = []; diff --git a/apps/server/test/pi/upstreams.test.ts b/apps/server/test/pi/upstreams.test.ts index ad82b41a..40dbf37d 100644 --- a/apps/server/test/pi/upstreams.test.ts +++ b/apps/server/test/pi/upstreams.test.ts @@ -394,7 +394,7 @@ describe.skipIf(!hasPg)("Pi 上游(真实 PG)", () => { const r4 = await newRun(null); const d4 = await drive(env, h.ctx.capabilities, r4, new AbortController().signal); expect(d4).toMatchObject({ status: "failed" }); - expect(d4.message).toMatch(/未配置 Agent 上游/); + expect(d4.message).toMatch(/未配置模型服务/); await setDefaultUpstream(h.ctx, fake.id); }); }); diff --git a/apps/server/test/rules/rule-groups.test.ts b/apps/server/test/rules/rule-groups.test.ts index bbd8aeed..e39a3aad 100644 --- a/apps/server/test/rules/rule-groups.test.ts +++ b/apps/server/test/rules/rule-groups.test.ts @@ -154,7 +154,7 @@ describe.skipIf(!hasPg)("规则组 API(真实 PG)", () => { expect(draft.body.data.items[0]).toMatchObject({ passed: true, pri: 100 }); }); - it("评估接入:版本目标引用规则组 → 硬排除拒绝、pri 进候选评估 trace;被引用的组不能删", async () => { + it("评估接入:收录版本引用规则组 → 硬排除拒绝、pri 进候选评估 trace;被引用的组不能删", async () => { const e = await seedEntity(h.db, { kind: "movie", title: "规则片", year: 2024 }); const slot = await seedSlot(h.db, { entityId: e }); const g = await h.api("POST", "/rules", { name: "片源", ruleString: "4K > 1080P", hardExcludes: ["REMUX"] }); @@ -285,7 +285,7 @@ describe.skipIf(!hasPg)("规则组 API(真实 PG)", () => { expect(plan.evaluation).toMatchObject({ pri: 100 }); }); - it("默认链:订阅默认组有层时对未引用规则组的版本目标生效;PUT /rules/defaults 校验类型", async () => { + it("默认链:订阅默认组有层时对未引用规则组的收录版本生效;PUT /rules/defaults 校验类型", async () => { const defs = (await h.api("GET", "/rules/defaults")).body.data; const dflt = defs.acquisition[0]; expect((await h.api("PUT", "/rules/defaults", { ...defs, acquisition: defs.upgrade })).status).toBe(400); diff --git a/apps/server/test/search/search.test.ts b/apps/server/test/search/search.test.ts index 872a139a..07704ae4 100644 --- a/apps/server/test/search/search.test.ts +++ b/apps/server/test/search/search.test.ts @@ -293,7 +293,7 @@ describe.skipIf(!up)("全局搜索", () => { expect(a.body.data.items[0]).toMatchObject({ siteId: sites.mteam, siteReleaseId: "912234" }); }); - it("库存叠加:已识别资源 → resolved + 订阅/版本目标缺口;未识别但标题命中唯一作品别名 → alias", async () => { + it("库存叠加:已识别资源 → resolved + 订阅/收录版本缺口;未识别但标题命中唯一作品别名 → alias", async () => { const entity = ( await db .insertInto("media_entity") @@ -346,7 +346,7 @@ describe.skipIf(!up)("全局搜索", () => { const mt = items.find((i) => i.siteReleaseId === "990001"); expect(mt).toMatchObject({ mediaEntityId: entity, entityMatch: "alias", inventory: { state: "gap" } }); - // 本地作品组:别名精确命中 + 版本目标状态摘要 + // 本地作品组:别名精确命中 + 收录版本状态摘要 const all = await req("/search?q=沙丘2&scope=media"); const local = all.body.data.media.local.items; expect(local[0]).toMatchObject({ @@ -428,7 +428,7 @@ describe.skipIf(!up)("全局搜索", () => { expect(unresolved.status).toBe(412); expect(unresolved.body.error.code).toBe("precondition_failed"); - // 已识别但作品没有版本目标 → submitted=false + no_slot + // 已识别但作品没有收录版本 → submitted=false + no_slot const other = ( await db .insertInto("media_entity") diff --git a/apps/server/test/search/search2.test.ts b/apps/server/test/search/search2.test.ts index 4540bd54..3ff572d3 100644 --- a/apps/server/test/search/search2.test.ts +++ b/apps/server/test/search/search2.test.ts @@ -167,7 +167,7 @@ describe.skipIf(!up)("两阶段搜索(SEARCH-2)", () => { sites.c = await addSite("hdhome", "家园", null, true); await db.updateTable("site").set({ enabled: false }).where("id", "=", sites.c).execute(); - // 本地已建档的沙丘2(接受了 TMDB 693134),有一个未满足的版本目标 + // 本地已建档的沙丘2(接受了 TMDB 693134),有一个未满足的收录版本 dune = ( await db .insertInto("media_entity") diff --git a/apps/server/test/sources-identity/lazy-identity.test.ts b/apps/server/test/sources-identity/lazy-identity.test.ts index 3941c8c4..913181c5 100644 --- a/apps/server/test/sources-identity/lazy-identity.test.ts +++ b/apps/server/test/sources-identity/lazy-identity.test.ts @@ -177,7 +177,7 @@ describe.skipIf(!pg)("DEC-1 惰性识别 + 待识别池", () => { expect(ds).toHaveLength(1); const d = ds[0]!; expect(d.severity).toBe("warning"); - expect(d.title).toMatch(/^来源「.+」的 3 条资源待确认身份:Twins$/); + expect(d.title).toMatch(/^来源「.+」的 3 条资源没识别出作品:Twins$/); const ev = d.evidence as { count: number; releases: { sourceReleaseId: string }[] }; expect(ev.count).toBe(3); expect(ev.releases.map((x) => x.sourceReleaseId).sort()).toEqual([...ids].sort()); @@ -226,7 +226,7 @@ describe.skipIf(!pg)("DEC-1 惰性识别 + 待识别池", () => { expect(after.status).toBe("open"); expect((after.evidence as { count: number }).count).toBe(1); expect(after.subject_id).toBe(pool[1]!.source_release_id); - expect(after.title).toMatch(/^待确认身份:Nightfall\.1995\./); + expect(after.title).toMatch(/^没识别出作品:Nightfall\.1995\./); // 最后一条忽略 → 事项关闭 const last = await h.db .selectFrom("identity_candidate") @@ -296,7 +296,7 @@ describe.skipIf(!pg)("迁移 0047:存量 identity_unresolved 清理", () => { .values({ kind: "identity_unresolved", severity: "info", - title: `待识别:${title}`, + title: `未识别:${title}`, subject_type: "source_release", subject_id: r.id, dedupe_key: `identity:${r.id}`, diff --git a/apps/server/test/subscriptions/subscriptions-c1a.test.ts b/apps/server/test/subscriptions/subscriptions-c1a.test.ts index e2ee728f..5f3b94a4 100644 --- a/apps/server/test/subscriptions/subscriptions-c1a.test.ts +++ b/apps/server/test/subscriptions/subscriptions-c1a.test.ts @@ -26,7 +26,7 @@ import { presetConditions, } from "../../src/modules/acquisition-organize/services/subscriptions-params.ts"; -// C1-A 订阅遗留收尾:包含 / 排除 → 订阅私有规则组(评估链纳入、随订阅删除)、MP 分辨率 / 来源 / 特效 → 版本目标、 +// C1-A 订阅遗留收尾:包含 / 排除 → 订阅私有规则组(评估链纳入、随订阅删除)、MP 分辨率 / 来源 / 特效 → 收录版本、 // 保存路径经路径映射传给 qB、库根用于首次整理、下载器实例、重置进度、日历。 const hasPg = await pgAvailable(); @@ -182,7 +182,7 @@ describe.skipIf(!hasPg)("C1-A 订阅遗留收尾(真实 PG + mock qB)", () => { expect(again[0]!.hard).toEqual({ exclude: ["CAM"] }); }); - it("评估链纳入私有组:包含没命中 / 排除命中都拒绝;命中包含建计划;与版本目标自己的包含取 AND", async () => { + it("评估链纳入私有组:包含没命中 / 排除命中都拒绝;命中包含建计划;与收录版本自己的包含取 AND", async () => { const e = await seedEntity(db(), { kind: "movie", title: "Filter Film", year: 2024 }); const c = await h.api("POST", "/subscriptions", { mediaEntityId: e, @@ -207,7 +207,7 @@ describe.skipIf(!hasPg)("C1-A 订阅遗留收尾(真实 PG + mock qB)", () => { const ok = await release(e, "Filter.Film.2024.1080p.WEB-DL.CHS.x264-GRP"); const r3 = await evaluateAcquisition(h.d, { sourceReleaseId: ok }); expect(r3.plans, JSON.stringify(r3)).toHaveLength(1); - // 版本目标自己也要求 "GRP2":与私有组的 CHS 取 AND + // 收录版本自己也要求 "GRP2":与私有组的 CHS 取 AND await db().updateTable("acquisition_plan").set({ status: "cancelled" }).where("id", "=", r3.plans[0]!).execute(); await db() .updateTable("acquisition_slot") @@ -223,7 +223,7 @@ describe.skipIf(!hasPg)("C1-A 订阅遗留收尾(真实 PG + mock qB)", () => { expect((await evaluateAcquisition(h.d, { sourceReleaseId: both })).plans).toHaveLength(1); }); - it("MP 分辨率 / 来源 / 特效:新建时建默认版本目标承载预设;改预设覆盖同一字段,手改过的字段保留;无法识别 400", async () => { + it("MP 分辨率 / 来源 / 特效:新建时建默认收录版本承载预设;改预设覆盖同一字段,手改过的字段保留;无法识别 400", async () => { const e = await seedEntity(db(), { kind: "series", title: "预设剧", year: 2025 }); const bad = await h.api("POST", "/subscriptions", { mediaEntityId: e, @@ -461,7 +461,7 @@ describe.skipIf(!hasPg)("C1-A 订阅遗留收尾(真实 PG + mock qB)", () => { }); expect(c.status, JSON.stringify(c.body)).toBe(201); const subId = c.body.data.id as string; - // E01 已入库(单版本目标:作品下资产都算) + // E01 已入库(单收录版本:作品下资产都算) const a = await db() .insertInto("media_asset") .values({ diff --git a/apps/server/test/subscriptions/subscriptions-v2.test.ts b/apps/server/test/subscriptions/subscriptions-v2.test.ts index aa28b637..6f372ec8 100644 --- a/apps/server/test/subscriptions/subscriptions-v2.test.ts +++ b/apps/server/test/subscriptions/subscriptions-v2.test.ts @@ -264,7 +264,7 @@ describe.skipIf(!hasPg)("订阅重做(真实 PG)", () => { expect((await h.api("GET", "/subscriptions/00000000-0000-0000-0000-000000000000/coverage")).status).toBe(404); }); - it("覆盖矩阵:集范围裁剪;总集数未知列到已见最高集并标 suspected;电影按内容版本 × 版本目标", async () => { + it("覆盖矩阵:集范围裁剪;总集数未知列到已见最高集并标 suspected;电影按内容版本 × 收录版本", async () => { const e = await seedEntity(db(), { kind: "series", title: "范围剧", year: 2025 }); await season(e, 1, null); const e3 = await episode(e, 1, 3); @@ -325,7 +325,7 @@ describe.skipIf(!hasPg)("订阅重做(真实 PG)", () => { // 暂停 const e4 = await seedEntity(db(), { kind: "series", title: `${tag}丁`, year: 2022 }); const s4 = await sub(e4, { status: "paused" }); - // 已完成(电影:唯一版本目标已入库) + // 已完成(电影:唯一收录版本已入库) const e5 = await seedEntity(db(), { kind: "movie", title: `${tag}戊`, year: 2021 }); const a5 = await asset(e5); const s5 = await sub(e5, { kind: "movie", mode: "once" }); @@ -584,7 +584,7 @@ describe.skipIf(!hasPg)("订阅重做(真实 PG)", () => { expect(r.status, JSON.stringify(bad)).toBe(400); } - // PATCH:autoFill 布尔 → on/off;best_version → 版本目标升级策略 + // PATCH:autoFill 布尔 → on/off;best_version → 收录版本升级策略 const sid = c.body.data.id; const sl = await slot(e, sid, "main"); const u = await h.api("PATCH", `/subscriptions/${sid}`, { @@ -611,7 +611,7 @@ describe.skipIf(!hasPg)("订阅重做(真实 PG)", () => { ).toEqual({ enabled: false, mode: "full_pack" }); }); - it("暂停 / 恢复 / 重新订阅 / 批量 / 版本目标排序", async () => { + it("暂停 / 恢复 / 重新订阅 / 批量 / 收录版本排序", async () => { const e = await seedEntity(db(), { kind: "movie", title: "动作片", year: 2024 }); const s = await sub(e, { kind: "movie", mode: "once" }); const a = await slot(e, s, "A", { position: 0 }); diff --git a/apps/server/test/system/storage.test.ts b/apps/server/test/system/storage.test.ts index 7788d378..0ade5655 100644 --- a/apps/server/test/system/storage.test.ts +++ b/apps/server/test/system/storage.test.ts @@ -211,7 +211,7 @@ describe.skipIf(!hasPg)("CACHE-2 存储(真实 PG)", () => { (${done}, 1, 'run_started', ${old}), (${done}, 2, 'run_started', ${now}), (${live}, 1, 'run_started', ${old})`.execute( h.db, ); - expect(await job("agent_event")(now)).toMatch(/Agent 事件 1、/); + expect(await job("agent_event")(now)).toMatch(/智能体事件 1、/); expect(await count(sql<{ n: number }>`select count(*)::int as n from agent_run_event`)).toBe(2); await sql`delete from audit_event`.execute(h.db); @@ -250,7 +250,7 @@ describe.skipIf(!hasPg)("CACHE-2 存储(真实 PG)", () => { await cand("r1", "unmatched", 31); await cand("r2", "unmatched", 29); await cand("r3", "ambiguous", 31); - expect(await job("identity_pool")(now)).toBe("归档 1 条待识别条目"); + expect(await job("identity_pool")(now)).toBe("归档 1 条未识别条目"); const st = await sql<{ t: string; s: string }>`select sr.title as t, c.status as s from identity_candidate c join source_release sr on sr.id = c.source_release_id order by sr.title`.execute(h.db); expect(st.rows).toEqual([ diff --git a/apps/web/app/api/agent-mock.test.ts b/apps/web/app/api/agent-mock.test.ts index c86f01f9..11f4de5f 100644 --- a/apps/web/app/api/agent-mock.test.ts +++ b/apps/web/app/api/agent-mock.test.ts @@ -15,7 +15,7 @@ class MemStore { } // 验收 S7:生产构建里 ?mock=1 不得把演示数据当真实运行展示 -describe("Agent 演示开关", () => { +describe("智能体演示开关", () => { it("只在开发构建或后端 provider=fake 时允许", () => { expect(agentMockAllowed({ dev: true })).toBe(true); expect(agentMockAllowed({ dev: false, provider: "fake" })).toBe(true); diff --git a/apps/web/app/api/downloads.ts b/apps/web/app/api/downloads.ts index 86da4215..a23386bc 100644 --- a/apps/web/app/api/downloads.ts +++ b/apps/web/app/api/downloads.ts @@ -157,7 +157,7 @@ export const PAUSE_REASON_LABEL = { user: "用户暂停", threshold: "阈值暂停", policy: "策略暂停", - agent: "Agent 暂停", + agent: "智能体暂停", } as const; export const PLAN_STATUS_LABEL: Record = { diff --git a/apps/web/app/api/feeds.ts b/apps/web/app/api/feeds.ts index 731b84df..30bc440a 100644 --- a/apps/web/app/api/feeds.ts +++ b/apps/web/app/api/feeds.ts @@ -106,7 +106,7 @@ export function useFetchFeedNow() { export function feedGapHint(f: FeedSource, now = Date.now()): string | null { if (f.possibleGap) { const at = new Date(f.possibleGap.detectedAt).toLocaleString("zh-CN", { hour12: false }); - return `疑似缺口(${at} 检测,${f.possibleGap.backfillQueued ? "已排补漏" : "Feed 未关联站点,无法自动补漏"})`; + return `疑似缺口(${at} 检测,${f.possibleGap.backfillQueued ? "已排补漏" : "RSS 未关联站点,无法自动补漏"})`; } if (!f.enabled || !f.lastSuccessAt) return null; const gapMs = now - new Date(f.lastSuccessAt).getTime(); diff --git a/apps/web/app/api/inventory.ts b/apps/web/app/api/inventory.ts index 3b1230fb..fd1d0dfe 100644 --- a/apps/web/app/api/inventory.ts +++ b/apps/web/app/api/inventory.ts @@ -64,7 +64,7 @@ export function inventoryFlags( }); } if (releaseResolution && releaseResolution === "resolved" && flags.length === 0) { - flags.push({ key: "tracked", label: "已识别,无版本目标", tone: "neutral" }); + flags.push({ key: "tracked", label: "已识别,无收录版本", tone: "neutral" }); } return flags; } diff --git a/apps/web/app/api/records.ts b/apps/web/app/api/records.ts index 1bb58dd2..7df956d0 100644 --- a/apps/web/app/api/records.ts +++ b/apps/web/app/api/records.ts @@ -107,7 +107,7 @@ export const RECORD_STATUS_TONE: Record = { auto: "自动", manual: "手动", agent: "Agent" }; +export const TRIGGER_LABEL: Record = { auto: "自动", manual: "手动", agent: "智能体" }; /** dryRun 生成的整理计划冻结为 artifact(kind=plan),GET /artifacts/{id} 读取逐文件预览 */ export function useArtifact(id: string | null | undefined) { diff --git a/apps/web/app/api/rules.ts b/apps/web/app/api/rules.ts index 4d822be7..f38d8e6c 100644 --- a/apps/web/app/api/rules.ts +++ b/apps/web/app/api/rules.ts @@ -99,14 +99,14 @@ export function useRuleSuppressions() { export type RuleRefs = { /** 规则组 id → 引用它的订阅(params.ruleGroupIds 或私有组) */ subscriptions: Map; - /** 规则组 id → 引用它的版本目标 */ + /** 规则组 id → 引用它的收录版本 */ slots: Map; }; const SubPage = cursorPage(Subscription); const SlotPage = cursorPage(AcquisitionSlot); -/** 引用关系:列表接口不带引用计数,前端从订阅与版本目标反查(各取前 200 条) */ +/** 引用关系:列表接口不带引用计数,前端从订阅与收录版本反查(各取前 200 条) */ export function useRuleRefs() { return useQuery({ queryKey: ruleKeys.refs, @@ -150,7 +150,7 @@ function useInvalidate() { const qc = useQueryClient(); return () => { void qc.invalidateQueries({ queryKey: ruleKeys.all }); - // 旧偏好标签页与订阅/版本目标的规则组显示同源 + // 旧偏好标签页与订阅/收录版本的规则组显示同源 void qc.invalidateQueries({ queryKey: ["preferences"] }); }; } @@ -175,7 +175,7 @@ export function useUpdateRuleGroup() { }); } -/** 删除:标记删除并写抑制记录;被版本目标/订阅引用时 409 */ +/** 删除:标记删除并写抑制记录;被收录版本/订阅引用时 409 */ export function useDeleteRuleGroup() { const done = useInvalidate(); return useMutation({ diff --git a/apps/web/app/api/sites.ts b/apps/web/app/api/sites.ts index e448799c..69a3b7f6 100644 --- a/apps/web/app/api/sites.ts +++ b/apps/web/app/api/sites.ts @@ -105,7 +105,7 @@ export function useSiteRequestStats(siteId: string | undefined) { export function accessSummary(s: SiteRequestStats): string { const dur = (sec: number) => (sec % 3600 === 0 ? `${sec / 3600} 小时` : `${Math.round(sec / 60)} 分钟`); const parts = [ - `快照每 ${dur(s.snapshotIntervalSec)} ${s.access.snapshotMaxPages} 页`, + `抓取每 ${dur(s.snapshotIntervalSec)} ${s.access.snapshotMaxPages} 页`, `健康检测每 ${dur(s.access.healthCheckIntervalSec)}`, ]; if (s.access.detailCacheSec > 0) parts.push(`详情缓存 ${dur(s.access.detailCacheSec)}`); diff --git a/apps/web/app/api/subscriptions.ts b/apps/web/app/api/subscriptions.ts index f4382228..06a479fe 100644 --- a/apps/web/app/api/subscriptions.ts +++ b/apps/web/app/api/subscriptions.ts @@ -77,7 +77,7 @@ export function useUpdateSubscription() { api.patch(`/subscriptions/${id}`, body).then((d) => parseOrThrow(Subscription, d, "/subscriptions")), onSuccess: () => { void qc.invalidateQueries({ queryKey: subscriptionKeys.all }); - // best_version 写到版本目标的升级策略 + // best_version 写到收录版本的升级策略 void qc.invalidateQueries({ queryKey: ["slots"] }); }, }); @@ -246,7 +246,7 @@ export function useFillSlot() { }); } -/** 版本目标排序:第一个成为主版本 */ +/** 收录版本排序:第一个成为主版本 */ export function useSetSlotOrder() { const done = useInvalidateSubs(); return useMutation({ diff --git a/apps/web/app/api/tasks.ts b/apps/web/app/api/tasks.ts index ab9b9900..c9fe7970 100644 --- a/apps/web/app/api/tasks.ts +++ b/apps/web/app/api/tasks.ts @@ -23,7 +23,7 @@ const RecordPage = cursorPage(PublicOrganizationRecord); const DecisionPage = cursorPage(Decision); const RESOLUTION_LABEL = { - unresolved: "待识别", + unresolved: "未识别", resolved: "已识别", ambiguous: "有歧义,待确认", unidentifiable: "无法识别", @@ -260,7 +260,7 @@ export function buildTimeline(input: { key: `agent-${d.id}`, stage: "agent", at: d.updatedAt, - title: "Agent 已介入调查", + title: "智能体已介入调查", tone: "neutral", href: `/console/agent/${d.agentRunId}`, }); diff --git a/apps/web/app/components/agent/agent-conversation.tsx b/apps/web/app/components/agent/agent-conversation.tsx index a9863c31..4495debf 100644 --- a/apps/web/app/components/agent/agent-conversation.tsx +++ b/apps/web/app/components/agent/agent-conversation.tsx @@ -249,8 +249,7 @@ export function ToolCallCard({
{call.toolCallId}
幂等键
- {audit?.idempotencyKey ?? - (meta && meta.kind !== "read" ? "服务端已生成,审计里没有取到" : "只读能力不记操作账本")} + {audit?.idempotencyKey ?? (meta && meta.kind !== "read" ? "服务端已生成,审计里没有取到" : "只读操作不记录")}
{audit?.grantId || audit?.approvalId ? ( <> @@ -652,7 +651,7 @@ export function AgentConversation({ at: m.at, role: "user", foldable: false, - node: , + node: , }); entries.sort((a, b) => a.at.localeCompare(b.at)); const blocks = groupConversation(entries); @@ -678,13 +677,13 @@ export function AgentConversation({ b.kind === "single" ? (
{b.entry.node}
) : ( - + - Agent + 智能体 {b.parts.map((p) => diff --git a/apps/web/app/components/agent/agent-drawer.tsx b/apps/web/app/components/agent/agent-drawer.tsx index a846a22b..2a5d498d 100644 --- a/apps/web/app/components/agent/agent-drawer.tsx +++ b/apps/web/app/components/agent/agent-drawer.tsx @@ -72,7 +72,7 @@ function DrawerTabs({ tab }: { tab: AgentDrawerTab }) { return (
{TABS.map((t) => ( @@ -161,10 +161,10 @@ export function AgentDrawer() { - Agent + 智能体 - {context?.label ? `当前对象:${context.label}` : "交代目标,Agent 在授权范围内执行并汇报。"} + {context?.label ? `当前对象:${context.label}` : "交代目标,智能体在授权范围内执行并汇报。"} ({agentShortcutLabel()} 开关,Esc 关闭) @@ -281,7 +281,7 @@ function AgentMiniDock({ runId }: { runId: string }) { : null; return (
@@ -290,7 +290,7 @@ function AgentMiniDock({ runId }: { runId: string }) { {run.data ? splitContextGoal(run.data.run.goal).text : ""} - {formatDateTime(view.expiresAt)} 前没选, - {view.timeoutAction === "recommended" ? "自动选推荐项" : "Agent 停止"}。 + {view.timeoutAction === "recommended" ? "自动选推荐项" : "智能体停止"}。
)} @@ -285,7 +285,7 @@ export function InlineApproval({ view, stepTitle }: { view: ApprovalView; stepTi ...(s === "rejected" && reason.trim() ? { reason: reason.trim() } : {}), }, { - onSuccess: () => toast.success(s === "approved" ? "已批准,Agent 继续执行" : "已拒绝,Agent 按理由调整"), + onSuccess: () => toast.success(s === "approved" ? "已批准,智能体继续执行" : "已拒绝,智能体按理由调整"), onError: (e) => toast.error(errorMessage(e)), }, ); @@ -332,7 +332,7 @@ export function InlineApproval({ view, stepTitle }: { view: ApprovalView; stepTi value={reason} maxLength={2000} aria-label="拒绝理由" - placeholder="拒绝时写理由或建议(可选),Agent 按它调整" + placeholder="拒绝时写理由或建议(可选),智能体按它调整" onChange={(e) => setReason(e.target.value)} /> ) : null} diff --git a/apps/web/app/components/agent/agent-run-panel.tsx b/apps/web/app/components/agent/agent-run-panel.tsx index fe820833..f469c0c7 100644 --- a/apps/web/app/components/agent/agent-run-panel.tsx +++ b/apps/web/app/components/agent/agent-run-panel.tsx @@ -114,8 +114,8 @@ export function GoalComposer({ />

{proposeFirst - ? "Agent 先调查并给出方案,你选定后才执行。" - : "Agent 列出步骤后直接执行,需要审批的动作会停在对应步骤等你批准。"}{" "} + ? "智能体先调查并给出方案,你选定后才执行。" + : "智能体列出步骤后直接执行,需要审批的动作会停在对应步骤等你批准。"}{" "} Enter 发送,Shift+Enter 换行。

{suggestions?.length ? ( @@ -400,7 +400,7 @@ export function AgentRunPanel({ viewportClassName="pr-1" contentClassName={column} busy={streaming} - label="Agent 会话记录" + label="智能体会话记录" > +
{target && targetRoot ? ( @@ -616,7 +621,7 @@ export function TransferDialog({ 冲突 {conflicts.length}(跳过,不覆盖) ) : null} {plan.pairs.some((p) => p.registered) ? ( - 含已登记资产,执行后同步位置 + 含已入库文件,执行后同步位置 ) : null}
{hnrBlocked ? ( @@ -777,7 +782,7 @@ export function DeleteDialog({ {single ? `删除 ${single}?` : `删除 ${items.length} 项?`} - 已检查做种任务、已登记资产和路径绑定。下载器里的任务不受影响。 + 已检查做种任务、已入库文件和路径绑定。下载器里的任务不受影响。
{check.isPending ? ( @@ -824,7 +829,7 @@ export function DeleteDialog({

))} {c.assets.length ? ( -

已登记资产 {c.assets.length} 个(删除后退役,已满足的版本目标退回待处理)

+

已入库文件 {c.assets.length} 个(删除后从媒体库移除,已满足的收录版本退回待处理)

) : null} {c.bindings.map((bd) => (

@@ -843,9 +848,9 @@ export function DeleteDialog({ @@ -1042,7 +1047,7 @@ export function RefreshMetadataDialog({ items, onClose }: { items: FileEntry[];

{s.ref}: {s.reason === "not_registered" - ? "没有已登记资产" + ? "没有已入库文件" : s.reason === "no_entity" ? "不属于任何已登记作品" : s.reason === "not_found" diff --git a/apps/web/app/components/files/dir-picker.tsx b/apps/web/app/components/files/dir-picker.tsx index 117ac992..2d18c0a3 100644 --- a/apps/web/app/components/files/dir-picker.tsx +++ b/apps/web/app/components/files/dir-picker.tsx @@ -29,7 +29,7 @@ export function DirPicker({ onSelect={onChange} /> ) : ( -

没有可写的库根。在设置里把库根设为纳管后再选。

+

没有可写入的媒体库目录。先在设置里添加。

)}
); diff --git a/apps/web/app/components/files/dir-tree.tsx b/apps/web/app/components/files/dir-tree.tsx index 747ce437..6e60dfb0 100644 --- a/apps/web/app/components/files/dir-tree.tsx +++ b/apps/web/app/components/files/dir-tree.tsx @@ -148,7 +148,7 @@ function RootNode({ )} {root.pendingCount ? ( - 待识别 {root.pendingCount} + 未识别 {root.pendingCount} {root.pendingPartial ? "+" : ""} ) : null} diff --git a/apps/web/app/components/files/file-detail.tsx b/apps/web/app/components/files/file-detail.tsx index 0dab7257..c492f0cf 100644 --- a/apps/web/app/components/files/file-detail.tsx +++ b/apps/web/app/components/files/file-detail.tsx @@ -78,7 +78,7 @@ function RecordsSection({ entityId }: { entityId: string }) { ); } -/** 选中单项的详情抽屉:文件信息、作品/版本目标/下载任务、识别、整理记录、附属文件、媒体信息、路径绑定与小文件预览 */ +/** 选中单项的详情抽屉:文件信息、作品/收录版本/下载任务、识别、整理记录、附属文件、媒体信息、路径绑定与小文件预览 */ export function FileDetail({ root, entry, @@ -148,7 +148,7 @@ export function FileDetail({ {e.asset ? ( - + {e.asset.slotStatus ? (SLOT_STATUS[e.asset.slotStatus]?.label ?? e.asset.slotStatus) : "查看"} ) : ( - "未满足任何版本目标" + "未满足任何收录版本" ), }, - { label: "主资产", value: e.asset.isPrimary ? "是" : "否" }, + { label: "主文件", value: e.asset.isPrimary ? "是" : "否" }, ]} /> diff --git a/apps/web/app/components/files/mock.ts b/apps/web/app/components/files/mock.ts index 99d0fbc2..f941cdf4 100644 --- a/apps/web/app/components/files/mock.ts +++ b/apps/web/app/components/files/mock.ts @@ -707,7 +707,7 @@ function doMove(body: { const tRoot = rootOf(body.target.root); const target = find(body.target.root, body.target.path).node; if (!target.dir || !tRoot.writable) - throw new ApiRequestError("bad_request", "not_writable:目标必须是可写库根下的目录"); + throw new ApiRequestError("bad_request", "not_writable:目标必须是可写媒体库目录下的目录"); const pairs = body.items.map((it) => { const { node } = find(it.root, it.path); const name = node.name; diff --git a/apps/web/app/components/files/model.ts b/apps/web/app/components/files/model.ts index 401e1ba0..0850ea1c 100644 --- a/apps/web/app/components/files/model.ts +++ b/apps/web/app/components/files/model.ts @@ -32,7 +32,7 @@ export function entryStatusView(e: Pick 合并作品 - 「{entity.title}」会标为已合并,旧 ID 跳转到目标作品,订阅、资产和历史记录一并转过去。合并错了可以再修订。 + 「{entity.title}」会标为已合并,旧 ID 跳转到目标作品,订阅、文件和历史记录一并转过去。合并错了可以再修订。 @@ -392,7 +392,7 @@ export function MergeDialog({ id="merge-reason" value={reason} onChange={(e) => setReason(e.target.value)} - placeholder="重复建档" + placeholder="重复添加" /> diff --git a/apps/web/app/components/library/identity-enrich.tsx b/apps/web/app/components/library/identity-enrich.tsx index 23b7b4f7..c79f0129 100644 --- a/apps/web/app/components/library/identity-enrich.tsx +++ b/apps/web/app/components/library/identity-enrich.tsx @@ -68,7 +68,7 @@ function FieldRow({ k, s, enrichmentId }: { k: EnrichFieldName; s: Suggestion; e {k !== "posterUrl" ?

{String(v.value)}

: null}
{EVIDENCE_LEVEL[v.evidenceLevel] ?? v.evidenceLevel} - {v.verbatim ? null : Agent 归纳} + {v.verbatim ? null : 智能体归纳}
@@ -142,7 +142,7 @@ export function EnrichSection({ item }: { item: IdentityPoolItem }) { {out.notice} {" · "} {out.cached ? "7 天内的缓存结果" : `搜索 ${out.usage.searchCalls} 次,读取网页 ${out.usage.fetchCalls} 次`} - {s ? ` · ${s.extractor === "model" ? `Agent 抽取${s.model ? `(${s.model})` : ""}` : "规则抽取"}` : ""} + {s ? ` · ${s.extractor === "model" ? `智能体抽取${s.model ? `(${s.model})` : ""}` : "规则抽取"}` : ""} {` · 今日 ${out.budget.usedToday}/${out.budget.dailyLimit}`}

{out.error ?

{out.error}

: null} diff --git a/apps/web/app/components/library/identity-external-sheet.tsx b/apps/web/app/components/library/identity-external-sheet.tsx index 3e91e408..b6d6fc8e 100644 --- a/apps/web/app/components/library/identity-external-sheet.tsx +++ b/apps/web/app/components/library/identity-external-sheet.tsx @@ -23,7 +23,7 @@ const ACTION: Record = { locked: "加人工锁", unlocked: "解除人工锁", }; -const ACTOR: Record = { user: "用户", agent: "Agent", system: "系统", api_key: "API Key" }; +const ACTOR: Record = { user: "用户", agent: "智能体", system: "系统", api_key: "API Key" }; export const identityRef = (i: Pick) => `${i.provider}:${i.entityType}:${i.externalId}`; diff --git a/apps/web/app/components/library/identity-pool-sheet.tsx b/apps/web/app/components/library/identity-pool-sheet.tsx index b5a823fd..ec96b646 100644 --- a/apps/web/app/components/library/identity-pool-sheet.tsx +++ b/apps/web/app/components/library/identity-pool-sheet.tsx @@ -76,7 +76,7 @@ function CandidateRow({ c, index }: { c: PoolCandidate; index: number }) { ) : ( - 本地未建档 + 不在媒体库 )} {c.evidence.length ? ( diff --git a/apps/web/app/components/library/identity-views.tsx b/apps/web/app/components/library/identity-views.tsx index c0afcc9d..b80e042b 100644 --- a/apps/web/app/components/library/identity-views.tsx +++ b/apps/web/app/components/library/identity-views.tsx @@ -148,7 +148,7 @@ export function PoolTab() { selectedKey={openId} empty={{ icon: SearchX, - title: filtered ? "没有符合条件的待识别资源" : "没有待识别资源", + title: filtered ? "没有符合条件的未识别资源" : "没有未识别资源", description: "未命中订阅、证据不足或冲突的资源会出现在这里;新增相近别名时自动重试,也可手动指定。", }} hasMore={pool.hasNextPage} @@ -249,7 +249,7 @@ export function ExternalTab() { empty={{ icon: Fingerprint, title: "没有外部身份", - description: "没有外部身份的作品也能建档入库。", + description: "没有外部编号的作品也能入库。", }} hasMore={list.hasNextPage} onLoadMore={() => list.fetchNextPage()} diff --git a/apps/web/app/components/library/labels.ts b/apps/web/app/components/library/labels.ts index 1332f271..b7ec7eaa 100644 --- a/apps/web/app/components/library/labels.ts +++ b/apps/web/app/components/library/labels.ts @@ -34,7 +34,7 @@ export const MEDIA_KIND: Record = { }; export const ENTITY_STATUS: Record = { - provisional: { label: "临时建档", tone: "warning" }, + provisional: { label: "临时作品", tone: "warning" }, active: { label: "正常", tone: "success" }, merged: { label: "已合并", tone: "neutral" }, archived: { label: "已归档", tone: "neutral" }, @@ -132,8 +132,8 @@ export const CANDIDATE_DECISION: Record = { }; export const DECISION_KIND: Record = { - identity_ambiguous: "身份歧义", - identity_unresolved: "待识别", + identity_ambiguous: "没识别出作品", + identity_unresolved: "未识别", download_stalled: "下载无进展", download_failed: "下载失败", path_conflict: "路径冲突", @@ -146,7 +146,7 @@ export const DECISION_KIND: Record = { export const DECISION_STATUS: Record = { open: { label: "待处理", tone: "warning" }, - investigating: { label: "Agent 调查中", tone: "neutral" }, + investigating: { label: "智能体调查中", tone: "neutral" }, awaiting_user: { label: "等你决定", tone: "warning" }, resolved: { label: "已处理", tone: "success" }, dismissed: { label: "已忽略", tone: "neutral" }, @@ -168,7 +168,7 @@ export const POOL_REASON: Record = { }; export const POOL_STATUS: Record = { - pending: { label: "待识别", tone: "warning" }, + pending: { label: "未识别", tone: "warning" }, resolved: { label: "已识别", tone: "success" }, dismissed: { label: "已忽略", tone: "neutral" }, archived: { label: "已归档", tone: "neutral" }, @@ -178,13 +178,13 @@ export const PREF_KIND: Record soft: { label: "软偏好", hint: "影响排序,不排除" }, hard: { label: "硬规则", hint: "命中即排除" }, version: { label: "版本偏好", hint: "同一作品内选哪种版本" }, - target: { label: "版本目标", hint: "为作品新建一个版本目标" }, + target: { label: "收录版本", hint: "为作品新建一个收录版本" }, }; export const PREF_ORIGIN: Record = { manual: "手动", conversation: "对话确认", - agent_inferred: "Agent 推断", + agent_inferred: "智能体推断", }; export const PREF_STATUS: Record = { @@ -219,12 +219,12 @@ export const SUB_STATUS: Record = { completed: { label: "已完成", tone: "neutral" }, cancelled: { label: "已取消", tone: "neutral" }, }; -export const SUB_ORIGIN: Record = { user: "用户", agent: "Agent", policy: "策略", import: "导入" }; +export const SUB_ORIGIN: Record = { user: "用户", agent: "智能体", policy: "策略", import: "导入" }; export const METADATA_SOURCE: Record = { tmdb: "TMDB", user: "人工", - agent: "Agent", + agent: "智能体", nfo: "NFO", site: "站点", import: "导入", @@ -316,7 +316,7 @@ export const opts = (rec: Record`)→ 中文;不认识的原样显示 */ +/** 收录版本 / 候选的原因串(如 `blocked:no_main_video`、`deferred:slot_busy;inflight:`)→ 中文;不认识的原样显示 */ const SLOT_REASON: Record = { "blocked:no_main_video": "没有可入库的主视频", "deferred:slot_busy": "已有计划在途,等它结束再评估", diff --git a/apps/web/app/components/library/preferences-form.tsx b/apps/web/app/components/library/preferences-form.tsx index 838041ce..4e64c881 100644 --- a/apps/web/app/components/library/preferences-form.tsx +++ b/apps/web/app/components/library/preferences-form.tsx @@ -108,7 +108,7 @@ export function PreferenceFormDialog({ {tag ? "编辑偏好" : "添加偏好"} {tag - ? "修改作用范围或规则会留下旧语义的抑制记录,Agent 不会按旧证据加回。" + ? "修改作用范围或规则会留下旧语义的抑制记录,智能体不会按旧证据加回。" : "手动添加等同对话确认。之前取消过的偏好,重新添加会解除抑制。"} diff --git a/apps/web/app/components/library/slot-card.tsx b/apps/web/app/components/library/slot-card.tsx index 64551b59..68e2495a 100644 --- a/apps/web/app/components/library/slot-card.tsx +++ b/apps/web/app/components/library/slot-card.tsx @@ -39,7 +39,7 @@ export function SlotCard({ {slot.requireDistinctAsset ? ( - 需不同物理资产{slot.distinctGroup ? ` · ${slot.distinctGroup}` : ""} + 需独立文件{slot.distinctGroup ? ` · ${slot.distinctGroup}` : ""} ) : null} r{slot.revision} @@ -72,7 +72,7 @@ export function SlotCard({
{summarizeCompletion(slot.completion)}
-
当前资产
+
当前文件
{currentAsset ?? "无"}
diff --git a/apps/web/app/components/library/subscriptions-policy.tsx b/apps/web/app/components/library/subscriptions-policy.tsx index 3bd75f2d..e108c9d2 100644 --- a/apps/web/app/components/library/subscriptions-policy.tsx +++ b/apps/web/app/components/library/subscriptions-policy.tsx @@ -353,8 +353,8 @@ function RulesSummary({ rules, valid }: { rules: AcquisitionPolicyRules | undefi return (
- 默认版本目标 - + 默认收录版本 +
新作品收录 @@ -718,7 +718,7 @@ function PolicyEditorSheet({ } > -

订阅的作品没有版本目标时,按这些模板创建。

+

订阅的作品没有收录版本时,按这些模板创建。

set("defaultSlots", v)} errors={errors} />
@@ -768,7 +768,7 @@ function PolicyEditorSheet({
-

新作品的版本目标模板

+

新作品的收录版本模板

{q.isPending ? ( @@ -65,7 +65,7 @@ export function SlotsPanel({ @@ -140,7 +140,7 @@ function SlotEditorSheet({ const errs: Record = {}; if (!meta.success) for (const i of meta.error.issues) errs[String(i.path[0])] ??= i.message; if (!rules.ok) Object.assign(errs, rules.errors); - if (requireDistinct && !distinctGroup.trim()) errs.distinctGroup = "要求不同物理资产时须填写分组"; + if (requireDistinct && !distinctGroup.trim()) errs.distinctGroup = "要求独立文件时须填写分组"; setErrors(errs); if (Object.keys(errs).length || !meta.success || !rules.ok) return; const common = { @@ -178,7 +178,7 @@ function SlotEditorSheet({ open onOpenChange={(o) => !o && !pending && onClose()} width="lg" - title={slot ? `编辑版本目标 ${slot.name}` : "新建版本目标"} + title={slot ? `编辑收录版本 ${slot.name}` : "新建收录版本"} description={ slot ? `当前 r${slot.revision}。保存时若已被修改,会提示刷新。` : "不满足硬条件的资源不收;软偏好决定先收哪个。" } @@ -225,10 +225,10 @@ function SlotEditorSheet({
-

要求不同物理资产

+

要求独立文件

同一个文件不能同时满足两个目标

- +
diff --git a/apps/web/app/components/naming/model.ts b/apps/web/app/components/naming/model.ts index 52b8b3fc..e61df131 100644 --- a/apps/web/app/components/naming/model.ts +++ b/apps/web/app/components/naming/model.ts @@ -3,7 +3,7 @@ import type { NamingKind, NamingOverride } from "@umt/contracts"; // 命名模板表单的纯函数:保存错误拆分、覆盖请求体、来源文案。 export const NAMING_KINDS: NamingKind[] = ["movie", "tv", "variety"]; -export const SOURCE_LABEL = { global: "全局", root: "库根", entity: "作品" } as const; +export const SOURCE_LABEL = { global: "全局", root: "媒体库目录", entity: "作品" } as const; /** 422 消息 "movie: 1:3 …; tv: …" → 按类型拆开,行列号改写成中文 */ export function splitNamingErrors(message: string): Partial> | null { diff --git a/apps/web/app/components/organize-wizard/model.ts b/apps/web/app/components/organize-wizard/model.ts index 38f60405..f086f563 100644 --- a/apps/web/app/components/organize-wizard/model.ts +++ b/apps/web/app/components/organize-wizard/model.ts @@ -358,7 +358,7 @@ export const FILE_KIND: Record = { export const TEMPLATE_SOURCE: Record = { entity: "作品覆盖", - root: "库根", + root: "媒体库目录", global: "全局", override: "保留原文件名", }; diff --git a/apps/web/app/components/organize-wizard/step-files.tsx b/apps/web/app/components/organize-wizard/step-files.tsx index 68d0f32b..1fe4e7ba 100644 --- a/apps/web/app/components/organize-wizard/step-files.tsx +++ b/apps/web/app/components/organize-wizard/step-files.tsx @@ -194,11 +194,11 @@ function SlotSelect({ const items = slots.data?.items ?? []; return ( - + @@ -177,7 +177,7 @@ function RootNamingEditor({ root }: { root: LibraryRoot }) { try { await save.mutateAsync({ id: root.id, naming: body }); setDraft({ ...(body ?? {}) }); - toast.success(body ? "库根命名覆盖已保存" : "已清除库根命名覆盖"); + toast.success(body ? "媒体库目录命名覆盖已保存" : "已清除媒体库目录命名覆盖"); } catch (err) { const split = splitNamingErrors(errorMessage(err)); if (split) setErrors(split); @@ -190,7 +190,7 @@ function RootNamingEditor({ root }: { root: LibraryRoot }) {
{KINDS.map((k) => ( - {NAMING_KIND_LABEL[k]}:{saved[k] ? "库根覆盖" : "沿用全局"} + {NAMING_KIND_LABEL[k]}:{saved[k] ? "媒体库目录覆盖" : "沿用全局"} ))}
diff --git a/apps/web/app/components/settings/path-mappings.tsx b/apps/web/app/components/settings/path-mappings.tsx index 9462a089..8a116ddc 100644 --- a/apps/web/app/components/settings/path-mappings.tsx +++ b/apps/web/app/components/settings/path-mappings.tsx @@ -104,7 +104,7 @@ export function PathMappingsPanel({

{m.remotePrefix}

→ {m.localPrefix}

{rootName(m.libraryRootId) ? ( -

库根:{rootName(m.libraryRootId)}

+

媒体库目录:{rootName(m.libraryRootId)}

) : null} {h.label} @@ -175,7 +175,7 @@ export function PathMappingsPanel({ disabled={create.isPending} /> setV((s) => ({ ...s, libraryRootId: x }))} disabled={create.isPending} diff --git a/apps/web/app/components/settings/pi-test.tsx b/apps/web/app/components/settings/pi-test.tsx index 594e454f..fcdf8a21 100644 --- a/apps/web/app/components/settings/pi-test.tsx +++ b/apps/web/app/components/settings/pi-test.tsx @@ -22,7 +22,7 @@ const SOURCE_TEXT: Record = { "stored credential": "已存凭证 /** 上游返回的多是英文原文:先给一句怎么办,原文留作排查 */ const ERROR_HINT: Record = { - network: "连不上上游,检查 Base URL、端口和网络", + network: "连不上模型服务,检查 Base URL、端口和网络", auth: "凭证被拒,检查 API Key 或重新订阅登录", model: "模型不可用,换一个模型 id", }; diff --git a/apps/web/app/components/settings/pi-upstreams.tsx b/apps/web/app/components/settings/pi-upstreams.tsx index 038c16cc..37f49c42 100644 --- a/apps/web/app/components/settings/pi-upstreams.tsx +++ b/apps/web/app/components/settings/pi-upstreams.tsx @@ -49,13 +49,13 @@ export function PiUpstreamsBlock() { setWizard(true)}> - 新建上游 + 新建模型服务 } > @@ -66,7 +66,7 @@ export function PiUpstreamsBlock() { error={list.error} onRetry={() => list.refetch()} onRowClick={(r) => setOpenId(r.id)} - empty={{ title: "还没有上游" }} + empty={{ title: "还没有模型服务" }} columns={[ { key: "name", @@ -181,7 +181,7 @@ function UpstreamDrawer({ const makeDefault = async () => { const model = v.model.trim(); if (!model) { - setErrors((e) => ({ ...e, defaultModel: "设为默认上游前先选默认模型" })); + setErrors((e) => ({ ...e, defaultModel: "设为默认模型服务前先选默认模型" })); return; } try { @@ -189,7 +189,7 @@ function UpstreamDrawer({ id: upstream.id, body: { makeDefault: true, ...(model !== (upstream.defaultModel ?? "") ? { defaultModel: model } : {}) }, }); - toast.success(`${upstream.name}已设为默认上游`); + toast.success(`${upstream.name}已设为默认模型服务`); } catch (err) { toast.error(errorMessage(err, "设置失败")); } @@ -204,7 +204,7 @@ function UpstreamDrawer({ description={ - {upstream.isDefault ? 默认上游 : null} + {upstream.isDefault ? 默认模型服务 : null} {provider?.name ?? upstream.provider} · {AUTH_LABEL[upstream.authKind]} @@ -402,11 +402,13 @@ function UpstreamDrawer({ { try { diff --git a/apps/web/app/components/settings/pi-wizard.tsx b/apps/web/app/components/settings/pi-wizard.tsx index 66a73e1a..8bf67c67 100644 --- a/apps/web/app/components/settings/pi-wizard.tsx +++ b/apps/web/app/components/settings/pi-wizard.tsx @@ -60,9 +60,9 @@ export function PiUpstreamWizard({ open, onClose }: { open: boolean; onClose: () const title = step === "provider" - ? "新建 Agent 上游" + ? "新建模型服务" : step === "config" - ? `新建上游 · ${provider?.name ?? ""}` + ? `新建模型服务 · ${provider?.name ?? ""}` : (created?.name ?? ""); const description = step === "provider" @@ -70,8 +70,8 @@ export function PiUpstreamWizard({ open, onClose }: { open: boolean; onClose: () : step === "config" ? "第 2 步,共 3 步:凭证与模型" : created?.authKind === "oauth" - ? "第 3 步,共 3 步:登录并测试。上游已创建。" - : "第 3 步,共 3 步:测试。上游已创建。"; + ? "第 3 步,共 3 步:登录并测试。模型服务已创建。" + : "第 3 步,共 3 步:测试。模型服务已创建。"; return ( !o && close()} title={title} description={description} width="lg"> @@ -413,7 +413,7 @@ function VerifyStep({ const finish = async () => { if (makeDefault && !model.trim()) { - setModelError("设为默认上游前先选默认模型"); + setModelError("设为默认模型服务前先选默认模型"); return; } const body = { @@ -422,7 +422,7 @@ function VerifyStep({ }; try { if (Object.keys(body).length) await update.mutateAsync({ id: upstream.id, body }); - if (makeDefault && !upstream.isDefault) toast.success(`${upstream.name}已设为默认上游`); + if (makeDefault && !upstream.isDefault) toast.success(`${upstream.name}已设为默认模型服务`); onDone(); } catch (err) { toast.error(errorMessage(err, "保存失败")); @@ -476,7 +476,7 @@ function VerifyStep({ onCheckedChange={setMakeDefault} disabled={upstream.isDefault} /> - {upstream.isDefault ? "已是默认上游" : "设为默认上游"} + {upstream.isDefault ? "已是默认模型服务" : "设为默认模型服务"} @@ -335,7 +335,7 @@ export function SiteAddDialog({ onClose }: { onClose: () => void }) { ) : (

- 不需要登录凭证。添加后在站点详情的"快照与 Feed"里新建 Feed。 + 不需要登录凭证。添加后在站点详情的"抓取与 RSS"里新建 RSS。

)} diff --git a/apps/web/app/components/sites/site-card.tsx b/apps/web/app/components/sites/site-card.tsx index 5e04be17..d93c4ef7 100644 --- a/apps/web/app/components/sites/site-card.tsx +++ b/apps/web/app/components/sites/site-card.tsx @@ -141,11 +141,11 @@ export function SiteMoreMenu({ site, onDialog }: { site: SiteListItem; onDialog: {item(, "编辑站点", () => onDialog({ kind: "edit", site }))} {item( , - "抓取快照", + "抓取最新资源", () => snap.mutate( { id: site.id }, - { onSuccess: () => toast.success("快照已排队"), onError: (e) => toast.error(errorMessage(e)) }, + { onSuccess: () => toast.success("已开始抓取"), onError: (e) => toast.error(errorMessage(e)) }, ), { disabled: !site.enabled }, )} @@ -314,13 +314,13 @@ export function SiteCard({ site, checks, onDialog }: Props) {
-
快照
+
抓取
{!site.snapshotEnabled ? "未开启" : site.lastSnapshotAt ? `${relTime(site.lastSnapshotAt)}${stale ? " · 已过期" : ""}` - : "还没有快照"} + : "还没抓取过"}
@@ -455,7 +455,7 @@ export function SiteTable({ }, { key: "snapshot", - header: "快照", + header: "抓取", hideBelow: "lg", cell: (s) => ( diff --git a/apps/web/app/components/sites/site-dialogs.tsx b/apps/web/app/components/sites/site-dialogs.tsx index 68de8523..9bfe2333 100644 --- a/apps/web/app/components/sites/site-dialogs.tsx +++ b/apps/web/app/components/sites/site-dialogs.tsx @@ -380,7 +380,7 @@ export function SiteEditSheet({ site, onClose }: { site: SiteListItem; onClose: open onOpenChange={(o) => !o && !busy && onClose()} title={`编辑 · ${site.name}`} - description="限流和快照周期留空时用「设置 → 站点默认」" + description="限流和抓取周期留空时用「设置 → 站点默认」" footer={ <>
{v.snapshotEnabled ? ( - +
  • 站点、{site.accounts?.length ?? 0} 个账号和全部凭证删除
  • 已抓到的资源保留,标为"站点已关闭",不再产生新候选
  • -
  • 该站 Feed 停用;已有下载任务按原策略继续
  • +
  • 该站 RSS 停用;已有下载任务按原策略继续
  • 同一站点以后可以重新添加
  • diff --git a/apps/web/app/components/sites/site-feed-dialog.tsx b/apps/web/app/components/sites/site-feed-dialog.tsx index 1566e4f6..3b2df6bd 100644 --- a/apps/web/app/components/sites/site-feed-dialog.tsx +++ b/apps/web/app/components/sites/site-feed-dialog.tsx @@ -216,7 +216,7 @@ export function SiteFeedDialog({

    {account ? `账号 ${account.label}` : "该站没有账号,按地址里的 passkey 抓取"} · 解析器按站点定义 · 每 30 - 分钟抓一次,可在 Feed 详情里改 + 分钟抓一次,可在 RSS 详情里改

    )} @@ -279,7 +279,7 @@ function FirstFetch({

    ) : timedOut ? (

    - 已保存。抓取还在排队(站点限流),结果稍后显示在 Feed 列表里。 + 已保存。抓取还在排队(站点限流),结果稍后显示在 RSS 列表里。

    ) : (

    diff --git a/apps/web/app/components/sites/site-overview.tsx b/apps/web/app/components/sites/site-overview.tsx index 94e8f9c7..015b3481 100644 --- a/apps/web/app/components/sites/site-overview.tsx +++ b/apps/web/app/components/sites/site-overview.tsx @@ -99,9 +99,9 @@ export function SiteOverview({ site }: { site: SiteListItem }) { ), }, { - label: "快照", + label: "抓取", value: site.snapshotEnabled - ? `${site.lastSnapshotAt ? relTime(site.lastSnapshotAt) : "还没有快照"} · ${site.snapshotIntervalSec ? `每 ${Math.round(site.snapshotIntervalSec / 60)} 分钟` : "默认周期"}` + ? `${site.lastSnapshotAt ? relTime(site.lastSnapshotAt) : "还没抓取过"} · ${site.snapshotIntervalSec ? `每 ${Math.round(site.snapshotIntervalSec / 60)} 分钟` : "默认周期"}` : "未开启", }, { label: "限流", value: rateLimitText(site.rateLimit) }, diff --git a/apps/web/app/components/status/storage-section.tsx b/apps/web/app/components/status/storage-section.tsx index b208bcb0..cb8e79f4 100644 --- a/apps/web/app/components/status/storage-section.tsx +++ b/apps/web/app/components/status/storage-section.tsx @@ -30,14 +30,14 @@ const LEVEL: Record = { /** 清理目标的界面名称 */ export const CLEANUP_LABEL: Record = { - observation: "Feed 观察记录", + observation: "RSS 抓取记录", sse_event: "SSE 补读事件", heartbeat: "进程心跳", task_run: "任务运行", - agent_event: "Agent 事件", + agent_event: "智能体事件", audit: "审计事件", capability_stats: "能力调用统计", - identity_pool: "待识别池归档", + identity_pool: "未识别资源归档", analyze: "统计信息更新", images: "图片缓存容量", kv_cache: "过期缓存", diff --git a/apps/web/app/components/subscriptions/actions.tsx b/apps/web/app/components/subscriptions/actions.tsx index 8381f5a1..5d86801b 100644 --- a/apps/web/app/components/subscriptions/actions.tsx +++ b/apps/web/app/components/subscriptions/actions.tsx @@ -216,7 +216,7 @@ export function DeleteSubscriptionsDialog({ open={open} onOpenChange={onOpenChange} title={ids.length > 1 ? `删除 ${ids.length} 个订阅?` : "删除这个订阅?"} - description="不再搜索和追更。已入库文件、版本目标和下载记录保留,可在订阅历史里重新订阅。" + description="不再搜索和追更。已入库文件、收录版本和下载记录保留,可在订阅历史里重新订阅。" confirmLabel="删除" destructive onConfirm={() => diff --git a/apps/web/app/components/subscriptions/coverage-matrix.tsx b/apps/web/app/components/subscriptions/coverage-matrix.tsx index 1bdd4c5b..13fa3e9a 100644 --- a/apps/web/app/components/subscriptions/coverage-matrix.tsx +++ b/apps/web/app/components/subscriptions/coverage-matrix.tsx @@ -22,7 +22,7 @@ import { seasonTitle, } from "./model.ts"; -// 覆盖矩阵(设计 §2.1):列 = 集(电影 = 内容版本),行 = 版本目标,首行"库内任一版本"。 +// 覆盖矩阵(设计 §2.1):列 = 集(电影 = 内容版本),行 = 收录版本,首行"库内任一版本"。 // 底色无彩,只有状态用状态色:已入库 success、缺失 destructive、可升级/延后 warning;在途用斜纹。 type Row = SubscriptionCoverage["rows"][number]; @@ -120,7 +120,7 @@ export function CoverageMatrix({ scope="col" className="sticky left-0 z-10 bg-surface-raised pr-2 text-left font-normal text-muted-foreground" > - 版本目标 + 收录版本 {cols.map((c) => ( @@ -284,7 +284,7 @@ export function CellSheet({ { label: "pri", value: c.pri !== null ? String(c.pri) : null }, { label: "可升级", value: c.state === "have" ? (c.upgradable ? "是,低于目标档位" : "否") : null }, { - label: "资产", + label: "文件", value: c.assetId ? ( mediaEntityId ? ( ) : null} {sel.row.slotId === null && c.state === "missing" ? ( -

    这一行汇总全部版本。要补全,点具体版本目标那一行的格。

    +

    这一行汇总全部版本。要补全,点具体收录版本那一行的格。

    ) : null} ) : null} diff --git a/apps/web/app/components/subscriptions/model.test.ts b/apps/web/app/components/subscriptions/model.test.ts index a0e3aef6..29c76d8b 100644 --- a/apps/web/app/components/subscriptions/model.test.ts +++ b/apps/web/app/components/subscriptions/model.test.ts @@ -86,7 +86,7 @@ describe("覆盖矩阵格", () => { expect(moveItem(["a", "b", "c"], 2, 0)).toEqual(["c", "a", "b"]); expect(moveItem(["a", "b"], 0, 5)).toEqual(["a", "b"]); }); - it("升级策略取自版本目标", () => { + it("升级策略取自收录版本", () => { expect(upgradeOf([{ upgradeThreshold: {} }], "series")).toBe("off"); expect(upgradeOf([{ upgradeThreshold: { enabled: true, mode: "full_pack" } }], "series")).toBe("full_pack"); expect(upgradeOf([{ upgradeThreshold: { enabled: true } }], "movie")).toBe("movie"); diff --git a/apps/web/app/components/subscriptions/model.ts b/apps/web/app/components/subscriptions/model.ts index 8936aaca..005062b8 100644 --- a/apps/web/app/components/subscriptions/model.ts +++ b/apps/web/app/components/subscriptions/model.ts @@ -55,7 +55,7 @@ export function seasonLabel( return x.seasonScope === "following_seasons" && x.seasonNumber > 0 ? `${base} 起` : base; } -/** 版本目标徽标:洗版中 → 在途 → 缺 N → 已齐;都没有 → 已入库 N */ +/** 收录版本徽标:洗版中 → 在途 → 缺 N → 已齐;都没有 → 已入库 N */ export function slotBadge(s: SubscriptionSlotSummary): ToneLabel { if (s.upgrading) return { @@ -136,7 +136,7 @@ export const PLAN_STAGE: Record = { reserved: "已预留", downl export const SKIP_REASON: Record = { out_of_range: "集范围外", not_aired: "未播出", - not_required: "该版本目标不要求这个内容版本", + not_required: "该收录版本不要求这个内容版本", suspected: "总集数未知,按已见最高集推断", deferred: "有延后的资源", }; @@ -155,7 +155,7 @@ export function seasonTitle(s: SubscriptionCoverage["seasons"][number], kind: Su return s.seasonNumber === 0 ? "特别篇" : `第 ${s.seasonNumber} 季`; } -// ── 版本目标 ── +// ── 收录版本 ── export type UpgradeStrategy = "off" | "per_unit" | "full_pack" | "movie"; export const UPGRADE_LABEL: Record = { @@ -165,7 +165,7 @@ export const UPGRADE_LABEL: Record = { movie: "洗版", }; -/** 订阅版本目标当前的升级策略:任一开启取其 mode */ +/** 订阅收录版本当前的升级策略:任一开启取其 mode */ export function upgradeOf( slots: Pick[], kind: SubscriptionMediaKind, @@ -231,7 +231,7 @@ export type SubscriptionDraft = { include: string; exclude: string; keywords: string; - /** MP 分辨率 / 资源质量 / 特效预设(写到该订阅自己的版本目标) */ + /** MP 分辨率 / 资源质量 / 特效预设(写到该订阅自己的收录版本) */ resolution: SubscriptionResolution | ""; source: SubscriptionSourcePreset | ""; effect: SubscriptionEffect | ""; @@ -334,7 +334,7 @@ const isInt = (s: string) => /^\d+$/.test(s.trim()); /** * 草稿 → params(PATCH 整体替换,所以以现有 params 为底保留未知键)。 - * upgradeChanged:只有升级策略改了才带 best_version(否则每次保存都会 bump 版本目标 revision)。 + * upgradeChanged:只有升级策略改了才带 best_version(否则每次保存都会 bump 收录版本 revision)。 */ export function draftToParams( d: SubscriptionDraft, diff --git a/apps/web/app/components/subscriptions/overview-item.tsx b/apps/web/app/components/subscriptions/overview-item.tsx index f2999920..4cb5c568 100644 --- a/apps/web/app/components/subscriptions/overview-item.tsx +++ b/apps/web/app/components/subscriptions/overview-item.tsx @@ -26,7 +26,9 @@ function Flags({ x }: { x: Item }) { return ( <> {x.group === "paused" ? 暂停 : null} - {x.group === "pending" ? {x.mediaEntityId ? "待定" : "尚未建档"} : null} + {x.group === "pending" ? ( + {x.mediaEntityId ? "待定" : "不在媒体库"} + ) : null} {x.upgrading ? 洗版中 : null} {x.awaitingNewSeason ? 等待新季 : null} @@ -72,7 +74,7 @@ export function ProgressBlock({ x, className }: { x: Pick没有版本目标
    ; + if (slots.length === 0) return 没有收录版本; return (
    {slots.slice(0, max).map((s) => { diff --git a/apps/web/app/components/subscriptions/slot-order.tsx b/apps/web/app/components/subscriptions/slot-order.tsx index 7ff0139b..d3d08047 100644 --- a/apps/web/app/components/subscriptions/slot-order.tsx +++ b/apps/web/app/components/subscriptions/slot-order.tsx @@ -4,7 +4,7 @@ import { Button } from "~/components/ui/button.tsx"; import { cn } from "~/lib/utils.ts"; import { moveItem } from "./model.ts"; -// 版本目标排序:拖动(桌面)或上移/下移(键盘、手机)。第一个是主版本。 +// 收录版本排序:拖动(桌面)或上移/下移(键盘、手机)。第一个是主版本。 export function SlotOrderList({ items, diff --git a/apps/web/app/components/subscriptions/slot-panel.tsx b/apps/web/app/components/subscriptions/slot-panel.tsx index 73720dab..9d2da3f9 100644 --- a/apps/web/app/components/subscriptions/slot-panel.tsx +++ b/apps/web/app/components/subscriptions/slot-panel.tsx @@ -16,7 +16,7 @@ import { import { SlotOrderList } from "./slot-order.tsx"; import { AutoFillFields } from "./subscription-form.tsx"; -// 对比视图的"版本目标"区(设计 §2.2):拖动排序(PUT slot-order)、行尾计数、补全此版本、自动补全开关与预算。 +// 对比视图的"收录版本"区(设计 §2.2):拖动排序(PUT slot-order)、行尾计数、补全此版本、自动补全开关与预算。 type SlotRow = SubscriptionCoverage["rows"][number] & { id: string }; @@ -83,7 +83,7 @@ export function SlotPanel({ return ( {rows.length === 0 ? (

    - {sub.mediaEntityId ? "还没有版本目标。在下方规则区新建。" : "作品建档后才有版本目标。"} + {sub.mediaEntityId ? "还没有收录版本。在下方规则区新建。" : "作品加入媒体库后才有收录版本。"}

    ) : ( void; error?: st kind: "title", titleQuery: c.year ? `${c.title} ${c.year}` : c.title, mediaKind: c.entityType === "movie" ? "movie" : "series", - note: `TMDB ${c.externalId},建档失败,按标题追踪`, + note: `TMDB ${c.externalId},加入媒体库失败,按标题追踪`, }); }; const row = (key: string, main: ReactNode, sub: string, onClick: () => void) => ( @@ -321,7 +321,7 @@ export function SubscriptionFormDialog({ const [totalTouched, setTotalTouched] = useState(false); const [errors, setErrors] = useState({}); const [dup, setDup] = useState(null); - // 编辑:版本目标到了再回填升级策略 + // 编辑:收录版本到了再回填升级策略 useEffect(() => { if (sub && slotsQ.isSuccess && !upgradeTouched) setD((x) => ({ ...x, upgrade: initialUpgrade })); }, [sub, slotsQ.isSuccess, initialUpgrade, upgradeTouched]); @@ -454,7 +454,7 @@ export function SubscriptionFormDialog({ {editing ? "编辑订阅" : "新建订阅"} - {editing ? titleText : initialTarget ? titleText : "从媒体库或 TMDB 选作品;选 TMDB 条目会先建档。"} + {editing ? titleText : initialTarget ? titleText : "从媒体库或 TMDB 选作品;选 TMDB 条目会先加入媒体库。"} {dup ? ( @@ -497,8 +497,8 @@ export function SubscriptionFormDialog({ {target.kind === "entity" ? "媒体库作品" : target.kind === "tmdb" - ? "TMDB 条目,保存时建档" - : (target.note ?? "按标题追踪,尚未建档")} + ? "TMDB 条目,保存时加入媒体库" + : (target.note ?? "按标题追踪,不在媒体库")}

    {initialTarget ? null : ( @@ -541,7 +541,9 @@ export function SubscriptionFormDialog({ ({ value: x.id, label: x.name }))} /> - + -
    +
    {editing ? ( slotsQ.isPending ? ( -

    读取版本目标…

    +

    读取收录版本…

    ) : subSlots.length === 0 ? ( -

    还没有版本目标。在对比视图里新建。

    +

    还没有收录版本。在对比视图里新建。

    ) : ( {groups.isPending ? (

    读取规则组…

    @@ -810,7 +812,7 @@ export function SubscriptionFormDialog({

    - 分辨率、资源质量、特效写进这个订阅自己的版本目标;UHD 和杜比全景声按标题匹配,与「包含」同时生效。 + 分辨率、资源质量、特效写进这个订阅自己的收录版本;UHD 和杜比全景声按标题匹配,与「包含」同时生效。

    { triggeredBy: null, outcome, }); - it("引擎跑完但 Agent 会话超出预算 / 失败:显示业务结果,不显示「完成」", () => { + it("引擎跑完但智能体会话超出预算 / 失败:显示业务结果,不显示「完成」", () => { expect(engineRunStatus({ status: "completed", business: agent("budget_exceeded") })).toEqual({ label: "超出预算", tone: "warning", diff --git a/apps/web/app/components/tasks/labels.ts b/apps/web/app/components/tasks/labels.ts index 4389c2bd..b271149e 100644 --- a/apps/web/app/components/tasks/labels.ts +++ b/apps/web/app/components/tasks/labels.ts @@ -8,9 +8,9 @@ export const BUSINESS_KIND_LABEL: Record = { subscription: "订阅", download: "下载", organization: "整理", - agent: "Agent", + agent: "智能体", schedule: "定时任务", - feed: "Feed", + feed: "RSS", site: "站点", decision: "待处理事项", media: "作品", @@ -23,7 +23,7 @@ export const SOURCE_LABEL: Record = { schedule: "定时", event: "事件", manual: "手动", - agent: "Agent", + agent: "智能体", }; /** 来源 → 服务端 triggeredBy 筛选值(逗号分隔多值) */ export const SOURCE_TRIGGERED_BY: Record = { @@ -46,15 +46,15 @@ export const WORKFLOW_LABEL: Record = { submitDownload: "提交下载", verifyAndImport: "核验入库", refreshMetadata: "刷新元数据", - reconcileLibrary: "资产对账", + reconcileLibrary: "文件核对", cleanupDownloadCache: "清理缓存", resolveDecision: "处理待处理事项", - runAgent: "Agent 会话", - snapshotSite: "站点快照", + runAgent: "智能体会话", + snapshotSite: "站点抓取", refreshSiteAccount: "刷新站点账号", searchSubscription: "订阅搜索", purgeTrash: "清理回收站", - refreshPiTokens: "刷新上游登录", + refreshPiTokens: "刷新模型服务登录", }; export const workflowLabel = (name: string) => WORKFLOW_LABEL[name] ?? name; @@ -115,11 +115,11 @@ export function businessLink(b: EngineRun["business"], input?: unknown): { to: s case "organization": return { to: `/console/organize/${id}`, label: "整理记录" }; case "agent": - return { to: `/console/agent/${id}`, label: "Agent 会话" }; + return { to: `/console/agent/${id}`, label: "智能体会话" }; case "schedule": return { to: `/console/schedules?job=${id}`, label: "定时任务" }; case "feed": - return { to: consolePath("sources"), label: "Feed" }; + return { to: consolePath("sources"), label: "RSS" }; case "site": return { to: `/console/sites/${id}`, label: "站点" }; case "decision": diff --git a/apps/web/app/components/workbench/download-dialog.tsx b/apps/web/app/components/workbench/download-dialog.tsx index e1c7d32d..b22ca448 100644 --- a/apps/web/app/components/workbench/download-dialog.tsx +++ b/apps/web/app/components/workbench/download-dialog.tsx @@ -19,7 +19,7 @@ import { errorMessage } from "~/lib/api.ts"; import { formatBytes } from "~/lib/utils.ts"; const STEPS = [ - "生成下载计划,预留版本目标和预算;每个版本目标同时只有一个在途计划", + "生成下载计划,预留收录版本和预算;每个收录版本同时只有一个在途计划", "已有相同资源或相同 hash 的任务时直接接管,不重复提交", "缓存卷超过硬阈值或读不到磁盘状态时无法提交", "提交前复核免费、做种等易变字段", @@ -93,7 +93,7 @@ export function DownloadDialog({ {slots.length ? (
    {release.entity - ? "这个作品没有版本目标。先在订阅里新建。" + ? "这个作品没有收录版本。先在订阅里新建。" : "资源未关联本地作品。先在「身份与元数据」指定。"}

    )} @@ -143,7 +143,7 @@ export function DownloadDialog({ }} > - 委托 Agent + 委托智能体
    @@ -80,7 +80,7 @@ export function SnapshotPanel({ site }: { site: Site }) { ) : snaps.error ? ( void snaps.refetch()} /> ) : !snaps.data.items.length ? ( -

    还没有快照。

    +

    还没抓取过。

    ) : (
      {snaps.data.items.map((s) => ( diff --git a/apps/web/app/lib/labels.ts b/apps/web/app/lib/labels.ts index a59a8b4e..c308c9c1 100644 --- a/apps/web/app/lib/labels.ts +++ b/apps/web/app/lib/labels.ts @@ -37,8 +37,8 @@ const DOWNLOAD: Record = { export const downloadLabel = (v: string | null | undefined) => pick(DOWNLOAD, v); const DECISION_KIND: Record = { - identity_ambiguous: "识别有歧义", - identity_unresolved: "待识别", + identity_ambiguous: "没识别出作品", + identity_unresolved: "未识别", download_stalled: "下载无进展", download_failed: "下载失败", path_conflict: "路径冲突", @@ -132,7 +132,7 @@ export function actorText(v: string | null | undefined): string { const kind = i < 0 ? v : v.slice(0, i); const rest = i < 0 ? "" : v.slice(i + 1); if (kind === "admin_session" || kind === "dev_admin" || kind === "user") return "管理员"; - if (kind === "agent" || kind === "agent_run") return "Agent"; + if (kind === "agent" || kind === "agent_run") return "智能体"; if (kind === "schedule" || kind === "scheduler") return "定时任务"; if (kind === "api_key") return "API Key"; if (kind === "policy") return `收录策略 ${rest}`; @@ -143,7 +143,7 @@ export function actorText(v: string | null | undefined): string { export const ACTOR_TYPES = [ { value: "user", label: "管理员" }, { value: "api_key", label: "API Key" }, - { value: "agent", label: "Agent" }, + { value: "agent", label: "智能体" }, { value: "system", label: "系统" }, ]; export const actorTypeLabel = (t: string) => ACTOR_TYPES.find((a) => a.value === t)?.label ?? t; diff --git a/apps/web/app/routes/console/agent.$runId.tsx b/apps/web/app/routes/console/agent.$runId.tsx index e918f9f3..82026097 100644 --- a/apps/web/app/routes/console/agent.$runId.tsx +++ b/apps/web/app/routes/console/agent.$runId.tsx @@ -17,7 +17,7 @@ export default function AgentRunPage() { return ( <> diff --git a/apps/web/app/routes/console/agent.tsx b/apps/web/app/routes/console/agent.tsx index a6bb57e2..4ca2f346 100644 --- a/apps/web/app/routes/console/agent.tsx +++ b/apps/web/app/routes/console/agent.tsx @@ -16,8 +16,8 @@ export default function AgentPage() { return ( <> {mock ? (
      diff --git a/apps/web/app/routes/console/audit.tsx b/apps/web/app/routes/console/audit.tsx index 9e7ecb98..a7b585ce 100644 --- a/apps/web/app/routes/console/audit.tsx +++ b/apps/web/app/routes/console/audit.tsx @@ -40,10 +40,10 @@ const ACTION_LABEL: Record = { "schedule.skip": "跳过定时任务", "acquisition_plan.manual_submit": "手动提交下载计划", "engine.run_retry": "重跑任务", - "capability.invoke": "Agent 调用能力", + "capability.invoke": "智能体调用能力", "site.created": "登记站点", "site.updated": "修改站点", - "feed_source.created": "新建 Feed", + "feed_source.created": "新建 RSS", "identity.release_assigned": "指定资源身份", "schedule.update": "修改定时任务", "files.mkdir": "新建目录", @@ -56,11 +56,11 @@ const ACTION_LABEL: Record = { }; const VERB: Record = { create: "新建", update: "修改", delete: "删除", test: "测试", verify: "校验" }; const NOUN: Record = { - library_root: "库根", + library_root: "媒体库目录", downloader_instance: "下载器", media_server_instance: "媒体服务器", path_mapping: "路径映射", - pi_upstream: "Agent 上游", + pi_upstream: "模型服务", schedule: "定时任务", automation_grant: "持续授权", }; diff --git a/apps/web/app/routes/console/downloads.plans.tsx b/apps/web/app/routes/console/downloads.plans.tsx index df560b76..a11e7691 100644 --- a/apps/web/app/routes/console/downloads.plans.tsx +++ b/apps/web/app/routes/console/downloads.plans.tsx @@ -78,7 +78,7 @@ export default function DownloadPlansPage() { return ( <> - + diff --git a/apps/web/app/routes/console/downloads.tsx b/apps/web/app/routes/console/downloads.tsx index bfde0232..a1962032 100644 --- a/apps/web/app/routes/console/downloads.tsx +++ b/apps/web/app/routes/console/downloads.tsx @@ -109,12 +109,12 @@ export default function DownloadsPage() { options={Object.entries(DOWNLOAD_STATUS_LABEL).map(([value, label]) => ({ value, label }))} /> f.set("managed", v)} options={[ - { value: "true", label: "纳管任务" }, - { value: "false", label: "未纳管" }, + { value: "true", label: "本工具" }, + { value: "false", label: "其他" }, ]} /> diff --git a/apps/web/app/routes/console/files.trash.tsx b/apps/web/app/routes/console/files.trash.tsx index 74bac6bc..ab4a3249 100644 --- a/apps/web/app/routes/console/files.trash.tsx +++ b/apps/web/app/routes/console/files.trash.tsx @@ -212,14 +212,14 @@ export default function FilesTrashPage() { }, { key: "asset", - header: "关联资产", + header: "媒体库记录", hideBelow: "lg", cell: (r) => { const snap = r.assetSnapshot as { title?: string; contentUnit?: string | null; mediaEntityId?: string } | null; if (!snap?.title) return —; const label = `${snap.title}${snap.contentUnit ? ` ${snap.contentUnit}` : ""}`; return ( - + {snap.mediaEntityId ? ( {label} @@ -274,7 +274,7 @@ export default function FilesTrashPage() { <> {filesMockEnabled() ? 演示数据 : null} @@ -419,7 +419,7 @@ function RestoreToDialog({ 还原到… - {ids.length} 项 · 选一个可写库根下的目录。同名项已存在时不覆盖。 + {ids.length} 项 · 选一个可写媒体库目录下的目录。同名项已存在时不覆盖。 diff --git a/apps/web/app/routes/console/files.tsx b/apps/web/app/routes/console/files.tsx index 05081a9a..3277c71b 100644 --- a/apps/web/app/routes/console/files.tsx +++ b/apps/web/app/routes/console/files.tsx @@ -97,7 +97,7 @@ import { cn, formatBytes } from "~/lib/utils.ts"; const HEALTH_REASON: Record = { root_missing: "挂载点不存在", - sentinel_missing: "目录里没有哨兵文件,挂载可能没生效", + sentinel_missing: "目录里没有标记文件,挂载可能没生效", timeout: "探测超时", permission_denied: "没有读取权限", }; @@ -398,7 +398,9 @@ export default function FilesPage() { onSuccess: (r) => { const ok = r.items.filter((i) => i.status === "ok").length; toast.success(`探测完成:${ok}/${r.items.length} 个读到媒体信息`, { - description: r.items.some((i) => i.savedToAsset) ? "媒体信息已写入资产" : "未登记文件的结果缓存 24 小时", + description: r.items.some((i) => i.savedToAsset) + ? "媒体信息已写入媒体库记录" + : "未登记文件的结果缓存 24 小时", }); }, onError: (err) => toast.error(filesErrorText(err, "探测失败")), @@ -540,7 +542,7 @@ export default function FilesPage() { if (items.every((i) => i.root === target.root && (i.path === target.path || parentRel(i.path) === target.path))) return; const tRoot = rootList.find((r) => r.id === target.root); - if (!tRoot?.writable) return toast.error("只能拖到可写的库根"); + if (!tRoot?.writable) return toast.error("只能拖到可写的媒体库目录"); setDialog({ kind: "transfer", items, mode: ev.altKey ? "copy" : "move", target }); }; @@ -567,7 +569,7 @@ export default function FilesPage() { plain icon={FolderTree} title="还没有登记目录" - description="在设置里添加库根或下载器路径映射。" + description="在设置里添加媒体库目录或下载器路径映射。" className="px-2 py-8" /> ) : null} @@ -596,7 +598,7 @@ export default function FilesPage() {

      {root.name} 离线

      - {offline ? filesErrorText(list.error) : healthReasonText(root.healthReason)}。离线期间资产状态不变。 + {offline ? filesErrorText(list.error) : healthReasonText(root.healthReason)}。离线期间文件状态不变。

      上次检查:{relTime(root.checkedAt)}

      - 版本目标满足率 + 收录版本满足率 {rate === null ? "—" : `${rate}%`}
      @@ -376,7 +376,7 @@ function PlanStrip() { 失败 {s.auto.failed} - + 启用 {s.checks.enabled} / {s.checks.total} @@ -464,7 +464,7 @@ function SourcesSection({ s }: { s: Dashboard["sources"] }) { ...siteRows, ...s.feeds.map((x) => ({ id: x.id, - kind: "Feed", + kind: "RSS", name: x.name, enabled: x.enabled, health: x.health, @@ -491,7 +491,7 @@ function SourcesSection({ s }: { s: Dashboard["sources"] }) { return (
      {rows.length === 0 ? ( - + ) : ( <> {accountIssues ?

      {accountIssues} 个站点的账号需要处理

      : null} diff --git a/apps/web/app/routes/console/inbox.tsx b/apps/web/app/routes/console/inbox.tsx index a3195355..b155fbe6 100644 --- a/apps/web/app/routes/console/inbox.tsx +++ b/apps/web/app/routes/console/inbox.tsx @@ -78,7 +78,7 @@ function GroupReleases({ d }: { d: Decision }) { ))} {count > list.length ? (
    • - 另有 {count - list.length} 条,可在媒体概览 → 待识别按标题检索 + 另有 {count - list.length} 条,可在媒体概览 → 未识别按标题检索
    • ) : null}
    @@ -94,7 +94,7 @@ function entityOf(d: Decision): string | null { } const IMPACT_KEY: Record = { - slots: "版本目标", + slots: "收录版本", units: "内容单元", files: "文件", releases: "资源", @@ -140,7 +140,7 @@ function Evidence({ d }: { d: Decision }) {
    - Agent 已检查 + 智能体已检查 {checked.map((c) => ( {c} @@ -303,7 +303,7 @@ function DecisionCard({ d }: { d: Decision }) { to={consolePath("agent", d.agentRunId)} className="ml-auto inline-flex items-center text-meta text-muted-foreground hover:text-foreground hover:underline" > - Agent 调查记录 + 智能体调查记录 ) : null} @@ -404,7 +404,7 @@ export default function InboxPage() { <> diff --git a/apps/web/app/routes/console/library.$id.tsx b/apps/web/app/routes/console/library.$id.tsx index 42b00361..baf39825 100644 --- a/apps/web/app/routes/console/library.$id.tsx +++ b/apps/web/app/routes/console/library.$id.tsx @@ -83,7 +83,7 @@ import { formatBytes, formatDateTime } from "~/lib/utils.ts"; // 作品详情(REQ §13.1 / §13.4.3):实际路径、外部身份映射及状态、元数据来源、各 Slot、候选拒绝理由、 // 内容单元、下载历史与校验结果,同一页同时展示。 -// MD-1(对照 MP 媒体详情):头部背景图 + 海报 + 评分 / 类型 / 状态 + 简介、主创、季折叠与每集行(库内状态按版本目标), +// MD-1(对照 MP 媒体详情):头部背景图 + 海报 + 评分 / 类型 / 状态 + 简介、主创、季折叠与每集行(库内状态按收录版本), // 头部 / 主创 / 季集组件与仅 TMDB 的作品详情页共用(components/media/media-detail)。 const pad = (n: number) => String(n).padStart(2, "0"); @@ -120,13 +120,13 @@ function PathsPanel({ detail }: { detail: LibraryDetail }) { const rootOf = (id: string | null) => roots.data?.items.find((r) => r.id === id); const bindings = detail.pathBindings.filter((b) => b.status !== "superseded"); return ( - + {bindings.length === 0 && detail.assets.length === 0 ? ( ) : ( @@ -139,7 +139,9 @@ function PathsPanel({ detail }: { detail: LibraryDetail }) {
  • {b.bindingKind === "season" ? `第 ${b.seasonNumber} 季目录` : "作品目录"} - 库根 {root?.name ?? "未知库根"} + + 媒体库目录 {root?.name ?? "未知媒体库目录"} + {b.status === "conflict" ? 绑定冲突 : null}

    @@ -171,7 +173,7 @@ function PathsPanel({ detail }: { detail: LibraryDetail }) { {l.libraryRootId ? ( - {rootOf(l.libraryRootId)?.name ?? "库根"} + {rootOf(l.libraryRootId)?.name ?? "媒体库目录"} ) : null}

  • @@ -474,12 +476,12 @@ function MetadataPanel({ detail, entity }: { detail: LibraryDetail; entity: Medi function SlotsPanel({ detail, units }: { detail: LibraryDetail; units: ContentUnit[] }) { if (detail.slots.length === 0) { return ( - + 去订阅 @@ -492,7 +494,7 @@ function SlotsPanel({ detail, units }: { detail: LibraryDetail; units: ContentUn } const assetOf = (id: string | null) => detail.assets.find((a) => a.asset.id === id); return ( - +
    {detail.slots.map((s) => { const cur = assetOf(s.currentAssetId); @@ -593,7 +595,7 @@ function UnitsPanel({ entity, detail }: { entity: ReturnType = { have: "success", @@ -701,7 +703,7 @@ function EpisodesPanel({ detail, units }: { detail: LibraryDetail; units: Conten } > {rows.length > 1 ? ( -
    +
    {rows.map((r, i) => (
    ), }, - { key: "slots", header: "版本目标", cell: (r) => }, + { key: "slots", header: "收录版本", cell: (r) => }, { key: "assets", - header: "资产", + header: "文件", align: "right", hideBelow: "sm", cell: (r) => {r.assetCount}, @@ -214,7 +214,7 @@ function WorksView() { ? { title: "没有符合条件的作品", description: "换个关键字或清除筛选。", icon: LibraryIcon } : { title: "还没有作品", - description: "扫描库根或新建订阅后,作品会出现在这里。", + description: "扫描媒体库目录或新建订阅后,作品会出现在这里。", icon: LibraryIcon, action: ( )}
    @@ -197,13 +197,13 @@ function InventoryPanel({ d }: { d: TmdbMediaDetail }) { const inv = d.inventory; const line = slotLine(inv); return ( - +
    -
    本地账本
    +
    媒体库
    {inv.state === "not_collected" ? ( -

    未收录。订阅或建档后开始追踪。

    +

    不在媒体库。

    ) : ( <>

    @@ -283,7 +283,7 @@ function SeasonsPanel({ d }: { d: TmdbMediaDetail }) { }); const seasons = [...d.seasons].sort((a, b) => (a.seasonNumber || 999) - (b.seasonNumber || 999)); return ( - +

    {seasons.map((s) => ( = { queued: { label: "排队", tone: "neutral" }, running: { label: "执行中", tone: "neutral" }, @@ -160,7 +160,8 @@ export default function OrganizeRecordPage() { }) } > - 让 Agent 排查 + + 让智能体排查 ) : null} diff --git a/apps/web/app/routes/console/sites.$siteId.tsx b/apps/web/app/routes/console/sites.$siteId.tsx index 31dd52e9..4b97ec66 100644 --- a/apps/web/app/routes/console/sites.$siteId.tsx +++ b/apps/web/app/routes/console/sites.$siteId.tsx @@ -49,7 +49,7 @@ const PROMOS = [ ]; const RESOLUTION: { value: ResolutionStatus; label: string }[] = [ { value: "resolved", label: "已识别" }, - { value: "unresolved", label: "待识别" }, + { value: "unresolved", label: "未识别" }, { value: "ambiguous", label: "有歧义" }, { value: "unidentifiable", label: "无法识别" }, ]; @@ -210,7 +210,7 @@ export default function SiteWorkbenchPage() { {health ? 健康 {health.text} : null} {!s.enabled ? 站点已停用,只显示历史数据 : null} {circuit ? {circuit} : null} - {s.snapshotEnabled && snapshotStale(s) ? 快照过期 : null} + {s.snapshotEnabled && snapshotStale(s) ? 抓取已过期 : null} ) : ( "加载中…" @@ -254,7 +254,7 @@ export default function SiteWorkbenchPage() { onClick={() => openAgentDrawer({ label: `站点:${s?.name ?? siteId}`, siteId })} > - 问 Agent + 问智能体 } @@ -277,7 +277,7 @@ export default function SiteWorkbenchPage() { 概览 资源 原站视图 - 快照与 Feed + 抓取与 RSS 账号 @@ -302,26 +302,26 @@ export default function SiteWorkbenchPage() { snap.mutate( { id: siteId }, { - onSuccess: () => toast.success("快照已排队"), + onSuccess: () => toast.success("已开始抓取"), onError: (e) => toast.error(errorMessage(e)), }, ) } > - 快照 + 刷新 } @@ -374,17 +374,15 @@ export default function SiteWorkbenchPage() { ) : ( @@ -437,11 +435,11 @@ export default function SiteWorkbenchPage() {
    {s ? : } setCreatingFeed(true)}> - 新建 Feed + 新建 RSS } > @@ -486,12 +484,12 @@ function CoverageBanner({ notes.push({ tone: "warning", text: coverage.lastSnapshotAt - ? `快照已过期(上次 ${relTime(coverage.lastSnapshotAt)})` - : "没有快照,列表只来自 Feed 和实时补查", + ? `抓取已过期(上次 ${relTime(coverage.lastSnapshotAt)})` + : "还没抓取过,列表只来自 RSS 和站点搜索", }); - if (coverage.liveError) notes.push({ tone: "warning", text: `实时补查失败:${coverage.liveError}` }); + if (coverage.liveError) notes.push({ tone: "warning", text: `去站点搜失败:${coverage.liveError}` }); else if (coverage.live) - notes.push({ tone: "success", text: coverage.coalesced ? "已合并到进行中的同一搜索" : "已实时补查" }); + notes.push({ tone: "success", text: coverage.coalesced ? "已合并到进行中的同一搜索" : "站点搜索完成" }); } if (!notes.length) return null; return ( diff --git a/apps/web/app/routes/console/sites.tsx b/apps/web/app/routes/console/sites.tsx index 3091977d..4ecf92a0 100644 --- a/apps/web/app/routes/console/sites.tsx +++ b/apps/web/app/routes/console/sites.tsx @@ -100,7 +100,7 @@ export default function SitesPage() { <> } /> @@ -108,7 +108,7 @@ export default function SourcesPage() { }} /> - + = { const KIND_LABEL: Record = { api: "API", worker: "Worker(io)", - "worker:agent": "Worker(Agent)", + "worker:agent": "Worker(智能体)", all: "单进程(all)", }; @@ -51,13 +51,13 @@ const KIND_LABEL: Record = { const KIND_NAME: Record = { api: "API", worker: "io worker", - "worker:agent": "Agent worker", + "worker:agent": "智能体 worker", all: "单进程", }; const MOUNT_REASON: Record = { root_missing: "目录不存在", - sentinel_missing: "哨兵文件缺失", + sentinel_missing: "目录不可访问或未挂载", permission_denied: "没有权限", error: "检查出错", timeout: "检查超时", @@ -227,7 +227,7 @@ function Processes({ d }: { d: SystemHealth }) { — ) : ( - io {p.concurrency.io ?? 0} · Agent {p.concurrency.agent ?? 0} + io {p.concurrency.io ?? 0} · 智能体 {p.concurrency.agent ?? 0} ), }, @@ -371,7 +371,7 @@ function History() { function Disks({ d }: { d: SystemHealth }) { const t = d.diskThresholds; - const scope = { data_dir: "数据目录", library_root: "库根", download_dir: "下载目录" } as const; + const scope = { data_dir: "数据目录", library_root: "媒体库目录", download_dir: "下载目录" } as const; return ( x.level))}>

    @@ -428,7 +428,7 @@ function Mounts({ d }: { d: SystemHealth }) { return ( {d.mounts.length === 0 ? ( -

    没有启用的库根或下载目录映射。

    +

    没有启用的媒体库目录或下载目录映射。

    ) : (
      {d.mounts.map((m) => ( @@ -436,7 +436,7 @@ function Mounts({ d }: { d: SystemHealth }) {

      - {m.kind === "library_root" ? "库根" : "下载目录"} + {m.kind === "library_root" ? "媒体库目录" : "下载目录"} {m.name}

      diff --git a/apps/web/app/routes/console/subscriptions.$id.tsx b/apps/web/app/routes/console/subscriptions.$id.tsx index f263ebc9..d941cf23 100644 --- a/apps/web/app/routes/console/subscriptions.$id.tsx +++ b/apps/web/app/routes/console/subscriptions.$id.tsx @@ -39,7 +39,7 @@ import { errorMessage } from "~/lib/api.ts"; import { actorText } from "~/lib/labels.ts"; import { cn, formatDateTime } from "~/lib/utils.ts"; -// 订阅对比视图(FE-5,docs/design/subscriptions.md §2):季页签 → 集 × 版本目标矩阵;版本目标排序与自动补全;订阅信息。 +// 订阅对比视图(FE-5,docs/design/subscriptions.md §2):季页签 → 集 × 收录版本矩阵;收录版本排序与自动补全;订阅信息。 export const meta = () => [{ title: "订阅详情 · UHD 媒体自动化控制台" }]; @@ -118,7 +118,7 @@ export default function SubscriptionDetailPage() { {item?.group ? {GROUP_LABEL[item.group]} : null} {item?.upgrading ? 洗版中 : null} {item?.awaitingNewSeason ? 等待新季 : null} - {!sub.mediaEntityId ? 尚未建档 : null} + {!sub.mediaEntityId ? 不在媒体库 : null}
      {item ? (
      @@ -163,7 +163,7 @@ export default function SubscriptionDetailPage() {
      @@ -172,7 +172,7 @@ export default function SubscriptionDetailPage() { } > {!sub.mediaEntityId ? ( -

      作品建档后才能对比。建档后自动关联到这个订阅。

      +

      作品加入媒体库后才能对比,加入后自动关联到这个订阅。

      ) : covQ.isPending ? ( ) : covQ.isError ? ( @@ -271,7 +271,7 @@ export default function SubscriptionDetailPage() { />
      {sub.mediaEntityId ? ( - + ) : null} diff --git a/apps/web/app/routes/console/subscriptions.tsx b/apps/web/app/routes/console/subscriptions.tsx index 13f7e6ea..62a84810 100644 --- a/apps/web/app/routes/console/subscriptions.tsx +++ b/apps/web/app/routes/console/subscriptions.tsx @@ -393,7 +393,7 @@ export default function SubscriptionsPage() { icon={CalendarClock} title={filtered || v.group || v.kind ? "没有符合条件的订阅" : "还没有订阅"} description={ - filtered || v.group || v.kind ? undefined : "从媒体库、搜索或 Agent 添加,也可以点「新建订阅」。" + filtered || v.group || v.kind ? undefined : "从媒体库、搜索或智能体添加,也可以点「新建订阅」。" } /> ) : grouped ? ( diff --git a/apps/web/app/routes/console/tasks.$id.tsx b/apps/web/app/routes/console/tasks.$id.tsx index ebdb47ca..e2714a4f 100644 --- a/apps/web/app/routes/console/tasks.$id.tsx +++ b/apps/web/app/routes/console/tasks.$id.tsx @@ -59,7 +59,7 @@ const OP_KIND: Record = { delete_source: "删除源文件", delete_target: "删除目标文件", verify: "校验", - register: "登记资产", + register: "登记入库", qb_pause: "暂停下载", qb_resume: "恢复下载", qb_remove: "移除下载", diff --git a/apps/web/app/routes/console/tasks.tsx b/apps/web/app/routes/console/tasks.tsx index f45f9388..12efd5a9 100644 --- a/apps/web/app/routes/console/tasks.tsx +++ b/apps/web/app/routes/console/tasks.tsx @@ -99,7 +99,7 @@ export default function TasksPage() { <> qc.invalidateQueries({ queryKey: qk.all("engine") })}> diff --git a/apps/web/scripts/smoke.mjs b/apps/web/scripts/smoke.mjs index adc3d11f..5cd0e717 100644 --- a/apps/web/scripts/smoke.mjs +++ b/apps/web/scripts/smoke.mjs @@ -219,12 +219,12 @@ async function main() { if (SHOTS) await page.screenshot({ path: join(OUT, `${name}.png`), fullPage: true }).catch(() => {}); results.push({ path, ms: Date.now() - t0, problems: [...new Set(problems)] }); } - // ── 交互:Agent 上游抽屉的模型选择(PI-2;只看来源行与可搜索下拉,不点「获取」,避免拿真实凭证出站)── + // ── 交互:模型服务抽屉的模型选择(PI-2;只看来源行与可搜索下拉,不点「获取」,避免拿真实凭证出站)── { - const label = "/console/settings#agent → 上游抽屉 · 模型选择"; + const label = "/console/settings#agent → 模型服务抽屉 · 模型选择"; const up = await page.request.get(`${BASE}/api/v1/pi/upstreams`); const first = up.ok() ? (await up.json().catch(() => null))?.data?.items?.[0] : null; - if (!first) skipped.push(`${label}(没有上游)`); + if (!first) skipped.push(`${label}(没有模型服务)`); else { problems = []; const t0 = Date.now(); @@ -254,9 +254,9 @@ async function main() { } } - // ── 交互:Agent 抽屉「历史」Tab(AGUI-2;演示数据,点一条在抽屉内打开、返回列表、关了再开仍停在历史)── + // ── 交互:智能体抽屉「历史」Tab(AGUI-2;演示数据,点一条在抽屉内打开、返回列表、关了再开仍停在历史)── { - const label = "Agent 抽屉 → 历史 → 抽屉内打开会话"; + const label = "智能体抽屉 → 历史 → 抽屉内打开会话"; problems = []; const t0 = Date.now(); try { @@ -265,7 +265,7 @@ async function main() { const launcher = page.locator("[data-agent-launcher]:visible").last(); await launcher.click(); await page.getByRole("tab", { name: "历史" }).click(); - const list = page.getByRole("navigation", { name: "Agent 会话列表" }); + const list = page.getByRole("navigation", { name: "智能体会话列表" }); await list.getByRole("button").first().click(); await page.getByRole("button", { name: "返回列表" }).waitFor({ timeout: 10_000 }); if (new URL(page.url()).pathname !== "/console/audit") problems.push("点历史会话跳走了页面"); diff --git a/docs/design/glossary.md b/docs/design/glossary.md index 0b4d3de1..feed20a6 100644 --- a/docs/design/glossary.md +++ b/docs/design/glossary.md @@ -1,6 +1,6 @@ # 术语表与文案规则 -界面、错误信息、Agent 提示词里同一概念只用一个名字。代码标识符(`slot`、`decision`、`feed`)不受限制。依据 REQ v0.7 §3.1。 +界面、错误信息、给模型的提示词里同一概念只用一个名字。代码标识符(`slot`、`decision`、`feed`)不受限制。依据 REQ v0.7 §3.1。 ## 术语 @@ -8,33 +8,38 @@ | --- | --- | --- | | MediaEntity | 作品 | 媒体实体、条目、影片 | | ContentUnit | 季 / 集;统称"内容单元" | 单元、分集单元 | -| Slot | 版本目标 | Slot、收录目标、目标槽。给模型的说明可写一次"版本目标(slot)"对应参数名 | +| Slot | 收录版本(DEAI-2 起;原"版本目标") | Slot、版本目标、收录目标、收录位、目标槽。给模型的说明可写一次"收录版本(slot)"对应参数名 | | AcquisitionPolicy | 收录策略(版本号称"策略版本") | 收录规则、规则集 | | RuleGroup | 规则组;条件叫"关键字"(内置)/"自定义条件";只有一条规则的组在规则页以"偏好"标签显示 | 偏好标签、过滤规则、规则集 | | Subscription | 订阅 | 追剧、关注 | | Site / SiteAccount | 站点 / 站点账号 | PT 源 | -| FeedSource | Feed | RSS 源、订阅源 | -| 站点列表/搜索结果的一次抓取 | 快照 | | +| FeedSource | RSS;新建叫"新建 RSS",地址叫"RSS 地址" | Feed、RSS 源、订阅源、来源健康、RSS 订阅 | +| 站点列表/搜索结果的一次抓取 | 抓取;时间写"上次抓取",按钮"刷新";站点实时搜索叫"去站点搜" | 快照、实时补查 | | SourceRelease | 资源 | 来源资源、站点资源、种子(种子只指 .torrent 文件) | -| 资源按版本目标评估后 | 候选 | | +| 资源按收录版本评估后 | 不单独命名;说"可下载的资源" | 候选(只在代码与给模型的说明里) | | Decision | 待处理事项;入口叫"待处理" | 决策、工单、待决 | | Approval | 审批(名词);按钮用"批准 / 拒绝" | 授权记录 | | DownloadTask | 下载任务 | 下载单 | | Organization record | 整理记录;动作叫"整理",结果状态叫"已入库" | 转移、入库记录 | | 整理方式 | 复制 / 移动 / 硬链接 | Copy / Move | -| Library root | 库根 | 媒体库根、根目录 | +| Library root | 媒体库目录 | 库根、媒体库根、根目录 | | 文件管理可浏览的目录(库根 + 下载器路径映射) | 登记目录 | 登记根、根 | -| MediaAsset / AssetLocation | 资产 / 资产位置(登记进媒体库的文件) | 媒体文件记录 | +| MediaAsset / AssetLocation | 文件 / 已入库文件;位置叫"文件位置" | 资产、资产位置、媒体文件记录 | | ExternalIdentity | 外部身份(TMDB/TVDB/IMDb ID) | 外部 ID 映射、外部标识 | -| 未确认身份的文件/资源 | 待识别 | 未识别(IA 重构时若改名,全站一起改) | +| 未确认身份的文件/资源 | 未识别 | 待识别、待识别池 | +| identity_ambiguous / 整理时认不出作品的待处理事项 | 没识别出作品;主按钮"指定作品" | 身份歧义、识别有歧义、无法确定作品、待确认身份 | +| 作品登记进本地库(不订阅) | 加入媒体库;状态"在媒体库 · 未下载"/"不在媒体库" | 建档、已建档、未收录 | +| 本工具提交并跟踪的下载任务(managed) | 本工具管理;反面叫"外部任务";筛选"来源:本工具 / 其他" | 纳管、未纳管、接管 | +| 媒体库目录里的 `.uhd-root` | 标记文件;按钮"检查目录";失败说"目录不可访问或未挂载" | 哨兵、哨兵文件 | +| 作品 / 版本 / 文件 / 外部身份的本地记录总称 | 不单独命名 | 账本、本地账本 | | Trash | 回收站 | 垃圾桶 | -| Agent | Agent;上游模型配置叫"Agent 上游" | 助手、智能体、AI、Pi 上游、模型上游 | -| PiUpstream | Agent 上游;在 Agent 设置分区和上游页内简称"上游" | Pi 上游、模型上游、Provider 配置 | +| Agent | 智能体(DEAI-2 起;"智能体会话""问智能体""智能体巡检");代码标识符、API 与 MCP 文档里的 agent 不改 | Agent(界面)、助手、AI、Agent 会话、Agent 对话 | +| PiUpstream | 模型服务;默认的一个叫"默认模型服务" | Agent 上游、上游、Pi 上游、模型上游、Provider 配置 | | OAuth 型上游的登录 | 订阅登录 | OAuth 登录、账号授权 | | 密钥、Cookie、密码、令牌的统称 | 凭证 | 凭据、认证信息 | | ScheduledJob | 定时任务 | 计划任务、周期作业、cron 任务 | -| 定时任务的配置页 `/console/schedules` | 计划;两节叫"自动任务"(kind=task)与"Agent 巡检" | 定时任务看板、计划任务 | -| kind=agent_check 的定时任务 | 巡检 | 巡查、Agent 定时任务 | +| 定时任务的配置页 `/console/schedules` | 计划;两节叫"智能体巡检"(置顶)与"自动任务"(kind=task,按分类分组);维护类任务合成一组"数据清理" | 定时任务看板、计划任务 | +| kind=agent_check 的定时任务 | 巡检;全称"智能体巡检" | 巡查、Agent 定时任务 | | 定时任务每次执行的记录 | 运行记录 | 历史、执行历史 | | AgentRun(parent_run_id 非空) | 子会话;派生它的叫"父会话" | 子代理、子 Agent、subagent | | AgentRun 被中止 | 停止;状态"已停止" | 中止、取消 | @@ -44,7 +49,7 @@ | agent.propose_options | 方案;动作叫"选定",都不要叫"退回" | 选项、提案 | | scope.proposeFirst | 先看方案再执行 | 只要方案、只给方案、plan_only 模式 | | 工作流引擎的一次执行 | 运行 | run | -| 正在写心跳的进程(process_heartbeat 当前行) | 当前实例;横幅按角色称呼:API / io worker / Agent worker / 单进程 | 活进程、在线实例 | +| 正在写心跳的进程(process_heartbeat 当前行) | 当前实例;横幅按角色称呼:API / io worker / 智能体 worker / 单进程 | 活进程、在线实例 | | 已退出、容器已重建或被同容器新进程替换的心跳行 | 已下线实例;正常退出的标"已退出" | 历史实例、僵尸进程、死进程 | ## 文案规则 @@ -61,6 +66,8 @@ 10. 标题、按钮、标签、Toast 末尾不加句号。 11. 订阅登录的条款提示只陈述事实:借用哪个官方客户端身份、可能违反条款、账号可能被限流或封禁;不劝阻,不美化。 12. 给模型看的文字(巡检目标、调查提示)同样适用:具体、短,用界面同一套术语。 +13. ID、hash、trace、token 数、耗时、模型 ID、原始字段、内部状态码和错误码不进默认视图,放进「技术信息」折叠区(`TechInfo` 组件,设置 → 界面可改为默认展开)。错误码先映射成一句人话 + 下一步动作(`lib/reasons.ts`),原码留在技术信息里。 +14. 只列非零项:0 计数、全部正常的状态不铺开,正常态不加徽章。 ## 标点 @@ -69,7 +76,7 @@ 1. 中文句子用全角标点:`,。;:?!、`。半角 `, ; : ? !` 只出现在代码、路径、URL、键值、时间(`12:30`)和整句英文里。 2. 全角标点前后不加空格;不叠用标点(不写 `,。`、`!!`、`?!`)。 3. 括号里有中文用全角 `()`,紧贴前后文字;括号里只有英文、数字、代码时用半角 `()`,前面是英文时空一格:`Oppenheimer (2023)`、`读取失败(ENOTCONN)`。 -4. 中文与英文、数字之间空一个半角空格:`3 个作品`、`打开 Agent`、`TMDB 不可用`;行首行尾和全角标点旁不加。 +4. 中文与英文、数字之间空一个半角空格:`3 个作品`、`打开 TMDB`、`TMDB 不可用`;行首行尾和全角标点旁不加。 5. 数字与单位:`%`、`°` 紧贴数字(`80%`);英文单位空一格,与 `formatBytes` 输出一致(`1.1 TB`、`15 s`);数字和字母组成的记号连写(`1080p`、`S01E02`、`H.265`、`4K`)。 6. 省略号只用一个 `…`(U+2026),不用 `...` 或 `……`;进行中状态写 `加载中…`,菜单项"需要再选一步"写 `移动到…`。 7. 标题、按钮、标签、表头、列表项、Toast 末尾不加句号(同文案规则 10)。 diff --git a/packages/agent/src/pi/model.ts b/packages/agent/src/pi/model.ts index 76c0aae0..817f5b45 100644 --- a/packages/agent/src/pi/model.ts +++ b/packages/agent/src/pi/model.ts @@ -23,8 +23,8 @@ const PROVIDER_ID = { anthropic: "umt-anthropic", "openai-compatible": "umt-open */ export function resolveModelFromSettings(settings: ResolvedAgentSettings): ResolvedPiModel { if (settings.provider !== "anthropic" && settings.provider !== "openai-compatible") - throw new Error(`provider ${settings.provider} 需要按上游装配(PiAgentRuntimeOptions.upstream)`); - if (!settings.apiKey) throw new Error("未配置上游密钥"); + throw new Error(`provider ${settings.provider} 需要按模型服务装配(PiAgentRuntimeOptions.upstream)`); + if (!settings.apiKey) throw new Error("未配置模型服务密钥"); const providerId = PROVIDER_ID[settings.provider]; const common = { id: settings.model, @@ -49,7 +49,7 @@ export function resolveModelFromSettings(settings: ResolvedAgentSettings): Resol models.setProvider(createProvider({ id: providerId, auth, models: [m], api: anthropicMessagesApi() })); model = m; } else { - if (!settings.baseUrl) throw new Error("openai-compatible 上游必须配置 baseUrl(带 /v1)"); + if (!settings.baseUrl) throw new Error("openai-compatible 模型服务必须配置 baseUrl(带 /v1)"); const m: Model<"openai-completions"> = { ...common, api: "openai-completions", diff --git a/packages/agent/src/pi/runtime.ts b/packages/agent/src/pi/runtime.ts index 06c2eb25..2343264d 100644 --- a/packages/agent/src/pi/runtime.ts +++ b/packages/agent/src/pi/runtime.ts @@ -400,7 +400,7 @@ class PiAgentRun implements AgentRunHandle { const err = record && "error" in record ? record.error : undefined; outcome = this.failed("error", redactString(err ? `${err.code}: ${err.message}` : "运行失败")); } else if (status === "suspended") { - outcome = this.failed("error", "上游进入延迟模式(deferred),v0.1 不支持"); + outcome = this.failed("error", "模型服务进入延迟模式(deferred),v0.1 不支持"); } else { outcome = { status: "completed", finalText: this.finalText, usage: this.usage }; this.emit({ type: "run_completed", finalText: outcome.finalText, usage: this.usage }); @@ -540,7 +540,7 @@ export function toolContent( if (!vision) return [ { type: "text", text }, - { type: "text", text: "(截图未发送:当前上游模型不支持图片输入;用 browser.snapshot 读取页面文字)" }, + { type: "text", text: "(截图未发送:当前模型不支持图片输入;用 browser.snapshot 读取页面文字)" }, ]; return [ { type: "text", text }, diff --git a/packages/agent/src/pi/upstream.ts b/packages/agent/src/pi/upstream.ts index b60771b9..0bc014da 100644 --- a/packages/agent/src/pi/upstream.ts +++ b/packages/agent/src/pi/upstream.ts @@ -329,7 +329,7 @@ export class PiUpstreamClient { private pickModel(modelId: string | null | undefined): { model: Model; inCatalog: boolean } { const all = this.models.getModels(this.providerId); const id = modelId ?? this.spec.model ?? all[0]?.id; - if (!id) throw new UpstreamError("model", "该上游没有可用模型,请填写模型 id"); + if (!id) throw new UpstreamError("model", "该模型服务没有可用模型,请填写模型 id"); const found = this.models.getModel(this.providerId, id); if (found) return { model: found, inCatalog: true }; const template = all[0]; @@ -369,7 +369,7 @@ export class PiUpstreamClient { throw classifyUpstreamError(err); } if (res.stopReason === "error" || res.stopReason === "aborted") - throw classifyUpstreamError(new Error(res.errorMessage ?? "上游返回错误")); + throw classifyUpstreamError(new Error(res.errorMessage ?? "模型服务返回错误")); } return { source, model: model.id, inCatalog }; } diff --git a/packages/contracts/src/api.ts b/packages/contracts/src/api.ts index a4db168d..478f87b8 100644 --- a/packages/contracts/src/api.ts +++ b/packages/contracts/src/api.ts @@ -718,7 +718,7 @@ export const Api = { path: "/subscriptions/{id}/coverage", scope: "read", summary: - "覆盖矩阵:季 → 集 × 版本目标(电影:内容版本 × 版本目标),每格 have/inflight/missing/skipped;缓存 30s,refresh=true 强制重算", + "覆盖矩阵:季 → 集 × 收录版本(电影:内容版本 × 收录版本),每格 have/inflight/missing/skipped;缓存 30s,refresh=true 强制重算", params: IdParam, query: z.object({ refresh: z.enum(["true", "false"]).optional() }), response: policy.SubscriptionCoverage, @@ -759,7 +759,7 @@ export const Api = { method: "post", path: "/subscriptions/{id}/slots/{slotId}/fill", scope: "admin", - summary: "手动补全某版本目标(不受自动补全开关与补全预算限制)", + summary: "手动补全某收录版本(不受自动补全开关与补全预算限制)", params: z.object({ id: Id, slotId: Id }), body: policy.SlotFillRequest, response: z.object({ runId: z.string(), deduplicated: z.boolean() }), @@ -768,7 +768,7 @@ export const Api = { method: "put", path: "/subscriptions/{id}/slot-order", scope: "admin", - summary: "版本目标排序(第一个为主版本)", + summary: "收录版本排序(第一个为主版本)", params: IdParam, body: policy.SlotOrderSet, response: z.array(policy.AcquisitionSlot), @@ -785,7 +785,7 @@ export const Api = { method: "get", path: "/subscriptions/{id}/progress", scope: "read", - summary: "订阅进度:季总集数账本、每集状态(已覆盖/在途/缺失/疑似缺集)、版本目标在途与延后", + summary: "订阅进度:季总集数账本、每集状态(已覆盖/在途/缺失/疑似缺集)、收录版本在途与延后", params: IdParam, response: policy.SubscriptionProgress, }), @@ -848,7 +848,7 @@ export const Api = { }, acquisitionSlots: crud( "/acquisition-slots", - "版本目标", + "收录版本", { row: policy.AcquisitionSlot, create: policy.AcquisitionSlotCreate, @@ -1226,7 +1226,7 @@ export const Api = { method: "get", path: "/agent/runs", scope: "read", - summary: "Agent 运行列表(新→旧)", + summary: "智能体运行列表(新→旧)", query: agent.AgentRunListQuery, response: cursorPage(agent.AgentRun), }), @@ -1234,7 +1234,7 @@ export const Api = { method: "get", path: "/agent/runs/{id}", scope: "read", - summary: "Agent 运行详情:事件按 seq 分页、追加消息、服务端核查的目标汇报", + summary: "智能体运行详情:事件按 seq 分页、追加消息、服务端核查的目标汇报", params: IdParam, query: agent.AgentRunDetailQuery, response: agent.AgentRunDetail, @@ -1616,7 +1616,7 @@ export const Api = { method: "get", path: "/capabilities/stats", scope: "read", - summary: "能力调用统计:每能力调用数、失败数、平均耗时、最近调用、调用来源(内置 Agent / MCP / REST)", + summary: "能力调用统计:每能力调用数、失败数、平均耗时、最近调用、调用来源(内置智能体 / MCP / REST)", query: mcp.CapabilityStatsQuery, response: mcp.CapabilityStatsResponse, }), @@ -1774,14 +1774,14 @@ export const Api = { method: "get", path: "/pi/upstreams", scope: "admin", - summary: "Agent 上游列表(不含密钥)", + summary: "模型服务列表(不含密钥)", response: pi.PiUpstreamList, }), get: ep({ method: "get", path: "/pi/upstreams/{id}", scope: "admin", - summary: "Agent 上游详情", + summary: "模型服务详情", params: IdParam, response: pi.PiUpstream, }), @@ -1790,7 +1790,7 @@ export const Api = { path: "/pi/upstreams", scope: "admin", auth: "session", - summary: "新建 Agent 上游(apiKey 只写,存 secret 表)", + summary: "新建模型服务(apiKey 只写,存 secret 表)", body: pi.PiUpstreamCreate, response: pi.PiUpstream, }), @@ -1799,7 +1799,7 @@ export const Api = { path: "/pi/upstreams/{id}", scope: "admin", auth: "session", - summary: "修改 Agent 上游(provider 不可改;makeDefault 设默认上游)", + summary: "修改模型服务(provider 不可改;makeDefault 设默认模型服务)", params: IdParam, body: pi.PiUpstreamUpdate, response: pi.PiUpstream, @@ -1809,7 +1809,7 @@ export const Api = { path: "/pi/upstreams/{id}", scope: "admin", auth: "session", - summary: "删除 Agent 上游(连同凭据;是默认上游时清空默认)", + summary: "删除模型服务(连同凭据;是默认模型服务时清空默认)", params: IdParam, response: OkResult, }), @@ -1818,7 +1818,7 @@ export const Api = { path: "/pi/upstreams/{id}/test", scope: "admin", auth: "session", - summary: "测试上游:默认只解析凭据与校验模型(不产生费用);mode=completion 发一次最小补全", + summary: "测试模型服务:默认只解析凭据与校验模型(不产生费用);mode=completion 发一次最小补全", params: IdParam, body: pi.PiUpstreamTestRequest, response: pi.PiUpstreamTestResult, @@ -1827,7 +1827,7 @@ export const Api = { method: "get", path: "/pi/upstreams/{id}/models", scope: "admin", - summary: "上游模型列表:已获取过远程列表时返回远程与静态目录的合并(远程优先),否则静态目录", + summary: "模型列表:已获取过远程列表时返回远程与静态目录的合并(远程优先),否则静态目录", params: IdParam, response: pi.PiUpstreamModels, }), @@ -1836,7 +1836,7 @@ export const Api = { path: "/pi/upstreams/{id}/models/refresh", scope: "admin", auth: "session", - summary: "获取模型列表:用该上游的凭证调 provider 的模型列表接口并存库;失败或不支持时回静态目录并给出原因", + summary: "获取模型列表:用该模型服务的凭证调 provider 的模型列表接口并存库;失败或不支持时回静态目录并给出原因", params: IdParam, response: pi.PiUpstreamModels, }), @@ -1845,7 +1845,7 @@ export const Api = { path: "/pi/models/probe", scope: "admin", auth: "session", - summary: "新建上游前试拉模型列表(凭证只用于这一次请求,不落库)", + summary: "新建模型服务前试拉模型列表(凭证只用于这一次请求,不落库)", body: pi.PiModelsProbeRequest, response: pi.PiUpstreamModels, }), @@ -2111,7 +2111,7 @@ export const Api = { method: "get", path: "/identity-pool", scope: "read", - summary: "待识别池(可按原始标题检索;候选不建正式作品)", + summary: "未识别资源(可按原始标题检索;候选不建正式作品)", query: identity.IdentityPoolQuery, response: cursorPage(identity.IdentityPoolItem), }), @@ -2119,7 +2119,7 @@ export const Api = { method: "post", path: "/identity-pool/{id}/assign", scope: "organize.execute", - summary: "为待识别资源手动指定候选 / 本地作品 / 外部身份", + summary: "为未识别资源手动指定候选 / 本地作品 / 外部身份", params: IdParam, body: identity.ReleaseIdentityAssign, response: ReleaseIdentityResult, @@ -2128,7 +2128,7 @@ export const Api = { method: "post", path: "/identity-pool/{id}/dismiss", scope: "organize.execute", - summary: "忽略待识别资源(不再自动复查)", + summary: "忽略未识别资源(不再自动复查)", params: IdParam, body: z.object({ note: z.string().max(2000).optional() }), response: identity.IdentityCandidate, @@ -2156,7 +2156,7 @@ export const Api = { path: "/identity-pool/{id}/enrich/apply", scope: "admin", auth: "session", - summary: "应用元数据补全建议:建本地临时作品(或并入同名作品)并关联待识别资源(只接受控制台管理员会话)", + summary: "应用元数据补全建议:建本地临时作品(或并入同名作品)并关联未识别资源(只接受控制台管理员会话)", params: IdParam, body: identity.EnrichApplyInput, response: identity.EnrichApplyResult, @@ -2177,7 +2177,7 @@ export const Api = { method: "get", path: "/library/{id}", scope: "read", - summary: "作品详情:实际路径、身份映射、元数据来源、各版本目标、候选拒绝理由、下载历史与校验结果", + summary: "作品详情:实际路径、身份映射、元数据来源、各收录版本、候选拒绝理由、下载历史与校验结果", params: IdParam, response: organization.LibraryDetail, }), @@ -2195,7 +2195,7 @@ export const Api = { method: "get", path: "/inventory", scope: "read", - summary: "库存叠加(站点工作台):按作品返回已有版本 / 版本目标缺口 / 下载中", + summary: "库存叠加(站点工作台):按作品返回已有版本 / 收录版本缺口 / 下载中", query: organization.InventoryQuery, response: organization.InventoryResponse, }), @@ -2472,7 +2472,7 @@ export const Api = { method: "post", path: "/browser/sessions/{id}/control/release", scope: "admin", - summary: "交还控制权:Agent 恢复前必须重新读取页面,旧元素引用失效", + summary: "交还控制权:智能体恢复前必须重新读取页面,旧元素引用失效", params: browser.BrowserSessionParams, body: browser.BrowserControlRelease, response: browser.BrowserControlResult, @@ -2486,7 +2486,7 @@ export const Api = { path: "/schedules", scope: "admin", auth: "session", - summary: "定时任务看板:全部登记的周期任务与 Agent 巡检、统计、调度器是否在线", + summary: "定时任务看板:全部登记的周期任务与智能体巡检、统计、调度器是否在线", response: schedules.ScheduleBoard, }), create: ep({ @@ -2494,7 +2494,7 @@ export const Api = { path: "/schedules", scope: "admin", auth: "session", - summary: "新建自定义 Agent 巡检(目标模板 + 能力白名单 + 预算 + 间隔)", + summary: "新建自定义智能体巡检(目标模板 + 能力白名单 + 预算 + 间隔)", body: schedules.AgentCheckCreate, response: schedules.ScheduledJob, }), @@ -2513,7 +2513,7 @@ export const Api = { path: "/schedules/{id}", scope: "admin", auth: "session", - summary: "删除自定义 Agent 巡检(内置任务不可删)", + summary: "删除自定义智能体巡检(内置任务不可删)", params: schedules.ScheduleIdParam, response: OkResult, }), diff --git a/packages/contracts/src/capabilities-io.ts b/packages/contracts/src/capabilities-io.ts index 263a3c03..28aad0e8 100644 --- a/packages/contracts/src/capabilities-io.ts +++ b/packages/contracts/src/capabilities-io.ts @@ -256,7 +256,7 @@ export const SubscriptionBrief = z.object({ status: SubscriptionStatus, priority: AcquisitionPriority, statusReason: z.string().nullable(), - /** 版本目标:总数 / 已满足数 */ + /** 收录版本:总数 / 已满足数 */ slots: z.object({ total: NonNegInt, satisfied: NonNegInt }), lastSearchedAt: z.string().nullable(), nextSearchAt: z.string().nullable(), diff --git a/packages/contracts/src/capability.ts b/packages/contracts/src/capability.ts index 304123be..744e46b7 100644 --- a/packages/contracts/src/capability.ts +++ b/packages/contracts/src/capability.ts @@ -133,7 +133,7 @@ export const CAPABILITY_CATALOG: Record "catalog.lookup": d( "catalog.lookup", "查作品", - "按标题/别名/外部 ID 查本地作品与元数据候选。确认作品身份、拿作品 ID 时用;查库里已有文件与版本目标用 inventory.query", + "按标题/别名/外部 ID 查本地作品与元数据候选。确认作品身份、拿作品 ID 时用;查库里已有文件与收录版本用 inventory.query", "read", "CatalogLookup", "read", @@ -177,7 +177,7 @@ export const CAPABILITY_CATALOG: Record "download.plan": d( "download.plan", "生成下载计划", - "评估候选,生成占用版本目标(slot)与预算预留的下载计划;不提交。选定资源后、提交前用;不会提交下载,也不预留版本目标", + "评估候选,生成占用收录版本(slot)与预算预留的下载计划;不提交。选定资源后、提交前用;不会提交下载,也不预留收录版本", "preview", "DownloadPlan", "organize.execute", @@ -199,7 +199,7 @@ export const CAPABILITY_CATALOG: Record "download.recheck": d( "download.recheck", "重新校验/恢复下载", - "对已纳管的下载任务强制重新校验(action=recheck),或恢复非阈值、非人工的暂停(action=resume);不换源、不删除、不改保存路径。下载失败或文件缺失时用;换源、删除不在此能力内", + "对本工具管理的下载任务强制重新校验(action=recheck),或恢复非阈值、非人工的暂停(action=resume);不换源、不删除、不改保存路径。下载失败或文件缺失时用;换源、删除不在此能力内", "execute", "DownloadRecheck", "organize.execute", @@ -287,7 +287,7 @@ export const CAPABILITY_CATALOG: Record "inventory.query": d( "inventory.query", "查库存", - "查询本地资产、版本目标满足情况与外部库存证据。查某个作品有哪些文件与版本目标时用;想盘点全部就不给 query,不要用单字母探查", + "查询本地资产、收录版本满足情况与外部库存证据。查某个作品有哪些文件与收录版本时用;想盘点全部就不给 query,不要用单字母探查", "read", "InventoryQuery", "read", @@ -299,7 +299,7 @@ export const CAPABILITY_CATALOG: Record "subscription.list": d( "subscription.list", "列出订阅", - "按状态或媒体类型分组列出订阅摘要(作品、模式、状态、版本目标满足数、上次/下次搜索)。盘点订阅、找缺版本时用;查单个作品的文件与版本目标用 inventory.query", + "按状态或媒体类型分组列出订阅摘要(作品、模式、状态、收录版本满足数、上次/下次搜索)。盘点订阅、找缺版本时用;查单个作品的文件与收录版本用 inventory.query", "read", "SubscriptionList", "read", diff --git a/packages/contracts/src/organization.ts b/packages/contracts/src/organization.ts index 4b1e043b..7731806f 100644 --- a/packages/contracts/src/organization.ts +++ b/packages/contracts/src/organization.ts @@ -489,7 +489,7 @@ export const Dashboard = z.object({ }), activeSubscriptions: NonNegInt, slotCounts: SlotStatusCounts, - /** 版本目标满足率 = satisfied / 全部版本目标(0–1);没有版本目标时为 null */ + /** 收录版本满足率 = satisfied / 全部收录版本(0–1);没有收录版本时为 null */ slotSatisfiedRate: z.number().min(0).max(1).nullable(), /** 本地账本计数:作品不含已合并;季 = content_unit.kind=season(不含 retired);资产 = active 的视频 / 原盘目录 */ library: z.object({ diff --git a/packages/contracts/src/pi.ts b/packages/contracts/src/pi.ts index 67d417f1..90af2088 100644 --- a/packages/contracts/src/pi.ts +++ b/packages/contracts/src/pi.ts @@ -245,9 +245,9 @@ export const PiUpstreamCreate = z }) .superRefine((v, ctx) => { if (v.authKind !== "api_key" && v.apiKey !== undefined) - ctx.addIssue({ code: "custom", path: ["apiKey"], message: "只有 api_key 型上游可以填 apiKey" }); + ctx.addIssue({ code: "custom", path: ["apiKey"], message: "只有 api_key 型模型服务可以填 apiKey" }); if (v.authKind === "oauth" && v.baseUrl) - ctx.addIssue({ code: "custom", path: ["baseUrl"], message: "订阅登录型上游不能覆盖 baseUrl" }); + ctx.addIssue({ code: "custom", path: ["baseUrl"], message: "订阅登录型模型服务不能覆盖 baseUrl" }); if (v.provider === "openai-compatible") { if (!v.baseUrl) ctx.addIssue({ code: "custom", path: ["baseUrl"], message: "自定义兼容端点必须填 baseUrl" }); if (!v.defaultModel) diff --git a/packages/contracts/src/policy.ts b/packages/contracts/src/policy.ts index 4738cdd0..2d213a61 100644 --- a/packages/contracts/src/policy.ts +++ b/packages/contracts/src/policy.ts @@ -79,7 +79,7 @@ export const Subscription = EntityBase.extend({ /** 上次主动搜索时间 / 下次到期(按 params.searchIntervalHours,缺省 24h) */ lastSearchedAt: Timestamp.nullable(), nextSearchAt: Timestamp.nullable(), - /** 上次进展:任一版本目标的单元转为已入库、或总集数变化时写入;总览"上次更新"另合并计划预留时间 */ + /** 上次进展:任一收录版本的单元转为已入库、或总集数变化时写入;总览"上次更新"另合并计划预留时间 */ lastProgressAt: Timestamp.nullable(), /** 派生字段(列表返回):作品标题,未建档时为 null */ mediaEntityTitle: z.string().nullable().optional(), @@ -97,10 +97,10 @@ export const FillBudget = z .strict(); export type FillBudget = z.infer; -/** MP 分辨率(resolution)→ 版本目标硬条件 resolutions */ +/** MP 分辨率(resolution)→ 收录版本硬条件 resolutions */ export const SubscriptionResolution = z.enum(["4k", "1080p", "720p"]); export type SubscriptionResolution = z.infer; -/** MP 资源质量(quality / resource_type)→ 版本目标硬条件 sources / codecs;uhd 写进订阅私有规则组的包含 */ +/** MP 资源质量(quality / resource_type)→ 收录版本硬条件 sources / codecs;uhd 写进订阅私有规则组的包含 */ export const SubscriptionSourcePreset = z.enum([ "bluray_disc", "remux", @@ -112,7 +112,7 @@ export const SubscriptionSourcePreset = z.enum([ "h264", ]); export type SubscriptionSourcePreset = z.infer; -/** MP 特效(effect)→ 版本目标硬条件 hdr + 软偏好 hdr 顺序;atmos 写进订阅私有规则组的包含 */ +/** MP 特效(effect)→ 收录版本硬条件 hdr + 软偏好 hdr 顺序;atmos 写进订阅私有规则组的包含 */ export const SubscriptionEffect = z.enum(["dv", "hdr", "sdr", "atmos"]); export type SubscriptionEffect = z.infer; @@ -120,7 +120,7 @@ export type SubscriptionEffect = z.infer; * subscription.params 的已知键(JSON 列,其余键原样保留): * siteIds 站点范围;episodeRange 集范围(对照 MP start/total_episode);searchIntervalHours 主动搜索周期(0 = 关); * stopAfter 截止时间;downloaderInstanceId 指定下载器;ruleGroupIds 规则组;keywords 追加检索词; - * include/exclude 订阅级关键词正则(作用于全部版本目标);libraryRootId 库根;downloadSavePath 下载保存路径; + * include/exclude 订阅级关键词正则(作用于全部收录版本);libraryRootId 库根;downloadSavePath 下载保存路径; * autoFill/fillBudget 多版本自动补全;resolution/source/effect 质量预设(MP resolution/quality/effect)。 * include/exclude 保存时同步为订阅私有规则组(scope.subscriptionId,随订阅删除),评估只读规则组。 * 写入时兼容 MP 字段与旧键(sites/downloader/save_path/start_episode/filter_groups/keyword/autoFillVersions…), @@ -150,7 +150,7 @@ export const SubscriptionParams = z downloadSavePath: z.string().min(1).max(1000).optional(), autoFill: AutoFillMode.optional(), fillBudget: FillBudget.optional(), - /** MP resolution / quality / effect:保存时写到该订阅自己的版本目标(硬条件 + 软偏好) */ + /** MP resolution / quality / effect:保存时写到该订阅自己的收录版本(硬条件 + 软偏好) */ resolution: SubscriptionResolution.optional(), source: SubscriptionSourcePreset.optional(), effect: SubscriptionEffect.optional(), @@ -296,11 +296,11 @@ export type SubscriptionGroup = z.infer; export const SubscriptionSort = z.enum(["lastUpdated", "nextSearch", "missing", "created", "name"]); export type SubscriptionSort = z.infer; -/** 版本目标在订阅范围内的计数摘要(卡片徽标 / 矩阵行尾) */ +/** 收录版本在订阅范围内的计数摘要(卡片徽标 / 矩阵行尾) */ export const SubscriptionSlotSummary = z.object({ slotId: Id, name: z.string(), - /** 同订阅内顺序最小的版本目标 */ + /** 同订阅内顺序最小的收录版本 */ primary: z.boolean(), status: z.string(), have: z.number().int(), @@ -347,7 +347,7 @@ export const SubscriptionOverviewItem = Subscription.extend({ source: z.enum(["subscription", "rule_group", "default"]), budget: FillBudget, }), - /** 任一版本目标开了升级且未达顶档 */ + /** 任一收录版本开了升级且未达顶档 */ upgrading: z.boolean(), /** 已完成分组里 continuous 订阅在等下一季 */ awaitingNewSeason: z.boolean(), @@ -418,7 +418,7 @@ export const SubscriptionCoverage = z.object({ columns: z.array(CoverageColumn), }), ), - /** 首行 slotId = null 为"库内任一版本";其后每个版本目标一行,按 position */ + /** 首行 slotId = null 为"库内任一版本";其后每个收录版本一行,按 position */ rows: z.array( z.object({ slotId: Id.nullable(), @@ -714,7 +714,7 @@ export type PreferenceSuppressionUpdate = z.infer; /** * 库存叠加状态(只在关联到作品时给出): - * downloading 有下载中任务 > gap 有未满足的版本目标 > owned 已有版本 > subscribed 只有订阅 > none 作品已登记但无库存/目标 + * downloading 有下载中任务 > gap 有未满足的收录版本 > owned 已有版本 > subscribed 只有订阅 > none 作品已登记但无库存/目标 */ export const SearchInventoryState = z.enum(["downloading", "gap", "owned", "subscribed", "none"]); export type SearchInventoryState = z.infer; @@ -188,10 +188,10 @@ export const WorkInventory = z.object({ /** 已入库版本数(主视频资产) */ versionCount: NonNegInt, downloadingCount: NonNegInt, - /** 版本目标:总数 / 已满足 */ + /** 收录版本:总数 / 已满足 */ slotTotal: NonNegInt, slotSatisfied: NonNegInt, - /** 未满足的版本目标(empty / attention / upgrade_pending) */ + /** 未满足的收录版本(empty / attention / upgrade_pending) */ missing: z.array(z.object({ slotId: Id, name: z.string() })), }); export type WorkInventory = z.infer; diff --git a/packages/contracts/src/settings.ts b/packages/contracts/src/settings.ts index 173e034f..2cea6f33 100644 --- a/packages/contracts/src/settings.ts +++ b/packages/contracts/src/settings.ts @@ -864,10 +864,10 @@ export const SETTINGS_SECTIONS: readonly SettingsSectionDef[] = [ { id: "library", label: "媒体库与目录", - description: "库根、哨兵状态、默认整理方式、命名模板", + description: "媒体库目录、默认整理方式、命名模板", legacyTabs: [{ tab: "library-roots", anchor: "roots" }], anchors: [ - { id: "roots", label: "库根" }, + { id: "roots", label: "媒体库目录" }, { id: "organize", label: "整理方式" }, { id: "naming", label: "命名模板" }, ], @@ -918,11 +918,11 @@ export const SETTINGS_SECTIONS: readonly SettingsSectionDef[] = [ }, { id: "agent", - label: "Agent", - description: "Agent 上游、运行预算、自动调查、网页搜索", + label: "智能体", + description: "模型服务、运行预算、自动调查、网页搜索", legacyTabs: [{ tab: "pi" }], anchors: [ - { id: "upstream", label: "上游" }, + { id: "upstream", label: "模型服务" }, { id: "runtime", label: "上下文与子会话" }, { id: "budget", label: "预算" }, { id: "investigate", label: "自动调查" }, diff --git a/packages/domain/src/acquisition/fill.ts b/packages/domain/src/acquisition/fill.ts index f7522185..cc2d0330 100644 --- a/packages/domain/src/acquisition/fill.ts +++ b/packages/domain/src/acquisition/fill.ts @@ -1,4 +1,4 @@ -// 多版本自动补全(docs/design/subscriptions.md §2.2):非主版本目标在主版本已满足的单元上补下其他版本。 +// 多版本自动补全(docs/design/subscriptions.md §2.2):非主收录版本在主版本已满足的单元上补下其他版本。 // 开关按 订阅 → 规则组(链上后者优先)→ 系统缺省(开)取值;补全预算按订阅、按自然日计,超出记延后。 export type AutoFillModeValue = "inherit" | "on" | "off"; diff --git a/packages/domain/src/naming/variables.ts b/packages/domain/src/naming/variables.ts index ddba12e9..4e2348c5 100644 --- a/packages/domain/src/naming/variables.ts +++ b/packages/domain/src/naming/variables.ts @@ -76,7 +76,7 @@ export const NAMING_VARIABLES: readonly NamingVariableDef[] = [ ours: true, }, { name: "issue", group: "ours", label: "综艺期号", example: "12", ours: true }, - { name: "slotName", group: "ours", label: "版本目标名", example: "4K HDR", ours: true }, + { name: "slotName", group: "ours", label: "收录版本名", example: "4K HDR", ours: true }, { name: "libraryRoot", group: "ours", label: "库根名", example: "电视剧-A", ours: true }, ]; @@ -124,7 +124,7 @@ function sourceOf(p: Partial): string | undefined { } /** - * 渲染上下文(MP 变量名):作品信息 + 发布解析 + 季集元数据 + 版本目标/库根。 + * 渲染上下文(MP 变量名):作品信息 + 发布解析 + 季集元数据 + 收录版本/库根。 * 标题用本地作品名,不用发布标题。空值不放进上下文(同 MP 剔除 None)。 */ export function buildNamingVars( diff --git a/packages/domain/src/slot/evaluate.ts b/packages/domain/src/slot/evaluate.ts index 1a7fe432..3183dfd3 100644 --- a/packages/domain/src/slot/evaluate.ts +++ b/packages/domain/src/slot/evaluate.ts @@ -376,14 +376,14 @@ export function currentPri(spec: SlotSpec, current: QualityFacts): number { return effectiveLadder(spec, pseudo, {}, ruleChainOf(spec, pseudo, {})).pri; } -/** 版本目标引用的规则组链求值;没有规则组时为 null */ +/** 收录版本引用的规则组链求值;没有规则组时为 null */ export function ruleChainOf(spec: SlotSpec, r: ParsedRelease, ctx: EvaluationContext = {}): RuleChainResult | null { if (!spec.rules?.groups.length) return null; return evaluateRuleChain(spec.rules.groups, r, ctx, catalogOf(spec.rules.atoms)); } /** - * 实际生效的阶梯:版本目标自身 soft.tiers 视为排在最前的匿名组,规则组链里有带层的组时 pri 取最后一个带层的组。 + * 实际生效的阶梯:收录版本自身 soft.tiers 视为排在最前的匿名组,规则组链里有带层的组时 pri 取最后一个带层的组。 */ export function effectiveLadder( spec: SlotSpec, diff --git a/packages/domain/src/slot/overview.ts b/packages/domain/src/slot/overview.ts index 0699acde..28936971 100644 --- a/packages/domain/src/slot/overview.ts +++ b/packages/domain/src/slot/overview.ts @@ -11,9 +11,9 @@ export interface SubscriptionGroupInput { mode: "once" | "continuous"; /** 范围内各季总集数是否都已知(电影恒为 true) */ totalsKnown: boolean; - /** 任一版本目标有已入库或在途单元 */ + /** 任一收录版本有已入库或在途单元 */ anyProgress: boolean; - /** 所有版本目标在范围内全覆盖(且总集数已知) */ + /** 所有收录版本在范围内全覆盖(且总集数已知) */ allSlotsComplete: boolean; } @@ -86,7 +86,7 @@ export interface CoverageRowResult { } /** - * 一行(一个版本目标或"库内任一版本")的格状态:已入库 > 在途(含整季通配预留)> 范围外/未播出 > 缺失。 + * 一行(一个收录版本或"库内任一版本")的格状态:已入库 > 在途(含整季通配预留)> 范围外/未播出 > 缺失。 * 在途按计划单元键与列键求交(unitsOverlap:s{季}* 通配与同季各集相交)。 */ export function coverageRow(i: CoverageRowInput): CoverageRowResult { diff --git a/packages/domain/src/slot/types.ts b/packages/domain/src/slot/types.ts index c37c2e05..515beaa4 100644 --- a/packages/domain/src/slot/types.ts +++ b/packages/domain/src/slot/types.ts @@ -116,7 +116,7 @@ export interface SlotUpgradePolicy { export type SlotCompletionSpec = { kind: "movie"; cuts?: (Cut | "default")[] } | { kind: "units" }; -/** 版本目标引用的规则组链(已按 scope 过滤、按引用顺序排列)与自定义条件 */ +/** 收录版本引用的规则组链(已按 scope 过滤、按引用顺序排列)与自定义条件 */ export interface SlotRuleChain { groups: RuleGroupSpec[]; atoms?: CustomAtom[]; diff --git a/packages/domain/test/naming.test.ts b/packages/domain/test/naming.test.ts index cf333f11..0ca4b8ef 100644 --- a/packages/domain/test/naming.test.ts +++ b/packages/domain/test/naming.test.ts @@ -125,7 +125,7 @@ describe("变量表与上下文", () => { "libraryRoot", ]); }); - it("季集元数据、版本目标、库根、外部 ID", () => { + it("季集元数据、收录版本、库根、外部 ID", () => { const v = buildNamingVars( { title: "绝命毒师", diff --git a/packages/domain/test/rules-slot.test.ts b/packages/domain/test/rules-slot.test.ts index 6af23bf0..3e9d1ff3 100644 --- a/packages/domain/test/rules-slot.test.ts +++ b/packages/domain/test/rules-slot.test.ts @@ -22,7 +22,7 @@ const base = (groups: RuleGroupSpec[], extra: Partial = {}): SlotSpec }); const rel = (t: string, subtitle?: string) => parseRelease(t, subtitle ? { subtitle } : {}); -describe("版本目标接入规则组", () => { +describe("收录版本接入规则组", () => { it("硬排除命中 → reject(rule.hard_exclude),trace 带组名与修订号", () => { const g = group("不要原盘", "", { id: "g1",