feat(web): W4-A wire console to real endpoints

- Agent page uses contracts (AgentRun/AgentRunDetail/AgentGoalReport) instead of a local
  mirror; ?mock=1 stays as an explicit demo switch; run snapshot refreshes on agent.run_status;
  stream auto-follow only while the user stays near the bottom (abort stays reachable)
- settings: Pi provider select incl. fake, new Agent runtime card (budgets, fakeScript)
- download trace uses /tasks?subjectId= and /organization-records?downloadTaskId= instead of
  time-window engine run matching and path matching; new business-task tab with cancel/retry
- workbench download dialog posts /acquisition-plans and shows per-slot rejection reasons
- preference tag revision history; subscription search by title/kind; identity/alias/subscription
  lists show entity titles; slot cards show current asset summary; policy preview sample titles
- DecisionSummary / IdentityRevisionList / ReleaseIdentityResult / TaskAccepted / PolicyPreviewSummary /
  OrganizePlanArtifactContent taken from contracts
- docs: SSE for console (cookie) vs GET /events?after=&limit= JSON replay for API keys

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-27 21:32:11 +08:00
1 parent 34e8751b45
commit 1023642ef9
30 files changed
+703 -271

No files matched your search

+37 -5
View File
@@ -9,7 +9,7 @@ import {
type SubscriptionStatus,
} from "@umt/contracts";
import { CalendarClock, Check, FilePlus2, Pause, Play, Plus, Search, XCircle } from "lucide-react";
import { useId, useMemo, useState } from "react";
import { useEffect, useId, useMemo, useState } from "react";
import { Link, useSearchParams } from "react-router";
import { toast } from "sonner";
import { useEntitySearch } from "~/api/library.ts";
@@ -115,8 +115,20 @@ export default function SubscriptionsPage() {
// ── 订阅列表 ──
function useDebounced(v: string, ms = 300) {
const [d, setD] = useState(v);
useEffect(() => {
const t = setTimeout(() => setD(v), ms);
return () => clearTimeout(t);
}, [v, ms]);
return d;
}
function EntityTitle({ sub }: { sub: Subscription }) {
const q = useEntityTitle(sub.mediaEntityId);
// 列表响应已带 mediaEntityTitle;没有时(旧数据/详情)再按 ID 取
const q = useEntityTitle(sub.mediaEntityTitle ? null : sub.mediaEntityId);
if (sub.mediaEntityId && sub.mediaEntityTitle)
return <span className="min-w-0 break-all text-foreground">{sub.mediaEntityTitle}</span>;
if (!sub.mediaEntityId)
return (
<span className="flex min-w-0 flex-wrap items-center gap-1.5">
@@ -137,7 +149,15 @@ function EntityTitle({ sub }: { sub: Subscription }) {
function SubscriptionsTab() {
const [status, setStatus] = useState("");
const filters: SubscriptionFilters = { status: (status || undefined) as SubscriptionFilters["status"] };
const [kind, setKind] = useState("");
const [search, setSearch] = useState("");
const dq = useDebounced(search.trim());
const filters: SubscriptionFilters = {
status: (status || undefined) as SubscriptionFilters["status"],
mediaKind: (kind || undefined) as SubscriptionFilters["mediaKind"],
q: dq || undefined,
};
const filtered = !!status || !!kind || !!search;
const q = useSubscriptions(filters);
const rows = useMemo(() => q.data?.pages.flatMap((p) => p.items), [q.data]);
const [openId, setOpenId] = useState<string | null>(null);
@@ -186,8 +206,20 @@ function SubscriptionsTab() {
return (
<>
<FilterBar onReset={status ? () => setStatus("") : undefined}>
<FilterBar
search={{ value: search, onChange: setSearch, placeholder: "按作品标题 / 别名 / 检索词搜索" }}
onReset={
filtered
? () => {
setStatus("");
setKind("");
setSearch("");
}
: undefined
}
>
<FilterSelect label="状态" value={status} onChange={setStatus} options={opts(SUB_STATUS)} />
<FilterSelect label="类型" value={kind} onChange={setKind} options={opts(SUB_KIND_LABEL)} />
</FilterBar>
<DataTable
columns={columns}
@@ -203,7 +235,7 @@ function SubscriptionsTab() {
loadingMore={q.isFetchingNextPage}
empty={{
icon: CalendarClock,
title: status ? "没有符合条件的订阅" : "还没有订阅",
title: filtered ? "没有符合条件的订阅" : "还没有订阅",
description: "订阅一部作品后,系统按收录目标持续补齐与升级;也可以只填标题,等作品建档后自动关联。",
}}
/>