console: 项目导航移到左侧栏,工作台不再承担项目模块

左侧栏 ProjectNav:项目完整列表(名 + 短 id + 我的角色,
选中高亮,点击切换)+ 创建项目(名/GitHub 仓库/repo_url)
+ 选中项目成员(邀请/改角色/新增移除);工作台删除
sec-projects 一节与任务列表里的项目下拉框,侧栏为唯一
项目选择器,自动选第一个项目逻辑不变。
薄模块补齐(均为已核实后端路由):成员移除 DELETE、
自动化详情(GET 单条 + 产出)与编辑 PATCH、分诊条目点进
任务/自动化详情、Workers 搜索与在线筛选、管理区设置配额
POST。主线新功能(Todos、Browser、taskMeta 等)原样保留,
未做任何回退。console 标记单测本地 53 项通过。
This commit is contained in:
host committed 2026-10-30 07:43:54 +00:00
1 parent d6369154c2
commit 08484302ac
16 files changed
+293 -100

No files matched your search

+9 -6
View File
@@ -9,8 +9,7 @@ import ConversationSection from "./sections/ConversationSection";
import TaskDetailPanel from "./sections/TaskDetailPanel"; import TaskDetailPanel from "./sections/TaskDetailPanel";
import ApiKeySection from "./sections/ApiKeySection"; import ApiKeySection from "./sections/ApiKeySection";
import AdminSection from "./sections/AdminSection"; import AdminSection from "./sections/AdminSection";
import ProjectsSection from "./sections/ProjectsSection"; import ProjectNav from "./sections/ProjectNav";
import MembersPanel from "./sections/MembersPanel";
import EnvSection from "./sections/EnvSection"; import EnvSection from "./sections/EnvSection";
import EnvContextPanel from "./sections/EnvContextPanel"; import EnvContextPanel from "./sections/EnvContextPanel";
import WorkersSection from "./sections/WorkersSection"; import WorkersSection from "./sections/WorkersSection";
@@ -25,7 +24,10 @@ import TodosSection from "./sections/TodosSection";
// data-cards row on top of a long stacked page, no detail stack // data-cards row on top of a long stacked page, no detail stack
// hanging under the conversation. The new shell is an IDE-style // hanging under the conversation. The new shell is an IDE-style
// workspace that never scrolls as one page: // workspace that never scrolls as one page:
// 左侧栏:导航(按工作流分组)+ 数据卡片(紧凑版,始终可见) // 左侧栏:导航(按工作流分组)+ 项目模块(ProjectNav:项目列表 +
// 创建项目 + 成员,id nav-projects,是全站唯一的项目选择器;
// 因此不再有独立的“项目与成员”视图,旧 ProjectsSection 已删,
// 工作台也不再带项目选择器)+ 数据卡片(紧凑版,始终可见)
// 任务工作台:左 = 任务列表轨(搜索/筛选/分页),中 = 派发 + 对话, // 任务工作台:左 = 任务列表轨(搜索/筛选/分页),中 = 派发 + 对话,
// 右 = 任务详情检查器(概览/协作/评审与审批/线程四个内页签, // 右 = 任务详情检查器(概览/协作/评审与审批/线程四个内页签,
// 见 TaskDetailPanel) // 见 TaskDetailPanel)
@@ -36,11 +38,10 @@ import TodosSection from "./sections/TodosSection";
// unmounted with a view. The view is synced to location.hash // unmounted with a view. The view is synced to location.hash
// (#view-tasks etc.) so a reload or a shared link lands on the // (#view-tasks etc.) so a reload or a shared link lands on the
// same view; unknown hashes fall back to the task workspace. // same view; unknown hashes fall back to the task workspace.
type ViewId = "tasks" | "todos" | "projects" | "resources" | "automation" | "admin"; type ViewId = "tasks" | "todos" | "resources" | "automation" | "admin";
const VIEWS: Array<[ViewId, string, string]> = [ const VIEWS: Array<[ViewId, string, string]> = [
["tasks", "任务工作台", "任务"], ["tasks", "任务工作台", "任务"],
["todos", "项目待办", "任务"], ["todos", "项目待办", "任务"],
["projects", "项目与成员", "管理"],
["resources", "环境与 Workers", "管理"], ["resources", "环境与 Workers", "管理"],
["automation", "技能与自动化", "管理"], ["automation", "技能与自动化", "管理"],
["admin", "系统与 Key", "管理"], ["admin", "系统与 Key", "管理"],
@@ -67,6 +68,9 @@ function SideNav({ view, setView }: { view: ViewId; setView: (v: ViewId) => void
</div> </div>
))} ))}
</nav> </nav>
<div id="nav-projects" className="shell-project-nav">
<ProjectNav />
</div>
<div className="shell-stats"> <div className="shell-stats">
<div className="shell-stats-label">概览</div> <div className="shell-stats-label">概览</div>
<DataCards /> <DataCards />
@@ -138,7 +142,6 @@ export default function App() {
) : ( ) : (
<main className="page"> <main className="page">
{view === "todos" ? (<><TodosSection /></>) {view === "todos" ? (<><TodosSection /></>)
: view === "projects" ? (<><ProjectsSection /><MembersPanel /></>)
: view === "resources" ? (<><EnvSection /><EnvContextPanel /><WorkersSection /><HealthPanel /></>) : view === "resources" ? (<><EnvSection /><EnvContextPanel /><WorkersSection /><HealthPanel /></>)
: view === "automation" ? (<><SkillsSection /><SkillsToolsPanel /><AutomationsSection /><TriageSection /></>) : view === "automation" ? (<><SkillsSection /><SkillsToolsPanel /><AutomationsSection /><TriageSection /></>)
: (<><AdminSection /><ApiKeySection /></>)} : (<><AdminSection /><ApiKeySection /></>)}
+1
View File
@@ -93,6 +93,7 @@ export function memberErr(e: Error): string {
if (m.includes("forbidden")) return "没有权限:只有项目 owner 或全局管理员可以管理成员"; if (m.includes("forbidden")) return "没有权限:只有项目 owner 或全局管理员可以管理成员";
if (m.includes("already_member")) return "该用户已是成员,改角色请用成员行的下拉框"; if (m.includes("already_member")) return "该用户已是成员,改角色请用成员行的下拉框";
if (m.includes("cannot demote the only owner")) return "不能把唯一 owner 降为非 owner:请先把另一位成员升为 owner"; if (m.includes("cannot demote the only owner")) return "不能把唯一 owner 降为非 owner:请先把另一位成员升为 owner";
if (m.includes("cannot remove the only owner")) return "不能移除唯一 owner:请先把另一位成员升为 owner";
if (m.includes("user not found")) return "找不到该用户:请核对 user_id 或用户名是否正确"; if (m.includes("user not found")) return "找不到该用户:请核对 user_id 或用户名是否正确";
if (m.includes("member not found")) return "该用户不是本项目成员"; if (m.includes("member not found")) return "该用户不是本项目成员";
if (m.includes("unknown role")) return "角色无效:只能是 viewer / contributor / maintainer / owner"; if (m.includes("unknown role")) return "角色无效:只能是 viewer / contributor / maintainer / owner";
+4 -2
View File
@@ -36,7 +36,9 @@ export interface Followup { mode?: string; status?: string; sender_task_id?: str
export interface ChatHistoryMessage { id: string; role: "user" | "assistant" | "tool" | "status"; source?: string; kind?: string; label?: string; text: string; ts_ms?: number | null } export interface ChatHistoryMessage { id: string; role: "user" | "assistant" | "tool" | "status"; source?: string; kind?: string; label?: string; text: string; ts_ms?: number | null }
export interface ChatHistory { task_id?: string; status?: string; messages: ChatHistoryMessage[]; continue_messages_stored?: number; legacy_continue_count?: number | null; trace_note?: string } export interface ChatHistory { task_id?: string; status?: string; messages: ChatHistoryMessage[]; continue_messages_stored?: number; legacy_continue_count?: number | null; trace_note?: string }
export interface PeerMessage { task_id?: string; mode?: string; status?: string; body?: string } export interface PeerMessage { task_id?: string; mode?: string; status?: string; body?: string }
export interface Automation { id: string; name?: string; prompt?: string; repo_url?: string; status?: string; due?: boolean; interval_seconds?: number; next_run_at?: number; last_run_at?: number; created_at?: number; cancelled_at?: number; run_once?: boolean } export interface Automation { id: string; name?: string; prompt?: string; repo_url?: string; status?: string; due?: boolean; interval_seconds?: number; next_run_at?: number; last_run_at?: number; last_claimed_at?: number; created_by?: string; created_at?: number; cancelled_at?: number; first_run_at?: number; run_once?: boolean; project_id?: string }
export interface AutomationRun { id?: string; automation_id?: string; project_id?: string; task_id?: string; status?: string; detail?: string; created_at?: number }
export interface AutomationDetail extends Automation { runs?: AutomationRun[]; runs_note?: string; heartbeat_binding?: unknown }
export interface Member { user_id: string; username?: string; role?: string } export interface Member { user_id: string; username?: string; role?: string }
export interface SkillItem { name?: string; id?: string; description?: string; detail?: string } export interface SkillItem { name?: string; id?: string; description?: string; detail?: string }
export interface McpServer { name?: string; id?: string; status?: string; detail?: string; details?: string; state?: string } export interface McpServer { name?: string; id?: string; status?: string; detail?: string; details?: string; state?: string }
@@ -45,7 +47,7 @@ export interface Agent { kind?: string; name?: string; status?: string; prompt?:
export interface ThreadInfo { id?: string; name?: string; thread_name?: string; archived?: boolean | null; cwd?: string; turns?: number; turn_count?: number; turns_count?: number } export interface ThreadInfo { id?: string; name?: string; thread_name?: string; archived?: boolean | null; cwd?: string; turns?: number; turn_count?: number; turns_count?: number }
export interface Triage { export interface Triage {
counts?: { automations_due?: number; failed_tasks?: number; pending_followups?: number; recent_runs?: number }; counts?: { automations_due?: number; failed_tasks?: number; pending_followups?: number; recent_runs?: number };
automations_due?: Array<{ name?: string; interval_seconds?: number }>; automations_due?: Array<{ id?: string; name?: string; status?: string; due?: boolean; interval_seconds?: number; next_run_at?: number }>;
failed_tasks?: Array<{ id?: string; status?: string; prompt?: string }>; failed_tasks?: Array<{ id?: string; status?: string; prompt?: string }>;
pending_followups?: Array<{ task_id?: string; body?: string }>; pending_followups?: Array<{ task_id?: string; body?: string }>;
recent_runs?: Array<{ automation_id?: string; status?: string; task_id?: string; task_status?: string }>; recent_runs?: Array<{ automation_id?: string; status?: string; task_id?: string; task_status?: string }>;
+4 -4
View File
@@ -1,5 +1,5 @@
import { createContext, useContext } from "react"; import { createContext, useContext } from "react";
import type { Automation, Env, Member, Project, Review, Approval, Followup, PeerMessage, Task, TaskCost, ThreadInfo, Triage, Worker, SkillItem, McpServer, Agent, DispatchBody, ReviewBody, TraceEntry, ChatHistory } from "./api/types"; import type { Automation, AutomationDetail, Env, Member, Project, Review, Approval, Followup, PeerMessage, Task, TaskCost, ThreadInfo, Triage, Worker, SkillItem, McpServer, Agent, DispatchBody, ReviewBody, TraceEntry, ChatHistory } from "./api/types";
export interface ConsoleCtx { export interface ConsoleCtx {
keySaved: boolean; keyState: string; keySaved: boolean; keyState: string;
@@ -7,13 +7,13 @@ export interface ConsoleCtx {
quotaText: string; quotaText: string;
healthText: string; refreshHealth: () => void; healthText: string; refreshHealth: () => void;
adminGate: string; adminBody: boolean; adminResult: string; adminUserId: string; setAdminUserId: (v: string) => void; adminGate: string; adminBody: boolean; adminResult: string; adminUserId: string; setAdminUserId: (v: string) => void;
adminQuota: () => void; adminSweep: () => void; adminBackup: () => void; adminQuota: () => void; adminSetQuota: (uid: string, usdText: string) => void; adminSweep: () => void; adminBackup: () => void;
projects: Project[]; projectId: string; projectMsg: string; projects: Project[]; projectId: string; projectMsg: string;
selectProject: (id: string) => void; refreshProjects: () => void; selectProject: (id: string) => void; refreshProjects: () => void;
createProject: (name: string, repo: string) => Promise<void>; createProject: (name: string, repo: string) => Promise<void>;
githubRepos: Array<{ clone_url: string; repo_full_name: string }>; githubRepos: Array<{ clone_url: string; repo_full_name: string }>;
workers: Worker[] | null; workersSummary: string; workersError: string; refreshWorkers: () => void; workers: Worker[] | null; workersSummary: string; workersError: string; refreshWorkers: () => void;
members: Member[]; memberMsg: string; memberError: string; refreshMembers: () => void; inviteMember: (raw: string, role: string) => Promise<void>; changeMemberRole: (uid: string, role: string) => Promise<void>; members: Member[]; memberMsg: string; memberError: string; refreshMembers: () => void; inviteMember: (raw: string, role: string) => Promise<void>; changeMemberRole: (uid: string, role: string) => Promise<void>; removeMember: (uid: string) => void;
skills: SkillItem[]; apps: SkillItem[]; mcpServers: McpServer[]; skillMsg: string; skillError: string; mcpMsg: string; mcpOauthMsg: string; mcpOauthBox: string; skills: SkillItem[]; apps: SkillItem[]; mcpServers: McpServer[]; skillMsg: string; skillError: string; mcpMsg: string; mcpOauthMsg: string; mcpOauthBox: string;
refreshSkills: () => void; reloadMcp: () => void; mcpOauth: (name: string) => Promise<void>; refreshSkills: () => void; reloadMcp: () => void; mcpOauth: (name: string) => Promise<void>;
envs: Env[]; envCount: string; envMsg: string; envRepoLine: string; envRepoBar: boolean; envRepoMsg: string; envSelect: string; setEnvSelect: (v: string) => void; envs: Env[]; envCount: string; envMsg: string; envRepoLine: string; envRepoBar: boolean; envRepoMsg: string; envSelect: string; setEnvSelect: (v: string) => void;
@@ -35,7 +35,7 @@ export interface ConsoleCtx {
followups: Followup[]; followups: Followup[];
approvals: Approval[]; approvalMsg: string; refreshApprovals: () => void; decideApproval: (id: string, decision: string) => void; approvals: Approval[]; approvalMsg: string; refreshApprovals: () => void; decideApproval: (id: string, decision: string) => void;
peerMessages: PeerMessage[]; peerMsg: string; postPeer: (mode: string, target: string, text: string) => Promise<void>; peerMessages: PeerMessage[]; peerMsg: string; postPeer: (mode: string, target: string, text: string) => Promise<void>;
automations: Automation[]; autoMsg: string; refreshAutomations: () => void; createAutomation: (name: string, prompt: string, interval: string, repo: string) => Promise<void>; updateAutomation: (a: Automation, name: string, prompt: string, interval: string, repo: string) => Promise<boolean>; cancelAutomation: (id: string) => void; automations: Automation[]; autoMsg: string; autoDetail: AutomationDetail | null; autoDetailMsg: string; refreshAutomations: () => void; createAutomation: (name: string, prompt: string, interval: string, repo: string) => Promise<void>; updateAutomation: (a: Automation, name: string, prompt: string, interval: string, repo: string) => Promise<boolean>; cancelAutomation: (id: string) => void; loadAutomationDetail: (id: string) => void; closeAutomationDetail: () => void;
triage: Triage | null; triageError: string; refreshTriage: () => void; triage: Triage | null; triageError: string; refreshTriage: () => void;
threadText: string; threadGateNote: string; threadMsg: string; threadList: ThreadInfo[]; threadAction: (kind: string, name: string) => void; threadText: string; threadGateNote: string; threadMsg: string; threadList: ThreadInfo[]; threadAction: (kind: string, name: string) => void;
dispatch: (body: DispatchBody, atts: Array<{ path: string; file: File }>) => Promise<string>; dispatchMsg: string; dispatch: (body: DispatchBody, atts: Array<{ path: string; file: File }>) => Promise<string>; dispatchMsg: string;
+30 -1
View File
@@ -253,6 +253,17 @@ export function useCatalog() {
} catch (e: any) { setMemberMsg(memberErr(e)); } } catch (e: any) { setMemberMsg(memberErr(e)); }
}, []); }, []);
const removeMember = useCallback(async (uid: string) => {
const pid = projectIdRef.current;
if (!pid) return;
if (!window.confirm("确定移除该成员吗?移除后其项目权限立即失效(唯一 owner 不可移除,服务端会拒绝)。")) return;
try {
await api(paths.projectMember(pid, uid), "DELETE");
setMemberMsg("成员已移除");
await refreshMembers(pid);
} catch (e: any) { setMemberMsg(memberErr(e)); }
}, [refreshMembers]);
const reloadMcp = useCallback(async () => { const reloadMcp = useCallback(async () => {
const pid = projectIdRef.current; const pid = projectIdRef.current;
if (!pid) { setMcpMsg("先选项目"); return; } if (!pid) { setMcpMsg("先选项目"); return; }
@@ -289,6 +300,24 @@ export function useCatalog() {
} catch (e: any) { setAdminResult(adminErr(e)); } } catch (e: any) { setAdminResult(adminErr(e)); }
}, [adminUserId]); }, [adminUserId]);
const adminSetQuota = useCallback(async (uid: string, usdText: string) => {
const target = uid.trim();
if (!target) { setAdminResult("请填要设置配额的用户 id"); return; }
// POST /admin/quotas {user_id, quota_usd_micros}: integer micros or
// null for unlimited, >= 0. The console takes dollars and converts.
let micros: number | null;
if (!usdText.trim()) micros = null;
else {
const usd = Number(usdText.trim());
if (!Number.isFinite(usd) || usd < 0) { setAdminResult("配额金额无效:请输入 >= 0 的美元数,留空表示不限额"); return; }
micros = Math.round(usd * 1000000);
}
try {
const d = await api<{ used_usd_micros?: number; quota_usd_micros?: number | null; remaining_usd_micros?: number | null; month?: string }>(paths.adminQuotas, "POST", { user_id: target, quota_usd_micros: micros });
setAdminResult("配额已设置:用户 " + target.slice(0, 8) + ":本月用量 " + fmtUsd(d.used_usd_micros) + " / 配额 " + fmtUsd(d.quota_usd_micros) + (d.remaining_usd_micros !== null && d.remaining_usd_micros !== undefined ? "(剩余 " + fmtUsd(d.remaining_usd_micros) + ")" : "") + " | 月份 " + (d.month || ""));
} catch (e: any) { setAdminResult(adminErr(e)); }
}, []);
const adminSweep = useCallback(async () => { const adminSweep = useCallback(async () => {
if (!window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。")) return; if (!window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。")) return;
try { try {
@@ -314,6 +343,6 @@ export function useCatalog() {
models, modelCustomVisible, models, modelCustomVisible,
refreshHealth, loadQuota, probeAdmin, refreshProjects, loadGithubRepos, loadRelayModels, refreshWorkers, refreshHealth, loadQuota, probeAdmin, refreshProjects, loadGithubRepos, loadRelayModels, refreshWorkers,
refreshEnvs, refreshMembers, refreshSkills, saveKeyInput, clearKeyInput, createProject, createEnv, refreshEnvs, refreshMembers, refreshSkills, saveKeyInput, clearKeyInput, createProject, createEnv,
archiveEnv, saveEnvRepo, inviteMember, changeMemberRole, reloadMcp, mcpOauth, adminQuota, adminSweep, adminBackup, archiveEnv, saveEnvRepo, inviteMember, changeMemberRole, removeMember, reloadMcp, mcpOauth, adminQuota, adminSetQuota, adminSweep, adminBackup,
}; };
} }
@@ -108,14 +108,14 @@ export function useConsoleStore(): ConsoleCtx {
healthText: catalog.healthText, refreshHealth: catalog.refreshHealth, healthText: catalog.healthText, refreshHealth: catalog.refreshHealth,
adminGate: catalog.adminGate, adminBody: catalog.adminBody, adminResult: catalog.adminResult, adminGate: catalog.adminGate, adminBody: catalog.adminBody, adminResult: catalog.adminResult,
adminUserId: catalog.adminUserId, setAdminUserId: catalog.setAdminUserId, adminUserId: catalog.adminUserId, setAdminUserId: catalog.setAdminUserId,
adminQuota: catalog.adminQuota, adminSweep: catalog.adminSweep, adminBackup: catalog.adminBackup, adminQuota: catalog.adminQuota, adminSetQuota: catalog.adminSetQuota, adminSweep: catalog.adminSweep, adminBackup: catalog.adminBackup,
projects: catalog.projects, projectId: catalog.projectId, projectMsg: catalog.projectMsg, projects: catalog.projects, projectId: catalog.projectId, projectMsg: catalog.projectMsg,
selectProject, refreshProjects: catalog.refreshProjects, selectProject, refreshProjects: catalog.refreshProjects,
createProject: catalog.createProject, createProject: catalog.createProject,
githubRepos: catalog.githubRepos, githubRepos: catalog.githubRepos,
workers: catalog.workers, workersSummary: catalog.workersSummary, workersError: catalog.workersError, refreshWorkers: catalog.refreshWorkers, workers: catalog.workers, workersSummary: catalog.workersSummary, workersError: catalog.workersError, refreshWorkers: catalog.refreshWorkers,
members: catalog.members, memberMsg: catalog.memberMsg, memberError: catalog.memberError, members: catalog.members, memberMsg: catalog.memberMsg, memberError: catalog.memberError,
refreshMembers: () => void catalog.refreshMembers(), inviteMember: catalog.inviteMember, changeMemberRole: catalog.changeMemberRole, refreshMembers: () => void catalog.refreshMembers(), inviteMember: catalog.inviteMember, changeMemberRole: catalog.changeMemberRole, removeMember: catalog.removeMember,
skills: catalog.skills, apps: catalog.apps, mcpServers: catalog.mcpServers, skillMsg: catalog.skillMsg, skillError: catalog.skillError, skills: catalog.skills, apps: catalog.apps, mcpServers: catalog.mcpServers, skillMsg: catalog.skillMsg, skillError: catalog.skillError,
mcpMsg: catalog.mcpMsg, mcpOauthMsg: catalog.mcpOauthMsg, mcpOauthBox: catalog.mcpOauthBox, mcpMsg: catalog.mcpMsg, mcpOauthMsg: catalog.mcpOauthMsg, mcpOauthBox: catalog.mcpOauthBox,
refreshSkills: () => void catalog.refreshSkills(), reloadMcp: catalog.reloadMcp, mcpOauth: catalog.mcpOauth, refreshSkills: () => void catalog.refreshSkills(), reloadMcp: catalog.reloadMcp, mcpOauth: catalog.mcpOauth,
@@ -144,7 +144,7 @@ export function useConsoleStore(): ConsoleCtx {
followups: detail.followups, followups: detail.followups,
approvals: detail.approvals, approvalMsg: detail.approvalMsg, refreshApprovals: detail.refreshApprovals, decideApproval: detail.decideApproval, approvals: detail.approvals, approvalMsg: detail.approvalMsg, refreshApprovals: detail.refreshApprovals, decideApproval: detail.decideApproval,
peerMessages: detail.peerMessages, peerMsg: detail.peerMsg, postPeer: detail.postPeer, peerMessages: detail.peerMessages, peerMsg: detail.peerMsg, postPeer: detail.postPeer,
automations: tools.automations, autoMsg: tools.autoMsg, refreshAutomations: () => void tools.refreshAutomations(), createAutomation: tools.createAutomation, updateAutomation: tools.updateAutomation, cancelAutomation: tools.cancelAutomation, automations: tools.automations, autoMsg: tools.autoMsg, autoDetail: tools.autoDetail, autoDetailMsg: tools.autoDetailMsg, refreshAutomations: () => void tools.refreshAutomations(), createAutomation: tools.createAutomation, updateAutomation: tools.updateAutomation, cancelAutomation: tools.cancelAutomation, loadAutomationDetail: tools.loadAutomationDetail, closeAutomationDetail: tools.closeAutomationDetail,
triage: tools.triage, triageError: tools.triageError, refreshTriage: () => void tools.refreshTriage(), triage: tools.triage, triageError: tools.triageError, refreshTriage: () => void tools.refreshTriage(),
threadText: detail.threadText, threadGateNote: detail.threadGateNote, threadMsg: detail.threadMsg, threadList: detail.threadList, threadAction: detail.threadAction, threadText: detail.threadText, threadGateNote: detail.threadGateNote, threadMsg: detail.threadMsg, threadList: detail.threadList, threadAction: detail.threadAction,
dispatch: async (body, atts) => { dispatch: async (body, atts) => {
@@ -1,13 +1,15 @@
import { useCallback, useState } from "react"; import { useCallback, useState } from "react";
import { api } from "../api/client"; import { api } from "../api/client";
import { paths } from "../api/paths"; import { paths } from "../api/paths";
import type { Automation, Triage } from "../api/types"; import type { Automation, AutomationDetail, Triage } from "../api/types";
// Project-scoped tools shown in the centre for their own views: // Project-scoped tools shown in the centre for their own views:
// automations (create/list/cancel) and the triage inbox (4 groups). // automations (create/list/cancel) and the triage inbox (4 groups).
export function useProjectTools(getProjectId: () => string) { export function useProjectTools(getProjectId: () => string) {
const [automations, setAutomations] = useState<Automation[]>([]); const [automations, setAutomations] = useState<Automation[]>([]);
const [autoMsg, setAutoMsg] = useState(""); const [autoMsg, setAutoMsg] = useState("");
const [autoDetail, setAutoDetail] = useState<AutomationDetail | null>(null);
const [autoDetailMsg, setAutoDetailMsg] = useState("");
const [triage, setTriage] = useState<Triage | null>(null); const [triage, setTriage] = useState<Triage | null>(null);
const [triageError, setTriageError] = useState(""); const [triageError, setTriageError] = useState("");
@@ -66,10 +68,26 @@ export function useProjectTools(getProjectId: () => string) {
const pid = getProjectId(); const pid = getProjectId();
if (!pid) return; if (!pid) return;
void api(paths.projectAutomationCancel(pid, id), "POST", {}) void api(paths.projectAutomationCancel(pid, id), "POST", {})
.then(() => { setAutoMsg("自动化已取消"); void refreshAutomations(pid); }) .then(() => { setAutoMsg("自动化已取消"); setAutoDetail((prev) => (prev && prev.id === id ? null : prev)); void refreshAutomations(pid); })
.catch((e: any) => setAutoMsg("取消失败:" + e.message)); .catch((e: any) => setAutoMsg("取消失败:" + e.message));
}, [getProjectId, refreshAutomations]); }, [getProjectId, refreshAutomations]);
// GET /projects/{pid}/automations/{aid}: full definition + recent
// runs (+ runs_note, heartbeat_binding). Verified against origin/main
// cloud/server.py _automation_get / cloud/automations.py get_automation.
const loadAutomationDetail = useCallback(async (id: string) => {
const pid = getProjectId();
if (!pid) { setAutoDetailMsg("先选项目"); return; }
try {
const d = await api<AutomationDetail>(paths.projectAutomation(pid, id));
setAutoDetail(d);
setAutoDetailMsg("");
} catch (e: any) { setAutoDetailMsg("详情加载失败:" + e.message); }
}, [getProjectId]);
const closeAutomationDetail = useCallback(() => { setAutoDetail(null); setAutoDetailMsg(""); }, []);
const refreshTriage = useCallback(async (pid?: string) => { const refreshTriage = useCallback(async (pid?: string) => {
const id = pid !== undefined ? pid : getProjectId(); const id = pid !== undefined ? pid : getProjectId();
if (!id) { setTriage(null); return; } if (!id) { setTriage(null); return; }
@@ -80,5 +98,5 @@ export function useProjectTools(getProjectId: () => string) {
} catch (e: any) { setTriageError("分诊加载失败:" + e.message); } } catch (e: any) { setTriageError("分诊加载失败:" + e.message); }
}, [getProjectId]); }, [getProjectId]);
return { automations, autoMsg, triage, triageError, refreshAutomations, createAutomation, updateAutomation, cancelAutomation, refreshTriage }; return { automations, autoMsg, autoDetail, autoDetailMsg, triage, triageError, refreshAutomations, createAutomation, updateAutomation, cancelAutomation, loadAutomationDetail, closeAutomationDetail, refreshTriage };
} }
@@ -1,8 +1,11 @@
import { useState } from "react";
import { useConsole } from "../context"; import { useConsole } from "../context";
import { Card, Bar } from "../components/ui"; import { Card, Bar } from "../components/ui";
export default function AdminSection() { export default function AdminSection() {
const ctx = useConsole(); const ctx = useConsole();
const [setUser, setSetUser] = useState("");
const [setUsd, setSetUsd] = useState("");
return ( return (
<Card id="adminCard" title={<>管理区 <span className="muted">(仅全局管理员)</span></>}> <Card id="adminCard" title={<>管理区 <span className="muted">(仅全局管理员)</span></>}>
<div className="muted">{ctx.adminGate}</div> <div className="muted">{ctx.adminGate}</div>
@@ -12,6 +15,11 @@ export default function AdminSection() {
<input type="text" placeholder="要查配额的用户 id" value={ctx.adminUserId} onChange={(e) => ctx.setAdminUserId(e.target.value)} /> <input type="text" placeholder="要查配额的用户 id" value={ctx.adminUserId} onChange={(e) => ctx.setAdminUserId(e.target.value)} />
<button onClick={ctx.adminQuota}>查该用户配额</button> <button onClick={ctx.adminQuota}>查该用户配额</button>
</Bar> </Bar>
<Bar>
<input type="text" placeholder="要设置配额的用户 id" value={setUser} onChange={(e) => setSetUser(e.target.value)} />
<input type="text" placeholder="美元配额(留空 = 不限额)" value={setUsd} onChange={(e) => setSetUsd(e.target.value)} />
<button className="primary" onClick={() => ctx.adminSetQuota(setUser, setUsd)}>设置配额</button>
</Bar>
<Bar> <Bar>
<button onClick={ctx.adminSweep}>手动触发 sweep(清理陈旧 running 任务)</button> <button onClick={ctx.adminSweep}>手动触发 sweep(清理陈旧 running 任务)</button>
<button onClick={ctx.adminBackup}>手动触发备份</button> <button onClick={ctx.adminBackup}>手动触发备份</button>
@@ -37,6 +37,7 @@ export default function MembersPanel() {
<select aria-label="成员角色" value={m.role || "viewer"} onChange={(e) => void ctx.changeMemberRole(m.user_id, e.target.value)}> <select aria-label="成员角色" value={m.role || "viewer"} onChange={(e) => void ctx.changeMemberRole(m.user_id, e.target.value)}>
{ROLES.map((r) => <option key={r} value={r}>{r}</option>)} {ROLES.map((r) => <option key={r} value={r}>{r}</option>)}
</select> </select>
<button onClick={() => void ctx.removeMember(m.user_id)}>移除</button>
</li> </li>
); );
})} })}
@@ -0,0 +1,51 @@
import { useState } from "react";
import { useConsole } from "../context";
import { Card, Bar } from "../components/ui";
import { shortId } from "../api/client";
import MembersPanel from "./MembersPanel";
// Left sidebar: the one project selector for the whole console.
// Lists every project (name + short id + my role), click selects it;
// below the list is the create-project form (same fields as the old
// workbench ProjectsSection: name, GitHub repo select, repo_url) and
// the selected project's members. The workbench no longer carries any
// project-module UI; it only consumes ctx.projectId.
export default function ProjectNav() {
const ctx = useConsole();
const [name, setName] = useState("");
const [repoSelect, setRepoSelect] = useState("");
const [repo, setRepo] = useState("");
return (
<div className="project-nav">
<Card title="项目">
<Bar><button onClick={ctx.refreshProjects}>刷新项目</button></Bar>
{ctx.projects.length === 0 ? <div className="muted">暂无项目</div> : (
<ul className="project-nav-list">
{ctx.projects.map((p) => (
<li key={p.id} className={"project-nav-item" + (p.id === ctx.projectId ? " selected" : "")} onClick={() => ctx.selectProject(p.id)} title={p.id}>
<span className="project-nav-name">{p.name}</span>
<span className="project-nav-meta">{shortId(p.id)}{p.my_role ? " · " + p.my_role : ""}</span>
</li>
))}
</ul>
)}
<h3>创建项目</h3>
<Bar>
<input type="text" placeholder="新项目名" value={name} onChange={(e) => setName(e.target.value)} />
</Bar>
<Bar>
<select aria-label="从 GitHub 仓库选" value={repoSelect} onChange={(e) => { setRepoSelect(e.target.value); if (e.target.value) setRepo(e.target.value); }}>
<option value="">(从 GitHub App 安装仓库里选,或手动输入)</option>
{ctx.githubRepos.map((r) => <option key={r.clone_url} value={r.clone_url}>{r.repo_full_name}</option>)}
</select>
</Bar>
<Bar>
<input type="text" placeholder="repo_url(可选)" value={repo} onChange={(e) => setRepo(e.target.value)} />
<button className="primary" onClick={async () => { await ctx.createProject(name, repo); setName(""); setRepo(""); setRepoSelect(""); }}>创建项目</button>
</Bar>
<span className="muted">{ctx.projectMsg}</span>
</Card>
<MembersPanel />
</div>
);
}
@@ -1,32 +0,0 @@
import { useState } from "react";
import { useConsole } from "../context";
import { Card, Bar } from "../components/ui";
import { shortId } from "../api/client";
export default function ProjectsSection() {
const ctx = useConsole();
const [name, setName] = useState("");
const [repoSelect, setRepoSelect] = useState("");
const [repo, setRepo] = useState("");
return (
<Card id="nav-projects" title="项目">
<Bar>
<select aria-label="项目" value={ctx.projectId} onChange={(e) => ctx.selectProject(e.target.value)}>
{ctx.projects.length === 0 ? <option value="">(先加载项目)</option> : null}
{ctx.projects.map((p) => <option key={p.id} value={p.id}>{p.name} ({shortId(p.id)})</option>)}
</select>
<button onClick={ctx.refreshProjects}>刷新</button>
</Bar>
<Bar>
<input type="text" placeholder="新项目名" value={name} onChange={(e) => setName(e.target.value)} />
<select aria-label="从 GitHub 仓库选" value={repoSelect} onChange={(e) => { setRepoSelect(e.target.value); if (e.target.value) setRepo(e.target.value); }}>
<option value="">(从 GitHub App 安装仓库里选,或手动输入)</option>
{ctx.githubRepos.map((r) => <option key={r.clone_url} value={r.clone_url}>{r.repo_full_name}</option>)}
</select>
<input type="text" placeholder="repo_url(可选)" value={repo} onChange={(e) => setRepo(e.target.value)} />
<button onClick={async () => { await ctx.createProject(name, repo); setName(""); setRepo(""); setRepoSelect(""); }}>新建项目</button>
</Bar>
<span className="muted">{ctx.projectMsg}</span>
</Card>
);
}
@@ -5,9 +5,12 @@ import { firstChar, durationOf, tokenOf } from "../api/taskMeta";
// 任务列表行 meta:首字 / 总耗时 / token,计算见 api/taskMeta.ts, // 任务列表行 meta:首字 / 总耗时 / token,计算见 api/taskMeta.ts,
// 与任务详情共用同一口径。 // 与任务详情共用同一口径。
// GitHub issues-style task list: search on top, the project selector // GitHub issues-style task list: search on top, a 主要聊天 header with
// (the auto-project fix — do not remove it), a 主要聊天 header with a // a + button, then hairline rows with a Primer status dot at the
// + button, then hairline rows with a Primer status dot at the left, // left. There is deliberately NO project selector here any more:
// the left sidebar (ProjectNav) is the one project selector, and the
// auto-select-first-project fix in the store keeps the list from
// ever starting empty.
// a 14px title and a 12px muted meta line. The search input IS the // a 14px title and a 12px muted meta line. The search input IS the
// existing task search (id/prompt); status and executor filters stay // existing task search (id/prompt); status and executor filters stay
// as a compact row below it. The selected row gets the light // as a compact row below it. The selected row gets the light
@@ -22,12 +25,6 @@ export default function TaskListSection({ onNew }: { onNew: () => void }) {
</svg> </svg>
<input type="text" placeholder="搜索" aria-label="搜索任务" value={ctx.searchText} onChange={(e) => ctx.setSearchText(e.target.value)} /> <input type="text" placeholder="搜索" aria-label="搜索任务" value={ctx.searchText} onChange={(e) => ctx.setSearchText(e.target.value)} />
</div> </div>
<div className="bar">
<select aria-label="项目" value={ctx.projectId} onChange={(e) => ctx.selectProject(e.target.value)}>
{ctx.projects.length === 0 ? <option value="">(先加载项目)</option> : null}
{ctx.projects.map((p) => <option key={p.id} value={p.id}>{p.name} ({shortId(p.id)})</option>)}
</select>
</div>
<div className="list-section-head"> <div className="list-section-head">
<span>主要聊天</span> <span>主要聊天</span>
<button type="button" aria-label="派发新任务" title="派发新任务" onClick={onNew}>+</button> <button type="button" aria-label="派发新任务" title="派发新任务" onClick={onNew}>+</button>
@@ -1,22 +1,52 @@
import { useState } from "react";
import { useConsole } from "../context"; import { useConsole } from "../context";
import { Card, Bar, DataListRaw } from "../components/ui"; import { Card, Bar, DataListRaw } from "../components/ui";
import { workerAgeText, shortId } from "../api/client"; import { workerAgeText, shortId } from "../api/client";
// Workers are read-only by design (GET /workers + worker-side
// heartbeat only — no start/stop/kill actions exist to surface), so
// production-grade here is list + status + refresh + frontend-only
// filtering of the already-fetched list. Non-admin users only get
// the online/offline aggregate from the backend (see _workers_list),
// so search/filter apply to the full list only and say so.
export default function WorkersSection() { export default function WorkersSection() {
const ctx = useConsole(); const ctx = useConsole();
const [query, setQuery] = useState("");
const [onlineFilter, setOnlineFilter] = useState<"all" | "online" | "offline">("all");
const isAlive = (w: { alive?: boolean | null; last_seen?: number; last_seen_seconds_ago?: number }) => {
const info = workerAgeText(w);
return w.alive !== undefined && w.alive !== null ? !!w.alive : (info.age !== null && info.age <= 120);
};
const q = query.trim().toLowerCase();
const filtered = (ctx.workers || []).filter((w) => {
if (q && !(((w.worker_id || w.id || "") + " " + (w.executor || "")).toLowerCase().includes(q))) return false;
if (onlineFilter === "online" && !isAlive(w)) return false;
if (onlineFilter === "offline" && isAlive(w)) return false;
return true;
});
return ( return (
<Card id="nav-workers" title="Workers"> <Card id="nav-workers" title="Workers">
<Bar><button onClick={ctx.refreshWorkers}>刷新</button></Bar> <Bar><button onClick={ctx.refreshWorkers}>刷新</button></Bar>
<Bar>
<input type="text" placeholder="搜索 worker_id / 执行器" aria-label="搜索 Worker" value={query} onChange={(e) => setQuery(e.target.value)} />
<select aria-label="在线状态筛选" value={onlineFilter} onChange={(e) => setOnlineFilter(e.target.value as "all" | "online" | "offline")}>
<option value="all">在线状态:全部</option>
<option value="online">仅在线</option>
<option value="offline">仅离线</option>
</select>
{ctx.workers ? <span className="muted">显示 {filtered.length} / 共 {ctx.workers.length} 个</span> : null}
</Bar>
{ctx.workersError ? <div className="muted">{ctx.workersError}</div> {ctx.workersError ? <div className="muted">{ctx.workersError}</div>
: ctx.workers === null ? <div className="muted">{ctx.workersSummary || "未加载"}</div> : ctx.workers === null ? <div className="muted">{ctx.workersSummary || "未加载"}(完整清单仅全局管理员可见,搜索/筛选仅对完整清单生效)</div>
: ctx.workers.length === 0 ? <div className="muted">无 worker 注册</div> : ctx.workers.length === 0 ? <div className="muted">无 worker 注册</div>
: filtered.length === 0 ? <div className="muted">没有符合筛选条件的 worker</div>
: ( : (
<DataListRaw> <DataListRaw>
{ctx.workers.map((w, i) => { {filtered.map((w, i) => {
const run = w.running === undefined || w.running === null ? "?" : w.running; const run = w.running === undefined || w.running === null ? "?" : w.running;
const cap = w.capacity === undefined || w.capacity === null ? "?" : w.capacity; const cap = w.capacity === undefined || w.capacity === null ? "?" : w.capacity;
const info = workerAgeText(w); const info = workerAgeText(w);
const alive = w.alive !== undefined && w.alive !== null ? !!w.alive : (info.age !== null && info.age <= 120); const alive = isAlive(w);
const live = alive ? " / 在线" : " / 离线(占用为上次上报值)"; const live = alive ? " / 在线" : " / 离线(占用为上次上报值)";
return ( return (
<li key={i} className={alive ? "is-online" : "is-offline"}> <li key={i} className={alive ? "is-online" : "is-offline"}>
@@ -2,7 +2,7 @@ import { useState } from "react";
import { useConsole } from "../../context"; import { useConsole } from "../../context";
import { Bar, DataList } from "../../components/ui"; import { Bar, DataList } from "../../components/ui";
import { fmt, shortId } from "../../api/client"; import { fmt, shortId } from "../../api/client";
import type { Automation } from "../../api/types"; import type { Automation, AutomationDetail } from "../../api/types";
const PROMPT_PREVIEW = 160; const PROMPT_PREVIEW = 160;
@@ -14,6 +14,49 @@ function humanInterval(s?: number): string {
return ""; return "";
} }
// Automation detail (read-only display): backed by GET
// /projects/{pid}/automations/{aid} — full definition + runs[] +
// runs_note + heartbeat_binding. Editing is NOT duplicated here:
// the single edit form is main's inline edit in the list below
// (updateAutomation with the (a, name, prompt, interval, repo)
// signature). Detail display + runs + task buttons are our side's
// addition; cross-view jumps use location.hash because the IDE
// shell switches views on hashchange (no sec-conv / sec-automations
// element to scroll to).
function AutomationDetailBox({ detail }: { detail: AutomationDetail }) {
const ctx = useConsole();
const openTask = (id?: string) => {
if (!id) return;
ctx.showDetail(id);
try { window.location.hash = "#view-tasks"; } catch { /* noop */ }
};
return (
<div className="card">
<h2>自动化详情:{detail.name}</h2>
<div className="muted">
状态 {detail.status}{detail.due ? "|到期" : ""} | 每 {detail.interval_seconds} 秒 | 下次 {fmt(detail.next_run_at)}
| 上次执行 {fmt(detail.last_run_at)} | 创建人 {detail.created_by ? shortId(detail.created_by) : ""} | 创建于 {fmt(detail.created_at)}
{detail.cancelled_at ? " | 已取消于 " + fmt(detail.cancelled_at) : ""}
{detail.heartbeat_binding ? " | 心跳绑定:已绑定" : ""}
</div>
<div className="muted" style={{ whiteSpace: "pre-wrap", marginTop: 8 }}>{detail.prompt || ""}</div>
{detail.repo_url ? <div className="muted">仓库 {detail.repo_url}</div> : null}
<Bar>
<button onClick={ctx.closeAutomationDetail}>关闭详情</button>
<span className="muted">编辑请在下面列表里点“编辑”。{ctx.autoDetailMsg}</span>
</Bar>
<div className="data-group"><b>最近产出({(detail.runs || []).length})</b>
<DataList empty="(无)" items={(detail.runs || []).map((r, i) => (
<span key={i} className="row-main">
{r.status}{r.created_at ? "|" + fmt(r.created_at) : ""}{r.task_id ? <> | 任务 <button onClick={() => openTask(r.task_id as string)}>{shortId(r.task_id)}</button></> : ""}{r.detail ? "|" + String(r.detail).slice(0, 120) : ""}
</span>
))} />
</div>
{detail.runs_note ? <div className="muted">{detail.runs_note}</div> : null}
</div>
);
}
export function AutomationPanel() { export function AutomationPanel() {
const ctx = useConsole(); const ctx = useConsole();
const [name, setName] = useState(""); const [name, setName] = useState("");
@@ -53,6 +96,7 @@ export function AutomationPanel() {
<div key={a.id}> <div key={a.id}>
<div className="row-main"> <div className="row-main">
[{a.status}{a.due ? "|到期" : ""}{a.run_once ? "|一次性" : ""}] {a.name}|每 {a.interval_seconds} 秒{humanInterval(a.interval_seconds)}|上次 {fmt(a.last_run_at) || "未运行"}|下次 {fmt(a.next_run_at)} [{a.status}{a.due ? "|到期" : ""}{a.run_once ? "|一次性" : ""}] {a.name}|每 {a.interval_seconds} 秒{humanInterval(a.interval_seconds)}|上次 {fmt(a.last_run_at) || "未运行"}|下次 {fmt(a.next_run_at)}
<button onClick={() => void ctx.loadAutomationDetail(a.id)}>详情</button>
{a.status === "active" ? <> <button onClick={() => startEdit(a)}>编辑</button> <button onClick={() => ctx.cancelAutomation(a.id)}>取消</button></> : null} {a.status === "active" ? <> <button onClick={() => startEdit(a)}>编辑</button> <button onClick={() => ctx.cancelAutomation(a.id)}>取消</button></> : null}
</div> </div>
{a.repo_url ? <div className="muted">仓库:{a.repo_url}</div> : null} {a.repo_url ? <div className="muted">仓库:{a.repo_url}</div> : null}
@@ -80,6 +124,8 @@ export function AutomationPanel() {
</div> </div>
); );
})} /> })} />
{ctx.autoDetailMsg && !ctx.autoDetail ? <div className="muted">{ctx.autoDetailMsg}</div> : null}
{ctx.autoDetail ? <AutomationDetailBox key={ctx.autoDetail.id} detail={ctx.autoDetail} /> : null}
</> </>
); );
} }
@@ -87,21 +133,45 @@ export function AutomationPanel() {
export function TriagePanel() { export function TriagePanel() {
const ctx = useConsole(); const ctx = useConsole();
const t = ctx.triage; const t = ctx.triage;
// Clicking an actionable item opens that task's detail (tasks view)
// or the automation's detail (automation view). Field names are
// exactly the triage payload's: failed_tasks[].id, pending_followups
// [].task_id, recent_runs[].task_id (nullable), automations_due[].id.
// Jumps use location.hash because the IDE shell switches views on
// hashchange; there is no sec-conv / sec-automations to scroll to.
const openTask = (id?: string) => {
if (!id) return;
ctx.showDetail(id);
try { window.location.hash = "#view-tasks"; } catch { /* noop */ }
};
const openAutomation = (id?: string) => {
if (!id) return;
void ctx.loadAutomationDetail(id);
try { window.location.hash = "#view-automation"; } catch { /* noop */ }
};
if (ctx.triageError) return <div className="muted">{ctx.triageError}</div>; if (ctx.triageError) return <div className="muted">{ctx.triageError}</div>;
if (!t) return <div className="muted">(先选项目)</div>; if (!t) return <div className="muted">(先选项目)</div>;
return ( return (
<> <>
<div className="data-group"><b>到期自动化({t.counts?.automations_due || 0})</b> <div className="data-group"><b>到期自动化({t.counts?.automations_due || 0})</b>
<DataList empty="(无)" items={(t.automations_due || []).map((a, i) => <span key={i} className="row-main">{a.name}|每 {a.interval_seconds} 秒</span>)} /> <DataList empty="(无)" items={(t.automations_due || []).map((a, i) => (
<span key={i} className="row-main">{a.id ? <button onClick={() => openAutomation(a.id)}>{a.name}|每 {a.interval_seconds} 秒</button> : <>{a.name}|每 {a.interval_seconds} 秒</>}</span>
))} />
</div> </div>
<div className="data-group"><b>失败/中断任务({t.counts?.failed_tasks || 0})</b> <div className="data-group"><b>失败/中断任务({t.counts?.failed_tasks || 0})</b>
<DataList empty="(无)" items={(t.failed_tasks || []).map((x, i) => <span key={i} className="row-main">{shortId(x.id)}|{x.status}|{String(x.prompt || "").slice(0, 80)}</span>)} /> <DataList empty="(无)" items={(t.failed_tasks || []).map((x, i) => (
<span key={i} className="row-main"><button onClick={() => openTask(x.id)}>{shortId(x.id)}|{x.status}|{String(x.prompt || "").slice(0, 80)}</button></span>
))} />
</div> </div>
<div className="data-group"><b>待处理后续消息({t.counts?.pending_followups || 0})</b> <div className="data-group"><b>待处理后续消息({t.counts?.pending_followups || 0})</b>
<DataList empty="(无)" items={(t.pending_followups || []).map((f, i) => <span key={i} className="row-main">任务 {shortId(f.task_id)}|{String(f.body || "").slice(0, 80)}</span>)} /> <DataList empty="(无)" items={(t.pending_followups || []).map((f, i) => (
<span key={i} className="row-main"><button onClick={() => openTask(f.task_id)}>任务 {shortId(f.task_id)}|{String(f.body || "").slice(0, 80)}</button></span>
))} />
</div> </div>
<div className="data-group"><b>最近自动化产出({t.counts?.recent_runs || 0})</b> <div className="data-group"><b>最近自动化产出({t.counts?.recent_runs || 0})</b>
<DataList empty="(无)" items={(t.recent_runs || []).map((r, i) => <span key={i} className="row-main">{shortId(r.automation_id)}|{r.status}{r.task_id ? "|任务 " + shortId(r.task_id) : ""}{r.task_status ? "|" + r.task_status : ""}</span>)} /> <DataList empty="(无)" items={(t.recent_runs || []).map((r, i) => (
<span key={i} className="row-main">{shortId(r.automation_id)}|{r.status}{r.task_id ? <>|任务 <button onClick={() => openTask(r.task_id)}>{shortId(r.task_id)}</button></> : ""}{r.task_status ? "|" + r.task_status : ""}</span>
))} />
</div> </div>
</> </>
); );
+16 -1
View File
@@ -254,7 +254,7 @@ pre{background:var(--inset-bg);color:var(--text);border:1px solid var(--line);bo
which is still tight — just in a different order. */ which is still tight — just in a different order. */
.dash-col{display:contents} .dash-col{display:contents}
#sec-dispatch{order:1} #sec-tasks{order:2} #sec-conv{order:3} #sec-dispatch{order:1} #sec-tasks{order:2} #sec-conv{order:3}
#sec-projects{order:4} #sec-env{order:5} #sec-workers{order:6} #sec-env{order:5} #sec-workers{order:6}
#sec-skills{order:7} #sec-automations{order:8} #sec-triage{order:9} #sec-skills{order:7} #sec-automations{order:8} #sec-triage{order:9}
#sec-admin{order:10} #sec-key{order:11} #sec-admin{order:10} #sec-key{order:11}
.dash-cards{grid-template-columns:repeat(2,1fr)} .dash-cards{grid-template-columns:repeat(2,1fr)}
@@ -285,6 +285,20 @@ pre{background:var(--inset-bg);color:var(--text);border:1px solid var(--line);bo
.shell-nav button{text-align:left;border:0;box-shadow:none;background:transparent;padding:8px;border-radius:var(--radius);font-weight:500} .shell-nav button{text-align:left;border:0;box-shadow:none;background:transparent;padding:8px;border-radius:var(--radius);font-weight:500}
.shell-nav button:hover{background:#fff} .shell-nav button:hover{background:#fff}
.shell-nav button.active{background:var(--text);color:#fff} .shell-nav button.active{background:var(--text);color:#fff}
/* Project sidebar (ProjectNav): the one project selector for the whole
console — project list + create + members — living in the IDE
shell's left sidebar. Same Primer tokens as the rest of the shell;
the sidebar is 260px wide, so its bars stack full-width. */
.shell-project-nav{padding:0 12px 16px;min-width:0}
.shell-project-nav .project-nav{display:flex;flex-direction:column;gap:16px;min-width:0}
.shell-project-nav .project-nav-list{list-style:none;margin:0;padding:0;border:1px solid var(--line);border-radius:var(--radius);overflow:hidden}
.shell-project-nav .project-nav-item{padding:8px 12px;cursor:pointer;border-bottom:1px solid var(--line);background:#fff}
.shell-project-nav .project-nav-item:last-child{border-bottom:0}
.shell-project-nav .project-nav-item:hover{background:var(--subtle-bg)}
.shell-project-nav .project-nav-item.selected{background:var(--selected-bg)}
.shell-project-nav .project-nav-name{display:block;font-size:14px;font-weight:600;overflow-wrap:anywhere}
.shell-project-nav .project-nav-meta{display:block;font-size:12px;color:var(--muted)}
.shell-project-nav .bar > input[type="text"],.shell-project-nav .bar > select{flex:1 1 100%}
.shell-stats{margin-top:auto;border-top:1px solid var(--line);padding:16px 12px} .shell-stats{margin-top:auto;border-top:1px solid var(--line);padding:16px 12px}
.shell-stats-label{font-size:12px;font-weight:600;color:var(--muted);padding:0 4px 8px} .shell-stats-label{font-size:12px;font-weight:600;color:var(--muted);padding:0 4px 8px}
.shell-stats .dash-cards{display:flex;flex-direction:column;gap:8px} .shell-stats .dash-cards{display:flex;flex-direction:column;gap:8px}
@@ -316,6 +330,7 @@ pre{background:var(--inset-bg);color:var(--text);border:1px solid var(--line);bo
.shell-side{flex-direction:row;flex-wrap:wrap;align-items:flex-start;border-right:0;border-bottom:1px solid var(--line);overflow:visible} .shell-side{flex-direction:row;flex-wrap:wrap;align-items:flex-start;border-right:0;border-bottom:1px solid var(--line);overflow:visible}
.shell-nav{flex-direction:row;flex-wrap:wrap} .shell-nav{flex-direction:row;flex-wrap:wrap}
.shell-nav-group{flex-direction:row;align-items:center} .shell-nav-group{flex-direction:row;align-items:center}
.shell-project-nav{flex:1 1 100%}
.shell-stats{display:none} .shell-stats{display:none}
.shell-hide-sm{display:none} .shell-hide-sm{display:none}
.workspace{grid-template-columns:1fr} .workspace{grid-template-columns:1fr}
+31 -31
View File
@@ -4,7 +4,7 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="viewport" content="width=device-width, initial-scale=1">
<title>Codex 命令台</title> <title>Codex 命令台</title>
<script type="module" crossorigin>(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const g of document.querySelectorAll('link[rel="modulepreload"]'))f(g);new MutationObserver(g=>{for(const C of g)if(C.type==="childList")for(const A of C.addedNodes)A.tagName==="LINK"&&A.rel==="modulepreload"&&f(A)}).observe(document,{childList:!0,subtree:!0});function h(g){const C={};return g.integrity&&(C.integrity=g.integrity),g.referrerPolicy&&(C.referrerPolicy=g.referrerPolicy),g.crossOrigin==="use-credentials"?C.credentials="include":g.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function f(g){if(g.ep)return;g.ep=!0;const C=h(g);fetch(g.href,C)}})();function Kh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var no={exports:{}},ui={};/** <script type="module" crossorigin>(function(){const r=document.createElement("link").relList;if(r&&r.supports&&r.supports("modulepreload"))return;for(const y of document.querySelectorAll('link[rel="modulepreload"]'))f(y);new MutationObserver(y=>{for(const C of y)if(C.type==="childList")for(const M of C.addedNodes)M.tagName==="LINK"&&M.rel==="modulepreload"&&f(M)}).observe(document,{childList:!0,subtree:!0});function h(y){const C={};return y.integrity&&(C.integrity=y.integrity),y.referrerPolicy&&(C.referrerPolicy=y.referrerPolicy),y.crossOrigin==="use-credentials"?C.credentials="include":y.crossOrigin==="anonymous"?C.credentials="omit":C.credentials="same-origin",C}function f(y){if(y.ep)return;y.ep=!0;const C=h(y);fetch(y.href,C)}})();function Jh(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var io={exports:{}},si={};/**
* @license React * @license React
* react-jsx-runtime.production.js * react-jsx-runtime.production.js
* *
@@ -12,7 +12,7 @@
* *
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/var Rh;function ky(){if(Rh)return ui;Rh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function h(f,g,C){var A=null;if(C!==void 0&&(A=""+C),g.key!==void 0&&(A=""+g.key),"key"in g){C={};for(var S in g)S!=="key"&&(C[S]=g[S])}else C=g;return g=C.ref,{$$typeof:i,type:f,key:A,ref:g!==void 0?g:null,props:C}}return ui.Fragment=r,ui.jsx=h,ui.jsxs=h,ui}var Dh;function By(){return Dh||(Dh=1,no.exports=ky()),no.exports}var c=By(),uo={exports:{}},yt={};/** */var Dh;function Hy(){if(Dh)return si;Dh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.fragment");function h(f,y,C){var M=null;if(C!==void 0&&(M=""+C),y.key!==void 0&&(M=""+y.key),"key"in y){C={};for(var S in y)S!=="key"&&(C[S]=y[S])}else C=y;return y=C.ref,{$$typeof:i,type:f,key:M,ref:y!==void 0?y:null,props:C}}return si.Fragment=r,si.jsx=h,si.jsxs=h,si}var Uh;function By(){return Uh||(Uh=1,io.exports=Hy()),io.exports}var c=By(),co={exports:{}},yt={};/**
* @license React * @license React
* react.production.js * react.production.js
* *
@@ -20,7 +20,7 @@
* *
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/var Uh;function qy(){if(Uh)return yt;Uh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),g=Symbol.for("react.profiler"),C=Symbol.for("react.consumer"),A=Symbol.for("react.context"),S=Symbol.for("react.forward_ref"),H=Symbol.for("react.suspense"),R=Symbol.for("react.memo"),Y=Symbol.for("react.lazy"),x=Symbol.for("react.activity"),k=Symbol.for("react.view_transition"),W=Symbol.iterator;function V(y){return y===null||typeof y!="object"?null:(y=W&&y[W]||y["@@iterator"],typeof y=="function"?y:null)}var I={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},X=Object.assign,ht={};function gt(y,D,ct){this.props=y,this.context=D,this.refs=ht,this.updater=ct||I}gt.prototype.isReactComponent={},gt.prototype.setState=function(y,D){if(typeof y!="object"&&typeof y!="function"&&y!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,y,D,"setState")},gt.prototype.forceUpdate=function(y){this.updater.enqueueForceUpdate(this,y,"forceUpdate")};function xt(){}xt.prototype=gt.prototype;function Z(y,D,ct){this.props=y,this.context=D,this.refs=ht,this.updater=ct||I}var Tt=Z.prototype=new xt;Tt.constructor=Z,X(Tt,gt.prototype),Tt.isPureReactComponent=!0;var w=Array.isArray;function Q(){}var et={H:null,A:null,T:null,S:null},zt=Object.prototype.hasOwnProperty;function Bt(y,D,ct){var it=ct.ref;return{$$typeof:i,type:y,key:D,ref:it!==void 0?it:null,props:ct}}function vt(y,D){return Bt(y.type,D,y.props)}function wt(y){return typeof y=="object"&&y!==null&&y.$$typeof===i}function q(y){var D={"=":"=0",":":"=2"};return"$"+y.replace(/[=:]/g,function(ct){return D[ct]})}var ot=/\/+/g;function rt(y,D){return typeof y=="object"&&y!==null&&y.key!=null?q(""+y.key):D.toString(36)}function G(y){switch(y.status){case"fulfilled":return y.value;case"rejected":throw y.reason;default:switch(typeof y.status=="string"?y.then(Q,Q):(y.status="pending",y.then(function(D){y.status==="pending"&&(y.status="fulfilled",y.value=D)},function(D){y.status==="pending"&&(y.status="rejected",y.reason=D)})),y.status){case"fulfilled":return y.value;case"rejected":throw y.reason}}throw y}function N(y,D,ct,it,Nt){var _t=typeof y;(_t==="undefined"||_t==="boolean")&&(y=null);var Rt=!1;if(y===null)Rt=!0;else switch(_t){case"bigint":case"string":case"number":Rt=!0;break;case"object":switch(y.$$typeof){case i:case r:Rt=!0;break;case Y:return Rt=y._init,N(Rt(y._payload),D,ct,it,Nt)}}if(Rt)return Nt=Nt(y),Rt=it===""?"."+rt(y,0):it,w(Nt)?(ct="",Rt!=null&&(ct=Rt.replace(ot,"$&/")+"/"),N(Nt,D,ct,"",function(ce){return ce})):Nt!=null&&(wt(Nt)&&(Nt=vt(Nt,ct+(Nt.key==null||y&&y.key===Nt.key?"":(""+Nt.key).replace(ot,"$&/")+"/")+Rt)),D.push(Nt)),1;Rt=0;var P=it===""?".":it+":";if(w(y))for(var dt=0;dt<y.length;dt++)it=y[dt],_t=P+rt(it,dt),Rt+=N(it,D,ct,_t,Nt);else if(dt=V(y),typeof dt=="function")for(y=dt.call(y),dt=0;!(it=y.next()).done;)it=it.value,_t=P+rt(it,dt++),Rt+=N(it,D,ct,_t,Nt);else if(_t==="object"){if(typeof y.then=="function")return N(G(y),D,ct,it,Nt);throw D=String(y),Error("Objects are not valid as a React child (found: "+(D==="[object Object]"?"object with keys {"+Object.keys(y).join(", ")+"}":D)+"). If you meant to render a collection of children, use an array instead.")}return Rt}function ut(y,D,ct){if(y==null)return y;var it=[],Nt=0;return N(y,it,"","",function(_t){return D.call(ct,_t,Nt++)}),it}function St(y){if(y._status===-1){var D=y._result,ct=D();ct.then(function(it){(y._status===0||y._status===-1)&&(y._status=1,y._result=it,ct.status===void 0&&(ct.status="fulfilled",ct.value=it))},function(it){(y._status===0||y._status===-1)&&(y._status=2,y._result=it,ct.status===void 0&&(ct.status="rejected",ct.reason=it))}),y._status===-1&&(y._status=0,y._result=ct)}if(y._status===1)return y._result.default;throw y._result}var pt=typeof reportError=="function"?reportError:function(y){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var D=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof y=="object"&&y!==null&&typeof y.message=="string"?String(y.message):String(y),error:y});if(!window.dispatchEvent(D))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",y);return}console.error(y)};function ge(y){var D=et.T,ct={};ct.types=D!==null?D.types:null,et.T=ct;try{var it=y(),Nt=et.S;Nt!==null&&Nt(ct,it),typeof it=="object"&&it!==null&&typeof it.then=="function"&&it.then(Q,pt)}catch(_t){pt(_t)}finally{D!==null&&ct.types!==null&&(D.types=ct.types),et.T=D}}function we(y){var D=et.T;if(D!==null){var ct=D.types;ct===null?D.types=[y]:ct.indexOf(y)===-1&&ct.push(y)}else ge(we.bind(null,y))}var Ce={map:ut,forEach:function(y,D,ct){ut(y,function(){D.apply(this,arguments)},ct)},count:function(y){var D=0;return ut(y,function(){D++}),D},toArLine truncated */var wh;function qy(){if(wh)return yt;wh=1;var i=Symbol.for("react.transitional.element"),r=Symbol.for("react.portal"),h=Symbol.for("react.fragment"),f=Symbol.for("react.strict_mode"),y=Symbol.for("react.profiler"),C=Symbol.for("react.consumer"),M=Symbol.for("react.context"),S=Symbol.for("react.forward_ref"),_=Symbol.for("react.suspense"),O=Symbol.for("react.memo"),Y=Symbol.for("react.lazy"),x=Symbol.for("react.activity"),H=Symbol.for("react.view_transition"),L=Symbol.iterator;function tt(g){return g===null||typeof g!="object"?null:(g=L&&g[L]||g["@@iterator"],typeof g=="function"?g:null)}var dt={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},J=Object.assign,gt={};function _t(g,U,ct){this.props=g,this.context=U,this.refs=gt,this.updater=ct||dt}_t.prototype.isReactComponent={},_t.prototype.setState=function(g,U){if(typeof g!="object"&&typeof g!="function"&&g!=null)throw Error("takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,g,U,"setState")},_t.prototype.forceUpdate=function(g){this.updater.enqueueForceUpdate(this,g,"forceUpdate")};function Dt(){}Dt.prototype=_t.prototype;function Q(g,U,ct){this.props=g,this.context=U,this.refs=gt,this.updater=ct||dt}var ft=Q.prototype=new Dt;ft.constructor=Q,J(ft,_t.prototype),ft.isPureReactComponent=!0;var D=Array.isArray;function X(){}var et={H:null,A:null,T:null,S:null},xt=Object.prototype.hasOwnProperty;function Mt(g,U,ct){var it=ct.ref;return{$$typeof:i,type:g,key:U,ref:it!==void 0?it:null,props:ct}}function mt(g,U){return Mt(g.type,U,g.props)}function Nt(g){return typeof g=="object"&&g!==null&&g.$$typeof===i}function B(g){var U={"=":"=0",":":"=2"};return"$"+g.replace(/[=:]/g,function(ct){return U[ct]})}var nt=/\/+/g;function rt(g,U){return typeof g=="object"&&g!==null&&g.key!=null?B(""+g.key):U.toString(36)}function G(g){switch(g.status){case"fulfilled":return g.value;case"rejected":throw g.reason;default:switch(typeof g.status=="string"?g.then(X,X):(g.status="pending",g.then(function(U){g.status==="pending"&&(g.status="fulfilled",g.value=U)},function(U){g.status==="pending"&&(g.status="rejected",g.reason=U)})),g.status){case"fulfilled":return g.value;case"rejected":throw g.reason}}throw g}function A(g,U,ct,it,Ot){var Et=typeof g;(Et==="undefined"||Et==="boolean")&&(g=null);var kt=!1;if(g===null)kt=!0;else switch(Et){case"bigint":case"string":case"number":kt=!0;break;case"object":switch(g.$$typeof){case i:case r:kt=!0;break;case Y:return kt=g._init,A(kt(g._payload),U,ct,it,Ot)}}if(kt)return Ot=Ot(g),kt=it===""?"."+rt(g,0):it,D(Ot)?(ct="",kt!=null&&(ct=kt.replace(nt,"$&/")+"/"),A(Ot,U,ct,"",function(se){return se})):Ot!=null&&(Nt(Ot)&&(Ot=mt(Ot,ct+(Ot.key==null||g&&g.key===Ot.key?"":(""+Ot.key).replace(nt,"$&/")+"/")+kt)),U.push(Ot)),1;kt=0;var P=it===""?".":it+":";if(D(g))for(var ht=0;ht<g.length;ht++)it=g[ht],Et=P+rt(it,ht),kt+=A(it,U,ct,Et,Ot);else if(ht=tt(g),typeof ht=="function")for(g=ht.call(g),ht=0;!(it=g.next()).done;)it=it.value,Et=P+rt(it,ht++),kt+=A(it,U,ct,Et,Ot);else if(Et==="object"){if(typeof g.then=="function")return A(G(g),U,ct,it,Ot);throw U=String(g),Error("Objects are not valid as a React child (found: "+(U==="[object Object]"?"object with keys {"+Object.keys(g).join(", ")+"}":U)+"). If you meant to render a collection of children, use an array instead.")}return kt}function ut(g,U,ct){if(g==null)return g;var it=[],Ot=0;return A(g,it,"","",function(Et){return U.call(ct,Et,Ot++)}),it}function bt(g){if(g._status===-1){var U=g._result,ct=U();ct.then(function(it){(g._status===0||g._status===-1)&&(g._status=1,g._result=it,ct.status===void 0&&(ct.status="fulfilled",ct.value=it))},function(it){(g._status===0||g._status===-1)&&(g._status=2,g._result=it,ct.status===void 0&&(ct.status="rejected",ct.reason=it))}),g._status===-1&&(g._status=0,g._result=ct)}if(g._status===1)return g._result.default;throw g._result}var pt=typeof reportError=="function"?reportError:function(g){if(typeof window=="object"&&typeof window.ErrorEvent=="function"){var U=new window.ErrorEvent("error",{bubbles:!0,cancelable:!0,message:typeof g=="object"&&g!==null&&typeof g.message=="string"?String(g.message):String(g),error:g});if(!window.dispatchEvent(U))return}else if(typeof process=="object"&&typeof process.emit=="function"){process.emit("uncaughtException",g);return}console.error(g)};function Se(g){var U=et.T,ct={};ct.types=U!==null?U.types:null,et.T=ct;try{var it=g(),Ot=et.S;Ot!==null&&Ot(ct,it),typeof it=="object"&&it!==null&&typeof it.then=="function"&&it.then(X,pt)}catch(Et){pt(Et)}finally{U!==null&&ct.types!==null&&(U.types=ct.types),et.T=U}}function we(g){var U=et.T;if(U!==null){var ct=U.types;ct===null?U.types=[g]:ct.indexOf(g)===-1&&ct.push(g)}else Se(we.bind(null,g))}var Ne={map:ut,forEach:function(g,U,ct){ut(g,function(){U.apply(this,arguments)},ct)},count:function(g){var U=0;return ut(g,function(){U++}),U}Line truncated
* @license React * @license React
* scheduler.production.js * scheduler.production.js
* *
@@ -28,7 +28,7 @@
* *
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/var wh;function Gy(){return wh||(wh=1,(function(i){function r(G,N){var ut=G.length;G.push(N);t:for(;0<ut;){var St=ut-1>>>1,pt=G[St];if(0<g(pt,N))G[St]=N,G[ut]=pt,ut=St;else break t}}function h(G){return G.length===0?null:G[0]}function f(G){if(G.length===0)return null;var N=G[0],ut=G.pop();if(ut!==N){G[0]=ut;t:for(var St=0,pt=G.length,ge=pt>>>1;St<ge;){var we=2*(St+1)-1,Ce=G[we],y=we+1,D=G[y];if(0>g(Ce,ut))y<pt&&0>g(D,Ce)?(G[St]=D,G[y]=ut,St=y):(G[St]=Ce,G[we]=ut,St=we);else if(y<pt&&0>g(D,ut))G[St]=D,G[y]=ut,St=y;else break t}}return N}function g(G,N){var ut=G.sortIndex-N.sortIndex;return ut!==0?ut:G.id-N.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var C=performance;i.unstable_now=function(){return C.now()}}else{var A=Date,S=A.now();i.unstable_now=function(){return A.now()-S}}var H=[],R=[],Y=1,x=null,k=3,W=!1,V=!1,I=!1,X=!1,ht=typeof setTimeout=="function"?setTimeout:null,gt=typeof clearTimeout=="function"?clearTimeout:null,xt=typeof setImmediate<"u"?setImmediate:null;function Z(G){for(var N=h(R);N!==null;){if(N.callback===null)f(R);else if(N.startTime<=G)f(R),N.sortIndex=N.expirationTime,r(H,N);else break;N=h(R)}}function Tt(G){if(I=!1,Z(G),!V)if(h(H)!==null)V=!0,w||(w=!0,wt());else{var N=h(R);N!==null&&rt(Tt,N.startTime-G)}}var w=!1,Q=-1,et=5,zt=-1;function Bt(){return X?!0:!(i.unstable_now()-zt<et)}function vt(){if(X=!1,w){var G=i.unstable_now();zt=G;var N=!0;try{t:{V=!1,I&&(I=!1,gt(Q),Q=-1),W=!0;var ut=k;try{e:{for(Z(G),x=h(H);x!==null&&!(x.expirationTime>G&&Bt());){var St=x.callback;if(typeof St=="function"){x.callback=null,k=x.priorityLevel;var pt=St(x.expirationTime<=G);if(G=i.unstable_now(),typeof pt=="function"){x.callback=pt,Z(G),N=!0;break e}x===h(H)&&f(H),Z(G)}else f(H);x=h(H)}if(x!==null)N=!0;else{var ge=h(R);ge!==null&&rt(Tt,ge.startTime-G),N=!1}}break t}finally{x=null,k=ut,W=!1}N=void 0}}finally{N?wt():w=!1}}}var wt;if(typeof xt=="function")wt=function(){xt(vt)};else if(typeof MessageChannel<"u"){var q=new MessageChannel,ot=q.port2;q.port1.onmessage=vt,wt=function(){ot.postMessage(null)}}else wt=function(){ht(vt,0)};function rt(G,N){Q=ht(function(){G(i.unstable_now())},N)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(G){G.callback=null},i.unstable_forceFrameRate=function(G){0>G||125<G?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):et=0<G?Math.floor(1e3/G):5},i.unstable_getCurrentPriorityLevel=function(){return k},i.unstable_next=function(G){switch(k){case 1:case 2:case 3:var N=3;break;default:N=k}var ut=k;k=N;try{return G()}finally{k=ut}},i.unstable_requestPaint=function(){X=!0},i.unstable_runWithPriority=function(G,N){switch(G){case 1:case 2:case 3:case 4:case 5:break;default:G=3}var ut=k;k=G;try{return N()}finally{k=ut}},i.unstable_scheduleCallback=function(G,N,ut){var St=i.unstable_now();switch(typeof ut=="object"&&ut!==null?(ut=ut.delay,ut=typeof ut=="number"&&0<ut?St+ut:St):ut=St,G){case 1:var pt=-1;break;case 2:pt=250;break;case 5:pt=1073741823;break;case 4:pt=1e4;break;default:pt=5e3}return pt=ut+pt,G={id:Y++,callback:N,priorityLevel:G,startTime:ut,expirationTime:pt,sortIndex:-1},ut>St?(G.sortIndex=ut,r(R,G),h(H)===null&&G===h(R)&&(I?(gt(Q),Q=-1):I=!0,rt(Tt,ut-St))):(G.sortIndex=pt,r(H,G),V||W||(V=!0,w||(w=!0,wt()))),G},i.unstable_shouldYield=Bt,i.unstable_wrapCallback=function(G){var N=k;return function(){var ut=k;k=N;try{return G.apply(this,arguments)}finally{k=ut}}}})(so)),so}var kh;function Qy(){return kh||(kh=1,co.exports=Gy()),co.exports}var ro={exports:{}},He={};/** */var Hh;function Gy(){return Hh||(Hh=1,(function(i){function r(G,A){var ut=G.length;G.push(A);t:for(;0<ut;){var bt=ut-1>>>1,pt=G[bt];if(0<y(pt,A))G[bt]=A,G[ut]=pt,ut=bt;else break t}}function h(G){return G.length===0?null:G[0]}function f(G){if(G.length===0)return null;var A=G[0],ut=G.pop();if(ut!==A){G[0]=ut;t:for(var bt=0,pt=G.length,Se=pt>>>1;bt<Se;){var we=2*(bt+1)-1,Ne=G[we],g=we+1,U=G[g];if(0>y(Ne,ut))g<pt&&0>y(U,Ne)?(G[bt]=U,G[g]=ut,bt=g):(G[bt]=Ne,G[we]=ut,bt=we);else if(g<pt&&0>y(U,ut))G[bt]=U,G[g]=ut,bt=g;else break t}}return A}function y(G,A){var ut=G.sortIndex-A.sortIndex;return ut!==0?ut:G.id-A.id}if(i.unstable_now=void 0,typeof performance=="object"&&typeof performance.now=="function"){var C=performance;i.unstable_now=function(){return C.now()}}else{var M=Date,S=M.now();i.unstable_now=function(){return M.now()-S}}var _=[],O=[],Y=1,x=null,H=3,L=!1,tt=!1,dt=!1,J=!1,gt=typeof setTimeout=="function"?setTimeout:null,_t=typeof clearTimeout=="function"?clearTimeout:null,Dt=typeof setImmediate<"u"?setImmediate:null;function Q(G){for(var A=h(O);A!==null;){if(A.callback===null)f(O);else if(A.startTime<=G)f(O),A.sortIndex=A.expirationTime,r(_,A);else break;A=h(O)}}function ft(G){if(dt=!1,Q(G),!tt)if(h(_)!==null)tt=!0,D||(D=!0,Nt());else{var A=h(O);A!==null&&rt(ft,A.startTime-G)}}var D=!1,X=-1,et=5,xt=-1;function Mt(){return J?!0:!(i.unstable_now()-xt<et)}function mt(){if(J=!1,D){var G=i.unstable_now();xt=G;var A=!0;try{t:{tt=!1,dt&&(dt=!1,_t(X),X=-1),L=!0;var ut=H;try{e:{for(Q(G),x=h(_);x!==null&&!(x.expirationTime>G&&Mt());){var bt=x.callback;if(typeof bt=="function"){x.callback=null,H=x.priorityLevel;var pt=bt(x.expirationTime<=G);if(G=i.unstable_now(),typeof pt=="function"){x.callback=pt,Q(G),A=!0;break e}x===h(_)&&f(_),Q(G)}else f(_);x=h(_)}if(x!==null)A=!0;else{var Se=h(O);Se!==null&&rt(ft,Se.startTime-G),A=!1}}break t}finally{x=null,H=ut,L=!1}A=void 0}}finally{A?Nt():D=!1}}}var Nt;if(typeof Dt=="function")Nt=function(){Dt(mt)};else if(typeof MessageChannel<"u"){var B=new MessageChannel,nt=B.port2;B.port1.onmessage=mt,Nt=function(){nt.postMessage(null)}}else Nt=function(){gt(mt,0)};function rt(G,A){X=gt(function(){G(i.unstable_now())},A)}i.unstable_IdlePriority=5,i.unstable_ImmediatePriority=1,i.unstable_LowPriority=4,i.unstable_NormalPriority=3,i.unstable_Profiling=null,i.unstable_UserBlockingPriority=2,i.unstable_cancelCallback=function(G){G.callback=null},i.unstable_forceFrameRate=function(G){0>G||125<G?console.error("forceFrameRate takes a positive int between 0 and 125, forcing frame rates higher than 125 fps is not supported"):et=0<G?Math.floor(1e3/G):5},i.unstable_getCurrentPriorityLevel=function(){return H},i.unstable_next=function(G){switch(H){case 1:case 2:case 3:var A=3;break;default:A=H}var ut=H;H=A;try{return G()}finally{H=ut}},i.unstable_requestPaint=function(){J=!0},i.unstable_runWithPriority=function(G,A){switch(G){case 1:case 2:case 3:case 4:case 5:break;default:G=3}var ut=H;H=G;try{return A()}finally{H=ut}},i.unstable_scheduleCallback=function(G,A,ut){var bt=i.unstable_now();switch(typeof ut=="object"&&ut!==null?(ut=ut.delay,ut=typeof ut=="number"&&0<ut?bt+ut:bt):ut=bt,G){case 1:var pt=-1;break;case 2:pt=250;break;case 5:pt=1073741823;break;case 4:pt=1e4;break;default:pt=5e3}return pt=ut+pt,G={id:Y++,callback:A,priorityLevel:G,startTime:ut,expirationTime:pt,sortIndex:-1},ut>bt?(G.sortIndex=ut,r(O,G),h(_)===null&&G===h(O)&&(dt?(_t(X),X=-1):dt=!0,rt(ft,ut-bt))):(G.sortIndex=pt,r(_,G),tt||L||(tt=!0,D||(D=!0,Nt()))),G},i.unstable_shouldYield=Mt,i.unstable_wrapCallback=function(G){var A=H;return function(){var ut=H;H=A;try{return G.apply(this,arguments)}finally{H=ut}}}})(oo)),oo}var Bh;function Qy(){return Bh||(Bh=1,ro.exports=Gy()),ro.exports}var fo={exports:{}},Ue={};/**
* @license React * @license React
* react-dom.production.js * react-dom.production.js
* *
@@ -36,7 +36,7 @@
* *
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/var Bh;function Xy(){if(Bh)return He;Bh=1;var i=yo();function r(Y){var x="https://react.dev/errors/"+Y;if(1<arguments.length){x+="?args[]="+encodeURIComponent(arguments[1]);for(var k=2;k<arguments.length;k++)x+="&args[]="+encodeURIComponent(arguments[k])}return"Minified React error #"+Y+"; visit "+x+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function h(){}var f={d:{f:h,r:function(){throw Error(r(522))},D:h,C:h,L:h,m:h,X:h,S:h,M:h},p:0,findDOMNode:null},g=Symbol.for("react.portal"),C=Symbol.for("react.recoverable"),A=Symbol.for("react.optimistic_key");function S(Y,x,k){var W=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:g,key:W==null?null:W===A?A:""+W,children:Y,containerInfo:x,implementation:k}}var H=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function R(Y,x){if(Y==="font")return"";if(typeof x=="string")return x==="use-credentials"?x:""}return He.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=f,He.browser=function(Y){return{$$typeof:C,_reason:Y}},He.createPortal=function(Y,x){var k=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!x||x.nodeType!==1&&x.nodeType!==9&&x.nodeType!==11)throw Error(r(299));return S(Y,x,null,k)},He.flushSync=function(Y){var x=H.T,k=f.p;try{if(H.T=null,f.p=2,Y)return Y()}finally{H.T=x,f.p=k,f.d.f()}},He.preconnect=function(Y,x){typeof Y=="string"&&(x?(x=x.crossOrigin,x=typeof x=="string"?x==="use-credentials"?x:"":void 0):x=null,f.d.C(Y,x))},He.prefetchDNS=function(Y){typeof Y=="string"&&f.d.D(Y)},He.preinit=function(Y,x){if(typeof Y=="string"&&x&&typeof x.as=="string"){var k=x.as,W=R(k,x.crossOrigin),V=typeof x.integrity=="string"?x.integrity:void 0,I=typeof x.fetchPriority=="string"?x.fetchPriority:void 0;k==="style"?f.d.S(Y,typeof x.precedence=="string"?x.precedence:void 0,{crossOrigin:W,integrity:V,fetchPriority:I}):k==="script"&&f.d.X(Y,{crossOrigin:W,integrity:V,fetchPriority:I,nonce:typeof x.nonce=="string"?x.nonce:void 0})}},He.preinitModule=function(Y,x){if(typeof Y=="string")if(typeof x=="object"&&x!==null){if(x.as==null||x.as==="script"){var k=R(x.as,x.crossOrigin);f.d.M(Y,{crossOrigin:k,integrity:typeof x.integrity=="string"?x.integrity:void 0,nonce:typeof x.nonce=="string"?x.nonce:void 0,fetchPriority:typeof x.fetchPriority=="string"?x.fetchPriority:void 0})}}else x==null&&f.d.M(Y)},He.preload=function(Y,x){if(typeof Y=="string"&&typeof x=="object"&&x!==null&&typeof x.as=="string"){var k=x.as,W=R(k,x.crossOrigin);f.d.L(Y,k,{crossOrigin:W,integrity:typeof x.integrity=="string"?x.integrity:void 0,nonce:typeof x.nonce=="string"?x.nonce:void 0,type:typeof x.type=="string"?x.type:void 0,fetchPriority:typeof x.fetchPriority=="string"?x.fetchPriority:void 0,referrerPolicy:typeof x.referrerPolicy=="string"?x.referrerPolicy:void 0,imageSrcSet:typeof x.imageSrcSet=="string"?x.imageSrcSet:void 0,imageSizes:typeof x.imageSizes=="string"?x.imageSizes:void 0,media:typeof x.media=="string"?x.media:void 0})}},He.preloadModule=function(Y,x){if(typeof Y=="string")if(x){var k=R(x.as,x.crossOrigin);f.d.m(Y,{as:typeof x.as=="string"&&x.as!=="script"?x.as:void 0,crossOrigin:k,integrity:typeof x.integrity=="string"?x.integrity:void 0,nonce:typeof x.nonce=="string"?x.nonce:void 0,fetchPriority:typeof x.fetchPriority=="string"?x.fetchPriority:void 0})}else f.d.m(Y)},He.requestFormReset=function(Y){f.d.r(Y)},He.unstable_batchedUpdates=function(Y,x){return Y(x)},He.useFormState=function(Y,x,k){return H.H.useFormState(Y,x,k)},He.useFormStatus=function(){return H.H.useHostTransitionStatus()},He.version="19.3.1",He}var qh;function Ly(){if(qh)return ro.exports;qh=1;function i(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(r){console.error(r)}}return i(),ro.exports=Xy(),ro.exports}/** */var qh;function Xy(){if(qh)return Ue;qh=1;var i=go();function r(Y){var x="https://react.dev/errors/"+Y;if(1<arguments.length){x+="?args[]="+encodeURIComponent(arguments[1]);for(var H=2;H<arguments.length;H++)x+="&args[]="+encodeURIComponent(arguments[H])}return"Minified React error #"+Y+"; visit "+x+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function h(){}var f={d:{f:h,r:function(){throw Error(r(522))},D:h,C:h,L:h,m:h,X:h,S:h,M:h},p:0,findDOMNode:null},y=Symbol.for("react.portal"),C=Symbol.for("react.recoverable"),M=Symbol.for("react.optimistic_key");function S(Y,x,H){var L=3<arguments.length&&arguments[3]!==void 0?arguments[3]:null;return{$$typeof:y,key:L==null?null:L===M?M:""+L,children:Y,containerInfo:x,implementation:H}}var _=i.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE;function O(Y,x){if(Y==="font")return"";if(typeof x=="string")return x==="use-credentials"?x:""}return Ue.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE=f,Ue.browser=function(Y){return{$$typeof:C,_reason:Y}},Ue.createPortal=function(Y,x){var H=2<arguments.length&&arguments[2]!==void 0?arguments[2]:null;if(!x||x.nodeType!==1&&x.nodeType!==9&&x.nodeType!==11)throw Error(r(299));return S(Y,x,null,H)},Ue.flushSync=function(Y){var x=_.T,H=f.p;try{if(_.T=null,f.p=2,Y)return Y()}finally{_.T=x,f.p=H,f.d.f()}},Ue.preconnect=function(Y,x){typeof Y=="string"&&(x?(x=x.crossOrigin,x=typeof x=="string"?x==="use-credentials"?x:"":void 0):x=null,f.d.C(Y,x))},Ue.prefetchDNS=function(Y){typeof Y=="string"&&f.d.D(Y)},Ue.preinit=function(Y,x){if(typeof Y=="string"&&x&&typeof x.as=="string"){var H=x.as,L=O(H,x.crossOrigin),tt=typeof x.integrity=="string"?x.integrity:void 0,dt=typeof x.fetchPriority=="string"?x.fetchPriority:void 0;H==="style"?f.d.S(Y,typeof x.precedence=="string"?x.precedence:void 0,{crossOrigin:L,integrity:tt,fetchPriority:dt}):H==="script"&&f.d.X(Y,{crossOrigin:L,integrity:tt,fetchPriority:dt,nonce:typeof x.nonce=="string"?x.nonce:void 0})}},Ue.preinitModule=function(Y,x){if(typeof Y=="string")if(typeof x=="object"&&x!==null){if(x.as==null||x.as==="script"){var H=O(x.as,x.crossOrigin);f.d.M(Y,{crossOrigin:H,integrity:typeof x.integrity=="string"?x.integrity:void 0,nonce:typeof x.nonce=="string"?x.nonce:void 0,fetchPriority:typeof x.fetchPriority=="string"?x.fetchPriority:void 0})}}else x==null&&f.d.M(Y)},Ue.preload=function(Y,x){if(typeof Y=="string"&&typeof x=="object"&&x!==null&&typeof x.as=="string"){var H=x.as,L=O(H,x.crossOrigin);f.d.L(Y,H,{crossOrigin:L,integrity:typeof x.integrity=="string"?x.integrity:void 0,nonce:typeof x.nonce=="string"?x.nonce:void 0,type:typeof x.type=="string"?x.type:void 0,fetchPriority:typeof x.fetchPriority=="string"?x.fetchPriority:void 0,referrerPolicy:typeof x.referrerPolicy=="string"?x.referrerPolicy:void 0,imageSrcSet:typeof x.imageSrcSet=="string"?x.imageSrcSet:void 0,imageSizes:typeof x.imageSizes=="string"?x.imageSizes:void 0,media:typeof x.media=="string"?x.media:void 0})}},Ue.preloadModule=function(Y,x){if(typeof Y=="string")if(x){var H=O(x.as,x.crossOrigin);f.d.m(Y,{as:typeof x.as=="string"&&x.as!=="script"?x.as:void 0,crossOrigin:H,integrity:typeof x.integrity=="string"?x.integrity:void 0,nonce:typeof x.nonce=="string"?x.nonce:void 0,fetchPriority:typeof x.fetchPriority=="string"?x.fetchPriority:void 0})}else f.d.m(Y)},Ue.requestFormReset=function(Y){f.d.r(Y)},Ue.unstable_batchedUpdates=function(Y,x){return Y(x)},Ue.useFormState=function(Y,x,H){return _.H.useFormState(Y,x,H)},Ue.useFormStatus=function(){return _.H.useHostTransitionStatus()},Ue.version="19.3.1",Ue}var Yh;function Ly(){if(Yh)return fo.exports;Yh=1;function i(){if(!(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(r){console.error(r)}}return i(),fo.exports=Xy(),fo.exports}/**
* @license React * @license React
* react-dom-client.production.js * react-dom-client.production.js
* *
@@ -44,38 +44,38 @@
* *
* This source code is licensed under the MIT license found in the * This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree. * LICENSE file in the root directory of this source tree.
*/var Yh;function Vy(){if(Yh)return ii;Yh=1;var i=Qy(),r=yo(),h=Ly();function f(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var l=2;l<arguments.length;l++)e+="&args[]="+encodeURIComponent(arguments[l])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function g(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function C(t){for(var e=t,l=e;l&&!l.alternate;)e=l,(e.flags&4098)!==0&&(t=e.return),l=e.return;for(;e.return;)e=e.return;return e.tag===3?t:null}function A(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function S(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function H(t){if(C(t)!==t)throw Error(f(188))}function R(t){var e=t.alternate;if(!e){if(e=C(t),e===null)throw Error(f(188));return e!==t?null:t}for(var l=t,a=e;;){var n=l.return;if(n===null)break;var u=n.alternate;if(u===null){if(a=n.return,a!==null){l=a;continue}break}if(n.child===u.child){for(u=n.child;u;){if(u===l)return H(n),t;if(u===a)return H(n),e;u=u.sibling}throw Error(f(188))}if(l.return!==a.return)l=n,a=u;else{for(var s=!1,o=n.child;o;){if(o===l){s=!0,l=n,a=u;break}if(o===a){s=!0,a=n,l=u;break}o=o.sibling}if(!s){for(o=u.child;o;){if(o===l){s=!0,l=u,a=n;break}if(o===a){s=!0,a=u,l=n;break}o=o.sibling}if(!s)throw Error(f(189))}}if(l.alternate!==a)throw Error(f(190))}if(l.tag!==3)throw Error(f(188));return l.stateNode.current===l?t:e}function Y(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=Y(t),e!==null)return e;t=t.sibling}return null}function x(t,e,l,a,n,u){for(;t!==null;){if((t.tag===5||t.tag===27||t.tag===6)&&l(t,a,n,u)||(t.tag!==22||t.memoizedState===null)&&(e||t.tag!==5&&t.tag!==27)&&x(t.child,e,l,a,n,u))return!0;t=t.sibling}return!1}function k(t){for(t=t.return;t!==null;){if(t.tag===3||t.tag===5||t.tag===27)return t;t=t.return}return null}function W(t){var e=!1;for(t=t.return;t!==null&&(t.tag===4&&(e=!0),!(t.tag===3||t.tag===5||t.tag===27));)t=t.return;return e}function V(t){var e=[null,null],l=k(t);return l===null||I(e,t,l.child,{foundSelf:!1}),e}function I(t,e,l,a){for(;l!==null;){if(l===e)a.foundSelf=!0;else if(l.tag===5||l.tag===27||l.tag===6){if(a.foundSelf)return t[1]=l,!0;t[0]=l}else if((l.tag!==22||l.memoizedState===null)&&I(t,e,l.child,a))return!0;l=l.sibling}return!1}function X(t){switch(t.tag){case 5:case 27:case 6:return t.stateNode;case 3:return t.stateNode.containerInfo;default:throw Error(f(559))}}var ht=null,gt=null;function xt(t,e,l){return t===l?!0:t===e?(ht=t,!0):!1}function Z(t,e,l){return t===l?(gt=t,!1):t===e?(gt!==null&&(ht=t),!0):!1}function Tt(t){if(t===null)return null;do t=t===null?null:t.return;while(t&&t.tag!==5&&t.tag!==27&&t.tag!==3);return t||null}function w(t,e,l){for(var a=0,n=t;n;n=l(n))a++;n=0;for(var u=e;u;u=l(u))n++;for(;0<a-n;)t=l(t),a--;for(;0<n-a;)e=l(e),n--;for(;a--;){if(t===e||e!==null&&t===e.alternate)return t;t=l(t),e=l(e)}return null}var Q=Object.assign,et=Symbol.for("react.element"),zt=Symbol.for("react.transitional.element"),Bt=Symbol.for("react.portal"),vt=Symbol.for("react.fragment"),wt=Symbol.for("react.strict_mode"),q=Symbol.for("react.profiler"),ot=Symbol.for("react.consumer"),rt=Symbol.for("react.context"),G=Symbol.for("react.forward_ref"),N=Symbol.for("react.suspense"),ut=Symbol.for("react.suspense_list"),St=Symbol.for("react.memo"),pt=Symbol.for("react.lazy"),ge=Symbol.for("react.activity"),we=Symbol.for("react.legacy_hidden"),Ce=Symbol.for("react.memo_cache_sentinel"),y=Symbol.for("react.view_transition"),D=Symbol.for("react.recoverable"),ct=Symbol.iterator;function it(t){return t===null||typeof t!="object"?null:(t=ct&&t[ct]||t["@@iterator"],typeof t=="function"?t:null)}var Nt=Symbol.for("react.client.reference");function _t(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===Nt?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case vt:return"Fragment";case q:return"Profiler";case wt:return"StrictMode";case N:return"Suspense";case ut:return"SuspenseList";case ge:return"Activity";case y:return"ViewTransition"}if(typeof t=="object")switch(t.$$typeof){case Bt:return"Portal";case rt:return t.displayName||"Context";case ot:return(t._context.displayName||"Context")+".Consumer";case G:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case St:return e=t.displayName||null,e!==null?e:_t(t.type)||"Memo";case pt:e=t._payload,t=t._init;try{return _t(t(e))}catch{}}return null}var Rt=Array.isArray,P=r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,dt=h.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ce={pending:!1,data:null,method:null,action:nLine truncated */var Gh;function Vy(){if(Gh)return ri;Gh=1;var i=Qy(),r=go(),h=Ly();function f(t){var e="https://react.dev/errors/"+t;if(1<arguments.length){e+="?args[]="+encodeURIComponent(arguments[1]);for(var l=2;l<arguments.length;l++)e+="&args[]="+encodeURIComponent(arguments[l])}return"Minified React error #"+t+"; visit "+e+" for the full message or use the non-minified dev environment for full errors and additional helpful warnings."}function y(t){return!(!t||t.nodeType!==1&&t.nodeType!==9&&t.nodeType!==11)}function C(t){for(var e=t,l=e;l&&!l.alternate;)e=l,(e.flags&4098)!==0&&(t=e.return),l=e.return;for(;e.return;)e=e.return;return e.tag===3?t:null}function M(t){if(t.tag===13){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function S(t){if(t.tag===31){var e=t.memoizedState;if(e===null&&(t=t.alternate,t!==null&&(e=t.memoizedState)),e!==null)return e.dehydrated}return null}function _(t){if(C(t)!==t)throw Error(f(188))}function O(t){var e=t.alternate;if(!e){if(e=C(t),e===null)throw Error(f(188));return e!==t?null:t}for(var l=t,a=e;;){var n=l.return;if(n===null)break;var u=n.alternate;if(u===null){if(a=n.return,a!==null){l=a;continue}break}if(n.child===u.child){for(u=n.child;u;){if(u===l)return _(n),t;if(u===a)return _(n),e;u=u.sibling}throw Error(f(188))}if(l.return!==a.return)l=n,a=u;else{for(var s=!1,o=n.child;o;){if(o===l){s=!0,l=n,a=u;break}if(o===a){s=!0,a=n,l=u;break}o=o.sibling}if(!s){for(o=u.child;o;){if(o===l){s=!0,l=u,a=n;break}if(o===a){s=!0,a=u,l=n;break}o=o.sibling}if(!s)throw Error(f(189))}}if(l.alternate!==a)throw Error(f(190))}if(l.tag!==3)throw Error(f(188));return l.stateNode.current===l?t:e}function Y(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t;for(t=t.child;t!==null;){if(e=Y(t),e!==null)return e;t=t.sibling}return null}function x(t,e,l,a,n,u){for(;t!==null;){if((t.tag===5||t.tag===27||t.tag===6)&&l(t,a,n,u)||(t.tag!==22||t.memoizedState===null)&&(e||t.tag!==5&&t.tag!==27)&&x(t.child,e,l,a,n,u))return!0;t=t.sibling}return!1}function H(t){for(t=t.return;t!==null;){if(t.tag===3||t.tag===5||t.tag===27)return t;t=t.return}return null}function L(t){var e=!1;for(t=t.return;t!==null&&(t.tag===4&&(e=!0),!(t.tag===3||t.tag===5||t.tag===27));)t=t.return;return e}function tt(t){var e=[null,null],l=H(t);return l===null||dt(e,t,l.child,{foundSelf:!1}),e}function dt(t,e,l,a){for(;l!==null;){if(l===e)a.foundSelf=!0;else if(l.tag===5||l.tag===27||l.tag===6){if(a.foundSelf)return t[1]=l,!0;t[0]=l}else if((l.tag!==22||l.memoizedState===null)&&dt(t,e,l.child,a))return!0;l=l.sibling}return!1}function J(t){switch(t.tag){case 5:case 27:case 6:return t.stateNode;case 3:return t.stateNode.containerInfo;default:throw Error(f(559))}}var gt=null,_t=null;function Dt(t,e,l){return t===l?!0:t===e?(gt=t,!0):!1}function Q(t,e,l){return t===l?(_t=t,!1):t===e?(_t!==null&&(gt=t),!0):!1}function ft(t){if(t===null)return null;do t=t===null?null:t.return;while(t&&t.tag!==5&&t.tag!==27&&t.tag!==3);return t||null}function D(t,e,l){for(var a=0,n=t;n;n=l(n))a++;n=0;for(var u=e;u;u=l(u))n++;for(;0<a-n;)t=l(t),a--;for(;0<n-a;)e=l(e),n--;for(;a--;){if(t===e||e!==null&&t===e.alternate)return t;t=l(t),e=l(e)}return null}var X=Object.assign,et=Symbol.for("react.element"),xt=Symbol.for("react.transitional.element"),Mt=Symbol.for("react.portal"),mt=Symbol.for("react.fragment"),Nt=Symbol.for("react.strict_mode"),B=Symbol.for("react.profiler"),nt=Symbol.for("react.consumer"),rt=Symbol.for("react.context"),G=Symbol.for("react.forward_ref"),A=Symbol.for("react.suspense"),ut=Symbol.for("react.suspense_list"),bt=Symbol.for("react.memo"),pt=Symbol.for("react.lazy"),Se=Symbol.for("react.activity"),we=Symbol.for("react.legacy_hidden"),Ne=Symbol.for("react.memo_cache_sentinel"),g=Symbol.for("react.view_transition"),U=Symbol.for("react.recoverable"),ct=Symbol.iterator;function it(t){return t===null||typeof t!="object"?null:(t=ct&&t[ct]||t["@@iterator"],typeof t=="function"?t:null)}var Ot=Symbol.for("react.client.reference");function Et(t){if(t==null)return null;if(typeof t=="function")return t.$$typeof===Ot?null:t.displayName||t.name||null;if(typeof t=="string")return t;switch(t){case mt:return"Fragment";case B:return"Profiler";case Nt:return"StrictMode";case A:return"Suspense";case ut:return"SuspenseList";case Se:return"Activity";case g:return"ViewTransition"}if(typeof t=="object")switch(t.$$typeof){case Mt:return"Portal";case rt:return t.displayName||"Context";case nt:return(t._context.displayName||"Context")+".Consumer";case G:var e=t.render;return t=t.displayName,t||(t=e.displayName||e.name||"",t=t!==""?"ForwardRef("+t+")":"ForwardRef"),t;case bt:return e=t.displayName||null,e!==null?e:Et(t.type)||"Memo";case pt:e=t._payload,t=t._init;try{return Et(t(e))}catch{}}return null}var kt=Array.isArray,P=r.__CLIENT_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,ht=h.__DOM_INTERNALS_DO_NOT_USE_OR_WARN_USERS_THEY_CANNOT_UPGRADE,se={pending:!1,data:null,method:null,actiLine truncated
at`)?" (<anonymous>)":-1<l.stack.indexOf("@")?"@unknown:0:0":""}return` at`)?" (<anonymous>)":-1<l.stack.indexOf("@")?"@unknown:0:0":""}return`
`+Be+t+se}var rl=!1;function Fl(t,e){if(!t||rl)return"";rl=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(e){var z=function(){throw Error()};if(Object.defineProperty(z.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(z,[])}catch(L){var p=L}Reflect.construct(t,[],z)}else{try{z.call()}catch(L){p=L}z=!1;try{var _=Object.getOwnPropertyDescriptor(t.prototype,"props");Object.defineProperty(t.prototype,"props",{configurable:!0,set:function(){throw Error()}}),z=!0,new t}finally{z&&(_!==void 0?Object.defineProperty(t.prototype,"props",_):delete t.prototype.props)}}}else{try{throw Error()}catch(L){p=L}(z=t())&&typeof z.catch=="function"&&z.catch(function(){})}}catch(L){if(L&&p&&typeof L.stack=="string")return[L.stack,p.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var n=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");n&&n.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var u=a.DetermineComponentFrameRoot(),s=u[0],o=u[1];if(s&&o){var m=s.split(` `+He+t+re}var fl=!1;function $l(t,e){if(!t||fl)return"";fl=!0;var l=Error.prepareStackTrace;Error.prepareStackTrace=void 0;try{var a={DetermineComponentFrameRoot:function(){try{if(e){var R=function(){throw Error()};if(Object.defineProperty(R.prototype,"props",{set:function(){throw Error()}}),typeof Reflect=="object"&&Reflect.construct){try{Reflect.construct(R,[])}catch(V){var p=V}Reflect.construct(t,[],R)}else{try{R.call()}catch(V){p=V}R=!1;try{var E=Object.getOwnPropertyDescriptor(t.prototype,"props");Object.defineProperty(t.prototype,"props",{configurable:!0,set:function(){throw Error()}}),R=!0,new t}finally{R&&(E!==void 0?Object.defineProperty(t.prototype,"props",E):delete t.prototype.props)}}}else{try{throw Error()}catch(V){p=V}(R=t())&&typeof R.catch=="function"&&R.catch(function(){})}}catch(V){if(V&&p&&typeof V.stack=="string")return[V.stack,p.stack]}return[null,null]}};a.DetermineComponentFrameRoot.displayName="DetermineComponentFrameRoot";var n=Object.getOwnPropertyDescriptor(a.DetermineComponentFrameRoot,"name");n&&n.configurable&&Object.defineProperty(a.DetermineComponentFrameRoot,"name",{value:"DetermineComponentFrameRoot"});var u=a.DetermineComponentFrameRoot(),s=u[0],o=u[1];if(s&&o){var m=s.split(`
`),j=o.split(` `),j=o.split(`
`);for(n=a=0;a<m.length&&!m[a].includes("DetermineComponentFrameRoot");)a++;for(;n<j.length&&!j[n].includes("DetermineComponentFrameRoot");)n++;if(a===m.length||n===j.length)for(a=m.length-1,n=j.length-1;1<=a&&0<=n&&m[a]!==j[n];)n--;for(;1<=a&&0<=n;a--,n--)if(m[a]!==j[n]){if(a!==1||n!==1)do if(a--,n--,0>n||m[a]!==j[n]){var E=` `);for(n=a=0;a<m.length&&!m[a].includes("DetermineComponentFrameRoot");)a++;for(;n<j.length&&!j[n].includes("DetermineComponentFrameRoot");)n++;if(a===m.length||n===j.length)for(a=m.length-1,n=j.length-1;1<=a&&0<=n&&m[a]!==j[n];)n--;for(;1<=a&&0<=n;a--,n--)if(m[a]!==j[n]){if(a!==1||n!==1)do if(a--,n--,0>n||m[a]!==j[n]){var N=`
`+m[a].replace(" at new "," at ");return t.displayName&&E.includes("<anonymous>")&&(E=E.replace("<anonymous>",t.displayName)),E}while(1<=a&&0<=n);break}}}finally{rl=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?re(l):""}function ma(t,e){switch(t.tag){case 26:case 27:case 5:return re(t.type);case 16:return re("Lazy");case 13:return t.child!==e&&e!==null?re("Suspense Fallback"):re("Suspense");case 19:return re("SuspenseList");case 0:case 15:return Fl(t.type,!1);case 11:return Fl(t.type.render,!1);case 1:return Fl(t.type,!0);case 31:return re("Activity");case 30:return re("ViewTransition");default:return""}}function Dl(t){try{var e="",l=null;do e+=ma(t,l),l=t,t=t.return;while(t);return e}catch(a){return` `+m[a].replace(" at new "," at ");return t.displayName&&N.includes("<anonymous>")&&(N=N.replace("<anonymous>",t.displayName)),N}while(1<=a&&0<=n);break}}}finally{fl=!1,Error.prepareStackTrace=l}return(l=t?t.displayName||t.name:"")?oe(l):""}function ya(t,e){switch(t.tag){case 26:case 27:case 5:return oe(t.type);case 16:return oe("Lazy");case 13:return t.child!==e&&e!==null?oe("Suspense Fallback"):oe("Suspense");case 19:return oe("SuspenseList");case 0:case 15:return $l(t.type,!1);case 11:return $l(t.type.render,!1);case 1:return $l(t.type,!0);case 31:return oe("Activity");case 30:return oe("ViewTransition");default:return""}}function kl(t){try{var e="",l=null;do e+=ya(t,l),l=t,t=t.return;while(t);return e}catch(a){return`
Error generating stack: `+a.message+` Error generating stack: `+a.message+`
`+a.stack}}var te=Object.prototype.hasOwnProperty,Pe=i.unstable_scheduleCallback,qe=i.unstable_cancelCallback,_l=i.unstable_shouldYield,ol=i.unstable_requestPaint,Wt=i.unstable_now,Ul=i.unstable_getCurrentPriorityLevel,mn=i.unstable_ImmediatePriority,Qa=i.unstable_UserBlockingPriority,Se=i.unstable_NormalPriority,Xa=i.unstable_LowPriority,ha=i.unstable_IdlePriority,su=i.log,ru=i.unstable_setDisableYieldValue,Wl=null,Ae=null;function fl(t){if(typeof su=="function"&&ru(t),Ae&&typeof Ae.setStrictMode=="function")try{Ae.setStrictMode(Wl,t)}catch{}}var Oe=Math.clz32?Math.clz32:du,ou=Math.log,fu=Math.LN2;function du(t){return t>>>=0,t===0?32:31-(ou(t)/fu|0)|0}var B=256,U=262144,$=4194304;function at(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&-t;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function O(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,s=t.pingedLanes;t=t.warmLanes;var o=a&134217727;return o!==0?(a=o&~u,a!==0?n=at(a):(s&=o,s!==0?n=at(s):l||(l=o&~t,l!==0&&(n=at(l))))):(o=a&~u,o!==0?n=at(o):s!==0?n=at(s):l||(l=a&~t,l!==0&&(n=at(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function J(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function nt(t,e){(e&8)!==0&&(e|=e&32);var l=t.entangledLanes;if(l!==0)for(t=t.entanglements,l&=e;0<l;){var a=31-Oe(l),n=1<<a;e|=t[a],l&=~n}return e}function qt(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function Dt(){var t=$;return $<<=1,($&62914560)===0&&($=4194304),t}function xe(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function be(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function Dc(t,e,l,a,n,u){var s=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var o=t.entanglements,m=t.expirationTimes,j=t.hiddenUpdates;for(l=s&~l;0<l;){var E=31-Oe(l),z=1<<E;o[E]=0,m[E]=-1;var p=j[E];if(p!==null)for(j[E]=null,E=0;E<p.length;E++){var _=p[E];_!==null&&(_.lane&=-536870913)}l&=~z}a!==0&&ci(t,a,0),u!==0&&n===0&&t.tag!==0&&(t.suspendedLanes|=u&~(s&~e))}function ci(t,e,l){t.pendingLanes|=e,t.suspendedLanes&=~e;var a=31-Oe(e);t.entangledLanes|=e,t.entanglements[a]=t.entanglements[a]|1073741824|l&261930}function hn(t,e){var l=t.entangledLanes|=e;for(t=t.entanglements;l;){var a=31-Oe(l),n=1<<a;n&e|t[a]&e&&(t[a]|=e),l&=~n}}function $l(t,e){var l=e&-e;return l=(l&42)!==0?1:Hl(l),(l&(t.suspendedLanes|e))!==0?0:l}function Hl(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function mu(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function hu(){var t=dt.p;return t!==0?t:(t=window.event,t===void 0?32:Eh(t.type))}function Ze(t,e){var l=dt.p;try{return dt.p=t,e()}finally{dt.p=l}}var ae=Math.random().toString(36).slice(2),ve="__reactFiber$"+ae,Ke="__reactProps$"+ae,vn="__reactContainer$"+ae,So="__reactEvents$"+ae,Ph="__reactListeners$"+ae,tv="__reactHandles$"+ae,xo="__reactResources$"+ae,vu="__reactMarker$"+ae,si="__reactLoad$"+ae;function ri(t){delete t[ve],delete t[Ke],delete t[Ph],delete t[tv]}function La(t){var e;if(e=t[ve])return e;for(var l=t.parentNode;l;){if(e=l[vn]||l[ve]){if(l=e.alternate,e.child!==null||l!==null&&l.child!==null)for(t=uh(t);t!==null;){if(l=t[ve])return l;t=uh(t)}return e}t=l,l=t.parentNode}return null}function yn(t){if(t=t[ve]||t[vn]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function yu(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(f(33))}function gn(t){var e=t[xo];return e||(e=t[xo]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function je(t){t[vu]=!0}function bo(t){t[si]=void 0}var jo=new Set,To={};function Va(t,e){pn(t,e),pn(t+"Capture",e)}function pn(t,Line truncated `+a.stack}}var te=Object.prototype.hasOwnProperty,tl=i.unstable_scheduleCallback,Be=i.unstable_cancelCallback,Nl=i.unstable_shouldYield,dl=i.unstable_requestPaint,$t=i.unstable_now,Hl=i.unstable_getCurrentPriorityLevel,vn=i.unstable_ImmediatePriority,Va=i.unstable_UserBlockingPriority,be=i.unstable_NormalPriority,Il=i.unstable_LowPriority,ga=i.unstable_IdlePriority,fu=i.log,du=i.unstable_setDisableYieldValue,Pl=null,Ce=null;function ml(t){if(typeof fu=="function"&&du(t),Ce&&typeof Ce.setStrictMode=="function")try{Ce.setStrictMode(Pl,t)}catch{}}var Ae=Math.clz32?Math.clz32:vu,mu=Math.log,hu=Math.LN2;function vu(t){return t>>>=0,t===0?32:31-(mu(t)/hu|0)|0}var q=256,k=262144,I=4194304;function at(t){var e=t&42;if(e!==0)return e;switch(t&-t){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:return 64;case 128:return 128;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:return t&-t;case 262144:case 524288:case 1048576:case 2097152:return t&3932160;case 4194304:case 8388608:case 16777216:case 33554432:return t&62914560;case 67108864:return 67108864;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 0;default:return t}}function qt(t,e,l){var a=t.pendingLanes;if(a===0)return 0;var n=0,u=t.suspendedLanes,s=t.pingedLanes;t=t.warmLanes;var o=a&134217727;return o!==0?(a=o&~u,a!==0?n=at(a):(s&=o,s!==0?n=at(s):l||(l=o&~t,l!==0&&(n=at(l))))):(o=a&~u,o!==0?n=at(o):s!==0?n=at(s):l||(l=a&~t,l!==0&&(n=at(l)))),n===0?0:e!==0&&e!==n&&(e&u)===0&&(u=n&-n,l=e&-e,u>=l||u===32&&(l&4194048)!==0)?e:n}function hl(t,e){return(t.pendingLanes&~(t.suspendedLanes&~t.pingedLanes)&e)===0}function w(t,e){(e&8)!==0&&(e|=e&32);var l=t.entangledLanes;if(l!==0)for(t=t.entanglements,l&=e;0<l;){var a=31-Ae(l),n=1<<a;e|=t[a],l&=~n}return e}function Z(t,e){switch(t){case 1:case 2:case 4:case 8:case 64:return e+250;case 16:case 32:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e+5e3;case 4194304:case 8388608:case 16777216:case 33554432:return-1;case 67108864:case 134217728:case 268435456:case 536870912:case 1073741824:return-1;default:return-1}}function K(){var t=I;return I<<=1,(I&62914560)===0&&(I=4194304),t}function Yt(t){for(var e=[],l=0;31>l;l++)e.push(t);return e}function St(t,e){t.pendingLanes|=e,e!==268435456&&(t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0)}function qe(t,e,l,a,n,u){var s=t.pendingLanes;t.pendingLanes=l,t.suspendedLanes=0,t.pingedLanes=0,t.warmLanes=0,t.expiredLanes&=l,t.entangledLanes&=l,t.errorRecoveryDisabledLanes&=l,t.shellSuspendCounter=0;var o=t.entanglements,m=t.expirationTimes,j=t.hiddenUpdates;for(l=s&~l;0<l;){var N=31-Ae(l),R=1<<N;o[N]=0,m[N]=-1;var p=j[N];if(p!==null)for(j[N]=null,N=0;N<p.length;N++){var E=p[N];E!==null&&(E.lane&=-536870913)}l&=~R}a!==0&&Cl(t,a,0),u!==0&&n===0&&t.tag!==0&&(t.suspendedLanes|=u&~(s&~e))}function Cl(t,e,l){t.pendingLanes|=e,t.suspendedLanes&=~e;var a=31-Ae(e);t.entangledLanes|=e,t.entanglements[a]=t.entanglements[a]|1073741824|l&261930}function yn(t,e){var l=t.entangledLanes|=e;for(t=t.entanglements;l;){var a=31-Ae(l),n=1<<a;n&e|t[a]&e&&(t[a]|=e),l&=~n}}function ta(t,e){var l=e&-e;return l=(l&42)!==0?1:Bl(l),(l&(t.suspendedLanes|e))!==0?0:l}function Bl(t){switch(t){case 2:t=1;break;case 8:t=4;break;case 32:t=16;break;case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:case 4194304:case 8388608:case 16777216:case 33554432:t=128;break;case 268435456:t=134217728;break;default:t=0}return t}function yu(t){return t&=-t,2<t?8<t?(t&134217727)!==0?32:268435456:8:2}function gu(){var t=ht.p;return t!==0?t:(t=window.event,t===void 0?32:Nh(t.type))}function Ke(t,e){var l=ht.p;try{return ht.p=t,e()}finally{ht.p=l}}var ne=Math.random().toString(36).slice(2),ye="__reactFiber$"+ne,Je="__reactProps$"+ne,gn="__reactContainer$"+ne,xo="__reactEvents$"+ne,Ph="__reactListeners$"+ne,tv="__reactHandles$"+ne,bo="__reactResources$"+ne,pu="__reactMarker$"+ne,fi="__reactLoad$"+ne;function di(t){delete t[ye],delete t[Je],delete t[Ph],delete t[tv]}function Za(t){var e;if(e=t[ye])return e;for(var l=t.parentNode;l;){if(e=l[gn]||l[ye]){if(l=e.alternate,e.child!==null||l!==null&&l.child!==null)for(t=ih(t);t!==null;){if(l=t[ye])return l;t=ih(t)}return e}t=l,l=t.parentNode}return null}function pn(t){if(t=t[ye]||t[gn]){var e=t.tag;if(e===5||e===6||e===13||e===31||e===26||e===27||e===3)return t}return null}function Su(t){var e=t.tag;if(e===5||e===26||e===27||e===6)return t.stateNode;throw Error(f(33))}function Sn(t){var e=t[bo];return e||(e=t[bo]={hoistableStyles:new Map,hoistableScripts:new Map}),e}function je(t){t[pu]=!0}function jo(t){t[fi]=void 0}var To=new Set,_o={};function Ka(t,e){xn(t,e),xn(t+"Capture",e)}function xn(t,eLine truncated
`).replace(G0,"")}function qm(t,e){return e=Bm(e),Bm(t)===e}function Vt(t,e,l,a,n,u){switch(l){case"children":if(typeof a=="string")e==="body"||e==="textarea"&&a===""||xn(t,a);else if(typeof a=="number"||typeof a=="bigint")e!=="body"&&xn(t,""+a);else return;break;case"className":fi(t,"class",a);break;case"tabIndex":fi(t,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":fi(t,l,a);break;case"style":Do(t,a,u);return;case"data":if(e!=="object"){fi(t,"data",a);break}case"src":case"href":if(a===""&&(e!=="a"||l!=="href")){t.removeAttribute(l);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=di(a),t.setAttribute(l,a);break;case"action":case"formAction":if(typeof a=="function"){t.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof u=="function"&&(l==="formAction"?(e!=="input"&&Vt(t,e,"name",n.name,n,null),Vt(t,e,"formEncType",n.formEncType,n,null),Vt(t,e,"formMethod",n.formMethod,n,null),Vt(t,e,"formTarget",n.formTarget,n,null)):(Vt(t,e,"encType",n.encType,n,null),Vt(t,e,"method",n.method,n,null),Vt(t,e,"target",n.target,n,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=di(a),t.setAttribute(l,a);break;case"onClick":a!=null&&(t.onclick=wl);return;case"onScroll":a!=null&&At("scroll",t);return;case"onScrollEnd":a!=null&&At("scrollend",t);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"multiple":t.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":t.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){t.removeAttribute("xlink:href");break}l=di(a),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,""):t.removeAttribute(l);break;case"capture":case"download":a===!0?t.setAttribute(l,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?t.setAttribute(l,a):t.removeAttribute(l);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?t.removeAttribute(l):t.setAttribute(l,a);break;case"popover":At("beforetoggle",t),At("toggle",t),oi(t,"popover",a);break;case"xlinkActuate":Il(t,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":Il(t,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":Il(t,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":Il(t,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":Il(t,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":Il(t,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":Il(t,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":Il(t,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":Il(t,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":oi(t,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")l=iv.get(l)||l,oi(t,l,a);else return}kt=!0}function Hr(t,e,l,a,n,u){switch(l){case"style":Do(t,a,u);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"children":if(typeof a=="string")xn(t,a);else if(typeof a=="number"||typeof a=="bigint")xn(t,""+a);else return;break;case"onScroll":a!=null&&At("scroll",t);return;case"onScrollEnd":a!=null&&At("scroLine truncated `).replace(G0,"")}function Ym(t,e){return e=qm(e),qm(t)===e}function Kt(t,e,l,a,n,u){switch(l){case"children":if(typeof a=="string")e==="body"||e==="textarea"&&a===""||jn(t,a);else if(typeof a=="number"||typeof a=="bigint")e!=="body"&&jn(t,""+a);else return;break;case"className":hi(t,"class",a);break;case"tabIndex":hi(t,"tabindex",a);break;case"dir":case"role":case"viewBox":case"width":case"height":hi(t,l,a);break;case"style":Uo(t,a,u);return;case"data":if(e!=="object"){hi(t,"data",a);break}case"src":case"href":if(a===""&&(e!=="a"||l!=="href")){t.removeAttribute(l);break}if(a==null||typeof a=="function"||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=vi(a),t.setAttribute(l,a);break;case"action":case"formAction":if(typeof a=="function"){t.setAttribute(l,"javascript:throw new Error('A React form was unexpectedly submitted. If you called form.submit() manually, consider using form.requestSubmit() instead. If you\\'re trying to use event.stopPropagation() in a submit event handler, consider also calling event.preventDefault().')");break}else typeof u=="function"&&(l==="formAction"?(e!=="input"&&Kt(t,e,"name",n.name,n,null),Kt(t,e,"formEncType",n.formEncType,n,null),Kt(t,e,"formMethod",n.formMethod,n,null),Kt(t,e,"formTarget",n.formTarget,n,null)):(Kt(t,e,"encType",n.encType,n,null),Kt(t,e,"method",n.method,n,null),Kt(t,e,"target",n.target,n,null)));if(a==null||typeof a=="symbol"||typeof a=="boolean"){t.removeAttribute(l);break}a=vi(a),t.setAttribute(l,a);break;case"onClick":a!=null&&(t.onclick=ql);return;case"onScroll":a!=null&&Rt("scroll",t);return;case"onScrollEnd":a!=null&&Rt("scrollend",t);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"multiple":t.multiple=a&&typeof a!="function"&&typeof a!="symbol";break;case"muted":t.muted=a&&typeof a!="function"&&typeof a!="symbol";break;case"suppressContentEditableWarning":case"suppressHydrationWarning":case"defaultValue":case"defaultChecked":case"innerHTML":case"ref":break;case"autoFocus":break;case"xlinkHref":if(a==null||typeof a=="function"||typeof a=="boolean"||typeof a=="symbol"){t.removeAttribute("xlink:href");break}l=vi(a),t.setAttributeNS("http://www.w3.org/1999/xlink","xlink:href",l);break;case"contentEditable":case"spellCheck":case"draggable":case"value":case"autoReverse":case"externalResourcesRequired":case"focusable":case"preserveAlpha":a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"inert":case"allowFullScreen":case"async":case"autoPlay":case"controls":case"credentialless":case"default":case"defer":case"disabled":case"disablePictureInPicture":case"disableRemotePlayback":case"formNoValidate":case"hidden":case"loop":case"noModule":case"noValidate":case"open":case"playsInline":case"readOnly":case"required":case"reversed":case"scoped":case"seamless":case"itemScope":a&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,""):t.removeAttribute(l);break;case"capture":case"download":a===!0?t.setAttribute(l,""):a!==!1&&a!=null&&typeof a!="function"&&typeof a!="symbol"?t.setAttribute(l,a):t.removeAttribute(l);break;case"cols":case"rows":case"size":case"span":a!=null&&typeof a!="function"&&typeof a!="symbol"&&!isNaN(a)&&1<=a?t.setAttribute(l,a):t.removeAttribute(l);break;case"rowSpan":case"start":a==null||typeof a=="function"||typeof a=="symbol"||isNaN(a)?t.removeAttribute(l):t.setAttribute(l,a);break;case"popover":Rt("beforetoggle",t),Rt("toggle",t),mi(t,"popover",a);break;case"xlinkActuate":ea(t,"http://www.w3.org/1999/xlink","xlink:actuate",a);break;case"xlinkArcrole":ea(t,"http://www.w3.org/1999/xlink","xlink:arcrole",a);break;case"xlinkRole":ea(t,"http://www.w3.org/1999/xlink","xlink:role",a);break;case"xlinkShow":ea(t,"http://www.w3.org/1999/xlink","xlink:show",a);break;case"xlinkTitle":ea(t,"http://www.w3.org/1999/xlink","xlink:title",a);break;case"xlinkType":ea(t,"http://www.w3.org/1999/xlink","xlink:type",a);break;case"xmlBase":ea(t,"http://www.w3.org/XML/1998/namespace","xml:base",a);break;case"xmlLang":ea(t,"http://www.w3.org/XML/1998/namespace","xml:lang",a);break;case"xmlSpace":ea(t,"http://www.w3.org/XML/1998/namespace","xml:space",a);break;case"is":mi(t,"is",a);break;case"innerText":case"textContent":return;default:if(!(2<l.length)||l[0]!=="o"&&l[0]!=="O"||l[1]!=="n"&&l[1]!=="N")l=iv.get(l)||l,mi(t,l,a);else return}Bt=!0}function Hr(t,e,l,a,n,u){switch(l){case"style":Uo(t,a,u);return;case"dangerouslySetInnerHTML":if(a!=null){if(typeof a!="object"||!("__html"in a))throw Error(f(61));if(l=a.__html,l!=null){if(n.children!=null)throw Error(f(60));(u!=null?u.__html:void 0)!==l&&(t.innerHTML=l)}}break;case"children":if(typeof a=="string")jn(t,a);else if(typeof a=="number"||typeof a=="bigint")jn(t,""+a);else return;break;case"onScroll":a!=null&&Rt("scroll",t);return;case"onScrollEnd":a!=null&&Rt("scroLine truncated
`;qt&&(xe+=qt+` `;Yt&&(qe+=Yt+`
`),Dt&&(xe+=(Array.isArray(Dt)?Dt.join(` `),St&&(qe+=(Array.isArray(St)?St.join(`
`):String(Dt))+` `):String(St))+`
`),xe+=JSON.stringify(nt,null,2).slice(0,4e3),jl(xe),ce("已返回登录信息,见下方")}catch(nt){ce("发起失败:"+nt.message)}},[]),U=d.useCallback(async()=>{const O=V.trim();if(!O){W("请填要查配额的用户 id");return}try{const J=await tt(K.adminQuotaUser(O));W("用户 "+O.slice(0,8)+":本月用量 "+fo(J.used_usd_micros)+" / 配额 "+fo(J.quota_usd_micros)+(J.remaining_usd_micros!==null&&J.remaining_usd_micros!==void 0?"(剩余 "+fo(J.remaining_usd_micros)+")":"")+" | 月份 "+(J.month||""))}catch(J){W(oo(J))}},[V]),$=d.useCallback(async()=>{if(window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。"))try{const J=(await tt(K.adminSweep,"POST",{})).swept||[];W("sweep 完成,清理 "+J.length+" 个任务"+(J.length?":"+J.join("、"):""))}catch(O){W(oo(O))}},[]),at=d.useCallback(async()=>{try{const O=await tt(K.adminBackup,"POST",{});W("备份完成:backend="+O.backend+" | 路径 "+O.path+" | 大小 "+O.size_bytes+" 字节 | 任务 "+O.task_count+" 行")}catch(O){W(oo(O))}},[]);return{keySaved:i,keyState:h,quotaText:g,healthText:A,adminGate:H,adminBody:Y,adminResult:k,adminUserId:V,setAdminUserId:I,projects:X,projectId:gt,setProjectId:xt,projectMsg:Z,githubRepos:w,workers:et,workersSummary:Bt,workersError:wt,members:ot,memberMsg:G,memberError:ut,skills:pt,apps:we,mcpServers:y,skillMsg:ct,skillError:Nt,mcpMsg:Rt,mcpOauthMsg:dt,mcpOauthBox:Ga,envs:Le,envCount:Gt,envMsg:Jl,envRepoLine:da,envRepoBar:zl,envRepoMsg:Tl,envSelect:se,setEnvSelect:re,models:rl,modelCustomVisible:ma,refreshHealth:qe,loadQuota:_l,probeAdmin:ol,refreshProjects:Wt,loadGithubRepos:Ul,loadRelayModels:mn,refreshWorkers:Qa,refreshEnvs:Se,refreshMembers:Xa,refreshSkills:ha,saveKeyInput:su,clearKeyInput:ru,createProject:Wl,createEnv:Ae,archiveEnv:fl,saveEnvRepo:Oe,inviteMember:ou,changeMemberRole:fu,reloadMcp:du,mcpOauth:B,adminQuota:U,adminSweep:$,adminBackup:at}}const mo=20;function ag(){const[i,r]=d.useState([]),[h,f]=d.useState(""),[g,C]=d.useState(""),[A,S]=d.useState(""),[H,R]=d.useState(""),[Y,x]=d.useState(0),[k,W]=d.useState(0),[V,I]=d.useState({}),X=d.useRef(0),ht=d.useRef({status:"",executor:"",q:""});ht.current={status:g,executor:A,q:H};const gt=d.useRef(""),xt=d.useCallback(async(ot,rt,G)=>{const N=ot!==void 0?ot:rt;if(!N){r([]),W(0),I({});return}gt.current=N;const ut=G!==void 0?G:X.current,St=ht.current;try{const pt=await tt(K.tasksPage(N,mo,ut*mo,St.status,St.executor,St.q));r(pt.tasks||[]),W(pt.total??(pt.tasks||[]).length),I(pt.status_counts||{}),f("")}catch(pt){f("加载失败:"+pt.message)}},[]),Z=d.useCallback(ot=>{const rt=Math.max(0,ot);X.current=rt,x(rt)},[]),Tt=d.useCallback(ot=>{Z(ot),xt(gt.current||void 0,void 0,ot)},[xt,Z]);d.useEffect(()=>{if(!gt.current)return;const ot=setTimeout(()=>{xt(gt.current,void 0,0)},300);return()=>clearTimeout(ot)},[g,A,H]);const w=d.useCallback(ot=>{C(ot),Z(0)},[Z]),Q=d.useCallback(ot=>{S(ot),Z(0)},[Z]),et=d.useCallback(ot=>{R(ot),Z(0)},[Z]),zt=d.useCallback(async ot=>{Z(0),await xt(ot,void 0,0)},[xt,Z]),Bt=Math.max(1,Math.ceil(k/mo)),vt=k===0?"共 0 个任务":"共 "+k+" 个任务,第 "+(Y+1)+" / "+Bt+" 页",wt=i,q=d.useCallback(async(ot,rt)=>{const G={...ot};rt.length&&(G.upload_id=await tg(rt));const N=await tt(K.tasksCreate,"POST",G);return"任务已建:"+Ne(N.id)},[]);return{tasks:i,taskError:h,statusFilter:g,setStatusFilter:w,executorFilter:A,setExecutorFilter:Q,searchText:H,setSearchText:et,filteredTasks:wt,taskCountText:vt,refreshTasks:xt,refreshTasksFirstPage:zt,page:Y,totalPages:Bt,taskTotal:k,taskStatusCounts:V,goToPage:Tt,dispatch:q}}function ng(i,r){const[h,f]=d.useState(null),[g,C]=d.useState(""),[A,S]=d.useState(""),[H,R]=d.useState(""),[Y,x]=d.useState(""),[k,W]=d.useState(void 0),[V,I]=d.useState(!1),[X,ht]=d.useState(""),[gt,xt]=d.useState([]),[Z,Tt]=d.useState([]),[w,Q]=d.useState(""),[et,zt]=d.useState([]),[Bt,vt]=d.useState(""),[wt,q]=d.useState(""),[ot,rt]=d.useState([]),[G,N]=d.useState(""),[ut,St]=d.useState(""),[pt,ge]=d.useState(""),[we,Ce]=d.useState(""),[y,D]=d.useState([]),[ct,it]=d.useState([]),[Nt,_t]=d.useState(""),[Rt,P]=d.useState([]),[dt,ce]=d.useState(""),[Ga,jl]=d.useState(""),[Le,le]=d.useState(""),[Gt,he]=d.useState(""),[Jl,pe]=d.useState([]),[da,Ve]=d.useState(""),[zl,Rl]=d.useState(null),Tl=d.useRef(null),Be=d.useRef(null),se=d.useRef(null);se.current=h;const re=d.useCallback(()=>{if(Tl.current){try{Tl.current.abort()}catch{}Tl.current=null}Be.current&&(clearTimeout(Be.current),Be.current=null)},[]),rl=d.useCallback(async B=>{try{const U=await tt(K.taskAgents(B));Tt(U.agents||[]),Q(U.note||"")}catch(U){Tt([]),Q("子代理链路加载失败:"+U.message)}},[]),Fl=d.useCallback(B=>{Be.current&&clearTimeout(Be.current),Be.current=setTimeout(()=>void rl(B),1e3)},[rl]),ma=d.useCallback(async B=>{if(!B.coordinator_parts){xt([]);return}try{const U=await tt(K.tasksByParent(B.project_id||"",B.id));xt(U.tasks||[])}catch{xt([])}},[]),Dl=d.useCallback(async B=>{try{Line truncated `),qe+=JSON.stringify(K,null,2).slice(0,4e3),_l(qe),se("已返回登录信息,见下方")}catch(K){se("发起失败:"+K.message)}},[]),I=d.useCallback(async()=>{const w=tt.trim();if(!w){L("请填要查配额的用户 id");return}try{const Z=await W(F.adminQuotaUser(w));L("用户 "+w.slice(0,8)+":本月用量 "+iu(Z.used_usd_micros)+" / 配额 "+iu(Z.quota_usd_micros)+(Z.remaining_usd_micros!==null&&Z.remaining_usd_micros!==void 0?"(剩余 "+iu(Z.remaining_usd_micros)+")":"")+" | 月份 "+(Z.month||""))}catch(Z){L(Dc(Z))}},[tt]),at=d.useCallback(async(w,Z)=>{const K=w.trim();if(!K){L("请填要设置配额的用户 id");return}let Yt;if(!Z.trim())Yt=null;else{const St=Number(Z.trim());if(!Number.isFinite(St)||St<0){L("配额金额无效:请输入 >= 0 的美元数,留空表示不限额");return}Yt=Math.round(St*1e6)}try{const St=await W(F.adminQuotas,"POST",{user_id:K,quota_usd_micros:Yt});L("配额已设置:用户 "+K.slice(0,8)+":本月用量 "+iu(St.used_usd_micros)+" / 配额 "+iu(St.quota_usd_micros)+(St.remaining_usd_micros!==null&&St.remaining_usd_micros!==void 0?"(剩余 "+iu(St.remaining_usd_micros)+")":"")+" | 月份 "+(St.month||""))}catch(St){L(Dc(St))}},[]),qt=d.useCallback(async()=>{if(window.confirm("确定手动触发 sweep 吗?超阈值的陈旧 running 任务会被标为 interrupted。"))try{const Z=(await W(F.adminSweep,"POST",{})).swept||[];L("sweep 完成,清理 "+Z.length+" 个任务"+(Z.length?":"+Z.join("、"):""))}catch(w){L(Dc(w))}},[]),hl=d.useCallback(async()=>{try{const w=await W(F.adminBackup,"POST",{});L("备份完成:backend="+w.backend+" | 路径 "+w.path+" | 大小 "+w.size_bytes+" 字节 | 任务 "+w.task_count+" 行")}catch(w){L(Dc(w))}},[]);return{keySaved:i,keyState:h,quotaText:y,healthText:M,adminGate:_,adminBody:Y,adminResult:H,adminUserId:tt,setAdminUserId:dt,projects:J,projectId:_t,setProjectId:Dt,projectMsg:Q,githubRepos:D,workers:et,workersSummary:Mt,workersError:Nt,members:nt,memberMsg:G,memberError:ut,skills:pt,apps:we,mcpServers:g,skillMsg:ct,skillError:Ot,mcpMsg:kt,mcpOauthMsg:ht,mcpOauthBox:La,envs:Ve,envCount:Qt,envMsg:Wl,envRepoLine:va,envRepoBar:Ul,envRepoMsg:El,envSelect:re,setEnvSelect:oe,models:fl,modelCustomVisible:ya,refreshHealth:Be,loadQuota:Nl,probeAdmin:dl,refreshProjects:$t,loadGithubRepos:Hl,loadRelayModels:vn,refreshWorkers:Va,refreshEnvs:be,refreshMembers:Il,refreshSkills:ga,saveKeyInput:fu,clearKeyInput:du,createProject:Pl,createEnv:Ce,archiveEnv:ml,saveEnvRepo:Ae,inviteMember:mu,changeMemberRole:hu,removeMember:vu,reloadMcp:q,mcpOauth:k,adminQuota:I,adminSetQuota:at,adminSweep:qt,adminBackup:hl}}const mo=20;function ag(){const[i,r]=d.useState([]),[h,f]=d.useState(""),[y,C]=d.useState(""),[M,S]=d.useState(""),[_,O]=d.useState(""),[Y,x]=d.useState(0),[H,L]=d.useState(0),[tt,dt]=d.useState({}),J=d.useRef(0),gt=d.useRef({status:"",executor:"",q:""});gt.current={status:y,executor:M,q:_};const _t=d.useRef(""),Dt=d.useCallback(async(nt,rt,G)=>{const A=nt!==void 0?nt:rt;if(!A){r([]),L(0),dt({});return}_t.current=A;const ut=G!==void 0?G:J.current,bt=gt.current;try{const pt=await W(F.tasksPage(A,mo,ut*mo,bt.status,bt.executor,bt.q));r(pt.tasks||[]),L(pt.total??(pt.tasks||[]).length),dt(pt.status_counts||{}),f("")}catch(pt){f("加载失败:"+pt.message)}},[]),Q=d.useCallback(nt=>{const rt=Math.max(0,nt);J.current=rt,x(rt)},[]),ft=d.useCallback(nt=>{Q(nt),Dt(_t.current||void 0,void 0,nt)},[Dt,Q]);d.useEffect(()=>{if(!_t.current)return;const nt=setTimeout(()=>{Dt(_t.current,void 0,0)},300);return()=>clearTimeout(nt)},[y,M,_]);const D=d.useCallback(nt=>{C(nt),Q(0)},[Q]),X=d.useCallback(nt=>{S(nt),Q(0)},[Q]),et=d.useCallback(nt=>{O(nt),Q(0)},[Q]),xt=d.useCallback(async nt=>{Q(0),await Dt(nt,void 0,0)},[Dt,Q]),Mt=Math.max(1,Math.ceil(H/mo)),mt=H===0?"共 0 个任务":"共 "+H+" 个任务,第 "+(Y+1)+" / "+Mt+" 页",Nt=i,B=d.useCallback(async(nt,rt)=>{const G={...nt};rt.length&&(G.upload_id=await tg(rt));const A=await W(F.tasksCreate,"POST",G);return"任务已建:"+pe(A.id)},[]);return{tasks:i,taskError:h,statusFilter:y,setStatusFilter:D,executorFilter:M,setExecutorFilter:X,searchText:_,setSearchText:et,filteredTasks:Nt,taskCountText:mt,refreshTasks:Dt,refreshTasksFirstPage:xt,page:Y,totalPages:Mt,taskTotal:H,taskStatusCounts:tt,goToPage:ft,dispatch:B}}function ng(i,r){const[h,f]=d.useState(null),[y,C]=d.useState(""),[M,S]=d.useState(""),[_,O]=d.useState(""),[Y,x]=d.useState(""),[H,L]=d.useState(void 0),[tt,dt]=d.useState(!1),[J,gt]=d.useState(""),[_t,Dt]=d.useState([]),[Q,ft]=d.useState([]),[D,X]=d.useState(""),[et,xt]=d.useState([]),[Mt,mt]=d.useState(""),[Nt,B]=d.useState(""),[nt,rt]=d.useState([]),[G,A]=d.useState(""),[ut,bt]=d.useState(""),[pt,Se]=d.useState(""),[we,Ne]=d.useState(""),[g,U]=d.useState([]),[ct,it]=d.useState([]),[Ot,Et]=d.useState(""),[kt,P]=d.useState([]),[ht,se]=d.useState(""),[La,_l]=d.useState(""),[Ve,ae]=d.useState(""),[Qt,ve]=d.useState(""),[Wl,xe]=d.useState([]),[va,Ze]=d.useState(""),Line truncated
`);hn=Hl.pop()||"";for(const mu of Hl){const hu=mu.split(` `);yn=Bl.pop()||"";for(const yu of Bl){const gu=yu.split(`
`).filter(Ze=>Ze.indexOf("data: ")===0).map(Ze=>Ze.slice(6)).join(` `).filter(Ke=>Ke.indexOf("data: ")===0).map(Ke=>Ke.slice(6)).join(`
`);if(hu)try{const Ze=JSON.parse(hu);if(Ze.type==="snapshot")at=Ze.entries||[],Object.keys(O).forEach(ae=>delete O[ae]),at.forEach((ae,ve)=>{ae.id&&(O[ae.id]=ve)}),J=Ze.note||"",rt([...at]),N(J),Fl(B);else if(Ze.type==="entry"){const ae=Ze.entry;ae.id&&ae.id in O?at[O[ae.id]]=ae:(ae.id&&(O[ae.id]=at.length),at.push(ae)),rt([...at]),Fl(B)}else Ze.type==="done"&&(nt=!0,qt=Ze.status,St(qt))}catch{}}if(nt)break}}catch(Dt){if(Dt&&Dt.name==="AbortError")return;N("链路流中断:"+Dt.message);return}nt&&r()},[re,Fl,r]),Se=d.useCallback(async B=>{re();try{const U=await tt(K.task(B));f(U);let $="";U.result_text&&($+=`=== result === `);if(gu)try{const Ke=JSON.parse(gu);if(Ke.type==="snapshot")at=Ke.entries||[],Object.keys(qt).forEach(ne=>delete qt[ne]),at.forEach((ne,ye)=>{ne.id&&(qt[ne.id]=ye)}),hl=Ke.note||"",rt([...at]),A(hl),$l(q);else if(Ke.type==="entry"){const ne=Ke.entry;ne.id&&ne.id in qt?at[qt[ne.id]]=ne:(ne.id&&(qt[ne.id]=at.length),at.push(ne)),rt([...at]),$l(q)}else Ke.type==="done"&&(w=!0,Z=Ke.status,bt(Z))}catch{}}if(w)break}}catch(K){if(K&&K.name==="AbortError")return;A("链路流中断:"+K.message);return}w&&r()},[oe,$l,r]),be=d.useCallback(async q=>{oe();try{const k=await W(F.task(q));f(k);let I="";k.result_text&&(I+=`=== result ===
`+U.result_text.slice(0,8e3)+` `+k.result_text.slice(0,8e3)+`
`),U.error_text&&($+=`=== error === `),k.error_text&&(I+=`=== error ===
`+U.error_text.slice(0,8e3)+` `+k.error_text.slice(0,8e3)+`
`),$||($="(暂无 result/error 输出)"),C($),x(""),W(void 0),Dl(B),I(!1),ht(""),ce(""),he(""),mn(U.status);const at=i(U.project_id),O=at==="maintainer"||at==="owner",J=at==="contributor"||O,nt=uu.includes(U.status),qt=[];["failed","interrupted","merge-failed"].includes(U.status)&&O||qt.push("重试:不可用——仅 failed/interrupted/merge-failed 可重试"+(O?"":",且仅 maintainer 可重试")),!nt&&J||qt.push("取消:不可用——"+(nt?"终态任务不可取消":"仅项目成员(contributor 及以上)可取消")),U.status==="completed"&&O||qt.push("合并:不可用——仅 completed 可合并"+(O?"":",且仅 maintainer 可合并")),U.status==="merged"&&O||qt.push("提 PR:不可用——仅 merged 可提 PR"+(O?"":",且仅 maintainer 可提 PR")),R(qt.join(";")),S(""),Qa(B,U.status),rl(B),ma(U),Pe(B),te(B),ol(B),Wt(B),Ul(B),qe(B),_l(B)}catch(U){f(null),C("加载失败:"+U.message)}},[re,Qa,rl,ma,Pe,te,ol,Wt,Ul,qe,_l,Dl,i]),Xa=d.useCallback(async()=>{const B=se.current;if(B){try{const U=await tt(K.task(B.id));f(U);let $="";U.result_text&&($+=`=== result === `),I||(I="(暂无 result/error 输出)"),C(I),x(""),L(void 0),kl(q),dt(!1),gt(""),se(""),ve(""),vn(k.status);const at=i(k.project_id),qt=at==="maintainer"||at==="owner",hl=at==="contributor"||qt,w=su.includes(k.status),Z=[];["failed","interrupted","merge-failed"].includes(k.status)&&qt||Z.push("重试:不可用——仅 failed/interrupted/merge-failed 可重试"+(qt?"":",且仅 maintainer 可重试")),!w&&hl||Z.push("取消:不可用——"+(w?"终态任务不可取消":"仅项目成员(contributor 及以上)可取消")),k.status==="completed"&&qt||Z.push("合并:不可用——仅 completed 可合并"+(qt?"":",且仅 maintainer 可合并")),k.status==="merged"&&qt||Z.push("提 PR:不可用——仅 merged 可提 PR"+(qt?"":",且仅 maintainer 可提 PR")),O(Z.join(";")),S(""),Va(q,k.status),fl(q),ya(k),tl(q),te(q),dl(q),$t(q),Hl(q),Be(q),Nl(q)}catch(k){f(null),C("加载失败:"+k.message)}},[oe,Va,fl,ya,tl,te,dl,$t,Hl,Be,Nl,kl,i]),Il=d.useCallback(async()=>{const q=re.current;if(q){try{const k=await W(F.task(q.id));f(k);let I="";k.result_text&&(I+=`=== result ===
`+U.result_text.slice(0,8e3)+` `+k.result_text.slice(0,8e3)+`
`),U.error_text&&($+=`=== error === `),k.error_text&&(I+=`=== error ===
`+U.error_text.slice(0,8e3)+` `+k.error_text.slice(0,8e3)+`
`),C($||"(暂无 result/error 输出)")}catch{}te(B.id),Pe(B.id),qe(B.id),_l(B.id),ma(B),rl(B.id),ol(B.id),Wt(B.id),Ul(B.id),Dl(B.id)}},[te,Pe,qe,_l,ma,rl,ol,Wt,Ul,Dl]),ha=d.useCallback(async B=>{const U=se.current;if(!U)return;const $={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!($[B]&&!window.confirm($[B])))try{const at=await tt(K.taskAction(U.id,B),"POST",{});S(B+" 已提交:"+JSON.stringify(at).slice(0,300)),r(),await Se(U.id)}catch(at){S(B+" 失败:"+at.message)}},[Se,r]),su=d.useCallback(async()=>{const B=se.current;if(B)try{const U=await tt(K.taskCost(B.id));x(JSON.stringify(U,null,2).slice(0,4e3))}catch(U){x("查成本失败:"+U.message)}},[]),ru=d.useCallback(async B=>{const U=se.current;if(U){if(!B.trim()){ht("分支消息必填");return}if(!uu.includes(U.status)){ht("仅终态任务可分支");return}try{const $=await tt(K.taskFork(U.id),"POST",{message:B.trim()});ht("分支任务已建:"+$.id.slice(0,8)),r()}catch($){ht("分支失败:"+$.message)}}},[r]),Wl=d.useCallback(async B=>{const U=se.current;if(U)try{await tt(K.taskReviews(U.id),"POST",B),vt("评审已登记(requested)"),await qe(U.id)}catch($){vt("登记评审需求失败:"+$.message)}},[qe]),Ae=d.useCallback(B=>{const U=se.current;if(!U)return;const $=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");$!==null&&tt(K.taskReviewFindings(U.id,B),"POST",{findings:$}).then(()=>void qe(U.id)).catch(at=>vt("写 findings 失败:"+at.message))},[qe]),fl=d.useCallback((B,U)=>{const $=se.current;if(!$)return;const at=U==="approved"?"approve":"deny";tt(K.taskApprovalDecision($.id,B,at),"POST",{}).then(()=>{_t("已记录"+(U==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),_l($.id)}).catch(O=>_t("决定失败:"+O.message))},[_l]),Oe=d.useCallback(async(B,U,$)=>{const at=se.current;if(at){if(!U.trim()){ce("目标任务 id 不能为空");return}if(!$.trim()){ce("消息正文不能为空");return}try{const O=await tt(K.taskMessagesTo(U.trim()),"POST",{from_task_id:at.id,body:$.trim(),mode:B});ce("已发送("+O.delivery+")"),await ol(at.id)}catch(O){ce("发送失败:"+O.message)}}},[ol]),ou=d.useCallback((B,U)=>{const $=se.current;if(!$)return;if(!["completed","failed","interrupted"].includes($.status)){he("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const at={};if(B==="rename"){if(!U.trim()){he("新线程名不能为空");return}at.name=U.trim()}tt(K.taskThreadAction($.id,B),"POST",at).then(O=>{he(B+" 已提交:"+JSON.stringify(O).slice(0,300)),Wt($.id),Ul($.id)}).catch(O=>he(B+" 失败:"+O.message))},[Wt,Ul]),fu=d.useCallback(async B=>{const U=se.current;if(!U)return;const $=B.trim();if(!$){Ve("消息不能为空");return}try{uu.includes(U.status)&&U.status!=="merged"?(await tt(K.taskContinue(U.id),"POST",{message:$}),Ve("已重新排队,同一会话继续"),r(),await Se(U.id)):["queued","running"].includes(U.status)?(await tt(K.taskFollowups(U.id),"POST",{body:$,mode:"direct"}),Ve("已直接发送"),await te(U.id),await Pe(U.id)):Ve("merged/merge-failed 任务不可继续,也不可发后续消息")}catch(at){Ve("发送失败:"+at.message)}},[Se,r,te,Pe]),du=d.useCallback(async()=>{const B=se.current;if(!B){Ce("先选一个任务");return}try{const U=await tt(K.taskTrace(B.id)),$=new Blob([JSON.stringify({task_id:B.id,status:U.status,note:U.note||"",count:(U.entries||[]).length,entries:U.entries||[]},null,2)],{type:"application/json"}),at=document.createElement("a");at.href=URL.createObjectURL($),at.download="trace-"+B.id.slice(0,8)+".json",document.body.appendChild(at),at.click(),at.remove(),setTimeout(()=>URL.revokeObjectURL(at.href),1e3),Ce("已下载完整链路 "+(U.entries||[]).length+" 条")}catch(U){Ce("下载失败:"+U.message)}},[]);return d.useEffect(()=>()=>re(),[re]),{currentTask:h,detailBody:g,actionMsg:A,actionReasons:H,costBox:Y,costSummary:k,forkVisible:V,setForkVisible:I,forkMsg:X,coordChildren:gt,agents:Z,agentsNote:w,reviews:et,reviewMsg:Bt,reviewError:wt,traceEntries:ot,traceNote:G,traceDone:ut,traceKind:pt,setTraceKind:ge,traceMsg:we,followups:y,approvals:ct,approvalMsg:Nt,peerMessages:Rt,peerMsg:dt,threadText:Ga,threadGateNote:Le,threadMsg:Gt,threadList:Jl,chatMsg:da,chatHistory:zl,showDetail:Se,refreshDetailLists:Xa,doAction:ha,taskCost:su,forkTask:ru,postReview:Wl,submitFindings:Ae,decideApproval:fl,postPeer:Oe,threadAction:ou,sendChatMessage:fu,downloadTrace:du,refreshReviews:()=>void qe(),refreshApprovals:()=>void _l()}}function ug(i){const[r,h]=d.useState([]),[f,g]=d.useState(""),[C,A]=d.useState(null),[S,H]=d.useState(""),R=d.useCallback(async V=>{const I=V!==void 0?V:i();if(!I){h([]);return}try{const X=await tt(K.projectAutomations(I));h(X.automations||[])}catch(X){g("自动化加载失败:"+X.message)}}Line truncated `),C(I||"(暂无 result/error 输出)")}catch{}te(q.id),tl(q.id),Be(q.id),Nl(q.id),ya(q),fl(q.id),dl(q.id),$t(q.id),Hl(q.id),kl(q.id)}},[te,tl,Be,Nl,ya,fl,dl,$t,Hl,kl]),ga=d.useCallback(async q=>{const k=re.current;if(!k)return;const I={cancel:"确定取消该任务吗?",merge:"确定把 completed 任务标为 merged 吗?",pr:"确定为该任务提 PR 吗?(会在仓库真实建 PR)"};if(!(I[q]&&!window.confirm(I[q])))try{const at=await W(F.taskAction(k.id,q),"POST",{});S(q+" 已提交:"+JSON.stringify(at).slice(0,300)),r(),await be(k.id)}catch(at){S(q+" 失败:"+at.message)}},[be,r]),fu=d.useCallback(async()=>{const q=re.current;if(q)try{const k=await W(F.taskCost(q.id));x(JSON.stringify(k,null,2).slice(0,4e3))}catch(k){x("查成本失败:"+k.message)}},[]),du=d.useCallback(async q=>{const k=re.current;if(k){if(!q.trim()){gt("分支消息必填");return}if(!su.includes(k.status)){gt("仅终态任务可分支");return}try{const I=await W(F.taskFork(k.id),"POST",{message:q.trim()});gt("分支任务已建:"+I.id.slice(0,8)),r()}catch(I){gt("分支失败:"+I.message)}}},[r]),Pl=d.useCallback(async q=>{const k=re.current;if(k)try{await W(F.taskReviews(k.id),"POST",q),mt("评审已登记(requested)"),await Be(k.id)}catch(I){mt("登记评审需求失败:"+I.message)}},[Be]),Ce=d.useCallback(q=>{const k=re.current;if(!k)return;const I=window.prompt("输入该条评审的 findings(将写回记录并标为 completed):");I!==null&&W(F.taskReviewFindings(k.id,q),"POST",{findings:I}).then(()=>void Be(k.id)).catch(at=>mt("写 findings 失败:"+at.message))},[Be]),ml=d.useCallback((q,k)=>{const I=re.current;if(!I)return;const at=k==="approved"?"approve":"deny";W(F.taskApprovalDecision(I.id,q,at),"POST",{}).then(()=>{Et("已记录"+(k==="approved"?"允许":"拒绝")+"(当前版本只留回执,不拦截实时行为)"),Nl(I.id)}).catch(qt=>Et("决定失败:"+qt.message))},[Nl]),Ae=d.useCallback(async(q,k,I)=>{const at=re.current;if(at){if(!k.trim()){se("目标任务 id 不能为空");return}if(!I.trim()){se("消息正文不能为空");return}try{const qt=await W(F.taskMessagesTo(k.trim()),"POST",{from_task_id:at.id,body:I.trim(),mode:q});se("已发送("+qt.delivery+")"),await dl(at.id)}catch(qt){se("发送失败:"+qt.message)}}},[dl]),mu=d.useCallback((q,k)=>{const I=re.current;if(!I)return;if(!["completed","failed","interrupted"].includes(I.status)){ve("仅 completed/failed/interrupted 任务可操作线程(merged/merge-failed 不可改,服务端同样 409)");return}const at={};if(q==="rename"){if(!k.trim()){ve("新线程名不能为空");return}at.name=k.trim()}W(F.taskThreadAction(I.id,q),"POST",at).then(qt=>{ve(q+" 已提交:"+JSON.stringify(qt).slice(0,300)),$t(I.id),Hl(I.id)}).catch(qt=>ve(q+" 失败:"+qt.message))},[$t,Hl]),hu=d.useCallback(async q=>{const k=re.current;if(!k)return;const I=q.trim();if(!I){Ze("消息不能为空");return}try{su.includes(k.status)&&k.status!=="merged"?(await W(F.taskContinue(k.id),"POST",{message:I}),Ze("已重新排队,同一会话继续"),r(),await be(k.id)):["queued","running"].includes(k.status)?(await W(F.taskFollowups(k.id),"POST",{body:I,mode:"direct"}),Ze("已直接发送"),await te(k.id),await tl(k.id)):Ze("merged/merge-failed 任务不可继续,也不可发后续消息")}catch(at){Ze("发送失败:"+at.message)}},[be,r,te,tl]),vu=d.useCallback(async()=>{const q=re.current;if(!q){Ne("先选一个任务");return}try{const k=await W(F.taskTrace(q.id)),I=new Blob([JSON.stringify({task_id:q.id,status:k.status,note:k.note||"",count:(k.entries||[]).length,entries:k.entries||[]},null,2)],{type:"application/json"}),at=document.createElement("a");at.href=URL.createObjectURL(I),at.download="trace-"+q.id.slice(0,8)+".json",document.body.appendChild(at),at.click(),at.remove(),setTimeout(()=>URL.revokeObjectURL(at.href),1e3),Ne("已下载完整链路 "+(k.entries||[]).length+" 条")}catch(k){Ne("下载失败:"+k.message)}},[]);return d.useEffect(()=>()=>oe(),[oe]),{currentTask:h,detailBody:y,actionMsg:M,actionReasons:_,costBox:Y,costSummary:H,forkVisible:tt,setForkVisible:dt,forkMsg:J,coordChildren:_t,agents:Q,agentsNote:D,reviews:et,reviewMsg:Mt,reviewError:Nt,traceEntries:nt,traceNote:G,traceDone:ut,traceKind:pt,setTraceKind:Se,traceMsg:we,followups:g,approvals:ct,approvalMsg:Ot,peerMessages:kt,peerMsg:ht,threadText:La,threadGateNote:Ve,threadMsg:Qt,threadList:Wl,chatMsg:va,chatHistory:Ul,showDetail:be,refreshDetailLists:Il,doAction:ga,taskCost:fu,forkTask:du,postReview:Pl,submitFindings:Ce,decideApproval:ml,postPeer:Ae,threadAction:mu,sendChatMessage:hu,downloadTrace:vu,refreshReviews:()=>void Be(),refreshApprovals:()=>void Nl()}}function ug(i){const[r,h]=d.useState([]),[f,y]=d.useState(""),[C,M]=d.useState(null),[S,_]=d.useState(""),[O,Y]=d.useState(null),[x,H]=d.useState(""),L=d.useCallback(async Q=>{const ft=Q!==void 0?Q:i();if(!ft){h([]);return}try{const D=await W(F.projectAutomations(ft));h(D.automations||[])}catLine truncated
(链路摘要已截断至 300 字,完整回复见右侧任务结果)`:f,label:"助手"}}if(i.kind==="exec"||i.kind==="file"||i.kind==="tool"||i.kind==="call"){const f=i.kind==="exec"?"命令":i.kind==="file"?"文件":i.kind==="tool"?"工具":"调用";return{id:h,role:"tool",kind:i.kind,text:vo(i),label:f}}return i.kind==="plan"?{id:h,role:"status",kind:i.kind,text:i.text||""}:i.kind==="reasoning"?{id:h,role:"status",kind:i.kind,text:"思考:"+(i.summary||"")}:i.kind==="agent-start"?{id:h,role:"status",kind:i.kind,text:"子代理开始:"+(i.name||"")+(i.prompt?"("+i.prompt+")":"")}:i.kind==="agent-done"?{id:h,role:"status",kind:i.kind,text:"子代理结束:"+(i.name||"")+" "+(i.status||"")}:{id:h,role:"status",kind:i.kind,text:vo(i)}}function pg(){const i=Zt(),r=i.currentTask,h=d.useMemo(()=>{if(!r)return[];let H;if(i.chatHistory){H=i.chatHistory.messages.map(V=>({id:V.id,role:V.role,text:V.text,label:V.label,kind:V.kind}));const k=new Set(H.map(V=>V.id)),W=i.traceEntries.filter(V=>V.id&&!k.has(V.id)&&!String(V.id).startsWith("fu-")).map((V,I)=>Xh(V,I));H=[...H,...W]}else{const k=r.prompt?[{id:"task-prompt",role:"user",text:r.prompt}]:[],W=i.traceEntries.filter(I=>!String(I.id||"").startsWith("fu-")).map((I,X)=>Xh(I,X)),V=i.followups.map((I,X)=>({id:"followup-"+X,role:"user",text:String(I.body||""),label:"后续消息"}));H=[...k,...W,...V]}const R=i.traceKind,Y=R?H.filter(k=>k.role==="user"||!k.kind||k.kind===R):H,x=i.traceDone?[{id:"trace-done",role:"status",text:"链路已结束("+i.traceDone+")"}]:[];return[...Y,...x]},[r,i.chatHistory,i.traceEntries,i.followups,i.traceKind,i.traceDone]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示继续会话"});const f=uu.includes(r.status),g=["queued","running"].includes(r.status),C=f||g,A=f?"终态任务:发送走继续接口(同一会话再跑一轮)":g?"运行中/排队中:发送走后续消息直接发送(跑完当前轮后优先执行)":"merged/merge-failed 任务不可继续,也不可发后续消息",S=i.chatHistory?i.chatHistory.legacy_continue_count??null:null;return c.jsxs("div",{className:"chat-column",children:[c.jsx("div",{className:"muted",children:"实时对话"}),c.jsxs(Ht,{children:[c.jsxs("select",{"aria-label":"链路类型",value:i.traceKind,onChange:H=>i.setTraceKind(H.target.value),children:[c.jsx("option",{value:"",children:"链路类型:全部"}),["exec","file","call","tool","message","reasoning","plan","agent-start","agent-done"].map(H=>c.jsx("option",{value:H,children:H==="exec"?"命令":H==="file"?"文件":H==="call"?"调用":H==="tool"?"工具":H==="message"?"消息":H==="reasoning"?"思考":H==="plan"?"计划":"子代理"},H))]}),c.jsx("button",{onClick:i.downloadTrace,children:"下载完整链路"}),c.jsx("span",{className:"muted",children:i.traceMsg})]}),i.traceNote?c.jsx("div",{children:c.jsx("span",{className:"err",children:i.traceNote})}):null,S!==null&&S>0?c.jsxs("div",{className:"muted",children:["此前有 ",S," 条继续消息发生在持久化之前,未入库,上面历史不完整;部署后新发的继续消息会正常显示。"]}):null,i.chatHistory&&i.chatHistory.trace_note?c.jsx("div",{className:"muted",children:i.chatHistory.trace_note}):null,c.jsx(yg,{messages:h,empty:"(暂无会话消息)"}),c.jsx(gg,{disabled:!C,placeholder:"消息",onSend:H=>void i.sendChatMessage(H)}),c.jsxs("div",{className:"muted",children:[A,"。",i.chatMsg]}),c.jsx("div",{className:"muted",children:"对话时间线:首条是本任务 prompt(用户);助手回复、命令/文件/工具/调用、计划/思考/子代理开始结束都在同一列里,不再另起一份链路名单。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。"})]})}function Sg(){return Zt().currentTask?c.jsx("div",{className:"conversation",children:c.jsx(pg,{})}):c.jsx("div",{className:"conversation",children:c.jsx("div",{className:"muted",children:"在任务列表里点一条任务,对话和详情就切到该任务;也可以在上面派发一个新任务。"})})}function xg(){const i=Zt(),r=i.currentTask,[h,f]=d.useState("");if(!r)return null;const g=uu.includes(r.status),C=["failed","interrupted","merge-failed"].includes(r.status);return c.jsxs(c.Fragment,{children:[c.jsxs(Ht,{children:[c.jsx("button",{disabled:!C,title:C?"":"仅 failed/interrupted/merge-failed 可重试",onClick:()=>i.doAction("retry"),children:"重试"}),c.jsx("button",{className:"danger",disabled:g,title:g?"终态任务不可取消":"",onClick:()=>i.doAction("cancel"),children:"取消"}),c.jsx("button",{disabled:r.status!=="completed",title:r.status!=="completed"?"仅 completed 可合并":"",onClick:()=>i.doAction("merge"),children:"合并(completed → merged)"}),c.jsx("button",{disabled:r.status!=="merged",title:r.status!=="merged"?"仅 merged 可提 PR":"",onClick:()=>i.doAction("pr"),children:"提 PR(须 mergedLine truncated (链路摘要已截断至 300 字,完整回复见右侧任务结果)`:f,label:"助手"}}if(i.kind==="exec"||i.kind==="file"||i.kind==="tool"||i.kind==="call"){const f=i.kind==="exec"?"命令":i.kind==="file"?"文件":i.kind==="tool"?"工具":"调用";return{id:h,role:"tool",kind:i.kind,text:yo(i),label:f}}return i.kind==="plan"?{id:h,role:"status",kind:i.kind,text:i.text||""}:i.kind==="reasoning"?{id:h,role:"status",kind:i.kind,text:"思考:"+(i.summary||"")}:i.kind==="agent-start"?{id:h,role:"status",kind:i.kind,text:"子代理开始:"+(i.name||"")+(i.prompt?"("+i.prompt+")":"")}:i.kind==="agent-done"?{id:h,role:"status",kind:i.kind,text:"子代理结束:"+(i.name||"")+" "+(i.status||"")}:{id:h,role:"status",kind:i.kind,text:yo(i)}}function pg(){const i=Lt(),r=i.currentTask,h=d.useMemo(()=>{if(!r)return[];let _;if(i.chatHistory){_=i.chatHistory.messages.map(tt=>({id:tt.id,role:tt.role,text:tt.text,label:tt.label,kind:tt.kind}));const H=new Set(_.map(tt=>tt.id)),L=i.traceEntries.filter(tt=>tt.id&&!H.has(tt.id)&&!String(tt.id).startsWith("fu-")).map((tt,dt)=>Lh(tt,dt));_=[..._,...L]}else{const H=r.prompt?[{id:"task-prompt",role:"user",text:r.prompt}]:[],L=i.traceEntries.filter(dt=>!String(dt.id||"").startsWith("fu-")).map((dt,J)=>Lh(dt,J)),tt=i.followups.map((dt,J)=>({id:"followup-"+J,role:"user",text:String(dt.body||""),label:"后续消息"}));_=[...H,...L,...tt]}const O=i.traceKind,Y=O?_.filter(H=>H.role==="user"||!H.kind||H.kind===O):_,x=i.traceDone?[{id:"trace-done",role:"status",text:"链路已结束("+i.traceDone+")"}]:[];return[...Y,...x]},[r,i.chatHistory,i.traceEntries,i.followups,i.traceKind,i.traceDone]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情后,这里显示继续会话"});const f=su.includes(r.status),y=["queued","running"].includes(r.status),C=f||y,M=f?"终态任务:发送走继续接口(同一会话再跑一轮)":y?"运行中/排队中:发送走后续消息直接发送(跑完当前轮后优先执行)":"merged/merge-failed 任务不可继续,也不可发后续消息",S=i.chatHistory?i.chatHistory.legacy_continue_count??null:null;return c.jsxs("div",{className:"chat-column",children:[c.jsx("div",{className:"muted",children:"实时对话"}),c.jsxs(At,{children:[c.jsxs("select",{"aria-label":"链路类型",value:i.traceKind,onChange:_=>i.setTraceKind(_.target.value),children:[c.jsx("option",{value:"",children:"链路类型:全部"}),["exec","file","call","tool","message","reasoning","plan","agent-start","agent-done"].map(_=>c.jsx("option",{value:_,children:_==="exec"?"命令":_==="file"?"文件":_==="call"?"调用":_==="tool"?"工具":_==="message"?"消息":_==="reasoning"?"思考":_==="plan"?"计划":"子代理"},_))]}),c.jsx("button",{onClick:i.downloadTrace,children:"下载完整链路"}),c.jsx("span",{className:"muted",children:i.traceMsg})]}),i.traceNote?c.jsx("div",{children:c.jsx("span",{className:"err",children:i.traceNote})}):null,S!==null&&S>0?c.jsxs("div",{className:"muted",children:["此前有 ",S," 条继续消息发生在持久化之前,未入库,上面历史不完整;部署后新发的继续消息会正常显示。"]}):null,i.chatHistory&&i.chatHistory.trace_note?c.jsx("div",{className:"muted",children:i.chatHistory.trace_note}):null,c.jsx(yg,{messages:h,empty:"(暂无会话消息)"}),c.jsx(gg,{disabled:!C,placeholder:"消息",onSend:_=>void i.sendChatMessage(_)}),c.jsxs("div",{className:"muted",children:[M,"。",i.chatMsg]}),c.jsx("div",{className:"muted",children:"对话时间线:首条是本任务 prompt(用户);助手回复、命令/文件/工具/调用、计划/思考/子代理开始结束都在同一列里,不再另起一份链路名单。流式进行中时新条目实时追加;你在底部时自动跟随,往上翻时暂停跟随,点“回到最新”恢复。"})]})}function Sg(){return Lt().currentTask?c.jsx("div",{className:"conversation",children:c.jsx(pg,{})}):c.jsx("div",{className:"conversation",children:c.jsx("div",{className:"muted",children:"在任务列表里点一条任务,对话和详情就切到该任务;也可以在上面派发一个新任务。"})})}function xg(){const i=Lt(),r=i.currentTask,[h,f]=d.useState("");if(!r)return null;const y=su.includes(r.status),C=["failed","interrupted","merge-failed"].includes(r.status);return c.jsxs(c.Fragment,{children:[c.jsxs(At,{children:[c.jsx("button",{disabled:!C,title:C?"":"仅 failed/interrupted/merge-failed 可重试",onClick:()=>i.doAction("retry"),children:"重试"}),c.jsx("button",{className:"danger",disabled:y,title:y?"终态任务不可取消":"",onClick:()=>i.doAction("cancel"),children:"取消"}),c.jsx("button",{disabled:r.status!=="completed",title:r.status!=="completed"?"仅 completed 可合并":"",onClick:()=>i.doAction("merge"),children:"合并(completed → merged)"}),c.jsx("button",{disabled:r.status!=="merged",title:r.status!=="merged"?"仅 merged 可提 PR":"",onClick:()=>i.doAction("pr"),childrLine truncated
`);for(;ot>=0;){const rt=vt.slice(0,ot);vt=vt.slice(ot+2);for(const G of rt.split(` `);for(;nt>=0;){const rt=mt.slice(0,nt);mt=mt.slice(nt+2);for(const G of rt.split(`
`))if(G.startsWith("data: "))try{const N=JSON.parse(G.slice(6));N.waiting?S(N.task_status==="interrupted"?"任务已中断,等待续跑…":N.task_status==="paused"?"任务已暂停…":"等待会话…"):N.image&&(f("data:image/jpeg;base64,"+N.image),S("直播中"));const ut={};N.holder&&(ut.holder=N.holder),N.url&&(ut.url=N.url),N.title!==void 0&&(ut.title=N.title),Object.keys(ut).length>0&&C(St=>({...St||{},...ut}))}catch{}ot=vt.indexOf(` `))if(G.startsWith("data: "))try{const A=JSON.parse(G.slice(6));A.waiting?S(A.task_status==="interrupted"?"任务已中断,等待续跑…":A.task_status==="paused"?"任务已暂停…":"等待会话…"):A.image&&(f("data:image/jpeg;base64,"+A.image),S("直播中"));const ut={};A.holder&&(ut.holder=A.holder),A.url&&(ut.url=A.url),A.title!==void 0&&(ut.title=A.title),Object.keys(ut).length>0&&C(bt=>({...bt||{},...ut}))}catch{}nt=mt.indexOf(`
`)}}S("流已断开")}catch(et){if(et.name==="AbortError")return;R(et instanceof Error?et.message:String(et)),S("未开始")}finally{x(!1)}},[r]),X=d.useCallback(()=>{var w;(w=k.current)==null||w.abort(),x(!1),S("已停止")},[]),ht=d.useCallback(async w=>{if(r)try{const Q=await tt(K.taskBrowserControl(r.id),"POST",{holder:w});C(Q),R("")}catch(Q){R(Q instanceof Error?Q.message:String(Q))}},[r]),gt=d.useCallback(async w=>{if(!r||(g==null?void 0:g.holder)!=="user"||!W.current)return;const Q=W.current.getBoundingClientRect(),et=Math.round((w.clientX-Q.left)/Q.width*1280),zt=Math.round((w.clientY-Q.top)/Q.height*800);try{await tt(K.taskBrowserInput(r.id),"POST",{type:"mouse_click",x:et,y:zt})}catch(Bt){R(Bt instanceof Error?Bt.message:String(Bt))}},[r,g]),xt=d.useCallback(async w=>{if(!(!r||(g==null?void 0:g.holder)!=="user")){w.preventDefault();try{w.key.length===1?await tt(K.taskBrowserInput(r.id),"POST",{type:"type",text:w.key}):await tt(K.taskBrowserInput(r.id),"POST",{type:"key",key:w.key})}catch(Q){R(Q instanceof Error?Q.message:String(Q))}}},[r,g]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情"});const Z=(g==null?void 0:g.holder)||"—",Tt=g!==null;return c.jsxs("div",{children:[c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[c.jsxs("span",{children:["状态 ",c.jsx("b",{children:A})]}),c.jsxs("span",{className:"muted",children:["会话 ",Tt?"活跃":"未激活(轮次结束即消失)"]}),c.jsxs("span",{className:"muted",children:["控制权 ",Z]}),g!=null&&g.url?c.jsx("span",{className:"muted",title:g.url,children:g.url.slice(0,80)}):null]}),c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:8},children:[Y?c.jsx("button",{type:"button",onClick:X,children:"停止观看"}):c.jsx("button",{type:"button",onClick:()=>void I(),children:"开始观看"}),c.jsx("button",{type:"button",onClick:()=>void V(),children:"刷新状态"}),Z!=="user"?c.jsx("button",{type:"button",onClick:()=>void ht("user"),disabled:!Tt,children:"接管"}):c.jsx("button",{type:"button",onClick:()=>void ht("agent"),children:"交还给 Agent"})]}),H?c.jsx("div",{className:"muted",style:{marginTop:8},children:H}):null,Z==="user"?c.jsx("div",{className:"muted",style:{marginTop:8},children:"你已接管:点画面即点击,键盘输入直达页面。交还后 Agent 续跑前会先快照确认你的改动。"}):null,c.jsx("div",{style:{marginTop:8},children:h?c.jsx("img",{ref:W,src:h,alt:"浏览器实时画面",tabIndex:0,onClick:w=>void gt(w),onKeyDown:w=>void xt(w),style:{width:"100%",maxWidth:1280,cursor:Z==="user"?"crosshair":"default",border:"1px solid var(--border, #ccc)"}}):c.jsx("div",{className:"muted",children:Tt?"点“开始观看”接入画面流(接管后点画面即点击)":"浏览器会话未激活:任务本轮未开浏览器,或轮次已结束。"})})]})}function jg(){const i=Zt(),[r,h]=d.useState("uncommittedChanges"),[f,g]=d.useState(""),[C,A]=d.useState(""),[S,H]=d.useState("");return c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"评审队列"}),c.jsxs("select",{"aria-label":"评审目标",value:r,onChange:R=>h(R.target.value),children:[c.jsx("option",{value:"uncommittedChanges",children:"未提交改动(工作区)"}),c.jsx("option",{value:"baseBranch",children:"与分支的差异"}),c.jsx("option",{value:"commit",children:"指定提交"}),c.jsx("option",{value:"custom",children:"自定义评审指令"})]}),c.jsx("input",{className:"stacked",placeholder:"与分支的差异用:分支名(如 main)",style:{width:"100%"},value:f,onChange:R=>g(R.target.value)}),c.jsx("input",{className:"stacked",placeholder:"指定提交用:提交 sha",style:{width:"100%"},value:C,onChange:R=>A(R.target.value)}),c.jsx("textarea",{placeholder:"自定义评审指令用:评审指令正文",value:S,onChange:R=>H(R.target.value)}),c.jsxs(Ht,{children:[c.jsx("button",{className:"primary",onClick:()=>void i.postReview({target_type:r,branch:f.trim()||void 0,sha:C.trim()||void 0,instructions:S.trim()||void 0}),children:"登记评审需求"}),c.jsx("button",{onClick:i.refreshReviews,children:"刷新队列"}),c.jsx("span",{className:"muted",children:i.reviewMsg})]}),c.jsx("div",{className:"muted",children:"本基线只登记评审需求(requested),不自动跑评审;评审是否执行、结果如何,以评审队列里的记录为准。"}),i.reviewError?c.jsx("div",{className:"muted",children:i.reviewError}):i.reviews.length===0?c.jsx("div",{className:"muted",children:"点任务行查看详情后,点“刷新队列”查看评审记录"}):c.jsx(cu,{children:i.reviews.map(R=>{let Y=R.target_type||"";return R.branch&&(Y+=":"+R.branch),R.sha&&(Y+=":"+String(R.sha).slice(0,12)),R.instructions&&(Y+=":"+String(R.instructions).slice(0,80)),c.jsxs("li",{children:[c.jsxs("span",{className:"row-main",children:[c.jsx("b",{children:"评审"})," ",Ne(R.id)," | ",Y," | ",c.jsx("span",{className:"st-"+(R.status||""),children:RLine truncated `)}}S("流已断开")}catch(et){if(et.name==="AbortError")return;O(et instanceof Error?et.message:String(et)),S("未开始")}finally{x(!1)}},[r]),J=d.useCallback(()=>{var D;(D=H.current)==null||D.abort(),x(!1),S("已停止")},[]),gt=d.useCallback(async D=>{if(r)try{const X=await W(F.taskBrowserControl(r.id),"POST",{holder:D});C(X),O("")}catch(X){O(X instanceof Error?X.message:String(X))}},[r]),_t=d.useCallback(async D=>{if(!r||(y==null?void 0:y.holder)!=="user"||!L.current)return;const X=L.current.getBoundingClientRect(),et=Math.round((D.clientX-X.left)/X.width*1280),xt=Math.round((D.clientY-X.top)/X.height*800);try{await W(F.taskBrowserInput(r.id),"POST",{type:"mouse_click",x:et,y:xt})}catch(Mt){O(Mt instanceof Error?Mt.message:String(Mt))}},[r,y]),Dt=d.useCallback(async D=>{if(!(!r||(y==null?void 0:y.holder)!=="user")){D.preventDefault();try{D.key.length===1?await W(F.taskBrowserInput(r.id),"POST",{type:"type",text:D.key}):await W(F.taskBrowserInput(r.id),"POST",{type:"key",key:D.key})}catch(X){O(X instanceof Error?X.message:String(X))}}},[r,y]);if(!r)return c.jsx("div",{className:"muted",children:"点任务行查看详情"});const Q=(y==null?void 0:y.holder)||"—",ft=y!==null;return c.jsxs("div",{children:[c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",alignItems:"center"},children:[c.jsxs("span",{children:["状态 ",c.jsx("b",{children:M})]}),c.jsxs("span",{className:"muted",children:["会话 ",ft?"活跃":"未激活(轮次结束即消失)"]}),c.jsxs("span",{className:"muted",children:["控制权 ",Q]}),y!=null&&y.url?c.jsx("span",{className:"muted",title:y.url,children:y.url.slice(0,80)}):null]}),c.jsxs("div",{style:{display:"flex",gap:8,flexWrap:"wrap",marginTop:8},children:[Y?c.jsx("button",{type:"button",onClick:J,children:"停止观看"}):c.jsx("button",{type:"button",onClick:()=>void dt(),children:"开始观看"}),c.jsx("button",{type:"button",onClick:()=>void tt(),children:"刷新状态"}),Q!=="user"?c.jsx("button",{type:"button",onClick:()=>void gt("user"),disabled:!ft,children:"接管"}):c.jsx("button",{type:"button",onClick:()=>void gt("agent"),children:"交还给 Agent"})]}),_?c.jsx("div",{className:"muted",style:{marginTop:8},children:_}):null,Q==="user"?c.jsx("div",{className:"muted",style:{marginTop:8},children:"你已接管:点画面即点击,键盘输入直达页面。交还后 Agent 续跑前会先快照确认你的改动。"}):null,c.jsx("div",{style:{marginTop:8},children:h?c.jsx("img",{ref:L,src:h,alt:"浏览器实时画面",tabIndex:0,onClick:D=>void _t(D),onKeyDown:D=>void Dt(D),style:{width:"100%",maxWidth:1280,cursor:Q==="user"?"crosshair":"default",border:"1px solid var(--border, #ccc)"}}):c.jsx("div",{className:"muted",children:ft?"点“开始观看”接入画面流(接管后点画面即点击)":"浏览器会话未激活:任务本轮未开浏览器,或轮次已结束。"})})]})}function jg(){const i=Lt(),[r,h]=d.useState("uncommittedChanges"),[f,y]=d.useState(""),[C,M]=d.useState(""),[S,_]=d.useState("");return c.jsxs(c.Fragment,{children:[c.jsx("h3",{children:"评审队列"}),c.jsxs("select",{"aria-label":"评审目标",value:r,onChange:O=>h(O.target.value),children:[c.jsx("option",{value:"uncommittedChanges",children:"未提交改动(工作区)"}),c.jsx("option",{value:"baseBranch",children:"与分支的差异"}),c.jsx("option",{value:"commit",children:"指定提交"}),c.jsx("option",{value:"custom",children:"自定义评审指令"})]}),c.jsx("input",{className:"stacked",placeholder:"与分支的差异用:分支名(如 main)",style:{width:"100%"},value:f,onChange:O=>y(O.target.value)}),c.jsx("input",{className:"stacked",placeholder:"指定提交用:提交 sha",style:{width:"100%"},value:C,onChange:O=>M(O.target.value)}),c.jsx("textarea",{placeholder:"自定义评审指令用:评审指令正文",value:S,onChange:O=>_(O.target.value)}),c.jsxs(At,{children:[c.jsx("button",{className:"primary",onClick:()=>void i.postReview({target_type:r,branch:f.trim()||void 0,sha:C.trim()||void 0,instructions:S.trim()||void 0}),children:"登记评审需求"}),c.jsx("button",{onClick:i.refreshReviews,children:"刷新队列"}),c.jsx("span",{className:"muted",children:i.reviewMsg})]}),c.jsx("div",{className:"muted",children:"本基线只登记评审需求(requested),不自动跑评审;评审是否执行、结果如何,以评审队列里的记录为准。"}),i.reviewError?c.jsx("div",{className:"muted",children:i.reviewError}):i.reviews.length===0?c.jsx("div",{className:"muted",children:"点任务行查看详情后,点“刷新队列”查看评审记录"}):c.jsx(ou,{children:i.reviews.map(O=>{let Y=O.target_type||"";return O.branch&&(Y+=":"+O.branch),O.sha&&(Y+=":"+String(O.sha).slice(0,12)),O.instructions&&(Y+=":"+String(O.instructions).slice(0,80)),c.jsxs("li",{children:[c.jsxs("span",{className:"row-main",children:[c.jsx("b",{children:"评审"})," ",pe(O.id)," | ",Y," | ",c.jsx("span",{className:"st-"+(O.status||""),children:O.sLine truncated
<style rel="stylesheet" crossorigin>:root{--text:#1f2328;--muted:#656d76;--subtle:#6e7781;--line:#d0d7de;--line-muted:#d8dee4;--paper:#ffffff;--card:#ffffff;--subtle-bg:#f6f8fa;--inset-bg:#f6f8fa;--accent:#0969da;--accent-emphasis:#0969da;--green:#2da44e;--green-hover:#2c974b;--success:#1a7f37;--danger:#d1242f;--danger-bg:#ffebe9;--attention:#9a6700;--done:#8250df;--selected-bg:#ddf4ff;--radius:6px;--shadow:0 1px 0 rgba(27,31,36,.04);--sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif;--mono:ui-monospace,SFMono-Regular,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;--ctl:32px}*{box-sizing:border-box}html,body,#root{height:100%}body{font-family:var(--sans);margin:0;color:var(--text);background:#fff;line-height:1.5;font-size:14px}a{color:var(--accent);text-underline-offset:3px}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}.muted{color:var(--muted);font-size:12px;line-height:1.5}.err{color:var(--danger)}.ok{color:var(--success)}button{cursor:pointer;font-weight:500;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text);box-shadow:var(--shadow)}button:hover{background:var(--subtle-bg)}button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}button.primary,button.seal{background:var(--green);border-color:#1b1f2426;color:#fff}button.primary:hover,button.seal:hover{background:var(--green-hover)}button.danger{color:var(--danger);border-color:var(--line);background:#fff}button.danger:hover{background:var(--danger-bg);color:var(--danger)}.icon-btn{width:32px;height:32px;padding:0;display:grid;place-items:center;border:1px solid var(--line);border-radius:var(--radius);background:#fff;color:var(--text);font-size:16px;line-height:1}.icon-btn:hover{background:var(--subtle-bg)}.dash-topbar{position:sticky;top:0;z-index:10;background:#24292f;color:#fff;border-bottom:1px solid rgba(255,255,255,.15);padding:10px 20px 8px}.dash-topbar .muted{color:#ffffffbf}.dash-topbar-row{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.dash-brand{font-weight:600;font-size:16px;color:#fff}.dash-status{font-size:13px;font-weight:600;color:#ffffffbf}.dash-status.ok{color:#2da44e}.dash-stamp{margin-left:auto}.dash-nav{display:flex;gap:4px;flex-wrap:wrap;margin-top:8px}.dash-nav a{color:#fff;text-decoration:none;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}.dash-nav a:hover{background:#ffffff1f;text-decoration:none}.tab-nav button{background:transparent;border:0;box-shadow:none;color:#fff;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}.tab-nav button:hover{background:#ffffff1f}.tab-nav button.active{background:#ffffff2e}.dash-page-tabs{display:flex}.dash-page-tabs>.tab-content{grid-column:1 / -1;min-width:0;display:flex;flex-direction:column;gap:20px}.tab-workbench{display:grid;grid-template-columns:320px minmax(0,1fr);gap:20px;align-items:start}.tab-workbench>*{min-width:0}@media(max-width:899px){.tab-workbench{grid-template-columns:1fr}.tab-workbench #sec-dispatch{order:1}.tab-workbench #sec-tasks{order:2}.tab-workbench #sec-conv{order:3}}.dash-page{max-width:1180px;margin:0 auto;padding:20px;display:flex;flex-direction:column;gap:20px}.dash-page>*{min-width:0}.dash-section>*{min-width:0}.dash-section{scroll-margin-top:130px;display:flex;flex-direction:column;gap:16px}.dash-section.card{padding:16px}.dash-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.dash-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:16px;min-width:0}.dash-card-label{color:var(--muted);font-size:12px;font-weight:600}.dash-card-value{font-size:24px;font-weight:600;line-height:1.3;overflow-wrap:anywhere}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:none;padding:16px}.card h2{font-size:14px;margin:0 0 12px;font-weight:600}.card h3{font-size:14px;margin:24px 0 12px;padding-top:16px;border-top:1px solid var(--line);font-weight:600}.card h3:first-of-type{border-top:0;padding-top:0}.composer h2{font-size:16px}.composer textarea.prompt{min-height:148px;font-size:14px;padding:12px;border-radius:var(--radius)}.composer textarea.coord{font-family:var(--mono);font-size:13px}.bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0}.bar>button,.bar>label{display:inline-flex;align-items:center;height:var(--ctl);min-height:var(--ctl);padding:0 16px;box-sizing:border-box;white-space:nowrap}.bar>input,.bar>select{height:var(--ctl);min-height:var(--ctl)}input,select,textarea,button{font:inherit;font-size:14px;padding:5px 12px;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text)}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px #0969da4d}select{-moz-appearance:none;appearance:none;-webkit-appearance:none;cursLine truncated <style rel="stylesheet" crossorigin>:root{--text:#1f2328;--muted:#656d76;--subtle:#6e7781;--line:#d0d7de;--line-muted:#d8dee4;--paper:#ffffff;--card:#ffffff;--subtle-bg:#f6f8fa;--inset-bg:#f6f8fa;--accent:#0969da;--accent-emphasis:#0969da;--green:#2da44e;--green-hover:#2c974b;--success:#1a7f37;--danger:#d1242f;--danger-bg:#ffebe9;--attention:#9a6700;--done:#8250df;--selected-bg:#ddf4ff;--radius:6px;--shadow:0 1px 0 rgba(27,31,36,.04);--sans:-apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif;--mono:ui-monospace,SFMono-Regular,SFMono-Regular,Menlo,Consolas,"Liberation Mono",monospace;--ctl:32px}*{box-sizing:border-box}html,body,#root{height:100%}body{font-family:var(--sans);margin:0;color:var(--text);background:#fff;line-height:1.5;font-size:14px}a{color:var(--accent);text-underline-offset:3px}:focus-visible{outline:2px solid var(--accent);outline-offset:2px}@media(prefers-reduced-motion:reduce){*{scroll-behavior:auto!important;transition:none!important}}.muted{color:var(--muted);font-size:12px;line-height:1.5}.err{color:var(--danger)}.ok{color:var(--success)}button{cursor:pointer;font-weight:500;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text);box-shadow:var(--shadow)}button:hover{background:var(--subtle-bg)}button:disabled{opacity:.45;cursor:not-allowed;box-shadow:none}button.primary,button.seal{background:var(--green);border-color:#1b1f2426;color:#fff}button.primary:hover,button.seal:hover{background:var(--green-hover)}button.danger{color:var(--danger);border-color:var(--line);background:#fff}button.danger:hover{background:var(--danger-bg);color:var(--danger)}.icon-btn{width:32px;height:32px;padding:0;display:grid;place-items:center;border:1px solid var(--line);border-radius:var(--radius);background:#fff;color:var(--text);font-size:16px;line-height:1}.icon-btn:hover{background:var(--subtle-bg)}.dash-topbar{position:sticky;top:0;z-index:10;background:#24292f;color:#fff;border-bottom:1px solid rgba(255,255,255,.15);padding:10px 20px 8px}.dash-topbar .muted{color:#ffffffbf}.dash-topbar-row{display:flex;gap:14px;align-items:center;flex-wrap:wrap}.dash-brand{font-weight:600;font-size:16px;color:#fff}.dash-status{font-size:13px;font-weight:600;color:#ffffffbf}.dash-status.ok{color:#2da44e}.dash-stamp{margin-left:auto}.dash-nav{display:flex;gap:4px;flex-wrap:wrap;margin-top:8px}.dash-nav a{color:#fff;text-decoration:none;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}.dash-nav a:hover{background:#ffffff1f;text-decoration:none}.tab-nav button{background:transparent;border:0;box-shadow:none;color:#fff;font-size:13px;font-weight:600;padding:6px 12px;border-radius:var(--radius)}.tab-nav button:hover{background:#ffffff1f}.tab-nav button.active{background:#ffffff2e}.dash-page-tabs{display:flex}.dash-page-tabs>.tab-content{grid-column:1 / -1;min-width:0;display:flex;flex-direction:column;gap:20px}.tab-workbench{display:grid;grid-template-columns:320px minmax(0,1fr);gap:20px;align-items:start}.tab-workbench>*{min-width:0}@media(max-width:899px){.tab-workbench{grid-template-columns:1fr}.tab-workbench #sec-dispatch{order:1}.tab-workbench #sec-tasks{order:2}.tab-workbench #sec-conv{order:3}}.dash-page{max-width:1180px;margin:0 auto;padding:20px;display:flex;flex-direction:column;gap:20px}.dash-page>*{min-width:0}.dash-section>*{min-width:0}.dash-section{scroll-margin-top:130px;display:flex;flex-direction:column;gap:16px}.dash-section.card{padding:16px}.dash-cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}.dash-card{background:#fff;border:1px solid var(--line);border-radius:var(--radius);padding:16px;min-width:0}.dash-card-label{color:var(--muted);font-size:12px;font-weight:600}.dash-card-value{font-size:24px;font-weight:600;line-height:1.3;overflow-wrap:anywhere}.card{background:var(--card);border:1px solid var(--line);border-radius:var(--radius);box-shadow:none;padding:16px}.card h2{font-size:14px;margin:0 0 12px;font-weight:600}.card h3{font-size:14px;margin:24px 0 12px;padding-top:16px;border-top:1px solid var(--line);font-weight:600}.card h3:first-of-type{border-top:0;padding-top:0}.composer h2{font-size:16px}.composer textarea.prompt{min-height:148px;font-size:14px;padding:12px;border-radius:var(--radius)}.composer textarea.coord{font-family:var(--mono);font-size:13px}.bar{display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin:12px 0}.bar>button,.bar>label{display:inline-flex;align-items:center;height:var(--ctl);min-height:var(--ctl);padding:0 16px;box-sizing:border-box;white-space:nowrap}.bar>input,.bar>select{height:var(--ctl);min-height:var(--ctl)}input,select,textarea,button{font:inherit;font-size:14px;padding:5px 12px;border-radius:var(--radius);border:1px solid var(--line);background:#fff;color:var(--text)}input:focus,select:focus,textarea:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px #0969da4d}select{-moz-appearance:none;appearance:none;-webkit-appearance:none;cursLine truncated
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>