feat(server): W4-A integration endpoints and contract gaps
- POST /acquisition-plans: manual submit via download.plan -> download.submit
(same authorization/audit/idempotency path as the agent), slotId or entityId
- read-only /media-assets, /asset-locations, /asset-unit-coverage, /path-bindings,
/reconcile-runs, /server-mappings, /artifacts, GET /title-aliases/{id};
unimplemented write endpoints removed from api.ts; registry test now checks both directions
- candidate evaluation upsert per (slot, source_release) + migration 0022 dedupe/unique index
- organization records expose downloadTaskId (+ list filter); inventory adds
subscribed/candidateReasons; feed sources expose derived possibleGap
- GET /events?after=&limit= JSON replay page for API key callers (same sse_event table)
- preference tag revision history from audit_event; subscriptions/identities/aliases
lists carry mediaEntityTitle; subscription q/mediaKind search; slot subscriptionId
filter + currentAsset summary; policy preview samples carry title/reasons
- DecisionSummary, IdentityRevisionList, ReleaseIdentityResult, PolicyPreviewSummary exported from contracts
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
ea4c384664
commit
d3ab54f2bd
34 files changed
+1083
-123
No files matched your search
@@ -72,6 +72,8 @@ export const Subscription = EntityBase.extend({
|
||||
statusReason: z.string().nullable(),
|
||||
lastCheckedAt: Timestamp.nullable(),
|
||||
createdBy: z.string().nullable(),
|
||||
/** 派生字段(列表返回):作品标题,未建档时为 null */
|
||||
mediaEntityTitle: z.string().nullable().optional(),
|
||||
});
|
||||
export type Subscription = z.infer<typeof Subscription>;
|
||||
export const SubscriptionCreate = Subscription.pick({
|
||||
@@ -145,6 +147,18 @@ export const AcquisitionSlot = EntityBase.extend({
|
||||
lastEvaluatedAt: Timestamp.nullable(),
|
||||
nextEvaluateAt: Timestamp.nullable(),
|
||||
revision: z.number().int().min(1),
|
||||
/** 派生字段(列表返回):当前资产的交付特征摘要,无资产为 null */
|
||||
currentAsset: z
|
||||
.object({
|
||||
id: Id,
|
||||
resolution: z.string().nullable(),
|
||||
sourceType: z.string().nullable(),
|
||||
releaseGroup: z.string().nullable(),
|
||||
sizeBytes: z.number().int().nullable(),
|
||||
status: z.string(),
|
||||
})
|
||||
.nullable()
|
||||
.optional(),
|
||||
});
|
||||
export type AcquisitionSlot = z.infer<typeof AcquisitionSlot>;
|
||||
export const AcquisitionSlotCreate = AcquisitionSlot.pick({
|
||||
@@ -206,6 +220,55 @@ export const PreferenceTag = EntityBase.extend({
|
||||
policyRevisionId: Id.nullable(),
|
||||
});
|
||||
export type PreferenceTag = z.infer<typeof PreferenceTag>;
|
||||
|
||||
/** 偏好标签修订历史:取自 audit_event(target_type=preference_tag),新→旧 */
|
||||
export const PreferenceTagRevision = z.object({
|
||||
/** audit_event id */
|
||||
id: Id,
|
||||
at: Timestamp,
|
||||
action: z.enum(["created", "updated", "deleted"]),
|
||||
actorType: z.string(),
|
||||
actorId: z.string().nullable(),
|
||||
/** 该次写入后的 revision */
|
||||
revision: z.number().int().min(1).nullable(),
|
||||
/** 本次改动的字段(created/deleted 为空) */
|
||||
changedFields: z.array(z.string()),
|
||||
/** 写入后的快照 */
|
||||
snapshot: z.object({
|
||||
label: z.string(),
|
||||
kind: z.string(),
|
||||
status: z.string(),
|
||||
scope: JsonObject,
|
||||
rule: JsonObject,
|
||||
description: z.string().nullable(),
|
||||
}),
|
||||
});
|
||||
export type PreferenceTagRevision = z.infer<typeof PreferenceTagRevision>;
|
||||
export const PreferenceTagRevisionList = z.object({ items: z.array(PreferenceTagRevision) });
|
||||
export type PreferenceTagRevisionList = z.infer<typeof PreferenceTagRevisionList>;
|
||||
|
||||
/** 策略预览结果(policy_revision.preview):最近 200 条来源资源按模板回放 */
|
||||
export const PolicyPreviewSummary = z.object({
|
||||
evaluatedAt: z.string(),
|
||||
sampleSize: z.number(),
|
||||
templates: z.number(),
|
||||
matched: z.number(),
|
||||
excluded: z.number(),
|
||||
samples: z.array(
|
||||
z.object({
|
||||
sourceReleaseId: z.string(),
|
||||
/** 资源标题(W4 起提供) */
|
||||
title: z.string().optional(),
|
||||
sizeBytes: z.number().nullable().optional(),
|
||||
template: z.string(),
|
||||
decision: z.string(),
|
||||
/** 拒绝/接受理由摘要 */
|
||||
reasons: z.array(z.string()).optional(),
|
||||
}),
|
||||
),
|
||||
valid: z.boolean(),
|
||||
});
|
||||
export type PolicyPreviewSummary = z.infer<typeof PolicyPreviewSummary>;
|
||||
/** fingerprint 由服务端计算 */
|
||||
export const PreferenceTagCreate = PreferenceTag.pick({
|
||||
label: true,
|
||||
|
||||
Reference in new issue
Block a user