diff --git a/AGENTS.md b/AGENTS.md index fc8ce3fa..c0a8f4e4 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -18,10 +18,10 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文 - `bun scripts/cli.ts server start`:以异步 job 启动 database、backend-core、frontend、provider-gateway 和主 server 用户服务,部署规则见 `docs/reference/deployment.md`。 - `bun scripts/cli.ts server status`:查询固定端口、容器状态、健康检查和访问 URL,判定标准见 `docs/reference/deployment.md`。 - `bun scripts/cli.ts server logs`:分页返回文件日志与 Docker 日志尾部,日志规则见 `docs/reference/observability.md`。 -- `bun scripts/cli.ts server rebuild `:以 build-first、Compose lock、no-deps force-recreate 和 post-up validation 的异步 job 重建单个服务,规则见 `docs/reference/deployment.md`。 +- `bun scripts/cli.ts server rebuild `:以 build-first、Compose lock、no-deps force-recreate 和 post-up validation 的异步 job 重建单个服务,规则见 `docs/reference/deployment.md`。 - `bun scripts/cli.ts provider attach [--master-server URL] [--up] [--force]`:在新增计算节点上生成两项配置的 provider-gateway 挂载包;默认只需要主 server URL(默认 `http://74.48.78.17/`)和唯一 Provider ID,生成的 Compose 固定 Docker socket、`pid: "host"`、`restart: always`、只读 `/workspace` 与 SSH 维护私钥挂载,规则见 `docs/reference/provider-gateway.md`。 - `bun scripts/cli.ts ssh [ssh-like args...]`:通过 provider-gateway 的 Host SSH / WSL SSH 维护桥打开近似原生 ssh 的交互会话或远端命令,并在远端 PATH 注入 `apply_patch`、`glob` 与 `skill-discover`;`apply-patch`、`py`、`skills`、结构化 `find`、`glob` 和 `argv` 子命令用于避免远端补丁、Python stdin、skill 发现与常用只读命令的嵌套转义问题,使用规则见 `docs/reference/cli.md` 和 `docs/reference/provider-gateway.md`。 -- `bun scripts/cli.ts microservice list/status/health/proxy`:管理和验证挂载在主 server 或计算节点 Docker 中的用户服务,Code Queue/Todo Note/Baidu Netdisk on main-server 与 FindJob/Pipeline/MET Nonlinear on D601 的规则见 `docs/reference/microservices.md`。 +- `bun scripts/cli.ts microservice list/status/health/proxy`:管理和验证挂载在主 server 或计算节点 Docker 中的用户服务,OA Event Flow/Code Queue/Todo Note/Baidu Netdisk on main-server 与 FindJob/Pipeline/MET Nonlinear on D601 的规则见 `docs/reference/microservices.md`。 - `bun scripts/cli.ts codex task `:按 Code Queue 任务 ID 查询初始 prompt、最后 assistant message、工具调用摘要、attempt/judge/error 和耗时,便于新任务引用历史 session。 - `bun scripts/cli.ts server stop`:以异步 job 停止固定 Compose 项目中的全部 UniDesk 服务,停止后用 `server status` 复核。 - `bun scripts/cli.ts job list` / `bun scripts/cli.ts job status latest`:查询 `.state/jobs/` 中的异步任务状态,job 机制见 `docs/reference/cli.md`。 @@ -31,10 +31,10 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文 ## Runtime - `bun`:TypeScript 运行时固定使用 Bun,组件入口和 CLI 都直接运行 `.ts` 文件,约束见 `docs/reference/config.md`。 -- `docker-compose.yml`:主 server 统一编排 core、frontend、database、本机 provider gateway、Todo Note 后端、Baidu Netdisk 后端和 Code Queue 后端,且只公开 frontend/provider ingress,服务拓扑见 `docs/reference/deployment.md`。 -- `src/components/frontend`:前端源码固定使用 TypeScript + React,`app.tsx` 只做 shell/router,左侧主模块与顶部子标签统一编译为模块前缀路由:`/ops//`、`/nodes//`、`/tasks//`、`/config//`,只有用户服务使用 `/app//` 深链接,运行总览包含通用性能面板,资源监控含曲线和进程资源排序表,Todo Note、FindJob、Pipeline、MET Nonlinear、Baidu Netdisk、Code Queue 等业务页必须拆到独立 TSX 模块,界面规则见 `docs/reference/frontend.md`。 +- `docker-compose.yml`:主 server 统一编排 core、frontend、database、本机 provider gateway、Todo Note 后端、Baidu Netdisk 后端、Code Queue 后端和 OA Event Flow 后端,且只公开 frontend/provider ingress,服务拓扑见 `docs/reference/deployment.md`。 +- `src/components/frontend`:前端源码固定使用 TypeScript + React,`app.tsx` 只做 shell/router,左侧主模块与顶部子标签统一编译为模块前缀路由:`/ops//`、`/nodes//`、`/tasks//`、`/config//`,只有用户服务使用 `/app//` 深链接,运行总览包含通用性能面板,资源监控含曲线和进程资源排序表,Todo Note、FindJob、Pipeline、MET Nonlinear、Baidu Netdisk、Code Queue、OA Event Flow 等业务页必须拆到独立 TSX 模块,界面规则见 `docs/reference/frontend.md`。 - `backend-core / frontend performance`:backend-core 暴露 `/api/performance`,frontend 暴露同源 `/api/frontend-performance` 并在 `/ops/performance/` 汇总组件请求、失败请求、内部操作和慢操作,规则见 `docs/reference/observability.md`。 -- `Pipeline OA event flow`:Pipeline 控制、事件取证和甘特图渲染必须最终 100% 由 OA 事件流驱动,交付态不得保留点对点控制、旧审核事件、旧 batch 推进、runner 私有 JSONL 或 frontend/CLI 直写状态,权威规则见 `docs/reference/pipeline-oa-event-flow.md`。 +- `Unified OA event flow`:`oa-event-flow` 是独立主 server 用户服务,提供事件表、按 tag 订阅和 Trace/STEP 统计中心,Code Queue 与 Pipeline 都必须接入统一事件流;共享契约见 `docs/reference/oa-event-flow.md`,Pipeline 专有控制流规则见 `docs/reference/pipeline-oa-event-flow.md`。 - `src/components/provider-gateway`:当前主 server `74.48.78.17` 也作为 provider gateway 接入 UniDesk,外部节点通过 `ws://74.48.78.17:18082/ws/provider` 接入,必须以 `restart: always` 部署 always-enabled 远程升级、sleep-and-validate 回滚保护和 Host SSH / WSL SSH 透传并完成自测,部署与 Playwright 公网前端验证方法见 `docs/reference/provider-gateway.md`。 - `microservices`:用户服务配置命名仍保留 `microservices`;用户服务指挂载在 UniDesk 核心服务上的用户业务能力,缺少这些服务时核心仍可运行。主 server 本地开发边界固定为只开发 UniDesk frontend;非 UniDesk 核心业务后端、Dockerfile、GPU/训练调试必须在目标计算节点通过 SSH 透传完成,Todo Note 这类明确写入主 server 的例外需单独登记,规则见 `docs/reference/microservices.md`。 - `docs/reference/e2e.md`:交付前必须执行的自测门禁、Playwright 登录、资源监控进程排序、JSON 展示断言和数据库命名卷持久化要求。 @@ -47,5 +47,6 @@ UniDesk 是一个以主 server 为统一入口的分布式工作平台;本文 - `docs/reference/microservices.md`:用户服务(兼容命名 `microservice`)的配置、代理、安全边界、Todo Note/Baidu Netdisk on main-server、FindJob/Pipeline/MET Nonlinear on D601 和验证规则。 - `docs/reference/windows-passthrough.md`:WSL provider 通过 SSH 透传调用 Windows cmd/PowerShell、Keil、COM 串口和 Windows 侧 skill 的长期规则。 - `docs/reference/constar-d601.md`:D601 上 ConStart/constar 固件工作区的 UniDesk SSH 入口、WSL skill wrapper、Keil 编译下载和串口/JSON-RPC 验证简要引导。 +- `docs/reference/oa-event-flow.md`:统一 OA 事件流微服务、事件表、tag 订阅、Trace/STEP 统计中心和前端可见性规则。 - `docs/reference/pipeline-oa-event-flow.md`:Pipeline/OA 事件流、审核/无审核流转、单步调试、甘特图渲染和最终去残留规则。 - `reference`:兼容旧路径的符号链接,指向 `docs/reference/`。 diff --git a/config.json b/config.json index 2e6c5629..a63749c4 100644 --- a/config.json +++ b/config.json @@ -126,7 +126,8 @@ "public": false, "allowedPathPrefixes": [ "/health", - "/api/" + "/api/", + "/oa/" ], "healthPath": "/health", "timeoutMs": 15000, @@ -452,6 +453,49 @@ "integrated": true } }, + { + "id": "oa-event-flow", + "name": "OA Event Flow", + "providerId": "main-server", + "description": "统一 OA 事件流与统计中心用户服务,为 Code Queue 和 Pipeline 提供事件表、tag 订阅、Trace/STEP stats 投影和事件可见性页面。", + "repository": { + "url": "https://github.com/pikasTech/unidesk", + "commitId": "b36d7f94d7a7db9527ca823a98599106096b3c91", + "dockerfile": "src/components/microservices/oa-event-flow/Dockerfile", + "composeFile": "docker-compose.yml", + "composeService": "oa-event-flow", + "containerName": "oa-event-flow-backend" + }, + "backend": { + "nodeBaseUrl": "http://oa-event-flow:4255", + "nodeBindHost": "oa-event-flow", + "nodePort": 4255, + "proxyMode": "provider-gateway-http", + "frontendOnly": true, + "public": false, + "allowedMethods": [ + "GET", + "HEAD", + "POST" + ], + "allowedPathPrefixes": [ + "/health", + "/logs", + "/api/" + ], + "healthPath": "/health", + "timeoutMs": 30000 + }, + "development": { + "providerId": "main-server", + "sshPassthrough": true, + "worktreePath": "/root/unidesk" + }, + "frontend": { + "route": "/apps/oa-event-flow", + "integrated": true + } + }, { "id": "code-queue", "name": "Code Queue", diff --git a/docker-compose.yml b/docker-compose.yml index 4481ada2..f5fa3c2c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -118,6 +118,7 @@ services: depends_on: - database - backend-core + - oa-event-flow expose: - "4222" environment: @@ -134,6 +135,7 @@ services: CODE_QUEUE_MODEL_REASONING_EFFORTS: "gpt-5.5=xhigh" CODE_QUEUE_SANDBOX: "danger-full-access" CODE_QUEUE_APPROVAL_POLICY: "never" + OA_EVENT_FLOW_BASE_URL: "http://oa-event-flow:4255" CODE_QUEUE_MAX_ATTEMPTS: "99" CODE_QUEUE_MAX_ACTIVE_QUEUES: "${UNIDESK_CODE_QUEUE_MAX_ACTIVE_QUEUES:-0}" NODE_OPTIONS: "${UNIDESK_CODE_QUEUE_NODE_OPTIONS:---max-old-space-size=768}" @@ -180,6 +182,36 @@ services: timeout: 3s retries: 20 + + oa-event-flow: + image: oa-event-flow + build: + context: . + dockerfile: src/components/microservices/oa-event-flow/Dockerfile + container_name: oa-event-flow-backend + restart: unless-stopped + depends_on: + - database + - backend-core + expose: + - "4255" + environment: + HOST: "0.0.0.0" + PORT: "4255" + DATABASE_URL: "postgres://${UNIDESK_DATABASE_USER}:${UNIDESK_DATABASE_PASSWORD}@database:5432/${UNIDESK_DATABASE_NAME}" + PIPELINE_OA_BRIDGE_BASE_URL: "http://backend-core:8080/api/microservices/pipeline/proxy" + PIPELINE_OA_BRIDGE_INTERVAL_MS: "${UNIDESK_PIPELINE_OA_BRIDGE_INTERVAL_MS:-15000}" + PIPELINE_OA_BRIDGE_RUN_LIMIT: "${UNIDESK_PIPELINE_OA_BRIDGE_RUN_LIMIT:-50}" + LOG_FILE: "/var/log/unidesk/${UNIDESK_LOG_DAY}/${UNIDESK_LOG_PREFIX}_oa-event-flow.jsonl" + UNIDESK_LOG_RETENTION_BYTES: "${UNIDESK_LOG_RETENTION_BYTES:-1GiB}" + volumes: + - ${UNIDESK_LOG_DIR}:/var/log/unidesk + healthcheck: + test: ["CMD", "bun", "-e", "fetch('http://127.0.0.1:4255/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))"] + interval: 5s + timeout: 3s + retries: 20 + project-manager: image: project-manager build: diff --git a/docs/reference/cli.md b/docs/reference/cli.md index b4052106..6e66f339 100644 --- a/docs/reference/cli.md +++ b/docs/reference/cli.md @@ -22,7 +22,7 @@ UniDesk 的统一 CLI 入口是根目录 `scripts/cli.ts`,运行方式固定 - `codex task ` 通过 Code Queue 私有代理按任务 ID 查询结构化执行摘要;默认只返回有界 prompt/response 预览、执行 Provider、工作目录、最后 assistant message、最近工具调用摘要、attempt、judge、错误、耗时和 trace 翻页提示,适合在新队列任务中引用历史 session 且避免噪声爆炸。 - `codex task --trace --tail|--from-start|--after-seq N|--before-seq N --limit N` 按页拉取 Code Queue 的逻辑 trace;响应会返回 `nextAfterSeq`、`previousBeforeSeq`、`hasMore`、`hasBefore` 和下一页/上一页命令,默认 `--trace` 取最新一页,需要完整 prompt/最后 response 时加 `--full`。 - `codex output --tail|--from-start|--after-seq N|--before-seq N --limit N [--full-text]` 按原始 output seq 分页读取底层记录;当 trace 行提示 `commandOmittedLines`、`bodyOmittedLines` 或 `rawSeqs` 时,用该命令按 seq 补取完整信息,默认仍有单条文本预览上限,显式 `--full-text` 才返回该页全文。 -- Code Queue 多队列 lane 由 `codex` 命令命名空间管理:`queues` 列表、`queue create ` 创建、`queue merge --into ` 合并、`move --queue ` 迁移;同一个 queue 内部串行执行,不同 queue 之间并行执行。合并只移动任务归属,必须保留源 queue 记录且不提供删除语义;合并后的目标 queue 按任务原 `queueEnteredAt`/`createdAt` 时间顺序串行。迁移 queued/retry_wait 任务后会立即调度目标 queue。 +- Code Queue 多队列 lane 由 `codex` 命令命名空间管理:`queues` 列表、`queue create ` 创建、`queue merge --into ` 合并、`move --queue ` 迁移;同一个 queue 内部串行执行,不同 queue 之间并行执行。合并会移动任务归属并自动删除源 queue 记录,只保留合并后的目标 queue;合并后的目标 queue 按任务原 `queueEnteredAt`/`createdAt` 时间顺序串行。迁移 queued/retry_wait 任务后会立即调度目标 queue。 - `job list` 与 `job status` 查询 `.state/jobs/` 文件系统状态,是异步命令的可观测入口。 - `debug health`、`debug dispatch` 与 `debug task` 走真实内部 core、WebSocket、数据库、provider、系统指标、Docker 状态和 Host SSH 维护桥流程,只用于开发调试,不写入 `TEST.md` 的正式验收步骤。 - `e2e run [--only pattern[,pattern...]] [--skip pattern[,pattern...]]` 使用 publicHost 派生的公开 frontend/provider ingress URL,并通过 Docker 内网验证 core API、PostgreSQL、provider self-connection、系统指标曲线、Docker 状态快照、provider.upgrade 预检和 Playwright 前端页面,是交付前的自动化 E2E 门禁;CLI 默认输出 check 状态摘要,完整诊断写入 `resultPath`,日常迭代应优先用 `--only` / `--skip` 跑最小必要集合。 diff --git a/docs/reference/e2e.md b/docs/reference/e2e.md index 34722e94..be08eda0 100644 --- a/docs/reference/e2e.md +++ b/docs/reference/e2e.md @@ -31,17 +31,17 @@ Typical targeted commands: - `bun scripts/cli.ts e2e run --only frontend --skip frontend:todo-note-integrated-visible,frontend:findjob-integrated-visible` - `bun scripts/cli.ts e2e run --only network,provider-ingress` -- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private user-service ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211`, Code Queue host port `14222` and File Browser provider port `4251` probes must fail. +- Public exposure: Docker port summary must show only frontend and provider ingress host mappings; public core、public database and known private user-service ports such as FindJob `3254`, MET Nonlinear `3288`, Todo Note `4211`, Code Queue host port `14222`, OA Event Flow `4255` and File Browser provider port `4251` probes must fail. - Core API: `docker exec unidesk-backend-core` calls internal `GET /api/overview`, which must report `dbReady: true`, `pgdata.volumeName=unidesk_pgdata_10gb`, a positive PostgreSQL database byte count, and at least one online node; internal `GET /api/performance` must report component request statistics, internal operation statistics, PGDATA usage and Code Queue PostgreSQL storage metadata. - Provider self-connection: internal `GET /api/nodes` must contain `main-server` with `status: online`, `labels.providerGatewayVersion` equal to `src/components/provider-gateway/package.json`, `labels.providerGatewayUpgradePolicy: "always-enabled"`, `labels.providerGatewayRestartPolicyOk: true`, `labels.providerGatewayPidModeOk: true`, and `labels.providerGatewayRuntimeGuardOk: true`; internal `GET /api/nodes/system-status` must contain CPU/memory/disk samples plus a non-empty process resource list sorted by memory by default; internal `GET /api/nodes/docker-status` must contain a Docker snapshot for `main-server`; every running `provider-gateway` container visible in Docker snapshots must report `restartPolicy: "always"` and `pidMode: "host"`; public provider ingress `/health` must return ok. - Provider remote control: internal `/api/dispatch` must successfully complete a real `provider.upgrade` task in `mode: "plan"` so the upgrade path is validated without recreating the running gateway during E2E. -- User services: internal `/api/microservices` must include `todo-note` and `code-queue` on `main-server`, canonical `filebrowser` on `D518`, plus `findjob`, `pipeline`, `met-nonlinear`, `claudeqq` and `filebrowser-d601` on `D601` with `public=false`; `/api/microservices/todo-note/health` must report `storage=postgres`, `/api/microservices/todo-note/proxy/api/instances` must expose the migrated Todo Note lists, and a temporary Todo Note list create/add/toggle/undo/delete cycle must succeed through the real provider-gateway proxy; `/api/microservices/code-queue/health` must return a Code Queue summary with default model `gpt-5.5`, and `/api/microservices/code-queue/proxy/api/tasks` must return queue state through the same proxy; `/api/microservices/filebrowser/health`, `/api/microservices/filebrowser-d601/health` and `/api/microservices/filebrowser/proxy/` must prove File Browser health and WebUI access through UniDesk proxy; `/api/microservices/findjob/health` and `/api/microservices/findjob/proxy/api/summary` must succeed through the real provider-gateway proxy; `/api/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:5` must return a bounded preview with `_unidesk.arrayLimits` metadata; `/api/microservices/pipeline/health`, `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` and `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` must return Pipeline health, registry/run previews and OA event-flow evidence; `/api/microservices/met-nonlinear/health`, `/api/microservices/met-nonlinear/proxy/api/queue`, `/api/microservices/met-nonlinear/proxy/api/projects?root=projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects?root=ex_projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects/config?path=` and `/api/microservices/met-nonlinear/proxy/api/images` must return the D601 TS backend health, queue/GPU policy, full project tree inputs, structured project detail and ready `met-nonlinear-ml:tf26` image status. +- User services: internal `/api/microservices` must include `todo-note`, `oa-event-flow` and `code-queue` on `main-server`, canonical `filebrowser` on `D518`, plus `findjob`, `pipeline`, `met-nonlinear`, `claudeqq` and `filebrowser-d601` on `D601` with `public=false`; `/api/microservices/todo-note/health` must report `storage=postgres`, `/api/microservices/todo-note/proxy/api/instances` must expose the migrated Todo Note lists, and a temporary Todo Note list create/add/toggle/undo/delete cycle must succeed through the real provider-gateway proxy; `/api/microservices/oa-event-flow/health`, `/api/microservices/oa-event-flow/proxy/api/diagnostics`, `/api/microservices/oa-event-flow/proxy/api/events`, `/api/microservices/oa-event-flow/proxy/api/events?tags=service:pipeline` and `/api/microservices/oa-event-flow/proxy/api/stats/trace` must prove the independent OA event table、Pipeline bridge 和 stats center are reachable through UniDesk proxy; `/api/microservices/code-queue/health` must return a Code Queue summary with default model `gpt-5.5`, and `/api/microservices/code-queue/proxy/api/tasks` must return queue state through the same proxy; `/api/microservices/filebrowser/health`, `/api/microservices/filebrowser-d601/health` and `/api/microservices/filebrowser/proxy/` must prove File Browser health and WebUI access through UniDesk proxy; `/api/microservices/findjob/health` and `/api/microservices/findjob/proxy/api/summary` must succeed through the real provider-gateway proxy; `/api/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:5` must return a bounded preview with `_unidesk.arrayLimits` metadata; `/api/microservices/pipeline/health`, `/api/microservices/pipeline/proxy/api/snapshot?__unideskArrayLimit=registry.components:8,runs:3` and `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` must return Pipeline health, registry/run previews and OA event-flow evidence; `/api/microservices/met-nonlinear/health`, `/api/microservices/met-nonlinear/proxy/api/queue`, `/api/microservices/met-nonlinear/proxy/api/projects?root=projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects?root=ex_projects&limit=500`, `/api/microservices/met-nonlinear/proxy/api/projects/config?path=` and `/api/microservices/met-nonlinear/proxy/api/images` must return the D601 TS backend health, queue/GPU policy, full project tree inputs, structured project detail and ready `met-nonlinear-ml:tf26` image status. - ClaudeQQ availability: `/api/microservices/claudeqq/health` must only pass when `ready=true`, NapCat HTTP and WebSocket are connected, and `napcat.loginState=logged_in`; `/api/microservices/claudeqq/proxy/api/napcat/login` must show the same logged-in account state and `/api/microservices/claudeqq/proxy/api/events/recent` must prove the backend can read the persistent event cache. A QR-code-only or not-logged-in NapCat state must be treated as unhealthy. - Database: the command writes an `unidesk_e2e_markers` row through `docker exec unidesk-database psql`, confirms provider state is stored in PostgreSQL, and checks Todo Note rows exist in `todo_note_instances` using the same named volume. - Pipeline OA event flow: `microservice:pipeline-oa-event-flow` must prove both no-audit and monitor-audit runs are driven by OA events end to end. The event stream must show `node-finished` as a neutral fact with `pipeline:{pipelineId}` and `epoch:{runId}` tags, OA policy as the source of downstream/audit decisions, monitor decisions as OA control events, and runner control-result evidence. E2E must fail if delivery still depends on a legacy detail audit policy flag as policy authority, independent legacy audit-request points, a legacy batch completion gate, direct monitor-to-runner calls, or frontend/CLI writes to Pipeline `.state`. - The same Pipeline OA diagnostics must fail on legacy file-transport residuals. Procedure containers, monitor sessions, UI/Gantt DTO builders and CLI fetches must consume prompt/control/stop/display evidence only from the OA event ledger and normalized HTTP read APIs; `control-prompts.jsonl`, `monitor-prompts.jsonl`, `monitor-control`, `control-events.jsonl`, monitor stop files, `.state/pipeline-runs/{runId}/control/commands/`, `PIPELINE_*_APPEND_FILE`, local JSONL append/read helpers, and monitor `/pipeline-state` mounts are forbidden in runtime source. - Pipeline live Gantt setup: when `frontend:pipeline-gantt-observation-live-running` is selected, E2E first looks for a current Pipeline run that already contains both a `node-long-running-observation` marker and a still-running execution interval. If no such candidate exists, the E2E setup starts the D601 `monitor-management-behavior-test` pipeline through `bun scripts/cli.ts ssh D601 ...` and polls the private backend proxy until the observation candidate exists; the acceptance assertion itself still opens the public frontend with Playwright and verifies the rendered arrows, absence of observation source pseudo-points, target arrow inset, and live flashing running bar through React DOM controls. -- Frontend: Playwright must open the public frontend URL derived from `network.publicHost`, not localhost or a Docker-internal URL; it logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, verifies desktop sidebar collapse and `PGDATA` overview metric, opens `运行总览 / 性能面板` to verify `Bwebui`、组件汇总、最近失败请求、内部操作汇总和最近慢操作, clicks `查看原始JSON` to verify Provider data from the frontend, confirms no raw JSON is visible before that click, opens task history to verify duration and failure diagnostics, opens resource nodes `资源监控` to verify CPU/Memory/Disk curves, the structured process resource table, default memory-desc sorting, sortable CPU column and provider upgrade precheck dispatch, opens `Docker 状态`, switches to `main-server`, and verifies the Docker Desktop-style container view including the database named volume `unidesk_pgdata_10gb`, opens `网关版本` and verifies the provider-gateway version, SSH 透传可用性、远程更新可用性 plus structured remote update records for `provider.upgrade`, then opens `用户服务 / 服务目录`、`用户服务 / Todo Note`、`用户服务 / Code Queue`、`用户服务 / FindJob`、`用户服务 / Pipeline` and `用户服务 / MET Nonlinear` to verify 主 server Todo Note/Code Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、Code Queue 队列/模型/输出/初始 `Submitted prompt`/终态任务自动加载完整 Trace/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、MiniMax 限额卡片、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Trace、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证深链接直达路由,例如公网 `http://:/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;还必须直开 `/app/code-queue/` 验证页面存在 `app-shell`、左侧主模块边栏、顶部状态栏、顶部子标签和 `code-queue-page`,防止用户服务 deep link 退化成缺 shell 的 standalone 页面;同时 `态势总览` 这类非用户服务页面应落在自己的模块前缀下,例如 `/ops/status/`。Playwright 必须覆盖默认可见时间按北京时间显示,至少包括顶部 `北京时间` 时钟、任务历史/网关版本更新时间和用户服务刷新时间,不得随浏览器本地时区漂移。Task history and provider upgrade records must not display a real sub-second duration as `0s`; MET Nonlinear running rows must show an ETA derived from backend progress or from `startedAt` plus epoch progress, and queue/completed rows must show training speed as `epoch/h`. +- Frontend: Playwright must open the public frontend URL derived from `network.publicHost`, not localhost or a Docker-internal URL; it logs in with the configured account, waits for `核心在线`, asserts that `main-server` and `Main Server Provider` are visible, verifies desktop sidebar collapse and `PGDATA` overview metric, opens `运行总览 / 性能面板` to verify `Bwebui`、组件汇总、最近失败请求、内部操作汇总和最近慢操作, clicks `查看原始JSON` to verify Provider data from the frontend, confirms no raw JSON is visible before that click, opens task history to verify duration and failure diagnostics, opens resource nodes `资源监控` to verify CPU/Memory/Disk curves, the structured process resource table, default memory-desc sorting, sortable CPU column and provider upgrade precheck dispatch, opens `Docker 状态`, switches to `main-server`, and verifies the Docker Desktop-style container view including the database named volume `unidesk_pgdata_10gb`, opens `网关版本` and verifies the provider-gateway version, SSH 透传可用性、远程更新可用性 plus structured remote update records for `provider.upgrade`, then opens `用户服务 / 服务目录`、`用户服务 / Todo Note`、`用户服务 / OA Event Flow`、`用户服务 / Code Queue`、`用户服务 / FindJob`、`用户服务 / Pipeline` and `用户服务 / MET Nonlinear` to verify 主 server Todo Note/OA Event Flow/Code Queue、D601、仓库引用、私有后端映射、Todo Note 迁移清单和树形任务、OA Event Flow 事件表和 Trace stats 表、Code Queue 队列/模型/输出/初始 `Submitted prompt`/终态任务自动加载完整 Trace/追加 prompt/打断控件、FindJob 指标和岗位预览、Pipeline 组件矩阵、MiniMax 限额卡片、结构化 OA 事件流诊断面板、React Flow 控制图、epoch 甘特图、甘特图渲染图导出、monitor 首列排序、长任务观察连线、无观察来源伪点、running node 实时闪动执行条和 OpenCode Trace、MET Nonlinear 项目库/Fork/待启动队列/当前队列/已完成/失败诊断/GPU/镜像都通过 React 控件展示。Playwright 还必须验证深链接直达路由,例如公网 `http://:/app/pipeline/` 能直接落到 Pipeline 页面,随后切到 `资源节点 / Docker 状态` 时地址栏更新为 `/nodes/docker/`,并且浏览器 history 返回链路仍能回到 `/app/pipeline/`;还必须直开 `/app/code-queue/` 验证页面存在 `app-shell`、左侧主模块边栏、顶部状态栏、顶部子标签和 `code-queue-page`,防止用户服务 deep link 退化成缺 shell 的 standalone 页面;同时 `态势总览` 这类非用户服务页面应落在自己的模块前缀下,例如 `/ops/status/`。Playwright 必须覆盖默认可见时间按北京时间显示,至少包括顶部 `北京时间` 时钟、任务历史/网关版本更新时间和用户服务刷新时间,不得随浏览器本地时区漂移。Task history and provider upgrade records must not display a real sub-second duration as `0s`; MET Nonlinear running rows must show an ETA derived from backend progress or from `startedAt` plus epoch progress, and queue/completed rows must show training speed as `epoch/h`. - Frontend dense-layout regression gate: whenever a frontend change touches Pipeline 右侧边栏、Trace timeline、详情抽屉、甘特图坐标或其他高信息密度面板, Playwright acceptance must inspect both `总高度` and `横向滚动条`. For Pipeline specifically, the OpenCode Trace session head must carry shared agent/model/session facts and the Trace body must use the same Code Queue `TraceView` styling; Playwright must fail if old `.pipeline-opencode-step`, `.pipeline-opencode-flow`, `.pipeline-step-message-card` or `.pipeline-opencode-part` user-visible styles reappear, if the Trace container introduces an internal horizontal scrollbar, or if `frontend:pipeline-gantt-frontend-y-accuracy` fails to prove the frontend `frontend-y` layout maps ticks, markers and execution bars from timestamps to y coordinates within tolerance. - OpenCode Trace must use Code Queue Trace styling and must not render the deprecated Pipeline continuous step connector; Playwright should fail if `.pipeline-opencode-flow`, `.pipeline-opencode-step` or any equivalent continuous connector/card returns to the user-visible Trace. - User service frontend assertions must wait for real backend data, not only the page skeleton. For Todo Note this means the page must show the migrated lists `CONSTAR`、`大论文`、`找工作`、`小论文`、`事务`, support creating a temporary list and task through the frontend, and delete that temporary list afterwards. The temporary list must be selected again by its unique generated name before deletion so E2E never deletes a migrated source list by accident. For FindJob this means the page must show a numeric `岗位总量`, `HEALTH OK`, and a non-empty `PREVIEW` count such as `40/1463 PREVIEW`; for Pipeline this means the page must show `Pipeline v2 工作台`, `Health OK`, a numeric component count, a non-empty React Flow control graph, `控制图`, `Epoch 甘特图`, and after clicking a Gantt execution line it must show `OpenCode Trace` rendered by the shared Code Queue-style Trace component with messages and tool-call groups; for MET Nonlinear this means the page must show `MET Nonlinear 训练编排`, `Health OK`, `Fork Project`, `加入待启动队列`, `启动队列`, `当前队列`, 最大并发设置、task queue and GPU/image panels, and must not show the removed hard-coded `创建10个10轮任务` frontend entry. The MET Nonlinear project library must render `projects/` and `ex_projects/` as a true path tree with folder Project counts; clicking a project row must open a structured detail panel containing `config.json`, `data/ 训练状态`, `模型参数`, `指标` and a parameter count such as `Total Params`; clicking a completed/current/failed job row must open a structured job detail and both the row and detail must show `epoch/h`. Full MET Nonlinear acceptance is driven by public frontend controls: choose a visible source Project, set batch size, epochs and max concurrency in inputs, fork into `projects/unidesk_forks/`, stage the selected forks, start the queue, and verify completed rows plus automatic `metnl-train-*` container removal; loading placeholders like `--` or empty states are not sufficient for E2E success. @@ -55,7 +55,7 @@ Remote update records in the frontend are covered by the same rule: `provider.up Provider operation availability is also covered by the structured rendering rule. `host.ssh` availability must be displayed as badges or equivalent controls derived from capabilities and `hostSsh*` labels, and remote update availability must be displayed from `provider.upgrade` capability plus the `always-enabled` policy; these fields must not require opening raw Provider JSON. -User service pages are covered by the same rule. `Todo Note` must show lists, task tree, filters, reminder input, movement controls, undo/redo and metrics as controls; `Code Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `File Browser` must show D518 as the default target, D601 as an alternate target, a screenshot export action, and an embedded upstream WebUI frame served through `/api/microservices//proxy/` with compact file rows that do not let material-icon fallback text cover file metadata; `FindJob` must show metrics, jobs and drafts as cards/tables; `Pipeline` must show component classes, React Flow graph nodes/edges, run cards, Gantt execution lines and OpenCode Trace timelines as controls; `MET Nonlinear` must show queue rows, GPU/image cards, a real path tree for the project library, structured project/job detail panels, project config preview, `data/` training state, model parameter count, metrics, progress bars, ETA, `epoch/h` speed and history diagnostics as controls; `ClaudeQQ` must show NapCat HTTP/WS/login badges, QR/login panel, event cache, subscriptions and message push controls; the full user-service config, summary, snapshot, jobs preview, drafts and run JSON can only appear after an explicit `查看原始JSON` click. +User service pages are covered by the same rule. `Todo Note` must show lists, task tree, filters, reminder input, movement controls, undo/redo and metrics as controls; `OA Event Flow` must show health, live tag stream state, event table, tag filter presets and Trace stats table as controls; `Code Queue` must show queue cards, live transcript, model/cwd/max attempt inputs, judge decision, attempt table, append prompt, interrupt and retry controls; `File Browser` must show D518 as the default target, D601 as an alternate target, a screenshot export action, and an embedded upstream WebUI frame served through `/api/microservices//proxy/` with compact file rows that do not let material-icon fallback text cover file metadata; `FindJob` must show metrics, jobs and drafts as cards/tables; `Pipeline` must show component classes, React Flow graph nodes/edges, run cards, Gantt execution lines and OpenCode Trace timelines as controls; `MET Nonlinear` must show queue rows, GPU/image cards, a real path tree for the project library, structured project/job detail panels, project config preview, `data/` training state, model parameter count, metrics, progress bars, ETA, `epoch/h` speed and history diagnostics as controls; `ClaudeQQ` must show NapCat HTTP/WS/login badges, QR/login panel, event cache, subscriptions and message push controls; the full user-service config, summary, snapshot, jobs preview, drafts, OA events and run JSON can only appear after an explicit `查看原始JSON` click. ## Public Boundary Rule diff --git a/docs/reference/frontend.md b/docs/reference/frontend.md index 644e505e..a3bf175d 100644 --- a/docs/reference/frontend.md +++ b/docs/reference/frontend.md @@ -6,7 +6,13 @@ UniDesk 前端是 React 组件化工业控制台,不追求展示型大屏效 frontend 应用源码必须使用 TypeScript + React,禁止在 `src/components/frontend` 中维护手写 `.js` / `.jsx` 应用源码。浏览器请求的 `/app.js` 只能由 frontend Bun server 从 `src/components/frontend/src/app.tsx` 及其 TSX imports 转译生成;`public/` 目录只保存 HTML/CSS 等静态资产,不提交手写 `app.js`。 -`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。用户服务前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台,`claudeqq.tsx` 承载 ClaudeQQ QQ 消息网关工作台,`baidu-netdisk.tsx` 承载 Baidu Netdisk 存储工作台,`code-queue.tsx` 承载 Code Queue 控制台;新增用户服务也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。 +`src/components/frontend/src/app.tsx` 只承担应用 shell、登录、全局数据加载、主模块/子标签路由和通用控制台页面。用户服务前端必须模块化到独立 TSX 文件,禁止继续把所有业务页面堆进 `app.tsx`。当前长期固定入口为:`todo-note.tsx` 承载 Todo Note 工作台,`findjob.tsx` 承载 FindJob 工作台,`pipeline.tsx` 承载 Pipeline 工作台,`met-nonlinear.tsx` 承载 MET Nonlinear 训练编排工作台,`claudeqq.tsx` 承载 ClaudeQQ QQ 消息网关工作台,`baidu-netdisk.tsx` 承载 Baidu Netdisk 存储工作台,`code-queue.tsx` 承载 Code Queue 控制台,`oa-event-flow.tsx` 承载统一 OA 事件流控制台;新增用户服务也必须按同样规则新增独立页面模块,并由 `app.tsx` 只做导入和路由分发。 + +## Shared Dialog Component + +通用模态弹窗统一复用 `src/components/frontend/src/dialog.tsx` 的 `UniDeskDialog`。新增确认弹窗、选择弹窗、表单弹窗或小窗口不得在业务页面内重复手写 `modal-backdrop` / `raw-dialog` 结构;业务页面只传入 title、body、actions、test id、关闭行为和少量业务 class。这样可以统一 `role="dialog"`、`aria-modal`、关闭按钮、点击遮罩关闭、滚动边界、action 区和 E2E 定位语义。 + +Code Queue 的 queue 合并弹窗是该公共组件的首个业务复用示例;后续用户服务前端需要独立小窗口时应优先复用 `UniDeskDialog`,只有原始 JSON 查看器这类已有专用展示器可以继续保留自己的内容渲染。 ## Deployment Contract @@ -32,7 +38,7 @@ frontend 所有默认可见的时间、日期、时钟、更新时间、心跳 ## Layout -左侧边栏只切换主模块:运行总览、资源节点、任务调度、用户服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,用户服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、ClaudeQQ、Baidu Netdisk、Code Queue 只属于用户服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。 +左侧边栏只切换主模块:运行总览、资源节点、任务调度、用户服务、系统配置。顶部标签只切换当前主模块内的子功能;例如资源节点下的节点清单、资源标签、心跳状态只属于资源节点,用户服务下的服务目录、Todo Note、FindJob、Pipeline、MET Nonlinear、ClaudeQQ、Baidu Netdisk、Code Queue、OA Event Flow 只属于用户服务,和运行总览、任务调度、系统配置没有重复或共享语义。桌面端左侧边栏必须支持收起,只保留模块 code 和展开按钮,以便最大化主面板空间;移动端左侧边栏会转为顶部横向主模块条,但高度必须在不同主模块之间保持一致,并保持窄条、单行、不换行;主内容区无论内容多少都必须从顶部向下排列,空状态也不得上下居中制造大块留白。 ## Route Model @@ -88,17 +94,18 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL - `FindJob` 子标签必须把 D601 findjob 后端渲染为 UniDesk React 控件,包括岗位指标、岗位预览、草稿报告和显式原始 JSON 按钮。 - `ClaudeQQ` 子标签必须把 D601 ClaudeQQ 后端渲染为 UniDesk React 控件,包括 NapCat 容器登录二维码、NapCat HTTP/WS 状态、事件缓存、QQ 事件订阅表、订阅创建表单、消息推送表单、主用户私聊账号 `645275593` 标记、最近 QQ 事件、已发送记录和显式原始 JSON 按钮。 - `Baidu Netdisk` 子标签必须把主 server `baidu-netdisk-backend` 后端渲染为 UniDesk React 控件,包括 OAuth 设备码二维码/用户码登录、账号容量、配置工作根文件浏览(当前默认百度网盘根目录 `/`)、staging 目录上传/下载任务、上传/下载自测按钮与 MD5 结果、脱敏安全说明、日志摘要和显式原始 JSON 按钮;不得把 access token、refresh token、dlink 或 staging 文件字节流裸露到浏览器。 - - `Code Queue` 子标签必须把主 server `code-queue-backend` 后端渲染为 UniDesk React 控件,包括多 queue lane、queue 内串行、queue 间并行、queue 合并(只能合并任务归属、不能删除源 queue,合并后按原 queueEnteredAt/createdAt 时间顺序串行)、任务 ID/复制任务 ID、引用按钮、任务耗时、任务提交/批量提交、引用任务 ID、创建成功提示、清空输入、模型下拉、显式入队份数、默认模型 `gpt-5.5`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;Codex CLI-like 输出流必须始终保留任务的初始 `Submitted prompt` 和运行中 `Steer prompt`;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;Trace 首屏可以是摘要预览,但终态任务被选中后必须自动在后台加载完整 Trace,手动“加载完整 Trace”也必须从 Code Queue output archive 分页补齐早期 trace,不得把 preview 的 `hasMore=false` 当成完整历史;即使热状态为控制体积裁剪了早期 raw output,也要从结构化 `basePrompt/displayPrompt/promptHistory` 和 archive 合成完整用户输入与 agent trace,并且初始 prompt 默认显示注入前 prompt 而不是引用注入全文;当初始 prompt 含引用注入时,引用内容必须默认折叠,并只在 Trace 的初始消息中提供可展开的“最终传入 Codex 的真实完整 prompt”,不得再渲染独立 Prompt 全量卡片;多轮引用注入必须按上游/最早上下文在前、直接引用在后的顺序排列,每一轮必须有明确 `Reference Round N/M` 分割线和时间范围,不能用固定 6 轮截断引用链;点击队列引用按钮必须自动把该任务 ID 写入提交表单的引用输入框,引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task ` 的提示;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。 + - `OA Event Flow` 子标签必须把主 server `oa-event-flow-backend` 后端渲染为 UniDesk React 控件,包括服务健康、事件表、tag 过滤、SSE live 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮;默认页面不得裸铺完整事件 JSON,事件表只展示结构化摘要,完整 envelope/payload 只能通过 `查看原始JSON` 打开。 + - `Code Queue` 子标签必须把主 server `code-queue-backend` 后端渲染为 UniDesk React 控件,包括多 queue lane、queue 内串行、queue 间并行、queue 合并(点击“合并 queue”后必须用公共 `UniDeskDialog` 打开独立小窗口,用下拉菜单选择源 queue;不得把源 queue 选择控件塞进正常提交任务的 Queue 选择区;合并后自动删除源 queue,只保留合并后的目标 queue,目标 queue 按原 queueEnteredAt/createdAt 时间顺序串行)、任务 ID/复制任务 ID、引用按钮、任务耗时、任务提交/批量提交、引用任务 ID、创建成功提示、清空输入、模型下拉、显式入队份数、默认模型 `gpt-5.5`、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、运行中追加 prompt、打断、手动重试和显式原始 JSON 按钮;Codex CLI-like 输出流必须始终保留任务的初始 `Submitted prompt` 和运行中 `Steer prompt`;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;Trace 首屏可以是摘要预览,但终态任务被选中后必须自动在后台加载完整 Trace,手动“加载完整 Trace”也必须从 Code Queue output archive 分页补齐早期 trace,不得把 preview 的 `hasMore=false` 当成完整历史;即使热状态为控制体积裁剪了早期 raw output,也要从结构化 `basePrompt/displayPrompt/promptHistory` 和 archive 合成完整用户输入与 agent trace,并且初始 prompt 默认显示注入前 prompt 而不是引用注入全文;当初始 prompt 含引用注入时,引用内容必须默认折叠,并只在 Trace 的初始消息中提供可展开的“最终传入 Codex 的真实完整 prompt”,不得再渲染独立 Prompt 全量卡片;多轮引用注入必须按上游/最早上下文在前、直接引用在后的顺序排列,每一轮必须有明确 `Reference Round N/M` 分割线和时间范围,不能用固定 6 轮截断引用链;点击队列引用按钮必须自动把该任务 ID 写入提交表单的引用输入框,引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task ` 的提示;连续执行同一 prompt 应通过入队份数一次性生成多条任务,避免快速连点造成操作员误判。 - `Code Queue` 前端改进必须在同一任务内重建并上线公网 frontend,不能只修改源码或本地 bundle;重建 frontend 是无状态 WebUI 替换,不会导致 Code Queue 长期任务失败。已结束未读任务只能在 task card 边角显示类似未读消息的 `codex-unread-badge` 圆点和“标为已读”操作,不得把整张卡片改成红色/琥珀色失败态边框、背景或胶囊标签;状态栏的“结束未读”提示也不得使用失败态红色。 - `Code Queue` 前端必须把 PostgreSQL-backed backend API 作为 task、queue、readAt/未读状态和 attempt 状态的唯一数据来源;不得用 `localStorage`、`sessionStorage` 或 IndexedDB 持久化这些业务状态,也不得在后端标记已读失败时伪造本地成功。前端允许保留 React 内存态、请求 in-flight guard 和本轮页面缓存,但刷新页面或切换设备后的状态必须完全由后端 PostgreSQL 数据恢复。 - `Code Queue` 前后端通信必须采用渐进式披露:首屏只请求 queue/task 轻量摘要、必要的 selected preview 和小体积统计,不得默认拉取完整 transcript、raw output、原始 JSON 或全部历史任务;加载下一页或搜索分页时必须显式传递 `selected=0`、`includeActive=0`、`stats=0` 等价开关,避免每一页重复请求 selected/active/stats;点击/选中 task 后再按需加载 summary、prompt part、trace step、raw output 或完整 Trace。`read`/`mark all read` 应调用专用 mutation 并用后端返回的 patch 更新当前内存态,不能为了隐藏未读圆点而强制刷新完整 overview;请求仍需遵守 PostgreSQL 权威源,失败时不得本地伪造已读。Code Queue 性能问题应优先通过缩小 API 响应、分页/cursor、去重 in-flight 请求、短 TTL 且 mutation 失效的页面缓存和后端 SQL 聚合解决,避免以重写渲染层或把大 JSON 藏在 DOM/React state 中规避慢请求。 - - `Code Queue` 的 queue/session 左侧边栏必须提供 task 关键词搜索,并采用顶部对齐和内容高度优先布局:搜索栏、列表、分组和 task card 都不得用居中、space-between、stretch 或隐式等高网格去拉满侧栏高度;item 少时允许下半部分留空,不能把单个 item 拉高来铺满;每个 task card 必须显示 `最近更新: ...前` 这类相对更新时间,便于判断运行中的 Trace 是否卡住;`queued` task card 的状态徽标必须显示排队原因,例如 `QUEUED(PREV TASK)`、`QUEUED(MEM LIMIT)`。SSE/事件驱动刷新只能更新队列列表与当前选中任务的详情,不得因为其他任务产生 `task-step` 或 `task-updated` 而自动切换当前选中的 task;只有用户点击任务、提交新任务或显式切换 queue 才能改变选中任务。提交任务时必须立即锁定 prompt、引用 ID、queue、模型、工作目录、最大尝试和入队份数等输入控件,显示等待状态,并用前端 in-flight guard 阻止重复点击造成重复入队;当解析到多个待入队任务时必须显式要求用户勾选批量确认,防止 `---` 分隔或入队份数误操作导致错误传入多个任务。Trace 面板的主滚动条使用全站细窄现代滚动条;工具调用块内部的横向滚动必须可滚动但隐藏横向滚动条,避免移动端阅读被滚动条占用。公共 `TraceView` 的自动滚动必须采用 follow-tail 语义:只有当前滚动位置在底部附近时才跟随新增输出;用户手动向上滚动后立即暂停自动滚动,异步刷新不得把视图拉回底部,直到用户再次滚动到最底部才恢复自动跟随。 + - `Code Queue` 的 queue/session 左侧边栏必须提供 task 关键词搜索,并采用顶部对齐和内容高度优先布局:搜索栏、列表、分组和 task card 都不得用居中、space-between、stretch 或隐式等高网格去拉满侧栏高度;item 少时允许下半部分留空,不能把单个 item 拉高来铺满;每个 task card 必须显示 `最近更新: ...前` 这类相对更新时间,便于判断运行中的 Trace 是否卡住;`queued` task card 的状态徽标必须显示排队原因,例如 `QUEUED(PREV TASK)`、`QUEUED(MEM LIMIT)`。Code Queue 的事件驱动刷新必须订阅统一 `oa-event-flow` 的 `service:code-queue` tag stream,并消费 `trace-stats-updated` 等统计变化事件;旧 Code Queue 私有 SSE 不得作为 Trace/STEP 刷新权威。左侧 STEP、执行过程摘要页的 read/edit/run/STEP 只能显示 `oa_trace_stats` 投影值;投影缺失时显示同步中/`--`,不得回退到 task JSON、transcript 或前端 `toolCallCount` 重算。事件驱动刷新只能更新队列列表与当前选中任务的详情,不得因为其他任务产生 `task-step` 或 `task-updated` 而自动切换当前选中的 task;只有用户点击任务、提交新任务或显式切换 queue 才能改变选中任务。提交任务时必须立即锁定 prompt、引用 ID、queue、模型、工作目录、最大尝试和入队份数等输入控件,显示等待状态,并用前端 in-flight guard 阻止重复点击造成重复入队;当解析到多个待入队任务时必须显式要求用户勾选批量确认,防止 `---` 分隔或入队份数误操作导致错误传入多个任务。Trace 面板的主滚动条使用全站细窄现代滚动条;工具调用块内部的横向滚动必须可滚动但隐藏横向滚动条,避免移动端阅读被滚动条占用。公共 `TraceView` 的自动滚动必须采用 follow-tail 语义:只有当前滚动位置在底部附近时才跟随新增输出;用户手动向上滚动后立即暂停自动滚动,异步刷新不得把视图拉回底部,直到用户再次滚动到最底部才恢复自动跟随。 - 用户服务页面不得 iframe 业务旧前端、Todo Note 原 Vite 前端或 Pipeline 自身 WebUI,不得把用户服务后端端口暴露为浏览器直连 URL,也不得把业务 API 的 JSON 裸铺在页面上。 - `Pipeline` 子标签是 D601 `/home/ubuntu/pipeline` 的 UniDesk host UI。 - Pipeline 仓库自带 WebUI 前端已经废弃;UniDesk frontend 是唯一用户可见的 Pipeline UI。 - Pipeline 用户服务只提供 backend/control API,UniDesk 通过 `/api/microservices/pipeline/proxy/...` 拉取 snapshot、Gantt DTO、node detail 和控制接口。 - Pipeline 页面必须通过同源用户服务代理展示 `model/minimax-m27` 的 MiniMax 限额,包括当前窗口总量/已用/剩余、重置时间和查询状态;主界面不得展示 API key,完整 quota JSON 只能通过显式 `查看原始JSON` 打开。 - - Pipeline 控制与观测的最终权威是 100% OA 事件流;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,完整规则见 `docs/reference/pipeline-oa-event-flow.md`。 + - Pipeline 控制与观测的最终权威是统一 `oa-event-flow` 事件流与 Pipeline OA 状态机;分阶段迁移不得在交付态保留点对点控制、旧审核事件或旧 batch 推进逻辑,共享事件表与 tag 订阅规则见 `docs/reference/oa-event-flow.md`,Pipeline 控制流完整规则见 `docs/reference/pipeline-oa-event-flow.md`。 - 基础视图必须包含组件矩阵、React Flow 控制图框图、epoch 列表、运行材料索引、epoch 甘特图和 node 精细控制面板。首屏信息顺序必须把核心操作前置:`控制图` 是 Pipeline hero 之后的第一个业务面板,`Epoch 甘特图` 紧随其后;观测指标、评分器、MiniMax 限额、OA 事件流和组件矩阵只能排在这两个核心面板之后,移动端也必须保持同一 DOM 顺序。控制图和甘特图的右侧详情栏默认收起,主图占满可用宽度;点击控制图 node 或甘特图执行线/事件点后才展开对应右侧栏,并必须提供手动收起入口。 - Pipeline scorer 结果必须结构化展示为 `x/N`、通过率、scorer 状态、item pass/fail badge 和 raw inspector 入口;主界面不得裸展示 scorer JSON。 - 用户点击控制图中的 node 后,必须通过同源用户服务代理抓取该 node 执行过程,并支持向运行中 node 追加 prompt、给下次尝试下发 guide、对已完成 node 排队 modify、提交 monitor 审核 approve,以及排队 restart/redo。 @@ -125,9 +132,9 @@ frontend shell 必须把左侧主模块与顶部子标签编译为统一的 URL - run 级图形事实数据使用 `GET /api/node-control/runs/{runId}?view=timeline&tail=N`;该接口不得要求返回后端 y 坐标。 - node 级 OpenCode 明细来自用户选择后的 `GET /api/node-control/runs/{runId}/nodes/{nodeId}`,避免跨 UniDesk provider HTTP 代理时一次性拉取全 run 的大体积 step 数组。 - 这些 monitor/control 事件的权威来源是 Pipeline OA 事件流;Pipeline 后端会为每条事件自动附加当前 `epoch:{runId}` 与 `pipeline:{pipelineId}` scope tag,并在 monitor 订阅时自动追加同一组 scope 限制,避免不同 epoch 之间串流。UniDesk 只消费 Pipeline 暴露的结构化读接口,不直接依赖旧的 monitor append 文件语义;OA 事件流的完整约束见 `docs/reference/pipeline-oa-event-flow.md`。 - - prompt 点来源于 attempt 级 `controlEventRecords` 中的 prompt-delivered 事件,必要时回退到 `controlPromptRecords` / `monitorPromptRecords`。 + - prompt 点只能来源于 attempt 级 `controlEventRecords` 中的 prompt-delivered 事件;`controlPromptRecords` / `monitorPromptRecords` 只能作为旧数据取证字段,不得参与交付态 UI 渲染或补点。 - Monitor prompt 点必须将 `node-finished`、`node-long-running-observation` 作为一等结构化事件展示;`node-finished` 是中性完成事实,不得携带是否审核的权威策略字段。是否需要审核由 OA backend 根据当前 epoch 的 Pipeline config/topology 判定;审核开启时同一个 node completion 在 UI 中只保留一条完成/等待审核点,不得再生成独立 `legacy node audit request event` 或 `legacy monitor audit request event` 点。 - - 控制点和 monitor/人工控制箭头来源于 run 级 `controlEvents`,必要时回退到 `controlCommands`。 + - 控制点和 monitor/人工控制箭头只能来源于 run 级 `controlEvents`;`controlCommands` 不得作为 Gantt/UI 补点或控制显示 fallback。 - 控制事件必须依赖后端记录的 `sourceKind`、`sourceNodeId`、`targetNodeId`、`commandId`、`eventId`、`resetNodeIds` 和 `interruptedProcedureRunIds` 等结构化字段识别 monitor、UniDesk frontend、CLI 等发起者,不能通过解析 monitor 自己的 step 文本来反推。 - `Pipeline` monitor 审核与详情展示。 - Pipeline 页面必须提供结构化 `OA 事件流` 诊断面板,通过 `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` 展示 100% 事件流模式、禁止残留数、no-audit 证据、monitor 审核证据、控制 queued/applied 计数和近期 run 证据;原始 diagnostics 只能通过 `查看原始JSON` 打开。 diff --git a/docs/reference/microservices.md b/docs/reference/microservices.md index 9e6436db..f1fd38e6 100644 --- a/docs/reference/microservices.md +++ b/docs/reference/microservices.md @@ -62,6 +62,21 @@ Todo Note 首次迁移或源 JSON 修复时,在主 server 通过 Docker 内网 Todo Note 数据迁移后必须验证:`microservice proxy todo-note /api/instances` 至少能看到 `CONSTAR`、`大论文`、`找工作`、`小论文`、`事务` 五个迁移清单,总任务数不低于源数据的 100 条;再通过代理创建临时清单、添加任务、切换完成、撤销并删除临时清单,证明写入路径走 PostgreSQL 且不会污染长期数据。 + +### OA Event Flow On Main Server + +当前 OA Event Flow 作为 `id=oa-event-flow` 的用户服务登记在 `config.json`: + +- Provider:`main-server`,由 backend-core 直接访问同一 Compose 网络内的 `http://oa-event-flow:4255`,公网不发布 `4255`。 +- 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/oa-event-flow`,属于 UniDesk 自有主 server 用户服务。 +- 部署引用:UniDesk 根仓库 `docker-compose.yml` 中的 `oa-event-flow` service,Dockerfile 为 `src/components/microservices/oa-event-flow/Dockerfile`,容器名为 `oa-event-flow-backend`。 +- 数据库:事件表、projection offset、Trace/STEP stats 和 Trace step 投影写入主 PostgreSQL;服务启动时自动创建/补齐 schema,不依赖仅首次生效的 database init SQL。 +- API:`GET /health`;`POST /api/events`;`GET /api/events`;`GET /api/events/stream`;`GET /api/stats/trace`;`GET /api/diagnostics`。 +- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`。 +- UniDesk 前端:`用户服务 / OA Event Flow` React 页面负责展示服务健康、事件表、tag 过滤、live stream 状态、Trace/STEP stats 表、Code Queue/Pipeline 标签入口和显式原始 JSON 按钮。 + +OA Event Flow 在 UniDesk 语境中按共享控制面基础设施管理:不得暴露公网端口,不得把事件或统计权威状态写入 `.state/`;Code Queue 与 Pipeline 都必须通过该服务发布事实事件、订阅 tag stream 和读取统计中心。共享事件流、统计中心和完成门禁见 `docs/reference/oa-event-flow.md`。 + ### Project Manager On Main Server 当前 Project Manager 作为 `id=project-manager` 的用户服务登记在 `config.json`: @@ -109,7 +124,7 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度 ### Code Queue On Main Server -当前 Code Queue 作为 `id=code-queue` 的用户服务登记在 `config.json`: +当前 Code Queue 作为 `id=code-queue` 的用户服务登记在 `config.json`,并接入统一 `oa-event-flow` 发布 Trace/STEP 事实事件与读取统计中心: - Provider:`main-server`,由本机 provider-gateway 通过 `microservice.http` 访问同一 Compose 网络内的 `http://code-queue:4222`。 - 代码引用:`https://github.com/pikasTech/unidesk` 与配置中的 `repository.commitId`;服务源码位于 `src/components/microservices/code-queue`,属于 UniDesk 自有控制面组件。 @@ -136,7 +151,8 @@ Baidu Netdisk 在 UniDesk 语境中按纯后端服务管理:不得暴露百度 - 模型选择:默认 Codex 模型是 `gpt-5.5`,内置模型队列包含 `gpt-5.5`、`gpt-5.4-mini`、`gpt-5.4`;`gpt-5.5` 的默认 reasoning effort 必须是 `xhigh`,可通过 `CODE_QUEUE_MODEL_REASONING_EFFORTS` 追加或覆盖模型级默认值;每个入队任务可通过前端模型下拉菜单或 API 覆盖 `model`、`cwd`、`reasoningEffort` 和 `maxAttempts`,`maxAttempts` 上限为 `99`。Judge 判定 `retry` 或非用户取消类 `fail` 时必须继续已有 `codexThreadId`,不能新建 session;重试间隔使用指数退避,从 `1s` 开始,最大 `10min`。MiniMax 不可用而进入 fallback/non-LLM 判定时,当前 attempt 的 429、Too Many Requests、exceeded retry limit、overloaded、stream disconnected 等服务/限流错误应判定为 `retry`,不能当作完成;MiniMax 可用时,这些内容只能作为当前 attempt 的 factual evidence 提供给 MiniMax,不能通过硬编码覆盖 MiniMax 结果。 - 状态与日志:`main-server` 默认工作目录为容器内 `/root/unidesk`,该路径映射主 server 的 `~/unidesk`;同时保留 `/workspace` 映射以兼容历史任务。非主 server Provider 的任务默认工作目录为 `/home/ubuntu`,任务 JSON、列表、Trace 摘要和 CLI 查询都必须显示 `providerId` 与最终 `cwd`。Code Queue 的任务、queue、`readAt`/未读状态、attempt、judge、`promptHistory`、active session 元数据、控制状态和 ClaudeQQ 通知 outbox 一律以主 PostgreSQL 为权威,分别写入 `unidesk_code_queue_tasks`、`unidesk_code_queue_queues` 与 `unidesk_code_queue_notifications`;`DATABASE_URL` 是必需配置,服务不得在 PostgreSQL 缺失或不可用时进入文件存储模式。`.state/code-queue/state.json` 不再作为任务或 queue 状态存储,不得重新引入本地 JSON fallback;服务启动必须以 PostgreSQL 为唯一来源恢复队列,并把 running/judging 任务恢复为 retry_wait。主 server 内存很少,Code Queue 必须把“内存是稀缺资源”作为核心设计约束:历史任务列表、详情、统计和只读 Trace 查询优先从 PostgreSQL 直读,进程内只保留当前 running/judging、queued、retry_wait 等调度必需热任务,不得把全部历史 task JSON 长期缓存到 Bun 堆;需要短期热缓存时必须有严格上限、可裁剪、可从 PostgreSQL 和 append-only 输出归档重建。WebUI 不得用 browser `localStorage`、`sessionStorage` 或 IndexedDB 持久化 task/queue/readAt/unread 等业务状态;浏览器只能保留临时 UI 内存缓存,刷新后必须重新从后端读取 PostgreSQL 权威数据。Codex CLI-like output/Trace 的完整记录可以使用 append-only 文件作为日志型归档,但任务状态、未读状态和列表摘要不得依赖这些文件作为权威来源;`/api/tasks//transcript` 与 `/api/tasks//output` 必须能分页重建完整历史,不得因为热状态裁剪而丢失早期 trace。热 task JSON 只保留可配置窗口以保证 `/health`、`/api/tasks` 和 PostgreSQL flush 不被长任务拖死;主 server 为 Code Queue 放宽到 600M 容器预算后仍默认 `CODE_QUEUE_IN_MEMORY_OUTPUT_RECORDS=10`、`CODE_QUEUE_IN_MEMORY_EVENT_RECORDS=10`,启动时必须在 PostgreSQL 查询侧裁剪 hot output/events,并只 flush dirty task,禁止启动后无条件重写全量历史 task JSON;更高预算才允许调大热窗口。WebUI 必须支持多 queue 查看、显式创建 queue、提交时下拉选择 queue、提交时下拉选择执行 Provider,并支持把已创建且非 active 的任务移动到其他 queue;queue 内串行,queue 间默认并行且不互相排队;`CODE_QUEUE_MAX_ACTIVE_QUEUES` 仅作为显式配置的全局 active slot 上限,`0` 表示不按 queue 数量限流,内存不足时由 cgroup memory pressure 阻止新 run 并在任务响应中暴露 `QUEUED(MEM LIMIT)`。Code Queue 镜像必须内置 Playwright Chromium 浏览器与系统依赖,并使用 `bun --smol` 运行后端,保证队列任务能直接执行公网 frontend Playwright 回归且主进程内存可控,不得只在宿主机临时安装。日志写入 UniDesk `logs/{YYYYMMDD}/{startStamp}_{YYYYMMDD}_{HH}_code-queue.jsonl`,按小时切片并按日志族默认保留 `1GiB`;Codex app-server 上游产生的 `logs_*.sqlite` 只能作为短暂缓冲,必须由 Code Queue 周期性导出为 `logs/{YYYYMMDD}/{startStamp}_{YYYYMMDD}_{HH}_codex-app-server.jsonl`,导出后删除/压缩已导出的 SQLite 行,避免重新形成 `logs_2.sqlite` 大文件;`/logs` 端点返回最近结构化日志。`/health` 的 `queue.storage.primary` 必须恒为 `postgres`,并通过 `queue.storage.postgresReady`、`queue.devReady` 和 `/api/dev-ready` 暴露 PostgreSQL 可用性、develop-ready 自检、必需工具、Docker socket、`docker compose`、默认工作目录、Codex config 状态和 `/root/.ssh` 共享 SSH key 状态。Codex CLI-like 输出可能很大,服务必须节流状态持久化,禁止对每个 output delta 同步重写完整 state 导致 `/health` 和控制 API 卡死;容器 healthcheck 必须使用带超时的 HTTP 探针,不能留下堆积的无超时探针进程。 - ClaudeQQ 通知:Code Queue 可通过 backend-core 的 `claudeqq` 用户服务代理调用 `POST /api/push/text`,在每个任务进入 `succeeded`、`failed` 或 `canceled` 终态后向配置目标发送最终 response,并附带 task id、queue、状态、模型、attempt、当前 running/queued/retry_wait 数和任务总耗时;当所有 queue 进入 `0 running / 0 queued` 空闲态时,必须单独发送一次空闲提醒。通知由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED` 控制,目标由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE=private|group`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_USER_ID`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_GROUP_ID` 配置,默认私聊 `645275593`;代理基址、最终 response 最大字符数、单次超时和发送尝试次数分别由 `CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_MAX_RESPONSE_CHARS`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_TIMEOUT_MS` 和 `CODE_QUEUE_NOTIFY_CLAUDEQQ_SEND_ATTEMPTS` 配置。任务终态和队列空闲通知必须先写入 PostgreSQL outbox 表 `unidesk_code_queue_notifications` 再异步发送;不得使用 `.state/code-queue/claudeqq-notifications.json`、`CODE_QUEUE_NOTIFY_CLAUDEQQ_OUTBOX_PATH` 或任何本地 JSON 作为通知权威存储。发送失败、NapCat 离线、代理 502 或容器重启时不能丢通知,必须按 `CODE_QUEUE_NOTIFY_CLAUDEQQ_RETRY_INTERVAL_MS` 指数退避重试并跨进程/容器重启保留。`/health` 的 `queue.notifications.claudeqq` 必须暴露非敏感配置、目标配置状态和 PostgreSQL outbox 统计;`GET /api/notifications/claudeqq` 返回 outbox 明细,`POST /api/notifications/claudeqq/drain` 手动触发发送,`POST /api/notifications/claudeqq/backfill` 可按 `since` 补入某次故障窗口内已终态任务,确保 QQ/NapCat 超时或离线不会让任务完成通知永久丢失。 -- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`。Code Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。 +- OA 接入:Code Queue 后端通过 `OA_EVENT_FLOW_BASE_URL=http://oa-event-flow:4255` 发布每个 TraceView 可见执行行的 `trace-step-created`、幂等种子/乱序校正用 `trace-stats-snapshot`、`task-updated` 和 queue 事件;服务启动或手动 backfill 时必须用相同 `eventId` 幂等回放历史 TraceView 可见执行行,避免历史任务停留在旧 STEP 统计口径。前端通过 `oa-event-flow` 的 `service:code-queue` tag stream 更新 STEP 和 Trace Summary,Code Queue 私有 SSE 不再作为刷新权威。`STEP` 表示 TraceView 可见执行行数,工具调用数必须由 `readCount+editCount+runCount` 展示,不能复用 `stepCount`。 +- 代理路径:只允许 `/health`、`/logs` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`、`DELETE`、`PATCH`。Code Queue 只在 Compose 内网暴露 `4222/tcp`,不得映射或开放到公网。 - UniDesk 前端:`用户服务 / Code Queue` React 页面负责展示队列卡片、任务 ID、复制任务 ID、引用按钮、任务耗时、默认模型、模型下拉、执行 Provider 下拉、Provider 对应默认工作目录、显式入队份数、引用任务 ID、清空输入、创建成功提示、MiniMax judge 状态、Codex CLI-like 输出流、attempt 终态、追加 prompt、打断和手动重试控件;整个 agent loop 消息流统一命名为专有名词 `Trace`,`Trace` 包含 assistant message、user prompt、system event 和 tool call;Code Queue 与 Pipeline/OpenCode messages 必须共用 `src/components/frontend/src/trace.tsx` 的 Trace 公共组件、统一 Trace item 接口和 codex/opencode port 适配层;连续 read/edit/run 工具调用只是在 Trace 内折叠为可展开工具调用组,汇总格式至少包含 `xx read, xx edit, xx run`,并展示读取文件、编辑文件、运行命令和耗时摘要;最近 3 个工具调用保持展开,工具调用内容不得自动换行且必须在工具调用块内部横向滚动,工具调用组展开后不得再增加额外左侧缩进;message 与 prompt 必须自动换行,普通 message 不显示左侧项目符号缩进且永不折叠;点击队列卡片引用按钮必须自动把该任务 ID 写入提交表单的引用任务 ID 输入框;引用任务 ID 创建新任务时必须自动注入 `bun scripts/cli.ts codex task ` 的提示,让 Codex 读取初始 prompt、最后消息和工具摘要后继续;连续执行同一 prompt 应使用 `入队份数` 一次性生成多条队列任务,而不是依赖快速连点按钮;左侧 queue/session 卡片的 `QUEUED` 状态必须显示原因,例如 `QUEUED(PREV TASK)`、`QUEUED(MEM LIMIT)`、`QUEUED(ACTIVE LIMIT)`;原始任务 JSON 只能通过显式 `查看原始JSON` 打开。 ## D601 User Services @@ -184,7 +200,7 @@ FindJob 在 UniDesk 语境中按纯后端服务管理:默认页面不得 ifram - 代理路径:只允许 `/health` 和 `/api/` 前缀;允许方法为 `GET`、`HEAD`、`POST`,其中 `POST` 仅用于 `/api/node-control/...` 这类 node 控制动作;Pipeline 自身 WebUI 前端已废弃,UniDesk 只访问 Pipeline control backend。 - UniDesk 前端:`用户服务 / Pipeline` React 页面负责展示 health、组件数量、React Flow pipeline 控制图框图、epoch 列表、epoch 甘特图、OA/procedure 结构化摘要、运行材料索引、点击 node 后的执行过程抓取、append prompt、guide 和 redo/restart 控件,以及显式原始 JSON 按钮。 -Pipeline 在 UniDesk 语境中按控制与观测后端服务管理:默认页面不得 iframe 或跳转到 Pipeline 自身 WebUI,也不得直接暴露 D601 的 `18082` 到公网。UniDesk frontend 只能通过 `/api/microservices/pipeline/health`、`/api/microservices/pipeline/proxy/api/snapshot?...` 和 `/api/microservices/pipeline/proxy/api/node-control/...` 访问 Pipeline control backend;超大 snapshot 必须使用 `__unideskArrayLimit=registry.components:,runs:` 做展示级裁剪。node 控制入口必须走 Pipeline 后端 OA control API,前端不得直接写 `.state`、runner prompt 文件或命令队列;scorer 结果必须在 UniDesk Pipeline UI 中以结构化 score 卡片展示。Pipeline 控制与观测的最终态必须 100% 由 OA 事件流驱动,不得保留点对点控制、旧审核事件或旧 batch 推进逻辑,权威规则见 `docs/reference/pipeline-oa-event-flow.md`。 +Pipeline 在 UniDesk 语境中按控制与观测后端服务管理:默认页面不得 iframe 或跳转到 Pipeline 自身 WebUI,也不得直接暴露 D601 的 `18082` 到公网。UniDesk frontend 只能通过 `/api/microservices/pipeline/health`、`/api/microservices/pipeline/proxy/api/snapshot?...`、`/api/microservices/pipeline/proxy/api/node-control/...` 和供主 server `oa-event-flow` bridge 使用的 `/api/microservices/pipeline/proxy/api/oa-event-flow/diagnostics` 访问 Pipeline control backend;主 server `oa-event-flow` 的迁移 bridge 当前只允许单一路径读取 `/api/snapshot` 与 `/api/oa-event-flow/diagnostics`,发布 `pipeline-run-snapshot` 到统一事件表,不得再实现 ledger/snapshot 双路径 fallback。超大 snapshot 必须使用 `__unideskArrayLimit=registry.components:,runs:` 做展示级裁剪。node 控制入口必须走 Pipeline 后端 OA control API,前端不得直接写 `.state`、runner prompt 文件或命令队列;scorer 结果必须在 UniDesk Pipeline UI 中以结构化 score 卡片展示。Pipeline 控制与观测的最终态必须 100% 由统一 `oa-event-flow` 与 Pipeline OA 状态机驱动,不得保留点对点控制、旧审核事件或旧 batch 推进逻辑;共享事件服务规则见 `docs/reference/oa-event-flow.md`,Pipeline 专有控制流规则见 `docs/reference/pipeline-oa-event-flow.md`。 Pipeline 的一个 epoch 是同一个 pipeline 从入口到终态完整执行一遍,UniDesk 前端把同一 `pipelineId` 下的多个 run 作为多个 epoch 管理。甘特图必须从 Pipeline snapshot 中的 `startedAt`、`finishedAt`、`durationMs` 和 procedure run 摘要生成,不得按某个 pipeline 实例或 node 名称硬编码布局。甘特图纵轴按时间从上到下排列,左侧固定时间列,后续每列对应一个 node;当前可见时间窗口内没有工作区间的 node 列应自动隐藏。默认页面不得展示裸 JSON、JSONL、worker log 或 control event 行;运行材料和 node 过程只能作为一组一行的结构化索引展示,完整原始内容只能在操作员点击 `查看原始JSON` 后显示。 @@ -241,7 +257,8 @@ ClaudeQQ 在 UniDesk 语境中按消息网关后端服务管理:不得直接 - `bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects?root=projects&limit=500'` 与 `bun scripts/cli.ts microservice proxy met-nonlinear '/api/projects/config?path=projects/' --raw`:验证项目库文件树输入和结构化项目详情;详情应包含 config、progress、data、model、metrics 字段,供前端渲染训练状态、模型参数量和指标。 - `bun scripts/cli.ts microservice health claudeqq`、`bun scripts/cli.ts microservice proxy claudeqq /api/napcat/login`、`bun scripts/cli.ts microservice proxy claudeqq /api/events/recent` 和 `bun scripts/cli.ts microservice proxy claudeqq /api/events/subscriptions`:验证 ClaudeQQ 后端、NapCat 容器登录、事件订阅和私有代理链路;消息推送使用 `POST /api/push/text`,不得开放 D601 `3290/3000/3001/6099` 公网端口。 - `bun scripts/cli.ts microservice health todo-note` 与 `bun scripts/cli.ts microservice proxy todo-note /api/instances`:验证主 server Todo Note 后端、PostgreSQL 存储和本机 provider-gateway 私有代理链路。 -- `bun scripts/cli.ts microservice health code-queue` 与 `bun scripts/cli.ts microservice proxy code-queue /api/tasks`:验证主 server Code Queue 后端、PostgreSQL 强制持久化和本机 provider-gateway 私有代理链路;写入、追加 prompt、打断和 readAt/未读状态都必须由 backend 写入 PostgreSQL,frontend 不得用本地存储伪造成功状态。 +- `bun scripts/cli.ts microservice health oa-event-flow`、`bun scripts/cli.ts microservice proxy oa-event-flow /api/diagnostics --raw` 与 `bun scripts/cli.ts microservice proxy oa-event-flow '/api/events?tags=service:code-queue&limit=20' --raw`:验证统一 OA 事件流、事件表、tag 查询和统计中心。 +- `bun scripts/cli.ts microservice health code-queue` 与 `bun scripts/cli.ts microservice proxy code-queue /api/tasks`:验证主 server Code Queue 后端、PostgreSQL 强制持久化、统一 OA 事件发布和本机 provider-gateway 私有代理链路;写入、追加 prompt、打断和 readAt/未读状态都必须由 backend 写入 PostgreSQL,frontend 不得用本地存储伪造成功状态。 - `bun scripts/cli.ts microservice health filebrowser`、`bun scripts/cli.ts microservice health filebrowser-d601` 与 `bun scripts/cli.ts microservice proxy filebrowser / --max-body-bytes 2000`:验证 D518 主 File Browser 和 D601 备用 File Browser 私有代理链路;浏览器 WebUI 必须通过 `/api/microservices/filebrowser/proxy/` 或 `/api/microservices/filebrowser-d601/proxy/` 访问,不得直接开放 `4251` 公网端口。 - `bun scripts/cli.ts --main-server-ip 74.48.78.17 microservice health findjob`:在计算节点或其他非主 server 主机上通过公网 frontend remote CLI 进行同一验证,不需要主 server SSH key。 diff --git a/docs/reference/oa-event-flow.md b/docs/reference/oa-event-flow.md new file mode 100644 index 00000000..0794bb54 --- /dev/null +++ b/docs/reference/oa-event-flow.md @@ -0,0 +1,91 @@ +# Unified OA Event Flow + +Unified OA Event Flow 是 UniDesk 的事件流与统计投影基础设施。它独立部署为主 server 用户服务 `id=oa-event-flow`,为 Pipeline、Code Queue 以及后续 agent runtime 提供统一的事实事件写入、按 tag 订阅、事件表查询和统计中心读接口。Pipeline 专有的流程推进规则仍以 `docs/reference/pipeline-oa-event-flow.md` 为准;本文件只定义跨业务共享的事件存储、订阅和统计中心契约。 + +## Architecture Boundary + +- `oa-event-flow` 必须是独立微服务,运行在主 server Compose 中,只暴露 Docker 内网端口,不开放公网端口;浏览器只能通过 UniDesk frontend 同源 `/api/microservices/oa-event-flow/...` 访问。 +- 业务服务只发布事实事件或控制意图,不直接修改其他服务状态;统计中心只消费事件流更新数据库投影,并通过读接口和派生事件暴露统计变化。 +- 前端页面不得轮询业务 transcript、JSONL、runner 私有文件或旧 SSE 来推断 STEP/Trace 统计;应订阅 `trace-stats-updated` 等统计变化事件,并按需拉取 `oa_trace_stats` 投影。 +- 事件表、投影表和 projection offset 必须写入主 PostgreSQL;`.state/` 只能保存日志、缓存或可重建工件,不能作为事件或统计权威状态。 +- 兼容迁移只能在同一重构闭环内存在;交付态不得保留两套互相竞争的 STEP 统计、Trace 刷新或 Pipeline 推进权威来源。 + +## Event Envelope + +所有写入 `oa_events` 的事件都必须使用统一 envelope: + +- `eventId`:全局稳定幂等键;重复发布同一事实必须命中同一 `eventId`,不能造成重复统计。 +- `type`:事实事件或派生事件类型,例如 `trace-step-created`、`task-updated`、`node-finished`、`trace-stats-updated`。 +- `createdAt`:事件发生时间;服务端可补齐但不得覆盖调用方提供的真实事实时间。 +- `sourceKind` 与 `sourceId`:事件来源类型和来源实例,例如 `service/code-queue`、`service/pipeline`、`projection/trace-stats`。 +- `aggregateType` 与 `aggregateId`:事件所属聚合,例如 `task/`、`pipeline/`、`epoch/`。 +- `correlationId` 与可选 `causationId`:跨服务追踪和派生事件回溯链路。 +- `tags`:字符串数组,用于 scope、业务标签、订阅和投影筛选。 +- `payload`:事件事实数据;只放事实和必要摘要,不放未分页的大 transcript、完整日志或 secret。 + +## Tag Contract + +Tag 是 OA 事件流的订阅和投影索引。所有 tag 必须是稳定字符串,不依赖 UI 文案。 + +- 共享 tag:`service:`、`trace`、`stats`、`control`、`diagnostics`。 +- Code Queue scope:`service:code-queue`、`task:`、`queue:`、`attempt:`。 +- Pipeline scope:`service:pipeline`、`pipeline:`、`epoch:`、`node:`、`attempt:`、`procedure:`。 +- 订阅接口按 tag 取交集:`tags=service:code-queue,task:` 只返回同时带有这两个 tag 的事件。 +- 事件生产者必须自动附加业务 scope tag;前端只传当前页面或当前选择的 tag,不在浏览器里拼接跨业务控制语义。 + +## Event API + +`oa-event-flow` 至少提供以下后端接口: + +- `GET /health`:返回数据库 ready、事件数、投影数、SSE client 数和服务启动时间。 +- `POST /api/events`:批量或单条写入 OA 事件;服务端按 `eventId` 幂等去重,成功写入后同步推进内置投影。 +- `GET /api/events?tags=&afterSeq=&limit=`:查询事件表,默认按 `sequence ASC` 返回有界结果。 +- `GET /api/events/stream?tags=&afterSeq=`:SSE 订阅事件表;连接建立后先补发 `afterSeq` 之后的有界 backlog,再推送 live event。 +- `GET /api/stats/trace?scopeId=` 或 `scopeIds=`:读取 Trace/STEP 统计投影,`scope` 形如 `task:`、`task::attempt:` 或 `pipeline:`。 +- `GET /api/diagnostics`:返回事件表、投影表、近期事件类型、近期统计变化和订阅客户端摘要。 + +## Statistics Center + +统计中心是事件流内置投影,不由前端或业务页面临时重算: + +- `oa_trace_stats` 是 Trace/STEP 统计权威表,按 `scopeId` 保存 `statsRevision`、`stepCount`、`llmStepCount`、`readCount`、`editCount`、`runCount`、`errorCount`、`traceLineCount`、`outputMaxSeq`、attempt 摘要和 `updatedAt`;其中 `stepCount` 表示 TraceView 可见执行行数,不等同于工具调用数,工具调用数由 `readCount+editCount+runCount` 表达。 +- Code Queue 任务级 scope `task:` 只表达整个任务的累计统计;每个执行过程摘要必须使用独立 attempt scope `task::attempt:`。同一个 `trace-step-created` 事实可由统计投影同时写入任务级和 attempt 级 `oa_trace_steps/oa_trace_stats`,但前端 attempt 卡片只能读取自己的 attempt scope,不得复用任务级累计统计。 +- 投影只消费已经写入 `oa_events` 的事实事件;不得直接读取 Code Queue transcript、Pipeline `.state` 或 runner JSONL 作为实时统计来源。 +- `trace-stats-snapshot` 可以作为迁移、服务重启或事件乱序时的权威种子事件;它仍必须进入事件表,由统计投影消费,不能绕过事件流直接写投影。投影消费 `trace-step-created` 时必须以 `oa_trace_steps` 的幂等行集合更新统计,不能把 snapshot 和 step 事件对同一个 step 重复累加。 +- 投影更新后必须发布派生事件 `trace-stats-updated`,带 `stats` payload 和 `stats` tag;前端订阅该事件来更新左侧 STEP、Trace 摘要指标和 OA 可视化页面。 +- 统计投影必须按 `eventId` 幂等;重复事实不能重复增加 STEP 或 read/edit/run 计数。 + +## Code Queue Integration + +- Code Queue 不再维护独立 SSE 作为前端 Trace/STEP 刷新权威;任务输出、状态变更、queue 变更和统计快照必须发布到 `oa-event-flow`。 +- Code Queue 左侧 task card 的 `STEP`、选中 task 的 Trace Summary 和全局统计只能读取 `oa_trace_stats`;本地 task JSON 中的历史字段只能作为发布 snapshot 事件的输入,不作为前端权威统计来源。统计尚未投影完成时必须明确显示 `statsSource=unavailable` 或 `STEP --`,不得回退到 transcript、本地 `stepCount` 或前端重算。 +- 运行中每个新的 TraceView 可见执行行都必须发布 `trace-step-created`,并带 `task:`、`queue:`、`attempt:`、`service:code-queue`、`trace` tag,以及 payload 中的 `scopeId=task:`、`attemptIndex` 和 `attemptScopeId=task::attempt:`;统计中心据此幂等更新任务级累计统计和 attempt 级独立统计,message/system/error 行只增加 STEP 或 error,不伪装为工具调用。 +- Trace Summary 顶部执行摘要读取任务级 `task:`;执行过程摘要 `#` 读取 `task::attempt:`。如果 attempt scope 尚未投影完成,必须显示 `statsSource=unavailable` 或 `--`,不得回退到任务级累计统计、transcript 重算或旧本地字段。 +- 任务入队、开始、终态、移动 queue、标记已读等状态事实必须发布 `task-updated` 或更具体的事实事件,供事件表和后续审计使用。 +- Code Queue 服务启动后可对 PostgreSQL 中已有任务回放每个 TraceView 可见执行行的 `trace-step-created`,并发布 `trace-stats-snapshot` 事件完成统计中心种子同步;回放必须使用相同 `eventId` 保持幂等,不得阻塞队列恢复。历史回放必须按 attempt start 行推导 `attemptIndex`,确保重建投影时 attempt scope 可独立恢复。 + +## Pipeline Integration + +- Pipeline 运行时的 OA 事实、控制事件和 Gantt 取证事件应写入同一个 `oa-event-flow`,并带 `service:pipeline` 与 Pipeline scope tag。 +- 当 Pipeline 后端部署在远端 provider 且尚未直接写入主 server `oa-event-flow` 时,主 server `oa-event-flow` 必须通过 backend-core 内网代理启用单一路径 Pipeline snapshot bridge:只读取 Pipeline 的 `/api/snapshot` 与 `/api/oa-event-flow/diagnostics` 结构化接口,并发布稳定的 `pipeline-run-snapshot` 迁移事件,确保统一事件表中可见 `service:pipeline` 与 `epoch:` 事实。bridge 只做迁移期事件可见性接入,不得再实现“先读 ledger,失败后 snapshot”的双路径 fallback;Pipeline 直接发布到统一事件流后,应删除该 bridge。 +- Pipeline 后端可继续向 UniDesk 暴露 Pipeline 专用 snapshot、Gantt DTO、node detail 和 control API,但这些 DTO 的权威来源必须是统一 OA 事件流,而不是旧文件通道或旧 batch 推进状态。 +- UniDesk Pipeline 页面可以保留结构化 Pipeline 诊断面板;跨业务事件表、tag 订阅和原始 OA 事件可见性统一进入 `用户服务 / OA Event Flow` 页面。 +- Pipeline 接入完成态必须同时满足本文件的共享事件流契约和 `docs/reference/pipeline-oa-event-flow.md` 的 Pipeline 控制流去残留门禁。 + +## Frontend Visibility + +`用户服务 / OA Event Flow` 是事件表和统计中心的可视化入口: + +- 页面必须显示服务健康、事件总数、最新 sequence、Trace stats scope 数和 live stream 状态。 +- 页面必须提供 tag 过滤输入,展示事件表列:sequence、type、source、aggregate、tags、createdAt、payload 摘要和显式 `查看原始JSON`。 +- 页面必须展示 Trace stats 表,至少包含 scope、service、subject、STEP、read/edit/run、error、outputMaxSeq、statsRevision 和更新时间。 +- 默认界面不得裸铺完整 JSON;完整事件和统计记录只能通过显式原始 JSON 按钮打开。 + +## Completion Gates + +- `config.json`、`docker-compose.yml`、CLI `server rebuild`、`server status/logs` 和 E2E 必须认识 `oa-event-flow`。 +- Code Queue 前端事件订阅必须指向 `oa-event-flow` 的 tag stream;不得再依赖 Code Queue 私有 `/api/events` SSE 作为刷新权威。 +- Code Queue Trace Summary 和 task list 响应必须带 `traceStats.source=oa-event-flow` 或明确的 `statsSource=unavailable` 状态;unavailable 时不得返回本地重算 STEP 伪装为权威统计。 +- Code Queue TraceView 的 attempt 执行摘要必须带 `attemptScopeId`、attempt 级 `traceStats.source=oa-event-flow` 或明确的 `statsSource=unavailable`;前端收到 `task-attempt` 的 `trace-stats-updated` 时只能刷新选中任务 Trace Summary,不能把 attempt 统计覆盖到左侧 task 级 STEP。 +- Pipeline bridge 启用时,`/api/diagnostics` 必须暴露 `pipelineBridge.mode=snapshot` 或各 run 的 `bridgeMode=snapshot` 状态,`GET /api/events?tags=service:pipeline` 必须能看到带 `epoch:` 的 Pipeline 事件;远端 Pipeline 后端直接发布到统一服务后,应删除 bridge 作为完成态清理项。 +- 新增或修改 OA 事件流页面、Code Queue TraceView 或 Pipeline OA 可视化后,必须重建并验证 `oa-event-flow`、相关业务后端和 `frontend`。 diff --git a/docs/reference/pipeline-oa-event-flow.md b/docs/reference/pipeline-oa-event-flow.md index 27a24a98..2bf350a8 100644 --- a/docs/reference/pipeline-oa-event-flow.md +++ b/docs/reference/pipeline-oa-event-flow.md @@ -4,12 +4,12 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程 ## Core Principles -- OA backend 是 Pipeline runtime 的唯一控制总线;node、monitor、runner、UniDesk frontend 和 CLI 都只能通过 OA 事件表达事实、订阅事实或发出控制意图。 +- 统一 `oa-event-flow` 微服务是跨业务 OA 事件存储、tag 订阅和统计投影基础设施;Pipeline backend 在其上实现 Pipeline runtime 的唯一控制总线。node、monitor、runner、UniDesk frontend 和 CLI 都只能通过 OA 事件表达事实、订阅事实或发出控制意图。 - Node 之间不得直接启动、暂停、重试或通知彼此;monitor 不得绕过 OA 直接控制 runner;frontend 不得直接写 Pipeline `.state`、prompt 文件、命令队列或 worker 状态文件。 - 事实事件只描述已经发生的事实,策略判断由 OA 加载 Pipeline config/topology 后完成;`node-finished` 不得携带 `legacy audit policy flag`、`nextNodeIds`、`shouldStartDownstream` 这类控制决策字段。 - 控制行为必须由 OA 控制/管理事件表达,例如 start downstream、pause、resume、single-step、append prompt、guide、modify、approve、redo/restart、skip、cancel 和 retry。 - 如果 procedure 或 monitor 结束后缺少预期 OA 提交,runner 必须把它视为可恢复的 OA contract failure:在重试预算内通过 OA 事件写入明确反馈并启动 fresh attempt,不能直接把一次缺失提交升级成最终失败;预算耗尽后才 fail closed。 -- UI、CLI、E2E 和调试工具都应消费 OA 归一化读接口或通过 OA 控制 API 写入事件,不得解析 monitor 文本、worker log、runner 私有文件或旧 JSONL 文件来反推控制语义。 +- UI、CLI、E2E 和调试工具都应消费统一 OA 归一化读接口、`oa-event-flow` 事件表/tag stream 或通过 OA 控制 API 写入事件,不得解析 monitor 文本、worker log、runner 私有文件或旧 JSONL 文件来反推控制语义。 - 旧的本地文件传输通道必须完全退出控制路径:不得创建、轮询或依赖 `control-prompts.jsonl`、`monitor-prompts.jsonl`、`monitor-control`、`control-events.jsonl`、monitor stop 文件、`.state/pipeline-runs/{runId}/control/commands/` 或给 monitor 容器挂载可读 `/pipeline-state` 来完成 prompt、stop、guide、approve、redo/restart、append 或 UI/Gantt 事件取证。 ## Benchmark Prompt And Skill Boundary @@ -41,8 +41,8 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程 ## Event Identity And Tags -- 每条事件必须有稳定 `eventId`、`type`、`createdAt`、`sourceKind`、`sourceId`、`correlationId` 和可选 `causationId`,用于排序、幂等、回放和追踪控制来源。 -- Pipeline 后端必须自动附加 scope tag:`pipeline:{pipelineId}`、`epoch:{runId}`,node 相关事件还必须带 `node:{nodeId}`;attempt/procedure 相关事件应带 `attempt:{attemptId}` 或 `procedure:{procedureRunId}`。 +- 每条事件必须满足统一 OA envelope:稳定 `eventId`、`type`、`createdAt`、`sourceKind`、`sourceId`、`aggregateType`、`aggregateId`、`correlationId`、可选 `causationId`、`tags` 和 `payload`,用于排序、幂等、回放和追踪控制来源;共享字段契约见 `docs/reference/oa-event-flow.md`。 +- Pipeline 后端必须自动附加 scope tag:`service:pipeline`、`pipeline:{pipelineId}`、`epoch:{runId}`,node 相关事件还必须带 `node:{nodeId}`;attempt/procedure 相关事件应带 `attempt:{attemptId}` 或 `procedure:{procedureRunId}`。 - Monitor 在 config 中只声明要订阅的业务 tag 或事件类型,OA backend 在订阅时自动追加当前 `pipeline:{pipelineId}` 与 `epoch:{runId}` 限制,避免不同 pipeline 或 epoch 串流。 - 一次 epoch 应绑定 config snapshot 或 config version;OA 的审核、拓扑推进、并发和单步策略都以该快照为准,避免运行中配置漂移导致控制解释不一致。 - 控制事件必须有幂等键,重复投递同一个 start、approve、redo/restart 或 append prompt 时,OA 应识别为同一意图而不是重复启动或重复写入。 @@ -89,7 +89,8 @@ Pipeline 的最终控制模型必须是 100% OA 事件流驱动。开发过程 ## Migration And Completion Gates -- 分阶段重构只允许按“schema 文档与测试门禁 -> OA 事件写入 -> OA dispatcher 接管控制 -> UI/CLI 改为 OA 读写 -> 删除旧事件和旧控制路径”的方向前进,不得把双写/双读作为最终设计。 +- 分阶段重构只允许按“统一 oa-event-flow schema 文档与测试门禁 -> Pipeline/Code Queue 事实事件写入 -> OA dispatcher 接管 Pipeline 控制 -> UI/CLI 改为 OA 读写 -> 删除旧事件、旧统计和旧控制路径”的方向前进,不得把双写/双读作为最终设计。 +- 远端 Pipeline 尚未直接发布到主 server `oa-event-flow` 时,可以通过主 server Pipeline snapshot bridge 把 `/api/snapshot` 与 `/api/oa-event-flow/diagnostics` 形成的结构化运行事实以 `service:pipeline`、`pipeline:{pipelineId}` 和 `epoch:{runId}` tag 写入统一事件表;该 bridge 是迁移期单一路径接入层,只能保持幂等可见性,不能成为新的控制权威,也不得保留 ledger/snapshot 双路径 fallback。Pipeline 直接发布统一事件后必须删除该 bridge。 - 完成态必须删除或禁用以下残留:`legacy detail audit policy flag` 作为权威策略字段、`legacy node audit request event` / `legacy monitor audit request event` 独立审核请求事件、`legacy batch completion gate` 控制推进、monitor 直接调用 runner、node 直接启动下游、frontend/CLI 直接写 `.state` 文件。 - 完成态还必须删除文件传输残留:`PIPELINE_MONITOR_APPEND_FILE`、`PIPELINE_MONITOR_STOP_FILE`、`PIPELINE_MONITOR_CONTROL_DIR`、`PIPELINE_NODE_PROMPT_APPEND_FILE`、`control-events.jsonl`、`runControlEventFile`、`appendJsonLine`、`readJsonLineRecords`、`readPromptAppendFile`、`parseCommandFile`、`parseMonitorCommandFile`、`readMonitorCommands`、`fallbackMonitorInterventions`、`syntheticMonitorStopPrompt` 以及 monitor 的 `/pipeline-state` 挂载都不得出现在运行代码中。 - E2E 必须覆盖有审核与无审核两条链路:无审核时由 OA 从 `node-finished` 推进下游;有审核时由 monitor 经 OA 控制事件推进下游;事件都必须带 `pipeline:{pipelineId}` 与 `epoch:{runId}` tag。 diff --git a/scripts/cli.ts b/scripts/cli.ts index 4e9616c0..bb026f6b 100644 --- a/scripts/cli.ts +++ b/scripts/cli.ts @@ -29,7 +29,7 @@ function help(): unknown { { command: "server stop", description: "Fire-and-forget docker-compose down for the fixed UniDesk stack." }, { command: "server status", description: "Show fixed ports, containers, service health, and public URLs." }, { command: "server logs [--tail-bytes N]", description: "Return bounded tails from file logs and docker logs." }, - { command: "server rebuild ", description: "Build first, then serialize, force-recreate, and validate one Compose service." }, + { command: "server rebuild ", description: "Build first, then serialize, force-recreate, and validate one Compose service." }, { command: "provider attach [--master-server URL] [--up] [--force]", description: "Generate the minimal external provider-gateway env/compose bundle; only master server URL and provider id are required." }, { command: "ssh [ssh-like args...]", description: "Open a Host SSH / WSL SSH maintenance session through the provider-gateway bridge with built-in remote helper tools in PATH." }, { command: "ssh apply-patch [tool args...] < patch.diff", description: "Invoke the injected remote apply_patch helper directly over SSH passthrough and stream the patch from local stdin." }, @@ -46,7 +46,7 @@ function help(): unknown { { command: "schedule upsert-pgdata-backup [--time HH:MM] [--remote-base /SERVER_DATA/UNIDESK_PG_DATA]", description: "Create or update the daily PGDATA physical backup task that uploads monthly rotated archives to Baidu Netdisk." }, { command: "codex task [--trace --tail|--from-start|--after-seq N|--before-seq N --limit N] [--full]", description: "Fetch a compact Code Queue task summary; trace rows are opt-in and paged with next/previous commands to avoid output explosion." }, { command: "codex output [--tail|--from-start|--after-seq N|--before-seq N --limit N] [--full-text]", description: "Fetch paged raw Code Queue output records by seq when a trace row has omitted command/output text." }, - { command: "codex (queues | queue create | queue merge --into | move --queue )", description: "List/create/merge Code Queue lanes and move a queued task; merge preserves task queue time order and keeps source queue records." }, + { command: "codex (queues | queue create | queue merge --into | move --queue )", description: "List/create/merge Code Queue lanes and move a queued task; merge preserves task queue time order and deletes the source queue record." }, { command: "job list", description: "List async jobs from .state/jobs." }, { command: "job status [--tail-bytes N]", description: "Show job state with bounded stdout/stderr tails." }, { command: "debug health", description: "Probe internal core, nodes, system/Docker status, frontend, provider ingress, and public boundary." }, @@ -164,7 +164,7 @@ async function main(): Promise { } if (sub === "rebuild") { if (!isRebuildableService(third)) { - throw new Error("server rebuild requires one of: backend-core, frontend, provider-gateway, todo-note, code-queue, project-manager, baidu-netdisk"); + throw new Error("server rebuild requires one of: backend-core, frontend, provider-gateway, todo-note, code-queue, project-manager, baidu-netdisk, oa-event-flow"); } emitJson(commandName, rebuildService(config, third)); return; diff --git a/scripts/src/check.ts b/scripts/src/check.ts index 752e3a14..c93fb2fa 100644 --- a/scripts/src/check.ts +++ b/scripts/src/check.ts @@ -36,6 +36,7 @@ function unifiedLogRotationItem(): CheckItem { "src/components/microservices/code-queue/src/index.ts", "src/components/microservices/project-manager/src/index.ts", "src/components/microservices/baidu-netdisk/src/index.ts", + "src/components/microservices/oa-event-flow/src/index.ts", ]; const offenders = serviceFiles.flatMap((path) => { const text = readFileSync(rootPath(path), "utf8"); @@ -64,6 +65,7 @@ export function runChecks(config: UniDeskConfig): { ok: boolean; items: CheckIte fileItem("src/components/backend-core/src/index.ts"), fileItem("src/components/frontend/src/index.ts"), fileItem("src/components/provider-gateway/src/index.ts"), + fileItem("src/components/microservices/oa-event-flow/src/index.ts"), fileItem("scripts/src/e2e.ts"), unifiedLogRotationItem(), commandItem("bun:version", ["bun", "--version"]), diff --git a/scripts/src/code-queue.ts b/scripts/src/code-queue.ts index b794781a..899e5cdb 100644 --- a/scripts/src/code-queue.ts +++ b/scripts/src/code-queue.ts @@ -307,18 +307,16 @@ function compactAttemptCycle(value: unknown, full: boolean): Record[]): Record { - return steps.reduce>((counts, step) => { - const kind = String(step.kind || "message"); - counts[kind] = (counts[kind] ?? 0) + 1; - return counts; - }, {}); -} - function renderTraceConsoleRows(summary: Record, steps: Record[]): string[] { const rows: string[] = []; const execution = asRecord(summary.execution) ?? {}; - const counts = traceStepCounts(steps); + const stats = asRecord(execution.traceStats) ?? asRecord(summary.traceStats); + const statsSource = String(execution.statsSource || summary.statsSource || ""); + const stat = (key: string): string | number => { + if (!stats || statsSource !== "oa-event-flow") return "--"; + const value = Number(stats[key]); + return Number.isFinite(value) && value >= 0 ? Math.floor(value) : "--"; + }; rows.push([ `task=${summary.id ?? ""}`, `status=${summary.status ?? ""}`, @@ -326,7 +324,11 @@ function renderTraceConsoleRows(summary: Record, steps: Record< `model=${summary.model ?? ""}`, `updated=${summary.updatedAt ?? ""}`, ].filter((item) => !item.endsWith("=")).join(" ")); - rows.push(`progressive steps=${steps.length} tools=${execution.toolCallCount ?? 0} read=${execution.readCount ?? counts.explored ?? 0} edit=${execution.editCount ?? counts.edited ?? 0} run=${execution.runCount ?? counts.ran ?? 0} errors=${counts.error ?? 0}`); + const read = stat("readCount"); + const edit = stat("editCount"); + const run = stat("runCount"); + const toolCount = typeof read === "number" || typeof edit === "number" || typeof run === "number" ? Number(read === "--" ? 0 : read) + Number(edit === "--" ? 0 : edit) + Number(run === "--" ? 0 : run) : "--"; + rows.push(`progressive steps=${steps.length} tools=${toolCount} read=${read} edit=${edit} run=${run} errors=${stat("errorCount")}`); for (const step of steps) { const head = [`#${step.seq ?? "?"}`, `[${step.label ?? traceKindLabel(step.kind)}]`, step.title ?? ""] .filter((item) => String(item).length > 0) diff --git a/scripts/src/docker.ts b/scripts/src/docker.ts index 5700afd8..f95f3a5e 100644 --- a/scripts/src/docker.ts +++ b/scripts/src/docker.ts @@ -19,7 +19,7 @@ export interface ContainerStatus { ports: string; } -const rebuildableServices = ["backend-core", "frontend", "provider-gateway", "todo-note", "code-queue", "project-manager", "baidu-netdisk"] as const; +const rebuildableServices = ["backend-core", "frontend", "provider-gateway", "todo-note", "code-queue", "project-manager", "baidu-netdisk", "oa-event-flow"] as const; export type RebuildableService = typeof rebuildableServices[number]; export function isRebuildableService(value: string | undefined): value is RebuildableService { @@ -140,6 +140,7 @@ export function writeComposeEnv(config: UniDeskConfig, freshLogPrefix: boolean): UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_DEFAULT_PROVIDER_ID") || "D601", UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_IMAGE"), UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR: runtimeSecret("UNIDESK_CODE_QUEUE_DEV_CONTAINER_WORKDIR") || "/home/ubuntu", + UNIDESK_OA_EVENT_FLOW_BASE_URL: runtimeSecret("UNIDESK_OA_EVENT_FLOW_BASE_URL") || "http://oa-event-flow:4255", UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED: runtimeSecret("UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED") || "true", UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL: runtimeSecret("UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL") || "http://backend-core:8080/api/microservices/claudeqq/proxy", UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE: runtimeSecret("UNIDESK_CODE_QUEUE_NOTIFY_CLAUDEQQ_TARGET_TYPE") || "private", @@ -374,6 +375,7 @@ export async function stackStatus(config: UniDeskConfig): Promise { { name: "code-queue", containerPort: 4222, hostPort: null }, { name: "project-manager", containerPort: 4233, hostPort: null }, { name: "baidu-netdisk", containerPort: 4244, hostPort: null }, + { name: "oa-event-flow", containerPort: 4255, hostPort: null }, ], containers: dockerContainers(config), health: { @@ -411,7 +413,7 @@ export function stackLogs(config: UniDeskConfig, tailBytes: number): unknown { const currentFiles = allFiles.filter((path) => basename(path).startsWith(runtimeEnv.logPrefix)); const selectedFiles = (currentFiles.length > 0 ? currentFiles : allFiles.slice(-12)).slice(-12); const files = selectedFiles.map((path) => ({ path, name: basename(path), tail: tailFile(path, tailBytes) })); - const containerNames = ["unidesk-database", "unidesk-backend-core", "unidesk-frontend", "unidesk-provider-gateway-main", "todo-note-backend", "code-queue-backend", "project-manager-backend", "baidu-netdisk-backend"]; + const containerNames = ["unidesk-database", "unidesk-backend-core", "unidesk-frontend", "unidesk-provider-gateway-main", "todo-note-backend", "code-queue-backend", "project-manager-backend", "baidu-netdisk-backend", "oa-event-flow-backend"]; const docker = containerNames.map((name) => { const result = runCommand(["docker", "logs", "--tail", "40", name], repoRoot); return { name, exitCode: result.exitCode, stdoutTail: result.stdout.slice(-tailBytes), stderrTail: result.stderr.slice(-tailBytes) }; diff --git a/scripts/src/e2e.ts b/scripts/src/e2e.ts index a911e626..676aa9e9 100644 --- a/scripts/src/e2e.ts +++ b/scripts/src/e2e.ts @@ -38,6 +38,7 @@ const NETWORK_CHECK_NAMES = [ "network:claudeqq-public-blocked", "network:todo-note-public-blocked", "network:code-queue-public-blocked", + "network:oa-event-flow-public-blocked", "network:filebrowser-public-blocked", ] as const; @@ -58,6 +59,7 @@ const SERVICE_CHECK_NAMES = [ "microservice:catalog-met-nonlinear", "microservice:catalog-claudeqq", "microservice:catalog-todo-note", + "microservice:catalog-oa-event-flow", "microservice:catalog-code-queue", "microservice:catalog-filebrowser", "microservice:filebrowser-health", @@ -88,6 +90,12 @@ const SERVICE_CHECK_NAMES = [ "microservice:code-queue-status", "microservice:code-queue-health", "microservice:code-queue-tasks", + "microservice:oa-event-flow-status", + "microservice:oa-event-flow-health", + "microservice:oa-event-flow-diagnostics", + "microservice:oa-event-flow-events", + "microservice:oa-event-flow-pipeline-bridge", + "microservice:oa-event-flow-stats", ] as const; const DATABASE_CHECK_NAMES = [ @@ -118,6 +126,7 @@ const FRONTEND_CHECK_NAMES = [ "frontend:microservice-catalog-visible", "frontend:todo-note-integrated-visible", "frontend:findjob-integrated-visible", + "frontend:oa-event-flow-visible", "frontend:code-queue-integrated-visible", "frontend:code-queue-enqueue-await-smoke", "frontend:code-queue-summary-mobile-wrap", @@ -309,6 +318,7 @@ const LAYOUT_OVERFLOW_PAGE_TEST_IDS: Record = { "/app/pipeline/": "pipeline-page", "/app/met-nonlinear/": "met-nonlinear-page", "/app/claudeqq/": "claudeqq-page", + "/app/oa-event-flow/": "oa-event-flow-page", "/app/code-queue/": "code-queue-page", }; @@ -935,6 +945,7 @@ async function exposureChecks(config: UniDeskConfig, urls: PublicUrls, checks: E const claudeqqPublic = await fetchProbe(`http://${config.network.publicHost}:3290/health`, 2500); const todoNotePublic = await fetchProbe(`http://${config.network.publicHost}:4211/api/health`, 2500); const codeQueuePublic = await fetchProbe(`http://${config.network.publicHost}:14222/health`, 2500); + const oaEventFlowPublic = await fetchProbe(`http://${config.network.publicHost}:4255/health`, 2500); const filebrowserPublic = await fetchProbe(`http://${config.network.publicHost}:4251/health`, 2500); addSelectedCheck(checks, options, "network:only-frontend-provider-ports", !portsText.includes(`:${config.network.core.port}->`) && !portsText.includes(`:${config.network.database.port}->`) && !portsText.includes(":14222->"), portSummary); addSelectedCheck(checks, options, "network:core-public-blocked", (corePublic as { reachable?: boolean }).reachable === false, corePublic); @@ -944,6 +955,7 @@ async function exposureChecks(config: UniDeskConfig, urls: PublicUrls, checks: E addSelectedCheck(checks, options, "network:claudeqq-public-blocked", (claudeqqPublic as { reachable?: boolean }).reachable === false, claudeqqPublic); addSelectedCheck(checks, options, "network:todo-note-public-blocked", (todoNotePublic as { reachable?: boolean }).reachable === false, todoNotePublic); addSelectedCheck(checks, options, "network:code-queue-public-blocked", (codeQueuePublic as { reachable?: boolean }).reachable === false, codeQueuePublic); + addSelectedCheck(checks, options, "network:oa-event-flow-public-blocked", (oaEventFlowPublic as { reachable?: boolean }).reachable === false, oaEventFlowPublic); addSelectedCheck(checks, options, "network:filebrowser-public-blocked", (filebrowserPublic as { reachable?: boolean }).reachable === false, filebrowserPublic); } @@ -975,6 +987,12 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 const todoNoteStatus = dockerCoreJson("/api/microservices/todo-note/status"); const todoNoteHealth = dockerCoreJson("/api/microservices/todo-note/health"); const todoNoteInstances = dockerCoreJson("/api/microservices/todo-note/proxy/api/instances"); + const oaEventFlowStatus = dockerCoreJson("/api/microservices/oa-event-flow/status"); + const oaEventFlowHealth = dockerCoreJson("/api/microservices/oa-event-flow/health"); + const oaEventFlowDiagnostics = dockerCoreJson("/api/microservices/oa-event-flow/proxy/api/diagnostics"); + const oaEventFlowEvents = dockerCoreJson("/api/microservices/oa-event-flow/proxy/api/events?limit=10"); + const oaEventFlowPipelineEvents = dockerCoreJson("/api/microservices/oa-event-flow/proxy/api/events?tags=service:pipeline&limit=10"); + const oaEventFlowStats = dockerCoreJson("/api/microservices/oa-event-flow/proxy/api/stats/trace?limit=10"); const codeQueueStatus = dockerCoreJson("/api/microservices/code-queue/status"); const codeQueueHealth = dockerCoreJson("/api/microservices/code-queue/health"); const codeQueueTasks = dockerCoreJson("/api/microservices/code-queue/proxy/api/tasks?limit=5"); @@ -1026,6 +1044,7 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 const metNonlinear = microserviceList.find((service) => service.id === "met-nonlinear"); const claudeqq = microserviceList.find((service) => service.id === "claudeqq"); const todoNote = microserviceList.find((service) => service.id === "todo-note"); + const oaEventFlow = microserviceList.find((service) => service.id === "oa-event-flow"); const codeQueue = microserviceList.find((service) => service.id === "code-queue"); const filebrowser = microserviceList.find((service) => service.id === "filebrowser"); const filebrowserD601 = microserviceList.find((service) => service.id === "filebrowser-d601"); @@ -1043,6 +1062,11 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 const claudeqqNapcatLoginBody = (claudeqqNapcatLogin as { body?: { ok?: boolean; napcat?: { containerized?: boolean; connected?: boolean; httpConnected?: boolean; wsConnected?: boolean; loginState?: string; qrcode?: { available?: boolean; dataUrl?: string } }; login?: { ready?: boolean; state?: string } } }).body; const claudeqqEventsBody = (claudeqqEvents as { body?: { ok?: boolean; events?: unknown[]; count?: number } }).body; const claudeqqSubscriptionsBody = (claudeqqSubscriptions as { body?: { ok?: boolean; subscriptions?: unknown[]; count?: number } }).body; + const oaEventFlowHealthBody = (oaEventFlowHealth as { body?: { ok?: boolean; service?: string; databaseReady?: boolean; eventCount?: number; traceStatsCount?: number; latestSequence?: number; pipelineBridge?: { enabled?: boolean; inFlight?: boolean; lastFinishedAt?: string; lastError?: string; insertedCount?: number; duplicateCount?: number } } }).body; + const oaEventFlowDiagnosticsBody = (oaEventFlowDiagnostics as { body?: { ok?: boolean; service?: string; databaseReady?: boolean; eventCount?: number; traceStatsCount?: number; latestSequence?: number; eventTypes?: unknown[]; recentEvents?: unknown[]; recentTraceStats?: unknown[]; pipelineBridge?: { enabled?: boolean; lastFinishedAt?: string; lastError?: string; insertedCount?: number; duplicateCount?: number; runs?: Record } } }).body; + const oaEventFlowEventsBody = (oaEventFlowEvents as { body?: { ok?: boolean; events?: unknown[]; returned?: number } }).body; + const oaEventFlowPipelineEventsBody = (oaEventFlowPipelineEvents as { body?: { ok?: boolean; events?: Array<{ tags?: unknown[]; sourceId?: string; type?: string; payload?: { runId?: string; pipelineId?: string } }>; returned?: number } }).body; + const oaEventFlowStatsBody = (oaEventFlowStats as { body?: { ok?: boolean; stats?: unknown[]; returned?: number } }).body; const codeQueueHealthBody = (codeQueueHealth as { body?: { ok?: boolean; queue?: { defaultModel?: string; judgeConfigured?: boolean; modelReasoningEfforts?: Record } } }).body; const codeQueueTasksBody = (codeQueueTasks as { body?: { ok?: boolean; queue?: { defaultModel?: string; modelReasoningEfforts?: Record }; tasks?: unknown[] } }).body; const filebrowserHealthBody = (filebrowserHealth as { body?: { status?: string } }).body; @@ -1074,6 +1098,7 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 addSelectedCheck(checks, options, "microservice:catalog-met-nonlinear", (microservices as { ok?: boolean }).ok === true && metNonlinear?.providerId === "D601" && metNonlinear.backend?.public === false && metNonlinear.runtime?.container?.name === "met-nonlinear-ts", { microservices }); addSelectedCheck(checks, options, "microservice:catalog-claudeqq", (microservices as { ok?: boolean }).ok === true && claudeqq?.providerId === "D601" && claudeqq.backend?.public === false && claudeqq.runtime?.container?.name === "claudeqq-backend", { microservices }); addSelectedCheck(checks, options, "microservice:catalog-todo-note", (microservices as { ok?: boolean }).ok === true && todoNote?.providerId === config.providerGateway.id && todoNote.backend?.public === false && todoNote.runtime?.container?.name === "todo-note-backend", { microservices }); + addSelectedCheck(checks, options, "microservice:catalog-oa-event-flow", (microservices as { ok?: boolean }).ok === true && oaEventFlow?.providerId === config.providerGateway.id && oaEventFlow.backend?.public === false && oaEventFlow.runtime?.container?.name === "oa-event-flow-backend", { microservices }); addSelectedCheck(checks, options, "microservice:catalog-code-queue", (microservices as { ok?: boolean }).ok === true && codeQueue?.providerId === config.providerGateway.id && codeQueue.backend?.public === false && codeQueue.runtime?.container?.name === "code-queue-backend", { microservices }); addSelectedCheck(checks, options, "microservice:catalog-filebrowser", (microservices as { ok?: boolean }).ok === true && filebrowser?.providerId === "D518" @@ -1126,6 +1151,22 @@ async function serviceChecks(config: UniDeskConfig, urls: PublicUrls, checks: E2 addSelectedCheck(checks, options, "microservice:todo-note-health", (todoNoteHealth as { ok?: boolean; body?: { ok?: boolean; storage?: string } }).ok === true && (todoNoteHealth as { body?: { ok?: boolean; storage?: string } }).body?.ok === true && (todoNoteHealth as { body?: { storage?: string } }).body?.storage === "postgres", todoNoteHealth); addSelectedCheck(checks, options, "microservice:todo-note-migrated-data", (todoNoteInstances as { ok?: boolean }).ok === true && todoNoteRows.length >= 5 && ["CONSTAR", "大论文", "找工作", "小论文", "事务"].every((name) => todoNoteNames.includes(name)) && todoNoteRows.reduce((sum, row) => sum + Number(row.todoCount ?? 0), 0) >= 100, { todoNoteInstances }); addSelectedCheck(checks, options, "microservice:todo-note-write-path", (todoNoteCreate as { ok?: boolean }).ok === true && (todoNoteAdd as { ok?: boolean }).ok === true && (todoNoteToggle as { ok?: boolean }).ok === true && (todoNoteUndo as { ok?: boolean }).ok === true && (todoNoteDelete as { ok?: boolean }).ok === true, { todoNoteCreate, todoNoteAdd, todoNoteToggle, todoNoteUndo, todoNoteDelete }); + addSelectedCheck(checks, options, "microservice:oa-event-flow-status", (oaEventFlowStatus as { ok?: boolean }).ok === true && (oaEventFlowStatus as { body?: { microservice?: { id?: string; providerId?: string } } }).body?.microservice?.providerId === config.providerGateway.id, oaEventFlowStatus); + addSelectedCheck(checks, options, "microservice:oa-event-flow-health", (oaEventFlowHealth as { ok?: boolean }).ok === true && oaEventFlowHealthBody?.ok === true && oaEventFlowHealthBody.service === "oa-event-flow" && oaEventFlowHealthBody.databaseReady === true, oaEventFlowHealth); + addSelectedCheck(checks, options, "microservice:oa-event-flow-diagnostics", (oaEventFlowDiagnostics as { ok?: boolean }).ok === true && oaEventFlowDiagnosticsBody?.ok === true && oaEventFlowDiagnosticsBody.service === "oa-event-flow" && oaEventFlowDiagnosticsBody.databaseReady === true && Number.isFinite(oaEventFlowDiagnosticsBody.eventCount) && Array.isArray(oaEventFlowDiagnosticsBody.eventTypes), oaEventFlowDiagnostics); + addSelectedCheck(checks, options, "microservice:oa-event-flow-events", (oaEventFlowEvents as { ok?: boolean }).ok === true && oaEventFlowEventsBody?.ok === true && Array.isArray(oaEventFlowEventsBody.events), oaEventFlowEvents); + addSelectedCheck(checks, options, "microservice:oa-event-flow-pipeline-bridge", + (oaEventFlowPipelineEvents as { ok?: boolean }).ok === true + && oaEventFlowPipelineEventsBody?.ok === true + && Array.isArray(oaEventFlowPipelineEventsBody.events) + && oaEventFlowPipelineEventsBody.events.some((event) => Array.isArray(event.tags) && event.tags.includes("service:pipeline") && event.tags.some((tag) => typeof tag === "string" && tag.startsWith("epoch:"))) + && oaEventFlowDiagnosticsBody?.pipelineBridge?.enabled === true + && String(oaEventFlowDiagnosticsBody.pipelineBridge.lastError || "").length === 0, + { + pipelineBridge: oaEventFlowDiagnosticsBody?.pipelineBridge, + sampleEvents: oaEventFlowPipelineEventsBody?.events?.slice(0, 5), + }); + addSelectedCheck(checks, options, "microservice:oa-event-flow-stats", (oaEventFlowStats as { ok?: boolean }).ok === true && oaEventFlowStatsBody?.ok === true && Array.isArray(oaEventFlowStatsBody.stats), oaEventFlowStats); addSelectedCheck(checks, options, "microservice:code-queue-status", (codeQueueStatus as { ok?: boolean }).ok === true && (codeQueueStatus as { body?: { microservice?: { id?: string; providerId?: string } } }).body?.microservice?.providerId === config.providerGateway.id, codeQueueStatus); addSelectedCheck(checks, options, "microservice:code-queue-health", (codeQueueHealth as { ok?: boolean }).ok === true && codeQueueHealthBody?.ok === true && codeQueueHealthBody.queue?.defaultModel === "gpt-5.5" && codeQueueHealthBody.queue?.modelReasoningEfforts?.["gpt-5.5"] === "xhigh", codeQueueHealth); addSelectedCheck(checks, options, "microservice:code-queue-tasks", (codeQueueTasks as { ok?: boolean }).ok === true && codeQueueTasksBody?.ok === true && Array.isArray(codeQueueTasksBody.tasks) && codeQueueTasksBody.queue?.defaultModel === "gpt-5.5" && codeQueueTasksBody.queue?.modelReasoningEfforts?.["gpt-5.5"] === "xhigh", codeQueueTasks); @@ -1226,6 +1267,7 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 const needMicroserviceCatalog = wants("frontend:microservice-catalog-visible"); const needTodoNote = wants("frontend:todo-note-integrated-visible"); const needFindJob = wants("frontend:findjob-integrated-visible"); + const needOaEventFlow = wants("frontend:oa-event-flow-visible"); const needCodeQueue = wantsAny([ "frontend:code-queue-integrated-visible", "frontend:code-queue-enqueue-await-smoke", @@ -1309,6 +1351,8 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 let microserviceCatalogText = ""; let todoNoteText = ""; let findjobText = ""; + let oaEventFlowText = ""; + let oaEventFlowMetrics: any = { pageVisible: false, eventTableVisible: false, statsVisible: false, tagFilterValue: "", rawButtonCount: 0 }; let codeQueueText = ""; let codeQueueOutputText = ""; let codeQueueTaskCount = 0; @@ -1524,9 +1568,9 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 } } - if (needMicroserviceCatalog || needTodoNote || needFindJob || needCodeQueue || needClaudeqq || needRouteDeepLink || needPipeline || needMetNonlinear) { + if (needMicroserviceCatalog || needTodoNote || needFindJob || needOaEventFlow || needCodeQueue || needClaudeqq || needRouteDeepLink || needPipeline || needMetNonlinear) { await page.getByRole("button", { name: /用户服务/ }).click(); - if (needMicroserviceCatalog || needTodoNote || needFindJob || needCodeQueue || needClaudeqq || needRouteDeepLink || needPipeline || needMetNonlinear) { + if (needMicroserviceCatalog || needTodoNote || needFindJob || needOaEventFlow || needCodeQueue || needClaudeqq || needRouteDeepLink || needPipeline || needMetNonlinear) { await page.waitForSelector('[data-testid="microservice-catalog-page"]', { timeout: 10000 }); } if (needMicroserviceCatalog) { @@ -1535,6 +1579,7 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 await page.waitForSelector('[data-testid="microservice-row-met-nonlinear"]', { timeout: 10000 }); await page.waitForSelector('[data-testid="microservice-row-claudeqq"]', { timeout: 10000 }); await page.waitForSelector('[data-testid="microservice-row-todo-note"]', { timeout: 10000 }); + await page.waitForSelector('[data-testid="microservice-row-oa-event-flow"]', { timeout: 10000 }); await page.waitForSelector('[data-testid="microservice-row-code-queue"]', { timeout: 10000 }); microserviceCatalogText = await page.locator('[data-testid="microservice-catalog-page"]').innerText({ timeout: 5000 }); } @@ -1591,8 +1636,29 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 }, undefined, { timeout: 30000 }); findjobText = await page.locator('[data-testid="findjob-page"]').innerText({ timeout: 5000 }); } + if (needOaEventFlow) { + await page.getByRole("button", { name: /OA Event Flow/ }).click(); + await page.waitForSelector('[data-testid="oa-event-flow-page"]', { timeout: 10000 }); + await page.waitForSelector('[data-testid="oa-event-flow-event-table"]', { timeout: 30000 }); + await page.waitForSelector('[data-testid="oa-event-flow-stats"]', { timeout: 30000 }); + oaEventFlowText = await page.locator('[data-testid="oa-event-flow-page"]').innerText({ timeout: 5000 }); + oaEventFlowMetrics = await page.evaluate(() => { + const pageRoot = document.querySelector('[data-testid="oa-event-flow-page"]') as HTMLElement | null; + const eventTable = document.querySelector('[data-testid="oa-event-flow-event-table"]') as HTMLElement | null; + const stats = document.querySelector('[data-testid="oa-event-flow-stats"]') as HTMLElement | null; + const filter = document.querySelector('[data-testid="oa-event-flow-tag-filter"]') as HTMLInputElement | null; + return { + pageVisible: Boolean(pageRoot), + eventTableVisible: Boolean(eventTable), + statsVisible: Boolean(stats), + tagFilterValue: filter?.value || "", + rawButtonCount: pageRoot?.querySelectorAll('[data-testid^="raw-oa-"]').length ?? 0, + textPreview: pageRoot?.innerText.slice(0, 800) || "", + }; + }); + } if (needCodeQueue) { - await page.getByRole("button", { name: /Code Queue/ }).click(); + await page.getByLabel("用户服务 子功能").getByRole("button", { name: "Code Queue" }).click(); await page.waitForSelector('[data-testid="code-queue-page"]', { timeout: 10000 }); await page.waitForFunction(() => { const text = document.body.innerText; @@ -1638,6 +1704,9 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 optionCount: select?.options.length ?? 0, createButtonVisible: Boolean(button && button.offsetParent !== null), mergeButtonVisible: Boolean(mergeButton && mergeButton.offsetParent !== null), + mergeButtonDisabled: Boolean(mergeButton?.disabled), + mergeSourceInlineMissing: document.querySelector('[data-testid="code-queue-task-form"] [data-testid="codex-merge-source-queue-select"]') === null, + mergeDialogMissingBeforeClick: document.querySelector('[data-testid="codex-merge-queue-dialog"]') === null, oldInputMissing: document.querySelector('[data-testid="code-queue-id-input"]') === null, promptDefaultEmpty: (prompt?.value || "") === "", providerValue: provider?.value || "", @@ -2101,6 +2170,9 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 optionCount: select?.options.length ?? 0, createButtonVisible: Boolean(button && button.offsetParent !== null), mergeButtonVisible: Boolean(mergeButton && mergeButton.offsetParent !== null), + mergeButtonDisabled: Boolean(mergeButton?.disabled), + mergeSourceInlineMissing: document.querySelector('[data-testid="code-queue-task-form"] [data-testid="codex-merge-source-queue-select"]') === null, + mergeDialogMissingBeforeClick: document.querySelector('[data-testid="codex-merge-queue-dialog"]') === null, oldInputMissing: document.querySelector('[data-testid="code-queue-id-input"]') === null, promptDefaultEmpty: (prompt?.value || "") === "", providerValue: provider?.value || "", @@ -2112,6 +2184,26 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 }; }); codeQueuePromptDefaultEmpty = Boolean(codeQueueSubmitQueueControl.promptDefaultEmpty); + if (codeQueueSubmitQueueControl.mergeButtonVisible && !codeQueueSubmitQueueControl.mergeButtonDisabled) { + await page.getByTestId("codex-merge-queue-button").click(); + await page.waitForSelector('[data-testid="codex-merge-queue-dialog"]', { timeout: 5000 }); + const mergeDialogMetrics = await page.evaluate(() => { + const dialog = document.querySelector('[data-testid="codex-merge-queue-dialog"]') as HTMLElement | null; + const select = document.querySelector('[data-testid="codex-merge-queue-dialog"] [data-testid="codex-merge-source-queue-select"]') as HTMLSelectElement | null; + const text = dialog?.textContent || ""; + return { + mergeDialogVisible: Boolean(dialog && dialog.offsetParent !== null), + mergeDialogSelectVisible: Boolean(select && select.offsetParent !== null), + mergeDialogSourceOptionCount: select?.options.length ?? 0, + mergeDialogSelectInsideSubmitForm: Boolean(document.querySelector('[data-testid="code-queue-task-form"] [data-testid="codex-merge-source-queue-select"]')), + mergeDialogUsesCommonComponent: Boolean(dialog?.classList.contains("unidesk-dialog")), + mergeDialogDeleteNoteVisible: text.includes("自动删除源 queue"), + }; + }); + codeQueueSubmitQueueControl = { ...codeQueueSubmitQueueControl, ...mergeDialogMetrics }; + await page.getByTestId("codex-merge-queue-cancel").click(); + await page.waitForSelector('[data-testid="codex-merge-queue-dialog"]', { state: "detached", timeout: 5000 }).catch(() => undefined); + } codeQueueSwitchMetrics = await page.locator('[data-testid="code-queue-filter-select"] option').evaluateAll((options) => ({ optionCount: options.length, queueValues: options.map((option) => (option as HTMLOptionElement).value).filter((value) => value !== "__all__"), @@ -2580,10 +2672,23 @@ async function frontendCheck(config: UniDeskConfig, urls: PublicUrls, checks: E2 addSelectedCheck(checks, options, "frontend:gateway-duration-subsecond-visible", gatewayHasSubsecondDuration && !gatewayHasRoundedZeroDuration, { gatewayHasSubsecondDuration, gatewayHasRoundedZeroDuration, gatewayTextPreview: gatewayText.slice(0, 900) }); addSelectedCheck(checks, options, "frontend:provider-operation-availability-visible", sshAvailabilityTexts.length >= 1 && upgradeAvailabilityTexts.length >= 1 && sshAvailabilityTexts.every((text) => text.includes("SSH 透传")) && upgradeAvailabilityTexts.every((text) => text.includes("远程更新")) && upgradeAvailabilityTexts.some((text) => text.includes("always-enabled")), { sshAvailabilityTexts, upgradeAvailabilityTexts }); addSelectedCheck(checks, options, "frontend:overview-pgdata-visible", bodyText.includes("PGDATA") && bodyText.includes(config.database.volume), { bodyPreview: bodyText.slice(0, 800) }); - addSelectedCheck(checks, options, "frontend:microservice-catalog-visible", microserviceCatalogTextLower.includes("findjob") && microserviceCatalogTextLower.includes("pipeline") && microserviceCatalogTextLower.includes("todo note") && microserviceCatalogTextLower.includes("met nonlinear") && microserviceCatalogTextLower.includes("claudeqq") && microserviceCatalogTextLower.includes("code queue") && microserviceCatalogText.includes("D601") && microserviceCatalogText.includes(config.providerGateway.id) && microserviceCatalogTextLower.includes("private") && microserviceCatalogText.includes("https://gitee.com/Lyon1998/findjob") && microserviceCatalogText.includes("https://github.com/pikasTech/pipeline") && microserviceCatalogText.includes("https://github.com/pikasTech/met_nonlinear") && microserviceCatalogText.includes("https://gitee.com/lyon1998/agent_skills") && microserviceCatalogText.includes("https://gitee.com/Lyon1998/todo_note") && microserviceCatalogText.includes("https://github.com/pikasTech/unidesk"), { microserviceCatalogPreview: microserviceCatalogText.slice(0, 2000) }); + addSelectedCheck(checks, options, "frontend:microservice-catalog-visible", microserviceCatalogTextLower.includes("findjob") && microserviceCatalogTextLower.includes("pipeline") && microserviceCatalogTextLower.includes("todo note") && microserviceCatalogTextLower.includes("met nonlinear") && microserviceCatalogTextLower.includes("claudeqq") && microserviceCatalogTextLower.includes("oa event flow") && microserviceCatalogTextLower.includes("code queue") && microserviceCatalogText.includes("D601") && microserviceCatalogText.includes(config.providerGateway.id) && microserviceCatalogTextLower.includes("private") && microserviceCatalogText.includes("https://gitee.com/Lyon1998/findjob") && microserviceCatalogText.includes("https://github.com/pikasTech/pipeline") && microserviceCatalogText.includes("https://github.com/pikasTech/met_nonlinear") && microserviceCatalogText.includes("https://gitee.com/lyon1998/agent_skills") && microserviceCatalogText.includes("https://gitee.com/Lyon1998/todo_note") && microserviceCatalogText.includes("https://github.com/pikasTech/unidesk"), { microserviceCatalogPreview: microserviceCatalogText.slice(0, 2000) }); addSelectedCheck(checks, options, "frontend:todo-note-integrated-visible", todoNoteTextLower.includes("todo note 工作台") && todoNoteText.includes("CONSTAR") && todoNoteText.includes("大论文") && todoNoteText.includes("UI E2E smoke task") && todoNoteText.includes("撤销") && todoNoteText.includes("重做") && todoNoteText.includes("全部展开") && todoNoteText.includes("仅 UniDesk frontend 代理访问"), { todoNoteTextPreview: todoNoteText.slice(0, 1400) }); addSelectedCheck(checks, options, "frontend:findjob-integrated-visible", findjobTextLower.includes("findjob 工作台".toLowerCase()) && findjobText.includes("岗位总量") && findjobText.includes("D601") && findjobText.includes("近期岗位") && findjobText.includes("仅 UniDesk frontend 代理访问") && /岗位总量\s+\d+/.test(findjobText) && /health\s+ok/i.test(findjobText) && /[1-9]\d*\/[1-9]\d*\s+preview/i.test(findjobText), { findjobTextPreview: findjobText.slice(0, 1200) }); - addSelectedCheck(checks, options, "frontend:code-queue-integrated-visible", codeQueueTextLower.includes("code queue") && codeQueueText.includes("gpt-5.4-mini") && codeQueueText.includes("gpt-5.4") && codeQueueText.includes("gpt-5.5") && codeQueueText.includes("提交任务") && codeQueueText.includes("执行 Provider") && codeQueueText.includes("入队份数") && codeQueueText.includes("追加 prompt") && codeQueueText.includes("打断") && codeQueueTextLower.includes("查看 queue") && codeQueueText.includes("创建 queue") && codeQueueText.includes("合并 queue") && codeQueueOptions.some((text) => text.includes("All queues")) && codeQueueTracePlacement.firstChildIsTrace === true && codeQueueTracePlacement.noPageTopStatus === true && codeQueueTracePlacement.filterInsideTracePanel === true && codeQueueTracePlacement.taskSearchVisible === true && codeQueueTracePlacement.traceStatusVisible === true && codeQueueTracePlacement.markAllReadVisible === true && codeQueueGlobalStatus.activeMicroserviceVisible === true && codeQueueSidebarUpdateMetrics.hasRecentUpdateLabel === true && codeQueueHtmlGuard.rootAttrMissing === true && codeQueueHtmlGuard.sourceAttrMissing === true && codeQueueHtmlGuard.sourceNoBasePrompt === true && codeQueueSubmitQueueControl.tagName === "select" && codeQueueSubmitQueueControl.createButtonVisible === true && codeQueueSubmitQueueControl.mergeButtonVisible === true && codeQueueSubmitQueueControl.oldInputMissing === true && codeQueueSubmitQueueControl.providerValue === "main-server" && codeQueueSubmitQueueControl.cwdValue === "/root/unidesk" && Array.isArray(codeQueueSubmitQueueControl.providerOptions) && codeQueueSubmitQueueControl.providerOptions.some((item: any) => item.value === "D601" && String(item.text || "").includes("/home/ubuntu")) && codeQueueSubmitQueueControl.maxAttemptsMax === "99" && codeQueueSubmitQueueControl.maxAttemptsValue === "99" && codeQueueSubmitQueueControl.moveQueueVisible === true && codeQueuePromptDefaultEmpty === true && codeQueueSubmitGuard.batchRowVisible === true && codeQueueSubmitGuard.checkboxVisible === true && codeQueueSubmitGuard.disabledBeforeConfirm === true && codeQueueSubmitGuard.enabledAfterConfirm === true && codeQueueSubmitGuard.waitElementMissingBeforeSubmit === true && codeQueueScrollbarMetrics.transcriptThin === true && codeQueueScrollbarMetrics.toolHorizontalHidden === true && (codeQueueSwitchMetrics.optionCount <= 1 || codeQueueSwitchMetrics.switched === true) && codeQueueTextLower.includes("attempts") && codeQueueText.includes("仅 UniDesk frontend 代理访问") && (codeQueueTaskCount === 0 || codeQueueOutputText.includes("Submitted prompt")), { codeQueueTaskCount, codeQueueOptions, codeQueueSwitchMetrics, codeQueueSubmitQueueControl, codeQueueSubmitGuard, codeQueueScrollbarMetrics, codeQueuePromptDefaultEmpty, codeQueueTracePlacement, codeQueueGlobalStatus, codeQueueSidebarUpdateMetrics, codeQueueHtmlGuard, codeQueueOutputPreview: codeQueueOutputText.slice(0, 900), codeQueueTextPreview: codeQueueText.slice(0, 1400) }); + addSelectedCheck(checks, options, "frontend:oa-event-flow-visible", + oaEventFlowText.toLowerCase().includes("oa event flow") + && oaEventFlowText.includes("事件表") + && oaEventFlowText.includes("统计中心") + && oaEventFlowText.includes("service:code-queue") + && oaEventFlowText.includes("仅 UniDesk frontend 代理访问") + && oaEventFlowMetrics.pageVisible === true + && oaEventFlowMetrics.eventTableVisible === true + && oaEventFlowMetrics.statsVisible === true + && oaEventFlowMetrics.tagFilterValue === "service:code-queue" + && Number(oaEventFlowMetrics.rawButtonCount || 0) >= 2 + && !oaEventFlowText.includes("{\n"), + { oaEventFlowMetrics, oaEventFlowTextPreview: oaEventFlowText.slice(0, 1400) }); + addSelectedCheck(checks, options, "frontend:code-queue-integrated-visible", codeQueueTextLower.includes("code queue") && codeQueueText.includes("gpt-5.4-mini") && codeQueueText.includes("gpt-5.4") && codeQueueText.includes("gpt-5.5") && codeQueueText.includes("提交任务") && codeQueueText.includes("执行 Provider") && codeQueueText.includes("入队份数") && codeQueueText.includes("追加 prompt") && codeQueueText.includes("打断") && codeQueueTextLower.includes("查看 queue") && codeQueueText.includes("创建 queue") && codeQueueText.includes("合并 queue") && codeQueueOptions.some((text) => text.includes("All queues")) && codeQueueTracePlacement.firstChildIsTrace === true && codeQueueTracePlacement.noPageTopStatus === true && codeQueueTracePlacement.filterInsideTracePanel === true && codeQueueTracePlacement.taskSearchVisible === true && codeQueueTracePlacement.traceStatusVisible === true && codeQueueTracePlacement.markAllReadVisible === true && codeQueueGlobalStatus.activeMicroserviceVisible === true && codeQueueSidebarUpdateMetrics.hasRecentUpdateLabel === true && codeQueueHtmlGuard.rootAttrMissing === true && codeQueueHtmlGuard.sourceAttrMissing === true && codeQueueHtmlGuard.sourceNoBasePrompt === true && codeQueueSubmitQueueControl.tagName === "select" && codeQueueSubmitQueueControl.createButtonVisible === true && codeQueueSubmitQueueControl.mergeButtonVisible === true && codeQueueSubmitQueueControl.mergeSourceInlineMissing === true && codeQueueSubmitQueueControl.mergeDialogMissingBeforeClick === true && (codeQueueSubmitQueueControl.mergeButtonDisabled === true || (codeQueueSubmitQueueControl.mergeDialogVisible === true && codeQueueSubmitQueueControl.mergeDialogSelectVisible === true && Number(codeQueueSubmitQueueControl.mergeDialogSourceOptionCount || 0) > 1 && codeQueueSubmitQueueControl.mergeDialogSelectInsideSubmitForm !== true && codeQueueSubmitQueueControl.mergeDialogUsesCommonComponent === true && codeQueueSubmitQueueControl.mergeDialogDeleteNoteVisible === true)) && codeQueueSubmitQueueControl.oldInputMissing === true && codeQueueSubmitQueueControl.providerValue === "main-server" && codeQueueSubmitQueueControl.cwdValue === "/root/unidesk" && Array.isArray(codeQueueSubmitQueueControl.providerOptions) && codeQueueSubmitQueueControl.providerOptions.some((item: any) => item.value === "D601" && String(item.text || "").includes("/home/ubuntu")) && codeQueueSubmitQueueControl.maxAttemptsMax === "99" && codeQueueSubmitQueueControl.maxAttemptsValue === "99" && codeQueueSubmitQueueControl.moveQueueVisible === true && codeQueuePromptDefaultEmpty === true && codeQueueSubmitGuard.batchRowVisible === true && codeQueueSubmitGuard.checkboxVisible === true && codeQueueSubmitGuard.disabledBeforeConfirm === true && codeQueueSubmitGuard.enabledAfterConfirm === true && codeQueueSubmitGuard.waitElementMissingBeforeSubmit === true && codeQueueScrollbarMetrics.transcriptThin === true && codeQueueScrollbarMetrics.toolHorizontalHidden === true && (codeQueueSwitchMetrics.optionCount <= 1 || codeQueueSwitchMetrics.switched === true) && codeQueueTextLower.includes("attempts") && codeQueueText.includes("仅 UniDesk frontend 代理访问") && (codeQueueTaskCount === 0 || codeQueueOutputText.includes("Submitted prompt")), { codeQueueTaskCount, codeQueueOptions, codeQueueSwitchMetrics, codeQueueSubmitQueueControl, codeQueueSubmitGuard, codeQueueScrollbarMetrics, codeQueuePromptDefaultEmpty, codeQueueTracePlacement, codeQueueGlobalStatus, codeQueueSidebarUpdateMetrics, codeQueueHtmlGuard, codeQueueOutputPreview: codeQueueOutputText.slice(0, 900), codeQueueTextPreview: codeQueueText.slice(0, 1400) }); addSelectedCheck(checks, options, "frontend:code-queue-enqueue-await-smoke", codeQueueEnqueueAwaitSmoke.checked === true && codeQueueEnqueueAwaitSmoke.delayedPostCount === 1 diff --git a/src/components/backend-core/src/index.ts b/src/components/backend-core/src/index.ts index 70585591..7f5ce456 100644 --- a/src/components/backend-core/src/index.ts +++ b/src/components/backend-core/src/index.ts @@ -2775,6 +2775,7 @@ async function getMicroserviceAvailabilitySummary(): Promise }, requestHeaders: Record, bodyText: string, + abortSignal?: AbortSignal, ): Promise { const baseUrl = new URL(service.backend.nodeBaseUrl); const upstreamUrl = new URL(targetPath, baseUrl); upstreamUrl.search = proxyOptions.query; const headers = headersFromMicroserviceRequest(requestHeaders); + const streamExpected = method === "GET" && (targetPath.endsWith("/stream") || String(headers.get("accept") || "").toLowerCase().includes("text/event-stream")); const controller = new AbortController(); - const timer = setTimeout(() => controller.abort(), Math.max(1000, service.backend.timeoutMs)); + const timer = streamExpected ? null : setTimeout(() => controller.abort(), Math.max(1000, service.backend.timeoutMs)); try { const upstream = await fetch(upstreamUrl, { method, headers, body: method === "GET" || method === "HEAD" ? undefined : bodyText, - signal: controller.signal, + signal: streamExpected ? abortSignal : controller.signal, }); - const rawBodyText = await upstream.text(); const upstreamContentType = upstream.headers.get("content-type") ?? "text/plain; charset=utf-8"; + if (upstreamContentType.toLowerCase().includes("text/event-stream")) { + const responseHeaders: Record = { + "content-type": upstreamContentType, + "cache-control": upstream.headers.get("cache-control") || "no-store, no-transform", + "connection": "keep-alive", + "x-unidesk-proxy-mode": "direct", + "x-unidesk-response-truncated": "false", + }; + const buffering = upstream.headers.get("x-accel-buffering"); + if (buffering !== null) responseHeaders["x-accel-buffering"] = buffering; + return new Response(upstream.body, { status: upstream.status, headers: responseHeaders }); + } + const rawBodyText = await upstream.text(); const limitedBodyText = applyJsonArrayLimits(rawBodyText, upstreamContentType, proxyOptions.jsonArrayLimits); const bounded = boundedMicroserviceBodyText(limitedBodyText, upstreamContentType, { serviceId: service.id, @@ -2922,7 +2937,7 @@ async function directMicroserviceResponse( } catch (error) { return jsonResponse({ ok: false, error: "direct microservice proxy failed", serviceId: service.id, detail: errorToJson(error) }, 502); } finally { - clearTimeout(timer); + if (timer !== null) clearTimeout(timer); } } @@ -2933,9 +2948,10 @@ async function fetchMicroserviceUpstreamResponse( proxyOptions: { query: string; jsonArrayLimits: Record }, requestHeaders: Record, bodyText: string, + abortSignal?: AbortSignal, ): Promise { if (canDirectProxyMicroservice(service)) { - return directMicroserviceResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText); + return directMicroserviceResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText, abortSignal); } if (!(await providerSupports(service.providerId, "microservice.http"))) { return jsonResponse({ ok: false, error: `provider does not declare microservice.http capability: ${service.providerId}` }, 409); @@ -3028,8 +3044,8 @@ async function microserviceRoute(req: Request, url: URL): Promise { return stale; } } - const response = await fetchMicroserviceUpstreamResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText); - if (method === "GET" || method === "HEAD") rememberMicroserviceCache(cacheKey, cacheTtlMs, await cacheableResponseSnapshot(response)); + const response = await fetchMicroserviceUpstreamResponse(service, method, targetPath, proxyOptions, requestHeaders, bodyText, req.signal); + if ((method === "GET" || method === "HEAD") && cacheTtlMs > 0) rememberMicroserviceCache(cacheKey, cacheTtlMs, await cacheableResponseSnapshot(response)); return response; } diff --git a/src/components/frontend/public/app.js b/src/components/frontend/public/app.js index 8bea653f..6e47d599 100644 --- a/src/components/frontend/public/app.js +++ b/src/components/frontend/public/app.js @@ -1,39 +1,39 @@ -(()=>{var YK=Object.create;var{getPrototypeOf:DK,defineProperty:oj,getOwnPropertyNames:tK}=Object;var SK=Object.prototype.hasOwnProperty;function PK(u){return this[u]}var MK,mK,Pu=(u,f,l)=>{var r=u!=null&&typeof u==="object";if(r){var y=f?MK??=new WeakMap:mK??=new WeakMap,i=y.get(u);if(i)return i}l=u!=null?YK(DK(u)):{};let _=f||!u||!u.__esModule?oj(l,"default",{value:u,enumerable:!0}):l;for(let n of tK(u))if(!SK.call(_,n))oj(_,n,{get:PK.bind(u,n),enumerable:!0});if(r)y.set(u,_);return _};var ol=(u,f)=>()=>(f||u((f={exports:{}}).exports,f),f.exports);var eu=((u)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(u,{get:(f,l)=>(typeof require<"u"?require:f)[l]}):u)(function(u){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+u+'" is not supported')});var AF=ol((gu)=>{var W_=Symbol.for("react.element"),pK=Symbol.for("react.portal"),CK=Symbol.for("react.fragment"),xK=Symbol.for("react.strict_mode"),RK=Symbol.for("react.profiler"),hK=Symbol.for("react.provider"),bK=Symbol.for("react.context"),vK=Symbol.for("react.forward_ref"),IK=Symbol.for("react.suspense"),kK=Symbol.for("react.memo"),gK=Symbol.for("react.lazy"),dj=Symbol.iterator;function sK(u){if(u===null||typeof u!=="object")return null;return u=dj&&u[dj]||u["@@iterator"],typeof u==="function"?u:null}var fF={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},lF=Object.assign,rF={};function py(u,f,l){this.props=u,this.context=f,this.refs=rF,this.updater=l||fF}py.prototype.isReactComponent={};py.prototype.setState=function(u,f){if(typeof u!=="object"&&typeof u!=="function"&&u!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,u,f,"setState")};py.prototype.forceUpdate=function(u){this.updater.enqueueForceUpdate(this,u,"forceUpdate")};function yF(){}yF.prototype=py.prototype;function Y8(u,f,l){this.props=u,this.context=f,this.refs=rF,this.updater=l||fF}var D8=Y8.prototype=new yF;D8.constructor=Y8;lF(D8,py.prototype);D8.isPureReactComponent=!0;var ej=Array.isArray,iF=Object.prototype.hasOwnProperty,t8={current:null},_F={key:!0,ref:!0,__self:!0,__source:!0};function nF(u,f,l){var r,y={},i=null,_=null;if(f!=null)for(r in f.ref!==void 0&&(_=f.ref),f.key!==void 0&&(i=""+f.key),f)iF.call(f,r)&&!_F.hasOwnProperty(r)&&(y[r]=f[r]);var n=arguments.length-2;if(n===1)y.children=l;else if(1{jF.exports=AF()});var GF=ol((Wf)=>{function p8(u,f){var l=u.length;u.push(f);u:for(;0>>1,y=u[r];if(0>>1;re$(n,l))$e$(j,n)?(u[r]=j,u[$]=l,r=$):(u[r]=n,u[_]=l,r=_);else if($e$(j,l))u[r]=j,u[$]=l,r=$;else break u}}return f}function e$(u,f){var l=u.sortIndex-f.sortIndex;return l!==0?l:u.id-f.id}if(typeof performance==="object"&&typeof performance.now==="function")C8=performance,Wf.unstable_now=function(){return C8.now()};else u3=Date,x8=u3.now(),Wf.unstable_now=function(){return u3.now()-x8};var C8,u3,x8,kr=[],R0=[],rL=1,Qr=null,jl=3,y3=!1,R1=!1,N_=!1,qF=typeof setTimeout==="function"?setTimeout:null,WF=typeof clearTimeout==="function"?clearTimeout:null,QF=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function R8(u){for(var f=Hr(R0);f!==null;){if(f.callback===null)r3(R0);else if(f.startTime<=u)r3(R0),f.sortIndex=f.expirationTime,p8(kr,f);else break;f=Hr(R0)}}function b8(u){if(N_=!1,R8(u),!R1)if(Hr(kr)!==null)R1=!0,I8(v8);else{var f=Hr(R0);f!==null&&k8(b8,f.startTime-u)}}function v8(u,f){R1=!1,N_&&(N_=!1,WF(z_),z_=-1),y3=!0;var l=jl;try{R8(f);for(Qr=Hr(kr);Qr!==null&&(!(Qr.expirationTime>f)||u&&!zF());){var r=Qr.callback;if(typeof r==="function"){Qr.callback=null,jl=Qr.priorityLevel;var y=r(Qr.expirationTime<=f);f=Wf.unstable_now(),typeof y==="function"?Qr.callback=y:Qr===Hr(kr)&&r3(kr),R8(f)}else r3(kr);Qr=Hr(kr)}if(Qr!==null)var i=!0;else{var _=Hr(R0);_!==null&&k8(b8,_.startTime-f),i=!1}return i}finally{Qr=null,jl=l,y3=!1}}var i3=!1,f3=null,z_=-1,cF=5,NF=-1;function zF(){return Wf.unstable_now()-NFu||125r?(u.sortIndex=l,p8(R0,u),Hr(kr)===null&&u===Hr(R0)&&(N_?(WF(z_),z_=-1):N_=!0,k8(b8,l-r))):(u.sortIndex=y,p8(kr,u),R1||y3||(R1=!0,I8(v8))),u};Wf.unstable_shouldYield=zF;Wf.unstable_wrapCallback=function(u){var f=jl;return function(){var l=jl;jl=f;try{return u.apply(this,arguments)}finally{jl=l}}}});var LF=ol((kS,KF)=>{KF.exports=GF()});var EQ=ol((rr)=>{var yL=wf(),fr=LF();function Uu(u){for(var f="https://reactjs.org/docs/error-decoder.html?invariant="+u,l=1;l"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),q2=Object.prototype.hasOwnProperty,iL=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,wF={},EF={};function _L(u){if(q2.call(EF,u))return!0;if(q2.call(wF,u))return!1;if(iL.test(u))return EF[u]=!0;return wF[u]=!0,!1}function nL(u,f,l,r){if(l!==null&&l.type===0)return!1;switch(typeof f){case"function":case"symbol":return!0;case"boolean":if(r)return!1;if(l!==null)return!l.acceptsBooleans;return u=u.toLowerCase().slice(0,5),u!=="data-"&&u!=="aria-";default:return!1}}function $L(u,f,l,r){if(f===null||typeof f>"u"||nL(u,f,l,r))return!0;if(r)return!1;if(l!==null)switch(l.type){case 3:return!f;case 4:return f===!1;case 5:return isNaN(f);case 6:return isNaN(f)||1>f}return!1}function Ll(u,f,l,r,y,i,_){this.acceptsBooleans=f===2||f===3||f===4,this.attributeName=r,this.attributeNamespace=y,this.mustUseProperty=l,this.propertyName=u,this.type=f,this.sanitizeURL=i,this.removeEmptyString=_}var il={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(u){il[u]=new Ll(u,0,!1,u,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(u){var f=u[0];il[f]=new Ll(f,1,!1,u[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(u){il[u]=new Ll(u,2,!1,u.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(u){il[u]=new Ll(u,2,!1,u,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(u){il[u]=new Ll(u,3,!1,u.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(u){il[u]=new Ll(u,3,!0,u,null,!1,!1)});["capture","download"].forEach(function(u){il[u]=new Ll(u,4,!1,u,null,!1,!1)});["cols","rows","size","span"].forEach(function(u){il[u]=new Ll(u,6,!1,u,null,!1,!1)});["rowSpan","start"].forEach(function(u){il[u]=new Ll(u,5,!1,u.toLowerCase(),null,!1,!1)});var A5=/[\-:]([a-z])/g;function j5(u){return u[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(u){var f=u.replace(A5,j5);il[f]=new Ll(f,1,!1,u,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(u){var f=u.replace(A5,j5);il[f]=new Ll(f,1,!1,u,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(u){var f=u.replace(A5,j5);il[f]=new Ll(f,1,!1,u,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(u){il[u]=new Ll(u,1,!1,u.toLowerCase(),null,!1,!1)});il.xlinkHref=new Ll("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(u){il[u]=new Ll(u,1,!1,u.toLowerCase(),null,!0,!0)});function F5(u,f,l,r){var y=il.hasOwnProperty(f)?il[f]:null;if(y!==null?y.type!==0:r||!(2{var yK=Object.create;var{getPrototypeOf:_K,defineProperty:U7,getOwnPropertyNames:$K}=Object;var tK=Object.prototype.hasOwnProperty;function cK(u){return this[u]}var AK,jK,Mu=(u,f,l)=>{var r=u!=null&&typeof u==="object";if(r){var n=f?AK??=new WeakMap:jK??=new WeakMap,i=n.get(u);if(i)return i}l=u!=null?yK(_K(u)):{};let y=f||!u||!u.__esModule?U7(l,"default",{value:u,enumerable:!0}):l;for(let _ of $K(u))if(!tK.call(y,_))U7(y,_,{get:cK.bind(u,_),enumerable:!0});if(r)n.set(u,y);return y};var ur=(u,f)=>()=>(f||u((f={exports:{}}).exports,f),f.exports);var eu=((u)=>typeof require<"u"?require:typeof Proxy<"u"?new Proxy(u,{get:(f,l)=>(typeof require<"u"?require:f)[l]}):u)(function(u){if(typeof require<"u")return require.apply(this,arguments);throw Error('Dynamic require of "'+u+'" is not supported')});var Z7=ur((su)=>{var Oy=Symbol.for("react.element"),FK=Symbol.for("react.portal"),JK=Symbol.for("react.fragment"),UK=Symbol.for("react.strict_mode"),QK=Symbol.for("react.profiler"),qK=Symbol.for("react.provider"),NK=Symbol.for("react.context"),WK=Symbol.for("react.forward_ref"),wK=Symbol.for("react.suspense"),LK=Symbol.for("react.memo"),KK=Symbol.for("react.lazy"),Q7=Symbol.iterator;function GK(u){if(u===null||typeof u!=="object")return null;return u=Q7&&u[Q7]||u["@@iterator"],typeof u==="function"?u:null}var W7={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},w7=Object.assign,L7={};function e1(u,f,l){this.props=u,this.context=f,this.refs=L7,this.updater=l||W7}e1.prototype.isReactComponent={};e1.prototype.setState=function(u,f){if(typeof u!=="object"&&typeof u!=="function"&&u!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,u,f,"setState")};e1.prototype.forceUpdate=function(u){this.updater.enqueueForceUpdate(this,u,"forceUpdate")};function K7(){}K7.prototype=e1.prototype;function v8(u,f,l){this.props=u,this.context=f,this.refs=L7,this.updater=l||W7}var k8=v8.prototype=new K7;k8.constructor=v8;w7(k8,e1.prototype);k8.isPureReactComponent=!0;var q7=Array.isArray,G7=Object.prototype.hasOwnProperty,I8={current:null},z7={key:!0,ref:!0,__self:!0,__source:!0};function T7(u,f,l){var r,n={},i=null,y=null;if(f!=null)for(r in f.ref!==void 0&&(y=f.ref),f.key!==void 0&&(i=""+f.key),f)G7.call(f,r)&&!z7.hasOwnProperty(r)&&(n[r]=f[r]);var _=arguments.length-2;if(_===1)n.children=l;else if(1<_){for(var $=Array(_),c=0;c<_;c++)$[c]=arguments[c+2];n.children=$}if(u&&u.defaultProps)for(r in _=u.defaultProps,_)n[r]===void 0&&(n[r]=_[r]);return{$$typeof:Oy,type:u,key:i,ref:y,props:n,_owner:I8.current}}function zK(u,f){return{$$typeof:Oy,type:u.type,key:f,ref:u.ref,props:u.props,_owner:u._owner}}function g8(u){return typeof u==="object"&&u!==null&&u.$$typeof===Oy}function TK(u){var f={"=":"=0",":":"=2"};return"$"+u.replace(/[=:]/g,function(l){return f[l]})}var N7=/\/+/g;function b8(u,f){return typeof u==="object"&&u!==null&&u.key!=null?TK(""+u.key):f.toString(36)}function rt(u,f,l,r,n){var i=typeof u;if(i==="undefined"||i==="boolean")u=null;var y=!1;if(u===null)y=!0;else switch(i){case"string":case"number":y=!0;break;case"object":switch(u.$$typeof){case Oy:case FK:y=!0}}if(y)return y=u,n=n(y),u=r===""?"."+b8(y,0):r,q7(n)?(l="",u!=null&&(l=u.replace(N7,"$&/")+"/"),rt(n,f,l,"",function(c){return c})):n!=null&&(g8(n)&&(n=zK(n,l+(!n.key||y&&y.key===n.key?"":(""+n.key).replace(N7,"$&/")+"/")+u)),f.push(n)),1;if(y=0,r=r===""?".":r+":",q7(u))for(var _=0;_{O7.exports=Z7()});var P7=ur((qf)=>{function d8(u,f){var l=u.length;u.push(f);u:for(;0>>1,n=u[r];if(0>>1;ryt(_,l))$yt(c,_)?(u[r]=c,u[$]=l,r=$):(u[r]=_,u[y]=l,r=y);else if($yt(c,l))u[r]=c,u[$]=l,r=$;else break u}}return f}function yt(u,f){var l=u.sortIndex-f.sortIndex;return l!==0?l:u.id-f.id}if(typeof performance==="object"&&typeof performance.now==="function")e8=performance,qf.unstable_now=function(){return e8.now()};else _t=Date,u4=_t.now(),qf.unstable_now=function(){return _t.now()-u4};var e8,_t,u4,ar=[],a0=[],VK=1,Qr=null,Fl=3,At=!1,en=!1,By=!1,X7=typeof setTimeout==="function"?setTimeout:null,Y7=typeof clearTimeout==="function"?clearTimeout:null,D7=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function f4(u){for(var f=Vr(a0);f!==null;){if(f.callback===null)ct(a0);else if(f.startTime<=u)ct(a0),f.sortIndex=f.expirationTime,d8(ar,f);else break;f=Vr(a0)}}function r4(u){if(By=!1,f4(u),!en)if(Vr(ar)!==null)en=!0,i4(n4);else{var f=Vr(a0);f!==null&&y4(r4,f.startTime-u)}}function n4(u,f){en=!1,By&&(By=!1,Y7(Vy),Vy=-1),At=!0;var l=Fl;try{f4(f);for(Qr=Vr(ar);Qr!==null&&(!(Qr.expirationTime>f)||u&&!p7());){var r=Qr.callback;if(typeof r==="function"){Qr.callback=null,Fl=Qr.priorityLevel;var n=r(Qr.expirationTime<=f);f=qf.unstable_now(),typeof n==="function"?Qr.callback=n:Qr===Vr(ar)&&ct(ar),f4(f)}else ct(ar);Qr=Vr(ar)}if(Qr!==null)var i=!0;else{var y=Vr(a0);y!==null&&y4(r4,y.startTime-f),i=!1}return i}finally{Qr=null,Fl=l,At=!1}}var jt=!1,$t=null,Vy=-1,S7=5,m7=-1;function p7(){return qf.unstable_now()-m7u||125r?(u.sortIndex=l,d8(a0,u),Vr(ar)===null&&u===Vr(a0)&&(By?(Y7(Vy),Vy=-1):By=!0,y4(r4,l-r))):(u.sortIndex=n,d8(ar,u),en||At||(en=!0,i4(n4))),u};qf.unstable_shouldYield=p7;qf.unstable_wrapCallback=function(u){var f=Fl;return function(){var l=Fl;Fl=f;try{return u.apply(this,arguments)}finally{Fl=l}}}});var C7=ur((pm,M7)=>{M7.exports=P7()});var xJ=ur((yr)=>{var DK=Qf(),nr=C7();function Uu(u){for(var f="https://reactjs.org/docs/error-decoder.html?invariant="+u,l=1;l"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),O4=Object.prototype.hasOwnProperty,XK=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,R7={},x7={};function YK(u){if(O4.call(x7,u))return!0;if(O4.call(R7,u))return!1;if(XK.test(u))return x7[u]=!0;return R7[u]=!0,!1}function SK(u,f,l,r){if(l!==null&&l.type===0)return!1;switch(typeof f){case"function":case"symbol":return!0;case"boolean":if(r)return!1;if(l!==null)return!l.acceptsBooleans;return u=u.toLowerCase().slice(0,5),u!=="data-"&&u!=="aria-";default:return!1}}function mK(u,f,l,r){if(f===null||typeof f>"u"||SK(u,f,l,r))return!0;if(r)return!1;if(l!==null)switch(l.type){case 3:return!f;case 4:return f===!1;case 5:return isNaN(f);case 6:return isNaN(f)||1>f}return!1}function Hl(u,f,l,r,n,i,y){this.acceptsBooleans=f===2||f===3||f===4,this.attributeName=r,this.attributeNamespace=n,this.mustUseProperty=l,this.propertyName=u,this.type=f,this.sanitizeURL=i,this.removeEmptyString=y}var _l={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(u){_l[u]=new Hl(u,0,!1,u,null,!1,!1)});[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(u){var f=u[0];_l[f]=new Hl(f,1,!1,u[1],null,!1,!1)});["contentEditable","draggable","spellCheck","value"].forEach(function(u){_l[u]=new Hl(u,2,!1,u.toLowerCase(),null,!1,!1)});["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(u){_l[u]=new Hl(u,2,!1,u,null,!1,!1)});"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(u){_l[u]=new Hl(u,3,!1,u.toLowerCase(),null,!1,!1)});["checked","multiple","muted","selected"].forEach(function(u){_l[u]=new Hl(u,3,!0,u,null,!1,!1)});["capture","download"].forEach(function(u){_l[u]=new Hl(u,4,!1,u,null,!1,!1)});["cols","rows","size","span"].forEach(function(u){_l[u]=new Hl(u,6,!1,u,null,!1,!1)});["rowSpan","start"].forEach(function(u){_l[u]=new Hl(u,5,!1,u.toLowerCase(),null,!1,!1)});var K2=/[\-:]([a-z])/g;function G2(u){return u[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(u){var f=u.replace(K2,G2);_l[f]=new Hl(f,1,!1,u,null,!1,!1)});"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(u){var f=u.replace(K2,G2);_l[f]=new Hl(f,1,!1,u,"http://www.w3.org/1999/xlink",!1,!1)});["xml:base","xml:lang","xml:space"].forEach(function(u){var f=u.replace(K2,G2);_l[f]=new Hl(f,1,!1,u,"http://www.w3.org/XML/1998/namespace",!1,!1)});["tabIndex","crossOrigin"].forEach(function(u){_l[u]=new Hl(u,1,!1,u.toLowerCase(),null,!1,!1)});_l.xlinkHref=new Hl("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1);["src","href","action","formAction"].forEach(function(u){_l[u]=new Hl(u,1,!1,u.toLowerCase(),null,!0,!0)});function z2(u,f,l,r){var n=_l.hasOwnProperty(f)?_l[f]:null;if(n!==null?n.type!==0:r||!(2n||y[_]!==i[n]){var $=` -`+y[_].replace(" at new "," at ");return u.displayName&&$.includes("")&&($=$.replace("",u.displayName)),$}while(1<=_&&0<=n);break}}}finally{s8=!1,Error.prepareStackTrace=l}return(u=u?u.displayName||u.name:"")?H_(u):""}function AL(u){switch(u.tag){case 5:return H_(u.type);case 16:return H_("Lazy");case 13:return H_("Suspense");case 19:return H_("SuspenseList");case 0:case 2:case 15:return u=a8(u.type,!1),u;case 11:return u=a8(u.type.render,!1),u;case 1:return u=a8(u.type,!0),u;default:return""}}function z2(u){if(u==null)return null;if(typeof u==="function")return u.displayName||u.name||null;if(typeof u==="string")return u;switch(u){case hy:return"Fragment";case Ry:return"Portal";case W2:return"Profiler";case J5:return"StrictMode";case c2:return"Suspense";case N2:return"SuspenseList"}if(typeof u==="object")switch(u.$$typeof){case BJ:return(u.displayName||"Context")+".Consumer";case OJ:return(u._context.displayName||"Context")+".Provider";case U5:var f=u.render;return u=u.displayName,u||(u=f.displayName||f.name||"",u=u!==""?"ForwardRef("+u+")":"ForwardRef"),u;case Q5:return f=u.displayName||null,f!==null?f:z2(u.type)||"Memo";case b0:f=u._payload,u=u._init;try{return z2(u(f))}catch(l){}}return null}function jL(u){var f=u.type;switch(u.tag){case 24:return"Cache";case 9:return(f.displayName||"Context")+".Consumer";case 10:return(f._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return u=f.render,u=u.displayName||u.name||"",f.displayName||(u!==""?"ForwardRef("+u+")":"ForwardRef");case 7:return"Fragment";case 5:return f;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return z2(f);case 8:return f===J5?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof f==="function")return f.displayName||f.name||null;if(typeof f==="string")return f}return null}function y1(u){switch(typeof u){case"boolean":case"number":case"string":case"undefined":return u;case"object":return u;default:return""}}function XJ(u){var f=u.type;return(u=u.nodeName)&&u.toLowerCase()==="input"&&(f==="checkbox"||f==="radio")}function FL(u){var f=XJ(u)?"checked":"value",l=Object.getOwnPropertyDescriptor(u.constructor.prototype,f),r=""+u[f];if(!u.hasOwnProperty(f)&&typeof l<"u"&&typeof l.get==="function"&&typeof l.set==="function"){var{get:y,set:i}=l;return Object.defineProperty(u,f,{configurable:!0,get:function(){return y.call(this)},set:function(_){r=""+_,i.call(this,_)}}),Object.defineProperty(u,f,{enumerable:l.enumerable}),{getValue:function(){return r},setValue:function(_){r=""+_},stopTracking:function(){u._valueTracker=null,delete u[f]}}}}function n3(u){u._valueTracker||(u._valueTracker=FL(u))}function YJ(u){if(!u)return!1;var f=u._valueTracker;if(!f)return!0;var l=f.getValue(),r="";return u&&(r=XJ(u)?u.checked?"true":"false":u.value),u=r,u!==l?(f.setValue(u),!0):!1}function Y3(u){if(u=u||(typeof document<"u"?document:void 0),typeof u>"u")return null;try{return u.activeElement||u.body}catch(f){return u.body}}function G2(u,f){var l=f.checked;return Of({},f,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:l!=null?l:u._wrapperState.initialChecked})}function ZF(u,f){var l=f.defaultValue==null?"":f.defaultValue,r=f.checked!=null?f.checked:f.defaultChecked;l=y1(f.value!=null?f.value:l),u._wrapperState={initialChecked:r,initialValue:l,controlled:f.type==="checkbox"||f.type==="radio"?f.checked!=null:f.value!=null}}function DJ(u,f){f=f.checked,f!=null&&F5(u,"checked",f,!1)}function K2(u,f){DJ(u,f);var l=y1(f.value),r=f.type;if(l!=null)if(r==="number"){if(l===0&&u.value===""||u.value!=l)u.value=""+l}else u.value!==""+l&&(u.value=""+l);else if(r==="submit"||r==="reset"){u.removeAttribute("value");return}f.hasOwnProperty("value")?L2(u,f.type,l):f.hasOwnProperty("defaultValue")&&L2(u,f.type,y1(f.defaultValue)),f.checked==null&&f.defaultChecked!=null&&(u.defaultChecked=!!f.defaultChecked)}function HF(u,f,l){if(f.hasOwnProperty("value")||f.hasOwnProperty("defaultValue")){var r=f.type;if(!(r!=="submit"&&r!=="reset"||f.value!==void 0&&f.value!==null))return;f=""+u._wrapperState.initialValue,l||f===u.value||(u.value=f),u.defaultValue=f}l=u.name,l!==""&&(u.name=""),u.defaultChecked=!!u._wrapperState.initialChecked,l!==""&&(u.name=l)}function L2(u,f,l){if(f!=="number"||Y3(u.ownerDocument)!==u)l==null?u.defaultValue=""+u._wrapperState.initialValue:u.defaultValue!==""+l&&(u.defaultValue=""+l)}var O_=Array.isArray;function ui(u,f,l,r){if(u=u.options,f){f={};for(var y=0;y"+f.valueOf().toString()+"";for(f=$3.firstChild;u.firstChild;)u.removeChild(u.firstChild);for(;f.firstChild;)u.appendChild(f.firstChild)}});function v_(u,f){if(f){var l=u.firstChild;if(l&&l===u.lastChild&&l.nodeType===3){l.nodeValue=f;return}}u.textContent=f}var S_={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},JL=["Webkit","ms","Moz","O"];Object.keys(S_).forEach(function(u){JL.forEach(function(f){f=f+u.charAt(0).toUpperCase()+u.substring(1),S_[f]=S_[u]})});function MJ(u,f,l){return f==null||typeof f==="boolean"||f===""?"":l||typeof f!=="number"||f===0||S_.hasOwnProperty(u)&&S_[u]?(""+f).trim():f+"px"}function mJ(u,f){u=u.style;for(var l in f)if(f.hasOwnProperty(l)){var r=l.indexOf("--")===0,y=MJ(l,f[l],r);l==="float"&&(l="cssFloat"),r?u.setProperty(l,y):u[l]=y}}var UL=Of({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function T2(u,f){if(f){if(UL[u]&&(f.children!=null||f.dangerouslySetInnerHTML!=null))throw Error(Uu(137,u));if(f.dangerouslySetInnerHTML!=null){if(f.children!=null)throw Error(Uu(60));if(typeof f.dangerouslySetInnerHTML!=="object"||!("__html"in f.dangerouslySetInnerHTML))throw Error(Uu(61))}if(f.style!=null&&typeof f.style!=="object")throw Error(Uu(62))}}function Z2(u,f){if(u.indexOf("-")===-1)return typeof f.is==="string";switch(u){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var H2=null;function q5(u){return u=u.target||u.srcElement||window,u.correspondingUseElement&&(u=u.correspondingUseElement),u.nodeType===3?u.parentNode:u}var O2=null,fi=null,li=null;function VF(u){if(u=jn(u)){if(typeof O2!=="function")throw Error(Uu(280));var f=u.stateNode;f&&(f=_6(f),O2(u.stateNode,u.type,f))}}function pJ(u){fi?li?li.push(u):li=[u]:fi=u}function CJ(){if(fi){var u=fi,f=li;if(li=fi=null,VF(u),f)for(u=0;u>>=0,u===0?32:31-(EL(u)/TL|0)|0}var A3=64,j3=4194304;function B_(u){switch(u&-u){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: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 u&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return u&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return u}}function P3(u,f){var l=u.pendingLanes;if(l===0)return 0;var r=0,y=u.suspendedLanes,i=u.pingedLanes,_=l&268435455;if(_!==0){var n=_&~y;n!==0?r=B_(n):(i&=_,i!==0&&(r=B_(i)))}else _=l&~y,_!==0?r=B_(_):i!==0&&(r=B_(i));if(r===0)return 0;if(f!==0&&f!==r&&(f&y)===0&&(y=r&-r,i=f&-f,y>=i||y===16&&(i&4194240)!==0))return f;if((r&4)!==0&&(r|=l&16),f=u.entangledLanes,f!==0)for(u=u.entanglements,f&=r;0l;l++)f.push(u);return f}function $n(u,f,l){u.pendingLanes|=f,f!==536870912&&(u.suspendedLanes=0,u.pingedLanes=0),u=u.eventTimes,f=31-Yr(f),u[f]=l}function BL(u,f){var l=u.pendingLanes&~f;u.pendingLanes=f,u.suspendedLanes=0,u.pingedLanes=0,u.expiredLanes&=f,u.mutableReadLanes&=f,u.entangledLanes&=f,f=u.entanglements;var r=u.eventTimes;for(u=u.expirationTimes;0=M_),pF=String.fromCharCode(32),CF=!1;function _U(u,f){switch(u){case"keyup":return rw.indexOf(f.keyCode)!==-1;case"keydown":return f.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function nU(u){return u=u.detail,typeof u==="object"&&"data"in u?u.data:null}var by=!1;function iw(u,f){switch(u){case"compositionend":return nU(f);case"keypress":if(f.which!==32)return null;return CF=!0,pF;case"textInput":return u=f.data,u===pF&&CF?null:u;default:return null}}function _w(u,f){if(by)return u==="compositionend"||!w5&&_U(u,f)?(u=yU(),L3=G5=g0=null,by=!1,u):null;switch(u){case"paste":return null;case"keypress":if(!(f.ctrlKey||f.altKey||f.metaKey)||f.ctrlKey&&f.altKey){if(f.char&&1=f)return{node:l,offset:f-u};u=r}u:{for(;l;){if(l.nextSibling){l=l.nextSibling;break u}l=l.parentNode}l=void 0}l=hF(l)}}function FU(u,f){return u&&f?u===f?!0:u&&u.nodeType===3?!1:f&&f.nodeType===3?FU(u,f.parentNode):("contains"in u)?u.contains(f):u.compareDocumentPosition?!!(u.compareDocumentPosition(f)&16):!1:!1}function JU(){for(var u=window,f=Y3();f instanceof u.HTMLIFrameElement;){try{var l=typeof f.contentWindow.location.href==="string"}catch(r){l=!1}if(l)u=f.contentWindow;else break;f=Y3(u.document)}return f}function E5(u){var f=u&&u.nodeName&&u.nodeName.toLowerCase();return f&&(f==="input"&&(u.type==="text"||u.type==="search"||u.type==="tel"||u.type==="url"||u.type==="password")||f==="textarea"||u.contentEditable==="true")}function qw(u){var f=JU(),l=u.focusedElem,r=u.selectionRange;if(f!==l&&l&&l.ownerDocument&&FU(l.ownerDocument.documentElement,l)){if(r!==null&&E5(l)){if(f=r.start,u=r.end,u===void 0&&(u=f),"selectionStart"in l)l.selectionStart=f,l.selectionEnd=Math.min(u,l.value.length);else if(u=(f=l.ownerDocument||document)&&f.defaultView||window,u.getSelection){u=u.getSelection();var y=l.textContent.length,i=Math.min(r.start,y);r=r.end===void 0?i:Math.min(r.end,y),!u.extend&&i>r&&(y=r,r=i,i=y),y=bF(l,i);var _=bF(l,r);y&&_&&(u.rangeCount!==1||u.anchorNode!==y.node||u.anchorOffset!==y.offset||u.focusNode!==_.node||u.focusOffset!==_.offset)&&(f=f.createRange(),f.setStart(y.node,y.offset),u.removeAllRanges(),i>r?(u.addRange(f),u.extend(_.node,_.offset)):(f.setEnd(_.node,_.offset),u.addRange(f)))}}f=[];for(u=l;u=u.parentNode;)u.nodeType===1&&f.push({element:u,left:u.scrollLeft,top:u.scrollTop});typeof l.focus==="function"&&l.focus();for(l=0;l=document.documentMode,vy=null,t2=null,p_=null,S2=!1;function vF(u,f,l){var r=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;S2||vy==null||vy!==Y3(r)||(r=vy,("selectionStart"in r)&&E5(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),p_&&o_(p_,r)||(p_=r,r=p3(t2,"onSelect"),0gy||(u.current=h2[gy],h2[gy]=null,gy--)}function cf(u,f){gy++,h2[gy]=u.current,u.current=f}var i1={},Ql=n1(i1),Sl=n1(!1),o1=i1;function ni(u,f){var l=u.type.contextTypes;if(!l)return i1;var r=u.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===f)return r.__reactInternalMemoizedMaskedChildContext;var y={},i;for(i in l)y[i]=f[i];return r&&(u=u.stateNode,u.__reactInternalMemoizedUnmaskedChildContext=f,u.__reactInternalMemoizedMaskedChildContext=y),y}function Pl(u){return u=u.childContextTypes,u!==null&&u!==void 0}function x3(){Gf(Sl),Gf(Ql)}function dF(u,f,l){if(Ql.current!==i1)throw Error(Uu(168));cf(Ql,f),cf(Sl,l)}function KU(u,f,l){var r=u.stateNode;if(f=f.childContextTypes,typeof r.getChildContext!=="function")return l;r=r.getChildContext();for(var y in r)if(!(y in f))throw Error(Uu(108,jL(u)||"Unknown",y));return Of({},l,r)}function R3(u){return u=(u=u.stateNode)&&u.__reactInternalMemoizedMergedChildContext||i1,o1=Ql.current,cf(Ql,u),cf(Sl,Sl.current),!0}function eF(u,f,l){var r=u.stateNode;if(!r)throw Error(Uu(169));l?(u=KU(u,f,o1),r.__reactInternalMemoizedMergedChildContext=u,Gf(Sl),Gf(Ql),cf(Ql,u)):Gf(Sl),cf(Sl,l)}var q0=null,n6=!1,_2=!1;function LU(u){q0===null?q0=[u]:q0.push(u)}function Tw(u){n6=!0,LU(u)}function $1(){if(!_2&&q0!==null){_2=!0;var u=0,f=nf;try{var l=q0;for(nf=1;u>=_,y-=_,W0=1<<32-Yr(f)+y|l<T?(X=B,B=null):X=B.sibling;var R=U(Q,B,c[T],z);if(R===null){B===null&&(B=X);break}u&&B&&R.alternate===null&&f(Q,B),N=i(R,N,T),H===null?w=R:H.sibling=R,H=R,B=X}if(T===c.length)return l(Q,B),Ef&&b1(Q,T),w;if(B===null){for(;TT?(X=B,B=null):X=B.sibling;var Y=U(Q,B,R.value,z);if(Y===null){B===null&&(B=X);break}u&&B&&Y.alternate===null&&f(Q,B),N=i(Y,N,T),H===null?w=Y:H.sibling=Y,H=Y,B=X}if(R.done)return l(Q,B),Ef&&b1(Q,T),w;if(B===null){for(;!R.done;T++,R=c.next())R=J(Q,R.value,z),R!==null&&(N=i(R,N,T),H===null?w=R:H.sibling=R,H=R);return Ef&&b1(Q,T),w}for(B=r(Q,B);!R.done;T++,R=c.next())R=q(B,Q,T,R.value,z),R!==null&&(u&&R.alternate!==null&&B.delete(R.key===null?T:R.key),N=i(R,N,T),H===null?w=R:H.sibling=R,H=R);return u&&B.forEach(function(P){return f(Q,P)}),Ef&&b1(Q,T),w}function K(Q,N,c,z){if(typeof c==="object"&&c!==null&&c.type===hy&&c.key===null&&(c=c.props.children),typeof c==="object"&&c!==null){switch(c.$$typeof){case _3:u:{for(var w=c.key,H=N;H!==null;){if(H.key===w){if(w=c.type,w===hy){if(H.tag===7){l(Q,H.sibling),N=y(H,c.props.children),N.return=Q,Q=N;break u}}else if(H.elementType===w||typeof w==="object"&&w!==null&&w.$$typeof===b0&&lJ(w)===H.type){l(Q,H.sibling),N=y(H,c.props),N.ref=E_(Q,H,c),N.return=Q,Q=N;break u}l(Q,H);break}else f(Q,H);H=H.sibling}c.type===hy?(N=a1(c.props.children,Q.mode,z,c.key),N.return=Q,Q=N):(z=X3(c.type,c.key,c.props,null,Q.mode,z),z.ref=E_(Q,N,c),z.return=Q,Q=z)}return _(Q);case Ry:u:{for(H=c.key;N!==null;){if(N.key===H)if(N.tag===4&&N.stateNode.containerInfo===c.containerInfo&&N.stateNode.implementation===c.implementation){l(Q,N.sibling),N=y(N,c.children||[]),N.return=Q,Q=N;break u}else{l(Q,N);break}else f(Q,N);N=N.sibling}N=Q2(c,Q.mode,z),N.return=Q,Q=N}return _(Q);case b0:return H=c._init,K(Q,N,H(c._payload),z)}if(O_(c))return W(Q,N,c,z);if(G_(c))return G(Q,N,c,z);c3(Q,c)}return typeof c==="string"&&c!==""||typeof c==="number"?(c=""+c,N!==null&&N.tag===6?(l(Q,N.sibling),N=y(N,c),N.return=Q,Q=N):(l(Q,N),N=U2(c,Q.mode,z),N.return=Q,Q=N),_(Q)):l(Q,N)}return K}var Ai=ZU(!0),HU=ZU(!1),v3=n1(null),I3=null,oy=null,O5=null;function B5(){O5=oy=I3=null}function V5(u){var f=v3.current;Gf(v3),u._currentValue=f}function I2(u,f,l){for(;u!==null;){var r=u.alternate;if((u.childLanes&f)!==f?(u.childLanes|=f,r!==null&&(r.childLanes|=f)):r!==null&&(r.childLanes&f)!==f&&(r.childLanes|=f),u===l)break;u=u.return}}function yi(u,f){I3=u,O5=oy=null,u=u.dependencies,u!==null&&u.firstContext!==null&&((u.lanes&f)!==0&&(tl=!0),u.firstContext=null)}function zr(u){var f=u._currentValue;if(O5!==u)if(u={context:u,memoizedValue:f,next:null},oy===null){if(I3===null)throw Error(Uu(308));oy=u,I3.dependencies={lanes:0,firstContext:u}}else oy=oy.next=u;return f}var k1=null;function X5(u){k1===null?k1=[u]:k1.push(u)}function OU(u,f,l,r){var y=f.interleaved;return y===null?(l.next=l,X5(f)):(l.next=y.next,y.next=l),f.interleaved=l,K0(u,r)}function K0(u,f){u.lanes|=f;var l=u.alternate;l!==null&&(l.lanes|=f),l=u;for(u=u.return;u!==null;)u.childLanes|=f,l=u.alternate,l!==null&&(l.childLanes|=f),l=u,u=u.return;return l.tag===3?l.stateNode:null}var v0=!1;function Y5(u){u.updateQueue={baseState:u.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function BU(u,f){u=u.updateQueue,f.updateQueue===u&&(f.updateQueue={baseState:u.baseState,firstBaseUpdate:u.firstBaseUpdate,lastBaseUpdate:u.lastBaseUpdate,shared:u.shared,effects:u.effects})}function N0(u,f){return{eventTime:u,lane:f,tag:0,payload:null,callback:null,next:null}}function u1(u,f,l){var r=u.updateQueue;if(r===null)return null;if(r=r.shared,(lf&2)!==0){var y=r.pending;return y===null?f.next=f:(f.next=y.next,y.next=f),r.pending=f,K0(u,l)}return y=r.interleaved,y===null?(f.next=f,X5(r)):(f.next=y.next,y.next=f),r.interleaved=f,K0(u,l)}function T3(u,f,l){if(f=f.updateQueue,f!==null&&(f=f.shared,(l&4194240)!==0)){var r=f.lanes;r&=u.pendingLanes,l|=r,f.lanes=l,c5(u,l)}}function rJ(u,f){var{updateQueue:l,alternate:r}=u;if(r!==null&&(r=r.updateQueue,l===r)){var y=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var _={eventTime:l.eventTime,lane:l.lane,tag:l.tag,payload:l.payload,callback:l.callback,next:null};i===null?y=i=_:i=i.next=_,l=l.next}while(l!==null);i===null?y=i=f:i=i.next=f}else y=i=f;l={baseState:r.baseState,firstBaseUpdate:y,lastBaseUpdate:i,shared:r.shared,effects:r.effects},u.updateQueue=l;return}u=l.lastBaseUpdate,u===null?l.firstBaseUpdate=f:u.next=f,l.lastBaseUpdate=f}function k3(u,f,l,r){var y=u.updateQueue;v0=!1;var{firstBaseUpdate:i,lastBaseUpdate:_}=y,n=y.shared.pending;if(n!==null){y.shared.pending=null;var $=n,j=$.next;$.next=null,_===null?i=j:_.next=j,_=$;var F=u.alternate;F!==null&&(F=F.updateQueue,n=F.lastBaseUpdate,n!==_&&(n===null?F.firstBaseUpdate=j:n.next=j,F.lastBaseUpdate=$))}if(i!==null){var J=y.baseState;_=0,F=j=$=null,n=i;do{var{lane:U,eventTime:q}=n;if((r&U)===U){F!==null&&(F=F.next={eventTime:q,lane:0,tag:n.tag,payload:n.payload,callback:n.callback,next:null});u:{var W=u,G=n;switch(U=f,q=l,G.tag){case 1:if(W=G.payload,typeof W==="function"){J=W.call(q,J,U);break u}J=W;break u;case 3:W.flags=W.flags&-65537|128;case 0:if(W=G.payload,U=typeof W==="function"?W.call(q,J,U):W,U===null||U===void 0)break u;J=Of({},J,U);break u;case 2:v0=!0}}n.callback!==null&&n.lane!==0&&(u.flags|=64,U=y.effects,U===null?y.effects=[n]:U.push(n))}else q={eventTime:q,lane:U,tag:n.tag,payload:n.payload,callback:n.callback,next:null},F===null?(j=F=q,$=J):F=F.next=q,_|=U;if(n=n.next,n===null)if(n=y.shared.pending,n===null)break;else U=n,n=U.next,U.next=null,y.lastBaseUpdate=U,y.shared.pending=null}while(1);if(F===null&&($=J),y.baseState=$,y.firstBaseUpdate=j,y.lastBaseUpdate=F,f=y.shared.interleaved,f!==null){y=f;do _|=y.lane,y=y.next;while(y!==f)}else i===null&&(y.shared.lanes=0);uy|=_,u.lanes=_,u.memoizedState=J}}function yJ(u,f,l){if(u=f.effects,f.effects=null,u!==null)for(f=0;fl?l:4,u(!0);var r=$2.transition;$2.transition={};try{u(!1),f()}finally{nf=l,$2.transition=r}}function IU(){return Gr().memoizedState}function Bw(u,f,l){var r=l1(u);if(l={lane:r,action:l,hasEagerState:!1,eagerState:null,next:null},kU(u))gU(f,l);else if(l=OU(u,f,l,r),l!==null){var y=Kl();Dr(l,u,r,y),sU(l,f,r)}}function Vw(u,f,l){var r=l1(u),y={lane:r,action:l,hasEagerState:!1,eagerState:null,next:null};if(kU(u))gU(f,y);else{var i=u.alternate;if(u.lanes===0&&(i===null||i.lanes===0)&&(i=f.lastRenderedReducer,i!==null))try{var _=f.lastRenderedState,n=i(_,l);if(y.hasEagerState=!0,y.eagerState=n,tr(n,_)){var $=f.interleaved;$===null?(y.next=y,X5(f)):(y.next=$.next,$.next=y),f.interleaved=y;return}}catch(j){}finally{}l=OU(u,f,y,r),l!==null&&(y=Kl(),Dr(l,u,r,y),sU(l,f,r))}}function kU(u){var f=u.alternate;return u===Hf||f!==null&&f===Hf}function gU(u,f){C_=s3=!0;var l=u.pending;l===null?f.next=f:(f.next=l.next,l.next=f),u.pending=f}function sU(u,f,l){if((l&4194240)!==0){var r=f.lanes;r&=u.pendingLanes,l|=r,f.lanes=l,c5(u,l)}}var a3={readContext:zr,useCallback:Fl,useContext:Fl,useEffect:Fl,useImperativeHandle:Fl,useInsertionEffect:Fl,useLayoutEffect:Fl,useMemo:Fl,useReducer:Fl,useRef:Fl,useState:Fl,useDebugValue:Fl,useDeferredValue:Fl,useTransition:Fl,useMutableSource:Fl,useSyncExternalStore:Fl,useId:Fl,unstable_isNewReconciler:!1},Xw={readContext:zr,useCallback:function(u,f){return sr().memoizedState=[u,f===void 0?null:f],u},useContext:zr,useEffect:_J,useImperativeHandle:function(u,f,l){return l=l!==null&&l!==void 0?l.concat([u]):null,H3(4194308,4,xU.bind(null,f,u),l)},useLayoutEffect:function(u,f){return H3(4194308,4,u,f)},useInsertionEffect:function(u,f){return H3(4,2,u,f)},useMemo:function(u,f){var l=sr();return f=f===void 0?null:f,u=u(),l.memoizedState=[u,f],u},useReducer:function(u,f,l){var r=sr();return f=l!==void 0?l(f):f,r.memoizedState=r.baseState=f,u={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:u,lastRenderedState:f},r.queue=u,u=u.dispatch=Bw.bind(null,Hf,u),[r.memoizedState,u]},useRef:function(u){var f=sr();return u={current:u},f.memoizedState=u},useState:iJ,useDebugValue:C5,useDeferredValue:function(u){return sr().memoizedState=u},useTransition:function(){var u=iJ(!1),f=u[0];return u=Ow.bind(null,u[1]),sr().memoizedState=u,[f,u]},useMutableSource:function(){},useSyncExternalStore:function(u,f,l){var r=Hf,y=sr();if(Ef){if(l===void 0)throw Error(Uu(407));l=l()}else{if(l=f(),df===null)throw Error(Uu(349));(e1&30)!==0||DU(r,f,l)}y.memoizedState=l;var i={value:l,getSnapshot:f};return y.queue=i,_J(SU.bind(null,r,i,u),[u]),r.flags|=2048,_n(9,tU.bind(null,r,i,l,f),void 0,null),l},useId:function(){var u=sr(),f=df.identifierPrefix;if(Ef){var l=c0,r=W0;l=(r&~(1<<32-Yr(r)-1)).toString(32)+l,f=":"+f+"R"+l,l=rn++,0_||n[y]!==i[_]){var $=` +`+n[y].replace(" at new "," at ");return u.displayName&&$.includes("")&&($=$.replace("",u.displayName)),$}while(1<=y&&0<=_);break}}}finally{$4=!1,Error.prepareStackTrace=l}return(u=u?u.displayName||u.name:"")?My(u):""}function pK(u){switch(u.tag){case 5:return My(u.type);case 16:return My("Lazy");case 13:return My("Suspense");case 19:return My("SuspenseList");case 0:case 2:case 15:return u=t4(u.type,!1),u;case 11:return u=t4(u.type.render,!1),u;case 1:return u=t4(u.type,!0),u;default:return""}}function D4(u){if(u==null)return null;if(typeof u==="function")return u.displayName||u.name||null;if(typeof u==="string")return u;switch(u){case ri:return"Fragment";case li:return"Portal";case H4:return"Profiler";case T2:return"StrictMode";case B4:return"Suspense";case V4:return"SuspenseList"}if(typeof u==="object")switch(u.$$typeof){case Ij:return(u.displayName||"Context")+".Consumer";case kj:return(u._context.displayName||"Context")+".Provider";case E2:var f=u.render;return u=u.displayName,u||(u=f.displayName||f.name||"",u=u!==""?"ForwardRef("+u+")":"ForwardRef"),u;case Z2:return f=u.displayName||null,f!==null?f:D4(u.type)||"Memo";case d0:f=u._payload,u=u._init;try{return D4(u(f))}catch(l){}}return null}function PK(u){var f=u.type;switch(u.tag){case 24:return"Cache";case 9:return(f.displayName||"Context")+".Consumer";case 10:return(f._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return u=f.render,u=u.displayName||u.name||"",f.displayName||(u!==""?"ForwardRef("+u+")":"ForwardRef");case 7:return"Fragment";case 5:return f;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return D4(f);case 8:return f===T2?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof f==="function")return f.displayName||f.name||null;if(typeof f==="string")return f}return null}function Fn(u){switch(typeof u){case"boolean":case"number":case"string":case"undefined":return u;case"object":return u;default:return""}}function sj(u){var f=u.type;return(u=u.nodeName)&&u.toLowerCase()==="input"&&(f==="checkbox"||f==="radio")}function MK(u){var f=sj(u)?"checked":"value",l=Object.getOwnPropertyDescriptor(u.constructor.prototype,f),r=""+u[f];if(!u.hasOwnProperty(f)&&typeof l<"u"&&typeof l.get==="function"&&typeof l.set==="function"){var{get:n,set:i}=l;return Object.defineProperty(u,f,{configurable:!0,get:function(){return n.call(this)},set:function(y){r=""+y,i.call(this,y)}}),Object.defineProperty(u,f,{enumerable:l.enumerable}),{getValue:function(){return r},setValue:function(y){r=""+y},stopTracking:function(){u._valueTracker=null,delete u[f]}}}}function Jt(u){u._valueTracker||(u._valueTracker=MK(u))}function aj(u){if(!u)return!1;var f=u._valueTracker;if(!f)return!0;var l=f.getValue(),r="";return u&&(r=sj(u)?u.checked?"true":"false":u.value),u=r,u!==l?(f.setValue(u),!0):!1}function Mt(u){if(u=u||(typeof document<"u"?document:void 0),typeof u>"u")return null;try{return u.activeElement||u.body}catch(f){return u.body}}function X4(u,f){var l=f.checked;return Of({},f,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:l!=null?l:u._wrapperState.initialChecked})}function b7(u,f){var l=f.defaultValue==null?"":f.defaultValue,r=f.checked!=null?f.checked:f.defaultChecked;l=Fn(f.value!=null?f.value:l),u._wrapperState={initialChecked:r,initialValue:l,controlled:f.type==="checkbox"||f.type==="radio"?f.checked!=null:f.value!=null}}function oj(u,f){f=f.checked,f!=null&&z2(u,"checked",f,!1)}function Y4(u,f){oj(u,f);var l=Fn(f.value),r=f.type;if(l!=null)if(r==="number"){if(l===0&&u.value===""||u.value!=l)u.value=""+l}else u.value!==""+l&&(u.value=""+l);else if(r==="submit"||r==="reset"){u.removeAttribute("value");return}f.hasOwnProperty("value")?S4(u,f.type,l):f.hasOwnProperty("defaultValue")&&S4(u,f.type,Fn(f.defaultValue)),f.checked==null&&f.defaultChecked!=null&&(u.defaultChecked=!!f.defaultChecked)}function v7(u,f,l){if(f.hasOwnProperty("value")||f.hasOwnProperty("defaultValue")){var r=f.type;if(!(r!=="submit"&&r!=="reset"||f.value!==void 0&&f.value!==null))return;f=""+u._wrapperState.initialValue,l||f===u.value||(u.value=f),u.defaultValue=f}l=u.name,l!==""&&(u.name=""),u.defaultChecked=!!u._wrapperState.initialChecked,l!==""&&(u.name=l)}function S4(u,f,l){if(f!=="number"||Mt(u.ownerDocument)!==u)l==null?u.defaultValue=""+u._wrapperState.initialValue:u.defaultValue!==""+l&&(u.defaultValue=""+l)}var Cy=Array.isArray;function Ji(u,f,l,r){if(u=u.options,f){f={};for(var n=0;n"+f.valueOf().toString()+"";for(f=Ut.firstChild;u.firstChild;)u.removeChild(u.firstChild);for(;f.firstChild;)u.appendChild(f.firstChild)}});function r_(u,f){if(f){var l=u.firstChild;if(l&&l===u.lastChild&&l.nodeType===3){l.nodeValue=f;return}}u.textContent=f}var Iy={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},CK=["Webkit","ms","Moz","O"];Object.keys(Iy).forEach(function(u){CK.forEach(function(f){f=f+u.charAt(0).toUpperCase()+u.substring(1),Iy[f]=Iy[u]})});function fF(u,f,l){return f==null||typeof f==="boolean"||f===""?"":l||typeof f!=="number"||f===0||Iy.hasOwnProperty(u)&&Iy[u]?(""+f).trim():f+"px"}function lF(u,f){u=u.style;for(var l in f)if(f.hasOwnProperty(l)){var r=l.indexOf("--")===0,n=fF(l,f[l],r);l==="float"&&(l="cssFloat"),r?u.setProperty(l,n):u[l]=n}}var RK=Of({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function P4(u,f){if(f){if(RK[u]&&(f.children!=null||f.dangerouslySetInnerHTML!=null))throw Error(Uu(137,u));if(f.dangerouslySetInnerHTML!=null){if(f.children!=null)throw Error(Uu(60));if(typeof f.dangerouslySetInnerHTML!=="object"||!("__html"in f.dangerouslySetInnerHTML))throw Error(Uu(61))}if(f.style!=null&&typeof f.style!=="object")throw Error(Uu(62))}}function M4(u,f){if(u.indexOf("-")===-1)return typeof f.is==="string";switch(u){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var C4=null;function O2(u){return u=u.target||u.srcElement||window,u.correspondingUseElement&&(u=u.correspondingUseElement),u.nodeType===3?u.parentNode:u}var R4=null,Ui=null,Qi=null;function g7(u){if(u=L_(u)){if(typeof R4!=="function")throw Error(Uu(280));var f=u.stateNode;f&&(f=F3(f),R4(u.stateNode,u.type,f))}}function rF(u){Ui?Qi?Qi.push(u):Qi=[u]:Ui=u}function nF(){if(Ui){var u=Ui,f=Qi;if(Qi=Ui=null,g7(u),f)for(u=0;u>>=0,u===0?32:31-(dK(u)/eK|0)|0}var Qt=64,qt=4194304;function Ry(u){switch(u&-u){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: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 u&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return u&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return u}}function ht(u,f){var l=u.pendingLanes;if(l===0)return 0;var r=0,n=u.suspendedLanes,i=u.pingedLanes,y=l&268435455;if(y!==0){var _=y&~n;_!==0?r=Ry(_):(i&=y,i!==0&&(r=Ry(i)))}else y=l&~n,y!==0?r=Ry(y):i!==0&&(r=Ry(i));if(r===0)return 0;if(f!==0&&f!==r&&(f&n)===0&&(n=r&-r,i=f&-f,n>=i||n===16&&(i&4194240)!==0))return f;if((r&4)!==0&&(r|=l&16),f=u.entangledLanes,f!==0)for(u=u.entanglements,f&=r;0l;l++)f.push(u);return f}function W_(u,f,l){u.pendingLanes|=f,f!==536870912&&(u.suspendedLanes=0,u.pingedLanes=0),u=u.eventTimes,f=31-mr(f),u[f]=l}function rG(u,f){var l=u.pendingLanes&~f;u.pendingLanes=f,u.suspendedLanes=0,u.pingedLanes=0,u.expiredLanes&=f,u.mutableReadLanes&=f,u.entangledLanes&=f,f=u.entanglements;var r=u.eventTimes;for(u=u.expirationTimes;0=sy),rj=String.fromCharCode(32),nj=!1;function zF(u,f){switch(u){case"keyup":return VG.indexOf(f.keyCode)!==-1;case"keydown":return f.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function TF(u){return u=u.detail,typeof u==="object"&&"data"in u?u.data:null}var ni=!1;function XG(u,f){switch(u){case"compositionend":return TF(f);case"keypress":if(f.which!==32)return null;return nj=!0,rj;case"textInput":return u=f.data,u===rj&&nj?null:u;default:return null}}function YG(u,f){if(ni)return u==="compositionend"||!m2&&zF(u,f)?(u=KF(),Ht=X2=ln=null,ni=!1,u):null;switch(u){case"paste":return null;case"keypress":if(!(f.ctrlKey||f.altKey||f.metaKey)||f.ctrlKey&&f.altKey){if(f.char&&1=f)return{node:l,offset:f-u};u=r}u:{for(;l;){if(l.nextSibling){l=l.nextSibling;break u}l=l.parentNode}l=void 0}l=_j(l)}}function HF(u,f){return u&&f?u===f?!0:u&&u.nodeType===3?!1:f&&f.nodeType===3?HF(u,f.parentNode):("contains"in u)?u.contains(f):u.compareDocumentPosition?!!(u.compareDocumentPosition(f)&16):!1:!1}function BF(){for(var u=window,f=Mt();f instanceof u.HTMLIFrameElement;){try{var l=typeof f.contentWindow.location.href==="string"}catch(r){l=!1}if(l)u=f.contentWindow;else break;f=Mt(u.document)}return f}function p2(u){var f=u&&u.nodeName&&u.nodeName.toLowerCase();return f&&(f==="input"&&(u.type==="text"||u.type==="search"||u.type==="tel"||u.type==="url"||u.type==="password")||f==="textarea"||u.contentEditable==="true")}function hG(u){var f=BF(),l=u.focusedElem,r=u.selectionRange;if(f!==l&&l&&l.ownerDocument&&HF(l.ownerDocument.documentElement,l)){if(r!==null&&p2(l)){if(f=r.start,u=r.end,u===void 0&&(u=f),"selectionStart"in l)l.selectionStart=f,l.selectionEnd=Math.min(u,l.value.length);else if(u=(f=l.ownerDocument||document)&&f.defaultView||window,u.getSelection){u=u.getSelection();var n=l.textContent.length,i=Math.min(r.start,n);r=r.end===void 0?i:Math.min(r.end,n),!u.extend&&i>r&&(n=r,r=i,i=n),n=$j(l,i);var y=$j(l,r);n&&y&&(u.rangeCount!==1||u.anchorNode!==n.node||u.anchorOffset!==n.offset||u.focusNode!==y.node||u.focusOffset!==y.offset)&&(f=f.createRange(),f.setStart(n.node,n.offset),u.removeAllRanges(),i>r?(u.addRange(f),u.extend(y.node,y.offset)):(f.setEnd(y.node,y.offset),u.addRange(f)))}}f=[];for(u=l;u=u.parentNode;)u.nodeType===1&&f.push({element:u,left:u.scrollLeft,top:u.scrollTop});typeof l.focus==="function"&&l.focus();for(l=0;l=document.documentMode,ii=null,I4=null,oy=null,g4=!1;function tj(u,f,l){var r=l.window===l?l.document:l.nodeType===9?l:l.ownerDocument;g4||ii==null||ii!==Mt(r)||(r=ii,("selectionStart"in r)&&p2(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),oy&&t_(oy,r)||(oy=r,r=kt(I4,"onSelect"),0$i||(u.current=l2[$i],l2[$i]=null,$i--)}function Nf(u,f){$i++,l2[$i]=u.current,u.current=f}var Jn={},ql=Qn(Jn),Ml=Qn(!1),$1=Jn;function Li(u,f){var l=u.type.contextTypes;if(!l)return Jn;var r=u.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===f)return r.__reactInternalMemoizedMaskedChildContext;var n={},i;for(i in l)n[i]=f[i];return r&&(u=u.stateNode,u.__reactInternalMemoizedUnmaskedChildContext=f,u.__reactInternalMemoizedMaskedChildContext=n),n}function Cl(u){return u=u.childContextTypes,u!==null&&u!==void 0}function gt(){Lf(Ml),Lf(ql)}function Qj(u,f,l){if(ql.current!==Jn)throw Error(Uu(168));Nf(ql,f),Nf(Ml,l)}function MF(u,f,l){var r=u.stateNode;if(f=f.childContextTypes,typeof r.getChildContext!=="function")return l;r=r.getChildContext();for(var n in r)if(!(n in f))throw Error(Uu(108,PK(u)||"Unknown",n));return Of({},l,r)}function st(u){return u=(u=u.stateNode)&&u.__reactInternalMemoizedMergedChildContext||Jn,$1=ql.current,Nf(ql,u),Nf(Ml,Ml.current),!0}function qj(u,f,l){var r=u.stateNode;if(!r)throw Error(Uu(169));l?(u=MF(u,f,$1),r.__reactInternalMemoizedMergedChildContext=u,Lf(Ml),Lf(ql),Nf(ql,u)):Lf(Ml),Nf(Ml,l)}var w0=null,J3=!1,W4=!1;function CF(u){w0===null?w0=[u]:w0.push(u)}function eG(u){J3=!0,CF(u)}function qn(){if(!W4&&w0!==null){W4=!0;var u=0,f=tf;try{var l=w0;for(tf=1;u>=y,n-=y,L0=1<<32-mr(f)+n|l<E?(D=H,H=null):D=H.sibling;var h=F(J,H,q[E],W);if(h===null){H===null&&(H=D);break}u&&H&&h.alternate===null&&f(J,H),N=i(h,N,E),Z===null?z=h:Z.sibling=h,Z=h,H=D}if(E===q.length)return l(J,H),zf&&f1(J,E),z;if(H===null){for(;EE?(D=H,H=null):D=H.sibling;var B=F(J,H,h.value,W);if(B===null){H===null&&(H=D);break}u&&H&&B.alternate===null&&f(J,H),N=i(B,N,E),Z===null?z=B:Z.sibling=B,Z=B,H=D}if(h.done)return l(J,H),zf&&f1(J,E),z;if(H===null){for(;!h.done;E++,h=q.next())h=j(J,h.value,W),h!==null&&(N=i(h,N,E),Z===null?z=h:Z.sibling=h,Z=h);return zf&&f1(J,E),z}for(H=r(J,H);!h.done;E++,h=q.next())h=U(H,J,E,h.value,W),h!==null&&(u&&h.alternate!==null&&H.delete(h.key===null?E:h.key),N=i(h,N,E),Z===null?z=h:Z.sibling=h,Z=h);return u&&H.forEach(function(X){return f(J,X)}),zf&&f1(J,E),z}function L(J,N,q,W){if(typeof q==="object"&&q!==null&&q.type===ri&&q.key===null&&(q=q.props.children),typeof q==="object"&&q!==null){switch(q.$$typeof){case Ft:u:{for(var z=q.key,Z=N;Z!==null;){if(Z.key===z){if(z=q.type,z===ri){if(Z.tag===7){l(J,Z.sibling),N=n(Z,q.props.children),N.return=J,J=N;break u}}else if(Z.elementType===z||typeof z==="object"&&z!==null&&z.$$typeof===d0&&wj(z)===Z.type){l(J,Z.sibling),N=n(Z,q.props),N.ref=my(J,Z,q),N.return=J,J=N;break u}l(J,Z);break}else f(J,Z);Z=Z.sibling}q.type===ri?(N=_1(q.props.children,J.mode,W,q.key),N.return=J,J=N):(W=Pt(q.type,q.key,q.props,null,J.mode,W),W.ref=my(J,N,q),W.return=J,J=W)}return y(J);case li:u:{for(Z=q.key;N!==null;){if(N.key===Z)if(N.tag===4&&N.stateNode.containerInfo===q.containerInfo&&N.stateNode.implementation===q.implementation){l(J,N.sibling),N=n(N,q.children||[]),N.return=J,J=N;break u}else{l(J,N);break}else f(J,N);N=N.sibling}N=Z4(q,J.mode,W),N.return=J,J=N}return y(J);case d0:return Z=q._init,L(J,N,Z(q._payload),W)}if(Cy(q))return Q(J,N,q,W);if(Dy(q))return w(J,N,q,W);zt(J,q)}return typeof q==="string"&&q!==""||typeof q==="number"?(q=""+q,N!==null&&N.tag===6?(l(J,N.sibling),N=n(N,q),N.return=J,J=N):(l(J,N),N=E4(q,J.mode,W),N.return=J,J=N),y(J)):l(J,N)}return L}var Gi=bF(!0),vF=bF(!1),dt=Qn(null),et=null,Ai=null,R2=null;function x2(){R2=Ai=et=null}function h2(u){var f=dt.current;Lf(dt),u._currentValue=f}function i2(u,f,l){for(;u!==null;){var r=u.alternate;if((u.childLanes&f)!==f?(u.childLanes|=f,r!==null&&(r.childLanes|=f)):r!==null&&(r.childLanes&f)!==f&&(r.childLanes|=f),u===l)break;u=u.return}}function Ni(u,f){et=u,R2=Ai=null,u=u.dependencies,u!==null&&u.firstContext!==null&&((u.lanes&f)!==0&&(Pl=!0),u.firstContext=null)}function Lr(u){var f=u._currentValue;if(R2!==u)if(u={context:u,memoizedValue:f,next:null},Ai===null){if(et===null)throw Error(Uu(308));Ai=u,et.dependencies={lanes:0,firstContext:u}}else Ai=Ai.next=u;return f}var n1=null;function b2(u){n1===null?n1=[u]:n1.push(u)}function kF(u,f,l,r){var n=f.interleaved;return n===null?(l.next=l,b2(f)):(l.next=n.next,n.next=l),f.interleaved=l,E0(u,r)}function E0(u,f){u.lanes|=f;var l=u.alternate;l!==null&&(l.lanes|=f),l=u;for(u=u.return;u!==null;)u.childLanes|=f,l=u.alternate,l!==null&&(l.childLanes|=f),l=u,u=u.return;return l.tag===3?l.stateNode:null}var e0=!1;function v2(u){u.updateQueue={baseState:u.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function IF(u,f){u=u.updateQueue,f.updateQueue===u&&(f.updateQueue={baseState:u.baseState,firstBaseUpdate:u.firstBaseUpdate,lastBaseUpdate:u.lastBaseUpdate,shared:u.shared,effects:u.effects})}function G0(u,f){return{eventTime:u,lane:f,tag:0,payload:null,callback:null,next:null}}function tn(u,f,l){var r=u.updateQueue;if(r===null)return null;if(r=r.shared,(lf&2)!==0){var n=r.pending;return n===null?f.next=f:(f.next=n.next,n.next=f),r.pending=f,E0(u,l)}return n=r.interleaved,n===null?(f.next=f,b2(r)):(f.next=n.next,n.next=f),r.interleaved=f,E0(u,l)}function Dt(u,f,l){if(f=f.updateQueue,f!==null&&(f=f.shared,(l&4194240)!==0)){var r=f.lanes;r&=u.pendingLanes,l|=r,f.lanes=l,B2(u,l)}}function Lj(u,f){var{updateQueue:l,alternate:r}=u;if(r!==null&&(r=r.updateQueue,l===r)){var n=null,i=null;if(l=l.firstBaseUpdate,l!==null){do{var y={eventTime:l.eventTime,lane:l.lane,tag:l.tag,payload:l.payload,callback:l.callback,next:null};i===null?n=i=y:i=i.next=y,l=l.next}while(l!==null);i===null?n=i=f:i=i.next=f}else n=i=f;l={baseState:r.baseState,firstBaseUpdate:n,lastBaseUpdate:i,shared:r.shared,effects:r.effects},u.updateQueue=l;return}u=l.lastBaseUpdate,u===null?l.firstBaseUpdate=f:u.next=f,l.lastBaseUpdate=f}function u3(u,f,l,r){var n=u.updateQueue;e0=!1;var{firstBaseUpdate:i,lastBaseUpdate:y}=n,_=n.shared.pending;if(_!==null){n.shared.pending=null;var $=_,c=$.next;$.next=null,y===null?i=c:y.next=c,y=$;var A=u.alternate;A!==null&&(A=A.updateQueue,_=A.lastBaseUpdate,_!==y&&(_===null?A.firstBaseUpdate=c:_.next=c,A.lastBaseUpdate=$))}if(i!==null){var j=n.baseState;y=0,A=c=$=null,_=i;do{var{lane:F,eventTime:U}=_;if((r&F)===F){A!==null&&(A=A.next={eventTime:U,lane:0,tag:_.tag,payload:_.payload,callback:_.callback,next:null});u:{var Q=u,w=_;switch(F=f,U=l,w.tag){case 1:if(Q=w.payload,typeof Q==="function"){j=Q.call(U,j,F);break u}j=Q;break u;case 3:Q.flags=Q.flags&-65537|128;case 0:if(Q=w.payload,F=typeof Q==="function"?Q.call(U,j,F):Q,F===null||F===void 0)break u;j=Of({},j,F);break u;case 2:e0=!0}}_.callback!==null&&_.lane!==0&&(u.flags|=64,F=n.effects,F===null?n.effects=[_]:F.push(_))}else U={eventTime:U,lane:F,tag:_.tag,payload:_.payload,callback:_.callback,next:null},A===null?(c=A=U,$=j):A=A.next=U,y|=F;if(_=_.next,_===null)if(_=n.shared.pending,_===null)break;else F=_,_=F.next,F.next=null,n.lastBaseUpdate=F,n.shared.pending=null}while(1);if(A===null&&($=j),n.baseState=$,n.firstBaseUpdate=c,n.lastBaseUpdate=A,f=n.shared.interleaved,f!==null){n=f;do y|=n.lane,n=n.next;while(n!==f)}else i===null&&(n.shared.lanes=0);A1|=y,u.lanes=y,u.memoizedState=j}}function Kj(u,f,l){if(u=f.effects,f.effects=null,u!==null)for(f=0;fl?l:4,u(!0);var r=L4.transition;L4.transition={};try{u(!1),f()}finally{tf=l,L4.transition=r}}function cJ(){return Kr().memoizedState}function rz(u,f,l){var r=An(u);if(l={lane:r,action:l,hasEagerState:!1,eagerState:null,next:null},AJ(u))jJ(f,l);else if(l=kF(u,f,l,r),l!==null){var n=Ol();pr(l,u,r,n),FJ(l,f,r)}}function nz(u,f,l){var r=An(u),n={lane:r,action:l,hasEagerState:!1,eagerState:null,next:null};if(AJ(u))jJ(f,n);else{var i=u.alternate;if(u.lanes===0&&(i===null||i.lanes===0)&&(i=f.lastRenderedReducer,i!==null))try{var y=f.lastRenderedState,_=i(y,l);if(n.hasEagerState=!0,n.eagerState=_,Pr(_,y)){var $=f.interleaved;$===null?(n.next=n,b2(f)):(n.next=$.next,$.next=n),f.interleaved=n;return}}catch(c){}finally{}l=kF(u,f,n,r),l!==null&&(n=Ol(),pr(l,u,r,n),FJ(l,f,r))}}function AJ(u){var f=u.alternate;return u===Zf||f!==null&&f===Zf}function jJ(u,f){dy=l3=!0;var l=u.pending;l===null?f.next=f:(f.next=l.next,l.next=f),u.pending=f}function FJ(u,f,l){if((l&4194240)!==0){var r=f.lanes;r&=u.pendingLanes,l|=r,f.lanes=l,B2(u,l)}}var r3={readContext:Lr,useCallback:Jl,useContext:Jl,useEffect:Jl,useImperativeHandle:Jl,useInsertionEffect:Jl,useLayoutEffect:Jl,useMemo:Jl,useReducer:Jl,useRef:Jl,useState:Jl,useDebugValue:Jl,useDeferredValue:Jl,useTransition:Jl,useMutableSource:Jl,useSyncExternalStore:Jl,useId:Jl,unstable_isNewReconciler:!1},iz={readContext:Lr,useCallback:function(u,f){return dr().memoizedState=[u,f===void 0?null:f],u},useContext:Lr,useEffect:zj,useImperativeHandle:function(u,f,l){return l=l!==null&&l!==void 0?l.concat([u]):null,Yt(4194308,4,iJ.bind(null,f,u),l)},useLayoutEffect:function(u,f){return Yt(4194308,4,u,f)},useInsertionEffect:function(u,f){return Yt(4,2,u,f)},useMemo:function(u,f){var l=dr();return f=f===void 0?null:f,u=u(),l.memoizedState=[u,f],u},useReducer:function(u,f,l){var r=dr();return f=l!==void 0?l(f):f,r.memoizedState=r.baseState=f,u={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:u,lastRenderedState:f},r.queue=u,u=u.dispatch=rz.bind(null,Zf,u),[r.memoizedState,u]},useRef:function(u){var f=dr();return u={current:u},f.memoizedState=u},useState:Gj,useDebugValue:e2,useDeferredValue:function(u){return dr().memoizedState=u},useTransition:function(){var u=Gj(!1),f=u[0];return u=lz.bind(null,u[1]),dr().memoizedState=u,[f,u]},useMutableSource:function(){},useSyncExternalStore:function(u,f,l){var r=Zf,n=dr();if(zf){if(l===void 0)throw Error(Uu(407));l=l()}else{if(l=f(),fl===null)throw Error(Uu(349));(c1&30)!==0||oF(r,f,l)}n.memoizedState=l;var i={value:l,getSnapshot:f};return n.queue=i,zj(eF.bind(null,r,i,u),[u]),r.flags|=2048,q_(9,dF.bind(null,r,i,l,f),void 0,null),l},useId:function(){var u=dr(),f=fl.identifierPrefix;if(zf){var l=K0,r=L0;l=(r&~(1<<32-mr(r)-1)).toString(32)+l,f=":"+f+"R"+l,l=U_++,0",u=u.removeChild(u.firstChild)):typeof r.is==="string"?u=_.createElement(l,{is:r.is}):(u=_.createElement(l),l==="select"&&(_=u,r.multiple?_.multiple=!0:r.size&&(_.size=r.size))):u=_.createElementNS(u,l),u[ar]=f,u[un]=r,iQ(u,f,!1,!1),f.stateNode=u;u:{switch(_=Z2(l,r),l){case"dialog":zf("cancel",u),zf("close",u),y=r;break;case"iframe":case"object":case"embed":zf("load",u),y=r;break;case"video":case"audio":for(y=0;yJi&&(f.flags|=128,r=!0,T_(i,!1),f.lanes=4194304)}else{if(!r)if(u=g3(_),u!==null){if(f.flags|=128,r=!0,l=u.updateQueue,l!==null&&(f.updateQueue=l,f.flags|=4),T_(i,!0),i.tail===null&&i.tailMode==="hidden"&&!_.alternate&&!Ef)return Jl(f),null}else 2*tf()-i.renderingStartTime>Ji&&l!==1073741824&&(f.flags|=128,r=!0,T_(i,!1),f.lanes=4194304);i.isBackwards?(_.sibling=f.child,f.child=_):(l=i.last,l!==null?l.sibling=_:f.child=_,i.last=_)}if(i.tail!==null)return f=i.tail,i.rendering=f,i.tail=f.sibling,i.renderingStartTime=tf(),f.sibling=null,l=Zf.current,cf(Zf,r?l&1|2:l&1),f;return Jl(f),null;case 22:case 23:return I5(),r=f.memoizedState!==null,u!==null&&u.memoizedState!==null!==r&&(f.flags|=8192),r&&(f.mode&1)!==0?(dl&1073741824)!==0&&(Jl(f),f.subtreeFlags&6&&(f.flags|=8192)):Jl(f),null;case 24:return null;case 25:return null}throw Error(Uu(156,f.tag))}function pw(u,f){switch(Z5(f),f.tag){case 1:return Pl(f.type)&&x3(),u=f.flags,u&65536?(f.flags=u&-65537|128,f):null;case 3:return ji(),Gf(Sl),Gf(Ql),S5(),u=f.flags,(u&65536)!==0&&(u&128)===0?(f.flags=u&-65537|128,f):null;case 5:return t5(f),null;case 13:if(Gf(Zf),u=f.memoizedState,u!==null&&u.dehydrated!==null){if(f.alternate===null)throw Error(Uu(340));$i()}return u=f.flags,u&65536?(f.flags=u&-65537|128,f):null;case 19:return Gf(Zf),null;case 4:return ji(),null;case 10:return V5(f.type._context),null;case 22:case 23:return I5(),null;case 24:return null;default:return null}}var z3=!1,Ul=!1,Cw=typeof WeakSet==="function"?WeakSet:Set,wu=null;function dy(u,f){var l=u.ref;if(l!==null)if(typeof l==="function")try{l(null)}catch(r){Vf(u,f,r)}else l.current=null}function f5(u,f,l){try{l()}catch(r){Vf(u,f,r)}}var cJ=!1;function xw(u,f){if(m2=M3,u=JU(),E5(u)){if("selectionStart"in u)var l={start:u.selectionStart,end:u.selectionEnd};else u:{l=(l=u.ownerDocument)&&l.defaultView||window;var r=l.getSelection&&l.getSelection();if(r&&r.rangeCount!==0){l=r.anchorNode;var{anchorOffset:y,focusNode:i}=r;r=r.focusOffset;try{l.nodeType,i.nodeType}catch(z){l=null;break u}var _=0,n=-1,$=-1,j=0,F=0,J=u,U=null;f:for(;;){for(var q;;){if(J!==l||y!==0&&J.nodeType!==3||(n=_+y),J!==i||r!==0&&J.nodeType!==3||($=_+r),J.nodeType===3&&(_+=J.nodeValue.length),(q=J.firstChild)===null)break;U=J,J=q}for(;;){if(J===u)break f;if(U===l&&++j===y&&(n=_),U===i&&++F===r&&($=_),(q=J.nextSibling)!==null)break;J=U,U=J.parentNode}J=q}l=n===-1||$===-1?null:{start:n,end:$}}else l=null}l=l||{start:0,end:0}}else l=null;p2={focusedElem:u,selectionRange:l},M3=!1;for(wu=f;wu!==null;)if(f=wu,u=f.child,(f.subtreeFlags&1028)!==0&&u!==null)u.return=f,wu=u;else for(;wu!==null;){f=wu;try{var W=f.alternate;if((f.flags&1024)!==0)switch(f.tag){case 0:case 11:case 15:break;case 1:if(W!==null){var{memoizedProps:G,memoizedState:K}=W,Q=f.stateNode,N=Q.getSnapshotBeforeUpdate(f.elementType===f.type?G:Br(f.type,G),K);Q.__reactInternalSnapshotBeforeUpdate=N}break;case 3:var c=f.stateNode.containerInfo;c.nodeType===1?c.textContent="":c.nodeType===9&&c.documentElement&&c.removeChild(c.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(Uu(163))}}catch(z){Vf(f,f.return,z)}if(u=f.sibling,u!==null){u.return=f.return,wu=u;break}wu=f.return}return W=cJ,cJ=!1,W}function x_(u,f,l){var r=f.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var y=r=r.next;do{if((y.tag&u)===u){var i=y.destroy;y.destroy=void 0,i!==void 0&&f5(f,l,i)}y=y.next}while(y!==r)}}function j6(u,f){if(f=f.updateQueue,f=f!==null?f.lastEffect:null,f!==null){var l=f=f.next;do{if((l.tag&u)===u){var r=l.create;l.destroy=r()}l=l.next}while(l!==f)}}function l5(u){var f=u.ref;if(f!==null){var l=u.stateNode;switch(u.tag){case 5:u=l;break;default:u=l}typeof f==="function"?f(u):f.current=u}}function $Q(u){var f=u.alternate;f!==null&&(u.alternate=null,$Q(f)),u.child=null,u.deletions=null,u.sibling=null,u.tag===5&&(f=u.stateNode,f!==null&&(delete f[ar],delete f[un],delete f[R2],delete f[ww],delete f[Ew])),u.stateNode=null,u.return=null,u.dependencies=null,u.memoizedProps=null,u.memoizedState=null,u.pendingProps=null,u.stateNode=null,u.updateQueue=null}function AQ(u){return u.tag===5||u.tag===3||u.tag===4}function NJ(u){u:for(;;){for(;u.sibling===null;){if(u.return===null||AQ(u.return))return null;u=u.return}u.sibling.return=u.return;for(u=u.sibling;u.tag!==5&&u.tag!==6&&u.tag!==18;){if(u.flags&2)continue u;if(u.child===null||u.tag===4)continue u;else u.child.return=u,u=u.child}if(!(u.flags&2))return u.stateNode}}function r5(u,f,l){var r=u.tag;if(r===5||r===6)u=u.stateNode,f?l.nodeType===8?l.parentNode.insertBefore(u,f):l.insertBefore(u,f):(l.nodeType===8?(f=l.parentNode,f.insertBefore(u,l)):(f=l,f.appendChild(u)),l=l._reactRootContainer,l!==null&&l!==void 0||f.onclick!==null||(f.onclick=C3));else if(r!==4&&(u=u.child,u!==null))for(r5(u,f,l),u=u.sibling;u!==null;)r5(u,f,l),u=u.sibling}function y5(u,f,l){var r=u.tag;if(r===5||r===6)u=u.stateNode,f?l.insertBefore(u,f):l.appendChild(u);else if(r!==4&&(u=u.child,u!==null))for(y5(u,f,l),u=u.sibling;u!==null;)y5(u,f,l),u=u.sibling}var rl=null,Vr=!1;function h0(u,f,l){for(l=l.child;l!==null;)jQ(u,f,l),l=l.sibling}function jQ(u,f,l){if(or&&typeof or.onCommitFiberUnmount==="function")try{or.onCommitFiberUnmount(l6,l)}catch(n){}switch(l.tag){case 5:Ul||dy(l,f);case 6:var r=rl,y=Vr;rl=null,h0(u,f,l),rl=r,Vr=y,rl!==null&&(Vr?(u=rl,l=l.stateNode,u.nodeType===8?u.parentNode.removeChild(l):u.removeChild(l)):rl.removeChild(l.stateNode));break;case 18:rl!==null&&(Vr?(u=rl,l=l.stateNode,u.nodeType===8?i2(u.parentNode,l):u.nodeType===1&&i2(u,l),s_(u)):i2(rl,l.stateNode));break;case 4:r=rl,y=Vr,rl=l.stateNode.containerInfo,Vr=!0,h0(u,f,l),rl=r,Vr=y;break;case 0:case 11:case 14:case 15:if(!Ul&&(r=l.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){y=r=r.next;do{var i=y,_=i.destroy;i=i.tag,_!==void 0&&((i&2)!==0?f5(l,f,_):(i&4)!==0&&f5(l,f,_)),y=y.next}while(y!==r)}h0(u,f,l);break;case 1:if(!Ul&&(dy(l,f),r=l.stateNode,typeof r.componentWillUnmount==="function"))try{r.props=l.memoizedProps,r.state=l.memoizedState,r.componentWillUnmount()}catch(n){Vf(l,f,n)}h0(u,f,l);break;case 21:h0(u,f,l);break;case 22:l.mode&1?(Ul=(r=Ul)||l.memoizedState!==null,h0(u,f,l),Ul=r):h0(u,f,l);break;default:h0(u,f,l)}}function zJ(u){var f=u.updateQueue;if(f!==null){u.updateQueue=null;var l=u.stateNode;l===null&&(l=u.stateNode=new Cw),f.forEach(function(r){var y=aw.bind(null,u,r);l.has(r)||(l.add(r),r.then(y,y))})}}function Or(u,f){var l=f.deletions;if(l!==null)for(var r=0;ry&&(y=_),r&=~i}if(r=y,r=tf()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3000>r?3000:4320>r?4320:1960*hw(r/1960))-r,10u?16:u,s0===null)var r=!1;else{if(u=s0,s0=null,e3=0,(lf&6)!==0)throw Error(Uu(331));var y=lf;lf|=4;for(wu=u.current;wu!==null;){var i=wu,_=i.child;if((wu.flags&16)!==0){var n=i.deletions;if(n!==null){for(var $=0;$tf()-b5?s1(u,0):h5|=l),Ml(u,f)}function NQ(u,f){f===0&&((u.mode&1)===0?f=1:(f=j3,j3<<=1,(j3&130023424)===0&&(j3=4194304)));var l=Kl();u=K0(u,f),u!==null&&($n(u,f,l),Ml(u,l))}function sw(u){var f=u.memoizedState,l=0;f!==null&&(l=f.retryLane),NQ(u,l)}function aw(u,f){var l=0;switch(u.tag){case 13:var{stateNode:r,memoizedState:y}=u;y!==null&&(l=y.retryLane);break;case 19:r=u.stateNode;break;default:throw Error(Uu(314))}r!==null&&r.delete(f),NQ(u,l)}var zQ;zQ=function(u,f,l){if(u!==null)if(u.memoizedProps!==f.pendingProps||Sl.current)tl=!0;else{if((u.lanes&l)===0&&(f.flags&128)===0)return tl=!1,Mw(u,f,l);tl=(u.flags&131072)!==0?!0:!1}else tl=!1,Ef&&(f.flags&1048576)!==0&&wU(f,b3,f.index);switch(f.lanes=0,f.tag){case 2:var r=f.type;O3(u,f),u=f.pendingProps;var y=ni(f,Ql.current);yi(f,l),y=M5(null,f,r,u,y,l);var i=m5();return f.flags|=1,typeof y==="object"&&y!==null&&typeof y.render==="function"&&y.$$typeof===void 0?(f.tag=1,f.memoizedState=null,f.updateQueue=null,Pl(r)?(i=!0,R3(f)):i=!1,f.memoizedState=y.state!==null&&y.state!==void 0?y.state:null,Y5(f),y.updater=A6,f.stateNode=y,y._reactInternals=f,g2(f,r,u,l),f=o2(null,f,r,!0,i,l)):(f.tag=0,Ef&&i&&T5(f),Gl(null,f,y,l),f=f.child),f;case 16:r=f.elementType;u:{switch(O3(u,f),u=f.pendingProps,y=r._init,r=y(r._payload),f.type=r,y=f.tag=dw(r),u=Br(r,u),y){case 0:f=a2(null,f,r,u,l);break u;case 1:f=QJ(null,f,r,u,l);break u;case 11:f=JJ(null,f,r,u,l);break u;case 14:f=UJ(null,f,r,Br(r.type,u),l);break u}throw Error(Uu(306,r,""))}return f;case 0:return r=f.type,y=f.pendingProps,y=f.elementType===r?y:Br(r,y),a2(u,f,r,y,l);case 1:return r=f.type,y=f.pendingProps,y=f.elementType===r?y:Br(r,y),QJ(u,f,r,y,l);case 3:u:{if(lQ(f),u===null)throw Error(Uu(387));r=f.pendingProps,i=f.memoizedState,y=i.element,BU(u,f),k3(f,r,null,l);var _=f.memoizedState;if(r=_.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:_.cache,pendingSuspenseBoundaries:_.pendingSuspenseBoundaries,transitions:_.transitions},f.updateQueue.baseState=i,f.memoizedState=i,f.flags&256){y=Fi(Error(Uu(423)),f),f=qJ(u,f,r,l,y);break u}else if(r!==y){y=Fi(Error(Uu(424)),f),f=qJ(u,f,r,l,y);break u}else for(el=e0(f.stateNode.containerInfo.firstChild),ur=f,Ef=!0,Xr=null,l=HU(f,null,r,l),f.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling;else{if($i(),r===y){f=L0(u,f,l);break u}Gl(u,f,r,l)}f=f.child}return f;case 5:return VU(f),u===null&&v2(f),r=f.type,y=f.pendingProps,i=u!==null?u.memoizedProps:null,_=y.children,C2(r,y)?_=null:i!==null&&C2(r,i)&&(f.flags|=32),fQ(u,f),Gl(u,f,_,l),f.child;case 6:return u===null&&v2(f),null;case 13:return rQ(u,f,l);case 4:return D5(f,f.stateNode.containerInfo),r=f.pendingProps,u===null?f.child=Ai(f,null,r,l):Gl(u,f,r,l),f.child;case 11:return r=f.type,y=f.pendingProps,y=f.elementType===r?y:Br(r,y),JJ(u,f,r,y,l);case 7:return Gl(u,f,f.pendingProps,l),f.child;case 8:return Gl(u,f,f.pendingProps.children,l),f.child;case 12:return Gl(u,f,f.pendingProps.children,l),f.child;case 10:u:{if(r=f.type._context,y=f.pendingProps,i=f.memoizedProps,_=y.value,cf(v3,r._currentValue),r._currentValue=_,i!==null)if(tr(i.value,_)){if(i.children===y.children&&!Sl.current){f=L0(u,f,l);break u}}else for(i=f.child,i!==null&&(i.return=f);i!==null;){var n=i.dependencies;if(n!==null){_=i.child;for(var $=n.firstContext;$!==null;){if($.context===r){if(i.tag===1){$=N0(-1,l&-l),$.tag=2;var j=i.updateQueue;if(j!==null){j=j.shared;var F=j.pending;F===null?$.next=$:($.next=F.next,F.next=$),j.pending=$}}i.lanes|=l,$=i.alternate,$!==null&&($.lanes|=l),I2(i.return,l,f),n.lanes|=l;break}$=$.next}}else if(i.tag===10)_=i.type===f.type?null:i.child;else if(i.tag===18){if(_=i.return,_===null)throw Error(Uu(341));_.lanes|=l,n=_.alternate,n!==null&&(n.lanes|=l),I2(_,l,f),_=i.sibling}else _=i.child;if(_!==null)_.return=i;else for(_=i;_!==null;){if(_===f){_=null;break}if(i=_.sibling,i!==null){i.return=_.return,_=i;break}_=_.return}i=_}Gl(u,f,y.children,l),f=f.child}return f;case 9:return y=f.type,r=f.pendingProps.children,yi(f,l),y=zr(y),r=r(y),f.flags|=1,Gl(u,f,r,l),f.child;case 14:return r=f.type,y=Br(r,f.pendingProps),y=Br(r.type,y),UJ(u,f,r,y,l);case 15:return eU(u,f,f.type,f.pendingProps,l);case 17:return r=f.type,y=f.pendingProps,y=f.elementType===r?y:Br(r,y),O3(u,f),f.tag=1,Pl(r)?(u=!0,R3(f)):u=!1,yi(f,l),aU(f,r,y),g2(f,r,y,l),o2(null,f,r,!0,u,l);case 19:return yQ(u,f,l);case 22:return uQ(u,f,l)}throw Error(Uu(156,f.tag))};function GQ(u,f){return kJ(u,f)}function ow(u,f,l,r){this.tag=u,this.key=l,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=f,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function cr(u,f,l,r){return new ow(u,f,l,r)}function g5(u){return u=u.prototype,!(!u||!u.isReactComponent)}function dw(u){if(typeof u==="function")return g5(u)?1:0;if(u!==void 0&&u!==null){if(u=u.$$typeof,u===U5)return 11;if(u===Q5)return 14}return 2}function r1(u,f){var l=u.alternate;return l===null?(l=cr(u.tag,f,u.key,u.mode),l.elementType=u.elementType,l.type=u.type,l.stateNode=u.stateNode,l.alternate=u,u.alternate=l):(l.pendingProps=f,l.type=u.type,l.flags=0,l.subtreeFlags=0,l.deletions=null),l.flags=u.flags&14680064,l.childLanes=u.childLanes,l.lanes=u.lanes,l.child=u.child,l.memoizedProps=u.memoizedProps,l.memoizedState=u.memoizedState,l.updateQueue=u.updateQueue,f=u.dependencies,l.dependencies=f===null?null:{lanes:f.lanes,firstContext:f.firstContext},l.sibling=u.sibling,l.index=u.index,l.ref=u.ref,l}function X3(u,f,l,r,y,i){var _=2;if(r=u,typeof u==="function")g5(u)&&(_=1);else if(typeof u==="string")_=5;else u:switch(u){case hy:return a1(l.children,y,i,f);case J5:_=8,y|=8;break;case W2:return u=cr(12,l,f,y|2),u.elementType=W2,u.lanes=i,u;case c2:return u=cr(13,l,f,y),u.elementType=c2,u.lanes=i,u;case N2:return u=cr(19,l,f,y),u.elementType=N2,u.lanes=i,u;case VJ:return J6(l,y,i,f);default:if(typeof u==="object"&&u!==null)switch(u.$$typeof){case OJ:_=10;break u;case BJ:_=9;break u;case U5:_=11;break u;case Q5:_=14;break u;case b0:_=16,r=null;break u}throw Error(Uu(130,u==null?u:typeof u,""))}return f=cr(_,l,f,y),f.elementType=u,f.type=r,f.lanes=i,f}function a1(u,f,l,r){return u=cr(7,u,r,f),u.lanes=l,u}function J6(u,f,l,r){return u=cr(22,u,r,f),u.elementType=VJ,u.lanes=l,u.stateNode={isHidden:!1},u}function U2(u,f,l){return u=cr(6,u,null,f),u.lanes=l,u}function Q2(u,f,l){return f=cr(4,u.children!==null?u.children:[],u.key,f),f.lanes=l,f.stateNode={containerInfo:u.containerInfo,pendingChildren:null,implementation:u.implementation},f}function ew(u,f,l,r,y){this.tag=f,this.containerInfo=u,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=d8(0),this.expirationTimes=d8(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=d8(0),this.identifierPrefix=r,this.onRecoverableError=y,this.mutableSourceEagerHydrationData=null}function s5(u,f,l,r,y,i,_,n,$){return u=new ew(u,f,l,n,$),f===1?(f=1,i===!0&&(f|=8)):f=0,i=cr(3,null,null,f),u.current=i,i.stateNode=u,i.memoizedState={element:r,isDehydrated:l,cache:null,transitions:null,pendingSuspenseBoundaries:null},Y5(i),u}function uE(u,f,l){var r=3{function TQ(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=="function")return;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(TQ)}catch(u){console.error(u)}}TQ(),ZQ.exports=EQ()});var OQ=ol((uA)=>{var HQ=e5();uA.createRoot=HQ.createRoot,uA.hydrateRoot=HQ.hydrateRoot;var iE});var NW=ol((g6)=>{var _H=wf(),nH=Symbol.for("react.element"),$H=Symbol.for("react.fragment"),AH=Object.prototype.hasOwnProperty,jH=_H.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,FH={key:!0,ref:!0,__self:!0,__source:!0};function cW(u,f,l){var r,y={},i=null,_=null;l!==void 0&&(i=""+l),f.key!==void 0&&(i=""+f.key),f.ref!==void 0&&(_=f.ref);for(r in f)AH.call(f,r)&&!FH.hasOwnProperty(r)&&(y[r]=f[r]);if(u&&u.defaultProps)for(r in f=u.defaultProps,f)y[r]===void 0&&(y[r]=f[r]);return{$$typeof:nH,type:u,key:i,ref:_,props:y,_owner:jH.current}}g6.Fragment=$H;g6.jsx=cW;g6.jsxs=cW});var GW=ol((bP,zW)=>{zW.exports=NW()});var oc=ol((ac)=>{var ki=wf();function OV(u,f){return u===f&&(u!==0||1/u===1/f)||u!==u&&f!==f}var BV=typeof Object.is==="function"?Object.is:OV,VV=ki.useState,XV=ki.useEffect,YV=ki.useLayoutEffect,DV=ki.useDebugValue;function tV(u,f){var l=f(),r=VV({inst:{value:l,getSnapshot:f}}),y=r[0].inst,i=r[1];return YV(function(){y.value=l,y.getSnapshot=f,h7(y)&&i({inst:y})},[u,l,f]),XV(function(){return h7(y)&&i({inst:y}),u(function(){h7(y)&&i({inst:y})})},[u]),DV(l),l}function h7(u){var f=u.getSnapshot;u=u.value;try{var l=f();return!BV(u,l)}catch(r){return!0}}function SV(u,f){return f()}var PV=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?SV:tV;ac.useSyncExternalStore=ki.useSyncExternalStore!==void 0?ki.useSyncExternalStore:PV});var ec=ol((MR,dc)=>{dc.exports=oc()});var fN=ol((uN)=>{var x4=wf(),MV=ec();function mV(u,f){return u===f&&(u!==0||1/u===1/f)||u!==u&&f!==f}var pV=typeof Object.is==="function"?Object.is:mV,CV=MV.useSyncExternalStore,xV=x4.useRef,RV=x4.useEffect,hV=x4.useMemo,bV=x4.useDebugValue;uN.useSyncExternalStoreWithSelector=function(u,f,l,r,y){var i=xV(null);if(i.current===null){var _={hasValue:!1,value:null};i.current=_}else _=i.current;i=hV(function(){function $(q){if(!j){if(j=!0,F=q,q=r(q),y!==void 0&&_.hasValue){var W=_.value;if(y(W,q))return J=W}return J=q}if(W=J,pV(F,q))return W;var G=r(q);if(y!==void 0&&y(W,G))return F=q,W;return F=q,J=G}var j=!1,F,J,U=l===void 0?null:l;return[function(){return $(f())},U===null?void 0:function(){return $(U())}]},[f,l,r,y]);var n=CV(u,i[0],i[1]);return RV(function(){_.hasValue=!0,_.value=n},[n]),bV(n),n}});var rN=ol((pR,lN)=>{lN.exports=fN()});var M1=Pu(wf(),1);var d$="北京时间";var uL={timeZone:"Asia/Shanghai",hour12:!1},fL={timeZone:"Asia/Shanghai",hour12:!1},lL=new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Shanghai",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23"});function P8(u){if(u===null||u===void 0||u==="")return null;let f=u instanceof Date?u:new Date(u);return Number.isNaN(f.getTime())?null:f}function FF(u){let f=P8(u);if(!f)return null;return lL.formatToParts(f).reduce((l,r)=>{if(r.type!=="literal")l[r.type]=r.value;return l},{})}function Nu(u){let f=P8(u);return f?f.toLocaleString("zh-CN",uL):"--"}function qf(u){let f=P8(u);return f?f.toLocaleTimeString("zh-CN",fL):"--"}function M8(u){let f=FF(u);if(!f)return"";let l=f.hour==="24"?"00":f.hour;return`${f.year}-${f.month}-${f.day}T${l}:${f.minute}`}function JF(u=new Date){let f=FF(u);if(!f)return"";return`${f.year}-${f.month}-${f.day}`}function UF(u){if(!u)return null;let f=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(u);if(!f)return null;let[,l,r,y,i,_,n="00"]=f,$=Date.UTC(Number(l),Number(r)-1,Number(y),Number(i)-8,Number(_),Number(n)),j=new Date($),F=M8(j);return Number.isNaN(j.getTime())||F!==`${l}-${r}-${y}T${i}:${_}`?null:j.toISOString()}var VG=Pu(OQ(),1);var z6=Pu(wf(),1);var BQ=Pu(wf(),1),Jn=BQ.default.createElement;function _E({active:u=!0,label:f="正在加载"}){if(!u)return null;return Jn("span",{className:"loading-spinner-indicator",role:"status","aria-label":f,title:f,"data-testid":"loading-title-indicator"},Jn("span",{className:"loading-spinner-ring","aria-hidden":!0}))}function Af({title:u,children:f,loading:l,level:r=2,className:y="",label:i="正在加载"}){return Jn(r===3?"h3":"h2",{className:`loading-title ${l?"is-loading":""} ${y}`.trim()},Jn("span",{className:"loading-title-text"},f??u),Jn(_E,{active:Boolean(l),label:i}))}class qi extends Error{unideskRequestError=!0;meta;constructor(u,f){super(u);this.name="UniDeskRequestError",this.meta=f}}function nE(u){return new Promise((f)=>setTimeout(f,u))}function qn(u,f="操作失败"){return u instanceof Error?u.message:String(u||f)}function c6(u,f=500){if(u===null||u===void 0)return"";let l=typeof u==="string"?u:JSON.stringify(u),r=String(l||"").replace(/\s+/gu," ").trim();return r.length>f?`${r.slice(0,f)}...`:r}function $E(u){try{let f=typeof location<"u"&&location.origin?location.origin:"http://localhost";return new URL(u,f).toString()}catch{return u}}function VQ(u){return String(u.method||"GET").toUpperCase()}function AE(u){if(u===null||u===void 0)return!1;if(typeof u!=="object")return!1;if(typeof Blob<"u"&&u instanceof Blob)return!1;if(typeof FormData<"u"&&u instanceof FormData)return!1;if(typeof URLSearchParams<"u"&&u instanceof URLSearchParams)return!1;if(typeof ArrayBuffer<"u"&&u instanceof ArrayBuffer)return!1;return!0}function XQ(u){let f=new Headers(u.headers||{}),l=AE(u.body)?JSON.stringify(u.body):u.body;if(l&&!f.has("content-type")&&typeof l==="string")f.set("content-type","application/json");return{...u,credentials:u.credentials||"same-origin",body:l,headers:f}}function YQ(u){if(u?.error&&typeof u.error==="object"&&typeof u.error.message==="string")return u.error.message;if(typeof u?.error==="string")return u.error;if(typeof u?.message==="string")return u.message;if(typeof u?.detail==="string")return u.detail;return""}function jE(u,f){if(!u||typeof u!=="object"||Array.isArray(u))return!1;return f.some((l)=>l!==!1&&u[l]===!1)}function Un(u,f,l,r,y={}){return{kind:u,method:l,url:$E(f),occurredAt:r.toISOString(),...y}}function Qn(u,f){if(!u)return"请求失败";return`HTTP ${u}${f?` ${f}`:""}`}function DQ(u){try{return{body:u?JSON.parse(u):null,parseError:""}}catch(f){return{body:{text:u},parseError:qn(f,"parse failed")}}}async function Xu(u,f={},l=0){let{failureFields:r=["ok"],strictJson:y=!1,retryInvalidJson:i=0,retryDelayMs:_=120,invalidJsonPrefix:n="服务返回了无效 JSON",invalidJsonPreview:$=!1,responsePreviewLength:j=500,...F}=f,J=VQ(F),U=new Date,q;try{q=await fetch(u,XQ(F))}catch(K){let Q=qn(K,"网络请求失败");throw new qi(Q,Un("network",u,J,U,{upstreamMessage:Q}))}let W=await q.text(),G=DQ(W);if(G.parseError){if(y&&J==="GET"&&l=300)?Qn(u.meta.status,u.meta.statusText):"应用请求失败",i=u.meta.status?Qn(u.meta.status):"",_=($)=>!$||$===y||$===i,n=!_(u.message)?u.message:_(u.meta.upstreamMessage)?"":u.meta.upstreamMessage||"";return{title:y,message:n,status:u.meta.status,statusText:u.meta.statusText,method:u.meta.method,url:u.meta.url,occurredAt:FE(u.meta.occurredAt),responsePreview:u.meta.responsePreview,parseError:u.meta.parseError,structured:!0}}let r=qn(u,f).split(/\r?\n/u);return{title:r[0]||f,message:r.slice(1).join(` -`),structured:r.length>1}}function JE(u,f="操作失败"){let l=fA(u,f),r=[l.title];if(l.message)r.push(`原因: ${l.message}`);if(l.method||l.url)r.push(`请求: ${[l.method,l.url].filter(Boolean).join(" ")}`);if(l.status)r.push(`状态: ${Qn(l.status,l.statusText)}`);if(l.occurredAt)r.push(`时间: ${l.occurredAt}`);if(l.parseError)r.push(`解析错误: ${l.parseError}`);if(l.responsePreview&&l.responsePreview!==l.message)r.push(`响应预览: ${l.responsePreview}`);return r.filter(Boolean).join(` -`)}function Bu(u,f="操作失败"){return SQ(u)?JE(u,f):qn(u,f)}var PQ=Pu(wf(),1);var A1=PQ.default.createElement;function Wn(u,f){return f?[A1("dt",{key:`${u}-label`},u),A1("dd",{key:u},f)]:null}function jf({error:u,wide:f=!1,fallback:l="操作失败",className:r=""}){if(!u)return null;let y=fA(u,l),i=[Wn("请求",[y.method,y.url].filter(Boolean).join(" ")),Wn("状态",y.status?`HTTP ${y.status}${y.statusText?` ${y.statusText}`:""}`:""),Wn("时间",y.occurredAt),Wn("解析错误",y.parseError),Wn("响应预览",y.responsePreview)].filter(Boolean);return A1("div",{className:`form-error unidesk-error${f?" wide":""}${r?` ${r}`:""}`,role:"alert","data-testid":"unidesk-error"},A1("div",{className:"unidesk-error-title"},A1("strong",null,y.title),y.status?A1("span",{className:"unidesk-error-code"},`HTTP ${y.status}`):null),y.message?A1("pre",{className:"unidesk-error-message"},y.message):null,i.length>0?A1("dl",{className:"unidesk-error-details"},i):null)}var er=Pu(wf(),1),MQ=er.default.createContext(null);function mQ({children:u}){let[f,l]=er.default.useState([]),[r,y]=er.default.useState(Date.now()),i=er.default.useCallback((J,U)=>{let W={id:`notif_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,type:J,message:U,timestamp:Date.now()};l((G)=>{let K=[...G,W];if(K.length>50)return K.slice(-50);return K})},[]),_=er.default.useCallback((J)=>{l((U)=>U.filter((q)=>q.id!==J))},[]),n=er.default.useCallback(()=>{l([]),y(Date.now())},[]),$=er.default.useMemo(()=>{return f.filter((J)=>J.timestamp>r).length},[f,r]),j=$>0,F={notifications:f,addNotification:i,removeNotification:_,clearNotifications:n,unreadCount:$,hasUnread:j};return UE(MQ.Provider,{value:F},u)}var UE=er.default.createElement;function wl(){let u=er.default.useContext(MQ);if(!u)throw Error("useNotification must be used within NotificationProvider");return u}var C=z6.default.createElement,{useEffect:N6}=z6.default,Wi=z6.default.useState;function ql(u,f={}){return Xu(u,{failureFields:["ok","success"],...f})}function El(u,f){return`${u}/microservices/baidu-netdisk/proxy${f}`}function QE(u){let f=Number(u);return Number.isFinite(f)?f.toLocaleString("zh-CN"):"--"}function j1(u){let f=Number(u);if(!Number.isFinite(f)||f<=0)return"--";let l=["B","KB","MB","GB","TB"],r=f,y=0;while(r>=1024&&y{y?.stopPropagation?.(),l(u,f)}},"查看原始JSON")}function Ni({title:u,text:f}){return C("div",{className:"empty-state"},C("strong",null,u),C("span",null,f))}function zi({title:u,text:f,href:l,badge:r,testId:y}){return C("a",{className:"doc-link-card",href:l,target:"_blank",rel:"noreferrer","data-testid":y},C("span",null,r||"DOC"),C("strong",null,u),C("p",null,f),C("code",null,l))}function qE(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function WE(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function cE(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function NE(u){return Array.isArray(u?.files)?u.files:[]}function zE(u){return Array.isArray(u?.jobs)?u.jobs:[]}function GE(u,f){if(!u||u===f)return f;let l=u.replace(/\/+$/u,""),r=l.slice(0,l.lastIndexOf("/"))||f;return r.lengthx.id==="baidu-netdisk")||null,[y,i]=Wi({loading:!1,actionLoading:!1,error:"",message:"",health:null,account:null,files:null,transfers:null,logs:null,selfTest:null,refreshedAt:null}),[_,n]=Wi("/"),[$,j]=Wi(null),[F,J]=Wi(""),[U,q]=Wi({localPath:"sample.txt",remotePath:"/sample.txt"}),[W,G]=Wi({fsId:"",localPath:"downloads/"}),{addNotification:K}=wl(),Q=y.health?.baidu?.appRoot||y.account?.rootPath||"/";N6(()=>{q((x)=>{let uu=new Set(["/sample.txt","/apps/UniDeskBaiduNetdisk/sample.txt"]);if(x.remotePath&&!uu.has(x.remotePath))return x;let nu=lA(Q,"sample.txt");return x.remotePath===nu?x:{...x,remotePath:nu}})},[Q]);async function N(x=_){let nu=await ql(El(l,`/api/files?dir=${encodeURIComponent(x||Q)}&limit=100`));i(($u)=>({...$u,files:nu}))}async function c(){let x=await ql(El(l,"/api/transfers?limit=80"));i((uu)=>({...uu,transfers:x}))}async function z(){if(!r)return;i((x)=>({...x,loading:!0,error:"",message:""}));try{let x=await ql(`${l}/microservices/baidu-netdisk/health`),uu=x?.baidu?.appRoot||Q,nu=null,$u=null;if(x?.auth?.loggedIn){nu=await ql(El(l,"/api/account?refresh=1"));let Wu=_&&_.startsWith(uu)?_:uu;n(Wu),$u=await ql(El(l,`/api/files?dir=${encodeURIComponent(Wu)}&limit=100`))}else n(uu);let Fu=await ql(El(l,"/api/transfers?limit=80")),Ku=await ql(El(l,"/logs?limit=60"));i((Wu)=>({...Wu,loading:!1,health:x,account:nu?.account||null,files:$u,transfers:Fu,logs:Ku,refreshedAt:new Date}))}catch(x){i((uu)=>({...uu,loading:!1,error:Bu(x,"百度网盘服务加载失败")}))}}async function w(){i((x)=>({...x,actionLoading:!0,error:"",message:""}));try{let x=await ql(El(l,"/api/auth/device/start"),{method:"POST",body:{}});j(x.session||null),i((uu)=>({...uu,actionLoading:!1,message:"设备码已生成,请扫码授权"}))}catch(x){i((uu)=>({...uu,actionLoading:!1,error:Bu(x,"创建设备码失败")}))}}async function H(x=!1){if(!$?.id)return;if(x)i((uu)=>({...uu,actionLoading:!0,error:""}));try{let uu=await ql(El(l,`/api/auth/device/status?sessionId=${encodeURIComponent($.id)}`));if(j(uu.session||null),uu.session?.status==="succeeded")i((nu)=>({...nu,actionLoading:!1,message:"授权成功,正在刷新账号与文件列表"})),await z();else if(x)i((nu)=>({...nu,actionLoading:!1}))}catch(uu){i((nu)=>({...nu,actionLoading:!1,error:Bu(uu,"轮询登录状态失败")}))}}async function B(){i((x)=>({...x,actionLoading:!0,error:"",message:""}));try{await ql(El(l,"/api/auth/logout"),{method:"POST",body:{}}),j(null),i((x)=>({...x,actionLoading:!1,account:null,files:null,message:"本地 token 已清除"})),await z()}catch(x){i((uu)=>({...uu,actionLoading:!1,error:Bu(x,"退出登录失败")}))}}async function T(x){x.preventDefault();let uu=F.trim();if(!uu)return;i((nu)=>({...nu,actionLoading:!0,error:"",message:""}));try{await ql(El(l,"/api/folders"),{method:"POST",body:{path:lA(_,uu)}}),J(""),i((nu)=>({...nu,actionLoading:!1,message:"文件夹已创建"})),await N(_)}catch(nu){i(($u)=>({...$u,actionLoading:!1,error:Bu(nu,"创建文件夹失败")}))}}async function X(x){if(!x)return;i((uu)=>({...uu,actionLoading:!0,error:"",message:""}));try{await ql(El(l,"/api/files/manage"),{method:"POST",body:{opera:"delete",filelist:[{path:x}],async:1}}),i((uu)=>({...uu,actionLoading:!1,message:"删除任务已提交"})),await N(_)}catch(uu){i((nu)=>({...nu,actionLoading:!1,error:Bu(uu,"删除失败")}))}}async function R(x){x.preventDefault(),i((uu)=>({...uu,actionLoading:!0,error:"",message:""}));try{await ql(El(l,"/api/transfers/upload-from-path"),{method:"POST",body:U}),i((uu)=>({...uu,actionLoading:!1,message:"上传任务已入队"})),await c()}catch(uu){i((nu)=>({...nu,actionLoading:!1,error:Bu(uu,"上传任务创建失败")}))}}async function Y(x){x.preventDefault(),i((uu)=>({...uu,actionLoading:!0,error:"",message:""}));try{await ql(El(l,"/api/transfers/download-to-path"),{method:"POST",body:W}),i((uu)=>({...uu,actionLoading:!1,message:"下载任务已入队"})),await c()}catch(uu){i((nu)=>({...nu,actionLoading:!1,error:Bu(uu,"下载任务创建失败")}))}}async function P(x,uu){i((nu)=>({...nu,actionLoading:!0,error:"",message:""}));try{await ql(El(l,`/api/transfers/${encodeURIComponent(x)}/${uu}`),{method:"POST",body:{}}),i((nu)=>({...nu,actionLoading:!1,message:uu==="cancel"?"已请求取消任务":"任务已重新入队"})),await c()}catch(nu){i(($u)=>({...$u,actionLoading:!1,error:Bu(nu,"任务操作失败")}))}}async function t(){i((x)=>({...x,actionLoading:!0,error:"",message:"正在运行上传/下载自测..."}));try{let x=await ql(El(l,"/api/self-test"),{method:"POST",body:{}});i((uu)=>({...uu,actionLoading:!1,selfTest:x,message:`上传/下载自测通过:${x.remotePath||""}`})),await N(_),await c()}catch(x){i((uu)=>({...uu,actionLoading:!1,error:Bu(x,"上传/下载自测失败")}))}}if(N6(()=>{if(!r)return;z();return},[r?.id,r?.runtime?.providerStatus]),N6(()=>{if(!$?.id||$.status!=="pending")return;let x=window.setInterval(()=>void H(!1),Math.max(5000,Number($.pollIntervalSeconds||5)*1000));return()=>window.clearInterval(x)},[$?.id,$?.status,$?.pollIntervalSeconds]),N6(()=>{if(!r)return;let x=window.setInterval(()=>void c(),5000);return()=>window.clearInterval(x)},[r?.id]),!r)return C(Ni,{title:"Baidu Netdisk 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=baidu-netdisk"});let O=qE(r),M=cE(r),S=WE(r),b=y.health||{},Z=y.account||b.auth?.account||null,D=b.auth||{},I=NE(y.files),k=zE(y.transfers),h=Z?.quota||{},o=Boolean(D.loggedIn||Z),s=Boolean(D.configured);return C("div",{className:"baidu-netdisk-page","data-testid":"baidu-netdisk-page"},C(iy,{title:"Baidu Netdisk 工作台",eyebrow:"Containerized Storage Gateway",loading:y.loading,actions:C("div",{className:"panel-actions"},C("a",{className:"ghost-btn",href:"/docs/issue/baidu-netdisk-env-setup.md",target:"_blank",rel:"noreferrer","data-testid":"baidu-netdisk-config-doc-link"},"配置文档"),C("button",{type:"button",className:"ghost-btn",onClick:z,disabled:y.loading,"data-testid":"baidu-netdisk-refresh"},y.loading?"刷新中":"刷新"),C(E0,{title:"Baidu Netdisk 用户服务",data:r,onOpen:f,testId:"raw-baidu-netdisk-service"}))},C("div",{className:"baidu-netdisk-hero"},C("div",null,C("div",{className:"node-version-line"},C(yy,{status:O.providerStatus==="online"?"online":"warn"},O.providerStatus||"unknown"),C("span",null,r.providerId),C(yy,{status:S.public?"warn":"private"},S.public?"公网暴露":"仅 UniDesk frontend 代理访问")),C("p",{className:"muted paragraph"},r.description)),C("div",{className:"microservice-ref-card"},C("span",null,"Repo"),C("strong",null,M.url||"--"),C("code",null,M.commitId||"--")),C("div",{className:"microservice-ref-card"},C("span",null,"Private Backend"),C("strong",null,`${S.nodeBindHost||"--"}:${S.nodePort||"--"}`),C("code",null,`${M.composeFile||"--"} / ${M.composeService||"--"}`))),C(jf,{error:y.error,wide:!0})),C("div",{className:"metric-grid"},C(ci,{label:"Health",value:b.ok?"OK":"--",hint:b.storage?.postgres||"postgres",tone:b.ok?"ok":"warn"}),C(ci,{label:"OAuth",value:s?"已配置":"待配置",hint:s?"client + secret + token key":"需要设置 UNIDESK_BAIDU_NETDISK_*",tone:s?"ok":"warn"}),C(ci,{label:"Login",value:o?"已登录":"未登录",hint:Z?.username||"Device Code QR",tone:o?"ok":"warn"}),C(ci,{label:"Work Root",value:KE(Q),hint:Q}),C(ci,{label:"Quota",value:j1(h.used),hint:h.total?`${h.usedPercent||0}% / ${j1(h.total)}`:"授权后刷新"}),C(ci,{label:"Transfers",value:QE(k.length),hint:`running ${y.transfers?.counts?.running||0} / failed ${y.transfers?.counts?.failed||0}`})),C(iy,{title:"文件浏览器",eyebrow:_,className:"baidu-files-panel",loading:y.loading,actions:C("div",{className:"panel-actions inline-actions"},C("button",{type:"button",className:"ghost-btn",onClick:()=>{let x=GE(_,Q);n(x),N(x)},disabled:!o||_===Q},"上级"),C("button",{type:"button",className:"ghost-btn",onClick:()=>N(_),disabled:!o},"刷新文件"),C(E0,{title:"Baidu Files",data:y.files,onOpen:f,testId:"raw-baidu-files"}))},C("form",{className:"baidu-pathbar",onSubmit:(x)=>{x.preventDefault(),N(_)}},C("input",{value:_,onChange:(x)=>n(x.target.value),disabled:!o}),C("button",{type:"submit",className:"ghost-btn",disabled:!o},"打开路径")),C("form",{className:"baidu-pathbar",onSubmit:T},C("input",{value:F,onChange:(x)=>J(x.target.value),placeholder:"新文件夹名称",disabled:!o}),C("button",{type:"submit",className:"primary-btn",disabled:!o||!F.trim()},"新建文件夹")),!o?C(Ni,{title:"等待授权",text:"登录后通过 /api/files 读取工作目录文件列表"}):I.length===0?C(Ni,{title:"目录为空",text:"可以从 staging 目录上传文件或新建文件夹"}):C("div",{className:"table-wrap","data-testid":"baidu-netdisk-file-table"},C("table",null,C("thead",null,C("tr",null,C("th",null,"名称"),C("th",null,"类型"),C("th",null,"大小"),C("th",null,"修改时间"),C("th",null,"fs_id"),C("th",null,"操作"))),C("tbody",null,I.map((x)=>C("tr",{key:x.fsId||x.path},C("td",null,C("strong",null,x.serverFilename||x.path),C("code",null,x.path||"--")),C("td",null,C(yy,{status:x.isDir?"queued":"private"},x.isDir?"DIR":"FILE")),C("td",null,x.isDir?"--":j1(x.size)),C("td",null,x.serverMtime?Nu(x.serverMtime*1000):"--"),C("td",null,C("code",null,x.fsId||"--")),C("td",null,C("div",{className:"inline-actions"},x.isDir?C("button",{type:"button",className:"ghost-btn",onClick:()=>{n(x.path),N(x.path)}},"打开"):C("button",{type:"button",className:"ghost-btn",onClick:()=>G((uu)=>({...uu,fsId:x.fsId}))},"填入下载"),C("button",{type:"button",className:"ghost-btn",onClick:()=>X(x.path),disabled:y.actionLoading},"删除"))))))))),C("div",{className:"baidu-netdisk-grid"},C(iy,{title:"配置与文档",eyebrow:"Deployment References",className:"baidu-docs-panel",actions:C("div",{className:"panel-actions inline-actions"},C("a",{className:"ghost-btn",href:"/docs/issue/baidu-netdisk-env-setup.md",target:"_blank",rel:"noreferrer"},"打开环境配置"),C("a",{className:"ghost-btn",href:"/docs/issue/baidu-netdisk-user-service.md",target:"_blank",rel:"noreferrer"},"打开服务方案"))},C("p",{className:"muted paragraph"},s?"OAuth 运行时变量已配置;如需轮换密钥、迁移部署或排查代理边界,可直接打开下面的项目内文档。":"首次使用请先按环境变量配置文档填入百度应用 client id / secret,然后重建 baidu-netdisk 服务并刷新本页。"),C("div",{className:"baidu-doc-grid","data-testid":"baidu-netdisk-doc-links"},C(zi,{title:"环境变量配置",text:"填写 UNIDESK_BAIDU_NETDISK_CLIENT_ID、CLIENT_SECRET、TOKEN_KEY,并执行重建与健康检查。",href:"/docs/issue/baidu-netdisk-env-setup.md",badge:"SETUP",testId:"baidu-netdisk-env-doc-card"}),C(zi,{title:"服务方案与 API",text:"说明 OAuth Device Code、根目录工作区、staging 上传下载任务和后端 API 设计。",href:"/docs/issue/baidu-netdisk-user-service.md",badge:"DESIGN"}),C(zi,{title:"用户服务安全边界",text:"查看 UniDesk microservice 私有代理、允许路径、frontendOnly 和密钥边界规则。",href:"/docs/reference/microservices.md",badge:"REF"}),C(zi,{title:"部署与重建流程",text:"查看 server rebuild、Compose 编排、健康检查和交付验证的长期规则。",href:"/docs/reference/deployment.md",badge:"DEPLOY"}),C(zi,{title:"CLI 验证命令",text:"查看 microservice health/proxy、server rebuild、job status 等命令入口。",href:"/docs/reference/cli.md",badge:"CLI"}),C(zi,{title:"百度设备码模式",text:"打开百度官方 OAuth Device Code 文档,对照扫码登录和轮询参数。",href:"https://pan.baidu.com/union/doc/fl1x114ti",badge:"OFFICIAL"}))),C(iy,{title:"设备码登录",eyebrow:"OAuth Device Code",className:"baidu-login-panel",loading:y.actionLoading,actions:C("div",{className:"panel-actions inline-actions"},C("button",{type:"button",className:"primary-btn",onClick:w,disabled:y.actionLoading||!s,"data-testid":"baidu-netdisk-start-login"},"生成二维码"),$?.id?C("button",{type:"button",className:"ghost-btn",onClick:()=>H(!0),disabled:y.actionLoading},"检查状态"):null,o?C("button",{type:"button",className:"ghost-btn",onClick:B,disabled:y.actionLoading},"清除本地登录"):null,C(E0,{title:"Baidu Device Session",data:$||D.latestSession,onOpen:f,testId:"raw-baidu-device-session"}))},C("div",{className:"baidu-login-card","data-testid":"baidu-netdisk-login-card"},C("div",{className:"baidu-qr-frame"},$?.qrcodeUrl?C("img",{src:$.qrcodeUrl,alt:"百度网盘设备码授权二维码","data-testid":"baidu-netdisk-qrcode"}):C(Ni,{title:s?"等待二维码":"OAuth 未配置",text:s?"点击生成二维码后使用百度网盘或百度 App 扫码":"设置 client id、secret 和 token key 后重建服务"})),C("div",{className:"claudeqq-login-copy"},C("div",{className:"node-version-line"},C(yy,{status:o?"online":$?.status==="pending"?"warn":"unknown"},o?"已登录":$?.status||"未开始"),C("span",null,$?.secondsRemaining!==void 0?`${$.secondsRemaining}s`:"--"),C("span",null,"scope basic,netdisk")),C("p",{className:"muted paragraph"},o?"access token / refresh token 已加密保存到 PostgreSQL;前端只看到脱敏登录态。":"后端使用百度 OAuth Device Code 轮询换取 token;二维码过期后重新生成即可。"),C("div",{className:"microservice-ref-card"},C("span",null,"User Code"),C("strong",null,$?.userCode||"--"),C("code",null,$?.verificationUrl||"https://openapi.baidu.com/device")),C("div",{className:"microservice-ref-card"},C("span",null,"Expires"),C("strong",null,$?.expiresAt?Nu($.expiresAt):"--"),C("code",null,$?.error||"no token exposed"))))),C(iy,{title:"账号与容量",eyebrow:y.refreshedAt?`Updated ${qf(y.refreshedAt)}`:"Account",loading:y.loading,actions:C("div",{className:"panel-actions inline-actions"},C(E0,{title:"Baidu Account",data:Z,onOpen:f,testId:"raw-baidu-account"}))},Z?C("div",{className:"baidu-account-card"},C("div",{className:"node-version-line"},C(yy,{status:"online"},"connected"),C("span",null,Z.baiduUid||"--"),C("span",null,`VIP ${Z.vipType??"--"}`)),C("h3",null,Z.username||"Baidu Netdisk"),C("p",{className:"muted paragraph"},`工作目录固定在 ${Z.rootPath||Q};v1 上传/下载只读写容器 staging 目录,不把大文件字节流穿过 UniDesk proxy。`),C("div",{className:"quota-bar"},C("span",{style:{width:`${Math.max(0,Math.min(100,Number(h.usedPercent||0)))}%`}})),C("div",{className:"microservice-ref-card"},C("span",null,"Quota"),C("strong",null,`${j1(h.used)} / ${j1(h.total)}`),C("code",null,`${h.usedPercent||0}% used`))):C(Ni,{title:"尚未登录",text:"扫码授权后这里会显示账号、UID、会员状态和容量"})),C(iy,{title:"传输任务",eyebrow:"staging path jobs",className:"baidu-transfers-panel",loading:y.actionLoading,actions:C("div",{className:"panel-actions inline-actions"},C("button",{type:"button",className:"primary-btn",onClick:t,disabled:!o||y.actionLoading,"data-testid":"baidu-netdisk-self-test"},"运行自测"),C("button",{type:"button",className:"ghost-btn",onClick:c},"刷新任务"),C(E0,{title:"Baidu Transfers",data:y.transfers,onOpen:f,testId:"raw-baidu-transfers"}))},C("div",{className:"baidu-transfer-forms"},C("form",{className:"stack-form",onSubmit:R,"data-testid":"baidu-upload-form"},C("label",null,"容器 staging 文件",C("input",{value:U.localPath,onChange:(x)=>q((uu)=>({...uu,localPath:x.target.value})),placeholder:"sample.txt"})),C("label",null,"百度网盘目标路径",C("input",{value:U.remotePath,onChange:(x)=>q((uu)=>({...uu,remotePath:x.target.value})),placeholder:lA(Q,"sample.txt")})),C("button",{type:"submit",className:"primary-btn",disabled:!o||y.actionLoading},"上传 staging 文件")),C("form",{className:"stack-form",onSubmit:Y,"data-testid":"baidu-download-form"},C("label",null,"文件 fs_id",C("input",{value:W.fsId,onChange:(x)=>G((uu)=>({...uu,fsId:x.target.value})),placeholder:"从文件表填入"})),C("label",null,"保存到 staging 路径",C("input",{value:W.localPath,onChange:(x)=>G((uu)=>({...uu,localPath:x.target.value})),placeholder:"downloads/"})),C("button",{type:"submit",className:"primary-btn",disabled:!o||!W.fsId||y.actionLoading},"下载到 staging"))),y.selfTest?C("div",{className:"baidu-account-card","data-testid":"baidu-netdisk-self-test-result"},C("div",{className:"node-version-line"},C(yy,{status:y.selfTest.ok?"online":"warn"},y.selfTest.ok?"self-test ok":"self-test"),C("span",null,j1(y.selfTest.sizeBytes))),C("h3",null,y.selfTest.remotePath||"Baidu self-test"),C("div",{className:"microservice-ref-card"},C("span",null,"fs_id"),C("strong",null,y.selfTest.fsId||"--"),C("code",null,y.selfTest.downloadedPath||"--")),C("div",{className:"microservice-ref-card"},C("span",null,"MD5"),C("strong",null,y.selfTest.downloadedMd5||"--"),C("code",null,y.selfTest.expectedMd5||"--")),C(E0,{title:"Baidu Self Test",data:y.selfTest,onOpen:f,testId:"raw-baidu-self-test"})):null,k.length===0?C(Ni,{title:"暂无传输任务",text:"上传/下载任务会在后端容器内执行,避免大文件穿过 UniDesk proxy"}):C("div",{className:"table-wrap","data-testid":"baidu-transfer-table"},C("table",null,C("thead",null,C("tr",null,C("th",null,"状态"),C("th",null,"方向"),C("th",null,"路径"),C("th",null,"进度"),C("th",null,"时间"),C("th",null,"操作"))),C("tbody",null,k.map((x)=>C("tr",{key:x.id},C("td",null,C(yy,{status:x.status},x.status)),C("td",null,x.direction),C("td",null,C("strong",null,x.remotePath||x.fsId||"--"),C("code",null,x.localPath||"--"),x.error?C("span",{className:"form-error"},x.error):null),C("td",null,C(LE,{percent:x.progressPercent}),C("span",{className:"muted"},`${j1(x.bytesDone)} / ${j1(x.sizeBytes)}`)),C("td",null,Nu(x.updatedAt)),C("td",null,C("div",{className:"inline-actions"},["queued","running"].includes(x.status)?C("button",{type:"button",className:"ghost-btn",onClick:()=>P(x.id,"cancel")},"取消"):null,["failed","canceled"].includes(x.status)?C("button",{type:"button",className:"ghost-btn",onClick:()=>P(x.id,"retry")},"重试"):null,C(E0,{title:`Transfer ${x.id}`,data:x,onOpen:f}))))))))),C(iy,{title:"安全与日志",eyebrow:"redacted diagnostics",className:"baidu-wide-panel",loading:y.loading,actions:C("div",{className:"panel-actions inline-actions"},C(E0,{title:"Baidu Health",data:b,onOpen:f,testId:"raw-baidu-health"}),C(E0,{title:"Baidu Logs",data:y.logs,onOpen:f,testId:"raw-baidu-logs"}))},C("div",{className:"policy-grid"},C("article",null,C("b",null,"私有后端"),C("span",null,"4244 只在 Compose 网络 expose,浏览器经 UniDesk 同源代理访问")),C("article",null,C("b",null,"Token 加密"),C("span",null,"access/refresh token 使用 BAIDU_NETDISK_TOKEN_KEY 加密后写入 PostgreSQL")),C("article",null,C("b",null,"无浏览器大文件流"),C("span",null,"上传/下载以容器 staging 目录为边界,避免 proxy 文本通道传输大字节流"))))))}var L6=Pu(wf(),1);var a=L6.default.createElement,{useEffect:wE}=L6.default,G6=L6.default.useState,_y={label:"主用户私聊账号",userId:645275593};function rA(u){let f=Number(u);return Number.isFinite(f)?f.toLocaleString("zh-CN"):"--"}async function F1(u,f={}){return Xu(u,{failureFields:["ok","success"],...f})}async function EE(u){let f=await fetch(u,{credentials:"same-origin"}),l=await f.text();try{return l?JSON.parse(l):{ok:f.ok,status:f.status}}catch{return{ok:f.ok,status:f.status,text:l}}}function K6({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return a("span",{className:`status-badge ${l}`},f||u||"unknown")}function Gi({label:u,value:f,hint:l,tone:r}){return a("article",{className:`metric-card ${r||""}`},a("div",{className:"metric-label"},u),a("div",{className:"metric-value"},f),a("div",{className:"metric-hint"},l))}function Ki({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return a("section",{className:`panel ${y||""}`},a("div",{className:"panel-head"},a("div",null,f?a("p",{className:"panel-eyebrow"},f):null,a(Af,{title:u,loading:i})),l?a("div",{className:"panel-actions"},l):null),a("div",{className:"panel-body"},r))}function cn({title:u,data:f,onOpen:l,testId:r}){return a("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:(y)=>{y?.stopPropagation?.(),l(u,f)}},"查看原始JSON")}function Nn({title:u,text:f}){return a("div",{className:"empty-state"},a("strong",null,u),a("span",null,f))}function TE(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function ZE(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function HE(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function J1(u,f){return`${u}/microservices/claudeqq/proxy${f}`}function OE(u){return Array.isArray(u?.events)?u.events.slice(0,80):[]}function BE(u){return Array.isArray(u?.subscriptions)?u.subscriptions.slice(0,50):[]}function VE(u){return Array.isArray(u?.messages)?u.messages.slice(0,30):[]}function CQ(u){let f=u?.text??u?.message??u?.raw?.raw_message;if(typeof f!=="string")return"--";return f.length>180?`${f.slice(0,177)}...`:f}function xQ(u){let f=u?.groupId??u?.group_id??(u?.message_type==="group"?u?.target_id:void 0),l=u?.userId??u?.user_id??(u?.message_type==="private"?u?.target_id:void 0);if(f)return`群 ${f}`;if(l)return`私聊 ${l}`;return"--"}function RQ({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((Z)=>Z.id==="claudeqq")||null,[y,i]=G6({loading:!1,qrLoading:!1,error:"",health:null,status:null,napcatLogin:null,napcatQrcode:null,qrcodeFetched:!1,qrcodeRefreshedAt:null,events:null,subscriptions:null,sent:null,refreshedAt:null}),[_,n]=G6({targetType:"private",targetId:String(_y.userId),message:""}),[$,j]=G6({name:"unidesk-callback",targetUrl:"",eventTypes:"message",secret:""}),[F,J]=G6(""),{addNotification:U}=wl();async function q(){if(!r)return;i((Z)=>({...Z,loading:!0,error:""}));try{let[Z,D,I,k,h]=await Promise.all([EE(`${l}/microservices/claudeqq/health`),F1(J1(l,"/api/server/status")),F1(J1(l,"/api/events/recent?limit=60")),F1(J1(l,"/api/events/subscriptions")),F1(J1(l,"/api/messages/sent?limit=20"))]);if(i((o)=>({...o,loading:!1,error:"",health:Z,status:D,events:I,subscriptions:k,sent:h,refreshedAt:new Date})),!y.qrcodeFetched)W(!1)}catch(Z){i((D)=>({...D,loading:!1,error:Bu(Z,"ClaudeQQ 加载失败")}))}}async function W(Z=!0){if(!r)return;i((D)=>({...D,qrLoading:!0,error:Z?"":D.error}));try{let D=await F1(J1(l,"/api/napcat/login")),I=D?.napcat?.qrcode||D?.qrcode||null;i((k)=>({...k,qrLoading:!1,error:"",napcatLogin:D,napcatQrcode:I,qrcodeFetched:!0,qrcodeRefreshedAt:new Date}))}catch(D){i((I)=>({...I,qrLoading:!1,error:Z||!I.napcatQrcode?Bu(D,"NapCat 二维码加载失败"):I.error}))}}async function G(Z){Z.preventDefault(),J("");let D=Number(_.targetId);if(!Number.isFinite(D)||D<=0||_.message.trim().length===0){i((I)=>({...I,error:"请填写 QQ 目标和消息内容"}));return}try{await F1(J1(l,"/api/push/text"),{method:"POST",body:JSON.stringify({userId:_.targetType==="private"?D:void 0,groupId:_.targetType==="group"?D:void 0,message:_.message})});let I="消息推送请求已提交";n((k)=>({...k,targetType:"private",targetId:String(_y.userId),message:""})),J(I),U("success",I),await q()}catch(I){i((k)=>({...k,error:Bu(I,"发送失败")}))}}async function K(Z){if(Z.preventDefault(),J(""),$.targetUrl.trim().length===0){i((D)=>({...D,error:"请填写订阅回调 URL"}));return}try{await F1(J1(l,"/api/events/subscriptions"),{method:"POST",body:JSON.stringify({name:$.name,targetUrl:$.targetUrl,eventTypes:$.eventTypes.split(",").map((I)=>I.trim()).filter(Boolean),secret:$.secret||void 0,enabled:!0})});let D="事件订阅已创建";J(D),U("success",D),await q()}catch(D){i((I)=>({...I,error:Bu(D,"订阅失败")}))}}async function Q(Z){if(!Z)return;J("");try{await F1(J1(l,`/api/events/subscriptions/${encodeURIComponent(Z)}`),{method:"DELETE"});let D="事件订阅已删除";J(D),U("success",D),await q()}catch(D){i((I)=>({...I,error:Bu(D,"删除订阅失败")}))}}if(wE(()=>{if(!r)return;q();return},[r?.id,r?.runtime?.providerStatus]),!r)return a(Nn,{title:"ClaudeQQ 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=claudeqq"});let N=TE(r),c=HE(r),z=ZE(r),w=y.health||{},H=y.status||{},B=y.napcatLogin||{},T=w.napcat||H.napcat||{},X={...B.napcat||{},...T,qrcode:y.napcatQrcode||{},webui:T.webui||B.napcat?.webui},R=B.login||{},Y=y.napcatQrcode||{},P=OE(y.events),t=BE(y.subscriptions),O=VE(y.sent),M=Boolean(X.httpConnected||R.ready),S=String(X.loginState||R.state||(M?"logged_in":"unknown")),b=Boolean(Y.available&&Y.dataUrl);return a("div",{className:"claudeqq-page","data-testid":"claudeqq-page"},a(Ki,{title:"ClaudeQQ 工作台",eyebrow:"D601 QQ Event Gateway",loading:y.loading,actions:a("div",{className:"panel-actions"},a("button",{type:"button",className:"ghost-btn",onClick:q,disabled:y.loading,"data-testid":"claudeqq-refresh-button"},y.loading?"刷新中":"刷新"),a(cn,{title:"ClaudeQQ 用户服务",data:r,onOpen:f,testId:"raw-claudeqq-service"}))},a("div",{className:"findjob-hero"},a("div",null,a("div",{className:"node-version-line"},a(K6,{status:N.providerStatus==="online"?"online":"warn"},N.providerStatus||"unknown"),a("span",null,r.providerId),a("span",null,z.public?"公网暴露":"仅 UniDesk frontend 代理访问")),a("p",{className:"muted paragraph"},r.description)),a("div",{className:"microservice-ref-card"},a("span",null,"Repo"),a("strong",null,c.url||"--"),a("code",null,c.commitId||"--")),a("div",{className:"microservice-ref-card"},a("span",null,"D601 Docker"),a("strong",null,`${z.nodeBindHost||"--"}:${z.nodePort||"--"}`),a("code",null,`${c.composeFile||"--"} / ${c.composeService||"--"}`))),a(jf,{error:y.error,wide:!0}),F?a("div",{className:"form-success wide"},F):null),a("div",{className:"metric-grid"},a(Gi,{label:"Health",value:w.ok||w.status==="ok"?"OK":"--",hint:"D601 /health",tone:w.ok||w.status==="ok"?"ok":"warn"}),a(Gi,{label:"NapCat HTTP",value:X.httpConnected||X.http?.connected?"OK":"离线",hint:`${X.httpHost||w.napcat?.httpHost||"--"}:${X.httpPort||w.napcat?.httpPort||"--"}`}),a(Gi,{label:"NapCat WS",value:X.wsConnected||X.ws?.connected?"OK":"离线",hint:`${X.wsHost||w.napcat?.wsHost||"--"}:${X.wsPort||w.napcat?.wsPort||"--"}`}),a(Gi,{label:"事件缓存",value:rA(y.events?.count??P.length),hint:"recent QQ events"}),a(Gi,{label:"订阅",value:rA(y.subscriptions?.count??t.length),hint:"webhook subscribers"}),a(Gi,{label:"已发送",value:rA(y.sent?.count??O.length),hint:"sent message log"})),a("div",{className:"findjob-grid"},a(Ki,{title:"NapCat 容器登录",eyebrow:"QR Login",className:"claudeqq-login-panel",loading:y.qrLoading,actions:a("div",{className:"panel-actions inline-actions"},a("button",{type:"button",className:"ghost-btn",onClick:()=>W(!0),disabled:y.qrLoading,"data-testid":"claudeqq-napcat-refresh"},y.qrLoading?"刷新中":"手动刷新二维码"),a(cn,{title:"NapCat Login",data:y.napcatLogin,onOpen:f,testId:"raw-claudeqq-napcat-login"}))},a("div",{className:"claudeqq-login-card","data-testid":"claudeqq-napcat-login"},a("div",{className:"claudeqq-qr-frame"},b?a("img",{src:Y.dataUrl,alt:"NapCat QQ 登录二维码","data-testid":"claudeqq-napcat-qrcode"}):a(Nn,{title:"等待二维码",text:"NapCat 容器启动后会把登录二维码写入 cache/qrcode.png"})),a("div",{className:"claudeqq-login-copy"},a("div",{className:"node-version-line"},a(K6,{status:M?"online":b?"warn":"unknown"},M?"已登录":b?"待扫码":"等待二维码"),a("span",null,S),a("span",null,"D601 containerized")),a("p",{className:"muted paragraph"},M?"NapCat 已登录,ClaudeQQ 可通过容器内 HTTP/WS 链路收发 QQ 消息。":"用手机 QQ 扫描二维码授权登录。二维码只在首次加载或手动刷新时更新,D601 的 NapCat 端口仍只绑定 127.0.0.1。"),a("div",{className:"microservice-ref-card"},a("span",null,"NapCat WebUI"),a("strong",null,X.webui?.url||"http://napcat:6099/webui"),a("code",null,"local-only / proxied QR login")),a("div",{className:"microservice-ref-card"},a("span",null,"QR Source"),a("strong",null,Y.modifiedAt?Nu(Y.modifiedAt):y.qrcodeRefreshedAt?Nu(y.qrcodeRefreshedAt):"--"),a("code",null,Y.file||"/napcat/cache/qrcode.png"))))),a(Ki,{title:"消息推送",eyebrow:"Push API"},a("div",{className:"microservice-ref-card"},a("span",null,_y.label),a("strong",null,String(_y.userId)),a("code",null,"private userId / 默认推送测试目标")),a("form",{className:"stack-form",onSubmit:G,"data-testid":"claudeqq-push-form"},a("label",null,"目标类型",a("select",{value:_.targetType,onChange:(Z)=>n((D)=>({...D,targetType:Z.target.value}))},a("option",{value:"private"},"私聊 userId"),a("option",{value:"group"},"群 groupId"))),a("label",null,"QQ ID",a("input",{value:_.targetId,onChange:(Z)=>n((D)=>({...D,targetId:Z.target.value})),placeholder:String(_y.userId)})),a("label",null,"消息内容",a("textarea",{value:_.message,onChange:(Z)=>n((D)=>({...D,message:Z.target.value})),rows:4,placeholder:"通过 ClaudeQQ 推送一条 QQ 消息"})),a("button",{type:"submit",className:"primary-btn"},"发送 QQ 消息")),a("p",{className:"muted paragraph"},`主 server 和其他用户服务可通过 UniDesk 同源代理调用 /api/push/text;当前人工推送测试默认使用 ${_y.label} ${_y.userId},不需要暴露 D601 后端端口。`)),a(Ki,{title:"QQ 事件订阅",eyebrow:"Webhook Subscription",loading:y.loading},a("form",{className:"stack-form",onSubmit:K,"data-testid":"claudeqq-subscription-form"},a("label",null,"订阅名称",a("input",{value:$.name,onChange:(Z)=>j((D)=>({...D,name:Z.target.value}))})),a("label",null,"回调 URL",a("input",{value:$.targetUrl,onChange:(Z)=>j((D)=>({...D,targetUrl:Z.target.value})),placeholder:"http://host.docker.internal:18080/..."})),a("label",null,"事件类型",a("input",{value:$.eventTypes,onChange:(Z)=>j((D)=>({...D,eventTypes:Z.target.value})),placeholder:"message,notice"})),a("label",null,"签名密钥",a("input",{value:$.secret,onChange:(Z)=>j((D)=>({...D,secret:Z.target.value})),placeholder:"可选,生成 x-claudeqq-signature"})),a("button",{type:"submit",className:"primary-btn"},"创建订阅")),t.length===0?a(Nn,{title:"暂无订阅",text:"可以为 main server 或其他用户服务注册 HTTP webhook"}):a("div",{className:"table-wrap","data-testid":"claudeqq-subscription-table"},a("table",null,a("thead",null,a("tr",null,a("th",null,"名称"),a("th",null,"状态"),a("th",null,"事件"),a("th",null,"回调"),a("th",null,"最近投递"),a("th",null,"操作"))),a("tbody",null,t.map((Z)=>a("tr",{key:Z.id},a("td",null,a("strong",null,Z.name||Z.id),a("code",null,Z.id||"--")),a("td",null,a(K6,{status:Z.enabled?"online":"warn"},Z.enabled?"enabled":"disabled")),a("td",null,Array.isArray(Z.eventTypes)?Z.eventTypes.join(", "):"message"),a("td",null,Z.targetUrl||"--"),a("td",null,Z.lastDelivery?`${Z.lastDelivery.ok?"OK":"FAIL"} ${Nu(Z.lastDelivery.at)}`:"--"),a("td",null,a("button",{type:"button",className:"ghost-btn",onClick:()=>Q(Z.id)},"删除"))))))),a("div",{className:"panel-actions inline-actions"},a(cn,{title:"ClaudeQQ Subscriptions",data:y.subscriptions,onOpen:f,testId:"raw-claudeqq-subscriptions"}))),a(Ki,{title:"最近 QQ 事件",eyebrow:y.refreshedAt?`Updated ${qf(y.refreshedAt)}`:"Event Stream",loading:y.loading},P.length===0?a(Nn,{title:"暂无事件",text:"等待 NapCat WebSocket 上报 QQ 消息事件,或通过订阅 API 消费后续事件"}):a("div",{className:"table-wrap","data-testid":"claudeqq-event-list"},a("table",null,a("thead",null,a("tr",null,a("th",null,"时间"),a("th",null,"类型"),a("th",null,"会话"),a("th",null,"消息"),a("th",null,"ID"))),a("tbody",null,P.map((Z)=>a("tr",{key:Z.id},a("td",null,Nu(Z.receivedAt||Z.timestamp)),a("td",null,a(K6,{status:Z.postType||Z.eventType},Z.postType||Z.eventType||"--")),a("td",null,xQ(Z)),a("td",null,CQ(Z)),a("td",null,a("code",null,Z.messageId||Z.id||"--"))))))),a("div",{className:"panel-actions inline-actions"},a(cn,{title:"ClaudeQQ Events",data:y.events,onOpen:f,testId:"raw-claudeqq-events"}))),a(Ki,{title:"已发送消息",eyebrow:`${O.length} Sent`,loading:y.loading},O.length===0?a(Nn,{title:"暂无发送记录",text:"发送日志来自 ClaudeQQ bot_workspace/messages/sent_messages.jsonl"}):a("div",{className:"table-wrap"},a("table",null,a("thead",null,a("tr",null,a("th",null,"时间"),a("th",null,"目标"),a("th",null,"消息"),a("th",null,"结果"))),a("tbody",null,O.map((Z,D)=>a("tr",{key:Z.id||D},a("td",null,Nu(Z.timestamp||Z.sentAt||Z.createdAt)),a("td",null,xQ(Z)),a("td",null,CQ(Z)),a("td",null,Z.status||Z.messageId||Z.message_id||"--")))))),a("div",{className:"panel-actions inline-actions"},a(cn,{title:"ClaudeQQ Sent Messages",data:y.sent,onOpen:f,testId:"raw-claudeqq-sent"})))))}var On=Pu(wf(),1);var vQ=Pu(wf(),1),Jf=vQ.default.createElement;function IQ({markdown:u,className:f,testId:l}){let r=String(u??"").trimEnd(),y=["markdown-body",f].filter(Boolean).join(" ");return Jf("div",{className:y,"data-testid":l},kQ(r,"md"))}function kQ(u,f){let l=XE(u).split(` -`),r=[],y=0;while(y\s?/u.test(i)){let J=[];while(y\s?(.*)$/u);if(q!==null){J.push(q[1]),y+=1;continue}if(U.trim().length===0){J.push(""),y+=1;continue}break}r.push(Jf("blockquote",{key:`${f}-quote-${y}`},kQ(J.join(` -`),`${f}-quote-${y}`)));continue}if(sQ(l,y)){let J=y,U=zn(l[y]??""),q=zn(l[y+1]??"");y+=2;let W=[];while(y0)W.push(zn(l[y]??"")),y+=1;r.push(PE(U,q,W,`${f}-table-${J}`));continue}let $=E6(i);if($!==null){let J=y,U=$.ordered,q=$.start,W=[];while(ytE(K,`${f}-list-${J}-${Q}`))));continue}let j=y,F=[];while(y0&&!DE(l,y))F.push(l[y].trim()),y+=1;if(F.length===0)F.push(i.trim()),y+=1;r.push(Jf("p",{key:`${f}-p-${j}`},u0(F.join(` -`),`${f}-p-${j}`)))}return r}function XE(u){return String(u||"").replace(/\r\n/gu,` +`+i.stack}return{value:u,source:f,stack:n,digest:null}}function z4(u,f,l){return{value:u,source:null,stack:l!=null?l:null,digest:f!=null?f:null}}function $2(u,f){try{console.error(f.value)}catch(l){setTimeout(function(){throw l})}}var $z=typeof WeakMap==="function"?WeakMap:Map;function UJ(u,f,l){l=G0(-1,l),l.tag=3,l.payload={element:null};var r=f.value;return l.callback=function(){i3||(i3=!0,N2=r),$2(u,f)},l}function QJ(u,f,l){l=G0(-1,l),l.tag=3;var r=u.type.getDerivedStateFromError;if(typeof r==="function"){var n=f.value;l.payload=function(){return r(n)},l.callback=function(){$2(u,f)}}var i=u.stateNode;return i!==null&&typeof i.componentDidCatch==="function"&&(l.callback=function(){$2(u,f),typeof r!=="function"&&(cn===null?cn=new Set([this]):cn.add(this));var y=f.stack;this.componentDidCatch(f.value,{componentStack:y!==null?y:""})}),l}function Zj(u,f,l){var r=u.pingCache;if(r===null){r=u.pingCache=new $z;var n=new Set;r.set(f,n)}else n=r.get(f),n===void 0&&(n=new Set,r.set(f,n));n.has(l)||(n.add(l),u=Kz.bind(null,u,f,l),f.then(u,u))}function Oj(u){do{var f;if(f=u.tag===13)f=u.memoizedState,f=f!==null?f.dehydrated!==null?!0:!1:!0;if(f)return u;u=u.return}while(u!==null);return null}function Hj(u,f,l,r,n){if((u.mode&1)===0)return u===f?u.flags|=65536:(u.flags|=128,l.flags|=131072,l.flags&=-52805,l.tag===1&&(l.alternate===null?l.tag=17:(f=G0(-1,1),f.tag=2,tn(l,f,1))),l.lanes|=1),u;return u.flags|=65536,u.lanes=n,u}var tz=O0.ReactCurrentOwner,Pl=!1;function Zl(u,f,l,r){f.child=u===null?vF(f,null,l,r):Gi(f,u.child,l,r)}function Bj(u,f,l,r,n){l=l.render;var i=f.ref;if(Ni(f,n),r=a2(u,f,l,r,i,n),l=o2(),u!==null&&!Pl)return f.updateQueue=u.updateQueue,f.flags&=-2053,u.lanes&=~n,Z0(u,f,n);return zf&&l&&P2(f),f.flags|=1,Zl(u,f,r,n),f.child}function Vj(u,f,l,r,n){if(u===null){var i=l.type;if(typeof i==="function"&&!_c(i)&&i.defaultProps===void 0&&l.compare===null&&l.defaultProps===void 0)return f.tag=15,f.type=i,qJ(u,f,i,r,n);return u=Pt(l.type,null,r,f,f.mode,n),u.ref=f.ref,u.return=f,f.child=u}if(i=u.child,(u.lanes&n)===0){var y=i.memoizedProps;if(l=l.compare,l=l!==null?l:t_,l(y,r)&&u.ref===f.ref)return Z0(u,f,n)}return f.flags|=1,u=jn(i,r),u.ref=f.ref,u.return=f,f.child=u}function qJ(u,f,l,r,n){if(u!==null){var i=u.memoizedProps;if(t_(i,r)&&u.ref===f.ref)if(Pl=!1,f.pendingProps=r=i,(u.lanes&n)!==0)(u.flags&131072)!==0&&(Pl=!0);else return f.lanes=u.lanes,Z0(u,f,n)}return t2(u,f,l,r,n)}function NJ(u,f,l){var r=f.pendingProps,n=r.children,i=u!==null?u.memoizedState:null;if(r.mode==="hidden")if((f.mode&1)===0)f.memoizedState={baseLanes:0,cachePool:null,transitions:null},Nf(Fi,fr),fr|=l;else{if((l&1073741824)===0)return u=i!==null?i.baseLanes|l:l,f.lanes=f.childLanes=1073741824,f.memoizedState={baseLanes:u,cachePool:null,transitions:null},f.updateQueue=null,Nf(Fi,fr),fr|=u,null;f.memoizedState={baseLanes:0,cachePool:null,transitions:null},r=i!==null?i.baseLanes:l,Nf(Fi,fr),fr|=r}else i!==null?(r=i.baseLanes|l,f.memoizedState=null):r=l,Nf(Fi,fr),fr|=r;return Zl(u,f,n,l),f.child}function WJ(u,f){var l=f.ref;if(u===null&&l!==null||u!==null&&u.ref!==l)f.flags|=512,f.flags|=2097152}function t2(u,f,l,r,n){var i=Cl(l)?$1:ql.current;if(i=Li(f,i),Ni(f,n),l=a2(u,f,l,r,i,n),r=o2(),u!==null&&!Pl)return f.updateQueue=u.updateQueue,f.flags&=-2053,u.lanes&=~n,Z0(u,f,n);return zf&&r&&P2(f),f.flags|=1,Zl(u,f,l,n),f.child}function Dj(u,f,l,r,n){if(Cl(l)){var i=!0;st(f)}else i=!1;if(Ni(f,n),f.stateNode===null)St(u,f),JJ(f,l,r),_2(f,l,r,n),r=!0;else if(u===null){var{stateNode:y,memoizedProps:_}=f;y.props=_;var $=y.context,c=l.contextType;typeof c==="object"&&c!==null?c=Lr(c):(c=Cl(l)?$1:ql.current,c=Li(f,c));var A=l.getDerivedStateFromProps,j=typeof A==="function"||typeof y.getSnapshotBeforeUpdate==="function";j||typeof y.UNSAFE_componentWillReceiveProps!=="function"&&typeof y.componentWillReceiveProps!=="function"||(_!==r||$!==c)&&Ej(f,y,r,c),e0=!1;var F=f.memoizedState;y.state=F,u3(f,r,y,n),$=f.memoizedState,_!==r||F!==$||Ml.current||e0?(typeof A==="function"&&(y2(f,l,A,r),$=f.memoizedState),(_=e0||Tj(f,l,_,r,F,$,c))?(j||typeof y.UNSAFE_componentWillMount!=="function"&&typeof y.componentWillMount!=="function"||(typeof y.componentWillMount==="function"&&y.componentWillMount(),typeof y.UNSAFE_componentWillMount==="function"&&y.UNSAFE_componentWillMount()),typeof y.componentDidMount==="function"&&(f.flags|=4194308)):(typeof y.componentDidMount==="function"&&(f.flags|=4194308),f.memoizedProps=r,f.memoizedState=$),y.props=r,y.state=$,y.context=c,r=_):(typeof y.componentDidMount==="function"&&(f.flags|=4194308),r=!1)}else{y=f.stateNode,IF(u,f),_=f.memoizedProps,c=f.type===f.elementType?_:Xr(f.type,_),y.props=c,j=f.pendingProps,F=y.context,$=l.contextType,typeof $==="object"&&$!==null?$=Lr($):($=Cl(l)?$1:ql.current,$=Li(f,$));var U=l.getDerivedStateFromProps;(A=typeof U==="function"||typeof y.getSnapshotBeforeUpdate==="function")||typeof y.UNSAFE_componentWillReceiveProps!=="function"&&typeof y.componentWillReceiveProps!=="function"||(_!==j||F!==$)&&Ej(f,y,r,$),e0=!1,F=f.memoizedState,y.state=F,u3(f,r,y,n);var Q=f.memoizedState;_!==j||F!==Q||Ml.current||e0?(typeof U==="function"&&(y2(f,l,U,r),Q=f.memoizedState),(c=e0||Tj(f,l,c,r,F,Q,$)||!1)?(A||typeof y.UNSAFE_componentWillUpdate!=="function"&&typeof y.componentWillUpdate!=="function"||(typeof y.componentWillUpdate==="function"&&y.componentWillUpdate(r,Q,$),typeof y.UNSAFE_componentWillUpdate==="function"&&y.UNSAFE_componentWillUpdate(r,Q,$)),typeof y.componentDidUpdate==="function"&&(f.flags|=4),typeof y.getSnapshotBeforeUpdate==="function"&&(f.flags|=1024)):(typeof y.componentDidUpdate!=="function"||_===u.memoizedProps&&F===u.memoizedState||(f.flags|=4),typeof y.getSnapshotBeforeUpdate!=="function"||_===u.memoizedProps&&F===u.memoizedState||(f.flags|=1024),f.memoizedProps=r,f.memoizedState=Q),y.props=r,y.state=Q,y.context=$,r=c):(typeof y.componentDidUpdate!=="function"||_===u.memoizedProps&&F===u.memoizedState||(f.flags|=4),typeof y.getSnapshotBeforeUpdate!=="function"||_===u.memoizedProps&&F===u.memoizedState||(f.flags|=1024),r=!1)}return c2(u,f,l,r,i,n)}function c2(u,f,l,r,n,i){WJ(u,f);var y=(f.flags&128)!==0;if(!r&&!y)return n&&qj(f,l,!1),Z0(u,f,i);r=f.stateNode,tz.current=f;var _=y&&typeof l.getDerivedStateFromError!=="function"?null:r.render();return f.flags|=1,u!==null&&y?(f.child=Gi(f,u.child,null,i),f.child=Gi(f,null,_,i)):Zl(u,f,_,i),f.memoizedState=r.state,n&&qj(f,l,!0),f.child}function wJ(u){var f=u.stateNode;f.pendingContext?Qj(u,f.pendingContext,f.pendingContext!==f.context):f.context&&Qj(u,f.context,!1),k2(u,f.containerInfo)}function Xj(u,f,l,r,n){return Ki(),C2(n),f.flags|=256,Zl(u,f,l,r),f.child}var A2={dehydrated:null,treeContext:null,retryLane:0};function j2(u){return{baseLanes:u,cachePool:null,transitions:null}}function LJ(u,f,l){var r=f.pendingProps,n=Ef.current,i=!1,y=(f.flags&128)!==0,_;if((_=y)||(_=u!==null&&u.memoizedState===null?!1:(n&2)!==0),_)i=!0,f.flags&=-129;else if(u===null||u.memoizedState!==null)n|=1;if(Nf(Ef,n&1),u===null){if(n2(f),u=f.memoizedState,u!==null&&(u=u.dehydrated,u!==null))return(f.mode&1)===0?f.lanes=1:u.data==="$!"?f.lanes=8:f.lanes=1073741824,null;return y=r.children,u=r.fallback,i?(r=f.mode,i=f.child,y={mode:"hidden",children:y},(r&1)===0&&i!==null?(i.childLanes=0,i.pendingProps=y):i=W3(y,r,0,null),u=_1(u,r,l,null),i.return=f,u.return=f,i.sibling=u,f.child=i,f.child.memoizedState=j2(l),f.memoizedState=A2,u):uc(f,y)}if(n=u.memoizedState,n!==null&&(_=n.dehydrated,_!==null))return cz(u,f,y,r,_,n,l);if(i){i=r.fallback,y=f.mode,n=u.child,_=n.sibling;var $={mode:"hidden",children:r.children};return(y&1)===0&&f.child!==n?(r=f.child,r.childLanes=0,r.pendingProps=$,f.deletions=null):(r=jn(n,$),r.subtreeFlags=n.subtreeFlags&14680064),_!==null?i=jn(_,i):(i=_1(i,y,l,null),i.flags|=2),i.return=f,r.return=f,r.sibling=i,f.child=r,r=i,i=f.child,y=u.child.memoizedState,y=y===null?j2(l):{baseLanes:y.baseLanes|l,cachePool:null,transitions:y.transitions},i.memoizedState=y,i.childLanes=u.childLanes&~l,f.memoizedState=A2,r}return i=u.child,u=i.sibling,r=jn(i,{mode:"visible",children:r.children}),(f.mode&1)===0&&(r.lanes=l),r.return=f,r.sibling=null,u!==null&&(l=f.deletions,l===null?(f.deletions=[u],f.flags|=16):l.push(u)),f.child=r,f.memoizedState=null,r}function uc(u,f){return f=W3({mode:"visible",children:f},u.mode,0,null),f.return=u,u.child=f}function Tt(u,f,l,r){return r!==null&&C2(r),Gi(f,u.child,null,l),u=uc(f,f.pendingProps.children),u.flags|=2,f.memoizedState=null,u}function cz(u,f,l,r,n,i,y){if(l){if(f.flags&256)return f.flags&=-257,r=z4(Error(Uu(422))),Tt(u,f,y,r);if(f.memoizedState!==null)return f.child=u.child,f.flags|=128,null;return i=r.fallback,n=f.mode,r=W3({mode:"visible",children:r.children},n,0,null),i=_1(i,n,y,null),i.flags|=2,r.return=f,i.return=f,r.sibling=i,f.child=r,(f.mode&1)!==0&&Gi(f,u.child,null,y),f.child.memoizedState=j2(y),f.memoizedState=A2,i}if((f.mode&1)===0)return Tt(u,f,y,null);if(n.data==="$!"){if(r=n.nextSibling&&n.nextSibling.dataset,r)var _=r.dgst;return r=_,i=Error(Uu(419)),r=z4(i,r,void 0),Tt(u,f,y,r)}if(_=(y&u.childLanes)!==0,Pl||_){if(r=fl,r!==null){switch(y&-y){case 4:n=2;break;case 16:n=8;break;case 64: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:case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:n=32;break;case 536870912:n=268435456;break;default:n=0}n=(n&(r.suspendedLanes|y))!==0?0:n,n!==0&&n!==i.retryLane&&(i.retryLane=n,E0(u,n),pr(r,u,n,-1))}return yc(),r=z4(Error(Uu(421))),Tt(u,f,y,r)}if(n.data==="$?")return f.flags|=128,f.child=u.child,f=Gz.bind(null,u),n._reactRetry=f,null;return u=i.treeContext,lr=$n(n.nextSibling),rr=f,zf=!0,Sr=null,u!==null&&(qr[Nr++]=L0,qr[Nr++]=K0,qr[Nr++]=t1,L0=u.id,K0=u.overflow,t1=f),f=uc(f,r.children),f.flags|=4096,f}function Yj(u,f,l){u.lanes|=f;var r=u.alternate;r!==null&&(r.lanes|=f),i2(u.return,f,l)}function T4(u,f,l,r,n){var i=u.memoizedState;i===null?u.memoizedState={isBackwards:f,rendering:null,renderingStartTime:0,last:r,tail:l,tailMode:n}:(i.isBackwards=f,i.rendering=null,i.renderingStartTime=0,i.last=r,i.tail=l,i.tailMode=n)}function KJ(u,f,l){var r=f.pendingProps,n=r.revealOrder,i=r.tail;if(Zl(u,f,r.children,l),r=Ef.current,(r&2)!==0)r=r&1|2,f.flags|=128;else{if(u!==null&&(u.flags&128)!==0)u:for(u=f.child;u!==null;){if(u.tag===13)u.memoizedState!==null&&Yj(u,l,f);else if(u.tag===19)Yj(u,l,f);else if(u.child!==null){u.child.return=u,u=u.child;continue}if(u===f)break u;for(;u.sibling===null;){if(u.return===null||u.return===f)break u;u=u.return}u.sibling.return=u.return,u=u.sibling}r&=1}if(Nf(Ef,r),(f.mode&1)===0)f.memoizedState=null;else switch(n){case"forwards":l=f.child;for(n=null;l!==null;)u=l.alternate,u!==null&&f3(u)===null&&(n=l),l=l.sibling;l=n,l===null?(n=f.child,f.child=null):(n=l.sibling,l.sibling=null),T4(f,!1,n,l,i);break;case"backwards":l=null,n=f.child;for(f.child=null;n!==null;){if(u=n.alternate,u!==null&&f3(u)===null){f.child=n;break}u=n.sibling,n.sibling=l,l=n,n=u}T4(f,!0,l,null,i);break;case"together":T4(f,!1,null,null,void 0);break;default:f.memoizedState=null}return f.child}function St(u,f){(f.mode&1)===0&&u!==null&&(u.alternate=null,f.alternate=null,f.flags|=2)}function Z0(u,f,l){if(u!==null&&(f.dependencies=u.dependencies),A1|=f.lanes,(l&f.childLanes)===0)return null;if(u!==null&&f.child!==u.child)throw Error(Uu(153));if(f.child!==null){u=f.child,l=jn(u,u.pendingProps),f.child=l;for(l.return=f;u.sibling!==null;)u=u.sibling,l=l.sibling=jn(u,u.pendingProps),l.return=f;l.sibling=null}return f.child}function Az(u,f,l){switch(f.tag){case 3:wJ(f),Ki();break;case 5:gF(f);break;case 1:Cl(f.type)&&st(f);break;case 4:k2(f,f.stateNode.containerInfo);break;case 10:var r=f.type._context,n=f.memoizedProps.value;Nf(dt,r._currentValue),r._currentValue=n;break;case 13:if(r=f.memoizedState,r!==null){if(r.dehydrated!==null)return Nf(Ef,Ef.current&1),f.flags|=128,null;if((l&f.child.childLanes)!==0)return LJ(u,f,l);return Nf(Ef,Ef.current&1),u=Z0(u,f,l),u!==null?u.sibling:null}Nf(Ef,Ef.current&1);break;case 19:if(r=(l&f.childLanes)!==0,(u.flags&128)!==0){if(r)return KJ(u,f,l);f.flags|=128}if(n=f.memoizedState,n!==null&&(n.rendering=null,n.tail=null,n.lastEffect=null),Nf(Ef,Ef.current),r)break;else return null;case 22:case 23:return f.lanes=0,NJ(u,f,l)}return Z0(u,f,l)}var GJ,F2,zJ,TJ;GJ=function(u,f){for(var l=f.child;l!==null;){if(l.tag===5||l.tag===6)u.appendChild(l.stateNode);else if(l.tag!==4&&l.child!==null){l.child.return=l,l=l.child;continue}if(l===f)break;for(;l.sibling===null;){if(l.return===null||l.return===f)return;l=l.return}l.sibling.return=l.return,l=l.sibling}};F2=function(){};zJ=function(u,f,l,r){var n=u.memoizedProps;if(n!==r){u=f.stateNode,i1(f0.current);var i=null;switch(l){case"input":n=X4(u,n),r=X4(u,r),i=[];break;case"select":n=Of({},n,{value:void 0}),r=Of({},r,{value:void 0}),i=[];break;case"textarea":n=m4(u,n),r=m4(u,r),i=[];break;default:typeof n.onClick!=="function"&&typeof r.onClick==="function"&&(u.onclick=It)}P4(l,r);var y;l=null;for(c in n)if(!r.hasOwnProperty(c)&&n.hasOwnProperty(c)&&n[c]!=null)if(c==="style"){var _=n[c];for(y in _)_.hasOwnProperty(y)&&(l||(l={}),l[y]="")}else c!=="dangerouslySetInnerHTML"&&c!=="children"&&c!=="suppressContentEditableWarning"&&c!=="suppressHydrationWarning"&&c!=="autoFocus"&&(l_.hasOwnProperty(c)?i||(i=[]):(i=i||[]).push(c,null));for(c in r){var $=r[c];if(_=n!=null?n[c]:void 0,r.hasOwnProperty(c)&&$!==_&&($!=null||_!=null))if(c==="style")if(_){for(y in _)!_.hasOwnProperty(y)||$&&$.hasOwnProperty(y)||(l||(l={}),l[y]="");for(y in $)$.hasOwnProperty(y)&&_[y]!==$[y]&&(l||(l={}),l[y]=$[y])}else l||(i||(i=[]),i.push(c,l)),l=$;else c==="dangerouslySetInnerHTML"?($=$?$.__html:void 0,_=_?_.__html:void 0,$!=null&&_!==$&&(i=i||[]).push(c,$)):c==="children"?typeof $!=="string"&&typeof $!=="number"||(i=i||[]).push(c,""+$):c!=="suppressContentEditableWarning"&&c!=="suppressHydrationWarning"&&(l_.hasOwnProperty(c)?($!=null&&c==="onScroll"&&wf("scroll",u),i||_===$||(i=[])):(i=i||[]).push(c,$))}l&&(i=i||[]).push("style",l);var c=i;if(f.updateQueue=c)f.flags|=4}};TJ=function(u,f,l,r){l!==r&&(f.flags|=4)};function py(u,f){if(!zf)switch(u.tailMode){case"hidden":f=u.tail;for(var l=null;f!==null;)f.alternate!==null&&(l=f),f=f.sibling;l===null?u.tail=null:l.sibling=null;break;case"collapsed":l=u.tail;for(var r=null;l!==null;)l.alternate!==null&&(r=l),l=l.sibling;r===null?f||u.tail===null?u.tail=null:u.tail.sibling=null:r.sibling=null}}function Ul(u){var f=u.alternate!==null&&u.alternate.child===u.child,l=0,r=0;if(f)for(var n=u.child;n!==null;)l|=n.lanes|n.childLanes,r|=n.subtreeFlags&14680064,r|=n.flags&14680064,n.return=u,n=n.sibling;else for(n=u.child;n!==null;)l|=n.lanes|n.childLanes,r|=n.subtreeFlags,r|=n.flags,n.return=u,n=n.sibling;return u.subtreeFlags|=r,u.childLanes=l,f}function jz(u,f,l){var r=f.pendingProps;switch(M2(f),f.tag){case 2:case 16:case 15:case 0:case 11:case 7:case 8:case 12:case 9:case 14:return Ul(f),null;case 1:return Cl(f.type)&>(),Ul(f),null;case 3:if(r=f.stateNode,zi(),Lf(Ml),Lf(ql),g2(),r.pendingContext&&(r.context=r.pendingContext,r.pendingContext=null),u===null||u.child===null)Gt(f)?f.flags|=4:u===null||u.memoizedState.isDehydrated&&(f.flags&256)===0||(f.flags|=1024,Sr!==null&&(L2(Sr),Sr=null));return F2(u,f),Ul(f),null;case 5:I2(f);var n=i1(J_.current);if(l=f.type,u!==null&&f.stateNode!=null)zJ(u,f,l,r,n),u.ref!==f.ref&&(f.flags|=512,f.flags|=2097152);else{if(!r){if(f.stateNode===null)throw Error(Uu(166));return Ul(f),null}if(u=i1(f0.current),Gt(f)){r=f.stateNode,l=f.type;var i=f.memoizedProps;switch(r[er]=f,r[j_]=i,u=(f.mode&1)!==0,l){case"dialog":wf("cancel",r),wf("close",r);break;case"iframe":case"object":case"embed":wf("load",r);break;case"video":case"audio":for(n=0;n",u=u.removeChild(u.firstChild)):typeof r.is==="string"?u=y.createElement(l,{is:r.is}):(u=y.createElement(l),l==="select"&&(y=u,r.multiple?y.multiple=!0:r.size&&(y.size=r.size))):u=y.createElementNS(u,l),u[er]=f,u[j_]=r,GJ(u,f,!1,!1),f.stateNode=u;u:{switch(y=M4(l,r),l){case"dialog":wf("cancel",u),wf("close",u),n=r;break;case"iframe":case"object":case"embed":wf("load",u),n=r;break;case"video":case"audio":for(n=0;nEi&&(f.flags|=128,r=!0,py(i,!1),f.lanes=4194304)}else{if(!r)if(u=f3(y),u!==null){if(f.flags|=128,r=!0,l=u.updateQueue,l!==null&&(f.updateQueue=l,f.flags|=4),py(i,!0),i.tail===null&&i.tailMode==="hidden"&&!y.alternate&&!zf)return Ul(f),null}else 2*Sf()-i.renderingStartTime>Ei&&l!==1073741824&&(f.flags|=128,r=!0,py(i,!1),f.lanes=4194304);i.isBackwards?(y.sibling=f.child,f.child=y):(l=i.last,l!==null?l.sibling=y:f.child=y,i.last=y)}if(i.tail!==null)return f=i.tail,i.rendering=f,i.tail=f.sibling,i.renderingStartTime=Sf(),f.sibling=null,l=Ef.current,Nf(Ef,r?l&1|2:l&1),f;return Ul(f),null;case 22:case 23:return ic(),r=f.memoizedState!==null,u!==null&&u.memoizedState!==null!==r&&(f.flags|=8192),r&&(f.mode&1)!==0?(fr&1073741824)!==0&&(Ul(f),f.subtreeFlags&6&&(f.flags|=8192)):Ul(f),null;case 24:return null;case 25:return null}throw Error(Uu(156,f.tag))}function Fz(u,f){switch(M2(f),f.tag){case 1:return Cl(f.type)&>(),u=f.flags,u&65536?(f.flags=u&-65537|128,f):null;case 3:return zi(),Lf(Ml),Lf(ql),g2(),u=f.flags,(u&65536)!==0&&(u&128)===0?(f.flags=u&-65537|128,f):null;case 5:return I2(f),null;case 13:if(Lf(Ef),u=f.memoizedState,u!==null&&u.dehydrated!==null){if(f.alternate===null)throw Error(Uu(340));Ki()}return u=f.flags,u&65536?(f.flags=u&-65537|128,f):null;case 19:return Lf(Ef),null;case 4:return zi(),null;case 10:return h2(f.type._context),null;case 22:case 23:return ic(),null;case 24:return null;default:return null}}var Et=!1,Ql=!1,Jz=typeof WeakSet==="function"?WeakSet:Set,zu=null;function ji(u,f){var l=u.ref;if(l!==null)if(typeof l==="function")try{l(null)}catch(r){Vf(u,f,r)}else l.current=null}function J2(u,f,l){try{l()}catch(r){Vf(u,f,r)}}var Sj=!1;function Uz(u,f){if(o4=bt,u=BF(),p2(u)){if("selectionStart"in u)var l={start:u.selectionStart,end:u.selectionEnd};else u:{l=(l=u.ownerDocument)&&l.defaultView||window;var r=l.getSelection&&l.getSelection();if(r&&r.rangeCount!==0){l=r.anchorNode;var{anchorOffset:n,focusNode:i}=r;r=r.focusOffset;try{l.nodeType,i.nodeType}catch(W){l=null;break u}var y=0,_=-1,$=-1,c=0,A=0,j=u,F=null;f:for(;;){for(var U;;){if(j!==l||n!==0&&j.nodeType!==3||(_=y+n),j!==i||r!==0&&j.nodeType!==3||($=y+r),j.nodeType===3&&(y+=j.nodeValue.length),(U=j.firstChild)===null)break;F=j,j=U}for(;;){if(j===u)break f;if(F===l&&++c===n&&(_=y),F===i&&++A===r&&($=y),(U=j.nextSibling)!==null)break;j=F,F=j.parentNode}j=U}l=_===-1||$===-1?null:{start:_,end:$}}else l=null}l=l||{start:0,end:0}}else l=null;d4={focusedElem:u,selectionRange:l},bt=!1;for(zu=f;zu!==null;)if(f=zu,u=f.child,(f.subtreeFlags&1028)!==0&&u!==null)u.return=f,zu=u;else for(;zu!==null;){f=zu;try{var Q=f.alternate;if((f.flags&1024)!==0)switch(f.tag){case 0:case 11:case 15:break;case 1:if(Q!==null){var{memoizedProps:w,memoizedState:L}=Q,J=f.stateNode,N=J.getSnapshotBeforeUpdate(f.elementType===f.type?w:Xr(f.type,w),L);J.__reactInternalSnapshotBeforeUpdate=N}break;case 3:var q=f.stateNode.containerInfo;q.nodeType===1?q.textContent="":q.nodeType===9&&q.documentElement&&q.removeChild(q.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(Uu(163))}}catch(W){Vf(f,f.return,W)}if(u=f.sibling,u!==null){u.return=f.return,zu=u;break}zu=f.return}return Q=Sj,Sj=!1,Q}function ey(u,f,l){var r=f.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var n=r=r.next;do{if((n.tag&u)===u){var i=n.destroy;n.destroy=void 0,i!==void 0&&J2(f,l,i)}n=n.next}while(n!==r)}}function q3(u,f){if(f=f.updateQueue,f=f!==null?f.lastEffect:null,f!==null){var l=f=f.next;do{if((l.tag&u)===u){var r=l.create;l.destroy=r()}l=l.next}while(l!==f)}}function U2(u){var f=u.ref;if(f!==null){var l=u.stateNode;switch(u.tag){case 5:u=l;break;default:u=l}typeof f==="function"?f(u):f.current=u}}function EJ(u){var f=u.alternate;f!==null&&(u.alternate=null,EJ(f)),u.child=null,u.deletions=null,u.sibling=null,u.tag===5&&(f=u.stateNode,f!==null&&(delete f[er],delete f[j_],delete f[f2],delete f[oG],delete f[dG])),u.stateNode=null,u.return=null,u.dependencies=null,u.memoizedProps=null,u.memoizedState=null,u.pendingProps=null,u.stateNode=null,u.updateQueue=null}function ZJ(u){return u.tag===5||u.tag===3||u.tag===4}function mj(u){u:for(;;){for(;u.sibling===null;){if(u.return===null||ZJ(u.return))return null;u=u.return}u.sibling.return=u.return;for(u=u.sibling;u.tag!==5&&u.tag!==6&&u.tag!==18;){if(u.flags&2)continue u;if(u.child===null||u.tag===4)continue u;else u.child.return=u,u=u.child}if(!(u.flags&2))return u.stateNode}}function Q2(u,f,l){var r=u.tag;if(r===5||r===6)u=u.stateNode,f?l.nodeType===8?l.parentNode.insertBefore(u,f):l.insertBefore(u,f):(l.nodeType===8?(f=l.parentNode,f.insertBefore(u,l)):(f=l,f.appendChild(u)),l=l._reactRootContainer,l!==null&&l!==void 0||f.onclick!==null||(f.onclick=It));else if(r!==4&&(u=u.child,u!==null))for(Q2(u,f,l),u=u.sibling;u!==null;)Q2(u,f,l),u=u.sibling}function q2(u,f,l){var r=u.tag;if(r===5||r===6)u=u.stateNode,f?l.insertBefore(u,f):l.appendChild(u);else if(r!==4&&(u=u.child,u!==null))for(q2(u,f,l),u=u.sibling;u!==null;)q2(u,f,l),u=u.sibling}var il=null,Yr=!1;function o0(u,f,l){for(l=l.child;l!==null;)OJ(u,f,l),l=l.sibling}function OJ(u,f,l){if(u0&&typeof u0.onCommitFiberUnmount==="function")try{u0.onCommitFiberUnmount(t3,l)}catch(_){}switch(l.tag){case 5:Ql||ji(l,f);case 6:var r=il,n=Yr;il=null,o0(u,f,l),il=r,Yr=n,il!==null&&(Yr?(u=il,l=l.stateNode,u.nodeType===8?u.parentNode.removeChild(l):u.removeChild(l)):il.removeChild(l.stateNode));break;case 18:il!==null&&(Yr?(u=il,l=l.stateNode,u.nodeType===8?N4(u.parentNode,l):u.nodeType===1&&N4(u,l),__(u)):N4(il,l.stateNode));break;case 4:r=il,n=Yr,il=l.stateNode.containerInfo,Yr=!0,o0(u,f,l),il=r,Yr=n;break;case 0:case 11:case 14:case 15:if(!Ql&&(r=l.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){n=r=r.next;do{var i=n,y=i.destroy;i=i.tag,y!==void 0&&((i&2)!==0?J2(l,f,y):(i&4)!==0&&J2(l,f,y)),n=n.next}while(n!==r)}o0(u,f,l);break;case 1:if(!Ql&&(ji(l,f),r=l.stateNode,typeof r.componentWillUnmount==="function"))try{r.props=l.memoizedProps,r.state=l.memoizedState,r.componentWillUnmount()}catch(_){Vf(l,f,_)}o0(u,f,l);break;case 21:o0(u,f,l);break;case 22:l.mode&1?(Ql=(r=Ql)||l.memoizedState!==null,o0(u,f,l),Ql=r):o0(u,f,l);break;default:o0(u,f,l)}}function pj(u){var f=u.updateQueue;if(f!==null){u.updateQueue=null;var l=u.stateNode;l===null&&(l=u.stateNode=new Jz),f.forEach(function(r){var n=zz.bind(null,u,r);l.has(r)||(l.add(r),r.then(n,n))})}}function Dr(u,f){var l=f.deletions;if(l!==null)for(var r=0;rn&&(n=y),r&=~i}if(r=n,r=Sf()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3000>r?3000:4320>r?4320:1960*qz(r/1960))-r,10u?16:u,rn===null)var r=!1;else{if(u=rn,rn=null,y3=0,(lf&6)!==0)throw Error(Uu(331));var n=lf;lf|=4;for(zu=u.current;zu!==null;){var i=zu,y=i.child;if((zu.flags&16)!==0){var _=i.deletions;if(_!==null){for(var $=0;$<_.length;$++){var c=_[$];for(zu=c;zu!==null;){var A=zu;switch(A.tag){case 0:case 11:case 15:ey(8,A,i)}var j=A.child;if(j!==null)j.return=A,zu=j;else for(;zu!==null;){A=zu;var{sibling:F,return:U}=A;if(EJ(A),A===c){zu=null;break}if(F!==null){F.return=U,zu=F;break}zu=U}}}var Q=i.alternate;if(Q!==null){var w=Q.child;if(w!==null){Q.child=null;do{var L=w.sibling;w.sibling=null,w=L}while(w!==null)}}zu=i}}if((i.subtreeFlags&2064)!==0&&y!==null)y.return=i,zu=y;else u:for(;zu!==null;){if(i=zu,(i.flags&2048)!==0)switch(i.tag){case 0:case 11:case 15:ey(9,i,i.return)}var J=i.sibling;if(J!==null){J.return=i.return,zu=J;break u}zu=i.return}}var N=u.current;for(zu=N;zu!==null;){y=zu;var q=y.child;if((y.subtreeFlags&2064)!==0&&q!==null)q.return=y,zu=q;else u:for(y=N;zu!==null;){if(_=zu,(_.flags&2048)!==0)try{switch(_.tag){case 0:case 11:case 15:q3(9,_)}}catch(z){Vf(_,_.return,z)}if(_===y){zu=null;break u}var W=_.sibling;if(W!==null){W.return=_.return,zu=W;break u}zu=_.return}}if(lf=n,qn(),u0&&typeof u0.onPostCommitFiberRoot==="function")try{u0.onPostCommitFiberRoot(t3,u)}catch(z){}r=!0}return r}finally{tf=l,wr.transition=f}}return!1}function xj(u,f,l){f=Ti(l,f),f=UJ(u,f,1),u=tn(u,f,1),f=Ol(),u!==null&&(W_(u,1,f),Rl(u,f))}function Vf(u,f,l){if(u.tag===3)xj(u,u,l);else for(;f!==null;){if(f.tag===3){xj(f,u,l);break}else if(f.tag===1){var r=f.stateNode;if(typeof f.type.getDerivedStateFromError==="function"||typeof r.componentDidCatch==="function"&&(cn===null||!cn.has(r))){u=Ti(l,u),u=QJ(f,u,1),f=tn(f,u,1),u=Ol(),f!==null&&(W_(f,1,u),Rl(f,u));break}}f=f.return}}function Kz(u,f,l){var r=u.pingCache;r!==null&&r.delete(f),f=Ol(),u.pingedLanes|=u.suspendedLanes&l,fl===u&&(yl&l)===l&&(af===4||af===3&&(yl&130023424)===yl&&500>Sf()-rc?y1(u,0):lc|=l),Rl(u,f)}function mJ(u,f){f===0&&((u.mode&1)===0?f=1:(f=qt,qt<<=1,(qt&130023424)===0&&(qt=4194304)));var l=Ol();u=E0(u,f),u!==null&&(W_(u,f,l),Rl(u,l))}function Gz(u){var f=u.memoizedState,l=0;f!==null&&(l=f.retryLane),mJ(u,l)}function zz(u,f){var l=0;switch(u.tag){case 13:var{stateNode:r,memoizedState:n}=u;n!==null&&(l=n.retryLane);break;case 19:r=u.stateNode;break;default:throw Error(Uu(314))}r!==null&&r.delete(f),mJ(u,l)}var pJ;pJ=function(u,f,l){if(u!==null)if(u.memoizedProps!==f.pendingProps||Ml.current)Pl=!0;else{if((u.lanes&l)===0&&(f.flags&128)===0)return Pl=!1,Az(u,f,l);Pl=(u.flags&131072)!==0?!0:!1}else Pl=!1,zf&&(f.flags&1048576)!==0&&RF(f,ot,f.index);switch(f.lanes=0,f.tag){case 2:var r=f.type;St(u,f),u=f.pendingProps;var n=Li(f,ql.current);Ni(f,l),n=a2(null,f,r,u,n,l);var i=o2();return f.flags|=1,typeof n==="object"&&n!==null&&typeof n.render==="function"&&n.$$typeof===void 0?(f.tag=1,f.memoizedState=null,f.updateQueue=null,Cl(r)?(i=!0,st(f)):i=!1,f.memoizedState=n.state!==null&&n.state!==void 0?n.state:null,v2(f),n.updater=Q3,f.stateNode=n,n._reactInternals=f,_2(f,r,u,l),f=c2(null,f,r,!0,i,l)):(f.tag=0,zf&&i&&P2(f),Zl(null,f,n,l),f=f.child),f;case 16:r=f.elementType;u:{switch(St(u,f),u=f.pendingProps,n=r._init,r=n(r._payload),f.type=r,n=f.tag=Ez(r),u=Xr(r,u),n){case 0:f=t2(null,f,r,u,l);break u;case 1:f=Dj(null,f,r,u,l);break u;case 11:f=Bj(null,f,r,u,l);break u;case 14:f=Vj(null,f,r,Xr(r.type,u),l);break u}throw Error(Uu(306,r,""))}return f;case 0:return r=f.type,n=f.pendingProps,n=f.elementType===r?n:Xr(r,n),t2(u,f,r,n,l);case 1:return r=f.type,n=f.pendingProps,n=f.elementType===r?n:Xr(r,n),Dj(u,f,r,n,l);case 3:u:{if(wJ(f),u===null)throw Error(Uu(387));r=f.pendingProps,i=f.memoizedState,n=i.element,IF(u,f),u3(f,r,null,l);var y=f.memoizedState;if(r=y.element,i.isDehydrated)if(i={element:r,isDehydrated:!1,cache:y.cache,pendingSuspenseBoundaries:y.pendingSuspenseBoundaries,transitions:y.transitions},f.updateQueue.baseState=i,f.memoizedState=i,f.flags&256){n=Ti(Error(Uu(423)),f),f=Xj(u,f,r,l,n);break u}else if(r!==n){n=Ti(Error(Uu(424)),f),f=Xj(u,f,r,l,n);break u}else for(lr=$n(f.stateNode.containerInfo.firstChild),rr=f,zf=!0,Sr=null,l=vF(f,null,r,l),f.child=l;l;)l.flags=l.flags&-3|4096,l=l.sibling;else{if(Ki(),r===n){f=Z0(u,f,l);break u}Zl(u,f,r,l)}f=f.child}return f;case 5:return gF(f),u===null&&n2(f),r=f.type,n=f.pendingProps,i=u!==null?u.memoizedProps:null,y=n.children,e4(r,n)?y=null:i!==null&&e4(r,i)&&(f.flags|=32),WJ(u,f),Zl(u,f,y,l),f.child;case 6:return u===null&&n2(f),null;case 13:return LJ(u,f,l);case 4:return k2(f,f.stateNode.containerInfo),r=f.pendingProps,u===null?f.child=Gi(f,null,r,l):Zl(u,f,r,l),f.child;case 11:return r=f.type,n=f.pendingProps,n=f.elementType===r?n:Xr(r,n),Bj(u,f,r,n,l);case 7:return Zl(u,f,f.pendingProps,l),f.child;case 8:return Zl(u,f,f.pendingProps.children,l),f.child;case 12:return Zl(u,f,f.pendingProps.children,l),f.child;case 10:u:{if(r=f.type._context,n=f.pendingProps,i=f.memoizedProps,y=n.value,Nf(dt,r._currentValue),r._currentValue=y,i!==null)if(Pr(i.value,y)){if(i.children===n.children&&!Ml.current){f=Z0(u,f,l);break u}}else for(i=f.child,i!==null&&(i.return=f);i!==null;){var _=i.dependencies;if(_!==null){y=i.child;for(var $=_.firstContext;$!==null;){if($.context===r){if(i.tag===1){$=G0(-1,l&-l),$.tag=2;var c=i.updateQueue;if(c!==null){c=c.shared;var A=c.pending;A===null?$.next=$:($.next=A.next,A.next=$),c.pending=$}}i.lanes|=l,$=i.alternate,$!==null&&($.lanes|=l),i2(i.return,l,f),_.lanes|=l;break}$=$.next}}else if(i.tag===10)y=i.type===f.type?null:i.child;else if(i.tag===18){if(y=i.return,y===null)throw Error(Uu(341));y.lanes|=l,_=y.alternate,_!==null&&(_.lanes|=l),i2(y,l,f),y=i.sibling}else y=i.child;if(y!==null)y.return=i;else for(y=i;y!==null;){if(y===f){y=null;break}if(i=y.sibling,i!==null){i.return=y.return,y=i;break}y=y.return}i=y}Zl(u,f,n.children,l),f=f.child}return f;case 9:return n=f.type,r=f.pendingProps.children,Ni(f,l),n=Lr(n),r=r(n),f.flags|=1,Zl(u,f,r,l),f.child;case 14:return r=f.type,n=Xr(r,f.pendingProps),n=Xr(r.type,n),Vj(u,f,r,n,l);case 15:return qJ(u,f,f.type,f.pendingProps,l);case 17:return r=f.type,n=f.pendingProps,n=f.elementType===r?n:Xr(r,n),St(u,f),f.tag=1,Cl(r)?(u=!0,st(f)):u=!1,Ni(f,l),JJ(f,r,n),_2(f,r,n,l),c2(null,f,r,!0,u,l);case 19:return KJ(u,f,l);case 22:return NJ(u,f,l)}throw Error(Uu(156,f.tag))};function PJ(u,f){return AF(u,f)}function Tz(u,f,l,r){this.tag=u,this.key=l,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=f,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function Wr(u,f,l,r){return new Tz(u,f,l,r)}function _c(u){return u=u.prototype,!(!u||!u.isReactComponent)}function Ez(u){if(typeof u==="function")return _c(u)?1:0;if(u!==void 0&&u!==null){if(u=u.$$typeof,u===E2)return 11;if(u===Z2)return 14}return 2}function jn(u,f){var l=u.alternate;return l===null?(l=Wr(u.tag,f,u.key,u.mode),l.elementType=u.elementType,l.type=u.type,l.stateNode=u.stateNode,l.alternate=u,u.alternate=l):(l.pendingProps=f,l.type=u.type,l.flags=0,l.subtreeFlags=0,l.deletions=null),l.flags=u.flags&14680064,l.childLanes=u.childLanes,l.lanes=u.lanes,l.child=u.child,l.memoizedProps=u.memoizedProps,l.memoizedState=u.memoizedState,l.updateQueue=u.updateQueue,f=u.dependencies,l.dependencies=f===null?null:{lanes:f.lanes,firstContext:f.firstContext},l.sibling=u.sibling,l.index=u.index,l.ref=u.ref,l}function Pt(u,f,l,r,n,i){var y=2;if(r=u,typeof u==="function")_c(u)&&(y=1);else if(typeof u==="string")y=5;else u:switch(u){case ri:return _1(l.children,n,i,f);case T2:y=8,n|=8;break;case H4:return u=Wr(12,l,f,n|2),u.elementType=H4,u.lanes=i,u;case B4:return u=Wr(13,l,f,n),u.elementType=B4,u.lanes=i,u;case V4:return u=Wr(19,l,f,n),u.elementType=V4,u.lanes=i,u;case gj:return W3(l,n,i,f);default:if(typeof u==="object"&&u!==null)switch(u.$$typeof){case kj:y=10;break u;case Ij:y=9;break u;case E2:y=11;break u;case Z2:y=14;break u;case d0:y=16,r=null;break u}throw Error(Uu(130,u==null?u:typeof u,""))}return f=Wr(y,l,f,n),f.elementType=u,f.type=r,f.lanes=i,f}function _1(u,f,l,r){return u=Wr(7,u,r,f),u.lanes=l,u}function W3(u,f,l,r){return u=Wr(22,u,r,f),u.elementType=gj,u.lanes=l,u.stateNode={isHidden:!1},u}function E4(u,f,l){return u=Wr(6,u,null,f),u.lanes=l,u}function Z4(u,f,l){return f=Wr(4,u.children!==null?u.children:[],u.key,f),f.lanes=l,f.stateNode={containerInfo:u.containerInfo,pendingChildren:null,implementation:u.implementation},f}function Zz(u,f,l,r,n){this.tag=f,this.containerInfo=u,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=A4(0),this.expirationTimes=A4(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=A4(0),this.identifierPrefix=r,this.onRecoverableError=n,this.mutableSourceEagerHydrationData=null}function $c(u,f,l,r,n,i,y,_,$){return u=new Zz(u,f,l,_,$),f===1?(f=1,i===!0&&(f|=8)):f=0,i=Wr(3,null,null,f),u.current=i,i.stateNode=u,i.memoizedState={element:r,isDehydrated:l,cache:null,transitions:null,pendingSuspenseBoundaries:null},v2(i),u}function Oz(u,f,l){var r=3{function hJ(){if(typeof __REACT_DEVTOOLS_GLOBAL_HOOK__>"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!=="function")return;try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(hJ)}catch(u){console.error(u)}}hJ(),bJ.exports=xJ()});var kJ=ur((Fc)=>{var vJ=jc();Fc.createRoot=vJ.createRoot,Fc.hydrateRoot=vJ.hydrateRoot;var Xz});var hQ=ur((y6)=>{var IZ=Qf(),gZ=Symbol.for("react.element"),sZ=Symbol.for("react.fragment"),aZ=Object.prototype.hasOwnProperty,oZ=IZ.__SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED.ReactCurrentOwner,dZ={key:!0,ref:!0,__self:!0,__source:!0};function xQ(u,f,l){var r,n={},i=null,y=null;l!==void 0&&(i=""+l),f.key!==void 0&&(i=""+f.key),f.ref!==void 0&&(y=f.ref);for(r in f)aZ.call(f,r)&&!dZ.hasOwnProperty(r)&&(n[r]=f[r]);if(u&&u.defaultProps)for(r in f=u.defaultProps,f)n[r]===void 0&&(n[r]=f[r]);return{$$typeof:gZ,type:u,key:i,ref:y,props:n,_owner:oZ.current}}y6.Fragment=sZ;y6.jsx=xQ;y6.jsxs=xQ});var vQ=ur((Rp,bQ)=>{bQ.exports=hQ()});var KN=ur((LN)=>{var $y=Qf();function UV(u,f){return u===f&&(u!==0||1/u===1/f)||u!==u&&f!==f}var QV=typeof Object.is==="function"?Object.is:UV,qV=$y.useState,NV=$y.useEffect,WV=$y.useLayoutEffect,wV=$y.useDebugValue;function LV(u,f){var l=f(),r=qV({inst:{value:l,getSnapshot:f}}),n=r[0].inst,i=r[1];return WV(function(){n.value=l,n.getSnapshot=f,r9(n)&&i({inst:n})},[u,l,f]),NV(function(){return r9(n)&&i({inst:n}),u(function(){r9(n)&&i({inst:n})})},[u]),wV(l),l}function r9(u){var f=u.getSnapshot;u=u.value;try{var l=f();return!QV(u,l)}catch(r){return!0}}function KV(u,f){return f()}var GV=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?KV:LV;LN.useSyncExternalStore=$y.useSyncExternalStore!==void 0?$y.useSyncExternalStore:GV});var zN=ur((Yh,GN)=>{GN.exports=KN()});var EN=ur((TN)=>{var e6=Qf(),zV=zN();function TV(u,f){return u===f&&(u!==0||1/u===1/f)||u!==u&&f!==f}var EV=typeof Object.is==="function"?Object.is:TV,ZV=zV.useSyncExternalStore,OV=e6.useRef,HV=e6.useEffect,BV=e6.useMemo,VV=e6.useDebugValue;TN.useSyncExternalStoreWithSelector=function(u,f,l,r,n){var i=OV(null);if(i.current===null){var y={hasValue:!1,value:null};i.current=y}else y=i.current;i=BV(function(){function $(U){if(!c){if(c=!0,A=U,U=r(U),n!==void 0&&y.hasValue){var Q=y.value;if(n(Q,U))return j=Q}return j=U}if(Q=j,EV(A,U))return Q;var w=r(U);if(n!==void 0&&n(Q,w))return A=U,Q;return A=U,j=w}var c=!1,A,j,F=l===void 0?null:l;return[function(){return $(f())},F===null?void 0:function(){return $(F())}]},[f,l,r,n]);var _=ZV(u,i[0],i[1]);return HV(function(){y.hasValue=!0,y.value=_},[_]),VV(_),_}});var ON=ur((mh,ZN)=>{ZN.exports=EN()});var kn=Mu(Qf(),1);var it="北京时间";var OK={timeZone:"Asia/Shanghai",hour12:!1},HK={timeZone:"Asia/Shanghai",hour12:!1},BK=new Intl.DateTimeFormat("en-CA",{timeZone:"Asia/Shanghai",year:"numeric",month:"2-digit",day:"2-digit",hour:"2-digit",minute:"2-digit",hourCycle:"h23"});function s8(u){if(u===null||u===void 0||u==="")return null;let f=u instanceof Date?u:new Date(u);return Number.isNaN(f.getTime())?null:f}function H7(u){let f=s8(u);if(!f)return null;return BK.formatToParts(f).reduce((l,r)=>{if(r.type!=="literal")l[r.type]=r.value;return l},{})}function Nu(u){let f=s8(u);return f?f.toLocaleString("zh-CN",OK):"--"}function $f(u){let f=s8(u);return f?f.toLocaleTimeString("zh-CN",HK):"--"}function a8(u){let f=H7(u);if(!f)return"";let l=f.hour==="24"?"00":f.hour;return`${f.year}-${f.month}-${f.day}T${l}:${f.minute}`}function B7(u=new Date){let f=H7(u);if(!f)return"";return`${f.year}-${f.month}-${f.day}`}function V7(u){if(!u)return null;let f=/^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2})(?::(\d{2}))?$/.exec(u);if(!f)return null;let[,l,r,n,i,y,_="00"]=f,$=Date.UTC(Number(l),Number(r)-1,Number(n),Number(i)-8,Number(y),Number(_)),c=new Date($),A=a8(c);return Number.isNaN(c.getTime())||A!==`${l}-${r}-${n}T${i}:${y}`?null:c.toISOString()}var dw=Mu(kJ(),1);var E3=Mu(Qf(),1);var IJ=Mu(Qf(),1),G_=IJ.default.createElement;function Yz({active:u=!0,label:f="正在加载"}){if(!u)return null;return G_("span",{className:"loading-spinner-indicator",role:"status","aria-label":f,title:f,"data-testid":"loading-title-indicator"},G_("span",{className:"loading-spinner-ring","aria-hidden":!0}))}function nf({title:u,children:f,loading:l,level:r=2,className:n="",label:i="正在加载"}){return G_(r===3?"h3":"h2",{className:`loading-title ${l?"is-loading":""} ${n}`.trim()},G_("span",{className:"loading-title-text"},f??u),G_(Yz,{active:Boolean(l),label:i}))}class Hi extends Error{unideskRequestError=!0;meta;constructor(u,f){super(u);this.name="UniDeskRequestError",this.meta=f}}function Sz(u){return new Promise((f)=>setTimeout(f,u))}function E_(u,f="操作失败"){return u instanceof Error?u.message:String(u||f)}function z3(u,f=500){if(u===null||u===void 0)return"";let l=typeof u==="string"?u:JSON.stringify(u),r=String(l||"").replace(/\s+/gu," ").trim();return r.length>f?`${r.slice(0,f)}...`:r}function mz(u){try{let f=typeof location<"u"&&location.origin?location.origin:"http://localhost";return new URL(u,f).toString()}catch{return u}}function gJ(u){return String(u.method||"GET").toUpperCase()}function pz(u){if(u===null||u===void 0)return!1;if(typeof u!=="object")return!1;if(typeof Blob<"u"&&u instanceof Blob)return!1;if(typeof FormData<"u"&&u instanceof FormData)return!1;if(typeof URLSearchParams<"u"&&u instanceof URLSearchParams)return!1;if(typeof ArrayBuffer<"u"&&u instanceof ArrayBuffer)return!1;return!0}function sJ(u){let f=new Headers(u.headers||{}),l=pz(u.body)?JSON.stringify(u.body):u.body;if(l&&!f.has("content-type")&&typeof l==="string")f.set("content-type","application/json");return{...u,credentials:u.credentials||"same-origin",body:l,headers:f}}function aJ(u){if(u?.error&&typeof u.error==="object"&&typeof u.error.message==="string")return u.error.message;if(typeof u?.error==="string")return u.error;if(typeof u?.message==="string")return u.message;if(typeof u?.detail==="string")return u.detail;return""}function Pz(u,f){if(!u||typeof u!=="object"||Array.isArray(u))return!1;return f.some((l)=>l!==!1&&u[l]===!1)}function z_(u,f,l,r,n={}){return{kind:u,method:l,url:mz(f),occurredAt:r.toISOString(),...n}}function T_(u,f){if(!u)return"请求失败";return`HTTP ${u}${f?` ${f}`:""}`}function oJ(u){try{return{body:u?JSON.parse(u):null,parseError:""}}catch(f){return{body:{text:u},parseError:E_(f,"parse failed")}}}async function Eu(u,f={},l=0){let{failureFields:r=["ok"],strictJson:n=!1,retryInvalidJson:i=0,retryDelayMs:y=120,invalidJsonPrefix:_="服务返回了无效 JSON",invalidJsonPreview:$=!1,responsePreviewLength:c=500,...A}=f,j=gJ(A),F=new Date,U;try{U=await fetch(u,sJ(A))}catch(L){let J=E_(L,"网络请求失败");throw new Hi(J,z_("network",u,j,F,{upstreamMessage:J}))}let Q=await U.text(),w=oJ(Q);if(w.parseError){if(n&&j==="GET"&&l=300)?T_(u.meta.status,u.meta.statusText):"应用请求失败",i=u.meta.status?T_(u.meta.status):"",y=($)=>!$||$===n||$===i,_=!y(u.message)?u.message:y(u.meta.upstreamMessage)?"":u.meta.upstreamMessage||"";return{title:n,message:_,status:u.meta.status,statusText:u.meta.statusText,method:u.meta.method,url:u.meta.url,occurredAt:Mz(u.meta.occurredAt),responsePreview:u.meta.responsePreview,parseError:u.meta.parseError,structured:!0}}let r=E_(u,f).split(/\r?\n/u);return{title:r[0]||f,message:r.slice(1).join(` +`),structured:r.length>1}}function Cz(u,f="操作失败"){let l=Jc(u,f),r=[l.title];if(l.message)r.push(`原因: ${l.message}`);if(l.method||l.url)r.push(`请求: ${[l.method,l.url].filter(Boolean).join(" ")}`);if(l.status)r.push(`状态: ${T_(l.status,l.statusText)}`);if(l.occurredAt)r.push(`时间: ${l.occurredAt}`);if(l.parseError)r.push(`解析错误: ${l.parseError}`);if(l.responsePreview&&l.responsePreview!==l.message)r.push(`响应预览: ${l.responsePreview}`);return r.filter(Boolean).join(` +`)}function Hu(u,f="操作失败"){return eJ(u)?Cz(u,f):E_(u,f)}var uU=Mu(Qf(),1);var Nn=uU.default.createElement;function Z_(u,f){return f?[Nn("dt",{key:`${u}-label`},u),Nn("dd",{key:u},f)]:null}function yf({error:u,wide:f=!1,fallback:l="操作失败",className:r=""}){if(!u)return null;let n=Jc(u,l),i=[Z_("请求",[n.method,n.url].filter(Boolean).join(" ")),Z_("状态",n.status?`HTTP ${n.status}${n.statusText?` ${n.statusText}`:""}`:""),Z_("时间",n.occurredAt),Z_("解析错误",n.parseError),Z_("响应预览",n.responsePreview)].filter(Boolean);return Nn("div",{className:`form-error unidesk-error${f?" wide":""}${r?` ${r}`:""}`,role:"alert","data-testid":"unidesk-error"},Nn("div",{className:"unidesk-error-title"},Nn("strong",null,n.title),n.status?Nn("span",{className:"unidesk-error-code"},`HTTP ${n.status}`):null),n.message?Nn("pre",{className:"unidesk-error-message"},n.message):null,i.length>0?Nn("dl",{className:"unidesk-error-details"},i):null)}var l0=Mu(Qf(),1),fU=l0.default.createContext(null);function lU({children:u}){let[f,l]=l0.default.useState([]),[r,n]=l0.default.useState(Date.now()),i=l0.default.useCallback((j,F)=>{let Q={id:`notif_${Date.now()}_${Math.random().toString(36).slice(2,8)}`,type:j,message:F,timestamp:Date.now()};l((w)=>{let L=[...w,Q];if(L.length>50)return L.slice(-50);return L})},[]),y=l0.default.useCallback((j)=>{l((F)=>F.filter((U)=>U.id!==j))},[]),_=l0.default.useCallback(()=>{l([]),n(Date.now())},[]),$=l0.default.useMemo(()=>{return f.filter((j)=>j.timestamp>r).length},[f,r]),c=$>0,A={notifications:f,addNotification:i,removeNotification:y,clearNotifications:_,unreadCount:$,hasUnread:c};return Rz(fU.Provider,{value:A},u)}var Rz=l0.default.createElement;function Bl(){let u=l0.default.useContext(fU);if(!u)throw Error("useNotification must be used within NotificationProvider");return u}var R=E3.default.createElement,{useEffect:T3}=E3.default,Bi=E3.default.useState;function Nl(u,f={}){return Eu(u,{failureFields:["ok","success"],...f})}function Vl(u,f){return`${u}/microservices/baidu-netdisk/proxy${f}`}function xz(u){let f=Number(u);return Number.isFinite(f)?f.toLocaleString("zh-CN"):"--"}function Wn(u){let f=Number(u);if(!Number.isFinite(f)||f<=0)return"--";let l=["B","KB","MB","GB","TB"],r=f,n=0;while(r>=1024&&n{n?.stopPropagation?.(),l(u,f)}},"查看原始JSON")}function Di({title:u,text:f}){return R("div",{className:"empty-state"},R("strong",null,u),R("span",null,f))}function Xi({title:u,text:f,href:l,badge:r,testId:n}){return R("a",{className:"doc-link-card",href:l,target:"_blank",rel:"noreferrer","data-testid":n},R("span",null,r||"DOC"),R("strong",null,u),R("p",null,f),R("code",null,l))}function hz(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function bz(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function vz(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function kz(u){return Array.isArray(u?.files)?u.files:[]}function Iz(u){return Array.isArray(u?.jobs)?u.jobs:[]}function gz(u,f){if(!u||u===f)return f;let l=u.replace(/\/+$/u,""),r=l.slice(0,l.lastIndexOf("/"))||f;return r.lengthx.id==="baidu-netdisk")||null,[n,i]=Bi({loading:!1,actionLoading:!1,error:"",message:"",health:null,account:null,files:null,transfers:null,logs:null,selfTest:null,refreshedAt:null}),[y,_]=Bi("/"),[$,c]=Bi(null),[A,j]=Bi(""),[F,U]=Bi({localPath:"sample.txt",remotePath:"/sample.txt"}),[Q,w]=Bi({fsId:"",localPath:"downloads/"}),{addNotification:L}=Bl(),J=n.health?.baidu?.appRoot||n.account?.rootPath||"/";T3(()=>{U((x)=>{let fu=new Set(["/sample.txt","/apps/UniDeskBaiduNetdisk/sample.txt"]);if(x.remotePath&&!fu.has(x.remotePath))return x;let $u=Uc(J,"sample.txt");return x.remotePath===$u?x:{...x,remotePath:$u}})},[J]);async function N(x=y){let $u=await Nl(Vl(l,`/api/files?dir=${encodeURIComponent(x||J)}&limit=100`));i((tu)=>({...tu,files:$u}))}async function q(){let x=await Nl(Vl(l,"/api/transfers?limit=80"));i((fu)=>({...fu,transfers:x}))}async function W(){if(!r)return;i((x)=>({...x,loading:!0,error:"",message:""}));try{let x=await Nl(`${l}/microservices/baidu-netdisk/health`),fu=x?.baidu?.appRoot||J,$u=null,tu=null;if(x?.auth?.loggedIn){$u=await Nl(Vl(l,"/api/account?refresh=1"));let qu=y&&y.startsWith(fu)?y:fu;_(qu),tu=await Nl(Vl(l,`/api/files?dir=${encodeURIComponent(qu)}&limit=100`))}else _(fu);let ju=await Nl(Vl(l,"/api/transfers?limit=80")),Gu=await Nl(Vl(l,"/logs?limit=60"));i((qu)=>({...qu,loading:!1,health:x,account:$u?.account||null,files:tu,transfers:ju,logs:Gu,refreshedAt:new Date}))}catch(x){i((fu)=>({...fu,loading:!1,error:Hu(x,"百度网盘服务加载失败")}))}}async function z(){i((x)=>({...x,actionLoading:!0,error:"",message:""}));try{let x=await Nl(Vl(l,"/api/auth/device/start"),{method:"POST",body:{}});c(x.session||null),i((fu)=>({...fu,actionLoading:!1,message:"设备码已生成,请扫码授权"}))}catch(x){i((fu)=>({...fu,actionLoading:!1,error:Hu(x,"创建设备码失败")}))}}async function Z(x=!1){if(!$?.id)return;if(x)i((fu)=>({...fu,actionLoading:!0,error:""}));try{let fu=await Nl(Vl(l,`/api/auth/device/status?sessionId=${encodeURIComponent($.id)}`));if(c(fu.session||null),fu.session?.status==="succeeded")i(($u)=>({...$u,actionLoading:!1,message:"授权成功,正在刷新账号与文件列表"})),await W();else if(x)i(($u)=>({...$u,actionLoading:!1}))}catch(fu){i(($u)=>({...$u,actionLoading:!1,error:Hu(fu,"轮询登录状态失败")}))}}async function H(){i((x)=>({...x,actionLoading:!0,error:"",message:""}));try{await Nl(Vl(l,"/api/auth/logout"),{method:"POST",body:{}}),c(null),i((x)=>({...x,actionLoading:!1,account:null,files:null,message:"本地 token 已清除"})),await W()}catch(x){i((fu)=>({...fu,actionLoading:!1,error:Hu(x,"退出登录失败")}))}}async function E(x){x.preventDefault();let fu=A.trim();if(!fu)return;i(($u)=>({...$u,actionLoading:!0,error:"",message:""}));try{await Nl(Vl(l,"/api/folders"),{method:"POST",body:{path:Uc(y,fu)}}),j(""),i(($u)=>({...$u,actionLoading:!1,message:"文件夹已创建"})),await N(y)}catch($u){i((tu)=>({...tu,actionLoading:!1,error:Hu($u,"创建文件夹失败")}))}}async function D(x){if(!x)return;i((fu)=>({...fu,actionLoading:!0,error:"",message:""}));try{await Nl(Vl(l,"/api/files/manage"),{method:"POST",body:{opera:"delete",filelist:[{path:x}],async:1}}),i((fu)=>({...fu,actionLoading:!1,message:"删除任务已提交"})),await N(y)}catch(fu){i(($u)=>({...$u,actionLoading:!1,error:Hu(fu,"删除失败")}))}}async function h(x){x.preventDefault(),i((fu)=>({...fu,actionLoading:!0,error:"",message:""}));try{await Nl(Vl(l,"/api/transfers/upload-from-path"),{method:"POST",body:F}),i((fu)=>({...fu,actionLoading:!1,message:"上传任务已入队"})),await q()}catch(fu){i(($u)=>({...$u,actionLoading:!1,error:Hu(fu,"上传任务创建失败")}))}}async function B(x){x.preventDefault(),i((fu)=>({...fu,actionLoading:!0,error:"",message:""}));try{await Nl(Vl(l,"/api/transfers/download-to-path"),{method:"POST",body:Q}),i((fu)=>({...fu,actionLoading:!1,message:"下载任务已入队"})),await q()}catch(fu){i(($u)=>({...$u,actionLoading:!1,error:Hu(fu,"下载任务创建失败")}))}}async function X(x,fu){i(($u)=>({...$u,actionLoading:!0,error:"",message:""}));try{await Nl(Vl(l,`/api/transfers/${encodeURIComponent(x)}/${fu}`),{method:"POST",body:{}}),i(($u)=>({...$u,actionLoading:!1,message:fu==="cancel"?"已请求取消任务":"任务已重新入队"})),await q()}catch($u){i((tu)=>({...tu,actionLoading:!1,error:Hu($u,"任务操作失败")}))}}async function m(){i((x)=>({...x,actionLoading:!0,error:"",message:"正在运行上传/下载自测..."}));try{let x=await Nl(Vl(l,"/api/self-test"),{method:"POST",body:{}});i((fu)=>({...fu,actionLoading:!1,selfTest:x,message:`上传/下载自测通过:${x.remotePath||""}`})),await N(y),await q()}catch(x){i((fu)=>({...fu,actionLoading:!1,error:Hu(x,"上传/下载自测失败")}))}}if(T3(()=>{if(!r)return;W();return},[r?.id,r?.runtime?.providerStatus]),T3(()=>{if(!$?.id||$.status!=="pending")return;let x=window.setInterval(()=>void Z(!1),Math.max(5000,Number($.pollIntervalSeconds||5)*1000));return()=>window.clearInterval(x)},[$?.id,$?.status,$?.pollIntervalSeconds]),T3(()=>{if(!r)return;let x=window.setInterval(()=>void q(),5000);return()=>window.clearInterval(x)},[r?.id]),!r)return R(Di,{title:"Baidu Netdisk 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=baidu-netdisk"});let O=hz(r),p=vz(r),Y=bz(r),v=n.health||{},T=n.account||v.auth?.account||null,S=v.auth||{},k=kz(n.files),I=Iz(n.transfers),b=T?.quota||{},o=Boolean(S.loggedIn||T),s=Boolean(S.configured);return R("div",{className:"baidu-netdisk-page","data-testid":"baidu-netdisk-page"},R(Q1,{title:"Baidu Netdisk 工作台",eyebrow:"Containerized Storage Gateway",loading:n.loading,actions:R("div",{className:"panel-actions"},R("a",{className:"ghost-btn",href:"/docs/issue/baidu-netdisk-env-setup.md",target:"_blank",rel:"noreferrer","data-testid":"baidu-netdisk-config-doc-link"},"配置文档"),R("button",{type:"button",className:"ghost-btn",onClick:W,disabled:n.loading,"data-testid":"baidu-netdisk-refresh"},n.loading?"刷新中":"刷新"),R(H0,{title:"Baidu Netdisk 用户服务",data:r,onOpen:f,testId:"raw-baidu-netdisk-service"}))},R("div",{className:"baidu-netdisk-hero"},R("div",null,R("div",{className:"node-version-line"},R(U1,{status:O.providerStatus==="online"?"online":"warn"},O.providerStatus||"unknown"),R("span",null,r.providerId),R(U1,{status:Y.public?"warn":"private"},Y.public?"公网暴露":"仅 UniDesk frontend 代理访问")),R("p",{className:"muted paragraph"},r.description)),R("div",{className:"microservice-ref-card"},R("span",null,"Repo"),R("strong",null,p.url||"--"),R("code",null,p.commitId||"--")),R("div",{className:"microservice-ref-card"},R("span",null,"Private Backend"),R("strong",null,`${Y.nodeBindHost||"--"}:${Y.nodePort||"--"}`),R("code",null,`${p.composeFile||"--"} / ${p.composeService||"--"}`))),R(yf,{error:n.error,wide:!0})),R("div",{className:"metric-grid"},R(Vi,{label:"Health",value:v.ok?"OK":"--",hint:v.storage?.postgres||"postgres",tone:v.ok?"ok":"warn"}),R(Vi,{label:"OAuth",value:s?"已配置":"待配置",hint:s?"client + secret + token key":"需要设置 UNIDESK_BAIDU_NETDISK_*",tone:s?"ok":"warn"}),R(Vi,{label:"Login",value:o?"已登录":"未登录",hint:T?.username||"Device Code QR",tone:o?"ok":"warn"}),R(Vi,{label:"Work Root",value:sz(J),hint:J}),R(Vi,{label:"Quota",value:Wn(b.used),hint:b.total?`${b.usedPercent||0}% / ${Wn(b.total)}`:"授权后刷新"}),R(Vi,{label:"Transfers",value:xz(I.length),hint:`running ${n.transfers?.counts?.running||0} / failed ${n.transfers?.counts?.failed||0}`})),R(Q1,{title:"文件浏览器",eyebrow:y,className:"baidu-files-panel",loading:n.loading,actions:R("div",{className:"panel-actions inline-actions"},R("button",{type:"button",className:"ghost-btn",onClick:()=>{let x=gz(y,J);_(x),N(x)},disabled:!o||y===J},"上级"),R("button",{type:"button",className:"ghost-btn",onClick:()=>N(y),disabled:!o},"刷新文件"),R(H0,{title:"Baidu Files",data:n.files,onOpen:f,testId:"raw-baidu-files"}))},R("form",{className:"baidu-pathbar",onSubmit:(x)=>{x.preventDefault(),N(y)}},R("input",{value:y,onChange:(x)=>_(x.target.value),disabled:!o}),R("button",{type:"submit",className:"ghost-btn",disabled:!o},"打开路径")),R("form",{className:"baidu-pathbar",onSubmit:E},R("input",{value:A,onChange:(x)=>j(x.target.value),placeholder:"新文件夹名称",disabled:!o}),R("button",{type:"submit",className:"primary-btn",disabled:!o||!A.trim()},"新建文件夹")),!o?R(Di,{title:"等待授权",text:"登录后通过 /api/files 读取工作目录文件列表"}):k.length===0?R(Di,{title:"目录为空",text:"可以从 staging 目录上传文件或新建文件夹"}):R("div",{className:"table-wrap","data-testid":"baidu-netdisk-file-table"},R("table",null,R("thead",null,R("tr",null,R("th",null,"名称"),R("th",null,"类型"),R("th",null,"大小"),R("th",null,"修改时间"),R("th",null,"fs_id"),R("th",null,"操作"))),R("tbody",null,k.map((x)=>R("tr",{key:x.fsId||x.path},R("td",null,R("strong",null,x.serverFilename||x.path),R("code",null,x.path||"--")),R("td",null,R(U1,{status:x.isDir?"queued":"private"},x.isDir?"DIR":"FILE")),R("td",null,x.isDir?"--":Wn(x.size)),R("td",null,x.serverMtime?Nu(x.serverMtime*1000):"--"),R("td",null,R("code",null,x.fsId||"--")),R("td",null,R("div",{className:"inline-actions"},x.isDir?R("button",{type:"button",className:"ghost-btn",onClick:()=>{_(x.path),N(x.path)}},"打开"):R("button",{type:"button",className:"ghost-btn",onClick:()=>w((fu)=>({...fu,fsId:x.fsId}))},"填入下载"),R("button",{type:"button",className:"ghost-btn",onClick:()=>D(x.path),disabled:n.actionLoading},"删除"))))))))),R("div",{className:"baidu-netdisk-grid"},R(Q1,{title:"配置与文档",eyebrow:"Deployment References",className:"baidu-docs-panel",actions:R("div",{className:"panel-actions inline-actions"},R("a",{className:"ghost-btn",href:"/docs/issue/baidu-netdisk-env-setup.md",target:"_blank",rel:"noreferrer"},"打开环境配置"),R("a",{className:"ghost-btn",href:"/docs/issue/baidu-netdisk-user-service.md",target:"_blank",rel:"noreferrer"},"打开服务方案"))},R("p",{className:"muted paragraph"},s?"OAuth 运行时变量已配置;如需轮换密钥、迁移部署或排查代理边界,可直接打开下面的项目内文档。":"首次使用请先按环境变量配置文档填入百度应用 client id / secret,然后重建 baidu-netdisk 服务并刷新本页。"),R("div",{className:"baidu-doc-grid","data-testid":"baidu-netdisk-doc-links"},R(Xi,{title:"环境变量配置",text:"填写 UNIDESK_BAIDU_NETDISK_CLIENT_ID、CLIENT_SECRET、TOKEN_KEY,并执行重建与健康检查。",href:"/docs/issue/baidu-netdisk-env-setup.md",badge:"SETUP",testId:"baidu-netdisk-env-doc-card"}),R(Xi,{title:"服务方案与 API",text:"说明 OAuth Device Code、根目录工作区、staging 上传下载任务和后端 API 设计。",href:"/docs/issue/baidu-netdisk-user-service.md",badge:"DESIGN"}),R(Xi,{title:"用户服务安全边界",text:"查看 UniDesk microservice 私有代理、允许路径、frontendOnly 和密钥边界规则。",href:"/docs/reference/microservices.md",badge:"REF"}),R(Xi,{title:"部署与重建流程",text:"查看 server rebuild、Compose 编排、健康检查和交付验证的长期规则。",href:"/docs/reference/deployment.md",badge:"DEPLOY"}),R(Xi,{title:"CLI 验证命令",text:"查看 microservice health/proxy、server rebuild、job status 等命令入口。",href:"/docs/reference/cli.md",badge:"CLI"}),R(Xi,{title:"百度设备码模式",text:"打开百度官方 OAuth Device Code 文档,对照扫码登录和轮询参数。",href:"https://pan.baidu.com/union/doc/fl1x114ti",badge:"OFFICIAL"}))),R(Q1,{title:"设备码登录",eyebrow:"OAuth Device Code",className:"baidu-login-panel",loading:n.actionLoading,actions:R("div",{className:"panel-actions inline-actions"},R("button",{type:"button",className:"primary-btn",onClick:z,disabled:n.actionLoading||!s,"data-testid":"baidu-netdisk-start-login"},"生成二维码"),$?.id?R("button",{type:"button",className:"ghost-btn",onClick:()=>Z(!0),disabled:n.actionLoading},"检查状态"):null,o?R("button",{type:"button",className:"ghost-btn",onClick:H,disabled:n.actionLoading},"清除本地登录"):null,R(H0,{title:"Baidu Device Session",data:$||S.latestSession,onOpen:f,testId:"raw-baidu-device-session"}))},R("div",{className:"baidu-login-card","data-testid":"baidu-netdisk-login-card"},R("div",{className:"baidu-qr-frame"},$?.qrcodeUrl?R("img",{src:$.qrcodeUrl,alt:"百度网盘设备码授权二维码","data-testid":"baidu-netdisk-qrcode"}):R(Di,{title:s?"等待二维码":"OAuth 未配置",text:s?"点击生成二维码后使用百度网盘或百度 App 扫码":"设置 client id、secret 和 token key 后重建服务"})),R("div",{className:"claudeqq-login-copy"},R("div",{className:"node-version-line"},R(U1,{status:o?"online":$?.status==="pending"?"warn":"unknown"},o?"已登录":$?.status||"未开始"),R("span",null,$?.secondsRemaining!==void 0?`${$.secondsRemaining}s`:"--"),R("span",null,"scope basic,netdisk")),R("p",{className:"muted paragraph"},o?"access token / refresh token 已加密保存到 PostgreSQL;前端只看到脱敏登录态。":"后端使用百度 OAuth Device Code 轮询换取 token;二维码过期后重新生成即可。"),R("div",{className:"microservice-ref-card"},R("span",null,"User Code"),R("strong",null,$?.userCode||"--"),R("code",null,$?.verificationUrl||"https://openapi.baidu.com/device")),R("div",{className:"microservice-ref-card"},R("span",null,"Expires"),R("strong",null,$?.expiresAt?Nu($.expiresAt):"--"),R("code",null,$?.error||"no token exposed"))))),R(Q1,{title:"账号与容量",eyebrow:n.refreshedAt?`Updated ${$f(n.refreshedAt)}`:"Account",loading:n.loading,actions:R("div",{className:"panel-actions inline-actions"},R(H0,{title:"Baidu Account",data:T,onOpen:f,testId:"raw-baidu-account"}))},T?R("div",{className:"baidu-account-card"},R("div",{className:"node-version-line"},R(U1,{status:"online"},"connected"),R("span",null,T.baiduUid||"--"),R("span",null,`VIP ${T.vipType??"--"}`)),R("h3",null,T.username||"Baidu Netdisk"),R("p",{className:"muted paragraph"},`工作目录固定在 ${T.rootPath||J};v1 上传/下载只读写容器 staging 目录,不把大文件字节流穿过 UniDesk proxy。`),R("div",{className:"quota-bar"},R("span",{style:{width:`${Math.max(0,Math.min(100,Number(b.usedPercent||0)))}%`}})),R("div",{className:"microservice-ref-card"},R("span",null,"Quota"),R("strong",null,`${Wn(b.used)} / ${Wn(b.total)}`),R("code",null,`${b.usedPercent||0}% used`))):R(Di,{title:"尚未登录",text:"扫码授权后这里会显示账号、UID、会员状态和容量"})),R(Q1,{title:"传输任务",eyebrow:"staging path jobs",className:"baidu-transfers-panel",loading:n.actionLoading,actions:R("div",{className:"panel-actions inline-actions"},R("button",{type:"button",className:"primary-btn",onClick:m,disabled:!o||n.actionLoading,"data-testid":"baidu-netdisk-self-test"},"运行自测"),R("button",{type:"button",className:"ghost-btn",onClick:q},"刷新任务"),R(H0,{title:"Baidu Transfers",data:n.transfers,onOpen:f,testId:"raw-baidu-transfers"}))},R("div",{className:"baidu-transfer-forms"},R("form",{className:"stack-form",onSubmit:h,"data-testid":"baidu-upload-form"},R("label",null,"容器 staging 文件",R("input",{value:F.localPath,onChange:(x)=>U((fu)=>({...fu,localPath:x.target.value})),placeholder:"sample.txt"})),R("label",null,"百度网盘目标路径",R("input",{value:F.remotePath,onChange:(x)=>U((fu)=>({...fu,remotePath:x.target.value})),placeholder:Uc(J,"sample.txt")})),R("button",{type:"submit",className:"primary-btn",disabled:!o||n.actionLoading},"上传 staging 文件")),R("form",{className:"stack-form",onSubmit:B,"data-testid":"baidu-download-form"},R("label",null,"文件 fs_id",R("input",{value:Q.fsId,onChange:(x)=>w((fu)=>({...fu,fsId:x.target.value})),placeholder:"从文件表填入"})),R("label",null,"保存到 staging 路径",R("input",{value:Q.localPath,onChange:(x)=>w((fu)=>({...fu,localPath:x.target.value})),placeholder:"downloads/"})),R("button",{type:"submit",className:"primary-btn",disabled:!o||!Q.fsId||n.actionLoading},"下载到 staging"))),n.selfTest?R("div",{className:"baidu-account-card","data-testid":"baidu-netdisk-self-test-result"},R("div",{className:"node-version-line"},R(U1,{status:n.selfTest.ok?"online":"warn"},n.selfTest.ok?"self-test ok":"self-test"),R("span",null,Wn(n.selfTest.sizeBytes))),R("h3",null,n.selfTest.remotePath||"Baidu self-test"),R("div",{className:"microservice-ref-card"},R("span",null,"fs_id"),R("strong",null,n.selfTest.fsId||"--"),R("code",null,n.selfTest.downloadedPath||"--")),R("div",{className:"microservice-ref-card"},R("span",null,"MD5"),R("strong",null,n.selfTest.downloadedMd5||"--"),R("code",null,n.selfTest.expectedMd5||"--")),R(H0,{title:"Baidu Self Test",data:n.selfTest,onOpen:f,testId:"raw-baidu-self-test"})):null,I.length===0?R(Di,{title:"暂无传输任务",text:"上传/下载任务会在后端容器内执行,避免大文件穿过 UniDesk proxy"}):R("div",{className:"table-wrap","data-testid":"baidu-transfer-table"},R("table",null,R("thead",null,R("tr",null,R("th",null,"状态"),R("th",null,"方向"),R("th",null,"路径"),R("th",null,"进度"),R("th",null,"时间"),R("th",null,"操作"))),R("tbody",null,I.map((x)=>R("tr",{key:x.id},R("td",null,R(U1,{status:x.status},x.status)),R("td",null,x.direction),R("td",null,R("strong",null,x.remotePath||x.fsId||"--"),R("code",null,x.localPath||"--"),x.error?R("span",{className:"form-error"},x.error):null),R("td",null,R(az,{percent:x.progressPercent}),R("span",{className:"muted"},`${Wn(x.bytesDone)} / ${Wn(x.sizeBytes)}`)),R("td",null,Nu(x.updatedAt)),R("td",null,R("div",{className:"inline-actions"},["queued","running"].includes(x.status)?R("button",{type:"button",className:"ghost-btn",onClick:()=>X(x.id,"cancel")},"取消"):null,["failed","canceled"].includes(x.status)?R("button",{type:"button",className:"ghost-btn",onClick:()=>X(x.id,"retry")},"重试"):null,R(H0,{title:`Transfer ${x.id}`,data:x,onOpen:f}))))))))),R(Q1,{title:"安全与日志",eyebrow:"redacted diagnostics",className:"baidu-wide-panel",loading:n.loading,actions:R("div",{className:"panel-actions inline-actions"},R(H0,{title:"Baidu Health",data:v,onOpen:f,testId:"raw-baidu-health"}),R(H0,{title:"Baidu Logs",data:n.logs,onOpen:f,testId:"raw-baidu-logs"}))},R("div",{className:"policy-grid"},R("article",null,R("b",null,"私有后端"),R("span",null,"4244 只在 Compose 网络 expose,浏览器经 UniDesk 同源代理访问")),R("article",null,R("b",null,"Token 加密"),R("span",null,"access/refresh token 使用 BAIDU_NETDISK_TOKEN_KEY 加密后写入 PostgreSQL")),R("article",null,R("b",null,"无浏览器大文件流"),R("span",null,"上传/下载以容器 staging 目录为边界,避免 proxy 文本通道传输大字节流"))))))}var H3=Mu(Qf(),1);var a=H3.default.createElement,{useEffect:oz}=H3.default,Z3=H3.default.useState,q1={label:"主用户私聊账号",userId:645275593};function Qc(u){let f=Number(u);return Number.isFinite(f)?f.toLocaleString("zh-CN"):"--"}async function wn(u,f={}){return Eu(u,{failureFields:["ok","success"],...f})}async function dz(u){let f=await fetch(u,{credentials:"same-origin"}),l=await f.text();try{return l?JSON.parse(l):{ok:f.ok,status:f.status}}catch{return{ok:f.ok,status:f.status,text:l}}}function O3({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return a("span",{className:`status-badge ${l}`},f||u||"unknown")}function Yi({label:u,value:f,hint:l,tone:r}){return a("article",{className:`metric-card ${r||""}`},a("div",{className:"metric-label"},u),a("div",{className:"metric-value"},f),a("div",{className:"metric-hint"},l))}function Si({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return a("section",{className:`panel ${n||""}`},a("div",{className:"panel-head"},a("div",null,f?a("p",{className:"panel-eyebrow"},f):null,a(nf,{title:u,loading:i})),l?a("div",{className:"panel-actions"},l):null),a("div",{className:"panel-body"},r))}function O_({title:u,data:f,onOpen:l,testId:r}){return a("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:(n)=>{n?.stopPropagation?.(),l(u,f)}},"查看原始JSON")}function H_({title:u,text:f}){return a("div",{className:"empty-state"},a("strong",null,u),a("span",null,f))}function ez(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function uT(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function fT(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function Ln(u,f){return`${u}/microservices/claudeqq/proxy${f}`}function lT(u){return Array.isArray(u?.events)?u.events.slice(0,80):[]}function rT(u){return Array.isArray(u?.subscriptions)?u.subscriptions.slice(0,50):[]}function nT(u){return Array.isArray(u?.messages)?u.messages.slice(0,30):[]}function nU(u){let f=u?.text??u?.message??u?.raw?.raw_message;if(typeof f!=="string")return"--";return f.length>180?`${f.slice(0,177)}...`:f}function iU(u){let f=u?.groupId??u?.group_id??(u?.message_type==="group"?u?.target_id:void 0),l=u?.userId??u?.user_id??(u?.message_type==="private"?u?.target_id:void 0);if(f)return`群 ${f}`;if(l)return`私聊 ${l}`;return"--"}function yU({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((T)=>T.id==="claudeqq")||null,[n,i]=Z3({loading:!1,qrLoading:!1,error:"",health:null,status:null,napcatLogin:null,napcatQrcode:null,qrcodeFetched:!1,qrcodeRefreshedAt:null,events:null,subscriptions:null,sent:null,refreshedAt:null}),[y,_]=Z3({targetType:"private",targetId:String(q1.userId),message:""}),[$,c]=Z3({name:"unidesk-callback",targetUrl:"",eventTypes:"message",secret:""}),[A,j]=Z3(""),{addNotification:F}=Bl();async function U(){if(!r)return;i((T)=>({...T,loading:!0,error:""}));try{let[T,S,k,I,b]=await Promise.all([dz(`${l}/microservices/claudeqq/health`),wn(Ln(l,"/api/server/status")),wn(Ln(l,"/api/events/recent?limit=60")),wn(Ln(l,"/api/events/subscriptions")),wn(Ln(l,"/api/messages/sent?limit=20"))]);if(i((o)=>({...o,loading:!1,error:"",health:T,status:S,events:k,subscriptions:I,sent:b,refreshedAt:new Date})),!n.qrcodeFetched)Q(!1)}catch(T){i((S)=>({...S,loading:!1,error:Hu(T,"ClaudeQQ 加载失败")}))}}async function Q(T=!0){if(!r)return;i((S)=>({...S,qrLoading:!0,error:T?"":S.error}));try{let S=await wn(Ln(l,"/api/napcat/login")),k=S?.napcat?.qrcode||S?.qrcode||null;i((I)=>({...I,qrLoading:!1,error:"",napcatLogin:S,napcatQrcode:k,qrcodeFetched:!0,qrcodeRefreshedAt:new Date}))}catch(S){i((k)=>({...k,qrLoading:!1,error:T||!k.napcatQrcode?Hu(S,"NapCat 二维码加载失败"):k.error}))}}async function w(T){T.preventDefault(),j("");let S=Number(y.targetId);if(!Number.isFinite(S)||S<=0||y.message.trim().length===0){i((k)=>({...k,error:"请填写 QQ 目标和消息内容"}));return}try{await wn(Ln(l,"/api/push/text"),{method:"POST",body:JSON.stringify({userId:y.targetType==="private"?S:void 0,groupId:y.targetType==="group"?S:void 0,message:y.message})});let k="消息推送请求已提交";_((I)=>({...I,targetType:"private",targetId:String(q1.userId),message:""})),j(k),F("success",k),await U()}catch(k){i((I)=>({...I,error:Hu(k,"发送失败")}))}}async function L(T){if(T.preventDefault(),j(""),$.targetUrl.trim().length===0){i((S)=>({...S,error:"请填写订阅回调 URL"}));return}try{await wn(Ln(l,"/api/events/subscriptions"),{method:"POST",body:JSON.stringify({name:$.name,targetUrl:$.targetUrl,eventTypes:$.eventTypes.split(",").map((k)=>k.trim()).filter(Boolean),secret:$.secret||void 0,enabled:!0})});let S="事件订阅已创建";j(S),F("success",S),await U()}catch(S){i((k)=>({...k,error:Hu(S,"订阅失败")}))}}async function J(T){if(!T)return;j("");try{await wn(Ln(l,`/api/events/subscriptions/${encodeURIComponent(T)}`),{method:"DELETE"});let S="事件订阅已删除";j(S),F("success",S),await U()}catch(S){i((k)=>({...k,error:Hu(S,"删除订阅失败")}))}}if(oz(()=>{if(!r)return;U();return},[r?.id,r?.runtime?.providerStatus]),!r)return a(H_,{title:"ClaudeQQ 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=claudeqq"});let N=ez(r),q=fT(r),W=uT(r),z=n.health||{},Z=n.status||{},H=n.napcatLogin||{},E=z.napcat||Z.napcat||{},D={...H.napcat||{},...E,qrcode:n.napcatQrcode||{},webui:E.webui||H.napcat?.webui},h=H.login||{},B=n.napcatQrcode||{},X=lT(n.events),m=rT(n.subscriptions),O=nT(n.sent),p=Boolean(D.httpConnected||h.ready),Y=String(D.loginState||h.state||(p?"logged_in":"unknown")),v=Boolean(B.available&&B.dataUrl);return a("div",{className:"claudeqq-page","data-testid":"claudeqq-page"},a(Si,{title:"ClaudeQQ 工作台",eyebrow:"D601 QQ Event Gateway",loading:n.loading,actions:a("div",{className:"panel-actions"},a("button",{type:"button",className:"ghost-btn",onClick:U,disabled:n.loading,"data-testid":"claudeqq-refresh-button"},n.loading?"刷新中":"刷新"),a(O_,{title:"ClaudeQQ 用户服务",data:r,onOpen:f,testId:"raw-claudeqq-service"}))},a("div",{className:"findjob-hero"},a("div",null,a("div",{className:"node-version-line"},a(O3,{status:N.providerStatus==="online"?"online":"warn"},N.providerStatus||"unknown"),a("span",null,r.providerId),a("span",null,W.public?"公网暴露":"仅 UniDesk frontend 代理访问")),a("p",{className:"muted paragraph"},r.description)),a("div",{className:"microservice-ref-card"},a("span",null,"Repo"),a("strong",null,q.url||"--"),a("code",null,q.commitId||"--")),a("div",{className:"microservice-ref-card"},a("span",null,"D601 Docker"),a("strong",null,`${W.nodeBindHost||"--"}:${W.nodePort||"--"}`),a("code",null,`${q.composeFile||"--"} / ${q.composeService||"--"}`))),a(yf,{error:n.error,wide:!0})),a("div",{className:"metric-grid"},a(Yi,{label:"Health",value:z.ok||z.status==="ok"?"OK":"--",hint:"D601 /health",tone:z.ok||z.status==="ok"?"ok":"warn"}),a(Yi,{label:"NapCat HTTP",value:D.httpConnected||D.http?.connected?"OK":"离线",hint:`${D.httpHost||z.napcat?.httpHost||"--"}:${D.httpPort||z.napcat?.httpPort||"--"}`}),a(Yi,{label:"NapCat WS",value:D.wsConnected||D.ws?.connected?"OK":"离线",hint:`${D.wsHost||z.napcat?.wsHost||"--"}:${D.wsPort||z.napcat?.wsPort||"--"}`}),a(Yi,{label:"事件缓存",value:Qc(n.events?.count??X.length),hint:"recent QQ events"}),a(Yi,{label:"订阅",value:Qc(n.subscriptions?.count??m.length),hint:"webhook subscribers"}),a(Yi,{label:"已发送",value:Qc(n.sent?.count??O.length),hint:"sent message log"})),a("div",{className:"findjob-grid"},a(Si,{title:"NapCat 容器登录",eyebrow:"QR Login",className:"claudeqq-login-panel",loading:n.qrLoading,actions:a("div",{className:"panel-actions inline-actions"},a("button",{type:"button",className:"ghost-btn",onClick:()=>Q(!0),disabled:n.qrLoading,"data-testid":"claudeqq-napcat-refresh"},n.qrLoading?"刷新中":"手动刷新二维码"),a(O_,{title:"NapCat Login",data:n.napcatLogin,onOpen:f,testId:"raw-claudeqq-napcat-login"}))},a("div",{className:"claudeqq-login-card","data-testid":"claudeqq-napcat-login"},a("div",{className:"claudeqq-qr-frame"},v?a("img",{src:B.dataUrl,alt:"NapCat QQ 登录二维码","data-testid":"claudeqq-napcat-qrcode"}):a(H_,{title:"等待二维码",text:"NapCat 容器启动后会把登录二维码写入 cache/qrcode.png"})),a("div",{className:"claudeqq-login-copy"},a("div",{className:"node-version-line"},a(O3,{status:p?"online":v?"warn":"unknown"},p?"已登录":v?"待扫码":"等待二维码"),a("span",null,Y),a("span",null,"D601 containerized")),a("p",{className:"muted paragraph"},p?"NapCat 已登录,ClaudeQQ 可通过容器内 HTTP/WS 链路收发 QQ 消息。":"用手机 QQ 扫描二维码授权登录。二维码只在首次加载或手动刷新时更新,D601 的 NapCat 端口仍只绑定 127.0.0.1。"),a("div",{className:"microservice-ref-card"},a("span",null,"NapCat WebUI"),a("strong",null,D.webui?.url||"http://napcat:6099/webui"),a("code",null,"local-only / proxied QR login")),a("div",{className:"microservice-ref-card"},a("span",null,"QR Source"),a("strong",null,B.modifiedAt?Nu(B.modifiedAt):n.qrcodeRefreshedAt?Nu(n.qrcodeRefreshedAt):"--"),a("code",null,B.file||"/napcat/cache/qrcode.png"))))),a(Si,{title:"消息推送",eyebrow:"Push API"},a("div",{className:"microservice-ref-card"},a("span",null,q1.label),a("strong",null,String(q1.userId)),a("code",null,"private userId / 默认推送测试目标")),a("form",{className:"stack-form",onSubmit:w,"data-testid":"claudeqq-push-form"},a("label",null,"目标类型",a("select",{value:y.targetType,onChange:(T)=>_((S)=>({...S,targetType:T.target.value}))},a("option",{value:"private"},"私聊 userId"),a("option",{value:"group"},"群 groupId"))),a("label",null,"QQ ID",a("input",{value:y.targetId,onChange:(T)=>_((S)=>({...S,targetId:T.target.value})),placeholder:String(q1.userId)})),a("label",null,"消息内容",a("textarea",{value:y.message,onChange:(T)=>_((S)=>({...S,message:T.target.value})),rows:4,placeholder:"通过 ClaudeQQ 推送一条 QQ 消息"})),a("button",{type:"submit",className:"primary-btn"},"发送 QQ 消息")),a("p",{className:"muted paragraph"},`主 server 和其他用户服务可通过 UniDesk 同源代理调用 /api/push/text;当前人工推送测试默认使用 ${q1.label} ${q1.userId},不需要暴露 D601 后端端口。`)),a(Si,{title:"QQ 事件订阅",eyebrow:"Webhook Subscription",loading:n.loading},a("form",{className:"stack-form",onSubmit:L,"data-testid":"claudeqq-subscription-form"},a("label",null,"订阅名称",a("input",{value:$.name,onChange:(T)=>c((S)=>({...S,name:T.target.value}))})),a("label",null,"回调 URL",a("input",{value:$.targetUrl,onChange:(T)=>c((S)=>({...S,targetUrl:T.target.value})),placeholder:"http://host.docker.internal:18080/..."})),a("label",null,"事件类型",a("input",{value:$.eventTypes,onChange:(T)=>c((S)=>({...S,eventTypes:T.target.value})),placeholder:"message,notice"})),a("label",null,"签名密钥",a("input",{value:$.secret,onChange:(T)=>c((S)=>({...S,secret:T.target.value})),placeholder:"可选,生成 x-claudeqq-signature"})),a("button",{type:"submit",className:"primary-btn"},"创建订阅")),m.length===0?a(H_,{title:"暂无订阅",text:"可以为 main server 或其他用户服务注册 HTTP webhook"}):a("div",{className:"table-wrap","data-testid":"claudeqq-subscription-table"},a("table",null,a("thead",null,a("tr",null,a("th",null,"名称"),a("th",null,"状态"),a("th",null,"事件"),a("th",null,"回调"),a("th",null,"最近投递"),a("th",null,"操作"))),a("tbody",null,m.map((T)=>a("tr",{key:T.id},a("td",null,a("strong",null,T.name||T.id),a("code",null,T.id||"--")),a("td",null,a(O3,{status:T.enabled?"online":"warn"},T.enabled?"enabled":"disabled")),a("td",null,Array.isArray(T.eventTypes)?T.eventTypes.join(", "):"message"),a("td",null,T.targetUrl||"--"),a("td",null,T.lastDelivery?`${T.lastDelivery.ok?"OK":"FAIL"} ${Nu(T.lastDelivery.at)}`:"--"),a("td",null,a("button",{type:"button",className:"ghost-btn",onClick:()=>J(T.id)},"删除"))))))),a("div",{className:"panel-actions inline-actions"},a(O_,{title:"ClaudeQQ Subscriptions",data:n.subscriptions,onOpen:f,testId:"raw-claudeqq-subscriptions"}))),a(Si,{title:"最近 QQ 事件",eyebrow:n.refreshedAt?`Updated ${$f(n.refreshedAt)}`:"Event Stream",loading:n.loading},X.length===0?a(H_,{title:"暂无事件",text:"等待 NapCat WebSocket 上报 QQ 消息事件,或通过订阅 API 消费后续事件"}):a("div",{className:"table-wrap","data-testid":"claudeqq-event-list"},a("table",null,a("thead",null,a("tr",null,a("th",null,"时间"),a("th",null,"类型"),a("th",null,"会话"),a("th",null,"消息"),a("th",null,"ID"))),a("tbody",null,X.map((T)=>a("tr",{key:T.id},a("td",null,Nu(T.receivedAt||T.timestamp)),a("td",null,a(O3,{status:T.postType||T.eventType},T.postType||T.eventType||"--")),a("td",null,iU(T)),a("td",null,nU(T)),a("td",null,a("code",null,T.messageId||T.id||"--"))))))),a("div",{className:"panel-actions inline-actions"},a(O_,{title:"ClaudeQQ Events",data:n.events,onOpen:f,testId:"raw-claudeqq-events"}))),a(Si,{title:"已发送消息",eyebrow:`${O.length} Sent`,loading:n.loading},O.length===0?a(H_,{title:"暂无发送记录",text:"发送日志来自 ClaudeQQ bot_workspace/messages/sent_messages.jsonl"}):a("div",{className:"table-wrap"},a("table",null,a("thead",null,a("tr",null,a("th",null,"时间"),a("th",null,"目标"),a("th",null,"消息"),a("th",null,"结果"))),a("tbody",null,O.map((T,S)=>a("tr",{key:T.id||S},a("td",null,Nu(T.timestamp||T.sentAt||T.createdAt)),a("td",null,iU(T)),a("td",null,nU(T)),a("td",null,T.status||T.messageId||T.message_id||"--")))))),a("div",{className:"panel-actions inline-actions"},a(O_,{title:"ClaudeQQ Sent Messages",data:n.sent,onOpen:f,testId:"raw-claudeqq-sent"})))))}var P_=Mu(Qf(),1);var tU=Mu(Qf(),1),Uf=tU.default.createElement;function cU({markdown:u,className:f,testId:l}){let r=String(u??"").trimEnd(),n=["markdown-body",f].filter(Boolean).join(" ");return Uf("div",{className:n,"data-testid":l},AU(r,"md"))}function AU(u,f){let l=iT(u).split(` +`),r=[],n=0;while(n\s?/u.test(i)){let j=[];while(n\s?(.*)$/u);if(U!==null){j.push(U[1]),n+=1;continue}if(F.trim().length===0){j.push(""),n+=1;continue}break}r.push(Uf("blockquote",{key:`${f}-quote-${n}`},AU(j.join(` +`),`${f}-quote-${n}`)));continue}if(FU(l,n)){let j=n,F=B_(l[n]??""),U=B_(l[n+1]??"");n+=2;let Q=[];while(n0)Q.push(B_(l[n]??"")),n+=1;r.push(cT(F,U,Q,`${f}-table-${j}`));continue}let $=V3(i);if($!==null){let j=n,F=$.ordered,U=$.start,Q=[];while(n$T(L,`${f}-list-${j}-${J}`))));continue}let c=n,A=[];while(n0&&!_T(l,n))A.push(l[n].trim()),n+=1;if(A.length===0)A.push(i.trim()),n+=1;r.push(Uf("p",{key:`${f}-p-${c}`},r0(A.join(` +`),`${f}-p-${c}`)))}return r}function iT(u){return String(u||"").replace(/\r\n/gu,` `).replace(/\r/gu,` -`).trimEnd()}function gQ(u){let f=u.match(/^\s*(```+|~~~+)\s*([A-Za-z0-9_-]+)?\s*$/u);if(f===null)return null;let l=f[1];return{marker:l.startsWith("`")?"`":"~",length:l.length,language:f[2]||""}}function YE(u,f){let l=u.trim();return l.length>=f.length&&l.split("").every((r)=>r===f.marker)}function yA(u){return/^(?: {4}|\t)/u.test(u)}function hQ(u,f,l){let r=f.trim().length>0?`language-${mE(f)}`:void 0;return Jf("pre",{key:l,className:"markdown-code-block"},Jf("code",{className:r},u))}function DE(u,f){let l=u[f]??"";if(l.trim().length===0)return!0;return gQ(l)!==null||yA(l)||/^(#{1,6})\s+.+$/u.test(l)||/^\s*(?:---+|\*\*\*+|___+)\s*$/u.test(l)||/^\s*>\s?/u.test(l)||sQ(u,f)||E6(l)!==null}function E6(u){let f=u.match(/^\s{0,3}[-*+]\s+(.+)$/u);if(f!==null)return{ordered:!1,text:f[1]};let l=u.match(/^\s{0,3}(\d+)[.)]\s+(.+)$/u);if(l!==null)return{ordered:!0,start:Number(l[1]),text:l[2]};return null}function tE(u,f){let l=u.match(/^\[([ xX])\]\s+(.+)$/u);if(l!==null){let r=l[1].toLowerCase()==="x";return Jf("li",{key:f,className:"task-list-item"},Jf("input",{type:"checkbox",checked:r,readOnly:!0,tabIndex:-1}),Jf("span",null,u0(l[2],`${f}-task`)))}return Jf("li",{key:f},u0(u,f))}function sQ(u,f){let l=u[f]??"",r=u[f+1]??"";if(!l.includes("|")||!r.includes("|"))return!1;let y=zn(l),i=zn(r);return y.length>1&&i.length===y.length&&i.every((_)=>/^:?-{3,}:?$/u.test(_.trim()))}function zn(u){let f=u.trim();if(f.startsWith("|"))f=f.slice(1);if(f.endsWith("|"))f=f.slice(0,-1);return f.split("|").map((l)=>l.trim())}function SE(u){let f=u.trim();if(f.startsWith(":")&&f.endsWith(":"))return"center";if(f.endsWith(":"))return"right";if(f.startsWith(":"))return"left";return}function PE(u,f,l,r){let y=f.map(SE);return Jf("div",{key:r,className:"markdown-table-wrap"},Jf("table",null,Jf("thead",null,Jf("tr",null,u.map((i,_)=>Jf("th",{key:`${r}-h-${_}`,style:y[_]?{textAlign:y[_]}:void 0},u0(i,`${r}-h-${_}`))))),Jf("tbody",null,l.map((i,_)=>Jf("tr",{key:`${r}-r-${_}`},u.map((n,$)=>Jf("td",{key:`${r}-r-${_}-${$}`,style:y[$]?{textAlign:y[$]}:void 0},u0(i[$]||"",`${r}-r-${_}-${$}`))))))))}function u0(u,f,l={}){let r=[],y=/`([^`\n]+)`|\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>)]+)|\*\*([^*\n]+)\*\*|__([^_\n]+)__|~~([^~\n]+)~~|\*([^*\n]+)\*|_([^_\n]+)_/gu,i=l.linkify!==!1,_=0,n=0;for(let $ of u.matchAll(y)){let j=$[0],F=$.index??0;w6(r,u.slice(_,F),`${f}-text-${n}`),_=F+j.length;let J=`${f}-inline-${n}`;if(n+=1,$[1]!==void 0){r.push(Jf("code",{key:J},$[1]));continue}if($[2]!==void 0&&$[3]!==void 0){if(!i){w6(r,j,`${J}-literal`);continue}r.push(bQ($[2],$[3],J));continue}if($[4]!==void 0){if(!i){w6(r,j,`${J}-literal`);continue}r.push(bQ($[4],$[4],J));continue}let U=$[5]??$[6];if(U!==void 0){r.push(Jf("strong",{key:J},u0(U,`${J}-strong`)));continue}if($[7]!==void 0){r.push(Jf("del",{key:J},u0($[7],`${J}-del`)));continue}let q=$[8]??$[9];if(q!==void 0)r.push(Jf("em",{key:J},u0(q,`${J}-em`)))}return w6(r,u.slice(_),`${f}-text-tail`),r}function w6(u,f,l){if(f.length===0)return;f.split(` -`).forEach((y,i)=>{if(i>0)u.push(Jf("br",{key:`${l}-br-${i}`}));if(y.length>0)u.push(y)})}function bQ(u,f,l){let r=ME(f);if(r===null)return Jf("span",{key:l},u);let y=/^(?:https?:|mailto:)/iu.test(r);return Jf("a",{key:l,href:r,target:y?"_blank":void 0,rel:y?"noreferrer":void 0},u0(u,`${l}-label`,{linkify:!1}))}function ME(u){let f=String(u||"").trim();if(/^(?:https?:|mailto:)/iu.test(f))return f;if(f.startsWith("/")&&!f.startsWith("//"))return f;if(f.startsWith("#"))return f;return null}function mE(u){return String(u||"").toLowerCase().replace(/[^a-z0-9_-]+/gu,"-").replace(/^-+|-+$/gu,"")||"text"}var jA=Pu(wf(),1);var Su=jA.default.createElement,{useEffect:pE,useRef:aQ}=jA.default;function CE(u,f){return Jq(u.toTrace(f))}function xE(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";let l=Math.floor(f/1000),r=Math.floor(l/3600),y=Math.floor(l%3600/60),i=l%60;if(r>0)return`${r}h ${String(y).padStart(2,"0")}m`;if(y>0)return`${y}m ${String(i).padStart(2,"0")}s`;return`${i}s`}function Sr(u){let f=Number(u);return Number.isFinite(f)&&f>=0?f:null}function wi(u,f=180){let l=String(u||"").replace(/\s+/gu," ").trim();return l.length>f?`${l.slice(0,f-1)}…`:l}function RE(u){if(!u)return 0;return u.split(/\r?\n/u).length}function nA(u){return{ran:"Ran",explored:"Explored",edited:"Edited",toolGroup:"Tool calls",plan:"Plan",message:"Message",system:"System",error:"Error"}[u]||"Message"}function $A(u){let f=Number(u||0);return Number.isFinite(f)&&f>0?`… +${Math.floor(f)} lines`:""}function hE(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>Math.max(f,Number(l?.seq??0)),0)}function oQ(u){return["explored","edited","ran"].includes(String(u?.kind||""))}function iq(u){let f={read:0,edit:0,run:0};for(let l of u){let r=String(l?.kind||"");if(r==="explored")f.read+=1;else if(r==="edited")f.edit+=1;else if(r==="ran")f.run+=1}return f}function _q(u){let f=iq(u);return`${f.read} read, ${f.edit} edit, ${f.run} run`}function nq(u){return u.replace(/^['"`([{<]+/u,"").replace(/['"`)\]}>.,;:]+$/u,"").replace(/:\d+(?::\d+)?$/u,"").trim()}function dQ(u){let l=String(u||"").match(/(?:~|\.{1,2}|\/)?(?:[A-Za-z0-9_.@+-]+\/)+[A-Za-z0-9_.@+-]+|[A-Za-z0-9_.@+-]+\.(?:c|cc|cpp|h|hpp|js|jsx|ts|tsx|json|md|py|sh|toml|ya?ml|txt|log|lock)/gu)||[],r=[];for(let y of l){let i=nq(y);if(i.length<2||i.includes("..."))continue;if(/^(http|https|status|method)$/iu.test(i))continue;if(!r.includes(i))r.push(i)}return r}function iA(u,f=4){if(u.length===0)return"--";let l=u.slice(0,f).join(", ");return u.length>f?`${l} +${u.length-f}`:l}function eQ(u){let f="";for(let l of u){if(l.length===0)continue;if(f.length>0&&!f.endsWith(` +`).trimEnd()}function jU(u){let f=u.match(/^\s*(```+|~~~+)\s*([A-Za-z0-9_-]+)?\s*$/u);if(f===null)return null;let l=f[1];return{marker:l.startsWith("`")?"`":"~",length:l.length,language:f[2]||""}}function yT(u,f){let l=u.trim();return l.length>=f.length&&l.split("").every((r)=>r===f.marker)}function qc(u){return/^(?: {4}|\t)/u.test(u)}function _U(u,f,l){let r=f.trim().length>0?`language-${jT(f)}`:void 0;return Uf("pre",{key:l,className:"markdown-code-block"},Uf("code",{className:r},u))}function _T(u,f){let l=u[f]??"";if(l.trim().length===0)return!0;return jU(l)!==null||qc(l)||/^(#{1,6})\s+.+$/u.test(l)||/^\s*(?:---+|\*\*\*+|___+)\s*$/u.test(l)||/^\s*>\s?/u.test(l)||FU(u,f)||V3(l)!==null}function V3(u){let f=u.match(/^\s{0,3}[-*+]\s+(.+)$/u);if(f!==null)return{ordered:!1,text:f[1]};let l=u.match(/^\s{0,3}(\d+)[.)]\s+(.+)$/u);if(l!==null)return{ordered:!0,start:Number(l[1]),text:l[2]};return null}function $T(u,f){let l=u.match(/^\[([ xX])\]\s+(.+)$/u);if(l!==null){let r=l[1].toLowerCase()==="x";return Uf("li",{key:f,className:"task-list-item"},Uf("input",{type:"checkbox",checked:r,readOnly:!0,tabIndex:-1}),Uf("span",null,r0(l[2],`${f}-task`)))}return Uf("li",{key:f},r0(u,f))}function FU(u,f){let l=u[f]??"",r=u[f+1]??"";if(!l.includes("|")||!r.includes("|"))return!1;let n=B_(l),i=B_(r);return n.length>1&&i.length===n.length&&i.every((y)=>/^:?-{3,}:?$/u.test(y.trim()))}function B_(u){let f=u.trim();if(f.startsWith("|"))f=f.slice(1);if(f.endsWith("|"))f=f.slice(0,-1);return f.split("|").map((l)=>l.trim())}function tT(u){let f=u.trim();if(f.startsWith(":")&&f.endsWith(":"))return"center";if(f.endsWith(":"))return"right";if(f.startsWith(":"))return"left";return}function cT(u,f,l,r){let n=f.map(tT);return Uf("div",{key:r,className:"markdown-table-wrap"},Uf("table",null,Uf("thead",null,Uf("tr",null,u.map((i,y)=>Uf("th",{key:`${r}-h-${y}`,style:n[y]?{textAlign:n[y]}:void 0},r0(i,`${r}-h-${y}`))))),Uf("tbody",null,l.map((i,y)=>Uf("tr",{key:`${r}-r-${y}`},u.map((_,$)=>Uf("td",{key:`${r}-r-${y}-${$}`,style:n[$]?{textAlign:n[$]}:void 0},r0(i[$]||"",`${r}-r-${y}-${$}`))))))))}function r0(u,f,l={}){let r=[],n=/`([^`\n]+)`|\[([^\]\n]+)\]\(([^)\s]+)(?:\s+"[^"]*")?\)|(https?:\/\/[^\s<>)]+)|\*\*([^*\n]+)\*\*|__([^_\n]+)__|~~([^~\n]+)~~|\*([^*\n]+)\*|_([^_\n]+)_/gu,i=l.linkify!==!1,y=0,_=0;for(let $ of u.matchAll(n)){let c=$[0],A=$.index??0;B3(r,u.slice(y,A),`${f}-text-${_}`),y=A+c.length;let j=`${f}-inline-${_}`;if(_+=1,$[1]!==void 0){r.push(Uf("code",{key:j},$[1]));continue}if($[2]!==void 0&&$[3]!==void 0){if(!i){B3(r,c,`${j}-literal`);continue}r.push($U($[2],$[3],j));continue}if($[4]!==void 0){if(!i){B3(r,c,`${j}-literal`);continue}r.push($U($[4],$[4],j));continue}let F=$[5]??$[6];if(F!==void 0){r.push(Uf("strong",{key:j},r0(F,`${j}-strong`)));continue}if($[7]!==void 0){r.push(Uf("del",{key:j},r0($[7],`${j}-del`)));continue}let U=$[8]??$[9];if(U!==void 0)r.push(Uf("em",{key:j},r0(U,`${j}-em`)))}return B3(r,u.slice(y),`${f}-text-tail`),r}function B3(u,f,l){if(f.length===0)return;f.split(` +`).forEach((n,i)=>{if(i>0)u.push(Uf("br",{key:`${l}-br-${i}`}));if(n.length>0)u.push(n)})}function $U(u,f,l){let r=AT(f);if(r===null)return Uf("span",{key:l},u);let n=/^(?:https?:|mailto:)/iu.test(r);return Uf("a",{key:l,href:r,target:n?"_blank":void 0,rel:n?"noreferrer":void 0},r0(u,`${l}-label`,{linkify:!1}))}function AT(u){let f=String(u||"").trim();if(/^(?:https?:|mailto:)/iu.test(f))return f;if(f.startsWith("/")&&!f.startsWith("//"))return f;if(f.startsWith("#"))return f;return null}function jT(u){return String(u||"").toLowerCase().replace(/[^a-z0-9_-]+/gu,"-").replace(/^-+|-+$/gu,"")||"text"}var Gc=Mu(Qf(),1);var Yu=Gc.default.createElement,{useEffect:FT,useRef:JU}=Gc.default;function JT(u,f){return BU(u.toTrace(f))}function UT(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";let l=Math.floor(f/1000),r=Math.floor(l/3600),n=Math.floor(l%3600/60),i=l%60;if(r>0)return`${r}h ${String(n).padStart(2,"0")}m`;if(n>0)return`${n}m ${String(i).padStart(2,"0")}s`;return`${i}s`}function Mr(u){let f=Number(u);return Number.isFinite(f)&&f>=0?f:null}function pi(u,f=180){let l=String(u||"").replace(/\s+/gu," ").trim();return l.length>f?`${l.slice(0,f-1)}…`:l}function QT(u){if(!u)return 0;return u.split(/\r?\n/u).length}function wc(u){return{ran:"Ran",explored:"Explored",edited:"Edited",toolGroup:"Tool calls",plan:"Plan",message:"Message",system:"System",error:"Error"}[u]||"Message"}function Lc(u){let f=Number(u||0);return Number.isFinite(f)&&f>0?`… +${Math.floor(f)} lines`:""}function qT(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>Math.max(f,Number(l?.seq??0)),0)}function UU(u){return["explored","edited","ran"].includes(String(u?.kind||""))}function GU(u){let f={read:0,edit:0,run:0};for(let l of u){let r=String(l?.kind||"");if(r==="explored")f.read+=1;else if(r==="edited")f.edit+=1;else if(r==="ran")f.run+=1}return f}function zU(u){let f=GU(u);return`${f.read} read, ${f.edit} edit, ${f.run} run`}function TU(u){return u.replace(/^['"`([{<]+/u,"").replace(/['"`)\]}>.,;:]+$/u,"").replace(/:\d+(?::\d+)?$/u,"").trim()}function QU(u){let l=String(u||"").match(/(?:~|\.{1,2}|\/)?(?:[A-Za-z0-9_.@+-]+\/)+[A-Za-z0-9_.@+-]+|[A-Za-z0-9_.@+-]+\.(?:c|cc|cpp|h|hpp|js|jsx|ts|tsx|json|md|py|sh|toml|ya?ml|txt|log|lock)/gu)||[],r=[];for(let n of l){let i=TU(n);if(i.length<2||i.includes("..."))continue;if(/^(http|https|status|method)$/iu.test(i))continue;if(!r.includes(i))r.push(i)}return r}function Nc(u,f=4){if(u.length===0)return"--";let l=u.slice(0,f).join(", ");return u.length>f?`${l} +${u.length-f}`:l}function qU(u){let f="";for(let l of u){if(l.length===0)continue;if(f.length>0&&!f.endsWith(` `)&&!l.startsWith(` `))f+=` -`;f+=l}return f}function $q(u){let f=String(u||"").replace(/\r\n/gu,` +`;f+=l}return f}function EU(u){let f=String(u||"").replace(/\r\n/gu,` `).replace(/\r/gu,` `).trimEnd();return f.length>0?f.split(` -`):[]}function AA(u){let f=String(u.status||"").trim();if(f.length>0)return f;let l=String(u.bodyPreview||"");return/^(item\/[A-Za-z]+(?:\/[A-Za-z]+)?):/u.exec(l)?.[1]||"item/fileChange"}function bE(u){let f=String(u.bodyPreview||"");return/file changes status=([A-Za-z0-9_-]+)/u.exec(f)?.[1]}function vE(u){return/^item\/(?:started|completed): file changes status=/u.test(String(u||"").trim())}function Aq(u){if(String(u.kind||"")!=="edited")return!1;let f=String(u.status||""),l=String(u.title||""),r=String(u.bodyPreview||""),y=String(u.commandPreview||"");if(l==="Edited files")return!0;if(/^item\/fileChange\//u.test(f))return!0;if((f==="item/started"||f==="item/completed")&&/file changes status=/u.test(r))return!0;if(/^Success\. Updated the following files:/mu.test(r))return!0;if(/^diff --git /mu.test(r))return!0;return/^([AMDRCU?]{1,2})\s+\S+/mu.test(r)||y.length>0&&Z6(r).length>0}function Li(u){return nq(String(u||"").replace(/^[ab]\//u,"").trim())}function FA(u){let f=/^([AMDRCU?]{1,2})\s+(.+)$/u.exec(u);if(!f)return null;let l=Li(f[2]||"");return l.length>0?{status:f[1]||"M",path:l}:null}function JA(u){let f=/^\*\*\*\s+(Add|Update|Delete)\s+File:\s+(.+)$/u.exec(u);if(f){let r=f[1]==="Add"?"A":f[1]==="Delete"?"D":"M",y=Li(f[2]||"");return y.length>0?{status:r,path:y}:null}let l=/^\*\*\*\s+Move to:\s+(.+)$/u.exec(u);if(l){let r=Li(l[1]||"");return r.length>0?{status:"R",path:r}:null}return null}function Z6(u){let f=[],l=(y,i)=>{let _=Li(i);if(_.length===0||_==="/dev/null")return;let n=f.find(($)=>$.path===_);if(n){if(n.status==="M"&&y!=="M")n.status=y;return}f.push({status:y,path:_})},r="";for(let y of $q(u)){let i=FA(y)||JA(y);if(i!==null){l(i.status,i.path),r=i.path;continue}let _=/^diff --git a\/(.+?) b\/(.+)$/u.exec(y);if(_){let J=_[2]||_[1]||"";l("M",J),r=Li(J);continue}let n=/^\+\+\+ b\/(.+)$/u.exec(y);if(n&&n[1]!=="/dev/null"){l("M",n[1]||""),r=Li(n[1]||"");continue}if(/^new file mode /u.exec(y)&&r)l("A",r);if(/^deleted file mode /u.exec(y)&&r)l("D",r);let F=/^rename to (.+)$/u.exec(y);if(F)l("R",F[1]||"")}return f}function IE(u){if(FA(u)!==null||JA(u)!==null)return"file";if(/^(diff --git |index |--- |\+\+\+ |\*\*\* Begin Patch|\*\*\* End Patch)/u.test(u))return"meta";if(/^@@ /u.test(u))return"hunk";if(/^\+/u.test(u))return"add";if(/^-/u.test(u))return"del";if(/^(Success\.|No changes|Updated\b|Created\b|Deleted\b|Added\s+\d+\s+lines?|Wrote\s+\d+\s+lines?|Read\s+\d+\s+files?|\.\.\.\[patch content truncated)/iu.test(u))return"note";return"context"}function UA(u){return $q(u).map((f)=>{let l=FA(f)||JA(f);if(l!==null)return{text:f,kind:"file",path:l.path,status:l.status};return{text:f,kind:IE(f)}})}function kE(u){return u.reduce((f,l)=>{if(l.kind==="add")f.added+=1;else if(l.kind==="del")f.removed+=1;return f},{added:0,removed:0})}function uq(u,f){return`${f} ${u} line${u===1?"":"s"}`}function gE(u,f){let l=[];if(u>0)l.push(uq(u,"Added"));if(f>0)l.push(uq(f,"removed"));return l.join(", ")}function sE(u){for(let l=u.length-1;l>=0;l-=1){let r=String(u[l]?.status||"").trim();if(r.length>0)return r}let f=String(u[u.length-1]?.method||"").trim();if(f==="item/fileChange/outputDelta")return"updated";if(f==="item/started")return"started";if(f==="item/completed")return"completed";return f.replace(/^item\//u,"")||"changed"}function aE(u){return`${u} file${u===1?"":"s"}`}function jq(u){let f=u.length>0?u:[],l=eQ(f.map((U)=>String(U.bodyPreview||""))),y=eQ(f.map((U)=>String(U.bodyPreview||"")).filter((U)=>U.trim().length>0&&!vE(U)))||l,i=Z6(y||l),_=f.map((U)=>({method:AA(U),status:bE(U),at:U.at})),n=UA(y||l),$=kE(n),j=gE($.added,$.removed),F=i.length>0?aE(i.length):"",J=j.length>0?`${j}${F?` in ${F}`:""}`:i.length>0?F:wi(y||l||"File changes",72);return{status:sE(_),summary:J,files:i,stages:_,lines:n,addedLines:$.added,removedLines:$.removed,rawText:l}}function oE(u){let f=u[0],l=u[u.length-1]||f,r=jq(u);return{...f,seq:Number.isFinite(Number(l?.seq))?Number(l?.seq):Number(f?.seq??0),at:l?.at||f?.at,title:r.files.length>0?`Edited ${r.summary}`:"Edited files",status:r.status,commandPreview:"",commandOmittedLines:void 0,bodyPreview:r.rawText,bodyOmittedLines:u.reduce((y,i)=>y+Number(i.bodyOmittedLines||0),0)||void 0,rawSeqs:u.flatMap((y)=>Array.isArray(y?.rawSeqs)?y.rawSeqs:[y?.seq]).filter((y)=>y!==void 0),editObservation:r}}function dE(u){let f=Array.isArray(u)?u:[],l=[],r=[],y=()=>{if(r.length===0)return;l.push(oE(r)),r=[]};for(let i of f){if(Aq(i)){if(AA(i)==="item/started"&&r.length>0)y();if(r.push(i),AA(i)==="item/completed")y();continue}y(),l.push(i)}return y(),l}function Fq(u){let f=[],l=[],r=[],y=(j,F)=>{for(let J of F)if(!j.includes(J))j.push(J)};for(let j of u){let F=String(j?.kind||""),J=[j?.commandPreview,j?.bodyPreview,j?.title].map((U)=>String(U||"")).join(` -`);if(F==="explored")y(f,dQ(J));else if(F==="edited")y(l,dQ(J));else if(F==="ran"){let U=String(j?.commandPreview||j?.title||"").trim();if(U.length>0&&!r.includes(U))r.push(wi(U,90))}}let i=u.map((j)=>Date.parse(String(j?.at||""))).filter((j)=>Number.isFinite(j)),_=i.length>=2?Math.max(0,Math.max(...i)-Math.min(...i)):0,n=u.reduce((j,F)=>j+(Sr(F?.durationMs)??Sr(F?.elapsedMs)??0),0),$=_>0?_:n;return{readFiles:f,editedFiles:l,runCommands:r,durationLabel:xE($)}}function eE(u,f=3){let l=Array.isArray(u)?u:[],r=[],y=[],i=Math.max(0,f),_=new Set;for(let $=l.length-1;$>=0&&i>0;$-=1){let j=l[$];if(!oQ(j))continue;_.add(j),i-=1}let n=()=>{if(y.length>=2){let $=iq(y);r.push({seq:Number(y[0]?.seq??0),at:y[0]?.at||y.at(-1)?.at,kind:"toolGroup",title:_q(y),status:`${y.length} calls`,items:y,counts:$,digest:Fq(y),rawSeqs:y.flatMap((j)=>Array.isArray(j?.rawSeqs)?j.rawSeqs:[j?.seq]).filter((j)=>j!==void 0)})}else r.push(...y);y=[]};for(let $ of l){if(oQ($)&&!_.has($)){y.push($);continue}n(),r.push($)}return n(),r}function Jq(u){return(Array.isArray(u)?u:[]).map((f,l)=>({...f,seq:Number.isFinite(Number(f?.seq))?Number(f.seq):l+1,kind:String(f?.kind||"message"),at:f?.at===void 0?void 0:String(f.at),durationMs:Sr(f?.durationMs)??void 0,title:f?.title===void 0?void 0:String(f.title),status:f?.status===void 0?void 0:String(f.status)}))}function Gn(u){let f=Sr(u?.state?.time?.start)??Sr(u?.time?.start),l=Sr(u?.state?.time?.end)??Sr(u?.time?.end);return Sr(u?.durationMs)??Sr(u?.elapsedMs)??Sr(u?.timing?.durationMs)??Sr(u?.metadata?.durationMs)??(f!==null&&l!==null&&l>=f?l-f:null)??void 0}function Kn(u,f){return u?.createdAt||u?.updatedAt||u?.completedAt||f||void 0}function _A(u,f){return u?.id||u?.messageId||f}function uT(u,f=1200){if(typeof u==="string")return u;if(u===void 0||u===null)return"";try{return wi(JSON.stringify(u),f)}catch{return wi(String(u),f)}}function Uq(u,f,l){if(typeof u?.metadata?.diff==="string"&&u.metadata.diff.length>0)return u.metadata.diff;if(typeof u?.metadata?.filediff?.patch==="string"&&u.metadata.filediff.patch.length>0)return u.metadata.filediff.patch;if(typeof u?.output==="string"&&u.output.length>0)return u.output;if(typeof u?.result==="string"&&u.result.length>0)return u.result;if(typeof f?.output==="string"&&f.output.length>0)return f.output;if(typeof l?.output==="string"&&l.output.length>0)return l.output;if(typeof u?.metadata?.output==="string"&&u.metadata.output.length>0)return u.metadata.output;return""}function T6(u,f){if(!u||typeof u!=="object"||Array.isArray(u))return"";for(let l of f){let r=u[l];if(typeof r==="string"&&r.length>0)return r;if(r!==void 0&&r!==null&&typeof r!=="object")return String(r)}return""}function fq(u,f){if(!u||typeof u!=="object"||Array.isArray(u))return null;for(let l of f){let r=Number(u[l]);if(Number.isFinite(r))return r}return null}function Qq(u,f){let l=f?.input&&typeof f.input==="object"&&!Array.isArray(f.input)?f.input:u?.input&&typeof u.input==="object"&&!Array.isArray(u.input)?u.input:{},r=T6(l,["command","cmd","script"]);if(r.length>0)return r;if(typeof u?.command==="string"&&u.command.length>0)return u.command;if(typeof f?.command==="string"&&f.command.length>0)return f.command;let y=String(u?.tool||u?.title||"tool"),i=T6(l,["filePath","filepath","path"])||T6(u,["filePath","filepath","path"]),_=T6(l,["pattern","query"]),n=fq(l,["offset"]),$=fq(l,["limit"]),j=[y];if(_.length>0)j.push(_);if(i.length>0)j.push(i);if(n!==null)j.push(`offset=${n}`);if($!==null)j.push(`limit=${$}`);return j.length>1?j.join(" "):y}function fT(u,f){let l=u?.part&&typeof u.part==="object"&&!Array.isArray(u.part)?u.part:{},r=String(u?.type||u?.event||u?.name||l?.type||"").toLowerCase(),y=String(l?.type||"").toLowerCase(),i=u?.at||u?.timestamp||l?.updatedAt||l?.createdAt,_=Number.isFinite(Number(u?.seq))?Number(u.seq):f;if(r==="step_start"||r==="step-start"||y==="step-start")return null;if(r==="step_finish"||r==="step-finish"||y==="step-finish")return null;if(y==="tool"||/tool|bash|command/iu.test(`${r} ${y}`)){let $=l?.state&&typeof l.state==="object"&&!Array.isArray(l.state)?l.state:{},j=Qq(l,$),F=Uq($,l,u),J=qq(j,String(l?.tool||l?.title||"")),U=J==="edited"?{status:String($?.status||l?.status||u?.status||""),summary:wi(F||j,72),files:Z6(F),stages:[],lines:UA(F),addedLines:0,removedLines:0,rawText:F}:void 0;return{seq:_,at:Kn(l,i),kind:J,title:String($?.title||l?.title||$?.metadata?.description||l?.tool||"OpenCode tool"),status:String($?.status||l?.status||u?.status||""),commandPreview:j,bodyPreview:F,durationMs:Gn(l),rawSeqs:[l?.id||l?.callID||u?.sessionID||_],editObservation:U}}let n=uT(l?.text??l?.content??l?.delta??u?.text??u?.content??u?.delta,3000).trim();if(n.length>0)return{seq:_,at:Kn(l,i),kind:y==="reasoning"?"message":/error|failed/iu.test(`${r} ${y}`)?"error":"message",title:y==="reasoning"?"Reasoning":/error|failed/iu.test(`${r} ${y}`)?"OpenCode error":"Assistant message",status:`opencode/${r||y||"event"}`,bodyPreview:n,durationMs:Gn(l),rawSeqs:[l?.id||u?.sessionID||_]};return null}function qq(u,f){let l=`${f} ${u}`.toLowerCase();if(/\b(read|grep|glob|list|ls|find|search|view|cat|sed|rg|head|tail|wc|file)\b/iu.test(l))return"explored";if(/\b(edit|write|patch|apply|update|create|delete|apply_patch|git apply|cat >|tee .*<<|sed -i|python3? .*write_text|mkdir|rm |touch )\b/iu.test(l))return"edited";return"ran"}function lT(u){let f=[],l=1;for(let r of Array.isArray(u)?u:[]){if(r?.kind&&r?.title){let n=String(r?.status||"");if(n==="opencode/step-start"||n==="opencode/step-finish")continue;f.push({...r,seq:Number.isFinite(Number(r?.seq))?Number(r.seq):l++});continue}let y=r?.createdAt||r?.updatedAt||r?.completedAt,i=String(r?.role||"assistant").toLowerCase(),_=Array.isArray(r?.parts)?r.parts:[];if(_.length===0){if(r?.part&&(r?.sessionID||String(r?.type||"").startsWith("step_")||String(r?.type||"").includes("tool"))){let n=fT(r,l);if(n!==null)f.push(n),l=Math.max(l+1,Number(n.seq)+1)}else if(r?.textPreview)f.push({seq:l++,at:y,kind:"message",title:`${i||"assistant"} message`,status:i,bodyPreview:String(r.textPreview),rawSeqs:[r?.messageId||l]});continue}for(let n of _){let $=String(n?.type||"").toLowerCase();if($==="step-start"||$==="step-finish")continue;if($==="text"||$==="reasoning"){let F=String(n?.textPreview||r?.textPreview||"").trim();if(F.length===0)continue;f.push({seq:l++,at:Kn(n,y),kind:"message",title:$==="reasoning"?"Reasoning":i==="user"?"User message":i==="system"?"System message":"Assistant message",status:$==="reasoning"?"reasoning":i,bodyPreview:F,durationMs:Gn(n),rawSeqs:[_A(n,l)]});continue}if($==="tool"){let F=n?.state&&typeof n.state==="object"&&!Array.isArray(n.state)?n.state:{},J=Qq(n,F),U=Uq(F,n,{}),q=qq(J,String(n?.tool||n?.title||"")),W=q==="edited"?{status:String(F?.status||n?.status||""),summary:wi(U||J,72),files:Z6(U),stages:[],lines:UA(U),addedLines:0,removedLines:0,rawText:U}:void 0;f.push({seq:l++,at:Kn(n,y),kind:q,title:String(n?.title||n?.tool||"tool"),status:String(n?.status||""),commandPreview:J,bodyPreview:U,durationMs:Gn(n),rawSeqs:[_A(n,l)],editObservation:W});continue}let j=String(n?.textPreview||n?.title||$||"").trim();if(j)f.push({seq:l++,at:Kn(n,y),kind:"system",title:$||"part",bodyPreview:j,status:String(n?.status||""),durationMs:Gn(n),rawSeqs:[_A(n,l)]})}}return f}var Wq={source:"opencode",toTrace:lT};function rT(u){return String(u||"unknown").toLowerCase().replace(/[^a-z0-9_-]+/gu,"-")||"unknown"}function lq(u){let f=String(u||"M").toUpperCase();if(f.startsWith("A")||f==="??")return"added";if(f.startsWith("D"))return"deleted";if(f.startsWith("R"))return"renamed";return"modified"}function yT(u){if(u==="item/fileChange/outputDelta")return"delta";return u.replace(/^item\//u,"")}function iT(u,f){if(u.kind==="file"){let y=String(u.status||"M");return Su("div",{key:`${f}-${u.text}`,className:`codex-edit-diff-line file ${lq(y)}`},Su("span",{className:`codex-edit-file-status ${lq(y)}`},y),Su("code",null,u.path||u.text.replace(/^([AMDRCU?]{1,2})\s+/u,"")))}let l=u.kind==="add"||u.kind==="del"?u.text.slice(0,1):u.kind==="hunk"?"@@":u.kind==="note"?"ok":"",r=u.kind==="add"||u.kind==="del"?u.text.slice(1):u.text;return Su("div",{key:`${f}-${u.text}`,className:`codex-edit-diff-line ${u.kind}`},Su("span",{className:"codex-edit-diff-sign"},l),Su("code",null,r||" "))}function _T(u,f){let l=u.lines.length>0?u.lines:u.files.map((y)=>({text:`${y.status} ${y.path}`,kind:"file",path:y.path,status:y.status})),r=Number(u.addedLines||0)+Number(u.removedLines||0)>0;return Su("div",{className:"codex-edit-observation","data-testid":"codex-edit-observation"},Su("div",{className:"codex-edit-observation-head"},Su("span",{className:"codex-edit-window-controls","aria-hidden":"true"},Su("i",null),Su("i",null),Su("i",null)),Su("strong",null,r?"git diff":"git diff --stat"),Su("code",null,u.summary||"File changes")),u.stages.length>0?Su("div",{className:"codex-edit-stage-strip"},u.stages.map((y,i)=>Su("span",{key:`${y.method}-${i}`,className:`codex-edit-stage ${rT(y.status||y.method)}`},Su("b",null,yT(y.method)),y.status?Su("em",null,y.status):null))):null,l.length>0?Su("div",{className:"codex-edit-diff",role:"list"},l.map(iT)):null,f?Su("div",{className:"codex-edit-omitted"},`${f} (查看原始JSON获取完整记录)`):null)}function rq(u,f,l){let r=$A(l);return Su("div",{className:`codex-transcript-stream ${u}`,"data-testid":`codex-trace-${u}`},Su("span",{className:"codex-transcript-stream-label"},u),Su("pre",{className:"codex-transcript-body"},f,r?` -${r} (查看原始JSON获取完整记录)`:""))}function cq(u,f=!1){let l=String(u.kind||"message"),r=["ran","explored","edited"].includes(l),y=$A(u.commandOmittedLines),i=$A(u.bodyOmittedLines),_=String(u.commandPreview||(r?u.title||"":"")),n=String(u.stdoutPreview||""),$=String(u.stderrPreview||""),j=n.length>0||$.length>0,F=Boolean(u.foldedReferencePrompt)&&String(u.fullPrompt||"").length>0,J=l==="edited"&&(u.editObservation!==void 0||Aq(u))?u.editObservation||jq([u]):null;return Su("article",{key:`${u.seq}-${l}`,className:`codex-transcript-item ${l} ${f?"nested":""}`},Su("div",{className:"codex-transcript-main"},Su("div",{className:"codex-transcript-title"},Su("span",{className:"codex-output-channel"},nA(l)),r&&J===null?null:Su("strong",null,J!==null?"File changes":String(u.title||nA(l))),u.status?Su("code",null,String(J?.status||u.status)):null,Su("time",null,Nu(u.at))),_&&J===null?Su("pre",{className:"codex-transcript-command"},_,y?` -${y}`:""):null,J!==null?_T(J,i):j?Su("div",{className:"codex-transcript-streams"},n.length>0?rq("stdout",n,u.stdoutOmittedLines):null,$.length>0?rq("stderr",$,u.stderrOmittedLines):null):u.bodyPreview?Su("pre",{className:"codex-transcript-body"},String(u.bodyPreview),i?` -${i} (查看原始JSON获取完整记录)`:""):null,F?Su("details",{className:"codex-initial-prompt-full","data-testid":"codex-initial-prompt-full"},Su("summary",null,Su("span",null,"引用注入已折叠,点击查看最终传入 Codex 的完整 prompt"),Su("code",null,`${u.fullPromptLines||RE(String(u.fullPrompt||""))} lines / ${u.fullPromptChars||String(u.fullPrompt||"").length} chars`)),Su("pre",{className:"codex-transcript-body codex-transcript-full-prompt","data-testid":"codex-initial-prompt-full-text"},String(u.fullPrompt||""))):null))}function nT(u){let f=Array.isArray(u.items)?u.items:[],l=u.digest&&typeof u.digest==="object"?u.digest:Fq(f);return Su("article",{key:`${u.seq}-toolGroup`,className:"codex-transcript-item toolGroup"},Su("div",{className:"codex-transcript-main"},Su("details",{className:"codex-tool-group","data-testid":"codex-tool-group"},Su("summary",null,Su("div",{className:"codex-tool-group-head"},Su("span",{className:"codex-output-channel"},nA("toolGroup")),Su("strong",null,String(u.title||_q(f))),Su("code",null,String(u.status||`${f.length} calls`)),Su("time",null,Nu(u.at)))),Su("div",{className:"codex-tool-group-digest"},Su("span",null,`read: ${iA(Array.isArray(l.readFiles)?l.readFiles:[])}`),Su("span",null,`edit: ${iA(Array.isArray(l.editedFiles)?l.editedFiles:[])}`),Su("span",null,`run: ${iA(Array.isArray(l.runCommands)?l.runCommands:[],2)}`),Su("span",null,`duration: ${l.durationLabel||"--"}`)),Su("div",{className:"codex-tool-group-items"},f.map((r)=>cq(r,!0))))))}var $T=16;function yq(u){return u.scrollHeight-u.scrollTop-u.clientHeight<=$T}function H6({items:u,input:f,port:l,autoScroll:r=!1,loading:y=!1,hasDetail:i=!0,emptyText:_="等待 Trace 输出...",loadingText:n="正在加载完整 Trace...",testId:$="trace-output",className:j="codex-transcript",keepRecentToolCalls:F=3,collapseTools:J=!0}){let U=aQ(null),q=aQ(!0),W=dE(l?CE(l,f):Jq(u)),G=J?eE(W,F):W,K=hE(W);pE(()=>{let c=U.current;if(!r||!c)return;if(!q.current&&!yq(c))return;c.scrollTop=c.scrollHeight,q.current=!0},[r,W.length,K]);let N={className:j,ref:U,onScroll:(c)=>{let z=c.currentTarget;q.current=yq(z)},"data-testid":$};if(y&&!i)return Su("div",N,Su("div",{className:"codex-output-empty"},n));return Su("div",N,G.length===0?Su("div",{className:"codex-output-empty"},_):G.map((c)=>String(c.kind||"")==="toolGroup"?nT(c):cq(c)))}var E=On.default.createElement,{useEffect:Q1,useMemo:Nq,useRef:Xf}=On.default,su=On.default.useState,AT=120,Yq=24,Dq=48,jT=1200;function QA(){return typeof document>"u"||document.visibilityState!=="hidden"}function _l(u,f="操作失败"){return Bu(u,f)}function Mr(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";let l=Math.floor(f/1000),r=Math.floor(l/3600),y=Math.floor(l%3600/60),i=l%60;if(r>0)return`${r}h ${String(y).padStart(2,"0")}m`;if(y>0)return`${y}m ${String(i).padStart(2,"0")}s`;return`${i}s`}function H0(u){if(u===null||u===void 0||u==="")return null;let f=u instanceof Date?u.getTime():new Date(u).getTime();return Number.isFinite(f)?f:null}function tq(u,f=Date.now()){let l=H0(u);if(l===null)return"--";let r=Math.max(0,Math.floor((f-l)/1000));if(r<1)return"刚刚";let y=Math.floor(r/86400),i=Math.floor(r%86400/3600),_=Math.floor(r%3600/60),n=r%60;if(y>0)return`${y}天${i>0?`${i}小时`:""}前`;if(i>0)return`${i}小时${_>0?`${_}分钟`:""}前`;if(_>0)return`${_}分钟${n}秒前`;return`${n}秒前`}function Sq(...u){let f="",l=-1/0;for(let r of u){let y=String(r||"");if(y.length===0)continue;let i=H0(r);if(i!==null&&i>=l)f=y,l=i;else if(f.length===0)f=y}return f}function FT(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";if(f<1000)return`${Math.round(f)}ms`;return`${(f/1000).toFixed(f<1e4?2:1)}s`}function GA(u,f=180){let l=String(u||"").replace(/\s+/gu," ").trim();return l.length>f?`${l.slice(0,f-1)}…`:l}async function mf(u,f={}){return Xu(u,{strictJson:!0,retryInvalidJson:1,invalidJsonPrefix:"Code Queue 返回了无效 JSON",invalidJsonPreview:!0,responsePreviewLength:jT,...f})}function jy({status:u,children:f,title:l}){let r=String(u||"unknown").toLowerCase();return E("span",{className:`status-badge ${r}`,title:l},f||u||"unknown")}function Ti({title:u,eyebrow:f,summary:l,actions:r,children:y,className:i,loading:_}){return E("section",{className:`panel ${i||""}`},E("div",{className:"panel-head"},E("div",null,f?E("p",{className:"panel-eyebrow"},f):null,E(Af,{title:u,loading:_}),l?E("div",{className:"panel-summary"},l):null),r?E("div",{className:"panel-actions"},r):null),E("div",{className:"panel-body"},y))}function Pq({title:u,data:f,onOpen:l,testId:r}){return E("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function Fy({title:u,text:f}){return E("div",{className:"empty-state"},E("strong",null,u),E("span",null,f))}function JT(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function UT(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function Sf(u,f){return`${u}/code-queue-direct${f}`}function BA(){return{headers:{"cache-control":"no-cache","x-unidesk-no-cache":"1"}}}function nl(u){return Array.isArray(u?.tasks)?u.tasks:[]}function U1(u){return u?.pagination&&typeof u.pagination==="object"&&!Array.isArray(u.pagination)?u.pagination:{}}function zq(u){let f=Date.parse(String(u?.updatedAt||u?.createdAt||""));return Number.isFinite(f)?f:0}function Gq(u){return H0(u?.queueEnteredAt)??H0(u?.createdAt)??H0(u?.updatedAt)??0}function QT(u,f){let l=Gq(u)-Gq(f);if(l!==0)return l;let r=(H0(u?.createdAt)??0)-(H0(f?.createdAt)??0);if(r!==0)return r;return String(u?.id||"").localeCompare(String(f?.id||""))}function qT(u,f=""){let l=new Map;for(let r of u)for(let y of r){let i=String(y?.id||"");if(i.length>0&&!l.has(i))l.set(i,y)}return Array.from(l.values()).sort((r,y)=>{let i=Oq(r)-Oq(y);if(i!==0)return i;let _=String(r?.id||"")===f?0:1,n=String(y?.id||"")===f?0:1;if(_!==n)return _-n;return zq(y)-zq(r)})}function Ei(u,f=""){let l=new Map;for(let r of u)for(let y of r){let i=String(y?.id||"");if(i.length===0)continue;l.set(i,{...l.get(i)||{},...y})}return qT([Array.from(l.values())],f)}function wn(u){return Array.isArray(u?.activeTaskIds)?u.activeTaskIds.map((f)=>String(f||"")).filter(Boolean):[String(u?.activeTaskId||"")].filter(Boolean)}var W1="__all__",WT="(max-width: 760px)",cT="(min-width: 761px)";function ml(u){return!u||u===W1}function NT(){return typeof window<"u"&&window.matchMedia(WT).matches}function zT(u){return ml(u)?"":`&queueId=${encodeURIComponent(u)}`}function KA(u){return String(u||"").trim().replace(/\s+/gu," ").slice(0,200)}function GT(u){let f=KA(u);return f.length===0?"":`&search=${encodeURIComponent(f)}`}function VA(u,f=""){return`${zT(u)}${GT(f)}`}function O6(u,f){return Number(u?.counts?.[f]||0)}function Kq(u,f=""){let l=new Map;for(let y of Array.isArray(u?.queues)?u.queues:[]){let i=String(y?.id||"").trim();if(i.length>0)l.set(i,{...y,name:String(y?.name||i).trim()||i})}for(let y of[String(u?.defaultQueueId||"default"),f].map((i)=>i.trim()).filter(Boolean))if(!l.has(y))l.set(y,{id:y,name:y,total:0,counts:{},activeTaskId:null,runnableTaskId:null,processing:!1});return Array.from(l.values()).sort((y,i)=>{let _=String(y?.id||"")===String(u?.defaultQueueId||"default")?0:1,n=String(i?.id||"")===String(u?.defaultQueueId||"default")?0:1;if(_!==n)return _-n;return String(y?.id||"").localeCompare(String(i?.id||""))})}function Mq(u){let f=String(u?.id||"default"),l=String(u?.name||"").trim();return l.length>0?l:f}function LA(u){let f=String(u?.id||"default"),l=Mq(u);return l===f?f:`${l} (${f})`}function wA(u){let f=O6(u,"running")+O6(u,"judging"),l=O6(u,"queued")+O6(u,"retry_wait"),r=Number(u?.total||0),y=[LA(u),`${r} tasks`];if(f>0)y.push(`${f} running`);if(l>0)y.push(`${l} queued`);return y.join(" · ")}function En(u,f){if(ml(f))return null;return u.find((l)=>String(l?.id||"")===f)||null}function Lq(u,f,l,r){if(ml(l)){let i=wn(u);return String(u?.activeTaskId||i[0]||r.find((_)=>Zq(_))?.id||"")}let y=En(f,l);return String(y?.activeTaskId||r.find((i)=>Zq(i))?.id||"")}function KT(u,f,l){if(!ml(f)){let r=En(u,f);return String(r?.runnableTaskId||l.find((y)=>String(y?.status||"")==="queued"||String(y?.status||"")==="retry_wait")?.id||"")}return String(l.find((r)=>String(r?.status||"")==="queued"||String(r?.status||"")==="retry_wait")?.id||"")}async function LT(u,f=W1,l=""){return mf(Sf(u,`/api/tasks/overview?limit=${Yq}&transcriptLimit=1&compact=1&selected=0${VA(f,l)}`),BA())}async function wT(u,f,l=0,r=W1,y=""){return mf(Sf(u,`/api/tasks/overview?limit=${Yq}&transcriptLimit=3&compact=1&afterSeq=${encodeURIComponent(String(Math.max(0,l)))}&preferId=${encodeURIComponent(f)}${VA(r,y)}`),BA())}async function wq(u,f,l,r=Dq,y=""){return mf(Sf(u,`/api/tasks/overview?limit=${encodeURIComponent(String(r))}&transcriptLimit=1&compact=1&selected=0&includeActive=0&stats=0&beforeId=${encodeURIComponent(l)}${VA(f,y)}`),BA())}async function ET(u,f){return mf(Sf(u,`/api/tasks/${encodeURIComponent(f)}/trace-summary`))}async function TT(u,f,l,r=null){let y=r===null||r===void 0||String(r).length===0?"":`&attempt=${encodeURIComponent(String(r))}`;return mf(Sf(u,`/api/tasks/${encodeURIComponent(f)}/prompt?part=${encodeURIComponent(l)}${y}`))}async function ZT(u,f,l=0,r=500,y=null){let i=y===null||y===void 0||String(y).length===0?"":`&attempt=${encodeURIComponent(String(y))}`;return mf(Sf(u,`/api/tasks/${encodeURIComponent(f)}/trace-steps?afterSeq=${encodeURIComponent(String(l))}&limit=${encodeURIComponent(String(r))}${i}`))}async function HT(u,f,l){return mf(Sf(u,`/api/tasks/${encodeURIComponent(f)}/trace-step?seq=${encodeURIComponent(String(l))}`))}async function OT(u,f){return mf(Sf(u,`/api/tasks/${encodeURIComponent(f)}/read`),{method:"POST",body:{}})}async function BT(u){return mf(Sf(u,"/api/tasks/read-all"),{method:"POST",body:{}})}function VT(u){return Array.isArray(u?.output)?u.output:[]}function XT(u){return Array.isArray(u?.attempts)?u.attempts:[]}function qA(u){return u?.counts&&typeof u.counts==="object"&&!Array.isArray(u.counts)?u.counts:{}}function YT(u){return u.split(/^\s*---+\s*$/gmu).map((f)=>f.trim()).filter(Boolean)}function Eq(u){let f=Number(u);return Number.isFinite(f)?Math.max(1,Math.min(50,Math.floor(f))):1}function ny(u){let f=[];for(let l of u.split(/[\s,,;;]+/u)){let r=l.trim();if(/^codex_\d+_[A-Za-z0-9_-]+$/u.test(r)&&!f.includes(r))f.push(r)}return f}function DT(u,f){let l=ny(f);if(l.length===0)return u;return[`引用 Code Queue 任务 ${l.join(" ")}。后端会在入队时只注入这些任务的 initial prompt 和 final response 全文;中间执行过程不注入,如需补充核查可运行:${l.map((r)=>`bun scripts/cli.ts codex task ${r}`).join(";")}`,"","本次任务:",u].join(` -`)}function tT(u){let r=u.trimStart();if(!r.startsWith("# Code Queue 已解析引用上下文"))return{hasInjection:!1,reference:"",userPrompt:u};let y=u.length-r.length,i=u.lastIndexOf(` +`):[]}function Kc(u){let f=String(u.status||"").trim();if(f.length>0)return f;let l=String(u.bodyPreview||"");return/^(item\/[A-Za-z]+(?:\/[A-Za-z]+)?):/u.exec(l)?.[1]||"item/fileChange"}function NT(u){let f=String(u.bodyPreview||"");return/file changes status=([A-Za-z0-9_-]+)/u.exec(f)?.[1]}function WT(u){return/^item\/(?:started|completed): file changes status=/u.test(String(u||"").trim())}function ZU(u){if(String(u.kind||"")!=="edited")return!1;let f=String(u.status||""),l=String(u.title||""),r=String(u.bodyPreview||""),n=String(u.commandPreview||"");if(l==="Edited files")return!0;if(/^item\/fileChange\//u.test(f))return!0;if((f==="item/started"||f==="item/completed")&&/file changes status=/u.test(r))return!0;if(/^Success\. Updated the following files:/mu.test(r))return!0;if(/^diff --git /mu.test(r))return!0;return/^([AMDRCU?]{1,2})\s+\S+/mu.test(r)||n.length>0&&X3(r).length>0}function mi(u){return TU(String(u||"").replace(/^[ab]\//u,"").trim())}function zc(u){let f=/^([AMDRCU?]{1,2})\s+(.+)$/u.exec(u);if(!f)return null;let l=mi(f[2]||"");return l.length>0?{status:f[1]||"M",path:l}:null}function Tc(u){let f=/^\*\*\*\s+(Add|Update|Delete)\s+File:\s+(.+)$/u.exec(u);if(f){let r=f[1]==="Add"?"A":f[1]==="Delete"?"D":"M",n=mi(f[2]||"");return n.length>0?{status:r,path:n}:null}let l=/^\*\*\*\s+Move to:\s+(.+)$/u.exec(u);if(l){let r=mi(l[1]||"");return r.length>0?{status:"R",path:r}:null}return null}function X3(u){let f=[],l=(n,i)=>{let y=mi(i);if(y.length===0||y==="/dev/null")return;let _=f.find(($)=>$.path===y);if(_){if(_.status==="M"&&n!=="M")_.status=n;return}f.push({status:n,path:y})},r="";for(let n of EU(u)){let i=zc(n)||Tc(n);if(i!==null){l(i.status,i.path),r=i.path;continue}let y=/^diff --git a\/(.+?) b\/(.+)$/u.exec(n);if(y){let j=y[2]||y[1]||"";l("M",j),r=mi(j);continue}let _=/^\+\+\+ b\/(.+)$/u.exec(n);if(_&&_[1]!=="/dev/null"){l("M",_[1]||""),r=mi(_[1]||"");continue}if(/^new file mode /u.exec(n)&&r)l("A",r);if(/^deleted file mode /u.exec(n)&&r)l("D",r);let A=/^rename to (.+)$/u.exec(n);if(A)l("R",A[1]||"")}return f}function wT(u){if(zc(u)!==null||Tc(u)!==null)return"file";if(/^(diff --git |index |--- |\+\+\+ |\*\*\* Begin Patch|\*\*\* End Patch)/u.test(u))return"meta";if(/^@@ /u.test(u))return"hunk";if(/^\+/u.test(u))return"add";if(/^-/u.test(u))return"del";if(/^(Success\.|No changes|Updated\b|Created\b|Deleted\b|Added\s+\d+\s+lines?|Wrote\s+\d+\s+lines?|Read\s+\d+\s+files?|\.\.\.\[patch content truncated)/iu.test(u))return"note";return"context"}function Ec(u){return EU(u).map((f)=>{let l=zc(f)||Tc(f);if(l!==null)return{text:f,kind:"file",path:l.path,status:l.status};return{text:f,kind:wT(f)}})}function LT(u){return u.reduce((f,l)=>{if(l.kind==="add")f.added+=1;else if(l.kind==="del")f.removed+=1;return f},{added:0,removed:0})}function NU(u,f){return`${f} ${u} line${u===1?"":"s"}`}function KT(u,f){let l=[];if(u>0)l.push(NU(u,"Added"));if(f>0)l.push(NU(f,"removed"));return l.join(", ")}function GT(u){for(let l=u.length-1;l>=0;l-=1){let r=String(u[l]?.status||"").trim();if(r.length>0)return r}let f=String(u[u.length-1]?.method||"").trim();if(f==="item/fileChange/outputDelta")return"updated";if(f==="item/started")return"started";if(f==="item/completed")return"completed";return f.replace(/^item\//u,"")||"changed"}function zT(u){return`${u} file${u===1?"":"s"}`}function OU(u){let f=u.length>0?u:[],l=qU(f.map((F)=>String(F.bodyPreview||""))),n=qU(f.map((F)=>String(F.bodyPreview||"")).filter((F)=>F.trim().length>0&&!WT(F)))||l,i=X3(n||l),y=f.map((F)=>({method:Kc(F),status:NT(F),at:F.at})),_=Ec(n||l),$=LT(_),c=KT($.added,$.removed),A=i.length>0?zT(i.length):"",j=c.length>0?`${c}${A?` in ${A}`:""}`:i.length>0?A:pi(n||l||"File changes",72);return{status:GT(y),summary:j,files:i,stages:y,lines:_,addedLines:$.added,removedLines:$.removed,rawText:l}}function TT(u){let f=u[0],l=u[u.length-1]||f,r=OU(u);return{...f,seq:Number.isFinite(Number(l?.seq))?Number(l?.seq):Number(f?.seq??0),at:l?.at||f?.at,title:r.files.length>0?`Edited ${r.summary}`:"Edited files",status:r.status,commandPreview:"",commandOmittedLines:void 0,bodyPreview:r.rawText,bodyOmittedLines:u.reduce((n,i)=>n+Number(i.bodyOmittedLines||0),0)||void 0,rawSeqs:u.flatMap((n)=>Array.isArray(n?.rawSeqs)?n.rawSeqs:[n?.seq]).filter((n)=>n!==void 0),editObservation:r}}function ET(u){let f=Array.isArray(u)?u:[],l=[],r=[],n=()=>{if(r.length===0)return;l.push(TT(r)),r=[]};for(let i of f){if(ZU(i)){if(Kc(i)==="item/started"&&r.length>0)n();if(r.push(i),Kc(i)==="item/completed")n();continue}n(),l.push(i)}return n(),l}function HU(u){let f=[],l=[],r=[],n=(c,A)=>{for(let j of A)if(!c.includes(j))c.push(j)};for(let c of u){let A=String(c?.kind||""),j=[c?.commandPreview,c?.bodyPreview,c?.title].map((F)=>String(F||"")).join(` +`);if(A==="explored")n(f,QU(j));else if(A==="edited")n(l,QU(j));else if(A==="ran"){let F=String(c?.commandPreview||c?.title||"").trim();if(F.length>0&&!r.includes(F))r.push(pi(F,90))}}let i=u.map((c)=>Date.parse(String(c?.at||""))).filter((c)=>Number.isFinite(c)),y=i.length>=2?Math.max(0,Math.max(...i)-Math.min(...i)):0,_=u.reduce((c,A)=>c+(Mr(A?.durationMs)??Mr(A?.elapsedMs)??0),0),$=y>0?y:_;return{readFiles:f,editedFiles:l,runCommands:r,durationLabel:UT($)}}function ZT(u,f=3){let l=Array.isArray(u)?u:[],r=[],n=[],i=Math.max(0,f),y=new Set;for(let $=l.length-1;$>=0&&i>0;$-=1){let c=l[$];if(!UU(c))continue;y.add(c),i-=1}let _=()=>{if(n.length>=2){let $=GU(n);r.push({seq:Number(n[0]?.seq??0),at:n[0]?.at||n.at(-1)?.at,kind:"toolGroup",title:zU(n),status:`${n.length} calls`,items:n,counts:$,digest:HU(n),rawSeqs:n.flatMap((c)=>Array.isArray(c?.rawSeqs)?c.rawSeqs:[c?.seq]).filter((c)=>c!==void 0)})}else r.push(...n);n=[]};for(let $ of l){if(UU($)&&!y.has($)){n.push($);continue}_(),r.push($)}return _(),r}function BU(u){return(Array.isArray(u)?u:[]).map((f,l)=>({...f,seq:Number.isFinite(Number(f?.seq))?Number(f.seq):l+1,kind:String(f?.kind||"message"),at:f?.at===void 0?void 0:String(f.at),durationMs:Mr(f?.durationMs)??void 0,title:f?.title===void 0?void 0:String(f.title),status:f?.status===void 0?void 0:String(f.status)}))}function V_(u){let f=Mr(u?.state?.time?.start)??Mr(u?.time?.start),l=Mr(u?.state?.time?.end)??Mr(u?.time?.end);return Mr(u?.durationMs)??Mr(u?.elapsedMs)??Mr(u?.timing?.durationMs)??Mr(u?.metadata?.durationMs)??(f!==null&&l!==null&&l>=f?l-f:null)??void 0}function D_(u,f){return u?.createdAt||u?.updatedAt||u?.completedAt||f||void 0}function Wc(u,f){return u?.id||u?.messageId||f}function OT(u,f=1200){if(typeof u==="string")return u;if(u===void 0||u===null)return"";try{return pi(JSON.stringify(u),f)}catch{return pi(String(u),f)}}function VU(u,f,l){if(typeof u?.metadata?.diff==="string"&&u.metadata.diff.length>0)return u.metadata.diff;if(typeof u?.metadata?.filediff?.patch==="string"&&u.metadata.filediff.patch.length>0)return u.metadata.filediff.patch;if(typeof u?.output==="string"&&u.output.length>0)return u.output;if(typeof u?.result==="string"&&u.result.length>0)return u.result;if(typeof f?.output==="string"&&f.output.length>0)return f.output;if(typeof l?.output==="string"&&l.output.length>0)return l.output;if(typeof u?.metadata?.output==="string"&&u.metadata.output.length>0)return u.metadata.output;return""}function D3(u,f){if(!u||typeof u!=="object"||Array.isArray(u))return"";for(let l of f){let r=u[l];if(typeof r==="string"&&r.length>0)return r;if(r!==void 0&&r!==null&&typeof r!=="object")return String(r)}return""}function WU(u,f){if(!u||typeof u!=="object"||Array.isArray(u))return null;for(let l of f){let r=Number(u[l]);if(Number.isFinite(r))return r}return null}function DU(u,f){let l=f?.input&&typeof f.input==="object"&&!Array.isArray(f.input)?f.input:u?.input&&typeof u.input==="object"&&!Array.isArray(u.input)?u.input:{},r=D3(l,["command","cmd","script"]);if(r.length>0)return r;if(typeof u?.command==="string"&&u.command.length>0)return u.command;if(typeof f?.command==="string"&&f.command.length>0)return f.command;let n=String(u?.tool||u?.title||"tool"),i=D3(l,["filePath","filepath","path"])||D3(u,["filePath","filepath","path"]),y=D3(l,["pattern","query"]),_=WU(l,["offset"]),$=WU(l,["limit"]),c=[n];if(y.length>0)c.push(y);if(i.length>0)c.push(i);if(_!==null)c.push(`offset=${_}`);if($!==null)c.push(`limit=${$}`);return c.length>1?c.join(" "):n}function HT(u,f){let l=u?.part&&typeof u.part==="object"&&!Array.isArray(u.part)?u.part:{},r=String(u?.type||u?.event||u?.name||l?.type||"").toLowerCase(),n=String(l?.type||"").toLowerCase(),i=u?.at||u?.timestamp||l?.updatedAt||l?.createdAt,y=Number.isFinite(Number(u?.seq))?Number(u.seq):f;if(r==="step_start"||r==="step-start"||n==="step-start")return null;if(r==="step_finish"||r==="step-finish"||n==="step-finish")return null;if(n==="tool"||/tool|bash|command/iu.test(`${r} ${n}`)){let $=l?.state&&typeof l.state==="object"&&!Array.isArray(l.state)?l.state:{},c=DU(l,$),A=VU($,l,u),j=XU(c,String(l?.tool||l?.title||"")),F=j==="edited"?{status:String($?.status||l?.status||u?.status||""),summary:pi(A||c,72),files:X3(A),stages:[],lines:Ec(A),addedLines:0,removedLines:0,rawText:A}:void 0;return{seq:y,at:D_(l,i),kind:j,title:String($?.title||l?.title||$?.metadata?.description||l?.tool||"OpenCode tool"),status:String($?.status||l?.status||u?.status||""),commandPreview:c,bodyPreview:A,durationMs:V_(l),rawSeqs:[l?.id||l?.callID||u?.sessionID||y],editObservation:F}}let _=OT(l?.text??l?.content??l?.delta??u?.text??u?.content??u?.delta,3000).trim();if(_.length>0)return{seq:y,at:D_(l,i),kind:n==="reasoning"?"message":/error|failed/iu.test(`${r} ${n}`)?"error":"message",title:n==="reasoning"?"Reasoning":/error|failed/iu.test(`${r} ${n}`)?"OpenCode error":"Assistant message",status:`opencode/${r||n||"event"}`,bodyPreview:_,durationMs:V_(l),rawSeqs:[l?.id||u?.sessionID||y]};return null}function XU(u,f){let l=`${f} ${u}`.toLowerCase();if(/\b(read|grep|glob|list|ls|find|search|view|cat|sed|rg|head|tail|wc|file)\b/iu.test(l))return"explored";if(/\b(edit|write|patch|apply|update|create|delete|apply_patch|git apply|cat >|tee .*<<|sed -i|python3? .*write_text|mkdir|rm |touch )\b/iu.test(l))return"edited";return"ran"}function BT(u){let f=[],l=1;for(let r of Array.isArray(u)?u:[]){if(r?.kind&&r?.title){let _=String(r?.status||"");if(_==="opencode/step-start"||_==="opencode/step-finish")continue;f.push({...r,seq:Number.isFinite(Number(r?.seq))?Number(r.seq):l++});continue}let n=r?.createdAt||r?.updatedAt||r?.completedAt,i=String(r?.role||"assistant").toLowerCase(),y=Array.isArray(r?.parts)?r.parts:[];if(y.length===0){if(r?.part&&(r?.sessionID||String(r?.type||"").startsWith("step_")||String(r?.type||"").includes("tool"))){let _=HT(r,l);if(_!==null)f.push(_),l=Math.max(l+1,Number(_.seq)+1)}else if(r?.textPreview)f.push({seq:l++,at:n,kind:"message",title:`${i||"assistant"} message`,status:i,bodyPreview:String(r.textPreview),rawSeqs:[r?.messageId||l]});continue}for(let _ of y){let $=String(_?.type||"").toLowerCase();if($==="step-start"||$==="step-finish")continue;if($==="text"||$==="reasoning"){let A=String(_?.textPreview||r?.textPreview||"").trim();if(A.length===0)continue;f.push({seq:l++,at:D_(_,n),kind:"message",title:$==="reasoning"?"Reasoning":i==="user"?"User message":i==="system"?"System message":"Assistant message",status:$==="reasoning"?"reasoning":i,bodyPreview:A,durationMs:V_(_),rawSeqs:[Wc(_,l)]});continue}if($==="tool"){let A=_?.state&&typeof _.state==="object"&&!Array.isArray(_.state)?_.state:{},j=DU(_,A),F=VU(A,_,{}),U=XU(j,String(_?.tool||_?.title||"")),Q=U==="edited"?{status:String(A?.status||_?.status||""),summary:pi(F||j,72),files:X3(F),stages:[],lines:Ec(F),addedLines:0,removedLines:0,rawText:F}:void 0;f.push({seq:l++,at:D_(_,n),kind:U,title:String(_?.title||_?.tool||"tool"),status:String(_?.status||""),commandPreview:j,bodyPreview:F,durationMs:V_(_),rawSeqs:[Wc(_,l)],editObservation:Q});continue}let c=String(_?.textPreview||_?.title||$||"").trim();if(c)f.push({seq:l++,at:D_(_,n),kind:"system",title:$||"part",bodyPreview:c,status:String(_?.status||""),durationMs:V_(_),rawSeqs:[Wc(_,l)]})}}return f}var YU={source:"opencode",toTrace:BT};function VT(u){return String(u||"unknown").toLowerCase().replace(/[^a-z0-9_-]+/gu,"-")||"unknown"}function wU(u){let f=String(u||"M").toUpperCase();if(f.startsWith("A")||f==="??")return"added";if(f.startsWith("D"))return"deleted";if(f.startsWith("R"))return"renamed";return"modified"}function DT(u){if(u==="item/fileChange/outputDelta")return"delta";return u.replace(/^item\//u,"")}function XT(u,f){if(u.kind==="file"){let n=String(u.status||"M");return Yu("div",{key:`${f}-${u.text}`,className:`codex-edit-diff-line file ${wU(n)}`},Yu("span",{className:`codex-edit-file-status ${wU(n)}`},n),Yu("code",null,u.path||u.text.replace(/^([AMDRCU?]{1,2})\s+/u,"")))}let l=u.kind==="add"||u.kind==="del"?u.text.slice(0,1):u.kind==="hunk"?"@@":u.kind==="note"?"ok":"",r=u.kind==="add"||u.kind==="del"?u.text.slice(1):u.text;return Yu("div",{key:`${f}-${u.text}`,className:`codex-edit-diff-line ${u.kind}`},Yu("span",{className:"codex-edit-diff-sign"},l),Yu("code",null,r||" "))}function YT(u,f){let l=u.lines.length>0?u.lines:u.files.map((n)=>({text:`${n.status} ${n.path}`,kind:"file",path:n.path,status:n.status})),r=Number(u.addedLines||0)+Number(u.removedLines||0)>0;return Yu("div",{className:"codex-edit-observation","data-testid":"codex-edit-observation"},Yu("div",{className:"codex-edit-observation-head"},Yu("span",{className:"codex-edit-window-controls","aria-hidden":"true"},Yu("i",null),Yu("i",null),Yu("i",null)),Yu("strong",null,r?"git diff":"git diff --stat"),Yu("code",null,u.summary||"File changes")),u.stages.length>0?Yu("div",{className:"codex-edit-stage-strip"},u.stages.map((n,i)=>Yu("span",{key:`${n.method}-${i}`,className:`codex-edit-stage ${VT(n.status||n.method)}`},Yu("b",null,DT(n.method)),n.status?Yu("em",null,n.status):null))):null,l.length>0?Yu("div",{className:"codex-edit-diff",role:"list"},l.map(XT)):null,f?Yu("div",{className:"codex-edit-omitted"},`${f} (查看原始JSON获取完整记录)`):null)}function LU(u,f,l){let r=Lc(l);return Yu("div",{className:`codex-transcript-stream ${u}`,"data-testid":`codex-trace-${u}`},Yu("span",{className:"codex-transcript-stream-label"},u),Yu("pre",{className:"codex-transcript-body"},f,r?` +${r} (查看原始JSON获取完整记录)`:""))}function SU(u,f=!1){let l=String(u.kind||"message"),r=["ran","explored","edited"].includes(l),n=Lc(u.commandOmittedLines),i=Lc(u.bodyOmittedLines),y=String(u.commandPreview||(r?u.title||"":"")),_=String(u.stdoutPreview||""),$=String(u.stderrPreview||""),c=_.length>0||$.length>0,A=Boolean(u.foldedReferencePrompt)&&String(u.fullPrompt||"").length>0,j=l==="edited"&&(u.editObservation!==void 0||ZU(u))?u.editObservation||OU([u]):null;return Yu("article",{key:`${u.seq}-${l}`,className:`codex-transcript-item ${l} ${f?"nested":""}`},Yu("div",{className:"codex-transcript-main"},Yu("div",{className:"codex-transcript-title"},Yu("span",{className:"codex-output-channel"},wc(l)),r&&j===null?null:Yu("strong",null,j!==null?"File changes":String(u.title||wc(l))),u.status?Yu("code",null,String(j?.status||u.status)):null,Yu("time",null,Nu(u.at))),y&&j===null?Yu("pre",{className:"codex-transcript-command"},y,n?` +${n}`:""):null,j!==null?YT(j,i):c?Yu("div",{className:"codex-transcript-streams"},_.length>0?LU("stdout",_,u.stdoutOmittedLines):null,$.length>0?LU("stderr",$,u.stderrOmittedLines):null):u.bodyPreview?Yu("pre",{className:"codex-transcript-body"},String(u.bodyPreview),i?` +${i} (查看原始JSON获取完整记录)`:""):null,A?Yu("details",{className:"codex-initial-prompt-full","data-testid":"codex-initial-prompt-full"},Yu("summary",null,Yu("span",null,"引用注入已折叠,点击查看最终传入 Codex 的完整 prompt"),Yu("code",null,`${u.fullPromptLines||QT(String(u.fullPrompt||""))} lines / ${u.fullPromptChars||String(u.fullPrompt||"").length} chars`)),Yu("pre",{className:"codex-transcript-body codex-transcript-full-prompt","data-testid":"codex-initial-prompt-full-text"},String(u.fullPrompt||""))):null))}function ST(u){let f=Array.isArray(u.items)?u.items:[],l=u.digest&&typeof u.digest==="object"?u.digest:HU(f);return Yu("article",{key:`${u.seq}-toolGroup`,className:"codex-transcript-item toolGroup"},Yu("div",{className:"codex-transcript-main"},Yu("details",{className:"codex-tool-group","data-testid":"codex-tool-group"},Yu("summary",null,Yu("div",{className:"codex-tool-group-head"},Yu("span",{className:"codex-output-channel"},wc("toolGroup")),Yu("strong",null,String(u.title||zU(f))),Yu("code",null,String(u.status||`${f.length} calls`)),Yu("time",null,Nu(u.at)))),Yu("div",{className:"codex-tool-group-digest"},Yu("span",null,`read: ${Nc(Array.isArray(l.readFiles)?l.readFiles:[])}`),Yu("span",null,`edit: ${Nc(Array.isArray(l.editedFiles)?l.editedFiles:[])}`),Yu("span",null,`run: ${Nc(Array.isArray(l.runCommands)?l.runCommands:[],2)}`),Yu("span",null,`duration: ${l.durationLabel||"--"}`)),Yu("div",{className:"codex-tool-group-items"},f.map((r)=>SU(r,!0))))))}var mT=16;function KU(u){return u.scrollHeight-u.scrollTop-u.clientHeight<=mT}function Y3({items:u,input:f,port:l,autoScroll:r=!1,loading:n=!1,hasDetail:i=!0,emptyText:y="等待 Trace 输出...",loadingText:_="正在加载完整 Trace...",testId:$="trace-output",className:c="codex-transcript",keepRecentToolCalls:A=3,collapseTools:j=!0}){let F=JU(null),U=JU(!0),Q=ET(l?JT(l,f):BU(u)),w=j?ZT(Q,A):Q,L=qT(Q);FT(()=>{let q=F.current;if(!r||!q)return;if(!U.current&&!KU(q))return;q.scrollTop=q.scrollHeight,U.current=!0},[r,Q.length,L]);let N={className:c,ref:F,onScroll:(q)=>{let W=q.currentTarget;U.current=KU(W)},"data-testid":$};if(n&&!i)return Yu("div",N,Yu("div",{className:"codex-output-empty"},_));return Yu("div",N,w.length===0?Yu("div",{className:"codex-output-empty"},y):w.map((q)=>String(q.kind||"")==="toolGroup"?ST(q):SU(q)))}var mU=Mu(Qf(),1),N1=mU.default.createElement;function S3(...u){return u.filter(Boolean).join(" ")}function pU({title:u,children:f,actions:l,onClose:r,className:n,backdropClassName:i,bodyClassName:y,actionsClassName:_,titleId:$,testId:c,closeTestId:A,closeLabel:j="关闭",disableClose:F=!1,closeOnBackdrop:U=!0}){let Q={className:S3("raw-dialog","unidesk-dialog",n),role:"dialog","aria-modal":"true"};if($)Q["aria-labelledby"]=$;else if(typeof u==="string")Q["aria-label"]=u;if(c)Q["data-testid"]=c;return N1("div",{className:S3("modal-backdrop","unidesk-dialog-backdrop",i),role:"presentation",onClick:(w)=>{if(U&&!F&&w.target===w.currentTarget)r()}},N1("section",Q,N1("div",{className:"raw-dialog-head unidesk-dialog-head"},N1("strong",$?{id:$}:null,u),N1("button",{type:"button",className:"ghost-btn",onClick:r,disabled:F,"data-testid":A},j)),N1("div",{className:S3("unidesk-dialog-body",y)},f),l?N1("div",{className:S3("unidesk-dialog-actions",_)},l):null))}var K=P_.default.createElement,{useEffect:Gn,useMemo:PU,useRef:Df}=P_.default,Iu=P_.default.useState,pT=120,eU=24,uQ=48,PT=1200;function Zc(){return typeof document>"u"||document.visibilityState!=="hidden"}function $l(u,f="操作失败"){return Hu(u,f)}function Rr(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";let l=Math.floor(f/1000),r=Math.floor(l/3600),n=Math.floor(l%3600/60),i=l%60;if(r>0)return`${r}h ${String(n).padStart(2,"0")}m`;if(n>0)return`${n}m ${String(i).padStart(2,"0")}s`;return`${i}s`}function X0(u){if(u===null||u===void 0||u==="")return null;let f=u instanceof Date?u.getTime():new Date(u).getTime();return Number.isFinite(f)?f:null}function fQ(u,f=Date.now()){let l=X0(u);if(l===null)return"--";let r=Math.max(0,Math.floor((f-l)/1000));if(r<1)return"刚刚";let n=Math.floor(r/86400),i=Math.floor(r%86400/3600),y=Math.floor(r%3600/60),_=r%60;if(n>0)return`${n}天${i>0?`${i}小时`:""}前`;if(i>0)return`${i}小时${y>0?`${y}分钟`:""}前`;if(y>0)return`${y}分钟${_}秒前`;return`${_}秒前`}function lQ(...u){let f="",l=-1/0;for(let r of u){let n=String(r||"");if(n.length===0)continue;let i=X0(r);if(i!==null&&i>=l)f=n,l=i;else if(f.length===0)f=n}return f}function MT(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";if(f<1000)return`${Math.round(f)}ms`;return`${(f/1000).toFixed(f<1e4?2:1)}s`}function Xc(u,f=180){let l=String(u||"").replace(/\s+/gu," ").trim();return l.length>f?`${l.slice(0,f-1)}…`:l}async function Rf(u,f={}){return Eu(u,{strictJson:!0,retryInvalidJson:1,invalidJsonPrefix:"Code Queue 返回了无效 JSON",invalidJsonPreview:!0,responsePreviewLength:PT,...f})}function K1({status:u,children:f,title:l}){let r=String(u||"unknown").toLowerCase();return K("span",{className:`status-badge ${r}`,title:l},f||u||"unknown")}function Mi({title:u,eyebrow:f,summary:l,actions:r,children:n,className:i,loading:y}){return K("section",{className:`panel ${i||""}`},K("div",{className:"panel-head"},K("div",null,f?K("p",{className:"panel-eyebrow"},f):null,K(nf,{title:u,loading:y}),l?K("div",{className:"panel-summary"},l):null),r?K("div",{className:"panel-actions"},r):null),K("div",{className:"panel-body"},n))}function rQ({title:u,data:f,onOpen:l,testId:r}){return K("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function G1({title:u,text:f}){return K("div",{className:"empty-state"},K("strong",null,u),K("span",null,f))}function CT(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function RT(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function xf(u,f){return`${u}/code-queue-direct${f}`}function xT(u){return`${u}/microservices/oa-event-flow/proxy/api/events/stream?tags=${encodeURIComponent("service:code-queue")}`}function Rc(){return{headers:{"cache-control":"no-cache","x-unidesk-no-cache":"1"}}}function tl(u){return Array.isArray(u?.tasks)?u.tasks:[]}function Kn(u){return u?.pagination&&typeof u.pagination==="object"&&!Array.isArray(u.pagination)?u.pagination:{}}function MU(u){let f=Date.parse(String(u?.updatedAt||u?.createdAt||""));return Number.isFinite(f)?f:0}function CU(u){return X0(u?.queueEnteredAt)??X0(u?.createdAt)??X0(u?.updatedAt)??0}function hT(u,f){let l=CU(u)-CU(f);if(l!==0)return l;let r=(X0(u?.createdAt)??0)-(X0(f?.createdAt)??0);if(r!==0)return r;return String(u?.id||"").localeCompare(String(f?.id||""))}function bT(u,f=""){let l=new Map;for(let r of u)for(let n of r){let i=String(n?.id||"");if(i.length>0&&!l.has(i))l.set(i,n)}return Array.from(l.values()).sort((r,n)=>{let i=sU(r)-sU(n);if(i!==0)return i;let y=String(r?.id||"")===f?0:1,_=String(n?.id||"")===f?0:1;if(y!==_)return y-_;return MU(n)-MU(r)})}function Pi(u,f=""){let l=new Map;for(let r of u)for(let n of r){let i=String(n?.id||"");if(i.length===0)continue;l.set(i,{...l.get(i)||{},...n})}return bT([Array.from(l.values())],f)}function Y_(u){return Array.isArray(u?.activeTaskIds)?u.activeTaskIds.map((f)=>String(f||"")).filter(Boolean):[String(u?.activeTaskId||"")].filter(Boolean)}var Tn="__all__",vT="(max-width: 760px)",kT="(min-width: 761px)";function xl(u){return!u||u===Tn}function IT(){return typeof window<"u"&&window.matchMedia(vT).matches}function gT(u){return xl(u)?"":`&queueId=${encodeURIComponent(u)}`}function Yc(u){return String(u||"").trim().replace(/\s+/gu," ").slice(0,200)}function sT(u){let f=Yc(u);return f.length===0?"":`&search=${encodeURIComponent(f)}`}function xc(u,f=""){return`${gT(u)}${sT(f)}`}function m3(u,f){return Number(u?.counts?.[f]||0)}function RU(u,f=""){let l=new Map;for(let n of Array.isArray(u?.queues)?u.queues:[]){let i=String(n?.id||"").trim();if(i.length>0)l.set(i,{...n,name:String(n?.name||i).trim()||i})}for(let n of[String(u?.defaultQueueId||"default"),f].map((i)=>i.trim()).filter(Boolean))if(!l.has(n))l.set(n,{id:n,name:n,total:0,counts:{},activeTaskId:null,runnableTaskId:null,processing:!1});return Array.from(l.values()).sort((n,i)=>{let y=String(n?.id||"")===String(u?.defaultQueueId||"default")?0:1,_=String(i?.id||"")===String(u?.defaultQueueId||"default")?0:1;if(y!==_)return y-_;return String(n?.id||"").localeCompare(String(i?.id||""))})}function nQ(u){let f=String(u?.id||"default"),l=String(u?.name||"").trim();return l.length>0?l:f}function Sc(u){let f=String(u?.id||"default"),l=nQ(u);return l===f?f:`${l} (${f})`}function S_(u){let f=m3(u,"running")+m3(u,"judging"),l=m3(u,"queued")+m3(u,"retry_wait"),r=Number(u?.total||0),n=[Sc(u),`${r} tasks`];if(f>0)n.push(`${f} running`);if(l>0)n.push(`${l} queued`);return n.join(" · ")}function Ri(u,f){if(xl(f))return null;return u.find((l)=>String(l?.id||"")===f)||null}function xU(u,f,l,r){if(xl(l)){let i=Y_(u);return String(u?.activeTaskId||i[0]||r.find((y)=>IU(y))?.id||"")}let n=Ri(f,l);return String(n?.activeTaskId||r.find((i)=>IU(i))?.id||"")}function aT(u,f,l){if(!xl(f)){let r=Ri(u,f);return String(r?.runnableTaskId||l.find((n)=>String(n?.status||"")==="queued"||String(n?.status||"")==="retry_wait")?.id||"")}return String(l.find((r)=>String(r?.status||"")==="queued"||String(r?.status||"")==="retry_wait")?.id||"")}async function oT(u,f=Tn,l=""){return Rf(xf(u,`/api/tasks/overview?limit=${eU}&transcriptLimit=1&compact=1&selected=0${xc(f,l)}`),Rc())}async function dT(u,f,l=0,r=Tn,n=""){return Rf(xf(u,`/api/tasks/overview?limit=${eU}&transcriptLimit=3&compact=1&afterSeq=${encodeURIComponent(String(Math.max(0,l)))}&preferId=${encodeURIComponent(f)}${xc(r,n)}`),Rc())}async function hU(u,f,l,r=uQ,n=""){return Rf(xf(u,`/api/tasks/overview?limit=${encodeURIComponent(String(r))}&transcriptLimit=1&compact=1&selected=0&includeActive=0&stats=0&beforeId=${encodeURIComponent(l)}${xc(f,n)}`),Rc())}async function eT(u,f){return Rf(xf(u,`/api/tasks/${encodeURIComponent(f)}/trace-summary`))}async function uE(u,f,l,r=null){let n=r===null||r===void 0||String(r).length===0?"":`&attempt=${encodeURIComponent(String(r))}`;return Rf(xf(u,`/api/tasks/${encodeURIComponent(f)}/prompt?part=${encodeURIComponent(l)}${n}`))}async function fE(u,f,l=0,r=500,n=null){let i=n===null||n===void 0||String(n).length===0?"":`&attempt=${encodeURIComponent(String(n))}`;return Rf(xf(u,`/api/tasks/${encodeURIComponent(f)}/trace-steps?afterSeq=${encodeURIComponent(String(l))}&limit=${encodeURIComponent(String(r))}${i}`))}async function lE(u,f,l){return Rf(xf(u,`/api/tasks/${encodeURIComponent(f)}/trace-step?seq=${encodeURIComponent(String(l))}`))}async function rE(u,f){return Rf(xf(u,`/api/tasks/${encodeURIComponent(f)}/read`),{method:"POST",body:{}})}async function nE(u){return Rf(xf(u,"/api/tasks/read-all"),{method:"POST",body:{}})}function iE(u){return Array.isArray(u?.output)?u.output:[]}function yE(u){return Array.isArray(u?.attempts)?u.attempts:[]}function Oc(u){return u?.counts&&typeof u.counts==="object"&&!Array.isArray(u.counts)?u.counts:{}}function _E(u){return u.split(/^\s*---+\s*$/gmu).map((f)=>f.trim()).filter(Boolean)}function bU(u){let f=Number(u);return Number.isFinite(f)?Math.max(1,Math.min(50,Math.floor(f))):1}function W1(u){let f=[];for(let l of u.split(/[\s,,;;]+/u)){let r=l.trim();if(/^codex_\d+_[A-Za-z0-9_-]+$/u.test(r)&&!f.includes(r))f.push(r)}return f}function $E(u,f){let l=W1(f);if(l.length===0)return u;return[`引用 Code Queue 任务 ${l.join(" ")}。后端会在入队时只注入这些任务的 initial prompt 和 final response 全文;中间执行过程不注入,如需补充核查可运行:${l.map((r)=>`bun scripts/cli.ts codex task ${r}`).join(";")}`,"","本次任务:",u].join(` +`)}function tE(u){let r=u.trimStart();if(!r.startsWith("# Code Queue 已解析引用上下文"))return{hasInjection:!1,reference:"",userPrompt:u};let n=u.length-r.length,i=u.lastIndexOf(` # 本次任务 -`);if(i0?u.split(/\r\n|\r|\n/u).length:0}function mq(u){let f=String(u?.displayPrompt||"");if(f.length>0)return f;let l=String(u?.prompt||"");return ST(tT(l).userPrompt)}function yr(u){return u?._traceSummary&&typeof u._traceSummary==="object"&&!Array.isArray(u._traceSummary)?u._traceSummary:null}function t6(u){return u?._promptDetails&&typeof u._promptDetails==="object"&&!Array.isArray(u._promptDetails)?u._promptDetails:{}}function XA(u){let f=yr(u)?.prompt;return f&&typeof f==="object"&&!Array.isArray(f)?f:{}}function pq(u){let f=yr(u)?.execution;return f&&typeof f==="object"&&!Array.isArray(f)?f:{}}function Hn(u){let f=Number(u);return Number.isFinite(f)&&f>=0?Math.floor(f):0}function PT(u){let f=yr(u);if(f!==null)return MT(f);let l=Number(u?.stepCount??u?.llmStepCount??0);return Number.isFinite(l)&&l>=0?Math.floor(l):0}function MT(u){let f=Tl(u?.execution)||{},l=Number(u?.stepCount??u?.llmStepCount??f.stepCount??f.llmStepCount??f.toolCallCount??0);return Hn(l)}function B6(u){if(!u||u?._traceSummaryLoaded!==!0)return!1;let f=yr(u),l=String(u?._traceSummaryUpdatedAt||f?.updatedAt||""),r=String(u?.updatedAt||"");if(r.length>0){let y=H0(l),i=H0(r);if(y!==null&&i!==null){if(y+10?l:mq(u)}function Cq(u){let f=yr(u);return String(f?.finalResponse||u?.finalResponse||"").trimEnd()}function xq(u){let l=yr(u)?.lastJudge||u?.lastJudge;return l&&typeof l==="object"&&!Array.isArray(l)?l:null}function Tl(u){return u&&typeof u==="object"&&!Array.isArray(u)?u:null}function mT(u){let f=yr(u)?.attempts;if(Array.isArray(f)&&f.length>0)return f;let l=pq(u),r=Cq(u),y=xq(u);if(Object.keys(l).length===0&&r.length===0&&y===null)return[];return[{index:Number(u?.currentAttempt||1),mode:u?.currentMode||"initial",startedAt:u?.startedAt,finishedAt:u?.finishedAt,terminalStatus:u?.status,execution:l,finalResponse:r,finalResponseChars:r.length,judge:y}]}function pT(u,f){return Tl(f?.execution)||pq(u)}function CT(u,f,l,r){let y=yr(u),i=Number(y?.currentAttempt||u?.currentAttempt||0),_=Number(l),n=Number.isFinite(_)&&_>0&&_===i,$=Sq(u?.updatedAt,y?.updatedAt);if(n&&!f?.finishedAt&&$.length>0)return $;return String(f?.updatedAt||f?.finishedAt||r.effectiveEndAt||(n?$:"")||$||u?.finishedAt||u?.startedAt||"")}function xT(u,f){let l=String(f?.finalResponse||f?.finalResponsePreview||"");if(Object.prototype.hasOwnProperty.call(f||{},"finalResponse")||Object.prototype.hasOwnProperty.call(f||{},"finalResponsePreview"))return l.trimEnd();return l.length>0?l.trimEnd():Cq(u)}function Rq(u,f){if(Object.prototype.hasOwnProperty.call(f||{},"judge"))return Tl(f?.judge);return xq(u)}function RT(u,f,l){if(!yZ(u))return!1;if(YA(f,l))return!1;if(f?.finishedAt)return!1;if(["succeeded","failed","canceled"].includes(String(f?.terminalStatus||"")))return!1;let r=yr(u),y=Number(r?.currentAttempt||u?.currentAttempt||0),i=Number(l);if(Number.isFinite(i)&&i>0&&Number.isFinite(y)&&y>0)return i===y;return!0}function hq(u){return`feedback:${String(u||"latest")}`}function hT(u,f,l){let r=String(f?.feedbackPrompt||"").trimEnd(),y=String(f?.feedbackPromptPreview||r||"").trimEnd(),i=Number(f?.feedbackPromptChars||r.length||y.length||0),_=Number(f?.feedbackPromptLines||Ay(r||y));if(r.length>0||y.length>0||i>0)return{text:r,preview:y,chars:i,lines:_,source:f?.feedbackPromptSource||"judge-feedback",forAttempt:f?.feedbackPromptForAttempt||Number(l||0)+1,truncated:Boolean(f?.feedbackPromptTruncated)};let n=Rq(u,f),$=String(n?.continuePrompt||"").trimEnd();if(n?.decision==="retry"&&$.length>0)return{text:"",preview:$,chars:$.length,lines:Ay($),source:"judge-continue-prompt",forAttempt:Number(l||0)+1,truncated:!1};return null}function bT(u){let f=XA(u);return Boolean(f.hasReferenceInjection||Number(f.referencePromptChars||0)>0||u?.referenceInjection||u?.referenceInjectionSummary)}function bq(u,f=null){if(f!==null&&f!==void 0){let r=(Tl(u?._traceStepsByAttempt)||{})[String(f)];return Array.isArray(r)?r:[]}return Array.isArray(u?._traceSteps)?u._traceSteps:[]}function EA(u){let l=[u?.seq,...Array.isArray(u?.rawSeqs)?u.rawSeqs:[]].map((r)=>Number(r)).filter((r)=>Number.isFinite(r));return l.length>0?Math.max(...l):0}function vT(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>Math.max(f,EA(l)),0)}function IT(u,f){let l=new Map;for(let r of[...Array.isArray(u)?u:[],...Array.isArray(f)?f:[]]){let y=String(r?.seq??`${r?.title||"step"}:${r?.at||""}`);l.set(y,{...l.get(y)||{},...r})}return Array.from(l.values()).sort((r,y)=>EA(r)-EA(y))}function Hi(u){return(Array.isArray(u?.summaryLines)?u.summaryLines:[]).map((f)=>String(f||""))}function vq(u){let f=String(u?.kind||"").trim().toLowerCase(),l=String(u?.status||"").trim().toLowerCase();return f==="error"||l==="error"}function kT(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>f+(vq(l)?1:0),0)}function S6(u){let f=String(u?.status||"").trim();if(f.length>0)return f;let l=Hi(u).join(` -`);return/^(item\/[A-Za-z]+(?:\/[A-Za-z]+)?):/u.exec(l)?.[1]||""}function Tq(u){return/^item\/(?:started|completed): file changes status=/u.test(String(u||"").trim())}function gT(u){let f=Hi(u);for(let r=f.length-1;r>=0;r-=1){let y=/file changes status=([A-Za-z0-9_-]+)/u.exec(f[r]||"")?.[1];if(y)return y}let l=S6(u);if(l==="item/fileChange/outputDelta")return"updated";if(l==="item/started")return"started";if(l==="item/completed")return"completed";return l.replace(/^item\//u,"")||String(u?.status||"changed")}function sT(u){if(String(u?.kind||"")!=="edited")return!1;let f=String(u?.title||""),l=String(u?.status||""),r=Hi(u).join(` -`);if(f==="Edited files")return!0;if(/^item\/fileChange\//u.test(l))return!0;if((l==="item/started"||l==="item/completed")&&/file changes status=/u.test(r))return!0;if(/^Success\. Updated the following files:/mu.test(r))return!0;if(/^diff --git /mu.test(r))return!0;return/^([AMDRCU?]{1,2})\s+\S+/mu.test(r)}function aT(u){if(u.length<=1)return u[0];let f=u.find((i)=>S6(i)==="item/fileChange/outputDelta")||u.find((i)=>Hi(i).some((_)=>!Tq(_)))||u.at(-1)||u[0],l=u.flatMap((i)=>Array.isArray(i?.rawSeqs)?i.rawSeqs:[i?.seq]).filter((i)=>i!==void 0),r=u.flatMap(Hi).filter((i)=>i.trim().length>0&&!Tq(i)),y=u[u.length-1]||f;return{...f,at:f?.at||y?.at,title:String(f?.title||"Edited files"),status:gT(y),summaryLines:r.length>0?r:Hi(f),rawSeqs:l}}function oT(u){let f=Array.isArray(u)?u:[],l=[],r=[],y=()=>{if(r.length>0)l.push(aT(r));r=[]};for(let i of f){if(sT(i)){if(S6(i)==="item/started"&&r.length>0)y();if(r.push(i),S6(i)==="item/completed")y();continue}y(),l.push(i)}return y(),l}function dT(u){let f=Hn(u?.stepCount??u?.llmStepCount??u?.toolCallCount);return{...u,stepCount:f,llmStepCount:f}}function TA(u,f=null){if(f!==null&&f!==void 0){let l=Tl(u?._traceStepsLoadedByAttempt)||{};return Boolean(l[String(f)])}return Boolean(u?._traceStepsLoaded)}function ZA(u){return u?._traceStepDetails&&typeof u._traceStepDetails==="object"&&!Array.isArray(u._traceStepDetails)?u._traceStepDetails:{}}function eT(u,f){let l=Number(u?.index);return Number.isFinite(l)?l:f+1}function YA(u,f){return Boolean(u?.synthetic)||Number(f)<=0}function P6(u){let f=Number(u);return Number.isFinite(f)?String(f):void 0}function uZ(u){let f=u?.timing&&typeof u.timing==="object"?u.timing:{},l=String(u?.status||"");if(["queued"].includes(l))return`等待 ${Mr(f.queueWaitMs??f.totalElapsedMs)}`;if(["running","judging","retry_wait"].includes(l))return`耗时 ${Mr(f.durationMs??f.totalElapsedMs)}`;return`耗时 ${Mr(f.durationMs??f.totalElapsedMs)}`}function Tn(u){return String(u?.queueId||"default")}function Iq(u){return Tl(u?.queuedReason)}function kq(u){let f=String(u?.queuedReasonLabel||"").trim();if(f.length>0)return f.toUpperCase();let l=Iq(u),r=String(l?.label||"").trim();return r.length>0?r.toUpperCase():""}function fZ(u){let f=String(u?.status||"unknown");if(f!=="queued")return f;let l=kq(u);return l.length>0?`QUEUED(${l})`:"QUEUED"}function lZ(u){if(String(u?.status||"")!=="queued")return;let f=Iq(u),l=String(f?.message||"").trim(),r=kq(u);if(l.length>0&&r.length>0)return`${r}: ${l}`;if(l.length>0)return l;return r.length>0?r:void 0}function rZ(u){return{system:"SYS",user:"YOU",assistant:"GPT",reasoning:"THINK",command:"CMD",diff:"DIFF",tool:"TOOL",error:"ERR"}[u]||u.toUpperCase()}function Zq(u){return["running","judging","retry_wait"].includes(String(u?.status||""))}function yZ(u){return String(u?.status||"")==="running"}function Pr(u){return["succeeded","failed","canceled"].includes(String(u?.status||""))}function gq(u){if(u?.promptEditable===!0)return!0;if(u?.promptEditable===!1)return!1;return String(u?.status||"")==="queued"&&!u?.startedAt&&Number(u?.currentAttempt||0)===0&&!u?.codexThreadId&&!u?.nextMode}function Z0(u){if(!Pr(u))return!1;if(u?.terminalUnread===!0)return!0;if(u?.terminalUnread===!1)return!1;return!u?.readAt}function Mf(u){let f=Number(u||0);return Number.isFinite(f)?f:0}function iZ(u){return Mf(u.queued)+Mf(u.retry_wait)}function _Z(u){return Mf(u.running)+Mf(u.judging)}function nZ(u,f){return Tl(u?.statistics)||Tl(f?.statistics)||{}}function $Z(u){return Array.isArray(u?.daily)?u.daily:[]}function AZ(u){return Tl(u?.totals)||{}}function DA(u,f){let l=Number(u?.[f]??0);return Number.isFinite(l)&&l>0?l:0}function WA(u,f){return u.reduce((l,r)=>Math.max(l,DA(r,f)),0)}var $y=700,Hq=220,q1=30,Zi=24,Zn=184,HA=Zn-Zi;function sq(u,f){if(f<=1)return $y/2;return q1+u*($y-q1*2)/(f-1)}function aq(u,f){let l=f>0?f:1;return Zn-Math.min(1,u/l)*HA}function cA(u,f,l){let r=u.length>0?u:[{[f]:0}],y=r.length>1?r:[r[0],r[0]];return y.map((i,_)=>`${sq(_,y.length).toFixed(2)},${aq(DA(i,f),l).toFixed(2)}`).join(" ")}function T0(u){let f=String(u||"");return/^\d{4}-\d{2}-\d{2}$/u.test(f)?f.slice(5):f||"--"}function V6(u){if(!u)return"";return`${String(u.seriesKey||"")}:${String(u.row?.date||u.index||"")}`}function jZ(u,f,l,r){let y=DA(u,r.key);return{...r,row:u,index:f,value:y,valueLabel:r.format(y),x:sq(f,l),y:aq(y,r.max),seriesKey:r.key}}function Oq(u){if(Z0(u))return 0;return{running:1,judging:2,retry_wait:3,queued:4,succeeded:8,failed:8,canceled:8}[String(u?.status||"")]??9}function Ln(u){if(!u)return!1;if(u?._traceSummaryLoaded===!0)return!1;return u?.summaryOnly===!0||u?._metaLoaded!==!0}function FZ(u){return Boolean(u?._metaLoaded)||u?.summaryOnly===!1}function JZ(u,f,l){let r=String(u?.[l]||""),y=String(f?.[l]||"");return r.length>y.length?r:y}function OA(u,f,l){let r=Array.isArray(u?.[l])?u[l]:[],y=Array.isArray(f?.[l])?f[l]:[];if(y.length===0&&r.length>0)return r;return r.length>y.length?r:y}function Bq(u,f){let l=f?.summaryOnly===!0&&FZ(u),r={...u,...f};if(!l)return r;for(let y of["prompt","basePrompt","displayPrompt","finalResponse"])r[y]=JZ(u,f,y);for(let y of["promptHistory","attempts","output","events"])r[y]=OA(u,f,y);if(u?.referenceInjection?.items&&!f?.referenceInjection?.items)r.referenceInjection=u.referenceInjection;if(u?.referenceInjectionSummary&&!f?.referenceInjectionSummary)r.referenceInjectionSummary=u.referenceInjectionSummary;r.summaryOnly=u?.summaryOnly===!1?!1:f.summaryOnly,r._metaLoaded=u?._metaLoaded,r._detailLoaded=u?._detailLoaded,r._transcriptComplete=u?._transcriptComplete,r._transcriptPreview=Object.prototype.hasOwnProperty.call(f,"_transcriptPreview")?f._transcriptPreview:u?._transcriptPreview;for(let y of["_traceSummary","_traceSummaryLoaded","_traceSteps","_traceStepsLoaded","_traceStepsByAttempt","_traceStepsLoadedByAttempt","_traceStepsNextAfterSeqByAttempt","_traceStepDetails","_promptDetails"])if(!Object.prototype.hasOwnProperty.call(f,y)&&Object.prototype.hasOwnProperty.call(u||{},y))r[y]=u[y];return r}function UZ(u){let f=u?.selected,l=f?.task&&typeof f.task==="object"?f.task:null;if(l!==null){let y=Boolean(f?.preview);return{...l,transcript:Array.isArray(f?.transcript)?f.transcript:[],_detailLoaded:Array.isArray(f?.transcript)&&f.transcript.length>0,_transcriptComplete:Boolean(!y&&!f?.hasMore&&Pr(l)),_transcriptPreview:y,_summaryLoaded:!0}}let r=nl(u)[0];return r?{...r,_summaryLoaded:!0}:null}function NA(u,f){let l=new Map;for(let r of[...Array.isArray(u)?u:[],...Array.isArray(f)?f:[]]){let y=`${Number(r?.seq??0)}:${String(r?.kind||"message")}`,i=l.get(y);if(!i){l.set(y,r);continue}let _={...i,...r};for(let[n,$]of[["bodyPreview","bodyOmittedLines"],["commandPreview","commandOmittedLines"]]){let j=String(i?.[n]||""),F=String(r?.[n]||"");if(j.length>F.length)_[n]=i[n],_[$]=i[$]}l.set(y,_)}return Array.from(l.values()).sort((r,y)=>Number(r?.seq??0)-Number(y?.seq??0))}function X6(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>Math.max(f,Number(l?.seq??0)),0)}function Vq(u,f=8){let l=Array.from(new Set((Array.isArray(u)?u:[]).map((y)=>Number(y?.seq??0)).filter((y)=>Number.isFinite(y)&&y>0))).sort((y,i)=>y-i);if(l.length===0)return 0;let r=l[Math.max(0,l.length-f)]??0;return Math.max(0,r-0.001)}function QZ(u,f){let l=Array.isArray(u?.codeModels)?u.codeModels:Array.isArray(u?.codexModels)?u.codexModels:[],r=["gpt-5.5","gpt-5.4-mini","gpt-5.4","minimax-m2.7"];return Array.from(new Set([...l,...r,f].map((y)=>String(y||"").trim()).filter(Boolean)))}function qZ(u,f){let r=(Array.isArray(u?.executionProviders)?u.executionProviders:[]).map((_)=>({id:String(_?.id||"").trim(),label:String(_?.label||_?.id||"").trim(),defaultWorkdir:String(_?.defaultWorkdir||"").trim(),kind:String(_?.kind||"").trim()})).filter((_)=>_.id.length>0),y=String(u?.mainProviderId||u?.defaultProviderId||"main-server").trim()||"main-server",i=new Map;for(let _ of[...r,{id:y,label:`${y} (master)`,defaultWorkdir:String(u?.defaultWorkdir||"/root/unidesk"),kind:"local"},f?{id:f,label:f,defaultWorkdir:D6(u,f),kind:""}:null].filter(Boolean))if(!i.has(_.id))i.set(_.id,_);return Array.from(i.values())}function D6(u,f){let l=String(f||"").trim(),r=u?.defaultWorkdirByProvider&&typeof u.defaultWorkdirByProvider==="object"?u.defaultWorkdirByProvider:{};if(typeof r[l]==="string"&&String(r[l]).trim().length>0)return String(r[l]).trim();let y=Array.isArray(u?.executionProviders)?u.executionProviders.find((_)=>String(_?.id||"")===l):null;if(typeof y?.defaultWorkdir==="string"&&y.defaultWorkdir.trim().length>0)return y.defaultWorkdir.trim();let i=String(u?.mainProviderId||u?.defaultProviderId||"main-server");return l===i?String(u?.defaultWorkdir||"/root/unidesk"):String(u?.remoteDefaultWorkdir||"/home/ubuntu")}function WZ(u){return PT(u)}function cZ({task:u,selected:f,onSelect:l,onCopy:r,onReference:y,onMarkRead:i,copied:_,markingRead:n}){let $=u?.lastJudge||{},j=String(u?.id||""),F=Z0(u),J=Sq(u?.updatedAt,yr(u)?.updatedAt),U=`最近更新: ${tq(J)}`,q=WZ(u);return E("article",{role:"button",tabIndex:0,className:`codex-task-card ${f?"selected":""} ${F?"unread-terminal":""}`,onClick:l,onKeyDown:(W)=>{if(W.key==="Enter"||W.key===" ")W.preventDefault(),l()},"data-unread-terminal":F?"true":"false","data-testid":`codex-task-${u?.id||"unknown"}`},F?E("span",{className:"codex-unread-badge",title:"待读","aria-label":"待读","data-testid":`codex-unread-task-${j||"unknown"}`}):null,E("div",{className:"codex-task-card-head"},E("div",{className:"codex-task-status-line"},E(jy,{status:u?.status,title:lZ(u)},fZ(u))),E("span",{className:"mono-text"},`${u?.currentAttempt||0}/${u?.maxAttempts||0}`)),E("div",{className:"codex-task-id-row"},E("code",{title:j},j||"unknown"),E("div",{className:"codex-task-id-actions"},E("button",{type:"button",className:"codex-copy-id-btn",onClick:(W)=>{W.stopPropagation(),y(j)},"data-testid":`codex-reference-task-${j||"unknown"}`},"引用"),E("button",{type:"button",className:"codex-copy-id-btn",onClick:(W)=>{W.stopPropagation(),r(j)},"data-testid":`codex-copy-task-id-${j||"unknown"}`},_?"已复制":"复制ID"),F?E("button",{type:"button",className:"codex-copy-id-btn codex-mark-read-btn",disabled:Boolean(n),onClick:(W)=>{W.stopPropagation(),i(j)},"data-testid":`codex-mark-task-read-${j||"unknown"}`},n?"标记中":"标为已读"):null)),E("strong",null,GA(mq(u),120)||"空任务"),E("div",{className:"codex-task-meta"},E("span",null,`queue=${Tn(u)}`),E("span",null,`provider=${u?.providerId||"main-server"}`),E("span",null,u?.model||"--"),E("span",null,uZ(u))),E("div",{className:"codex-task-meta codex-task-update-meta"},E("span",{className:"codex-task-recent-update codex-task-step-count",title:"STEP 按 read/edit/run 工具动作统计","data-testid":`codex-task-step-count-${j||"unknown"}`},`STEP ${q}`),E("span",{className:"codex-task-recent-update",title:J?`更新时间: ${Nu(J)}`:U,"data-testid":`codex-task-recent-update-${j||"unknown"}`},U),E("span",null,Nu(J||u?.updatedAt))),gq(u)?E("div",{className:"codex-judge-line","data-testid":`codex-task-prompt-editable-${j||"unknown"}`},"queued prompt 可编辑"):null,$?.decision?E("div",{className:"codex-judge-line"},`judge=${$.decision} ${Math.round(Number($.confidence||0)*100)}%`):null)}function zA({title:u,tasks:f,selectedId:l,onSelect:r,onCopy:y,onReference:i,onMarkRead:_,copiedTaskId:n,markingReadTaskId:$,emptyText:j}){let F=Array.isArray(f)?f:[];return E("section",{className:"codex-task-section"},E("div",{className:"codex-task-section-head"},E("span",null,u),E("code",null,String(F.length))),F.length===0?E("p",{className:"codex-task-section-empty"},j):E("div",{className:"codex-task-section-list"},F.map((J)=>E(cZ,{key:J.id,task:J,selected:l===J.id,onSelect:()=>r(J.id),onCopy:y,onReference:i,onMarkRead:_,copied:n===J.id,markingRead:$===J.id}))))}function NZ(){return E("span",{className:"codex-stats-icon","aria-hidden":"true"},E("svg",{viewBox:"0 0 36 24",focusable:"false"},E("path",{className:"grid",d:"M3 20.5H33M3 12.5H33M3 4.5H33"}),E("polyline",{className:"line tasks",points:"3,18 9,14 15,15 21,8 27,10 33,4"}),E("polyline",{className:"line retry",points:"3,20 9,17 15,18 21,13 27,14 33,9"})))}function zZ({stats:u,queueName:f,onRaw:l}){let r=$Z(u),y=AZ(u),i=r.at(-1)||{},_=WA(r,"executedTasks"),n=WA(r,"retryAttempts"),$=WA(r,"avgDurationMs"),j=r.length>0,F=Tl(u?.range)||{},[J,U]=su(null),[q,W]=su(null),G=[];if(_>0)G.push(`tasks ${_}`);if(n>0)G.push(`retry ${n}`);if($>0)G.push(`avg ${Mr($)}`);let K=[{key:"executedTasks",className:"tasks",label:"执行任务",max:_,format:(T)=>`${Mf(T)} tasks`},{key:"retryAttempts",className:"retry",label:"重试次数",max:n,format:(T)=>`${Mf(T)} retries`},{key:"avgDurationMs",className:"duration",label:"平均耗时",max:$,format:(T)=>Mr(T)}],Q=J||q,N=V6(Q),c=String(Q?.row?.date||""),z=Q?{left:`${Math.max(8,Math.min(92,Number(Q.x)/$y*100))}%`,top:`${Math.max(14,Math.min(86,Number(Q.y)/Hq*100))}%`}:void 0;Q1(()=>{U(null),W(null)},[f,F.startDate,F.endDate,r.length]);let w=(T)=>{U(T)},H=(T)=>{let X=V6(T);W((R)=>V6(R)===X?null:T),U(T)},B=K.flatMap((T)=>r.map((X,R)=>{let Y=jZ(X,R,r.length,T),P=V6(Y),t=N===P,O=String(X?.date||`day-${R}`),M=`${T0(O)} ${T.label}: ${Y.valueLabel}`;return E("g",{key:`${T.key}-${O}`,className:`stat-point-group ${T.className} ${t?"active":""}`,role:"button",tabIndex:0,"aria-label":M,"data-testid":`codex-stats-point-${T.className}-${O}`,onMouseEnter:()=>w(Y),onFocus:()=>w(Y),onClick:()=>H(Y),onKeyDown:(S)=>{if(S.key==="Enter"||S.key===" ")S.preventDefault(),H(Y)}},E("circle",{className:"stat-hit-point",cx:Y.x,cy:Y.y,r:13}),E("circle",{className:`stat-point ${T.className} ${t?"active":""}`,cx:Y.x,cy:Y.y,r:t?5.6:4.2}))}));return E(Ti,{title:"统计曲线",eyebrow:`Daily task stats / ${f}`,className:"codex-stats-panel",summary:E("span",null,`${T0(F.startDate)} -> ${T0(F.endDate)} · ${u?.timezone||"Asia/Shanghai"}`),actions:Tl(u)?E(Pq,{title:"Code Queue Stats",data:u,onOpen:l,testId:"raw-codex-stats"}):null},E("div",{className:"codex-stats-hero","data-testid":"codex-stats-panel"},E(NZ),E("div",null,E("strong",null,`${Mf(y.executedTasks)} tasks / ${Mf(y.retryAttempts)} retries`),E("span",null,`平均完成耗时 ${Mr(y.avgDurationMs??void 0)} · 终态 ${Mf(y.completedTasks)} 个`))),j?E("div",{className:"codex-stats-chart","data-testid":"codex-stats-chart",onMouseLeave:()=>U(null)},E("svg",{viewBox:`0 0 ${$y} ${Hq}`,preserveAspectRatio:"none",role:"img","aria-label":"Code Queue daily task statistics"},E("line",{className:"axis",x1:q1,x2:$y-q1,y1:Zn,y2:Zn}),E("line",{className:"grid",x1:q1,x2:$y-q1,y1:Zi+HA/2,y2:Zi+HA/2}),E("line",{className:"grid",x1:q1,x2:$y-q1,y1:Zi,y2:Zi}),E("polyline",{className:"stat-line tasks",points:cA(r,"executedTasks",_)}),E("polyline",{className:"stat-line retry",points:cA(r,"retryAttempts",n)}),E("polyline",{className:"stat-line duration",points:cA(r,"avgDurationMs",$)}),Q?E("g",{className:"stat-cursor-layer","data-testid":"codex-stats-active-point"},E("line",{className:"stat-cursor",x1:Q.x,x2:Q.x,y1:Zi,y2:Zn}),E("circle",{className:`stat-point-active ${Q.className}`,cx:Q.x,cy:Q.y,r:8})):null,E("g",{className:"stat-point-layer"},B)),Q?E("div",{className:"codex-stats-tooltip active",style:z,"data-testid":"codex-stats-tooltip"},E("b",null,T0(Q.row?.date)),E("span",null,`${Q.label} · ${Q.valueLabel}`),E("code",null,`${Mf(Q.row?.executedTasks)} exec / ${Mf(Q.row?.retryAttempts)} retry / ${Mr(Q.row?.avgDurationMs??void 0)}`)):null,E("div",{className:"codex-stats-legend"},E("span",{className:"tasks"},"执行任务"),E("span",{className:"retry"},"重试次数"),E("span",{className:"duration"},"平均耗时")),E("div",{className:"codex-stats-scale"},E("span",null,T0(r[0]?.date)),E("span",null,G.join(" · ")||"暂无峰值"),E("span",null,T0(r.at(-1)?.date))),E("div",{className:`codex-stats-focus ${Q?"active":""}`,"data-testid":"codex-stats-focus"},Q?E(On.default.Fragment,null,E("div",null,E("strong",null,T0(Q.row?.date)),E("span",null,`${Q.label} · ${Q.valueLabel}`)),E("div",{className:"codex-stats-focus-metrics"},E("code",null,`${Mf(Q.row?.executedTasks)} exec`),E("code",null,`${Mf(Q.row?.retryAttempts)} retry`),E("code",null,Mr(Q.row?.avgDurationMs??void 0)))):E("span",null,"将鼠标悬停到曲线数据点查看明细,点击数据点可固定。"))):E(Fy,{title:"暂无统计",text:"任务开始执行后会生成按天汇总的曲线。"}),E("div",{className:"codex-stats-summary-grid"},E("article",null,E("span",null,"今日执行"),E("strong",null,String(Mf(i.executedTasks))),E("code",null,T0(i.date))),E("article",null,E("span",null,"今日重试"),E("strong",null,String(Mf(i.retryAttempts))),E("code",null,`累计 ${Mf(y.retryAttempts)}`)),E("article",null,E("span",null,"平均耗时"),E("strong",null,Mr(y.avgDurationMs??void 0)),E("code",null,`${Mf(y.durationSamples)} samples`))),E("div",{className:"codex-stats-daily-list","data-testid":"codex-stats-daily-list"},r.slice(-7).map((T)=>E("div",{key:String(T?.date||""),className:`codex-stats-daily-row ${c===String(T?.date||"")?"active":""}`,"data-testid":`codex-stats-day-${String(T?.date||"unknown")}`},E("span",null,T0(T?.date)),E("b",null,`${Mf(T?.executedTasks)} exec`),E("b",null,`${Mf(T?.retryAttempts)} retry`),E("code",null,Mr(T?.avgDurationMs??void 0))))))}function GZ({task:u,queueRows:f,busy:l,onMove:r}){let y=String(u?.id||""),i=Tn(u),[_,n]=su(i);Q1(()=>{n(i)},[y,i]);let $=!y||l||["running","judging","retry_wait"].includes(String(u?.status||""));return E("div",{className:"codex-task-move-control","data-testid":"codex-task-queue-move-control"},E("label",null,"任务 queue",E("select",{value:_,disabled:!y||l,onChange:(j)=>n(String(j.target.value||i)),"data-testid":"codex-task-queue-move-select"},f.map((j)=>E("option",{key:String(j?.id||""),value:String(j?.id||"")},wA(j))))),E("button",{type:"button",className:"ghost-btn",disabled:$||_===i,onClick:()=>r(_),title:$?"运行中 / judging / retry_wait 的任务不能移动;请先打断或等当前 turn 结束":"移动已创建任务到另一个 queue","data-testid":"codex-task-queue-move-button"},"移动"))}function Xq(u,f=4){let l=(Array.isArray(u)?u:[]).map((y)=>String(y||"").trim()).filter(Boolean);if(l.length===0)return"--";let r=l.slice(0,f).join(" / ");return l.length>f?`${r} +${l.length-f}`:r}function KZ({task:u,loading:f,onLoadPromptPart:l,testId:r="codex-initial-prompt-full",textTestId:y="codex-initial-prompt-full-text",baseTextTestId:i="codex-initial-prompt-base"}){let _=XA(u),n=t6(u),$=Y6(u).trimEnd(),j=String(n.full?.text||""),F=bT(u),J=Number(_.promptChars||u?.promptChars||j.length),U=Number(_.basePromptLines||Ay($)),q=Number(_.promptLines||Ay(j));return E("section",{className:"codex-progressive-card codex-progressive-prompt","data-testid":"codex-progressive-prompt"},E("div",{className:"codex-progressive-card-head"},E("span",{className:"codex-output-channel"},"Prompt"),E("strong",null,"Submitted prompt / 原始用户 prompt"),E("code",null,`${U||Ay($)} lines / ${$.length} chars`)),E("pre",{className:"codex-prompt-full","data-testid":i},$||"空 prompt"),F?E("details",{className:"codex-reference-injection codex-progressive-full-prompt","data-testid":r,onToggle:(W)=>{if(W.currentTarget?.open&&!j)l?.("full")}},E("summary",null,E("span",null,"引用注入已折叠,点击按需拉取最终进入 Code agent 的完整 prompt"),E("code",null,j?`${q||Ay(j)} lines / ${j.length} chars`:`${Number.isFinite(J)&&J>0?J:"--"} chars`)),E("pre",{className:"codex-prompt-full codex-prompt-final-full","data-testid":y},j||(f?"正在按需拉取完整 prompt...":"展开后将只请求 full prompt,不拉取完整 transcript。"))):null)}function oq({task:u,attempt:f,attemptIndex:l,loading:r,onLoadSteps:y,onLoadStep:i,testId:_="codex-execution-summary"}){let n=oT(bq(u,l)),$=dT(pT(u,f)),j=yr(u),F=ZA(u),J=TA(u,l),U=Number(f?.errorCount??j?.errorCount??kT(n)),q=Hn($.toolCallCount),W=Hn($.stepCount??$.llmStepCount??$.toolCallCount),G=Array.isArray($.editedFiles)?$.editedFiles:[],K=Array.isArray($.commands)?$.commands:[],N=YA(f,l)?` · ${String(f?.label||"recovered thread execution")}`:l?` #${l}`:"",c=CT(u,f,l,$),z=`最近更新: ${tq(c)}`,w=RT(u,f,l);return E("details",{className:`codex-progressive-card codex-execution-summary ${w?"running":""}`,"data-testid":_,"data-attempt-index":P6(l),"data-running":w?"true":"false",onToggle:(H)=>{if(H.currentTarget?.open&&!J)y?.(l)}},E("summary",null,E("div",{className:"codex-progressive-card-head"},E("span",{className:"codex-output-channel"},"Summary"),E("strong",null,`执行过程摘要${N}`),w?E("span",{className:"codex-summary-running-pill","data-testid":`${_}-running`},"执行中"):null,E("code",{title:c?`最近更新: ${Nu(c)}`:z},`${Mr($.durationMs??$.totalElapsedMs)} / ${q} tools / ${z}`)),E("div",{className:"codex-execution-digest"},E("span",null,`read ${Number($.readCount||0)}`),E("span",null,`edit ${Number($.editCount||0)}`),E("span",null,`run ${Number($.runCount||0)}`),E("span",null,`STEP ${W}`),U>0?E("span",{className:"codex-execution-error-pill","data-testid":`${_}-error-count`},`Error ${U}`):null)),E("div",{className:"codex-execution-digest expanded"},E("span",null,`修改文件:${Xq(G,6)}`),E("span",null,`执行命令:${Xq(K,4)}`)),n.length===0?E("div",{className:"codex-output-empty"},r?"正在按需拉取步骤 summary...":"展开后将只请求执行步骤 summary,不拉取单步骤全量。"):E("div",{className:"codex-trace-step-list"},n.map((H)=>{let B=String(H?.seq??""),T=F[B],X=Array.isArray(H?.summaryLines)?H.summaryLines.slice(0,4):[];return E("details",{key:B||`${H?.title}-${H?.at}`,className:`codex-trace-step ${String(H?.kind||"message")} ${vq(H)?"error":""}`,"data-testid":`codex-trace-step-${B||"unknown"}`,onToggle:(R)=>{if(R.currentTarget?.open&&!T)i?.(H?.seq)}},E("summary",null,E("span",{className:"codex-output-channel"},LZ(H?.kind)),E("strong",null,String(H?.title||"Trace step")),H?.status?E("code",null,String(H.status)):null,E("time",null,Nu(H?.at))),E("div",{className:"codex-trace-step-summary"},X.length>0?X.map((R,Y)=>E("pre",{key:`${B}-${Y}`},String(R||""))):E("span",null,"无 summary")),T?.line?E(H6,{items:[T.line],autoScroll:!1,loading:!1,hasDetail:!0,emptyText:"无步骤详情",testId:`codex-trace-step-detail-${B||"unknown"}`,className:"codex-transcript codex-step-detail-transcript",collapseTools:!1}):E("div",{className:"codex-output-empty"},r?"正在按需拉取这个步骤的全量数据...":"展开后将只请求这个单步骤的全量数据。"))})))}function LZ(u){let f=String(u||"");if(f==="ran")return"Ran";if(f==="explored")return"Explored";if(f==="edited")return"Edited";if(f==="error")return"Error";if(f==="system")return"System";return"Message"}function dq({task:u,attempt:f,attemptIndex:l,testId:r="codex-final-response"}){let y=xT(u,f);if(y.length===0)return null;let i=Number(f?.finalResponseChars||y.length),_=l?` #${l}`:"";return E("section",{className:"codex-progressive-card codex-final-response","data-testid":r,"data-attempt-index":P6(l)},E("div",{className:"codex-progressive-card-head"},E("span",{className:"codex-output-channel"},"Final"),E("strong",null,`最终 response${_}`),E("code",null,`${Number.isFinite(i)?i:y.length} chars`)),E(IQ,{markdown:y,className:"codex-transcript-body codex-markdown",testId:`${r}-markdown`}))}function eq({task:u,attempt:f,attemptIndex:l,testId:r="codex-progressive-judge"}){let y=Rq(u,f);if(!y?.decision)return null;let i=l?` #${l}`:"";return E("section",{className:"codex-progressive-card codex-progressive-judge","data-testid":r,"data-attempt-index":P6(l)},E("div",{className:"codex-progressive-card-head"},E("span",{className:"codex-output-channel"},"Judge"),E("strong",null,`完成判定${i}`),E("code",null,`${y.decision} ${Math.round(Number(y.confidence||0)*100)}%`)),E("div",{className:"codex-judge-card","data-testid":`${r}-card`},E(jy,{status:y.decision},y.decision),E("strong",null,`${Math.round(Number(y.confidence||0)*100)}% confidence`),E("p",{"data-testid":`${r}-reason`},y.reason||"--"),y.continuePrompt?E("pre",{"data-testid":`${r}-continue-prompt`},String(y.continuePrompt||"")):null))}function wZ({task:u,attempt:f,attemptIndex:l,loading:r,onLoadPromptPart:y,testId:i="codex-judge-feedback-prompt"}){let _=hT(u,f,l);if(_===null)return null;let n=hq(l),j=t6(u)[n],F=String(j?.text||"").trimEnd(),J=String(_.preview||_.text||"").trimEnd(),U=F||String(_.text||"").trimEnd(),q=Number(j?.chars||_.chars||U.length||J.length),W=Number(j?.lines||_.lines||Ay(U||J)),G=j?.forAttempt||_.forAttempt||Number(l||0)+1;return E("details",{className:"codex-progressive-card codex-judge-feedback-prompt","data-testid":i,"data-attempt-index":P6(l),onToggle:(K)=>{if(K.currentTarget?.open&&!F)y?.("feedback",l)}},E("summary",null,E("div",{className:"codex-progressive-card-head"},E("span",{className:"codex-output-channel"},"Prompt"),E("strong",null,`judge feedback prompt #${l} -> #${G}`),E("code",null,`${W||"--"} lines / ${Number.isFinite(q)?q:J.length} chars`)),E("p",{className:"codex-feedback-preview","data-testid":`${i}-preview`},J||"展开后按需拉取 judge feedback prompt。")),E("pre",{className:"codex-prompt-full codex-feedback-full","data-testid":`${i}-text`},U||(r?"正在按需拉取 judge feedback prompt...":"展开后将只请求这一次 judge feedback prompt。")))}function EZ({task:u,attempt:f,position:l,loading:r,onLoadPromptPart:y,onLoadSteps:i,onLoadStep:_}){let n=eT(f,l),$=l===0,j=YA(f,n),F=j?String(f?.label||"Recovered thread execution"):`Attempt ${n}`;return E("section",{className:"codex-attempt-cycle","data-testid":`codex-attempt-cycle-${n}`},E("div",{className:"codex-attempt-cycle-head"},E("span",{className:"codex-output-channel"},F),E("strong",null,String(f?.mode||(n<=1?"initial":"retry"))),f?.terminalStatus?E(jy,{status:f.terminalStatus},f.terminalStatus):null,E("code",null,`${Nu(f?.startedAt)} -> ${Nu(f?.finishedAt)}`)),E(oq,{task:u,attempt:f,attemptIndex:n,loading:r,onLoadSteps:i,onLoadStep:_,testId:$?"codex-execution-summary":`codex-execution-summary-attempt-${n}`}),j?null:E(dq,{task:u,attempt:f,attemptIndex:n,testId:$?"codex-final-response":`codex-final-response-attempt-${n}`}),j?null:E(eq,{task:u,attempt:f,attemptIndex:n,testId:$?"codex-progressive-judge":`codex-progressive-judge-attempt-${n}`}),j?null:E(wZ,{task:u,attempt:f,attemptIndex:n,loading:r,onLoadPromptPart:y,testId:$?"codex-judge-feedback-prompt":`codex-judge-feedback-prompt-attempt-${n}`}))}function TZ({task:u,loading:f,onLoadPromptPart:l,onLoadSteps:r,onLoadStep:y}){if(!u)return E(Fy,{title:"未选择任务",text:"从左侧队列选择任务,或提交新 Codex 任务。"});let i=mT(u);return E("div",{className:"codex-transcript codex-progressive-trace","data-testid":"codex-output"},f&&!yr(u)?E("div",{className:"codex-output-empty"},"正在加载 Trace Summary..."):null,E(KZ,{task:u,loading:f,onLoadPromptPart:l}),i.length>0?i.map((_,n)=>E(EZ,{key:`${_?.index||n+1}-${_?.startedAt||n}`,task:u,attempt:_,position:n,loading:f,onLoadPromptPart:l,onLoadSteps:r,onLoadStep:y})):[E(oq,{key:"execution",task:u,loading:f,onLoadSteps:r,onLoadStep:y}),E(dq,{key:"final",task:u}),E(eq,{key:"judge",task:u})])}function ZZ({task:u}){let f=VT(u);if(!u||f.length===0)return E(Fy,{title:"暂无原始消息",text:"原始 Codex app-server 消息会保留在任务 JSON 中。"});return E("details",{className:"codex-raw-output"},E("summary",null,`原始 messages (${f.length})`),E("div",null,f.map((l)=>E("article",{key:`${l.seq}-${l.channel}`,className:`codex-output-line ${l.channel||"system"}`},E("div",{className:"codex-output-meta"},E("span",{className:"codex-output-channel"},rZ(String(l.channel||"system"))),E("span",null,Nu(l.at)),l.method?E("code",null,l.method):null),E("pre",null,String(l.text||""))))))}function HZ({task:u}){let f=XT(u).slice().reverse();if(f.length===0)return E(Fy,{title:"尚无 attempt",text:"任务开始运行后,这里会记录 Codex 终态、传输中断和 stderr tail。"});return E("div",{className:"table-wrap codex-attempt-table"},E("table",null,E("thead",null,E("tr",null,E("th",null,"#"),E("th",null,"模式"),E("th",null,"终态"),E("th",null,"传输"),E("th",null,"退出"),E("th",null,"完成时间"))),E("tbody",null,f.map((l)=>E("tr",{key:`${l.index}-${l.startedAt}`},E("td",null,l.index),E("td",null,l.mode),E("td",null,E(jy,{status:l.terminalStatus||"unknown"},l.terminalStatus||"unknown")),E("td",null,l.transportClosedBeforeTerminal?E(jy,{status:"failed"},"closed-before-terminal"):E(jy,{status:"succeeded"},"normal")),E("td",null,`code=${l.appServerExitCode??"--"} signal=${l.appServerSignal??"--"}`),E("td",null,Nu(l.finishedAt)))))))}function uW({microservices:u,onRaw:f,apiBaseUrl:l="/api",initialTasksData:r=null,standalone:y=!1}){let i=u.find((V)=>V.id==="code-queue")||null,_=UZ(r),n=String(_?.id||""),$=new Map;if(_!==null&&n.length>0)$.set(n,{task:_,maxSeq:X6(Array.isArray(_.transcript)?_.transcript:[]),complete:Boolean(_._transcriptComplete),completeUpdatedAt:_._transcriptComplete?String(_.updatedAt||""):""});let j=typeof performance>"u"?0:performance.now(),F=Xf(n),J=Xf(0),U=Xf(0),q=Xf(0),W=Xf(!1),G=Xf(!1),K=Xf(!1),Q=Xf(null),N=Xf(new Map),c=Xf(new Map),z=Xf(new Map),w=Xf(new Map),H=Xf(new Set),B=Xf(null),T=Xf(null),X=Xf(!1),R=Xf(!1),Y=Xf(Boolean(r)),P=Xf(new Map),t=Xf(new Set),O=Xf($),M=Xf(r),[S,b]=su(null),[Z,D]=su(r),[I,k]=su(n),[h,o]=su(_),[s,x]=su(!1),[uu,nu]=su(""),[$u,Fu]=su(null),[Ku,Wu]=su(!1),[m,d]=su(!1),[e,cu]=su(""),[g,Qu]=su(""),[Eu,Tu]=su("default"),[Du,ff]=su(W1),[rf,Lf]=su("main-server"),[gf,jr]=su("gpt-5.5"),[Ol,ef]=su("/root/unidesk"),[hr,vl]=su(99),[ju,bu]=su(1),[ul,tu]=su(!1),[ou,Al]=su(!1),[Bl,sf]=su(""),[Il,Fr]=su(""),[kl,M0]=su(""),[r_,y_]=su(!0),[fu,Zu]=su(()=>typeof window>"u"?!0:window.matchMedia(cT).matches),[Lu,ku]=su(!1),[Uf,vu]=su(""),[yf,_f]=su(""),{addNotification:xf}=wl(),[i_,B$]=su(""),[__,V$]=su(""),[n_,X$]=su(!1),[Vl,br]=su(r?{phase:"complete",taskId:n,queueMs:0,detailMs:0,totalMs:j,chunks:_?1:0,transcriptRows:Array.isArray(_?.transcript)?_.transcript.length:0,partial:Boolean(r?.selected?.hasMore||Ln(_)),completedAt:new Date}:null),[$_,A_]=su(r?new Date:null),[Vy,Y$]=su(!1),vr=Sy(nl(Z)),D$=vr.filter(Z0),Rf=Z?.queue||S?.body?.queue||S?.queue||{},z8=nZ(Z,Rf),t$=U1(Z),Jr=Kq(Rf,Eu),Xy=En(Jr,Du),p1=Number((ml(Du)?Rf?.total:Xy?.total)??t$.total??vr.length),j_=wn(Rf),Mj=ml(Du)?j_:[String(En(Jr,Du)?.activeTaskId||"")].filter(Boolean),Yy=Lq(Rf,Jr,Du,vr),mj=ml(Du)?qA(Rf):qA(Xy||{}),S$=qA(Rf),P$=iZ(S$),G8=Math.max(_Z(S$),j_.length),K8=Mf((ml(Du)?Rf?.unreadTerminal:Xy?.unreadTerminal)??D$.length),m0=Z?D$.length:K8,Dy=ml(Du)?"All queues":LA(Xy||{id:Du,name:Du}),C1=KA(uu),Xl=C1.length>0,M$=Xl?Sy(nl($u)):[],F_=U1($u),gl=Xl?M$:vr,m$=gl.filter(Z0),p$=gl.filter((V)=>!Pr(V)).sort(QT),L8=gl.filter((V)=>Pr(V)&&!Z0(V)),C$=Xl?F_:t$,Ir=Xl?Number(F_.total??M$.length):p1,x$=C$.hasMore===!0&&String(C$.nextBeforeId||"").length>0,J_=Xl?m:Vy,R$=i?JT(i):{},sG=i?UT(i):{},w8=Nq(()=>YT(e),[e]),F0=Nq(()=>{let V=Eq(ju);return w8.flatMap((v)=>Array.from({length:V},()=>DT(v,g)))},[w8,ju,g]),U_=F0.length,pj=U_>1&&!ul,aG=ou||Lu||U_===0||pj,Cj=QZ(Rf,gf),xj=qZ(Rf,rf),oG=D6(Rf,rf),E8=h?.id&&h?.activeTurnId&&String(h?.status)==="running",dG=h?.id&&!["succeeded","failed","canceled"].includes(String(h?.status||"")),eG=h?.id&&["succeeded","failed","canceled"].includes(String(h?.status||"")),ty=h?.id&&gq(h);function Ur(V){let v=typeof V==="function"?V(M.current):V;return M.current=v,D(v),v}function uK(V,v,lu=!0){let yu=Array.from(new Set(V.map((qu)=>String(qu||"")).filter(Boolean)));for(let qu of yu)if(P.current.set(qu,v),lu)t.current.add(qu);return yu}function Rj(V){for(let v of V.map((lu)=>String(lu||"")).filter(Boolean))P.current.delete(v),t.current.delete(v)}function h$(V){let v=String(V?.id||""),lu=v?P.current.get(v):void 0;if(!lu)return V;if(String(V?.status||"").length>0&&!Pr(V))return P.current.delete(v),t.current.delete(v),V;return{...V,readAt:V?.readAt||lu,terminalUnread:!1}}function T8(V){let v=String(V?.id||"");return v.length>0&&t.current.has(v)&&Pr(V)}function Sy(V,v=!0){let lu=[];for(let yu of Array.isArray(V)?V:[]){let qu=h$(yu);if(v&&T8(qu))continue;lu.push(qu)}return lu}function fK(V,v=!0){if(!V||!Array.isArray(V?.tasks))return V;let lu=Sy(nl(V),v),yu=U1(V);return{...V,tasks:lu,pagination:V.pagination?{...yu,returned:lu.length}:V.pagination}}function lK(V){let v=String(V||Rf?.mainProviderId||"main-server").trim()||"main-server";Lf(v),ef(D6(Rf,v))}function b$(V,v,lu=null,yu=null){let qu=new Set(uK(V,v));if(qu.size===0&&yu===null&&lu===null)return;Ur((Gu)=>{if(!Gu)return Gu;let Ou=nl(Gu).flatMap((Cu)=>{let xu=String(Cu?.id||"");if(!qu.has(xu)){let Qf=h$(Cu);return T8(Qf)?[]:[Qf]}let Ru=yu&&String(yu?.id||"")===xu?yu:{},mu={...Cu,...Ru,readAt:v,terminalUnread:!1};return T8(mu)?[]:[mu]});return{...Gu,queue:lu||Gu.queue,tasks:qu.size>0?Ei([Ou],Yy):Ou}});for(let Gu of qu){let Ou=O.current.get(Gu);if(Ou?.task){let Cu=yu&&String(yu?.id||"")===Gu?yu:{},xu={...Ou.task,...Cu,readAt:v,terminalUnread:!1};if(O.current.set(Gu,{...Ou,task:xu}),F.current===Gu)o(xu)}}}Q1(()=>{tu(!1)},[e,ju,g]),Q1(()=>{let V=KA(uu);U.current+=1;let v=U.current;if(!i||V.length===0){Fu(null),Wu(!1),d(!1),K.current=!1;return}Wu(!0),Fu(null);let lu=window.setTimeout(()=>{(async()=>{try{let yu=await LT(l,Du,V);if(v!==U.current)return;Fu(fK(yu))}catch(yu){if(v===U.current)Fu(null),vu(_l(yu,"搜索 Codex tasks 失败"))}finally{if(v===U.current)Wu(!1)}})()},240);return()=>window.clearTimeout(lu)},[i?.id,l,Du,uu]),Q1(()=>{Fr(h?Y6(h):""),M0(Array.isArray(h?.referenceTaskIds)?h.referenceTaskIds.join(" "):"")},[I]);function J0(V,v,lu){let yu=O.current.get(V)||{},qu=yu.task||{},Gu=Array.isArray(qu.transcript)?qu.transcript:[],Ou=Bq(qu,v),Cu=Object.prototype.hasOwnProperty.call(v,"transcript")?NA(Gu,Array.isArray(v.transcript)?v.transcript:[]):Gu,xu={...qu,...Ou,transcript:Cu,output:Array.isArray(Ou.output)?OA(qu,Ou,"output"):Array.isArray(qu.output)?qu.output:[],events:Array.isArray(Ou.events)?OA(qu,Ou,"events"):Array.isArray(qu.events)?qu.events:[]},Ru=h$(xu),mu=String(Ru?.updatedAt||""),Qf=Boolean(v._transcriptComplete)&&Pr(Ru),fl=Boolean(yu.complete)&&Pr(Ru)&&String(yu.completeUpdatedAt||"")===mu,du=Qf||fl,af={...yu,task:Ru,maxSeq:X6(Cu),complete:du,completeUpdatedAt:du?mu:""};if(O.current.set(V,af),lu===q.current&&F.current===V)o(Ru);return af}async function Py(V,v=!1,lu,yu){if(!i||!V)return;let Gu=O.current.get(V)?.task;if(!v&&B6(Gu))return;let Ou=V,Cu=N.current.get(Ou);if(Cu){if(v||!B6(Gu))Cu.refreshAfter=!0;return Cu.promise}let xu=q.current,Ru=performance.now();if(F.current===V)x(!0);let mu={promise:Promise.resolve(),refreshAfter:!1},Qf=(async()=>{try{let fl=await ET(l,V);if(xu!==q.current||F.current!==V)return;let du=fl?.summary||{},af=String(du.updatedAt||"");J0(V,{id:V,status:du.status,updatedAt:af,startedAt:du.startedAt,finishedAt:du.finishedAt,currentAttempt:du.currentAttempt,maxAttempts:du.maxAttempts,finalResponse:du.finalResponse,lastJudge:du.lastJudge,lastError:du.lastError,attempts:Array.isArray(du.attempts)?du.attempts:[],stepCount:du.stepCount,llmStepCount:du.llmStepCount,timing:du.timing,_traceSummary:du,_traceSummaryLoaded:!0,_traceSummaryUpdatedAt:af,_detailLoaded:!0},xu),br({phase:"complete",taskId:V,queueMs:yu??0,detailMs:performance.now()-Ru,totalMs:lu===void 0?performance.now()-Ru:performance.now()-lu,chunks:1,transcriptRows:Number(du?.execution?.traceLineCount||du?.execution?.stepCount||0),partial:!1,completedAt:new Date})}finally{let fl=Boolean(mu.refreshAfter&&F.current===V&&!B6(O.current.get(V)?.task));if(N.current.delete(Ou),xu===q.current&&F.current===V)x(!1);if(fl)window.setTimeout(()=>{Py(V,!0).catch((du)=>vu(_l(du,"自动刷新 Trace Summary 失败")))},0)}})();mu.promise=Qf,N.current.set(Ou,mu),await Qf}async function rK(V,v=null){let lu=F.current;if(!i||!lu||!V)return;let yu=O.current.get(lu)?.task,qu=t6(yu),Gu=V==="feedback"||V==="judge-feedback"?hq(v):V;if(qu[Gu]?.text)return;let Ou=`${lu}:${Gu}`,Cu=c.current.get(Ou);if(Cu)return Cu;let xu=q.current;if(F.current===lu)x(!0);let Ru=(async()=>{try{let mu=await TT(l,lu,V,v);if(xu!==q.current||F.current!==lu)return;let Qf=O.current.get(lu)?.task,fl=t6(Qf);J0(lu,{...V==="full"?{prompt:String(mu?.text||""),promptChars:Number(mu?.chars||0)}:{},_promptDetails:{...fl,[Gu]:mu}},xu)}finally{if(c.current.delete(Ou),xu===q.current&&F.current===lu)x(!1)}})();c.current.set(Ou,Ru),await Ru}async function Z8(V=null,v={}){let lu=F.current;if(!i||!lu)return;let yu=O.current.get(lu)?.task,qu=V===null||V===void 0||String(V).length===0?"":String(V),Gu=TA(yu,qu||null),Ou=Boolean(v.force),Cu=Boolean(v.incremental);if(Gu&&!Ou)return;let xu=bq(yu,qu||null),Ru=Cu&&xu.length>0?vT(xu):0,mu=`${lu}:${qu||"all"}:${Ru}`,Qf=z.current.get(mu);if(Qf)return Qf;let fl=q.current;if(F.current===lu)x(!0);let du=(async()=>{try{let af=await ZT(l,lu,Ru,500,qu||null);if(fl!==q.current||F.current!==lu)return;let p0=Array.isArray(af?.steps)?af.steps:[],C0=Ru>0?IT(xu,p0):p0;if(qu){let x0=O.current.get(lu)?.task,My=Tl(x0?._traceStepsByAttempt)||{},ll=Tl(x0?._traceStepsLoadedByAttempt)||{},U0=Tl(x0?._traceStepsNextAfterSeqByAttempt)||{};J0(lu,{_traceStepsByAttempt:{...My,[qu]:C0},_traceStepsLoadedByAttempt:{...ll,[qu]:!0},_traceStepsNextAfterSeqByAttempt:{...U0,[qu]:af?.nextAfterSeq}},fl)}else J0(lu,{_traceSteps:C0,_traceStepsLoaded:!0,_traceStepsHasMore:Boolean(af?.hasMore),_traceStepsNextAfterSeq:af?.nextAfterSeq},fl)}finally{if(z.current.delete(mu),fl===q.current&&F.current===lu)x(!1)}})();z.current.set(mu,du),await du}async function yK(V){let v=F.current,lu=String(V??"");if(!i||!v||lu.length===0)return;let yu=O.current.get(v)?.task;if(ZA(yu)[lu]?.line)return;let Gu=`${v}:${lu}`,Ou=w.current.get(Gu);if(Ou)return Ou;let Cu=q.current;if(F.current===v)x(!0);let xu=(async()=>{try{let Ru=await HT(l,v,V);if(Cu!==q.current||F.current!==v)return;let mu=O.current.get(v)?.task,Qf=ZA(mu);J0(v,{_traceStepDetails:{...Qf,[lu]:Ru}},Cu)}finally{if(w.current.delete(Gu),Cu===q.current&&F.current===v)x(!1)}})();w.current.set(Gu,xu),await xu}function iK(V){if(F.current!==V)return;let v=O.current.get(V)?.task;if(!v)return;if(TA(v,null))Z8(null,{force:!0,incremental:!0}).catch((yu)=>vu(_l(yu,"增量刷新 Trace Steps 失败")));let lu=Tl(v?._traceStepsLoadedByAttempt)||{};for(let yu of Object.keys(lu).filter((qu)=>lu[qu]))Z8(yu,{force:!0,incremental:!0}).catch((qu)=>vu(_l(qu,"增量刷新 Attempt Trace Steps 失败")))}async function xS(V,v,lu){if(!i||!V)return;let yu=performance.now(),qu=q.current,Gu=O.current.get(V);if(Gu?.task){if(o(Gu.task),x(Ln(Gu.task)||!Gu.complete),!Ln(Gu.task)&&Gu.complete&&Pr(Gu.task)&&String(Gu.completeUpdatedAt||"")===String(Gu.task?.updatedAt||"")){br({phase:"complete",taskId:V,queueMs:lu??0,detailMs:0,totalMs:v===void 0?0:performance.now()-v,chunks:0,transcriptRows:Array.isArray(Gu.task.transcript)?Gu.task.transcript.length:0,completedAt:new Date});return}}else x(!0);let Ou=Q.current;if(Ou?.taskId===V&&Ou.token===qu)return Ou.promise;let Cu=(async()=>{try{let xu=await mf(Sf(l,`/api/tasks/${encodeURIComponent(V)}?meta=1`));if(qu!==q.current||F.current!==V)return;let Ru=O.current.get(V),mu=Array.isArray(Ru?.task?.transcript)?Ru.task.transcript:[],Qf=xu?.task||{},fl=Boolean(Ru?.complete)&&String(Ru?.completeUpdatedAt||"")===String(Qf?.updatedAt||"");J0(V,{...Qf,summaryOnly:!1,_metaLoaded:!0,transcript:mu,_detailLoaded:mu.length>0,_transcriptComplete:fl},qu);let du=Ln(Ru?.task)||Boolean(Ru?.task?._transcriptPreview),af=du?0:mu.length>0?Vq(mu):0,p0=!du&&Ru?.complete&&Pr(Qf)&&String(Ru?.completeUpdatedAt||"")===String(Qf?.updatedAt||"")?X6(mu):af,C0=!0,x0=0,My=mu.length;while(C0){let ll=await mf(Sf(l,`/api/tasks/${encodeURIComponent(V)}/transcript?afterSeq=${encodeURIComponent(String(p0))}&limit=${AT}&fullText=1`));if(qu!==q.current||F.current!==V)return;let U0=O.current.get(V),v$=Array.isArray(U0?.task?.transcript)?U0.task.transcript:[],hf=NA(v$,Array.isArray(ll?.transcript)?ll.transcript:[]);x0+=1,My=hf.length;let I$=Boolean(!ll?.hasMore);if(J0(V,{status:ll?.status||Qf.status,updatedAt:ll?.updatedAt||Qf.updatedAt,transcript:hf,_detailLoaded:I$||hf.length>0,_transcriptComplete:I$,_transcriptPreview:du&&!I$},qu),C0=Boolean(ll?.hasMore),p0=Number(ll?.nextAfterSeq??X6(hf)),!C0)break;await new Promise((my)=>window.setTimeout(my,0))}br({phase:"complete",taskId:V,queueMs:lu??0,detailMs:performance.now()-yu,totalMs:v===void 0?performance.now()-yu:performance.now()-v,chunks:x0,transcriptRows:My,completedAt:new Date})}finally{if(Q.current?.taskId===V&&Q.current?.token===qu)Q.current=null;if(qu===q.current&&F.current===V)x(!1)}})();Q.current={taskId:V,token:qu,promise:Cu},await Cu}async function Yl(V=F.current,v=!0,lu=Du){if(!i)return;if(!v&&R.current)return;let yu=performance.now();if(v)R.current=!0;if(v)br({phase:"loading",taskId:String(V||F.current||""),startedAt:new Date});let qu=J.current+1;J.current=qu;let Gu=String(V||F.current||""),Ou=Gu?O.current.get(Gu):null,Cu=Array.isArray(Ou?.task?.transcript)?Ou.task.transcript:[],xu=Vq(Cu),Ru=null;if(Ru=await wT(l,Gu,xu,lu),qu!==J.current){if(v)R.current=!1;return}let mu=performance.now()-yu;b(S||{});let Qf=Ru?.queue||{},fl=String(Qf?.activeTaskId||wn(Qf)[0]||""),du=Ru;Ur((al)=>{let Q_=nl(Ru),x1=nl(al),q_=x1.length>0?Ei([x1,Q_],fl):Ei([Q_],fl),k$=Sy(q_),BK=U1(Ru),g$=U1(al),VK=x1.length>Q_.length&&(g$.hasMore===!1||String(g$.nextBeforeId||"").length>0),XK={...BK,...VK?{hasMore:g$.hasMore,nextBeforeId:g$.nextBeforeId}:{},returned:k$.length};return du={...Ru,tasks:k$,pagination:XK},du});let af=nl(du),p0=Kq(Qf,Eu),C0=Lq(Qf,p0,lu,af),x0=KT(p0,lu,af),My=Gu||F.current,ll=du?.selected||null,U0=ll?.task||null,v$=Array.isArray(ll?.transcript)?ll.transcript:null,hf=My||C0||x0||af[0]?.id||"";if(F.current!==hf)q.current+=1;F.current=hf,k(hf);let my=af.find((al)=>al.id===hf);if(my){let al=O.current.get(hf);if(al?.task)O.current.set(hf,{...al,task:{...my,...al.task,status:my.status,updatedAt:my.updatedAt}})}if(U0?.id===hf&&v$!==null){let al=O.current.get(hf),Q_=Array.isArray(al?.task?.transcript)?al.task.transcript:[],x1=NA(Q_,v$),q_=Boolean(ll?.preview);if(J0(hf,{...U0,_summaryLoaded:!0,transcript:x1,_detailLoaded:!ll?.hasMore||x1.length>0,_transcriptComplete:!q_&&!ll?.hasMore&&Pr(U0),_transcriptPreview:q_},q.current),x(!1),v)br({phase:"complete",taskId:hf,queueMs:mu,detailMs:Math.max(0,performance.now()-yu-mu),totalMs:performance.now()-yu,chunks:1,transcriptRows:x1.length,partial:Boolean(q_||ll?.hasMore||Ln(U0)),completedAt:new Date});if(A_(new Date),v)R.current=!1;Py(hf,!1,v?yu:void 0,v?mu:void 0).catch((k$)=>vu(_l(k$,"加载 Codex Trace Summary 失败")));return}if(v)br({phase:"session",taskId:hf,queueMs:mu,totalMs:mu,startedAt:new Date(Date.now()-mu)});if(hf)Py(hf,!0,v?yu:void 0,v?mu:void 0).catch((al)=>vu(_l(al,"加载 Codex Trace Summary 失败")));else if(q.current+=1,o(null),x(!1),v)br({phase:"complete",taskId:"",queueMs:mu,detailMs:0,totalMs:performance.now()-yu,chunks:0,transcriptRows:0,completedAt:new Date});if(A_(new Date),v)R.current=!1}async function hj(){if(Xl){if(!i||m||K.current)return;let v=String(F_.nextBeforeId||"");if(!v)return;K.current=!0,d(!0),vu("");try{let lu=await wq(l,Du,v,Dq,C1),yu=nl(lu),qu=lu?.queue||Rf||{},Gu=String(qu?.activeTaskId||wn(qu)[0]||Yy||"");Fu((Ou)=>{let Cu=Sy(Ei([nl(Ou),yu],Gu)),xu=U1(lu);return{...Ou||{},queue:qu,tasks:Cu,pagination:{...xu,returned:Cu.length}}})}catch(lu){vu(_l(lu,"加载更多搜索结果失败"))}finally{K.current=!1,d(!1)}return}if(!i||Vy||G.current)return;let V=String(U1(Z).nextBeforeId||"");if(!V)return;G.current=!0,Y$(!0),vu("");try{let v=await wq(l,Du,V),lu=nl(v),yu=v?.queue||Rf||{},qu=String(yu?.activeTaskId||wn(yu)[0]||Yy||"");Ur((Gu)=>{let Ou=Sy(Ei([nl(Gu),lu],qu)),Cu=U1(v);return{...Gu||{},queue:yu,statistics:v?.statistics||Gu?.statistics,tasks:Ou,pagination:{...Cu,returned:Ou.length}}})}catch(v){vu(_l(v,"加载更早 Codex tasks 失败"))}finally{G.current=!1,Y$(!1)}}function _K(V){let v=V.currentTarget;if(!v||J_||!x$)return;if(v.scrollHeight-v.scrollTop-v.clientHeight<120)hj()}async function sl(V,v){ku(!0),vu("");try{await V()}catch(lu){vu(_l(lu,v))}finally{ku(!1)}}async function H8(V){if(!V)return;try{let v=!1;try{if(navigator.clipboard?.writeText)await navigator.clipboard.writeText(V),v=!0}catch{v=!1}if(!v){let yu=document.createElement("textarea");yu.value=V,yu.style.position="fixed",yu.style.opacity="0",document.body.appendChild(yu),yu.select(),v=document.execCommand("copy"),document.body.removeChild(yu)}if(!v)throw Error("browser clipboard rejected the copy request");B$(V);let lu=`已复制任务 ID:${V}`;_f(lu),xf("success",lu),window.setTimeout(()=>B$((yu)=>yu===V?"":yu),1600)}catch(v){vu(`复制任务 ID 失败:${_l(v)}`)}}function O8(V){if(!V)return;Qu(V);let v=`已引用任务 ID:${V};提交时后端会读取并注入该任务上下文`;_f(v),xf("success",v)}async function B8(V){if(!i||!V)return;let v=new Date().toISOString();J.current+=1,b$([V],v,null,{id:V,readAt:v,terminalUnread:!1}),V$(V);let lu=!1;if(await sl(async()=>{let yu=await OT(l,V),qu=yu?.task||{id:V,readAt:new Date().toISOString(),terminalUnread:!1},Gu=String(qu?.readAt||new Date().toISOString());b$([V],Gu,yu?.queue||null,qu),lu=!0;let Ou=`已将任务 ${V} 标为已读`;_f(Ou),xf("success",Ou)},"标记 Codex task 已读失败"),!lu)Rj([V]),Yl(F.current,!1).catch((yu)=>vu(_l(yu,"刷新 Codex tasks 失败")));V$((yu)=>yu===V?"":yu)}async function nK(){if(!i||n_)return;X$(!0);let V=new Date().toISOString(),v=Array.from(new Set([...nl(M.current).filter(Z0).map((yu)=>String(yu?.id||"")).filter(Boolean),...Array.from(O.current.entries()).filter(([,yu])=>Z0(yu?.task)).map(([yu])=>yu)]));if(J.current+=1,v.length>0)b$(v,V);let lu=!1;if(await sl(async()=>{let yu=await BT(l),qu=String(yu?.readAt||new Date().toISOString()),Gu=nl(M.current).filter(Z0).map((mu)=>String(mu?.id||"")).filter(Boolean),Ou=Array.from(O.current.entries()).filter(([,mu])=>Z0(mu?.task)).map(([mu])=>mu),Cu=Array.from(new Set([...v,...Gu,...Ou]));b$(Cu,qu,yu?.queue||null);let xu=Number(yu?.count||Cu.length);lu=!0;let Ru=`已将 ${xu} 个已结束未读任务标为已读`;_f(Ru),xf("success",Ru)},"全部标为已读失败"),!lu&&v.length>0)Rj(v),Yl(F.current,!1).catch((yu)=>vu(_l(yu,"刷新 Codex tasks 失败")));X$(!1)}function $K(V){let v=V||W1;if(ff(v),!ml(v))Tu(v);if(Ur(null),!(ml(v)?F.current:""))F.current="",q.current+=1,k(""),o(null),x(!0)}async function AK(){let V=typeof window>"u"?"":window.prompt("输入新的 Codex queue ID(字母/数字/._-,最长 64)","new-lane"),v=String(V||"").trim();if(!v)return;await sl(async()=>{let lu=await mf(Sf(l,"/api/queues"),{method:"POST",body:{queueId:v}}),yu=String(lu?.queue?.id||v);Tu(yu),ff(yu),Ur(null),F.current="",q.current+=1,k(""),o(null);let qu=`已创建并切换到 queue:${yu}`;_f(qu),xf("success",qu),await Yl("",!0,yu)},"创建 Codex queue 失败")}async function jK(){let V=String(Eu||"default").trim()||"default",v=En(Jr,V)||{id:V,name:V},lu=typeof window>"u"?null:window.prompt(`输入 queue 显示名称(ID 不变:${V};留空恢复为 ID)`,Mq(v));if(lu===null)return;await sl(async()=>{let yu=await mf(Sf(l,`/api/queues/${encodeURIComponent(V)}`),{method:"PATCH",body:{name:String(lu)}}),qu=yu?.queue||{id:V,name:String(lu||V)};if(yu?.summary)Ur((Ou)=>Ou?{...Ou,queue:yu.summary}:Ou);let Gu=`已更新 queue 名称:${LA(qu)}`;_f(Gu),xf("success",Gu),await Yl(F.current,!0,Du)},"修改 Codex queue 名称失败")}async function FK(){let V=String(Eu||"default").trim()||"default",v=Jr.filter((Gu)=>String(Gu?.id||"")!==V);if(v.length===0){_f("没有可合并的其他 queue;请先创建或选择另一个 queue。");return}let lu=typeof window>"u"?"":window.prompt(`将源 queue 合并到当前 queue=${V};源 queue 记录会保留且不会删除。请输入源 queue ID:`,String(v[0]?.id||"")),yu=String(lu||"").trim();if(!yu)return;if(yu===V){vu("源 queue 和目标 queue 不能相同。");return}if(!(typeof window>"u"?!0:window.confirm(`确认把 queue=${yu} 的全部任务合并到 queue=${V}?源 queue 不会删除;任务会按原 queueEnteredAt/createdAt 时间顺序进入目标 queue。`)))return;await sl(async()=>{let Gu=await mf(Sf(l,`/api/queues/${encodeURIComponent(V)}/merge`),{method:"POST",body:{sourceQueueId:yu}});if(Gu?.summary)Ur((xu)=>xu?{...xu,queue:Gu.summary}:xu);Tu(V),ff(V),Ur(null);let Ou=Number(Gu?.mergedTaskCount||0),Cu=`已将 queue=${yu} 合并到 ${V},移动 ${Ou} 个任务;源 queue 已保留。`;_f(Cu),xf("success",Cu),await Yl(F.current,!0,V)},"合并 Codex queue 失败")}async function JK(V){if(V.preventDefault(),W.current){_f("任务正在提交中,请等待当前请求完成,已阻止重复提交。");return}if(F0.length>1&&!ul){vu(`检测到将创建 ${F0.length} 个任务;请先勾选“确认批量入队”,避免误传多个任务。`);return}W.current=!0,Al(!0),_f("正在提交 Code Queue 任务,请等待后端确认,输入已临时锁定。"),await sl(async()=>{if(F0.length===0)throw Error("prompt 不能为空");let v=ny(g),lu=Eu.trim()||"default",yu=[...F0],qu=(mu)=>({prompt:mu,queueId:lu,providerId:rf,model:gf,cwd:Ol,maxAttempts:Number(hr),...v.length>0?{referenceTaskIds:v}:{}}),Gu=yu.length===1?qu(yu[0]):{tasks:yu.map(qu)},Ou=await mf(Sf(l,yu.length===1?"/api/tasks":"/api/tasks/batch"),{method:"POST",body:Gu}),Cu=Ou?.tasks?.[0]?.id||"",xu=Array.isArray(Ou?.tasks)?Ou.tasks.map((mu)=>String(mu?.id||"")).filter(Boolean):[],Ru=`已创建 ${xu.length||yu.length} 个任务${xu.length>0?`:${xu.join(" / ")}`:""}`;if(_f(Ru),xf("success",Ru),cu(""),Qu(""),tu(!1),F.current=Cu,Du!==lu)Ur(null);Tu(lu),await Yl(Cu,!0,lu)},"Codex 任务入队失败"),W.current=!1,Al(!1)}async function UK(V){if(V.preventDefault(),!h?.id)return;await sl(async()=>{await mf(Sf(l,`/api/tasks/${encodeURIComponent(h.id)}/steer`),{method:"POST",body:{prompt:Bl}}),sf(""),await Yl(h.id)},"追加 prompt 失败")}async function QK(V){V.preventDefault();let v=String(h?.id||"");if(!v||!ty)return;await sl(async()=>{let lu=ny(kl),yu=await mf(Sf(l,`/api/tasks/${encodeURIComponent(v)}/edit`),{method:"POST",body:{prompt:Il,referenceTaskIds:lu}}),qu={...yu?.task||h||{},_traceSummary:null,_traceSummaryLoaded:!1,_traceSummaryUpdatedAt:"",_promptDetails:{},_traceSteps:[],_traceStepsLoaded:!1,_traceStepsByAttempt:{},_traceStepsLoadedByAttempt:{},_traceStepsNextAfterSeqByAttempt:{},_traceStepDetails:{}};O.current.set(v,{...O.current.get(v)||{},task:qu,complete:!1,completeUpdatedAt:""}),F.current=v,o(qu),k(v),Fr(Y6(qu)),M0(Array.isArray(qu?.referenceTaskIds)?qu.referenceTaskIds.join(" "):""),Ur((Ou)=>{if(!Ou)return Ou;let Cu=nl(Ou).map((xu)=>String(xu?.id||"")===v?{...xu,...qu}:xu);return{...Ou,queue:yu?.queue||Ou.queue,tasks:Ei([Cu],Yy)}});let Gu=yu?.changed===!1?`任务 ${v} 的 prompt 未变化`:`已更新 queued 任务 ${v} 的用户 prompt`;_f(Gu),xf("success",Gu),await Yl(v,!0,Du)},"编辑 queued 任务 prompt 失败")}async function qK(){if(!h?.id)return;await sl(async()=>{await mf(Sf(l,`/api/tasks/${encodeURIComponent(h.id)}/interrupt`),{method:"POST",body:{}}),await Yl(h.id)},"打断 Codex session 失败")}async function WK(){if(!h?.id)return;await sl(async()=>{await mf(Sf(l,`/api/tasks/${encodeURIComponent(h.id)}/retry`),{method:"POST",body:{}}),await Yl(h.id)},"重新入队失败")}async function cK(V){let v=String(h?.id||""),lu=String(V||"").trim();if(!v||!lu)return;let yu=Tn(h);if(lu===yu){_f(`任务 ${v} 已在 queue=${lu}`);return}await sl(async()=>{let Gu=(await mf(Sf(l,`/api/tasks/${encodeURIComponent(v)}/move`),{method:"POST",body:{queueId:lu}}))?.task||{...h,queueId:lu};if(O.current.set(v,{...O.current.get(v)||{},task:Gu}),F.current=v,o(Gu),k(v),Tu(lu),!ml(Du))Ur(null),ff(lu);let Ou=`已将任务 ${v} 从 ${yu} 移动到 ${lu}`;_f(Ou),xf("success",Ou),await Yl(v,!0,ml(Du)?W1:lu)},"移动任务 queue 失败")}async function NK(){let V=F.current;if(!V)return;let v=performance.now();await sl(async()=>{br({phase:"session",taskId:V,queueMs:0,totalMs:0,partial:!0,startedAt:new Date}),await Py(V,!0,v,0)},"刷新 Trace Summary 失败")}function zK(V){F.current=V,q.current+=1,k(V);let v=O.current.get(V);if(v?.task)o(v.task),x(!1);else{x(!0);let lu=vr.find((yu)=>yu.id===V);if(lu)o(lu);else o(null)}Yl(V).catch((lu)=>vu(_l(lu,"切换 Codex session 失败")))}function V8(V){if(zK(V),NT())Zu(!1)}function bj(V,v,lu){if(!V||!Array.isArray(V?.tasks)||v.length===0||Object.keys(lu).length===0)return V;let yu=!1,qu=nl(V).map((Gu)=>{if(String(Gu?.id||"")!==v)return Gu;return yu=!0,h$(Bq(Gu,lu))});return yu?{...V,tasks:qu}:V}function GK(V,v){Ur((lu)=>bj(lu,V,v)),Fu((lu)=>bj(lu,V,v))}function KK(V,v,lu){let yu=String(V?.type||"");if(yu==="queue-updated")return!0;if(v.length===0)return!0;if(!lu)return yu!=="task-step";if(V?.queueId&&String(V.queueId)!==Tn(lu))return!0;if(V?.status&&String(V.status)!==String(lu?.status||""))return!0;return yu!=="task-step"&&String(V?.reason||"")!=="output"}function vj(){if(!i||!QA())return;if(B.current!==null)window.clearTimeout(B.current);B.current=window.setTimeout(()=>{B.current=null,Yl(F.current,!1).catch((V)=>vu(_l(V,"Code Queue 事件刷新失败")))},120)}function LK(V,v){if(!i||!QA()||F.current!==V)return;if(X.current=X.current||v,T.current!==null)return;T.current=window.setTimeout(()=>{T.current=null;let lu=X.current;if(X.current=!1,Py(V,!0).catch((yu)=>vu(_l(yu,"事件刷新 Trace Summary 失败"))),lu)iK(V)},250)}function wK(V){let v=String(V?.taskId||""),lu=Hn(V?.stepCount),yu={},qu=v.length>0?nl(M.current).find((Gu)=>String(Gu?.id||"")===v):null;if(v.length>0){if(V?.status)yu.status=String(V.status);if(V?.updatedAt)yu.updatedAt=String(V.updatedAt);if(V?.queueId)yu.queueId=String(V.queueId);if(Number.isFinite(Number(V?.stepCount)))yu.stepCount=lu,yu.llmStepCount=lu;if(Object.keys(yu).length>0&&(O.current.has(v)||F.current===v))J0(v,yu,q.current);if(Object.keys(yu).length>0)GK(v,yu);if(F.current===v&&(V?.type==="task-step"||V?.type==="task-updated"))LK(v,V?.type==="task-step")}if(KK(V,v,qu))vj()}Q1(()=>{if(Y.current){Y.current=!1;return}sl(()=>Yl(F.current),"Code Queue 加载失败")},[i?.id,Du]),Q1(()=>{if(!i||typeof EventSource>"u")return;let V=new EventSource(Sf(l,"/api/events"),{withCredentials:!0}),v=(yu)=>{try{wK(JSON.parse(String(yu.data||"{}")))}catch(qu){vu(_l(qu,"解析 Code Queue 事件失败"))}},lu=()=>{if(QA())vj()};return V.addEventListener("task-step",v),V.addEventListener("task-updated",v),V.addEventListener("queue-updated",v),document.addEventListener("visibilitychange",lu),()=>{if(V.close(),document.removeEventListener("visibilitychange",lu),B.current!==null)window.clearTimeout(B.current),B.current=null;if(T.current!==null)window.clearTimeout(T.current),T.current=null}},[i?.id,l,Du]),Q1(()=>{if(!i||!h||s)return;let V=String(h.id||"");if(!V)return;let v=String(h.updatedAt||"");if(B6(h))return;let lu=`${V}:${v||"unknown"}:${String(h?._traceSummaryUpdatedAt||"")}`;if(H.current.has(lu))return;H.current.add(lu),Py(V,!0).catch((yu)=>vu(_l(yu,"自动加载 Trace Summary 失败")))},[i?.id,h?.id,h?.updatedAt,h?.stepCount,h?.llmStepCount,h?._traceSummaryUpdatedAt,h?._traceSummaryLoaded,s]);let EK=gl.length===0?E(Fy,{title:Xl?Ku?"搜索中":"没有匹配任务":"队列为空",text:Xl?Ku?`正在搜索包含“${C1}”的 task...`:`未找到包含“${C1}”的 task;可换个关键词或切换 queue。`:"提交一个任务后,Codex 会串行执行并保存输出。"}):[m$.length>0?E(zA,{key:"unread",title:"已结束未读",tasks:m$,selectedId:I,emptyText:"暂无已结束未读任务。",onSelect:V8,onCopy:H8,onReference:O8,onMarkRead:B8,copiedTaskId:i_,markingReadTaskId:__}):null,E(zA,{key:"active",title:"运行 / 排队",tasks:p$,selectedId:I,emptyText:"当前没有运行或排队任务。",onSelect:V8,onCopy:H8,onReference:O8,onMarkRead:B8,copiedTaskId:i_,markingReadTaskId:__}),E(zA,{key:"history",title:"历史 session",tasks:L8,selectedId:I,emptyText:"最近没有完成、失败或取消的 session。",onSelect:V8,onCopy:H8,onReference:O8,onMarkRead:B8,copiedTaskId:i_,markingReadTaskId:__}),E("div",{key:"pagination",className:"codex-task-pagination","data-testid":"codex-task-pagination"},E("span",null,Xl?`搜索“${C1}” · 已显示 ${gl.length} / ${Number.isFinite(Ir)?Ir:gl.length}`:`已加载 ${gl.length} / ${Number.isFinite(Ir)?Ir:gl.length}`),x$?E("button",{type:"button",className:"ghost-btn",disabled:J_,onClick:()=>void hj(),"data-testid":"codex-load-more-tasks-button"},J_?"加载中":Xl?"加载更多结果":"加载更早任务"):E("code",null,Xl?"已到结果末尾":"已到队列末尾"))],Ij=(V,v=!1)=>E("label",{className:`code-queue-switcher ${v?"compact":""}`},E("span",null,v?"Queue":"查看 queue"),E("select",{value:Du,onChange:(lu)=>$K(String(lu.target.value||W1)),"data-testid":V},E("option",{value:W1},`All queues · ${Number.isFinite(p1)?p1:vr.length} tasks · ${j_.length} running`),Jr.map((lu)=>E("option",{key:String(lu?.id||""),value:String(lu?.id||"")},wA(lu))))),TK=E("div",{className:"codex-task-search","data-testid":"codex-task-search"},E("label",{htmlFor:"codex-task-search-input"},"搜索 task"),E("div",{className:"codex-task-search-row"},E("input",{id:"codex-task-search-input",type:"search",value:uu,placeholder:"关键词 / task ID / prompt",autoComplete:"off",onChange:(V)=>nu(String(V.target.value||"")),"data-testid":"codex-task-search-input"}),uu?E("button",{type:"button",className:"ghost-btn",onClick:()=>nu(""),"data-testid":"codex-task-search-clear"},"清除"):null),E("small",{"data-testid":"codex-task-search-summary"},Xl?Ku?"搜索中...":`匹配 ${gl.length}/${Number.isFinite(Ir)?Ir:gl.length}`:"支持 task ID、prompt、状态、provider、模型和最近输出关键词")),ZK=E("div",{className:"codex-trace-status","data-testid":"codex-trace-status-summary"},E("span",{className:"codex-trace-status-chip queued"},E("b",null,"排队"),String(P$)),E("span",{className:"codex-trace-status-chip running"},E("b",null,"运行"),String(G8)),E("span",{className:`codex-trace-status-chip unread ${m0>0?"warn":""}`},E("b",null,"结束未读"),String(m0)),E("span",{className:"codex-trace-status-chip service"},E("b",null,"服务"),`${R$.providerStatus||"unknown"} · ${i?.providerId||"main-server"} · ${sG.public?"公网暴露":"仅 UniDesk frontend 代理访问"}`),E("span",{className:"codex-trace-status-chip"},E("b",null,"执行节点"),xj.map((V)=>V.id).join(" / ")),E("span",{className:"codex-trace-status-chip"},E("b",null,"模型"),Cj.join(" / ")),E("span",{className:"codex-trace-status-chip"},E("b",null,"加载"),Vl?.phase==="complete"?FT(Vl?.totalMs):String(Vl?.phase||"idle")),E("span",{className:"codex-trace-status-chip"},E("b",null,"刷新"),$_?qf($_):"--")),HK=E(Ti,{title:h?`Trace ${String(h.id).slice(0,22)}`:"Trace 输出",eyebrow:h?`${h.status} / view=${Dy} / task queue=${Tn(h)} / provider=${h.providerId||"main-server"} / ${h.model} / agent loop trace`:`Agent loop trace / view=${Dy}`,summary:ZK,loading:s||Vy||Ku||m||Vl?.phase==="loading",actions:E("div",{className:"panel-actions"},Ij("code-queue-filter-select"),E("button",{type:"button",className:"ghost-btn codex-mark-all-read-btn",disabled:m0===0||Lu||n_,onClick:()=>void nK(),"data-testid":"codex-mark-all-read-button"},n_?"标记中":`全部标已读${m0>0?` (${m0})`:""}`),h?E("button",{type:"button",className:"ghost-btn",disabled:s||Lu,onClick:()=>void NK(),"data-testid":"codex-load-full-trace-button"},s?"加载中":yr(h)?"刷新 Summary":"加载 Summary"):null,E("button",{type:"button",className:"codex-session-title-toggle",onClick:()=>Zu((V)=>!V),"data-testid":"code-queue-sidebar-toggle"},fu?"收起队列":"展开队列"),E("label",{className:"inline-check"},E("input",{type:"checkbox",checked:r_,onChange:(V)=>y_(Boolean(V.target.checked))}),"自动滚动"),E("button",{type:"button",className:"ghost-btn",disabled:!dG||Lu,onClick:()=>void qK(),"data-testid":"codex-interrupt-button"},"打断"),E("button",{type:"button",className:"ghost-btn",disabled:!eG||Lu,onClick:()=>void WK()},"重试"),h?E(Pq,{title:"Codex Task",data:h,onOpen:f,testId:"raw-codex-task"}):null),className:"codex-output-panel"},E("div",{className:`codex-session-shell ${fu?"":"queue-collapsed"}`},fu?E("aside",{className:"codex-session-sidebar","data-testid":"codex-session-sidebar"},E("div",{className:"codex-session-sidebar-head"},E("div",null,E("span",null,ml(Du)?"All queues":"Queue lane"),E("strong",null,`${Dy} · ${vr.length}/${Number.isFinite(p1)?p1:vr.length} sessions · 未读 ${m0}`)),E("button",{type:"button",className:"ghost-btn",onClick:()=>Zu(!1)},"收起")),Ij("code-queue-filter-sidebar",!0),TK,E("div",{className:"codex-task-list codex-task-list-session",onScroll:_K,"data-testid":"codex-task-list-scroll"},EK)):null,E("div",{className:"codex-session-main"},E("div",{className:"codex-output-stack"},E(TZ,{task:h,loading:s,onLoadPromptPart:rK,onLoadSteps:Z8,onLoadStep:yK}),E(ZZ,{task:h})))));if(!i)return E(Fy,{title:"Code Queue 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=code-queue"});let kj=Number(Vl?.totalMs),gj=Number(Vl?.queueMs),sj=Number(Vl?.detailMs),aj=Number(Vl?.transcriptRows),OK=Vl?.phase==="complete"?"complete":String(Vl?.phase||"idle");return E("div",{className:`code-queue-page ${y?"codex-standalone-page":""}`,"data-testid":"code-queue-page","data-load-state":OK,"data-load-total-ms":Number.isFinite(kj)?String(Math.round(kj*10)/10):"","data-load-queue-ms":Number.isFinite(gj)?String(Math.round(gj*10)/10):"","data-load-detail-ms":Number.isFinite(sj)?String(Math.round(sj*10)/10):"","data-load-transcript-rows":Number.isFinite(aj)?String(aj):"","data-load-task-id":String(Vl?.taskId||I||""),"data-load-partial":Vl?.partial?"true":"false"},E(jf,{error:Uf,wide:!0}),yf?E("div",{className:"form-success wide","data-testid":"codex-create-success"},yf):null,E("div",{className:"codex-session-stage codex-session-stage-top"},HK),E("div",{className:"code-queue-layout"},E("div",{className:"codex-left-rail"},E(Ti,{title:"提交任务",eyebrow:ou?"Submitting...":F0.length>1?`${F0.length} tasks`:"Single or Batch",className:"codex-compose-panel",loading:ou},E("form",{className:`codex-task-form ${ou?"is-submitting":""}`,onSubmit:JK,"data-testid":"code-queue-task-form","aria-busy":ou?"true":"false"},E("label",null,"Prompt / 多任务用单独一行 --- 分隔",E("textarea",{value:e,rows:8,disabled:ou,onChange:(V)=>cu(V.target.value),placeholder:"写入 Codex 任务;多个任务之间用 --- 分隔。"})),E("label",{className:"codex-reference-field"},"引用任务 ID(可选)",E("input",{value:g,disabled:ou,onChange:(V)=>Qu(V.target.value),placeholder:"codex_...;支持空格/逗号分隔多个 ID","data-testid":"codex-reference-task-id"}),ny(g).length>0?E("code",null,`后端将解析并注入:${ny(g).join(" / ")}`):null),E("div",{className:"codex-form-grid"},E("label",{className:"codex-submit-queue-field"},"Queue",E("div",{className:"codex-submit-queue-row"},E("select",{value:Eu,disabled:ou,onChange:(V)=>Tu(String(V.target.value||"default")),"data-testid":"code-queue-id-select"},Jr.map((V)=>E("option",{key:String(V?.id||""),value:String(V?.id||"")},wA(V)))),E("button",{type:"button",className:"ghost-btn codex-rename-queue-btn",onClick:()=>void jK(),disabled:Lu||ou||!Eu,title:"修改当前 queue 的显示名称,ID 不变","data-testid":"codex-rename-queue-button"},"改名"),E("button",{type:"button",className:"ghost-btn codex-merge-queue-btn",onClick:()=>void FK(),disabled:Lu||ou||Jr.length<2,title:"把其他 queue 合并到当前 queue;源 queue 记录会保留,不会删除","data-testid":"codex-merge-queue-button"},"合并 queue"),E("button",{type:"button",className:"ghost-btn codex-create-queue-btn",onClick:()=>void AK(),disabled:Lu||ou,"data-testid":"codex-create-queue-button"},"创建 queue"))),E("label",null,"模型",E("select",{value:gf,disabled:ou,onChange:(V)=>jr(V.target.value),"data-testid":"codex-model-select"},Cj.map((V)=>E("option",{key:V,value:V},V)))),E("label",null,"执行 Provider",E("select",{value:rf,disabled:ou,onChange:(V)=>lK(String(V.target.value||"main-server")),"data-testid":"codex-provider-select"},xj.map((V)=>E("option",{key:V.id,value:V.id},`${V.label||V.id} · ${V.defaultWorkdir||D6(Rf,V.id)}`)))),E("label",null,"工作目录",E("input",{value:Ol,disabled:ou,onChange:(V)=>ef(V.target.value),placeholder:oG||Rf?.defaultWorkdir||"/root/unidesk","data-testid":"codex-cwd-input"})),E("label",null,"最大尝试",E("input",{type:"number",min:1,max:99,value:hr,disabled:ou,onChange:(V)=>vl(Number(V.target.value)),"data-testid":"codex-max-attempts-input"})),E("label",null,"入队份数",E("input",{type:"number",min:1,max:50,value:ju,disabled:ou,onChange:(V)=>bu(Number(V.target.value)),"data-testid":"codex-repeat-count-input"}))),U_>1?E("label",{className:`codex-batch-confirm ${ul?"confirmed":""}`,"data-testid":"codex-batch-confirm-row"},E("input",{type:"checkbox",checked:ul,disabled:ou,onChange:(V)=>tu(Boolean(V.target.checked)),"data-testid":"codex-batch-confirm-checkbox"}),E("span",null,`确认批量入队 ${U_} 个任务(prompt 分段 ${w8.length} × 入队份数 ${Eq(ju)})`)):null,ou?E("div",{className:"codex-submit-wait","data-testid":"codex-submit-wait"},"正在提交到后端,已锁定输入以防重复提交..."):null,E("div",{className:"codex-form-actions"},E("button",{type:"button",className:"ghost-btn",disabled:Lu||ou||e.length===0&&g.length===0,onClick:()=>{cu(""),Qu(""),tu(!1);let V="已清空任务输入栏";_f(V),xf("success",V)},"data-testid":"codex-clear-input-button"},"清空输入"),E("button",{type:"submit",className:"primary-btn",disabled:aG,"data-testid":"codex-enqueue-button"},ou?"提交中,请等待...":pj?`请确认批量入队 ${U_} 个任务`:F0.length>1?`批量入队 ${F0.length} 个任务`:"入队并运行"))))),E("div",{className:"codex-main-stage"},E("div",{className:"codex-detail-grid"},E(Ti,{title:"运行控制",eyebrow:ty?"Queued prompt editable":E8?"Active turn steer":"Steer when running",loading:Lu},E("div",{className:"codex-run-control-stack"},E(GZ,{task:h,queueRows:Jr,busy:Lu,onMove:cK}),h?.id?E("form",{className:"codex-steer-form codex-edit-prompt-form",onSubmit:QK,"data-testid":"codex-edit-prompt-form"},E("label",null,"编辑 queued 用户 prompt",E("textarea",{value:Il,rows:5,onChange:(V)=>Fr(V.target.value),placeholder:"仅 QUEUED 且尚未开始运行的任务可在这里修改原始用户 prompt。",disabled:!ty||Lu,"data-testid":"codex-edit-prompt-textarea"})),E("label",{className:"codex-reference-field"},"引用任务 ID(可选,留空会清除引用)",E("input",{value:kl,disabled:!ty||Lu,onChange:(V)=>M0(V.target.value),placeholder:"codex_...;支持空格/逗号分隔多个 ID","data-testid":"codex-edit-reference-task-id"}),ny(kl).length>0?E("code",null,`将保留/注入:${ny(kl).join(" / ")}`):null),E("div",{className:"codex-form-actions"},E("button",{type:"button",className:"ghost-btn",disabled:!h?.id||Lu,onClick:()=>{Fr(h?Y6(h):""),M0(Array.isArray(h?.referenceTaskIds)?h.referenceTaskIds.join(" "):"")},"data-testid":"codex-edit-prompt-reset"},"恢复当前值"),E("button",{type:"submit",className:"primary-btn",disabled:!ty||Lu||Il.trim().length===0,title:ty?"保存后会重写尚未运行任务的用户 prompt":"只有 QUEUED 且尚未开始的任务可编辑 prompt","data-testid":"codex-edit-prompt-submit"},"保存 queued prompt"))):null,E("form",{className:"codex-steer-form",onSubmit:UK},E("label",null,"追加 prompt",E("textarea",{value:Bl,rows:4,onChange:(V)=>sf(V.target.value),placeholder:"给正在运行的 Codex session 推入新的指令或纠偏。",disabled:!E8})),E("button",{type:"submit",className:"primary-btn",disabled:!E8||Lu||Bl.trim().length===0,"data-testid":"codex-steer-button"},"推入运行中 session")))),E(Ti,{title:"完成判定",eyebrow:h?.lastJudge?h.lastJudge.source:"judge",loading:s},h?.lastJudge?E("div",{className:"codex-judge-card","data-testid":"codex-task-judge-card"},E(jy,{status:h.lastJudge.decision},h.lastJudge.decision),E("strong",null,`${Math.round(Number(h.lastJudge.confidence||0)*100)}% confidence`),E("p",{"data-testid":"codex-task-judge-reason"},GA(h.lastJudge.reason||"--",180)),h.lastJudge.continuePrompt?E("code",{"data-testid":"codex-task-judge-continue-prompt"},GA(h.lastJudge.continuePrompt,160)):null):E(Fy,{title:"尚未判定",text:"Codex turn 结束后会由 MiniMax M2.7 或 fallback judge 判定 complete/retry/fail;retry 会在已有 thread 追加继续执行 prompt。"}))),E(zZ,{stats:z8,queueName:Dy,onRaw:f}),E(Ti,{title:"Attempts",eyebrow:"terminal vs interruption",loading:s},E(HZ,{task:h})))))}var Bn=Pu(wf(),1);var Vu=Bn.default.createElement,{useEffect:tA}=Bn.default,SA=Bn.default.useState,OZ=Bn.default.useRef,MA=` +本次任务:`,r=f.indexOf(l);if(r===-1)return u;return f.slice(r+l.length).trimStart()}function L1(u){return u.length>0?u.split(/\r\n|\r|\n/u).length:0}function iQ(u){let f=String(u?.displayPrompt||"");if(f.length>0)return f;let l=String(u?.prompt||"");return cE(tE(l).userPrompt)}function _r(u){return u?._traceSummary&&typeof u._traceSummary==="object"&&!Array.isArray(u._traceSummary)?u._traceSummary:null}function x3(u){return u?._promptDetails&&typeof u._promptDetails==="object"&&!Array.isArray(u._promptDetails)?u._promptDetails:{}}function hc(u){let f=_r(u)?.prompt;return f&&typeof f==="object"&&!Array.isArray(f)?f:{}}function yQ(u){let f=_r(u)?.execution;return f&&typeof f==="object"&&!Array.isArray(f)?f:{}}function vU(u){let f=Number(u);return Number.isFinite(f)&&f>=0?Math.floor(f):0}function D0(u){let f=Number(u);return Number.isFinite(f)&&f>=0?Math.floor(f):null}function _Q(u){let f=Hf(u?.traceStats);if(f&&(u?.statsSource==="oa-event-flow"||f.source==="oa-event-flow"))return f;let l=_r(u),r=Hf(l?.traceStats);if(r&&(l?.statsSource==="oa-event-flow"||r.source==="oa-event-flow"))return r;return null}function AE(u,f){let l=Hf(f?.traceStats)||Hf(f?.execution?.traceStats),r=String(f?.statsSource||f?.execution?.statsSource||"");if(l&&(r==="oa-event-flow"||l.source==="oa-event-flow"))return l;return null}function jE(u,f){return D0(_Q(u)?.[f])}function $Q(u){let f=jE(u,"stepCount");if(f!==null)return f;let l=_r(u);if(l!==null)return FE(l);return null}function FE(u){let f=Hf(u?.traceStats);if(!f||u?.statsSource!=="oa-event-flow"&&f.source!=="oa-event-flow")return null;return D0(f.stepCount)}function p3(u){if(!u||u?._traceSummaryLoaded!==!0)return!1;let f=_r(u),l=String(u?._traceSummaryUpdatedAt||f?.updatedAt||""),r=String(u?.updatedAt||"");if(r.length>0){let n=X0(l),i=X0(r);if(n!==null&&i!==null){if(n+10?l:iQ(u)}function tQ(u){let f=_r(u);return String(f?.finalResponse||u?.finalResponse||"").trimEnd()}function cQ(u){let l=_r(u)?.lastJudge||u?.lastJudge;return l&&typeof l==="object"&&!Array.isArray(l)?l:null}function Hf(u){return u&&typeof u==="object"&&!Array.isArray(u)?u:null}function JE(u){let f=_r(u)?.attempts;if(Array.isArray(f)&&f.length>0)return f;let l=yQ(u),r=tQ(u),n=cQ(u);if(Object.keys(l).length===0&&r.length===0&&n===null)return[];return[{index:Number(u?.currentAttempt||1),mode:u?.currentMode||"initial",startedAt:u?.startedAt,finishedAt:u?.finishedAt,terminalStatus:u?.status,execution:l,finalResponse:r,finalResponseChars:r.length,judge:n}]}function UE(u,f){return Hf(f?.execution)||yQ(u)}function QE(u,f,l,r){let n=_r(u),i=Number(n?.currentAttempt||u?.currentAttempt||0),y=Number(l),_=Number.isFinite(y)&&y>0&&y===i,$=lQ(u?.updatedAt,n?.updatedAt);if(_&&!f?.finishedAt&&$.length>0)return $;return String(f?.updatedAt||f?.finishedAt||r.effectiveEndAt||(_?$:"")||$||u?.finishedAt||u?.startedAt||"")}function qE(u,f){let l=String(f?.finalResponse||f?.finalResponsePreview||"");if(Object.prototype.hasOwnProperty.call(f||{},"finalResponse")||Object.prototype.hasOwnProperty.call(f||{},"finalResponsePreview"))return l.trimEnd();return l.length>0?l.trimEnd():tQ(u)}function AQ(u,f){if(Object.prototype.hasOwnProperty.call(f||{},"judge"))return Hf(f?.judge);return cQ(u)}function NE(u,f,l){if(!YE(u))return!1;if(bc(f,l))return!1;if(f?.finishedAt)return!1;if(["succeeded","failed","canceled"].includes(String(f?.terminalStatus||"")))return!1;let r=_r(u),n=Number(r?.currentAttempt||u?.currentAttempt||0),i=Number(l);if(Number.isFinite(i)&&i>0&&Number.isFinite(n)&&n>0)return i===n;return!0}function jQ(u){return`feedback:${String(u||"latest")}`}function WE(u,f,l){let r=String(f?.feedbackPrompt||"").trimEnd(),n=String(f?.feedbackPromptPreview||r||"").trimEnd(),i=Number(f?.feedbackPromptChars||r.length||n.length||0),y=Number(f?.feedbackPromptLines||L1(r||n));if(r.length>0||n.length>0||i>0)return{text:r,preview:n,chars:i,lines:y,source:f?.feedbackPromptSource||"judge-feedback",forAttempt:f?.feedbackPromptForAttempt||Number(l||0)+1,truncated:Boolean(f?.feedbackPromptTruncated)};let _=AQ(u,f),$=String(_?.continuePrompt||"").trimEnd();if(_?.decision==="retry"&&$.length>0)return{text:"",preview:$,chars:$.length,lines:L1($),source:"judge-continue-prompt",forAttempt:Number(l||0)+1,truncated:!1};return null}function wE(u){let f=hc(u);return Boolean(f.hasReferenceInjection||Number(f.referencePromptChars||0)>0||u?.referenceInjection||u?.referenceInjectionSummary)}function FQ(u,f=null){if(f!==null&&f!==void 0){let r=(Hf(u?._traceStepsByAttempt)||{})[String(f)];return Array.isArray(r)?r:[]}return Array.isArray(u?._traceSteps)?u._traceSteps:[]}function mc(u){let l=[u?.seq,...Array.isArray(u?.rawSeqs)?u.rawSeqs:[]].map((r)=>Number(r)).filter((r)=>Number.isFinite(r));return l.length>0?Math.max(...l):0}function LE(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>Math.max(f,mc(l)),0)}function KE(u,f){let l=new Map;for(let r of[...Array.isArray(u)?u:[],...Array.isArray(f)?f:[]]){let n=String(r?.seq??`${r?.title||"step"}:${r?.at||""}`);l.set(n,{...l.get(n)||{},...r})}return Array.from(l.values()).sort((r,n)=>mc(r)-mc(n))}function xi(u){return(Array.isArray(u?.summaryLines)?u.summaryLines:[]).map((f)=>String(f||""))}function GE(u){let f=String(u?.kind||"").trim().toLowerCase(),l=String(u?.status||"").trim().toLowerCase();return f==="error"||l==="error"}function h3(u){let f=String(u?.status||"").trim();if(f.length>0)return f;let l=xi(u).join(` +`);return/^(item\/[A-Za-z]+(?:\/[A-Za-z]+)?):/u.exec(l)?.[1]||""}function kU(u){return/^item\/(?:started|completed): file changes status=/u.test(String(u||"").trim())}function zE(u){let f=xi(u);for(let r=f.length-1;r>=0;r-=1){let n=/file changes status=([A-Za-z0-9_-]+)/u.exec(f[r]||"")?.[1];if(n)return n}let l=h3(u);if(l==="item/fileChange/outputDelta")return"updated";if(l==="item/started")return"started";if(l==="item/completed")return"completed";return l.replace(/^item\//u,"")||String(u?.status||"changed")}function TE(u){if(String(u?.kind||"")!=="edited")return!1;let f=String(u?.title||""),l=String(u?.status||""),r=xi(u).join(` +`);if(f==="Edited files")return!0;if(/^item\/fileChange\//u.test(l))return!0;if((l==="item/started"||l==="item/completed")&&/file changes status=/u.test(r))return!0;if(/^Success\. Updated the following files:/mu.test(r))return!0;if(/^diff --git /mu.test(r))return!0;return/^([AMDRCU?]{1,2})\s+\S+/mu.test(r)}function EE(u){if(u.length<=1)return u[0];let f=u.find((i)=>h3(i)==="item/fileChange/outputDelta")||u.find((i)=>xi(i).some((y)=>!kU(y)))||u.at(-1)||u[0],l=u.flatMap((i)=>Array.isArray(i?.rawSeqs)?i.rawSeqs:[i?.seq]).filter((i)=>i!==void 0),r=u.flatMap(xi).filter((i)=>i.trim().length>0&&!kU(i)),n=u[u.length-1]||f;return{...f,at:f?.at||n?.at,title:String(f?.title||"Edited files"),status:zE(n),summaryLines:r.length>0?r:xi(f),rawSeqs:l}}function ZE(u){let f=Array.isArray(u)?u:[],l=[],r=[],n=()=>{if(r.length>0)l.push(EE(r));r=[]};for(let i of f){if(TE(i)){if(h3(i)==="item/started"&&r.length>0)n();if(r.push(i),h3(i)==="item/completed")n();continue}n(),l.push(i)}return n(),l}function OE(u){return{...u}}function pc(u,f=null){if(f!==null&&f!==void 0){let l=Hf(u?._traceStepsLoadedByAttempt)||{};return Boolean(l[String(f)])}return Boolean(u?._traceStepsLoaded)}function Pc(u){return u?._traceStepDetails&&typeof u._traceStepDetails==="object"&&!Array.isArray(u._traceStepDetails)?u._traceStepDetails:{}}function HE(u,f){let l=Number(u?.index);return Number.isFinite(l)?l:f+1}function bc(u,f){return Boolean(u?.synthetic)||Number(f)<=0}function b3(u){let f=Number(u);return Number.isFinite(f)?String(f):void 0}function BE(u){let f=u?.timing&&typeof u.timing==="object"?u.timing:{},l=String(u?.status||"");if(["queued"].includes(l))return`等待 ${Rr(f.queueWaitMs??f.totalElapsedMs)}`;if(["running","judging","retry_wait"].includes(l))return`耗时 ${Rr(f.durationMs??f.totalElapsedMs)}`;return`耗时 ${Rr(f.durationMs??f.totalElapsedMs)}`}function m_(u){return String(u?.queueId||"default")}function JQ(u){return Hf(u?.queuedReason)}function UQ(u){let f=String(u?.queuedReasonLabel||"").trim();if(f.length>0)return f.toUpperCase();let l=JQ(u),r=String(l?.label||"").trim();return r.length>0?r.toUpperCase():""}function VE(u){let f=String(u?.status||"unknown");if(f!=="queued")return f;let l=UQ(u);return l.length>0?`QUEUED(${l})`:"QUEUED"}function DE(u){if(String(u?.status||"")!=="queued")return;let f=JQ(u),l=String(f?.message||"").trim(),r=UQ(u);if(l.length>0&&r.length>0)return`${r}: ${l}`;if(l.length>0)return l;return r.length>0?r:void 0}function XE(u){return{system:"SYS",user:"YOU",assistant:"GPT",reasoning:"THINK",command:"CMD",diff:"DIFF",tool:"TOOL",error:"ERR"}[u]||u.toUpperCase()}function IU(u){return["running","judging","retry_wait"].includes(String(u?.status||""))}function YE(u){return String(u?.status||"")==="running"}function Cr(u){return["succeeded","failed","canceled"].includes(String(u?.status||""))}function QQ(u){if(u?.promptEditable===!0)return!0;if(u?.promptEditable===!1)return!1;return String(u?.status||"")==="queued"&&!u?.startedAt&&Number(u?.currentAttempt||0)===0&&!u?.codexThreadId&&!u?.nextMode}function V0(u){if(!Cr(u))return!1;if(u?.terminalUnread===!0)return!0;if(u?.terminalUnread===!1)return!1;return!u?.readAt}function Cf(u){let f=Number(u||0);return Number.isFinite(f)?f:0}function SE(u){return Cf(u.queued)+Cf(u.retry_wait)}function mE(u){return Cf(u.running)+Cf(u.judging)}function pE(u,f){return Hf(u?.statistics)||Hf(f?.statistics)||{}}function PE(u){return Array.isArray(u?.daily)?u.daily:[]}function ME(u){return Hf(u?.totals)||{}}function vc(u,f){let l=Number(u?.[f]??0);return Number.isFinite(l)&&l>0?l:0}function Hc(u,f){return u.reduce((l,r)=>Math.max(l,vc(r,f)),0)}var w1=700,gU=220,zn=30,Ci=24,p_=184,Mc=p_-Ci;function qQ(u,f){if(f<=1)return w1/2;return zn+u*(w1-zn*2)/(f-1)}function NQ(u,f){let l=f>0?f:1;return p_-Math.min(1,u/l)*Mc}function Bc(u,f,l){let r=u.length>0?u:[{[f]:0}],n=r.length>1?r:[r[0],r[0]];return n.map((i,y)=>`${qQ(y,n.length).toFixed(2)},${NQ(vc(i,f),l).toFixed(2)}`).join(" ")}function B0(u){let f=String(u||"");return/^\d{4}-\d{2}-\d{2}$/u.test(f)?f.slice(5):f||"--"}function P3(u){if(!u)return"";return`${String(u.seriesKey||"")}:${String(u.row?.date||u.index||"")}`}function CE(u,f,l,r){let n=vc(u,r.key);return{...r,row:u,index:f,value:n,valueLabel:r.format(n),x:qQ(f,l),y:NQ(n,r.max),seriesKey:r.key}}function sU(u){if(V0(u))return 0;return{running:1,judging:2,retry_wait:3,queued:4,succeeded:8,failed:8,canceled:8}[String(u?.status||"")]??9}function X_(u){if(!u)return!1;if(u?._traceSummaryLoaded===!0)return!1;return u?.summaryOnly===!0||u?._metaLoaded!==!0}function RE(u){return Boolean(u?._metaLoaded)||u?.summaryOnly===!1}function xE(u,f,l){let r=String(u?.[l]||""),n=String(f?.[l]||"");return r.length>n.length?r:n}function Cc(u,f,l){let r=Array.isArray(u?.[l])?u[l]:[],n=Array.isArray(f?.[l])?f[l]:[];if(n.length===0&&r.length>0)return r;return r.length>n.length?r:n}function aU(u,f){let l=f?.summaryOnly===!0&&RE(u),r={...u,...f};if(!l)return r;for(let n of["prompt","basePrompt","displayPrompt","finalResponse"])r[n]=xE(u,f,n);for(let n of["promptHistory","attempts","output","events"])r[n]=Cc(u,f,n);if(u?.referenceInjection?.items&&!f?.referenceInjection?.items)r.referenceInjection=u.referenceInjection;if(u?.referenceInjectionSummary&&!f?.referenceInjectionSummary)r.referenceInjectionSummary=u.referenceInjectionSummary;r.summaryOnly=u?.summaryOnly===!1?!1:f.summaryOnly,r._metaLoaded=u?._metaLoaded,r._detailLoaded=u?._detailLoaded,r._transcriptComplete=u?._transcriptComplete,r._transcriptPreview=Object.prototype.hasOwnProperty.call(f,"_transcriptPreview")?f._transcriptPreview:u?._transcriptPreview;for(let n of["_traceSummary","_traceSummaryLoaded","_traceSteps","_traceStepsLoaded","_traceStepsByAttempt","_traceStepsLoadedByAttempt","_traceStepsNextAfterSeqByAttempt","_traceStepDetails","_promptDetails"])if(!Object.prototype.hasOwnProperty.call(f,n)&&Object.prototype.hasOwnProperty.call(u||{},n))r[n]=u[n];return r}function hE(u){let f=u?.selected,l=f?.task&&typeof f.task==="object"?f.task:null;if(l!==null){let n=Boolean(f?.preview);return{...l,transcript:Array.isArray(f?.transcript)?f.transcript:[],_detailLoaded:Array.isArray(f?.transcript)&&f.transcript.length>0,_transcriptComplete:Boolean(!n&&!f?.hasMore&&Cr(l)),_transcriptPreview:n,_summaryLoaded:!0}}let r=tl(u)[0];return r?{...r,_summaryLoaded:!0}:null}function Vc(u,f){let l=new Map;for(let r of[...Array.isArray(u)?u:[],...Array.isArray(f)?f:[]]){let n=`${Number(r?.seq??0)}:${String(r?.kind||"message")}`,i=l.get(n);if(!i){l.set(n,r);continue}let y={...i,...r};for(let[_,$]of[["bodyPreview","bodyOmittedLines"],["commandPreview","commandOmittedLines"]]){let c=String(i?.[_]||""),A=String(r?.[_]||"");if(c.length>A.length)y[_]=i[_],y[$]=i[$]}l.set(n,y)}return Array.from(l.values()).sort((r,n)=>Number(r?.seq??0)-Number(n?.seq??0))}function M3(u){return(Array.isArray(u)?u:[]).reduce((f,l)=>Math.max(f,Number(l?.seq??0)),0)}function oU(u,f=8){let l=Array.from(new Set((Array.isArray(u)?u:[]).map((n)=>Number(n?.seq??0)).filter((n)=>Number.isFinite(n)&&n>0))).sort((n,i)=>n-i);if(l.length===0)return 0;let r=l[Math.max(0,l.length-f)]??0;return Math.max(0,r-0.001)}function bE(u,f){let l=Array.isArray(u?.codeModels)?u.codeModels:Array.isArray(u?.codexModels)?u.codexModels:[],r=["gpt-5.5","gpt-5.4-mini","gpt-5.4","minimax-m2.7"];return Array.from(new Set([...l,...r,f].map((n)=>String(n||"").trim()).filter(Boolean)))}function vE(u,f){let r=(Array.isArray(u?.executionProviders)?u.executionProviders:[]).map((y)=>({id:String(y?.id||"").trim(),label:String(y?.label||y?.id||"").trim(),defaultWorkdir:String(y?.defaultWorkdir||"").trim(),kind:String(y?.kind||"").trim()})).filter((y)=>y.id.length>0),n=String(u?.mainProviderId||u?.defaultProviderId||"main-server").trim()||"main-server",i=new Map;for(let y of[...r,{id:n,label:`${n} (master)`,defaultWorkdir:String(u?.defaultWorkdir||"/root/unidesk"),kind:"local"},f?{id:f,label:f,defaultWorkdir:R3(u,f),kind:""}:null].filter(Boolean))if(!i.has(y.id))i.set(y.id,y);return Array.from(i.values())}function R3(u,f){let l=String(f||"").trim(),r=u?.defaultWorkdirByProvider&&typeof u.defaultWorkdirByProvider==="object"?u.defaultWorkdirByProvider:{};if(typeof r[l]==="string"&&String(r[l]).trim().length>0)return String(r[l]).trim();let n=Array.isArray(u?.executionProviders)?u.executionProviders.find((y)=>String(y?.id||"")===l):null;if(typeof n?.defaultWorkdir==="string"&&n.defaultWorkdir.trim().length>0)return n.defaultWorkdir.trim();let i=String(u?.mainProviderId||u?.defaultProviderId||"main-server");return l===i?String(u?.defaultWorkdir||"/root/unidesk"):String(u?.remoteDefaultWorkdir||"/home/ubuntu")}function kE(u){return $Q(u)}function IE({task:u,selected:f,onSelect:l,onCopy:r,onReference:n,onMarkRead:i,copied:y,markingRead:_}){let $=u?.lastJudge||{},c=String(u?.id||""),A=V0(u),j=lQ(u?.updatedAt,_r(u)?.updatedAt),F=`最近更新: ${fQ(j)}`,U=kE(u),Q=U===null?"--":String(U),w=U===null?"STEP 统计中心同步中":"STEP 来自 OA Event Flow 统计中心";return K("article",{role:"button",tabIndex:0,className:`codex-task-card ${f?"selected":""} ${A?"unread-terminal":""}`,onClick:l,onKeyDown:(L)=>{if(L.key==="Enter"||L.key===" ")L.preventDefault(),l()},"data-unread-terminal":A?"true":"false","data-testid":`codex-task-${u?.id||"unknown"}`},A?K("span",{className:"codex-unread-badge",title:"待读","aria-label":"待读","data-testid":`codex-unread-task-${c||"unknown"}`}):null,K("div",{className:"codex-task-card-head"},K("div",{className:"codex-task-status-line"},K(K1,{status:u?.status,title:DE(u)},VE(u))),K("span",{className:"mono-text"},`${u?.currentAttempt||0}/${u?.maxAttempts||0}`)),K("div",{className:"codex-task-id-row"},K("code",{title:c},c||"unknown"),K("div",{className:"codex-task-id-actions"},K("button",{type:"button",className:"codex-copy-id-btn",onClick:(L)=>{L.stopPropagation(),n(c)},"data-testid":`codex-reference-task-${c||"unknown"}`},"引用"),K("button",{type:"button",className:"codex-copy-id-btn",onClick:(L)=>{L.stopPropagation(),r(c)},"data-testid":`codex-copy-task-id-${c||"unknown"}`},y?"已复制":"复制ID"),A?K("button",{type:"button",className:"codex-copy-id-btn codex-mark-read-btn",disabled:Boolean(_),onClick:(L)=>{L.stopPropagation(),i(c)},"data-testid":`codex-mark-task-read-${c||"unknown"}`},_?"标记中":"标为已读"):null)),K("strong",null,Xc(iQ(u),120)||"空任务"),K("div",{className:"codex-task-meta"},K("span",null,`queue=${m_(u)}`),K("span",null,`provider=${u?.providerId||"main-server"}`),K("span",null,u?.model||"--"),K("span",null,BE(u))),K("div",{className:"codex-task-meta codex-task-update-meta"},K("span",{className:"codex-task-recent-update codex-task-step-count",title:w,"data-testid":`codex-task-step-count-${c||"unknown"}`},`STEP ${Q}`),K("span",{className:"codex-task-recent-update",title:j?`更新时间: ${Nu(j)}`:F,"data-testid":`codex-task-recent-update-${c||"unknown"}`},F),K("span",null,Nu(j||u?.updatedAt))),QQ(u)?K("div",{className:"codex-judge-line","data-testid":`codex-task-prompt-editable-${c||"unknown"}`},"queued prompt 可编辑"):null,$?.decision?K("div",{className:"codex-judge-line"},`judge=${$.decision} ${Math.round(Number($.confidence||0)*100)}%`):null)}function Dc({title:u,tasks:f,selectedId:l,onSelect:r,onCopy:n,onReference:i,onMarkRead:y,copiedTaskId:_,markingReadTaskId:$,emptyText:c}){let A=Array.isArray(f)?f:[];return K("section",{className:"codex-task-section"},K("div",{className:"codex-task-section-head"},K("span",null,u),K("code",null,String(A.length))),A.length===0?K("p",{className:"codex-task-section-empty"},c):K("div",{className:"codex-task-section-list"},A.map((j)=>K(IE,{key:j.id,task:j,selected:l===j.id,onSelect:()=>r(j.id),onCopy:n,onReference:i,onMarkRead:y,copied:_===j.id,markingRead:$===j.id}))))}function gE(){return K("span",{className:"codex-stats-icon","aria-hidden":"true"},K("svg",{viewBox:"0 0 36 24",focusable:"false"},K("path",{className:"grid",d:"M3 20.5H33M3 12.5H33M3 4.5H33"}),K("polyline",{className:"line tasks",points:"3,18 9,14 15,15 21,8 27,10 33,4"}),K("polyline",{className:"line retry",points:"3,20 9,17 15,18 21,13 27,14 33,9"})))}function sE({stats:u,queueName:f,onRaw:l}){let r=PE(u),n=ME(u),i=r.at(-1)||{},y=Hc(r,"executedTasks"),_=Hc(r,"retryAttempts"),$=Hc(r,"avgDurationMs"),c=r.length>0,A=Hf(u?.range)||{},[j,F]=Iu(null),[U,Q]=Iu(null),w=[];if(y>0)w.push(`tasks ${y}`);if(_>0)w.push(`retry ${_}`);if($>0)w.push(`avg ${Rr($)}`);let L=[{key:"executedTasks",className:"tasks",label:"执行任务",max:y,format:(E)=>`${Cf(E)} tasks`},{key:"retryAttempts",className:"retry",label:"重试次数",max:_,format:(E)=>`${Cf(E)} retries`},{key:"avgDurationMs",className:"duration",label:"平均耗时",max:$,format:(E)=>Rr(E)}],J=j||U,N=P3(J),q=String(J?.row?.date||""),W=J?{left:`${Math.max(8,Math.min(92,Number(J.x)/w1*100))}%`,top:`${Math.max(14,Math.min(86,Number(J.y)/gU*100))}%`}:void 0;Gn(()=>{F(null),Q(null)},[f,A.startDate,A.endDate,r.length]);let z=(E)=>{F(E)},Z=(E)=>{let D=P3(E);Q((h)=>P3(h)===D?null:E),F(E)},H=L.flatMap((E)=>r.map((D,h)=>{let B=CE(D,h,r.length,E),X=P3(B),m=N===X,O=String(D?.date||`day-${h}`),p=`${B0(O)} ${E.label}: ${B.valueLabel}`;return K("g",{key:`${E.key}-${O}`,className:`stat-point-group ${E.className} ${m?"active":""}`,role:"button",tabIndex:0,"aria-label":p,"data-testid":`codex-stats-point-${E.className}-${O}`,onMouseEnter:()=>z(B),onFocus:()=>z(B),onClick:()=>Z(B),onKeyDown:(Y)=>{if(Y.key==="Enter"||Y.key===" ")Y.preventDefault(),Z(B)}},K("circle",{className:"stat-hit-point",cx:B.x,cy:B.y,r:13}),K("circle",{className:`stat-point ${E.className} ${m?"active":""}`,cx:B.x,cy:B.y,r:m?5.6:4.2}))}));return K(Mi,{title:"统计曲线",eyebrow:`Daily task stats / ${f}`,className:"codex-stats-panel",summary:K("span",null,`${B0(A.startDate)} -> ${B0(A.endDate)} · ${u?.timezone||"Asia/Shanghai"}`),actions:Hf(u)?K(rQ,{title:"Code Queue Stats",data:u,onOpen:l,testId:"raw-codex-stats"}):null},K("div",{className:"codex-stats-hero","data-testid":"codex-stats-panel"},K(gE),K("div",null,K("strong",null,`${Cf(n.executedTasks)} tasks / ${Cf(n.retryAttempts)} retries`),K("span",null,`平均完成耗时 ${Rr(n.avgDurationMs??void 0)} · 终态 ${Cf(n.completedTasks)} 个`))),c?K("div",{className:"codex-stats-chart","data-testid":"codex-stats-chart",onMouseLeave:()=>F(null)},K("svg",{viewBox:`0 0 ${w1} ${gU}`,preserveAspectRatio:"none",role:"img","aria-label":"Code Queue daily task statistics"},K("line",{className:"axis",x1:zn,x2:w1-zn,y1:p_,y2:p_}),K("line",{className:"grid",x1:zn,x2:w1-zn,y1:Ci+Mc/2,y2:Ci+Mc/2}),K("line",{className:"grid",x1:zn,x2:w1-zn,y1:Ci,y2:Ci}),K("polyline",{className:"stat-line tasks",points:Bc(r,"executedTasks",y)}),K("polyline",{className:"stat-line retry",points:Bc(r,"retryAttempts",_)}),K("polyline",{className:"stat-line duration",points:Bc(r,"avgDurationMs",$)}),J?K("g",{className:"stat-cursor-layer","data-testid":"codex-stats-active-point"},K("line",{className:"stat-cursor",x1:J.x,x2:J.x,y1:Ci,y2:p_}),K("circle",{className:`stat-point-active ${J.className}`,cx:J.x,cy:J.y,r:8})):null,K("g",{className:"stat-point-layer"},H)),J?K("div",{className:"codex-stats-tooltip active",style:W,"data-testid":"codex-stats-tooltip"},K("b",null,B0(J.row?.date)),K("span",null,`${J.label} · ${J.valueLabel}`),K("code",null,`${Cf(J.row?.executedTasks)} exec / ${Cf(J.row?.retryAttempts)} retry / ${Rr(J.row?.avgDurationMs??void 0)}`)):null,K("div",{className:"codex-stats-legend"},K("span",{className:"tasks"},"执行任务"),K("span",{className:"retry"},"重试次数"),K("span",{className:"duration"},"平均耗时")),K("div",{className:"codex-stats-scale"},K("span",null,B0(r[0]?.date)),K("span",null,w.join(" · ")||"暂无峰值"),K("span",null,B0(r.at(-1)?.date))),K("div",{className:`codex-stats-focus ${J?"active":""}`,"data-testid":"codex-stats-focus"},J?K(P_.default.Fragment,null,K("div",null,K("strong",null,B0(J.row?.date)),K("span",null,`${J.label} · ${J.valueLabel}`)),K("div",{className:"codex-stats-focus-metrics"},K("code",null,`${Cf(J.row?.executedTasks)} exec`),K("code",null,`${Cf(J.row?.retryAttempts)} retry`),K("code",null,Rr(J.row?.avgDurationMs??void 0)))):K("span",null,"将鼠标悬停到曲线数据点查看明细,点击数据点可固定。"))):K(G1,{title:"暂无统计",text:"任务开始执行后会生成按天汇总的曲线。"}),K("div",{className:"codex-stats-summary-grid"},K("article",null,K("span",null,"今日执行"),K("strong",null,String(Cf(i.executedTasks))),K("code",null,B0(i.date))),K("article",null,K("span",null,"今日重试"),K("strong",null,String(Cf(i.retryAttempts))),K("code",null,`累计 ${Cf(n.retryAttempts)}`)),K("article",null,K("span",null,"平均耗时"),K("strong",null,Rr(n.avgDurationMs??void 0)),K("code",null,`${Cf(n.durationSamples)} samples`))),K("div",{className:"codex-stats-daily-list","data-testid":"codex-stats-daily-list"},r.slice(-7).map((E)=>K("div",{key:String(E?.date||""),className:`codex-stats-daily-row ${q===String(E?.date||"")?"active":""}`,"data-testid":`codex-stats-day-${String(E?.date||"unknown")}`},K("span",null,B0(E?.date)),K("b",null,`${Cf(E?.executedTasks)} exec`),K("b",null,`${Cf(E?.retryAttempts)} retry`),K("code",null,Rr(E?.avgDurationMs??void 0))))))}function aE({task:u,queueRows:f,busy:l,onMove:r}){let n=String(u?.id||""),i=m_(u),[y,_]=Iu(i);Gn(()=>{_(i)},[n,i]);let $=!n||l||["running","judging","retry_wait"].includes(String(u?.status||""));return K("div",{className:"codex-task-move-control","data-testid":"codex-task-queue-move-control"},K("label",null,"任务 queue",K("select",{value:y,disabled:!n||l,onChange:(c)=>_(String(c.target.value||i)),"data-testid":"codex-task-queue-move-select"},f.map((c)=>K("option",{key:String(c?.id||""),value:String(c?.id||"")},S_(c))))),K("button",{type:"button",className:"ghost-btn",disabled:$||y===i,onClick:()=>r(y),title:$?"运行中 / judging / retry_wait 的任务不能移动;请先打断或等当前 turn 结束":"移动已创建任务到另一个 queue","data-testid":"codex-task-queue-move-button"},"移动"))}function dU(u,f=4){let l=(Array.isArray(u)?u:[]).map((n)=>String(n||"").trim()).filter(Boolean);if(l.length===0)return"--";let r=l.slice(0,f).join(" / ");return l.length>f?`${r} +${l.length-f}`:r}function oE({task:u,loading:f,onLoadPromptPart:l,testId:r="codex-initial-prompt-full",textTestId:n="codex-initial-prompt-full-text",baseTextTestId:i="codex-initial-prompt-base"}){let y=hc(u),_=x3(u),$=C3(u).trimEnd(),c=String(_.full?.text||""),A=wE(u),j=Number(y.promptChars||u?.promptChars||c.length),F=Number(y.basePromptLines||L1($)),U=Number(y.promptLines||L1(c));return K("section",{className:"codex-progressive-card codex-progressive-prompt","data-testid":"codex-progressive-prompt"},K("div",{className:"codex-progressive-card-head"},K("span",{className:"codex-output-channel"},"Prompt"),K("strong",null,"Submitted prompt / 原始用户 prompt"),K("code",null,`${F||L1($)} lines / ${$.length} chars`)),K("pre",{className:"codex-prompt-full","data-testid":i},$||"空 prompt"),A?K("details",{className:"codex-reference-injection codex-progressive-full-prompt","data-testid":r,onToggle:(Q)=>{if(Q.currentTarget?.open&&!c)l?.("full")}},K("summary",null,K("span",null,"引用注入已折叠,点击按需拉取最终进入 Code agent 的完整 prompt"),K("code",null,c?`${U||L1(c)} lines / ${c.length} chars`:`${Number.isFinite(j)&&j>0?j:"--"} chars`)),K("pre",{className:"codex-prompt-full codex-prompt-final-full","data-testid":n},c||(f?"正在按需拉取完整 prompt...":"展开后将只请求 full prompt,不拉取完整 transcript。"))):null)}function WQ({task:u,attempt:f,attemptIndex:l,loading:r,onLoadSteps:n,onLoadStep:i,testId:y="codex-execution-summary"}){let _=ZE(FQ(u,l)),$=OE(UE(u,f)),c=f?AE(u,f):_Q(u),A=Pc(u),j=pc(u,l),F=D0(c?.errorCount),U=D0(c?.stepCount??c?.llmStepCount),Q=D0(c?.readCount),w=D0(c?.editCount),L=D0(c?.runCount),J=D0(c?.stepCount??c?.llmStepCount),N=U===null?"--":String(U),q=J===null?"--":String(J),W=Array.isArray($.editedFiles)?$.editedFiles:[],z=Array.isArray($.commands)?$.commands:[],H=bc(f,l)?` · ${String(f?.label||"recovered thread execution")}`:l?` #${l}`:"",E=QE(u,f,l,$),D=`最近更新: ${fQ(E)}`,h=NE(u,f,l);return K("details",{className:`codex-progressive-card codex-execution-summary ${h?"running":""}`,"data-testid":y,"data-attempt-index":b3(l),"data-running":h?"true":"false",onToggle:(B)=>{if(B.currentTarget?.open&&!j)n?.(l)}},K("summary",null,K("div",{className:"codex-progressive-card-head"},K("span",{className:"codex-output-channel"},"Summary"),K("strong",null,`执行过程摘要${H}`),h?K("span",{className:"codex-summary-running-pill","data-testid":`${y}-running`},"执行中"):null,K("code",{title:E?`最近更新: ${Nu(E)}`:D},`${Rr($.durationMs??$.totalElapsedMs)} / ${N} tools / ${D}`)),K("div",{className:"codex-execution-digest"},K("span",{title:"来自 OA Event Flow 统计中心"},`read ${Q===null?"--":Q}`),K("span",{title:"来自 OA Event Flow 统计中心"},`edit ${w===null?"--":w}`),K("span",{title:"来自 OA Event Flow 统计中心"},`run ${L===null?"--":L}`),K("span",{title:"来自 OA Event Flow 统计中心"},`STEP ${q}`),F!==null&&F>0?K("span",{className:"codex-execution-error-pill","data-testid":`${y}-error-count`},`Error ${F}`):null)),K("div",{className:"codex-execution-digest expanded"},K("span",null,`修改文件:${dU(W,6)}`),K("span",null,`执行命令:${dU(z,4)}`)),_.length===0?K("div",{className:"codex-output-empty"},r?"正在按需拉取步骤 summary...":"展开后将只请求执行步骤 summary,不拉取单步骤全量。"):K("div",{className:"codex-trace-step-list"},_.map((B)=>{let X=String(B?.seq??""),m=A[X],O=Array.isArray(B?.summaryLines)?B.summaryLines.slice(0,4):[];return K("details",{key:X||`${B?.title}-${B?.at}`,className:`codex-trace-step ${String(B?.kind||"message")} ${GE(B)?"error":""}`,"data-testid":`codex-trace-step-${X||"unknown"}`,onToggle:(p)=>{if(p.currentTarget?.open&&!m)i?.(B?.seq)}},K("summary",null,K("span",{className:"codex-output-channel"},dE(B?.kind)),K("strong",null,String(B?.title||"Trace step")),B?.status?K("code",null,String(B.status)):null,K("time",null,Nu(B?.at))),K("div",{className:"codex-trace-step-summary"},O.length>0?O.map((p,Y)=>K("pre",{key:`${X}-${Y}`},String(p||""))):K("span",null,"无 summary")),m?.line?K(Y3,{items:[m.line],autoScroll:!1,loading:!1,hasDetail:!0,emptyText:"无步骤详情",testId:`codex-trace-step-detail-${X||"unknown"}`,className:"codex-transcript codex-step-detail-transcript",collapseTools:!1}):K("div",{className:"codex-output-empty"},r?"正在按需拉取这个步骤的全量数据...":"展开后将只请求这个单步骤的全量数据。"))})))}function dE(u){let f=String(u||"");if(f==="ran")return"Ran";if(f==="explored")return"Explored";if(f==="edited")return"Edited";if(f==="error")return"Error";if(f==="system")return"System";return"Message"}function wQ({task:u,attempt:f,attemptIndex:l,testId:r="codex-final-response"}){let n=qE(u,f);if(n.length===0)return null;let i=Number(f?.finalResponseChars||n.length),y=l?` #${l}`:"";return K("section",{className:"codex-progressive-card codex-final-response","data-testid":r,"data-attempt-index":b3(l)},K("div",{className:"codex-progressive-card-head"},K("span",{className:"codex-output-channel"},"Final"),K("strong",null,`最终 response${y}`),K("code",null,`${Number.isFinite(i)?i:n.length} chars`)),K(cU,{markdown:n,className:"codex-transcript-body codex-markdown",testId:`${r}-markdown`}))}function LQ({task:u,attempt:f,attemptIndex:l,testId:r="codex-progressive-judge"}){let n=AQ(u,f);if(!n?.decision)return null;let i=l?` #${l}`:"";return K("section",{className:"codex-progressive-card codex-progressive-judge","data-testid":r,"data-attempt-index":b3(l)},K("div",{className:"codex-progressive-card-head"},K("span",{className:"codex-output-channel"},"Judge"),K("strong",null,`完成判定${i}`),K("code",null,`${n.decision} ${Math.round(Number(n.confidence||0)*100)}%`)),K("div",{className:"codex-judge-card","data-testid":`${r}-card`},K(K1,{status:n.decision},n.decision),K("strong",null,`${Math.round(Number(n.confidence||0)*100)}% confidence`),K("p",{"data-testid":`${r}-reason`},n.reason||"--"),n.continuePrompt?K("pre",{"data-testid":`${r}-continue-prompt`},String(n.continuePrompt||"")):null))}function eE({task:u,attempt:f,attemptIndex:l,loading:r,onLoadPromptPart:n,testId:i="codex-judge-feedback-prompt"}){let y=WE(u,f,l);if(y===null)return null;let _=jQ(l),c=x3(u)[_],A=String(c?.text||"").trimEnd(),j=String(y.preview||y.text||"").trimEnd(),F=A||String(y.text||"").trimEnd(),U=Number(c?.chars||y.chars||F.length||j.length),Q=Number(c?.lines||y.lines||L1(F||j)),w=c?.forAttempt||y.forAttempt||Number(l||0)+1;return K("details",{className:"codex-progressive-card codex-judge-feedback-prompt","data-testid":i,"data-attempt-index":b3(l),onToggle:(L)=>{if(L.currentTarget?.open&&!A)n?.("feedback",l)}},K("summary",null,K("div",{className:"codex-progressive-card-head"},K("span",{className:"codex-output-channel"},"Prompt"),K("strong",null,`judge feedback prompt #${l} -> #${w}`),K("code",null,`${Q||"--"} lines / ${Number.isFinite(U)?U:j.length} chars`)),K("p",{className:"codex-feedback-preview","data-testid":`${i}-preview`},j||"展开后按需拉取 judge feedback prompt。")),K("pre",{className:"codex-prompt-full codex-feedback-full","data-testid":`${i}-text`},F||(r?"正在按需拉取 judge feedback prompt...":"展开后将只请求这一次 judge feedback prompt。")))}function uZ({task:u,attempt:f,position:l,loading:r,onLoadPromptPart:n,onLoadSteps:i,onLoadStep:y}){let _=HE(f,l),$=l===0,c=bc(f,_),A=c?String(f?.label||"Recovered thread execution"):`Attempt ${_}`;return K("section",{className:"codex-attempt-cycle","data-testid":`codex-attempt-cycle-${_}`},K("div",{className:"codex-attempt-cycle-head"},K("span",{className:"codex-output-channel"},A),K("strong",null,String(f?.mode||(_<=1?"initial":"retry"))),f?.terminalStatus?K(K1,{status:f.terminalStatus},f.terminalStatus):null,K("code",null,`${Nu(f?.startedAt)} -> ${Nu(f?.finishedAt)}`)),K(WQ,{task:u,attempt:f,attemptIndex:_,loading:r,onLoadSteps:i,onLoadStep:y,testId:$?"codex-execution-summary":`codex-execution-summary-attempt-${_}`}),c?null:K(wQ,{task:u,attempt:f,attemptIndex:_,testId:$?"codex-final-response":`codex-final-response-attempt-${_}`}),c?null:K(LQ,{task:u,attempt:f,attemptIndex:_,testId:$?"codex-progressive-judge":`codex-progressive-judge-attempt-${_}`}),c?null:K(eE,{task:u,attempt:f,attemptIndex:_,loading:r,onLoadPromptPart:n,testId:$?"codex-judge-feedback-prompt":`codex-judge-feedback-prompt-attempt-${_}`}))}function fZ({task:u,loading:f,onLoadPromptPart:l,onLoadSteps:r,onLoadStep:n}){if(!u)return K(G1,{title:"未选择任务",text:"从左侧队列选择任务,或提交新 Codex 任务。"});let i=JE(u);return K("div",{className:"codex-transcript codex-progressive-trace","data-testid":"codex-output"},f&&!_r(u)?K("div",{className:"codex-output-empty"},"正在加载 Trace Summary..."):null,K(oE,{task:u,loading:f,onLoadPromptPart:l}),i.length>0?i.map((y,_)=>K(uZ,{key:`${y?.index||_+1}-${y?.startedAt||_}`,task:u,attempt:y,position:_,loading:f,onLoadPromptPart:l,onLoadSteps:r,onLoadStep:n})):[K(WQ,{key:"execution",task:u,loading:f,onLoadSteps:r,onLoadStep:n}),K(wQ,{key:"final",task:u}),K(LQ,{key:"judge",task:u})])}function lZ({task:u}){let f=iE(u);if(!u||f.length===0)return K(G1,{title:"暂无原始消息",text:"原始 Codex app-server 消息会保留在任务 JSON 中。"});return K("details",{className:"codex-raw-output"},K("summary",null,`原始 messages (${f.length})`),K("div",null,f.map((l)=>K("article",{key:`${l.seq}-${l.channel}`,className:`codex-output-line ${l.channel||"system"}`},K("div",{className:"codex-output-meta"},K("span",{className:"codex-output-channel"},XE(String(l.channel||"system"))),K("span",null,Nu(l.at)),l.method?K("code",null,l.method):null),K("pre",null,String(l.text||""))))))}function rZ({task:u}){let f=yE(u).slice().reverse();if(f.length===0)return K(G1,{title:"尚无 attempt",text:"任务开始运行后,这里会记录 Codex 终态、传输中断和 stderr tail。"});return K("div",{className:"table-wrap codex-attempt-table"},K("table",null,K("thead",null,K("tr",null,K("th",null,"#"),K("th",null,"模式"),K("th",null,"终态"),K("th",null,"传输"),K("th",null,"退出"),K("th",null,"完成时间"))),K("tbody",null,f.map((l)=>K("tr",{key:`${l.index}-${l.startedAt}`},K("td",null,l.index),K("td",null,l.mode),K("td",null,K(K1,{status:l.terminalStatus||"unknown"},l.terminalStatus||"unknown")),K("td",null,l.transportClosedBeforeTerminal?K(K1,{status:"failed"},"closed-before-terminal"):K(K1,{status:"succeeded"},"normal")),K("td",null,`code=${l.appServerExitCode??"--"} signal=${l.appServerSignal??"--"}`),K("td",null,Nu(l.finishedAt)))))))}function KQ({microservices:u,onRaw:f,apiBaseUrl:l="/api",initialTasksData:r=null,standalone:n=!1}){let i=u.find((V)=>V.id==="code-queue")||null,y=hE(r),_=String(y?.id||""),$=new Map;if(y!==null&&_.length>0)$.set(_,{task:y,maxSeq:M3(Array.isArray(y.transcript)?y.transcript:[]),complete:Boolean(y._transcriptComplete),completeUpdatedAt:y._transcriptComplete?String(y.updatedAt||""):""});let c=typeof performance>"u"?0:performance.now(),A=Df(_),j=Df(0),F=Df(0),U=Df(0),Q=Df(!1),w=Df(!1),L=Df(!1),J=Df(null),N=Df(new Map),q=Df(new Map),W=Df(new Map),z=Df(new Map),Z=Df(new Set),H=Df(null),E=Df(null),D=Df(!1),h=Df(!1),B=Df(Boolean(r)),X=Df(new Map),m=Df(new Set),O=Df($),p=Df(r),[Y,v]=Iu(null),[T,S]=Iu(r),[k,I]=Iu(_),[b,o]=Iu(y),[s,x]=Iu(!1),[fu,$u]=Iu(""),[tu,ju]=Iu(null),[Gu,qu]=Iu(!1),[P,e]=Iu(!1),[uu,Wu]=Iu(""),[g,Qu]=Iu(""),[Tu,Zu]=Iu("default"),[Xu,ff]=Iu(Tn),[_f,Jf]=Iu(!1),[mf,Yl]=Iu(""),[Al,jl]=Iu("main-server"),[Hr,al]=Iu("gpt-5.5"),[Au,vu]=Iu("/root/unidesk"),[Kl,Su]=Iu(99),[pf,Gl]=Iu(1),[zl,Pf]=Iu(!1),[rf,Ir]=Iu(!1),[Fr,x1]=Iu(""),[gn,b0]=Iu(""),[lu,Ou]=Iu(""),[mu,ou]=Iu(!0),[Af,vf]=Iu(()=>typeof window>"u"?!0:window.matchMedia(kT).matches),[pu,ll]=Iu(!1),[J0,Gf]=Iu(""),[d9,kf]=Iu(""),{addNotification:ol}=Bl(),[Wy,C$]=Iu(""),[wy,R$]=Iu(""),[h1,Ly]=Iu(!1),[Tl,gr]=Iu(r?{phase:"complete",taskId:_,queueMs:0,detailMs:0,totalMs:c,chunks:y?1:0,transcriptRows:Array.isArray(y?.transcript)?y.transcript.length:0,partial:Boolean(r?.selected?.hasMore||X_(y)),completedAt:new Date}:null),[x$,h$]=Iu(r?new Date:null),[Ky,b$]=Iu(!1),sr=s1(tl(T)),Gy=sr.filter(V0),If=T?.queue||Y?.body?.queue||Y?.queue||{},V8=pE(T,If),v$=Kn(T),Br=RU(If,Tu),sn=String(Tu||"default").trim()||"default",an=Br.filter((V)=>String(V?.id||"")!==sn),b1=Ri(Br,Xu),v0=Number((xl(Xu)?If?.total:b1?.total)??v$.total??sr.length),zy=Y_(If),e9=xl(Xu)?zy:[String(Ri(Br,Xu)?.activeTaskId||"")].filter(Boolean),v1=xU(If,Br,Xu,sr),u7=xl(Xu)?Oc(If):Oc(b1||{}),k$=Oc(If),D8=SE(k$),X8=Math.max(mE(k$),zy.length),Y8=Cf((xl(Xu)?If?.unreadTerminal:b1?.unreadTerminal)??Gy.length),U0=T?Gy.length:Y8,k1=xl(Xu)?"All queues":Sc(b1||{id:Xu,name:Xu}),k0=Yc(fu),Sl=k0.length>0,I$=Sl?s1(tl(tu)):[],on=Kn(tu),Jr=Sl?I$:sr,g$=Jr.filter(V0),s$=Jr.filter((V)=>!Cr(V)).sort(hT),qL=Jr.filter((V)=>Cr(V)&&!V0(V)),f7=Sl?on:v$,I1=Sl?Number(on.total??I$.length):v0,l7=f7.hasMore===!0&&String(f7.nextBeforeId||"").length>0,S8=Sl?P:Ky,NL=i?CT(i):{},WL=i?RT(i):{},m8=PU(()=>_E(uu),[uu]),Q0=PU(()=>{let V=bU(pf);return m8.flatMap((C)=>Array.from({length:V},()=>$E(C,g)))},[m8,pf,g]),Ty=Q0.length,r7=Ty>1&&!zl,wL=rf||pu||Ty===0||r7,n7=bE(If,Hr),i7=vE(If,Al),LL=R3(If,Al),p8=b?.id&&b?.activeTurnId&&String(b?.status)==="running",KL=b?.id&&!["succeeded","failed","canceled"].includes(String(b?.status||"")),GL=b?.id&&["succeeded","failed","canceled"].includes(String(b?.status||"")),g1=b?.id&&QQ(b);function Ur(V){let C=typeof V==="function"?V(p.current):V;return p.current=C,S(C),C}function zL(V,C,d=!0){let iu=Array.from(new Set(V.map((Ju)=>String(Ju||"")).filter(Boolean)));for(let Ju of iu)if(X.current.set(Ju,C),d)m.current.add(Ju);return iu}function y7(V){for(let C of V.map((d)=>String(d||"")).filter(Boolean))X.current.delete(C),m.current.delete(C)}function a$(V){let C=String(V?.id||""),d=C?X.current.get(C):void 0;if(!d)return V;if(String(V?.status||"").length>0&&!Cr(V))return X.current.delete(C),m.current.delete(C),V;return{...V,readAt:V?.readAt||d,terminalUnread:!1}}function P8(V){let C=String(V?.id||"");return C.length>0&&m.current.has(C)&&Cr(V)}function s1(V,C=!0){let d=[];for(let iu of Array.isArray(V)?V:[]){let Ju=a$(iu);if(C&&P8(Ju))continue;d.push(Ju)}return d}function TL(V,C=!0){if(!V||!Array.isArray(V?.tasks))return V;let d=s1(tl(V),C),iu=Kn(V);return{...V,tasks:d,pagination:V.pagination?{...iu,returned:d.length}:V.pagination}}function EL(V){let C=String(V||If?.mainProviderId||"main-server").trim()||"main-server";jl(C),vu(R3(If,C))}function o$(V,C,d=null,iu=null){let Ju=new Set(zL(V,C));if(Ju.size===0&&iu===null&&d===null)return;Ur((Ku)=>{if(!Ku)return Ku;let wu=tl(Ku).flatMap((Ru)=>{let hu=String(Ru?.id||"");if(!Ju.has(hu)){let jf=a$(Ru);return P8(jf)?[]:[jf]}let Pu=iu&&String(iu?.id||"")===hu?iu:{},xu={...Ru,...Pu,readAt:C,terminalUnread:!1};return P8(xu)?[]:[xu]});return{...Ku,queue:d||Ku.queue,tasks:Ju.size>0?Pi([wu],v1):wu}});for(let Ku of Ju){let wu=O.current.get(Ku);if(wu?.task){let Ru=iu&&String(iu?.id||"")===Ku?iu:{},hu={...wu.task,...Ru,readAt:C,terminalUnread:!1};if(O.current.set(Ku,{...wu,task:hu}),A.current===Ku)o(hu)}}}Gn(()=>{Pf(!1)},[uu,pf,g]),Gn(()=>{let V=Yc(fu);F.current+=1;let C=F.current;if(!i||V.length===0){ju(null),qu(!1),e(!1),L.current=!1;return}qu(!0),ju(null);let d=window.setTimeout(()=>{(async()=>{try{let iu=await oT(l,Xu,V);if(C!==F.current)return;ju(TL(iu))}catch(iu){if(C===F.current)ju(null),Gf($l(iu,"搜索 Codex tasks 失败"))}finally{if(C===F.current)qu(!1)}})()},240);return()=>window.clearTimeout(d)},[i?.id,l,Xu,fu]),Gn(()=>{b0(b?C3(b):""),Ou(Array.isArray(b?.referenceTaskIds)?b.referenceTaskIds.join(" "):"")},[k]);function q0(V,C,d){let iu=O.current.get(V)||{},Ju=iu.task||{},Ku=Array.isArray(Ju.transcript)?Ju.transcript:[],wu=aU(Ju,C),Ru=Object.prototype.hasOwnProperty.call(C,"transcript")?Vc(Ku,Array.isArray(C.transcript)?C.transcript:[]):Ku,hu={...Ju,...wu,transcript:Ru,output:Array.isArray(wu.output)?Cc(Ju,wu,"output"):Array.isArray(Ju.output)?Ju.output:[],events:Array.isArray(wu.events)?Cc(Ju,wu,"events"):Array.isArray(Ju.events)?Ju.events:[]},Pu=a$(hu),xu=String(Pu?.updatedAt||""),jf=Boolean(C._transcriptComplete)&&Cr(Pu),rl=Boolean(iu.complete)&&Cr(Pu)&&String(iu.completeUpdatedAt||"")===xu,du=jf||rl,ef={...iu,task:Pu,maxSeq:M3(Ru),complete:du,completeUpdatedAt:du?xu:""};if(O.current.set(V,ef),d===U.current&&A.current===V)o(Pu);return ef}async function a1(V,C=!1,d,iu){if(!i||!V)return;let Ku=O.current.get(V)?.task;if(!C&&p3(Ku))return;let wu=V,Ru=N.current.get(wu);if(Ru){if(C||!p3(Ku))Ru.refreshAfter=!0;return Ru.promise}let hu=U.current,Pu=performance.now();if(A.current===V)x(!0);let xu={promise:Promise.resolve(),refreshAfter:!1},jf=(async()=>{try{let rl=await eT(l,V);if(hu!==U.current||A.current!==V)return;let du=rl?.summary||{},ef=String(du.updatedAt||"");q0(V,{id:V,status:du.status,updatedAt:ef,startedAt:du.startedAt,finishedAt:du.finishedAt,currentAttempt:du.currentAttempt,maxAttempts:du.maxAttempts,finalResponse:du.finalResponse,lastJudge:du.lastJudge,lastError:du.lastError,attempts:Array.isArray(du.attempts)?du.attempts:[],stepCount:du.stepCount,llmStepCount:du.llmStepCount,traceStats:du.traceStats,statsSource:du.statsSource,timing:du.timing,_traceSummary:du,_traceSummaryLoaded:!0,_traceSummaryUpdatedAt:ef,_detailLoaded:!0},hu),gr({phase:"complete",taskId:V,queueMs:iu??0,detailMs:performance.now()-Pu,totalMs:d===void 0?performance.now()-Pu:performance.now()-d,chunks:1,transcriptRows:Number(du?.execution?.traceLineCount||du?.execution?.stepCount||0),partial:!1,completedAt:new Date})}finally{let rl=Boolean(xu.refreshAfter&&A.current===V&&!p3(O.current.get(V)?.task));if(N.current.delete(wu),hu===U.current&&A.current===V)x(!1);if(rl)window.setTimeout(()=>{a1(V,!0).catch((du)=>Gf($l(du,"自动刷新 Trace Summary 失败")))},0)}})();xu.promise=jf,N.current.set(wu,xu),await jf}async function ZL(V,C=null){let d=A.current;if(!i||!d||!V)return;let iu=O.current.get(d)?.task,Ju=x3(iu),Ku=V==="feedback"||V==="judge-feedback"?jQ(C):V;if(Ju[Ku]?.text)return;let wu=`${d}:${Ku}`,Ru=q.current.get(wu);if(Ru)return Ru;let hu=U.current;if(A.current===d)x(!0);let Pu=(async()=>{try{let xu=await uE(l,d,V,C);if(hu!==U.current||A.current!==d)return;let jf=O.current.get(d)?.task,rl=x3(jf);q0(d,{...V==="full"?{prompt:String(xu?.text||""),promptChars:Number(xu?.chars||0)}:{},_promptDetails:{...rl,[Ku]:xu}},hu)}finally{if(q.current.delete(wu),hu===U.current&&A.current===d)x(!1)}})();q.current.set(wu,Pu),await Pu}async function M8(V=null,C={}){let d=A.current;if(!i||!d)return;let iu=O.current.get(d)?.task,Ju=V===null||V===void 0||String(V).length===0?"":String(V),Ku=pc(iu,Ju||null),wu=Boolean(C.force),Ru=Boolean(C.incremental);if(Ku&&!wu)return;let hu=FQ(iu,Ju||null),Pu=Ru&&hu.length>0?LE(hu):0,xu=`${d}:${Ju||"all"}:${Pu}`,jf=W.current.get(xu);if(jf)return jf;let rl=U.current;if(A.current===d)x(!0);let du=(async()=>{try{let ef=await fE(l,d,Pu,500,Ju||null);if(rl!==U.current||A.current!==d)return;let I0=Array.isArray(ef?.steps)?ef.steps:[],g0=Pu>0?KE(hu,I0):I0;if(Ju){let s0=O.current.get(d)?.task,o1=Hf(s0?._traceStepsByAttempt)||{},nl=Hf(s0?._traceStepsLoadedByAttempt)||{},N0=Hf(s0?._traceStepsNextAfterSeqByAttempt)||{};q0(d,{_traceStepsByAttempt:{...o1,[Ju]:g0},_traceStepsLoadedByAttempt:{...nl,[Ju]:!0},_traceStepsNextAfterSeqByAttempt:{...N0,[Ju]:ef?.nextAfterSeq}},rl)}else q0(d,{_traceSteps:g0,_traceStepsLoaded:!0,_traceStepsHasMore:Boolean(ef?.hasMore),_traceStepsNextAfterSeq:ef?.nextAfterSeq},rl)}finally{if(W.current.delete(xu),rl===U.current&&A.current===d)x(!1)}})();W.current.set(xu,du),await du}async function OL(V){let C=A.current,d=String(V??"");if(!i||!C||d.length===0)return;let iu=O.current.get(C)?.task;if(Pc(iu)[d]?.line)return;let Ku=`${C}:${d}`,wu=z.current.get(Ku);if(wu)return wu;let Ru=U.current;if(A.current===C)x(!0);let hu=(async()=>{try{let Pu=await lE(l,C,V);if(Ru!==U.current||A.current!==C)return;let xu=O.current.get(C)?.task,jf=Pc(xu);q0(C,{_traceStepDetails:{...jf,[d]:Pu}},Ru)}finally{if(z.current.delete(Ku),Ru===U.current&&A.current===C)x(!1)}})();z.current.set(Ku,hu),await hu}function HL(V){if(A.current!==V)return;let C=O.current.get(V)?.task;if(!C)return;if(pc(C,null))M8(null,{force:!0,incremental:!0}).catch((iu)=>Gf($l(iu,"增量刷新 Trace Steps 失败")));let d=Hf(C?._traceStepsLoadedByAttempt)||{};for(let iu of Object.keys(d).filter((Ju)=>d[Ju]))M8(iu,{force:!0,incremental:!0}).catch((Ju)=>Gf($l(Ju,"增量刷新 Attempt Trace Steps 失败")))}async function Vm(V,C,d){if(!i||!V)return;let iu=performance.now(),Ju=U.current,Ku=O.current.get(V);if(Ku?.task){if(o(Ku.task),x(X_(Ku.task)||!Ku.complete),!X_(Ku.task)&&Ku.complete&&Cr(Ku.task)&&String(Ku.completeUpdatedAt||"")===String(Ku.task?.updatedAt||"")){gr({phase:"complete",taskId:V,queueMs:d??0,detailMs:0,totalMs:C===void 0?0:performance.now()-C,chunks:0,transcriptRows:Array.isArray(Ku.task.transcript)?Ku.task.transcript.length:0,completedAt:new Date});return}}else x(!0);let wu=J.current;if(wu?.taskId===V&&wu.token===Ju)return wu.promise;let Ru=(async()=>{try{let hu=await Rf(xf(l,`/api/tasks/${encodeURIComponent(V)}?meta=1`));if(Ju!==U.current||A.current!==V)return;let Pu=O.current.get(V),xu=Array.isArray(Pu?.task?.transcript)?Pu.task.transcript:[],jf=hu?.task||{},rl=Boolean(Pu?.complete)&&String(Pu?.completeUpdatedAt||"")===String(jf?.updatedAt||"");q0(V,{...jf,summaryOnly:!1,_metaLoaded:!0,transcript:xu,_detailLoaded:xu.length>0,_transcriptComplete:rl},Ju);let du=X_(Pu?.task)||Boolean(Pu?.task?._transcriptPreview),ef=du?0:xu.length>0?oU(xu):0,I0=!du&&Pu?.complete&&Cr(jf)&&String(Pu?.completeUpdatedAt||"")===String(jf?.updatedAt||"")?M3(xu):ef,g0=!0,s0=0,o1=xu.length;while(g0){let nl=await Rf(xf(l,`/api/tasks/${encodeURIComponent(V)}/transcript?afterSeq=${encodeURIComponent(String(I0))}&limit=${pT}&fullText=1`));if(Ju!==U.current||A.current!==V)return;let N0=O.current.get(V),d$=Array.isArray(N0?.task?.transcript)?N0.task.transcript:[],gf=Vc(d$,Array.isArray(nl?.transcript)?nl.transcript:[]);s0+=1,o1=gf.length;let e$=Boolean(!nl?.hasMore);if(q0(V,{status:nl?.status||jf.status,updatedAt:nl?.updatedAt||jf.updatedAt,transcript:gf,_detailLoaded:e$||gf.length>0,_transcriptComplete:e$,_transcriptPreview:du&&!e$},Ju),g0=Boolean(nl?.hasMore),I0=Number(nl?.nextAfterSeq??M3(gf)),!g0)break;await new Promise((d1)=>window.setTimeout(d1,0))}gr({phase:"complete",taskId:V,queueMs:d??0,detailMs:performance.now()-iu,totalMs:C===void 0?performance.now()-iu:performance.now()-C,chunks:s0,transcriptRows:o1,completedAt:new Date})}finally{if(J.current?.taskId===V&&J.current?.token===Ju)J.current=null;if(Ju===U.current&&A.current===V)x(!1)}})();J.current={taskId:V,token:Ju,promise:Ru},await Ru}async function ml(V=A.current,C=!0,d=Xu){if(!i)return;if(!C&&h.current)return;let iu=performance.now();if(C)h.current=!0;if(C)gr({phase:"loading",taskId:String(V||A.current||""),startedAt:new Date});let Ju=j.current+1;j.current=Ju;let Ku=String(V||A.current||""),wu=Ku?O.current.get(Ku):null,Ru=Array.isArray(wu?.task?.transcript)?wu.task.transcript:[],hu=oU(Ru),Pu=null;if(Pu=await dT(l,Ku,hu,d),Ju!==j.current){if(C)h.current=!1;return}let xu=performance.now()-iu;v(Y||{});let jf=Pu?.queue||{},rl=String(jf?.activeTaskId||Y_(jf)[0]||""),du=Pu;Ur((el)=>{let Ey=tl(Pu),dn=tl(el),Zy=dn.length>0?Pi([dn,Ey],rl):Pi([Ey],rl),ut=s1(Zy),rK=Kn(Pu),ft=Kn(el),nK=dn.length>Ey.length&&(ft.hasMore===!1||String(ft.nextBeforeId||"").length>0),iK={...rK,...nK?{hasMore:ft.hasMore,nextBeforeId:ft.nextBeforeId}:{},returned:ut.length};return du={...Pu,tasks:ut,pagination:iK},du});let ef=tl(du),I0=RU(jf,Tu),g0=xU(jf,I0,d,ef),s0=aT(I0,d,ef),o1=Ku||A.current,nl=du?.selected||null,N0=nl?.task||null,d$=Array.isArray(nl?.transcript)?nl.transcript:null,gf=o1||g0||s0||ef[0]?.id||"";if(A.current!==gf)U.current+=1;A.current=gf,I(gf);let d1=ef.find((el)=>el.id===gf);if(d1){let el=O.current.get(gf);if(el?.task)O.current.set(gf,{...el,task:{...d1,...el.task,status:d1.status,updatedAt:d1.updatedAt}})}if(N0?.id===gf&&d$!==null){let el=O.current.get(gf),Ey=Array.isArray(el?.task?.transcript)?el.task.transcript:[],dn=Vc(Ey,d$),Zy=Boolean(nl?.preview);if(q0(gf,{...N0,_summaryLoaded:!0,transcript:dn,_detailLoaded:!nl?.hasMore||dn.length>0,_transcriptComplete:!Zy&&!nl?.hasMore&&Cr(N0),_transcriptPreview:Zy},U.current),x(!1),C)gr({phase:"complete",taskId:gf,queueMs:xu,detailMs:Math.max(0,performance.now()-iu-xu),totalMs:performance.now()-iu,chunks:1,transcriptRows:dn.length,partial:Boolean(Zy||nl?.hasMore||X_(N0)),completedAt:new Date});if(h$(new Date),C)h.current=!1;a1(gf,!1,C?iu:void 0,C?xu:void 0).catch((ut)=>Gf($l(ut,"加载 Codex Trace Summary 失败")));return}if(C)gr({phase:"session",taskId:gf,queueMs:xu,totalMs:xu,startedAt:new Date(Date.now()-xu)});if(gf)a1(gf,!0,C?iu:void 0,C?xu:void 0).catch((el)=>Gf($l(el,"加载 Codex Trace Summary 失败")));else if(U.current+=1,o(null),x(!1),C)gr({phase:"complete",taskId:"",queueMs:xu,detailMs:0,totalMs:performance.now()-iu,chunks:0,transcriptRows:0,completedAt:new Date});if(h$(new Date),C)h.current=!1}async function _7(){if(Sl){if(!i||P||L.current)return;let C=String(on.nextBeforeId||"");if(!C)return;L.current=!0,e(!0),Gf("");try{let d=await hU(l,Xu,C,uQ,k0),iu=tl(d),Ju=d?.queue||If||{},Ku=String(Ju?.activeTaskId||Y_(Ju)[0]||v1||"");ju((wu)=>{let Ru=s1(Pi([tl(wu),iu],Ku)),hu=Kn(d);return{...wu||{},queue:Ju,tasks:Ru,pagination:{...hu,returned:Ru.length}}})}catch(d){Gf($l(d,"加载更多搜索结果失败"))}finally{L.current=!1,e(!1)}return}if(!i||Ky||w.current)return;let V=String(Kn(T).nextBeforeId||"");if(!V)return;w.current=!0,b$(!0),Gf("");try{let C=await hU(l,Xu,V),d=tl(C),iu=C?.queue||If||{},Ju=String(iu?.activeTaskId||Y_(iu)[0]||v1||"");Ur((Ku)=>{let wu=s1(Pi([tl(Ku),d],Ju)),Ru=Kn(C);return{...Ku||{},queue:iu,statistics:C?.statistics||Ku?.statistics,tasks:wu,pagination:{...Ru,returned:wu.length}}})}catch(C){Gf($l(C,"加载更早 Codex tasks 失败"))}finally{w.current=!1,b$(!1)}}function BL(V){let C=V.currentTarget;if(!C||S8||!l7)return;if(C.scrollHeight-C.scrollTop-C.clientHeight<120)_7()}async function dl(V,C){ll(!0),Gf("");try{await V()}catch(d){Gf($l(d,C))}finally{ll(!1)}}async function C8(V){if(!V)return;try{let C=!1;try{if(navigator.clipboard?.writeText)await navigator.clipboard.writeText(V),C=!0}catch{C=!1}if(!C){let iu=document.createElement("textarea");iu.value=V,iu.style.position="fixed",iu.style.opacity="0",document.body.appendChild(iu),iu.select(),C=document.execCommand("copy"),document.body.removeChild(iu)}if(!C)throw Error("browser clipboard rejected the copy request");C$(V);let d=`已复制任务 ID:${V}`;kf(d),ol("success",d),window.setTimeout(()=>C$((iu)=>iu===V?"":iu),1600)}catch(C){Gf(`复制任务 ID 失败:${$l(C)}`)}}function R8(V){if(!V)return;Qu(V);let C=`已引用任务 ID:${V};提交时后端会读取并注入该任务上下文`;kf(C),ol("success",C)}async function x8(V){if(!i||!V)return;let C=new Date().toISOString();j.current+=1,o$([V],C,null,{id:V,readAt:C,terminalUnread:!1}),R$(V);let d=!1;if(await dl(async()=>{let iu=await rE(l,V),Ju=iu?.task||{id:V,readAt:new Date().toISOString(),terminalUnread:!1},Ku=String(Ju?.readAt||new Date().toISOString());o$([V],Ku,iu?.queue||null,Ju),d=!0;let wu=`已将任务 ${V} 标为已读`;kf(wu),ol("success",wu)},"标记 Codex task 已读失败"),!d)y7([V]),ml(A.current,!1).catch((iu)=>Gf($l(iu,"刷新 Codex tasks 失败")));R$((iu)=>iu===V?"":iu)}async function VL(){if(!i||h1)return;Ly(!0);let V=new Date().toISOString(),C=Array.from(new Set([...tl(p.current).filter(V0).map((iu)=>String(iu?.id||"")).filter(Boolean),...Array.from(O.current.entries()).filter(([,iu])=>V0(iu?.task)).map(([iu])=>iu)]));if(j.current+=1,C.length>0)o$(C,V);let d=!1;if(await dl(async()=>{let iu=await nE(l),Ju=String(iu?.readAt||new Date().toISOString()),Ku=tl(p.current).filter(V0).map((xu)=>String(xu?.id||"")).filter(Boolean),wu=Array.from(O.current.entries()).filter(([,xu])=>V0(xu?.task)).map(([xu])=>xu),Ru=Array.from(new Set([...C,...Ku,...wu]));o$(Ru,Ju,iu?.queue||null);let hu=Number(iu?.count||Ru.length);d=!0;let Pu=`已将 ${hu} 个已结束未读任务标为已读`;kf(Pu),ol("success",Pu)},"全部标为已读失败"),!d&&C.length>0)y7(C),ml(A.current,!1).catch((iu)=>Gf($l(iu,"刷新 Codex tasks 失败")));Ly(!1)}function DL(V){let C=V||Tn;if(ff(C),!xl(C))Zu(C);if(Ur(null),!(xl(C)?A.current:""))A.current="",U.current+=1,I(""),o(null),x(!0)}async function XL(){let V=typeof window>"u"?"":window.prompt("输入新的 Codex queue ID(字母/数字/._-,最长 64)","new-lane"),C=String(V||"").trim();if(!C)return;await dl(async()=>{let d=await Rf(xf(l,"/api/queues"),{method:"POST",body:{queueId:C}}),iu=String(d?.queue?.id||C);Zu(iu),ff(iu),Ur(null),A.current="",U.current+=1,I(""),o(null);let Ju=`已创建并切换到 queue:${iu}`;kf(Ju),ol("success",Ju),await ml("",!0,iu)},"创建 Codex queue 失败")}async function YL(){let V=String(Tu||"default").trim()||"default",C=Ri(Br,V)||{id:V,name:V},d=typeof window>"u"?null:window.prompt(`输入 queue 显示名称(ID 不变:${V};留空恢复为 ID)`,nQ(C));if(d===null)return;await dl(async()=>{let iu=await Rf(xf(l,`/api/queues/${encodeURIComponent(V)}`),{method:"PATCH",body:{name:String(d)}}),Ju=iu?.queue||{id:V,name:String(d||V)};if(iu?.summary)Ur((wu)=>wu?{...wu,queue:iu.summary}:wu);let Ku=`已更新 queue 名称:${Sc(Ju)}`;kf(Ku),ol("success",Ku),await ml(A.current,!0,Xu)},"修改 Codex queue 名称失败")}function SL(){if(an.length===0){kf("没有可合并的其他 queue;请先创建或选择另一个 queue。");return}let V=an.some((C)=>String(C?.id||"")===mf)?mf:"";Yl(V),Jf(!0)}async function mL(){let V=sn;if(an.length===0){kf("没有可合并的其他 queue;请先创建或选择另一个 queue。");return}let d=String(mf||""||"").trim();if(!d){kf("请先选择要合并的源 queue。");return}if(d===V){Gf("源 queue 和目标 queue 不能相同。");return}await dl(async()=>{let iu=await Rf(xf(l,`/api/queues/${encodeURIComponent(V)}/merge`),{method:"POST",body:{sourceQueueId:d}});if(iu?.summary)Ur((wu)=>wu?{...wu,queue:iu.summary}:wu);Zu(V),ff(V),Ur(null);let Ju=Number(iu?.mergedTaskCount||0),Ku=`已将 queue=${d} 合并到 ${V},移动 ${Ju} 个任务;源 queue 已自动删除。`;kf(Ku),ol("success",Ku),Jf(!1),Yl(""),await ml(A.current,!0,V)},"合并 Codex queue 失败")}async function pL(V){if(V.preventDefault(),Q.current){kf("任务正在提交中,请等待当前请求完成,已阻止重复提交。");return}if(Q0.length>1&&!zl){Gf(`检测到将创建 ${Q0.length} 个任务;请先勾选“确认批量入队”,避免误传多个任务。`);return}Q.current=!0,Ir(!0),kf("正在提交 Code Queue 任务,请等待后端确认,输入已临时锁定。"),await dl(async()=>{if(Q0.length===0)throw Error("prompt 不能为空");let C=W1(g),d=Tu.trim()||"default",iu=[...Q0],Ju=(xu)=>({prompt:xu,queueId:d,providerId:Al,model:Hr,cwd:Au,maxAttempts:Number(Kl),...C.length>0?{referenceTaskIds:C}:{}}),Ku=iu.length===1?Ju(iu[0]):{tasks:iu.map(Ju)},wu=await Rf(xf(l,iu.length===1?"/api/tasks":"/api/tasks/batch"),{method:"POST",body:Ku}),Ru=wu?.tasks?.[0]?.id||"",hu=Array.isArray(wu?.tasks)?wu.tasks.map((xu)=>String(xu?.id||"")).filter(Boolean):[],Pu=`已创建 ${hu.length||iu.length} 个任务${hu.length>0?`:${hu.join(" / ")}`:""}`;if(kf(Pu),ol("success",Pu),Wu(""),Qu(""),Pf(!1),A.current=Ru,Xu!==d)Ur(null);Zu(d),await ml(Ru,!0,d)},"Codex 任务入队失败"),Q.current=!1,Ir(!1)}async function PL(V){if(V.preventDefault(),!b?.id)return;await dl(async()=>{await Rf(xf(l,`/api/tasks/${encodeURIComponent(b.id)}/steer`),{method:"POST",body:{prompt:Fr}}),x1(""),await ml(b.id)},"追加 prompt 失败")}async function ML(V){V.preventDefault();let C=String(b?.id||"");if(!C||!g1)return;await dl(async()=>{let d=W1(lu),iu=await Rf(xf(l,`/api/tasks/${encodeURIComponent(C)}/edit`),{method:"POST",body:{prompt:gn,referenceTaskIds:d}}),Ju={...iu?.task||b||{},_traceSummary:null,_traceSummaryLoaded:!1,_traceSummaryUpdatedAt:"",_promptDetails:{},_traceSteps:[],_traceStepsLoaded:!1,_traceStepsByAttempt:{},_traceStepsLoadedByAttempt:{},_traceStepsNextAfterSeqByAttempt:{},_traceStepDetails:{}};O.current.set(C,{...O.current.get(C)||{},task:Ju,complete:!1,completeUpdatedAt:""}),A.current=C,o(Ju),I(C),b0(C3(Ju)),Ou(Array.isArray(Ju?.referenceTaskIds)?Ju.referenceTaskIds.join(" "):""),Ur((wu)=>{if(!wu)return wu;let Ru=tl(wu).map((hu)=>String(hu?.id||"")===C?{...hu,...Ju}:hu);return{...wu,queue:iu?.queue||wu.queue,tasks:Pi([Ru],v1)}});let Ku=iu?.changed===!1?`任务 ${C} 的 prompt 未变化`:`已更新 queued 任务 ${C} 的用户 prompt`;kf(Ku),ol("success",Ku),await ml(C,!0,Xu)},"编辑 queued 任务 prompt 失败")}async function CL(){if(!b?.id)return;await dl(async()=>{await Rf(xf(l,`/api/tasks/${encodeURIComponent(b.id)}/interrupt`),{method:"POST",body:{}}),await ml(b.id)},"打断 Codex session 失败")}async function RL(){if(!b?.id)return;await dl(async()=>{await Rf(xf(l,`/api/tasks/${encodeURIComponent(b.id)}/retry`),{method:"POST",body:{}}),await ml(b.id)},"重新入队失败")}async function xL(V){let C=String(b?.id||""),d=String(V||"").trim();if(!C||!d)return;let iu=m_(b);if(d===iu){kf(`任务 ${C} 已在 queue=${d}`);return}await dl(async()=>{let Ku=(await Rf(xf(l,`/api/tasks/${encodeURIComponent(C)}/move`),{method:"POST",body:{queueId:d}}))?.task||{...b,queueId:d};if(O.current.set(C,{...O.current.get(C)||{},task:Ku}),A.current=C,o(Ku),I(C),Zu(d),!xl(Xu))Ur(null),ff(d);let wu=`已将任务 ${C} 从 ${iu} 移动到 ${d}`;kf(wu),ol("success",wu),await ml(C,!0,xl(Xu)?Tn:d)},"移动任务 queue 失败")}async function hL(){let V=A.current;if(!V)return;let C=performance.now();await dl(async()=>{gr({phase:"session",taskId:V,queueMs:0,totalMs:0,partial:!0,startedAt:new Date}),await a1(V,!0,C,0)},"刷新 Trace Summary 失败")}function bL(V){A.current=V,U.current+=1,I(V);let C=O.current.get(V);if(C?.task)o(C.task),x(!1);else{x(!0);let d=sr.find((iu)=>iu.id===V);if(d)o(d);else o(null)}ml(V).catch((d)=>Gf($l(d,"切换 Codex session 失败")))}function h8(V){if(bL(V),IT())vf(!1)}function $7(V,C,d){if(!V||!Array.isArray(V?.tasks)||C.length===0||Object.keys(d).length===0)return V;let iu=!1,Ju=tl(V).map((Ku)=>{if(String(Ku?.id||"")!==C)return Ku;return iu=!0,a$(aU(Ku,d))});return iu?{...V,tasks:Ju}:V}function vL(V,C){Ur((d)=>$7(d,V,C)),ju((d)=>$7(d,V,C))}function kL(V,C,d){let iu=String(V?.type||"");if(iu==="queue-updated")return!0;if(iu==="trace-stats-updated"||iu==="trace-step-created")return!1;if(C.length===0)return!0;if(!d)return!0;if(V?.queueId&&String(V.queueId)!==m_(d))return!0;if(V?.status&&String(V.status)!==String(d?.status||""))return!0;return String(V?.reason||"")!=="output"}function t7(){if(!i||!Zc())return;if(H.current!==null)window.clearTimeout(H.current);H.current=window.setTimeout(()=>{H.current=null,ml(A.current,!1).catch((V)=>Gf($l(V,"Code Queue 事件刷新失败")))},120)}function IL(V,C){if(!i||!Zc()||A.current!==V)return;if(D.current=D.current||C,E.current!==null)return;E.current=window.setTimeout(()=>{E.current=null;let d=D.current;if(D.current=!1,a1(V,!0).catch((iu)=>Gf($l(iu,"事件刷新 Trace Summary 失败"))),d)HL(V)},250)}function gL(V){let C=Hf(V?.payload)||V,d=Hf(C?.stats),iu=String(V?.type||C?.type||""),Ju=String(C?.subjectKind||d?.subjectKind||""),Ku=String(C?.scopeId||d?.scopeId||""),wu=String(C?.taskId||(Ju==="task"?C?.subjectId:"")||d?.taskId||V?.taskId||""),Ru=C?.attemptIndex??d?.attemptIndex,hu=Ru===null||Ru===void 0||Ru===""?null:D0(Ru),Pu=d||Hf(C?.traceStats),xu=Pu?.stepCount??Pu?.llmStepCount,jf=Pu?.outputMaxSeq;return{...C,type:iu,eventId:V?.eventId||C?.eventId,sequence:V?.sequence??C?.sequence,taskId:wu,subjectKind:Ju,scopeId:Ku,attemptIndex:hu,stepCount:xu,outputMaxSeq:jf,updatedAt:Pu?.updatedAt||C?.updatedAt||V?.createdAt,traceStats:Pu,statsSource:Pu?"oa-event-flow":C?.statsSource}}function sL(V){let C=gL(V),d=String(C?.taskId||""),iu=String(C?.scopeId||C?.traceStats?.scopeId||""),Ju=String(C?.subjectKind||C?.traceStats?.subjectKind||"")==="task-attempt"||Number(C?.attemptIndex)>0||/:attempt:\d+$/u.test(iu),Ku=vU(C?.stepCount),wu={},Ru=d.length>0?tl(p.current).find((hu)=>String(hu?.id||"")===d):null;if(d.length>0){if(C?.status)wu.status=String(C.status);if(C?.updatedAt)wu.updatedAt=String(C.updatedAt);if(C?.queueId)wu.queueId=String(C.queueId);if(!Ju&&Number.isFinite(Number(C?.stepCount)))wu.stepCount=Ku,wu.llmStepCount=Ku;if(!Ju&&Number.isFinite(Number(C?.outputMaxSeq)))wu.outputMaxSeq=vU(C.outputMaxSeq);if(!Ju&&Hf(C?.traceStats))wu.traceStats=C.traceStats,wu.statsSource="oa-event-flow";if(Object.keys(wu).length>0&&(O.current.has(d)||A.current===d))q0(d,wu,U.current);if(Object.keys(wu).length>0)vL(d,wu);if(A.current===d&&(C?.type==="trace-step-created"||C?.type==="task-updated"||C?.type==="trace-stats-updated")){let hu=$Q(Ru),Pu=C?.type==="trace-step-created"||C?.type==="trace-stats-updated"&&!Ju&&Number.isFinite(Number(C?.stepCount))&&(hu===null||Ku>hu);IL(d,Pu)}}if(kL(C,d,Ru))t7()}Gn(()=>{if(B.current){B.current=!1;return}dl(()=>ml(A.current),"Code Queue 加载失败")},[i?.id,Xu]),Gn(()=>{if(!i||typeof EventSource>"u")return;let V=new EventSource(xT(l),{withCredentials:!0}),C=(iu)=>{try{sL(JSON.parse(String(iu.data||"{}")))}catch(Ju){Gf($l(Ju,"解析 Code Queue 事件失败"))}},d=()=>{if(Zc())t7()};return V.addEventListener("trace-step-created",C),V.addEventListener("task-updated",C),V.addEventListener("queue-updated",C),V.addEventListener("trace-stats-updated",C),document.addEventListener("visibilitychange",d),()=>{if(V.close(),document.removeEventListener("visibilitychange",d),H.current!==null)window.clearTimeout(H.current),H.current=null;if(E.current!==null)window.clearTimeout(E.current),E.current=null}},[i?.id,l,Xu]),Gn(()=>{if(!i||!b||s)return;let V=String(b.id||"");if(!V)return;let C=String(b.updatedAt||"");if(p3(b))return;let d=`${V}:${C||"unknown"}:${String(b?._traceSummaryUpdatedAt||"")}`;if(Z.current.has(d))return;Z.current.add(d),a1(V,!0).catch((iu)=>Gf($l(iu,"自动加载 Trace Summary 失败")))},[i?.id,b?.id,b?.updatedAt,b?.traceStats?.statsRevision,b?._traceSummaryUpdatedAt,b?._traceSummaryLoaded,s]);let aL=Jr.length===0?K(G1,{title:Sl?Gu?"搜索中":"没有匹配任务":"队列为空",text:Sl?Gu?`正在搜索包含“${k0}”的 task...`:`未找到包含“${k0}”的 task;可换个关键词或切换 queue。`:"提交一个任务后,Codex 会串行执行并保存输出。"}):[g$.length>0?K(Dc,{key:"unread",title:"已结束未读",tasks:g$,selectedId:k,emptyText:"暂无已结束未读任务。",onSelect:h8,onCopy:C8,onReference:R8,onMarkRead:x8,copiedTaskId:Wy,markingReadTaskId:wy}):null,K(Dc,{key:"active",title:"运行 / 排队",tasks:s$,selectedId:k,emptyText:"当前没有运行或排队任务。",onSelect:h8,onCopy:C8,onReference:R8,onMarkRead:x8,copiedTaskId:Wy,markingReadTaskId:wy}),K(Dc,{key:"history",title:"历史 session",tasks:qL,selectedId:k,emptyText:"最近没有完成、失败或取消的 session。",onSelect:h8,onCopy:C8,onReference:R8,onMarkRead:x8,copiedTaskId:Wy,markingReadTaskId:wy}),K("div",{key:"pagination",className:"codex-task-pagination","data-testid":"codex-task-pagination"},K("span",null,Sl?`搜索“${k0}” · 已显示 ${Jr.length} / ${Number.isFinite(I1)?I1:Jr.length}`:`已加载 ${Jr.length} / ${Number.isFinite(I1)?I1:Jr.length}`),l7?K("button",{type:"button",className:"ghost-btn",disabled:S8,onClick:()=>void _7(),"data-testid":"codex-load-more-tasks-button"},S8?"加载中":Sl?"加载更多结果":"加载更早任务"):K("code",null,Sl?"已到结果末尾":"已到队列末尾"))],c7=(V,C=!1)=>K("label",{className:`code-queue-switcher ${C?"compact":""}`},K("span",null,C?"Queue":"查看 queue"),K("select",{value:Xu,onChange:(d)=>DL(String(d.target.value||Tn)),"data-testid":V},K("option",{value:Tn},`All queues · ${Number.isFinite(v0)?v0:sr.length} tasks · ${zy.length} running`),Br.map((d)=>K("option",{key:String(d?.id||""),value:String(d?.id||"")},S_(d))))),oL=K("div",{className:"codex-task-search","data-testid":"codex-task-search"},K("label",{htmlFor:"codex-task-search-input"},"搜索 task"),K("div",{className:"codex-task-search-row"},K("input",{id:"codex-task-search-input",type:"search",value:fu,placeholder:"关键词 / task ID / prompt",autoComplete:"off",onChange:(V)=>$u(String(V.target.value||"")),"data-testid":"codex-task-search-input"}),fu?K("button",{type:"button",className:"ghost-btn",onClick:()=>$u(""),"data-testid":"codex-task-search-clear"},"清除"):null),K("small",{"data-testid":"codex-task-search-summary"},Sl?Gu?"搜索中...":`匹配 ${Jr.length}/${Number.isFinite(I1)?I1:Jr.length}`:"支持 task ID、prompt、状态、provider、模型和最近输出关键词")),dL=K("div",{className:"codex-trace-status","data-testid":"codex-trace-status-summary"},K("span",{className:"codex-trace-status-chip queued"},K("b",null,"排队"),String(D8)),K("span",{className:"codex-trace-status-chip running"},K("b",null,"运行"),String(X8)),K("span",{className:`codex-trace-status-chip unread ${U0>0?"warn":""}`},K("b",null,"结束未读"),String(U0)),K("span",{className:"codex-trace-status-chip service"},K("b",null,"服务"),`${NL.providerStatus||"unknown"} · ${i?.providerId||"main-server"} · ${WL.public?"公网暴露":"仅 UniDesk frontend 代理访问"}`),K("span",{className:"codex-trace-status-chip"},K("b",null,"执行节点"),i7.map((V)=>V.id).join(" / ")),K("span",{className:"codex-trace-status-chip"},K("b",null,"模型"),n7.join(" / ")),K("span",{className:"codex-trace-status-chip"},K("b",null,"加载"),Tl?.phase==="complete"?MT(Tl?.totalMs):String(Tl?.phase||"idle")),K("span",{className:"codex-trace-status-chip"},K("b",null,"刷新"),x$?$f(x$):"--")),eL=K(Mi,{title:b?`Trace ${String(b.id).slice(0,22)}`:"Trace 输出",eyebrow:b?`${b.status} / view=${k1} / task queue=${m_(b)} / provider=${b.providerId||"main-server"} / ${b.model} / agent loop trace`:`Agent loop trace / view=${k1}`,summary:dL,loading:s||Ky||Gu||P||Tl?.phase==="loading",actions:K("div",{className:"panel-actions"},c7("code-queue-filter-select"),K("button",{type:"button",className:"ghost-btn codex-mark-all-read-btn",disabled:U0===0||pu||h1,onClick:()=>void VL(),"data-testid":"codex-mark-all-read-button"},h1?"标记中":`全部标已读${U0>0?` (${U0})`:""}`),b?K("button",{type:"button",className:"ghost-btn",disabled:s||pu,onClick:()=>void hL(),"data-testid":"codex-load-full-trace-button"},s?"加载中":_r(b)?"刷新 Summary":"加载 Summary"):null,K("button",{type:"button",className:"codex-session-title-toggle",onClick:()=>vf((V)=>!V),"data-testid":"code-queue-sidebar-toggle"},Af?"收起队列":"展开队列"),K("label",{className:"inline-check"},K("input",{type:"checkbox",checked:mu,onChange:(V)=>ou(Boolean(V.target.checked))}),"自动滚动"),K("button",{type:"button",className:"ghost-btn",disabled:!KL||pu,onClick:()=>void CL(),"data-testid":"codex-interrupt-button"},"打断"),K("button",{type:"button",className:"ghost-btn",disabled:!GL||pu,onClick:()=>void RL()},"重试"),b?K(rQ,{title:"Codex Task",data:b,onOpen:f,testId:"raw-codex-task"}):null),className:"codex-output-panel"},K("div",{className:`codex-session-shell ${Af?"":"queue-collapsed"}`},Af?K("aside",{className:"codex-session-sidebar","data-testid":"codex-session-sidebar"},K("div",{className:"codex-session-sidebar-head"},K("div",null,K("span",null,xl(Xu)?"All queues":"Queue lane"),K("strong",null,`${k1} · ${sr.length}/${Number.isFinite(v0)?v0:sr.length} sessions · 未读 ${U0}`)),K("button",{type:"button",className:"ghost-btn",onClick:()=>vf(!1)},"收起")),c7("code-queue-filter-sidebar",!0),oL,K("div",{className:"codex-task-list codex-task-list-session",onScroll:BL,"data-testid":"codex-task-list-scroll"},aL)):null,K("div",{className:"codex-session-main"},K("div",{className:"codex-output-stack"},K(fZ,{task:b,loading:s,onLoadPromptPart:ZL,onLoadSteps:M8,onLoadStep:OL}),K(lZ,{task:b})))));if(!i)return K(G1,{title:"Code Queue 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=code-queue"});let A7=Number(Tl?.totalMs),j7=Number(Tl?.queueMs),F7=Number(Tl?.detailMs),J7=Number(Tl?.transcriptRows),uK=Tl?.phase==="complete"?"complete":String(Tl?.phase||"idle"),fK=Ri(Br,sn)||{id:sn,name:sn},lK=_f?K(pU,{title:"合并 queue",titleId:"codex-merge-dialog-title",className:"codex-merge-dialog",backdropClassName:"codex-merge-dialog-backdrop",bodyClassName:"codex-merge-dialog-body",actionsClassName:"codex-merge-dialog-actions",testId:"codex-merge-queue-dialog",closeTestId:"codex-merge-queue-close",disableClose:pu,onClose:()=>Jf(!1),actions:[K("button",{key:"cancel",type:"button",className:"ghost-btn",onClick:()=>Jf(!1),disabled:pu,"data-testid":"codex-merge-queue-cancel"},"取消"),K("button",{key:"confirm",type:"button",className:"primary-btn",onClick:()=>void mL(),disabled:pu||rf||!mf,"data-testid":"codex-merge-queue-confirm"},pu?"合并中...":"确认合并")]},K("p",{className:"codex-merge-dialog-target"},"目标 queue:",K("code",null,S_(fK))),K("label",null,"源 queue",K("select",{value:mf,disabled:pu||rf,onChange:(V)=>Yl(String(V.target.value||"")),"data-testid":"codex-merge-source-queue-select"},K("option",{value:""},"选择要合并进来的源 queue"),an.map((V)=>K("option",{key:String(V?.id||""),value:String(V?.id||"")},S_(V))))),K("p",{className:"codex-merge-dialog-note"},"会把源 queue 的任务归属合并到目标 queue,并自动删除源 queue;目标 queue 会按原 queueEnteredAt/createdAt 时间顺序运行。")):null;return K("div",{className:`code-queue-page ${n?"codex-standalone-page":""}`,"data-testid":"code-queue-page","data-load-state":uK,"data-load-total-ms":Number.isFinite(A7)?String(Math.round(A7*10)/10):"","data-load-queue-ms":Number.isFinite(j7)?String(Math.round(j7*10)/10):"","data-load-detail-ms":Number.isFinite(F7)?String(Math.round(F7*10)/10):"","data-load-transcript-rows":Number.isFinite(J7)?String(J7):"","data-load-task-id":String(Tl?.taskId||k||""),"data-load-partial":Tl?.partial?"true":"false"},K(yf,{error:J0,wide:!0}),lK,K("div",{className:"codex-session-stage codex-session-stage-top"},eL),K("div",{className:"code-queue-layout"},K("div",{className:"codex-left-rail"},K(Mi,{title:"提交任务",eyebrow:rf?"Submitting...":Q0.length>1?`${Q0.length} tasks`:"Single or Batch",className:"codex-compose-panel",loading:rf},K("form",{className:`codex-task-form ${rf?"is-submitting":""}`,onSubmit:pL,"data-testid":"code-queue-task-form","aria-busy":rf?"true":"false"},K("label",null,"Prompt / 多任务用单独一行 --- 分隔",K("textarea",{value:uu,rows:8,disabled:rf,onChange:(V)=>Wu(V.target.value),placeholder:"写入 Codex 任务;多个任务之间用 --- 分隔。"})),K("label",{className:"codex-reference-field"},"引用任务 ID(可选)",K("input",{value:g,disabled:rf,onChange:(V)=>Qu(V.target.value),placeholder:"codex_...;支持空格/逗号分隔多个 ID","data-testid":"codex-reference-task-id"}),W1(g).length>0?K("code",null,`后端将解析并注入:${W1(g).join(" / ")}`):null),K("div",{className:"codex-form-grid"},K("label",{className:"codex-submit-queue-field"},"Queue",K("div",{className:"codex-submit-queue-row"},K("select",{className:"codex-submit-queue-select",value:Tu,disabled:rf,onChange:(V)=>Zu(String(V.target.value||"default")),"data-testid":"code-queue-id-select"},Br.map((V)=>K("option",{key:String(V?.id||""),value:String(V?.id||"")},S_(V)))),K("button",{type:"button",className:"ghost-btn codex-rename-queue-btn",onClick:()=>void YL(),disabled:pu||rf||!Tu,title:"修改当前 queue 的显示名称,ID 不变","data-testid":"codex-rename-queue-button"},"改名"),K("button",{type:"button",className:"ghost-btn codex-merge-queue-btn",onClick:()=>SL(),disabled:pu||rf||an.length===0,title:"打开独立窗口选择源 queue 并合并到当前 queue;合并后自动删除源 queue","data-testid":"codex-merge-queue-button"},"合并 queue"),K("button",{type:"button",className:"ghost-btn codex-create-queue-btn",onClick:()=>void XL(),disabled:pu||rf,"data-testid":"codex-create-queue-button"},"创建 queue"))),K("label",null,"模型",K("select",{value:Hr,disabled:rf,onChange:(V)=>al(V.target.value),"data-testid":"codex-model-select"},n7.map((V)=>K("option",{key:V,value:V},V)))),K("label",null,"执行 Provider",K("select",{value:Al,disabled:rf,onChange:(V)=>EL(String(V.target.value||"main-server")),"data-testid":"codex-provider-select"},i7.map((V)=>K("option",{key:V.id,value:V.id},`${V.label||V.id} · ${V.defaultWorkdir||R3(If,V.id)}`)))),K("label",null,"工作目录",K("input",{value:Au,disabled:rf,onChange:(V)=>vu(V.target.value),placeholder:LL||If?.defaultWorkdir||"/root/unidesk","data-testid":"codex-cwd-input"})),K("label",null,"最大尝试",K("input",{type:"number",min:1,max:99,value:Kl,disabled:rf,onChange:(V)=>Su(Number(V.target.value)),"data-testid":"codex-max-attempts-input"})),K("label",null,"入队份数",K("input",{type:"number",min:1,max:50,value:pf,disabled:rf,onChange:(V)=>Gl(Number(V.target.value)),"data-testid":"codex-repeat-count-input"}))),Ty>1?K("label",{className:`codex-batch-confirm ${zl?"confirmed":""}`,"data-testid":"codex-batch-confirm-row"},K("input",{type:"checkbox",checked:zl,disabled:rf,onChange:(V)=>Pf(Boolean(V.target.checked)),"data-testid":"codex-batch-confirm-checkbox"}),K("span",null,`确认批量入队 ${Ty} 个任务(prompt 分段 ${m8.length} × 入队份数 ${bU(pf)})`)):null,rf?K("div",{className:"codex-submit-wait","data-testid":"codex-submit-wait"},"正在提交到后端,已锁定输入以防重复提交..."):null,K("div",{className:"codex-form-actions"},K("button",{type:"button",className:"ghost-btn",disabled:pu||rf||uu.length===0&&g.length===0,onClick:()=>{Wu(""),Qu(""),Pf(!1);let V="已清空任务输入栏";kf(V),ol("success",V)},"data-testid":"codex-clear-input-button"},"清空输入"),K("button",{type:"submit",className:"primary-btn",disabled:wL,"data-testid":"codex-enqueue-button"},rf?"提交中,请等待...":r7?`请确认批量入队 ${Ty} 个任务`:Q0.length>1?`批量入队 ${Q0.length} 个任务`:"入队并运行"))))),K("div",{className:"codex-main-stage"},K("div",{className:"codex-detail-grid"},K(Mi,{title:"运行控制",eyebrow:g1?"Queued prompt editable":p8?"Active turn steer":"Steer when running",loading:pu},K("div",{className:"codex-run-control-stack"},K(aE,{task:b,queueRows:Br,busy:pu,onMove:xL}),b?.id?K("form",{className:"codex-steer-form codex-edit-prompt-form",onSubmit:ML,"data-testid":"codex-edit-prompt-form"},K("label",null,"编辑 queued 用户 prompt",K("textarea",{value:gn,rows:5,onChange:(V)=>b0(V.target.value),placeholder:"仅 QUEUED 且尚未开始运行的任务可在这里修改原始用户 prompt。",disabled:!g1||pu,"data-testid":"codex-edit-prompt-textarea"})),K("label",{className:"codex-reference-field"},"引用任务 ID(可选,留空会清除引用)",K("input",{value:lu,disabled:!g1||pu,onChange:(V)=>Ou(V.target.value),placeholder:"codex_...;支持空格/逗号分隔多个 ID","data-testid":"codex-edit-reference-task-id"}),W1(lu).length>0?K("code",null,`将保留/注入:${W1(lu).join(" / ")}`):null),K("div",{className:"codex-form-actions"},K("button",{type:"button",className:"ghost-btn",disabled:!b?.id||pu,onClick:()=>{b0(b?C3(b):""),Ou(Array.isArray(b?.referenceTaskIds)?b.referenceTaskIds.join(" "):"")},"data-testid":"codex-edit-prompt-reset"},"恢复当前值"),K("button",{type:"submit",className:"primary-btn",disabled:!g1||pu||gn.trim().length===0,title:g1?"保存后会重写尚未运行任务的用户 prompt":"只有 QUEUED 且尚未开始的任务可编辑 prompt","data-testid":"codex-edit-prompt-submit"},"保存 queued prompt"))):null,K("form",{className:"codex-steer-form",onSubmit:PL},K("label",null,"追加 prompt",K("textarea",{value:Fr,rows:4,onChange:(V)=>x1(V.target.value),placeholder:"给正在运行的 Codex session 推入新的指令或纠偏。",disabled:!p8})),K("button",{type:"submit",className:"primary-btn",disabled:!p8||pu||Fr.trim().length===0,"data-testid":"codex-steer-button"},"推入运行中 session")))),K(Mi,{title:"完成判定",eyebrow:b?.lastJudge?b.lastJudge.source:"judge",loading:s},b?.lastJudge?K("div",{className:"codex-judge-card","data-testid":"codex-task-judge-card"},K(K1,{status:b.lastJudge.decision},b.lastJudge.decision),K("strong",null,`${Math.round(Number(b.lastJudge.confidence||0)*100)}% confidence`),K("p",{"data-testid":"codex-task-judge-reason"},Xc(b.lastJudge.reason||"--",180)),b.lastJudge.continuePrompt?K("code",{"data-testid":"codex-task-judge-continue-prompt"},Xc(b.lastJudge.continuePrompt,160)):null):K(G1,{title:"尚未判定",text:"Codex turn 结束后会由 MiniMax M2.7 或 fallback judge 判定 complete/retry/fail;retry 会在已有 thread 追加继续执行 prompt。"}))),K(sE,{stats:V8,queueName:k1,onRaw:f}),K(Mi,{title:"Attempts",eyebrow:"terminal vs interruption",loading:s},K(rZ,{task:b})))))}var M_=Mu(Qf(),1);var Bu=M_.default.createElement,{useEffect:kc}=M_.default,Ic=M_.default.useState,nZ=M_.default.useRef,sc=` :root { --surfacePrimary: #ffffff; --surfaceSecondary: #f8fafc; @@ -224,64 +224,64 @@ nav .material-icons::before { display: none !important; } } -`;function PA({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return Vu("section",{className:`panel ${y||""}`},Vu("div",{className:"panel-head"},Vu("div",null,f?Vu("p",{className:"panel-eyebrow"},f):null,Vu(Af,{title:u,loading:i})),l?Vu("div",{className:"panel-actions"},l):null),Vu("div",{className:"panel-body"},r))}function BZ({title:u,data:f,onOpen:l,testId:r}){return Vu("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function VZ({title:u,text:f}){return Vu("div",{className:"empty-state"},Vu("strong",null,u),Vu("span",null,f))}function fW(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function lW(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function rW(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function XZ(u){return u.filter((l)=>l?.id==="filebrowser"||String(l?.id||"").startsWith("filebrowser-")).sort((l,r)=>{let y=(i)=>i.providerId==="D518"?0:i.providerId==="D601"?1:i.id==="filebrowser"?2:3;return y(l)-y(r)||String(l.id).localeCompare(String(r.id))})}function YZ(u){if(u?.providerId==="D518")return"D518";return u?.providerId||u?.name||u?.id||"Unknown"}function DZ(u,f,l="/"){let r=l.startsWith("/")?l:`/${l}`;return`${u}/microservices/${encodeURIComponent(f)}/proxy${r}`}function tZ(u,f){return`${u}/microservices/${encodeURIComponent(f)}/health`}async function SZ(u,f=16000){let l=new AbortController,r=setTimeout(()=>l.abort(),f);try{return await Xu(u,{signal:l.signal,failureFields:[!1]})}finally{clearTimeout(r)}}function yW(u){if(u?.providerId==="main-server")return"host / -> /srv";if(u?.providerId==="D601"||u?.providerId==="D518")return"WSL / + /mnt/c -> /srv";return"provider / -> /srv"}function M6(u){return u?.status==="OK"||u?.ok===!0}function PZ({service:u,active:f,health:l,onSelect:r,onRaw:y}){let i=fW(u),_=lW(u),n=rW(u),$=i.container||{},j=M6(l?.body);return Vu("button",{type:"button",className:`filebrowser-target-card ${f?"active":""}`,"data-testid":`filebrowser-target-card-${u.id}`,onClick:r},Vu("span",{className:`status-badge ${j?"ok":i.providerStatus==="online"?"running":"warn"}`},j?"Health OK":i.providerStatus||"unknown"),Vu("strong",null,u.name||u.id),Vu("span",null,yW(u)),Vu("code",null,`${_.nodeBindHost||"--"}:${_.nodePort||"--"}`),Vu("small",null,$.name?`${$.name} / ${$.state||"--"}`:`${n.composeService||"--"}`),Vu("span",{className:"filebrowser-card-raw",onClick:(F)=>{F.stopPropagation(),y(`${u.name} service`,u)}},"JSON"))}function iW(u){try{return u?.contentDocument||u?.contentWindow?.document||null}catch{return null}}function mA(u){let f=iW(u);if(f===null||f.head===null)return!1;let l=f.getElementById("unidesk-filebrowser-compact-style");if(l===null)l=f.createElement("style"),l.id="unidesk-filebrowser-compact-style",f.head.appendChild(l);if(l.textContent!==MA)l.textContent=MA;return!0}function MZ(u,f){let l=URL.createObjectURL(u),r=document.createElement("a");r.href=l,r.download=f,document.body.appendChild(r),r.click(),r.remove(),setTimeout(()=>URL.revokeObjectURL(l),2000)}function mZ(u,f){let l=iW(u);if(l===null||l.documentElement===null)throw Error("无法访问 File Browser iframe 文档");mA(u);let r=Math.max(640,Math.ceil(u.clientWidth||l.documentElement.clientWidth||1280)),y=Math.max(480,Math.ceil(u.clientHeight||l.documentElement.clientHeight||720)),i=l.documentElement.cloneNode(!0);i.querySelectorAll("script, style, link[rel='stylesheet'], link[rel='preload'], link[rel='icon']").forEach((F)=>F.remove()),i.querySelectorAll("img").forEach((F)=>{F.removeAttribute("src"),F.removeAttribute("srcset")});let _=i.querySelector("head");if(_===null)_=l.createElement("head"),i.insertBefore(_,i.firstChild);let n=l.createElement("style");n.textContent=`${MA} -html,body{width:${r}px!important;min-height:${y}px!important;overflow:hidden!important;}`,_.appendChild(n);let $=new XMLSerializer().serializeToString(i),j=`${$}`;MZ(new Blob([j],{type:"image/svg+xml;charset=utf-8"}),f.replace(/\.png$/i,".svg"))}function _W({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=XZ(Array.isArray(u)?u:[]),y=new URLSearchParams(window.location.search).get("target")||"",i=y==="filebrowser-d518"?"filebrowser":y,_=r.some((H)=>H.id===i)?i:r[0]?.id||"",[n,$]=SA(_),[j,F]=SA({loading:!1,refreshedAt:null,health:{},error:""}),[J,U]=SA({exporting:!1,message:"",error:""}),q=OZ(null),W=r.find((H)=>H.id===n)||r[0]||null,G=fW(W),K=lW(W),Q=rW(W),N=W?j.health[W.id]:null,c=W?DZ(l,W.id,"/"):"about:blank";tA(()=>{if(r.length===0)return;if(!n||!r.some((H)=>H.id===n))$(r[0].id)},[r.map((H)=>H.id).join(",")]),tA(()=>{let H=0,B=setInterval(()=>{if(H+=1,mA(q.current)||H>=24)clearInterval(B)},500);return()=>clearInterval(B)},[c]),tA(()=>{if(r.length===0)return;let H=!1;async function B(){F((R)=>({...R,loading:!0,error:""}));let X=await Promise.all(r.map(async(R)=>{try{let Y=await SZ(tZ(l,R.id));return[R.id,{ok:!0,body:Y}]}catch(Y){return[R.id,{ok:!1,error:Bu(Y,"File Browser health failed")}]}}));if(H)return;F({loading:!1,refreshedAt:new Date().toISOString(),health:Object.fromEntries(X),error:""})}B();let T=setInterval(B,30000);return()=>{H=!0,clearInterval(T)}},[r.map((H)=>`${H.id}:${H.runtime?.providerStatus||""}`).join(","),l]);function z(H){$(H);let B=new URL(window.location.href);B.searchParams.set("target",H),window.history.replaceState({},"",`${B.pathname}${B.search}`)}async function w(){if(J.exporting)return;U({exporting:!0,message:"",error:""});try{let H=new Date().toISOString().replace(/[-:.TZ]/g,"").slice(0,14);await mZ(q.current,`unidesk-filebrowser-${W?.id||"target"}-${H}.png`),U({exporting:!1,message:"截图已导出",error:""})}catch(H){U({exporting:!1,message:"",error:Bu(H,"截图导出失败")})}}if(r.length===0)return Vu(VZ,{title:"File Browser 未登记",text:"请在 config.json 的 microservices 中登记 id=filebrowser 或 filebrowser-* 用户服务"});return Vu("div",{className:"filebrowser-page","data-testid":"filebrowser-page"},j.error?Vu(jf,{error:j.error,wide:!0}):null,Vu(PA,{title:"文件管理器",eyebrow:"File Browser / Host Files",loading:j.loading,actions:Vu("div",{className:"panel-actions"},W?Vu("button",{type:"button",className:"ghost-btn",onClick:w,disabled:J.exporting,"data-testid":"filebrowser-export-screenshot"},J.exporting?"导出中...":"导出截图"):null,W?Vu("a",{className:"ghost-btn",href:c,target:"_blank",rel:"noreferrer"},"新窗口打开"):null,W?Vu(BZ,{title:"File Browser 当前目标",data:{service:W,health:N},onOpen:f,testId:"raw-filebrowser-active"}):null)},Vu("div",{className:"filebrowser-hero"},Vu("div",null,Vu("span",{className:`status-badge ${M6(N?.body)?"ok":"warn"}`},M6(N?.body)?"Health OK":"Health Pending"),Vu("h3",null,W?.name||"File Browser"),Vu("p",{className:"muted paragraph"},W?.description||"通过 UniDesk 登录态代理访问,不开放 File Browser 公网端口。"),J.error?Vu("p",{className:"filebrowser-shot-error"},J.error):null,J.message?Vu("p",{className:"filebrowser-shot-ok"},J.message):null),Vu("div",{className:"microservice-ref-card"},Vu("span",null,"Provider"),Vu("strong",null,W?.providerId||"--"),Vu("code",null,G.providerName||W?.providerId||"--")),Vu("div",{className:"microservice-ref-card"},Vu("span",null,"Private Backend"),Vu("strong",null,`${K.nodeBindHost||"--"}:${K.nodePort||"--"}`),Vu("code",null,K.nodeBaseUrl||"--")),Vu("div",{className:"microservice-ref-card"},Vu("span",null,"Image"),Vu("strong",null,Q.dockerfile||"filebrowser/filebrowser:v2.63.3"),Vu("code",null,Q.commitId||"--")),Vu("div",{className:"microservice-ref-card"},Vu("span",null,"Mount"),Vu("strong",null,yW(W)),Vu("code",null,W?.providerId==="main-server"?"/root, /var, /home":"/home, /mnt/c, /mnt/d")))),Vu(PA,{title:"浏览目标",eyebrow:`${r.length} host targets`,loading:j.loading},Vu("div",{className:"filebrowser-target-grid"},r.map((H)=>Vu(PZ,{key:H.id,service:H,active:H.id===W?.id,health:j.health[H.id],onSelect:()=>z(H.id),onRaw:f})))),Vu(PA,{title:`${YZ(W)} 文件视图`,eyebrow:N?.body?`Health ${M6(N.body)?"OK":"UNKNOWN"} / ${j.refreshedAt?qf(j.refreshedAt):"--"}`:"Embedded WebUI",className:"filebrowser-frame-panel"},Vu("div",{className:"filebrowser-frame-shell"},Vu("div",{className:"filebrowser-frame-toolbar"},Vu("span",null,"BaseURL"),Vu("code",null,`/api/microservices/${W?.id||"filebrowser"}/proxy`),Vu("span",null,"Root"),Vu("code",null,"/srv"),Vu("span",{className:"filebrowser-compact-note"},"Compact layout injected")),Vu("iframe",{ref:q,key:c,title:`${W?.name||"File Browser"} WebUI`,src:c,className:"filebrowser-frame","data-testid":"filebrowser-frame",onLoad:(H)=>mA(H.currentTarget),sandbox:"allow-downloads allow-forms allow-modals allow-same-origin allow-scripts"}))))}var x6=Pu(wf(),1);var Ju=x6.default.createElement,{useEffect:pZ}=x6.default,CZ=x6.default.useState;function m6({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return Ju("span",{className:`status-badge ${l}`},f||u||"unknown")}function c1({label:u,value:f,hint:l,tone:r}){return Ju("article",{className:`metric-card ${r||""}`},Ju("div",{className:"metric-label"},u),Ju("div",{className:"metric-value"},f),Ju("div",{className:"metric-hint"},l))}function p6({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return Ju("section",{className:`panel ${y||""}`},Ju("div",{className:"panel-head"},Ju("div",null,f?Ju("p",{className:"panel-eyebrow"},f):null,Ju(Af,{title:u,loading:i})),l?Ju("div",{className:"panel-actions"},l):null),Ju("div",{className:"panel-body"},r))}function C6({title:u,data:f,onOpen:l,testId:r}){return Ju("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function pA({title:u,text:f}){return Ju("div",{className:"empty-state"},Ju("strong",null,u),Ju("span",null,f))}function xZ(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function RZ(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function hZ(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function Jy(u,f){let l=u&&typeof u==="object"?u[f]:void 0;return Number.isFinite(Number(l))?String(l):"--"}function bZ(u){return(Array.isArray(u?.jobs)?u.jobs:[]).slice(0,40)}function vZ(u){return(Array.isArray(u?.drafts)?u.drafts:[]).slice(0,12)}function nW({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((W)=>W.id==="findjob")||null,[y,i]=CZ({loading:!1,error:"",health:null,summary:null,jobs:null,drafts:null,refreshedAt:null});async function _(){if(!r)return;i((W)=>({...W,loading:!0,error:""}));try{let[W,G,K,Q]=await Promise.all([Xu(`${l}/microservices/findjob/health`),Xu(`${l}/microservices/findjob/proxy/api/summary`),Xu(`${l}/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:40`),Xu(`${l}/microservices/findjob/proxy/api/drafts`)]);i({loading:!1,error:"",health:W,summary:G,jobs:K,drafts:Q,refreshedAt:new Date})}catch(W){i((G)=>({...G,loading:!1,error:Bu(W,"FindJob 加载失败")}))}}if(pZ(()=>{_()},[r?.id,r?.runtime?.providerStatus]),!r)return Ju(pA,{title:"FindJob 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=findjob"});let n=xZ(r),$=hZ(r),j=RZ(r),F=y.summary||{},J=bZ(y.jobs),U=vZ(y.drafts),q=y.jobs?._unidesk?.arrayLimits?.jobs;return Ju("div",{className:"findjob-page","data-testid":"findjob-page"},Ju(p6,{title:"FindJob 工作台",eyebrow:"D601 用户服务",loading:y.loading,actions:Ju("div",{className:"panel-actions"},Ju("button",{type:"button",className:"ghost-btn",onClick:_,disabled:y.loading,"data-testid":"findjob-refresh-button"},y.loading?"刷新中":"刷新"),Ju(C6,{title:"FindJob 用户服务",data:r,onOpen:f,testId:"raw-findjob-service"}))},Ju("div",{className:"findjob-hero"},Ju("div",null,Ju("div",{className:"node-version-line"},Ju(m6,{status:n.providerStatus==="online"?"online":"warn"},n.providerStatus||"unknown"),Ju("span",null,r.providerId),Ju("span",null,j.public?"公网暴露":"仅 UniDesk frontend 代理访问")),Ju("p",{className:"muted paragraph"},r.description)),Ju("div",{className:"microservice-ref-card"},Ju("span",null,"Repo"),Ju("strong",null,$.url||"--"),Ju("code",null,$.commitId||"--")),Ju("div",{className:"microservice-ref-card"},Ju("span",null,"D601 Docker"),Ju("strong",null,`${j.nodeBindHost||"--"}:${j.nodePort||"--"}`),Ju("code",null,`${$.composeFile||"--"} / ${$.composeService||"--"}`))),Ju(jf,{error:y.error,wide:!0})),Ju("div",{className:"findjob-grid"},Ju(p6,{title:"岗位指标",eyebrow:y.refreshedAt?`Updated ${qf(y.refreshedAt)}`:"Summary",loading:y.loading},Ju("div",{className:"metric-grid"},Ju(c1,{label:"岗位总量",value:Jy(F,"totalJobs"),hint:"tracked jobs",tone:"ok"}),Ju(c1,{label:"原始岗位",value:Jy(F,"rawJobs"),hint:"raw queue"}),Ju(c1,{label:"已验证",value:Jy(F,"verifiedJobs"),hint:"verified set"}),Ju(c1,{label:"优先处理",value:Jy(F,"prioritizedJobs"),hint:"prioritized"}),Ju(c1,{label:"过期",value:Jy(F,"staleJobs"),hint:"stale jobs",tone:"warn"}),Ju(c1,{label:"无效",value:Jy(F,"invalidJobs"),hint:"invalid jobs",tone:"warn"}),Ju(c1,{label:"上海",value:Jy(F,"shanghaiJobs"),hint:"city filter"}),Ju(c1,{label:"Health",value:y.health?.ok?"OK":"--",hint:"D601 /api/health"})),Ju("div",{className:"panel-actions inline-actions"},Ju(C6,{title:"FindJob Summary",data:F,onOpen:f,testId:"raw-findjob-summary"}))),Ju(p6,{title:"近期岗位",eyebrow:q?`${q.returnedLength}/${q.originalLength} Preview`:`${J.length} Preview`,loading:y.loading},J.length===0?Ju(pA,{title:"暂无岗位预览",text:"等待 D601 findjob backend 返回 /api/jobs"}):Ju("div",{className:"table-wrap findjob-job-table"},Ju("table",null,Ju("thead",null,Ju("tr",null,Ju("th",null,"优先级"),Ju("th",null,"状态"),Ju("th",null,"单位"),Ju("th",null,"职位"),Ju("th",null,"城市"),Ju("th",null,"阶段"),Ju("th",null,"截止"),Ju("th",null,"证据"))),Ju("tbody",null,J.map((W)=>Ju("tr",{key:W.id},Ju("td",null,Ju(m6,{status:String(W.priority||"").toLowerCase()||"unknown"},W.priority||"--")),Ju("td",null,Ju(m6,{status:String(W.status||"").toLowerCase()||"unknown"},W.status||"--")),Ju("td",null,W.organization_name||"--",Ju("code",null,W.id||"--")),Ju("td",null,W.display_title||W.title||"--"),Ju("td",null,W.display_city||W.city||"--"),Ju("td",null,W.workflow_stage||"--"),Ju("td",null,W.deadline||"--"),Ju("td",null,W.evidence_url?Ju("a",{href:W.evidence_url,target:"_blank",rel:"noreferrer"},"打开"):Ju("span",{className:"muted"},"无"))))))),Ju("div",{className:"panel-actions inline-actions"},Ju(C6,{title:"FindJob Jobs Preview",data:y.jobs,onOpen:f,testId:"raw-findjob-jobs"}))),Ju(p6,{title:"草稿与报告",eyebrow:`${U.length} Drafts`,loading:y.loading},U.length===0?Ju(pA,{title:"暂无草稿",text:"D601 findjob backend 未返回 drafts"}):Ju("div",{className:"draft-list"},U.map((W)=>Ju("article",{key:W.id,className:"draft-card"},Ju("div",{className:"node-card-head"},Ju("strong",null,W.id),Ju(m6,{status:W.status},W.status||"--")),Ju("div",{className:"docker-meta compact"},Ju("span",null,W.workflow_stage||"--"),Ju("span",null,`jobs ${W.counts?.jobs??0}`),Ju("span",null,`reports ${W.counts?.reports??0}`)),Ju("span",null,W.latestReportPath||"暂无报告"),Ju("code",null,Nu(W.updated_at||W.updatedAt))))),Ju("div",{className:"panel-actions inline-actions"},Ju(C6,{title:"FindJob Drafts",data:y.drafts,onOpen:f,testId:"raw-findjob-drafts"})))))}var Dn=Pu(wf(),1);var p=Dn.default.createElement,{useEffect:IZ}=Dn.default,CA=Dn.default.useState;function Vn(u){let f=Number(u);return Number.isFinite(f)?`${Math.max(0,Math.min(100,f)).toFixed(1)}%`:"--"}function RA(u){if(u===null||u===void 0||u==="")return"--";let f=Number(u);if(!Number.isFinite(f))return"--";if(f<60)return`${Math.max(0,Math.round(f))}s`;if(f<3600)return`${Math.floor(f/60)}m ${Math.round(f%60)}s`;return`${Math.floor(f/3600)}h ${Math.floor(f%3600/60)}m`}function hA(u,f=2){let l=Number(u);if(!Number.isFinite(l))return u===!1?"false":u===!0?"true":"--";let r=Math.abs(l);if(Number.isInteger(l)||r>=1000)return l.toLocaleString("zh-CN",{maximumFractionDigits:0});if(r>=1)return l.toLocaleString("zh-CN",{maximumFractionDigits:f});return l.toLocaleString("zh-CN",{maximumFractionDigits:Math.max(f,6)})}function Yn(u){if(u===null||u===void 0||u==="")return"--";if(typeof u==="boolean")return u?"true":"false";if(typeof u==="number")return hA(u,4);if(Array.isArray(u))return u.map((f)=>Yn(f)).join(" x ");if(typeof u==="object")return"已上报";return String(u)}function R6(u){let f=Number(u);if(!Number.isFinite(f)||f<=0)return"--";let l=f>=100?0:f>=10?1:2;return`${f.toLocaleString("zh-CN",{maximumFractionDigits:l})} epoch/h`}function h6(u){return u.replace(/[^a-zA-Z0-9_-]/g,"-")}function pl(u){return u&&typeof u==="object"&&!Array.isArray(u)?u:{}}function Xn({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return p("span",{className:`status-badge ${l}`},f||u||"unknown")}function N1({label:u,value:f,hint:l,tone:r}){return p("article",{className:`metric-card ${r||""}`},p("div",{className:"metric-label"},u),p("div",{className:"metric-value"},f),p("div",{className:"metric-hint"},l))}function xA({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return p("section",{className:`panel ${y||""}`},p("div",{className:"panel-head"},p("div",null,f?p("p",{className:"panel-eyebrow"},f):null,p(Af,{title:u,loading:i})),l?p("div",{className:"panel-actions"},l):null),p("div",{className:"panel-body"},r))}function Oi({title:u,data:f,onOpen:l,testId:r}){return p("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:(y)=>{y?.stopPropagation?.(),l(u,f)}},"查看原始JSON")}function O0({title:u,text:f}){return p("div",{className:"empty-state"},p("strong",null,u),p("span",null,f))}function kZ(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function gZ(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function sZ(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function aZ(u){return u?.counts&&typeof u.counts==="object"&&!Array.isArray(u.counts)?u.counts:{}}function oZ(u){return Array.isArray(u?.jobs)?u.jobs.slice(0,240):[]}function dZ(u){return Array.isArray(u?.projects)?u.projects.slice(0,1000):[]}function b6(u){return Array.isArray(u?.projects)?u.projects:[]}function eZ(u,f){if(Array.isArray(f?.gpu))return f.gpu;if(Array.isArray(u?.gpu))return u.gpu;return[]}function ir(u,f){return`${u}/microservices/met-nonlinear/proxy${f}`}function $W(u){return u.startedAt&&u.finishedAt?RA((Date.parse(u.finishedAt)-Date.parse(u.startedAt))/1000):"--"}function uH(u){let f=u.progress||{};if(f.etaSeconds!==null&&f.etaSeconds!==void 0&&f.etaSeconds!==""){let _=Number(f.etaSeconds);if(Number.isFinite(_))return Math.max(0,_)}let l=Number(f.currentEpoch),r=Number(f.epochTarget??u.epochTarget),y=Date.parse(u.startedAt||"");if(!Number.isFinite(l)||l<=0||!Number.isFinite(r)||r<=l||!Number.isFinite(y))return null;let i=Math.max(0,(Date.now()-y)/1000);if(i<=0)return null;return Math.max(0,i/l*(r-l))}function AW(u){let f=u.progress||{},l=Number(f.epochPerHour);if(Number.isFinite(l)&&l>0)return l;let r=Date.parse(u.startedAt||""),y=["succeeded","failed","canceled"].includes(u.status)?Date.parse(u.finishedAt||""):Date.now();if(!Number.isFinite(r)||!Number.isFinite(y)||y<=r)return null;let i=Number(f.currentEpoch??u.epochTarget);if(!Number.isFinite(i)||i<=0)return null;return i/((y-r)/3600000)}function jW(u){if(u==="staged")return"待启动";if(u==="queued")return"排队中";if(u==="running")return"训练中";if(u==="succeeded")return"已完成";if(u==="failed")return"失败";if(u==="canceled")return"已取消";return u||"unknown"}function FW(u,f,l){return{name:u,path:f,depth:l,count:0,children:[],project:null}}function fH(u){let f=FW("","",-1);for(let r of u){let i=String(r?.projectPath||"").replace(/\\/g,"/").split("/").filter(Boolean);if(i.length===0)continue;let _=f,n=[];for(let[$,j]of i.entries()){n.push(j);let F=n.join("/"),J=_.children.find((U)=>U.path===F);if(!J)J=FW(j,F,$),_.children.push(J);if($===i.length-1)J.project=r;_=J}}let l=(r)=>{let y=r.children.reduce((i,_)=>i+l(_),0);return r.count=(r.project?1:0)+y,r.children.sort((i,_)=>{if(Boolean(i.project)!==Boolean(_.project))return i.project?1:-1;return i.name.localeCompare(_.name,"zh-CN",{numeric:!0,sensitivity:"base"})}),r.count};return l(f),f}function lH(u){let f=pl(u.data);return pl(f.project).projectPath?pl(f.project):f}function rH(u){return pl(pl(u.data).job)}function JW({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((m)=>m.id==="met-nonlinear")||null,[y,i]=CA({loading:!1,actionBusy:!1,error:"",health:null,summary:null,queue:null,projects:null,history:null,images:null,refreshedAt:null}),[_,n]=CA({loading:!1,error:"",kind:"",key:"",title:"",data:null}),[$,j]=CA(()=>({activeTab:"projects",selectedProjects:{},expandedProjectDirs:{},sourceProject:"",forkCount:1,forkEpochs:200,forkPrefix:`ui_fork_${Date.now()}`,maxConcurrency:3,targetGpuName:"2080 Ti",actionMessage:""}));function F(m){j((d)=>({...d,...m}))}async function J(m=$.activeTab){if(!r)return;i((d)=>({...d,loading:!0,error:""}));try{let d=[["health",Xu(`${l}/microservices/met-nonlinear/health`)],["summary",Xu(ir(l,"/api/summary"))]];if(m==="projects")d.push(["projectsRoot",Xu(ir(l,"/api/projects?root=projects&limit=500"))]),d.push(["exProjectsRoot",Xu(ir(l,"/api/projects?root=ex_projects&limit=500"))]);if(m==="current"||m==="completed"||m==="failed")d.push(["queue",Xu(ir(l,"/api/queue"))]);if(m==="completed"||m==="failed")d.push(["history",Xu(ir(l,"/api/history"))]);if(m==="gpu")d.push(["images",Xu(ir(l,"/api/images"))]);let e=Object.fromEntries(await Promise.all(d.map(async([g,Qu])=>[g,await Qu]))),cu={loading:!1,actionBusy:!1,error:"",health:e.health,summary:e.summary,refreshedAt:new Date};if(e.projectsRoot||e.exProjectsRoot){let{projectsRoot:g,exProjectsRoot:Qu}=e;cu.projects={ok:g?.ok!==!1&&Qu?.ok!==!1,roots:[{root:"projects",count:b6(g).length},{root:"ex_projects",count:b6(Qu).length}],projects:[...b6(g),...b6(Qu)]}}if(e.queue)cu.queue=e.queue;if(e.history)cu.history=e.history;if(e.images)cu.images=e.images;i((g)=>({...g,...cu}))}catch(d){i((e)=>({...e,loading:!1,actionBusy:!1,error:Bu(d,"MET Nonlinear 加载失败")}))}}async function U(m,d){i((e)=>({...e,actionBusy:!0,error:""})),F({actionMessage:`${m}...`});try{let e=await d();F({actionMessage:e||`${m}完成`}),await J()}catch(e){i((cu)=>({...cu,actionBusy:!1,error:Bu(e,`${m}失败`)}))}}async function q(){await U("保存并发设置",async()=>{await Xu(ir(l,"/api/queue/settings"),{method:"PUT",body:JSON.stringify({maxConcurrency:Number($.maxConcurrency),targetGpuName:$.targetGpuName})})})}function W(){return Object.entries($.selectedProjects).filter(([,m])=>m).map(([m])=>m)}async function G(){let m=W();if(m.length===0)throw Error("请先选择至少一个 project");await U("加入待启动队列",async()=>{await Xu(ir(l,"/api/queue"),{method:"POST",body:JSON.stringify({projectPaths:m,maxConcurrency:Number($.maxConcurrency),targetGpuName:$.targetGpuName,start:!1})}),F({activeTab:"current",selectedProjects:{}})})}async function K(){let m=$.sourceProject||t[0]?.projectPath;if(!m)throw Error("请先选择源 project");await U("Fork Project",async()=>{let d=await Xu(ir(l,"/api/projects/fork"),{method:"POST",body:JSON.stringify({sourceProject:m,count:Number($.forkCount),epochs:Number($.forkEpochs),prefix:$.forkPrefix})}),e=Array.isArray(d.projectPaths)?d.projectPaths:[],cu=e.reduce((g,Qu)=>{return g[Qu]=!0,g},{...$.selectedProjects});return F({selectedProjects:cu}),`已 fork ${e.length} 个 project,并已自动勾选;请确认后点击加入待启动队列。`})}async function Q(){await U("启动队列",async()=>{await Xu(ir(l,"/api/queue/start"),{method:"POST",body:JSON.stringify({maxConcurrency:Number($.maxConcurrency),targetGpuName:$.targetGpuName})}),F({activeTab:"current"})})}async function N(m){await U("取消任务",async()=>{await Xu(ir(l,`/api/jobs/${encodeURIComponent(m.id)}/cancel`),{method:"POST",body:JSON.stringify({})})})}async function c(m){let d=String(m?.projectPath||"");if(!d)return;n({loading:!0,error:"",kind:"project",key:d,title:d,data:null});try{let e=await Xu(ir(l,`/api/projects/config?path=${encodeURIComponent(d)}`));n({loading:!1,error:"",kind:"project",key:d,title:d,data:e})}catch(e){n({loading:!1,error:Bu(e,"Project 详情加载失败"),kind:"project",key:d,title:d,data:null})}}async function z(m){let d=String(m?.id||"");if(!d)return;n({loading:!0,error:"",kind:"job",key:d,title:m.projectPath||d,data:null});try{let e=await Xu(ir(l,`/api/jobs/${encodeURIComponent(d)}`));n({loading:!1,error:"",kind:"job",key:d,title:e?.job?.projectPath||m.projectPath||d,data:e})}catch(e){n({loading:!1,error:Bu(e,"Job 详情加载失败"),kind:"job",key:d,title:m.projectPath||d,data:null})}}if(IZ(()=>{J($.activeTab)},[r?.id,r?.runtime?.providerStatus,$.activeTab]),!r)return p(O0,{title:"MET Nonlinear 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=met-nonlinear"});let w=kZ(r),H=sZ(r),B=gZ(r),T=aZ(y.queue?.queue||y.summary?.queue),X=eZ(y.health,y.queue),R=y.health?.targetGpu||y.summary?.targetGpu||X.find((m)=>String(m.name||"").includes("2080")),Y=y.images?.mlImage||y.health?.image||{},P=oZ(y.queue),t=dZ(y.projects),O=fH(t),M=$.sourceProject||t[0]?.projectPath||"",S=P.filter((m)=>["staged","queued","running"].includes(m.status)),b=P.filter((m)=>m.status==="succeeded"),Z=P.filter((m)=>["failed","canceled"].includes(m.status)),D=Array.isArray(y.history?.jobs)?y.history.jobs.slice(0,120):[],I=[{id:"projects",label:"项目库",count:t.length},{id:"current",label:"当前队列",count:S.length||Number(T.staged||0)+Number(T.queued||0)+Number(T.running||0)},{id:"completed",label:"已完成",count:b.length||Number(T.succeeded||0)},{id:"failed",label:"失败诊断",count:Z.length||Number(T.failed||0)+Number(T.canceled||0)},{id:"gpu",label:"GPU/镜像",count:X.length}];function k(m,d){if(m.length===0)return p(O0,{title:d==="current"?"当前队列为空":"暂无记录",text:d==="current"?"从项目库选择或 fork project 后先加入待启动队列,再启动队列。":"终态任务会显示耗时、exit code 和失败诊断。"});return p("div",{className:"table-wrap met-job-table"},p("table",null,p("thead",null,p("tr",null,p("th",null,"状态"),p("th",null,"Project"),p("th",null,"Epoch"),p("th",null,"速度"),p("th",null,"ETA/耗时"),p("th",null,"GPU"),p("th",null,"Exit"),p("th",null,"更新时间"),p("th",null,"操作"))),p("tbody",null,m.map((e)=>{let cu=e.progress||{},g=["staged","queued","running"].includes(e.status),Qu=_.kind==="job"&&_.key===e.id;return p("tr",{key:e.id,className:`met-click-row ${Qu?"active":""}`,onClick:()=>z(e),"data-testid":`met-job-row-${h6(e.id)}`},p("td",null,p(Xn,{status:e.status},jW(e.status))),p("td",null,p("button",{type:"button",className:"met-inline-link",onClick:(Eu)=>{Eu.stopPropagation(),z(e)}},e.projectPath),p("code",null,e.id)),p("td",null,p("span",null,`${cu.currentEpoch??"--"} / ${cu.epochTarget??e.epochTarget??"--"}`),p("div",{className:"met-progress"},p("span",{style:{width:Vn(cu.progressPercent)}}))),p("td",null,p("strong",null,R6(AW(e)))),p("td",null,e.status==="succeeded"||e.status==="failed"||e.status==="canceled"?$W(e):e.status==="running"?`ETA ${RA(uH(e))}`:"--"),p("td",null,e.gpuName||"--"),p("td",null,e.exitCode??"--"),p("td",null,Nu(e.updatedAt)),p("td",null,g?p("button",{type:"button",className:"ghost-btn mini",onClick:(Eu)=>{Eu.stopPropagation(),N(e)},disabled:y.actionBusy},"取消"):null,p(Oi,{title:`MET Job ${e.id}`,data:e,onOpen:f,testId:`raw-met-job-${e.id}`})))}))))}function h(){return p("div",{className:"met-queue-summary","data-testid":"met-current-summary"},p(Xn,{status:"staged"},`待启动 ${T.staged??0}`),p(Xn,{status:"queued"},`排队中 ${T.queued??0}`),p(Xn,{status:"running"},`训练中 ${T.running??0}`),p("span",null,`最大并发 ${y.summary?.queue?.maxConcurrency??y.queue?.queue?.maxConcurrency??$.maxConcurrency}`),p("span",null,`目标 GPU ${y.summary?.queue?.targetGpuName??y.queue?.queue?.targetGpuName??$.targetGpuName}`))}function o(m,d){let e=$.expandedProjectDirs[m];return e===void 0?d<2:Boolean(e)}function s(m,d){let e=o(m,d);F({expandedProjectDirs:{...$.expandedProjectDirs,[m]:!e}})}function x(m){let d=8+Math.max(0,m.depth)*16;if(Boolean(m.project)){let g=m.project,Qu=Boolean($.selectedProjects[g.projectPath]),Eu=_.kind==="project"&&_.key===g.projectPath;return p("div",{key:m.path,className:`met-tree-row project ${Qu?"selected":""} ${Eu?"active":""}`,style:{paddingLeft:d},onClick:()=>c(g),"data-testid":`met-project-node-${h6(g.projectPath)}`},p("div",{className:"met-tree-name"},p("input",{type:"checkbox",checked:Qu,onClick:(Tu)=>Tu.stopPropagation(),onChange:(Tu)=>F({selectedProjects:{...$.selectedProjects,[g.projectPath]:Tu.target.checked}}),"data-testid":`met-project-checkbox-${h6(g.projectPath)}`}),p("button",{type:"button",className:"met-inline-link project-path",onClick:(Tu)=>{Tu.stopPropagation(),c(g)}},m.name)),p("span",null,g.useModel||"--"),p("span",null,g.epochTrain??"--"),p("span",null,Vn(g.progress?.progressPercent)),p("span",null,R6(g.progress?.epochPerHour)))}let cu=o(m.path,m.depth);return p(Dn.default.Fragment,{key:m.path},p("div",{className:"met-tree-row folder",style:{paddingLeft:d},"data-testid":`met-project-folder-${h6(m.path)}`},p("button",{type:"button",className:"met-tree-toggle",onClick:()=>s(m.path,m.depth),"aria-label":cu?`折叠 ${m.path}`:`展开 ${m.path}`},cu?"-":"+"),p("strong",null,m.name),p("span",{className:"met-tree-count"},`${m.count} projects`)),cu?m.children.map((g)=>x(g)):null)}function uu(m){return p("div",{className:"met-detail-kv"},m.map((d)=>p("div",{key:d.label,className:"met-detail-kv-item"},p("span",null,d.label),p("strong",null,Yn(d.value)),d.hint?p("small",null,d.hint):null)))}function nu(m,d){return p("div",{className:"met-detail-section"},p("h3",null,m),uu(d))}function $u(m){if(!Array.isArray(m)||m.length===0)return p(O0,{title:"模型层未上报",text:"等待 data/model_info.json 或 compute_analysis.json 生成。"});return p("div",{className:"table-wrap met-layer-table"},p("table",null,p("thead",null,p("tr",null,p("th",null,"Layer"),p("th",null,"Type"),p("th",null,"Params"),p("th",null,"Trainable"),p("th",null,"Compute"))),p("tbody",null,m.slice(0,18).map((d,e)=>p("tr",{key:`${d.name||"layer"}-${e}`},p("td",null,d.name||`#${e+1}`),p("td",null,d.type||"--"),p("td",null,hA(d.num_params)),p("td",null,d.trainable===void 0?"--":String(Boolean(d.trainable))),p("td",null,hA(d.compute?.total??d.estimated_cost?.weighted_units?.total)))))))}function Fu(m){let d=Array.isArray(m)?m:[];if(d.length===0)return p(O0,{title:"data/ 暂无文件",text:"训练或评估完成后会生成 training_state、metrics、model_info 等文件。"});return p("div",{className:"met-file-chip-grid"},d.slice(0,48).map((e)=>p("span",{key:e},e)),d.length>48?p("span",null,`+${d.length-48}`):null)}function Ku(m){let d=String(m||"").replace(/\x1b\[[0-9;]*[A-Za-z]/g,"").split(/\r?\n/).map((e)=>e.trim()).filter(Boolean).slice(-12);if(d.length===0)return p(O0,{title:"暂无日志尾部",text:"该任务未上报 logTail 或日志已轮转。"});return p("div",{className:"met-log-lines"},d.map((e,cu)=>p("div",{key:`${cu}-${e.slice(0,16)}`},e)))}function Wu(){if(_.loading)return p("section",{className:"met-detail-panel","data-testid":"met-detail-panel"},p("div",{className:"panel-head compact"},p("div",null,p("p",{className:"panel-eyebrow"},"Detail Loading"),p(Af,{title:"详情加载中",loading:!0}))),p(O0,{title:"详情加载中",text:_.title||"正在读取 D601 data/ 和 config.json"}));if(_.error)return p("section",{className:"met-detail-panel","data-testid":"met-detail-panel"},p(jf,{error:_.error,wide:!0}));if(!_.data)return p("section",{className:"met-detail-panel muted","data-testid":"met-detail-panel"},p(O0,{title:"选择一个项目或任务查看详情",text:"项目库、当前队列、已完成和失败诊断中的行都可以点击;默认只展示结构化字段,原始 JSON 需显式点击按钮。"}));let m=lH(_),d=rH(_),e=pl(m.config),cu=pl(m.progress||d.progress),g=pl(m.data),Qu=pl(m.metrics||g.metrics||cu.trainingInfo?.evaluation_metrics),Eu=pl(g.trainingInfo||cu.trainingInfo),Tu=pl(g.trainingState),Du=pl(m.model||g.model),ff=Array.isArray(Du.modelSummary)&&Du.modelSummary.length>0?Du.modelSummary:Du.computeLayers,rf=pl(Eu.evaluation_metrics),Lf=_.kind==="job"?"训练任务详情":"Project 详情";return p("section",{className:"met-detail-panel","data-testid":"met-detail-panel"},p("div",{className:"panel-head compact"},p("div",null,p("p",{className:"panel-eyebrow"},_.kind==="job"?"Job + Project Detail":"Project Library Detail"),p(Af,{title:Lf}),p("code",null,m.projectPath||d.projectPath||_.title)),p("div",{className:"panel-actions"},p(Oi,{title:`MET ${Lf}`,data:_.data,onOpen:f,testId:"raw-met-detail"}))),_.kind==="job"?nu("任务状态",[{label:"Job ID",value:d.id},{label:"状态",value:jW(d.status)},{label:"GPU",value:d.gpuName},{label:"Exit Code",value:d.exitCode},{label:"耗时",value:$W(d)},{label:"训练速度",value:R6(AW({...d,progress:cu}))}]):null,nu("config.json",[{label:"use_model",value:e.use_model},{label:"epoch_train",value:e.epoch_train},{label:"step_per_epoch",value:e.step_per_epoch},{label:"learning_rate",value:e.learning_rate},{label:"using_gpu",value:e.using_gpu},{label:"use_points",value:e.use_points},{label:"sample_rate",value:e.sample_rate},{label:"time_clipped_s",value:e.time_clipped_s},{label:"H_UNITS",value:e.H_UNITS},{label:"INNER_KAN_UNITS",value:e.INNER_KAN_UNITS},{label:"INNER_KAN_LAYERS",value:e.INNER_KAN_LAYERS},{label:"GRID_SIZE",value:e.GRID_SIZE},{label:"SPLINE_ORDER",value:e.SPLINE_ORDER},{label:"USE_FAST_MODEL",value:e.USE_FAST_MODEL},{label:"IIR_TRAINABLE",value:e.IIR_TRAINABLE}]),nu("data/ 训练状态",[{label:"Epoch",value:`${cu.currentEpoch??Tu.current_epoch??Tu.completed_epoch??"--"} / ${cu.epochTarget??e.epoch_train??"--"}`},{label:"Progress",value:Vn(cu.progressPercent)},{label:"Last Loss",value:cu.lastLoss??Tu.loss},{label:"Last Val Loss",value:cu.lastValLoss??Tu.val_loss},{label:"Min Loss",value:Eu.min_loss??Tu.min_loss},{label:"Min Val Loss",value:Eu.min_val_loss??Tu.min_val_loss},{label:"Log Lines",value:cu.logLineCount},{label:"ETA",value:RA(cu.etaSeconds??Tu.remaining_time)},{label:"训练速度",value:R6(cu.epochPerHour??Tu.smoothed_speed)},{label:"Training Alive",value:Tu.training_alive}]),nu("模型参数",[{label:"Model Type",value:Du.modelType??e.use_model},{label:"Total Params",value:Du.totalParams,hint:Du.totalParams===null||Du.totalParams===void 0?"未上报":"data/model_info.json"},{label:"Trainable",value:Du.trainableParams},{label:"Non-trainable",value:Du.nonTrainableParams},{label:"Compute Cost",value:Du.computeCost},{label:"Estimate Status",value:Du.estimateStatus},{label:"Unsupported Layers",value:Du.unsupportedLayerCount}]),nu("指标",[{label:"train_loss",value:Qu.train_loss??rf.train_loss},{label:"val_loss",value:Qu.val_loss??rf.val_loss},{label:"train_mae",value:Qu.train_mae??rf.train_mae},{label:"val_mae",value:Qu.val_mae??rf.val_mae},{label:"train_afmae",value:Qu.train_afmae??rf.train_afmae},{label:"val_afmae",value:Qu.val_afmae??rf.val_afmae},{label:"freq_drift_hz",value:Qu.freq_drift_hz},{label:"sens_drift_percent",value:Qu.sens_drift_percent},{label:"linearity_percent",value:Qu.linearity_percent},{label:"weights_source",value:Qu.weights_source??rf.weights_source},{label:"lr min/mean/max",value:`${Yn(Eu.learning_rate_min)} / ${Yn(Eu.learning_rate_mean)} / ${Yn(Eu.learning_rate_max)}`}]),p("div",{className:"met-detail-section"},p("h3",null,"模型层"),$u(ff)),p("div",{className:"met-detail-section"},p("h3",null,"data/ 文件"),Fu(g.files)),_.kind==="job"?p("div",{className:"met-detail-section"},p("h3",null,"日志尾部"),Ku(pl(_.data).logTail)):null)}return p("div",{className:"met-page","data-testid":"met-nonlinear-page"},p(xA,{title:"MET Nonlinear 训练编排",eyebrow:"D601 GPU 用户服务",loading:y.loading||y.actionBusy,actions:p("div",{className:"panel-actions"},p("button",{type:"button",className:"ghost-btn",onClick:J,disabled:y.loading,"data-testid":"met-refresh-button"},y.loading?"刷新中":"刷新"),p(Oi,{title:"MET Nonlinear 用户服务",data:r,onOpen:f,testId:"raw-met-service"}))},p("div",{className:"findjob-hero"},p("div",null,p("div",{className:"node-version-line"},p(Xn,{status:w.providerStatus==="online"?"online":"warn"},w.providerStatus||"unknown"),p("span",null,r.providerId),p("span",null,B.public?"公网暴露":"仅 UniDesk frontend 代理访问")),p("p",{className:"muted paragraph"},r.description)),p("div",{className:"microservice-ref-card"},p("span",null,"Repo"),p("strong",null,H.url||"--"),p("code",null,H.commitId||"--")),p("div",{className:"microservice-ref-card"},p("span",null,"D601 Docker"),p("strong",null,`${B.nodeBindHost||"--"}:${B.nodePort||"--"}`),p("code",null,`${H.composeFile||"--"} / ${H.containerName||"--"}`))),p(jf,{error:y.error,wide:!0}),$.actionMessage?p("div",{className:"met-action-log","data-testid":"met-action-message"},$.actionMessage):null),p("div",{className:"met-grid"},p(xA,{title:"核心状态",eyebrow:y.refreshedAt?`Updated ${qf(y.refreshedAt)}`:"Queue + GPU",loading:y.loading},p("div",{className:"metric-grid"},p(N1,{label:"Staged",value:T.staged??0,hint:"加入队列未开始",tone:Number(T.staged||0)>0?"warn":""}),p(N1,{label:"Queued",value:T.queued??0,hint:"排队等待调度",tone:Number(T.queued||0)>0?"warn":""}),p(N1,{label:"Running",value:T.running??0,hint:`max ${y.summary?.queue?.maxConcurrency??y.queue?.queue?.maxConcurrency??"--"}`,tone:Number(T.running||0)>0?"ok":""}),p(N1,{label:"Succeeded",value:T.succeeded??0,hint:"已完成"}),p(N1,{label:"Failed",value:T.failed??0,hint:"需要诊断",tone:Number(T.failed||0)>0?"warn":""}),p(N1,{label:"2080Ti Free",value:R?Vn(Number(R.freeRatio)*100):"--",hint:R?`${R.memoryFreeMiB}/${R.memoryTotalMiB} MiB`:"等待 GPU 上报"}),p(N1,{label:"ML Image",value:Y.present?"READY":"MISSING",hint:Y.image||"met-nonlinear-ml:tf26",tone:Y.present?"ok":"warn"}),p(N1,{label:"Health",value:y.health?.ok?"OK":"--",hint:"D601 /health"}))),p(xA,{title:"队列控制",eyebrow:"Downloader-like staging",loading:y.actionBusy},p("div",{className:"met-control-strip"},p("label",null,"最大并发",p("input",{type:"number",min:1,max:16,value:$.maxConcurrency,"data-testid":"met-max-concurrency-input",onChange:(m)=>F({maxConcurrency:m.target.value})})),p("label",null,"目标 GPU",p("input",{value:$.targetGpuName,"data-testid":"met-target-gpu-input",onChange:(m)=>F({targetGpuName:m.target.value})})),p("button",{type:"button",className:"ghost-btn",onClick:q,disabled:y.actionBusy,"data-testid":"met-save-settings-button"},"保存设置"),p("button",{type:"button",className:"primary-btn",onClick:Q,disabled:y.actionBusy||Number(T.staged||0)===0,"data-testid":"met-start-queue-button"},"启动队列")),p("p",{className:"muted paragraph"},"Project 先进入待启动队列,不会立即训练;点击启动队列后才切换为排队中,并由 D601 scheduler 按最大并发和 2080Ti 显存策略调度。")),p("section",{className:"panel met-workspace"},p("div",{className:"met-tabs",role:"tablist"},I.map((m)=>p("button",{key:m.id,type:"button",className:$.activeTab===m.id?"active":"",onClick:()=>F({activeTab:m.id}),"data-testid":`met-tab-${m.id}`},`${m.label} ${m.count}`))),p("div",{className:"panel-body"},$.activeTab==="projects"?p("div",{className:"met-form-grid","data-testid":"met-projects-pane"},p("div",{className:"met-fork-card"},p("h3",null,"Fork Project"),p("label",null,"源 Project",p("select",{value:M,"data-testid":"met-source-project-select",onChange:(m)=>F({sourceProject:m.target.value})},t.map((m)=>p("option",{key:m.projectPath,value:m.projectPath},`${m.projectPath} · ${m.useModel||"model?"}`)))),p("label",null,"Fork 数量",p("input",{type:"number",min:1,max:100,value:$.forkCount,"data-testid":"met-fork-count-input",onChange:(m)=>F({forkCount:m.target.value})})),p("label",null,"训练轮数",p("input",{type:"number",min:1,max:1e5,value:$.forkEpochs,"data-testid":"met-fork-epochs-input",onChange:(m)=>F({forkEpochs:m.target.value})})),p("label",null,"目标前缀",p("input",{value:$.forkPrefix,"data-testid":"met-fork-prefix-input",onChange:(m)=>F({forkPrefix:m.target.value})})),p("button",{type:"button",className:"primary-btn",onClick:K,disabled:y.actionBusy||!M,"data-testid":"met-fork-button"},"Fork Project"),p("p",{className:"muted paragraph"},"Fork 只创建新 Project 并自动勾选,不会直接训练;需要在右侧确认后加入待启动队列。")),p("div",{className:"met-project-list"},p("div",{className:"panel-head compact"},p("div",null,p("p",{className:"panel-eyebrow"},`Existing Projects · ${(y.projects?.roots||[]).map((m)=>`${m.root} ${m.count}`).join(" / ")}`),p(Af,{title:"选择已有 Project",loading:y.loading||y.actionBusy})),p("button",{type:"button",className:"ghost-btn",onClick:G,disabled:y.actionBusy||W().length===0,"data-testid":"met-stage-selected-button"},`加入待启动队列 (${W().length})`)),t.length===0?p(O0,{title:"暂无 project",text:"等待 D601 返回 /api/projects"}):p("div",{className:"met-project-table","data-testid":"met-project-tree"},p("div",{className:"met-tree-header"},p("span",null,"文件树 Project"),p("span",null,"Model"),p("span",null,"Epochs"),p("span",null,"Progress"),p("span",null,"速度")),O.children.map((m)=>x(m)))),Wu()):null,$.activeTab==="current"?p("div",{"data-testid":"met-current-pane"},h(),k(S,"current"),Wu(),p("div",{className:"panel-actions inline-actions"},p(Oi,{title:"MET Queue",data:y.queue,onOpen:f,testId:"raw-met-queue"}))):null,$.activeTab==="completed"?p("div",{"data-testid":"met-completed-pane"},k(b.length>0?b:D.filter((m)=>m.status==="succeeded"),"completed"),Wu()):null,$.activeTab==="failed"?p("div",{"data-testid":"met-failed-pane"},k(Z.length>0?Z:D.filter((m)=>["failed","canceled"].includes(m.status)),"failed"),Wu(),p("div",{className:"panel-actions inline-actions"},p(Oi,{title:"MET History",data:y.history,onOpen:f,testId:"raw-met-history"}))):null,$.activeTab==="gpu"?p("div",{className:"met-gpu-pane","data-testid":"met-gpu-pane"},X.length===0?p(O0,{title:"暂无 GPU 上报",text:"等待 D601 met-nonlinear-ts 或 ML image 提供 nvidia-smi 数据"}):p("div",{className:"table-wrap"},p("table",null,p("thead",null,p("tr",null,p("th",null,"Index"),p("th",null,"Name"),p("th",null,"Free"),p("th",null,"Policy"))),p("tbody",null,X.map((m)=>p("tr",{key:m.index},p("td",null,m.index),p("td",null,m.name),p("td",null,`${m.memoryFreeMiB} / ${m.memoryTotalMiB} MiB`,p("div",{className:"met-progress"},p("span",{style:{width:Vn(Number(m.freeRatio)*100)}}))),p("td",null,String(m.name||"").includes("2080")?"target 2080Ti, <20% 限制并发":"non-target")))))),p("div",{className:"panel-actions inline-actions"},p(Oi,{title:"MET Images",data:y.images,onOpen:f,testId:"raw-met-images"}))):null))))}var I6=[{id:"ops",label:"运行总览",code:"OPS",tabs:[{id:"status",label:"态势总览"},{id:"performance",label:"性能面板"},{id:"events",label:"事件摘要"},{id:"logs",label:"服务日志"}]},{id:"nodes",label:"资源节点",code:"NODE",tabs:[{id:"list",label:"节点清单"},{id:"monitor",label:"资源监控"},{id:"docker",label:"Docker 状态"},{id:"gateway",label:"网关版本"},{id:"labels",label:"资源标签"},{id:"heartbeats",label:"心跳状态"}]},{id:"tasks",label:"任务调度",code:"TASK",tabs:[{id:"dispatch",label:"下发任务"},{id:"scheduled",label:"定时任务"},{id:"pending",label:"待处理任务"},{id:"history",label:"任务历史"},{id:"results",label:"执行结果"}]},{id:"apps",label:"用户服务",code:"APP",routeSegment:"app",tabs:[{id:"catalog",label:"服务目录"},{id:"todo-note",label:"Todo Note"},{id:"findjob",label:"FindJob"},{id:"pipeline",label:"Pipeline"},{id:"met-nonlinear",label:"MET Nonlinear"},{id:"claudeqq",label:"ClaudeQQ"},{id:"baidu-netdisk",label:"Baidu Netdisk"},{id:"filebrowser",label:"File Browser"},{id:"code-queue",label:"Code Queue"},{id:"project-manager",label:"Project Manager"}]},{id:"config",label:"系统配置",code:"CFG",tabs:[{id:"topology",label:"连接拓扑"},{id:"auth",label:"认证策略"},{id:"security",label:"安全边界"}]}],tn=Object.fromEntries(I6.map((u)=>[u.id,u.tabs[0]?.id??""]));function yH(u){let f=String(u||"").trim();if(!f)return"";try{return decodeURIComponent(f)}catch{return f}}function v6(u){let f=String(u||"/"),[l]=f.split(/[?#]/u,1);if(l==="/")return"/";let y=`/${l.split("/").map(yH).filter(Boolean).join("/")}`;return y.endsWith("/")?y:`${y}/`}function iH(u){let f=2166136261;for(let l of u)f^=l.charCodeAt(0),f=Math.imul(f,16777619);return Math.abs(f>>>0).toString(36)}function bA(u){return String(u||"").normalize("NFKD").replace(/[\u0300-\u036f]/gu,"").toLowerCase().replace(/[^a-z0-9]+/gu,"-").replace(/^-+|-+$/gu,"")}function UW(u){return String(u||"").trim().toLowerCase().replace(/[\s/\\?#%]+/gu,"-").replace(/-+/gu,"-").replace(/^-+|-+$/gu,"")}function QW(u){let f=bA(u.routeSegment||"")||UW(u.routeSegment||"");if(f)return f;let l=bA(u.id||"");if(l)return l;let r=bA(u.label||"")||UW(u.label||"");if(r)return r;return`route-${iH(JSON.stringify(u))}`}function vA(u,f){return`${u}:${f}`}function qW(u){let f=u.map(($)=>{let j=QW($);return{...$,routeSegment:j,tabs:$.tabs.map((F)=>({...F,routeSegment:QW(F)}))}}),l={},r={},y={},i=f.map(($)=>{let j=$.tabs[0]?.id??"";y[$.id]=j;let F=$.tabs.map((q)=>{let W=`/${$.routeSegment}/${q.routeSegment}/`,G=[W],K={moduleId:$.id,tabId:q.id};for(let Q of G)l[v6(Q)]=K;return r[vA($.id,q.id)]=W,{...q,canonicalPath:W,aliases:G}}),J=`/${$.routeSegment}/`,U={moduleId:$.id,tabId:j};return l[v6(J)]=U,{...$,routeSegment:$.routeSegment,canonicalPath:J,tabs:F}}),_=i[0],n={moduleId:_?.id||"",tabId:_?.tabs[0]?.id||""};return l["/"]=n,{modules:i,moduleById:Object.fromEntries(i.map(($)=>[$.id,$])),defaultActiveTabs:y,routeMap:l,canonicalPathByTarget:r,fallbackTarget:n}}function IA(u,f){return u.routeMap[v6(f)]||u.fallbackTarget}function k6(u,f,l){return u.canonicalPathByTarget[vA(f,l)]||u.canonicalPathByTarget[vA(u.fallbackTarget.moduleId,u.fallbackTarget.tabId)]||"/"}function WW(u,f){let l=u.routeMap[v6(f)];if(!l)return null;return k6(u,l.moduleId,l.tabId)}var Y1=Pu(wf(),1);var ru=Pu(GW(),1),iu=Pu(wf(),1);function Yf(u){if(typeof u==="string"||typeof u==="number")return""+u;let f="";if(Array.isArray(u)){for(let l=0,r;l{}};function LW(){for(var u=0,f=arguments.length,l={},r;u=0)r=l.slice(y+1),l=l.slice(0,y);if(l&&!f.hasOwnProperty(l))throw Error("unknown type: "+l);return{type:l,name:r}})}s6.prototype=LW.prototype={constructor:s6,on:function(u,f){var l=this._,r=UH(u+"",l),y,i=-1,_=r.length;if(arguments.length<2){while(++i<_)if((y=(u=r[i]).type)&&(y=QH(l[y],u.name)))return y;return}if(f!=null&&typeof f!=="function")throw Error("invalid callback: "+f);while(++i<_)if(y=(u=r[i]).type)l[y]=KW(l[y],u.name,f);else if(f==null)for(y in l)l[y]=KW(l[y],u.name,null);return this},copy:function(){var u={},f=this._;for(var l in f)u[l]=f[l].slice();return new s6(u)},call:function(u,f){if((y=arguments.length-2)>0)for(var l=Array(y),r=0,y,i;r=0&&(f=u.slice(0,l))!=="xmlns")u=u.slice(l+1);return kA.hasOwnProperty(f)?{space:kA[f],local:u}:u}function gA(u){let f;while(f=u.sourceEvent)u=f;return u}function Wl(u,f){if(u=gA(u),f===void 0)f=u.currentTarget;if(f){var l=f.ownerSVGElement||f;if(l.createSVGPoint){var r=l.createSVGPoint();return r.x=u.clientX,r.y=u.clientY,r=r.matrixTransform(f.getScreenCTM().inverse()),[r.x,r.y]}if(f.getBoundingClientRect){var y=f.getBoundingClientRect();return[u.clientX-y.left-f.clientLeft,u.clientY-y.top-f.clientTop]}}return[u.pageX,u.pageY]}function qH(){}function z1(u){return u==null?qH:function(){return this.querySelector(u)}}function sA(u){if(typeof u!=="function")u=z1(u);for(var f=this._groups,l=f.length,r=Array(l),y=0;y=c)c=N+1;while(!(w=K[c])&&++c=0;)if(_=r[y]){if(i&&_.compareDocumentPosition(i)^4)i.parentNode.insertBefore(_,i);i=_}return this}function $9(u){if(!u)u=OH;function f(J,U){return J&&U?u(J.__data__,U.__data__):!J-!U}for(var l=this._groups,r=l.length,y=Array(r),i=0;if?1:u>=f?0:NaN}function A9(){var u=arguments[0];return arguments[0]=this,u.apply(null,arguments),this}function j9(){return Array.from(this)}function F9(){for(var u=this._groups,f=0,l=u.length;f1?this.each((f==null?SH:typeof f==="function"?MH:PH)(u,f,l==null?"":l)):G1(this.node(),u)}function G1(u,f){return u.style.getPropertyValue(f)||Mn(u).getComputedStyle(u,null).getPropertyValue(f)}function mH(u){return function(){delete this[u]}}function pH(u,f){return function(){this[u]=f}}function CH(u,f){return function(){var l=f.apply(this,arguments);if(l==null)delete this[u];else this[u]=l}}function c9(u,f){return arguments.length>1?this.each((f==null?mH:typeof f==="function"?CH:pH)(u,f)):this.node()[u]}function wW(u){return u.trim().split(/^|\s+/)}function N9(u){return u.classList||new EW(u)}function EW(u){this._node=u,this._names=wW(u.getAttribute("class")||"")}EW.prototype={add:function(u){var f=this._names.indexOf(u);if(f<0)this._names.push(u),this._node.setAttribute("class",this._names.join(" "))},remove:function(u){var f=this._names.indexOf(u);if(f>=0)this._names.splice(f,1),this._node.setAttribute("class",this._names.join(" "))},contains:function(u){return this._names.indexOf(u)>=0}};function TW(u,f){var l=N9(u),r=-1,y=f.length;while(++r=0)l=f.slice(r+1),f=f.slice(0,r);return{type:f,name:l}})}function _O(u){return function(){var f=this.__on;if(!f)return;for(var l=0,r=-1,y=f.length,i;l()=>u;function xn(u,{sourceEvent:f,subject:l,target:r,identifier:y,active:i,x:_,y:n,dx:$,dy:j,dispatch:F}){Object.defineProperties(this,{type:{value:u,enumerable:!0,configurable:!0},sourceEvent:{value:f,enumerable:!0,configurable:!0},subject:{value:l,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:y,enumerable:!0,configurable:!0},active:{value:i,enumerable:!0,configurable:!0},x:{value:_,enumerable:!0,configurable:!0},y:{value:n,enumerable:!0,configurable:!0},dx:{value:$,enumerable:!0,configurable:!0},dy:{value:j,enumerable:!0,configurable:!0},_:{value:F}})}xn.prototype.on=function(){var u=this._.on.apply(this._,arguments);return u===this._?this:u};function NO(u){return!u.ctrlKey&&!u.button}function zO(){return this.parentNode}function GO(u,f){return f==null?{x:u.x,y:u.y}:f}function KO(){return navigator.maxTouchPoints||"ontouchstart"in this}function Rn(){var u=NO,f=zO,l=GO,r=KO,y={},i=Uy("start","drag","end"),_=0,n,$,j,F,J=0;function U(z){z.on("mousedown.drag",q).filter(r).on("touchstart.drag",K).on("touchmove.drag",Q,BW).on("touchend.drag touchcancel.drag",N).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function q(z,w){if(F||!u.call(this,z,w))return;var H=c(this,f.call(this,z,w),z,w,"mouse");if(!H)return;If(z.view).on("mousemove.drag",W,Qy).on("mouseup.drag",G,Qy),Xi(z.view),d6(z),j=!1,n=z.clientX,$=z.clientY,H("start",z)}function W(z){if(V0(z),!j){var w=z.clientX-n,H=z.clientY-$;j=w*w+H*H>J}y.mouse("drag",z)}function G(z){If(z.view).on("mousemove.drag mouseup.drag",null),pn(z.view,j),V0(z),y.mouse("end",z)}function K(z,w){if(!u.call(this,z,w))return;var H=z.changedTouches,B=f.call(this,z,w),T=H.length,X,R;for(X=0;X>8&15|f>>4&240,f>>4&15|f&240,(f&15)<<4|f&15,1):l===8?e6(f>>24&255,f>>16&255,f>>8&255,(f&255)/255):l===4?e6(f>>12&15|f>>8&240,f>>8&15|f>>4&240,f>>4&15|f&240,((f&15)<<4|f&15)/255):null):(f=wO.exec(u))?new Cl(f[1],f[2],f[3],1):(f=EO.exec(u))?new Cl(f[1]*255/100,f[2]*255/100,f[3]*255/100,1):(f=TO.exec(u))?e6(f[1],f[2],f[3],f[4]):(f=ZO.exec(u))?e6(f[1]*255/100,f[2]*255/100,f[3]*255/100,f[4]):(f=HO.exec(u))?PW(f[1],f[2]/100,f[3]/100,1):(f=OO.exec(u))?PW(f[1],f[2]/100,f[3]/100,f[4]):VW.hasOwnProperty(u)?DW(VW[u]):u==="transparent"?new Cl(NaN,NaN,NaN,0):null}function DW(u){return new Cl(u>>16&255,u>>8&255,u&255,1)}function e6(u,f,l,r){if(r<=0)u=f=l=NaN;return new Cl(u,f,l,r)}function XO(u){if(!(u instanceof In))u=pr(u);if(!u)return new Cl;return u=u.rgb(),new Cl(u.r,u.g,u.b,u.opacity)}function Di(u,f,l,r){return arguments.length===1?XO(u):new Cl(u,f,l,r==null?1:r)}function Cl(u,f,l,r){this.r=+u,this.g=+f,this.b=+l,this.opacity=+r}hn(Cl,Di,D9(In,{brighter(u){return u=u==null?f4:Math.pow(f4,u),new Cl(this.r*u,this.g*u,this.b*u,this.opacity)},darker(u){return u=u==null?bn:Math.pow(bn,u),new Cl(this.r*u,this.g*u,this.b*u,this.opacity)},rgb(){return this},clamp(){return new Cl(Wy(this.r),Wy(this.g),Wy(this.b),l4(this.opacity))},displayable(){return-0.5<=this.r&&this.r<255.5&&(-0.5<=this.g&&this.g<255.5)&&(-0.5<=this.b&&this.b<255.5)&&(0<=this.opacity&&this.opacity<=1)},hex:tW,formatHex:tW,formatHex8:YO,formatRgb:SW,toString:SW}));function tW(){return`#${qy(this.r)}${qy(this.g)}${qy(this.b)}`}function YO(){return`#${qy(this.r)}${qy(this.g)}${qy(this.b)}${qy((isNaN(this.opacity)?1:this.opacity)*255)}`}function SW(){let u=l4(this.opacity);return`${u===1?"rgb(":"rgba("}${Wy(this.r)}, ${Wy(this.g)}, ${Wy(this.b)}${u===1?")":`, ${u})`}`}function l4(u){return isNaN(u)?1:Math.max(0,Math.min(1,u))}function Wy(u){return Math.max(0,Math.min(255,Math.round(u)||0))}function qy(u){return u=Wy(u),(u<16?"0":"")+u.toString(16)}function PW(u,f,l,r){if(r<=0)u=f=l=NaN;else if(l<=0||l>=1)u=f=NaN;else if(f<=0)u=NaN;return new mr(u,f,l,r)}function mW(u){if(u instanceof mr)return new mr(u.h,u.s,u.l,u.opacity);if(!(u instanceof In))u=pr(u);if(!u)return new mr;if(u instanceof mr)return u;u=u.rgb();var f=u.r/255,l=u.g/255,r=u.b/255,y=Math.min(f,l,r),i=Math.max(f,l,r),_=NaN,n=i-y,$=(i+y)/2;if(n){if(f===i)_=(l-r)/n+(l0&&$<1?0:_;return new mr(_,n,$,u.opacity)}function pW(u,f,l,r){return arguments.length===1?mW(u):new mr(u,f,l,r==null?1:r)}function mr(u,f,l,r){this.h=+u,this.s=+f,this.l=+l,this.opacity=+r}hn(mr,pW,D9(In,{brighter(u){return u=u==null?f4:Math.pow(f4,u),new mr(this.h,this.s,this.l*u,this.opacity)},darker(u){return u=u==null?bn:Math.pow(bn,u),new mr(this.h,this.s,this.l*u,this.opacity)},rgb(){var u=this.h%360+(this.h<0)*360,f=isNaN(u)||isNaN(this.s)?0:this.s,l=this.l,r=l+(l<0.5?l:1-l)*f,y=2*l-r;return new Cl(t9(u>=240?u-240:u+120,y,r),t9(u,y,r),t9(u<120?u+240:u-120,y,r),this.opacity)},clamp(){return new mr(MW(this.h),u4(this.s),u4(this.l),l4(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&(0<=this.l&&this.l<=1)&&(0<=this.opacity&&this.opacity<=1)},formatHsl(){let u=l4(this.opacity);return`${u===1?"hsl(":"hsla("}${MW(this.h)}, ${u4(this.s)*100}%, ${u4(this.l)*100}%${u===1?")":`, ${u})`}`}}));function MW(u){return u=(u||0)%360,u<0?u+360:u}function u4(u){return Math.max(0,Math.min(1,u||0))}function t9(u,f,l){return(u<60?f+(l-f)*u/60:u<180?l:u<240?f+(l-f)*(240-u)/60:f)*255}function S9(u,f,l,r,y){var i=u*u,_=i*u;return((1-3*u+3*i-_)*f+(4-6*i+3*_)*l+(1+3*u+3*i-3*_)*r+_*y)/6}function P9(u){var f=u.length-1;return function(l){var r=l<=0?l=0:l>=1?(l=1,f-1):Math.floor(l*f),y=u[r],i=u[r+1],_=r>0?u[r-1]:2*y-i,n=r()=>u;function tO(u,f){return function(l){return u+l*f}}function SO(u,f,l){return u=Math.pow(u,l),f=Math.pow(f,l)-u,l=1/l,function(r){return Math.pow(u+r*f,l)}}function CW(u){return(u=+u)===1?y4:function(f,l){return l-f?SO(f,l,u):kn(isNaN(f)?l:f)}}function y4(u,f){var l=f-u;return l?tO(u,l):kn(isNaN(u)?f:u)}var cy=function u(f){var l=CW(f);function r(y,i){var _=l((y=Di(y)).r,(i=Di(i)).r),n=l(y.g,i.g),$=l(y.b,i.b),j=y4(y.opacity,i.opacity);return function(F){return y.r=_(F),y.g=n(F),y.b=$(F),y.opacity=j(F),y+""}}return r.gamma=u,r}(1);function xW(u){return function(f){var l=f.length,r=Array(l),y=Array(l),i=Array(l),_,n;for(_=0;_l)if(i=f.slice(l,i),n[_])n[_]+=i;else n[++_]=i;if((r=r[0])===(y=y[0]))if(n[_])n[_]+=y;else n[++_]=y;else n[++_]=null,$.push({i:_,x:cl(r,y)});l=x9.lastIndex}if(l180)F+=360;else if(F-j>180)j+=360;U.push({i:J.push(y(J)+"rotate(",null,r)-2,x:cl(j,F)})}else if(F)J.push(y(J)+"rotate("+F+r)}function n(j,F,J,U){if(j!==F)U.push({i:J.push(y(J)+"skewX(",null,r)-2,x:cl(j,F)});else if(F)J.push(y(J)+"skewX("+F+r)}function $(j,F,J,U,q,W){if(j!==J||F!==U){var G=q.push(y(q)+"scale(",null,",",null,")");W.push({i:G-4,x:cl(j,J)},{i:G-2,x:cl(F,U)})}else if(J!==1||U!==1)q.push(y(q)+"scale("+J+","+U+")")}return function(j,F){var J=[],U=[];return j=u(j),F=u(F),i(j.translateX,j.translateY,F.translateX,F.translateY,J,U),_(j.rotate,F.rotate,J,U),n(j.skewX,F.skewX,J,U),$(j.scaleX,j.scaleY,F.scaleX,F.scaleY,J,U),j=F=null,function(q){var W=-1,G=U.length,K;while(++W=0)u._call.call(void 0,f);u=u._next}--Si}function dW(){zy=(A4=on.now())+j4,Si=sn=0;try{fc()}finally{Si=0,rB(),zy=0}}function lB(){var u=on.now(),f=u-A4;if(f>eW)j4-=f,A4=u}function rB(){var u,f=$4,l,r=1/0;while(f)if(f._call){if(r>f._time)r=f._time;u=f,f=f._next}else l=f._next,f._next=null,f=u?u._next=l:$4=l;an=u,v9(r)}function v9(u){if(Si)return;if(sn)sn=clearTimeout(sn);var f=u-zy;if(f>24){if(u<1/0)sn=setTimeout(dW,u-on.now()-j4);if(gn)gn=clearInterval(gn)}else{if(!gn)A4=on.now(),gn=setInterval(lB,eW);Si=1,uc(dW)}}function u$(u,f,l){var r=new dn;return f=f==null?0:+f,r.restart((y)=>{r.stop(),u(y+f)},f,l),r}var iB=Uy("start","end","cancel","interrupt"),_B=[],yc=0,lc=1,U4=2,J4=3,rc=4,Q4=5,f$=6;function X0(u,f,l,r,y,i){var _=u.__transition;if(!_)u.__transition={};else if(l in _)return;nB(u,l,{name:f,index:r,group:y,on:iB,tween:_B,time:i.time,delay:i.delay,duration:i.duration,ease:i.ease,timer:null,state:yc})}function l$(u,f){var l=kf(u,f);if(l.state>yc)throw Error("too late; already scheduled");return l}function $l(u,f){var l=kf(u,f);if(l.state>J4)throw Error("too late; already running");return l}function kf(u,f){var l=u.__transition;if(!l||!(l=l[f]))throw Error("transition not found");return l}function nB(u,f,l){var r=u.__transition,y;r[f]=l,l.timer=F4(i,0,l.time);function i(j){if(l.state=lc,l.timer.restart(_,l.delay,l.time),l.delay<=j)_(j-l.delay)}function _(j){var F,J,U,q;if(l.state!==lc)return $();for(F in r){if(q=r[F],q.name!==l.name)continue;if(q.state===J4)return u$(_);if(q.state===rc)q.state=f$,q.timer.stop(),q.on.call("interrupt",u,u.__data__,q.index,q.group),delete r[F];else if(+FU4&&r.state=0)f=f.slice(0,l);return!f||f==="start"})}function HB(u,f,l){var r,y,i=ZB(f)?l$:$l;return function(){var _=i(this,u),n=_.on;if(n!==r)(y=(r=n).copy()).on(f,l);_.on=y}}function l7(u,f){var l=this._id;return arguments.length<2?kf(this.node(),l).on.on(u):this.each(HB(l,u,f))}function OB(u){return function(){var f=this.parentNode;for(var l in this.__transition)if(+l!==u)return;if(f)f.removeChild(this)}}function r7(){return this.on("end.remove",OB(this._id))}function y7(u){var f=this._name,l=this._id;if(typeof u!=="function")u=z1(u);for(var r=this._groups,y=r.length,i=Array(y),_=0;_()=>u;function Q7(u,{sourceEvent:f,target:l,transform:r,dispatch:y}){Object.defineProperties(this,{type:{value:u,enumerable:!0,configurable:!0},sourceEvent:{value:f,enumerable:!0,configurable:!0},target:{value:l,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:y}})}function Cr(u,f,l){this.k=u,this.x=f,this.y=l}Cr.prototype={constructor:Cr,scale:function(u){return u===1?this:new Cr(this.k*u,this.x,this.y)},translate:function(u,f){return u===0&f===0?this:new Cr(this.k,this.x+this.k*u,this.y+this.k*f)},apply:function(u){return[u[0]*this.k+this.x,u[1]*this.k+this.y]},applyX:function(u){return u*this.k+this.x},applyY:function(u){return u*this.k+this.y},invert:function(u){return[(u[0]-this.x)/this.k,(u[1]-this.y)/this.k]},invertX:function(u){return(u-this.x)/this.k},invertY:function(u){return(u-this.y)/this.k},rescaleX:function(u){return u.copy().domain(u.range().map(this.invertX,this).map(u.invert,u))},rescaleY:function(u){return u.copy().domain(u.range().map(this.invertY,this).map(u.invert,u))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var Gy=new Cr(1,0,0);i$.prototype=Cr.prototype;function i$(u){while(!u.__zoom)if(!(u=u.parentNode))return Gy;return u.__zoom}function T4(u){u.stopImmediatePropagation()}function Ky(u){u.preventDefault(),u.stopImmediatePropagation()}function bB(u){return(!u.ctrlKey||u.type==="wheel")&&!u.button}function vB(){var u=this;if(u instanceof SVGElement){if(u=u.ownerSVGElement||u,u.hasAttribute("viewBox"))return u=u.viewBox.baseVal,[[u.x,u.y],[u.x+u.width,u.y+u.height]];return[[0,0],[u.width.baseVal.value,u.height.baseVal.value]]}return[[0,0],[u.clientWidth,u.clientHeight]]}function nc(){return this.__zoom||Gy}function IB(u){return-u.deltaY*(u.deltaMode===1?0.05:u.deltaMode?1:0.002)*(u.ctrlKey?10:1)}function kB(){return navigator.maxTouchPoints||"ontouchstart"in this}function gB(u,f,l){var r=u.invertX(f[0][0])-l[0][0],y=u.invertX(f[1][0])-l[1][0],i=u.invertY(f[0][1])-l[0][1],_=u.invertY(f[1][1])-l[1][1];return u.translate(y>r?(r+y)/2:Math.min(0,r)||Math.max(0,y),_>i?(i+_)/2:Math.min(0,i)||Math.max(0,_))}function _$(){var u=bB,f=vB,l=gB,r=IB,y=kB,i=[0,1/0],_=[[-1/0,-1/0],[1/0,1/0]],n=250,$=Ny,j=Uy("start","zoom","end"),F,J,U,q=500,W=150,G=0,K=10;function Q(O){O.property("__zoom",nc).on("wheel.zoom",T,{passive:!1}).on("mousedown.zoom",X).on("dblclick.zoom",R).filter(y).on("touchstart.zoom",Y).on("touchmove.zoom",P).on("touchend.zoom touchcancel.zoom",t).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}Q.transform=function(O,M,S,b){var Z=O.selection?O.selection():O;if(Z.property("__zoom",nc),O!==Z)w(O,M,S,b);else Z.interrupt().each(function(){H(this,arguments).event(b).start().zoom(null,typeof M==="function"?M.apply(this,arguments):M).end()})},Q.scaleBy=function(O,M,S,b){Q.scaleTo(O,function(){var Z=this.__zoom.k,D=typeof M==="function"?M.apply(this,arguments):M;return Z*D},S,b)},Q.scaleTo=function(O,M,S,b){Q.transform(O,function(){var Z=f.apply(this,arguments),D=this.__zoom,I=S==null?z(Z):typeof S==="function"?S.apply(this,arguments):S,k=D.invert(I),h=typeof M==="function"?M.apply(this,arguments):M;return l(c(N(D,h),I,k),Z,_)},S,b)},Q.translateBy=function(O,M,S,b){Q.transform(O,function(){return l(this.__zoom.translate(typeof M==="function"?M.apply(this,arguments):M,typeof S==="function"?S.apply(this,arguments):S),f.apply(this,arguments),_)},null,b)},Q.translateTo=function(O,M,S,b,Z){Q.transform(O,function(){var D=f.apply(this,arguments),I=this.__zoom,k=b==null?z(D):typeof b==="function"?b.apply(this,arguments):b;return l(Gy.translate(k[0],k[1]).scale(I.k).translate(typeof M==="function"?-M.apply(this,arguments):-M,typeof S==="function"?-S.apply(this,arguments):-S),D,_)},b,Z)};function N(O,M){return M=Math.max(i[0],Math.min(i[1],M)),M===O.k?O:new Cr(M,O.x,O.y)}function c(O,M,S){var b=M[0]-S[0]*O.k,Z=M[1]-S[1]*O.k;return b===O.x&&Z===O.y?O:new Cr(O.k,b,Z)}function z(O){return[(+O[0][0]+ +O[1][0])/2,(+O[0][1]+ +O[1][1])/2]}function w(O,M,S,b){O.on("start.zoom",function(){H(this,arguments).event(b).start()}).on("interrupt.zoom end.zoom",function(){H(this,arguments).event(b).end()}).tween("zoom",function(){var Z=this,D=arguments,I=H(Z,D).event(b),k=f.apply(Z,D),h=S==null?z(k):typeof S==="function"?S.apply(Z,D):S,o=Math.max(k[1][0]-k[0][0],k[1][1]-k[0][1]),s=Z.__zoom,x=typeof M==="function"?M.apply(Z,D):M,uu=$(s.invert(h).concat(o/s.k),x.invert(h).concat(o/x.k));return function(nu){if(nu===1)nu=x;else{var $u=uu(nu),Fu=o/$u[2];nu=new Cr(Fu,h[0]-$u[0]*Fu,h[1]-$u[1]*Fu)}I.zoom(null,nu)}})}function H(O,M,S){return!S&&O.__zooming||new B(O,M)}function B(O,M){this.that=O,this.args=M,this.active=0,this.sourceEvent=null,this.extent=f.apply(O,M),this.taps=0}B.prototype={event:function(O){if(O)this.sourceEvent=O;return this},start:function(){if(++this.active===1)this.that.__zooming=this,this.emit("start");return this},zoom:function(O,M){if(this.mouse&&O!=="mouse")this.mouse[1]=M.invert(this.mouse[0]);if(this.touch0&&O!=="touch")this.touch0[1]=M.invert(this.touch0[0]);if(this.touch1&&O!=="touch")this.touch1[1]=M.invert(this.touch1[0]);return this.that.__zoom=M,this.emit("zoom"),this},end:function(){if(--this.active===0)delete this.that.__zooming,this.emit("end");return this},emit:function(O){var M=If(this.that).datum();j.call(O,this.that,new Q7(O,{sourceEvent:this.sourceEvent,target:Q,type:O,transform:this.that.__zoom,dispatch:j}),M)}};function T(O,...M){if(!u.apply(this,arguments))return;var S=H(this,M).event(O),b=this.__zoom,Z=Math.max(i[0],Math.min(i[1],b.k*Math.pow(2,r.apply(this,arguments)))),D=Wl(O);if(S.wheel){if(S.mouse[0][0]!==D[0]||S.mouse[0][1]!==D[1])S.mouse[1]=b.invert(S.mouse[0]=D);clearTimeout(S.wheel)}else if(b.k===Z)return;else S.mouse=[D,b.invert(D)],K1(this),S.start();Ky(O),S.wheel=setTimeout(I,W),S.zoom("mouse",l(c(N(b,Z),S.mouse[0],S.mouse[1]),S.extent,_));function I(){S.wheel=null,S.end()}}function X(O,...M){if(U||!u.apply(this,arguments))return;var S=O.currentTarget,b=H(this,M,!0).event(O),Z=If(O.view).on("mousemove.zoom",h,!0).on("mouseup.zoom",o,!0),D=Wl(O,S),I=O.clientX,k=O.clientY;Xi(O.view),T4(O),b.mouse=[D,this.__zoom.invert(D)],K1(this),b.start();function h(s){if(Ky(s),!b.moved){var x=s.clientX-I,uu=s.clientY-k;b.moved=x*x+uu*uu>G}b.event(s).zoom("mouse",l(c(b.that.__zoom,b.mouse[0]=Wl(s,S),b.mouse[1]),b.extent,_))}function o(s){Z.on("mousemove.zoom mouseup.zoom",null),pn(s.view,b.moved),Ky(s),b.event(s).end()}}function R(O,...M){if(!u.apply(this,arguments))return;var S=this.__zoom,b=Wl(O.changedTouches?O.changedTouches[0]:O,this),Z=S.invert(b),D=S.k*(O.shiftKey?0.5:2),I=l(c(N(S,D),b,Z),f.apply(this,M),_);if(Ky(O),n>0)If(this).transition().duration(n).call(w,I,b,O);else If(this).call(Q.transform,I,b,O)}function Y(O,...M){if(!u.apply(this,arguments))return;var S=O.touches,b=S.length,Z=H(this,M,O.changedTouches.length===b).event(O),D,I,k,h;T4(O);for(I=0;I"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:(u)=>`Node type "${u}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:(u)=>`The old edge with id=${u} does not exist.`,error009:(u)=>`Marker type "${u}" doesn't exist.`,error008:(u,{id:f,sourceHandle:l,targetHandle:r})=>`Couldn't create edge for ${u} handle id: "${u==="source"?l:r}", edge id: ${f}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:(u)=>`Edge type "${u}" not found. Using fallback type "default".`,error012:(u)=>`Node with id "${u}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(u="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${u}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},xi=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],z7=["Enter"," ","Escape"],G7={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:u,x:f,y:l})=>`Moved selected node ${u}. New position, x: ${f}, y: ${l}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"},E1;(function(u){u.Strict="strict",u.Loose="loose"})(E1||(E1={}));var D0;(function(u){u.Free="free",u.Vertical="vertical",u.Horizontal="horizontal"})(D0||(D0={}));var Ly;(function(u){u.Partial="partial",u.Full="full"})(Ly||(Ly={}));var K7={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null},y0;(function(u){u.Bezier="default",u.Straight="straight",u.Step="step",u.SmoothStep="smoothstep",u.SimpleBezier="simplebezier"})(y0||(y0={}));var T1;(function(u){u.Arrow="arrow",u.ArrowClosed="arrowclosed"})(T1||(T1={}));var zu;(function(u){u.Left="left",u.Top="top",u.Right="right",u.Bottom="bottom"})(zu||(zu={}));var $c={[zu.Left]:zu.Right,[zu.Right]:zu.Left,[zu.Top]:zu.Bottom,[zu.Bottom]:zu.Top};function L7(u){return u===null?null:u?"valid":"invalid"}var w7=(u)=>("id"in u)&&("source"in u)&&("target"in u),Kc=(u)=>("id"in u)&&("position"in u)&&!("source"in u)&&!("target"in u),E7=(u)=>("id"in u)&&("internals"in u)&&!("source"in u)&&!("target"in u);var A$=(u,f=[0,0])=>{let{width:l,height:r}=i0(u),y=u.origin??f,i=l*y[0],_=r*y[1];return{x:u.position.x-i,y:u.position.y-_}},T7=(u,f={nodeOrigin:[0,0]})=>{if(u.length===0)return{x:0,y:0,width:0,height:0};let l=u.reduce((r,y)=>{let i=typeof y==="string",_=!f.nodeLookup&&!i?y:void 0;if(f.nodeLookup)_=i?f.nodeLookup.get(y):!E7(y)?f.nodeLookup.get(y.id):y;let n=_?O4(_,f.nodeOrigin):{x:0,y:0,x2:0,y2:0};return V4(r,n)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return X4(l)},Ri=(u,f={})=>{let l={x:1/0,y:1/0,x2:-1/0,y2:-1/0},r=!1;return u.forEach((y)=>{if(f.filter===void 0||f.filter(y))l=V4(l,O4(y)),r=!0}),r?X4(l):{x:0,y:0,width:0,height:0}},B4=(u,f,[l,r,y]=[0,0,1],i=!1,_=!1)=>{let n={...vi(f,[l,r,y]),width:f.width/y,height:f.height/y},$=[];for(let j of u.values()){let{measured:F,selectable:J=!0,hidden:U=!1}=j;if(_&&!J||U)continue;let q=F.width??j.width??j.initialWidth??null,W=F.height??j.height??j.initialHeight??null,G=hi(n,Ey(j)),K=(q??0)*(W??0),Q=i&&G>0;if(!j.internals.handleBounds||Q||G>=K||j.dragging)$.push(j)}return $},Lc=(u,f)=>{let l=new Set;return u.forEach((r)=>{l.add(r.id)}),f.filter((r)=>l.has(r.source)||l.has(r.target))};function sB(u,f){let l=new Map,r=f?.nodes?new Set(f.nodes.map((y)=>y.id)):null;return u.forEach((y)=>{if(y.measured.width&&y.measured.height&&(f?.includeHiddenNodes||!y.hidden)&&(!r||r.has(y.id)))l.set(y.id,y)}),l}async function wc({nodes:u,width:f,height:l,panZoom:r,minZoom:y,maxZoom:i},_){if(u.size===0)return Promise.resolve(!0);let n=sB(u,_),$=Ri(n),j=j$($,f,l,_?.minZoom??y,_?.maxZoom??i,_?.padding??0.1);return await r.setViewport(j,{duration:_?.duration,ease:_?.ease,interpolate:_?.interpolate}),Promise.resolve(!0)}function Z7({nodeId:u,nextPosition:f,nodeLookup:l,nodeOrigin:r=[0,0],nodeExtent:y,onError:i}){let _=l.get(u),n=_.parentId?l.get(_.parentId):void 0,{x:$,y:j}=n?n.internals.positionAbsolute:{x:0,y:0},F=_.origin??r,J=_.extent||y;if(_.extent==="parent"&&!_.expandParent)if(!n)i?.("005",_r.error005());else{let q=n.measured.width,W=n.measured.height;if(q&&W)J=[[$,j],[$+q,j+W]]}else if(n&&Ci(_.extent))J=[[_.extent[0][0]+$,_.extent[0][1]+j],[_.extent[1][0]+$,_.extent[1][1]+j]];let U=Ci(J)?wy(f,J,_.measured):f;if(_.measured.width===void 0||_.measured.height===void 0)i?.("015",_r.error015());return{position:{x:U.x-$+(_.measured.width??0)*F[0],y:U.y-j+(_.measured.height??0)*F[1]},positionAbsolute:U}}async function Ec({nodesToRemove:u=[],edgesToRemove:f=[],nodes:l,edges:r,onBeforeDelete:y}){let i=new Set(u.map((U)=>U.id)),_=[];for(let U of l){if(U.deletable===!1)continue;let q=i.has(U.id),W=!q&&U.parentId&&_.find((G)=>G.id===U.parentId);if(q||W)_.push(U)}let n=new Set(f.map((U)=>U.id)),$=r.filter((U)=>U.deletable!==!1),F=Lc(_,$);for(let U of $)if(n.has(U.id)&&!F.find((W)=>W.id===U.id))F.push(U);if(!y)return{edges:F,nodes:_};let J=await y({nodes:_,edges:F});if(typeof J==="boolean")return J?{edges:F,nodes:_}:{edges:[],nodes:[]};return J}var pi=(u,f=0,l=1)=>Math.min(Math.max(u,f),l),wy=(u={x:0,y:0},f,l)=>({x:pi(u.x,f[0][0],f[1][0]-(l?.width??0)),y:pi(u.y,f[0][1],f[1][1]-(l?.height??0))});function Tc(u,f,l){let{width:r,height:y}=i0(l),{x:i,y:_}=l.internals.positionAbsolute;return wy(u,[[i,_],[i+r,_+y]],f)}var Ac=(u,f,l)=>{if(ul)return-pi(Math.abs(u-l),1,f)/f;return 0},Zc=(u,f,l=15,r=40)=>{let y=Ac(u.x,r,f.width-r)*l,i=Ac(u.y,r,f.height-r)*l;return[y,i]},V4=(u,f)=>({x:Math.min(u.x,f.x),y:Math.min(u.y,f.y),x2:Math.max(u.x2,f.x2),y2:Math.max(u.y2,f.y2)}),N7=({x:u,y:f,width:l,height:r})=>({x:u,y:f,x2:u+l,y2:f+r}),X4=({x:u,y:f,x2:l,y2:r})=>({x:u,y:f,width:l-u,height:r-f}),Ey=(u,f=[0,0])=>{let{x:l,y:r}=E7(u)?u.internals.positionAbsolute:A$(u,f);return{x:l,y:r,width:u.measured?.width??u.width??u.initialWidth??0,height:u.measured?.height??u.height??u.initialHeight??0}},O4=(u,f=[0,0])=>{let{x:l,y:r}=E7(u)?u.internals.positionAbsolute:A$(u,f);return{x:l,y:r,x2:l+(u.measured?.width??u.width??u.initialWidth??0),y2:r+(u.measured?.height??u.height??u.initialHeight??0)}},H7=(u,f)=>X4(V4(N7(u),N7(f))),hi=(u,f)=>{let l=Math.max(0,Math.min(u.x+u.width,f.x+f.width)-Math.max(u.x,f.x)),r=Math.max(0,Math.min(u.y+u.height,f.y+f.height)-Math.max(u.y,f.y));return Math.ceil(l*r)},O7=(u)=>Kr(u.width)&&Kr(u.height)&&Kr(u.x)&&Kr(u.y),Kr=(u)=>!isNaN(u)&&isFinite(u),B7=(u,f)=>{},bi=(u,f=[1,1])=>{return{x:f[0]*Math.round(u.x/f[0]),y:f[1]*Math.round(u.y/f[1])}},vi=({x:u,y:f},[l,r,y],i=!1,_=[1,1])=>{let n={x:(u-l)/y,y:(f-r)/y};return i?bi(n,_):n},$$=({x:u,y:f},[l,r,y])=>{return{x:u*y+l,y:f*y+r}};function Mi(u,f){if(typeof u==="number")return Math.floor((f-f/(1+u))*0.5);if(typeof u==="string"&&u.endsWith("px")){let l=parseFloat(u);if(!Number.isNaN(l))return Math.floor(l)}if(typeof u==="string"&&u.endsWith("%")){let l=parseFloat(u);if(!Number.isNaN(l))return Math.floor(f*l*0.01)}return console.error(`[React Flow] The padding value "${u}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function aB(u,f,l){if(typeof u==="string"||typeof u==="number"){let r=Mi(u,l),y=Mi(u,f);return{top:r,right:y,bottom:r,left:y,x:y*2,y:r*2}}if(typeof u==="object"){let r=Mi(u.top??u.y??0,l),y=Mi(u.bottom??u.y??0,l),i=Mi(u.left??u.x??0,f),_=Mi(u.right??u.x??0,f);return{top:r,right:_,bottom:y,left:i,x:i+_,y:r+y}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function oB(u,f,l,r,y,i){let{x:_,y:n}=$$(u,[f,l,r]),{x:$,y:j}=$$({x:u.x+u.width,y:u.y+u.height},[f,l,r]),F=y-$,J=i-j;return{left:Math.floor(_),top:Math.floor(n),right:Math.floor(F),bottom:Math.floor(J)}}var j$=(u,f,l,r,y,i)=>{let _=aB(i,f,l),n=(f-_.x)/u.width,$=(l-_.y)/u.height,j=Math.min(n,$),F=pi(j,r,y),J=u.x+u.width/2,U=u.y+u.height/2,q=f/2-J*F,W=l/2-U*F,G=oB(u,q,W,F,f,l),K={left:Math.min(G.left-_.left,0),top:Math.min(G.top-_.top,0),right:Math.min(G.right-_.right,0),bottom:Math.min(G.bottom-_.bottom,0)};return{x:q-K.left+K.right,y:W-K.top+K.bottom,zoom:F}},Ii=()=>typeof navigator<"u"&&navigator?.userAgent?.indexOf("Mac")>=0;function Ci(u){return u!==void 0&&u!==null&&u!=="parent"}function i0(u){return{width:u.measured?.width??u.width??u.initialWidth??0,height:u.measured?.height??u.height??u.initialHeight??0}}function V7(u){return(u.measured?.width??u.width??u.initialWidth)!==void 0&&(u.measured?.height??u.height??u.initialHeight)!==void 0}function X7(u,f={width:0,height:0},l,r,y){let i={...u},_=r.get(l);if(_){let n=_.origin||y;i.x+=_.internals.positionAbsolute.x-(f.width??0)*n[0],i.y+=_.internals.positionAbsolute.y-(f.height??0)*n[1]}return i}function Y7(u,f){if(u.size!==f.size)return!1;for(let l of u)if(!f.has(l))return!1;return!0}function Hc(){let u,f;return{promise:new Promise((r,y)=>{u=r,f=y}),resolve:u,reject:f}}function Oc(u){return{...G7,...u||{}}}function n$(u,{snapGrid:f=[0,0],snapToGrid:l=!1,transform:r,containerBounds:y}){let{x:i,y:_}=Lr(u),n=vi({x:i-(y?.left??0),y:_-(y?.top??0)},r),{x:$,y:j}=l?bi(n,f):n;return{xSnapped:$,ySnapped:j,...n}}var Y4=(u)=>({width:u.offsetWidth,height:u.offsetHeight}),D7=(u)=>u?.getRootNode?.()||window?.document,dB=["INPUT","SELECT","TEXTAREA"];function t7(u){let f=u.composedPath?.()?.[0]||u.target;if(f?.nodeType!==1)return!1;return dB.includes(f.nodeName)||f.hasAttribute("contenteditable")||!!f.closest(".nokey")}var S7=(u)=>("clientX"in u),Lr=(u,f)=>{let l=S7(u),r=l?u.clientX:u.touches?.[0].clientX,y=l?u.clientY:u.touches?.[0].clientY;return{x:r-(f?.left??0),y:y-(f?.top??0)}},jc=(u,f,l,r,y)=>{let i=f.querySelectorAll(`.${u}`);if(!i||!i.length)return null;return Array.from(i).map((_)=>{let n=_.getBoundingClientRect();return{id:_.getAttribute("data-handleid"),type:u,nodeId:y,position:_.getAttribute("data-handlepos"),x:(n.left-l.left)/r,y:(n.top-l.top)/r,...Y4(_)}})};function D4({sourceX:u,sourceY:f,targetX:l,targetY:r,sourceControlX:y,sourceControlY:i,targetControlX:_,targetControlY:n}){let $=u*0.125+y*0.375+_*0.375+l*0.125,j=f*0.125+i*0.375+n*0.375+r*0.125,F=Math.abs($-u),J=Math.abs(j-f);return[$,j,F,J]}function Z4(u,f){if(u>=0)return 0.5*u;return f*25*Math.sqrt(-u)}function Fc({pos:u,x1:f,y1:l,x2:r,y2:y,c:i}){switch(u){case zu.Left:return[f-Z4(f-r,i),l];case zu.Right:return[f+Z4(r-f,i),l];case zu.Top:return[f,l-Z4(l-y,i)];case zu.Bottom:return[f,l+Z4(y-l,i)]}}function t4({sourceX:u,sourceY:f,sourcePosition:l=zu.Bottom,targetX:r,targetY:y,targetPosition:i=zu.Top,curvature:_=0.25}){let[n,$]=Fc({pos:l,x1:u,y1:f,x2:r,y2:y,c:_}),[j,F]=Fc({pos:i,x1:r,y1:y,x2:u,y2:f,c:_}),[J,U,q,W]=D4({sourceX:u,sourceY:f,targetX:r,targetY:y,sourceControlX:n,sourceControlY:$,targetControlX:j,targetControlY:F});return[`M${u},${f} C${n},${$} ${j},${F} ${r},${y}`,J,U,q,W]}function P7({sourceX:u,sourceY:f,targetX:l,targetY:r}){let y=Math.abs(l-u)/2,i=l0}var eB=({source:u,sourceHandle:f,target:l,targetHandle:r})=>`xy-edge__${u}${f||""}-${l}${r||""}`,uV=(u,f)=>{return f.some((l)=>l.source===u.source&&l.target===u.target&&(l.sourceHandle===u.sourceHandle||!l.sourceHandle&&!u.sourceHandle)&&(l.targetHandle===u.targetHandle||!l.targetHandle&&!u.targetHandle))},M7=(u,f,l={})=>{if(!u.source||!u.target)return B7("006",_r.error006()),f;let r=l.getEdgeId||eB,y;if(w7(u))y={...u};else y={...u,id:r(u)};if(uV(y,f))return f;if(y.sourceHandle===null)delete y.sourceHandle;if(y.targetHandle===null)delete y.targetHandle;return f.concat(y)};function S4({sourceX:u,sourceY:f,targetX:l,targetY:r}){let[y,i,_,n]=P7({sourceX:u,sourceY:f,targetX:l,targetY:r});return[`M ${u},${f}L ${l},${r}`,y,i,_,n]}var Jc={[zu.Left]:{x:-1,y:0},[zu.Right]:{x:1,y:0},[zu.Top]:{x:0,y:-1},[zu.Bottom]:{x:0,y:1}},fV=({source:u,sourcePosition:f=zu.Bottom,target:l})=>{if(f===zu.Left||f===zu.Right)return u.xMath.sqrt(Math.pow(f.x-u.x,2)+Math.pow(f.y-u.y,2));function lV({source:u,sourcePosition:f=zu.Bottom,target:l,targetPosition:r=zu.Top,center:y,offset:i,stepPosition:_}){let n=Jc[f],$=Jc[r],j={x:u.x+n.x*i,y:u.y+n.y*i},F={x:l.x+$.x*i,y:l.y+$.y*i},J=fV({source:j,sourcePosition:f,target:F}),U=J.x!==0?"x":"y",q=J[U],W=[],G,K,Q={x:0,y:0},N={x:0,y:0},[,,c,z]=P7({sourceX:u.x,sourceY:u.y,targetX:l.x,targetY:l.y});if(n[U]*$[U]===-1){if(U==="x")G=y.x??j.x+(F.x-j.x)*_,K=y.y??(j.y+F.y)/2;else G=y.x??(j.x+F.x)/2,K=y.y??j.y+(F.y-j.y)*_;let T=[{x:G,y:j.y},{x:G,y:F.y}],X=[{x:j.x,y:K},{x:F.x,y:K}];if(n[U]===q)W=U==="x"?T:X;else W=U==="x"?X:T}else{let T=[{x:j.x,y:F.y}],X=[{x:F.x,y:j.y}];if(U==="x")W=n.x===q?X:T;else W=n.y===q?T:X;if(f===r){let O=Math.abs(u[U]-l[U]);if(O<=i){let M=Math.min(i-1,i-O);if(n[U]===q)Q[U]=(j[U]>u[U]?-1:1)*M;else N[U]=(F[U]>l[U]?-1:1)*M}}if(f!==r){let O=U==="x"?"y":"x",M=n[U]===$[O],S=j[O]>F[O],b=j[O]=t)G=(R.x+Y.x)/2,K=W[0].y;else G=W[0].x,K=(R.y+Y.y)/2}let w={x:j.x+Q.x,y:j.y+Q.y},H={x:F.x+N.x,y:F.y+N.y};return[[u,...w.x!==W[0].x||w.y!==W[0].y?[w]:[],...W,...H.x!==W[W.length-1].x||H.y!==W[W.length-1].y?[H]:[],l],G,K,c,z]}function rV(u,f,l,r){let y=Math.min(Uc(u,f)/2,Uc(f,l)/2,r),{x:i,y:_}=f;if(u.x===i&&i===l.x||u.y===_&&_===l.y)return`L${i} ${_}`;if(u.y===_){let j=u.xl.id===f))||null}function P4(u,f){if(!u)return"";if(typeof u==="string")return u;return`${f?`${f}__`:""}${Object.keys(u).sort().map((r)=>`${r}=${u[r]}`).join("&")}`}function Yc(u,{id:f,defaultColor:l,defaultMarkerStart:r,defaultMarkerEnd:y}){let i=new Set;return u.reduce((_,n)=>{return[n.markerStart||r,n.markerEnd||y].forEach(($)=>{if($&&typeof $==="object"){let j=P4($,f);if(!i.has(j))_.push({id:j,color:$.color||l,...$}),i.add(j)}}),_},[]).sort((_,n)=>_.id.localeCompare(n.id))}var Dc=1000,yV=10,m7={nodeOrigin:[0,0],nodeExtent:xi,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},iV={...m7,checkEquality:!0};function p7(u,f){let l={...u};for(let r in f)if(f[r]!==void 0)l[r]=f[r];return l}function tc(u,f,l){let r=p7(m7,l);for(let y of u.values())if(y.parentId)x7(y,u,f,r);else{let i=A$(y,r.nodeOrigin),_=Ci(y.extent)?y.extent:r.nodeExtent,n=wy(i,_,i0(y));y.internals.positionAbsolute=n}}function _V(u,f){if(!u.handles)return!u.measured?void 0:f?.internals.handleBounds;let l=[],r=[];for(let y of u.handles){let i={id:y.id,width:y.width??1,height:y.height??1,nodeId:u.id,x:y.x,y:y.y,position:y.position,type:y.type};if(y.type==="source")l.push(i);else if(y.type==="target")r.push(i)}return{source:l,target:r}}function C7(u){return u==="manual"}function M4(u,f,l,r={}){let y=p7(iV,r),i={i:0},_=new Map(f),n=y?.elevateNodesOnSelect&&!C7(y.zIndexMode)?Dc:0,$=u.length>0,j=!1;f.clear(),l.clear();for(let F of u){let J=_.get(F.id);if(y.checkEquality&&F===J?.internals.userNode)f.set(F.id,J);else{let U=A$(F,y.nodeOrigin),q=Ci(F.extent)?F.extent:y.nodeExtent,W=wy(U,q,i0(F));J={...y.defaults,...F,measured:{width:F.measured?.width,height:F.measured?.height},internals:{positionAbsolute:W,handleBounds:_V(F,J),z:Sc(F,n,y.zIndexMode),userNode:F}},f.set(F.id,J)}if((J.measured===void 0||J.measured.width===void 0||J.measured.height===void 0)&&!J.hidden)$=!1;if(F.parentId)x7(J,f,l,r,i);j||=F.selected??!1}return{nodesInitialized:$,hasSelectedNodes:j}}function nV(u,f){if(!u.parentId)return;let l=f.get(u.parentId);if(l)l.set(u.id,u);else f.set(u.parentId,new Map([[u.id,u]]))}function x7(u,f,l,r,y){let{elevateNodesOnSelect:i,nodeOrigin:_,nodeExtent:n,zIndexMode:$}=p7(m7,r),j=u.parentId,F=f.get(j);if(!F){console.warn(`Parent node ${j} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}if(nV(u,l),y&&!F.parentId&&F.internals.rootParentIndex===void 0&&$==="auto")F.internals.rootParentIndex=++y.i,F.internals.z=F.internals.z+y.i*yV;if(y&&F.internals.rootParentIndex!==void 0)y.i=F.internals.rootParentIndex;let J=i&&!C7($)?Dc:0,{x:U,y:q,z:W}=$V(u,F,_,n,J,$),{positionAbsolute:G}=u.internals,K=U!==G.x||q!==G.y;if(K||W!==u.internals.z)f.set(u.id,{...u,internals:{...u.internals,positionAbsolute:K?{x:U,y:q}:G,z:W}})}function Sc(u,f,l){let r=Kr(u.zIndex)?u.zIndex:0;if(C7(l))return r;return r+(u.selected?f:0)}function $V(u,f,l,r,y,i){let{x:_,y:n}=f.internals.positionAbsolute,$=i0(u),j=A$(u,l),F=Ci(u.extent)?wy(j,u.extent,$):j,J=wy({x:_+F.x,y:n+F.y},r,$);if(u.extent==="parent")J=Tc(J,$,f);let U=Sc(u,y,i),q=f.internals.z??0;return{x:J.x,y:J.y,z:q>=U?q+1:U}}function m4(u,f,l,r=[0,0]){let y=[],i=new Map;for(let _ of u){let n=f.get(_.parentId);if(!n)continue;let $=i.get(_.parentId)?.expandedRect??Ey(n),j=H7($,_.rect);i.set(_.parentId,{expandedRect:j,parent:n})}if(i.size>0)i.forEach(({expandedRect:_,parent:n},$)=>{let j=n.internals.positionAbsolute,F=i0(n),J=n.origin??r,U=_.x0||q>0||K||Q)y.push({id:$,type:"position",position:{x:n.position.x-U+K,y:n.position.y-q+Q}}),l.get($)?.forEach((N)=>{if(!u.some((c)=>c.id===N.id))y.push({id:N.id,type:"position",position:{x:N.position.x+U,y:N.position.y+q}})});if(F.width<_.width||F.height<_.height||U||q)y.push({id:$,type:"dimensions",setAttributes:!0,dimensions:{width:W+(U?J[0]*U-K:0),height:G+(q?J[1]*q-Q:0)}})});return y}function Pc(u,f,l,r,y,i,_){let n=r?.querySelector(".xyflow__viewport"),$=!1;if(!n)return{changes:[],updatedInternals:$};let j=[],F=window.getComputedStyle(n),{m22:J}=new window.DOMMatrixReadOnly(F.transform),U=[];for(let q of u.values()){let W=f.get(q.id);if(!W)continue;if(W.hidden){f.set(W.id,{...W,internals:{...W.internals,handleBounds:void 0}}),$=!0;continue}let G=Y4(q.nodeElement),K=W.measured.width!==G.width||W.measured.height!==G.height;if(!!(G.width&&G.height&&(K||!W.internals.handleBounds||q.force))){let N=q.nodeElement.getBoundingClientRect(),c=Ci(W.extent)?W.extent:i,{positionAbsolute:z}=W.internals;if(W.parentId&&W.extent==="parent")z=Tc(z,G,f.get(W.parentId));else if(c)z=wy(z,c,G);let w={...W,measured:G,internals:{...W.internals,positionAbsolute:z,handleBounds:{source:jc("source",q.nodeElement,N,J,W.id),target:jc("target",q.nodeElement,N,J,W.id)}}};if(f.set(W.id,w),W.parentId)x7(w,f,l,{nodeOrigin:y,zIndexMode:_});if($=!0,K){if(j.push({id:W.id,type:"dimensions",dimensions:G}),W.expandParent&&W.parentId)U.push({id:W.id,parentId:W.parentId,rect:Ey(w,y)})}}}if(U.length>0){let q=m4(U,f,l,y);j.push(...q)}return{changes:j,updatedInternals:$}}async function Mc({delta:u,panZoom:f,transform:l,translateExtent:r,width:y,height:i}){if(!f||!u.x&&!u.y)return Promise.resolve(!1);let _=await f.setViewportConstrained({x:l[0]+u.x,y:l[1]+u.y,zoom:l[2]},[[0,0],[y,i]],r),n=!!_&&(_.x!==l[0]||_.y!==l[1]||_.k!==l[2]);return Promise.resolve(n)}function cc(u,f,l,r,y,i){let _=y,n=r.get(_)||new Map;r.set(_,n.set(l,f)),_=`${y}-${u}`;let $=r.get(_)||new Map;if(r.set(_,$.set(l,f)),i){_=`${y}-${u}-${i}`;let j=r.get(_)||new Map;r.set(_,j.set(l,f))}}function R7(u,f,l){u.clear(),f.clear();for(let r of l){let{source:y,target:i,sourceHandle:_=null,targetHandle:n=null}=r,$={edgeId:r.id,source:y,target:i,sourceHandle:_,targetHandle:n},j=`${y}-${_}--${i}-${n}`,F=`${i}-${n}--${y}-${_}`;cc("source",$,F,u,y,_),cc("target",$,j,u,i,n),f.set(r.id,r)}}function mc(u,f){if(!u.parentId)return!1;let l=f.get(u.parentId);if(!l)return!1;if(l.selected)return!0;return mc(l,f)}function Nc(u,f,l){let r=u;do{if(r?.matches?.(f))return!0;if(r===l)return!1;r=r?.parentElement}while(r);return!1}function AV(u,f,l,r){let y=new Map;for(let[i,_]of u)if((_.selected||_.id===r)&&(!_.parentId||!mc(_,u))&&(_.draggable||f&&typeof _.draggable>"u")){let n=u.get(i);if(n)y.set(i,{id:i,position:n.position||{x:0,y:0},distance:{x:l.x-n.internals.positionAbsolute.x,y:l.y-n.internals.positionAbsolute.y},extent:n.extent,parentId:n.parentId,origin:n.origin,expandParent:n.expandParent,internals:{positionAbsolute:n.internals.positionAbsolute||{x:0,y:0}},measured:{width:n.measured.width??0,height:n.measured.height??0}})}return y}function q7({nodeId:u,dragItems:f,nodeLookup:l,dragging:r=!0}){let y=[];for(let[_,n]of f){let $=l.get(_)?.internals.userNode;if($)y.push({...$,position:n.position,dragging:r})}if(!u)return[y[0],y];let i=l.get(u)?.internals.userNode;return[!i?y[0]:{...i,position:f.get(u)?.position||i.position,dragging:r},y]}function jV({dragItems:u,snapGrid:f,x:l,y:r}){let y=u.values().next().value;if(!y)return null;let i={x:l-y.distance.x,y:r-y.distance.y},_=bi(i,f);return{x:_.x-i.x,y:_.y-i.y}}function pc({onNodeMouseDown:u,getStoreItems:f,onDragStart:l,onDrag:r,onDragStop:y}){let i={x:null,y:null},_=0,n=new Map,$=!1,j={x:0,y:0},F=null,J=!1,U=null,q=!1,W=!1,G=null;function K({noDragClassName:N,handleSelector:c,domNode:z,isSelectable:w,nodeId:H,nodeClickDistance:B=0}){U=If(z);function T({x:P,y:t}){let{nodeLookup:O,nodeExtent:M,snapGrid:S,snapToGrid:b,nodeOrigin:Z,onNodeDrag:D,onSelectionDrag:I,onError:k,updateNodePositions:h}=f();i={x:P,y:t};let o=!1,s=n.size>1,x=s&&M?N7(Ri(n)):null,uu=s&&b?jV({dragItems:n,snapGrid:S,x:P,y:t}):null;for(let[nu,$u]of n){if(!O.has(nu))continue;let Fu={x:P-$u.distance.x,y:t-$u.distance.y};if(b)Fu=uu?{x:Math.round(Fu.x+uu.x),y:Math.round(Fu.y+uu.y)}:bi(Fu,S);let Ku=null;if(s&&M&&!$u.extent&&x){let{positionAbsolute:d}=$u.internals,e=d.x-x.x+M[0][0],cu=d.x+$u.measured.width-x.x2+M[1][0],g=d.y-x.y+M[0][1],Qu=d.y+$u.measured.height-x.y2+M[1][1];Ku=[[e,g],[cu,Qu]]}let{position:Wu,positionAbsolute:m}=Z7({nodeId:nu,nextPosition:Fu,nodeLookup:O,nodeExtent:Ku?Ku:M,nodeOrigin:Z,onError:k});o=o||$u.position.x!==Wu.x||$u.position.y!==Wu.y,$u.position=Wu,$u.internals.positionAbsolute=m}if(W=W||o,!o)return;if(h(n,!0),G&&(r||D||!H&&I)){let[nu,$u]=q7({nodeId:H,dragItems:n,nodeLookup:O});if(r?.(G,n,nu,$u),D?.(G,nu,$u),!H)I?.(G,$u)}}async function X(){if(!F)return;let{transform:P,panBy:t,autoPanSpeed:O,autoPanOnNodeDrag:M}=f();if(!M){$=!1,cancelAnimationFrame(_);return}let[S,b]=Zc(j,F,O);if(S!==0||b!==0){if(i.x=(i.x??0)-S/P[2],i.y=(i.y??0)-b/P[2],await t({x:S,y:b}))T(i)}_=requestAnimationFrame(X)}function R(P){let{nodeLookup:t,multiSelectionActive:O,nodesDraggable:M,transform:S,snapGrid:b,snapToGrid:Z,selectNodesOnDrag:D,onNodeDragStart:I,onSelectionDragStart:k,unselectNodesAndEdges:h}=f();if(J=!0,(!D||!w)&&!O&&H){if(!t.get(H)?.selected)h()}if(w&&D&&H)u?.(H);let o=n$(P.sourceEvent,{transform:S,snapGrid:b,snapToGrid:Z,containerBounds:F});if(i=o,n=AV(t,M,o,H),n.size>0&&(l||I||!H&&k)){let[s,x]=q7({nodeId:H,dragItems:n,nodeLookup:t});if(l?.(P.sourceEvent,n,s,x),I?.(P.sourceEvent,s,x),!H)k?.(P.sourceEvent,x)}}let Y=Rn().clickDistance(B).on("start",(P)=>{let{domNode:t,nodeDragThreshold:O,transform:M,snapGrid:S,snapToGrid:b}=f();if(F=t?.getBoundingClientRect()||null,q=!1,W=!1,G=P.sourceEvent,O===0)R(P);i=n$(P.sourceEvent,{transform:M,snapGrid:S,snapToGrid:b,containerBounds:F}),j=Lr(P.sourceEvent,F)}).on("drag",(P)=>{let{autoPanOnNodeDrag:t,transform:O,snapGrid:M,snapToGrid:S,nodeDragThreshold:b,nodeLookup:Z}=f(),D=n$(P.sourceEvent,{transform:O,snapGrid:M,snapToGrid:S,containerBounds:F});if(G=P.sourceEvent,P.sourceEvent.type==="touchmove"&&P.sourceEvent.touches.length>1||H&&!Z.has(H))q=!0;if(q)return;if(!$&&t&&J)$=!0,X();if(!J){let I=Lr(P.sourceEvent,F),k=I.x-j.x,h=I.y-j.y;if(Math.sqrt(k*k+h*h)>b)R(P)}if((i.x!==D.xSnapped||i.y!==D.ySnapped)&&n&&J)j=Lr(P.sourceEvent,F),T(D)}).on("end",(P)=>{if(!J||q)return;if($=!1,J=!1,cancelAnimationFrame(_),n.size>0){let{nodeLookup:t,updateNodePositions:O,onNodeDragStop:M,onSelectionDragStop:S}=f();if(W)O(n,!1),W=!1;if(y||M||!H&&S){let[b,Z]=q7({nodeId:H,dragItems:n,nodeLookup:t,dragging:!1});if(y?.(P.sourceEvent,n,b,Z),M?.(P.sourceEvent,b,Z),!H)S?.(P.sourceEvent,Z)}}}).filter((P)=>{let t=P.target;return!P.button&&(!N||!Nc(t,`.${N}`,z))&&(!c||Nc(t,c,z))});U.call(Y)}function Q(){U?.on(".drag",null)}return{update:K,destroy:Q}}function FV(u,f,l){let r=[],y={x:u.x-l,y:u.y-l,width:l*2,height:l*2};for(let i of f.values())if(hi(y,Ey(i))>0)r.push(i);return r}var JV=250;function UV(u,f,l,r){let y=[],i=1/0,_=FV(u,l,f+JV);for(let n of _){let $=[...n.internals.handleBounds?.source??[],...n.internals.handleBounds?.target??[]];for(let j of $){if(r.nodeId===j.nodeId&&r.type===j.type&&r.id===j.id)continue;let{x:F,y:J}=Z1(n,j,j.position,!0),U=Math.sqrt(Math.pow(F-u.x,2)+Math.pow(J-u.y,2));if(U>f)continue;if(U1){let n=r.type==="source"?"target":"source";return y.find(($)=>$.type===n)??y[0]}return y[0]}function Cc(u,f,l,r,y,i=!1){let _=r.get(u);if(!_)return null;let n=y==="strict"?_.internals.handleBounds?.[f]:[..._.internals.handleBounds?.source??[],..._.internals.handleBounds?.target??[]],$=(l?n?.find((j)=>j.id===l):n?.[0])??null;return $&&i?{...$,...Z1(_,$,$.position,!0)}:$}function xc(u,f){if(u)return u;else if(f?.classList.contains("target"))return"target";else if(f?.classList.contains("source"))return"source";return null}function QV(u,f){let l=null;if(f)l=!0;else if(u&&!f)l=!1;return l}var Rc=()=>!0;function qV(u,{connectionMode:f,connectionRadius:l,handleId:r,nodeId:y,edgeUpdaterType:i,isTarget:_,domNode:n,nodeLookup:$,lib:j,autoPanOnConnect:F,flowId:J,panBy:U,cancelConnection:q,onConnectStart:W,onConnect:G,onConnectEnd:K,isValidConnection:Q=Rc,onReconnectEnd:N,updateConnection:c,getTransform:z,getFromHandle:w,autoPanSpeed:H,dragThreshold:B=1,handleDomNode:T}){let X=D7(u.target),R=0,Y,{x:P,y:t}=Lr(u),O=xc(i,T),M=n?.getBoundingClientRect(),S=!1;if(!M||!O)return;let b=Cc(y,O,r,$,f);if(!b)return;let Z=Lr(u,M),D=!1,I=null,k=!1,h=null;function o(){if(!F||!M)return;let[Wu,m]=Zc(Z,M,H);U({x:Wu,y:m}),R=requestAnimationFrame(o)}let s={...b,nodeId:y,type:O,position:b.position},x=$.get(y),nu={inProgress:!0,isValid:null,from:Z1(x,s,zu.Left,!0),fromHandle:s,fromPosition:s.position,fromNode:x,to:Z,toHandle:null,toPosition:$c[s.position],toNode:null,pointer:Z};function $u(){S=!0,c(nu),W?.(u,{nodeId:y,handleId:r,handleType:O})}if(B===0)$u();function Fu(Wu){if(!S){let{x:Qu,y:Eu}=Lr(Wu),Tu=Qu-P,Du=Eu-t;if(!(Tu*Tu+Du*Du>B*B))return;$u()}if(!w()||!s){Ku(Wu);return}let m=z();if(Z=Lr(Wu,M),Y=UV(vi(Z,m,!1,[1,1]),l,$,s),!D)o(),D=!0;let d=hc(Wu,{handle:Y,connectionMode:f,fromNodeId:y,fromHandleId:r,fromType:_?"target":"source",isValidConnection:Q,doc:X,lib:j,flowId:J,nodeLookup:$});h=d.handleDomNode,I=d.connection,k=QV(!!Y,d.isValid);let e=$.get(y),cu=e?Z1(e,s,zu.Left,!0):nu.from,g={...nu,from:cu,isValid:k,to:d.toHandle&&k?$$({x:d.toHandle.x,y:d.toHandle.y},m):Z,toHandle:d.toHandle,toPosition:k&&d.toHandle?d.toHandle.position:$c[s.position],toNode:d.toHandle?$.get(d.toHandle.nodeId):null,pointer:Z};c(g),nu=g}function Ku(Wu){if("touches"in Wu&&Wu.touches.length>0)return;if(S){if((Y||h)&&I&&k)G?.(I);let{inProgress:m,...d}=nu,e={...d,toPosition:nu.toHandle?nu.toPosition:null};if(K?.(Wu,e),i)N?.(Wu,e)}q(),cancelAnimationFrame(R),D=!1,k=!1,I=null,h=null,X.removeEventListener("mousemove",Fu),X.removeEventListener("mouseup",Ku),X.removeEventListener("touchmove",Fu),X.removeEventListener("touchend",Ku)}X.addEventListener("mousemove",Fu),X.addEventListener("mouseup",Ku),X.addEventListener("touchmove",Fu),X.addEventListener("touchend",Ku)}function hc(u,{handle:f,connectionMode:l,fromNodeId:r,fromHandleId:y,fromType:i,doc:_,lib:n,flowId:$,isValidConnection:j=Rc,nodeLookup:F}){let J=i==="target",U=f?_.querySelector(`.${n}-flow__handle[data-id="${$}-${f?.nodeId}-${f?.id}-${f?.type}"]`):null,{x:q,y:W}=Lr(u),G=_.elementFromPoint(q,W),K=G?.classList.contains(`${n}-flow__handle`)?G:U,Q={handleDomNode:K,isValid:!1,connection:null,toHandle:null};if(K){let N=xc(void 0,K),c=K.getAttribute("data-nodeid"),z=K.getAttribute("data-handleid"),w=K.classList.contains("connectable"),H=K.classList.contains("connectableend");if(!c||!N)return Q;let B={source:J?c:r,sourceHandle:J?z:y,target:J?r:c,targetHandle:J?y:z};Q.connection=B;let X=w&&H&&(l===E1.Strict?J&&N==="source"||!J&&N==="target":c!==r||z!==y);Q.isValid=X&&j(B),Q.toHandle=Cc(c,N,z,F,l,!0)}return Q}var p4={onPointerDown:qV,isValid:hc};function bc({domNode:u,panZoom:f,getTransform:l,getViewScale:r}){let y=If(u);function i({translateExtent:n,width:$,height:j,zoomStep:F=1,pannable:J=!0,zoomable:U=!0,inversePan:q=!1}){let W=(c)=>{if(c.sourceEvent.type!=="wheel"||!f)return;let z=l(),w=c.sourceEvent.ctrlKey&&Ii()?10:1,H=-c.sourceEvent.deltaY*(c.sourceEvent.deltaMode===1?0.05:c.sourceEvent.deltaMode?1:0.002)*F,B=z[2]*Math.pow(2,H*w);f.scaleTo(B)},G=[0,0],K=(c)=>{if(c.sourceEvent.type==="mousedown"||c.sourceEvent.type==="touchstart")G=[c.sourceEvent.clientX??c.sourceEvent.touches[0].clientX,c.sourceEvent.clientY??c.sourceEvent.touches[0].clientY]},Q=(c)=>{let z=l();if(c.sourceEvent.type!=="mousemove"&&c.sourceEvent.type!=="touchmove"||!f)return;let w=[c.sourceEvent.clientX??c.sourceEvent.touches[0].clientX,c.sourceEvent.clientY??c.sourceEvent.touches[0].clientY],H=[w[0]-G[0],w[1]-G[1]];G=w;let B=r()*Math.max(z[2],Math.log(z[2]))*(q?-1:1),T={x:z[0]-H[0]*B,y:z[1]-H[1]*B},X=[[0,0],[$,j]];f.setViewportConstrained({x:T.x,y:T.y,zoom:z[2]},X,n)},N=_$().on("start",K).on("zoom",J?Q:null).on("zoom.wheel",U?W:null);y.call(N,{})}function _(){y.on("zoom",null)}return{update:i,destroy:_,pointer:Wl}}var C4=(u)=>({x:u.x,y:u.y,zoom:u.k}),W7=({x:u,y:f,zoom:l})=>Gy.translate(u,f).scale(l),mi=(u,f)=>u.target.closest(`.${f}`),vc=(u,f)=>f===2&&Array.isArray(u)&&u.includes(2),WV=(u)=>((u*=2)<=1?u*u*u:(u-=2)*u*u+2)/2,c7=(u,f=0,l=WV,r=()=>{})=>{let y=typeof f==="number"&&f>0;if(!y)r();return y?u.transition().duration(f).ease(l).on("end",r):u},Ic=(u)=>{let f=u.ctrlKey&&Ii()?10:1;return-u.deltaY*(u.deltaMode===1?0.05:u.deltaMode?1:0.002)*f};function cV({zoomPanValues:u,noWheelClassName:f,d3Selection:l,d3Zoom:r,panOnScrollMode:y,panOnScrollSpeed:i,zoomOnPinch:_,onPanZoomStart:n,onPanZoom:$,onPanZoomEnd:j}){return(F)=>{if(mi(F,f)){if(F.ctrlKey)F.preventDefault();return!1}F.preventDefault(),F.stopImmediatePropagation();let J=l.property("__zoom").k||1;if(F.ctrlKey&&_){let K=Wl(F),Q=Ic(F),N=J*Math.pow(2,Q);r.scaleTo(l,N,K,F);return}let U=F.deltaMode===1?20:1,q=y===D0.Vertical?0:F.deltaX*U,W=y===D0.Horizontal?0:F.deltaY*U;if(!Ii()&&F.shiftKey&&y!==D0.Vertical)q=F.deltaY*U,W=0;r.translateBy(l,-(q/J)*i,-(W/J)*i,{internal:!0});let G=C4(l.property("__zoom"));if(clearTimeout(u.panScrollTimeout),!u.isPanScrolling)u.isPanScrolling=!0,n?.(F,G);else $?.(F,G),u.panScrollTimeout=setTimeout(()=>{j?.(F,G),u.isPanScrolling=!1},150)}}function NV({noWheelClassName:u,preventScrolling:f,d3ZoomHandler:l}){return function(r,y){let i=r.type==="wheel",_=!f&&i&&!r.ctrlKey,n=mi(r,u);if(r.ctrlKey&&i&&n)r.preventDefault();if(_||n)return null;r.preventDefault(),l.call(this,r,y)}}function zV({zoomPanValues:u,onDraggingChange:f,onPanZoomStart:l}){return(r)=>{if(r.sourceEvent?.internal)return;let y=C4(r.transform);if(u.mouseButton=r.sourceEvent?.button||0,u.isZoomingOrPanning=!0,u.prevViewport=y,r.sourceEvent?.type==="mousedown")f(!0);if(l)l?.(r.sourceEvent,y)}}function GV({zoomPanValues:u,panOnDrag:f,onPaneContextMenu:l,onTransformChange:r,onPanZoom:y}){return(i)=>{if(u.usedRightMouseButton=!!(l&&vc(f,u.mouseButton??0)),!i.sourceEvent?.sync)r([i.transform.x,i.transform.y,i.transform.k]);if(y&&!i.sourceEvent?.internal)y?.(i.sourceEvent,C4(i.transform))}}function KV({zoomPanValues:u,panOnDrag:f,panOnScroll:l,onDraggingChange:r,onPanZoomEnd:y,onPaneContextMenu:i}){return(_)=>{if(_.sourceEvent?.internal)return;if(u.isZoomingOrPanning=!1,i&&vc(f,u.mouseButton??0)&&!u.usedRightMouseButton&&_.sourceEvent)i(_.sourceEvent);if(u.usedRightMouseButton=!1,r(!1),y){let n=C4(_.transform);u.prevViewport=n,clearTimeout(u.timerId),u.timerId=setTimeout(()=>{y?.(_.sourceEvent,n)},l?150:0)}}}function LV({zoomActivationKeyPressed:u,zoomOnScroll:f,zoomOnPinch:l,panOnDrag:r,panOnScroll:y,zoomOnDoubleClick:i,userSelectionActive:_,noWheelClassName:n,noPanClassName:$,lib:j,connectionInProgress:F}){return(J)=>{let U=u||f,q=l&&J.ctrlKey,W=J.type==="wheel";if(J.button===1&&J.type==="mousedown"&&(mi(J,`${j}-flow__node`)||mi(J,`${j}-flow__edge`)))return!0;if(!r&&!U&&!y&&!i&&!l)return!1;if(_)return!1;if(F&&!W)return!1;if(mi(J,n)&&W)return!1;if(mi(J,$)&&(!W||y&&W&&!u))return!1;if(!l&&J.ctrlKey&&W)return!1;if(!l&&J.type==="touchstart"&&J.touches?.length>1)return J.preventDefault(),!1;if(!U&&!y&&!q&&W)return!1;if(!r&&(J.type==="mousedown"||J.type==="touchstart"))return!1;if(Array.isArray(r)&&!r.includes(J.button)&&J.type==="mousedown")return!1;let G=Array.isArray(r)&&r.includes(J.button)||!J.button||J.button<=1;return(!J.ctrlKey||W)&&G}}function kc({domNode:u,minZoom:f,maxZoom:l,translateExtent:r,viewport:y,onPanZoom:i,onPanZoomStart:_,onPanZoomEnd:n,onDraggingChange:$}){let j={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{x:0,y:0,zoom:0},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},F=u.getBoundingClientRect(),J=_$().scaleExtent([f,l]).translateExtent(r),U=If(u).call(J);N({x:y.x,y:y.y,zoom:pi(y.zoom,f,l)},[[0,0],[F.width,F.height]],r);let q=U.on("wheel.zoom"),W=U.on("dblclick.zoom");J.wheelDelta(Ic);function G(Y,P){if(U)return new Promise((t)=>{J?.interpolate(P?.interpolate==="linear"?r0:Ny).transform(c7(U,P?.duration,P?.ease,()=>t(!0)),Y)});return Promise.resolve(!1)}function K({noWheelClassName:Y,noPanClassName:P,onPaneContextMenu:t,userSelectionActive:O,panOnScroll:M,panOnDrag:S,panOnScrollMode:b,panOnScrollSpeed:Z,preventScrolling:D,zoomOnPinch:I,zoomOnScroll:k,zoomOnDoubleClick:h,zoomActivationKeyPressed:o,lib:s,onTransformChange:x,connectionInProgress:uu,paneClickDistance:nu,selectionOnDrag:$u}){if(O&&!j.isZoomingOrPanning)Q();let Fu=M&&!o&&!O;J.clickDistance($u?1/0:!Kr(nu)||nu<0?0:nu);let Ku=Fu?cV({zoomPanValues:j,noWheelClassName:Y,d3Selection:U,d3Zoom:J,panOnScrollMode:b,panOnScrollSpeed:Z,zoomOnPinch:I,onPanZoomStart:_,onPanZoom:i,onPanZoomEnd:n}):NV({noWheelClassName:Y,preventScrolling:D,d3ZoomHandler:q});if(U.on("wheel.zoom",Ku,{passive:!1}),!O){let m=zV({zoomPanValues:j,onDraggingChange:$,onPanZoomStart:_});J.on("start",m);let d=GV({zoomPanValues:j,panOnDrag:S,onPaneContextMenu:!!t,onPanZoom:i,onTransformChange:x});J.on("zoom",d);let e=KV({zoomPanValues:j,panOnDrag:S,panOnScroll:M,onPaneContextMenu:t,onPanZoomEnd:n,onDraggingChange:$});J.on("end",e)}let Wu=LV({zoomActivationKeyPressed:o,panOnDrag:S,zoomOnScroll:k,panOnScroll:M,zoomOnDoubleClick:h,zoomOnPinch:I,userSelectionActive:O,noPanClassName:P,noWheelClassName:Y,lib:s,connectionInProgress:uu});if(J.filter(Wu),h)U.on("dblclick.zoom",W);else U.on("dblclick.zoom",null)}function Q(){J.on("zoom",null)}async function N(Y,P,t){let O=W7(Y),M=J?.constrain()(O,P,t);if(M)await G(M);return new Promise((S)=>S(M))}async function c(Y,P){let t=W7(Y);return await G(t,P),new Promise((O)=>O(t))}function z(Y){if(U){let P=W7(Y),t=U.property("__zoom");if(t.k!==Y.zoom||t.x!==Y.x||t.y!==Y.y)J?.transform(U,P,null,{sync:!0})}}function w(){let Y=U?i$(U.node()):{x:0,y:0,k:1};return{x:Y.x,y:Y.y,zoom:Y.k}}function H(Y,P){if(U)return new Promise((t)=>{J?.interpolate(P?.interpolate==="linear"?r0:Ny).scaleTo(c7(U,P?.duration,P?.ease,()=>t(!0)),Y)});return Promise.resolve(!1)}function B(Y,P){if(U)return new Promise((t)=>{J?.interpolate(P?.interpolate==="linear"?r0:Ny).scaleBy(c7(U,P?.duration,P?.ease,()=>t(!0)),Y)});return Promise.resolve(!1)}function T(Y){J?.scaleExtent(Y)}function X(Y){J?.translateExtent(Y)}function R(Y){let P=!Kr(Y)||Y<0?0:Y;J?.clickDistance(P)}return{update:K,destroy:Q,setViewport:c,setViewportConstrained:N,getViewport:w,scaleTo:H,scaleBy:B,setScaleExtent:T,setTranslateExtent:X,syncViewport:z,setClickDistance:R}}var H1;(function(u){u.Line="line",u.Handle="handle"})(H1||(H1={}));function wV({width:u,prevWidth:f,height:l,prevHeight:r,affectsX:y,affectsY:i}){let _=u-f,n=l-r,$=[_>0?1:_<0?-1:0,n>0?1:n<0?-1:0];if(_&&y)$[0]=$[0]*-1;if(n&&i)$[1]=$[1]*-1;return $}function zc(u){let f=u.includes("right")||u.includes("left"),l=u.includes("bottom")||u.includes("top"),r=u.includes("left"),y=u.includes("top");return{isHorizontal:f,isVertical:l,affectsX:r,affectsY:y}}function L1(u,f){return Math.max(0,f-u)}function w1(u,f){return Math.max(0,u-f)}function H4(u,f,l){return Math.max(0,f-u,u-l)}function Gc(u,f){return u?!f:f}function EV(u,f,l,r,y,i,_,n){let{affectsX:$,affectsY:j}=f,{isHorizontal:F,isVertical:J}=f,U=F&&J,{xSnapped:q,ySnapped:W}=l,{minWidth:G,maxWidth:K,minHeight:Q,maxHeight:N}=r,{x:c,y:z,width:w,height:H,aspectRatio:B}=u,T=Math.floor(F?q-u.pointerX:0),X=Math.floor(J?W-u.pointerY:0),R=w+($?-T:T),Y=H+(j?-X:X),P=-i[0]*w,t=-i[1]*H,O=H4(R,G,K),M=H4(Y,Q,N);if(_){let Z=0,D=0;if($&&T<0)Z=L1(c+T+P,_[0][0]);else if(!$&&T>0)Z=w1(c+R+P,_[1][0]);if(j&&X<0)D=L1(z+X+t,_[0][1]);else if(!j&&X>0)D=w1(z+Y+t,_[1][1]);O=Math.max(O,Z),M=Math.max(M,D)}if(n){let Z=0,D=0;if($&&T>0)Z=w1(c+T,n[0][0]);else if(!$&&T<0)Z=L1(c+R,n[1][0]);if(j&&X>0)D=w1(z+X,n[0][1]);else if(!j&&X<0)D=L1(z+Y,n[1][1]);O=Math.max(O,Z),M=Math.max(M,D)}if(y){if(F){let Z=H4(R/B,Q,N)*B;if(O=Math.max(O,Z),_){let D=0;if(!$&&!j||$&&!j&&U)D=w1(z+t+R/B,_[1][1])*B;else D=L1(z+t+($?T:-T)/B,_[0][1])*B;O=Math.max(O,D)}if(n){let D=0;if(!$&&!j||$&&!j&&U)D=L1(z+R/B,n[1][1])*B;else D=w1(z+($?T:-T)/B,n[0][1])*B;O=Math.max(O,D)}}if(J){let Z=H4(Y*B,G,K)/B;if(M=Math.max(M,Z),_){let D=0;if(!$&&!j||j&&!$&&U)D=w1(c+Y*B+P,_[1][0])/B;else D=L1(c+(j?X:-X)*B+P,_[0][0])/B;M=Math.max(M,D)}if(n){let D=0;if(!$&&!j||j&&!$&&U)D=L1(c+Y*B,n[1][0])/B;else D=w1(c+(j?X:-X)*B,n[0][0])/B;M=Math.max(M,D)}}}if(X=X+(X<0?M:-M),T=T+(T<0?O:-O),y)if(U)if(R>Y*B)X=(Gc($,j)?-T:T)/B;else T=(Gc($,j)?-X:X)*B;else if(F)X=T/B,j=$;else T=X*B,$=j;let S=$?c+T:c,b=j?z+X:z;return{width:w+($?-T:T),height:H+(j?-X:X),x:i[0]*T*(!$?1:-1)+S,y:i[1]*X*(!j?1:-1)+b}}var gc={width:0,height:0,x:0,y:0},TV={...gc,pointerX:0,pointerY:0,aspectRatio:1};function ZV(u){return[[0,0],[u.measured.width,u.measured.height]]}function HV(u,f,l){let r=f.position.x+u.position.x,y=f.position.y+u.position.y,i=u.measured.width??0,_=u.measured.height??0,n=l[0]*i,$=l[1]*_;return[[r-n,y-$],[r+i-n,y+_-$]]}function sc({domNode:u,nodeId:f,getStoreItems:l,onChange:r,onEnd:y}){let i=If(u),_={controlDirection:zc("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function n({controlPosition:j,boundaries:F,keepAspectRatio:J,resizeDirection:U,onResizeStart:q,onResize:W,onResizeEnd:G,shouldResize:K}){let Q={...gc},N={...TV};_={boundaries:F,resizeDirection:U,keepAspectRatio:J,controlDirection:zc(j)};let c=void 0,z=null,w=[],H=void 0,B=void 0,T=void 0,X=!1,R=Rn().on("start",(Y)=>{let{nodeLookup:P,transform:t,snapGrid:O,snapToGrid:M,nodeOrigin:S,paneDomNode:b}=l();if(c=P.get(f),!c)return;z=b?.getBoundingClientRect()??null;let{xSnapped:Z,ySnapped:D}=n$(Y.sourceEvent,{transform:t,snapGrid:O,snapToGrid:M,containerBounds:z});if(Q={width:c.measured.width??0,height:c.measured.height??0,x:c.position.x??0,y:c.position.y??0},N={...Q,pointerX:Z,pointerY:D,aspectRatio:Q.width/Q.height},H=void 0,c.parentId&&(c.extent==="parent"||c.expandParent))H=P.get(c.parentId),B=H&&c.extent==="parent"?ZV(H):void 0;w=[],T=void 0;for(let[I,k]of P)if(k.parentId===f){if(w.push({id:I,position:{...k.position},extent:k.extent}),k.extent==="parent"||k.expandParent){let h=HV(k,c,k.origin??S);if(T)T=[[Math.min(h[0][0],T[0][0]),Math.min(h[0][1],T[0][1])],[Math.max(h[1][0],T[1][0]),Math.max(h[1][1],T[1][1])]];else T=h}}q?.(Y,{...Q})}).on("drag",(Y)=>{let{transform:P,snapGrid:t,snapToGrid:O,nodeOrigin:M}=l(),S=n$(Y.sourceEvent,{transform:P,snapGrid:t,snapToGrid:O,containerBounds:z}),b=[];if(!c)return;let{x:Z,y:D,width:I,height:k}=Q,h={},o=c.origin??M,{width:s,height:x,x:uu,y:nu}=EV(N,_.controlDirection,S,_.boundaries,_.keepAspectRatio,o,B,T),$u=s!==I,Fu=x!==k,Ku=uu!==Z&&$u,Wu=nu!==D&&Fu;if(!Ku&&!Wu&&!$u&&!Fu)return;if(Ku||Wu||o[0]===1||o[1]===1){if(h.x=Ku?uu:Q.x,h.y=Wu?nu:Q.y,Q.x=h.x,Q.y=h.y,w.length>0){let cu=uu-Z,g=nu-D;for(let Qu of w)Qu.position={x:Qu.position.x-cu+o[0]*(s-I),y:Qu.position.y-g+o[1]*(x-k)},b.push(Qu)}}if($u||Fu)h.width=$u&&(!_.resizeDirection||_.resizeDirection==="horizontal")?s:Q.width,h.height=Fu&&(!_.resizeDirection||_.resizeDirection==="vertical")?x:Q.height,Q.width=h.width,Q.height=h.height;if(H&&c.expandParent){let cu=o[0]*(h.width??0);if(h.x&&h.x{if(!X)return;G?.(Y,{...Q}),y?.({...Q}),X=!1});i.call(R)}function $(){i.on(".drag",null)}return{update:n,destroy:$}}var nN=Pu(wf(),1),$N=Pu(rN(),1);var yN=(u)=>{let f,l=new Set,r=(F,J)=>{let U=typeof F==="function"?F(f):F;if(!Object.is(U,f)){let q=f;f=(J!=null?J:typeof U!=="object"||U===null)?U:Object.assign({},f,U),l.forEach((W)=>W(f,q))}},y=()=>f,$={setState:r,getState:y,getInitialState:()=>j,subscribe:(F)=>{return l.add(F),()=>l.delete(F)},destroy:()=>{l.clear()}},j=f=u(r,y,$);return $},iN=(u)=>u?yN(u):yN;var{useDebugValue:vV}=nN.default,{useSyncExternalStoreWithSelector:IV}=$N.default,kV=(u)=>u;function b7(u,f=kV,l){let r=IV(u.subscribe,u.getState,u.getServerState||u.getInitialState,f,l);return vV(r),r}var _N=(u,f)=>{let l=iN(u),r=(y,i=f)=>b7(l,y,i);return Object.assign(r,l),r},AN=(u,f)=>u?_N(u,f):_N;function Kf(u,f){if(Object.is(u,f))return!0;if(typeof u!=="object"||u===null||typeof f!=="object"||f===null)return!1;if(u instanceof Map&&f instanceof Map){if(u.size!==f.size)return!1;for(let[r,y]of u)if(!Object.is(y,f.get(r)))return!1;return!0}if(u instanceof Set&&f instanceof Set){if(u.size!==f.size)return!1;for(let r of u)if(!f.has(r))return!1;return!0}let l=Object.keys(u);if(l.length!==Object.keys(f).length)return!1;for(let r of l)if(!Object.prototype.hasOwnProperty.call(f,r)||!Object.is(u[r],f[r]))return!1;return!0}var gV=Pu(e5(),1),v4=iu.createContext(null),sV=v4.Provider,DN=_r.error001();function uf(u,f){let l=iu.useContext(v4);if(l===null)throw Error(DN);return b7(l,u,f)}function Tf(){let u=iu.useContext(v4);if(u===null)throw Error(DN);return iu.useMemo(()=>({getState:u.getState,setState:u.setState,subscribe:u.subscribe}),[u])}var jN={display:"none"},aV={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},tN="react-flow__node-desc",SN="react-flow__edge-desc",oV="react-flow__aria-live",dV=(u)=>u.ariaLiveMessage,eV=(u)=>u.ariaLabelConfig;function uX({rfId:u}){let f=uf(dV);return ru.jsx("div",{id:`${oV}-${u}`,"aria-live":"assertive","aria-atomic":"true",style:aV,children:f})}function fX({rfId:u,disableKeyboardA11y:f}){let l=uf(eV);return ru.jsxs(ru.Fragment,{children:[ru.jsx("div",{id:`${tN}-${u}`,style:jN,children:f?l["node.a11yDescription.default"]:l["node.a11yDescription.keyboardDisabled"]}),ru.jsx("div",{id:`${SN}-${u}`,style:jN,children:l["edge.a11yDescription.default"]}),!f&&ru.jsx(uX,{rfId:u})]})}var I4=iu.forwardRef(({position:u="top-left",children:f,className:l,style:r,...y},i)=>{let _=`${u}`.split("-");return ru.jsx("div",{className:Yf(["react-flow__panel",l,..._]),style:r,ref:i,...y,children:f})});I4.displayName="Panel";function lX({proOptions:u,position:f="bottom-right"}){if(u?.hideAttribution)return null;return ru.jsx(I4,{position:f,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev",children:ru.jsx("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}var rX=(u)=>{let f=[],l=[];for(let[,r]of u.nodeLookup)if(r.selected)f.push(r.internals.userNode);for(let[,r]of u.edgeLookup)if(r.selected)l.push(r);return{selectedNodes:f,selectedEdges:l}},R4=(u)=>u.id;function yX(u,f){return Kf(u.selectedNodes.map(R4),f.selectedNodes.map(R4))&&Kf(u.selectedEdges.map(R4),f.selectedEdges.map(R4))}function iX({onSelectionChange:u}){let f=Tf(),{selectedNodes:l,selectedEdges:r}=uf(rX,yX);return iu.useEffect(()=>{let y={nodes:l,edges:r};u?.(y),f.getState().onSelectionChangeHandlers.forEach((i)=>i(y))},[l,r,u]),null}var _X=(u)=>!!u.onSelectionChangeHandlers;function nX({onSelectionChange:u}){let f=uf(_X);if(u||f)return ru.jsx(iX,{onSelectionChange:u});return null}var k7=typeof window<"u"?iu.useLayoutEffect:iu.useEffect,PN=[0,0],$X={x:0,y:0,zoom:1},AX=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],FN=[...AX,"rfId"],jX=(u)=>({setNodes:u.setNodes,setEdges:u.setEdges,setMinZoom:u.setMinZoom,setMaxZoom:u.setMaxZoom,setTranslateExtent:u.setTranslateExtent,setNodeExtent:u.setNodeExtent,reset:u.reset,setDefaultNodesAndEdges:u.setDefaultNodesAndEdges}),JN={translateExtent:xi,nodeOrigin:PN,minZoom:0.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function FX(u){let{setNodes:f,setEdges:l,setMinZoom:r,setMaxZoom:y,setTranslateExtent:i,setNodeExtent:_,reset:n,setDefaultNodesAndEdges:$}=uf(jX,Kf),j=Tf();k7(()=>{return $(u.defaultNodes,u.defaultEdges),()=>{F.current=JN,n()}},[]);let F=iu.useRef(JN);return k7(()=>{for(let J of FN){let U=u[J],q=F.current[J];if(U===q)continue;if(typeof u[J]>"u")continue;if(J==="nodes")f(U);else if(J==="edges")l(U);else if(J==="minZoom")r(U);else if(J==="maxZoom")y(U);else if(J==="translateExtent")i(U);else if(J==="nodeExtent")_(U);else if(J==="ariaLabelConfig")j.setState({ariaLabelConfig:Oc(U)});else if(J==="fitView")j.setState({fitViewQueued:U});else if(J==="fitViewOptions")j.setState({fitViewOptions:U});else j.setState({[J]:U})}F.current=u},FN.map((J)=>u[J])),null}function UN(){if(typeof window>"u"||!window.matchMedia)return null;return window.matchMedia("(prefers-color-scheme: dark)")}function JX(u){let[f,l]=iu.useState(u==="system"?null:u);return iu.useEffect(()=>{if(u!=="system"){l(u);return}let r=UN(),y=()=>l(r?.matches?"dark":"light");return y(),r?.addEventListener("change",y),()=>{r?.removeEventListener("change",y)}},[u]),f!==null?f:UN()?.matches?"dark":"light"}var QN=typeof document<"u"?document:null;function J$(u=null,f={target:QN,actInsideInputWithModifier:!0}){let[l,r]=iu.useState(!1),y=iu.useRef(!1),i=iu.useRef(new Set([])),[_,n]=iu.useMemo(()=>{if(u!==null){let j=(Array.isArray(u)?u:[u]).filter((J)=>typeof J==="string").map((J)=>J.replace("+",` +`;function gc({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return Bu("section",{className:`panel ${n||""}`},Bu("div",{className:"panel-head"},Bu("div",null,f?Bu("p",{className:"panel-eyebrow"},f):null,Bu(nf,{title:u,loading:i})),l?Bu("div",{className:"panel-actions"},l):null),Bu("div",{className:"panel-body"},r))}function iZ({title:u,data:f,onOpen:l,testId:r}){return Bu("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function yZ({title:u,text:f}){return Bu("div",{className:"empty-state"},Bu("strong",null,u),Bu("span",null,f))}function GQ(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function zQ(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function TQ(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function _Z(u){return u.filter((l)=>l?.id==="filebrowser"||String(l?.id||"").startsWith("filebrowser-")).sort((l,r)=>{let n=(i)=>i.providerId==="D518"?0:i.providerId==="D601"?1:i.id==="filebrowser"?2:3;return n(l)-n(r)||String(l.id).localeCompare(String(r.id))})}function $Z(u){if(u?.providerId==="D518")return"D518";return u?.providerId||u?.name||u?.id||"Unknown"}function tZ(u,f,l="/"){let r=l.startsWith("/")?l:`/${l}`;return`${u}/microservices/${encodeURIComponent(f)}/proxy${r}`}function cZ(u,f){return`${u}/microservices/${encodeURIComponent(f)}/health`}async function AZ(u,f=16000){let l=new AbortController,r=setTimeout(()=>l.abort(),f);try{return await Eu(u,{signal:l.signal,failureFields:[!1]})}finally{clearTimeout(r)}}function EQ(u){if(u?.providerId==="main-server")return"host / -> /srv";if(u?.providerId==="D601"||u?.providerId==="D518")return"WSL / + /mnt/c -> /srv";return"provider / -> /srv"}function v3(u){return u?.status==="OK"||u?.ok===!0}function jZ({service:u,active:f,health:l,onSelect:r,onRaw:n}){let i=GQ(u),y=zQ(u),_=TQ(u),$=i.container||{},c=v3(l?.body);return Bu("button",{type:"button",className:`filebrowser-target-card ${f?"active":""}`,"data-testid":`filebrowser-target-card-${u.id}`,onClick:r},Bu("span",{className:`status-badge ${c?"ok":i.providerStatus==="online"?"running":"warn"}`},c?"Health OK":i.providerStatus||"unknown"),Bu("strong",null,u.name||u.id),Bu("span",null,EQ(u)),Bu("code",null,`${y.nodeBindHost||"--"}:${y.nodePort||"--"}`),Bu("small",null,$.name?`${$.name} / ${$.state||"--"}`:`${_.composeService||"--"}`),Bu("span",{className:"filebrowser-card-raw",onClick:(A)=>{A.stopPropagation(),n(`${u.name} service`,u)}},"JSON"))}function ZQ(u){try{return u?.contentDocument||u?.contentWindow?.document||null}catch{return null}}function ac(u){let f=ZQ(u);if(f===null||f.head===null)return!1;let l=f.getElementById("unidesk-filebrowser-compact-style");if(l===null)l=f.createElement("style"),l.id="unidesk-filebrowser-compact-style",f.head.appendChild(l);if(l.textContent!==sc)l.textContent=sc;return!0}function FZ(u,f){let l=URL.createObjectURL(u),r=document.createElement("a");r.href=l,r.download=f,document.body.appendChild(r),r.click(),r.remove(),setTimeout(()=>URL.revokeObjectURL(l),2000)}function JZ(u,f){let l=ZQ(u);if(l===null||l.documentElement===null)throw Error("无法访问 File Browser iframe 文档");ac(u);let r=Math.max(640,Math.ceil(u.clientWidth||l.documentElement.clientWidth||1280)),n=Math.max(480,Math.ceil(u.clientHeight||l.documentElement.clientHeight||720)),i=l.documentElement.cloneNode(!0);i.querySelectorAll("script, style, link[rel='stylesheet'], link[rel='preload'], link[rel='icon']").forEach((A)=>A.remove()),i.querySelectorAll("img").forEach((A)=>{A.removeAttribute("src"),A.removeAttribute("srcset")});let y=i.querySelector("head");if(y===null)y=l.createElement("head"),i.insertBefore(y,i.firstChild);let _=l.createElement("style");_.textContent=`${sc} +html,body{width:${r}px!important;min-height:${n}px!important;overflow:hidden!important;}`,y.appendChild(_);let $=new XMLSerializer().serializeToString(i),c=`${$}`;FZ(new Blob([c],{type:"image/svg+xml;charset=utf-8"}),f.replace(/\.png$/i,".svg"))}function OQ({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=_Z(Array.isArray(u)?u:[]),n=new URLSearchParams(window.location.search).get("target")||"",i=n==="filebrowser-d518"?"filebrowser":n,y=r.some((Z)=>Z.id===i)?i:r[0]?.id||"",[_,$]=Ic(y),[c,A]=Ic({loading:!1,refreshedAt:null,health:{},error:""}),[j,F]=Ic({exporting:!1,message:"",error:""}),U=nZ(null),Q=r.find((Z)=>Z.id===_)||r[0]||null,w=GQ(Q),L=zQ(Q),J=TQ(Q),N=Q?c.health[Q.id]:null,q=Q?tZ(l,Q.id,"/"):"about:blank";kc(()=>{if(r.length===0)return;if(!_||!r.some((Z)=>Z.id===_))$(r[0].id)},[r.map((Z)=>Z.id).join(",")]),kc(()=>{let Z=0,H=setInterval(()=>{if(Z+=1,ac(U.current)||Z>=24)clearInterval(H)},500);return()=>clearInterval(H)},[q]),kc(()=>{if(r.length===0)return;let Z=!1;async function H(){A((h)=>({...h,loading:!0,error:""}));let D=await Promise.all(r.map(async(h)=>{try{let B=await AZ(cZ(l,h.id));return[h.id,{ok:!0,body:B}]}catch(B){return[h.id,{ok:!1,error:Hu(B,"File Browser health failed")}]}}));if(Z)return;A({loading:!1,refreshedAt:new Date().toISOString(),health:Object.fromEntries(D),error:""})}H();let E=setInterval(H,30000);return()=>{Z=!0,clearInterval(E)}},[r.map((Z)=>`${Z.id}:${Z.runtime?.providerStatus||""}`).join(","),l]);function W(Z){$(Z);let H=new URL(window.location.href);H.searchParams.set("target",Z),window.history.replaceState({},"",`${H.pathname}${H.search}`)}async function z(){if(j.exporting)return;F({exporting:!0,message:"",error:""});try{let Z=new Date().toISOString().replace(/[-:.TZ]/g,"").slice(0,14);await JZ(U.current,`unidesk-filebrowser-${Q?.id||"target"}-${Z}.png`),F({exporting:!1,message:"截图已导出",error:""})}catch(Z){F({exporting:!1,message:"",error:Hu(Z,"截图导出失败")})}}if(r.length===0)return Bu(yZ,{title:"File Browser 未登记",text:"请在 config.json 的 microservices 中登记 id=filebrowser 或 filebrowser-* 用户服务"});return Bu("div",{className:"filebrowser-page","data-testid":"filebrowser-page"},c.error?Bu(yf,{error:c.error,wide:!0}):null,Bu(gc,{title:"文件管理器",eyebrow:"File Browser / Host Files",loading:c.loading,actions:Bu("div",{className:"panel-actions"},Q?Bu("button",{type:"button",className:"ghost-btn",onClick:z,disabled:j.exporting,"data-testid":"filebrowser-export-screenshot"},j.exporting?"导出中...":"导出截图"):null,Q?Bu("a",{className:"ghost-btn",href:q,target:"_blank",rel:"noreferrer"},"新窗口打开"):null,Q?Bu(iZ,{title:"File Browser 当前目标",data:{service:Q,health:N},onOpen:f,testId:"raw-filebrowser-active"}):null)},Bu("div",{className:"filebrowser-hero"},Bu("div",null,Bu("span",{className:`status-badge ${v3(N?.body)?"ok":"warn"}`},v3(N?.body)?"Health OK":"Health Pending"),Bu("h3",null,Q?.name||"File Browser"),Bu("p",{className:"muted paragraph"},Q?.description||"通过 UniDesk 登录态代理访问,不开放 File Browser 公网端口。"),j.error?Bu("p",{className:"filebrowser-shot-error"},j.error):null,j.message?Bu("p",{className:"filebrowser-shot-ok"},j.message):null),Bu("div",{className:"microservice-ref-card"},Bu("span",null,"Provider"),Bu("strong",null,Q?.providerId||"--"),Bu("code",null,w.providerName||Q?.providerId||"--")),Bu("div",{className:"microservice-ref-card"},Bu("span",null,"Private Backend"),Bu("strong",null,`${L.nodeBindHost||"--"}:${L.nodePort||"--"}`),Bu("code",null,L.nodeBaseUrl||"--")),Bu("div",{className:"microservice-ref-card"},Bu("span",null,"Image"),Bu("strong",null,J.dockerfile||"filebrowser/filebrowser:v2.63.3"),Bu("code",null,J.commitId||"--")),Bu("div",{className:"microservice-ref-card"},Bu("span",null,"Mount"),Bu("strong",null,EQ(Q)),Bu("code",null,Q?.providerId==="main-server"?"/root, /var, /home":"/home, /mnt/c, /mnt/d")))),Bu(gc,{title:"浏览目标",eyebrow:`${r.length} host targets`,loading:c.loading},Bu("div",{className:"filebrowser-target-grid"},r.map((Z)=>Bu(jZ,{key:Z.id,service:Z,active:Z.id===Q?.id,health:c.health[Z.id],onSelect:()=>W(Z.id),onRaw:f})))),Bu(gc,{title:`${$Z(Q)} 文件视图`,eyebrow:N?.body?`Health ${v3(N.body)?"OK":"UNKNOWN"} / ${c.refreshedAt?$f(c.refreshedAt):"--"}`:"Embedded WebUI",className:"filebrowser-frame-panel"},Bu("div",{className:"filebrowser-frame-shell"},Bu("div",{className:"filebrowser-frame-toolbar"},Bu("span",null,"BaseURL"),Bu("code",null,`/api/microservices/${Q?.id||"filebrowser"}/proxy`),Bu("span",null,"Root"),Bu("code",null,"/srv"),Bu("span",{className:"filebrowser-compact-note"},"Compact layout injected")),Bu("iframe",{ref:U,key:q,title:`${Q?.name||"File Browser"} WebUI`,src:q,className:"filebrowser-frame","data-testid":"filebrowser-frame",onLoad:(Z)=>ac(Z.currentTarget),sandbox:"allow-downloads allow-forms allow-modals allow-same-origin allow-scripts"}))))}var s3=Mu(Qf(),1);var Fu=s3.default.createElement,{useEffect:UZ}=s3.default,QZ=s3.default.useState;function k3({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return Fu("span",{className:`status-badge ${l}`},f||u||"unknown")}function En({label:u,value:f,hint:l,tone:r}){return Fu("article",{className:`metric-card ${r||""}`},Fu("div",{className:"metric-label"},u),Fu("div",{className:"metric-value"},f),Fu("div",{className:"metric-hint"},l))}function I3({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return Fu("section",{className:`panel ${n||""}`},Fu("div",{className:"panel-head"},Fu("div",null,f?Fu("p",{className:"panel-eyebrow"},f):null,Fu(nf,{title:u,loading:i})),l?Fu("div",{className:"panel-actions"},l):null),Fu("div",{className:"panel-body"},r))}function g3({title:u,data:f,onOpen:l,testId:r}){return Fu("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function oc({title:u,text:f}){return Fu("div",{className:"empty-state"},Fu("strong",null,u),Fu("span",null,f))}function qZ(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function NZ(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function WZ(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function z1(u,f){let l=u&&typeof u==="object"?u[f]:void 0;return Number.isFinite(Number(l))?String(l):"--"}function wZ(u){return(Array.isArray(u?.jobs)?u.jobs:[]).slice(0,40)}function LZ(u){return(Array.isArray(u?.drafts)?u.drafts:[]).slice(0,12)}function HQ({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((Q)=>Q.id==="findjob")||null,[n,i]=QZ({loading:!1,error:"",health:null,summary:null,jobs:null,drafts:null,refreshedAt:null});async function y(){if(!r)return;i((Q)=>({...Q,loading:!0,error:""}));try{let[Q,w,L,J]=await Promise.all([Eu(`${l}/microservices/findjob/health`),Eu(`${l}/microservices/findjob/proxy/api/summary`),Eu(`${l}/microservices/findjob/proxy/api/jobs?__unideskArrayLimit=jobs:40`),Eu(`${l}/microservices/findjob/proxy/api/drafts`)]);i({loading:!1,error:"",health:Q,summary:w,jobs:L,drafts:J,refreshedAt:new Date})}catch(Q){i((w)=>({...w,loading:!1,error:Hu(Q,"FindJob 加载失败")}))}}if(UZ(()=>{y()},[r?.id,r?.runtime?.providerStatus]),!r)return Fu(oc,{title:"FindJob 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=findjob"});let _=qZ(r),$=WZ(r),c=NZ(r),A=n.summary||{},j=wZ(n.jobs),F=LZ(n.drafts),U=n.jobs?._unidesk?.arrayLimits?.jobs;return Fu("div",{className:"findjob-page","data-testid":"findjob-page"},Fu(I3,{title:"FindJob 工作台",eyebrow:"D601 用户服务",loading:n.loading,actions:Fu("div",{className:"panel-actions"},Fu("button",{type:"button",className:"ghost-btn",onClick:y,disabled:n.loading,"data-testid":"findjob-refresh-button"},n.loading?"刷新中":"刷新"),Fu(g3,{title:"FindJob 用户服务",data:r,onOpen:f,testId:"raw-findjob-service"}))},Fu("div",{className:"findjob-hero"},Fu("div",null,Fu("div",{className:"node-version-line"},Fu(k3,{status:_.providerStatus==="online"?"online":"warn"},_.providerStatus||"unknown"),Fu("span",null,r.providerId),Fu("span",null,c.public?"公网暴露":"仅 UniDesk frontend 代理访问")),Fu("p",{className:"muted paragraph"},r.description)),Fu("div",{className:"microservice-ref-card"},Fu("span",null,"Repo"),Fu("strong",null,$.url||"--"),Fu("code",null,$.commitId||"--")),Fu("div",{className:"microservice-ref-card"},Fu("span",null,"D601 Docker"),Fu("strong",null,`${c.nodeBindHost||"--"}:${c.nodePort||"--"}`),Fu("code",null,`${$.composeFile||"--"} / ${$.composeService||"--"}`))),Fu(yf,{error:n.error,wide:!0})),Fu("div",{className:"findjob-grid"},Fu(I3,{title:"岗位指标",eyebrow:n.refreshedAt?`Updated ${$f(n.refreshedAt)}`:"Summary",loading:n.loading},Fu("div",{className:"metric-grid"},Fu(En,{label:"岗位总量",value:z1(A,"totalJobs"),hint:"tracked jobs",tone:"ok"}),Fu(En,{label:"原始岗位",value:z1(A,"rawJobs"),hint:"raw queue"}),Fu(En,{label:"已验证",value:z1(A,"verifiedJobs"),hint:"verified set"}),Fu(En,{label:"优先处理",value:z1(A,"prioritizedJobs"),hint:"prioritized"}),Fu(En,{label:"过期",value:z1(A,"staleJobs"),hint:"stale jobs",tone:"warn"}),Fu(En,{label:"无效",value:z1(A,"invalidJobs"),hint:"invalid jobs",tone:"warn"}),Fu(En,{label:"上海",value:z1(A,"shanghaiJobs"),hint:"city filter"}),Fu(En,{label:"Health",value:n.health?.ok?"OK":"--",hint:"D601 /api/health"})),Fu("div",{className:"panel-actions inline-actions"},Fu(g3,{title:"FindJob Summary",data:A,onOpen:f,testId:"raw-findjob-summary"}))),Fu(I3,{title:"近期岗位",eyebrow:U?`${U.returnedLength}/${U.originalLength} Preview`:`${j.length} Preview`,loading:n.loading},j.length===0?Fu(oc,{title:"暂无岗位预览",text:"等待 D601 findjob backend 返回 /api/jobs"}):Fu("div",{className:"table-wrap findjob-job-table"},Fu("table",null,Fu("thead",null,Fu("tr",null,Fu("th",null,"优先级"),Fu("th",null,"状态"),Fu("th",null,"单位"),Fu("th",null,"职位"),Fu("th",null,"城市"),Fu("th",null,"阶段"),Fu("th",null,"截止"),Fu("th",null,"证据"))),Fu("tbody",null,j.map((Q)=>Fu("tr",{key:Q.id},Fu("td",null,Fu(k3,{status:String(Q.priority||"").toLowerCase()||"unknown"},Q.priority||"--")),Fu("td",null,Fu(k3,{status:String(Q.status||"").toLowerCase()||"unknown"},Q.status||"--")),Fu("td",null,Q.organization_name||"--",Fu("code",null,Q.id||"--")),Fu("td",null,Q.display_title||Q.title||"--"),Fu("td",null,Q.display_city||Q.city||"--"),Fu("td",null,Q.workflow_stage||"--"),Fu("td",null,Q.deadline||"--"),Fu("td",null,Q.evidence_url?Fu("a",{href:Q.evidence_url,target:"_blank",rel:"noreferrer"},"打开"):Fu("span",{className:"muted"},"无"))))))),Fu("div",{className:"panel-actions inline-actions"},Fu(g3,{title:"FindJob Jobs Preview",data:n.jobs,onOpen:f,testId:"raw-findjob-jobs"}))),Fu(I3,{title:"草稿与报告",eyebrow:`${F.length} Drafts`,loading:n.loading},F.length===0?Fu(oc,{title:"暂无草稿",text:"D601 findjob backend 未返回 drafts"}):Fu("div",{className:"draft-list"},F.map((Q)=>Fu("article",{key:Q.id,className:"draft-card"},Fu("div",{className:"node-card-head"},Fu("strong",null,Q.id),Fu(k3,{status:Q.status},Q.status||"--")),Fu("div",{className:"docker-meta compact"},Fu("span",null,Q.workflow_stage||"--"),Fu("span",null,`jobs ${Q.counts?.jobs??0}`),Fu("span",null,`reports ${Q.counts?.reports??0}`)),Fu("span",null,Q.latestReportPath||"暂无报告"),Fu("code",null,Nu(Q.updated_at||Q.updatedAt))))),Fu("div",{className:"panel-actions inline-actions"},Fu(g3,{title:"FindJob Drafts",data:n.drafts,onOpen:f,testId:"raw-findjob-drafts"})))))}var h_=Mu(Qf(),1);var M=h_.default.createElement,{useEffect:KZ}=h_.default,dc=h_.default.useState;function C_(u){let f=Number(u);return Number.isFinite(f)?`${Math.max(0,Math.min(100,f)).toFixed(1)}%`:"--"}function u5(u){if(u===null||u===void 0||u==="")return"--";let f=Number(u);if(!Number.isFinite(f))return"--";if(f<60)return`${Math.max(0,Math.round(f))}s`;if(f<3600)return`${Math.floor(f/60)}m ${Math.round(f%60)}s`;return`${Math.floor(f/3600)}h ${Math.floor(f%3600/60)}m`}function f5(u,f=2){let l=Number(u);if(!Number.isFinite(l))return u===!1?"false":u===!0?"true":"--";let r=Math.abs(l);if(Number.isInteger(l)||r>=1000)return l.toLocaleString("zh-CN",{maximumFractionDigits:0});if(r>=1)return l.toLocaleString("zh-CN",{maximumFractionDigits:f});return l.toLocaleString("zh-CN",{maximumFractionDigits:Math.max(f,6)})}function x_(u){if(u===null||u===void 0||u==="")return"--";if(typeof u==="boolean")return u?"true":"false";if(typeof u==="number")return f5(u,4);if(Array.isArray(u))return u.map((f)=>x_(f)).join(" x ");if(typeof u==="object")return"已上报";return String(u)}function a3(u){let f=Number(u);if(!Number.isFinite(f)||f<=0)return"--";let l=f>=100?0:f>=10?1:2;return`${f.toLocaleString("zh-CN",{maximumFractionDigits:l})} epoch/h`}function o3(u){return u.replace(/[^a-zA-Z0-9_-]/g,"-")}function hl(u){return u&&typeof u==="object"&&!Array.isArray(u)?u:{}}function R_({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return M("span",{className:`status-badge ${l}`},f||u||"unknown")}function Zn({label:u,value:f,hint:l,tone:r}){return M("article",{className:`metric-card ${r||""}`},M("div",{className:"metric-label"},u),M("div",{className:"metric-value"},f),M("div",{className:"metric-hint"},l))}function ec({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return M("section",{className:`panel ${n||""}`},M("div",{className:"panel-head"},M("div",null,f?M("p",{className:"panel-eyebrow"},f):null,M(nf,{title:u,loading:i})),l?M("div",{className:"panel-actions"},l):null),M("div",{className:"panel-body"},r))}function hi({title:u,data:f,onOpen:l,testId:r}){return M("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:(n)=>{n?.stopPropagation?.(),l(u,f)}},"查看原始JSON")}function Y0({title:u,text:f}){return M("div",{className:"empty-state"},M("strong",null,u),M("span",null,f))}function GZ(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function zZ(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function TZ(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function EZ(u){return u?.counts&&typeof u.counts==="object"&&!Array.isArray(u.counts)?u.counts:{}}function ZZ(u){return Array.isArray(u?.jobs)?u.jobs.slice(0,240):[]}function OZ(u){return Array.isArray(u?.projects)?u.projects.slice(0,1000):[]}function d3(u){return Array.isArray(u?.projects)?u.projects:[]}function HZ(u,f){if(Array.isArray(f?.gpu))return f.gpu;if(Array.isArray(u?.gpu))return u.gpu;return[]}function $r(u,f){return`${u}/microservices/met-nonlinear/proxy${f}`}function BQ(u){return u.startedAt&&u.finishedAt?u5((Date.parse(u.finishedAt)-Date.parse(u.startedAt))/1000):"--"}function BZ(u){let f=u.progress||{};if(f.etaSeconds!==null&&f.etaSeconds!==void 0&&f.etaSeconds!==""){let y=Number(f.etaSeconds);if(Number.isFinite(y))return Math.max(0,y)}let l=Number(f.currentEpoch),r=Number(f.epochTarget??u.epochTarget),n=Date.parse(u.startedAt||"");if(!Number.isFinite(l)||l<=0||!Number.isFinite(r)||r<=l||!Number.isFinite(n))return null;let i=Math.max(0,(Date.now()-n)/1000);if(i<=0)return null;return Math.max(0,i/l*(r-l))}function VQ(u){let f=u.progress||{},l=Number(f.epochPerHour);if(Number.isFinite(l)&&l>0)return l;let r=Date.parse(u.startedAt||""),n=["succeeded","failed","canceled"].includes(u.status)?Date.parse(u.finishedAt||""):Date.now();if(!Number.isFinite(r)||!Number.isFinite(n)||n<=r)return null;let i=Number(f.currentEpoch??u.epochTarget);if(!Number.isFinite(i)||i<=0)return null;return i/((n-r)/3600000)}function DQ(u){if(u==="staged")return"待启动";if(u==="queued")return"排队中";if(u==="running")return"训练中";if(u==="succeeded")return"已完成";if(u==="failed")return"失败";if(u==="canceled")return"已取消";return u||"unknown"}function XQ(u,f,l){return{name:u,path:f,depth:l,count:0,children:[],project:null}}function VZ(u){let f=XQ("","",-1);for(let r of u){let i=String(r?.projectPath||"").replace(/\\/g,"/").split("/").filter(Boolean);if(i.length===0)continue;let y=f,_=[];for(let[$,c]of i.entries()){_.push(c);let A=_.join("/"),j=y.children.find((F)=>F.path===A);if(!j)j=XQ(c,A,$),y.children.push(j);if($===i.length-1)j.project=r;y=j}}let l=(r)=>{let n=r.children.reduce((i,y)=>i+l(y),0);return r.count=(r.project?1:0)+n,r.children.sort((i,y)=>{if(Boolean(i.project)!==Boolean(y.project))return i.project?1:-1;return i.name.localeCompare(y.name,"zh-CN",{numeric:!0,sensitivity:"base"})}),r.count};return l(f),f}function DZ(u){let f=hl(u.data);return hl(f.project).projectPath?hl(f.project):f}function XZ(u){return hl(hl(u.data).job)}function YQ({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((P)=>P.id==="met-nonlinear")||null,[n,i]=dc({loading:!1,actionBusy:!1,error:"",health:null,summary:null,queue:null,projects:null,history:null,images:null,refreshedAt:null}),[y,_]=dc({loading:!1,error:"",kind:"",key:"",title:"",data:null}),[$,c]=dc(()=>({activeTab:"projects",selectedProjects:{},expandedProjectDirs:{},sourceProject:"",forkCount:1,forkEpochs:200,forkPrefix:`ui_fork_${Date.now()}`,maxConcurrency:3,targetGpuName:"2080 Ti",actionMessage:""}));function A(P){c((e)=>({...e,...P}))}async function j(P=$.activeTab){if(!r)return;i((e)=>({...e,loading:!0,error:""}));try{let e=[["health",Eu(`${l}/microservices/met-nonlinear/health`)],["summary",Eu($r(l,"/api/summary"))]];if(P==="projects")e.push(["projectsRoot",Eu($r(l,"/api/projects?root=projects&limit=500"))]),e.push(["exProjectsRoot",Eu($r(l,"/api/projects?root=ex_projects&limit=500"))]);if(P==="current"||P==="completed"||P==="failed")e.push(["queue",Eu($r(l,"/api/queue"))]);if(P==="completed"||P==="failed")e.push(["history",Eu($r(l,"/api/history"))]);if(P==="gpu")e.push(["images",Eu($r(l,"/api/images"))]);let uu=Object.fromEntries(await Promise.all(e.map(async([g,Qu])=>[g,await Qu]))),Wu={loading:!1,actionBusy:!1,error:"",health:uu.health,summary:uu.summary,refreshedAt:new Date};if(uu.projectsRoot||uu.exProjectsRoot){let{projectsRoot:g,exProjectsRoot:Qu}=uu;Wu.projects={ok:g?.ok!==!1&&Qu?.ok!==!1,roots:[{root:"projects",count:d3(g).length},{root:"ex_projects",count:d3(Qu).length}],projects:[...d3(g),...d3(Qu)]}}if(uu.queue)Wu.queue=uu.queue;if(uu.history)Wu.history=uu.history;if(uu.images)Wu.images=uu.images;i((g)=>({...g,...Wu}))}catch(e){i((uu)=>({...uu,loading:!1,actionBusy:!1,error:Hu(e,"MET Nonlinear 加载失败")}))}}async function F(P,e){i((uu)=>({...uu,actionBusy:!0,error:""})),A({actionMessage:`${P}...`});try{let uu=await e();A({actionMessage:uu||`${P}完成`}),await j()}catch(uu){i((Wu)=>({...Wu,actionBusy:!1,error:Hu(uu,`${P}失败`)}))}}async function U(){await F("保存并发设置",async()=>{await Eu($r(l,"/api/queue/settings"),{method:"PUT",body:JSON.stringify({maxConcurrency:Number($.maxConcurrency),targetGpuName:$.targetGpuName})})})}function Q(){return Object.entries($.selectedProjects).filter(([,P])=>P).map(([P])=>P)}async function w(){let P=Q();if(P.length===0)throw Error("请先选择至少一个 project");await F("加入待启动队列",async()=>{await Eu($r(l,"/api/queue"),{method:"POST",body:JSON.stringify({projectPaths:P,maxConcurrency:Number($.maxConcurrency),targetGpuName:$.targetGpuName,start:!1})}),A({activeTab:"current",selectedProjects:{}})})}async function L(){let P=$.sourceProject||m[0]?.projectPath;if(!P)throw Error("请先选择源 project");await F("Fork Project",async()=>{let e=await Eu($r(l,"/api/projects/fork"),{method:"POST",body:JSON.stringify({sourceProject:P,count:Number($.forkCount),epochs:Number($.forkEpochs),prefix:$.forkPrefix})}),uu=Array.isArray(e.projectPaths)?e.projectPaths:[],Wu=uu.reduce((g,Qu)=>{return g[Qu]=!0,g},{...$.selectedProjects});return A({selectedProjects:Wu}),`已 fork ${uu.length} 个 project,并已自动勾选;请确认后点击加入待启动队列。`})}async function J(){await F("启动队列",async()=>{await Eu($r(l,"/api/queue/start"),{method:"POST",body:JSON.stringify({maxConcurrency:Number($.maxConcurrency),targetGpuName:$.targetGpuName})}),A({activeTab:"current"})})}async function N(P){await F("取消任务",async()=>{await Eu($r(l,`/api/jobs/${encodeURIComponent(P.id)}/cancel`),{method:"POST",body:JSON.stringify({})})})}async function q(P){let e=String(P?.projectPath||"");if(!e)return;_({loading:!0,error:"",kind:"project",key:e,title:e,data:null});try{let uu=await Eu($r(l,`/api/projects/config?path=${encodeURIComponent(e)}`));_({loading:!1,error:"",kind:"project",key:e,title:e,data:uu})}catch(uu){_({loading:!1,error:Hu(uu,"Project 详情加载失败"),kind:"project",key:e,title:e,data:null})}}async function W(P){let e=String(P?.id||"");if(!e)return;_({loading:!0,error:"",kind:"job",key:e,title:P.projectPath||e,data:null});try{let uu=await Eu($r(l,`/api/jobs/${encodeURIComponent(e)}`));_({loading:!1,error:"",kind:"job",key:e,title:uu?.job?.projectPath||P.projectPath||e,data:uu})}catch(uu){_({loading:!1,error:Hu(uu,"Job 详情加载失败"),kind:"job",key:e,title:P.projectPath||e,data:null})}}if(KZ(()=>{j($.activeTab)},[r?.id,r?.runtime?.providerStatus,$.activeTab]),!r)return M(Y0,{title:"MET Nonlinear 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=met-nonlinear"});let z=GZ(r),Z=TZ(r),H=zZ(r),E=EZ(n.queue?.queue||n.summary?.queue),D=HZ(n.health,n.queue),h=n.health?.targetGpu||n.summary?.targetGpu||D.find((P)=>String(P.name||"").includes("2080")),B=n.images?.mlImage||n.health?.image||{},X=ZZ(n.queue),m=OZ(n.projects),O=VZ(m),p=$.sourceProject||m[0]?.projectPath||"",Y=X.filter((P)=>["staged","queued","running"].includes(P.status)),v=X.filter((P)=>P.status==="succeeded"),T=X.filter((P)=>["failed","canceled"].includes(P.status)),S=Array.isArray(n.history?.jobs)?n.history.jobs.slice(0,120):[],k=[{id:"projects",label:"项目库",count:m.length},{id:"current",label:"当前队列",count:Y.length||Number(E.staged||0)+Number(E.queued||0)+Number(E.running||0)},{id:"completed",label:"已完成",count:v.length||Number(E.succeeded||0)},{id:"failed",label:"失败诊断",count:T.length||Number(E.failed||0)+Number(E.canceled||0)},{id:"gpu",label:"GPU/镜像",count:D.length}];function I(P,e){if(P.length===0)return M(Y0,{title:e==="current"?"当前队列为空":"暂无记录",text:e==="current"?"从项目库选择或 fork project 后先加入待启动队列,再启动队列。":"终态任务会显示耗时、exit code 和失败诊断。"});return M("div",{className:"table-wrap met-job-table"},M("table",null,M("thead",null,M("tr",null,M("th",null,"状态"),M("th",null,"Project"),M("th",null,"Epoch"),M("th",null,"速度"),M("th",null,"ETA/耗时"),M("th",null,"GPU"),M("th",null,"Exit"),M("th",null,"更新时间"),M("th",null,"操作"))),M("tbody",null,P.map((uu)=>{let Wu=uu.progress||{},g=["staged","queued","running"].includes(uu.status),Qu=y.kind==="job"&&y.key===uu.id;return M("tr",{key:uu.id,className:`met-click-row ${Qu?"active":""}`,onClick:()=>W(uu),"data-testid":`met-job-row-${o3(uu.id)}`},M("td",null,M(R_,{status:uu.status},DQ(uu.status))),M("td",null,M("button",{type:"button",className:"met-inline-link",onClick:(Tu)=>{Tu.stopPropagation(),W(uu)}},uu.projectPath),M("code",null,uu.id)),M("td",null,M("span",null,`${Wu.currentEpoch??"--"} / ${Wu.epochTarget??uu.epochTarget??"--"}`),M("div",{className:"met-progress"},M("span",{style:{width:C_(Wu.progressPercent)}}))),M("td",null,M("strong",null,a3(VQ(uu)))),M("td",null,uu.status==="succeeded"||uu.status==="failed"||uu.status==="canceled"?BQ(uu):uu.status==="running"?`ETA ${u5(BZ(uu))}`:"--"),M("td",null,uu.gpuName||"--"),M("td",null,uu.exitCode??"--"),M("td",null,Nu(uu.updatedAt)),M("td",null,g?M("button",{type:"button",className:"ghost-btn mini",onClick:(Tu)=>{Tu.stopPropagation(),N(uu)},disabled:n.actionBusy},"取消"):null,M(hi,{title:`MET Job ${uu.id}`,data:uu,onOpen:f,testId:`raw-met-job-${uu.id}`})))}))))}function b(){return M("div",{className:"met-queue-summary","data-testid":"met-current-summary"},M(R_,{status:"staged"},`待启动 ${E.staged??0}`),M(R_,{status:"queued"},`排队中 ${E.queued??0}`),M(R_,{status:"running"},`训练中 ${E.running??0}`),M("span",null,`最大并发 ${n.summary?.queue?.maxConcurrency??n.queue?.queue?.maxConcurrency??$.maxConcurrency}`),M("span",null,`目标 GPU ${n.summary?.queue?.targetGpuName??n.queue?.queue?.targetGpuName??$.targetGpuName}`))}function o(P,e){let uu=$.expandedProjectDirs[P];return uu===void 0?e<2:Boolean(uu)}function s(P,e){let uu=o(P,e);A({expandedProjectDirs:{...$.expandedProjectDirs,[P]:!uu}})}function x(P){let e=8+Math.max(0,P.depth)*16;if(Boolean(P.project)){let g=P.project,Qu=Boolean($.selectedProjects[g.projectPath]),Tu=y.kind==="project"&&y.key===g.projectPath;return M("div",{key:P.path,className:`met-tree-row project ${Qu?"selected":""} ${Tu?"active":""}`,style:{paddingLeft:e},onClick:()=>q(g),"data-testid":`met-project-node-${o3(g.projectPath)}`},M("div",{className:"met-tree-name"},M("input",{type:"checkbox",checked:Qu,onClick:(Zu)=>Zu.stopPropagation(),onChange:(Zu)=>A({selectedProjects:{...$.selectedProjects,[g.projectPath]:Zu.target.checked}}),"data-testid":`met-project-checkbox-${o3(g.projectPath)}`}),M("button",{type:"button",className:"met-inline-link project-path",onClick:(Zu)=>{Zu.stopPropagation(),q(g)}},P.name)),M("span",null,g.useModel||"--"),M("span",null,g.epochTrain??"--"),M("span",null,C_(g.progress?.progressPercent)),M("span",null,a3(g.progress?.epochPerHour)))}let Wu=o(P.path,P.depth);return M(h_.default.Fragment,{key:P.path},M("div",{className:"met-tree-row folder",style:{paddingLeft:e},"data-testid":`met-project-folder-${o3(P.path)}`},M("button",{type:"button",className:"met-tree-toggle",onClick:()=>s(P.path,P.depth),"aria-label":Wu?`折叠 ${P.path}`:`展开 ${P.path}`},Wu?"-":"+"),M("strong",null,P.name),M("span",{className:"met-tree-count"},`${P.count} projects`)),Wu?P.children.map((g)=>x(g)):null)}function fu(P){return M("div",{className:"met-detail-kv"},P.map((e)=>M("div",{key:e.label,className:"met-detail-kv-item"},M("span",null,e.label),M("strong",null,x_(e.value)),e.hint?M("small",null,e.hint):null)))}function $u(P,e){return M("div",{className:"met-detail-section"},M("h3",null,P),fu(e))}function tu(P){if(!Array.isArray(P)||P.length===0)return M(Y0,{title:"模型层未上报",text:"等待 data/model_info.json 或 compute_analysis.json 生成。"});return M("div",{className:"table-wrap met-layer-table"},M("table",null,M("thead",null,M("tr",null,M("th",null,"Layer"),M("th",null,"Type"),M("th",null,"Params"),M("th",null,"Trainable"),M("th",null,"Compute"))),M("tbody",null,P.slice(0,18).map((e,uu)=>M("tr",{key:`${e.name||"layer"}-${uu}`},M("td",null,e.name||`#${uu+1}`),M("td",null,e.type||"--"),M("td",null,f5(e.num_params)),M("td",null,e.trainable===void 0?"--":String(Boolean(e.trainable))),M("td",null,f5(e.compute?.total??e.estimated_cost?.weighted_units?.total)))))))}function ju(P){let e=Array.isArray(P)?P:[];if(e.length===0)return M(Y0,{title:"data/ 暂无文件",text:"训练或评估完成后会生成 training_state、metrics、model_info 等文件。"});return M("div",{className:"met-file-chip-grid"},e.slice(0,48).map((uu)=>M("span",{key:uu},uu)),e.length>48?M("span",null,`+${e.length-48}`):null)}function Gu(P){let e=String(P||"").replace(/\x1b\[[0-9;]*[A-Za-z]/g,"").split(/\r?\n/).map((uu)=>uu.trim()).filter(Boolean).slice(-12);if(e.length===0)return M(Y0,{title:"暂无日志尾部",text:"该任务未上报 logTail 或日志已轮转。"});return M("div",{className:"met-log-lines"},e.map((uu,Wu)=>M("div",{key:`${Wu}-${uu.slice(0,16)}`},uu)))}function qu(){if(y.loading)return M("section",{className:"met-detail-panel","data-testid":"met-detail-panel"},M("div",{className:"panel-head compact"},M("div",null,M("p",{className:"panel-eyebrow"},"Detail Loading"),M(nf,{title:"详情加载中",loading:!0}))),M(Y0,{title:"详情加载中",text:y.title||"正在读取 D601 data/ 和 config.json"}));if(y.error)return M("section",{className:"met-detail-panel","data-testid":"met-detail-panel"},M(yf,{error:y.error,wide:!0}));if(!y.data)return M("section",{className:"met-detail-panel muted","data-testid":"met-detail-panel"},M(Y0,{title:"选择一个项目或任务查看详情",text:"项目库、当前队列、已完成和失败诊断中的行都可以点击;默认只展示结构化字段,原始 JSON 需显式点击按钮。"}));let P=DZ(y),e=XZ(y),uu=hl(P.config),Wu=hl(P.progress||e.progress),g=hl(P.data),Qu=hl(P.metrics||g.metrics||Wu.trainingInfo?.evaluation_metrics),Tu=hl(g.trainingInfo||Wu.trainingInfo),Zu=hl(g.trainingState),Xu=hl(P.model||g.model),ff=Array.isArray(Xu.modelSummary)&&Xu.modelSummary.length>0?Xu.modelSummary:Xu.computeLayers,_f=hl(Tu.evaluation_metrics),Jf=y.kind==="job"?"训练任务详情":"Project 详情";return M("section",{className:"met-detail-panel","data-testid":"met-detail-panel"},M("div",{className:"panel-head compact"},M("div",null,M("p",{className:"panel-eyebrow"},y.kind==="job"?"Job + Project Detail":"Project Library Detail"),M(nf,{title:Jf}),M("code",null,P.projectPath||e.projectPath||y.title)),M("div",{className:"panel-actions"},M(hi,{title:`MET ${Jf}`,data:y.data,onOpen:f,testId:"raw-met-detail"}))),y.kind==="job"?$u("任务状态",[{label:"Job ID",value:e.id},{label:"状态",value:DQ(e.status)},{label:"GPU",value:e.gpuName},{label:"Exit Code",value:e.exitCode},{label:"耗时",value:BQ(e)},{label:"训练速度",value:a3(VQ({...e,progress:Wu}))}]):null,$u("config.json",[{label:"use_model",value:uu.use_model},{label:"epoch_train",value:uu.epoch_train},{label:"step_per_epoch",value:uu.step_per_epoch},{label:"learning_rate",value:uu.learning_rate},{label:"using_gpu",value:uu.using_gpu},{label:"use_points",value:uu.use_points},{label:"sample_rate",value:uu.sample_rate},{label:"time_clipped_s",value:uu.time_clipped_s},{label:"H_UNITS",value:uu.H_UNITS},{label:"INNER_KAN_UNITS",value:uu.INNER_KAN_UNITS},{label:"INNER_KAN_LAYERS",value:uu.INNER_KAN_LAYERS},{label:"GRID_SIZE",value:uu.GRID_SIZE},{label:"SPLINE_ORDER",value:uu.SPLINE_ORDER},{label:"USE_FAST_MODEL",value:uu.USE_FAST_MODEL},{label:"IIR_TRAINABLE",value:uu.IIR_TRAINABLE}]),$u("data/ 训练状态",[{label:"Epoch",value:`${Wu.currentEpoch??Zu.current_epoch??Zu.completed_epoch??"--"} / ${Wu.epochTarget??uu.epoch_train??"--"}`},{label:"Progress",value:C_(Wu.progressPercent)},{label:"Last Loss",value:Wu.lastLoss??Zu.loss},{label:"Last Val Loss",value:Wu.lastValLoss??Zu.val_loss},{label:"Min Loss",value:Tu.min_loss??Zu.min_loss},{label:"Min Val Loss",value:Tu.min_val_loss??Zu.min_val_loss},{label:"Log Lines",value:Wu.logLineCount},{label:"ETA",value:u5(Wu.etaSeconds??Zu.remaining_time)},{label:"训练速度",value:a3(Wu.epochPerHour??Zu.smoothed_speed)},{label:"Training Alive",value:Zu.training_alive}]),$u("模型参数",[{label:"Model Type",value:Xu.modelType??uu.use_model},{label:"Total Params",value:Xu.totalParams,hint:Xu.totalParams===null||Xu.totalParams===void 0?"未上报":"data/model_info.json"},{label:"Trainable",value:Xu.trainableParams},{label:"Non-trainable",value:Xu.nonTrainableParams},{label:"Compute Cost",value:Xu.computeCost},{label:"Estimate Status",value:Xu.estimateStatus},{label:"Unsupported Layers",value:Xu.unsupportedLayerCount}]),$u("指标",[{label:"train_loss",value:Qu.train_loss??_f.train_loss},{label:"val_loss",value:Qu.val_loss??_f.val_loss},{label:"train_mae",value:Qu.train_mae??_f.train_mae},{label:"val_mae",value:Qu.val_mae??_f.val_mae},{label:"train_afmae",value:Qu.train_afmae??_f.train_afmae},{label:"val_afmae",value:Qu.val_afmae??_f.val_afmae},{label:"freq_drift_hz",value:Qu.freq_drift_hz},{label:"sens_drift_percent",value:Qu.sens_drift_percent},{label:"linearity_percent",value:Qu.linearity_percent},{label:"weights_source",value:Qu.weights_source??_f.weights_source},{label:"lr min/mean/max",value:`${x_(Tu.learning_rate_min)} / ${x_(Tu.learning_rate_mean)} / ${x_(Tu.learning_rate_max)}`}]),M("div",{className:"met-detail-section"},M("h3",null,"模型层"),tu(ff)),M("div",{className:"met-detail-section"},M("h3",null,"data/ 文件"),ju(g.files)),y.kind==="job"?M("div",{className:"met-detail-section"},M("h3",null,"日志尾部"),Gu(hl(y.data).logTail)):null)}return M("div",{className:"met-page","data-testid":"met-nonlinear-page"},M(ec,{title:"MET Nonlinear 训练编排",eyebrow:"D601 GPU 用户服务",loading:n.loading||n.actionBusy,actions:M("div",{className:"panel-actions"},M("button",{type:"button",className:"ghost-btn",onClick:j,disabled:n.loading,"data-testid":"met-refresh-button"},n.loading?"刷新中":"刷新"),M(hi,{title:"MET Nonlinear 用户服务",data:r,onOpen:f,testId:"raw-met-service"}))},M("div",{className:"findjob-hero"},M("div",null,M("div",{className:"node-version-line"},M(R_,{status:z.providerStatus==="online"?"online":"warn"},z.providerStatus||"unknown"),M("span",null,r.providerId),M("span",null,H.public?"公网暴露":"仅 UniDesk frontend 代理访问")),M("p",{className:"muted paragraph"},r.description)),M("div",{className:"microservice-ref-card"},M("span",null,"Repo"),M("strong",null,Z.url||"--"),M("code",null,Z.commitId||"--")),M("div",{className:"microservice-ref-card"},M("span",null,"D601 Docker"),M("strong",null,`${H.nodeBindHost||"--"}:${H.nodePort||"--"}`),M("code",null,`${Z.composeFile||"--"} / ${Z.containerName||"--"}`))),M(yf,{error:n.error,wide:!0}),$.actionMessage?M("div",{className:"met-action-log","data-testid":"met-action-message"},$.actionMessage):null),M("div",{className:"met-grid"},M(ec,{title:"核心状态",eyebrow:n.refreshedAt?`Updated ${$f(n.refreshedAt)}`:"Queue + GPU",loading:n.loading},M("div",{className:"metric-grid"},M(Zn,{label:"Staged",value:E.staged??0,hint:"加入队列未开始",tone:Number(E.staged||0)>0?"warn":""}),M(Zn,{label:"Queued",value:E.queued??0,hint:"排队等待调度",tone:Number(E.queued||0)>0?"warn":""}),M(Zn,{label:"Running",value:E.running??0,hint:`max ${n.summary?.queue?.maxConcurrency??n.queue?.queue?.maxConcurrency??"--"}`,tone:Number(E.running||0)>0?"ok":""}),M(Zn,{label:"Succeeded",value:E.succeeded??0,hint:"已完成"}),M(Zn,{label:"Failed",value:E.failed??0,hint:"需要诊断",tone:Number(E.failed||0)>0?"warn":""}),M(Zn,{label:"2080Ti Free",value:h?C_(Number(h.freeRatio)*100):"--",hint:h?`${h.memoryFreeMiB}/${h.memoryTotalMiB} MiB`:"等待 GPU 上报"}),M(Zn,{label:"ML Image",value:B.present?"READY":"MISSING",hint:B.image||"met-nonlinear-ml:tf26",tone:B.present?"ok":"warn"}),M(Zn,{label:"Health",value:n.health?.ok?"OK":"--",hint:"D601 /health"}))),M(ec,{title:"队列控制",eyebrow:"Downloader-like staging",loading:n.actionBusy},M("div",{className:"met-control-strip"},M("label",null,"最大并发",M("input",{type:"number",min:1,max:16,value:$.maxConcurrency,"data-testid":"met-max-concurrency-input",onChange:(P)=>A({maxConcurrency:P.target.value})})),M("label",null,"目标 GPU",M("input",{value:$.targetGpuName,"data-testid":"met-target-gpu-input",onChange:(P)=>A({targetGpuName:P.target.value})})),M("button",{type:"button",className:"ghost-btn",onClick:U,disabled:n.actionBusy,"data-testid":"met-save-settings-button"},"保存设置"),M("button",{type:"button",className:"primary-btn",onClick:J,disabled:n.actionBusy||Number(E.staged||0)===0,"data-testid":"met-start-queue-button"},"启动队列")),M("p",{className:"muted paragraph"},"Project 先进入待启动队列,不会立即训练;点击启动队列后才切换为排队中,并由 D601 scheduler 按最大并发和 2080Ti 显存策略调度。")),M("section",{className:"panel met-workspace"},M("div",{className:"met-tabs",role:"tablist"},k.map((P)=>M("button",{key:P.id,type:"button",className:$.activeTab===P.id?"active":"",onClick:()=>A({activeTab:P.id}),"data-testid":`met-tab-${P.id}`},`${P.label} ${P.count}`))),M("div",{className:"panel-body"},$.activeTab==="projects"?M("div",{className:"met-form-grid","data-testid":"met-projects-pane"},M("div",{className:"met-fork-card"},M("h3",null,"Fork Project"),M("label",null,"源 Project",M("select",{value:p,"data-testid":"met-source-project-select",onChange:(P)=>A({sourceProject:P.target.value})},m.map((P)=>M("option",{key:P.projectPath,value:P.projectPath},`${P.projectPath} · ${P.useModel||"model?"}`)))),M("label",null,"Fork 数量",M("input",{type:"number",min:1,max:100,value:$.forkCount,"data-testid":"met-fork-count-input",onChange:(P)=>A({forkCount:P.target.value})})),M("label",null,"训练轮数",M("input",{type:"number",min:1,max:1e5,value:$.forkEpochs,"data-testid":"met-fork-epochs-input",onChange:(P)=>A({forkEpochs:P.target.value})})),M("label",null,"目标前缀",M("input",{value:$.forkPrefix,"data-testid":"met-fork-prefix-input",onChange:(P)=>A({forkPrefix:P.target.value})})),M("button",{type:"button",className:"primary-btn",onClick:L,disabled:n.actionBusy||!p,"data-testid":"met-fork-button"},"Fork Project"),M("p",{className:"muted paragraph"},"Fork 只创建新 Project 并自动勾选,不会直接训练;需要在右侧确认后加入待启动队列。")),M("div",{className:"met-project-list"},M("div",{className:"panel-head compact"},M("div",null,M("p",{className:"panel-eyebrow"},`Existing Projects · ${(n.projects?.roots||[]).map((P)=>`${P.root} ${P.count}`).join(" / ")}`),M(nf,{title:"选择已有 Project",loading:n.loading||n.actionBusy})),M("button",{type:"button",className:"ghost-btn",onClick:w,disabled:n.actionBusy||Q().length===0,"data-testid":"met-stage-selected-button"},`加入待启动队列 (${Q().length})`)),m.length===0?M(Y0,{title:"暂无 project",text:"等待 D601 返回 /api/projects"}):M("div",{className:"met-project-table","data-testid":"met-project-tree"},M("div",{className:"met-tree-header"},M("span",null,"文件树 Project"),M("span",null,"Model"),M("span",null,"Epochs"),M("span",null,"Progress"),M("span",null,"速度")),O.children.map((P)=>x(P)))),qu()):null,$.activeTab==="current"?M("div",{"data-testid":"met-current-pane"},b(),I(Y,"current"),qu(),M("div",{className:"panel-actions inline-actions"},M(hi,{title:"MET Queue",data:n.queue,onOpen:f,testId:"raw-met-queue"}))):null,$.activeTab==="completed"?M("div",{"data-testid":"met-completed-pane"},I(v.length>0?v:S.filter((P)=>P.status==="succeeded"),"completed"),qu()):null,$.activeTab==="failed"?M("div",{"data-testid":"met-failed-pane"},I(T.length>0?T:S.filter((P)=>["failed","canceled"].includes(P.status)),"failed"),qu(),M("div",{className:"panel-actions inline-actions"},M(hi,{title:"MET History",data:n.history,onOpen:f,testId:"raw-met-history"}))):null,$.activeTab==="gpu"?M("div",{className:"met-gpu-pane","data-testid":"met-gpu-pane"},D.length===0?M(Y0,{title:"暂无 GPU 上报",text:"等待 D601 met-nonlinear-ts 或 ML image 提供 nvidia-smi 数据"}):M("div",{className:"table-wrap"},M("table",null,M("thead",null,M("tr",null,M("th",null,"Index"),M("th",null,"Name"),M("th",null,"Free"),M("th",null,"Policy"))),M("tbody",null,D.map((P)=>M("tr",{key:P.index},M("td",null,P.index),M("td",null,P.name),M("td",null,`${P.memoryFreeMiB} / ${P.memoryTotalMiB} MiB`,M("div",{className:"met-progress"},M("span",{style:{width:C_(Number(P.freeRatio)*100)}}))),M("td",null,String(P.name||"").includes("2080")?"target 2080Ti, <20% 限制并发":"non-target")))))),M("div",{className:"panel-actions inline-actions"},M(hi,{title:"MET Images",data:n.images,onOpen:f,testId:"raw-met-images"}))):null))))}var u6=[{id:"ops",label:"运行总览",code:"OPS",tabs:[{id:"status",label:"态势总览"},{id:"performance",label:"性能面板"},{id:"events",label:"事件摘要"},{id:"logs",label:"服务日志"}]},{id:"nodes",label:"资源节点",code:"NODE",tabs:[{id:"list",label:"节点清单"},{id:"monitor",label:"资源监控"},{id:"docker",label:"Docker 状态"},{id:"gateway",label:"网关版本"},{id:"labels",label:"资源标签"},{id:"heartbeats",label:"心跳状态"}]},{id:"tasks",label:"任务调度",code:"TASK",tabs:[{id:"dispatch",label:"下发任务"},{id:"scheduled",label:"定时任务"},{id:"pending",label:"待处理任务"},{id:"history",label:"任务历史"},{id:"results",label:"执行结果"}]},{id:"apps",label:"用户服务",code:"APP",routeSegment:"app",tabs:[{id:"catalog",label:"服务目录"},{id:"todo-note",label:"Todo Note"},{id:"findjob",label:"FindJob"},{id:"pipeline",label:"Pipeline"},{id:"met-nonlinear",label:"MET Nonlinear"},{id:"claudeqq",label:"ClaudeQQ"},{id:"baidu-netdisk",label:"Baidu Netdisk"},{id:"filebrowser",label:"File Browser"},{id:"oa-event-flow",label:"OA Event Flow"},{id:"code-queue",label:"Code Queue"},{id:"project-manager",label:"Project Manager"}]},{id:"config",label:"系统配置",code:"CFG",tabs:[{id:"topology",label:"连接拓扑"},{id:"auth",label:"认证策略"},{id:"security",label:"安全边界"}]}],b_=Object.fromEntries(u6.map((u)=>[u.id,u.tabs[0]?.id??""]));function YZ(u){let f=String(u||"").trim();if(!f)return"";try{return decodeURIComponent(f)}catch{return f}}function e3(u){let f=String(u||"/"),[l]=f.split(/[?#]/u,1);if(l==="/")return"/";let n=`/${l.split("/").map(YZ).filter(Boolean).join("/")}`;return n.endsWith("/")?n:`${n}/`}function SZ(u){let f=2166136261;for(let l of u)f^=l.charCodeAt(0),f=Math.imul(f,16777619);return Math.abs(f>>>0).toString(36)}function l5(u){return String(u||"").normalize("NFKD").replace(/[\u0300-\u036f]/gu,"").toLowerCase().replace(/[^a-z0-9]+/gu,"-").replace(/^-+|-+$/gu,"")}function SQ(u){return String(u||"").trim().toLowerCase().replace(/[\s/\\?#%]+/gu,"-").replace(/-+/gu,"-").replace(/^-+|-+$/gu,"")}function mQ(u){let f=l5(u.routeSegment||"")||SQ(u.routeSegment||"");if(f)return f;let l=l5(u.id||"");if(l)return l;let r=l5(u.label||"")||SQ(u.label||"");if(r)return r;return`route-${SZ(JSON.stringify(u))}`}function r5(u,f){return`${u}:${f}`}function pQ(u){let f=u.map(($)=>{let c=mQ($);return{...$,routeSegment:c,tabs:$.tabs.map((A)=>({...A,routeSegment:mQ(A)}))}}),l={},r={},n={},i=f.map(($)=>{let c=$.tabs[0]?.id??"";n[$.id]=c;let A=$.tabs.map((U)=>{let Q=`/${$.routeSegment}/${U.routeSegment}/`,w=[Q],L={moduleId:$.id,tabId:U.id};for(let J of w)l[e3(J)]=L;return r[r5($.id,U.id)]=Q,{...U,canonicalPath:Q,aliases:w}}),j=`/${$.routeSegment}/`,F={moduleId:$.id,tabId:c};return l[e3(j)]=F,{...$,routeSegment:$.routeSegment,canonicalPath:j,tabs:A}}),y=i[0],_={moduleId:y?.id||"",tabId:y?.tabs[0]?.id||""};return l["/"]=_,{modules:i,moduleById:Object.fromEntries(i.map(($)=>[$.id,$])),defaultActiveTabs:n,routeMap:l,canonicalPathByTarget:r,fallbackTarget:_}}function n5(u,f){return u.routeMap[e3(f)]||u.fallbackTarget}function f6(u,f,l){return u.canonicalPathByTarget[r5(f,l)]||u.canonicalPathByTarget[r5(u.fallbackTarget.moduleId,u.fallbackTarget.tabId)]||"/"}function PQ(u,f){let l=u.routeMap[e3(f)];if(!l)return null;return f6(u,l.moduleId,l.tabId)}var i6=Mu(Qf(),1);var nu=i6.default.createElement,{useEffect:MQ,useMemo:mZ}=i6.default,i5=i6.default.useState;function n6({status:u,children:f,title:l}){let r=String(u||"unknown").toLowerCase();return nu("span",{className:`status-badge ${r}`,title:l},f||u||"unknown")}function v_({label:u,value:f,hint:l,tone:r}){return nu("article",{className:`metric-card ${r||""}`},nu("div",{className:"metric-label"},u),nu("div",{className:"metric-value"},f),nu("div",{className:"metric-hint"},l))}function l6({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return nu("section",{className:`panel ${n||""}`},nu("div",{className:"panel-head"},nu("div",null,f?nu("p",{className:"panel-eyebrow"},f):null,nu(nf,{title:u,loading:i})),l?nu("div",{className:"panel-actions"},l):null),nu("div",{className:"panel-body"},r))}function k_({title:u,data:f,onOpen:l,testId:r}){return nu("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l?.(u,f)},"查看原始JSON")}function y5({title:u,text:f}){return nu("div",{className:"empty-state"},nu("strong",null,u),nu("span",null,f))}function pZ(u){return u&&typeof u==="object"&&!Array.isArray(u)?u:null}function S0(u){return Array.isArray(u)?u:[]}function bl(u){let f=Number(u);return Number.isFinite(f)?f.toLocaleString("zh-CN"):"--"}function CQ(u,f=140){if(u===null||u===void 0)return"--";let l=typeof u==="string"?u:JSON.stringify(u),r=String(l||"").replace(/\s+/gu," ").trim();return r.length>f?`${r.slice(0,f-1)}...`:r||"--"}function PZ(u){return S0(u?.tags).map((f)=>String(f||"").trim()).filter(Boolean)}function T1(u){let f=Number(u);return Number.isFinite(f)&&f>=0?Math.floor(f):0}function MZ(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function CZ(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function RZ(u){return String(u||"").split(/[\s,]+/u).map((f)=>f.trim()).filter(Boolean).join(",")}function r6(u,f){return`${u}/microservices/oa-event-flow/proxy${f}`}function xZ(u){if(u.includes("error")||u.includes("failed"))return"failed";if(u.includes("stats"))return"ok";if(u.includes("step")||u.includes("updated"))return"running";return"queued"}function hZ(u){let f=String(u?.subjectKind||"trace"),l=String(u?.subjectId||u?.scopeId||"");return l?`${f}:${l}`:String(u?.scopeId||"--")}function bZ({tags:u}){let f=PZ({tags:u}).slice(0,6);return nu("div",{className:"oa-tag-rail"},f.length===0?nu("span",{className:"muted"},"--"):f.map((l)=>nu("code",{key:l},l)))}function vZ({events:u,onRaw:f}){let l=[...S0(u)].reverse();return l.length===0?nu(y5,{title:"事件表暂无记录",text:"等待 Code Queue 或 Pipeline 按 tag 发布 OA 事件"}):nu("div",{className:"table-wrap oa-event-table-wrap"},nu("table",{className:"oa-event-table","data-testid":"oa-event-flow-event-table"},nu("thead",null,nu("tr",null,nu("th",null,"Seq"),nu("th",null,"Type"),nu("th",null,"Source"),nu("th",null,"Aggregate"),nu("th",null,"Tags"),nu("th",null,"Payload"),nu("th",null,"Created"),nu("th",null,"Raw"))),nu("tbody",null,l.map((r)=>{let n=String(r?.type||"event"),i=`${String(r?.aggregateType||"--")}:${String(r?.aggregateId||"--")}`;return nu("tr",{key:r?.eventId||r?.sequence},nu("td",null,nu("code",null,bl(r?.sequence))),nu("td",null,nu(n6,{status:xZ(n)},n)),nu("td",null,nu("strong",null,r?.sourceId||"--"),nu("code",null,r?.sourceKind||"--")),nu("td",null,nu("code",null,i)),nu("td",null,nu(bZ,{tags:r?.tags})),nu("td",null,nu("span",{className:"oa-payload-preview"},CQ(r?.payload,180))),nu("td",null,Nu(r?.createdAt)),nu("td",null,nu(k_,{title:`OA Event ${r?.sequence||""}`,data:r,onOpen:f,testId:`raw-oa-event-${r?.sequence||"unknown"}`})))}))))}function kZ({stats:u,onRaw:f}){let l=S0(u);return l.length===0?nu(y5,{title:"统计中心暂无投影",text:"trace-stats-snapshot / trace-step-created 进入事件流后会更新这里"}):nu("div",{className:"table-wrap oa-stats-table-wrap"},nu("table",{className:"oa-stats-table","data-testid":"oa-event-flow-stats"},nu("thead",null,nu("tr",null,nu("th",null,"Scope"),nu("th",null,"Service"),nu("th",null,"STEP"),nu("th",null,"Read"),nu("th",null,"Edit"),nu("th",null,"Run"),nu("th",null,"Error"),nu("th",null,"Output Seq"),nu("th",null,"Revision"),nu("th",null,"Updated"),nu("th",null,"Raw"))),nu("tbody",null,l.map((r)=>nu("tr",{key:r?.scopeId||`${r?.serviceId}-${r?.subjectId}`},nu("td",null,nu("strong",null,hZ(r)),nu("code",null,r?.scopeId||"--")),nu("td",null,nu(n6,{status:String(r?.serviceId||"unknown")==="code-queue"?"running":"queued"},r?.serviceId||"--")),nu("td",null,nu("strong",null,bl(T1(r?.stepCount??r?.llmStepCount)))),nu("td",null,bl(T1(r?.readCount))),nu("td",null,bl(T1(r?.editCount))),nu("td",null,bl(T1(r?.runCount))),nu("td",null,bl(T1(r?.errorCount))),nu("td",null,nu("code",null,bl(T1(r?.outputMaxSeq)))),nu("td",null,bl(T1(r?.statsRevision))),nu("td",null,Nu(r?.updatedAt)),nu("td",null,nu(k_,{title:`OA Trace Stats ${r?.scopeId||""}`,data:r,onOpen:f,testId:`raw-oa-stats-${String(r?.scopeId||"unknown").replace(/[^a-zA-Z0-9_-]/gu,"_")}`})))))))}function RQ({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((z)=>z.id==="oa-event-flow")||null,[n,i]=i5("service:code-queue"),[y,_]=i5({loading:!1,error:"",health:null,diagnostics:null,events:[],stats:[],refreshedAt:null}),[$,c]=i5({status:"idle",message:"未连接",lastEventAt:""}),A=mZ(()=>RZ(n),[n]);async function j(){if(!r)return;_((z)=>({...z,loading:!0,error:""}));try{let z=A?`tags=${encodeURIComponent(A)}&`:"",[Z,H,E,D]=await Promise.all([Eu(`${l}/microservices/oa-event-flow/health`,{failureFields:[]}),Eu(r6(l,"/api/diagnostics")),Eu(r6(l,`/api/events?${z}limit=100`)),Eu(r6(l,`/api/stats/trace?${z}limit=100`))]);_({loading:!1,error:"",health:Z,diagnostics:H,events:S0(E?.events),stats:S0(D?.stats),refreshedAt:new Date})}catch(z){_((Z)=>({...Z,loading:!1,error:Hu(z,"OA Event Flow 加载失败")}))}}if(MQ(()=>{j()},[r?.id,r?.runtime?.providerStatus,A]),MQ(()=>{if(!r||typeof EventSource>"u")return;let z=A?`?tags=${encodeURIComponent(A)}`:"",Z=new EventSource(`${r6(l,"/api/events/stream")}${z}`,{withCredentials:!0});c({status:"running",message:"SSE connecting",lastEventAt:""});let H=(h)=>{c({status:"online",message:CQ(h.data,120),lastEventAt:new Date().toISOString()})},E=(h)=>{try{let B=JSON.parse(String(h.data||"{}"));c({status:"online",message:String(B?.type||h.type||"event"),lastEventAt:new Date().toISOString()}),_((X)=>{let m=[...S0(X.events).filter((p)=>String(p?.eventId||"")!==String(B?.eventId||"")),B].sort((p,Y)=>Number(p?.sequence||0)-Number(Y?.sequence||0)).slice(-100),O=B?.type==="trace-stats-updated"&&pZ(B?.payload?.stats)?[B.payload.stats,...S0(X.stats).filter((p)=>String(p?.scopeId||"")!==String(B.payload.stats.scopeId||""))].slice(0,100):X.stats;return{...X,events:m,stats:O}})}catch(B){c({status:"warn",message:Hu(B,"SSE 事件解析失败"),lastEventAt:new Date().toISOString()})}},D=()=>{c((h)=>({...h,status:"warn",message:"SSE reconnecting"}))};return Z.addEventListener("hello",H),Z.addEventListener("task-updated",E),Z.addEventListener("queue-updated",E),Z.addEventListener("trace-step-created",E),Z.addEventListener("trace-stats-snapshot",E),Z.addEventListener("trace-stats-updated",E),Z.addEventListener("trace-error",E),Z.onerror=D,()=>Z.close()},[r?.id,l,A]),!r)return nu(y5,{title:"OA Event Flow 未登记",text:"请在 config.json 的 microservices 中登记 id=oa-event-flow"});let F=MZ(r),U=CZ(r),Q=y.diagnostics||{},w=y.health||{},L=Q.eventCount??w.eventCount,J=Q.traceStatsCount??w.traceStatsCount,N=Q.latestSequence??w.latestSequence,q=Q.pipelineBridge||w.pipelineBridge||{},W=S0(Q.eventTypes).slice(0,8);return nu("div",{className:"oa-event-flow-page","data-testid":"oa-event-flow-page"},nu(l6,{title:"OA Event Flow 控制台",eyebrow:"Unified OA Event Bus + Stats Projection",loading:y.loading,actions:nu("div",{className:"panel-actions"},nu("button",{type:"button",className:"ghost-btn",onClick:j,disabled:y.loading,"data-testid":"oa-event-flow-refresh"},y.loading?"刷新中":"刷新"),nu(k_,{title:"OA Event Flow Service",data:r,onOpen:f,testId:"raw-oa-event-flow-service"}))},nu("div",{className:"oa-flow-hero"},nu("div",null,nu("div",{className:"node-version-line"},nu(n6,{status:w?.ok||F.providerStatus==="online"?"online":"warn"},w?.ok?"HEALTH OK":F.providerStatus||"unknown"),nu(n6,{status:$.status},$.status.toUpperCase()),nu("span",null,U.public?"公网暴露":"仅 UniDesk frontend 代理访问")),nu("p",{className:"muted paragraph"},"独立事件流微服务统一承载 Code Queue 与 Pipeline 的事件发布、tag 订阅、事件表审计和 Trace/STEP 统计投影。")),nu("div",{className:"oa-flow-signal"},nu("span",null,"stream"),nu("strong",null,$.message||"--"),nu("code",null,$.lastEventAt?$f(new Date($.lastEventAt)):"waiting"))),nu(yf,{error:y.error,wide:!0})),nu("div",{className:"oa-flow-metrics"},nu(v_,{label:"事件总量",value:bl(L),hint:`latest seq ${bl(N)}`,tone:"ok"}),nu(v_,{label:"Trace Stats",value:bl(J),hint:"oa_trace_stats 投影"}),nu(v_,{label:"SSE Clients",value:bl(w?.sseClientCount??S0(Q.sseClients).length),hint:$.message||"tag subscription"}),nu(v_,{label:"Pipeline Bridge",value:q?.enabled?bl(q?.insertedCount):"OFF",hint:q?.lastError||q?.lastFinishedAt||`${q?.mode||"snapshot"} service:pipeline`}),nu(v_,{label:"DB",value:w?.databaseReady||Q.databaseReady?"READY":"WAIT",hint:w?.databaseLastError||Q.databaseLastError||"PostgreSQL persisted"})),nu(l6,{title:"标签订阅",eyebrow:y.refreshedAt?`Updated ${$f(y.refreshedAt)}`:"Tag Pub/Sub"},nu("div",{className:"oa-filter-bar"},nu("label",null,nu("span",null,"tags"),nu("input",{value:n,onChange:(z)=>i(z.target.value),placeholder:"service:code-queue, trace","data-testid":"oa-event-flow-tag-filter"})),nu("div",{className:"oa-filter-presets"},nu("button",{type:"button",className:"ghost-btn",onClick:()=>i("service:code-queue")},"Code Queue"),nu("button",{type:"button",className:"ghost-btn",onClick:()=>i("service:pipeline")},"Pipeline"),nu("button",{type:"button",className:"ghost-btn",onClick:()=>i("trace")},"Trace"),nu("button",{type:"button",className:"ghost-btn",onClick:()=>i("")},"All")),nu("code",null,A||"all events")),nu("div",{className:"oa-type-strip"},W.length===0?nu("span",{className:"muted"},"等待事件类型统计"):W.map((z)=>nu("span",{key:z.type,className:"data-chip"},`${z.type} ${bl(z.count)}`)))),nu("div",{className:"oa-flow-grid"},nu(l6,{title:"事件表",eyebrow:"oa_events persisted log",className:"oa-flow-wide",loading:y.loading,actions:nu(k_,{title:"OA Event Query",data:{events:y.events,diagnostics:Q},onOpen:f,testId:"raw-oa-events"})},nu(vZ,{events:y.events,onRaw:f})),nu(l6,{title:"统计中心",eyebrow:"oa_trace_stats read model",className:"oa-flow-wide",loading:y.loading,actions:nu(k_,{title:"OA Trace Stats",data:y.stats,onOpen:f,testId:"raw-oa-trace-stats"})},nu(kZ,{stats:y.stats,onRaw:f}))))}var Rn=Mu(Qf(),1);var ru=Mu(vQ(),1),yu=Mu(Qf(),1);function Xf(u){if(typeof u==="string"||typeof u==="number")return""+u;let f="";if(Array.isArray(u)){for(let l=0,r;l{}};function IQ(){for(var u=0,f=arguments.length,l={},r;u=0)r=l.slice(n+1),l=l.slice(0,n);if(l&&!f.hasOwnProperty(l))throw Error("unknown type: "+l);return{type:l,name:r}})}_6.prototype=IQ.prototype={constructor:_6,on:function(u,f){var l=this._,r=uO(u+"",l),n,i=-1,y=r.length;if(arguments.length<2){while(++i0)for(var l=Array(n),r=0,n,i;r=0&&(f=u.slice(0,l))!=="xmlns")u=u.slice(l+1);return _5.hasOwnProperty(f)?{space:_5[f],local:u}:u}function $5(u){let f;while(f=u.sourceEvent)u=f;return u}function Wl(u,f){if(u=$5(u),f===void 0)f=u.currentTarget;if(f){var l=f.ownerSVGElement||f;if(l.createSVGPoint){var r=l.createSVGPoint();return r.x=u.clientX,r.y=u.clientY,r=r.matrixTransform(f.getScreenCTM().inverse()),[r.x,r.y]}if(f.getBoundingClientRect){var n=f.getBoundingClientRect();return[u.clientX-n.left-f.clientLeft,u.clientY-n.top-f.clientTop]}}return[u.pageX,u.pageY]}function lO(){}function On(u){return u==null?lO:function(){return this.querySelector(u)}}function t5(u){if(typeof u!=="function")u=On(u);for(var f=this._groups,l=f.length,r=Array(l),n=0;n=q)q=N+1;while(!(z=L[q])&&++q=0;)if(y=r[n]){if(i&&y.compareDocumentPosition(i)^4)i.parentNode.insertBefore(y,i);i=y}return this}function K5(u){if(!u)u=UO;function f(j,F){return j&&F?u(j.__data__,F.__data__):!j-!F}for(var l=this._groups,r=l.length,n=Array(r),i=0;if?1:u>=f?0:NaN}function G5(){var u=arguments[0];return arguments[0]=this,u.apply(null,arguments),this}function z5(){return Array.from(this)}function T5(){for(var u=this._groups,f=0,l=u.length;f1?this.each((f==null?KO:typeof f==="function"?zO:GO)(u,f,l==null?"":l)):Hn(this.node(),u)}function Hn(u,f){return u.style.getPropertyValue(f)||s_(u).getComputedStyle(u,null).getPropertyValue(f)}function TO(u){return function(){delete this[u]}}function EO(u,f){return function(){this[u]=f}}function ZO(u,f){return function(){var l=f.apply(this,arguments);if(l==null)delete this[u];else this[u]=l}}function V5(u,f){return arguments.length>1?this.each((f==null?TO:typeof f==="function"?ZO:EO)(u,f)):this.node()[u]}function gQ(u){return u.trim().split(/^|\s+/)}function D5(u){return u.classList||new sQ(u)}function sQ(u){this._node=u,this._names=gQ(u.getAttribute("class")||"")}sQ.prototype={add:function(u){var f=this._names.indexOf(u);if(f<0)this._names.push(u),this._node.setAttribute("class",this._names.join(" "))},remove:function(u){var f=this._names.indexOf(u);if(f>=0)this._names.splice(f,1),this._node.setAttribute("class",this._names.join(" "))},contains:function(u){return this._names.indexOf(u)>=0}};function aQ(u,f){var l=D5(u),r=-1,n=f.length;while(++r=0)l=f.slice(r+1),f=f.slice(0,r);return{type:f,name:l}})}function IO(u){return function(){var f=this.__on;if(!f)return;for(var l=0,r=-1,n=f.length,i;l()=>u;function e_(u,{sourceEvent:f,subject:l,target:r,identifier:n,active:i,x:y,y:_,dx:$,dy:c,dispatch:A}){Object.defineProperties(this,{type:{value:u,enumerable:!0,configurable:!0},sourceEvent:{value:f,enumerable:!0,configurable:!0},subject:{value:l,enumerable:!0,configurable:!0},target:{value:r,enumerable:!0,configurable:!0},identifier:{value:n,enumerable:!0,configurable:!0},active:{value:i,enumerable:!0,configurable:!0},x:{value:y,enumerable:!0,configurable:!0},y:{value:_,enumerable:!0,configurable:!0},dx:{value:$,enumerable:!0,configurable:!0},dy:{value:c,enumerable:!0,configurable:!0},_:{value:A}})}e_.prototype.on=function(){var u=this._.on.apply(this._,arguments);return u===this._?this:u};function iH(u){return!u.ctrlKey&&!u.button}function yH(){return this.parentNode}function _H(u,f){return f==null?{x:u.x,y:u.y}:f}function $H(){return navigator.maxTouchPoints||"ontouchstart"in this}function u$(){var u=iH,f=yH,l=_H,r=$H,n={},i=E1("start","drag","end"),y=0,_,$,c,A,j=0;function F(W){W.on("mousedown.drag",U).filter(r).on("touchstart.drag",L).on("touchmove.drag",J,uq).on("touchend.drag touchcancel.drag",N).style("touch-action","none").style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}function U(W,z){if(A||!u.call(this,W,z))return;var Z=q(this,f.call(this,W,z),W,z,"mouse");if(!Z)return;of(W.view).on("mousemove.drag",Q,Z1).on("mouseup.drag",w,Z1),ki(W.view),c6(W),c=!1,_=W.clientX,$=W.clientY,Z("start",W)}function Q(W){if(p0(W),!c){var z=W.clientX-_,Z=W.clientY-$;c=z*z+Z*Z>j}n.mouse("drag",W)}function w(W){of(W.view).on("mousemove.drag mouseup.drag",null),o_(W.view,c),p0(W),n.mouse("end",W)}function L(W,z){if(!u.call(this,W,z))return;var Z=W.changedTouches,H=f.call(this,W,z),E=Z.length,D,h;for(D=0;D>8&15|f>>4&240,f>>4&15|f&240,(f&15)<<4|f&15,1):l===8?A6(f>>24&255,f>>16&255,f>>8&255,(f&255)/255):l===4?A6(f>>12&15|f>>8&240,f>>8&15|f>>4&240,f>>4&15|f&240,((f&15)<<4|f&15)/255):null):(f=cH.exec(u))?new vl(f[1],f[2],f[3],1):(f=AH.exec(u))?new vl(f[1]*255/100,f[2]*255/100,f[3]*255/100,1):(f=jH.exec(u))?A6(f[1],f[2],f[3],f[4]):(f=FH.exec(u))?A6(f[1]*255/100,f[2]*255/100,f[3]*255/100,f[4]):(f=JH.exec(u))?_q(f[1],f[2]/100,f[3]/100,1):(f=UH.exec(u))?_q(f[1],f[2]/100,f[3]/100,f[4]):fq.hasOwnProperty(u)?nq(fq[u]):u==="transparent"?new vl(NaN,NaN,NaN,0):null}function nq(u){return new vl(u>>16&255,u>>8&255,u&255,1)}function A6(u,f,l,r){if(r<=0)u=f=l=NaN;return new vl(u,f,l,r)}function NH(u){if(!(u instanceof n$))u=hr(u);if(!u)return new vl;return u=u.rgb(),new vl(u.r,u.g,u.b,u.opacity)}function gi(u,f,l,r){return arguments.length===1?NH(u):new vl(u,f,l,r==null?1:r)}function vl(u,f,l,r){this.r=+u,this.g=+f,this.b=+l,this.opacity=+r}f$(vl,gi,I5(n$,{brighter(u){return u=u==null?F6:Math.pow(F6,u),new vl(this.r*u,this.g*u,this.b*u,this.opacity)},darker(u){return u=u==null?l$:Math.pow(l$,u),new vl(this.r*u,this.g*u,this.b*u,this.opacity)},rgb(){return this},clamp(){return new vl(H1(this.r),H1(this.g),H1(this.b),J6(this.opacity))},displayable(){return-0.5<=this.r&&this.r<255.5&&(-0.5<=this.g&&this.g<255.5)&&(-0.5<=this.b&&this.b<255.5)&&(0<=this.opacity&&this.opacity<=1)},hex:iq,formatHex:iq,formatHex8:WH,formatRgb:yq,toString:yq}));function iq(){return`#${O1(this.r)}${O1(this.g)}${O1(this.b)}`}function WH(){return`#${O1(this.r)}${O1(this.g)}${O1(this.b)}${O1((isNaN(this.opacity)?1:this.opacity)*255)}`}function yq(){let u=J6(this.opacity);return`${u===1?"rgb(":"rgba("}${H1(this.r)}, ${H1(this.g)}, ${H1(this.b)}${u===1?")":`, ${u})`}`}function J6(u){return isNaN(u)?1:Math.max(0,Math.min(1,u))}function H1(u){return Math.max(0,Math.min(255,Math.round(u)||0))}function O1(u){return u=H1(u),(u<16?"0":"")+u.toString(16)}function _q(u,f,l,r){if(r<=0)u=f=l=NaN;else if(l<=0||l>=1)u=f=NaN;else if(f<=0)u=NaN;return new xr(u,f,l,r)}function tq(u){if(u instanceof xr)return new xr(u.h,u.s,u.l,u.opacity);if(!(u instanceof n$))u=hr(u);if(!u)return new xr;if(u instanceof xr)return u;u=u.rgb();var f=u.r/255,l=u.g/255,r=u.b/255,n=Math.min(f,l,r),i=Math.max(f,l,r),y=NaN,_=i-n,$=(i+n)/2;if(_){if(f===i)y=(l-r)/_+(l0&&$<1?0:y;return new xr(y,_,$,u.opacity)}function cq(u,f,l,r){return arguments.length===1?tq(u):new xr(u,f,l,r==null?1:r)}function xr(u,f,l,r){this.h=+u,this.s=+f,this.l=+l,this.opacity=+r}f$(xr,cq,I5(n$,{brighter(u){return u=u==null?F6:Math.pow(F6,u),new xr(this.h,this.s,this.l*u,this.opacity)},darker(u){return u=u==null?l$:Math.pow(l$,u),new xr(this.h,this.s,this.l*u,this.opacity)},rgb(){var u=this.h%360+(this.h<0)*360,f=isNaN(u)||isNaN(this.s)?0:this.s,l=this.l,r=l+(l<0.5?l:1-l)*f,n=2*l-r;return new vl(g5(u>=240?u-240:u+120,n,r),g5(u,n,r),g5(u<120?u+240:u-120,n,r),this.opacity)},clamp(){return new xr($q(this.h),j6(this.s),j6(this.l),J6(this.opacity))},displayable(){return(0<=this.s&&this.s<=1||isNaN(this.s))&&(0<=this.l&&this.l<=1)&&(0<=this.opacity&&this.opacity<=1)},formatHsl(){let u=J6(this.opacity);return`${u===1?"hsl(":"hsla("}${$q(this.h)}, ${j6(this.s)*100}%, ${j6(this.l)*100}%${u===1?")":`, ${u})`}`}}));function $q(u){return u=(u||0)%360,u<0?u+360:u}function j6(u){return Math.max(0,Math.min(1,u||0))}function g5(u,f,l){return(u<60?f+(l-f)*u/60:u<180?l:u<240?f+(l-f)*(240-u)/60:f)*255}function s5(u,f,l,r,n){var i=u*u,y=i*u;return((1-3*u+3*i-y)*f+(4-6*i+3*y)*l+(1+3*u+3*i-3*y)*r+y*n)/6}function a5(u){var f=u.length-1;return function(l){var r=l<=0?l=0:l>=1?(l=1,f-1):Math.floor(l*f),n=u[r],i=u[r+1],y=r>0?u[r-1]:2*n-i,_=r()=>u;function LH(u,f){return function(l){return u+l*f}}function KH(u,f,l){return u=Math.pow(u,l),f=Math.pow(f,l)-u,l=1/l,function(r){return Math.pow(u+r*f,l)}}function Aq(u){return(u=+u)===1?Q6:function(f,l){return l-f?KH(f,l,u):i$(isNaN(f)?l:f)}}function Q6(u,f){var l=f-u;return l?LH(u,l):i$(isNaN(u)?f:u)}var B1=function u(f){var l=Aq(f);function r(n,i){var y=l((n=gi(n)).r,(i=gi(i)).r),_=l(n.g,i.g),$=l(n.b,i.b),c=Q6(n.opacity,i.opacity);return function(A){return n.r=y(A),n.g=_(A),n.b=$(A),n.opacity=c(A),n+""}}return r.gamma=u,r}(1);function jq(u){return function(f){var l=f.length,r=Array(l),n=Array(l),i=Array(l),y,_;for(y=0;yl)if(i=f.slice(l,i),_[y])_[y]+=i;else _[++y]=i;if((r=r[0])===(n=n[0]))if(_[y])_[y]+=n;else _[++y]=n;else _[++y]=null,$.push({i:y,x:wl(r,n)});l=fA.lastIndex}if(l180)A+=360;else if(A-c>180)c+=360;F.push({i:j.push(n(j)+"rotate(",null,r)-2,x:wl(c,A)})}else if(A)j.push(n(j)+"rotate("+A+r)}function _(c,A,j,F){if(c!==A)F.push({i:j.push(n(j)+"skewX(",null,r)-2,x:wl(c,A)});else if(A)j.push(n(j)+"skewX("+A+r)}function $(c,A,j,F,U,Q){if(c!==j||A!==F){var w=U.push(n(U)+"scale(",null,",",null,")");Q.push({i:w-4,x:wl(c,j)},{i:w-2,x:wl(A,F)})}else if(j!==1||F!==1)U.push(n(U)+"scale("+j+","+F+")")}return function(c,A){var j=[],F=[];return c=u(c),A=u(A),i(c.translateX,c.translateY,A.translateX,A.translateY,j,F),y(c.rotate,A.rotate,j,F),_(c.skewX,A.skewX,j,F),$(c.scaleX,c.scaleY,A.scaleX,A.scaleY,j,F),c=A=null,function(U){var Q=-1,w=F.length,L;while(++Q=0)u._call.call(void 0,f);u=u._next}--ai}function Gq(){D1=(L6=t$.now())+K6,ai=_$=0;try{Eq()}finally{ai=0,bH(),D1=0}}function hH(){var u=t$.now(),f=u-L6;if(f>zq)K6-=f,L6=u}function bH(){var u,f=w6,l,r=1/0;while(f)if(f._call){if(r>f._time)r=f._time;u=f,f=f._next}else l=f._next,f._next=null,f=u?u._next=l:w6=l;$$=u,iA(r)}function iA(u){if(ai)return;if(_$)_$=clearTimeout(_$);var f=u-D1;if(f>24){if(u<1/0)_$=setTimeout(Gq,u-t$.now()-K6);if(y$)y$=clearInterval(y$)}else{if(!y$)L6=t$.now(),y$=setInterval(hH,zq);ai=1,Tq(Gq)}}function j$(u,f,l){var r=new c$;return f=f==null?0:+f,r.restart((n)=>{r.stop(),u(n+f)},f,l),r}var kH=E1("start","end","cancel","interrupt"),IH=[],Hq=0,Zq=1,T6=2,z6=3,Oq=4,E6=5,F$=6;function P0(u,f,l,r,n,i){var y=u.__transition;if(!y)u.__transition={};else if(l in y)return;gH(u,l,{name:f,index:r,group:n,on:kH,tween:IH,time:i.time,delay:i.delay,duration:i.duration,ease:i.ease,timer:null,state:Hq})}function J$(u,f){var l=df(u,f);if(l.state>Hq)throw Error("too late; already scheduled");return l}function cl(u,f){var l=df(u,f);if(l.state>z6)throw Error("too late; already running");return l}function df(u,f){var l=u.__transition;if(!l||!(l=l[f]))throw Error("transition not found");return l}function gH(u,f,l){var r=u.__transition,n;r[f]=l,l.timer=G6(i,0,l.time);function i(c){if(l.state=Zq,l.timer.restart(y,l.delay,l.time),l.delay<=c)y(c-l.delay)}function y(c){var A,j,F,U;if(l.state!==Zq)return $();for(A in r){if(U=r[A],U.name!==l.name)continue;if(U.state===z6)return j$(y);if(U.state===Oq)U.state=F$,U.timer.stop(),U.on.call("interrupt",u,u.__data__,U.index,U.group),delete r[A];else if(+AT6&&r.state=0)f=f.slice(0,l);return!f||f==="start"})}function JB(u,f,l){var r,n,i=FB(f)?J$:cl;return function(){var y=i(this,u),_=y.on;if(_!==r)(n=(r=_).copy()).on(f,l);y.on=n}}function QA(u,f){var l=this._id;return arguments.length<2?df(this.node(),l).on.on(u):this.each(JB(l,u,f))}function UB(u){return function(){var f=this.parentNode;for(var l in this.__transition)if(+l!==u)return;if(f)f.removeChild(this)}}function qA(){return this.on("end.remove",UB(this._id))}function NA(u){var f=this._name,l=this._id;if(typeof u!=="function")u=On(u);for(var r=this._groups,n=r.length,i=Array(n),y=0;y()=>u;function OA(u,{sourceEvent:f,target:l,transform:r,dispatch:n}){Object.defineProperties(this,{type:{value:u,enumerable:!0,configurable:!0},sourceEvent:{value:f,enumerable:!0,configurable:!0},target:{value:l,enumerable:!0,configurable:!0},transform:{value:r,enumerable:!0,configurable:!0},_:{value:n}})}function br(u,f,l){this.k=u,this.x=f,this.y=l}br.prototype={constructor:br,scale:function(u){return u===1?this:new br(this.k*u,this.x,this.y)},translate:function(u,f){return u===0&f===0?this:new br(this.k,this.x+this.k*u,this.y+this.k*f)},apply:function(u){return[u[0]*this.k+this.x,u[1]*this.k+this.y]},applyX:function(u){return u*this.k+this.x},applyY:function(u){return u*this.k+this.y},invert:function(u){return[(u[0]-this.x)/this.k,(u[1]-this.y)/this.k]},invertX:function(u){return(u-this.x)/this.k},invertY:function(u){return(u-this.y)/this.k},rescaleX:function(u){return u.copy().domain(u.range().map(this.invertX,this).map(u.invert,u))},rescaleY:function(u){return u.copy().domain(u.range().map(this.invertY,this).map(u.invert,u))},toString:function(){return"translate("+this.x+","+this.y+") scale("+this.k+")"}};var X1=new br(1,0,0);q$.prototype=br.prototype;function q$(u){while(!u.__zoom)if(!(u=u.parentNode))return X1;return u.__zoom}function p6(u){u.stopImmediatePropagation()}function Y1(u){u.preventDefault(),u.stopImmediatePropagation()}function VB(u){return(!u.ctrlKey||u.type==="wheel")&&!u.button}function DB(){var u=this;if(u instanceof SVGElement){if(u=u.ownerSVGElement||u,u.hasAttribute("viewBox"))return u=u.viewBox.baseVal,[[u.x,u.y],[u.x+u.width,u.y+u.height]];return[[0,0],[u.width.baseVal.value,u.height.baseVal.value]]}return[[0,0],[u.clientWidth,u.clientHeight]]}function Dq(){return this.__zoom||X1}function XB(u){return-u.deltaY*(u.deltaMode===1?0.05:u.deltaMode?1:0.002)*(u.ctrlKey?10:1)}function YB(){return navigator.maxTouchPoints||"ontouchstart"in this}function SB(u,f,l){var r=u.invertX(f[0][0])-l[0][0],n=u.invertX(f[1][0])-l[1][0],i=u.invertY(f[0][1])-l[0][1],y=u.invertY(f[1][1])-l[1][1];return u.translate(n>r?(r+n)/2:Math.min(0,r)||Math.max(0,n),y>i?(i+y)/2:Math.min(0,i)||Math.max(0,y))}function N$(){var u=VB,f=DB,l=SB,r=XB,n=YB,i=[0,1/0],y=[[-1/0,-1/0],[1/0,1/0]],_=250,$=V1,c=E1("start","zoom","end"),A,j,F,U=500,Q=150,w=0,L=10;function J(O){O.property("__zoom",Dq).on("wheel.zoom",E,{passive:!1}).on("mousedown.zoom",D).on("dblclick.zoom",h).filter(n).on("touchstart.zoom",B).on("touchmove.zoom",X).on("touchend.zoom touchcancel.zoom",m).style("-webkit-tap-highlight-color","rgba(0,0,0,0)")}J.transform=function(O,p,Y,v){var T=O.selection?O.selection():O;if(T.property("__zoom",Dq),O!==T)z(O,p,Y,v);else T.interrupt().each(function(){Z(this,arguments).event(v).start().zoom(null,typeof p==="function"?p.apply(this,arguments):p).end()})},J.scaleBy=function(O,p,Y,v){J.scaleTo(O,function(){var T=this.__zoom.k,S=typeof p==="function"?p.apply(this,arguments):p;return T*S},Y,v)},J.scaleTo=function(O,p,Y,v){J.transform(O,function(){var T=f.apply(this,arguments),S=this.__zoom,k=Y==null?W(T):typeof Y==="function"?Y.apply(this,arguments):Y,I=S.invert(k),b=typeof p==="function"?p.apply(this,arguments):p;return l(q(N(S,b),k,I),T,y)},Y,v)},J.translateBy=function(O,p,Y,v){J.transform(O,function(){return l(this.__zoom.translate(typeof p==="function"?p.apply(this,arguments):p,typeof Y==="function"?Y.apply(this,arguments):Y),f.apply(this,arguments),y)},null,v)},J.translateTo=function(O,p,Y,v,T){J.transform(O,function(){var S=f.apply(this,arguments),k=this.__zoom,I=v==null?W(S):typeof v==="function"?v.apply(this,arguments):v;return l(X1.translate(I[0],I[1]).scale(k.k).translate(typeof p==="function"?-p.apply(this,arguments):-p,typeof Y==="function"?-Y.apply(this,arguments):-Y),S,y)},v,T)};function N(O,p){return p=Math.max(i[0],Math.min(i[1],p)),p===O.k?O:new br(p,O.x,O.y)}function q(O,p,Y){var v=p[0]-Y[0]*O.k,T=p[1]-Y[1]*O.k;return v===O.x&&T===O.y?O:new br(O.k,v,T)}function W(O){return[(+O[0][0]+ +O[1][0])/2,(+O[0][1]+ +O[1][1])/2]}function z(O,p,Y,v){O.on("start.zoom",function(){Z(this,arguments).event(v).start()}).on("interrupt.zoom end.zoom",function(){Z(this,arguments).event(v).end()}).tween("zoom",function(){var T=this,S=arguments,k=Z(T,S).event(v),I=f.apply(T,S),b=Y==null?W(I):typeof Y==="function"?Y.apply(T,S):Y,o=Math.max(I[1][0]-I[0][0],I[1][1]-I[0][1]),s=T.__zoom,x=typeof p==="function"?p.apply(T,S):p,fu=$(s.invert(b).concat(o/s.k),x.invert(b).concat(o/x.k));return function($u){if($u===1)$u=x;else{var tu=fu($u),ju=o/tu[2];$u=new br(ju,b[0]-tu[0]*ju,b[1]-tu[1]*ju)}k.zoom(null,$u)}})}function Z(O,p,Y){return!Y&&O.__zooming||new H(O,p)}function H(O,p){this.that=O,this.args=p,this.active=0,this.sourceEvent=null,this.extent=f.apply(O,p),this.taps=0}H.prototype={event:function(O){if(O)this.sourceEvent=O;return this},start:function(){if(++this.active===1)this.that.__zooming=this,this.emit("start");return this},zoom:function(O,p){if(this.mouse&&O!=="mouse")this.mouse[1]=p.invert(this.mouse[0]);if(this.touch0&&O!=="touch")this.touch0[1]=p.invert(this.touch0[0]);if(this.touch1&&O!=="touch")this.touch1[1]=p.invert(this.touch1[0]);return this.that.__zoom=p,this.emit("zoom"),this},end:function(){if(--this.active===0)delete this.that.__zooming,this.emit("end");return this},emit:function(O){var p=of(this.that).datum();c.call(O,this.that,new OA(O,{sourceEvent:this.sourceEvent,target:J,type:O,transform:this.that.__zoom,dispatch:c}),p)}};function E(O,...p){if(!u.apply(this,arguments))return;var Y=Z(this,p).event(O),v=this.__zoom,T=Math.max(i[0],Math.min(i[1],v.k*Math.pow(2,r.apply(this,arguments)))),S=Wl(O);if(Y.wheel){if(Y.mouse[0][0]!==S[0]||Y.mouse[0][1]!==S[1])Y.mouse[1]=v.invert(Y.mouse[0]=S);clearTimeout(Y.wheel)}else if(v.k===T)return;else Y.mouse=[S,v.invert(S)],Bn(this),Y.start();Y1(O),Y.wheel=setTimeout(k,Q),Y.zoom("mouse",l(q(N(v,T),Y.mouse[0],Y.mouse[1]),Y.extent,y));function k(){Y.wheel=null,Y.end()}}function D(O,...p){if(F||!u.apply(this,arguments))return;var Y=O.currentTarget,v=Z(this,p,!0).event(O),T=of(O.view).on("mousemove.zoom",b,!0).on("mouseup.zoom",o,!0),S=Wl(O,Y),k=O.clientX,I=O.clientY;ki(O.view),p6(O),v.mouse=[S,this.__zoom.invert(S)],Bn(this),v.start();function b(s){if(Y1(s),!v.moved){var x=s.clientX-k,fu=s.clientY-I;v.moved=x*x+fu*fu>w}v.event(s).zoom("mouse",l(q(v.that.__zoom,v.mouse[0]=Wl(s,Y),v.mouse[1]),v.extent,y))}function o(s){T.on("mousemove.zoom mouseup.zoom",null),o_(s.view,v.moved),Y1(s),v.event(s).end()}}function h(O,...p){if(!u.apply(this,arguments))return;var Y=this.__zoom,v=Wl(O.changedTouches?O.changedTouches[0]:O,this),T=Y.invert(v),S=Y.k*(O.shiftKey?0.5:2),k=l(q(N(Y,S),v,T),f.apply(this,p),y);if(Y1(O),_>0)of(this).transition().duration(_).call(z,k,v,O);else of(this).call(J.transform,k,v,O)}function B(O,...p){if(!u.apply(this,arguments))return;var Y=O.touches,v=Y.length,T=Z(this,p,O.changedTouches.length===v).event(O),S,k,I,b;p6(O);for(k=0;k"[React Flow]: Seems like you have not used zustand provider as an ancestor. Help: https://reactflow.dev/error#001",error002:()=>"It looks like you've created a new nodeTypes or edgeTypes object. If this wasn't on purpose please define the nodeTypes/edgeTypes outside of the component or memoize them.",error003:(u)=>`Node type "${u}" not found. Using fallback type "default".`,error004:()=>"The React Flow parent container needs a width and a height to render the graph.",error005:()=>"Only child nodes can use a parent extent.",error006:()=>"Can't create edge. An edge needs a source and a target.",error007:(u)=>`The old edge with id=${u} does not exist.`,error009:(u)=>`Marker type "${u}" doesn't exist.`,error008:(u,{id:f,sourceHandle:l,targetHandle:r})=>`Couldn't create edge for ${u} handle id: "${u==="source"?l:r}", edge id: ${f}.`,error010:()=>"Handle: No node id found. Make sure to only use a Handle inside a custom Node.",error011:(u)=>`Edge type "${u}" not found. Using fallback type "default".`,error012:(u)=>`Node with id "${u}" does not exist, it may have been removed. This can happen when a node is deleted before the "onNodeClick" handler is called.`,error013:(u="react")=>`It seems that you haven't loaded the styles. Please import '@xyflow/${u}/dist/style.css' or base.css to make sure everything is working properly.`,error014:()=>"useNodeConnections: No node ID found. Call useNodeConnections inside a custom Node or provide a node ID.",error015:()=>"It seems that you are trying to drag a node that is not initialized. Please use onNodesChange as explained in the docs."},ly=[[Number.NEGATIVE_INFINITY,Number.NEGATIVE_INFINITY],[Number.POSITIVE_INFINITY,Number.POSITIVE_INFINITY]],XA=["Enter"," ","Escape"],YA={"node.a11yDescription.default":"Press enter or space to select a node. Press delete to remove it and escape to cancel.","node.a11yDescription.keyboardDisabled":"Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.","node.a11yDescription.ariaLiveMessage":({direction:u,x:f,y:l})=>`Moved selected node ${u}. New position, x: ${f}, y: ${l}`,"edge.a11yDescription.default":"Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.","controls.ariaLabel":"Control Panel","controls.zoomIn.ariaLabel":"Zoom In","controls.zoomOut.ariaLabel":"Zoom Out","controls.fitView.ariaLabel":"Fit View","controls.interactive.ariaLabel":"Toggle Interactivity","minimap.ariaLabel":"Mini Map","handle.ariaLabel":"Handle"},Xn;(function(u){u.Strict="strict",u.Loose="loose"})(Xn||(Xn={}));var C0;(function(u){u.Free="free",u.Vertical="vertical",u.Horizontal="horizontal"})(C0||(C0={}));var S1;(function(u){u.Partial="partial",u.Full="full"})(S1||(S1={}));var SA={inProgress:!1,isValid:null,from:null,fromHandle:null,fromPosition:null,fromNode:null,to:null,toHandle:null,toPosition:null,toNode:null,pointer:null},_0;(function(u){u.Bezier="default",u.Straight="straight",u.Step="step",u.SmoothStep="smoothstep",u.SimpleBezier="simplebezier"})(_0||(_0={}));var Yn;(function(u){u.Arrow="arrow",u.ArrowClosed="arrowclosed"})(Yn||(Yn={}));var Lu;(function(u){u.Left="left",u.Top="top",u.Right="right",u.Bottom="bottom"})(Lu||(Lu={}));var Xq={[Lu.Left]:Lu.Right,[Lu.Right]:Lu.Left,[Lu.Top]:Lu.Bottom,[Lu.Bottom]:Lu.Top};function mA(u){return u===null?null:u?"valid":"invalid"}var pA=(u)=>("id"in u)&&("source"in u)&&("target"in u),kq=(u)=>("id"in u)&&("position"in u)&&!("source"in u)&&!("target"in u),PA=(u)=>("id"in u)&&("internals"in u)&&!("source"in u)&&!("target"in u);var L$=(u,f=[0,0])=>{let{width:l,height:r}=$0(u),n=u.origin??f,i=l*n[0],y=r*n[1];return{x:u.position.x-i,y:u.position.y-y}},MA=(u,f={nodeOrigin:[0,0]})=>{if(u.length===0)return{x:0,y:0,width:0,height:0};let l=u.reduce((r,n)=>{let i=typeof n==="string",y=!f.nodeLookup&&!i?n:void 0;if(f.nodeLookup)y=i?f.nodeLookup.get(n):!PA(n)?f.nodeLookup.get(n.id):n;let _=y?C6(y,f.nodeOrigin):{x:0,y:0,x2:0,y2:0};return x6(r,_)},{x:1/0,y:1/0,x2:-1/0,y2:-1/0});return h6(l)},ry=(u,f={})=>{let l={x:1/0,y:1/0,x2:-1/0,y2:-1/0},r=!1;return u.forEach((n)=>{if(f.filter===void 0||f.filter(n))l=x6(l,C6(n)),r=!0}),r?h6(l):{x:0,y:0,width:0,height:0}},R6=(u,f,[l,r,n]=[0,0,1],i=!1,y=!1)=>{let _={...yy(f,[l,r,n]),width:f.width/n,height:f.height/n},$=[];for(let c of u.values()){let{measured:A,selectable:j=!0,hidden:F=!1}=c;if(y&&!j||F)continue;let U=A.width??c.width??c.initialWidth??null,Q=A.height??c.height??c.initialHeight??null,w=ny(_,p1(c)),L=(U??0)*(Q??0),J=i&&w>0;if(!c.internals.handleBounds||J||w>=L||c.dragging)$.push(c)}return $},Iq=(u,f)=>{let l=new Set;return u.forEach((r)=>{l.add(r.id)}),f.filter((r)=>l.has(r.source)||l.has(r.target))};function mB(u,f){let l=new Map,r=f?.nodes?new Set(f.nodes.map((n)=>n.id)):null;return u.forEach((n)=>{if(n.measured.width&&n.measured.height&&(f?.includeHiddenNodes||!n.hidden)&&(!r||r.has(n.id)))l.set(n.id,n)}),l}async function gq({nodes:u,width:f,height:l,panZoom:r,minZoom:n,maxZoom:i},y){if(u.size===0)return Promise.resolve(!0);let _=mB(u,y),$=ry(_),c=K$($,f,l,y?.minZoom??n,y?.maxZoom??i,y?.padding??0.1);return await r.setViewport(c,{duration:y?.duration,ease:y?.ease,interpolate:y?.interpolate}),Promise.resolve(!0)}function CA({nodeId:u,nextPosition:f,nodeLookup:l,nodeOrigin:r=[0,0],nodeExtent:n,onError:i}){let y=l.get(u),_=y.parentId?l.get(y.parentId):void 0,{x:$,y:c}=_?_.internals.positionAbsolute:{x:0,y:0},A=y.origin??r,j=y.extent||n;if(y.extent==="parent"&&!y.expandParent)if(!_)i?.("005",tr.error005());else{let U=_.measured.width,Q=_.measured.height;if(U&&Q)j=[[$,c],[$+U,c+Q]]}else if(_&&fy(y.extent))j=[[y.extent[0][0]+$,y.extent[0][1]+c],[y.extent[1][0]+$,y.extent[1][1]+c]];let F=fy(j)?m1(f,j,y.measured):f;if(y.measured.width===void 0||y.measured.height===void 0)i?.("015",tr.error015());return{position:{x:F.x-$+(y.measured.width??0)*A[0],y:F.y-c+(y.measured.height??0)*A[1]},positionAbsolute:F}}async function sq({nodesToRemove:u=[],edgesToRemove:f=[],nodes:l,edges:r,onBeforeDelete:n}){let i=new Set(u.map((F)=>F.id)),y=[];for(let F of l){if(F.deletable===!1)continue;let U=i.has(F.id),Q=!U&&F.parentId&&y.find((w)=>w.id===F.parentId);if(U||Q)y.push(F)}let _=new Set(f.map((F)=>F.id)),$=r.filter((F)=>F.deletable!==!1),A=Iq(y,$);for(let F of $)if(_.has(F.id)&&!A.find((Q)=>Q.id===F.id))A.push(F);if(!n)return{edges:A,nodes:y};let j=await n({nodes:y,edges:A});if(typeof j==="boolean")return j?{edges:A,nodes:y}:{edges:[],nodes:[]};return j}var uy=(u,f=0,l=1)=>Math.min(Math.max(u,f),l),m1=(u={x:0,y:0},f,l)=>({x:uy(u.x,f[0][0],f[1][0]-(l?.width??0)),y:uy(u.y,f[0][1],f[1][1]-(l?.height??0))});function aq(u,f,l){let{width:r,height:n}=$0(l),{x:i,y}=l.internals.positionAbsolute;return m1(u,[[i,y],[i+r,y+n]],f)}var Yq=(u,f,l)=>{if(ul)return-uy(Math.abs(u-l),1,f)/f;return 0},oq=(u,f,l=15,r=40)=>{let n=Yq(u.x,r,f.width-r)*l,i=Yq(u.y,r,f.height-r)*l;return[n,i]},x6=(u,f)=>({x:Math.min(u.x,f.x),y:Math.min(u.y,f.y),x2:Math.max(u.x2,f.x2),y2:Math.max(u.y2,f.y2)}),DA=({x:u,y:f,width:l,height:r})=>({x:u,y:f,x2:u+l,y2:f+r}),h6=({x:u,y:f,x2:l,y2:r})=>({x:u,y:f,width:l-u,height:r-f}),p1=(u,f=[0,0])=>{let{x:l,y:r}=PA(u)?u.internals.positionAbsolute:L$(u,f);return{x:l,y:r,width:u.measured?.width??u.width??u.initialWidth??0,height:u.measured?.height??u.height??u.initialHeight??0}},C6=(u,f=[0,0])=>{let{x:l,y:r}=PA(u)?u.internals.positionAbsolute:L$(u,f);return{x:l,y:r,x2:l+(u.measured?.width??u.width??u.initialWidth??0),y2:r+(u.measured?.height??u.height??u.initialHeight??0)}},RA=(u,f)=>h6(x6(DA(u),DA(f))),ny=(u,f)=>{let l=Math.max(0,Math.min(u.x+u.width,f.x+f.width)-Math.max(u.x,f.x)),r=Math.max(0,Math.min(u.y+u.height,f.y+f.height)-Math.max(u.y,f.y));return Math.ceil(l*r)},xA=(u)=>Gr(u.width)&&Gr(u.height)&&Gr(u.x)&&Gr(u.y),Gr=(u)=>!isNaN(u)&&isFinite(u),hA=(u,f)=>{},iy=(u,f=[1,1])=>{return{x:f[0]*Math.round(u.x/f[0]),y:f[1]*Math.round(u.y/f[1])}},yy=({x:u,y:f},[l,r,n],i=!1,y=[1,1])=>{let _={x:(u-l)/n,y:(f-r)/n};return i?iy(_,y):_},w$=({x:u,y:f},[l,r,n])=>{return{x:u*n+l,y:f*n+r}};function di(u,f){if(typeof u==="number")return Math.floor((f-f/(1+u))*0.5);if(typeof u==="string"&&u.endsWith("px")){let l=parseFloat(u);if(!Number.isNaN(l))return Math.floor(l)}if(typeof u==="string"&&u.endsWith("%")){let l=parseFloat(u);if(!Number.isNaN(l))return Math.floor(f*l*0.01)}return console.error(`[React Flow] The padding value "${u}" is invalid. Please provide a number or a string with a valid unit (px or %).`),0}function pB(u,f,l){if(typeof u==="string"||typeof u==="number"){let r=di(u,l),n=di(u,f);return{top:r,right:n,bottom:r,left:n,x:n*2,y:r*2}}if(typeof u==="object"){let r=di(u.top??u.y??0,l),n=di(u.bottom??u.y??0,l),i=di(u.left??u.x??0,f),y=di(u.right??u.x??0,f);return{top:r,right:y,bottom:n,left:i,x:i+y,y:r+n}}return{top:0,right:0,bottom:0,left:0,x:0,y:0}}function PB(u,f,l,r,n,i){let{x:y,y:_}=w$(u,[f,l,r]),{x:$,y:c}=w$({x:u.x+u.width,y:u.y+u.height},[f,l,r]),A=n-$,j=i-c;return{left:Math.floor(y),top:Math.floor(_),right:Math.floor(A),bottom:Math.floor(j)}}var K$=(u,f,l,r,n,i)=>{let y=pB(i,f,l),_=(f-y.x)/u.width,$=(l-y.y)/u.height,c=Math.min(_,$),A=uy(c,r,n),j=u.x+u.width/2,F=u.y+u.height/2,U=f/2-j*A,Q=l/2-F*A,w=PB(u,U,Q,A,f,l),L={left:Math.min(w.left-y.left,0),top:Math.min(w.top-y.top,0),right:Math.min(w.right-y.right,0),bottom:Math.min(w.bottom-y.bottom,0)};return{x:U-L.left+L.right,y:Q-L.top+L.bottom,zoom:A}},_y=()=>typeof navigator<"u"&&navigator?.userAgent?.indexOf("Mac")>=0;function fy(u){return u!==void 0&&u!==null&&u!=="parent"}function $0(u){return{width:u.measured?.width??u.width??u.initialWidth??0,height:u.measured?.height??u.height??u.initialHeight??0}}function bA(u){return(u.measured?.width??u.width??u.initialWidth)!==void 0&&(u.measured?.height??u.height??u.initialHeight)!==void 0}function vA(u,f={width:0,height:0},l,r,n){let i={...u},y=r.get(l);if(y){let _=y.origin||n;i.x+=y.internals.positionAbsolute.x-(f.width??0)*_[0],i.y+=y.internals.positionAbsolute.y-(f.height??0)*_[1]}return i}function kA(u,f){if(u.size!==f.size)return!1;for(let l of u)if(!f.has(l))return!1;return!0}function dq(){let u,f;return{promise:new Promise((r,n)=>{u=r,f=n}),resolve:u,reject:f}}function eq(u){return{...YA,...u||{}}}function W$(u,{snapGrid:f=[0,0],snapToGrid:l=!1,transform:r,containerBounds:n}){let{x:i,y}=zr(u),_=yy({x:i-(n?.left??0),y:y-(n?.top??0)},r),{x:$,y:c}=l?iy(_,f):_;return{xSnapped:$,ySnapped:c,..._}}var b6=(u)=>({width:u.offsetWidth,height:u.offsetHeight}),IA=(u)=>u?.getRootNode?.()||window?.document,MB=["INPUT","SELECT","TEXTAREA"];function gA(u){let f=u.composedPath?.()?.[0]||u.target;if(f?.nodeType!==1)return!1;return MB.includes(f.nodeName)||f.hasAttribute("contenteditable")||!!f.closest(".nokey")}var sA=(u)=>("clientX"in u),zr=(u,f)=>{let l=sA(u),r=l?u.clientX:u.touches?.[0].clientX,n=l?u.clientY:u.touches?.[0].clientY;return{x:r-(f?.left??0),y:n-(f?.top??0)}},Sq=(u,f,l,r,n)=>{let i=f.querySelectorAll(`.${u}`);if(!i||!i.length)return null;return Array.from(i).map((y)=>{let _=y.getBoundingClientRect();return{id:y.getAttribute("data-handleid"),type:u,nodeId:n,position:y.getAttribute("data-handlepos"),x:(_.left-l.left)/r,y:(_.top-l.top)/r,...b6(y)}})};function v6({sourceX:u,sourceY:f,targetX:l,targetY:r,sourceControlX:n,sourceControlY:i,targetControlX:y,targetControlY:_}){let $=u*0.125+n*0.375+y*0.375+l*0.125,c=f*0.125+i*0.375+_*0.375+r*0.125,A=Math.abs($-u),j=Math.abs(c-f);return[$,c,A,j]}function P6(u,f){if(u>=0)return 0.5*u;return f*25*Math.sqrt(-u)}function mq({pos:u,x1:f,y1:l,x2:r,y2:n,c:i}){switch(u){case Lu.Left:return[f-P6(f-r,i),l];case Lu.Right:return[f+P6(r-f,i),l];case Lu.Top:return[f,l-P6(l-n,i)];case Lu.Bottom:return[f,l+P6(n-l,i)]}}function k6({sourceX:u,sourceY:f,sourcePosition:l=Lu.Bottom,targetX:r,targetY:n,targetPosition:i=Lu.Top,curvature:y=0.25}){let[_,$]=mq({pos:l,x1:u,y1:f,x2:r,y2:n,c:y}),[c,A]=mq({pos:i,x1:r,y1:n,x2:u,y2:f,c:y}),[j,F,U,Q]=v6({sourceX:u,sourceY:f,targetX:r,targetY:n,sourceControlX:_,sourceControlY:$,targetControlX:c,targetControlY:A});return[`M${u},${f} C${_},${$} ${c},${A} ${r},${n}`,j,F,U,Q]}function aA({sourceX:u,sourceY:f,targetX:l,targetY:r}){let n=Math.abs(l-u)/2,i=l0}var CB=({source:u,sourceHandle:f,target:l,targetHandle:r})=>`xy-edge__${u}${f||""}-${l}${r||""}`,RB=(u,f)=>{return f.some((l)=>l.source===u.source&&l.target===u.target&&(l.sourceHandle===u.sourceHandle||!l.sourceHandle&&!u.sourceHandle)&&(l.targetHandle===u.targetHandle||!l.targetHandle&&!u.targetHandle))},oA=(u,f,l={})=>{if(!u.source||!u.target)return hA("006",tr.error006()),f;let r=l.getEdgeId||CB,n;if(pA(u))n={...u};else n={...u,id:r(u)};if(RB(n,f))return f;if(n.sourceHandle===null)delete n.sourceHandle;if(n.targetHandle===null)delete n.targetHandle;return f.concat(n)};function I6({sourceX:u,sourceY:f,targetX:l,targetY:r}){let[n,i,y,_]=aA({sourceX:u,sourceY:f,targetX:l,targetY:r});return[`M ${u},${f}L ${l},${r}`,n,i,y,_]}var pq={[Lu.Left]:{x:-1,y:0},[Lu.Right]:{x:1,y:0},[Lu.Top]:{x:0,y:-1},[Lu.Bottom]:{x:0,y:1}},xB=({source:u,sourcePosition:f=Lu.Bottom,target:l})=>{if(f===Lu.Left||f===Lu.Right)return u.xMath.sqrt(Math.pow(f.x-u.x,2)+Math.pow(f.y-u.y,2));function hB({source:u,sourcePosition:f=Lu.Bottom,target:l,targetPosition:r=Lu.Top,center:n,offset:i,stepPosition:y}){let _=pq[f],$=pq[r],c={x:u.x+_.x*i,y:u.y+_.y*i},A={x:l.x+$.x*i,y:l.y+$.y*i},j=xB({source:c,sourcePosition:f,target:A}),F=j.x!==0?"x":"y",U=j[F],Q=[],w,L,J={x:0,y:0},N={x:0,y:0},[,,q,W]=aA({sourceX:u.x,sourceY:u.y,targetX:l.x,targetY:l.y});if(_[F]*$[F]===-1){if(F==="x")w=n.x??c.x+(A.x-c.x)*y,L=n.y??(c.y+A.y)/2;else w=n.x??(c.x+A.x)/2,L=n.y??c.y+(A.y-c.y)*y;let E=[{x:w,y:c.y},{x:w,y:A.y}],D=[{x:c.x,y:L},{x:A.x,y:L}];if(_[F]===U)Q=F==="x"?E:D;else Q=F==="x"?D:E}else{let E=[{x:c.x,y:A.y}],D=[{x:A.x,y:c.y}];if(F==="x")Q=_.x===U?D:E;else Q=_.y===U?E:D;if(f===r){let O=Math.abs(u[F]-l[F]);if(O<=i){let p=Math.min(i-1,i-O);if(_[F]===U)J[F]=(c[F]>u[F]?-1:1)*p;else N[F]=(A[F]>l[F]?-1:1)*p}}if(f!==r){let O=F==="x"?"y":"x",p=_[F]===$[O],Y=c[O]>A[O],v=c[O]=m)w=(h.x+B.x)/2,L=Q[0].y;else w=Q[0].x,L=(h.y+B.y)/2}let z={x:c.x+J.x,y:c.y+J.y},Z={x:A.x+N.x,y:A.y+N.y};return[[u,...z.x!==Q[0].x||z.y!==Q[0].y?[z]:[],...Q,...Z.x!==Q[Q.length-1].x||Z.y!==Q[Q.length-1].y?[Z]:[],l],w,L,q,W]}function bB(u,f,l,r){let n=Math.min(Pq(u,f)/2,Pq(f,l)/2,r),{x:i,y}=f;if(u.x===i&&i===l.x||u.y===y&&y===l.y)return`L${i} ${y}`;if(u.y===y){let c=u.xl.id===f))||null}function g6(u,f){if(!u)return"";if(typeof u==="string")return u;return`${f?`${f}__`:""}${Object.keys(u).sort().map((r)=>`${r}=${u[r]}`).join("&")}`}function rN(u,{id:f,defaultColor:l,defaultMarkerStart:r,defaultMarkerEnd:n}){let i=new Set;return u.reduce((y,_)=>{return[_.markerStart||r,_.markerEnd||n].forEach(($)=>{if($&&typeof $==="object"){let c=g6($,f);if(!i.has(c))y.push({id:c,color:$.color||l,...$}),i.add(c)}}),y},[]).sort((y,_)=>y.id.localeCompare(_.id))}var nN=1000,vB=10,dA={nodeOrigin:[0,0],nodeExtent:ly,elevateNodesOnSelect:!0,zIndexMode:"basic",defaults:{}},kB={...dA,checkEquality:!0};function eA(u,f){let l={...u};for(let r in f)if(f[r]!==void 0)l[r]=f[r];return l}function iN(u,f,l){let r=eA(dA,l);for(let n of u.values())if(n.parentId)f9(n,u,f,r);else{let i=L$(n,r.nodeOrigin),y=fy(n.extent)?n.extent:r.nodeExtent,_=m1(i,y,$0(n));n.internals.positionAbsolute=_}}function IB(u,f){if(!u.handles)return!u.measured?void 0:f?.internals.handleBounds;let l=[],r=[];for(let n of u.handles){let i={id:n.id,width:n.width??1,height:n.height??1,nodeId:u.id,x:n.x,y:n.y,position:n.position,type:n.type};if(n.type==="source")l.push(i);else if(n.type==="target")r.push(i)}return{source:l,target:r}}function u9(u){return u==="manual"}function s6(u,f,l,r={}){let n=eA(kB,r),i={i:0},y=new Map(f),_=n?.elevateNodesOnSelect&&!u9(n.zIndexMode)?nN:0,$=u.length>0,c=!1;f.clear(),l.clear();for(let A of u){let j=y.get(A.id);if(n.checkEquality&&A===j?.internals.userNode)f.set(A.id,j);else{let F=L$(A,n.nodeOrigin),U=fy(A.extent)?A.extent:n.nodeExtent,Q=m1(F,U,$0(A));j={...n.defaults,...A,measured:{width:A.measured?.width,height:A.measured?.height},internals:{positionAbsolute:Q,handleBounds:IB(A,j),z:yN(A,_,n.zIndexMode),userNode:A}},f.set(A.id,j)}if((j.measured===void 0||j.measured.width===void 0||j.measured.height===void 0)&&!j.hidden)$=!1;if(A.parentId)f9(j,f,l,r,i);c||=A.selected??!1}return{nodesInitialized:$,hasSelectedNodes:c}}function gB(u,f){if(!u.parentId)return;let l=f.get(u.parentId);if(l)l.set(u.id,u);else f.set(u.parentId,new Map([[u.id,u]]))}function f9(u,f,l,r,n){let{elevateNodesOnSelect:i,nodeOrigin:y,nodeExtent:_,zIndexMode:$}=eA(dA,r),c=u.parentId,A=f.get(c);if(!A){console.warn(`Parent node ${c} not found. Please make sure that parent nodes are in front of their child nodes in the nodes array.`);return}if(gB(u,l),n&&!A.parentId&&A.internals.rootParentIndex===void 0&&$==="auto")A.internals.rootParentIndex=++n.i,A.internals.z=A.internals.z+n.i*vB;if(n&&A.internals.rootParentIndex!==void 0)n.i=A.internals.rootParentIndex;let j=i&&!u9($)?nN:0,{x:F,y:U,z:Q}=sB(u,A,y,_,j,$),{positionAbsolute:w}=u.internals,L=F!==w.x||U!==w.y;if(L||Q!==u.internals.z)f.set(u.id,{...u,internals:{...u.internals,positionAbsolute:L?{x:F,y:U}:w,z:Q}})}function yN(u,f,l){let r=Gr(u.zIndex)?u.zIndex:0;if(u9(l))return r;return r+(u.selected?f:0)}function sB(u,f,l,r,n,i){let{x:y,y:_}=f.internals.positionAbsolute,$=$0(u),c=L$(u,l),A=fy(u.extent)?m1(c,u.extent,$):c,j=m1({x:y+A.x,y:_+A.y},r,$);if(u.extent==="parent")j=aq(j,$,f);let F=yN(u,n,i),U=f.internals.z??0;return{x:j.x,y:j.y,z:U>=F?U+1:F}}function a6(u,f,l,r=[0,0]){let n=[],i=new Map;for(let y of u){let _=f.get(y.parentId);if(!_)continue;let $=i.get(y.parentId)?.expandedRect??p1(_),c=RA($,y.rect);i.set(y.parentId,{expandedRect:c,parent:_})}if(i.size>0)i.forEach(({expandedRect:y,parent:_},$)=>{let c=_.internals.positionAbsolute,A=$0(_),j=_.origin??r,F=y.x0||U>0||L||J)n.push({id:$,type:"position",position:{x:_.position.x-F+L,y:_.position.y-U+J}}),l.get($)?.forEach((N)=>{if(!u.some((q)=>q.id===N.id))n.push({id:N.id,type:"position",position:{x:N.position.x+F,y:N.position.y+U}})});if(A.width0){let U=a6(F,f,l,n);c.push(...U)}return{changes:c,updatedInternals:$}}async function $N({delta:u,panZoom:f,transform:l,translateExtent:r,width:n,height:i}){if(!f||!u.x&&!u.y)return Promise.resolve(!1);let y=await f.setViewportConstrained({x:l[0]+u.x,y:l[1]+u.y,zoom:l[2]},[[0,0],[n,i]],r),_=!!y&&(y.x!==l[0]||y.y!==l[1]||y.k!==l[2]);return Promise.resolve(_)}function xq(u,f,l,r,n,i){let y=n,_=r.get(y)||new Map;r.set(y,_.set(l,f)),y=`${n}-${u}`;let $=r.get(y)||new Map;if(r.set(y,$.set(l,f)),i){y=`${n}-${u}-${i}`;let c=r.get(y)||new Map;r.set(y,c.set(l,f))}}function l9(u,f,l){u.clear(),f.clear();for(let r of l){let{source:n,target:i,sourceHandle:y=null,targetHandle:_=null}=r,$={edgeId:r.id,source:n,target:i,sourceHandle:y,targetHandle:_},c=`${n}-${y}--${i}-${_}`,A=`${i}-${_}--${n}-${y}`;xq("source",$,A,u,n,y),xq("target",$,c,u,i,_),f.set(r.id,r)}}function tN(u,f){if(!u.parentId)return!1;let l=f.get(u.parentId);if(!l)return!1;if(l.selected)return!0;return tN(l,f)}function hq(u,f,l){let r=u;do{if(r?.matches?.(f))return!0;if(r===l)return!1;r=r?.parentElement}while(r);return!1}function aB(u,f,l,r){let n=new Map;for(let[i,y]of u)if((y.selected||y.id===r)&&(!y.parentId||!tN(y,u))&&(y.draggable||f&&typeof y.draggable>"u")){let _=u.get(i);if(_)n.set(i,{id:i,position:_.position||{x:0,y:0},distance:{x:l.x-_.internals.positionAbsolute.x,y:l.y-_.internals.positionAbsolute.y},extent:_.extent,parentId:_.parentId,origin:_.origin,expandParent:_.expandParent,internals:{positionAbsolute:_.internals.positionAbsolute||{x:0,y:0}},measured:{width:_.measured.width??0,height:_.measured.height??0}})}return n}function HA({nodeId:u,dragItems:f,nodeLookup:l,dragging:r=!0}){let n=[];for(let[y,_]of f){let $=l.get(y)?.internals.userNode;if($)n.push({...$,position:_.position,dragging:r})}if(!u)return[n[0],n];let i=l.get(u)?.internals.userNode;return[!i?n[0]:{...i,position:f.get(u)?.position||i.position,dragging:r},n]}function oB({dragItems:u,snapGrid:f,x:l,y:r}){let n=u.values().next().value;if(!n)return null;let i={x:l-n.distance.x,y:r-n.distance.y},y=iy(i,f);return{x:y.x-i.x,y:y.y-i.y}}function cN({onNodeMouseDown:u,getStoreItems:f,onDragStart:l,onDrag:r,onDragStop:n}){let i={x:null,y:null},y=0,_=new Map,$=!1,c={x:0,y:0},A=null,j=!1,F=null,U=!1,Q=!1,w=null;function L({noDragClassName:N,handleSelector:q,domNode:W,isSelectable:z,nodeId:Z,nodeClickDistance:H=0}){F=of(W);function E({x:X,y:m}){let{nodeLookup:O,nodeExtent:p,snapGrid:Y,snapToGrid:v,nodeOrigin:T,onNodeDrag:S,onSelectionDrag:k,onError:I,updateNodePositions:b}=f();i={x:X,y:m};let o=!1,s=_.size>1,x=s&&p?DA(ry(_)):null,fu=s&&v?oB({dragItems:_,snapGrid:Y,x:X,y:m}):null;for(let[$u,tu]of _){if(!O.has($u))continue;let ju={x:X-tu.distance.x,y:m-tu.distance.y};if(v)ju=fu?{x:Math.round(ju.x+fu.x),y:Math.round(ju.y+fu.y)}:iy(ju,Y);let Gu=null;if(s&&p&&!tu.extent&&x){let{positionAbsolute:e}=tu.internals,uu=e.x-x.x+p[0][0],Wu=e.x+tu.measured.width-x.x2+p[1][0],g=e.y-x.y+p[0][1],Qu=e.y+tu.measured.height-x.y2+p[1][1];Gu=[[uu,g],[Wu,Qu]]}let{position:qu,positionAbsolute:P}=CA({nodeId:$u,nextPosition:ju,nodeLookup:O,nodeExtent:Gu?Gu:p,nodeOrigin:T,onError:I});o=o||tu.position.x!==qu.x||tu.position.y!==qu.y,tu.position=qu,tu.internals.positionAbsolute=P}if(Q=Q||o,!o)return;if(b(_,!0),w&&(r||S||!Z&&k)){let[$u,tu]=HA({nodeId:Z,dragItems:_,nodeLookup:O});if(r?.(w,_,$u,tu),S?.(w,$u,tu),!Z)k?.(w,tu)}}async function D(){if(!A)return;let{transform:X,panBy:m,autoPanSpeed:O,autoPanOnNodeDrag:p}=f();if(!p){$=!1,cancelAnimationFrame(y);return}let[Y,v]=oq(c,A,O);if(Y!==0||v!==0){if(i.x=(i.x??0)-Y/X[2],i.y=(i.y??0)-v/X[2],await m({x:Y,y:v}))E(i)}y=requestAnimationFrame(D)}function h(X){let{nodeLookup:m,multiSelectionActive:O,nodesDraggable:p,transform:Y,snapGrid:v,snapToGrid:T,selectNodesOnDrag:S,onNodeDragStart:k,onSelectionDragStart:I,unselectNodesAndEdges:b}=f();if(j=!0,(!S||!z)&&!O&&Z){if(!m.get(Z)?.selected)b()}if(z&&S&&Z)u?.(Z);let o=W$(X.sourceEvent,{transform:Y,snapGrid:v,snapToGrid:T,containerBounds:A});if(i=o,_=aB(m,p,o,Z),_.size>0&&(l||k||!Z&&I)){let[s,x]=HA({nodeId:Z,dragItems:_,nodeLookup:m});if(l?.(X.sourceEvent,_,s,x),k?.(X.sourceEvent,s,x),!Z)I?.(X.sourceEvent,x)}}let B=u$().clickDistance(H).on("start",(X)=>{let{domNode:m,nodeDragThreshold:O,transform:p,snapGrid:Y,snapToGrid:v}=f();if(A=m?.getBoundingClientRect()||null,U=!1,Q=!1,w=X.sourceEvent,O===0)h(X);i=W$(X.sourceEvent,{transform:p,snapGrid:Y,snapToGrid:v,containerBounds:A}),c=zr(X.sourceEvent,A)}).on("drag",(X)=>{let{autoPanOnNodeDrag:m,transform:O,snapGrid:p,snapToGrid:Y,nodeDragThreshold:v,nodeLookup:T}=f(),S=W$(X.sourceEvent,{transform:O,snapGrid:p,snapToGrid:Y,containerBounds:A});if(w=X.sourceEvent,X.sourceEvent.type==="touchmove"&&X.sourceEvent.touches.length>1||Z&&!T.has(Z))U=!0;if(U)return;if(!$&&m&&j)$=!0,D();if(!j){let k=zr(X.sourceEvent,A),I=k.x-c.x,b=k.y-c.y;if(Math.sqrt(I*I+b*b)>v)h(X)}if((i.x!==S.xSnapped||i.y!==S.ySnapped)&&_&&j)c=zr(X.sourceEvent,A),E(S)}).on("end",(X)=>{if(!j||U)return;if($=!1,j=!1,cancelAnimationFrame(y),_.size>0){let{nodeLookup:m,updateNodePositions:O,onNodeDragStop:p,onSelectionDragStop:Y}=f();if(Q)O(_,!1),Q=!1;if(n||p||!Z&&Y){let[v,T]=HA({nodeId:Z,dragItems:_,nodeLookup:m,dragging:!1});if(n?.(X.sourceEvent,_,v,T),p?.(X.sourceEvent,v,T),!Z)Y?.(X.sourceEvent,T)}}}).filter((X)=>{let m=X.target;return!X.button&&(!N||!hq(m,`.${N}`,W))&&(!q||hq(m,q,W))});F.call(B)}function J(){F?.on(".drag",null)}return{update:L,destroy:J}}function dB(u,f,l){let r=[],n={x:u.x-l,y:u.y-l,width:l*2,height:l*2};for(let i of f.values())if(ny(n,p1(i))>0)r.push(i);return r}var eB=250;function uV(u,f,l,r){let n=[],i=1/0,y=dB(u,l,f+eB);for(let _ of y){let $=[..._.internals.handleBounds?.source??[],..._.internals.handleBounds?.target??[]];for(let c of $){if(r.nodeId===c.nodeId&&r.type===c.type&&r.id===c.id)continue;let{x:A,y:j}=Sn(_,c,c.position,!0),F=Math.sqrt(Math.pow(A-u.x,2)+Math.pow(j-u.y,2));if(F>f)continue;if(F1){let _=r.type==="source"?"target":"source";return n.find(($)=>$.type===_)??n[0]}return n[0]}function AN(u,f,l,r,n,i=!1){let y=r.get(u);if(!y)return null;let _=n==="strict"?y.internals.handleBounds?.[f]:[...y.internals.handleBounds?.source??[],...y.internals.handleBounds?.target??[]],$=(l?_?.find((c)=>c.id===l):_?.[0])??null;return $&&i?{...$,...Sn(y,$,$.position,!0)}:$}function jN(u,f){if(u)return u;else if(f?.classList.contains("target"))return"target";else if(f?.classList.contains("source"))return"source";return null}function fV(u,f){let l=null;if(f)l=!0;else if(u&&!f)l=!1;return l}var FN=()=>!0;function lV(u,{connectionMode:f,connectionRadius:l,handleId:r,nodeId:n,edgeUpdaterType:i,isTarget:y,domNode:_,nodeLookup:$,lib:c,autoPanOnConnect:A,flowId:j,panBy:F,cancelConnection:U,onConnectStart:Q,onConnect:w,onConnectEnd:L,isValidConnection:J=FN,onReconnectEnd:N,updateConnection:q,getTransform:W,getFromHandle:z,autoPanSpeed:Z,dragThreshold:H=1,handleDomNode:E}){let D=IA(u.target),h=0,B,{x:X,y:m}=zr(u),O=jN(i,E),p=_?.getBoundingClientRect(),Y=!1;if(!p||!O)return;let v=AN(n,O,r,$,f);if(!v)return;let T=zr(u,p),S=!1,k=null,I=!1,b=null;function o(){if(!A||!p)return;let[qu,P]=oq(T,p,Z);F({x:qu,y:P}),h=requestAnimationFrame(o)}let s={...v,nodeId:n,type:O,position:v.position},x=$.get(n),$u={inProgress:!0,isValid:null,from:Sn(x,s,Lu.Left,!0),fromHandle:s,fromPosition:s.position,fromNode:x,to:T,toHandle:null,toPosition:Xq[s.position],toNode:null,pointer:T};function tu(){Y=!0,q($u),Q?.(u,{nodeId:n,handleId:r,handleType:O})}if(H===0)tu();function ju(qu){if(!Y){let{x:Qu,y:Tu}=zr(qu),Zu=Qu-X,Xu=Tu-m;if(!(Zu*Zu+Xu*Xu>H*H))return;tu()}if(!z()||!s){Gu(qu);return}let P=W();if(T=zr(qu,p),B=uV(yy(T,P,!1,[1,1]),l,$,s),!S)o(),S=!0;let e=JN(qu,{handle:B,connectionMode:f,fromNodeId:n,fromHandleId:r,fromType:y?"target":"source",isValidConnection:J,doc:D,lib:c,flowId:j,nodeLookup:$});b=e.handleDomNode,k=e.connection,I=fV(!!B,e.isValid);let uu=$.get(n),Wu=uu?Sn(uu,s,Lu.Left,!0):$u.from,g={...$u,from:Wu,isValid:I,to:e.toHandle&&I?w$({x:e.toHandle.x,y:e.toHandle.y},P):T,toHandle:e.toHandle,toPosition:I&&e.toHandle?e.toHandle.position:Xq[s.position],toNode:e.toHandle?$.get(e.toHandle.nodeId):null,pointer:T};q(g),$u=g}function Gu(qu){if("touches"in qu&&qu.touches.length>0)return;if(Y){if((B||b)&&k&&I)w?.(k);let{inProgress:P,...e}=$u,uu={...e,toPosition:$u.toHandle?$u.toPosition:null};if(L?.(qu,uu),i)N?.(qu,uu)}U(),cancelAnimationFrame(h),S=!1,I=!1,k=null,b=null,D.removeEventListener("mousemove",ju),D.removeEventListener("mouseup",Gu),D.removeEventListener("touchmove",ju),D.removeEventListener("touchend",Gu)}D.addEventListener("mousemove",ju),D.addEventListener("mouseup",Gu),D.addEventListener("touchmove",ju),D.addEventListener("touchend",Gu)}function JN(u,{handle:f,connectionMode:l,fromNodeId:r,fromHandleId:n,fromType:i,doc:y,lib:_,flowId:$,isValidConnection:c=FN,nodeLookup:A}){let j=i==="target",F=f?y.querySelector(`.${_}-flow__handle[data-id="${$}-${f?.nodeId}-${f?.id}-${f?.type}"]`):null,{x:U,y:Q}=zr(u),w=y.elementFromPoint(U,Q),L=w?.classList.contains(`${_}-flow__handle`)?w:F,J={handleDomNode:L,isValid:!1,connection:null,toHandle:null};if(L){let N=jN(void 0,L),q=L.getAttribute("data-nodeid"),W=L.getAttribute("data-handleid"),z=L.classList.contains("connectable"),Z=L.classList.contains("connectableend");if(!q||!N)return J;let H={source:j?q:r,sourceHandle:j?W:n,target:j?r:q,targetHandle:j?n:W};J.connection=H;let D=z&&Z&&(l===Xn.Strict?j&&N==="source"||!j&&N==="target":q!==r||W!==n);J.isValid=D&&c(H),J.toHandle=AN(q,N,W,A,l,!0)}return J}var o6={onPointerDown:lV,isValid:JN};function UN({domNode:u,panZoom:f,getTransform:l,getViewScale:r}){let n=of(u);function i({translateExtent:_,width:$,height:c,zoomStep:A=1,pannable:j=!0,zoomable:F=!0,inversePan:U=!1}){let Q=(q)=>{if(q.sourceEvent.type!=="wheel"||!f)return;let W=l(),z=q.sourceEvent.ctrlKey&&_y()?10:1,Z=-q.sourceEvent.deltaY*(q.sourceEvent.deltaMode===1?0.05:q.sourceEvent.deltaMode?1:0.002)*A,H=W[2]*Math.pow(2,Z*z);f.scaleTo(H)},w=[0,0],L=(q)=>{if(q.sourceEvent.type==="mousedown"||q.sourceEvent.type==="touchstart")w=[q.sourceEvent.clientX??q.sourceEvent.touches[0].clientX,q.sourceEvent.clientY??q.sourceEvent.touches[0].clientY]},J=(q)=>{let W=l();if(q.sourceEvent.type!=="mousemove"&&q.sourceEvent.type!=="touchmove"||!f)return;let z=[q.sourceEvent.clientX??q.sourceEvent.touches[0].clientX,q.sourceEvent.clientY??q.sourceEvent.touches[0].clientY],Z=[z[0]-w[0],z[1]-w[1]];w=z;let H=r()*Math.max(W[2],Math.log(W[2]))*(U?-1:1),E={x:W[0]-Z[0]*H,y:W[1]-Z[1]*H},D=[[0,0],[$,c]];f.setViewportConstrained({x:E.x,y:E.y,zoom:W[2]},D,_)},N=N$().on("start",L).on("zoom",j?J:null).on("zoom.wheel",F?Q:null);n.call(N,{})}function y(){n.on("zoom",null)}return{update:i,destroy:y,pointer:Wl}}var d6=(u)=>({x:u.x,y:u.y,zoom:u.k}),BA=({x:u,y:f,zoom:l})=>X1.translate(u,f).scale(l),ei=(u,f)=>u.target.closest(`.${f}`),QN=(u,f)=>f===2&&Array.isArray(u)&&u.includes(2),rV=(u)=>((u*=2)<=1?u*u*u:(u-=2)*u*u+2)/2,VA=(u,f=0,l=rV,r=()=>{})=>{let n=typeof f==="number"&&f>0;if(!n)r();return n?u.transition().duration(f).ease(l).on("end",r):u},qN=(u)=>{let f=u.ctrlKey&&_y()?10:1;return-u.deltaY*(u.deltaMode===1?0.05:u.deltaMode?1:0.002)*f};function nV({zoomPanValues:u,noWheelClassName:f,d3Selection:l,d3Zoom:r,panOnScrollMode:n,panOnScrollSpeed:i,zoomOnPinch:y,onPanZoomStart:_,onPanZoom:$,onPanZoomEnd:c}){return(A)=>{if(ei(A,f)){if(A.ctrlKey)A.preventDefault();return!1}A.preventDefault(),A.stopImmediatePropagation();let j=l.property("__zoom").k||1;if(A.ctrlKey&&y){let L=Wl(A),J=qN(A),N=j*Math.pow(2,J);r.scaleTo(l,N,L,A);return}let F=A.deltaMode===1?20:1,U=n===C0.Vertical?0:A.deltaX*F,Q=n===C0.Horizontal?0:A.deltaY*F;if(!_y()&&A.shiftKey&&n!==C0.Vertical)U=A.deltaY*F,Q=0;r.translateBy(l,-(U/j)*i,-(Q/j)*i,{internal:!0});let w=d6(l.property("__zoom"));if(clearTimeout(u.panScrollTimeout),!u.isPanScrolling)u.isPanScrolling=!0,_?.(A,w);else $?.(A,w),u.panScrollTimeout=setTimeout(()=>{c?.(A,w),u.isPanScrolling=!1},150)}}function iV({noWheelClassName:u,preventScrolling:f,d3ZoomHandler:l}){return function(r,n){let i=r.type==="wheel",y=!f&&i&&!r.ctrlKey,_=ei(r,u);if(r.ctrlKey&&i&&_)r.preventDefault();if(y||_)return null;r.preventDefault(),l.call(this,r,n)}}function yV({zoomPanValues:u,onDraggingChange:f,onPanZoomStart:l}){return(r)=>{if(r.sourceEvent?.internal)return;let n=d6(r.transform);if(u.mouseButton=r.sourceEvent?.button||0,u.isZoomingOrPanning=!0,u.prevViewport=n,r.sourceEvent?.type==="mousedown")f(!0);if(l)l?.(r.sourceEvent,n)}}function _V({zoomPanValues:u,panOnDrag:f,onPaneContextMenu:l,onTransformChange:r,onPanZoom:n}){return(i)=>{if(u.usedRightMouseButton=!!(l&&QN(f,u.mouseButton??0)),!i.sourceEvent?.sync)r([i.transform.x,i.transform.y,i.transform.k]);if(n&&!i.sourceEvent?.internal)n?.(i.sourceEvent,d6(i.transform))}}function $V({zoomPanValues:u,panOnDrag:f,panOnScroll:l,onDraggingChange:r,onPanZoomEnd:n,onPaneContextMenu:i}){return(y)=>{if(y.sourceEvent?.internal)return;if(u.isZoomingOrPanning=!1,i&&QN(f,u.mouseButton??0)&&!u.usedRightMouseButton&&y.sourceEvent)i(y.sourceEvent);if(u.usedRightMouseButton=!1,r(!1),n){let _=d6(y.transform);u.prevViewport=_,clearTimeout(u.timerId),u.timerId=setTimeout(()=>{n?.(y.sourceEvent,_)},l?150:0)}}}function tV({zoomActivationKeyPressed:u,zoomOnScroll:f,zoomOnPinch:l,panOnDrag:r,panOnScroll:n,zoomOnDoubleClick:i,userSelectionActive:y,noWheelClassName:_,noPanClassName:$,lib:c,connectionInProgress:A}){return(j)=>{let F=u||f,U=l&&j.ctrlKey,Q=j.type==="wheel";if(j.button===1&&j.type==="mousedown"&&(ei(j,`${c}-flow__node`)||ei(j,`${c}-flow__edge`)))return!0;if(!r&&!F&&!n&&!i&&!l)return!1;if(y)return!1;if(A&&!Q)return!1;if(ei(j,_)&&Q)return!1;if(ei(j,$)&&(!Q||n&&Q&&!u))return!1;if(!l&&j.ctrlKey&&Q)return!1;if(!l&&j.type==="touchstart"&&j.touches?.length>1)return j.preventDefault(),!1;if(!F&&!n&&!U&&Q)return!1;if(!r&&(j.type==="mousedown"||j.type==="touchstart"))return!1;if(Array.isArray(r)&&!r.includes(j.button)&&j.type==="mousedown")return!1;let w=Array.isArray(r)&&r.includes(j.button)||!j.button||j.button<=1;return(!j.ctrlKey||Q)&&w}}function NN({domNode:u,minZoom:f,maxZoom:l,translateExtent:r,viewport:n,onPanZoom:i,onPanZoomStart:y,onPanZoomEnd:_,onDraggingChange:$}){let c={isZoomingOrPanning:!1,usedRightMouseButton:!1,prevViewport:{x:0,y:0,zoom:0},mouseButton:0,timerId:void 0,panScrollTimeout:void 0,isPanScrolling:!1},A=u.getBoundingClientRect(),j=N$().scaleExtent([f,l]).translateExtent(r),F=of(u).call(j);N({x:n.x,y:n.y,zoom:uy(n.zoom,f,l)},[[0,0],[A.width,A.height]],r);let U=F.on("wheel.zoom"),Q=F.on("dblclick.zoom");j.wheelDelta(qN);function w(B,X){if(F)return new Promise((m)=>{j?.interpolate(X?.interpolate==="linear"?y0:V1).transform(VA(F,X?.duration,X?.ease,()=>m(!0)),B)});return Promise.resolve(!1)}function L({noWheelClassName:B,noPanClassName:X,onPaneContextMenu:m,userSelectionActive:O,panOnScroll:p,panOnDrag:Y,panOnScrollMode:v,panOnScrollSpeed:T,preventScrolling:S,zoomOnPinch:k,zoomOnScroll:I,zoomOnDoubleClick:b,zoomActivationKeyPressed:o,lib:s,onTransformChange:x,connectionInProgress:fu,paneClickDistance:$u,selectionOnDrag:tu}){if(O&&!c.isZoomingOrPanning)J();let ju=p&&!o&&!O;j.clickDistance(tu?1/0:!Gr($u)||$u<0?0:$u);let Gu=ju?nV({zoomPanValues:c,noWheelClassName:B,d3Selection:F,d3Zoom:j,panOnScrollMode:v,panOnScrollSpeed:T,zoomOnPinch:k,onPanZoomStart:y,onPanZoom:i,onPanZoomEnd:_}):iV({noWheelClassName:B,preventScrolling:S,d3ZoomHandler:U});if(F.on("wheel.zoom",Gu,{passive:!1}),!O){let P=yV({zoomPanValues:c,onDraggingChange:$,onPanZoomStart:y});j.on("start",P);let e=_V({zoomPanValues:c,panOnDrag:Y,onPaneContextMenu:!!m,onPanZoom:i,onTransformChange:x});j.on("zoom",e);let uu=$V({zoomPanValues:c,panOnDrag:Y,panOnScroll:p,onPaneContextMenu:m,onPanZoomEnd:_,onDraggingChange:$});j.on("end",uu)}let qu=tV({zoomActivationKeyPressed:o,panOnDrag:Y,zoomOnScroll:I,panOnScroll:p,zoomOnDoubleClick:b,zoomOnPinch:k,userSelectionActive:O,noPanClassName:X,noWheelClassName:B,lib:s,connectionInProgress:fu});if(j.filter(qu),b)F.on("dblclick.zoom",Q);else F.on("dblclick.zoom",null)}function J(){j.on("zoom",null)}async function N(B,X,m){let O=BA(B),p=j?.constrain()(O,X,m);if(p)await w(p);return new Promise((Y)=>Y(p))}async function q(B,X){let m=BA(B);return await w(m,X),new Promise((O)=>O(m))}function W(B){if(F){let X=BA(B),m=F.property("__zoom");if(m.k!==B.zoom||m.x!==B.x||m.y!==B.y)j?.transform(F,X,null,{sync:!0})}}function z(){let B=F?q$(F.node()):{x:0,y:0,k:1};return{x:B.x,y:B.y,zoom:B.k}}function Z(B,X){if(F)return new Promise((m)=>{j?.interpolate(X?.interpolate==="linear"?y0:V1).scaleTo(VA(F,X?.duration,X?.ease,()=>m(!0)),B)});return Promise.resolve(!1)}function H(B,X){if(F)return new Promise((m)=>{j?.interpolate(X?.interpolate==="linear"?y0:V1).scaleBy(VA(F,X?.duration,X?.ease,()=>m(!0)),B)});return Promise.resolve(!1)}function E(B){j?.scaleExtent(B)}function D(B){j?.translateExtent(B)}function h(B){let X=!Gr(B)||B<0?0:B;j?.clickDistance(X)}return{update:L,destroy:J,setViewport:q,setViewportConstrained:N,getViewport:z,scaleTo:Z,scaleBy:H,setScaleExtent:E,setTranslateExtent:D,syncViewport:W,setClickDistance:h}}var mn;(function(u){u.Line="line",u.Handle="handle"})(mn||(mn={}));function cV({width:u,prevWidth:f,height:l,prevHeight:r,affectsX:n,affectsY:i}){let y=u-f,_=l-r,$=[y>0?1:y<0?-1:0,_>0?1:_<0?-1:0];if(y&&n)$[0]=$[0]*-1;if(_&&i)$[1]=$[1]*-1;return $}function bq(u){let f=u.includes("right")||u.includes("left"),l=u.includes("bottom")||u.includes("top"),r=u.includes("left"),n=u.includes("top");return{isHorizontal:f,isVertical:l,affectsX:r,affectsY:n}}function Vn(u,f){return Math.max(0,f-u)}function Dn(u,f){return Math.max(0,u-f)}function M6(u,f,l){return Math.max(0,f-u,u-l)}function vq(u,f){return u?!f:f}function AV(u,f,l,r,n,i,y,_){let{affectsX:$,affectsY:c}=f,{isHorizontal:A,isVertical:j}=f,F=A&&j,{xSnapped:U,ySnapped:Q}=l,{minWidth:w,maxWidth:L,minHeight:J,maxHeight:N}=r,{x:q,y:W,width:z,height:Z,aspectRatio:H}=u,E=Math.floor(A?U-u.pointerX:0),D=Math.floor(j?Q-u.pointerY:0),h=z+($?-E:E),B=Z+(c?-D:D),X=-i[0]*z,m=-i[1]*Z,O=M6(h,w,L),p=M6(B,J,N);if(y){let T=0,S=0;if($&&E<0)T=Vn(q+E+X,y[0][0]);else if(!$&&E>0)T=Dn(q+h+X,y[1][0]);if(c&&D<0)S=Vn(W+D+m,y[0][1]);else if(!c&&D>0)S=Dn(W+B+m,y[1][1]);O=Math.max(O,T),p=Math.max(p,S)}if(_){let T=0,S=0;if($&&E>0)T=Dn(q+E,_[0][0]);else if(!$&&E<0)T=Vn(q+h,_[1][0]);if(c&&D>0)S=Dn(W+D,_[0][1]);else if(!c&&D<0)S=Vn(W+B,_[1][1]);O=Math.max(O,T),p=Math.max(p,S)}if(n){if(A){let T=M6(h/H,J,N)*H;if(O=Math.max(O,T),y){let S=0;if(!$&&!c||$&&!c&&F)S=Dn(W+m+h/H,y[1][1])*H;else S=Vn(W+m+($?E:-E)/H,y[0][1])*H;O=Math.max(O,S)}if(_){let S=0;if(!$&&!c||$&&!c&&F)S=Vn(W+h/H,_[1][1])*H;else S=Dn(W+($?E:-E)/H,_[0][1])*H;O=Math.max(O,S)}}if(j){let T=M6(B*H,w,L)/H;if(p=Math.max(p,T),y){let S=0;if(!$&&!c||c&&!$&&F)S=Dn(q+B*H+X,y[1][0])/H;else S=Vn(q+(c?D:-D)*H+X,y[0][0])/H;p=Math.max(p,S)}if(_){let S=0;if(!$&&!c||c&&!$&&F)S=Vn(q+B*H,_[1][0])/H;else S=Dn(q+(c?D:-D)*H,_[0][0])/H;p=Math.max(p,S)}}}if(D=D+(D<0?p:-p),E=E+(E<0?O:-O),n)if(F)if(h>B*H)D=(vq($,c)?-E:E)/H;else E=(vq($,c)?-D:D)*H;else if(A)D=E/H,c=$;else E=D*H,$=c;let Y=$?q+E:q,v=c?W+D:W;return{width:z+($?-E:E),height:Z+(c?-D:D),x:i[0]*E*(!$?1:-1)+Y,y:i[1]*D*(!c?1:-1)+v}}var WN={width:0,height:0,x:0,y:0},jV={...WN,pointerX:0,pointerY:0,aspectRatio:1};function FV(u){return[[0,0],[u.measured.width,u.measured.height]]}function JV(u,f,l){let r=f.position.x+u.position.x,n=f.position.y+u.position.y,i=u.measured.width??0,y=u.measured.height??0,_=l[0]*i,$=l[1]*y;return[[r-_,n-$],[r+i-_,n+y-$]]}function wN({domNode:u,nodeId:f,getStoreItems:l,onChange:r,onEnd:n}){let i=of(u),y={controlDirection:bq("bottom-right"),boundaries:{minWidth:0,minHeight:0,maxWidth:Number.MAX_VALUE,maxHeight:Number.MAX_VALUE},resizeDirection:void 0,keepAspectRatio:!1};function _({controlPosition:c,boundaries:A,keepAspectRatio:j,resizeDirection:F,onResizeStart:U,onResize:Q,onResizeEnd:w,shouldResize:L}){let J={...WN},N={...jV};y={boundaries:A,resizeDirection:F,keepAspectRatio:j,controlDirection:bq(c)};let q=void 0,W=null,z=[],Z=void 0,H=void 0,E=void 0,D=!1,h=u$().on("start",(B)=>{let{nodeLookup:X,transform:m,snapGrid:O,snapToGrid:p,nodeOrigin:Y,paneDomNode:v}=l();if(q=X.get(f),!q)return;W=v?.getBoundingClientRect()??null;let{xSnapped:T,ySnapped:S}=W$(B.sourceEvent,{transform:m,snapGrid:O,snapToGrid:p,containerBounds:W});if(J={width:q.measured.width??0,height:q.measured.height??0,x:q.position.x??0,y:q.position.y??0},N={...J,pointerX:T,pointerY:S,aspectRatio:J.width/J.height},Z=void 0,q.parentId&&(q.extent==="parent"||q.expandParent))Z=X.get(q.parentId),H=Z&&q.extent==="parent"?FV(Z):void 0;z=[],E=void 0;for(let[k,I]of X)if(I.parentId===f){if(z.push({id:k,position:{...I.position},extent:I.extent}),I.extent==="parent"||I.expandParent){let b=JV(I,q,I.origin??Y);if(E)E=[[Math.min(b[0][0],E[0][0]),Math.min(b[0][1],E[0][1])],[Math.max(b[1][0],E[1][0]),Math.max(b[1][1],E[1][1])]];else E=b}}U?.(B,{...J})}).on("drag",(B)=>{let{transform:X,snapGrid:m,snapToGrid:O,nodeOrigin:p}=l(),Y=W$(B.sourceEvent,{transform:X,snapGrid:m,snapToGrid:O,containerBounds:W}),v=[];if(!q)return;let{x:T,y:S,width:k,height:I}=J,b={},o=q.origin??p,{width:s,height:x,x:fu,y:$u}=AV(N,y.controlDirection,Y,y.boundaries,y.keepAspectRatio,o,H,E),tu=s!==k,ju=x!==I,Gu=fu!==T&&tu,qu=$u!==S&&ju;if(!Gu&&!qu&&!tu&&!ju)return;if(Gu||qu||o[0]===1||o[1]===1){if(b.x=Gu?fu:J.x,b.y=qu?$u:J.y,J.x=b.x,J.y=b.y,z.length>0){let Wu=fu-T,g=$u-S;for(let Qu of z)Qu.position={x:Qu.position.x-Wu+o[0]*(s-k),y:Qu.position.y-g+o[1]*(x-I)},v.push(Qu)}}if(tu||ju)b.width=tu&&(!y.resizeDirection||y.resizeDirection==="horizontal")?s:J.width,b.height=ju&&(!y.resizeDirection||y.resizeDirection==="vertical")?x:J.height,J.width=b.width,J.height=b.height;if(Z&&q.expandParent){let Wu=o[0]*(b.width??0);if(b.x&&b.x{if(!D)return;w?.(B,{...J}),n?.({...J}),D=!1});i.call(h)}function $(){i.on(".drag",null)}return{update:_,destroy:$}}var DN=Mu(Qf(),1),XN=Mu(ON(),1);var HN=(u)=>{let f,l=new Set,r=(A,j)=>{let F=typeof A==="function"?A(f):A;if(!Object.is(F,f)){let U=f;f=(j!=null?j:typeof F!=="object"||F===null)?F:Object.assign({},f,F),l.forEach((Q)=>Q(f,U))}},n=()=>f,$={setState:r,getState:n,getInitialState:()=>c,subscribe:(A)=>{return l.add(A),()=>l.delete(A)},destroy:()=>{l.clear()}},c=f=u(r,n,$);return $},BN=(u)=>u?HN(u):HN;var{useDebugValue:DV}=DN.default,{useSyncExternalStoreWithSelector:XV}=XN.default,YV=(u)=>u;function n9(u,f=YV,l){let r=XV(u.subscribe,u.getState,u.getServerState||u.getInitialState,f,l);return DV(r),r}var VN=(u,f)=>{let l=BN(u),r=(n,i=f)=>n9(l,n,i);return Object.assign(r,l),r},YN=(u,f)=>u?VN(u,f):VN;function Kf(u,f){if(Object.is(u,f))return!0;if(typeof u!=="object"||u===null||typeof f!=="object"||f===null)return!1;if(u instanceof Map&&f instanceof Map){if(u.size!==f.size)return!1;for(let[r,n]of u)if(!Object.is(n,f.get(r)))return!1;return!0}if(u instanceof Set&&f instanceof Set){if(u.size!==f.size)return!1;for(let r of u)if(!f.has(r))return!1;return!0}let l=Object.keys(u);if(l.length!==Object.keys(f).length)return!1;for(let r of l)if(!Object.prototype.hasOwnProperty.call(f,r)||!Object.is(u[r],f[r]))return!1;return!0}var SV=Mu(jc(),1),r8=yu.createContext(null),mV=r8.Provider,nW=tr.error001();function uf(u,f){let l=yu.useContext(r8);if(l===null)throw Error(nW);return n9(l,u,f)}function Tf(){let u=yu.useContext(r8);if(u===null)throw Error(nW);return yu.useMemo(()=>({getState:u.getState,setState:u.setState,subscribe:u.subscribe}),[u])}var SN={display:"none"},pV={position:"absolute",width:1,height:1,margin:-1,border:0,padding:0,overflow:"hidden",clip:"rect(0px, 0px, 0px, 0px)",clipPath:"inset(100%)"},iW="react-flow__node-desc",yW="react-flow__edge-desc",PV="react-flow__aria-live",MV=(u)=>u.ariaLiveMessage,CV=(u)=>u.ariaLabelConfig;function RV({rfId:u}){let f=uf(MV);return ru.jsx("div",{id:`${PV}-${u}`,"aria-live":"assertive","aria-atomic":"true",style:pV,children:f})}function xV({rfId:u,disableKeyboardA11y:f}){let l=uf(CV);return ru.jsxs(ru.Fragment,{children:[ru.jsx("div",{id:`${iW}-${u}`,style:SN,children:f?l["node.a11yDescription.default"]:l["node.a11yDescription.keyboardDisabled"]}),ru.jsx("div",{id:`${yW}-${u}`,style:SN,children:l["edge.a11yDescription.default"]}),!f&&ru.jsx(RV,{rfId:u})]})}var n8=yu.forwardRef(({position:u="top-left",children:f,className:l,style:r,...n},i)=>{let y=`${u}`.split("-");return ru.jsx("div",{className:Xf(["react-flow__panel",l,...y]),style:r,ref:i,...n,children:f})});n8.displayName="Panel";function hV({proOptions:u,position:f="bottom-right"}){if(u?.hideAttribution)return null;return ru.jsx(n8,{position:f,className:"react-flow__attribution","data-message":"Please only hide this attribution when you are subscribed to React Flow Pro: https://pro.reactflow.dev",children:ru.jsx("a",{href:"https://reactflow.dev",target:"_blank",rel:"noopener noreferrer","aria-label":"React Flow attribution",children:"React Flow"})})}var bV=(u)=>{let f=[],l=[];for(let[,r]of u.nodeLookup)if(r.selected)f.push(r.internals.userNode);for(let[,r]of u.edgeLookup)if(r.selected)l.push(r);return{selectedNodes:f,selectedEdges:l}},u8=(u)=>u.id;function vV(u,f){return Kf(u.selectedNodes.map(u8),f.selectedNodes.map(u8))&&Kf(u.selectedEdges.map(u8),f.selectedEdges.map(u8))}function kV({onSelectionChange:u}){let f=Tf(),{selectedNodes:l,selectedEdges:r}=uf(bV,vV);return yu.useEffect(()=>{let n={nodes:l,edges:r};u?.(n),f.getState().onSelectionChangeHandlers.forEach((i)=>i(n))},[l,r,u]),null}var IV=(u)=>!!u.onSelectionChangeHandlers;function gV({onSelectionChange:u}){let f=uf(IV);if(u||f)return ru.jsx(kV,{onSelectionChange:u});return null}var _9=typeof window<"u"?yu.useLayoutEffect:yu.useEffect,_W=[0,0],sV={x:0,y:0,zoom:1},aV=["nodes","edges","defaultNodes","defaultEdges","onConnect","onConnectStart","onConnectEnd","onClickConnectStart","onClickConnectEnd","nodesDraggable","autoPanOnNodeFocus","nodesConnectable","nodesFocusable","edgesFocusable","edgesReconnectable","elevateNodesOnSelect","elevateEdgesOnSelect","minZoom","maxZoom","nodeExtent","onNodesChange","onEdgesChange","elementsSelectable","connectionMode","snapGrid","snapToGrid","translateExtent","connectOnClick","defaultEdgeOptions","fitView","fitViewOptions","onNodesDelete","onEdgesDelete","onDelete","onNodeDrag","onNodeDragStart","onNodeDragStop","onSelectionDrag","onSelectionDragStart","onSelectionDragStop","onMoveStart","onMove","onMoveEnd","noPanClassName","nodeOrigin","autoPanOnConnect","autoPanOnNodeDrag","onError","connectionRadius","isValidConnection","selectNodesOnDrag","nodeDragThreshold","connectionDragThreshold","onBeforeDelete","debug","autoPanSpeed","ariaLabelConfig","zIndexMode"],mN=[...aV,"rfId"],oV=(u)=>({setNodes:u.setNodes,setEdges:u.setEdges,setMinZoom:u.setMinZoom,setMaxZoom:u.setMaxZoom,setTranslateExtent:u.setTranslateExtent,setNodeExtent:u.setNodeExtent,reset:u.reset,setDefaultNodesAndEdges:u.setDefaultNodesAndEdges}),pN={translateExtent:ly,nodeOrigin:_W,minZoom:0.5,maxZoom:2,elementsSelectable:!0,noPanClassName:"nopan",rfId:"1"};function dV(u){let{setNodes:f,setEdges:l,setMinZoom:r,setMaxZoom:n,setTranslateExtent:i,setNodeExtent:y,reset:_,setDefaultNodesAndEdges:$}=uf(oV,Kf),c=Tf();_9(()=>{return $(u.defaultNodes,u.defaultEdges),()=>{A.current=pN,_()}},[]);let A=yu.useRef(pN);return _9(()=>{for(let j of mN){let F=u[j],U=A.current[j];if(F===U)continue;if(typeof u[j]>"u")continue;if(j==="nodes")f(F);else if(j==="edges")l(F);else if(j==="minZoom")r(F);else if(j==="maxZoom")n(F);else if(j==="translateExtent")i(F);else if(j==="nodeExtent")y(F);else if(j==="ariaLabelConfig")c.setState({ariaLabelConfig:eq(F)});else if(j==="fitView")c.setState({fitViewQueued:F});else if(j==="fitViewOptions")c.setState({fitViewOptions:F});else c.setState({[j]:F})}A.current=u},mN.map((j)=>u[j])),null}function PN(){if(typeof window>"u"||!window.matchMedia)return null;return window.matchMedia("(prefers-color-scheme: dark)")}function eV(u){let[f,l]=yu.useState(u==="system"?null:u);return yu.useEffect(()=>{if(u!=="system"){l(u);return}let r=PN(),n=()=>l(r?.matches?"dark":"light");return n(),r?.addEventListener("change",n),()=>{r?.removeEventListener("change",n)}},[u]),f!==null?f:PN()?.matches?"dark":"light"}var MN=typeof document<"u"?document:null;function z$(u=null,f={target:MN,actInsideInputWithModifier:!0}){let[l,r]=yu.useState(!1),n=yu.useRef(!1),i=yu.useRef(new Set([])),[y,_]=yu.useMemo(()=>{if(u!==null){let c=(Array.isArray(u)?u:[u]).filter((j)=>typeof j==="string").map((j)=>j.replace("+",` `).replace(` `,` +`).split(` -`)),F=j.reduce((J,U)=>J.concat(...U),[]);return[j,F]}return[[],[]]},[u]);return iu.useEffect(()=>{let $=f?.target??QN,j=f?.actInsideInputWithModifier??!0;if(u!==null){let F=(q)=>{if(y.current=q.ctrlKey||q.metaKey||q.shiftKey||q.altKey,(!y.current||y.current&&!j)&&t7(q))return!1;let G=WN(q.code,n);if(i.current.add(q[G]),qN(_,i.current,!1)){let K=q.composedPath?.()?.[0]||q.target,Q=K?.nodeName==="BUTTON"||K?.nodeName==="A";if(f.preventDefault!==!1&&(y.current||!Q))q.preventDefault();r(!0)}},J=(q)=>{let W=WN(q.code,n);if(qN(_,i.current,!0))r(!1),i.current.clear();else i.current.delete(q[W]);if(q.key==="Meta")i.current.clear();y.current=!1},U=()=>{i.current.clear(),r(!1)};return $?.addEventListener("keydown",F),$?.addEventListener("keyup",J),window.addEventListener("blur",U),window.addEventListener("contextmenu",U),()=>{$?.removeEventListener("keydown",F),$?.removeEventListener("keyup",J),window.removeEventListener("blur",U),window.removeEventListener("contextmenu",U)}}},[u,r]),l}function qN(u,f,l){return u.filter((r)=>l||r.length===f.size).some((r)=>r.every((y)=>f.has(y)))}function WN(u,f){return f.includes(u)?"code":"key"}var UX=()=>{let u=Tf();return iu.useMemo(()=>{return{zoomIn:(f)=>{let{panZoom:l}=u.getState();return l?l.scaleBy(1.2,f):Promise.resolve(!1)},zoomOut:(f)=>{let{panZoom:l}=u.getState();return l?l.scaleBy(0.8333333333333334,f):Promise.resolve(!1)},zoomTo:(f,l)=>{let{panZoom:r}=u.getState();return r?r.scaleTo(f,l):Promise.resolve(!1)},getZoom:()=>u.getState().transform[2],setViewport:async(f,l)=>{let{transform:[r,y,i],panZoom:_}=u.getState();if(!_)return Promise.resolve(!1);return await _.setViewport({x:f.x??r,y:f.y??y,zoom:f.zoom??i},l),Promise.resolve(!0)},getViewport:()=>{let[f,l,r]=u.getState().transform;return{x:f,y:l,zoom:r}},setCenter:async(f,l,r)=>{return u.getState().setCenter(f,l,r)},fitBounds:async(f,l)=>{let{width:r,height:y,minZoom:i,maxZoom:_,panZoom:n}=u.getState(),$=j$(f,r,y,i,_,l?.padding??0.1);if(!n)return Promise.resolve(!1);return await n.setViewport($,{duration:l?.duration,ease:l?.ease,interpolate:l?.interpolate}),Promise.resolve(!0)},screenToFlowPosition:(f,l={})=>{let{transform:r,snapGrid:y,snapToGrid:i,domNode:_}=u.getState();if(!_)return f;let{x:n,y:$}=_.getBoundingClientRect(),j={x:f.x-n,y:f.y-$},F=l.snapGrid??y,J=l.snapToGrid??i;return vi(j,r,J,F)},flowToScreenPosition:(f)=>{let{transform:l,domNode:r}=u.getState();if(!r)return f;let{x:y,y:i}=r.getBoundingClientRect(),_=$$(f,l);return{x:_.x+y,y:_.y+i}}}},[])};function MN(u,f){let l=[],r=new Map,y=[];for(let i of u)if(i.type==="add"){y.push(i);continue}else if(i.type==="remove"||i.type==="replace")r.set(i.id,[i]);else{let _=r.get(i.id);if(_)_.push(i);else r.set(i.id,[i])}for(let i of f){let _=r.get(i.id);if(!_){l.push(i);continue}if(_[0].type==="remove")continue;if(_[0].type==="replace"){l.push({..._[0].item});continue}let n={...i};for(let $ of _)QX($,n);l.push(n)}if(y.length)y.forEach((i)=>{if(i.index!==void 0)l.splice(i.index,0,{...i.item});else l.push({...i.item})});return l}function QX(u,f){switch(u.type){case"select":{f.selected=u.selected;break}case"position":{if(typeof u.position<"u")f.position=u.position;if(typeof u.dragging<"u")f.dragging=u.dragging;break}case"dimensions":{if(typeof u.dimensions<"u"){if(f.measured={...u.dimensions},u.setAttributes){if(u.setAttributes===!0||u.setAttributes==="width")f.width=u.dimensions.width;if(u.setAttributes===!0||u.setAttributes==="height")f.height=u.dimensions.height}}if(typeof u.resizing==="boolean")f.resizing=u.resizing;break}}}function qX(u,f){return MN(u,f)}function WX(u,f){return MN(u,f)}function Ty(u,f){return{id:u,type:"select",selected:f}}function gi(u,f=new Set,l=!1){let r=[];for(let[y,i]of u){let _=f.has(y);if(!(i.selected===void 0&&!_)&&i.selected!==_){if(l)i.selected=_;r.push(Ty(i.id,_))}}return r}function cN({items:u=[],lookup:f}){let l=[],r=new Map(u.map((y)=>[y.id,y]));for(let[y,i]of u.entries()){let _=f.get(i.id),n=_?.internals?.userNode??_;if(n!==void 0&&n!==i)l.push({id:i.id,item:i,type:"replace"});if(n===void 0)l.push({item:i,type:"add",index:y})}for(let[y]of f)if(r.get(y)===void 0)l.push({id:y,type:"remove"});return l}function NN(u){return{id:u.id,type:"remove"}}var zN=(u)=>Kc(u),cX=(u)=>w7(u);function mN(u){return iu.forwardRef(u)}function GN(u){let[f,l]=iu.useState(BigInt(0)),[r]=iu.useState(()=>NX(()=>l((y)=>y+BigInt(1))));return k7(()=>{let y=r.get();if(y.length)u(y),r.reset()},[f]),r}function NX(u){let f=[];return{get:()=>f,reset:()=>{f=[]},push:(l)=>{f.push(l),u()}}}var pN=iu.createContext(null);function zX({children:u}){let f=Tf(),l=iu.useCallback((n)=>{let{nodes:$=[],setNodes:j,hasDefaultNodes:F,onNodesChange:J,nodeLookup:U,fitViewQueued:q,onNodesChangeMiddlewareMap:W}=f.getState(),G=$;for(let Q of n)G=typeof Q==="function"?Q(G):Q;let K=cN({items:G,lookup:U});for(let Q of W.values())K=Q(K);if(F)j(G);if(K.length>0)J?.(K);else if(q)window.requestAnimationFrame(()=>{let{fitViewQueued:Q,nodes:N,setNodes:c}=f.getState();if(Q)c(N)})},[]),r=GN(l),y=iu.useCallback((n)=>{let{edges:$=[],setEdges:j,hasDefaultEdges:F,onEdgesChange:J,edgeLookup:U}=f.getState(),q=$;for(let W of n)q=typeof W==="function"?W(q):W;if(F)j(q);else if(J)J(cN({items:q,lookup:U}))},[]),i=GN(y),_=iu.useMemo(()=>({nodeQueue:r,edgeQueue:i}),[]);return ru.jsx(pN.Provider,{value:_,children:u})}function GX(){let u=iu.useContext(pN);if(!u)throw Error("useBatchContext must be used within a BatchProvider");return u}var KX=(u)=>!!u.panZoom;function s7(){let u=UX(),f=Tf(),l=GX(),r=uf(KX),y=iu.useMemo(()=>{let i=(J)=>f.getState().nodeLookup.get(J),_=(J)=>{l.nodeQueue.push(J)},n=(J)=>{l.edgeQueue.push(J)},$=(J)=>{let{nodeLookup:U,nodeOrigin:q}=f.getState(),W=zN(J)?J:U.get(J.id),G=W.parentId?X7(W.position,W.measured,W.parentId,U,q):W.position,K={...W,position:G,width:W.measured?.width??W.width,height:W.measured?.height??W.height};return Ey(K)},j=(J,U,q={replace:!1})=>{_((W)=>W.map((G)=>{if(G.id===J){let K=typeof U==="function"?U(G):U;return q.replace&&zN(K)?K:{...G,...K}}return G}))},F=(J,U,q={replace:!1})=>{n((W)=>W.map((G)=>{if(G.id===J){let K=typeof U==="function"?U(G):U;return q.replace&&cX(K)?K:{...G,...K}}return G}))};return{getNodes:()=>f.getState().nodes.map((J)=>({...J})),getNode:(J)=>i(J)?.internals.userNode,getInternalNode:i,getEdges:()=>{let{edges:J=[]}=f.getState();return J.map((U)=>({...U}))},getEdge:(J)=>f.getState().edgeLookup.get(J),setNodes:_,setEdges:n,addNodes:(J)=>{let U=Array.isArray(J)?J:[J];l.nodeQueue.push((q)=>[...q,...U])},addEdges:(J)=>{let U=Array.isArray(J)?J:[J];l.edgeQueue.push((q)=>[...q,...U])},toObject:()=>{let{nodes:J=[],edges:U=[],transform:q}=f.getState(),[W,G,K]=q;return{nodes:J.map((Q)=>({...Q})),edges:U.map((Q)=>({...Q})),viewport:{x:W,y:G,zoom:K}}},deleteElements:async({nodes:J=[],edges:U=[]})=>{let{nodes:q,edges:W,onNodesDelete:G,onEdgesDelete:K,triggerNodeChanges:Q,triggerEdgeChanges:N,onDelete:c,onBeforeDelete:z}=f.getState(),{nodes:w,edges:H}=await Ec({nodesToRemove:J,edgesToRemove:U,nodes:q,edges:W,onBeforeDelete:z}),B=H.length>0,T=w.length>0;if(B){let X=H.map(NN);K?.(H),N(X)}if(T){let X=w.map(NN);G?.(w),Q(X)}if(T||B)c?.({nodes:w,edges:H});return{deletedNodes:w,deletedEdges:H}},getIntersectingNodes:(J,U=!0,q)=>{let W=O7(J),G=W?J:$(J),K=q!==void 0;if(!G)return[];return(q||f.getState().nodes).filter((Q)=>{let N=f.getState().nodeLookup.get(Q.id);if(N&&!W&&(Q.id===J.id||!N.internals.positionAbsolute))return!1;let c=Ey(K?Q:N),z=hi(c,G);return U&&z>0||z>=c.width*c.height||z>=G.width*G.height})},isNodeIntersecting:(J,U,q=!0)=>{let G=O7(J)?J:$(J);if(!G)return!1;let K=hi(G,U);return q&&K>0||K>=U.width*U.height||K>=G.width*G.height},updateNode:j,updateNodeData:(J,U,q={replace:!1})=>{j(J,(W)=>{let G=typeof U==="function"?U(W):U;return q.replace?{...W,data:G}:{...W,data:{...W.data,...G}}},q)},updateEdge:F,updateEdgeData:(J,U,q={replace:!1})=>{F(J,(W)=>{let G=typeof U==="function"?U(W):U;return q.replace?{...W,data:G}:{...W,data:{...W.data,...G}}},q)},getNodesBounds:(J)=>{let{nodeLookup:U,nodeOrigin:q}=f.getState();return T7(J,{nodeLookup:U,nodeOrigin:q})},getHandleConnections:({type:J,id:U,nodeId:q})=>Array.from(f.getState().connectionLookup.get(`${q}-${J}${U?`-${U}`:""}`)?.values()??[]),getNodeConnections:({type:J,handleId:U,nodeId:q})=>Array.from(f.getState().connectionLookup.get(`${q}${J?U?`-${J}-${U}`:`-${J}`:""}`)?.values()??[]),fitView:async(J)=>{let U=f.getState().fitViewResolver??Hc();return f.setState({fitViewQueued:!0,fitViewOptions:J,fitViewResolver:U}),l.nodeQueue.push((q)=>[...q]),U.promise}}},[]);return iu.useMemo(()=>{return{...y,...u,viewportInitialized:r}},[r])}var KN=(u)=>u.selected,LX=typeof window<"u"?window:void 0;function wX({deleteKeyCode:u,multiSelectionKeyCode:f}){let l=Tf(),{deleteElements:r}=s7(),y=J$(u,{actInsideInputWithModifier:!1}),i=J$(f,{target:LX});iu.useEffect(()=>{if(y){let{edges:_,nodes:n}=l.getState();r({nodes:n.filter(KN),edges:_.filter(KN)}),l.setState({nodesSelectionActive:!1})}},[y]),iu.useEffect(()=>{l.setState({multiSelectionActive:i})},[i])}function EX(u){let f=Tf();iu.useEffect(()=>{let l=()=>{if(!u.current||!(u.current.checkVisibility?.()??!0))return!1;let r=Y4(u.current);if(r.height===0||r.width===0)f.getState().onError?.("004",_r.error004());f.setState({width:r.width||500,height:r.height||500})};if(u.current){l(),window.addEventListener("resize",l);let r=new ResizeObserver(()=>l());return r.observe(u.current),()=>{if(window.removeEventListener("resize",l),r&&u.current)r.unobserve(u.current)}}},[])}var k4={position:"absolute",width:"100%",height:"100%",top:0,left:0},TX=(u)=>({userSelectionActive:u.userSelectionActive,lib:u.lib,connectionInProgress:u.connection.inProgress});function ZX({onPaneContextMenu:u,zoomOnScroll:f=!0,zoomOnPinch:l=!0,panOnScroll:r=!1,panOnScrollSpeed:y=0.5,panOnScrollMode:i=D0.Free,zoomOnDoubleClick:_=!0,panOnDrag:n=!0,defaultViewport:$,translateExtent:j,minZoom:F,maxZoom:J,zoomActivationKeyCode:U,preventScrolling:q=!0,children:W,noWheelClassName:G,noPanClassName:K,onViewportChange:Q,isControlledViewport:N,paneClickDistance:c,selectionOnDrag:z}){let w=Tf(),H=iu.useRef(null),{userSelectionActive:B,lib:T,connectionInProgress:X}=uf(TX,Kf),R=J$(U),Y=iu.useRef();EX(H);let P=iu.useCallback((t)=>{if(Q?.({x:t[0],y:t[1],zoom:t[2]}),!N)w.setState({transform:t})},[Q,N]);return iu.useEffect(()=>{if(H.current){Y.current=kc({domNode:H.current,minZoom:F,maxZoom:J,translateExtent:j,viewport:$,onDraggingChange:(S)=>w.setState((b)=>b.paneDragging===S?b:{paneDragging:S}),onPanZoomStart:(S,b)=>{let{onViewportChangeStart:Z,onMoveStart:D}=w.getState();D?.(S,b),Z?.(b)},onPanZoom:(S,b)=>{let{onViewportChange:Z,onMove:D}=w.getState();D?.(S,b),Z?.(b)},onPanZoomEnd:(S,b)=>{let{onViewportChangeEnd:Z,onMoveEnd:D}=w.getState();D?.(S,b),Z?.(b)}});let{x:t,y:O,zoom:M}=Y.current.getViewport();return w.setState({panZoom:Y.current,transform:[t,O,M],domNode:H.current.closest(".react-flow")}),()=>{Y.current?.destroy()}}},[]),iu.useEffect(()=>{Y.current?.update({onPaneContextMenu:u,zoomOnScroll:f,zoomOnPinch:l,panOnScroll:r,panOnScrollSpeed:y,panOnScrollMode:i,zoomOnDoubleClick:_,panOnDrag:n,zoomActivationKeyPressed:R,preventScrolling:q,noPanClassName:K,userSelectionActive:B,noWheelClassName:G,lib:T,onTransformChange:P,connectionInProgress:X,selectionOnDrag:z,paneClickDistance:c})},[u,f,l,r,y,i,_,n,R,q,K,B,G,T,P,X,z,c]),ru.jsx("div",{className:"react-flow__renderer",ref:H,style:k4,children:W})}var HX=(u)=>({userSelectionActive:u.userSelectionActive,userSelectionRect:u.userSelectionRect});function OX(){let{userSelectionActive:u,userSelectionRect:f}=uf(HX,Kf);if(!(u&&f))return null;return ru.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:f.width,height:f.height,transform:`translate(${f.x}px, ${f.y}px)`}})}var v7=(u,f)=>{return(l)=>{if(l.target!==f.current)return;u?.(l)}},BX=(u)=>({userSelectionActive:u.userSelectionActive,elementsSelectable:u.elementsSelectable,connectionInProgress:u.connection.inProgress,dragging:u.paneDragging});function VX({isSelecting:u,selectionKeyPressed:f,selectionMode:l=Ly.Full,panOnDrag:r,paneClickDistance:y,selectionOnDrag:i,onSelectionStart:_,onSelectionEnd:n,onPaneClick:$,onPaneContextMenu:j,onPaneScroll:F,onPaneMouseEnter:J,onPaneMouseMove:U,onPaneMouseLeave:q,children:W}){let G=Tf(),{userSelectionActive:K,elementsSelectable:Q,dragging:N,connectionInProgress:c}=uf(BX,Kf),z=Q&&(u||K),w=iu.useRef(null),H=iu.useRef(),B=iu.useRef(new Set),T=iu.useRef(new Set),X=iu.useRef(!1),R=(Z)=>{if(X.current||c){X.current=!1;return}$?.(Z),G.getState().resetSelectedElements(),G.setState({nodesSelectionActive:!1})},Y=(Z)=>{if(Array.isArray(r)&&r?.includes(2)){Z.preventDefault();return}j?.(Z)},P=F?(Z)=>F(Z):void 0,t=(Z)=>{if(X.current)Z.stopPropagation(),X.current=!1},O=(Z)=>{let{domNode:D}=G.getState();if(H.current=D?.getBoundingClientRect(),!H.current)return;let I=Z.target===w.current;if(!I&&!!Z.target.closest(".nokey")||!u||!(i&&I||f)||Z.button!==0||!Z.isPrimary)return;Z.target?.setPointerCapture?.(Z.pointerId),X.current=!1;let{x:o,y:s}=Lr(Z.nativeEvent,H.current);if(G.setState({userSelectionRect:{width:0,height:0,startX:o,startY:s,x:o,y:s}}),!I)Z.stopPropagation(),Z.preventDefault()},M=(Z)=>{let{userSelectionRect:D,transform:I,nodeLookup:k,edgeLookup:h,connectionLookup:o,triggerNodeChanges:s,triggerEdgeChanges:x,defaultEdgeOptions:uu,resetSelectedElements:nu}=G.getState();if(!H.current||!D)return;let{x:$u,y:Fu}=Lr(Z.nativeEvent,H.current),{startX:Ku,startY:Wu}=D;if(!X.current){let g=f?0:y;if(Math.hypot($u-Ku,Fu-Wu)<=g)return;nu(),_?.(Z)}X.current=!0;let m={startX:Ku,startY:Wu,x:$ug.id)),T.current=new Set;let cu=uu?.selectable??!0;for(let g of B.current){let Qu=o.get(g);if(!Qu)continue;for(let{edgeId:Eu}of Qu.values()){let Tu=h.get(Eu);if(Tu&&(Tu.selectable??cu))T.current.add(Eu)}}if(!Y7(d,B.current)){let g=gi(k,B.current,!0);s(g)}if(!Y7(e,T.current)){let g=gi(h,T.current);x(g)}G.setState({userSelectionRect:m,userSelectionActive:!0,nodesSelectionActive:!1})},S=(Z)=>{if(Z.button!==0)return;if(Z.target?.releasePointerCapture?.(Z.pointerId),!K&&Z.target===w.current&&G.getState().userSelectionRect)R?.(Z);if(G.setState({userSelectionActive:!1,userSelectionRect:null}),X.current)n?.(Z),G.setState({nodesSelectionActive:B.current.size>0})},b=r===!0||Array.isArray(r)&&r.includes(0);return ru.jsxs("div",{className:Yf(["react-flow__pane",{draggable:b,dragging:N,selection:u}]),onClick:z?void 0:v7(R,w),onContextMenu:v7(Y,w),onWheel:v7(P,w),onPointerEnter:z?void 0:J,onPointerMove:z?M:U,onPointerUp:z?S:void 0,onPointerDownCapture:z?O:void 0,onClickCapture:z?t:void 0,onPointerLeave:q,ref:w,style:k4,children:[W,ru.jsx(OX,{})]})}function g7({id:u,store:f,unselect:l=!1,nodeRef:r}){let{addSelectedNodes:y,unselectNodesAndEdges:i,multiSelectionActive:_,nodeLookup:n,onError:$}=f.getState(),j=n.get(u);if(!j){$?.("012",_r.error012(u));return}if(f.setState({nodesSelectionActive:!1}),!j.selected)y([u]);else if(l||j.selected&&_)i({nodes:[j],edges:[]}),requestAnimationFrame(()=>r?.current?.blur())}function CN({nodeRef:u,disabled:f=!1,noDragClassName:l,handleSelector:r,nodeId:y,isSelectable:i,nodeClickDistance:_}){let n=Tf(),[$,j]=iu.useState(!1),F=iu.useRef();return iu.useEffect(()=>{F.current=pc({getStoreItems:()=>n.getState(),onNodeMouseDown:(J)=>{g7({id:J,store:n,nodeRef:u})},onDragStart:()=>{j(!0)},onDragStop:()=>{j(!1)}})},[]),iu.useEffect(()=>{if(f||!u.current||!F.current)return;return F.current.update({noDragClassName:l,handleSelector:r,domNode:u.current,isSelectable:i,nodeId:y,nodeClickDistance:_}),()=>{F.current?.destroy()}},[l,r,f,i,u,y,_]),$}var XX=(u)=>(f)=>f.selected&&(f.draggable||u&&typeof f.draggable>"u");function xN(){let u=Tf();return iu.useCallback((l)=>{let{nodeExtent:r,snapToGrid:y,snapGrid:i,nodesDraggable:_,onError:n,updateNodePositions:$,nodeLookup:j,nodeOrigin:F}=u.getState(),J=new Map,U=XX(_),q=y?i[0]:5,W=y?i[1]:5,G=l.direction.x*q*l.factor,K=l.direction.y*W*l.factor;for(let[,Q]of j){if(!U(Q))continue;let N={x:Q.internals.positionAbsolute.x+G,y:Q.internals.positionAbsolute.y+K};if(y)N=bi(N,i);let{position:c,positionAbsolute:z}=Z7({nodeId:Q.id,nextPosition:N,nodeLookup:j,nodeExtent:r,nodeOrigin:F,onError:n});Q.position=c,Q.internals.positionAbsolute=z,J.set(Q.id,Q)}$(J)},[])}var a7=iu.createContext(null),YX=a7.Provider;a7.Consumer;var RN=()=>{return iu.useContext(a7)},DX=(u)=>({connectOnClick:u.connectOnClick,noPanClassName:u.noPanClassName,rfId:u.rfId}),tX=(u,f,l)=>(r)=>{let{connectionClickStartHandle:y,connectionMode:i,connection:_}=r,{fromHandle:n,toHandle:$,isValid:j}=_,F=$?.nodeId===u&&$?.id===f&&$?.type===l;return{connectingFrom:n?.nodeId===u&&n?.id===f&&n?.type===l,connectingTo:F,clickConnecting:y?.nodeId===u&&y?.id===f&&y?.type===l,isPossibleEndHandle:i===E1.Strict?n?.type!==l:u!==n?.nodeId||f!==n?.id,connectionInProcess:!!n,clickConnectionInProcess:!!y,valid:F&&j}};function SX({type:u="source",position:f=zu.Top,isValidConnection:l,isConnectable:r=!0,isConnectableStart:y=!0,isConnectableEnd:i=!0,id:_,onConnect:n,children:$,className:j,onMouseDown:F,onTouchStart:J,...U},q){let W=_||null,G=u==="target",K=Tf(),Q=RN(),{connectOnClick:N,noPanClassName:c,rfId:z}=uf(DX,Kf),{connectingFrom:w,connectingTo:H,clickConnecting:B,isPossibleEndHandle:T,connectionInProcess:X,clickConnectionInProcess:R,valid:Y}=uf(tX(Q,W,u),Kf);if(!Q)K.getState().onError?.("010",_r.error010());let P=(M)=>{let{defaultEdgeOptions:S,onConnect:b,hasDefaultEdges:Z}=K.getState(),D={...S,...M};if(Z){let{edges:I,setEdges:k}=K.getState();k(M7(D,I))}b?.(D),n?.(D)},t=(M)=>{if(!Q)return;let S=S7(M.nativeEvent);if(y&&(S&&M.button===0||!S)){let b=K.getState();p4.onPointerDown(M.nativeEvent,{handleDomNode:M.currentTarget,autoPanOnConnect:b.autoPanOnConnect,connectionMode:b.connectionMode,connectionRadius:b.connectionRadius,domNode:b.domNode,nodeLookup:b.nodeLookup,lib:b.lib,isTarget:G,handleId:W,nodeId:Q,flowId:b.rfId,panBy:b.panBy,cancelConnection:b.cancelConnection,onConnectStart:b.onConnectStart,onConnectEnd:(...Z)=>K.getState().onConnectEnd?.(...Z),updateConnection:b.updateConnection,onConnect:P,isValidConnection:l||((...Z)=>K.getState().isValidConnection?.(...Z)??!0),getTransform:()=>K.getState().transform,getFromHandle:()=>K.getState().connection.fromHandle,autoPanSpeed:b.autoPanSpeed,dragThreshold:b.connectionDragThreshold})}if(S)F?.(M);else J?.(M)},O=(M)=>{let{onClickConnectStart:S,onClickConnectEnd:b,connectionClickStartHandle:Z,connectionMode:D,isValidConnection:I,lib:k,rfId:h,nodeLookup:o,connection:s}=K.getState();if(!Q||!Z&&!y)return;if(!Z){S?.(M.nativeEvent,{nodeId:Q,handleId:W,handleType:u}),K.setState({connectionClickStartHandle:{nodeId:Q,type:u,id:W}});return}let x=D7(M.target),uu=l||I,{connection:nu,isValid:$u}=p4.isValid(M.nativeEvent,{handle:{nodeId:Q,id:W,type:u},connectionMode:D,fromNodeId:Z.nodeId,fromHandleId:Z.id||null,fromType:Z.type,isValidConnection:uu,flowId:h,doc:x,lib:k,nodeLookup:o});if($u&&nu)P(nu);let Fu=structuredClone(s);delete Fu.inProgress,Fu.toPosition=Fu.toHandle?Fu.toHandle.position:null,b?.(M,Fu),K.setState({connectionClickStartHandle:null})};return ru.jsx("div",{"data-handleid":W,"data-nodeid":Q,"data-handlepos":f,"data-id":`${z}-${Q}-${W}-${u}`,className:Yf(["react-flow__handle",`react-flow__handle-${f}`,"nodrag",c,j,{source:!G,target:G,connectable:r,connectablestart:y,connectableend:i,clickconnecting:B,connectingfrom:w,connectingto:H,valid:Y,connectionindicator:r&&(!X||T)&&(X||R?i:y)}]),onMouseDown:t,onTouchStart:t,onClick:N?O:void 0,ref:q,...U,children:$})}var Zy=iu.memo(mN(SX));function PX({data:u,isConnectable:f,sourcePosition:l=zu.Bottom}){return ru.jsxs(ru.Fragment,{children:[u?.label,ru.jsx(Zy,{type:"source",position:l,isConnectable:f})]})}function MX({data:u,isConnectable:f,targetPosition:l=zu.Top,sourcePosition:r=zu.Bottom}){return ru.jsxs(ru.Fragment,{children:[ru.jsx(Zy,{type:"target",position:l,isConnectable:f}),u?.label,ru.jsx(Zy,{type:"source",position:r,isConnectable:f})]})}function mX(){return null}function pX({data:u,isConnectable:f,targetPosition:l=zu.Top}){return ru.jsxs(ru.Fragment,{children:[ru.jsx(Zy,{type:"target",position:l,isConnectable:f}),u?.label]})}var b4={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},LN={input:PX,default:MX,output:pX,group:mX};function CX(u){if(u.internals.handleBounds===void 0)return{width:u.width??u.initialWidth??u.style?.width,height:u.height??u.initialHeight??u.style?.height};return{width:u.width??u.style?.width,height:u.height??u.style?.height}}var xX=(u)=>{let{width:f,height:l,x:r,y}=Ri(u.nodeLookup,{filter:(i)=>!!i.selected});return{width:Kr(f)?f:null,height:Kr(l)?l:null,userSelectionActive:u.userSelectionActive,transformString:`translate(${u.transform[0]}px,${u.transform[1]}px) scale(${u.transform[2]}) translate(${r}px,${y}px)`}};function RX({onSelectionContextMenu:u,noPanClassName:f,disableKeyboardA11y:l}){let r=Tf(),{width:y,height:i,transformString:_,userSelectionActive:n}=uf(xX,Kf),$=xN(),j=iu.useRef(null);iu.useEffect(()=>{if(!l)j.current?.focus({preventScroll:!0})},[l]);let F=!n&&y!==null&&i!==null;if(CN({nodeRef:j,disabled:!F}),!F)return null;let J=u?(q)=>{let W=r.getState().nodes.filter((G)=>G.selected);u(q,W)}:void 0,U=(q)=>{if(Object.prototype.hasOwnProperty.call(b4,q.key))q.preventDefault(),$({direction:b4[q.key],factor:q.shiftKey?4:1})};return ru.jsx("div",{className:Yf(["react-flow__nodesselection","react-flow__container",f]),style:{transform:_},children:ru.jsx("div",{ref:j,className:"react-flow__nodesselection-rect",onContextMenu:J,tabIndex:l?void 0:-1,onKeyDown:l?void 0:U,style:{width:y,height:i}})})}var wN=typeof window<"u"?window:void 0,hX=(u)=>{return{nodesSelectionActive:u.nodesSelectionActive,userSelectionActive:u.userSelectionActive}};function hN({children:u,onPaneClick:f,onPaneMouseEnter:l,onPaneMouseMove:r,onPaneMouseLeave:y,onPaneContextMenu:i,onPaneScroll:_,paneClickDistance:n,deleteKeyCode:$,selectionKeyCode:j,selectionOnDrag:F,selectionMode:J,onSelectionStart:U,onSelectionEnd:q,multiSelectionKeyCode:W,panActivationKeyCode:G,zoomActivationKeyCode:K,elementsSelectable:Q,zoomOnScroll:N,zoomOnPinch:c,panOnScroll:z,panOnScrollSpeed:w,panOnScrollMode:H,zoomOnDoubleClick:B,panOnDrag:T,defaultViewport:X,translateExtent:R,minZoom:Y,maxZoom:P,preventScrolling:t,onSelectionContextMenu:O,noWheelClassName:M,noPanClassName:S,disableKeyboardA11y:b,onViewportChange:Z,isControlledViewport:D}){let{nodesSelectionActive:I,userSelectionActive:k}=uf(hX,Kf),h=J$(j,{target:wN}),o=J$(G,{target:wN}),s=o||T,x=o||z,uu=F&&s!==!0,nu=h||k||uu;return wX({deleteKeyCode:$,multiSelectionKeyCode:W}),ru.jsx(ZX,{onPaneContextMenu:i,elementsSelectable:Q,zoomOnScroll:N,zoomOnPinch:c,panOnScroll:x,panOnScrollSpeed:w,panOnScrollMode:H,zoomOnDoubleClick:B,panOnDrag:!h&&s,defaultViewport:X,translateExtent:R,minZoom:Y,maxZoom:P,zoomActivationKeyCode:K,preventScrolling:t,noWheelClassName:M,noPanClassName:S,onViewportChange:Z,isControlledViewport:D,paneClickDistance:n,selectionOnDrag:uu,children:ru.jsxs(VX,{onSelectionStart:U,onSelectionEnd:q,onPaneClick:f,onPaneMouseEnter:l,onPaneMouseMove:r,onPaneMouseLeave:y,onPaneContextMenu:i,onPaneScroll:_,panOnDrag:s,isSelecting:!!nu,selectionMode:J,selectionKeyPressed:h,paneClickDistance:n,selectionOnDrag:uu,children:[u,I&&ru.jsx(RX,{onSelectionContextMenu:O,noPanClassName:S,disableKeyboardA11y:b})]})})}hN.displayName="FlowRenderer";var bX=iu.memo(hN),vX=(u)=>(f)=>{return u?B4(f.nodeLookup,{x:0,y:0,width:f.width,height:f.height},f.transform,!0).map((l)=>l.id):Array.from(f.nodeLookup.keys())};function IX(u){return uf(iu.useCallback(vX(u),[u]),Kf)}var kX=(u)=>u.updateNodeInternals;function gX(){let u=uf(kX),[f]=iu.useState(()=>{if(typeof ResizeObserver>"u")return null;return new ResizeObserver((l)=>{let r=new Map;l.forEach((y)=>{let i=y.target.getAttribute("data-id");r.set(i,{id:i,nodeElement:y.target,force:!0})}),u(r)})});return iu.useEffect(()=>{return()=>{f?.disconnect()}},[f]),f}function sX({node:u,nodeType:f,hasDimensions:l,resizeObserver:r}){let y=Tf(),i=iu.useRef(null),_=iu.useRef(null),n=iu.useRef(u.sourcePosition),$=iu.useRef(u.targetPosition),j=iu.useRef(f),F=l&&!!u.internals.handleBounds;return iu.useEffect(()=>{if(i.current&&!u.hidden&&(!F||_.current!==i.current)){if(_.current)r?.unobserve(_.current);r?.observe(i.current),_.current=i.current}},[F,u.hidden]),iu.useEffect(()=>{return()=>{if(_.current)r?.unobserve(_.current),_.current=null}},[]),iu.useEffect(()=>{if(i.current){let J=j.current!==f,U=n.current!==u.sourcePosition,q=$.current!==u.targetPosition;if(J||U||q)j.current=f,n.current=u.sourcePosition,$.current=u.targetPosition,y.getState().updateNodeInternals(new Map([[u.id,{id:u.id,nodeElement:i.current,force:!0}]]))}},[u.id,f,u.sourcePosition,u.targetPosition]),i}function aX({id:u,onClick:f,onMouseEnter:l,onMouseMove:r,onMouseLeave:y,onContextMenu:i,onDoubleClick:_,nodesDraggable:n,elementsSelectable:$,nodesConnectable:j,nodesFocusable:F,resizeObserver:J,noDragClassName:U,noPanClassName:q,disableKeyboardA11y:W,rfId:G,nodeTypes:K,nodeClickDistance:Q,onError:N}){let{node:c,internals:z,isParent:w}=uf(($u)=>{let Fu=$u.nodeLookup.get(u),Ku=$u.parentLookup.has(u);return{node:Fu,internals:Fu.internals,isParent:Ku}},Kf),H=c.type||"default",B=K?.[H]||LN[H];if(B===void 0)N?.("003",_r.error003(H)),H="default",B=K?.default||LN.default;let T=!!(c.draggable||n&&typeof c.draggable>"u"),X=!!(c.selectable||$&&typeof c.selectable>"u"),R=!!(c.connectable||j&&typeof c.connectable>"u"),Y=!!(c.focusable||F&&typeof c.focusable>"u"),P=Tf(),t=V7(c),O=sX({node:c,nodeType:H,hasDimensions:t,resizeObserver:J}),M=CN({nodeRef:O,disabled:c.hidden||!T,noDragClassName:U,handleSelector:c.dragHandle,nodeId:u,isSelectable:X,nodeClickDistance:Q}),S=xN();if(c.hidden)return null;let b=i0(c),Z=CX(c),D=X||T||f||l||r||y,I=l?($u)=>l($u,{...z.userNode}):void 0,k=r?($u)=>r($u,{...z.userNode}):void 0,h=y?($u)=>y($u,{...z.userNode}):void 0,o=i?($u)=>i($u,{...z.userNode}):void 0,s=_?($u)=>_($u,{...z.userNode}):void 0,x=($u)=>{let{selectNodesOnDrag:Fu,nodeDragThreshold:Ku}=P.getState();if(X&&(!Fu||!T||Ku>0))g7({id:u,store:P,nodeRef:O});if(f)f($u,{...z.userNode})},uu=($u)=>{if(t7($u.nativeEvent)||W)return;if(z7.includes($u.key)&&X){let Fu=$u.key==="Escape";g7({id:u,store:P,unselect:Fu,nodeRef:O})}else if(T&&c.selected&&Object.prototype.hasOwnProperty.call(b4,$u.key)){$u.preventDefault();let{ariaLabelConfig:Fu}=P.getState();P.setState({ariaLiveMessage:Fu["node.a11yDescription.ariaLiveMessage"]({direction:$u.key.replace("Arrow","").toLowerCase(),x:~~z.positionAbsolute.x,y:~~z.positionAbsolute.y})}),S({direction:b4[$u.key],factor:$u.shiftKey?4:1})}},nu=()=>{if(W||!O.current?.matches(":focus-visible"))return;let{transform:$u,width:Fu,height:Ku,autoPanOnNodeFocus:Wu,setCenter:m}=P.getState();if(!Wu)return;if(!(B4(new Map([[u,c]]),{x:0,y:0,width:Fu,height:Ku},$u,!0).length>0))m(c.position.x+b.width/2,c.position.y+b.height/2,{zoom:$u[2]})};return ru.jsx("div",{className:Yf(["react-flow__node",`react-flow__node-${H}`,{[q]:T},c.className,{selected:c.selected,selectable:X,parent:w,draggable:T,dragging:M}]),ref:O,style:{zIndex:z.z,transform:`translate(${z.positionAbsolute.x}px,${z.positionAbsolute.y}px)`,pointerEvents:D?"all":"none",visibility:t?"visible":"hidden",...c.style,...Z},"data-id":u,"data-testid":`rf__node-${u}`,onMouseEnter:I,onMouseMove:k,onMouseLeave:h,onContextMenu:o,onClick:x,onDoubleClick:s,onKeyDown:Y?uu:void 0,tabIndex:Y?0:void 0,onFocus:Y?nu:void 0,role:c.ariaRole??(Y?"group":void 0),"aria-roledescription":"node","aria-describedby":W?void 0:`${tN}-${G}`,"aria-label":c.ariaLabel,...c.domAttributes,children:ru.jsx(YX,{value:u,children:ru.jsx(B,{id:u,data:c.data,type:H,positionAbsoluteX:z.positionAbsolute.x,positionAbsoluteY:z.positionAbsolute.y,selected:c.selected??!1,selectable:X,draggable:T,deletable:c.deletable??!0,isConnectable:R,sourcePosition:c.sourcePosition,targetPosition:c.targetPosition,dragging:M,dragHandle:c.dragHandle,zIndex:z.z,parentId:c.parentId,...b})})})}var oX=iu.memo(aX),dX=(u)=>({nodesDraggable:u.nodesDraggable,nodesConnectable:u.nodesConnectable,nodesFocusable:u.nodesFocusable,elementsSelectable:u.elementsSelectable,onError:u.onError});function bN(u){let{nodesDraggable:f,nodesConnectable:l,nodesFocusable:r,elementsSelectable:y,onError:i}=uf(dX,Kf),_=IX(u.onlyRenderVisibleElements),n=gX();return ru.jsx("div",{className:"react-flow__nodes",style:k4,children:_.map(($)=>{return ru.jsx(oX,{id:$,nodeTypes:u.nodeTypes,nodeExtent:u.nodeExtent,onClick:u.onNodeClick,onMouseEnter:u.onNodeMouseEnter,onMouseMove:u.onNodeMouseMove,onMouseLeave:u.onNodeMouseLeave,onContextMenu:u.onNodeContextMenu,onDoubleClick:u.onNodeDoubleClick,noDragClassName:u.noDragClassName,noPanClassName:u.noPanClassName,rfId:u.rfId,disableKeyboardA11y:u.disableKeyboardA11y,resizeObserver:n,nodesDraggable:f,nodesConnectable:l,nodesFocusable:r,elementsSelectable:y,nodeClickDistance:u.nodeClickDistance,onError:i},$)})})}bN.displayName="NodeRenderer";var eX=iu.memo(bN);function uY(u){return uf(iu.useCallback((l)=>{if(!u)return l.edges.map((y)=>y.id);let r=[];if(l.width&&l.height)for(let y of l.edges){let i=l.nodeLookup.get(y.source),_=l.nodeLookup.get(y.target);if(i&&_&&Vc({sourceNode:i,targetNode:_,width:l.width,height:l.height,transform:l.transform}))r.push(y.id)}return r},[u]),Kf)}var fY=({color:u="none",strokeWidth:f=1})=>{let l={strokeWidth:f,...u&&{stroke:u}};return ru.jsx("polyline",{className:"arrow",style:l,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},lY=({color:u="none",strokeWidth:f=1})=>{let l={strokeWidth:f,...u&&{stroke:u,fill:u}};return ru.jsx("polyline",{className:"arrowclosed",style:l,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},EN={[T1.Arrow]:fY,[T1.ArrowClosed]:lY};function rY(u){let f=Tf();return iu.useMemo(()=>{if(!Object.prototype.hasOwnProperty.call(EN,u))return f.getState().onError?.("009",_r.error009(u)),null;return EN[u]},[u])}var yY=({id:u,type:f,color:l,width:r=12.5,height:y=12.5,markerUnits:i="strokeWidth",strokeWidth:_,orient:n="auto-start-reverse"})=>{let $=rY(f);if(!$)return null;return ru.jsx("marker",{className:"react-flow__arrowhead",id:u,markerWidth:`${r}`,markerHeight:`${y}`,viewBox:"-10 -10 20 20",markerUnits:i,orient:n,refX:"0",refY:"0",children:ru.jsx($,{color:l,strokeWidth:_})})},vN=({defaultColor:u,rfId:f})=>{let l=uf((i)=>i.edges),r=uf((i)=>i.defaultEdgeOptions),y=iu.useMemo(()=>{return Yc(l,{id:f,defaultColor:u,defaultMarkerStart:r?.markerStart,defaultMarkerEnd:r?.markerEnd})},[l,r,f,u]);if(!y.length)return null;return ru.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:ru.jsx("defs",{children:y.map((i)=>ru.jsx(yY,{id:i.id,type:i.type,color:i.color,width:i.width,height:i.height,markerUnits:i.markerUnits,strokeWidth:i.strokeWidth,orient:i.orient},i.id))})})};vN.displayName="MarkerDefinitions";var iY=iu.memo(vN);function IN({x:u,y:f,label:l,labelStyle:r,labelShowBg:y=!0,labelBgStyle:i,labelBgPadding:_=[2,4],labelBgBorderRadius:n=2,children:$,className:j,...F}){let[J,U]=iu.useState({x:1,y:0,width:0,height:0}),q=Yf(["react-flow__edge-textwrapper",j]),W=iu.useRef(null);if(iu.useEffect(()=>{if(W.current){let G=W.current.getBBox();U({x:G.x,y:G.y,width:G.width,height:G.height})}},[l]),!l)return null;return ru.jsxs("g",{transform:`translate(${u-J.width/2} ${f-J.height/2})`,className:q,visibility:J.width?"visible":"hidden",...F,children:[y&&ru.jsx("rect",{width:J.width+2*_[0],x:-_[0],y:-_[1],height:J.height+2*_[1],className:"react-flow__edge-textbg",style:i,rx:n,ry:n}),ru.jsx("text",{className:"react-flow__edge-text",y:J.height/2,dy:"0.3em",ref:W,style:r,children:l}),$]})}IN.displayName="EdgeText";var _Y=iu.memo(IN);function si({path:u,labelX:f,labelY:l,label:r,labelStyle:y,labelShowBg:i,labelBgStyle:_,labelBgPadding:n,labelBgBorderRadius:$,interactionWidth:j=20,...F}){return ru.jsxs(ru.Fragment,{children:[ru.jsx("path",{...F,d:u,fill:"none",className:Yf(["react-flow__edge-path",F.className])}),j?ru.jsx("path",{d:u,fill:"none",strokeOpacity:0,strokeWidth:j,className:"react-flow__edge-interaction"}):null,r&&Kr(f)&&Kr(l)?ru.jsx(_Y,{x:f,y:l,label:r,labelStyle:y,labelShowBg:i,labelBgStyle:_,labelBgPadding:n,labelBgBorderRadius:$}):null]})}function TN({pos:u,x1:f,y1:l,x2:r,y2:y}){if(u===zu.Left||u===zu.Right)return[0.5*(f+r),l];return[f,0.5*(l+y)]}function kN({sourceX:u,sourceY:f,sourcePosition:l=zu.Bottom,targetX:r,targetY:y,targetPosition:i=zu.Top}){let[_,n]=TN({pos:l,x1:u,y1:f,x2:r,y2:y}),[$,j]=TN({pos:i,x1:r,y1:y,x2:u,y2:f}),[F,J,U,q]=D4({sourceX:u,sourceY:f,targetX:r,targetY:y,sourceControlX:_,sourceControlY:n,targetControlX:$,targetControlY:j});return[`M${u},${f} C${_},${n} ${$},${j} ${r},${y}`,F,J,U,q]}function gN(u){return iu.memo(({id:f,sourceX:l,sourceY:r,targetX:y,targetY:i,sourcePosition:_,targetPosition:n,label:$,labelStyle:j,labelShowBg:F,labelBgStyle:J,labelBgPadding:U,labelBgBorderRadius:q,style:W,markerEnd:G,markerStart:K,interactionWidth:Q})=>{let[N,c,z]=kN({sourceX:l,sourceY:r,sourcePosition:_,targetX:y,targetY:i,targetPosition:n}),w=u.isInternal?void 0:f;return ru.jsx(si,{id:w,path:N,labelX:c,labelY:z,label:$,labelStyle:j,labelShowBg:F,labelBgStyle:J,labelBgPadding:U,labelBgBorderRadius:q,style:W,markerEnd:G,markerStart:K,interactionWidth:Q})})}var nY=gN({isInternal:!1}),sN=gN({isInternal:!0});nY.displayName="SimpleBezierEdge";sN.displayName="SimpleBezierEdgeInternal";function aN(u){return iu.memo(({id:f,sourceX:l,sourceY:r,targetX:y,targetY:i,label:_,labelStyle:n,labelShowBg:$,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:J,style:U,sourcePosition:q=zu.Bottom,targetPosition:W=zu.Top,markerEnd:G,markerStart:K,pathOptions:Q,interactionWidth:N})=>{let[c,z,w]=F$({sourceX:l,sourceY:r,sourcePosition:q,targetX:y,targetY:i,targetPosition:W,borderRadius:Q?.borderRadius,offset:Q?.offset,stepPosition:Q?.stepPosition}),H=u.isInternal?void 0:f;return ru.jsx(si,{id:H,path:c,labelX:z,labelY:w,label:_,labelStyle:n,labelShowBg:$,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:J,style:U,markerEnd:G,markerStart:K,interactionWidth:N})})}var oN=aN({isInternal:!1}),dN=aN({isInternal:!0});oN.displayName="SmoothStepEdge";dN.displayName="SmoothStepEdgeInternal";function eN(u){return iu.memo(({id:f,...l})=>{let r=u.isInternal?void 0:f;return ru.jsx(oN,{...l,id:r,pathOptions:iu.useMemo(()=>({borderRadius:0,offset:l.pathOptions?.offset}),[l.pathOptions?.offset])})})}var $Y=eN({isInternal:!1}),uz=eN({isInternal:!0});$Y.displayName="StepEdge";uz.displayName="StepEdgeInternal";function fz(u){return iu.memo(({id:f,sourceX:l,sourceY:r,targetX:y,targetY:i,label:_,labelStyle:n,labelShowBg:$,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:J,style:U,markerEnd:q,markerStart:W,interactionWidth:G})=>{let[K,Q,N]=S4({sourceX:l,sourceY:r,targetX:y,targetY:i}),c=u.isInternal?void 0:f;return ru.jsx(si,{id:c,path:K,labelX:Q,labelY:N,label:_,labelStyle:n,labelShowBg:$,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:J,style:U,markerEnd:q,markerStart:W,interactionWidth:G})})}var AY=fz({isInternal:!1}),lz=fz({isInternal:!0});AY.displayName="StraightEdge";lz.displayName="StraightEdgeInternal";function rz(u){return iu.memo(({id:f,sourceX:l,sourceY:r,targetX:y,targetY:i,sourcePosition:_=zu.Bottom,targetPosition:n=zu.Top,label:$,labelStyle:j,labelShowBg:F,labelBgStyle:J,labelBgPadding:U,labelBgBorderRadius:q,style:W,markerEnd:G,markerStart:K,pathOptions:Q,interactionWidth:N})=>{let[c,z,w]=t4({sourceX:l,sourceY:r,sourcePosition:_,targetX:y,targetY:i,targetPosition:n,curvature:Q?.curvature}),H=u.isInternal?void 0:f;return ru.jsx(si,{id:H,path:c,labelX:z,labelY:w,label:$,labelStyle:j,labelShowBg:F,labelBgStyle:J,labelBgPadding:U,labelBgBorderRadius:q,style:W,markerEnd:G,markerStart:K,interactionWidth:N})})}var jY=rz({isInternal:!1}),yz=rz({isInternal:!0});jY.displayName="BezierEdge";yz.displayName="BezierEdgeInternal";var ZN={default:yz,straight:lz,step:uz,smoothstep:dN,simplebezier:sN},HN={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null},FY=(u,f,l)=>{if(l===zu.Left)return u-f;if(l===zu.Right)return u+f;return u},JY=(u,f,l)=>{if(l===zu.Top)return u-f;if(l===zu.Bottom)return u+f;return u},ON="react-flow__edgeupdater";function BN({position:u,centerX:f,centerY:l,radius:r=10,onMouseDown:y,onMouseEnter:i,onMouseOut:_,type:n}){return ru.jsx("circle",{onMouseDown:y,onMouseEnter:i,onMouseOut:_,className:Yf([ON,`${ON}-${n}`]),cx:FY(f,r,u),cy:JY(l,r,u),r,stroke:"transparent",fill:"transparent"})}function UY({isReconnectable:u,reconnectRadius:f,edge:l,sourceX:r,sourceY:y,targetX:i,targetY:_,sourcePosition:n,targetPosition:$,onReconnect:j,onReconnectStart:F,onReconnectEnd:J,setReconnecting:U,setUpdateHover:q}){let W=Tf(),G=(z,w)=>{if(z.button!==0)return;let{autoPanOnConnect:H,domNode:B,connectionMode:T,connectionRadius:X,lib:R,onConnectStart:Y,cancelConnection:P,nodeLookup:t,rfId:O,panBy:M,updateConnection:S}=W.getState(),b=w.type==="target",Z=(k,h)=>{U(!1),J?.(k,l,w.type,h)},D=(k)=>j?.(l,k),I=(k,h)=>{U(!0),F?.(z,l,w.type),Y?.(k,h)};p4.onPointerDown(z.nativeEvent,{autoPanOnConnect:H,connectionMode:T,connectionRadius:X,domNode:B,handleId:w.id,nodeId:w.nodeId,nodeLookup:t,isTarget:b,edgeUpdaterType:w.type,lib:R,flowId:O,cancelConnection:P,panBy:M,isValidConnection:(...k)=>W.getState().isValidConnection?.(...k)??!0,onConnect:D,onConnectStart:I,onConnectEnd:(...k)=>W.getState().onConnectEnd?.(...k),onReconnectEnd:Z,updateConnection:S,getTransform:()=>W.getState().transform,getFromHandle:()=>W.getState().connection.fromHandle,dragThreshold:W.getState().connectionDragThreshold,handleDomNode:z.currentTarget})},K=(z)=>G(z,{nodeId:l.target,id:l.targetHandle??null,type:"target"}),Q=(z)=>G(z,{nodeId:l.source,id:l.sourceHandle??null,type:"source"}),N=()=>q(!0),c=()=>q(!1);return ru.jsxs(ru.Fragment,{children:[(u===!0||u==="source")&&ru.jsx(BN,{position:n,centerX:r,centerY:y,radius:f,onMouseDown:K,onMouseEnter:N,onMouseOut:c,type:"source"}),(u===!0||u==="target")&&ru.jsx(BN,{position:$,centerX:i,centerY:_,radius:f,onMouseDown:Q,onMouseEnter:N,onMouseOut:c,type:"target"})]})}function QY({id:u,edgesFocusable:f,edgesReconnectable:l,elementsSelectable:r,onClick:y,onDoubleClick:i,onContextMenu:_,onMouseEnter:n,onMouseMove:$,onMouseLeave:j,reconnectRadius:F,onReconnect:J,onReconnectStart:U,onReconnectEnd:q,rfId:W,edgeTypes:G,noPanClassName:K,onError:Q,disableKeyboardA11y:N}){let c=uf((m)=>m.edgeLookup.get(u)),z=uf((m)=>m.defaultEdgeOptions);c=z?{...z,...c}:c;let w=c.type||"default",H=G?.[w]||ZN[w];if(H===void 0)Q?.("011",_r.error011(w)),w="default",H=G?.default||ZN.default;let B=!!(c.focusable||f&&typeof c.focusable>"u"),T=typeof J<"u"&&(c.reconnectable||l&&typeof c.reconnectable>"u"),X=!!(c.selectable||r&&typeof c.selectable>"u"),R=iu.useRef(null),[Y,P]=iu.useState(!1),[t,O]=iu.useState(!1),M=Tf(),{zIndex:S,sourceX:b,sourceY:Z,targetX:D,targetY:I,sourcePosition:k,targetPosition:h}=uf(iu.useCallback((m)=>{let d=m.nodeLookup.get(c.source),e=m.nodeLookup.get(c.target);if(!d||!e)return{zIndex:c.zIndex,...HN};let cu=Xc({id:u,sourceNode:d,targetNode:e,sourceHandle:c.sourceHandle||null,targetHandle:c.targetHandle||null,connectionMode:m.connectionMode,onError:Q});return{zIndex:Bc({selected:c.selected,zIndex:c.zIndex,sourceNode:d,targetNode:e,elevateOnSelect:m.elevateEdgesOnSelect,zIndexMode:m.zIndexMode}),...cu||HN}},[c.source,c.target,c.sourceHandle,c.targetHandle,c.selected,c.zIndex]),Kf),o=iu.useMemo(()=>c.markerStart?`url('#${P4(c.markerStart,W)}')`:void 0,[c.markerStart,W]),s=iu.useMemo(()=>c.markerEnd?`url('#${P4(c.markerEnd,W)}')`:void 0,[c.markerEnd,W]);if(c.hidden||b===null||Z===null||D===null||I===null)return null;let x=(m)=>{let{addSelectedEdges:d,unselectNodesAndEdges:e,multiSelectionActive:cu}=M.getState();if(X)if(M.setState({nodesSelectionActive:!1}),c.selected&&cu)e({nodes:[],edges:[c]}),R.current?.blur();else d([u]);if(y)y(m,c)},uu=i?(m)=>{i(m,{...c})}:void 0,nu=_?(m)=>{_(m,{...c})}:void 0,$u=n?(m)=>{n(m,{...c})}:void 0,Fu=$?(m)=>{$(m,{...c})}:void 0,Ku=j?(m)=>{j(m,{...c})}:void 0,Wu=(m)=>{if(!N&&z7.includes(m.key)&&X){let{unselectNodesAndEdges:d,addSelectedEdges:e}=M.getState();if(m.key==="Escape")R.current?.blur(),d({edges:[c]});else e([u])}};return ru.jsx("svg",{style:{zIndex:S},children:ru.jsxs("g",{className:Yf(["react-flow__edge",`react-flow__edge-${w}`,c.className,K,{selected:c.selected,animated:c.animated,inactive:!X&&!y,updating:Y,selectable:X}]),onClick:x,onDoubleClick:uu,onContextMenu:nu,onMouseEnter:$u,onMouseMove:Fu,onMouseLeave:Ku,onKeyDown:B?Wu:void 0,tabIndex:B?0:void 0,role:c.ariaRole??(B?"group":"img"),"aria-roledescription":"edge","data-id":u,"data-testid":`rf__edge-${u}`,"aria-label":c.ariaLabel===null?void 0:c.ariaLabel||`Edge from ${c.source} to ${c.target}`,"aria-describedby":B?`${SN}-${W}`:void 0,ref:R,...c.domAttributes,children:[!t&&ru.jsx(H,{id:u,source:c.source,target:c.target,type:c.type,selected:c.selected,animated:c.animated,selectable:X,deletable:c.deletable??!0,label:c.label,labelStyle:c.labelStyle,labelShowBg:c.labelShowBg,labelBgStyle:c.labelBgStyle,labelBgPadding:c.labelBgPadding,labelBgBorderRadius:c.labelBgBorderRadius,sourceX:b,sourceY:Z,targetX:D,targetY:I,sourcePosition:k,targetPosition:h,data:c.data,style:c.style,sourceHandleId:c.sourceHandle,targetHandleId:c.targetHandle,markerStart:o,markerEnd:s,pathOptions:"pathOptions"in c?c.pathOptions:void 0,interactionWidth:c.interactionWidth}),T&&ru.jsx(UY,{edge:c,isReconnectable:T,reconnectRadius:F,onReconnect:J,onReconnectStart:U,onReconnectEnd:q,sourceX:b,sourceY:Z,targetX:D,targetY:I,sourcePosition:k,targetPosition:h,setUpdateHover:P,setReconnecting:O})]})})}var qY=iu.memo(QY),WY=(u)=>({edgesFocusable:u.edgesFocusable,edgesReconnectable:u.edgesReconnectable,elementsSelectable:u.elementsSelectable,connectionMode:u.connectionMode,onError:u.onError});function iz({defaultMarkerColor:u,onlyRenderVisibleElements:f,rfId:l,edgeTypes:r,noPanClassName:y,onReconnect:i,onEdgeContextMenu:_,onEdgeMouseEnter:n,onEdgeMouseMove:$,onEdgeMouseLeave:j,onEdgeClick:F,reconnectRadius:J,onEdgeDoubleClick:U,onReconnectStart:q,onReconnectEnd:W,disableKeyboardA11y:G}){let{edgesFocusable:K,edgesReconnectable:Q,elementsSelectable:N,onError:c}=uf(WY,Kf),z=uY(f);return ru.jsxs("div",{className:"react-flow__edges",children:[ru.jsx(iY,{defaultColor:u,rfId:l}),z.map((w)=>{return ru.jsx(qY,{id:w,edgesFocusable:K,edgesReconnectable:Q,elementsSelectable:N,noPanClassName:y,onReconnect:i,onContextMenu:_,onMouseEnter:n,onMouseMove:$,onMouseLeave:j,onClick:F,reconnectRadius:J,onDoubleClick:U,onReconnectStart:q,onReconnectEnd:W,rfId:l,onError:c,edgeTypes:r,disableKeyboardA11y:G},w)})]})}iz.displayName="EdgeRenderer";var cY=iu.memo(iz),NY=(u)=>`translate(${u.transform[0]}px,${u.transform[1]}px) scale(${u.transform[2]})`;function zY({children:u}){let f=uf(NY);return ru.jsx("div",{className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:f},children:u})}function GY(u){let f=s7(),l=iu.useRef(!1);iu.useEffect(()=>{if(!l.current&&f.viewportInitialized&&u)setTimeout(()=>u(f),1),l.current=!0},[u,f.viewportInitialized])}var KY=(u)=>u.panZoom?.syncViewport;function LY(u){let f=uf(KY),l=Tf();return iu.useEffect(()=>{if(u)f?.(u),l.setState({transform:[u.x,u.y,u.zoom]})},[u,f]),null}function VN(u){return u.connection.inProgress?{...u.connection,to:vi(u.connection.to,u.transform)}:{...u.connection}}function wY(u){if(u)return(l)=>{let r=VN(l);return u(r)};return VN}function EY(u){let f=wY(u);return uf(f,Kf)}var TY=(u)=>({nodesConnectable:u.nodesConnectable,isValid:u.connection.isValid,inProgress:u.connection.inProgress,width:u.width,height:u.height});function ZY({containerStyle:u,style:f,type:l,component:r}){let{nodesConnectable:y,width:i,height:_,isValid:n,inProgress:$}=uf(TY,Kf);if(!(i&&y&&$))return null;return ru.jsx("svg",{style:u,width:i,height:_,className:"react-flow__connectionline react-flow__container",children:ru.jsx("g",{className:Yf(["react-flow__connection",L7(n)]),children:ru.jsx(_z,{style:f,type:l,CustomComponent:r,isValid:n})})})}var _z=({style:u,type:f=y0.Bezier,CustomComponent:l,isValid:r})=>{let{inProgress:y,from:i,fromNode:_,fromHandle:n,fromPosition:$,to:j,toNode:F,toHandle:J,toPosition:U,pointer:q}=EY();if(!y)return;if(l)return ru.jsx(l,{connectionLineType:f,connectionLineStyle:u,fromNode:_,fromHandle:n,fromX:i.x,fromY:i.y,toX:j.x,toY:j.y,fromPosition:$,toPosition:U,connectionStatus:L7(r),toNode:F,toHandle:J,pointer:q});let W="",G={sourceX:i.x,sourceY:i.y,sourcePosition:$,targetX:j.x,targetY:j.y,targetPosition:U};switch(f){case y0.Bezier:[W]=t4(G);break;case y0.SimpleBezier:[W]=kN(G);break;case y0.Step:[W]=F$({...G,borderRadius:0});break;case y0.SmoothStep:[W]=F$(G);break;default:[W]=S4(G)}return ru.jsx("path",{d:W,fill:"none",className:"react-flow__connection-path",style:u})};_z.displayName="ConnectionLine";var HY={};function XN(u=HY){let f=iu.useRef(u),l=Tf();iu.useEffect(()=>{},[u])}function OY(){let u=Tf(),f=iu.useRef(!1);iu.useEffect(()=>{},[])}function nz({nodeTypes:u,edgeTypes:f,onInit:l,onNodeClick:r,onEdgeClick:y,onNodeDoubleClick:i,onEdgeDoubleClick:_,onNodeMouseEnter:n,onNodeMouseMove:$,onNodeMouseLeave:j,onNodeContextMenu:F,onSelectionContextMenu:J,onSelectionStart:U,onSelectionEnd:q,connectionLineType:W,connectionLineStyle:G,connectionLineComponent:K,connectionLineContainerStyle:Q,selectionKeyCode:N,selectionOnDrag:c,selectionMode:z,multiSelectionKeyCode:w,panActivationKeyCode:H,zoomActivationKeyCode:B,deleteKeyCode:T,onlyRenderVisibleElements:X,elementsSelectable:R,defaultViewport:Y,translateExtent:P,minZoom:t,maxZoom:O,preventScrolling:M,defaultMarkerColor:S,zoomOnScroll:b,zoomOnPinch:Z,panOnScroll:D,panOnScrollSpeed:I,panOnScrollMode:k,zoomOnDoubleClick:h,panOnDrag:o,onPaneClick:s,onPaneMouseEnter:x,onPaneMouseMove:uu,onPaneMouseLeave:nu,onPaneScroll:$u,onPaneContextMenu:Fu,paneClickDistance:Ku,nodeClickDistance:Wu,onEdgeContextMenu:m,onEdgeMouseEnter:d,onEdgeMouseMove:e,onEdgeMouseLeave:cu,reconnectRadius:g,onReconnect:Qu,onReconnectStart:Eu,onReconnectEnd:Tu,noDragClassName:Du,noWheelClassName:ff,noPanClassName:rf,disableKeyboardA11y:Lf,nodeExtent:gf,rfId:jr,viewport:Ol,onViewportChange:ef}){return XN(u),XN(f),OY(),GY(l),LY(Ol),ru.jsx(bX,{onPaneClick:s,onPaneMouseEnter:x,onPaneMouseMove:uu,onPaneMouseLeave:nu,onPaneContextMenu:Fu,onPaneScroll:$u,paneClickDistance:Ku,deleteKeyCode:T,selectionKeyCode:N,selectionOnDrag:c,selectionMode:z,onSelectionStart:U,onSelectionEnd:q,multiSelectionKeyCode:w,panActivationKeyCode:H,zoomActivationKeyCode:B,elementsSelectable:R,zoomOnScroll:b,zoomOnPinch:Z,zoomOnDoubleClick:h,panOnScroll:D,panOnScrollSpeed:I,panOnScrollMode:k,panOnDrag:o,defaultViewport:Y,translateExtent:P,minZoom:t,maxZoom:O,onSelectionContextMenu:J,preventScrolling:M,noDragClassName:Du,noWheelClassName:ff,noPanClassName:rf,disableKeyboardA11y:Lf,onViewportChange:ef,isControlledViewport:!!Ol,children:ru.jsxs(zY,{children:[ru.jsx(cY,{edgeTypes:f,onEdgeClick:y,onEdgeDoubleClick:_,onReconnect:Qu,onReconnectStart:Eu,onReconnectEnd:Tu,onlyRenderVisibleElements:X,onEdgeContextMenu:m,onEdgeMouseEnter:d,onEdgeMouseMove:e,onEdgeMouseLeave:cu,reconnectRadius:g,defaultMarkerColor:S,noPanClassName:rf,disableKeyboardA11y:Lf,rfId:jr}),ru.jsx(ZY,{style:G,type:W,component:K,containerStyle:Q}),ru.jsx("div",{className:"react-flow__edgelabel-renderer"}),ru.jsx(eX,{nodeTypes:u,onNodeClick:r,onNodeDoubleClick:i,onNodeMouseEnter:n,onNodeMouseMove:$,onNodeMouseLeave:j,onNodeContextMenu:F,nodeClickDistance:Wu,onlyRenderVisibleElements:X,noPanClassName:rf,noDragClassName:Du,disableKeyboardA11y:Lf,nodeExtent:gf,rfId:jr}),ru.jsx("div",{className:"react-flow__viewport-portal"})]})})}nz.displayName="GraphView";var BY=iu.memo(nz),YN=({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,width:y,height:i,fitView:_,fitViewOptions:n,minZoom:$=0.5,maxZoom:j=2,nodeOrigin:F,nodeExtent:J,zIndexMode:U="basic"}={})=>{let q=new Map,W=new Map,G=new Map,K=new Map,Q=r??f??[],N=l??u??[],c=F??[0,0],z=J??xi;R7(G,K,Q);let{nodesInitialized:w}=M4(N,q,W,{nodeOrigin:c,nodeExtent:z,zIndexMode:U}),H=[0,0,1];if(_&&y&&i){let B=Ri(q,{filter:(Y)=>!!((Y.width||Y.initialWidth)&&(Y.height||Y.initialHeight))}),{x:T,y:X,zoom:R}=j$(B,y,i,$,j,n?.padding??0.1);H=[T,X,R]}return{rfId:"1",width:y??0,height:i??0,transform:H,nodes:N,nodesInitialized:w,nodeLookup:q,parentLookup:W,edges:Q,edgeLookup:K,connectionLookup:G,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:l!==void 0,hasDefaultEdges:r!==void 0,panZoom:null,minZoom:$,maxZoom:j,translateExtent:xi,nodeExtent:z,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:E1.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:c,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:_??!1,fitViewOptions:n,fitViewResolver:null,connection:{...K7},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:B7,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:G7,zIndexMode:U,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map}},VY=({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,width:y,height:i,fitView:_,fitViewOptions:n,minZoom:$,maxZoom:j,nodeOrigin:F,nodeExtent:J,zIndexMode:U})=>AN((q,W)=>{async function G(){let{nodeLookup:K,panZoom:Q,fitViewOptions:N,fitViewResolver:c,width:z,height:w,minZoom:H,maxZoom:B}=W();if(!Q)return;await wc({nodes:K,width:z,height:w,panZoom:Q,minZoom:H,maxZoom:B},N),c?.resolve(!0),q({fitViewResolver:null})}return{...YN({nodes:u,edges:f,width:y,height:i,fitView:_,fitViewOptions:n,minZoom:$,maxZoom:j,nodeOrigin:F,nodeExtent:J,defaultNodes:l,defaultEdges:r,zIndexMode:U}),setNodes:(K)=>{let{nodeLookup:Q,parentLookup:N,nodeOrigin:c,elevateNodesOnSelect:z,fitViewQueued:w,zIndexMode:H,nodesSelectionActive:B}=W(),{nodesInitialized:T,hasSelectedNodes:X}=M4(K,Q,N,{nodeOrigin:c,nodeExtent:J,elevateNodesOnSelect:z,checkEquality:!0,zIndexMode:H}),R=B&&X;if(w&&T)G(),q({nodes:K,nodesInitialized:T,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:R});else q({nodes:K,nodesInitialized:T,nodesSelectionActive:R})},setEdges:(K)=>{let{connectionLookup:Q,edgeLookup:N}=W();R7(Q,N,K),q({edges:K})},setDefaultNodesAndEdges:(K,Q)=>{if(K){let{setNodes:N}=W();N(K),q({hasDefaultNodes:!0})}if(Q){let{setEdges:N}=W();N(Q),q({hasDefaultEdges:!0})}},updateNodeInternals:(K)=>{let{triggerNodeChanges:Q,nodeLookup:N,parentLookup:c,domNode:z,nodeOrigin:w,nodeExtent:H,debug:B,fitViewQueued:T,zIndexMode:X}=W(),{changes:R,updatedInternals:Y}=Pc(K,N,c,z,w,H,X);if(!Y)return;if(tc(N,c,{nodeOrigin:w,nodeExtent:H,zIndexMode:X}),T)G(),q({fitViewQueued:!1,fitViewOptions:void 0});else q({});if(R?.length>0){if(B)console.log("React Flow: trigger node changes",R);Q?.(R)}},updateNodePositions:(K,Q=!1)=>{let N=[],c=[],{nodeLookup:z,triggerNodeChanges:w,connection:H,updateConnection:B,onNodesChangeMiddlewareMap:T}=W();for(let[X,R]of K){let Y=z.get(X),P=!!(Y?.expandParent&&Y?.parentId&&R?.position),t={id:X,type:"position",position:P?{x:Math.max(0,R.position.x),y:Math.max(0,R.position.y)}:R.position,dragging:Q};if(Y&&H.inProgress&&H.fromNode.id===Y.id){let O=Z1(Y,H.fromHandle,zu.Left,!0);B({...H,from:O})}if(P&&Y.parentId)N.push({id:X,parentId:Y.parentId,rect:{...R.internals.positionAbsolute,width:R.measured.width??0,height:R.measured.height??0}});c.push(t)}if(N.length>0){let{parentLookup:X,nodeOrigin:R}=W(),Y=m4(N,z,X,R);c.push(...Y)}for(let X of T.values())c=X(c);w(c)},triggerNodeChanges:(K)=>{let{onNodesChange:Q,setNodes:N,nodes:c,hasDefaultNodes:z,debug:w}=W();if(K?.length){if(z){let H=qX(K,c);N(H)}if(w)console.log("React Flow: trigger node changes",K);Q?.(K)}},triggerEdgeChanges:(K)=>{let{onEdgesChange:Q,setEdges:N,edges:c,hasDefaultEdges:z,debug:w}=W();if(K?.length){if(z){let H=WX(K,c);N(H)}if(w)console.log("React Flow: trigger edge changes",K);Q?.(K)}},addSelectedNodes:(K)=>{let{multiSelectionActive:Q,edgeLookup:N,nodeLookup:c,triggerNodeChanges:z,triggerEdgeChanges:w}=W();if(Q){let H=K.map((B)=>Ty(B,!0));z(H);return}z(gi(c,new Set([...K]),!0)),w(gi(N))},addSelectedEdges:(K)=>{let{multiSelectionActive:Q,edgeLookup:N,nodeLookup:c,triggerNodeChanges:z,triggerEdgeChanges:w}=W();if(Q){let H=K.map((B)=>Ty(B,!0));w(H);return}w(gi(N,new Set([...K]))),z(gi(c,new Set,!0))},unselectNodesAndEdges:({nodes:K,edges:Q}={})=>{let{edges:N,nodes:c,nodeLookup:z,triggerNodeChanges:w,triggerEdgeChanges:H}=W(),B=K?K:c,T=Q?Q:N,X=[];for(let Y of B){if(!Y.selected)continue;let P=z.get(Y.id);if(P)P.selected=!1;X.push(Ty(Y.id,!1))}let R=[];for(let Y of T){if(!Y.selected)continue;R.push(Ty(Y.id,!1))}w(X),H(R)},setMinZoom:(K)=>{let{panZoom:Q,maxZoom:N}=W();Q?.setScaleExtent([K,N]),q({minZoom:K})},setMaxZoom:(K)=>{let{panZoom:Q,minZoom:N}=W();Q?.setScaleExtent([N,K]),q({maxZoom:K})},setTranslateExtent:(K)=>{W().panZoom?.setTranslateExtent(K),q({translateExtent:K})},resetSelectedElements:()=>{let{edges:K,nodes:Q,triggerNodeChanges:N,triggerEdgeChanges:c,elementsSelectable:z}=W();if(!z)return;let w=Q.reduce((B,T)=>T.selected?[...B,Ty(T.id,!1)]:B,[]),H=K.reduce((B,T)=>T.selected?[...B,Ty(T.id,!1)]:B,[]);N(w),c(H)},setNodeExtent:(K)=>{let{nodes:Q,nodeLookup:N,parentLookup:c,nodeOrigin:z,elevateNodesOnSelect:w,nodeExtent:H,zIndexMode:B}=W();if(K[0][0]===H[0][0]&&K[0][1]===H[0][1]&&K[1][0]===H[1][0]&&K[1][1]===H[1][1])return;M4(Q,N,c,{nodeOrigin:z,nodeExtent:K,elevateNodesOnSelect:w,checkEquality:!1,zIndexMode:B}),q({nodeExtent:K})},panBy:(K)=>{let{transform:Q,width:N,height:c,panZoom:z,translateExtent:w}=W();return Mc({delta:K,panZoom:z,transform:Q,translateExtent:w,width:N,height:c})},setCenter:async(K,Q,N)=>{let{width:c,height:z,maxZoom:w,panZoom:H}=W();if(!H)return Promise.resolve(!1);let B=typeof N?.zoom<"u"?N.zoom:w;return await H.setViewport({x:c/2-K*B,y:z/2-Q*B,zoom:B},{duration:N?.duration,ease:N?.ease,interpolate:N?.interpolate}),Promise.resolve(!0)},cancelConnection:()=>{q({connection:{...K7}})},updateConnection:(K)=>{q({connection:K})},reset:()=>q({...YN()})}},Object.is);function XY({initialNodes:u,initialEdges:f,defaultNodes:l,defaultEdges:r,initialWidth:y,initialHeight:i,initialMinZoom:_,initialMaxZoom:n,initialFitViewOptions:$,fitView:j,nodeOrigin:F,nodeExtent:J,zIndexMode:U,children:q}){let[W]=iu.useState(()=>VY({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,width:y,height:i,fitView:j,minZoom:_,maxZoom:n,fitViewOptions:$,nodeOrigin:F,nodeExtent:J,zIndexMode:U}));return ru.jsx(sV,{value:W,children:ru.jsx(zX,{children:q})})}function YY({children:u,nodes:f,edges:l,defaultNodes:r,defaultEdges:y,width:i,height:_,fitView:n,fitViewOptions:$,minZoom:j,maxZoom:F,nodeOrigin:J,nodeExtent:U,zIndexMode:q}){if(iu.useContext(v4))return ru.jsx(ru.Fragment,{children:u});return ru.jsx(XY,{initialNodes:f,initialEdges:l,defaultNodes:r,defaultEdges:y,initialWidth:i,initialHeight:_,fitView:n,initialFitViewOptions:$,initialMinZoom:j,initialMaxZoom:F,nodeOrigin:J,nodeExtent:U,zIndexMode:q,children:u})}var DY={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function tY({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,className:y,nodeTypes:i,edgeTypes:_,onNodeClick:n,onEdgeClick:$,onInit:j,onMove:F,onMoveStart:J,onMoveEnd:U,onConnect:q,onConnectStart:W,onConnectEnd:G,onClickConnectStart:K,onClickConnectEnd:Q,onNodeMouseEnter:N,onNodeMouseMove:c,onNodeMouseLeave:z,onNodeContextMenu:w,onNodeDoubleClick:H,onNodeDragStart:B,onNodeDrag:T,onNodeDragStop:X,onNodesDelete:R,onEdgesDelete:Y,onDelete:P,onSelectionChange:t,onSelectionDragStart:O,onSelectionDrag:M,onSelectionDragStop:S,onSelectionContextMenu:b,onSelectionStart:Z,onSelectionEnd:D,onBeforeDelete:I,connectionMode:k,connectionLineType:h=y0.Bezier,connectionLineStyle:o,connectionLineComponent:s,connectionLineContainerStyle:x,deleteKeyCode:uu="Backspace",selectionKeyCode:nu="Shift",selectionOnDrag:$u=!1,selectionMode:Fu=Ly.Full,panActivationKeyCode:Ku="Space",multiSelectionKeyCode:Wu=Ii()?"Meta":"Control",zoomActivationKeyCode:m=Ii()?"Meta":"Control",snapToGrid:d,snapGrid:e,onlyRenderVisibleElements:cu=!1,selectNodesOnDrag:g,nodesDraggable:Qu,autoPanOnNodeFocus:Eu,nodesConnectable:Tu,nodesFocusable:Du,nodeOrigin:ff=PN,edgesFocusable:rf,edgesReconnectable:Lf,elementsSelectable:gf=!0,defaultViewport:jr=$X,minZoom:Ol=0.5,maxZoom:ef=2,translateExtent:hr=xi,preventScrolling:vl=!0,nodeExtent:ju,defaultMarkerColor:bu="#b1b1b7",zoomOnScroll:ul=!0,zoomOnPinch:tu=!0,panOnScroll:ou=!1,panOnScrollSpeed:Al=0.5,panOnScrollMode:Bl=D0.Free,zoomOnDoubleClick:sf=!0,panOnDrag:Il=!0,onPaneClick:Fr,onPaneMouseEnter:kl,onPaneMouseMove:M0,onPaneMouseLeave:r_,onPaneScroll:y_,onPaneContextMenu:fu,paneClickDistance:Zu=1,nodeClickDistance:Lu=0,children:ku,onReconnect:Uf,onReconnectStart:vu,onReconnectEnd:yf,onEdgeContextMenu:_f,onEdgeDoubleClick:xf,onEdgeMouseEnter:i_,onEdgeMouseMove:B$,onEdgeMouseLeave:__,reconnectRadius:V$=10,onNodesChange:n_,onEdgesChange:X$,noDragClassName:Vl="nodrag",noWheelClassName:br="nowheel",noPanClassName:$_="nopan",fitView:A_,fitViewOptions:Vy,connectOnClick:Y$,attributionPosition:vr,proOptions:D$,defaultEdgeOptions:Rf,elevateNodesOnSelect:z8=!0,elevateEdgesOnSelect:t$=!1,disableKeyboardA11y:Jr=!1,autoPanOnConnect:Xy,autoPanOnNodeDrag:p1,autoPanSpeed:j_,connectionRadius:Mj,isValidConnection:Yy,onError:mj,style:S$,id:P$,nodeDragThreshold:G8,connectionDragThreshold:K8,viewport:m0,onViewportChange:Dy,width:C1,height:Xl,colorMode:M$="light",debug:F_,onScroll:gl,ariaLabelConfig:m$,zIndexMode:p$="basic",...L8},C$){let Ir=P$||"1",x$=JX(M$),J_=iu.useCallback((R$)=>{R$.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),gl?.(R$)},[gl]);return ru.jsx("div",{"data-testid":"rf__wrapper",...L8,onScroll:J_,style:{...S$,...DY},ref:C$,className:Yf(["react-flow",y,x$]),id:P$,role:"application",children:ru.jsxs(YY,{nodes:u,edges:f,width:C1,height:Xl,fitView:A_,fitViewOptions:Vy,minZoom:Ol,maxZoom:ef,nodeOrigin:ff,nodeExtent:ju,zIndexMode:p$,children:[ru.jsx(FX,{nodes:u,edges:f,defaultNodes:l,defaultEdges:r,onConnect:q,onConnectStart:W,onConnectEnd:G,onClickConnectStart:K,onClickConnectEnd:Q,nodesDraggable:Qu,autoPanOnNodeFocus:Eu,nodesConnectable:Tu,nodesFocusable:Du,edgesFocusable:rf,edgesReconnectable:Lf,elementsSelectable:gf,elevateNodesOnSelect:z8,elevateEdgesOnSelect:t$,minZoom:Ol,maxZoom:ef,nodeExtent:ju,onNodesChange:n_,onEdgesChange:X$,snapToGrid:d,snapGrid:e,connectionMode:k,translateExtent:hr,connectOnClick:Y$,defaultEdgeOptions:Rf,fitView:A_,fitViewOptions:Vy,onNodesDelete:R,onEdgesDelete:Y,onDelete:P,onNodeDragStart:B,onNodeDrag:T,onNodeDragStop:X,onSelectionDrag:M,onSelectionDragStart:O,onSelectionDragStop:S,onMove:F,onMoveStart:J,onMoveEnd:U,noPanClassName:$_,nodeOrigin:ff,rfId:Ir,autoPanOnConnect:Xy,autoPanOnNodeDrag:p1,autoPanSpeed:j_,onError:mj,connectionRadius:Mj,isValidConnection:Yy,selectNodesOnDrag:g,nodeDragThreshold:G8,connectionDragThreshold:K8,onBeforeDelete:I,debug:F_,ariaLabelConfig:m$,zIndexMode:p$}),ru.jsx(BY,{onInit:j,onNodeClick:n,onEdgeClick:$,onNodeMouseEnter:N,onNodeMouseMove:c,onNodeMouseLeave:z,onNodeContextMenu:w,onNodeDoubleClick:H,nodeTypes:i,edgeTypes:_,connectionLineType:h,connectionLineStyle:o,connectionLineComponent:s,connectionLineContainerStyle:x,selectionKeyCode:nu,selectionOnDrag:$u,selectionMode:Fu,deleteKeyCode:uu,multiSelectionKeyCode:Wu,panActivationKeyCode:Ku,zoomActivationKeyCode:m,onlyRenderVisibleElements:cu,defaultViewport:jr,translateExtent:hr,minZoom:Ol,maxZoom:ef,preventScrolling:vl,zoomOnScroll:ul,zoomOnPinch:tu,zoomOnDoubleClick:sf,panOnScroll:ou,panOnScrollSpeed:Al,panOnScrollMode:Bl,panOnDrag:Il,onPaneClick:Fr,onPaneMouseEnter:kl,onPaneMouseMove:M0,onPaneMouseLeave:r_,onPaneScroll:y_,onPaneContextMenu:fu,paneClickDistance:Zu,nodeClickDistance:Lu,onSelectionContextMenu:b,onSelectionStart:Z,onSelectionEnd:D,onReconnect:Uf,onReconnectStart:vu,onReconnectEnd:yf,onEdgeContextMenu:_f,onEdgeDoubleClick:xf,onEdgeMouseEnter:i_,onEdgeMouseMove:B$,onEdgeMouseLeave:__,reconnectRadius:V$,defaultMarkerColor:bu,noDragClassName:Vl,noWheelClassName:br,noPanClassName:$_,rfId:Ir,disableKeyboardA11y:Jr,nodeExtent:ju,viewport:m0,onViewportChange:Dy}),ru.jsx(nX,{onSelectionChange:t}),ku,ru.jsx(lX,{proOptions:D$,position:vr}),ru.jsx(fX,{rfId:Ir,disableKeyboardA11y:Jr})]})})}var $z=mN(tY);var yh=_r.error014();function SY({dimensions:u,lineWidth:f,variant:l,className:r}){return ru.jsx("path",{strokeWidth:f,d:`M${u[0]/2} 0 V${u[1]} M0 ${u[1]/2} H${u[0]}`,className:Yf(["react-flow__background-pattern",l,r])})}function PY({radius:u,className:f}){return ru.jsx("circle",{cx:u,cy:u,r:u,className:Yf(["react-flow__background-pattern","dots",f])})}var O1;(function(u){u.Lines="lines",u.Dots="dots",u.Cross="cross"})(O1||(O1={}));var MY={[O1.Dots]:1,[O1.Lines]:1,[O1.Cross]:6},mY=(u)=>({transform:u.transform,patternId:`pattern-${u.rfId}`});function Az({id:u,variant:f=O1.Dots,gap:l=20,size:r,lineWidth:y=1,offset:i=0,color:_,bgColor:n,style:$,className:j,patternClassName:F}){let J=iu.useRef(null),{transform:U,patternId:q}=uf(mY,Kf),W=r||MY[f],G=f===O1.Dots,K=f===O1.Cross,Q=Array.isArray(l)?l:[l,l],N=[Q[0]*U[2]||1,Q[1]*U[2]||1],c=W*U[2],z=Array.isArray(i)?i:[i,i],w=K?[c,c]:N,H=[z[0]*U[2]||1+w[0]/2,z[1]*U[2]||1+w[1]/2],B=`${q}${u?u:""}`;return ru.jsxs("svg",{className:Yf(["react-flow__background",j]),style:{...$,...k4,"--xy-background-color-props":n,"--xy-background-pattern-color-props":_},ref:J,"data-testid":"rf__background",children:[ru.jsx("pattern",{id:B,x:U[0]%N[0],y:U[1]%N[1],width:N[0],height:N[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${H[0]},-${H[1]})`,children:G?ru.jsx(PY,{radius:c/2,className:F}):ru.jsx(SY,{dimensions:w,lineWidth:y,variant:f,className:F})}),ru.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${B})`})]})}Az.displayName="Background";var jz=iu.memo(Az);function pY(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:ru.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function CY(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:ru.jsx("path",{d:"M0 0h32v4.2H0z"})})}function xY(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:ru.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function RY(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:ru.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function hY(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:ru.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function h4({children:u,className:f,...l}){return ru.jsx("button",{type:"button",className:Yf(["react-flow__controls-button",f]),...l,children:u})}var bY=(u)=>({isInteractive:u.nodesDraggable||u.nodesConnectable||u.elementsSelectable,minZoomReached:u.transform[2]<=u.minZoom,maxZoomReached:u.transform[2]>=u.maxZoom,ariaLabelConfig:u.ariaLabelConfig});function Fz({style:u,showZoom:f=!0,showFitView:l=!0,showInteractive:r=!0,fitViewOptions:y,onZoomIn:i,onZoomOut:_,onFitView:n,onInteractiveChange:$,className:j,children:F,position:J="bottom-left",orientation:U="vertical","aria-label":q}){let W=Tf(),{isInteractive:G,minZoomReached:K,maxZoomReached:Q,ariaLabelConfig:N}=uf(bY,Kf),{zoomIn:c,zoomOut:z,fitView:w}=s7(),H=()=>{c(),i?.()},B=()=>{z(),_?.()},T=()=>{w(y),n?.()},X=()=>{W.setState({nodesDraggable:!G,nodesConnectable:!G,elementsSelectable:!G}),$?.(!G)};return ru.jsxs(I4,{className:Yf(["react-flow__controls",U==="horizontal"?"horizontal":"vertical",j]),position:J,style:u,"data-testid":"rf__controls","aria-label":q??N["controls.ariaLabel"],children:[f&&ru.jsxs(ru.Fragment,{children:[ru.jsx(h4,{onClick:H,className:"react-flow__controls-zoomin",title:N["controls.zoomIn.ariaLabel"],"aria-label":N["controls.zoomIn.ariaLabel"],disabled:Q,children:ru.jsx(pY,{})}),ru.jsx(h4,{onClick:B,className:"react-flow__controls-zoomout",title:N["controls.zoomOut.ariaLabel"],"aria-label":N["controls.zoomOut.ariaLabel"],disabled:K,children:ru.jsx(CY,{})})]}),l&&ru.jsx(h4,{className:"react-flow__controls-fitview",onClick:T,title:N["controls.fitView.ariaLabel"],"aria-label":N["controls.fitView.ariaLabel"],children:ru.jsx(xY,{})}),r&&ru.jsx(h4,{className:"react-flow__controls-interactive",onClick:X,title:N["controls.interactive.ariaLabel"],"aria-label":N["controls.interactive.ariaLabel"],children:G?ru.jsx(hY,{}):ru.jsx(RY,{})}),F]})}Fz.displayName="Controls";var Jz=iu.memo(Fz);function vY({id:u,x:f,y:l,width:r,height:y,style:i,color:_,strokeColor:n,strokeWidth:$,className:j,borderRadius:F,shapeRendering:J,selected:U,onClick:q}){let{background:W,backgroundColor:G}=i||{},K=_||W||G;return ru.jsx("rect",{className:Yf(["react-flow__minimap-node",{selected:U},j]),x:f,y:l,rx:F,ry:F,width:r,height:y,style:{fill:K,stroke:n,strokeWidth:$},shapeRendering:J,onClick:q?(Q)=>q(Q,u):void 0})}var IY=iu.memo(vY),kY=(u)=>u.nodes.map((f)=>f.id),I7=(u)=>u instanceof Function?u:()=>u;function gY({nodeStrokeColor:u,nodeColor:f,nodeClassName:l="",nodeBorderRadius:r=5,nodeStrokeWidth:y,nodeComponent:i=IY,onClick:_}){let n=uf(kY,Kf),$=I7(f),j=I7(u),F=I7(l),J=typeof window>"u"||!!window.chrome?"crispEdges":"geometricPrecision";return ru.jsx(ru.Fragment,{children:n.map((U)=>ru.jsx(aY,{id:U,nodeColorFunc:$,nodeStrokeColorFunc:j,nodeClassNameFunc:F,nodeBorderRadius:r,nodeStrokeWidth:y,NodeComponent:i,onClick:_,shapeRendering:J},U))})}function sY({id:u,nodeColorFunc:f,nodeStrokeColorFunc:l,nodeClassNameFunc:r,nodeBorderRadius:y,nodeStrokeWidth:i,shapeRendering:_,NodeComponent:n,onClick:$}){let{node:j,x:F,y:J,width:U,height:q}=uf((W)=>{let G=W.nodeLookup.get(u);if(!G)return{node:void 0,x:0,y:0,width:0,height:0};let K=G.internals.userNode,{x:Q,y:N}=G.internals.positionAbsolute,{width:c,height:z}=i0(K);return{node:K,x:Q,y:N,width:c,height:z}},Kf);if(!j||j.hidden||!V7(j))return null;return ru.jsx(n,{x:F,y:J,width:U,height:q,style:j.style,selected:!!j.selected,className:r(j),color:f(j),borderRadius:y,strokeColor:l(j),strokeWidth:i,shapeRendering:_,onClick:$,id:j.id})}var aY=iu.memo(sY),oY=iu.memo(gY),dY=200,eY=150,uD=(u)=>!u.hidden,fD=(u)=>{let f={x:-u.transform[0]/u.transform[2],y:-u.transform[1]/u.transform[2],width:u.width/u.transform[2],height:u.height/u.transform[2]};return{viewBB:f,boundingRect:u.nodeLookup.size>0?H7(Ri(u.nodeLookup,{filter:uD}),f):f,rfId:u.rfId,panZoom:u.panZoom,translateExtent:u.translateExtent,flowWidth:u.width,flowHeight:u.height,ariaLabelConfig:u.ariaLabelConfig}},lD="react-flow__minimap-desc";function Uz({style:u,className:f,nodeStrokeColor:l,nodeColor:r,nodeClassName:y="",nodeBorderRadius:i=5,nodeStrokeWidth:_,nodeComponent:n,bgColor:$,maskColor:j,maskStrokeColor:F,maskStrokeWidth:J,position:U="bottom-right",onClick:q,onNodeClick:W,pannable:G=!1,zoomable:K=!1,ariaLabel:Q,inversePan:N,zoomStep:c=1,offsetScale:z=5}){let w=Tf(),H=iu.useRef(null),{boundingRect:B,viewBB:T,rfId:X,panZoom:R,translateExtent:Y,flowWidth:P,flowHeight:t,ariaLabelConfig:O}=uf(fD,Kf),M=u?.width??dY,S=u?.height??eY,b=B.width/M,Z=B.height/S,D=Math.max(b,Z),I=D*M,k=D*S,h=z*D,o=B.x-(I-B.width)/2-h,s=B.y-(k-B.height)/2-h,x=I+h*2,uu=k+h*2,nu=`${lD}-${X}`,$u=iu.useRef(0),Fu=iu.useRef();$u.current=D,iu.useEffect(()=>{if(H.current&&R)return Fu.current=bc({domNode:H.current,panZoom:R,getTransform:()=>w.getState().transform,getViewScale:()=>$u.current}),()=>{Fu.current?.destroy()}},[R]),iu.useEffect(()=>{Fu.current?.update({translateExtent:Y,width:P,height:t,inversePan:N,pannable:G,zoomStep:c,zoomable:K})},[G,K,N,c,Y,P,t]);let Ku=q?(d)=>{let[e,cu]=Fu.current?.pointer(d)||[0,0];q(d,{x:e,y:cu})}:void 0,Wu=W?iu.useCallback((d,e)=>{let cu=w.getState().nodeLookup.get(e).internals.userNode;W(d,cu)},[]):void 0,m=Q??O["minimap.ariaLabel"];return ru.jsx(I4,{position:U,style:{...u,"--xy-minimap-background-color-props":typeof $==="string"?$:void 0,"--xy-minimap-mask-background-color-props":typeof j==="string"?j:void 0,"--xy-minimap-mask-stroke-color-props":typeof F==="string"?F:void 0,"--xy-minimap-mask-stroke-width-props":typeof J==="number"?J*D:void 0,"--xy-minimap-node-background-color-props":typeof r==="string"?r:void 0,"--xy-minimap-node-stroke-color-props":typeof l==="string"?l:void 0,"--xy-minimap-node-stroke-width-props":typeof _==="number"?_:void 0},className:Yf(["react-flow__minimap",f]),"data-testid":"rf__minimap",children:ru.jsxs("svg",{width:M,height:S,viewBox:`${o} ${s} ${x} ${uu}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":nu,ref:H,onClick:Ku,children:[m&&ru.jsx("title",{id:nu,children:m}),ru.jsx(oY,{onClick:Wu,nodeColor:r,nodeStrokeColor:l,nodeBorderRadius:i,nodeClassName:y,nodeStrokeWidth:_,nodeComponent:n}),ru.jsx("path",{className:"react-flow__minimap-mask",d:`M${o-h},${s-h}h${x+h*2}v${uu+h*2}h${-x-h*2}z - M${T.x},${T.y}h${T.width}v${T.height}h${-T.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}Uz.displayName="MiniMap";var ih=iu.memo(Uz),rD=(u)=>(f)=>u?`${Math.max(1/f.transform[2],1)}`:void 0,yD={[H1.Line]:"right",[H1.Handle]:"bottom-right"};function iD({nodeId:u,position:f,variant:l=H1.Handle,className:r,style:y=void 0,children:i,color:_,minWidth:n=10,minHeight:$=10,maxWidth:j=Number.MAX_VALUE,maxHeight:F=Number.MAX_VALUE,keepAspectRatio:J=!1,resizeDirection:U,autoScale:q=!0,shouldResize:W,onResizeStart:G,onResize:K,onResizeEnd:Q}){let N=RN(),c=typeof u==="string"?u:N,z=Tf(),w=iu.useRef(null),H=l===H1.Handle,B=uf(iu.useCallback(rD(H&&q),[H,q]),Kf),T=iu.useRef(null),X=f??yD[l];iu.useEffect(()=>{if(!w.current||!c)return;if(!T.current)T.current=sc({domNode:w.current,nodeId:c,getStoreItems:()=>{let{nodeLookup:Y,transform:P,snapGrid:t,snapToGrid:O,nodeOrigin:M,domNode:S}=z.getState();return{nodeLookup:Y,transform:P,snapGrid:t,snapToGrid:O,nodeOrigin:M,paneDomNode:S}},onChange:(Y,P)=>{let{triggerNodeChanges:t,nodeLookup:O,parentLookup:M,nodeOrigin:S}=z.getState(),b=[],Z={x:Y.x,y:Y.y},D=O.get(c);if(D&&D.expandParent&&D.parentId){let I=D.origin??S,k=Y.width??D.measured.width??0,h=Y.height??D.measured.height??0,o={id:D.id,parentId:D.parentId,rect:{width:k,height:h,...X7({x:Y.x??D.position.x,y:Y.y??D.position.y},{width:k,height:h},D.parentId,O,I)}},s=m4([o],O,M,S);b.push(...s),Z.x=Y.x?Math.max(I[0]*k,Y.x):void 0,Z.y=Y.y?Math.max(I[1]*h,Y.y):void 0}if(Z.x!==void 0&&Z.y!==void 0){let I={id:c,type:"position",position:{...Z}};b.push(I)}if(Y.width!==void 0&&Y.height!==void 0){let k={id:c,type:"dimensions",resizing:!0,setAttributes:!U?!0:U==="horizontal"?"width":"height",dimensions:{width:Y.width,height:Y.height}};b.push(k)}for(let I of P){let k={...I,type:"position"};b.push(k)}t(b)},onEnd:({width:Y,height:P})=>{let t={id:c,type:"dimensions",resizing:!1,dimensions:{width:Y,height:P}};z.getState().triggerNodeChanges([t])}});return T.current.update({controlPosition:X,boundaries:{minWidth:n,minHeight:$,maxWidth:j,maxHeight:F},keepAspectRatio:J,resizeDirection:U,onResizeStart:G,onResize:K,onResizeEnd:Q,shouldResize:W}),()=>{T.current?.destroy()}},[X,n,$,j,F,J,G,K,Q,W]);let R=X.split("-");return ru.jsx("div",{className:Yf(["react-flow__resize-control","nodrag",...R,l,r]),ref:w,style:{...y,scale:B,..._&&{[H?"backgroundColor":"borderColor"]:_}},children:i})}var _h=iu.memo(iD);var L=Y1.default.createElement,{useEffect:$0}=Y1.default,xl=Y1.default.useState,X1=Y1.default.useRef,z$=[{id:"in-left",side:"left",position:zu.Left,style:{top:"50%"}},{id:"in-top-left",side:"top",slot:"left",slotIndex:-1,position:zu.Top,style:{left:"28%"}},{id:"in-top-mid",side:"top",slot:"mid",slotIndex:0,position:zu.Top,style:{left:"50%"}},{id:"in-top-right",side:"top",slot:"right",slotIndex:1,position:zu.Top,style:{left:"72%"}},{id:"in-bottom-left",side:"bottom",slot:"left",slotIndex:-1,position:zu.Bottom,style:{left:"28%"}},{id:"in-bottom-mid",side:"bottom",slot:"mid",slotIndex:0,position:zu.Bottom,style:{left:"50%"}},{id:"in-bottom-right",side:"bottom",slot:"right",slotIndex:1,position:zu.Bottom,style:{left:"72%"}}],q$=[{id:"out-right",position:zu.Right,style:{top:"50%"}}],Qz=["#4eb7a8","#d7a13a","#69aee8","#e0835f","#b7d86b","#d98bd2","#5fc6bf"],ai=236,oi=88,qz=15000,_D=10,o7=96,_0=72,d7=64,Wz=12;function g4(){return typeof document>"u"||document.visibilityState!=="hidden"}function cz(u,f){let l=Number.parseFloat(String(u||""));return Number.isFinite(l)?l/100:f}function nD(u,f,l){let r=String(u.side||"");if(r!=="top"&&r!=="bottom")return 0;let y=Number(u.slotIndex||0),i=r==="top"?"in-top-mid":"in-bottom-mid",_=f.get(u.id)||0,n=f.get(i)||0;if(y===0)return n===0?-26:28+_*74;let $=l===0?Math.abs(y)*2:Math.sign(l)===Math.sign(y)?-3:3;if(n>0&&_===0)return-14+$;return 8+_*74+$}function s4(u){let f=u.filter((i,_)=>{let n=u[_-1];return!n||Math.abs(n.x-i.x)>0.5||Math.abs(n.y-i.y)>0.5});if(f.length<2)return"";let l=`M ${f[0].x},${f[0].y}`,r=f[0];for(let i=1;i0.5||Math.abs(U.y-r.y)>0.5)l+=` L ${U.x},${U.y}`;l+=` Q ${n.x},${n.y} ${q.x},${q.y}`,r=q}let y=f[f.length-1];return`${l} L ${y.x},${y.y}`}function mz(u,f,l,r,y,i,_=""){let n=l>=u,$=Math.max(1,Math.abs(l-u)),j=Math.abs(r-f),F=Math.max(34,Math.min(118,$*0.26)),J=Math.min(280,Math.abs(i));if(n&&y===zu.Left&&J<4&&j<28&&$<420)return`M ${u},${f} C ${u+F},${f} ${l-F},${r} ${l},${r}`;if(n&&y===zu.Left&&(_==="direct-forward-left"||$<=260&&j<=210)){let Q=Math.max(42,Math.min(140,$*0.48)),N=Math.max(-28,Math.min(28,i*0.18));return`M ${u},${f} C ${u+Q},${f+N} ${l-Q},${r} ${l},${r}`}if(n){let Q=u+F;if(y===zu.Top||y===zu.Bottom){let z=y===zu.Top?-1:1,w=r+z*(54+J*0.42);return s4([{x:u,y:f},{x:Q,y:f},{x:Q+Math.min(120,$*0.18),y:w},{x:l,y:w},{x:l,y:r+z*34},{x:l,y:r}])}let N=l-F,c=(f+r)/2+i;return s4([{x:u,y:f},{x:Q,y:f},{x:Q+Math.min(110,$*0.16),y:c},{x:N-Math.min(90,$*0.12),y:c},{x:N,y:r},{x:l,y:r}])}let W=y===zu.Bottom?1:y===zu.Top?-1:i>=0?1:-1,G=Math.max(u,l)+92+Math.min(180,J*0.52),K=W<0?Math.min(f,r)-84-J*0.62:Math.max(f,r)+84+J*0.62;if(y===zu.Top||y===zu.Bottom)return s4([{x:u,y:f},{x:u+F,y:f},{x:G,y:K},{x:l,y:K},{x:l,y:r+W*38},{x:l,y:r}]);return s4([{x:u,y:f},{x:u+F,y:f},{x:G,y:K},{x:l-F,y:K},{x:l-F,y:r},{x:l,y:r}])}function $D({data:u}){return L("div",{className:"pipeline-flow-node-body"},z$.map((f)=>L(Zy,{key:f.id,id:f.id,type:"target",position:f.position,isConnectable:!1,className:`pipeline-flow-handle input ${f.side} slot-${f.slot||"mid"}`,style:f.style})),q$.map((f)=>L(Zy,{key:f.id,id:f.id,type:"source",position:f.position,isConnectable:!1,className:"pipeline-flow-handle output right",style:f.style})),u?.label)}function AD({id:u,sourceX:f,sourceY:l,targetX:r,targetY:y,targetPosition:i,markerEnd:_,markerStart:n,style:$,data:j}){let F=Number(j?.laneOffset||0),J=mz(f,l,r,y,i,F,String(j?.routeMode||""));return L(si,{id:u,path:J,markerEnd:_,markerStart:n,style:$,interactionWidth:28})}var jD={pipelineCurve:AD},FD={pipelineNode:$D};function e4(u){if(!u)return"--";let f=new Date(u);if(Number.isNaN(f.getTime()))return"--";return qf(f)}function Er(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";let l=Math.round(f/1000);if(l<60)return`${l}s`;if(l<3600)return`${Math.floor(l/60)}m ${l%60}s`;return`${Math.floor(l/3600)}h ${Math.floor(l%3600/60)}m`}function e7(u){let f=Number(u);if(!Number.isFinite(f))return"--";return f.toLocaleString("zh-CN")}function Nz(u){let f=Number(u);if(!Number.isFinite(f))return"--";return`${Math.round(Math.max(0,Math.min(1,f))*100)}%`}function Yu(u){return typeof u==="object"&&u!==null&&!Array.isArray(u)}function Hu(u){return Array.isArray(u)?u:[]}function hu(u){if(!u)return null;let f=new Date(u);return Number.isNaN(f.getTime())?null:f.getTime()}function G$(u){return Number.isFinite(Number(u))?new Date(Number(u)).toISOString():""}function w$(...u){for(let f of u){let l=hu(f);if(l!==null)return new Date(l).toISOString()}return""}function Uj(...u){let f=u.map(hu).filter((l)=>l!==null);return f.length>0?new Date(Math.max(...f)).toISOString():""}function Qj(u){return["succeeded","failed","skipped","cancelled","canceled","completed"].includes(String(u||"").toLowerCase())}function pz(u){let f=Rz(u).toLowerCase();return["running","active","in-progress","in_progress"].includes(f)}function zz(u,f="status"){return u.reduce((l,r)=>{let y=String(r?.[f]||"unknown").toLowerCase();return l[y]=(l[y]||0)+1,l},{})}function Cz(u){if(!u||typeof u!=="string")return null;try{let f=JSON.parse(u);return Yu(f)?f:null}catch{return null}}function uj(u){let f=u.map(Cz).filter((i)=>Boolean(i)),l=f.flatMap((i)=>[i.timestamp,i.createdAt,i.updatedAt]).filter(Boolean),r=Uj(...l),y=Array.from(new Set(f.map((i)=>String(i.event||i.action||i.type||"")).filter(Boolean))).slice(0,3);return{total:u.length,parsed:f.length,lastAt:r,eventKinds:y}}function u8(u){if(u===null||u===void 0)return"--";if(typeof u==="boolean")return u?"是":"否";if(typeof u==="number")return String(u);if(typeof u==="string")return u.length>80?`${u.slice(0,77)}...`:u;if(Array.isArray(u))return`${u.length} 项`;if(typeof u==="object")return`${Object.keys(u).length} 字段`;return String(u)}function xz(u,f=280){if(u===null||u===void 0)return"";let r=(typeof u==="string"?u:String(u)).replace(/\r\n/gu,` -`).trim();return r.length>f?`${r.slice(0,Math.max(0,f-1))}...`:r}function Rz(u){if(typeof u==="string")return u;if(Yu(u))return String(u.status||u.state||u.phase||"unknown");return"unknown"}function JD(u){return u.filter((f)=>f&&f.value!==void 0&&f.value!==null&&String(f.value)!=="")}function _j({items:u}){let f=JD(Hu(u));return L("div",{className:"pipeline-kv-grid"},f.map((l)=>L("span",{key:l.label},L("b",null,l.label),L("span",null,l.value))))}function qj({items:u}){let f=Hu(u).map((l)=>String(l||"")).filter(Boolean);if(f.length===0)return null;return L("div",{className:"pipeline-chip-row"},f.map((l,r)=>L("span",{key:`${r}-${l}`},l)))}function nj(u,f){let l=String(f?.procedureRunId||""),r=Hu(u?.procedureRuns);return r.find((y)=>String(Tr(y))===l)||r.at(-1)||null}function UD(u,f){let l=String(f||"");if(!l)return null;return Hu(u?.procedureRuns).find((r)=>Tr(r)===l)||null}function fj(u){return Hu(u?.attempts).length}function Gz(u){return Hu(u?.attempts).reduce((f,l)=>f+n8(l).length,0)}function n8(u){return Hu(u?.opencodeMessages?.steps).filter(Yu)}function hz(u){let f=String(u?.status||"").toLowerCase();if(["error","failed","failure"].includes(f))return"failed";if(["completed","succeeded","success"].includes(f))return"succeeded";if(["running","started","in_progress"].includes(f))return"running";return"unknown"}function QD(u,f){let l=$j(u.map((i)=>i?.agent)).slice(0,3),r=$j(u.map((i)=>i?.model)).slice(0,3),y=f.length<=2?f.map((i)=>`session ${i}`):[`sessions ${f.length}`,...f.slice(0,2).map((i)=>`session ${i}`)];return[...l.map((i)=>`agent ${i}`),...r.map((i)=>`model ${i}`),...y]}function W$(u,f=0){return String(u?.messageId||u?.index||"")||`step-${f}`}function qD({steps:u,sessionIds:f,sessionFacts:l,matchedStepKey:r}){let y=Hu(u),i=y.findIndex((K,Q)=>W$(K,Q)===r),_=i>=0?y[i]:null,n=y.flatMap((K)=>[hu(K?.createdAt),hu(K?.completedAt)]).filter((K)=>K!==null),$=n.length>0?Math.min(...n):null,j=n.length>0?Math.max(...n):null,F=$!==null&&j!==null?Math.max(0,j-$):null,J=y.reduce((K,Q)=>K+Hu(Q?.parts).filter((N)=>String(N?.type||"").toLowerCase()==="tool").length,0),U=y.reduce((K,Q)=>K+Hu(Q?.parts).filter((N)=>["text","reasoning"].includes(String(N?.type||"").toLowerCase())).length,0),q=y.reduce((K,Q)=>K+Hu(Q?.parts).filter((N)=>String(N?.type||"").toLowerCase()==="tool"&&hz(N)==="failed").length,0),W=[`${y.length} steps`,`${f.length} sessions`,`${U} messages`,`${J} tools`,F!==null?`duration ${Er(F)}`:"",q>0?`${q} failed tools`:""].filter(Boolean),G=_?[`Step ${_?.index??i+1}`,String(_?.role||"role --"),_?.model?`model ${_.model}`:"",_?.finish?`finish ${_.finish}`:"",_?.durationMs!==void 0&&_?.durationMs!==null?`duration ${Er(_.durationMs)}`:""].filter(Boolean):[];return L("section",{className:"pipeline-trace-timeline","data-testid":"pipeline-step-timeline"},L("div",{className:"pipeline-trace-head"},L("div",null,L("b",null,"OpenCode Trace"),L("span",null,"Trace 使用 Code Queue 统一样式展示完整 agent loop;Pipeline 旧 step/message/tool 卡片样式已废弃。")),L("div",{className:"pipeline-trace-session-head","data-testid":"pipeline-step-timeline-session"},L("span",null,W.join(" / ")||"Trace"),l.length>0?L(qj,{items:l}):null)),_?L("div",{className:"pipeline-trace-focus","data-testid":"pipeline-trace-matched-step"},L("span",{className:"codex-output-channel"},"Matched"),L("strong",null,`Gantt selection -> ${G.join(" / ")}`),L("time",null,`${e4(_?.createdAt)} -> ${e4(_?.completedAt)}`)):null,L(H6,{port:Wq,input:y,className:"codex-transcript pipeline-trace",testId:"pipeline-opencode-step-trace",emptyText:"暂无 OpenCode Trace 输出",keepRecentToolCalls:3}))}function c$(u){return Hu(u).flatMap((f)=>{if(Yu(f))return[f];let l=Cz(f);return l?[l]:[]})}function xr(u){return String(u?.event||u?.action||u?.requestedAction||u?.type||"").toLowerCase()}function Hy(u){return w$(u?.timestamp,u?.createdAt,u?.updatedAt,u?.startedAt,u?.finishedAt)}function WD(u){return hu(Hy(u))}function $8(u){return String(u?.attempt||u?.id||"")}function $j(u){let f=new Set,l=[];for(let r of u){let y=String(r||"");if(!y||f.has(y))continue;f.add(y),l.push(y)}return l}function Kz(u){switch(String(u||"").toLowerCase()){case"monitor":return"monitor";case"webui":return"webui";case"cli":return"cli";case"system":return"runner";default:return String(u||"--")}}function Oy(u){return String(u?.requestedAction||u?.action||"").toLowerCase()}function N$(u){switch(Oy(u)){case"guide":return"引导";case"modify":return"修改";case"approve":return"审核通过";case"restart":return"重启";case"redo":return"重做";default:return String(u?.requestedAction||u?.action||"控制")}}function Lz(u){switch(xr(u)){case"initial-prompt-delivered":return"初始 prompt";case"append-prompt-delivered":return"追加 prompt";case"append-prompt-queued":return"追加 prompt 已排队";case"monitor-prompt-delivered":return"Monitor prompt";case"node-long-running-observation":return"长任务观察";case"node-finished":return"节点完成";case"oa-policy-downstream-evaluated":return"OA 下游策略";case"control-command-queued":return`${N$(u)} 已发起`;case"control-command-applied":return`${N$(u)} 已生效`;case"control-command-ignored":return`${N$(u)} 已忽略`;default:return String(u?.event||u?.action||u?.requestedAction||"event")}}function wz(u){return xz(u?.promptPreview||u?.reasonPreview||u?.prompt||u?.reason||"",240)}function cD(u){let f=String(u?.prompt||""),l=String(u?.reason||u?.restartReason||""),r=f?"":String(u?.promptPreview||""),y=l?"":String(u?.reasonPreview||"");return[f||r?{label:f?"prompt":"prompt preview",value:f||r}:null,l||y?{label:l?"reason":"reason preview",value:l||y}:null,Hu(u?.resetNodeIds).length>0?{label:"reset nodes",value:Hu(u.resetNodeIds).join(", ")}:null,Hu(u?.runningResetNodeIds).length>0?{label:"interrupted running nodes",value:Hu(u.runningResetNodeIds).join(", ")}:null,Hu(u?.interruptedProcedureRunIds).length>0?{label:"interrupted procedures",value:Hu(u.interruptedProcedureRunIds).join(", ")}:null,u?.interruptedProcedureRunId?{label:"interrupted procedure",value:String(u.interruptedProcedureRunId)}:null].filter(Boolean)}function lj(u){let f=n8(u),l=f.map(($)=>hu($?.createdAt)).filter(($)=>$!==null),r=f.map(($)=>hu($?.completedAt)??hu($?.createdAt)).filter(($)=>$!==null),y=c$(u?.controlEventRecords).map(($)=>WD($)).filter(($)=>$!==null),i=Hu(u?.assistantOutputs).map(($)=>hu($?.updatedAt)).filter(($)=>$!==null),_=l[0]??y[0]??i[0]??null,n=r.at(-1)??y.at(-1)??i.at(-1)??_;return{startMs:_,endMs:n}}function ND(u,f,l,r,y=""){let i=Hu(u?.procedureRuns).filter((n)=>A8(n,f)===l);if(i.length===0)return null;if(y){let n=i.find(($)=>Tr($)===y);if(n)return n}if(r===null)return i.at(-1)||null;let _=i.find((n)=>{let $=hu(a4(n,u)),j=hu(o4(n,u))??$;return $!==null&&j!==null&&r>=$-1000&&r<=j+1000});if(_)return _;return i.slice().sort((n,$)=>{let j=hu(a4(n,u))??r,F=hu(o4(n,u))??j,J=hu(a4($,u))??r,U=hu(o4($,u))??J,q=Math.min(Math.abs(j-r),Math.abs(F-r)),W=Math.min(Math.abs(J-r),Math.abs(U-r));return q-W})[0]||null}function bz(u,f){let l=Hu(u?.attempts).filter(Yu);if(l.length===0)return null;let r=String(f?.attempt||"");if(r){let _=l.find((n)=>$8(n)===r);if(_)return _}let y=Number.isFinite(Number(f?.ms))?Number(f.ms):null;if(y===null)return l.at(-1)||null;let i=l.find((_)=>{let n=lj(_);return Number.isFinite(n.startMs)&&Number.isFinite(n.endMs)&&y>=Number(n.startMs)-1000&&y<=Number(n.endMs)+1000});if(i)return i;return l.slice().sort((_,n)=>{let $=lj(_),j=lj(n),F=Math.min(Math.abs(Number($.startMs??y)-y),Math.abs(Number($.endMs??y)-y)),J=Math.min(Math.abs(Number(j.startMs??y)-y),Math.abs(Number(j.endMs??y)-y));return F-J})[0]||l.at(-1)||null}function vz(u,f){let l=n8(u);if(l.length===0)return{step:null,stepIndex:-1,stepKey:""};if(f===null){let i=l[0];return{step:i,stepIndex:0,stepKey:W$(i,0)}}for(let i=0;i=n-1000&&f<=$+1000)return{step:_,stepIndex:i,stepKey:W$(_,i)}}let r=l.findIndex((i)=>{let _=hu(i?.createdAt)??hu(i?.completedAt);return _!==null&&_>=f});if(r>=0){let i=l[r];return{step:i,stepIndex:r,stepKey:W$(i,r)}}let y=Math.max(0,l.length-1);return{step:l[y],stepIndex:y,stepKey:W$(l[y],y)}}function zD(u,f){let l=String(f?.runId||u?.runId||"");if(String(f?.mode||"")==="interval"){let j=f?.interval||{},F=nj(u,j)||j.raw||{};return{mode:"interval",runId:l,interval:j,marker:null,nodeId:String(j?.nodeId||A8(F,l)||""),procedure:F,attempt:null,matchedStep:null,matchedStepIndex:-1,matchedStepKey:""}}let r=Yu(f?.marker)?f.marker:{},y=Number.isFinite(Number(r?.ms))?Number(r.ms):null,i=String(r?.nodeId||""),_=i?ND(u,l,i,y,String(r?.procedureRunId||"")):null,n=_?bz(_,r):null,$=n?vz(n,y):{step:null,stepIndex:-1,stepKey:""};return{mode:"event",runId:l,interval:null,marker:r,nodeId:i,procedure:_,attempt:n,matchedStep:$.step,matchedStepIndex:$.stepIndex,matchedStepKey:$.stepKey}}function GD({procedure:u,matchedStepKey:f="",matchedAttemptId:l=""}){let r=Hu(u?.attempts);if(r.length===0)return L($r,{title:"暂无 attempt 详情",text:"当前 procedure 还没有可展示的 attempt / OpenCode Trace;若刚点击甘特线,请等待 node 详情抓取完成。"});return r.map((y,i)=>{let _=y?.opencodeMessages||{},n=n8(y),$=Hu(_.sessionIds).map((U)=>String(U)).filter(Boolean),j=QD(n,$),F=$8(y)||`attempt-${i+1}`,J=n.reduce((U,q)=>U+Hu(q?.parts).filter((W)=>String(W?.type||"").toLowerCase()==="tool"&&hz(W)==="failed").length,0);return L("article",{key:F,className:`pipeline-attempt-card ${l===F?"matched":""}`},L("div",{className:"pipeline-attempt-head"},L("div",null,L("strong",null,F),L("span",null,_.source||"opencode")),L("div",{className:"pipeline-attempt-badges"},L("span",null,`${n.length} steps`),L("span",null,`${_.toolCallCount??"--"} tools`),J>0?L("span",{className:"danger"},`${J} failed`):null)),L(_j,{items:[{label:"messages",value:_.messageCount??"--"},{label:"steps",value:_.stepCount??n.length},{label:"tools",value:_.toolCallCount??"--"},{label:"updated",value:Nu(_.updatedAt)},{label:"sessions",value:$.join(", ")||"--"}]}),n.length===0?L("p",{className:"muted paragraph"},"当前 attempt 尚未返回 OpenCode Trace;请确认 D601 pipeline-control 已重建并重新抓取。"):L(qD,{steps:n,sessionIds:$,sessionFacts:j,matchedStepKey:f}))})}function rj(u,f){return`${u}::${f}`}function f8(u,f,l){if(!Yu(u))return null;return String(u.runId||"")===f&&String(u.nodeId||"")===l?u:null}function KD(u,f){let l=Yu(u)?u:{};if(!Yu(f))return l;let r=Hu(f.attempts),y=Hu(l.attempts);return{...l,...f,attempts:r.length>0?r:y}}function LD(u,f,l,r){if(!f8(f,l,r))return u;let y=Hu(f.procedureRuns),i=Yu(u)?u:{};return{...i,...f,controlCommands:Hu(f.controlCommands).length>0?f.controlCommands:i.controlCommands,controlEvents:Hu(f.controlEvents).length>0?f.controlEvents:i.controlEvents,procedureRuns:y.length>0?y:i.procedureRuns}}function wD({selection:u,runDetails:f,nodeDetails:l,nodeDetailsState:r,onRaw:y,onCollapse:i}){if(!u?.mode)return L("aside",{className:"pipeline-gantt-detail-panel empty","data-testid":"pipeline-gantt-detail-panel"},L("div",{className:"pipeline-gantt-detail-head"},L("div",null,L("span",{className:"panel-eyebrow"},"Gantt Detail"),L(Af,{title:"未选择元素",level:3})),L("button",{type:"button",className:"ghost-btn mini",onClick:i,"data-testid":"pipeline-gantt-sidebar-collapse"},"收起")),L($r,{title:"选择一条执行线或一个控制点",text:"点击甘特图中的 node 执行线、prompt 点或控制点,在这里查看结构化过程和 OpenCode step。"}));let _=String(u?.runId||""),n=String(u?.interval?.nodeId||u?.marker?.nodeId||""),$=f?.runId===_?f.details:null,j=f8(l,_,n),F=String(r?.runId||"")===_&&String(r?.nodeId||"")===n,J=LD($,j,_,n),U=(String(f?.runId||"")!==_||Boolean(f?.loading))&&!J,q=String(f?.runId||"")===_?String(f?.error||""):"",W=F?String(r?.error||""):"",G=J?zD(J,u):null,K=G?.interval||u?.interval||null,Q=G?.marker||u?.marker||null,N=String(K?.procedureRunId||Q?.procedureRunId||""),c=j?UD(j,N)||nj(j,K||{procedureRunId:N}):null,z=G?.procedure||(J?nj(J,K||{procedureRunId:N}):null)||K?.raw||{};if(c&&(fj(z)===0||Gz(c)>=Gz(z)))z=KD(z,c);let w=G?.attempt||null,H=String(G?.matchedStepKey||"");if(!w&&Q&&fj(z)>0)w=bz(z,Q),H=String(vz(w,Number.isFinite(Number(Q?.ms))?Number(Q.ms):null).stepKey||"");let B=$8(w),T=fj(z)>0,X=F&&Boolean(r?.loading)&&!T,R=Boolean(U||X),Y=[T?"":q,W].filter(Boolean).join(" / "),P=F&&r?.fetchedAt?r.fetchedAt:f?.fetchedAt,t=Rz(z?.status||K?.status||Q?.status||Q?.event),O=u?.mode==="event"?Q?.label||Lz(Q?.raw||Q)||"event":G?.nodeId||K?.nodeId||"node",M=Q?cD(Q?.raw||Q):[],S=Q?[xr(Q?.raw||Q)?`event ${xr(Q?.raw||Q)}`:"",Q?.promptEvent?`prompt ${Q.promptEvent}`:"",Q?.action?`action ${Q.action}`:"",Q?.sourceKind?`source ${Kz(Q.sourceKind)}`:"",Q?.sourceNodeId?`from ${Q.sourceNodeId}`:"",Q?.targetNodeId?`to ${Q.targetNodeId}`:"",Q?.snapReason?`draw ${Q.snapReason}`:""].filter(Boolean):[];return L("aside",{className:"pipeline-gantt-detail-panel","data-testid":"pipeline-gantt-detail-panel"},L("div",{className:"pipeline-gantt-detail-head"},L("div",null,L("span",{className:"panel-eyebrow"},u?.mode==="event"?"Gantt Event Detail":"Gantt Line Detail"),L(Af,{title:O,level:3,loading:R})),L("div",{className:"pipeline-gantt-detail-head-actions"},L(D1,{status:t},t),L("button",{type:"button",className:"ghost-btn mini",onClick:i,"data-testid":"pipeline-gantt-sidebar-collapse"},"收起"))),Q?L("article",{className:"pipeline-event-card"},L("div",{className:"pipeline-event-card-head"},L("strong",null,Q?.label||Lz(Q?.raw||Q)),L(qj,{items:S})),L(_j,{items:[{label:"event time",value:Nu(Q?.timestampIso||Q?.timestamp||"--")},Q?.snapped?{label:"drawn time",value:Nu(Q?.renderedTimestampIso||Q?.ms)}:null,{label:"node",value:Q?.nodeId||"--"},{label:"procedure",value:Q?.procedureRunId||Tr(z)||"--"},{label:"attempt",value:Q?.attempt||B||"--"},{label:"source kind",value:Q?.sourceKind?Kz(Q.sourceKind):"--"},{label:"source node",value:Q?.sourceNodeId||"--"},{label:"target node",value:Q?.targetNodeId||"--"},{label:"command",value:Q?.commandId||Q?.eventId||"--"},Q?.snapReason?{label:"placement",value:Q.snapReason}:null]}),M.length>0?L("div",{className:"pipeline-event-blocks"},M.map((b,Z)=>L("section",{key:`${b.label}-${Z}`,className:"pipeline-event-text-block"},L("b",null,b.label),L("p",null,b.value)))):null,wz(Q?.raw||Q)?L("p",{className:"pipeline-text-preview"},wz(Q?.raw||Q)):null):null,L(_j,{items:[{label:"epoch",value:_||K?.runId||"--"},{label:"node",value:G?.nodeId||K?.nodeId||Q?.nodeId||"--"},{label:"procedure",value:K?.procedureRunId||Q?.procedureRunId||Tr(z)||"--"},{label:"started",value:Nu(K?.startedAt||z?.startedAt)},{label:"finished",value:Nu(K?.finishedAt||z?.finishedAt)},{label:"duration",value:Er(K?.durationMs||z?.durationMs)},{label:"fetched",value:P?qf(P):"--"},G?.matchedStep?{label:"matched step",value:`Step ${G.matchedStep.index??G.matchedStepIndex+1}`}:null]}),L(jf,{error:Y}),L("div",{className:"pipeline-gantt-detail-actions"},L(Rr,{title:`Procedure ${K?.procedureRunId||Q?.procedureRunId||G?.nodeId||"node"}`,data:z,onOpen:y,testId:"raw-pipeline-gantt-procedure"}),Q?L(Rr,{title:`Pipeline event ${Q?.id||Q?.commandId||Q?.eventId||G?.nodeId||"event"}`,data:Q?.raw||Q,onOpen:y,testId:"raw-pipeline-gantt-event"}):null,J?L(Rr,{title:`Pipeline run ${_||"--"}`,data:J,onOpen:y,testId:"raw-pipeline-gantt-node-details"}):null),!R&&!Tr(z)&&!Q?L($r,{title:"暂无过程详情",text:"当前选择还没有可匹配的 procedure 运行记录。"}):null,!R&&Tr(z)?L(GD,{procedure:z,matchedStepKey:H,matchedAttemptId:B}):null)}function ED({value:u}){let l=String(u||"--").split(/([_-])/u);return L(Y1.default.Fragment,null,l.map((r,y)=>r==="-"||r==="_"?L(Y1.default.Fragment,{key:y},r,L("wbr",null)):L(Y1.default.Fragment,{key:y},r)))}async function B1(u,f={}){return Xu(u,{invalidJsonPrefix:"Pipeline 返回了无效 JSON",...f})}function D1({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return L("span",{className:`status-badge ${l}`},f||u||"unknown")}function Hl({label:u,value:f,hint:l,tone:r}){return L("article",{className:`metric-card ${r||""}`},L("div",{className:"metric-label"},u),L("div",{className:"metric-value"},f),L("div",{className:"metric-hint"},l))}function n0({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return L("section",{className:`panel ${y||""}`},L("div",{className:"panel-head"},L("div",null,f?L("p",{className:"panel-eyebrow"},f):null,L(Af,{title:u,loading:i})),l?L("div",{className:"panel-actions"},l):null),L("div",{className:"panel-body"},r))}function Rr({title:u,data:f,onOpen:l,testId:r}){return L("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function wr({title:u,subtitle:f,facts:l,data:r,onRaw:y,testId:i}){let _=Hu(l).map((n)=>String(n||"")).filter(Boolean);return L("article",{className:"pipeline-evidence-row"},L("div",{className:"pipeline-evidence-main"},L("strong",null,u),f?L("span",null,f):null),L("div",{className:"pipeline-evidence-facts"},_.map((n,$)=>L("span",{key:`${$}-${n.slice(0,16)}`},n))),r!==void 0?L(Rr,{title:u,data:r,onOpen:y,testId:i}):null)}function $r({title:u,text:f}){return L("div",{className:"empty-state"},L("strong",null,u),L("span",null,f))}function TD(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function ZD(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function HD(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function OD(u){return{components:Array.isArray(u?.registry?.components)?u.registry.components:[],pipelines:Array.isArray(u?.pipelines)?u.pipelines:[],runs:Array.isArray(u?.runs)?u.runs:[]}}function Ez(u,f,l){let r=u?._unidesk?.arrayLimits?.[f],y=Number(r?.originalLength);return Number.isFinite(y)?y:l}function Iz(u){if(!u||typeof u!=="object"||Array.isArray(u))return"--";return`${u.componentClass||"--"}/${u.id||"--"}`}function l8(u){if(!u||typeof u!=="object"||Array.isArray(u))return"";let f=String(u.componentClass||"").trim(),l=String(u.id||"").trim();return f&&l?`${f}/${l}`:""}function Wj(u){return u?.config&&typeof u.config==="object"&&!Array.isArray(u.config)?u.config:{}}function kz(u){let f=Wj(u),l=Array.isArray(f.nodes)?f.nodes:Array.isArray(u?.nodes)?u.nodes:[],r=new Map;for(let _ of l){let n=String(_?.id||_?.nodeId||"");if(n)r.set(n,{..._,id:n})}let y=cj(u),i=(_)=>{if(_&&!r.has(_))r.set(_,{id:_})};for(let _ of Nj(u))K$(_).forEach(i);for(let _ of y)i(String(_?.from||_?.source||"")),i(String(_?.to||_?.target||""));return Array.from(r.values())}function cj(u){let f=Wj(u);return Array.isArray(f.edges)?f.edges:Array.isArray(u?.edges)?u.edges:[]}function Nj(u){let f=Wj(u);return Array.isArray(f.topologicalBatches)?f.topologicalBatches:Array.isArray(u?.topologicalBatches)?u.topologicalBatches:[]}function BD(u){let f=new Map;for(let l of u){let r=l8(l);if(r)f.set(r,l);let y=Array.isArray(l?.refs)?l.refs:[];for(let i of y){let _=l8(i);if(_)f.set(_,l)}}return f}function Tz(u,f){let l=f.get(l8(u?.componentRef));if(l)return l;let r=l8({componentClass:u?.kind,id:u?.id});return r?f.get(r)||null:null}function Zz(u,f){let l=gz(u,f);return String(l?.status||"pending")}function gz(u,f){return(Array.isArray(u?.nodes)?u.nodes:[]).find((r)=>r?.nodeId===f||r?.id===f)||null}function VD(u){return u.reduce((f,l)=>{let r=String(l?.status||"unknown").toLowerCase();return f[r]=(f[r]||0)+1,f},{})}function XD(u){if(Array.isArray(u?.scorers))return u.scorers.filter(Yu);if(Array.isArray(u?.summary?.scorers))return u.summary.scorers.filter(Yu);if(Array.isArray(u?.artifact?.summary?.scorers))return u.artifact.summary.scorers.filter(Yu);return[]}function YD(u){if(Yu(u?.run))return u.run;if(Yu(u?.runSummary))return u.runSummary;return null}function DD(u,f){if(!Yu(u)&&!Yu(f))return null;if(!Yu(u))return f;if(!Yu(f))return u;return{...u,...f,request:Yu(u.request)||Yu(f.request)?{...Yu(u.request)?u.request:{},...Yu(f.request)?f.request:{}}:f.request??u.request,artifact:Yu(u.artifact)||Yu(f.artifact)?{...Yu(u.artifact)?u.artifact:{},...Yu(f.artifact)?f.artifact:{}}:f.artifact??u.artifact,summary:Yu(u.summary)||Yu(f.summary)?{...Yu(u.summary)?u.summary:{},...Yu(f.summary)?f.summary:{}}:f.summary??u.summary}}function r8(u){let f=XD(u),l=f.find((F)=>Yu(F?.score))||f[0]||null,r=Yu(l?.score)?l.score:{},y=Number(r.passed),i=Number(r.total),_=Number(r.ratio),n=Number.isFinite(_)?_:Number.isFinite(y)&&Number.isFinite(i)&&i>0?y/i:null,$=n===null?null:Math.round(Math.max(0,Math.min(100,n<=1?n*100:n))),j=String(r.text||(Number.isFinite(y)&&Number.isFinite(i)?`${y}/${i}`:""));return{scorer:l,scorers:f,score:r,passed:Number.isFinite(y)?y:null,total:Number.isFinite(i)?i:null,percent:$,text:j}}function Aj(u){let f=r8(u);return f.text||(f.scorers.length>0?String(f.scorer?.status||"pending"):"--")}function zj(u){let f=r8(u);if(f.total>0&&f.passed===f.total)return"succeeded";if(f.total>0&&f.passed>0)return"running";if(f.scorers.length>0)return"failed";return"pending"}function tD(u){return Array.isArray(u?.items)?u.items.filter(Yu):[]}function SD({run:u}){let f=Aj(u);return L("span",{className:`pipeline-score-badge ${zj(u)}`},`score ${f}`)}function PD({run:u,onRaw:f}){let r=r8(u).scorers;if(!u)return L($r,{title:"暂无评分",text:"选择一个 epoch 后会显示 scorer 结果。"});if(r.length===0)return L("div",{className:"pipeline-score-empty"},L("strong",null,"评分器等待中"),L("span",null,"DAG 完成后,Pipeline control backend 会把 scorer summary 追加到 run artifact,并通过 UniDesk 显示。"));return L("div",{className:"pipeline-score-board","data-testid":"pipeline-score-board"},r.map((y,i)=>{let _=r8({scorers:[y]}),n=tD(y),$=_.percent??0;return L("article",{key:`${y.scorerId||y.component||i}`,className:`pipeline-score-card ${zj({scorers:[y]})}`},L("div",{className:"pipeline-score-head"},L("div",null,L("span",null,y.scorerId||y.component||"scorer"),L("strong",null,_.text||y.status||"--")),L(D1,{status:y.status||"unknown"},y.status||"unknown")),L("div",{className:"pipeline-score-meter","aria-label":`score ${$}%`},L("span",{style:{width:`${$}%`}})),L("div",{className:"pipeline-score-facts"},L("span",null,`${$}%`),L("span",null,y.component||"--"),L("span",null,y.applicationCheckoutRef||"--")),n.length>0?L("div",{className:"pipeline-score-items"},n.map((j)=>L("span",{key:`${j.id||j.filter}`,className:`pipeline-score-item ${String(j.status||"").toLowerCase()}`,title:`${j.filter||"--"} / ran=${j.ran??"?"}`},L("b",null,j.id||"--"),L("small",null,j.status||"--")))):L("p",{className:"muted paragraph"},"当前 scorer 尚未返回 item 级结果。"),y.error?L("p",{className:"pipeline-score-error"},xz(y.error,360)):null,L("div",{className:"panel-actions inline-actions"},L(Rr,{title:`Scorer ${y.scorerId||i}`,data:y,onOpen:f,testId:"raw-pipeline-score"})))}))}function MD(u){let f=u.reduce((l,r)=>{let y=String(r?.componentClass||"unknown");return l[y]=(l[y]||0)+1,l},{});return Object.entries(f).map(([l,r])=>({name:l,count:Number(r)})).sort((l,r)=>r.count-l.count||l.name.localeCompare(r.name))}function K$(u){if(Array.isArray(u))return u.map((f)=>typeof f==="string"?f:String(f?.id||f?.nodeId||"")).filter(Boolean);if(Array.isArray(u?.nodes))return K$(u.nodes);if(Array.isArray(u?.nodeIds))return K$(u.nodeIds);return[]}function mD(u){return Yu(u?.instanceInputs?.monitor)?u.instanceInputs.monitor:{}}function sz(u,f){if(String(u?.kind||"").toLowerCase()!=="procedure")return!1;let l=mD(u);if(u?.instanceInputs?.monitorMode===!0||l.enabled===!0)return!0;let r=Iz(u?.componentRef);return String(f?.id||f?.config?.id||r||"").toLowerCase().includes("monitor")}function pD(u){return u.filter((f)=>sz(f)).map((f)=>String(f?.id||"")).filter(Boolean)}function CD(u,f){if(f.length===0)return u;let l=new Set(f),r=f.filter((y)=>u.includes(y));if(r.length===0)return u;return[...r,...u.filter((y)=>!l.has(y))]}function xD(u,f){if(f.length===0)return u;let l=new Set(f),r=f.filter((i)=>u.some((_)=>_.includes(i)));if(r.length===0)return u;let y=u.map((i)=>i.filter((_)=>!l.has(_))).filter((i)=>i.length>0);return[r,...y]}function RD(u,f,l){let y=Nj(u).map(K$).filter((U)=>U.length>0);if(y.length>0)return y;let i=f.map((U)=>String(U?.id||"")).filter(Boolean),_=new Set(i),n=new Map(i.map((U)=>[U,0])),$=new Map(i.map((U)=>[U,[]]));for(let U of l){let q=String(U?.from||U?.source||""),W=String(U?.to||U?.target||"");if(!_.has(q)||!_.has(W))continue;$.get(q)?.push(W),n.set(W,(n.get(W)||0)+1)}let j=new Map,F=i.filter((U)=>(n.get(U)||0)===0);for(let U of F)j.set(U,0);while(F.length>0){let U=F.shift(),q=(j.get(U)||0)+1;for(let W of $.get(U)||[])if(n.set(W,Math.max(0,(n.get(W)||0)-1)),j.set(W,Math.max(j.get(W)||0,q)),(n.get(W)||0)===0)F.push(W)}i.forEach((U)=>{if(!j.has(U))j.set(U,0)});let J=Math.max(0,...Array.from(j.values()));return Array.from({length:J+1},(U,q)=>i.filter((W)=>j.get(W)===q)).filter((U)=>U.length>0)}function hD(u,f,l){let y=Nj(u).map(K$).filter((n)=>n.length>0),i=y.length>0?y.flatMap((n)=>n):(()=>{let n=f.map((G)=>String(G?.id||"")).filter(Boolean),$=new Set(n),j=l.filter((G)=>String(G?.edgeType||"").toLowerCase()!=="rework"),F=new Map(n.map((G)=>[G,0])),J=new Map(n.map((G)=>[G,[]]));for(let G of j){let K=String(G?.from||G?.source||""),Q=String(G?.to||G?.target||"");if(!$.has(K)||!$.has(Q))continue;J.get(K)?.push(Q),F.set(Q,(F.get(Q)||0)+1)}let U=new Map,q=n.filter((G)=>(F.get(G)||0)===0);for(let G of q)U.set(G,0);while(q.length>0){let G=q.shift(),K=(U.get(G)||0)+1;for(let Q of J.get(G)||[])if(F.set(Q,Math.max(0,(F.get(Q)||0)-1)),U.set(Q,Math.max(U.get(Q)||0,K)),(F.get(Q)||0)===0)q.push(Q)}n.forEach((G)=>{if(!U.has(G))U.set(G,0)});let W=Math.max(0,...Array.from(U.values()));return Array.from({length:W+1},(G,K)=>n.filter((Q)=>U.get(Q)===K)).flatMap((G)=>G)})(),_=new Set(i);for(let n of f){let $=String(n?.id||"");if(!$||_.has($))continue;i.push($),_.add($)}return CD(i,pD(f))}function U$(u){return`${u.source}->${u.target}-${u.index}`}function Hz(u,f,l){let r=kz(u),y=cj(u),i=BD(l),_=new Map(r.map((t)=>[String(t?.id||""),t])),n=r.filter((t)=>sz(t,Tz(t,i))).map((t)=>String(t?.id||"")).filter(Boolean),$=xD(RD(u,r,y),n),j=[],F=new Map,J=330,U=122;$.forEach((t,O)=>{let M=t.length*122;t.forEach((S,b)=>{let Z=_.get(S)||{id:S},D=Tz(Z,i),I=Zz(f,S).toLowerCase(),k=String(Z.kind||D?.componentClass||"node").toLowerCase(),h=Iz(Z.componentRef||D),o=String(D?.config?.version||D?.version||""),s=String(D?.config?.description||D?.description||""),x=b*122-Math.floor(M/2);F.set(S,{column:O,row:b,y:x}),j.push({id:S,type:"pipelineNode",position:{x:O*330,y:x},data:{exportLabel:{id:S,kind:k,componentRef:h,componentVersion:o,componentDescription:s,status:I},label:L("div",{className:"flow-node-label"},L("strong",null,S),L("span",null,k),L("code",{title:s||h},o?`${h}@${o}`:h),L(D1,{status:I},I))},className:`pipeline-flow-node ${k} ${I}`})})});let q=y.flatMap((t,O)=>{let M=String(t?.from||t?.source||""),S=String(t?.to||t?.target||"");if(!_.has(M)||!_.has(S))return[];return[{source:M,target:S,index:O,condition:t?.condition,edgeType:t?.edgeType}]}),W=q.reduce((t,O)=>t.set(O.source,(t.get(O.source)||0)+1),new Map),G=q.reduce((t,O)=>t.set(O.target,(t.get(O.target)||0)+1),new Map),K=q.reduce((t,O)=>{let M=`${O.source}->${O.target}`;return t.set(M,(t.get(M)||0)+1)},new Map),Q=new Map,N=new Map,c=new Map,z=new Map,w=new Map,H=new Map,B=q.reduce((t,O)=>{let M=F.get(O.source),S=F.get(O.target),b=(S?.column||0)-(M?.column||0);if(b<=0||String(O.edgeType||"").toLowerCase()==="rework"||b!==1)return t;let D=`${O.source}->column:${S?.column??""}`,I=t.get(D)||[];return I.push(O),t.set(D,I),t},new Map);for(let t of B.values()){if(t.length<2)continue;t.slice().sort((O,M)=>{let S=F.get(O.target),b=F.get(M.target);return(S?.y||0)-(b?.y||0)||O.index-M.index}).forEach((O,M,S)=>{H.set(U$(O),{slot:M-(S.length-1)/2,count:S.length})})}[...q].sort((t,O)=>{let M=F.get(t.source),S=F.get(t.target),b=F.get(O.source),Z=F.get(O.target),D=Math.abs((S?.column||0)-(M?.column||0))*330+Math.abs((S?.y||0)-(M?.y||0)),I=Math.abs((Z?.column||0)-(b?.column||0))*330+Math.abs((Z?.y||0)-(b?.y||0));return D-I||t.index-O.index}).forEach((t)=>{let O=F.get(t.source)||{column:0,row:0,y:0},M=F.get(t.target)||{column:0,row:0,y:0},S=M.column-O.column,b=Math.max(0,S),Z=S<=0||String(t.edgeType||"").toLowerCase()==="rework",D=O.y-M.y,I=G.get(t.target)||1,k=H.has(U$(t)),h=!Z&&b<=1&&(k||I===1),o=w.get(t.target)||new Map;w.set(t.target,o);let s=z$.slice().sort((x,uu)=>{let nu=(Wu)=>{let m=String(Wu.side),d=0;if(Z){if(m==="left")d+=86;if(m==="top")d+=M.y<=0?-22:12;if(m==="bottom")d+=M.y>=0?-22:12;if(Math.abs(M.y)<12&&m!=="left")d+=t.index%2===0?m==="top"?-6:6:m==="bottom"?-6:6;return d}if(h){if(m==="left")d-=k?72:44;if(m!=="left")d+=k?72:44;return d+Math.abs(D)*0.02}if(m==="left")d+=b<=1?0:24;if(m==="top")d+=D<-36?-18:42;if(m==="bottom")d+=D>36?-18:42;if(b<=1&&Math.abs(D)<=82&&m!=="left")d+=38;if(b>1&&m!=="left")d-=10;return d},$u=O.y-M.y,Fu=$u!==0?$u:t.index%2===0?-1:1,Ku=(Wu)=>{let m=o.get(Wu.id)||0;return nu(Wu)+m*64+nD(Wu,o,Fu)};return Ku(x)-Ku(uu)||String(x.id).localeCompare(String(uu.id))})[0];o.set(s.id,(o.get(s.id)||0)+1),z.set(U$(t),s)});let X=q.map((t)=>{let O=Zz(f,t.target).toLowerCase(),M=`${t.source}->${t.target}`,S=Q.get(t.source)||0,b=N.get(t.target)||0,Z=c.get(M)||0;Q.set(t.source,S+1),N.set(t.target,b+1),c.set(M,Z+1);let D=S-((W.get(t.source)||1)-1)/2,I=b-((G.get(t.target)||1)-1)/2,k=Z-((K.get(M)||1)-1)/2,h=F.get(t.source),o=F.get(t.target),s=(o?.column||0)-(h?.column||0),x=Math.max(1,Math.abs(s)),uu=s<=0||String(t.edgeType||"").toLowerCase()==="rework",nu=Math.abs((o?.y||0)-(h?.y||0)),$u=H.get(U$(t)),Fu=!uu&&s===1&&(G.get(t.target)||0)>1,Ku=$u?$u.slot:k*2+D+I*0.45,Wu=Ku===0?t.index%2===0?-1:1:Math.sign(Ku),m=z.get(U$(t))||z$[1],d=m.side==="top"?-1:m.side==="bottom"?1:Wu,e=uu||x>1||nu>96||Math.abs(Ku)>0.2||m.side!=="left",cu=uu?118+x*18:22+x*16,g=m.side==="left"?0:28,Qu=e?Math.max(-280,Math.min(280,d*Math.min(180,cu+g+nu*0.22)+Ku*28)):0,Eu=Math.max(0,Math.min(q$.length-1,Math.round(D+(q$.length-1)/2))),Tu=q$[Eu]||q$[1],Du=O==="succeeded"?"var(--accent-2)":O==="running"?"var(--accent)":O==="failed"?"var(--danger)":"rgba(129, 147, 159, 0.78)",ff=h?.column||0,rf=o?.column||0,Lf=Qu===0?0:Math.sign(Qu),gf=uu?`feedback:${ff}->${rf}:${Lf}`:$u?`fanout:${ff}->${rf}:${t.source}`:Fu?`fanin:${ff}->${rf}:${t.target}`:m.side!=="left"||x>1?`corridor:${ff}->${rf}:${m.side}:${Lf}:${Math.round(Math.abs(Qu)/56)}`:"";return{id:`${t.source}->${t.target}-${t.index}`,source:t.source,target:t.target,sourceHandle:Tu.id,targetHandle:m.id,type:"pipelineCurve",zIndex:12,animated:O==="running",data:{baseEdgeColor:Du,laneOffset:Qu,routeMode:$u&&m.side==="left"?"direct-forward-left":"",targetSide:m.side,isFeedback:uu,overlapGroup:gf},targetStatus:O}}),R=X.reduce((t,O)=>{let M=String(O.data?.overlapGroup||"");return M?t.set(M,(t.get(M)||0)+1):t},new Map),Y=new Map,P=X.map((t)=>{let O=String(t.targetStatus||"pending"),M={...t};delete M.targetStatus;let S=String(t.data?.overlapGroup||""),b=S?R.get(S)||0:0,Z=b>1?Y.get(S)||0:-1;if(b>1)Y.set(S,Z+1);let D=Z>=0?Qz[Z%Qz.length]:String(t.data.baseEdgeColor),I={stroke:D};if(t.data.isFeedback)I.strokeDasharray="9 7";return{...M,data:{...t.data,edgeColor:D,overlapSlot:Z,overlapCount:b},style:I,markerEnd:{type:T1.ArrowClosed,color:D},className:`pipeline-flow-edge ${O} ${t.data.isFeedback?"feedback":""} ${Z>=0?"overlap-colored":""}`}});return{nodes:j,edges:P}}function Rl(u){return String(u??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function Oz(u){let f=String(u||"");if(f.includes("--accent-2"))return"#4eb7a8";if(f.includes("--accent"))return"#d7a13a";if(f.includes("--danger"))return"#cf6a54";return f.startsWith("#")?f:"#81939f"}function y8(u){return`arrow-${u.replace(/[^a-zA-Z0-9_-]+/g,"")}`}function az(u,f="pipeline"){return String(u||f).replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,"")||f}function Bz(u,f){let l=u.position.x,r=u.position.y,y=z$.find((i)=>i.id===f);if(y?.side==="top")return{x:l+ai*cz(y.style?.left,0.5),y:r,position:zu.Top};if(y?.side==="bottom")return{x:l+ai*cz(y.style?.left,0.5),y:r+oi,position:zu.Bottom};return{x:l,y:r+oi/2,position:zu.Left}}function bD(u){return{x:u.position.x+ai,y:u.position.y+oi/2}}function vD(u,f){let l=Math.min(...u.nodes.map((G)=>G.position.x),0)-220,r=Math.min(...u.nodes.map((G)=>G.position.y),0)-220,y=Math.max(...u.nodes.map((G)=>G.position.x+ai),1)+220,i=Math.max(...u.nodes.map((G)=>G.position.y+oi),1)+220,_=Math.ceil(y-l),n=Math.ceil(i-r),$=new Map(u.nodes.map((G)=>[G.id,G])),j=u.edges.map((G)=>Oz(G.data?.edgeColor||G.style?.stroke)),J=Array.from(new Set(["#4eb7a8","#d7a13a","#cf6a54","#81939f",...j])).map((G)=>``).join(""),U=u.edges.flatMap((G)=>{let K=$.get(G.source),Q=$.get(G.target);if(!K||!Q)return[];let N=bD(K),c=Bz(Q,String(G.targetHandle||"in-left")),z=mz(N.x,N.y,c.x,c.y,c.position,Number(G.data?.laneOffset||0),String(G.data?.routeMode||"")),w=Oz(G.data?.edgeColor||G.style?.stroke),H=G.data?.isFeedback?' stroke-dasharray="9 7"':"";return``}).join(` -`),q=u.nodes.map((G)=>{let K=G.data?.exportLabel||{},Q=String(K.status||"pending").toLowerCase(),N=Q==="succeeded"?"#4eb7a8":Q==="running"?"#d7a13a":Q==="failed"?"#cf6a54":"#81939f",c=G.position.x,z=G.position.y,w=z$.map((H)=>{let B=Bz(G,H.id);if(H.side==="top"||H.side==="bottom")return``;return``}).join(` +`)),A=c.reduce((j,F)=>j.concat(...F),[]);return[c,A]}return[[],[]]},[u]);return yu.useEffect(()=>{let $=f?.target??MN,c=f?.actInsideInputWithModifier??!0;if(u!==null){let A=(U)=>{if(n.current=U.ctrlKey||U.metaKey||U.shiftKey||U.altKey,(!n.current||n.current&&!c)&&gA(U))return!1;let w=RN(U.code,_);if(i.current.add(U[w]),CN(y,i.current,!1)){let L=U.composedPath?.()?.[0]||U.target,J=L?.nodeName==="BUTTON"||L?.nodeName==="A";if(f.preventDefault!==!1&&(n.current||!J))U.preventDefault();r(!0)}},j=(U)=>{let Q=RN(U.code,_);if(CN(y,i.current,!0))r(!1),i.current.clear();else i.current.delete(U[Q]);if(U.key==="Meta")i.current.clear();n.current=!1},F=()=>{i.current.clear(),r(!1)};return $?.addEventListener("keydown",A),$?.addEventListener("keyup",j),window.addEventListener("blur",F),window.addEventListener("contextmenu",F),()=>{$?.removeEventListener("keydown",A),$?.removeEventListener("keyup",j),window.removeEventListener("blur",F),window.removeEventListener("contextmenu",F)}}},[u,r]),l}function CN(u,f,l){return u.filter((r)=>l||r.length===f.size).some((r)=>r.every((n)=>f.has(n)))}function RN(u,f){return f.includes(u)?"code":"key"}var uD=()=>{let u=Tf();return yu.useMemo(()=>{return{zoomIn:(f)=>{let{panZoom:l}=u.getState();return l?l.scaleBy(1.2,f):Promise.resolve(!1)},zoomOut:(f)=>{let{panZoom:l}=u.getState();return l?l.scaleBy(0.8333333333333334,f):Promise.resolve(!1)},zoomTo:(f,l)=>{let{panZoom:r}=u.getState();return r?r.scaleTo(f,l):Promise.resolve(!1)},getZoom:()=>u.getState().transform[2],setViewport:async(f,l)=>{let{transform:[r,n,i],panZoom:y}=u.getState();if(!y)return Promise.resolve(!1);return await y.setViewport({x:f.x??r,y:f.y??n,zoom:f.zoom??i},l),Promise.resolve(!0)},getViewport:()=>{let[f,l,r]=u.getState().transform;return{x:f,y:l,zoom:r}},setCenter:async(f,l,r)=>{return u.getState().setCenter(f,l,r)},fitBounds:async(f,l)=>{let{width:r,height:n,minZoom:i,maxZoom:y,panZoom:_}=u.getState(),$=K$(f,r,n,i,y,l?.padding??0.1);if(!_)return Promise.resolve(!1);return await _.setViewport($,{duration:l?.duration,ease:l?.ease,interpolate:l?.interpolate}),Promise.resolve(!0)},screenToFlowPosition:(f,l={})=>{let{transform:r,snapGrid:n,snapToGrid:i,domNode:y}=u.getState();if(!y)return f;let{x:_,y:$}=y.getBoundingClientRect(),c={x:f.x-_,y:f.y-$},A=l.snapGrid??n,j=l.snapToGrid??i;return yy(c,r,j,A)},flowToScreenPosition:(f)=>{let{transform:l,domNode:r}=u.getState();if(!r)return f;let{x:n,y:i}=r.getBoundingClientRect(),y=w$(f,l);return{x:y.x+n,y:y.y+i}}}},[])};function $W(u,f){let l=[],r=new Map,n=[];for(let i of u)if(i.type==="add"){n.push(i);continue}else if(i.type==="remove"||i.type==="replace")r.set(i.id,[i]);else{let y=r.get(i.id);if(y)y.push(i);else r.set(i.id,[i])}for(let i of f){let y=r.get(i.id);if(!y){l.push(i);continue}if(y[0].type==="remove")continue;if(y[0].type==="replace"){l.push({...y[0].item});continue}let _={...i};for(let $ of y)fD($,_);l.push(_)}if(n.length)n.forEach((i)=>{if(i.index!==void 0)l.splice(i.index,0,{...i.item});else l.push({...i.item})});return l}function fD(u,f){switch(u.type){case"select":{f.selected=u.selected;break}case"position":{if(typeof u.position<"u")f.position=u.position;if(typeof u.dragging<"u")f.dragging=u.dragging;break}case"dimensions":{if(typeof u.dimensions<"u"){if(f.measured={...u.dimensions},u.setAttributes){if(u.setAttributes===!0||u.setAttributes==="width")f.width=u.dimensions.width;if(u.setAttributes===!0||u.setAttributes==="height")f.height=u.dimensions.height}}if(typeof u.resizing==="boolean")f.resizing=u.resizing;break}}}function lD(u,f){return $W(u,f)}function rD(u,f){return $W(u,f)}function P1(u,f){return{id:u,type:"select",selected:f}}function ty(u,f=new Set,l=!1){let r=[];for(let[n,i]of u){let y=f.has(n);if(!(i.selected===void 0&&!y)&&i.selected!==y){if(l)i.selected=y;r.push(P1(i.id,y))}}return r}function xN({items:u=[],lookup:f}){let l=[],r=new Map(u.map((n)=>[n.id,n]));for(let[n,i]of u.entries()){let y=f.get(i.id),_=y?.internals?.userNode??y;if(_!==void 0&&_!==i)l.push({id:i.id,item:i,type:"replace"});if(_===void 0)l.push({item:i,type:"add",index:n})}for(let[n]of f)if(r.get(n)===void 0)l.push({id:n,type:"remove"});return l}function hN(u){return{id:u.id,type:"remove"}}var bN=(u)=>kq(u),nD=(u)=>pA(u);function tW(u){return yu.forwardRef(u)}function vN(u){let[f,l]=yu.useState(BigInt(0)),[r]=yu.useState(()=>iD(()=>l((n)=>n+BigInt(1))));return _9(()=>{let n=r.get();if(n.length)u(n),r.reset()},[f]),r}function iD(u){let f=[];return{get:()=>f,reset:()=>{f=[]},push:(l)=>{f.push(l),u()}}}var cW=yu.createContext(null);function yD({children:u}){let f=Tf(),l=yu.useCallback((_)=>{let{nodes:$=[],setNodes:c,hasDefaultNodes:A,onNodesChange:j,nodeLookup:F,fitViewQueued:U,onNodesChangeMiddlewareMap:Q}=f.getState(),w=$;for(let J of _)w=typeof J==="function"?J(w):J;let L=xN({items:w,lookup:F});for(let J of Q.values())L=J(L);if(A)c(w);if(L.length>0)j?.(L);else if(U)window.requestAnimationFrame(()=>{let{fitViewQueued:J,nodes:N,setNodes:q}=f.getState();if(J)q(N)})},[]),r=vN(l),n=yu.useCallback((_)=>{let{edges:$=[],setEdges:c,hasDefaultEdges:A,onEdgesChange:j,edgeLookup:F}=f.getState(),U=$;for(let Q of _)U=typeof Q==="function"?Q(U):Q;if(A)c(U);else if(j)j(xN({items:U,lookup:F}))},[]),i=vN(n),y=yu.useMemo(()=>({nodeQueue:r,edgeQueue:i}),[]);return ru.jsx(cW.Provider,{value:y,children:u})}function _D(){let u=yu.useContext(cW);if(!u)throw Error("useBatchContext must be used within a BatchProvider");return u}var $D=(u)=>!!u.panZoom;function t9(){let u=uD(),f=Tf(),l=_D(),r=uf($D),n=yu.useMemo(()=>{let i=(j)=>f.getState().nodeLookup.get(j),y=(j)=>{l.nodeQueue.push(j)},_=(j)=>{l.edgeQueue.push(j)},$=(j)=>{let{nodeLookup:F,nodeOrigin:U}=f.getState(),Q=bN(j)?j:F.get(j.id),w=Q.parentId?vA(Q.position,Q.measured,Q.parentId,F,U):Q.position,L={...Q,position:w,width:Q.measured?.width??Q.width,height:Q.measured?.height??Q.height};return p1(L)},c=(j,F,U={replace:!1})=>{y((Q)=>Q.map((w)=>{if(w.id===j){let L=typeof F==="function"?F(w):F;return U.replace&&bN(L)?L:{...w,...L}}return w}))},A=(j,F,U={replace:!1})=>{_((Q)=>Q.map((w)=>{if(w.id===j){let L=typeof F==="function"?F(w):F;return U.replace&&nD(L)?L:{...w,...L}}return w}))};return{getNodes:()=>f.getState().nodes.map((j)=>({...j})),getNode:(j)=>i(j)?.internals.userNode,getInternalNode:i,getEdges:()=>{let{edges:j=[]}=f.getState();return j.map((F)=>({...F}))},getEdge:(j)=>f.getState().edgeLookup.get(j),setNodes:y,setEdges:_,addNodes:(j)=>{let F=Array.isArray(j)?j:[j];l.nodeQueue.push((U)=>[...U,...F])},addEdges:(j)=>{let F=Array.isArray(j)?j:[j];l.edgeQueue.push((U)=>[...U,...F])},toObject:()=>{let{nodes:j=[],edges:F=[],transform:U}=f.getState(),[Q,w,L]=U;return{nodes:j.map((J)=>({...J})),edges:F.map((J)=>({...J})),viewport:{x:Q,y:w,zoom:L}}},deleteElements:async({nodes:j=[],edges:F=[]})=>{let{nodes:U,edges:Q,onNodesDelete:w,onEdgesDelete:L,triggerNodeChanges:J,triggerEdgeChanges:N,onDelete:q,onBeforeDelete:W}=f.getState(),{nodes:z,edges:Z}=await sq({nodesToRemove:j,edgesToRemove:F,nodes:U,edges:Q,onBeforeDelete:W}),H=Z.length>0,E=z.length>0;if(H){let D=Z.map(hN);L?.(Z),N(D)}if(E){let D=z.map(hN);w?.(z),J(D)}if(E||H)q?.({nodes:z,edges:Z});return{deletedNodes:z,deletedEdges:Z}},getIntersectingNodes:(j,F=!0,U)=>{let Q=xA(j),w=Q?j:$(j),L=U!==void 0;if(!w)return[];return(U||f.getState().nodes).filter((J)=>{let N=f.getState().nodeLookup.get(J.id);if(N&&!Q&&(J.id===j.id||!N.internals.positionAbsolute))return!1;let q=p1(L?J:N),W=ny(q,w);return F&&W>0||W>=q.width*q.height||W>=w.width*w.height})},isNodeIntersecting:(j,F,U=!0)=>{let w=xA(j)?j:$(j);if(!w)return!1;let L=ny(w,F);return U&&L>0||L>=F.width*F.height||L>=w.width*w.height},updateNode:c,updateNodeData:(j,F,U={replace:!1})=>{c(j,(Q)=>{let w=typeof F==="function"?F(Q):F;return U.replace?{...Q,data:w}:{...Q,data:{...Q.data,...w}}},U)},updateEdge:A,updateEdgeData:(j,F,U={replace:!1})=>{A(j,(Q)=>{let w=typeof F==="function"?F(Q):F;return U.replace?{...Q,data:w}:{...Q,data:{...Q.data,...w}}},U)},getNodesBounds:(j)=>{let{nodeLookup:F,nodeOrigin:U}=f.getState();return MA(j,{nodeLookup:F,nodeOrigin:U})},getHandleConnections:({type:j,id:F,nodeId:U})=>Array.from(f.getState().connectionLookup.get(`${U}-${j}${F?`-${F}`:""}`)?.values()??[]),getNodeConnections:({type:j,handleId:F,nodeId:U})=>Array.from(f.getState().connectionLookup.get(`${U}${j?F?`-${j}-${F}`:`-${j}`:""}`)?.values()??[]),fitView:async(j)=>{let F=f.getState().fitViewResolver??dq();return f.setState({fitViewQueued:!0,fitViewOptions:j,fitViewResolver:F}),l.nodeQueue.push((U)=>[...U]),F.promise}}},[]);return yu.useMemo(()=>{return{...n,...u,viewportInitialized:r}},[r])}var kN=(u)=>u.selected,tD=typeof window<"u"?window:void 0;function cD({deleteKeyCode:u,multiSelectionKeyCode:f}){let l=Tf(),{deleteElements:r}=t9(),n=z$(u,{actInsideInputWithModifier:!1}),i=z$(f,{target:tD});yu.useEffect(()=>{if(n){let{edges:y,nodes:_}=l.getState();r({nodes:_.filter(kN),edges:y.filter(kN)}),l.setState({nodesSelectionActive:!1})}},[n]),yu.useEffect(()=>{l.setState({multiSelectionActive:i})},[i])}function AD(u){let f=Tf();yu.useEffect(()=>{let l=()=>{if(!u.current||!(u.current.checkVisibility?.()??!0))return!1;let r=b6(u.current);if(r.height===0||r.width===0)f.getState().onError?.("004",tr.error004());f.setState({width:r.width||500,height:r.height||500})};if(u.current){l(),window.addEventListener("resize",l);let r=new ResizeObserver(()=>l());return r.observe(u.current),()=>{if(window.removeEventListener("resize",l),r&&u.current)r.unobserve(u.current)}}},[])}var i8={position:"absolute",width:"100%",height:"100%",top:0,left:0},jD=(u)=>({userSelectionActive:u.userSelectionActive,lib:u.lib,connectionInProgress:u.connection.inProgress});function FD({onPaneContextMenu:u,zoomOnScroll:f=!0,zoomOnPinch:l=!0,panOnScroll:r=!1,panOnScrollSpeed:n=0.5,panOnScrollMode:i=C0.Free,zoomOnDoubleClick:y=!0,panOnDrag:_=!0,defaultViewport:$,translateExtent:c,minZoom:A,maxZoom:j,zoomActivationKeyCode:F,preventScrolling:U=!0,children:Q,noWheelClassName:w,noPanClassName:L,onViewportChange:J,isControlledViewport:N,paneClickDistance:q,selectionOnDrag:W}){let z=Tf(),Z=yu.useRef(null),{userSelectionActive:H,lib:E,connectionInProgress:D}=uf(jD,Kf),h=z$(F),B=yu.useRef();AD(Z);let X=yu.useCallback((m)=>{if(J?.({x:m[0],y:m[1],zoom:m[2]}),!N)z.setState({transform:m})},[J,N]);return yu.useEffect(()=>{if(Z.current){B.current=NN({domNode:Z.current,minZoom:A,maxZoom:j,translateExtent:c,viewport:$,onDraggingChange:(Y)=>z.setState((v)=>v.paneDragging===Y?v:{paneDragging:Y}),onPanZoomStart:(Y,v)=>{let{onViewportChangeStart:T,onMoveStart:S}=z.getState();S?.(Y,v),T?.(v)},onPanZoom:(Y,v)=>{let{onViewportChange:T,onMove:S}=z.getState();S?.(Y,v),T?.(v)},onPanZoomEnd:(Y,v)=>{let{onViewportChangeEnd:T,onMoveEnd:S}=z.getState();S?.(Y,v),T?.(v)}});let{x:m,y:O,zoom:p}=B.current.getViewport();return z.setState({panZoom:B.current,transform:[m,O,p],domNode:Z.current.closest(".react-flow")}),()=>{B.current?.destroy()}}},[]),yu.useEffect(()=>{B.current?.update({onPaneContextMenu:u,zoomOnScroll:f,zoomOnPinch:l,panOnScroll:r,panOnScrollSpeed:n,panOnScrollMode:i,zoomOnDoubleClick:y,panOnDrag:_,zoomActivationKeyPressed:h,preventScrolling:U,noPanClassName:L,userSelectionActive:H,noWheelClassName:w,lib:E,onTransformChange:X,connectionInProgress:D,selectionOnDrag:W,paneClickDistance:q})},[u,f,l,r,n,i,y,_,h,U,L,H,w,E,X,D,W,q]),ru.jsx("div",{className:"react-flow__renderer",ref:Z,style:i8,children:Q})}var JD=(u)=>({userSelectionActive:u.userSelectionActive,userSelectionRect:u.userSelectionRect});function UD(){let{userSelectionActive:u,userSelectionRect:f}=uf(JD,Kf);if(!(u&&f))return null;return ru.jsx("div",{className:"react-flow__selection react-flow__container",style:{width:f.width,height:f.height,transform:`translate(${f.x}px, ${f.y}px)`}})}var i9=(u,f)=>{return(l)=>{if(l.target!==f.current)return;u?.(l)}},QD=(u)=>({userSelectionActive:u.userSelectionActive,elementsSelectable:u.elementsSelectable,connectionInProgress:u.connection.inProgress,dragging:u.paneDragging});function qD({isSelecting:u,selectionKeyPressed:f,selectionMode:l=S1.Full,panOnDrag:r,paneClickDistance:n,selectionOnDrag:i,onSelectionStart:y,onSelectionEnd:_,onPaneClick:$,onPaneContextMenu:c,onPaneScroll:A,onPaneMouseEnter:j,onPaneMouseMove:F,onPaneMouseLeave:U,children:Q}){let w=Tf(),{userSelectionActive:L,elementsSelectable:J,dragging:N,connectionInProgress:q}=uf(QD,Kf),W=J&&(u||L),z=yu.useRef(null),Z=yu.useRef(),H=yu.useRef(new Set),E=yu.useRef(new Set),D=yu.useRef(!1),h=(T)=>{if(D.current||q){D.current=!1;return}$?.(T),w.getState().resetSelectedElements(),w.setState({nodesSelectionActive:!1})},B=(T)=>{if(Array.isArray(r)&&r?.includes(2)){T.preventDefault();return}c?.(T)},X=A?(T)=>A(T):void 0,m=(T)=>{if(D.current)T.stopPropagation(),D.current=!1},O=(T)=>{let{domNode:S}=w.getState();if(Z.current=S?.getBoundingClientRect(),!Z.current)return;let k=T.target===z.current;if(!k&&!!T.target.closest(".nokey")||!u||!(i&&k||f)||T.button!==0||!T.isPrimary)return;T.target?.setPointerCapture?.(T.pointerId),D.current=!1;let{x:o,y:s}=zr(T.nativeEvent,Z.current);if(w.setState({userSelectionRect:{width:0,height:0,startX:o,startY:s,x:o,y:s}}),!k)T.stopPropagation(),T.preventDefault()},p=(T)=>{let{userSelectionRect:S,transform:k,nodeLookup:I,edgeLookup:b,connectionLookup:o,triggerNodeChanges:s,triggerEdgeChanges:x,defaultEdgeOptions:fu,resetSelectedElements:$u}=w.getState();if(!Z.current||!S)return;let{x:tu,y:ju}=zr(T.nativeEvent,Z.current),{startX:Gu,startY:qu}=S;if(!D.current){let g=f?0:n;if(Math.hypot(tu-Gu,ju-qu)<=g)return;$u(),y?.(T)}D.current=!0;let P={startX:Gu,startY:qu,x:tug.id)),E.current=new Set;let Wu=fu?.selectable??!0;for(let g of H.current){let Qu=o.get(g);if(!Qu)continue;for(let{edgeId:Tu}of Qu.values()){let Zu=b.get(Tu);if(Zu&&(Zu.selectable??Wu))E.current.add(Tu)}}if(!kA(e,H.current)){let g=ty(I,H.current,!0);s(g)}if(!kA(uu,E.current)){let g=ty(b,E.current);x(g)}w.setState({userSelectionRect:P,userSelectionActive:!0,nodesSelectionActive:!1})},Y=(T)=>{if(T.button!==0)return;if(T.target?.releasePointerCapture?.(T.pointerId),!L&&T.target===z.current&&w.getState().userSelectionRect)h?.(T);if(w.setState({userSelectionActive:!1,userSelectionRect:null}),D.current)_?.(T),w.setState({nodesSelectionActive:H.current.size>0})},v=r===!0||Array.isArray(r)&&r.includes(0);return ru.jsxs("div",{className:Xf(["react-flow__pane",{draggable:v,dragging:N,selection:u}]),onClick:W?void 0:i9(h,z),onContextMenu:i9(B,z),onWheel:i9(X,z),onPointerEnter:W?void 0:j,onPointerMove:W?p:F,onPointerUp:W?Y:void 0,onPointerDownCapture:W?O:void 0,onClickCapture:W?m:void 0,onPointerLeave:U,ref:z,style:i8,children:[Q,ru.jsx(UD,{})]})}function $9({id:u,store:f,unselect:l=!1,nodeRef:r}){let{addSelectedNodes:n,unselectNodesAndEdges:i,multiSelectionActive:y,nodeLookup:_,onError:$}=f.getState(),c=_.get(u);if(!c){$?.("012",tr.error012(u));return}if(f.setState({nodesSelectionActive:!1}),!c.selected)n([u]);else if(l||c.selected&&y)i({nodes:[c],edges:[]}),requestAnimationFrame(()=>r?.current?.blur())}function AW({nodeRef:u,disabled:f=!1,noDragClassName:l,handleSelector:r,nodeId:n,isSelectable:i,nodeClickDistance:y}){let _=Tf(),[$,c]=yu.useState(!1),A=yu.useRef();return yu.useEffect(()=>{A.current=cN({getStoreItems:()=>_.getState(),onNodeMouseDown:(j)=>{$9({id:j,store:_,nodeRef:u})},onDragStart:()=>{c(!0)},onDragStop:()=>{c(!1)}})},[]),yu.useEffect(()=>{if(f||!u.current||!A.current)return;return A.current.update({noDragClassName:l,handleSelector:r,domNode:u.current,isSelectable:i,nodeId:n,nodeClickDistance:y}),()=>{A.current?.destroy()}},[l,r,f,i,u,n,y]),$}var ND=(u)=>(f)=>f.selected&&(f.draggable||u&&typeof f.draggable>"u");function jW(){let u=Tf();return yu.useCallback((l)=>{let{nodeExtent:r,snapToGrid:n,snapGrid:i,nodesDraggable:y,onError:_,updateNodePositions:$,nodeLookup:c,nodeOrigin:A}=u.getState(),j=new Map,F=ND(y),U=n?i[0]:5,Q=n?i[1]:5,w=l.direction.x*U*l.factor,L=l.direction.y*Q*l.factor;for(let[,J]of c){if(!F(J))continue;let N={x:J.internals.positionAbsolute.x+w,y:J.internals.positionAbsolute.y+L};if(n)N=iy(N,i);let{position:q,positionAbsolute:W}=CA({nodeId:J.id,nextPosition:N,nodeLookup:c,nodeExtent:r,nodeOrigin:A,onError:_});J.position=q,J.internals.positionAbsolute=W,j.set(J.id,J)}$(j)},[])}var c9=yu.createContext(null),WD=c9.Provider;c9.Consumer;var FW=()=>{return yu.useContext(c9)},wD=(u)=>({connectOnClick:u.connectOnClick,noPanClassName:u.noPanClassName,rfId:u.rfId}),LD=(u,f,l)=>(r)=>{let{connectionClickStartHandle:n,connectionMode:i,connection:y}=r,{fromHandle:_,toHandle:$,isValid:c}=y,A=$?.nodeId===u&&$?.id===f&&$?.type===l;return{connectingFrom:_?.nodeId===u&&_?.id===f&&_?.type===l,connectingTo:A,clickConnecting:n?.nodeId===u&&n?.id===f&&n?.type===l,isPossibleEndHandle:i===Xn.Strict?_?.type!==l:u!==_?.nodeId||f!==_?.id,connectionInProcess:!!_,clickConnectionInProcess:!!n,valid:A&&c}};function KD({type:u="source",position:f=Lu.Top,isValidConnection:l,isConnectable:r=!0,isConnectableStart:n=!0,isConnectableEnd:i=!0,id:y,onConnect:_,children:$,className:c,onMouseDown:A,onTouchStart:j,...F},U){let Q=y||null,w=u==="target",L=Tf(),J=FW(),{connectOnClick:N,noPanClassName:q,rfId:W}=uf(wD,Kf),{connectingFrom:z,connectingTo:Z,clickConnecting:H,isPossibleEndHandle:E,connectionInProcess:D,clickConnectionInProcess:h,valid:B}=uf(LD(J,Q,u),Kf);if(!J)L.getState().onError?.("010",tr.error010());let X=(p)=>{let{defaultEdgeOptions:Y,onConnect:v,hasDefaultEdges:T}=L.getState(),S={...Y,...p};if(T){let{edges:k,setEdges:I}=L.getState();I(oA(S,k))}v?.(S),_?.(S)},m=(p)=>{if(!J)return;let Y=sA(p.nativeEvent);if(n&&(Y&&p.button===0||!Y)){let v=L.getState();o6.onPointerDown(p.nativeEvent,{handleDomNode:p.currentTarget,autoPanOnConnect:v.autoPanOnConnect,connectionMode:v.connectionMode,connectionRadius:v.connectionRadius,domNode:v.domNode,nodeLookup:v.nodeLookup,lib:v.lib,isTarget:w,handleId:Q,nodeId:J,flowId:v.rfId,panBy:v.panBy,cancelConnection:v.cancelConnection,onConnectStart:v.onConnectStart,onConnectEnd:(...T)=>L.getState().onConnectEnd?.(...T),updateConnection:v.updateConnection,onConnect:X,isValidConnection:l||((...T)=>L.getState().isValidConnection?.(...T)??!0),getTransform:()=>L.getState().transform,getFromHandle:()=>L.getState().connection.fromHandle,autoPanSpeed:v.autoPanSpeed,dragThreshold:v.connectionDragThreshold})}if(Y)A?.(p);else j?.(p)},O=(p)=>{let{onClickConnectStart:Y,onClickConnectEnd:v,connectionClickStartHandle:T,connectionMode:S,isValidConnection:k,lib:I,rfId:b,nodeLookup:o,connection:s}=L.getState();if(!J||!T&&!n)return;if(!T){Y?.(p.nativeEvent,{nodeId:J,handleId:Q,handleType:u}),L.setState({connectionClickStartHandle:{nodeId:J,type:u,id:Q}});return}let x=IA(p.target),fu=l||k,{connection:$u,isValid:tu}=o6.isValid(p.nativeEvent,{handle:{nodeId:J,id:Q,type:u},connectionMode:S,fromNodeId:T.nodeId,fromHandleId:T.id||null,fromType:T.type,isValidConnection:fu,flowId:b,doc:x,lib:I,nodeLookup:o});if(tu&&$u)X($u);let ju=structuredClone(s);delete ju.inProgress,ju.toPosition=ju.toHandle?ju.toHandle.position:null,v?.(p,ju),L.setState({connectionClickStartHandle:null})};return ru.jsx("div",{"data-handleid":Q,"data-nodeid":J,"data-handlepos":f,"data-id":`${W}-${J}-${Q}-${u}`,className:Xf(["react-flow__handle",`react-flow__handle-${f}`,"nodrag",q,c,{source:!w,target:w,connectable:r,connectablestart:n,connectableend:i,clickconnecting:H,connectingfrom:z,connectingto:Z,valid:B,connectionindicator:r&&(!D||E)&&(D||h?i:n)}]),onMouseDown:m,onTouchStart:m,onClick:N?O:void 0,ref:U,...F,children:$})}var M1=yu.memo(tW(KD));function GD({data:u,isConnectable:f,sourcePosition:l=Lu.Bottom}){return ru.jsxs(ru.Fragment,{children:[u?.label,ru.jsx(M1,{type:"source",position:l,isConnectable:f})]})}function zD({data:u,isConnectable:f,targetPosition:l=Lu.Top,sourcePosition:r=Lu.Bottom}){return ru.jsxs(ru.Fragment,{children:[ru.jsx(M1,{type:"target",position:l,isConnectable:f}),u?.label,ru.jsx(M1,{type:"source",position:r,isConnectable:f})]})}function TD(){return null}function ED({data:u,isConnectable:f,targetPosition:l=Lu.Top}){return ru.jsxs(ru.Fragment,{children:[ru.jsx(M1,{type:"target",position:l,isConnectable:f}),u?.label]})}var l8={ArrowUp:{x:0,y:-1},ArrowDown:{x:0,y:1},ArrowLeft:{x:-1,y:0},ArrowRight:{x:1,y:0}},IN={input:GD,default:zD,output:ED,group:TD};function ZD(u){if(u.internals.handleBounds===void 0)return{width:u.width??u.initialWidth??u.style?.width,height:u.height??u.initialHeight??u.style?.height};return{width:u.width??u.style?.width,height:u.height??u.style?.height}}var OD=(u)=>{let{width:f,height:l,x:r,y:n}=ry(u.nodeLookup,{filter:(i)=>!!i.selected});return{width:Gr(f)?f:null,height:Gr(l)?l:null,userSelectionActive:u.userSelectionActive,transformString:`translate(${u.transform[0]}px,${u.transform[1]}px) scale(${u.transform[2]}) translate(${r}px,${n}px)`}};function HD({onSelectionContextMenu:u,noPanClassName:f,disableKeyboardA11y:l}){let r=Tf(),{width:n,height:i,transformString:y,userSelectionActive:_}=uf(OD,Kf),$=jW(),c=yu.useRef(null);yu.useEffect(()=>{if(!l)c.current?.focus({preventScroll:!0})},[l]);let A=!_&&n!==null&&i!==null;if(AW({nodeRef:c,disabled:!A}),!A)return null;let j=u?(U)=>{let Q=r.getState().nodes.filter((w)=>w.selected);u(U,Q)}:void 0,F=(U)=>{if(Object.prototype.hasOwnProperty.call(l8,U.key))U.preventDefault(),$({direction:l8[U.key],factor:U.shiftKey?4:1})};return ru.jsx("div",{className:Xf(["react-flow__nodesselection","react-flow__container",f]),style:{transform:y},children:ru.jsx("div",{ref:c,className:"react-flow__nodesselection-rect",onContextMenu:j,tabIndex:l?void 0:-1,onKeyDown:l?void 0:F,style:{width:n,height:i}})})}var gN=typeof window<"u"?window:void 0,BD=(u)=>{return{nodesSelectionActive:u.nodesSelectionActive,userSelectionActive:u.userSelectionActive}};function JW({children:u,onPaneClick:f,onPaneMouseEnter:l,onPaneMouseMove:r,onPaneMouseLeave:n,onPaneContextMenu:i,onPaneScroll:y,paneClickDistance:_,deleteKeyCode:$,selectionKeyCode:c,selectionOnDrag:A,selectionMode:j,onSelectionStart:F,onSelectionEnd:U,multiSelectionKeyCode:Q,panActivationKeyCode:w,zoomActivationKeyCode:L,elementsSelectable:J,zoomOnScroll:N,zoomOnPinch:q,panOnScroll:W,panOnScrollSpeed:z,panOnScrollMode:Z,zoomOnDoubleClick:H,panOnDrag:E,defaultViewport:D,translateExtent:h,minZoom:B,maxZoom:X,preventScrolling:m,onSelectionContextMenu:O,noWheelClassName:p,noPanClassName:Y,disableKeyboardA11y:v,onViewportChange:T,isControlledViewport:S}){let{nodesSelectionActive:k,userSelectionActive:I}=uf(BD,Kf),b=z$(c,{target:gN}),o=z$(w,{target:gN}),s=o||E,x=o||W,fu=A&&s!==!0,$u=b||I||fu;return cD({deleteKeyCode:$,multiSelectionKeyCode:Q}),ru.jsx(FD,{onPaneContextMenu:i,elementsSelectable:J,zoomOnScroll:N,zoomOnPinch:q,panOnScroll:x,panOnScrollSpeed:z,panOnScrollMode:Z,zoomOnDoubleClick:H,panOnDrag:!b&&s,defaultViewport:D,translateExtent:h,minZoom:B,maxZoom:X,zoomActivationKeyCode:L,preventScrolling:m,noWheelClassName:p,noPanClassName:Y,onViewportChange:T,isControlledViewport:S,paneClickDistance:_,selectionOnDrag:fu,children:ru.jsxs(qD,{onSelectionStart:F,onSelectionEnd:U,onPaneClick:f,onPaneMouseEnter:l,onPaneMouseMove:r,onPaneMouseLeave:n,onPaneContextMenu:i,onPaneScroll:y,panOnDrag:s,isSelecting:!!$u,selectionMode:j,selectionKeyPressed:b,paneClickDistance:_,selectionOnDrag:fu,children:[u,k&&ru.jsx(HD,{onSelectionContextMenu:O,noPanClassName:Y,disableKeyboardA11y:v})]})})}JW.displayName="FlowRenderer";var VD=yu.memo(JW),DD=(u)=>(f)=>{return u?R6(f.nodeLookup,{x:0,y:0,width:f.width,height:f.height},f.transform,!0).map((l)=>l.id):Array.from(f.nodeLookup.keys())};function XD(u){return uf(yu.useCallback(DD(u),[u]),Kf)}var YD=(u)=>u.updateNodeInternals;function SD(){let u=uf(YD),[f]=yu.useState(()=>{if(typeof ResizeObserver>"u")return null;return new ResizeObserver((l)=>{let r=new Map;l.forEach((n)=>{let i=n.target.getAttribute("data-id");r.set(i,{id:i,nodeElement:n.target,force:!0})}),u(r)})});return yu.useEffect(()=>{return()=>{f?.disconnect()}},[f]),f}function mD({node:u,nodeType:f,hasDimensions:l,resizeObserver:r}){let n=Tf(),i=yu.useRef(null),y=yu.useRef(null),_=yu.useRef(u.sourcePosition),$=yu.useRef(u.targetPosition),c=yu.useRef(f),A=l&&!!u.internals.handleBounds;return yu.useEffect(()=>{if(i.current&&!u.hidden&&(!A||y.current!==i.current)){if(y.current)r?.unobserve(y.current);r?.observe(i.current),y.current=i.current}},[A,u.hidden]),yu.useEffect(()=>{return()=>{if(y.current)r?.unobserve(y.current),y.current=null}},[]),yu.useEffect(()=>{if(i.current){let j=c.current!==f,F=_.current!==u.sourcePosition,U=$.current!==u.targetPosition;if(j||F||U)c.current=f,_.current=u.sourcePosition,$.current=u.targetPosition,n.getState().updateNodeInternals(new Map([[u.id,{id:u.id,nodeElement:i.current,force:!0}]]))}},[u.id,f,u.sourcePosition,u.targetPosition]),i}function pD({id:u,onClick:f,onMouseEnter:l,onMouseMove:r,onMouseLeave:n,onContextMenu:i,onDoubleClick:y,nodesDraggable:_,elementsSelectable:$,nodesConnectable:c,nodesFocusable:A,resizeObserver:j,noDragClassName:F,noPanClassName:U,disableKeyboardA11y:Q,rfId:w,nodeTypes:L,nodeClickDistance:J,onError:N}){let{node:q,internals:W,isParent:z}=uf((tu)=>{let ju=tu.nodeLookup.get(u),Gu=tu.parentLookup.has(u);return{node:ju,internals:ju.internals,isParent:Gu}},Kf),Z=q.type||"default",H=L?.[Z]||IN[Z];if(H===void 0)N?.("003",tr.error003(Z)),Z="default",H=L?.default||IN.default;let E=!!(q.draggable||_&&typeof q.draggable>"u"),D=!!(q.selectable||$&&typeof q.selectable>"u"),h=!!(q.connectable||c&&typeof q.connectable>"u"),B=!!(q.focusable||A&&typeof q.focusable>"u"),X=Tf(),m=bA(q),O=mD({node:q,nodeType:Z,hasDimensions:m,resizeObserver:j}),p=AW({nodeRef:O,disabled:q.hidden||!E,noDragClassName:F,handleSelector:q.dragHandle,nodeId:u,isSelectable:D,nodeClickDistance:J}),Y=jW();if(q.hidden)return null;let v=$0(q),T=ZD(q),S=D||E||f||l||r||n,k=l?(tu)=>l(tu,{...W.userNode}):void 0,I=r?(tu)=>r(tu,{...W.userNode}):void 0,b=n?(tu)=>n(tu,{...W.userNode}):void 0,o=i?(tu)=>i(tu,{...W.userNode}):void 0,s=y?(tu)=>y(tu,{...W.userNode}):void 0,x=(tu)=>{let{selectNodesOnDrag:ju,nodeDragThreshold:Gu}=X.getState();if(D&&(!ju||!E||Gu>0))$9({id:u,store:X,nodeRef:O});if(f)f(tu,{...W.userNode})},fu=(tu)=>{if(gA(tu.nativeEvent)||Q)return;if(XA.includes(tu.key)&&D){let ju=tu.key==="Escape";$9({id:u,store:X,unselect:ju,nodeRef:O})}else if(E&&q.selected&&Object.prototype.hasOwnProperty.call(l8,tu.key)){tu.preventDefault();let{ariaLabelConfig:ju}=X.getState();X.setState({ariaLiveMessage:ju["node.a11yDescription.ariaLiveMessage"]({direction:tu.key.replace("Arrow","").toLowerCase(),x:~~W.positionAbsolute.x,y:~~W.positionAbsolute.y})}),Y({direction:l8[tu.key],factor:tu.shiftKey?4:1})}},$u=()=>{if(Q||!O.current?.matches(":focus-visible"))return;let{transform:tu,width:ju,height:Gu,autoPanOnNodeFocus:qu,setCenter:P}=X.getState();if(!qu)return;if(!(R6(new Map([[u,q]]),{x:0,y:0,width:ju,height:Gu},tu,!0).length>0))P(q.position.x+v.width/2,q.position.y+v.height/2,{zoom:tu[2]})};return ru.jsx("div",{className:Xf(["react-flow__node",`react-flow__node-${Z}`,{[U]:E},q.className,{selected:q.selected,selectable:D,parent:z,draggable:E,dragging:p}]),ref:O,style:{zIndex:W.z,transform:`translate(${W.positionAbsolute.x}px,${W.positionAbsolute.y}px)`,pointerEvents:S?"all":"none",visibility:m?"visible":"hidden",...q.style,...T},"data-id":u,"data-testid":`rf__node-${u}`,onMouseEnter:k,onMouseMove:I,onMouseLeave:b,onContextMenu:o,onClick:x,onDoubleClick:s,onKeyDown:B?fu:void 0,tabIndex:B?0:void 0,onFocus:B?$u:void 0,role:q.ariaRole??(B?"group":void 0),"aria-roledescription":"node","aria-describedby":Q?void 0:`${iW}-${w}`,"aria-label":q.ariaLabel,...q.domAttributes,children:ru.jsx(WD,{value:u,children:ru.jsx(H,{id:u,data:q.data,type:Z,positionAbsoluteX:W.positionAbsolute.x,positionAbsoluteY:W.positionAbsolute.y,selected:q.selected??!1,selectable:D,draggable:E,deletable:q.deletable??!0,isConnectable:h,sourcePosition:q.sourcePosition,targetPosition:q.targetPosition,dragging:p,dragHandle:q.dragHandle,zIndex:W.z,parentId:q.parentId,...v})})})}var PD=yu.memo(pD),MD=(u)=>({nodesDraggable:u.nodesDraggable,nodesConnectable:u.nodesConnectable,nodesFocusable:u.nodesFocusable,elementsSelectable:u.elementsSelectable,onError:u.onError});function UW(u){let{nodesDraggable:f,nodesConnectable:l,nodesFocusable:r,elementsSelectable:n,onError:i}=uf(MD,Kf),y=XD(u.onlyRenderVisibleElements),_=SD();return ru.jsx("div",{className:"react-flow__nodes",style:i8,children:y.map(($)=>{return ru.jsx(PD,{id:$,nodeTypes:u.nodeTypes,nodeExtent:u.nodeExtent,onClick:u.onNodeClick,onMouseEnter:u.onNodeMouseEnter,onMouseMove:u.onNodeMouseMove,onMouseLeave:u.onNodeMouseLeave,onContextMenu:u.onNodeContextMenu,onDoubleClick:u.onNodeDoubleClick,noDragClassName:u.noDragClassName,noPanClassName:u.noPanClassName,rfId:u.rfId,disableKeyboardA11y:u.disableKeyboardA11y,resizeObserver:_,nodesDraggable:f,nodesConnectable:l,nodesFocusable:r,elementsSelectable:n,nodeClickDistance:u.nodeClickDistance,onError:i},$)})})}UW.displayName="NodeRenderer";var CD=yu.memo(UW);function RD(u){return uf(yu.useCallback((l)=>{if(!u)return l.edges.map((n)=>n.id);let r=[];if(l.width&&l.height)for(let n of l.edges){let i=l.nodeLookup.get(n.source),y=l.nodeLookup.get(n.target);if(i&&y&&fN({sourceNode:i,targetNode:y,width:l.width,height:l.height,transform:l.transform}))r.push(n.id)}return r},[u]),Kf)}var xD=({color:u="none",strokeWidth:f=1})=>{let l={strokeWidth:f,...u&&{stroke:u}};return ru.jsx("polyline",{className:"arrow",style:l,strokeLinecap:"round",fill:"none",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4"})},hD=({color:u="none",strokeWidth:f=1})=>{let l={strokeWidth:f,...u&&{stroke:u,fill:u}};return ru.jsx("polyline",{className:"arrowclosed",style:l,strokeLinecap:"round",strokeLinejoin:"round",points:"-5,-4 0,0 -5,4 -5,-4"})},sN={[Yn.Arrow]:xD,[Yn.ArrowClosed]:hD};function bD(u){let f=Tf();return yu.useMemo(()=>{if(!Object.prototype.hasOwnProperty.call(sN,u))return f.getState().onError?.("009",tr.error009(u)),null;return sN[u]},[u])}var vD=({id:u,type:f,color:l,width:r=12.5,height:n=12.5,markerUnits:i="strokeWidth",strokeWidth:y,orient:_="auto-start-reverse"})=>{let $=bD(f);if(!$)return null;return ru.jsx("marker",{className:"react-flow__arrowhead",id:u,markerWidth:`${r}`,markerHeight:`${n}`,viewBox:"-10 -10 20 20",markerUnits:i,orient:_,refX:"0",refY:"0",children:ru.jsx($,{color:l,strokeWidth:y})})},QW=({defaultColor:u,rfId:f})=>{let l=uf((i)=>i.edges),r=uf((i)=>i.defaultEdgeOptions),n=yu.useMemo(()=>{return rN(l,{id:f,defaultColor:u,defaultMarkerStart:r?.markerStart,defaultMarkerEnd:r?.markerEnd})},[l,r,f,u]);if(!n.length)return null;return ru.jsx("svg",{className:"react-flow__marker","aria-hidden":"true",children:ru.jsx("defs",{children:n.map((i)=>ru.jsx(vD,{id:i.id,type:i.type,color:i.color,width:i.width,height:i.height,markerUnits:i.markerUnits,strokeWidth:i.strokeWidth,orient:i.orient},i.id))})})};QW.displayName="MarkerDefinitions";var kD=yu.memo(QW);function qW({x:u,y:f,label:l,labelStyle:r,labelShowBg:n=!0,labelBgStyle:i,labelBgPadding:y=[2,4],labelBgBorderRadius:_=2,children:$,className:c,...A}){let[j,F]=yu.useState({x:1,y:0,width:0,height:0}),U=Xf(["react-flow__edge-textwrapper",c]),Q=yu.useRef(null);if(yu.useEffect(()=>{if(Q.current){let w=Q.current.getBBox();F({x:w.x,y:w.y,width:w.width,height:w.height})}},[l]),!l)return null;return ru.jsxs("g",{transform:`translate(${u-j.width/2} ${f-j.height/2})`,className:U,visibility:j.width?"visible":"hidden",...A,children:[n&&ru.jsx("rect",{width:j.width+2*y[0],x:-y[0],y:-y[1],height:j.height+2*y[1],className:"react-flow__edge-textbg",style:i,rx:_,ry:_}),ru.jsx("text",{className:"react-flow__edge-text",y:j.height/2,dy:"0.3em",ref:Q,style:r,children:l}),$]})}qW.displayName="EdgeText";var ID=yu.memo(qW);function cy({path:u,labelX:f,labelY:l,label:r,labelStyle:n,labelShowBg:i,labelBgStyle:y,labelBgPadding:_,labelBgBorderRadius:$,interactionWidth:c=20,...A}){return ru.jsxs(ru.Fragment,{children:[ru.jsx("path",{...A,d:u,fill:"none",className:Xf(["react-flow__edge-path",A.className])}),c?ru.jsx("path",{d:u,fill:"none",strokeOpacity:0,strokeWidth:c,className:"react-flow__edge-interaction"}):null,r&&Gr(f)&&Gr(l)?ru.jsx(ID,{x:f,y:l,label:r,labelStyle:n,labelShowBg:i,labelBgStyle:y,labelBgPadding:_,labelBgBorderRadius:$}):null]})}function aN({pos:u,x1:f,y1:l,x2:r,y2:n}){if(u===Lu.Left||u===Lu.Right)return[0.5*(f+r),l];return[f,0.5*(l+n)]}function NW({sourceX:u,sourceY:f,sourcePosition:l=Lu.Bottom,targetX:r,targetY:n,targetPosition:i=Lu.Top}){let[y,_]=aN({pos:l,x1:u,y1:f,x2:r,y2:n}),[$,c]=aN({pos:i,x1:r,y1:n,x2:u,y2:f}),[A,j,F,U]=v6({sourceX:u,sourceY:f,targetX:r,targetY:n,sourceControlX:y,sourceControlY:_,targetControlX:$,targetControlY:c});return[`M${u},${f} C${y},${_} ${$},${c} ${r},${n}`,A,j,F,U]}function WW(u){return yu.memo(({id:f,sourceX:l,sourceY:r,targetX:n,targetY:i,sourcePosition:y,targetPosition:_,label:$,labelStyle:c,labelShowBg:A,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:U,style:Q,markerEnd:w,markerStart:L,interactionWidth:J})=>{let[N,q,W]=NW({sourceX:l,sourceY:r,sourcePosition:y,targetX:n,targetY:i,targetPosition:_}),z=u.isInternal?void 0:f;return ru.jsx(cy,{id:z,path:N,labelX:q,labelY:W,label:$,labelStyle:c,labelShowBg:A,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:U,style:Q,markerEnd:w,markerStart:L,interactionWidth:J})})}var gD=WW({isInternal:!1}),wW=WW({isInternal:!0});gD.displayName="SimpleBezierEdge";wW.displayName="SimpleBezierEdgeInternal";function LW(u){return yu.memo(({id:f,sourceX:l,sourceY:r,targetX:n,targetY:i,label:y,labelStyle:_,labelShowBg:$,labelBgStyle:c,labelBgPadding:A,labelBgBorderRadius:j,style:F,sourcePosition:U=Lu.Bottom,targetPosition:Q=Lu.Top,markerEnd:w,markerStart:L,pathOptions:J,interactionWidth:N})=>{let[q,W,z]=G$({sourceX:l,sourceY:r,sourcePosition:U,targetX:n,targetY:i,targetPosition:Q,borderRadius:J?.borderRadius,offset:J?.offset,stepPosition:J?.stepPosition}),Z=u.isInternal?void 0:f;return ru.jsx(cy,{id:Z,path:q,labelX:W,labelY:z,label:y,labelStyle:_,labelShowBg:$,labelBgStyle:c,labelBgPadding:A,labelBgBorderRadius:j,style:F,markerEnd:w,markerStart:L,interactionWidth:N})})}var KW=LW({isInternal:!1}),GW=LW({isInternal:!0});KW.displayName="SmoothStepEdge";GW.displayName="SmoothStepEdgeInternal";function zW(u){return yu.memo(({id:f,...l})=>{let r=u.isInternal?void 0:f;return ru.jsx(KW,{...l,id:r,pathOptions:yu.useMemo(()=>({borderRadius:0,offset:l.pathOptions?.offset}),[l.pathOptions?.offset])})})}var sD=zW({isInternal:!1}),TW=zW({isInternal:!0});sD.displayName="StepEdge";TW.displayName="StepEdgeInternal";function EW(u){return yu.memo(({id:f,sourceX:l,sourceY:r,targetX:n,targetY:i,label:y,labelStyle:_,labelShowBg:$,labelBgStyle:c,labelBgPadding:A,labelBgBorderRadius:j,style:F,markerEnd:U,markerStart:Q,interactionWidth:w})=>{let[L,J,N]=I6({sourceX:l,sourceY:r,targetX:n,targetY:i}),q=u.isInternal?void 0:f;return ru.jsx(cy,{id:q,path:L,labelX:J,labelY:N,label:y,labelStyle:_,labelShowBg:$,labelBgStyle:c,labelBgPadding:A,labelBgBorderRadius:j,style:F,markerEnd:U,markerStart:Q,interactionWidth:w})})}var aD=EW({isInternal:!1}),ZW=EW({isInternal:!0});aD.displayName="StraightEdge";ZW.displayName="StraightEdgeInternal";function OW(u){return yu.memo(({id:f,sourceX:l,sourceY:r,targetX:n,targetY:i,sourcePosition:y=Lu.Bottom,targetPosition:_=Lu.Top,label:$,labelStyle:c,labelShowBg:A,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:U,style:Q,markerEnd:w,markerStart:L,pathOptions:J,interactionWidth:N})=>{let[q,W,z]=k6({sourceX:l,sourceY:r,sourcePosition:y,targetX:n,targetY:i,targetPosition:_,curvature:J?.curvature}),Z=u.isInternal?void 0:f;return ru.jsx(cy,{id:Z,path:q,labelX:W,labelY:z,label:$,labelStyle:c,labelShowBg:A,labelBgStyle:j,labelBgPadding:F,labelBgBorderRadius:U,style:Q,markerEnd:w,markerStart:L,interactionWidth:N})})}var oD=OW({isInternal:!1}),HW=OW({isInternal:!0});oD.displayName="BezierEdge";HW.displayName="BezierEdgeInternal";var oN={default:HW,straight:ZW,step:TW,smoothstep:GW,simplebezier:wW},dN={sourceX:null,sourceY:null,targetX:null,targetY:null,sourcePosition:null,targetPosition:null},dD=(u,f,l)=>{if(l===Lu.Left)return u-f;if(l===Lu.Right)return u+f;return u},eD=(u,f,l)=>{if(l===Lu.Top)return u-f;if(l===Lu.Bottom)return u+f;return u},eN="react-flow__edgeupdater";function uW({position:u,centerX:f,centerY:l,radius:r=10,onMouseDown:n,onMouseEnter:i,onMouseOut:y,type:_}){return ru.jsx("circle",{onMouseDown:n,onMouseEnter:i,onMouseOut:y,className:Xf([eN,`${eN}-${_}`]),cx:dD(f,r,u),cy:eD(l,r,u),r,stroke:"transparent",fill:"transparent"})}function uX({isReconnectable:u,reconnectRadius:f,edge:l,sourceX:r,sourceY:n,targetX:i,targetY:y,sourcePosition:_,targetPosition:$,onReconnect:c,onReconnectStart:A,onReconnectEnd:j,setReconnecting:F,setUpdateHover:U}){let Q=Tf(),w=(W,z)=>{if(W.button!==0)return;let{autoPanOnConnect:Z,domNode:H,connectionMode:E,connectionRadius:D,lib:h,onConnectStart:B,cancelConnection:X,nodeLookup:m,rfId:O,panBy:p,updateConnection:Y}=Q.getState(),v=z.type==="target",T=(I,b)=>{F(!1),j?.(I,l,z.type,b)},S=(I)=>c?.(l,I),k=(I,b)=>{F(!0),A?.(W,l,z.type),B?.(I,b)};o6.onPointerDown(W.nativeEvent,{autoPanOnConnect:Z,connectionMode:E,connectionRadius:D,domNode:H,handleId:z.id,nodeId:z.nodeId,nodeLookup:m,isTarget:v,edgeUpdaterType:z.type,lib:h,flowId:O,cancelConnection:X,panBy:p,isValidConnection:(...I)=>Q.getState().isValidConnection?.(...I)??!0,onConnect:S,onConnectStart:k,onConnectEnd:(...I)=>Q.getState().onConnectEnd?.(...I),onReconnectEnd:T,updateConnection:Y,getTransform:()=>Q.getState().transform,getFromHandle:()=>Q.getState().connection.fromHandle,dragThreshold:Q.getState().connectionDragThreshold,handleDomNode:W.currentTarget})},L=(W)=>w(W,{nodeId:l.target,id:l.targetHandle??null,type:"target"}),J=(W)=>w(W,{nodeId:l.source,id:l.sourceHandle??null,type:"source"}),N=()=>U(!0),q=()=>U(!1);return ru.jsxs(ru.Fragment,{children:[(u===!0||u==="source")&&ru.jsx(uW,{position:_,centerX:r,centerY:n,radius:f,onMouseDown:L,onMouseEnter:N,onMouseOut:q,type:"source"}),(u===!0||u==="target")&&ru.jsx(uW,{position:$,centerX:i,centerY:y,radius:f,onMouseDown:J,onMouseEnter:N,onMouseOut:q,type:"target"})]})}function fX({id:u,edgesFocusable:f,edgesReconnectable:l,elementsSelectable:r,onClick:n,onDoubleClick:i,onContextMenu:y,onMouseEnter:_,onMouseMove:$,onMouseLeave:c,reconnectRadius:A,onReconnect:j,onReconnectStart:F,onReconnectEnd:U,rfId:Q,edgeTypes:w,noPanClassName:L,onError:J,disableKeyboardA11y:N}){let q=uf((P)=>P.edgeLookup.get(u)),W=uf((P)=>P.defaultEdgeOptions);q=W?{...W,...q}:q;let z=q.type||"default",Z=w?.[z]||oN[z];if(Z===void 0)J?.("011",tr.error011(z)),z="default",Z=w?.default||oN.default;let H=!!(q.focusable||f&&typeof q.focusable>"u"),E=typeof j<"u"&&(q.reconnectable||l&&typeof q.reconnectable>"u"),D=!!(q.selectable||r&&typeof q.selectable>"u"),h=yu.useRef(null),[B,X]=yu.useState(!1),[m,O]=yu.useState(!1),p=Tf(),{zIndex:Y,sourceX:v,sourceY:T,targetX:S,targetY:k,sourcePosition:I,targetPosition:b}=uf(yu.useCallback((P)=>{let e=P.nodeLookup.get(q.source),uu=P.nodeLookup.get(q.target);if(!e||!uu)return{zIndex:q.zIndex,...dN};let Wu=lN({id:u,sourceNode:e,targetNode:uu,sourceHandle:q.sourceHandle||null,targetHandle:q.targetHandle||null,connectionMode:P.connectionMode,onError:J});return{zIndex:uN({selected:q.selected,zIndex:q.zIndex,sourceNode:e,targetNode:uu,elevateOnSelect:P.elevateEdgesOnSelect,zIndexMode:P.zIndexMode}),...Wu||dN}},[q.source,q.target,q.sourceHandle,q.targetHandle,q.selected,q.zIndex]),Kf),o=yu.useMemo(()=>q.markerStart?`url('#${g6(q.markerStart,Q)}')`:void 0,[q.markerStart,Q]),s=yu.useMemo(()=>q.markerEnd?`url('#${g6(q.markerEnd,Q)}')`:void 0,[q.markerEnd,Q]);if(q.hidden||v===null||T===null||S===null||k===null)return null;let x=(P)=>{let{addSelectedEdges:e,unselectNodesAndEdges:uu,multiSelectionActive:Wu}=p.getState();if(D)if(p.setState({nodesSelectionActive:!1}),q.selected&&Wu)uu({nodes:[],edges:[q]}),h.current?.blur();else e([u]);if(n)n(P,q)},fu=i?(P)=>{i(P,{...q})}:void 0,$u=y?(P)=>{y(P,{...q})}:void 0,tu=_?(P)=>{_(P,{...q})}:void 0,ju=$?(P)=>{$(P,{...q})}:void 0,Gu=c?(P)=>{c(P,{...q})}:void 0,qu=(P)=>{if(!N&&XA.includes(P.key)&&D){let{unselectNodesAndEdges:e,addSelectedEdges:uu}=p.getState();if(P.key==="Escape")h.current?.blur(),e({edges:[q]});else uu([u])}};return ru.jsx("svg",{style:{zIndex:Y},children:ru.jsxs("g",{className:Xf(["react-flow__edge",`react-flow__edge-${z}`,q.className,L,{selected:q.selected,animated:q.animated,inactive:!D&&!n,updating:B,selectable:D}]),onClick:x,onDoubleClick:fu,onContextMenu:$u,onMouseEnter:tu,onMouseMove:ju,onMouseLeave:Gu,onKeyDown:H?qu:void 0,tabIndex:H?0:void 0,role:q.ariaRole??(H?"group":"img"),"aria-roledescription":"edge","data-id":u,"data-testid":`rf__edge-${u}`,"aria-label":q.ariaLabel===null?void 0:q.ariaLabel||`Edge from ${q.source} to ${q.target}`,"aria-describedby":H?`${yW}-${Q}`:void 0,ref:h,...q.domAttributes,children:[!m&&ru.jsx(Z,{id:u,source:q.source,target:q.target,type:q.type,selected:q.selected,animated:q.animated,selectable:D,deletable:q.deletable??!0,label:q.label,labelStyle:q.labelStyle,labelShowBg:q.labelShowBg,labelBgStyle:q.labelBgStyle,labelBgPadding:q.labelBgPadding,labelBgBorderRadius:q.labelBgBorderRadius,sourceX:v,sourceY:T,targetX:S,targetY:k,sourcePosition:I,targetPosition:b,data:q.data,style:q.style,sourceHandleId:q.sourceHandle,targetHandleId:q.targetHandle,markerStart:o,markerEnd:s,pathOptions:"pathOptions"in q?q.pathOptions:void 0,interactionWidth:q.interactionWidth}),E&&ru.jsx(uX,{edge:q,isReconnectable:E,reconnectRadius:A,onReconnect:j,onReconnectStart:F,onReconnectEnd:U,sourceX:v,sourceY:T,targetX:S,targetY:k,sourcePosition:I,targetPosition:b,setUpdateHover:X,setReconnecting:O})]})})}var lX=yu.memo(fX),rX=(u)=>({edgesFocusable:u.edgesFocusable,edgesReconnectable:u.edgesReconnectable,elementsSelectable:u.elementsSelectable,connectionMode:u.connectionMode,onError:u.onError});function BW({defaultMarkerColor:u,onlyRenderVisibleElements:f,rfId:l,edgeTypes:r,noPanClassName:n,onReconnect:i,onEdgeContextMenu:y,onEdgeMouseEnter:_,onEdgeMouseMove:$,onEdgeMouseLeave:c,onEdgeClick:A,reconnectRadius:j,onEdgeDoubleClick:F,onReconnectStart:U,onReconnectEnd:Q,disableKeyboardA11y:w}){let{edgesFocusable:L,edgesReconnectable:J,elementsSelectable:N,onError:q}=uf(rX,Kf),W=RD(f);return ru.jsxs("div",{className:"react-flow__edges",children:[ru.jsx(kD,{defaultColor:u,rfId:l}),W.map((z)=>{return ru.jsx(lX,{id:z,edgesFocusable:L,edgesReconnectable:J,elementsSelectable:N,noPanClassName:n,onReconnect:i,onContextMenu:y,onMouseEnter:_,onMouseMove:$,onMouseLeave:c,onClick:A,reconnectRadius:j,onDoubleClick:F,onReconnectStart:U,onReconnectEnd:Q,rfId:l,onError:q,edgeTypes:r,disableKeyboardA11y:w},z)})]})}BW.displayName="EdgeRenderer";var nX=yu.memo(BW),iX=(u)=>`translate(${u.transform[0]}px,${u.transform[1]}px) scale(${u.transform[2]})`;function yX({children:u}){let f=uf(iX);return ru.jsx("div",{className:"react-flow__viewport xyflow__viewport react-flow__container",style:{transform:f},children:u})}function _X(u){let f=t9(),l=yu.useRef(!1);yu.useEffect(()=>{if(!l.current&&f.viewportInitialized&&u)setTimeout(()=>u(f),1),l.current=!0},[u,f.viewportInitialized])}var $X=(u)=>u.panZoom?.syncViewport;function tX(u){let f=uf($X),l=Tf();return yu.useEffect(()=>{if(u)f?.(u),l.setState({transform:[u.x,u.y,u.zoom]})},[u,f]),null}function fW(u){return u.connection.inProgress?{...u.connection,to:yy(u.connection.to,u.transform)}:{...u.connection}}function cX(u){if(u)return(l)=>{let r=fW(l);return u(r)};return fW}function AX(u){let f=cX(u);return uf(f,Kf)}var jX=(u)=>({nodesConnectable:u.nodesConnectable,isValid:u.connection.isValid,inProgress:u.connection.inProgress,width:u.width,height:u.height});function FX({containerStyle:u,style:f,type:l,component:r}){let{nodesConnectable:n,width:i,height:y,isValid:_,inProgress:$}=uf(jX,Kf);if(!(i&&n&&$))return null;return ru.jsx("svg",{style:u,width:i,height:y,className:"react-flow__connectionline react-flow__container",children:ru.jsx("g",{className:Xf(["react-flow__connection",mA(_)]),children:ru.jsx(VW,{style:f,type:l,CustomComponent:r,isValid:_})})})}var VW=({style:u,type:f=_0.Bezier,CustomComponent:l,isValid:r})=>{let{inProgress:n,from:i,fromNode:y,fromHandle:_,fromPosition:$,to:c,toNode:A,toHandle:j,toPosition:F,pointer:U}=AX();if(!n)return;if(l)return ru.jsx(l,{connectionLineType:f,connectionLineStyle:u,fromNode:y,fromHandle:_,fromX:i.x,fromY:i.y,toX:c.x,toY:c.y,fromPosition:$,toPosition:F,connectionStatus:mA(r),toNode:A,toHandle:j,pointer:U});let Q="",w={sourceX:i.x,sourceY:i.y,sourcePosition:$,targetX:c.x,targetY:c.y,targetPosition:F};switch(f){case _0.Bezier:[Q]=k6(w);break;case _0.SimpleBezier:[Q]=NW(w);break;case _0.Step:[Q]=G$({...w,borderRadius:0});break;case _0.SmoothStep:[Q]=G$(w);break;default:[Q]=I6(w)}return ru.jsx("path",{d:Q,fill:"none",className:"react-flow__connection-path",style:u})};VW.displayName="ConnectionLine";var JX={};function lW(u=JX){let f=yu.useRef(u),l=Tf();yu.useEffect(()=>{},[u])}function UX(){let u=Tf(),f=yu.useRef(!1);yu.useEffect(()=>{},[])}function DW({nodeTypes:u,edgeTypes:f,onInit:l,onNodeClick:r,onEdgeClick:n,onNodeDoubleClick:i,onEdgeDoubleClick:y,onNodeMouseEnter:_,onNodeMouseMove:$,onNodeMouseLeave:c,onNodeContextMenu:A,onSelectionContextMenu:j,onSelectionStart:F,onSelectionEnd:U,connectionLineType:Q,connectionLineStyle:w,connectionLineComponent:L,connectionLineContainerStyle:J,selectionKeyCode:N,selectionOnDrag:q,selectionMode:W,multiSelectionKeyCode:z,panActivationKeyCode:Z,zoomActivationKeyCode:H,deleteKeyCode:E,onlyRenderVisibleElements:D,elementsSelectable:h,defaultViewport:B,translateExtent:X,minZoom:m,maxZoom:O,preventScrolling:p,defaultMarkerColor:Y,zoomOnScroll:v,zoomOnPinch:T,panOnScroll:S,panOnScrollSpeed:k,panOnScrollMode:I,zoomOnDoubleClick:b,panOnDrag:o,onPaneClick:s,onPaneMouseEnter:x,onPaneMouseMove:fu,onPaneMouseLeave:$u,onPaneScroll:tu,onPaneContextMenu:ju,paneClickDistance:Gu,nodeClickDistance:qu,onEdgeContextMenu:P,onEdgeMouseEnter:e,onEdgeMouseMove:uu,onEdgeMouseLeave:Wu,reconnectRadius:g,onReconnect:Qu,onReconnectStart:Tu,onReconnectEnd:Zu,noDragClassName:Xu,noWheelClassName:ff,noPanClassName:_f,disableKeyboardA11y:Jf,nodeExtent:mf,rfId:Yl,viewport:Al,onViewportChange:jl}){return lW(u),lW(f),UX(),_X(l),tX(Al),ru.jsx(VD,{onPaneClick:s,onPaneMouseEnter:x,onPaneMouseMove:fu,onPaneMouseLeave:$u,onPaneContextMenu:ju,onPaneScroll:tu,paneClickDistance:Gu,deleteKeyCode:E,selectionKeyCode:N,selectionOnDrag:q,selectionMode:W,onSelectionStart:F,onSelectionEnd:U,multiSelectionKeyCode:z,panActivationKeyCode:Z,zoomActivationKeyCode:H,elementsSelectable:h,zoomOnScroll:v,zoomOnPinch:T,zoomOnDoubleClick:b,panOnScroll:S,panOnScrollSpeed:k,panOnScrollMode:I,panOnDrag:o,defaultViewport:B,translateExtent:X,minZoom:m,maxZoom:O,onSelectionContextMenu:j,preventScrolling:p,noDragClassName:Xu,noWheelClassName:ff,noPanClassName:_f,disableKeyboardA11y:Jf,onViewportChange:jl,isControlledViewport:!!Al,children:ru.jsxs(yX,{children:[ru.jsx(nX,{edgeTypes:f,onEdgeClick:n,onEdgeDoubleClick:y,onReconnect:Qu,onReconnectStart:Tu,onReconnectEnd:Zu,onlyRenderVisibleElements:D,onEdgeContextMenu:P,onEdgeMouseEnter:e,onEdgeMouseMove:uu,onEdgeMouseLeave:Wu,reconnectRadius:g,defaultMarkerColor:Y,noPanClassName:_f,disableKeyboardA11y:Jf,rfId:Yl}),ru.jsx(FX,{style:w,type:Q,component:L,containerStyle:J}),ru.jsx("div",{className:"react-flow__edgelabel-renderer"}),ru.jsx(CD,{nodeTypes:u,onNodeClick:r,onNodeDoubleClick:i,onNodeMouseEnter:_,onNodeMouseMove:$,onNodeMouseLeave:c,onNodeContextMenu:A,nodeClickDistance:qu,onlyRenderVisibleElements:D,noPanClassName:_f,noDragClassName:Xu,disableKeyboardA11y:Jf,nodeExtent:mf,rfId:Yl}),ru.jsx("div",{className:"react-flow__viewport-portal"})]})})}DW.displayName="GraphView";var QX=yu.memo(DW),rW=({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,width:n,height:i,fitView:y,fitViewOptions:_,minZoom:$=0.5,maxZoom:c=2,nodeOrigin:A,nodeExtent:j,zIndexMode:F="basic"}={})=>{let U=new Map,Q=new Map,w=new Map,L=new Map,J=r??f??[],N=l??u??[],q=A??[0,0],W=j??ly;l9(w,L,J);let{nodesInitialized:z}=s6(N,U,Q,{nodeOrigin:q,nodeExtent:W,zIndexMode:F}),Z=[0,0,1];if(y&&n&&i){let H=ry(U,{filter:(B)=>!!((B.width||B.initialWidth)&&(B.height||B.initialHeight))}),{x:E,y:D,zoom:h}=K$(H,n,i,$,c,_?.padding??0.1);Z=[E,D,h]}return{rfId:"1",width:n??0,height:i??0,transform:Z,nodes:N,nodesInitialized:z,nodeLookup:U,parentLookup:Q,edges:J,edgeLookup:L,connectionLookup:w,onNodesChange:null,onEdgesChange:null,hasDefaultNodes:l!==void 0,hasDefaultEdges:r!==void 0,panZoom:null,minZoom:$,maxZoom:c,translateExtent:ly,nodeExtent:W,nodesSelectionActive:!1,userSelectionActive:!1,userSelectionRect:null,connectionMode:Xn.Strict,domNode:null,paneDragging:!1,noPanClassName:"nopan",nodeOrigin:q,nodeDragThreshold:1,connectionDragThreshold:1,snapGrid:[15,15],snapToGrid:!1,nodesDraggable:!0,nodesConnectable:!0,nodesFocusable:!0,edgesFocusable:!0,edgesReconnectable:!0,elementsSelectable:!0,elevateNodesOnSelect:!0,elevateEdgesOnSelect:!0,selectNodesOnDrag:!0,multiSelectionActive:!1,fitViewQueued:y??!1,fitViewOptions:_,fitViewResolver:null,connection:{...SA},connectionClickStartHandle:null,connectOnClick:!0,ariaLiveMessage:"",autoPanOnConnect:!0,autoPanOnNodeDrag:!0,autoPanOnNodeFocus:!0,autoPanSpeed:15,connectionRadius:20,onError:hA,isValidConnection:void 0,onSelectionChangeHandlers:[],lib:"react",debug:!1,ariaLabelConfig:YA,zIndexMode:F,onNodesChangeMiddlewareMap:new Map,onEdgesChangeMiddlewareMap:new Map}},qX=({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,width:n,height:i,fitView:y,fitViewOptions:_,minZoom:$,maxZoom:c,nodeOrigin:A,nodeExtent:j,zIndexMode:F})=>YN((U,Q)=>{async function w(){let{nodeLookup:L,panZoom:J,fitViewOptions:N,fitViewResolver:q,width:W,height:z,minZoom:Z,maxZoom:H}=Q();if(!J)return;await gq({nodes:L,width:W,height:z,panZoom:J,minZoom:Z,maxZoom:H},N),q?.resolve(!0),U({fitViewResolver:null})}return{...rW({nodes:u,edges:f,width:n,height:i,fitView:y,fitViewOptions:_,minZoom:$,maxZoom:c,nodeOrigin:A,nodeExtent:j,defaultNodes:l,defaultEdges:r,zIndexMode:F}),setNodes:(L)=>{let{nodeLookup:J,parentLookup:N,nodeOrigin:q,elevateNodesOnSelect:W,fitViewQueued:z,zIndexMode:Z,nodesSelectionActive:H}=Q(),{nodesInitialized:E,hasSelectedNodes:D}=s6(L,J,N,{nodeOrigin:q,nodeExtent:j,elevateNodesOnSelect:W,checkEquality:!0,zIndexMode:Z}),h=H&&D;if(z&&E)w(),U({nodes:L,nodesInitialized:E,fitViewQueued:!1,fitViewOptions:void 0,nodesSelectionActive:h});else U({nodes:L,nodesInitialized:E,nodesSelectionActive:h})},setEdges:(L)=>{let{connectionLookup:J,edgeLookup:N}=Q();l9(J,N,L),U({edges:L})},setDefaultNodesAndEdges:(L,J)=>{if(L){let{setNodes:N}=Q();N(L),U({hasDefaultNodes:!0})}if(J){let{setEdges:N}=Q();N(J),U({hasDefaultEdges:!0})}},updateNodeInternals:(L)=>{let{triggerNodeChanges:J,nodeLookup:N,parentLookup:q,domNode:W,nodeOrigin:z,nodeExtent:Z,debug:H,fitViewQueued:E,zIndexMode:D}=Q(),{changes:h,updatedInternals:B}=_N(L,N,q,W,z,Z,D);if(!B)return;if(iN(N,q,{nodeOrigin:z,nodeExtent:Z,zIndexMode:D}),E)w(),U({fitViewQueued:!1,fitViewOptions:void 0});else U({});if(h?.length>0){if(H)console.log("React Flow: trigger node changes",h);J?.(h)}},updateNodePositions:(L,J=!1)=>{let N=[],q=[],{nodeLookup:W,triggerNodeChanges:z,connection:Z,updateConnection:H,onNodesChangeMiddlewareMap:E}=Q();for(let[D,h]of L){let B=W.get(D),X=!!(B?.expandParent&&B?.parentId&&h?.position),m={id:D,type:"position",position:X?{x:Math.max(0,h.position.x),y:Math.max(0,h.position.y)}:h.position,dragging:J};if(B&&Z.inProgress&&Z.fromNode.id===B.id){let O=Sn(B,Z.fromHandle,Lu.Left,!0);H({...Z,from:O})}if(X&&B.parentId)N.push({id:D,parentId:B.parentId,rect:{...h.internals.positionAbsolute,width:h.measured.width??0,height:h.measured.height??0}});q.push(m)}if(N.length>0){let{parentLookup:D,nodeOrigin:h}=Q(),B=a6(N,W,D,h);q.push(...B)}for(let D of E.values())q=D(q);z(q)},triggerNodeChanges:(L)=>{let{onNodesChange:J,setNodes:N,nodes:q,hasDefaultNodes:W,debug:z}=Q();if(L?.length){if(W){let Z=lD(L,q);N(Z)}if(z)console.log("React Flow: trigger node changes",L);J?.(L)}},triggerEdgeChanges:(L)=>{let{onEdgesChange:J,setEdges:N,edges:q,hasDefaultEdges:W,debug:z}=Q();if(L?.length){if(W){let Z=rD(L,q);N(Z)}if(z)console.log("React Flow: trigger edge changes",L);J?.(L)}},addSelectedNodes:(L)=>{let{multiSelectionActive:J,edgeLookup:N,nodeLookup:q,triggerNodeChanges:W,triggerEdgeChanges:z}=Q();if(J){let Z=L.map((H)=>P1(H,!0));W(Z);return}W(ty(q,new Set([...L]),!0)),z(ty(N))},addSelectedEdges:(L)=>{let{multiSelectionActive:J,edgeLookup:N,nodeLookup:q,triggerNodeChanges:W,triggerEdgeChanges:z}=Q();if(J){let Z=L.map((H)=>P1(H,!0));z(Z);return}z(ty(N,new Set([...L]))),W(ty(q,new Set,!0))},unselectNodesAndEdges:({nodes:L,edges:J}={})=>{let{edges:N,nodes:q,nodeLookup:W,triggerNodeChanges:z,triggerEdgeChanges:Z}=Q(),H=L?L:q,E=J?J:N,D=[];for(let B of H){if(!B.selected)continue;let X=W.get(B.id);if(X)X.selected=!1;D.push(P1(B.id,!1))}let h=[];for(let B of E){if(!B.selected)continue;h.push(P1(B.id,!1))}z(D),Z(h)},setMinZoom:(L)=>{let{panZoom:J,maxZoom:N}=Q();J?.setScaleExtent([L,N]),U({minZoom:L})},setMaxZoom:(L)=>{let{panZoom:J,minZoom:N}=Q();J?.setScaleExtent([N,L]),U({maxZoom:L})},setTranslateExtent:(L)=>{Q().panZoom?.setTranslateExtent(L),U({translateExtent:L})},resetSelectedElements:()=>{let{edges:L,nodes:J,triggerNodeChanges:N,triggerEdgeChanges:q,elementsSelectable:W}=Q();if(!W)return;let z=J.reduce((H,E)=>E.selected?[...H,P1(E.id,!1)]:H,[]),Z=L.reduce((H,E)=>E.selected?[...H,P1(E.id,!1)]:H,[]);N(z),q(Z)},setNodeExtent:(L)=>{let{nodes:J,nodeLookup:N,parentLookup:q,nodeOrigin:W,elevateNodesOnSelect:z,nodeExtent:Z,zIndexMode:H}=Q();if(L[0][0]===Z[0][0]&&L[0][1]===Z[0][1]&&L[1][0]===Z[1][0]&&L[1][1]===Z[1][1])return;s6(J,N,q,{nodeOrigin:W,nodeExtent:L,elevateNodesOnSelect:z,checkEquality:!1,zIndexMode:H}),U({nodeExtent:L})},panBy:(L)=>{let{transform:J,width:N,height:q,panZoom:W,translateExtent:z}=Q();return $N({delta:L,panZoom:W,transform:J,translateExtent:z,width:N,height:q})},setCenter:async(L,J,N)=>{let{width:q,height:W,maxZoom:z,panZoom:Z}=Q();if(!Z)return Promise.resolve(!1);let H=typeof N?.zoom<"u"?N.zoom:z;return await Z.setViewport({x:q/2-L*H,y:W/2-J*H,zoom:H},{duration:N?.duration,ease:N?.ease,interpolate:N?.interpolate}),Promise.resolve(!0)},cancelConnection:()=>{U({connection:{...SA}})},updateConnection:(L)=>{U({connection:L})},reset:()=>U({...rW()})}},Object.is);function NX({initialNodes:u,initialEdges:f,defaultNodes:l,defaultEdges:r,initialWidth:n,initialHeight:i,initialMinZoom:y,initialMaxZoom:_,initialFitViewOptions:$,fitView:c,nodeOrigin:A,nodeExtent:j,zIndexMode:F,children:U}){let[Q]=yu.useState(()=>qX({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,width:n,height:i,fitView:c,minZoom:y,maxZoom:_,fitViewOptions:$,nodeOrigin:A,nodeExtent:j,zIndexMode:F}));return ru.jsx(mV,{value:Q,children:ru.jsx(yD,{children:U})})}function WX({children:u,nodes:f,edges:l,defaultNodes:r,defaultEdges:n,width:i,height:y,fitView:_,fitViewOptions:$,minZoom:c,maxZoom:A,nodeOrigin:j,nodeExtent:F,zIndexMode:U}){if(yu.useContext(r8))return ru.jsx(ru.Fragment,{children:u});return ru.jsx(NX,{initialNodes:f,initialEdges:l,defaultNodes:r,defaultEdges:n,initialWidth:i,initialHeight:y,fitView:_,initialFitViewOptions:$,initialMinZoom:c,initialMaxZoom:A,nodeOrigin:j,nodeExtent:F,zIndexMode:U,children:u})}var wX={width:"100%",height:"100%",overflow:"hidden",position:"relative",zIndex:0};function LX({nodes:u,edges:f,defaultNodes:l,defaultEdges:r,className:n,nodeTypes:i,edgeTypes:y,onNodeClick:_,onEdgeClick:$,onInit:c,onMove:A,onMoveStart:j,onMoveEnd:F,onConnect:U,onConnectStart:Q,onConnectEnd:w,onClickConnectStart:L,onClickConnectEnd:J,onNodeMouseEnter:N,onNodeMouseMove:q,onNodeMouseLeave:W,onNodeContextMenu:z,onNodeDoubleClick:Z,onNodeDragStart:H,onNodeDrag:E,onNodeDragStop:D,onNodesDelete:h,onEdgesDelete:B,onDelete:X,onSelectionChange:m,onSelectionDragStart:O,onSelectionDrag:p,onSelectionDragStop:Y,onSelectionContextMenu:v,onSelectionStart:T,onSelectionEnd:S,onBeforeDelete:k,connectionMode:I,connectionLineType:b=_0.Bezier,connectionLineStyle:o,connectionLineComponent:s,connectionLineContainerStyle:x,deleteKeyCode:fu="Backspace",selectionKeyCode:$u="Shift",selectionOnDrag:tu=!1,selectionMode:ju=S1.Full,panActivationKeyCode:Gu="Space",multiSelectionKeyCode:qu=_y()?"Meta":"Control",zoomActivationKeyCode:P=_y()?"Meta":"Control",snapToGrid:e,snapGrid:uu,onlyRenderVisibleElements:Wu=!1,selectNodesOnDrag:g,nodesDraggable:Qu,autoPanOnNodeFocus:Tu,nodesConnectable:Zu,nodesFocusable:Xu,nodeOrigin:ff=_W,edgesFocusable:_f,edgesReconnectable:Jf,elementsSelectable:mf=!0,defaultViewport:Yl=sV,minZoom:Al=0.5,maxZoom:jl=2,translateExtent:Hr=ly,preventScrolling:al=!0,nodeExtent:Au,defaultMarkerColor:vu="#b1b1b7",zoomOnScroll:Kl=!0,zoomOnPinch:Su=!0,panOnScroll:pf=!1,panOnScrollSpeed:Gl=0.5,panOnScrollMode:zl=C0.Free,zoomOnDoubleClick:Pf=!0,panOnDrag:rf=!0,onPaneClick:Ir,onPaneMouseEnter:Fr,onPaneMouseMove:x1,onPaneMouseLeave:gn,onPaneScroll:b0,onPaneContextMenu:lu,paneClickDistance:Ou=1,nodeClickDistance:mu=0,children:ou,onReconnect:Af,onReconnectStart:vf,onReconnectEnd:pu,onEdgeContextMenu:ll,onEdgeDoubleClick:J0,onEdgeMouseEnter:Gf,onEdgeMouseMove:d9,onEdgeMouseLeave:kf,reconnectRadius:ol=10,onNodesChange:Wy,onEdgesChange:C$,noDragClassName:wy="nodrag",noWheelClassName:R$="nowheel",noPanClassName:h1="nopan",fitView:Ly,fitViewOptions:Tl,connectOnClick:gr,attributionPosition:x$,proOptions:h$,defaultEdgeOptions:Ky,elevateNodesOnSelect:b$=!0,elevateEdgesOnSelect:sr=!1,disableKeyboardA11y:Gy=!1,autoPanOnConnect:If,autoPanOnNodeDrag:V8,autoPanSpeed:v$,connectionRadius:Br,isValidConnection:sn,onError:an,style:b1,id:v0,nodeDragThreshold:zy,connectionDragThreshold:e9,viewport:v1,onViewportChange:u7,width:k$,height:D8,colorMode:X8="light",debug:Y8,onScroll:U0,ariaLabelConfig:k1,zIndexMode:k0="basic",...Sl},I$){let on=v0||"1",Jr=eV(X8),g$=yu.useCallback((s$)=>{s$.currentTarget.scrollTo({top:0,left:0,behavior:"instant"}),U0?.(s$)},[U0]);return ru.jsx("div",{"data-testid":"rf__wrapper",...Sl,onScroll:g$,style:{...b1,...wX},ref:I$,className:Xf(["react-flow",n,Jr]),id:v0,role:"application",children:ru.jsxs(WX,{nodes:u,edges:f,width:k$,height:D8,fitView:Ly,fitViewOptions:Tl,minZoom:Al,maxZoom:jl,nodeOrigin:ff,nodeExtent:Au,zIndexMode:k0,children:[ru.jsx(dV,{nodes:u,edges:f,defaultNodes:l,defaultEdges:r,onConnect:U,onConnectStart:Q,onConnectEnd:w,onClickConnectStart:L,onClickConnectEnd:J,nodesDraggable:Qu,autoPanOnNodeFocus:Tu,nodesConnectable:Zu,nodesFocusable:Xu,edgesFocusable:_f,edgesReconnectable:Jf,elementsSelectable:mf,elevateNodesOnSelect:b$,elevateEdgesOnSelect:sr,minZoom:Al,maxZoom:jl,nodeExtent:Au,onNodesChange:Wy,onEdgesChange:C$,snapToGrid:e,snapGrid:uu,connectionMode:I,translateExtent:Hr,connectOnClick:gr,defaultEdgeOptions:Ky,fitView:Ly,fitViewOptions:Tl,onNodesDelete:h,onEdgesDelete:B,onDelete:X,onNodeDragStart:H,onNodeDrag:E,onNodeDragStop:D,onSelectionDrag:p,onSelectionDragStart:O,onSelectionDragStop:Y,onMove:A,onMoveStart:j,onMoveEnd:F,noPanClassName:h1,nodeOrigin:ff,rfId:on,autoPanOnConnect:If,autoPanOnNodeDrag:V8,autoPanSpeed:v$,onError:an,connectionRadius:Br,isValidConnection:sn,selectNodesOnDrag:g,nodeDragThreshold:zy,connectionDragThreshold:e9,onBeforeDelete:k,debug:Y8,ariaLabelConfig:k1,zIndexMode:k0}),ru.jsx(QX,{onInit:c,onNodeClick:_,onEdgeClick:$,onNodeMouseEnter:N,onNodeMouseMove:q,onNodeMouseLeave:W,onNodeContextMenu:z,onNodeDoubleClick:Z,nodeTypes:i,edgeTypes:y,connectionLineType:b,connectionLineStyle:o,connectionLineComponent:s,connectionLineContainerStyle:x,selectionKeyCode:$u,selectionOnDrag:tu,selectionMode:ju,deleteKeyCode:fu,multiSelectionKeyCode:qu,panActivationKeyCode:Gu,zoomActivationKeyCode:P,onlyRenderVisibleElements:Wu,defaultViewport:Yl,translateExtent:Hr,minZoom:Al,maxZoom:jl,preventScrolling:al,zoomOnScroll:Kl,zoomOnPinch:Su,zoomOnDoubleClick:Pf,panOnScroll:pf,panOnScrollSpeed:Gl,panOnScrollMode:zl,panOnDrag:rf,onPaneClick:Ir,onPaneMouseEnter:Fr,onPaneMouseMove:x1,onPaneMouseLeave:gn,onPaneScroll:b0,onPaneContextMenu:lu,paneClickDistance:Ou,nodeClickDistance:mu,onSelectionContextMenu:v,onSelectionStart:T,onSelectionEnd:S,onReconnect:Af,onReconnectStart:vf,onReconnectEnd:pu,onEdgeContextMenu:ll,onEdgeDoubleClick:J0,onEdgeMouseEnter:Gf,onEdgeMouseMove:d9,onEdgeMouseLeave:kf,reconnectRadius:ol,defaultMarkerColor:vu,noDragClassName:wy,noWheelClassName:R$,noPanClassName:h1,rfId:on,disableKeyboardA11y:Gy,nodeExtent:Au,viewport:v1,onViewportChange:u7}),ru.jsx(gV,{onSelectionChange:m}),ou,ru.jsx(hV,{proOptions:h$,position:x$}),ru.jsx(xV,{rfId:on,disableKeyboardA11y:Gy})]})})}var XW=tW(LX);var fb=tr.error014();function KX({dimensions:u,lineWidth:f,variant:l,className:r}){return ru.jsx("path",{strokeWidth:f,d:`M${u[0]/2} 0 V${u[1]} M0 ${u[1]/2} H${u[0]}`,className:Xf(["react-flow__background-pattern",l,r])})}function GX({radius:u,className:f}){return ru.jsx("circle",{cx:u,cy:u,r:u,className:Xf(["react-flow__background-pattern","dots",f])})}var pn;(function(u){u.Lines="lines",u.Dots="dots",u.Cross="cross"})(pn||(pn={}));var zX={[pn.Dots]:1,[pn.Lines]:1,[pn.Cross]:6},TX=(u)=>({transform:u.transform,patternId:`pattern-${u.rfId}`});function YW({id:u,variant:f=pn.Dots,gap:l=20,size:r,lineWidth:n=1,offset:i=0,color:y,bgColor:_,style:$,className:c,patternClassName:A}){let j=yu.useRef(null),{transform:F,patternId:U}=uf(TX,Kf),Q=r||zX[f],w=f===pn.Dots,L=f===pn.Cross,J=Array.isArray(l)?l:[l,l],N=[J[0]*F[2]||1,J[1]*F[2]||1],q=Q*F[2],W=Array.isArray(i)?i:[i,i],z=L?[q,q]:N,Z=[W[0]*F[2]||1+z[0]/2,W[1]*F[2]||1+z[1]/2],H=`${U}${u?u:""}`;return ru.jsxs("svg",{className:Xf(["react-flow__background",c]),style:{...$,...i8,"--xy-background-color-props":_,"--xy-background-pattern-color-props":y},ref:j,"data-testid":"rf__background",children:[ru.jsx("pattern",{id:H,x:F[0]%N[0],y:F[1]%N[1],width:N[0],height:N[1],patternUnits:"userSpaceOnUse",patternTransform:`translate(-${Z[0]},-${Z[1]})`,children:w?ru.jsx(GX,{radius:q/2,className:A}):ru.jsx(KX,{dimensions:z,lineWidth:n,variant:f,className:A})}),ru.jsx("rect",{x:"0",y:"0",width:"100%",height:"100%",fill:`url(#${H})`})]})}YW.displayName="Background";var SW=yu.memo(YW);function EX(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 32",children:ru.jsx("path",{d:"M32 18.133H18.133V32h-4.266V18.133H0v-4.266h13.867V0h4.266v13.867H32z"})})}function ZX(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 5",children:ru.jsx("path",{d:"M0 0h32v4.2H0z"})})}function OX(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 32 30",children:ru.jsx("path",{d:"M3.692 4.63c0-.53.4-.938.939-.938h5.215V0H4.708C2.13 0 0 2.054 0 4.63v5.216h3.692V4.631zM27.354 0h-5.2v3.692h5.17c.53 0 .984.4.984.939v5.215H32V4.631A4.624 4.624 0 0027.354 0zm.954 24.83c0 .532-.4.94-.939.94h-5.215v3.768h5.215c2.577 0 4.631-2.13 4.631-4.707v-5.139h-3.692v5.139zm-23.677.94c-.531 0-.939-.4-.939-.94v-5.138H0v5.139c0 2.577 2.13 4.707 4.708 4.707h5.138V25.77H4.631z"})})}function HX(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:ru.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0 8 0 4.571 3.429 4.571 7.619v3.048H3.048A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047zm4.724-13.866H7.467V7.619c0-2.59 2.133-4.724 4.723-4.724 2.591 0 4.724 2.133 4.724 4.724v3.048z"})})}function BX(){return ru.jsx("svg",{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 25 32",children:ru.jsx("path",{d:"M21.333 10.667H19.81V7.619C19.81 3.429 16.38 0 12.19 0c-4.114 1.828-1.37 2.133.305 2.438 1.676.305 4.42 2.59 4.42 5.181v3.048H3.047A3.056 3.056 0 000 13.714v15.238A3.056 3.056 0 003.048 32h18.285a3.056 3.056 0 003.048-3.048V13.714a3.056 3.056 0 00-3.048-3.047zM12.19 24.533a3.056 3.056 0 01-3.047-3.047 3.056 3.056 0 013.047-3.048 3.056 3.056 0 013.048 3.048 3.056 3.056 0 01-3.048 3.047z"})})}function f8({children:u,className:f,...l}){return ru.jsx("button",{type:"button",className:Xf(["react-flow__controls-button",f]),...l,children:u})}var VX=(u)=>({isInteractive:u.nodesDraggable||u.nodesConnectable||u.elementsSelectable,minZoomReached:u.transform[2]<=u.minZoom,maxZoomReached:u.transform[2]>=u.maxZoom,ariaLabelConfig:u.ariaLabelConfig});function mW({style:u,showZoom:f=!0,showFitView:l=!0,showInteractive:r=!0,fitViewOptions:n,onZoomIn:i,onZoomOut:y,onFitView:_,onInteractiveChange:$,className:c,children:A,position:j="bottom-left",orientation:F="vertical","aria-label":U}){let Q=Tf(),{isInteractive:w,minZoomReached:L,maxZoomReached:J,ariaLabelConfig:N}=uf(VX,Kf),{zoomIn:q,zoomOut:W,fitView:z}=t9(),Z=()=>{q(),i?.()},H=()=>{W(),y?.()},E=()=>{z(n),_?.()},D=()=>{Q.setState({nodesDraggable:!w,nodesConnectable:!w,elementsSelectable:!w}),$?.(!w)};return ru.jsxs(n8,{className:Xf(["react-flow__controls",F==="horizontal"?"horizontal":"vertical",c]),position:j,style:u,"data-testid":"rf__controls","aria-label":U??N["controls.ariaLabel"],children:[f&&ru.jsxs(ru.Fragment,{children:[ru.jsx(f8,{onClick:Z,className:"react-flow__controls-zoomin",title:N["controls.zoomIn.ariaLabel"],"aria-label":N["controls.zoomIn.ariaLabel"],disabled:J,children:ru.jsx(EX,{})}),ru.jsx(f8,{onClick:H,className:"react-flow__controls-zoomout",title:N["controls.zoomOut.ariaLabel"],"aria-label":N["controls.zoomOut.ariaLabel"],disabled:L,children:ru.jsx(ZX,{})})]}),l&&ru.jsx(f8,{className:"react-flow__controls-fitview",onClick:E,title:N["controls.fitView.ariaLabel"],"aria-label":N["controls.fitView.ariaLabel"],children:ru.jsx(OX,{})}),r&&ru.jsx(f8,{className:"react-flow__controls-interactive",onClick:D,title:N["controls.interactive.ariaLabel"],"aria-label":N["controls.interactive.ariaLabel"],children:w?ru.jsx(BX,{}):ru.jsx(HX,{})}),A]})}mW.displayName="Controls";var pW=yu.memo(mW);function DX({id:u,x:f,y:l,width:r,height:n,style:i,color:y,strokeColor:_,strokeWidth:$,className:c,borderRadius:A,shapeRendering:j,selected:F,onClick:U}){let{background:Q,backgroundColor:w}=i||{},L=y||Q||w;return ru.jsx("rect",{className:Xf(["react-flow__minimap-node",{selected:F},c]),x:f,y:l,rx:A,ry:A,width:r,height:n,style:{fill:L,stroke:_,strokeWidth:$},shapeRendering:j,onClick:U?(J)=>U(J,u):void 0})}var XX=yu.memo(DX),YX=(u)=>u.nodes.map((f)=>f.id),y9=(u)=>u instanceof Function?u:()=>u;function SX({nodeStrokeColor:u,nodeColor:f,nodeClassName:l="",nodeBorderRadius:r=5,nodeStrokeWidth:n,nodeComponent:i=XX,onClick:y}){let _=uf(YX,Kf),$=y9(f),c=y9(u),A=y9(l),j=typeof window>"u"||!!window.chrome?"crispEdges":"geometricPrecision";return ru.jsx(ru.Fragment,{children:_.map((F)=>ru.jsx(pX,{id:F,nodeColorFunc:$,nodeStrokeColorFunc:c,nodeClassNameFunc:A,nodeBorderRadius:r,nodeStrokeWidth:n,NodeComponent:i,onClick:y,shapeRendering:j},F))})}function mX({id:u,nodeColorFunc:f,nodeStrokeColorFunc:l,nodeClassNameFunc:r,nodeBorderRadius:n,nodeStrokeWidth:i,shapeRendering:y,NodeComponent:_,onClick:$}){let{node:c,x:A,y:j,width:F,height:U}=uf((Q)=>{let w=Q.nodeLookup.get(u);if(!w)return{node:void 0,x:0,y:0,width:0,height:0};let L=w.internals.userNode,{x:J,y:N}=w.internals.positionAbsolute,{width:q,height:W}=$0(L);return{node:L,x:J,y:N,width:q,height:W}},Kf);if(!c||c.hidden||!bA(c))return null;return ru.jsx(_,{x:A,y:j,width:F,height:U,style:c.style,selected:!!c.selected,className:r(c),color:f(c),borderRadius:n,strokeColor:l(c),strokeWidth:i,shapeRendering:y,onClick:$,id:c.id})}var pX=yu.memo(mX),PX=yu.memo(SX),MX=200,CX=150,RX=(u)=>!u.hidden,xX=(u)=>{let f={x:-u.transform[0]/u.transform[2],y:-u.transform[1]/u.transform[2],width:u.width/u.transform[2],height:u.height/u.transform[2]};return{viewBB:f,boundingRect:u.nodeLookup.size>0?RA(ry(u.nodeLookup,{filter:RX}),f):f,rfId:u.rfId,panZoom:u.panZoom,translateExtent:u.translateExtent,flowWidth:u.width,flowHeight:u.height,ariaLabelConfig:u.ariaLabelConfig}},hX="react-flow__minimap-desc";function PW({style:u,className:f,nodeStrokeColor:l,nodeColor:r,nodeClassName:n="",nodeBorderRadius:i=5,nodeStrokeWidth:y,nodeComponent:_,bgColor:$,maskColor:c,maskStrokeColor:A,maskStrokeWidth:j,position:F="bottom-right",onClick:U,onNodeClick:Q,pannable:w=!1,zoomable:L=!1,ariaLabel:J,inversePan:N,zoomStep:q=1,offsetScale:W=5}){let z=Tf(),Z=yu.useRef(null),{boundingRect:H,viewBB:E,rfId:D,panZoom:h,translateExtent:B,flowWidth:X,flowHeight:m,ariaLabelConfig:O}=uf(xX,Kf),p=u?.width??MX,Y=u?.height??CX,v=H.width/p,T=H.height/Y,S=Math.max(v,T),k=S*p,I=S*Y,b=W*S,o=H.x-(k-H.width)/2-b,s=H.y-(I-H.height)/2-b,x=k+b*2,fu=I+b*2,$u=`${hX}-${D}`,tu=yu.useRef(0),ju=yu.useRef();tu.current=S,yu.useEffect(()=>{if(Z.current&&h)return ju.current=UN({domNode:Z.current,panZoom:h,getTransform:()=>z.getState().transform,getViewScale:()=>tu.current}),()=>{ju.current?.destroy()}},[h]),yu.useEffect(()=>{ju.current?.update({translateExtent:B,width:X,height:m,inversePan:N,pannable:w,zoomStep:q,zoomable:L})},[w,L,N,q,B,X,m]);let Gu=U?(e)=>{let[uu,Wu]=ju.current?.pointer(e)||[0,0];U(e,{x:uu,y:Wu})}:void 0,qu=Q?yu.useCallback((e,uu)=>{let Wu=z.getState().nodeLookup.get(uu).internals.userNode;Q(e,Wu)},[]):void 0,P=J??O["minimap.ariaLabel"];return ru.jsx(n8,{position:F,style:{...u,"--xy-minimap-background-color-props":typeof $==="string"?$:void 0,"--xy-minimap-mask-background-color-props":typeof c==="string"?c:void 0,"--xy-minimap-mask-stroke-color-props":typeof A==="string"?A:void 0,"--xy-minimap-mask-stroke-width-props":typeof j==="number"?j*S:void 0,"--xy-minimap-node-background-color-props":typeof r==="string"?r:void 0,"--xy-minimap-node-stroke-color-props":typeof l==="string"?l:void 0,"--xy-minimap-node-stroke-width-props":typeof y==="number"?y:void 0},className:Xf(["react-flow__minimap",f]),"data-testid":"rf__minimap",children:ru.jsxs("svg",{width:p,height:Y,viewBox:`${o} ${s} ${x} ${fu}`,className:"react-flow__minimap-svg",role:"img","aria-labelledby":$u,ref:Z,onClick:Gu,children:[P&&ru.jsx("title",{id:$u,children:P}),ru.jsx(PX,{onClick:qu,nodeColor:r,nodeStrokeColor:l,nodeBorderRadius:i,nodeClassName:n,nodeStrokeWidth:y,nodeComponent:_}),ru.jsx("path",{className:"react-flow__minimap-mask",d:`M${o-b},${s-b}h${x+b*2}v${fu+b*2}h${-x-b*2}z + M${E.x},${E.y}h${E.width}v${E.height}h${-E.width}z`,fillRule:"evenodd",pointerEvents:"none"})]})})}PW.displayName="MiniMap";var lb=yu.memo(PW),bX=(u)=>(f)=>u?`${Math.max(1/f.transform[2],1)}`:void 0,vX={[mn.Line]:"right",[mn.Handle]:"bottom-right"};function kX({nodeId:u,position:f,variant:l=mn.Handle,className:r,style:n=void 0,children:i,color:y,minWidth:_=10,minHeight:$=10,maxWidth:c=Number.MAX_VALUE,maxHeight:A=Number.MAX_VALUE,keepAspectRatio:j=!1,resizeDirection:F,autoScale:U=!0,shouldResize:Q,onResizeStart:w,onResize:L,onResizeEnd:J}){let N=FW(),q=typeof u==="string"?u:N,W=Tf(),z=yu.useRef(null),Z=l===mn.Handle,H=uf(yu.useCallback(bX(Z&&U),[Z,U]),Kf),E=yu.useRef(null),D=f??vX[l];yu.useEffect(()=>{if(!z.current||!q)return;if(!E.current)E.current=wN({domNode:z.current,nodeId:q,getStoreItems:()=>{let{nodeLookup:B,transform:X,snapGrid:m,snapToGrid:O,nodeOrigin:p,domNode:Y}=W.getState();return{nodeLookup:B,transform:X,snapGrid:m,snapToGrid:O,nodeOrigin:p,paneDomNode:Y}},onChange:(B,X)=>{let{triggerNodeChanges:m,nodeLookup:O,parentLookup:p,nodeOrigin:Y}=W.getState(),v=[],T={x:B.x,y:B.y},S=O.get(q);if(S&&S.expandParent&&S.parentId){let k=S.origin??Y,I=B.width??S.measured.width??0,b=B.height??S.measured.height??0,o={id:S.id,parentId:S.parentId,rect:{width:I,height:b,...vA({x:B.x??S.position.x,y:B.y??S.position.y},{width:I,height:b},S.parentId,O,k)}},s=a6([o],O,p,Y);v.push(...s),T.x=B.x?Math.max(k[0]*I,B.x):void 0,T.y=B.y?Math.max(k[1]*b,B.y):void 0}if(T.x!==void 0&&T.y!==void 0){let k={id:q,type:"position",position:{...T}};v.push(k)}if(B.width!==void 0&&B.height!==void 0){let I={id:q,type:"dimensions",resizing:!0,setAttributes:!F?!0:F==="horizontal"?"width":"height",dimensions:{width:B.width,height:B.height}};v.push(I)}for(let k of X){let I={...k,type:"position"};v.push(I)}m(v)},onEnd:({width:B,height:X})=>{let m={id:q,type:"dimensions",resizing:!1,dimensions:{width:B,height:X}};W.getState().triggerNodeChanges([m])}});return E.current.update({controlPosition:D,boundaries:{minWidth:_,minHeight:$,maxWidth:c,maxHeight:A},keepAspectRatio:j,resizeDirection:F,onResizeStart:w,onResize:L,onResizeEnd:J,shouldResize:Q}),()=>{E.current?.destroy()}},[D,_,$,c,A,j,w,L,J,Q]);let h=D.split("-");return ru.jsx("div",{className:Xf(["react-flow__resize-control","nodrag",...h,l,r]),ref:z,style:{...n,scale:H,...y&&{[Z?"backgroundColor":"borderColor"]:y}},children:i})}var rb=yu.memo(kX);var G=Rn.default.createElement,{useEffect:A0}=Rn.default,kl=Rn.default.useState,Cn=Rn.default.useRef,B$=[{id:"in-left",side:"left",position:Lu.Left,style:{top:"50%"}},{id:"in-top-left",side:"top",slot:"left",slotIndex:-1,position:Lu.Top,style:{left:"28%"}},{id:"in-top-mid",side:"top",slot:"mid",slotIndex:0,position:Lu.Top,style:{left:"50%"}},{id:"in-top-right",side:"top",slot:"right",slotIndex:1,position:Lu.Top,style:{left:"72%"}},{id:"in-bottom-left",side:"bottom",slot:"left",slotIndex:-1,position:Lu.Bottom,style:{left:"28%"}},{id:"in-bottom-mid",side:"bottom",slot:"mid",slotIndex:0,position:Lu.Bottom,style:{left:"50%"}},{id:"in-bottom-right",side:"bottom",slot:"right",slotIndex:1,position:Lu.Bottom,style:{left:"72%"}}],Z$=[{id:"out-right",position:Lu.Right,style:{top:"50%"}}],MW=["#4eb7a8","#d7a13a","#69aee8","#e0835f","#b7d86b","#d98bd2","#5fc6bf"],jy=236,Fy=88,CW=15000,IX=10,A9=96,t0=72,j9=64,RW=12;function y8(){return typeof document>"u"||document.visibilityState!=="hidden"}function xW(u,f){let l=Number.parseFloat(String(u||""));return Number.isFinite(l)?l/100:f}function gX(u,f,l){let r=String(u.side||"");if(r!=="top"&&r!=="bottom")return 0;let n=Number(u.slotIndex||0),i=r==="top"?"in-top-mid":"in-bottom-mid",y=f.get(u.id)||0,_=f.get(i)||0;if(n===0)return _===0?-26:28+y*74;let $=l===0?Math.abs(n)*2:Math.sign(l)===Math.sign(n)?-3:3;if(_>0&&y===0)return-14+$;return 8+y*74+$}function _8(u){let f=u.filter((i,y)=>{let _=u[y-1];return!_||Math.abs(_.x-i.x)>0.5||Math.abs(_.y-i.y)>0.5});if(f.length<2)return"";let l=`M ${f[0].x},${f[0].y}`,r=f[0];for(let i=1;i0.5||Math.abs(F.y-r.y)>0.5)l+=` L ${F.x},${F.y}`;l+=` Q ${_.x},${_.y} ${U.x},${U.y}`,r=U}let n=f[f.length-1];return`${l} L ${n.x},${n.y}`}function yw(u,f,l,r,n,i,y=""){let _=l>=u,$=Math.max(1,Math.abs(l-u)),c=Math.abs(r-f),A=Math.max(34,Math.min(118,$*0.26)),j=Math.min(280,Math.abs(i));if(_&&n===Lu.Left&&j<4&&c<28&&$<420)return`M ${u},${f} C ${u+A},${f} ${l-A},${r} ${l},${r}`;if(_&&n===Lu.Left&&(y==="direct-forward-left"||$<=260&&c<=210)){let J=Math.max(42,Math.min(140,$*0.48)),N=Math.max(-28,Math.min(28,i*0.18));return`M ${u},${f} C ${u+J},${f+N} ${l-J},${r} ${l},${r}`}if(_){let J=u+A;if(n===Lu.Top||n===Lu.Bottom){let W=n===Lu.Top?-1:1,z=r+W*(54+j*0.42);return _8([{x:u,y:f},{x:J,y:f},{x:J+Math.min(120,$*0.18),y:z},{x:l,y:z},{x:l,y:r+W*34},{x:l,y:r}])}let N=l-A,q=(f+r)/2+i;return _8([{x:u,y:f},{x:J,y:f},{x:J+Math.min(110,$*0.16),y:q},{x:N-Math.min(90,$*0.12),y:q},{x:N,y:r},{x:l,y:r}])}let Q=n===Lu.Bottom?1:n===Lu.Top?-1:i>=0?1:-1,w=Math.max(u,l)+92+Math.min(180,j*0.52),L=Q<0?Math.min(f,r)-84-j*0.62:Math.max(f,r)+84+j*0.62;if(n===Lu.Top||n===Lu.Bottom)return _8([{x:u,y:f},{x:u+A,y:f},{x:w,y:L},{x:l,y:L},{x:l,y:r+Q*38},{x:l,y:r}]);return _8([{x:u,y:f},{x:u+A,y:f},{x:w,y:L},{x:l-A,y:L},{x:l-A,y:r},{x:l,y:r}])}function sX({data:u}){return G("div",{className:"pipeline-flow-node-body"},B$.map((f)=>G(M1,{key:f.id,id:f.id,type:"target",position:f.position,isConnectable:!1,className:`pipeline-flow-handle input ${f.side} slot-${f.slot||"mid"}`,style:f.style})),Z$.map((f)=>G(M1,{key:f.id,id:f.id,type:"source",position:f.position,isConnectable:!1,className:"pipeline-flow-handle output right",style:f.style})),u?.label)}function aX({id:u,sourceX:f,sourceY:l,targetX:r,targetY:n,targetPosition:i,markerEnd:y,markerStart:_,style:$,data:c}){let A=Number(c?.laneOffset||0),j=yw(f,l,r,n,i,A,String(c?.routeMode||""));return G(cy,{id:u,path:j,markerEnd:y,markerStart:_,style:$,interactionWidth:28})}var oX={pipelineCurve:aX},dX={pipelineNode:sX};function A8(u){if(!u)return"--";let f=new Date(u);if(Number.isNaN(f.getTime()))return"--";return $f(f)}function Er(u){let f=Number(u);if(!Number.isFinite(f)||f<0)return"--";let l=Math.round(f/1000);if(l<60)return`${l}s`;if(l<3600)return`${Math.floor(l/60)}m ${l%60}s`;return`${Math.floor(l/3600)}h ${Math.floor(l%3600/60)}m`}function F9(u){let f=Number(u);if(!Number.isFinite(f))return"--";return f.toLocaleString("zh-CN")}function hW(u){let f=Number(u);if(!Number.isFinite(f))return"--";return`${Math.round(Math.max(0,Math.min(1,f))*100)}%`}function Du(u){return typeof u==="object"&&u!==null&&!Array.isArray(u)}function Vu(u){return Array.isArray(u)?u:[]}function ku(u){if(!u)return null;let f=new Date(u);return Number.isNaN(f.getTime())?null:f.getTime()}function V$(u){return Number.isFinite(Number(u))?new Date(Number(u)).toISOString():""}function Y$(...u){for(let f of u){let l=ku(f);if(l!==null)return new Date(l).toISOString()}return""}function O9(...u){let f=u.map(ku).filter((l)=>l!==null);return f.length>0?new Date(Math.max(...f)).toISOString():""}function H9(u){return["succeeded","failed","skipped","cancelled","canceled","completed"].includes(String(u||"").toLowerCase())}function _w(u){let f=cw(u).toLowerCase();return["running","active","in-progress","in_progress"].includes(f)}function bW(u,f="status"){return u.reduce((l,r)=>{let n=String(r?.[f]||"unknown").toLowerCase();return l[n]=(l[n]||0)+1,l},{})}function $w(u){if(!u||typeof u!=="string")return null;try{let f=JSON.parse(u);return Du(f)?f:null}catch{return null}}function J9(u){let f=u.map($w).filter((i)=>Boolean(i)),l=f.flatMap((i)=>[i.timestamp,i.createdAt,i.updatedAt]).filter(Boolean),r=O9(...l),n=Array.from(new Set(f.map((i)=>String(i.event||i.action||i.type||"")).filter(Boolean))).slice(0,3);return{total:u.length,parsed:f.length,lastAt:r,eventKinds:n}}function j8(u){if(u===null||u===void 0)return"--";if(typeof u==="boolean")return u?"是":"否";if(typeof u==="number")return String(u);if(typeof u==="string")return u.length>80?`${u.slice(0,77)}...`:u;if(Array.isArray(u))return`${u.length} 项`;if(typeof u==="object")return`${Object.keys(u).length} 字段`;return String(u)}function tw(u,f=280){if(u===null||u===void 0)return"";let r=(typeof u==="string"?u:String(u)).replace(/\r\n/gu,` +`).trim();return r.length>f?`${r.slice(0,Math.max(0,f-1))}...`:r}function cw(u){if(typeof u==="string")return u;if(Du(u))return String(u.status||u.state||u.phase||"unknown");return"unknown"}function eX(u){return u.filter((f)=>f&&f.value!==void 0&&f.value!==null&&String(f.value)!=="")}function w9({items:u}){let f=eX(Vu(u));return G("div",{className:"pipeline-kv-grid"},f.map((l)=>G("span",{key:l.label},G("b",null,l.label),G("span",null,l.value))))}function B9({items:u}){let f=Vu(u).map((l)=>String(l||"")).filter(Boolean);if(f.length===0)return null;return G("div",{className:"pipeline-chip-row"},f.map((l,r)=>G("span",{key:`${r}-${l}`},l)))}function L9(u,f){let l=String(f?.procedureRunId||""),r=Vu(u?.procedureRuns);return r.find((n)=>String(Zr(n))===l)||r.at(-1)||null}function uY(u,f){let l=String(f||"");if(!l)return null;return Vu(u?.procedureRuns).find((r)=>Zr(r)===l)||null}function U9(u){return Vu(u?.attempts).length}function vW(u){return Vu(u?.attempts).reduce((f,l)=>f+W8(l).length,0)}function W8(u){return Vu(u?.opencodeMessages?.steps).filter(Du)}function Aw(u){let f=String(u?.status||"").toLowerCase();if(["error","failed","failure"].includes(f))return"failed";if(["completed","succeeded","success"].includes(f))return"succeeded";if(["running","started","in_progress"].includes(f))return"running";return"unknown"}function fY(u,f){let l=G9(u.map((i)=>i?.agent)).slice(0,3),r=G9(u.map((i)=>i?.model)).slice(0,3),n=f.length<=2?f.map((i)=>`session ${i}`):[`sessions ${f.length}`,...f.slice(0,2).map((i)=>`session ${i}`)];return[...l.map((i)=>`agent ${i}`),...r.map((i)=>`model ${i}`),...n]}function O$(u,f=0){return String(u?.messageId||u?.index||"")||`step-${f}`}function lY({steps:u,sessionIds:f,sessionFacts:l,matchedStepKey:r}){let n=Vu(u),i=n.findIndex((L,J)=>O$(L,J)===r),y=i>=0?n[i]:null,_=n.flatMap((L)=>[ku(L?.createdAt),ku(L?.completedAt)]).filter((L)=>L!==null),$=_.length>0?Math.min(..._):null,c=_.length>0?Math.max(..._):null,A=$!==null&&c!==null?Math.max(0,c-$):null,j=n.reduce((L,J)=>L+Vu(J?.parts).filter((N)=>String(N?.type||"").toLowerCase()==="tool").length,0),F=n.reduce((L,J)=>L+Vu(J?.parts).filter((N)=>["text","reasoning"].includes(String(N?.type||"").toLowerCase())).length,0),U=n.reduce((L,J)=>L+Vu(J?.parts).filter((N)=>String(N?.type||"").toLowerCase()==="tool"&&Aw(N)==="failed").length,0),Q=[`${n.length} steps`,`${f.length} sessions`,`${F} messages`,`${j} tools`,A!==null?`duration ${Er(A)}`:"",U>0?`${U} failed tools`:""].filter(Boolean),w=y?[`Step ${y?.index??i+1}`,String(y?.role||"role --"),y?.model?`model ${y.model}`:"",y?.finish?`finish ${y.finish}`:"",y?.durationMs!==void 0&&y?.durationMs!==null?`duration ${Er(y.durationMs)}`:""].filter(Boolean):[];return G("section",{className:"pipeline-trace-timeline","data-testid":"pipeline-step-timeline"},G("div",{className:"pipeline-trace-head"},G("div",null,G("b",null,"OpenCode Trace"),G("span",null,"Trace 使用 Code Queue 统一样式展示完整 agent loop;Pipeline 旧 step/message/tool 卡片样式已废弃。")),G("div",{className:"pipeline-trace-session-head","data-testid":"pipeline-step-timeline-session"},G("span",null,Q.join(" / ")||"Trace"),l.length>0?G(B9,{items:l}):null)),y?G("div",{className:"pipeline-trace-focus","data-testid":"pipeline-trace-matched-step"},G("span",{className:"codex-output-channel"},"Matched"),G("strong",null,`Gantt selection -> ${w.join(" / ")}`),G("time",null,`${A8(y?.createdAt)} -> ${A8(y?.completedAt)}`)):null,G(Y3,{port:YU,input:n,className:"codex-transcript pipeline-trace",testId:"pipeline-opencode-step-trace",emptyText:"暂无 OpenCode Trace 输出",keepRecentToolCalls:3}))}function K9(u){return Vu(u).flatMap((f)=>{if(Du(f))return[f];let l=$w(f);return l?[l]:[]})}function vr(u){return String(u?.event||u?.action||u?.requestedAction||u?.type||"").toLowerCase()}function Ay(u){return Y$(u?.timestamp,u?.createdAt,u?.updatedAt,u?.startedAt,u?.finishedAt)}function rY(u){return ku(Ay(u))}function w8(u){return String(u?.attempt||u?.id||"")}function G9(u){let f=new Set,l=[];for(let r of u){let n=String(r||"");if(!n||f.has(n))continue;f.add(n),l.push(n)}return l}function kW(u){switch(String(u||"").toLowerCase()){case"monitor":return"monitor";case"webui":return"webui";case"cli":return"cli";case"system":return"runner";default:return String(u||"--")}}function C1(u){return String(u?.requestedAction||u?.action||"").toLowerCase()}function H$(u){switch(C1(u)){case"guide":return"引导";case"modify":return"修改";case"approve":return"审核通过";case"restart":return"重启";case"redo":return"重做";default:return String(u?.requestedAction||u?.action||"控制")}}function IW(u){switch(vr(u)){case"initial-prompt-delivered":return"初始 prompt";case"append-prompt-delivered":return"追加 prompt";case"append-prompt-queued":return"追加 prompt 已排队";case"monitor-prompt-delivered":return"Monitor prompt";case"node-long-running-observation":return"长任务观察";case"node-finished":return"节点完成";case"oa-policy-downstream-evaluated":return"OA 下游策略";case"control-command-queued":return`${H$(u)} 已发起`;case"control-command-applied":return`${H$(u)} 已生效`;case"control-command-ignored":return`${H$(u)} 已忽略`;default:return String(u?.event||u?.action||u?.requestedAction||"event")}}function gW(u){return tw(u?.promptPreview||u?.reasonPreview||u?.prompt||u?.reason||"",240)}function nY(u){let f=String(u?.prompt||""),l=String(u?.reason||u?.restartReason||""),r=f?"":String(u?.promptPreview||""),n=l?"":String(u?.reasonPreview||"");return[f||r?{label:f?"prompt":"prompt preview",value:f||r}:null,l||n?{label:l?"reason":"reason preview",value:l||n}:null,Vu(u?.resetNodeIds).length>0?{label:"reset nodes",value:Vu(u.resetNodeIds).join(", ")}:null,Vu(u?.runningResetNodeIds).length>0?{label:"interrupted running nodes",value:Vu(u.runningResetNodeIds).join(", ")}:null,Vu(u?.interruptedProcedureRunIds).length>0?{label:"interrupted procedures",value:Vu(u.interruptedProcedureRunIds).join(", ")}:null,u?.interruptedProcedureRunId?{label:"interrupted procedure",value:String(u.interruptedProcedureRunId)}:null].filter(Boolean)}function Q9(u){let f=W8(u),l=f.map(($)=>ku($?.createdAt)).filter(($)=>$!==null),r=f.map(($)=>ku($?.completedAt)??ku($?.createdAt)).filter(($)=>$!==null),n=K9(u?.controlEventRecords).map(($)=>rY($)).filter(($)=>$!==null),i=Vu(u?.assistantOutputs).map(($)=>ku($?.updatedAt)).filter(($)=>$!==null),y=l[0]??n[0]??i[0]??null,_=r.at(-1)??n.at(-1)??i.at(-1)??y;return{startMs:y,endMs:_}}function iY(u,f,l,r,n=""){let i=Vu(u?.procedureRuns).filter((_)=>L8(_,f)===l);if(i.length===0)return null;if(n){let _=i.find(($)=>Zr($)===n);if(_)return _}if(r===null)return i.at(-1)||null;let y=i.find((_)=>{let $=ku($8(_,u)),c=ku(t8(_,u))??$;return $!==null&&c!==null&&r>=$-1000&&r<=c+1000});if(y)return y;return i.slice().sort((_,$)=>{let c=ku($8(_,u))??r,A=ku(t8(_,u))??c,j=ku($8($,u))??r,F=ku(t8($,u))??j,U=Math.min(Math.abs(c-r),Math.abs(A-r)),Q=Math.min(Math.abs(j-r),Math.abs(F-r));return U-Q})[0]||null}function jw(u,f){let l=Vu(u?.attempts).filter(Du);if(l.length===0)return null;let r=String(f?.attempt||"");if(r){let y=l.find((_)=>w8(_)===r);if(y)return y}let n=Number.isFinite(Number(f?.ms))?Number(f.ms):null;if(n===null)return l.at(-1)||null;let i=l.find((y)=>{let _=Q9(y);return Number.isFinite(_.startMs)&&Number.isFinite(_.endMs)&&n>=Number(_.startMs)-1000&&n<=Number(_.endMs)+1000});if(i)return i;return l.slice().sort((y,_)=>{let $=Q9(y),c=Q9(_),A=Math.min(Math.abs(Number($.startMs??n)-n),Math.abs(Number($.endMs??n)-n)),j=Math.min(Math.abs(Number(c.startMs??n)-n),Math.abs(Number(c.endMs??n)-n));return A-j})[0]||l.at(-1)||null}function Fw(u,f){let l=W8(u);if(l.length===0)return{step:null,stepIndex:-1,stepKey:""};if(f===null){let i=l[0];return{step:i,stepIndex:0,stepKey:O$(i,0)}}for(let i=0;i=_-1000&&f<=$+1000)return{step:y,stepIndex:i,stepKey:O$(y,i)}}let r=l.findIndex((i)=>{let y=ku(i?.createdAt)??ku(i?.completedAt);return y!==null&&y>=f});if(r>=0){let i=l[r];return{step:i,stepIndex:r,stepKey:O$(i,r)}}let n=Math.max(0,l.length-1);return{step:l[n],stepIndex:n,stepKey:O$(l[n],n)}}function yY(u,f){let l=String(f?.runId||u?.runId||"");if(String(f?.mode||"")==="interval"){let c=f?.interval||{},A=L9(u,c)||c.raw||{};return{mode:"interval",runId:l,interval:c,marker:null,nodeId:String(c?.nodeId||L8(A,l)||""),procedure:A,attempt:null,matchedStep:null,matchedStepIndex:-1,matchedStepKey:""}}let r=Du(f?.marker)?f.marker:{},n=Number.isFinite(Number(r?.ms))?Number(r.ms):null,i=String(r?.nodeId||""),y=i?iY(u,l,i,n,String(r?.procedureRunId||"")):null,_=y?jw(y,r):null,$=_?Fw(_,n):{step:null,stepIndex:-1,stepKey:""};return{mode:"event",runId:l,interval:null,marker:r,nodeId:i,procedure:y,attempt:_,matchedStep:$.step,matchedStepIndex:$.stepIndex,matchedStepKey:$.stepKey}}function _Y({procedure:u,matchedStepKey:f="",matchedAttemptId:l=""}){let r=Vu(u?.attempts);if(r.length===0)return G(Ar,{title:"暂无 attempt 详情",text:"当前 procedure 还没有可展示的 attempt / OpenCode Trace;若刚点击甘特线,请等待 node 详情抓取完成。"});return r.map((n,i)=>{let y=n?.opencodeMessages||{},_=W8(n),$=Vu(y.sessionIds).map((F)=>String(F)).filter(Boolean),c=fY(_,$),A=w8(n)||`attempt-${i+1}`,j=_.reduce((F,U)=>F+Vu(U?.parts).filter((Q)=>String(Q?.type||"").toLowerCase()==="tool"&&Aw(Q)==="failed").length,0);return G("article",{key:A,className:`pipeline-attempt-card ${l===A?"matched":""}`},G("div",{className:"pipeline-attempt-head"},G("div",null,G("strong",null,A),G("span",null,y.source||"opencode")),G("div",{className:"pipeline-attempt-badges"},G("span",null,`${_.length} steps`),G("span",null,`${y.toolCallCount??"--"} tools`),j>0?G("span",{className:"danger"},`${j} failed`):null)),G(w9,{items:[{label:"messages",value:y.messageCount??"--"},{label:"steps",value:y.stepCount??"--"},{label:"tools",value:y.toolCallCount??"--"},{label:"updated",value:Nu(y.updatedAt)},{label:"sessions",value:$.join(", ")||"--"}]}),_.length===0?G("p",{className:"muted paragraph"},"当前 attempt 尚未返回 OpenCode Trace;请确认 D601 pipeline-control 已重建并重新抓取。"):G(lY,{steps:_,sessionIds:$,sessionFacts:c,matchedStepKey:f}))})}function q9(u,f){return`${u}::${f}`}function F8(u,f,l){if(!Du(u))return null;return String(u.runId||"")===f&&String(u.nodeId||"")===l?u:null}function $Y(u,f){let l=Du(u)?u:{};if(!Du(f))return l;let r=Vu(f.attempts),n=Vu(l.attempts);return{...l,...f,attempts:r.length>0?r:n}}function tY(u,f,l,r){if(!F8(f,l,r))return u;let n=Vu(f.procedureRuns),i=Du(u)?u:{};return{...i,...f,controlEvents:Vu(f.controlEvents).length>0?f.controlEvents:i.controlEvents,procedureRuns:n.length>0?n:i.procedureRuns}}function cY({selection:u,runDetails:f,nodeDetails:l,nodeDetailsState:r,onRaw:n,onCollapse:i}){if(!u?.mode)return G("aside",{className:"pipeline-gantt-detail-panel empty","data-testid":"pipeline-gantt-detail-panel"},G("div",{className:"pipeline-gantt-detail-head"},G("div",null,G("span",{className:"panel-eyebrow"},"Gantt Detail"),G(nf,{title:"未选择元素",level:3})),G("button",{type:"button",className:"ghost-btn mini",onClick:i,"data-testid":"pipeline-gantt-sidebar-collapse"},"收起")),G(Ar,{title:"选择一条执行线或一个控制点",text:"点击甘特图中的 node 执行线、prompt 点或控制点,在这里查看结构化过程和 OpenCode step。"}));let y=String(u?.runId||""),_=String(u?.interval?.nodeId||u?.marker?.nodeId||""),$=f?.runId===y?f.details:null,c=F8(l,y,_),A=String(r?.runId||"")===y&&String(r?.nodeId||"")===_,j=tY($,c,y,_),F=(String(f?.runId||"")!==y||Boolean(f?.loading))&&!j,U=String(f?.runId||"")===y?String(f?.error||""):"",Q=A?String(r?.error||""):"",w=j?yY(j,u):null,L=w?.interval||u?.interval||null,J=w?.marker||u?.marker||null,N=String(L?.procedureRunId||J?.procedureRunId||""),q=c?uY(c,N)||L9(c,L||{procedureRunId:N}):null,W=w?.procedure||(j?L9(j,L||{procedureRunId:N}):null)||L?.raw||{};if(q&&(U9(W)===0||vW(q)>=vW(W)))W=$Y(W,q);let z=w?.attempt||null,Z=String(w?.matchedStepKey||"");if(!z&&J&&U9(W)>0)z=jw(W,J),Z=String(Fw(z,Number.isFinite(Number(J?.ms))?Number(J.ms):null).stepKey||"");let H=w8(z),E=U9(W)>0,D=A&&Boolean(r?.loading)&&!E,h=Boolean(F||D),B=[E?"":U,Q].filter(Boolean).join(" / "),X=A&&r?.fetchedAt?r.fetchedAt:f?.fetchedAt,m=cw(W?.status||L?.status||J?.status||J?.event),O=u?.mode==="event"?J?.label||IW(J?.raw||J)||"event":w?.nodeId||L?.nodeId||"node",p=J?nY(J?.raw||J):[],Y=J?[vr(J?.raw||J)?`event ${vr(J?.raw||J)}`:"",J?.promptEvent?`prompt ${J.promptEvent}`:"",J?.action?`action ${J.action}`:"",J?.sourceKind?`source ${kW(J.sourceKind)}`:"",J?.sourceNodeId?`from ${J.sourceNodeId}`:"",J?.targetNodeId?`to ${J.targetNodeId}`:"",J?.snapReason?`draw ${J.snapReason}`:""].filter(Boolean):[];return G("aside",{className:"pipeline-gantt-detail-panel","data-testid":"pipeline-gantt-detail-panel"},G("div",{className:"pipeline-gantt-detail-head"},G("div",null,G("span",{className:"panel-eyebrow"},u?.mode==="event"?"Gantt Event Detail":"Gantt Line Detail"),G(nf,{title:O,level:3,loading:h})),G("div",{className:"pipeline-gantt-detail-head-actions"},G(xn,{status:m},m),G("button",{type:"button",className:"ghost-btn mini",onClick:i,"data-testid":"pipeline-gantt-sidebar-collapse"},"收起"))),J?G("article",{className:"pipeline-event-card"},G("div",{className:"pipeline-event-card-head"},G("strong",null,J?.label||IW(J?.raw||J)),G(B9,{items:Y})),G(w9,{items:[{label:"event time",value:Nu(J?.timestampIso||J?.timestamp||"--")},J?.snapped?{label:"drawn time",value:Nu(J?.renderedTimestampIso||J?.ms)}:null,{label:"node",value:J?.nodeId||"--"},{label:"procedure",value:J?.procedureRunId||Zr(W)||"--"},{label:"attempt",value:J?.attempt||H||"--"},{label:"source kind",value:J?.sourceKind?kW(J.sourceKind):"--"},{label:"source node",value:J?.sourceNodeId||"--"},{label:"target node",value:J?.targetNodeId||"--"},{label:"command",value:J?.commandId||J?.eventId||"--"},J?.snapReason?{label:"placement",value:J.snapReason}:null]}),p.length>0?G("div",{className:"pipeline-event-blocks"},p.map((v,T)=>G("section",{key:`${v.label}-${T}`,className:"pipeline-event-text-block"},G("b",null,v.label),G("p",null,v.value)))):null,gW(J?.raw||J)?G("p",{className:"pipeline-text-preview"},gW(J?.raw||J)):null):null,G(w9,{items:[{label:"epoch",value:y||L?.runId||"--"},{label:"node",value:w?.nodeId||L?.nodeId||J?.nodeId||"--"},{label:"procedure",value:L?.procedureRunId||J?.procedureRunId||Zr(W)||"--"},{label:"started",value:Nu(L?.startedAt||W?.startedAt)},{label:"finished",value:Nu(L?.finishedAt||W?.finishedAt)},{label:"duration",value:Er(L?.durationMs||W?.durationMs)},{label:"fetched",value:X?$f(X):"--"},w?.matchedStep?{label:"matched step",value:`Step ${w.matchedStep.index??w.matchedStepIndex+1}`}:null]}),G(yf,{error:B}),G("div",{className:"pipeline-gantt-detail-actions"},G(kr,{title:`Procedure ${L?.procedureRunId||J?.procedureRunId||w?.nodeId||"node"}`,data:W,onOpen:n,testId:"raw-pipeline-gantt-procedure"}),J?G(kr,{title:`Pipeline event ${J?.id||J?.commandId||J?.eventId||w?.nodeId||"event"}`,data:J?.raw||J,onOpen:n,testId:"raw-pipeline-gantt-event"}):null,j?G(kr,{title:`Pipeline run ${y||"--"}`,data:j,onOpen:n,testId:"raw-pipeline-gantt-node-details"}):null),!h&&!Zr(W)&&!J?G(Ar,{title:"暂无过程详情",text:"当前选择还没有可匹配的 procedure 运行记录。"}):null,!h&&Zr(W)?G(_Y,{procedure:W,matchedStepKey:Z,matchedAttemptId:H}):null)}function AY({value:u}){let l=String(u||"--").split(/([_-])/u);return G(Rn.default.Fragment,null,l.map((r,n)=>r==="-"||r==="_"?G(Rn.default.Fragment,{key:n},r,G("wbr",null)):G(Rn.default.Fragment,{key:n},r)))}async function Pn(u,f={}){return Eu(u,{invalidJsonPrefix:"Pipeline 返回了无效 JSON",...f})}function xn({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return G("span",{className:`status-badge ${l}`},f||u||"unknown")}function Xl({label:u,value:f,hint:l,tone:r}){return G("article",{className:`metric-card ${r||""}`},G("div",{className:"metric-label"},u),G("div",{className:"metric-value"},f),G("div",{className:"metric-hint"},l))}function c0({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return G("section",{className:`panel ${n||""}`},G("div",{className:"panel-head"},G("div",null,f?G("p",{className:"panel-eyebrow"},f):null,G(nf,{title:u,loading:i})),l?G("div",{className:"panel-actions"},l):null),G("div",{className:"panel-body"},r))}function kr({title:u,data:f,onOpen:l,testId:r}){return G("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function Tr({title:u,subtitle:f,facts:l,data:r,onRaw:n,testId:i}){let y=Vu(l).map((_)=>String(_||"")).filter(Boolean);return G("article",{className:"pipeline-evidence-row"},G("div",{className:"pipeline-evidence-main"},G("strong",null,u),f?G("span",null,f):null),G("div",{className:"pipeline-evidence-facts"},y.map((_,$)=>G("span",{key:`${$}-${_.slice(0,16)}`},_))),r!==void 0?G(kr,{title:u,data:r,onOpen:n,testId:i}):null)}function Ar({title:u,text:f}){return G("div",{className:"empty-state"},G("strong",null,u),G("span",null,f))}function jY(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function FY(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function JY(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function UY(u){return{components:Array.isArray(u?.registry?.components)?u.registry.components:[],pipelines:Array.isArray(u?.pipelines)?u.pipelines:[],runs:Array.isArray(u?.runs)?u.runs:[]}}function sW(u,f,l){let r=u?._unidesk?.arrayLimits?.[f],n=Number(r?.originalLength);return Number.isFinite(n)?n:l}function Jw(u){if(!u||typeof u!=="object"||Array.isArray(u))return"--";return`${u.componentClass||"--"}/${u.id||"--"}`}function J8(u){if(!u||typeof u!=="object"||Array.isArray(u))return"";let f=String(u.componentClass||"").trim(),l=String(u.id||"").trim();return f&&l?`${f}/${l}`:""}function V9(u){return u?.config&&typeof u.config==="object"&&!Array.isArray(u.config)?u.config:{}}function Uw(u){let f=V9(u),l=Array.isArray(f.nodes)?f.nodes:Array.isArray(u?.nodes)?u.nodes:[],r=new Map;for(let y of l){let _=String(y?.id||y?.nodeId||"");if(_)r.set(_,{...y,id:_})}let n=D9(u),i=(y)=>{if(y&&!r.has(y))r.set(y,{id:y})};for(let y of X9(u))D$(y).forEach(i);for(let y of n)i(String(y?.from||y?.source||"")),i(String(y?.to||y?.target||""));return Array.from(r.values())}function D9(u){let f=V9(u);return Array.isArray(f.edges)?f.edges:Array.isArray(u?.edges)?u.edges:[]}function X9(u){let f=V9(u);return Array.isArray(f.topologicalBatches)?f.topologicalBatches:Array.isArray(u?.topologicalBatches)?u.topologicalBatches:[]}function QY(u){let f=new Map;for(let l of u){let r=J8(l);if(r)f.set(r,l);let n=Array.isArray(l?.refs)?l.refs:[];for(let i of n){let y=J8(i);if(y)f.set(y,l)}}return f}function aW(u,f){let l=f.get(J8(u?.componentRef));if(l)return l;let r=J8({componentClass:u?.kind,id:u?.id});return r?f.get(r)||null:null}function oW(u,f){let l=Qw(u,f);return String(l?.status||"pending")}function Qw(u,f){return(Array.isArray(u?.nodes)?u.nodes:[]).find((r)=>r?.nodeId===f||r?.id===f)||null}function qY(u){return u.reduce((f,l)=>{let r=String(l?.status||"unknown").toLowerCase();return f[r]=(f[r]||0)+1,f},{})}function NY(u){if(Array.isArray(u?.scorers))return u.scorers.filter(Du);if(Array.isArray(u?.summary?.scorers))return u.summary.scorers.filter(Du);if(Array.isArray(u?.artifact?.summary?.scorers))return u.artifact.summary.scorers.filter(Du);return[]}function WY(u){if(Du(u?.run))return u.run;if(Du(u?.runSummary))return u.runSummary;return null}function wY(u,f){if(!Du(u)&&!Du(f))return null;if(!Du(u))return f;if(!Du(f))return u;return{...u,...f,request:Du(u.request)||Du(f.request)?{...Du(u.request)?u.request:{},...Du(f.request)?f.request:{}}:f.request??u.request,artifact:Du(u.artifact)||Du(f.artifact)?{...Du(u.artifact)?u.artifact:{},...Du(f.artifact)?f.artifact:{}}:f.artifact??u.artifact,summary:Du(u.summary)||Du(f.summary)?{...Du(u.summary)?u.summary:{},...Du(f.summary)?f.summary:{}}:f.summary??u.summary}}function U8(u){let f=NY(u),l=f.find((A)=>Du(A?.score))||f[0]||null,r=Du(l?.score)?l.score:{},n=Number(r.passed),i=Number(r.total),y=Number(r.ratio),_=Number.isFinite(y)?y:Number.isFinite(n)&&Number.isFinite(i)&&i>0?n/i:null,$=_===null?null:Math.round(Math.max(0,Math.min(100,_<=1?_*100:_))),c=String(r.text||(Number.isFinite(n)&&Number.isFinite(i)?`${n}/${i}`:""));return{scorer:l,scorers:f,score:r,passed:Number.isFinite(n)?n:null,total:Number.isFinite(i)?i:null,percent:$,text:c}}function z9(u){let f=U8(u);return f.text||(f.scorers.length>0?String(f.scorer?.status||"pending"):"--")}function Y9(u){let f=U8(u);if(f.total>0&&f.passed===f.total)return"succeeded";if(f.total>0&&f.passed>0)return"running";if(f.scorers.length>0)return"failed";return"pending"}function LY(u){return Array.isArray(u?.items)?u.items.filter(Du):[]}function KY({run:u}){let f=z9(u);return G("span",{className:`pipeline-score-badge ${Y9(u)}`},`score ${f}`)}function GY({run:u,onRaw:f}){let r=U8(u).scorers;if(!u)return G(Ar,{title:"暂无评分",text:"选择一个 epoch 后会显示 scorer 结果。"});if(r.length===0)return G("div",{className:"pipeline-score-empty"},G("strong",null,"评分器等待中"),G("span",null,"DAG 完成后,Pipeline control backend 会把 scorer summary 追加到 run artifact,并通过 UniDesk 显示。"));return G("div",{className:"pipeline-score-board","data-testid":"pipeline-score-board"},r.map((n,i)=>{let y=U8({scorers:[n]}),_=LY(n),$=y.percent??0;return G("article",{key:`${n.scorerId||n.component||i}`,className:`pipeline-score-card ${Y9({scorers:[n]})}`},G("div",{className:"pipeline-score-head"},G("div",null,G("span",null,n.scorerId||n.component||"scorer"),G("strong",null,y.text||n.status||"--")),G(xn,{status:n.status||"unknown"},n.status||"unknown")),G("div",{className:"pipeline-score-meter","aria-label":`score ${$}%`},G("span",{style:{width:`${$}%`}})),G("div",{className:"pipeline-score-facts"},G("span",null,`${$}%`),G("span",null,n.component||"--"),G("span",null,n.applicationCheckoutRef||"--")),_.length>0?G("div",{className:"pipeline-score-items"},_.map((c)=>G("span",{key:`${c.id||c.filter}`,className:`pipeline-score-item ${String(c.status||"").toLowerCase()}`,title:`${c.filter||"--"} / ran=${c.ran??"?"}`},G("b",null,c.id||"--"),G("small",null,c.status||"--")))):G("p",{className:"muted paragraph"},"当前 scorer 尚未返回 item 级结果。"),n.error?G("p",{className:"pipeline-score-error"},tw(n.error,360)):null,G("div",{className:"panel-actions inline-actions"},G(kr,{title:`Scorer ${n.scorerId||i}`,data:n,onOpen:f,testId:"raw-pipeline-score"})))}))}function zY(u){let f=u.reduce((l,r)=>{let n=String(r?.componentClass||"unknown");return l[n]=(l[n]||0)+1,l},{});return Object.entries(f).map(([l,r])=>({name:l,count:Number(r)})).sort((l,r)=>r.count-l.count||l.name.localeCompare(r.name))}function D$(u){if(Array.isArray(u))return u.map((f)=>typeof f==="string"?f:String(f?.id||f?.nodeId||"")).filter(Boolean);if(Array.isArray(u?.nodes))return D$(u.nodes);if(Array.isArray(u?.nodeIds))return D$(u.nodeIds);return[]}function TY(u){return Du(u?.instanceInputs?.monitor)?u.instanceInputs.monitor:{}}function qw(u,f){if(String(u?.kind||"").toLowerCase()!=="procedure")return!1;let l=TY(u);if(u?.instanceInputs?.monitorMode===!0||l.enabled===!0)return!0;let r=Jw(u?.componentRef);return String(f?.id||f?.config?.id||r||"").toLowerCase().includes("monitor")}function EY(u){return u.filter((f)=>qw(f)).map((f)=>String(f?.id||"")).filter(Boolean)}function ZY(u,f){if(f.length===0)return u;let l=new Set(f),r=f.filter((n)=>u.includes(n));if(r.length===0)return u;return[...r,...u.filter((n)=>!l.has(n))]}function OY(u,f){if(f.length===0)return u;let l=new Set(f),r=f.filter((i)=>u.some((y)=>y.includes(i)));if(r.length===0)return u;let n=u.map((i)=>i.filter((y)=>!l.has(y))).filter((i)=>i.length>0);return[r,...n]}function HY(u,f,l){let n=X9(u).map(D$).filter((F)=>F.length>0);if(n.length>0)return n;let i=f.map((F)=>String(F?.id||"")).filter(Boolean),y=new Set(i),_=new Map(i.map((F)=>[F,0])),$=new Map(i.map((F)=>[F,[]]));for(let F of l){let U=String(F?.from||F?.source||""),Q=String(F?.to||F?.target||"");if(!y.has(U)||!y.has(Q))continue;$.get(U)?.push(Q),_.set(Q,(_.get(Q)||0)+1)}let c=new Map,A=i.filter((F)=>(_.get(F)||0)===0);for(let F of A)c.set(F,0);while(A.length>0){let F=A.shift(),U=(c.get(F)||0)+1;for(let Q of $.get(F)||[])if(_.set(Q,Math.max(0,(_.get(Q)||0)-1)),c.set(Q,Math.max(c.get(Q)||0,U)),(_.get(Q)||0)===0)A.push(Q)}i.forEach((F)=>{if(!c.has(F))c.set(F,0)});let j=Math.max(0,...Array.from(c.values()));return Array.from({length:j+1},(F,U)=>i.filter((Q)=>c.get(Q)===U)).filter((F)=>F.length>0)}function BY(u,f,l){let n=X9(u).map(D$).filter((_)=>_.length>0),i=n.length>0?n.flatMap((_)=>_):(()=>{let _=f.map((w)=>String(w?.id||"")).filter(Boolean),$=new Set(_),c=l.filter((w)=>String(w?.edgeType||"").toLowerCase()!=="rework"),A=new Map(_.map((w)=>[w,0])),j=new Map(_.map((w)=>[w,[]]));for(let w of c){let L=String(w?.from||w?.source||""),J=String(w?.to||w?.target||"");if(!$.has(L)||!$.has(J))continue;j.get(L)?.push(J),A.set(J,(A.get(J)||0)+1)}let F=new Map,U=_.filter((w)=>(A.get(w)||0)===0);for(let w of U)F.set(w,0);while(U.length>0){let w=U.shift(),L=(F.get(w)||0)+1;for(let J of j.get(w)||[])if(A.set(J,Math.max(0,(A.get(J)||0)-1)),F.set(J,Math.max(F.get(J)||0,L)),(A.get(J)||0)===0)U.push(J)}_.forEach((w)=>{if(!F.has(w))F.set(w,0)});let Q=Math.max(0,...Array.from(F.values()));return Array.from({length:Q+1},(w,L)=>_.filter((J)=>F.get(J)===L)).flatMap((w)=>w)})(),y=new Set(i);for(let _ of f){let $=String(_?.id||"");if(!$||y.has($))continue;i.push($),y.add($)}return ZY(i,EY(f))}function T$(u){return`${u.source}->${u.target}-${u.index}`}function dW(u,f,l){let r=Uw(u),n=D9(u),i=QY(l),y=new Map(r.map((m)=>[String(m?.id||""),m])),_=r.filter((m)=>qw(m,aW(m,i))).map((m)=>String(m?.id||"")).filter(Boolean),$=OY(HY(u,r,n),_),c=[],A=new Map,j=330,F=122;$.forEach((m,O)=>{let p=m.length*122;m.forEach((Y,v)=>{let T=y.get(Y)||{id:Y},S=aW(T,i),k=oW(f,Y).toLowerCase(),I=String(T.kind||S?.componentClass||"node").toLowerCase(),b=Jw(T.componentRef||S),o=String(S?.config?.version||S?.version||""),s=String(S?.config?.description||S?.description||""),x=v*122-Math.floor(p/2);A.set(Y,{column:O,row:v,y:x}),c.push({id:Y,type:"pipelineNode",position:{x:O*330,y:x},data:{exportLabel:{id:Y,kind:I,componentRef:b,componentVersion:o,componentDescription:s,status:k},label:G("div",{className:"flow-node-label"},G("strong",null,Y),G("span",null,I),G("code",{title:s||b},o?`${b}@${o}`:b),G(xn,{status:k},k))},className:`pipeline-flow-node ${I} ${k}`})})});let U=n.flatMap((m,O)=>{let p=String(m?.from||m?.source||""),Y=String(m?.to||m?.target||"");if(!y.has(p)||!y.has(Y))return[];return[{source:p,target:Y,index:O,condition:m?.condition,edgeType:m?.edgeType}]}),Q=U.reduce((m,O)=>m.set(O.source,(m.get(O.source)||0)+1),new Map),w=U.reduce((m,O)=>m.set(O.target,(m.get(O.target)||0)+1),new Map),L=U.reduce((m,O)=>{let p=`${O.source}->${O.target}`;return m.set(p,(m.get(p)||0)+1)},new Map),J=new Map,N=new Map,q=new Map,W=new Map,z=new Map,Z=new Map,H=U.reduce((m,O)=>{let p=A.get(O.source),Y=A.get(O.target),v=(Y?.column||0)-(p?.column||0);if(v<=0||String(O.edgeType||"").toLowerCase()==="rework"||v!==1)return m;let S=`${O.source}->column:${Y?.column??""}`,k=m.get(S)||[];return k.push(O),m.set(S,k),m},new Map);for(let m of H.values()){if(m.length<2)continue;m.slice().sort((O,p)=>{let Y=A.get(O.target),v=A.get(p.target);return(Y?.y||0)-(v?.y||0)||O.index-p.index}).forEach((O,p,Y)=>{Z.set(T$(O),{slot:p-(Y.length-1)/2,count:Y.length})})}[...U].sort((m,O)=>{let p=A.get(m.source),Y=A.get(m.target),v=A.get(O.source),T=A.get(O.target),S=Math.abs((Y?.column||0)-(p?.column||0))*330+Math.abs((Y?.y||0)-(p?.y||0)),k=Math.abs((T?.column||0)-(v?.column||0))*330+Math.abs((T?.y||0)-(v?.y||0));return S-k||m.index-O.index}).forEach((m)=>{let O=A.get(m.source)||{column:0,row:0,y:0},p=A.get(m.target)||{column:0,row:0,y:0},Y=p.column-O.column,v=Math.max(0,Y),T=Y<=0||String(m.edgeType||"").toLowerCase()==="rework",S=O.y-p.y,k=w.get(m.target)||1,I=Z.has(T$(m)),b=!T&&v<=1&&(I||k===1),o=z.get(m.target)||new Map;z.set(m.target,o);let s=B$.slice().sort((x,fu)=>{let $u=(qu)=>{let P=String(qu.side),e=0;if(T){if(P==="left")e+=86;if(P==="top")e+=p.y<=0?-22:12;if(P==="bottom")e+=p.y>=0?-22:12;if(Math.abs(p.y)<12&&P!=="left")e+=m.index%2===0?P==="top"?-6:6:P==="bottom"?-6:6;return e}if(b){if(P==="left")e-=I?72:44;if(P!=="left")e+=I?72:44;return e+Math.abs(S)*0.02}if(P==="left")e+=v<=1?0:24;if(P==="top")e+=S<-36?-18:42;if(P==="bottom")e+=S>36?-18:42;if(v<=1&&Math.abs(S)<=82&&P!=="left")e+=38;if(v>1&&P!=="left")e-=10;return e},tu=O.y-p.y,ju=tu!==0?tu:m.index%2===0?-1:1,Gu=(qu)=>{let P=o.get(qu.id)||0;return $u(qu)+P*64+gX(qu,o,ju)};return Gu(x)-Gu(fu)||String(x.id).localeCompare(String(fu.id))})[0];o.set(s.id,(o.get(s.id)||0)+1),W.set(T$(m),s)});let D=U.map((m)=>{let O=oW(f,m.target).toLowerCase(),p=`${m.source}->${m.target}`,Y=J.get(m.source)||0,v=N.get(m.target)||0,T=q.get(p)||0;J.set(m.source,Y+1),N.set(m.target,v+1),q.set(p,T+1);let S=Y-((Q.get(m.source)||1)-1)/2,k=v-((w.get(m.target)||1)-1)/2,I=T-((L.get(p)||1)-1)/2,b=A.get(m.source),o=A.get(m.target),s=(o?.column||0)-(b?.column||0),x=Math.max(1,Math.abs(s)),fu=s<=0||String(m.edgeType||"").toLowerCase()==="rework",$u=Math.abs((o?.y||0)-(b?.y||0)),tu=Z.get(T$(m)),ju=!fu&&s===1&&(w.get(m.target)||0)>1,Gu=tu?tu.slot:I*2+S+k*0.45,qu=Gu===0?m.index%2===0?-1:1:Math.sign(Gu),P=W.get(T$(m))||B$[1],e=P.side==="top"?-1:P.side==="bottom"?1:qu,uu=fu||x>1||$u>96||Math.abs(Gu)>0.2||P.side!=="left",Wu=fu?118+x*18:22+x*16,g=P.side==="left"?0:28,Qu=uu?Math.max(-280,Math.min(280,e*Math.min(180,Wu+g+$u*0.22)+Gu*28)):0,Tu=Math.max(0,Math.min(Z$.length-1,Math.round(S+(Z$.length-1)/2))),Zu=Z$[Tu]||Z$[1],Xu=O==="succeeded"?"var(--accent-2)":O==="running"?"var(--accent)":O==="failed"?"var(--danger)":"rgba(129, 147, 159, 0.78)",ff=b?.column||0,_f=o?.column||0,Jf=Qu===0?0:Math.sign(Qu),mf=fu?`feedback:${ff}->${_f}:${Jf}`:tu?`fanout:${ff}->${_f}:${m.source}`:ju?`fanin:${ff}->${_f}:${m.target}`:P.side!=="left"||x>1?`corridor:${ff}->${_f}:${P.side}:${Jf}:${Math.round(Math.abs(Qu)/56)}`:"";return{id:`${m.source}->${m.target}-${m.index}`,source:m.source,target:m.target,sourceHandle:Zu.id,targetHandle:P.id,type:"pipelineCurve",zIndex:12,animated:O==="running",data:{baseEdgeColor:Xu,laneOffset:Qu,routeMode:tu&&P.side==="left"?"direct-forward-left":"",targetSide:P.side,isFeedback:fu,overlapGroup:mf},targetStatus:O}}),h=D.reduce((m,O)=>{let p=String(O.data?.overlapGroup||"");return p?m.set(p,(m.get(p)||0)+1):m},new Map),B=new Map,X=D.map((m)=>{let O=String(m.targetStatus||"pending"),p={...m};delete p.targetStatus;let Y=String(m.data?.overlapGroup||""),v=Y?h.get(Y)||0:0,T=v>1?B.get(Y)||0:-1;if(v>1)B.set(Y,T+1);let S=T>=0?MW[T%MW.length]:String(m.data.baseEdgeColor),k={stroke:S};if(m.data.isFeedback)k.strokeDasharray="9 7";return{...p,data:{...m.data,edgeColor:S,overlapSlot:T,overlapCount:v},style:k,markerEnd:{type:Yn.ArrowClosed,color:S},className:`pipeline-flow-edge ${O} ${m.data.isFeedback?"feedback":""} ${T>=0?"overlap-colored":""}`}});return{nodes:c,edges:X}}function Il(u){return String(u??"").replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}function eW(u){let f=String(u||"");if(f.includes("--accent-2"))return"#4eb7a8";if(f.includes("--accent"))return"#d7a13a";if(f.includes("--danger"))return"#cf6a54";return f.startsWith("#")?f:"#81939f"}function Q8(u){return`arrow-${u.replace(/[^a-zA-Z0-9_-]+/g,"")}`}function Nw(u,f="pipeline"){return String(u||f).replace(/[^a-zA-Z0-9_-]+/g,"-").replace(/^-|-$/g,"")||f}function uw(u,f){let l=u.position.x,r=u.position.y,n=B$.find((i)=>i.id===f);if(n?.side==="top")return{x:l+jy*xW(n.style?.left,0.5),y:r,position:Lu.Top};if(n?.side==="bottom")return{x:l+jy*xW(n.style?.left,0.5),y:r+Fy,position:Lu.Bottom};return{x:l,y:r+Fy/2,position:Lu.Left}}function VY(u){return{x:u.position.x+jy,y:u.position.y+Fy/2}}function DY(u,f){let l=Math.min(...u.nodes.map((w)=>w.position.x),0)-220,r=Math.min(...u.nodes.map((w)=>w.position.y),0)-220,n=Math.max(...u.nodes.map((w)=>w.position.x+jy),1)+220,i=Math.max(...u.nodes.map((w)=>w.position.y+Fy),1)+220,y=Math.ceil(n-l),_=Math.ceil(i-r),$=new Map(u.nodes.map((w)=>[w.id,w])),c=u.edges.map((w)=>eW(w.data?.edgeColor||w.style?.stroke)),j=Array.from(new Set(["#4eb7a8","#d7a13a","#cf6a54","#81939f",...c])).map((w)=>``).join(""),F=u.edges.flatMap((w)=>{let L=$.get(w.source),J=$.get(w.target);if(!L||!J)return[];let N=VY(L),q=uw(J,String(w.targetHandle||"in-left")),W=yw(N.x,N.y,q.x,q.y,q.position,Number(w.data?.laneOffset||0),String(w.data?.routeMode||"")),z=eW(w.data?.edgeColor||w.style?.stroke),Z=w.data?.isFeedback?' stroke-dasharray="9 7"':"";return``}).join(` +`),U=u.nodes.map((w)=>{let L=w.data?.exportLabel||{},J=String(L.status||"pending").toLowerCase(),N=J==="succeeded"?"#4eb7a8":J==="running"?"#d7a13a":J==="failed"?"#cf6a54":"#81939f",q=w.position.x,W=w.position.y,z=B$.map((Z)=>{let H=uw(w,Z.id);if(Z.side==="top"||Z.side==="bottom")return``;return``}).join(` `);return` - - ${w} - - ${Rl(K.id||G.id)} - ${Rl(K.kind||"node")} - ${Rl(K.componentRef||"--")} - ${Rl(Q)} + + ${z} + + ${Il(L.id||w.id)} + ${Il(L.kind||"node")} + ${Il(L.componentRef||"--")} + ${Il(J)} `}).join(` -`);return{svg:` - ${J} +`);return{svg:` + ${j} - ${Rl(f)} - ${q}${U} - `,width:_,height:n}}function ID(u){let f=String(u||"").toLowerCase();if(f==="succeeded"||f==="completed")return"#4eb7a8";if(f==="failed")return"#cf6a54";if(pz(f))return"#69aee8";return"#d7a13a"}function kD(u){let f=String(u?.kind||""),l=String(u?.tone||u?.status||"").toLowerCase();if(f==="prompt"&&l==="initial")return"#d7a13a";if(f==="prompt"&&l==="monitor")return"#69aee8";if(f==="prompt")return"#4eb7a8";if(l==="modify")return"#e0b95a";if(l==="approve"||l==="guide"||l==="monitor")return"#4eb7a8";if(l==="restart"||l==="redo")return"#d7a13a";if(l==="ignored")return"#81939f";if(l==="webui")return"#69aee8";if(l==="cli")return"#d7a13a";return"#a7bac5"}function Vz(u){let f=String(u?.sourceKind||"").toLowerCase(),l=String(u?.action||"").toLowerCase(),r=String(u?.status||"").toLowerCase();if(l==="observe"||r==="observation"||f==="monitor")return"#4eb7a8";if(f==="webui")return"#69aee8";if(f==="cli")return"#d7a13a";if(r.includes("ignored"))return"#81939f";return"#8aa0ad"}function gD(u,f,l){let r=kD(u),y=String(u?.kind||"");if(y==="control-source")return``;if(y==="control-target"){let _=String(u?.tone||"").toLowerCase()==="approve"?"rgba(78,183,168,0.22)":"#081118";return``}return``}function sD(u){let f=Hu(u.visibleNodeIds).map((Z)=>String(Z||"")).filter(Boolean),l=Hu(u.intervals).filter(Yu),r=Hu(u.markers).filter(Yu),y=Hu(u.arrows).filter(Yu),i=Hu(u.ticks).filter(Yu),_=Yu(u.bounds)?u.bounds:{},n=Yu(u.backendLayout)?u.backendLayout:null,$=Math.max(240,Math.round(Number(u.chartHeight||360))),j=Math.max(_0,108),F=128,J=24,U=58,q=56,W=128+Math.max(1,f.length)*j,G=Math.max(760,W+48),K=114+$+24,Q=24,N=58,c=114,z=(Z)=>152+Z*j,w=(Z)=>z(Z)+j/2,H=Hu(u.meta).map((Z)=>String(Z||"")).filter(Boolean).slice(0,4).join(" · "),B=new Map(r.map((Z)=>[String(Z.id||""),Z])),X=Array.from(new Set(["#4eb7a8","#69aee8","#d7a13a","#cf6a54","#8aa0ad",...y.map(Vz)])).map((Z)=>``).join(""),R=i.map((Z)=>{let D=114+yG(Z,_,$,n);return` - - ${Rl(Nu(Z.ms))} - +${Rl(Er(Number(Z.offsetMs??Number(Z.ms)-Number(_.startMs))))} + ${Il(f)} + ${U}${F} + `,width:y,height:_}}function XY(u){let f=String(u||"").toLowerCase();if(f==="succeeded"||f==="completed")return"#4eb7a8";if(f==="failed")return"#cf6a54";if(_w(f))return"#69aee8";return"#d7a13a"}function YY(u){let f=String(u?.kind||""),l=String(u?.tone||u?.status||"").toLowerCase();if(f==="prompt"&&l==="initial")return"#d7a13a";if(f==="prompt"&&l==="monitor")return"#69aee8";if(f==="prompt")return"#4eb7a8";if(l==="modify")return"#e0b95a";if(l==="approve"||l==="guide"||l==="monitor")return"#4eb7a8";if(l==="restart"||l==="redo")return"#d7a13a";if(l==="ignored")return"#81939f";if(l==="webui")return"#69aee8";if(l==="cli")return"#d7a13a";return"#a7bac5"}function fw(u){let f=String(u?.sourceKind||"").toLowerCase(),l=String(u?.action||"").toLowerCase(),r=String(u?.status||"").toLowerCase();if(l==="observe"||r==="observation"||f==="monitor")return"#4eb7a8";if(f==="webui")return"#69aee8";if(f==="cli")return"#d7a13a";if(r.includes("ignored"))return"#81939f";return"#8aa0ad"}function SY(u,f,l){let r=YY(u),n=String(u?.kind||"");if(n==="control-source")return``;if(n==="control-target"){let y=String(u?.tone||"").toLowerCase()==="approve"?"rgba(78,183,168,0.22)":"#081118";return``}return``}function mY(u){let f=Vu(u.visibleNodeIds).map((T)=>String(T||"")).filter(Boolean),l=Vu(u.intervals).filter(Du),r=Vu(u.markers).filter(Du),n=Vu(u.arrows).filter(Du),i=Vu(u.ticks).filter(Du),y=Du(u.bounds)?u.bounds:{},_=Du(u.backendLayout)?u.backendLayout:null,$=Math.max(240,Math.round(Number(u.chartHeight||360))),c=Math.max(t0,108),A=128,j=24,F=58,U=56,Q=128+Math.max(1,f.length)*c,w=Math.max(760,Q+48),L=114+$+24,J=24,N=58,q=114,W=(T)=>152+T*c,z=(T)=>W(T)+c/2,Z=Vu(u.meta).map((T)=>String(T||"")).filter(Boolean).slice(0,4).join(" · "),H=new Map(r.map((T)=>[String(T.id||""),T])),D=Array.from(new Set(["#4eb7a8","#69aee8","#d7a13a","#cf6a54","#8aa0ad",...n.map(fw)])).map((T)=>``).join(""),h=i.map((T)=>{let S=114+Ew(T,y,$,_);return` + + ${Il(Nu(T.ms))} + +${Il(Er(Number(T.offsetMs??Number(T.ms)-Number(y.startMs))))} `}).join(` -`),Y=['','TIME',...f.map((Z,D)=>{let I=z(D),k=Z.length>18?`${Z.slice(0,16)}…`:Z;return` - - ${Rl(k)} - node ${D+1} +`),B=['','TIME',...f.map((T,S)=>{let k=W(S),I=T.length>18?`${T.slice(0,16)}…`:T;return` + + ${Il(I)} + node ${S+1} `})].join(` -`),P=f.map((Z,D)=>{return``}).join(` -`),t=l.map((Z)=>{let D=f.indexOf(String(Z.nodeId||""));if(D<0)return"";let I=114+_8(Z,_,$,n),k=Math.max(2,rG(Z,_,$,n)),h=ID(Z.status),o=w(D)-3.5,s=Z.live?``:"",x=k>=28?`${Rl(String(Z.status||"working"))} - ${Rl(Er(Z.durationMs))}`:"";return` - +`),X=f.map((T,S)=>{return``}).join(` +`),m=l.map((T)=>{let S=f.indexOf(String(T.nodeId||""));if(S<0)return"";let k=114+N8(T,y,$,_),I=Math.max(2,Tw(T,y,$,_)),b=XY(T.status),o=z(S)-3.5,s=T.live?``:"",x=I>=28?`${Il(String(T.status||"working"))} + ${Il(Er(T.durationMs))}`:"";return` + ${s} ${x} `}).join(` -`),O=r.map((Z)=>{let D=f.indexOf(String(Z.nodeId||""));if(D<0)return"";let I=114+nr(Z,_,$,n);return gD(Z,w(D),I)}).join(` -`),M=y.map((Z)=>{let D=B.get(String(Z.targetMarkerId||""));if(!D)return"";let I=B.get(String(Z.sourceMarkerId||"")),k=String(I?.nodeId||Z.sourceNodeId||""),h=String(D.nodeId||Z.targetNodeId||""),o=f.indexOf(k),s=f.indexOf(h);if(o<0||s<0)return"";let x=w(o)-24-128,uu=w(s)-24-128,nu=di(n)?Bf(Z.sourceY??Z.y1)??(I?nr(I,_,$,n):nr(D,_,$,n)):I?nr(I,_,$,n):nr(D,_,$,n),$u=di(n)?Bf(Z.targetY??Z.y2)??nr(D,_,$,n):nr(D,_,$,n),Fu=Vz(Z),Ku=String(Z.action||"").toLowerCase()==="observe"?"3 4":"6 5",Wu=Rl(iG(x,nu,uu,$u));return` - `}).join(` -`),S=f.length===0?'No visible Gantt nodes':"";return{svg:` - ${X} +`),O=r.map((T)=>{let S=f.indexOf(String(T.nodeId||""));if(S<0)return"";let k=114+cr(T,y,$,_);return SY(T,z(S),k)}).join(` +`),p=n.map((T)=>{let S=H.get(String(T.targetMarkerId||""));if(!S)return"";let k=H.get(String(T.sourceMarkerId||"")),I=String(k?.nodeId||T.sourceNodeId||""),b=String(S.nodeId||T.targetNodeId||""),o=f.indexOf(I),s=f.indexOf(b);if(o<0||s<0)return"";let x=z(o)-24-128,fu=z(s)-24-128,$u=Jy(_)?Bf(T.sourceY??T.y1)??(k?cr(k,y,$,_):cr(S,y,$,_)):k?cr(k,y,$,_):cr(S,y,$,_),tu=Jy(_)?Bf(T.targetY??T.y2)??cr(S,y,$,_):cr(S,y,$,_),ju=fw(T),Gu=String(T.action||"").toLowerCase()==="observe"?"3 4":"6 5",qu=Il(Zw(x,$u,fu,tu));return` + `}).join(` +`),Y=f.length===0?'No visible Gantt nodes':"";return{svg:` + ${D} - - ${Rl(u.title||"Pipeline Epoch Gantt")} - ${Rl(H)} - ${Y} + + ${Il(u.title||"Pipeline Epoch Gantt")} + ${Il(Z)} + ${B} - ${P} - ${R} - ${t} - ${M} + ${X} + ${h} + ${m} + ${p} ${O} - ${S} - `,width:G,height:K}}function i8(u,f){let l=URL.createObjectURL(u),r=document.createElement("a");r.href=l,r.download=f,r.click(),setTimeout(()=>URL.revokeObjectURL(l),1000)}async function oz(u,f){let l=az(f,"pipeline"),{svg:r,width:y,height:i}=vD(u,f),_=new Blob([r],{type:"image/svg+xml;charset=utf-8"}),n=URL.createObjectURL(_);try{let $=new Image;await new Promise((U,q)=>{$.onload=()=>U(),$.onerror=()=>q(Error("svg image load failed")),$.src=n});let j=document.createElement("canvas");j.width=y,j.height=i;let F=j.getContext("2d");if(!F)throw Error("canvas unavailable");F.drawImage($,0,0);let J=await new Promise((U)=>j.toBlob(U,"image/png"));if(!J)throw Error("png export failed");i8(J,`${l}.png`)}catch{i8(_,`${l}.svg`)}finally{URL.revokeObjectURL(n)}}async function aD(u){let f=az(String(u?.title||"pipeline-gantt"),"pipeline-gantt"),{svg:l,width:r,height:y}=sD(u),i=new Blob([l],{type:"image/svg+xml;charset=utf-8"}),_=URL.createObjectURL(i);try{let n=new Image;await new Promise((J,U)=>{n.onload=()=>J(),n.onerror=()=>U(Error("gantt svg image load failed")),n.src=_});let $=document.createElement("canvas");$.width=r,$.height=y;let j=$.getContext("2d");if(!j)throw Error("canvas unavailable");j.drawImage(n,0,0);let F=await new Promise((J)=>$.toBlob(J,"image/png"));if(!F)throw Error("gantt png export failed");i8(F,`${f}.png`)}catch{i8(i,`${f}.svg`)}finally{URL.revokeObjectURL(_)}}async function oD(u){for(let f of u){if(f.flow.nodes.length===0)continue;await oz(f.flow,f.title),await new Promise((l)=>setTimeout(l,750))}}function Xz(u,f){return u.find((l)=>String(l?.pipelineId||"")===f)||null}function Yz(u){return hu(u?.startedAt)??hu(u?.artifact?.startedAt)??hu(u?.request?.createdAt)??hu(u?.updatedAt)??0}function dD(u,f){return u.filter((l)=>String(l?.pipelineId||"")===f).slice().sort((l,r)=>Yz(l)-Yz(r)||String(l?.runId||"").localeCompare(String(r?.runId||"")))}function jj(u,f){let l=String(f?.runId||""),r=u.findIndex((_)=>String(_?.runId||"")===l),y=r>=0?r+1:u.length,i=String(f?.status||"--");return`Epoch ${y} / ${l||"--"} / ${i}`}function Tr(u){return String(u?.procedureRunId||u?.runId||"")}function A8(u,f){let l=String(u?.nodeId||u?.request?.nodeId||"");if(l)return l;let r=Tr(u),y=`${f}__`;if(r.startsWith(y))return r.slice(y.length).replace(/__\d+$/u,"");return""}function a4(u,f){let l=Yu(u?.artifact)?u.artifact:{},r=Yu(u?.request)?u.request:{};return w$(u?.startedAt,l.startedAt,r.createdAt,r.startedAt,u?.createdAt,u?.updatedAt,f?.startedAt,f?.request?.createdAt)}function o4(u,f){let l=String(u?.status?.status||u?.artifact?.status||u?.status||"").toLowerCase(),r=Yu(u?.artifact)?u.artifact:{},y=Qj(l);return w$(u?.finishedAt,r.finishedAt,u?.completedAt,y?u?.updatedAt:void 0,y?r.updatedAt:void 0,y?f?.updatedAt:void 0)}function dz(u,f,l=Date.now()){let r=String(u?.runId||""),y=new Set(f.map((i)=>String(i?.id||"")).filter(Boolean));return Hu(u?.procedureRuns).flatMap((i)=>{let _=A8(i,r);if(!_)return[];let n=String(i?.status?.status||i?.artifact?.status||i?.status||"unknown").toLowerCase(),$=a4(i,u),j=hu($);if(j===null)return[];let F=o4(i,u),J=hu(F)??(Qj(n)?hu(i?.updatedAt)??j+1000:l),U=Math.max(j+1000,J);return[{nodeId:_,knownNode:y.has(_),procedureRunId:Tr(i),status:n,startMs:j,endMs:U,startedAt:G$(j),finishedAt:G$(U),durationMs:U-j,runId:r,raw:i}]}).sort((i,_)=>i.startMs-_.startMs||i.endMs-_.endMs||i.nodeId.localeCompare(_.nodeId))}function eD(u,f,l=[]){let r=f.map((F)=>Number(F.startMs)).filter(Number.isFinite),y=f.map((F)=>Number(F.endMs)).filter(Number.isFinite);for(let F of l){let J=Bf(F?.eventMs??F?.ms);if(J!==null)r.push(J),y.push(J)}let i=hu(u?.startedAt)??hu(u?.artifact?.startedAt)??hu(u?.request?.createdAt),_=hu(u?.finishedAt)??hu(u?.artifact?.finishedAt)??hu(u?.updatedAt);if(i!==null)r.push(i);if(_!==null)y.push(_);let n=Date.now(),$=r.length>0?Math.min(...r):n-60000,j=Math.max($+60000,y.length>0?Math.max(...y):n);return{startMs:$,endMs:j,durationMs:j-$}}var d4=12,ez=20,Fj=100,ut=!1;function t1(u){let f=Number(u);if(!Number.isFinite(f))return 0;return Math.max(0,Math.min(100,Math.round(f*100)/100))}function ft(u){let f=Math.max(d4,Number(u||d4)),l=Math.log(f/d4)/Math.log(ez);return t1(l*100)}var L$=ft(Fj);function Gj(u){let f=t1(u)/100,l=d4*Math.pow(ez,f),r=f<0.24?"全局":f<0.64?"均衡":"细节";return{value:t1(f*100),pxPerMinute:l,label:r}}function yj(u){let f=Math.round(Number(u));return Math.abs(f-Fj)<=1?Fj:f}function lt(u,f=L$){let l=Math.max(1,Number(u.durationMs||0)/60000),r=Gj(f);return Math.round(Math.max(360,Math.min(7200,l*Number(r.pxPerMinute||48))))}function rt(u,f=7){let l=Math.max(1,Number(u.endMs||0)-Number(u.startMs||0));return Array.from({length:f},(r,y)=>{let i=f===1?0:y/(f-1);return{ms:Number(u.startMs)+l*i,percent:i*100}})}function yt(u,f){let l=Math.max(1,Number(f.endMs)-Number(f.startMs));return Math.max(0,Math.min(100,(u-Number(f.startMs))/l*100))}function Bf(u){let f=Number(u);return Number.isFinite(f)?f:null}function Kj(u){return pz(u?.status)&&!Qj(u?.status)}function uG(u,f,l,r){let y=Math.max(1,l-f),i=Math.max(0,Math.min(1,(u-f)/y));return Number((i*r).toFixed(3))}function Dz(u,f){if(!f)return null;let l=Bf(f?.startMs),r=Bf(f?.endMs),y=Bf(f?.chartHeight);if(l===null||r===null||y===null)return null;return uG(u,l,r,y)}function fG(u,f){let l=Bf(u?.rawStartMs??u?.startMs)??Bf(u?.startMs)??f,r=Bf(u?.endMs)??l+1000;if(!Kj(u))return Math.max(l+1000,r);return Math.max(l+1000,r,f)}function it(u,f,l,r){let y=Bf(u?.startMs)??r-60000,i=Bf(u?.endMs)??r,_=l.reduce((W,G)=>Math.max(W,fG(G,r)),i),n=Math.max(y+60000,i,_),$=Math.max(1,n-y),j={startMs:y,endMs:n,durationMs:$},F=lt(j,f),J=Gj(f),U=Math.max(5,Math.min(18,Math.round(F/150))),q=rt(j,U).map((W)=>{let G=Number(W.ms),K=uG(G,y,n,F);return{...W,y:K,timestamp:G$(G),offsetMs:G-y}});return{source:"frontend-y",startMs:y,endMs:n,durationMs:$,chartHeight:F,scale:t1(f),normalizedScale:Number((t1(f)/100).toFixed(3)),pxPerMinute:Number(Number(J.pxPerMinute||0).toFixed(3)),ticks:q}}function _t(u,f,l){if(!Kj(u))return u;let r=Bf(u?.rawStartMs??u?.startMs)??Bf(u?.startMs)??l,y=fG(u,l),i=Dz(r,f),_=Dz(y,f),n=Bf(i??u?.y1??u?.startY)??0,$=Bf(_??u?.y2??u?.endY)??n+10,j=Math.max(24,$-n);return{...u,live:!0,startMs:r,endMs:y,durationMs:Math.max(1000,y-r),finishedAt:G$(y),y1:n,y2:$,startY:n,endY:$,height:j}}function Lj(u,f,l){return yt(u,f)/100*l}function di(u){return Boolean(u&&String(u?.source||"")!=="frontend-y")}function lG(u,f,l,r,y){if(di(r))for(let _ of y){let n=Bf(u?.[_]);if(n!==null)return n}let i=Bf(u?.ms??u?.eventMs??u?.startMs);return Lj(i??Number(f.startMs),f,l)}function _8(u,f,l,r){return lG(u,f,l,r,["y1","startY"])}function Jj(u,f,l,r){if(di(r)){let i=Bf(u?.y2??u?.endY);if(i!==null)return i}let y=Bf(u?.endMs)??Number(f.endMs);return Lj(y,f,l)}function rG(u,f,l,r){if(di(r)){let i=Bf(u?.height);if(i!==null)return Math.max(1,i)}let y=u?.live?24:10;return Math.max(y,Jj(u,f,l,r)-_8(u,f,l,r))}function nr(u,f,l,r){return lG(u,f,l,r,["y","timeAxisY"])}function yG(u,f,l,r){if(di(r)||String(r?.source||"")==="frontend-y"){let _=Bf(u?.y);if(_!==null)return _}let y=Bf(u?.percent);if(y!==null)return y/100*l;let i=Bf(u?.ms)??Number(f.startMs);return Lj(i,f,l)}function nt(u){let f=String(u?.promptEvent||u?.raw?.promptEvent||u?.event||"").toLowerCase();if(!["node-long-running-observation","node-finished"].includes(f))return"";let l=String(u?.sourceNodeId||u?.raw?.sourceNodeId||u?.raw?.detail?.nodeId||""),r=String(u?.nodeId||u?.targetNodeId||"");return l&&l!==r?l:""}function $t(u,f){let l=new Set(f.map((y)=>[String(y.sourceNodeId||""),String(y.targetNodeId||""),String(y.targetMarkerId||""),String(y.action||"")].join(":"))),r=[...f];for(let y of u){let i=nt(y),_=String(y?.nodeId||""),n=String(y?.id||"");if(!i||!_||!n)continue;let $=[i,_,n,"observe"].join(":");if(l.has($))continue;l.add($),r.push({id:`observation-arrow:${n}:${i}:${_}`,commandId:String(y?.commandId||y?.eventId||n),sourceNodeId:i,targetNodeId:_,sourceMarkerId:"",targetMarkerId:n,sourceKind:"monitor",action:"observe",status:"observation"})}return{markers:u,arrows:r}}function tz(u,f=""){let l=xr(u)||f,r=String(u?.promptEvent||"");if(l==="initial-prompt-delivered")return"initial";if(r==="node-finished"||r==="node-long-running-observation"||r.startsWith("monitor-"))return"monitor";if(l==="monitor-prompt-delivered"||String(u?.sourceKind||"").toLowerCase()==="monitor"||f==="monitor-prompt-queued")return"monitor";return"append"}function At(u){return Hu(u?.tags||u?.raw?.tags).map((f)=>String(f||"")).filter(Boolean)}function Sz(u,f=""){let l=xr(u)||f,r=String(u?.promptEvent||"");if(l==="initial-prompt-delivered")return"初始 prompt";if(r==="node-long-running-observation")return"长任务观察";if(r==="node-finished")return At(u).includes("monitor.audit")?"节点完成 / OA 审核":"节点完成";if(r==="monitor-interval")return"旧版轮询";if(r==="monitor-start")return"Monitor start";if(r==="monitor-stop")return"Monitor stop";if(l==="monitor-prompt-delivered"||f==="monitor-prompt-queued")return"Monitor prompt";if(l==="append-prompt-queued")return"追加 prompt 已排队";return"追加 prompt"}function Pz(u){let f=xr(u);if(f==="control-command-applied")return 3;if(f==="control-command-ignored")return 2;if(f==="control-command-queued")return 1;return 0}function Mz(u,f){let l=String(u?.commandId||"");if(l)return`command:${l}`;return["fallback",Hy(u)||w$(u?.createdAt,u?.timestamp)||`index-${f}`,String(u?.sourceKind||""),String(u?.sourceNodeId||""),String(u?.targetNodeId||""),Oy(u)].join(":")}function jt(u){return $j([u?.targetNodeId,...Hu(u?.resetNodeIds)])}function Ft(u,f){let l=N$(u),r=xr(u),y=String(u?.targetNodeId||""),i=Boolean(y)&&f!==y;if(r==="control-command-applied")return i?`${l} 波及`:`${l} 生效`;if(r==="control-command-ignored")return`${l} 忽略`;if(r==="control-command-queued")return`${l} 已发起`;return i?`${l} 波及`:l}function Jt(u){if(xr(u)==="control-command-ignored")return"ignored";let l=Oy(u);if(l==="restart"||l==="redo")return"restart";if(l==="modify")return"modify";if(l==="approve")return"approve";if(l==="guide")return"guide";return"pending"}function Ut(u){let f=String(u?.sourceKind||"").toLowerCase();if(f==="monitor")return"monitor";if(f==="webui")return"webui";if(f==="cli")return"cli";return"system"}function Qt(u,f,l,r){let y=u.filter((j)=>String(j.nodeId||"")===f).sort((j,F)=>Number(j.startMs)-Number(F.startMs)),i=y.find((j)=>l>=Number(j.startMs)-1000&&l<=Number(j.endMs)+1000);if(i)return{ms:l,onInterval:!0,snapReason:"inside-interval",procedureRunId:String(i.procedureRunId||"")};let _=Oy(r),n=y.slice().reverse().find((j)=>Number(j.endMs)<=l+1000);if(n&&_==="approve")return{ms:Number(n.endMs),onInterval:!0,snapReason:"previous-interval-end",procedureRunId:String(n.procedureRunId||"")};let $=y.find((j)=>Number(j.startMs)>=l-1000);if($&&["guide","modify","restart","redo"].includes(_))return{ms:Number($.startMs),onInterval:!0,snapReason:"next-interval-start",procedureRunId:String($.procedureRunId||"")};return{ms:l,onInterval:!1,snapReason:"event-time",procedureRunId:String(r?.procedureRunId||"")}}function iG(u,f,l,r){let y=Math.hypot(l-u,r-f),i=y>Wz?Wz:0,_=i>0?l-(l-u)/y*i:l,n=i>0?r-(r-f)/y*i:r,$=_-u,j=Math.max(16,Math.min(42,Math.abs($)*0.45+12)),F=$===0?1:Math.sign($);return`M ${u},${f} C ${u+F*j},${f} ${_-F*j},${n} ${_},${n}`}function qt(u,f){let l=String(u?.runId||f?.runId||""),r=dz({...Yu(f)?f:{},...Yu(u)?u:{},runId:l,procedureRuns:Hu(u?.procedureRuns).length>0?u.procedureRuns:f?.procedureRuns},[]),y=[],i=[],_=[],n=new Set,$=new Map,j=(U,q)=>{if(!U.nodeId||!Number.isFinite(Number(U.ms)))return;if(n.has(U.id))return;n.add(U.id),q.push(U)};for(let U of Hu(u?.procedureRuns)){let q=A8(U,l),W=Tr(U);if(!q)continue;for(let G of Hu(U?.attempts)){let K=$8(G),Q=new Set,N=new Set;for(let z of c$(G?.controlEventRecords)){let w=xr(z);if(!["initial-prompt-delivered","append-prompt-delivered","monitor-prompt-delivered"].includes(w))continue;let H=Hy(z),B=hu(H);if(B===null)continue;let T=String(z?.eventId||"");if(T)Q.add(T);N.add(`${w}:${H}:${String(z?.sourceKind||"")}:${String(z?.promptPreview||"")}`),j({id:`prompt:${T||`${W}:${K}:${w}:${B}`}`,runId:l,nodeId:q,procedureRunId:W,attempt:K,kind:"prompt",tone:tz(z,w),status:"delivered",label:Sz(z,w),ms:B,timestampIso:H,sourceKind:String(z?.sourceKind||""),sourceNodeId:String(z?.sourceNodeId||""),targetNodeId:q,action:"",eventId:T,commandId:String(z?.commandId||""),raw:z},y)}let c=[{records:c$(G?.controlPromptRecords),fallbackKind:"append-prompt-queued"},{records:c$(G?.monitorPromptRecords),fallbackKind:"monitor-prompt-queued"}];for(let z of c)for(let w of z.records){let H=Hy(w),B=hu(H);if(B===null)continue;let T=String(w?.eventId||"");if(T&&Q.has(T))continue;let R=`${z.fallbackKind==="monitor-prompt-queued"?"monitor-prompt-delivered":"append-prompt-delivered"}:${H}:${String(w?.sourceKind||"")}:${String(w?.promptPreview||"")}`;if(N.has(R))continue;j({id:`prompt-fallback:${T||`${W}:${K}:${z.fallbackKind}:${B}`}`,runId:l,nodeId:q,procedureRunId:W,attempt:K,kind:"prompt",tone:tz(w,z.fallbackKind),status:"queued",label:Sz(w,z.fallbackKind),ms:B,timestampIso:H,sourceKind:String(w?.sourceKind||""),sourceNodeId:String(w?.sourceNodeId||""),targetNodeId:q,action:"",eventId:T,commandId:String(w?.commandId||""),raw:w},y)}}}let F=new Map;c$(u?.controlEvents).forEach((U,q)=>{let W=Mz(U,q),G=F.get(W)||{key:W,events:[],commands:[]};G.events.push(U),F.set(W,G)}),Hu(u?.controlCommands).filter(Yu).forEach((U,q)=>{let W=Mz(U,q),G=F.get(W)||{key:W,events:[],commands:[]};G.commands.push(U),F.set(W,G)});for(let U of F.values()){let q=Hu(U.events).slice().sort((P,t)=>Pz(t)-Pz(P)),W=Hu(U.commands),G=Hu(U.events).find((P)=>xr(P)==="control-command-queued")||W[0]||null,K=q[0]||W[0]||G;if(!G&&!K)continue;let Q=String(G?.sourceNodeId||K?.sourceNodeId||""),N=String(G?.sourceKind||K?.sourceKind||""),c=Hy(G)||Hy(K)||w$(G?.createdAt,K?.createdAt),z=hu(c),w=String(K?.commandId||G?.commandId||U.key),H=(xr(K)||"control-command-queued").replace(/^control-command-/u,""),B="";if(Q&&z!==null)B=`control-source:${w}:${Q}`,$.set(w,B),j({id:B,runId:l,nodeId:Q,procedureRunId:String(G?.procedureRunId||K?.procedureRunId||""),attempt:"",kind:"control-source",tone:Ut(G||K),status:H,label:`${N$(G||K)} 发起`,ms:z,timestampIso:c,action:Oy(G||K),sourceKind:N,sourceNodeId:Q,targetNodeId:String(K?.targetNodeId||G?.targetNodeId||""),commandId:w,raw:G||K},i);let T=K||G,X=Hy(T)||c,R=hu(X);if(R===null)continue;let Y=jt(T);for(let P of Y){let t=Qt(r,P,R,T),O=`control-target:${w}:${P}`;if(j({id:O,runId:l,nodeId:P,procedureRunId:t.procedureRunId,attempt:"",kind:"control-target",tone:Jt(T),status:H,label:Ft(T,P),ms:t.ms,eventMs:R,onInterval:t.onInterval,snapReason:t.snapReason,snapped:Number(t.ms)!==R,timestampIso:X,renderedTimestampIso:G$(Number(t.ms)),action:Oy(T),sourceKind:N,sourceNodeId:Q,targetNodeId:P,commandId:w,raw:T},i),B&&Q&&Q!==P)_.push({id:`control-arrow:${w}:${Q}:${P}`,commandId:w,sourceNodeId:Q,targetNodeId:P,sourceMarkerId:B,targetMarkerId:O,sourceKind:N,action:Oy(T),status:H})}}let J=[...y,...i].sort((U,q)=>Number(U.ms)-Number(q.ms)||String(U.nodeId).localeCompare(String(q.nodeId))||String(U.id).localeCompare(String(q.id)));return{...$t(J,_),sourceMarkerByCommand:$}}function Wt({details:u,selectedNodeId:f,selectedNodeRuntime:l,control:r,onRaw:y}){if(!u)return L("span",{className:"muted"},"点击“抓取过程”读取 node 运行材料;主界面只显示结构化摘要,完整内容需点开原始 JSON。");let i=Hu(u.procedureRuns),_=i.at(-1)||{},n=Hu(_.attempts),$=n.at(-1)||{},j=Hu(_.workerLogTail),F=Hu($.controlEventsTail),J=Hu($.controlPromptsTail),U=Hu($.monitorPromptsTail),q=uj(F),W=uj(J),G=uj(U),K=$.opencodeMessages||{};return L("div",{className:"pipeline-evidence-list compact"},L(wr,{title:"Node runtime",subtitle:f||"--",facts:[`status ${l?.status||"pending"}`,`attempts ${l?.attempts??n.length}`,`procedure ${l?.currentProcedureRunId||Tr(_)||"--"}`,r.fetchedAt?`fetched ${qf(r.fetchedAt)}`:"not fetched"],data:u.node||u,onRaw:y,testId:"raw-pipeline-node-runtime"}),L(wr,{title:"Procedure runs",subtitle:`${i.length} groups`,facts:[`latest ${_.status?.status||_.status||"--"}`,`steps ${Hu(_.recentSteps).length}`,`duration ${Er(hu(_.finishedAt)&&hu(_.startedAt)?Number(hu(_.finishedAt))-Number(hu(_.startedAt)):_.durationMs)}`],data:i,onRaw:y,testId:"raw-pipeline-node-procedures"}),L(wr,{title:"OpenCode messages",subtitle:String(K.exists?"available":"not indexed"),facts:[`messages ${u8(K.messageCount)}`,`size ${u8(K.size)}`,`updated ${Nu(K.updatedAt)}`],data:K,onRaw:y,testId:"raw-pipeline-node-messages"}),L(wr,{title:"Control prompts",subtitle:"manual / monitor append queues",facts:[`manual tail ${W.total}`,`monitor tail ${G.total}`,`last ${Nu(Uj(W.lastAt,G.lastAt))}`],data:{controlPromptsTail:J,monitorPromptsTail:U},onRaw:y,testId:"raw-pipeline-node-prompts"}),L(wr,{title:"Control events",subtitle:q.eventKinds.length>0?q.eventKinds.join(", "):"event tail",facts:[`tail ${q.total}`,`parsed ${q.parsed}`,`last ${Nu(q.lastAt)}`],data:F,onRaw:y,testId:"raw-pipeline-node-events"}),L(wr,{title:"Worker log",subtitle:"tail is hidden on main canvas",facts:[`tail ${j.length} lines`,"raw only via button",`procedure ${Tr(_)||"--"}`],data:j,onRaw:y,testId:"raw-pipeline-node-worker-log"}))}function ct({activeRun:u,onRaw:f}){if(!u)return L($r,{title:"暂无运行材料",text:"没有 Pipeline epoch 时不会展示运行材料索引。"});let l=Hu(u.nodes),r=Hu(u.procedureRuns),y=Hu(u.submissions),i=Hu(u.workerLogTail),_=zz(l),n=zz(r),$=r.filter((F)=>String(F?.status||"").toLowerCase()==="failed"),j=Uj(...r.flatMap((F)=>[F.updatedAt,F.finishedAt,F.startedAt]));return L("div",{className:"pipeline-evidence-list"},L(wr,{title:"Epoch overview",subtitle:u.runId||"--",facts:[`pipeline ${u.pipelineId||"--"}`,`status ${u.status||"--"}`,`started ${Nu(u.startedAt)}`,`updated ${Nu(u.updatedAt)}`],data:u,onRaw:f,testId:"raw-pipeline-run"}),L(wr,{title:"Node states",subtitle:`${l.length} nodes`,facts:[`running ${_.running||0}`,`succeeded ${_.succeeded||0}`,`failed ${_.failed||0}`,`pending ${_.pending||0}`],data:l,onRaw:f,testId:"raw-pipeline-run-nodes"}),L(wr,{title:"Procedure run index",subtitle:`${r.length} procedure records`,facts:[`succeeded ${n.succeeded||0}`,`failed ${n.failed||0}`,`latest ${Nu(j)}`,`errors ${$.length}`],data:r,onRaw:f,testId:"raw-pipeline-run-procedures"}),L(wr,{title:"OA submissions",subtitle:`${y.length} submission files`,facts:[`records ${y.length}`,`task ${u8(u.task)}`,"raw grouped by run"],data:y,onRaw:f,testId:"raw-pipeline-run-submissions"}),L(wr,{title:"Worker log tail",subtitle:"hidden from main interface",facts:[`tail ${i.length} lines`,"display raw only after click",`updated ${Nu(u.updatedAt)}`],data:i,onRaw:f,testId:"raw-pipeline-run-worker-log"}))}function Nt({diagnostics:u,onRaw:f}){let l=Hu(u?.runs).filter(Yu),r=Hu(u?.forbiddenResiduals),y=Yu(u?.guarantees)?u.guarantees:{},i=u?.hasNeutralNodeFinishedEvidence===!0&&u?.hasNoAuditPolicyEvidence===!0&&u?.hasAuditPolicyEvidence===!0,_=u?.ok===!0&&i&&r.length===0,n=l[0]||null,$=[{label:"中性完成事实",ok:y.neutralNodeFinished===!0,hint:"node-finished 不携带流程策略"},{label:"Config 策略判定",ok:y.auditPolicyFromConfig===!0,hint:"OA backend 读取当前 epoch 配置"},{label:"控制命令来自 OA",ok:y.runnerConsumesControlCommandsFromOaEvents===!0,hint:"runner 只消费 OA control.command"},{label:"无独立审核事件",ok:y.noIndependentAuditRequestEvent===!0,hint:"审核由 node-finished + policy 派生"},{label:"无批次门禁",ok:y.noBatchFinishedControlGate===!0,hint:"下游启动由每个 node 完成驱动"}];return L("div",{className:"pipeline-oa-panel","data-testid":"pipeline-oa-event-flow-panel"},L("div",{className:"metric-grid compact"},L(Hl,{label:"OA Flow",value:_?"100%":"--",hint:String(u?.mode||"waiting diagnostics"),tone:_?"ok":"warn"}),L(Hl,{label:"禁止残留",value:r.length,hint:r.length===0?"source scan clean":"needs cleanup",tone:r.length===0?"ok":"warn"}),L(Hl,{label:"No-audit",value:u?.hasNoAuditPolicyEvidence?"OK":"--",hint:"OA 下游策略证据",tone:u?.hasNoAuditPolicyEvidence?"ok":"warn"}),L(Hl,{label:"Monitor 审核",value:u?.hasAuditPolicyEvidence?"OK":"--",hint:"OA 控制事件闭环",tone:u?.hasAuditPolicyEvidence?"ok":"warn"})),L("div",{className:"pipeline-oa-guarantees"},$.map((j)=>L("article",{key:j.label,className:`pipeline-oa-guarantee ${j.ok?"ok":"warn"}`},L(D1,{status:j.ok?"online":"warn"},j.ok?"OK":"MISS"),L("div",null,L("strong",null,j.label),L("span",null,j.hint))))),L("div",{className:"pipeline-evidence-list compact"},l.slice(0,6).map((j)=>L(wr,{key:j.runId,title:String(j.runId||"--"),subtitle:[Number(j.monitorAuditNodeFinishedCount||0)>0?"monitor audit":"",Number(j.noAuditPolicyCount||0)>0?"no-audit policy":""].filter(Boolean).join(" / ")||"event evidence",facts:[`events ${j.eventCount||0}`,`node-finished ${j.nodeFinishedCount||0}`,`policy-in-detail ${j.nodeFinishedWithPolicyCount||0}`,`queued ${j.controlQueuedCount||0}`,`applied ${j.controlAppliedCount||0}`],data:j,onRaw:f,testId:`raw-pipeline-oa-run-${String(j.runId||"run").replace(/[^a-zA-Z0-9_.-]+/g,"-")}`}))),n?L("p",{className:"muted paragraph"},`最新证据 ${n.runId}: ${n.nodeFinishedCount||0} 个 node-finished,${n.controlAppliedCount||0} 个控制结果。`):L($r,{title:"暂无 OA 事件流证据",text:"等待 Pipeline backend 暴露 diagnostics。"}),u?L("div",{className:"panel-actions inline-actions"},L(Rr,{title:"Pipeline OA Event Flow Diagnostics",data:u,onOpen:f,testId:"raw-pipeline-oa-event-flow"})):null)}function zt({quota:u,onRaw:f}){let l=Yu(u?.summary)?u.summary:{},r=Yu(u?.target)?u.target:{},y=Yu(u?.cache)?u.cache:{},i=u?.ok===!0,_=String(u?.modelId||l.modelName||r.modelName||"MiniMax-M2.7"),n=l.totalCount??r.currentIntervalTotalCount,$=l.usageCount??r.currentIntervalUsageCount,j=l.remainingCount??r.currentIntervalRemainingCount,F=l.remainingRatio??(Number.isFinite(Number(n))&&Number(n)>0&&Number.isFinite(Number(j))?Number(j)/Number(n):void 0),J=l.usageRatio??(Number.isFinite(Number(n))&&Number(n)>0&&Number.isFinite(Number($))?Number($)/Number(n):void 0),U=l.resetAt||r.endAt,q=l.remainsMs??r.remainsMs,W=Number(j),G=!i||Number.isFinite(W)&&W<=0?"warn":"ok",K=[i?`endpoint ${u?.endpoint||"--"}`:"quota unavailable",`fetched ${e4(u?.fetchedAt)}`,y.hit?`cache ${Er(y.ageMs)}`:"live quota"];return L("div",{className:"pipeline-minimax-quota-panel","data-testid":"pipeline-minimax-quota-panel"},L("div",{className:"metric-grid compact"},L(Hl,{label:"MiniMax",value:i?_:"--",hint:u?.modelComponent||u?.error||"model/minimax-m27",tone:G}),L(Hl,{label:"当前窗口",value:`${e7($)}/${e7(n)}`,hint:`已用 ${Nz(J)}`,tone:G}),L(Hl,{label:"剩余额度",value:e7(j),hint:`剩余 ${Nz(F)}`,tone:G}),L(Hl,{label:"重置时间",value:e4(U),hint:q!==void 0?`约 ${Er(q)}`:Nu(U),tone:G})),L(qj,{items:K}),i?L("p",{className:"muted paragraph"},`MiniMax 限额来自 D601 Pipeline 后端实时查询;当前模型匹配 ${l.modelName||r.modelName||_}。`):L(jf,{error:u?.error||"MiniMax 限额查询失败"}),u?L("div",{className:"panel-actions inline-actions"},L(Rr,{title:"Pipeline MiniMax Quota",data:u,onOpen:f,testId:"raw-pipeline-minimax-quota"})):null)}function Gt({epochs:u,activeRun:f,activePipeline:l,pipelineNodes:r,pipelineEdges:y,runDetails:i,nodeDetails:_,nodeDetailsState:n,ganttScale:$=L$,onGanttScaleChange:j,onRunChange:F,onIntervalSelect:J,onMarkerSelect:U,selection:q,detailOpen:W,onDetailOpenChange:G,onRaw:K}){let[Q,N]=xl(ut),[c,z]=xl({startY:0,endY:0,startMs:0,endMs:0}),[w,H]=xl(Date.now()),B=X1(null),T=String(f?.runId||""),X=Boolean(W),R=(ju)=>{if(typeof G==="function")G(ju)},Y=t1($??L$),P=String(i?.runId||"")===T?i?.details:null,t=P?{...Yu(f)?f:{},...Yu(P)?P:{},runId:T,procedureRuns:Hu(P?.procedureRuns).length>0?P.procedureRuns:f?.procedureRuns}:f,O=dz(t,r,w),M=P?qt(P,t):{markers:[],arrows:[]},S=Hu(M.markers),b=eD(t,O,S),Z=it(b,Y,O,w),D=String(Z.source||"frontend-y"),I=O.map((ju)=>_t(ju,Z,w)),k={startMs:Number(Z.startMs),endMs:Number(Z.endMs),durationMs:Math.max(1,Number(Z.durationMs??Number(Z.endMs)-Number(Z.startMs)))},h=Gj(Y),o={...h,pxPerMinute:Number(Z.pxPerMinute??h.pxPerMinute)},s=Math.round(Number(Z.chartHeight||360)),x=O.some(Kj);$0(()=>{if(!T||!x)return;let ju=window.setInterval(()=>H(Date.now()),1000);return()=>window.clearInterval(ju)},[T,x]);let uu=hD(l,r,Array.isArray(y)?y:[]),nu=r.map((ju)=>String(ju?.id||"")).filter(Boolean),$u=I.map((ju)=>String(ju.nodeId||"")).filter(Boolean),Fu=S.map((ju)=>String(ju.nodeId||"")).filter(Boolean),Ku=Array.from(new Set([...uu,...nu,...$u,...Fu])),Wu={startY:0,endY:s,startMs:Number(k.startMs),endMs:Number(k.endMs)},m=Number(c?.endY||0)>0?c:Wu,d=(ju)=>{return _8(ju,k,s,Z)<=Number(m.endY)&&Jj(ju,k,s,Z)>=Number(m.startY)},e=(ju)=>{let bu=nr(ju,k,s,Z);return bu>=Number(m.startY)&&bu<=Number(m.endY)},cu=new Set(Ku.filter((ju)=>I.some((bu)=>bu.nodeId===ju&&d(bu))||S.some((bu)=>bu.nodeId===ju&&e(bu)))),g=Q?Ku.filter((ju)=>cu.has(ju)):Ku,Qu=`${o7}px ${g.length>0?g.map(()=>`${_0}px`).join(" "):"minmax(160px, 1fr)"}`,Eu=Hu(Z.ticks).filter(Yu),Tu=String(q?.mode==="interval"?q?.interval?.procedureRunId||"":""),Du=String(q?.mode==="event"?q?.marker?.id||"":""),ff=()=>{let ju=B.current;if(!ju){z(Wu);return}let bu=Math.max(0,ju.scrollTop-d7),ul=Math.max(120,ju.clientHeight-d7),tu=Math.min(s,bu+ul),ou={startY:bu,endY:tu,startMs:Number(k.startMs),endMs:Number(k.endMs)},Al=Math.max(0,Math.min(1,bu/s)),Bl=Math.max(Al,Math.min(1,tu/s)),sf=Math.max(1,Number(k.endMs)-Number(k.startMs));ou.startMs=Number(k.startMs)+sf*Al,ou.endMs=Number(k.startMs)+sf*Bl,z(ou)};$0(()=>{let ju=B.current,bu=window.setTimeout(ff,0);return ju?.addEventListener("scroll",ff),window.addEventListener("resize",ff),()=>{window.clearTimeout(bu),ju?.removeEventListener("scroll",ff),window.removeEventListener("resize",ff)}},[T,k.startMs,k.endMs,s]);let rf=Math.max(0,Ku.length-g.length),Lf=new Set(S.filter((ju)=>g.includes(String(ju.nodeId||""))&&e(ju)).map((ju)=>String(ju.id))),gf=new Map(S.map((ju)=>[String(ju.id),ju])),jr=Hu(M.arrows).filter((ju)=>{if(!Lf.has(String(ju.targetMarkerId||"")))return!1;if(String(ju.action||"")==="observe")return g.includes(String(ju.sourceNodeId||""));return Lf.has(String(ju.sourceMarkerId||""))}),Ol=o7+Math.max(1,g.length)*_0,ef=(ju)=>{let bu=t1(ju.target.value);if(typeof j==="function")j(bu);window.setTimeout(ff,0)},hr=()=>aD({title:`${l?.id||"pipeline"}-${T||"epoch"}-gantt`,meta:[`run ${T||"--"}`,`${Nu(k.startMs)} -> ${Nu(k.endMs)}`,`duration ${Er(k.durationMs)}`,`${o.label} / ${yj(o.pxPerMinute)} px/min`,`${g.length}/${Ku.length} nodes`,`${S.length} markers`],visibleNodeIds:g,intervals:I,markers:S.filter((ju)=>g.includes(String(ju.nodeId||""))),arrows:jr,ticks:Eu,bounds:k,chartHeight:s,backendLayout:Z}),vl=Yu(P?.gantt?.diagnostics)?P.gantt.diagnostics:null;return L(n0,{title:"Epoch 甘特图",eyebrow:`${l?.id||"pipeline"} / ${u.length} epochs`,className:"pipeline-wide-panel",loading:i?.loading,actions:L("div",{className:"pipeline-gantt-actions"},L("select",{value:T,disabled:u.length===0,onChange:(ju)=>F(ju.target.value),"data-testid":"pipeline-epoch-select"},u.map((ju)=>L("option",{key:ju.runId,value:ju.runId},jj(u,ju)))),L("label",{className:"pipeline-gantt-toggle"},L("input",{type:"checkbox","data-testid":"pipeline-gantt-auto-hide-idle",checked:Q,onChange:(ju)=>{N(Boolean(ju.target.checked)),window.setTimeout(ff,0)}}),L("span",null,"自动隐藏空闲列")),L("label",{className:"pipeline-gantt-scale"},L("span",null,L("b",null,"时间尺度"),L("em",{"data-testid":"pipeline-gantt-scale-label"},`${o.label} · ${yj(o.pxPerMinute)} px/min`)),L("input",{type:"range",min:0,max:100,step:0.01,value:Y,onChange:ef,"aria-label":"调整甘特图时间尺度","data-testid":"pipeline-gantt-time-scale"}),L("small",null,L("span",null,"全局"),L("span",null,"细节"))),f?L("button",{type:"button",className:"ghost-btn",onClick:hr,disabled:g.length===0,"data-testid":"pipeline-export-gantt"},"导出甘特图"):null,f?L(Rr,{title:`Pipeline Epoch ${f.runId}`,data:f,onOpen:K,testId:"raw-pipeline-epoch-gantt"}):null)},!f?L($r,{title:"暂无 Epoch",text:"当前 pipeline 还没有完整运行记录。"}):I.length===0?L($r,{title:"暂无时间区间",text:"等待 D601 Pipeline backend 在 procedure summary 中返回 startedAt / finishedAt。"}):L("div",{className:"pipeline-gantt-wrap"},L("div",{className:`pipeline-gantt-detail-layout ${X?"detail-open":"detail-collapsed"}`,"data-testid":"pipeline-gantt-detail-layout","data-sidebar-open":X?"true":"false"},L("div",{className:"pipeline-gantt-main"},L("div",{className:"pipeline-gantt-main-head"},L("div",{className:"pipeline-gantt-meta"},L("span",null,`time ${Nu(k.startMs)} -> ${Nu(k.endMs)}`),L("span",null,`duration ${Er(k.durationMs)}`),L("span",null,`scale ${o.label} / ${yj(o.pxPerMinute)} px/min`),L("span",null,`layout ${D}`),vl?L("span",null,`align ${vl.timeAxisAlignmentOk===!1?"check":"ok"}`):null,L("span",null,`visible ${g.length}/${Ku.length} nodes`),P?L("span",null,`markers ${S.length}`):null,Q&&rf>0?L("span",null,`hidden idle ${rf}`):null),!X?L("button",{type:"button",className:"pipeline-sidecar-tab right",disabled:!q?.mode,onClick:()=>R(!0),"data-testid":"pipeline-gantt-sidebar-toggle"},q?.mode?"展开详情":"点击甘特图元素展开详情"):null),L("div",{className:"pipeline-gantt-viewport",ref:B,"data-testid":"pipeline-epoch-gantt","data-pipeline-id":l?.id||"","data-run-id":T,"data-layout-source":D,"data-start-ms":String(k.startMs),"data-end-ms":String(k.endMs),"data-chart-height":String(s)},L("div",{className:"pipeline-gantt-board",style:{gridTemplateColumns:Qu,minWidth:`${Ol}px`}},L("div",{className:"pipeline-gantt-head time"},"Time"),g.length===0?L("div",{className:"pipeline-gantt-head empty"},"当前时间窗无工作节点"):g.map((ju)=>L("div",{key:`head-${ju}`,className:"pipeline-gantt-head node",title:ju,"data-testid":"pipeline-gantt-head-node","data-node-id":ju},L(ED,{value:ju}))),L("div",{className:"pipeline-gantt-time-axis",style:{height:`${s}px`}},Eu.map((ju)=>{let bu=yG(ju,k,s,Z);return L("div",{key:`tick-${ju.ms}-${bu}`,className:"pipeline-gantt-tick",style:{top:`${bu}px`},"data-testid":"pipeline-gantt-tick","data-ms":String(ju.ms),"data-y":String(bu)},L("b",null,Nu(ju.ms)),L("span",null,`+${Er(Number(ju.offsetMs??Number(ju.ms)-Number(k.startMs)))}`))})),g.length>0?L("svg",{className:"pipeline-gantt-arrow-layer",width:g.length*_0,height:s,viewBox:`0 0 ${g.length*_0} ${s}`,style:{left:`${o7}px`,top:`${d7}px`,width:`${g.length*_0}px`,height:`${s}px`},"aria-hidden":"true"},L("defs",null,L("marker",{id:"pipeline-gantt-arrowhead",viewBox:"0 0 10 10",refX:9,refY:5,markerWidth:6,markerHeight:6,orient:"auto-start-reverse"},L("path",{d:"M 0 0 L 10 5 L 0 10 z",fill:"context-stroke"}))),jr.map((ju)=>{let bu=gf.get(String(ju.targetMarkerId||""));if(!bu)return null;let ul=gf.get(String(ju.sourceMarkerId||"")),tu=String(ul?.nodeId||ju.sourceNodeId||""),ou=g.indexOf(tu),Al=g.indexOf(String(bu.nodeId||""));if(ou<0||Al<0)return null;let Bl=ou*_0+_0/2,sf=Al*_0+_0/2,Il=ul?nr(ul,k,s,Z):nr(bu,k,s,Z),Fr=nr(bu,k,s,Z);return L("path",{key:ju.id,className:`pipeline-gantt-arrow ${String(ju.sourceKind||"").toLowerCase()} ${String(ju.status||"").toLowerCase()} ${String(ju.action||"").toLowerCase()}`,d:iG(Bl,Il,sf,Fr),markerEnd:"url(#pipeline-gantt-arrowhead)","data-testid":String(ju.action||"")==="observe"?"pipeline-gantt-observation-arrow":"pipeline-gantt-arrow","data-source-node-id":String(ju.sourceNodeId||""),"data-target-node-id":String(ju.targetNodeId||""),"data-target-marker-id":String(ju.targetMarkerId||""),"data-action":String(ju.action||""),"data-source-y":String(Il),"data-target-y":String(Fr)})})):null,g.length===0?L("div",{className:"pipeline-gantt-empty-col",style:{height:`${s}px`}},"滚动到有活动的时间段后,相关 node 列会自动出现。"):g.map((ju)=>{let bu=I.filter((tu)=>tu.nodeId===ju),ul=S.filter((tu)=>String(tu.nodeId||"")===ju);return L("div",{key:`col-${ju}`,className:"pipeline-gantt-node-col",style:{height:`${s}px`}},bu.map((tu)=>{let ou=_8(tu,k,s,Z),Al=Jj(tu,k,s,Z),Bl=rG(tu,k,s,Z),sf=String(tu.procedureRunId||`${ju}-${tu.startMs}`);return L("button",{key:sf,type:"button",className:`pipeline-gantt-bar ${tu.status} ${tu.live?"live":""} ${Tu===sf?"selected":""}`,style:{top:`${ou}px`,height:`${Bl}px`},title:`${ju} ${tu.status} ${Nu(tu.startedAt||tu.startMs)} -> ${Nu(tu.finishedAt||tu.endMs)}`,onClick:()=>J(tu),"data-testid":"pipeline-gantt-line","data-node-id":ju,"data-procedure-run-id":String(tu.procedureRunId||""),"data-status":String(tu.status||""),"data-live":tu.live?"true":"false","data-start-ms":String(tu.startMs||""),"data-end-ms":String(tu.endMs||""),"data-y1":String(ou),"data-y2":String(Al),"data-natural-height":String(Math.max(0,Al-ou))},L("strong",null,tu.status||"working"),L("span",null,Er(tu.durationMs)))}),ul.map((tu)=>L("button",{key:tu.id,type:"button",className:`pipeline-gantt-marker ${tu.kind} ${tu.tone||""} ${tu.status||""} ${Du===String(tu.id)?"selected":""}`,style:{top:`${nr(tu,k,s,Z)}px`},title:`${tu.label||"event"} / ${Nu(tu.timestampIso||tu.timestamp||tu.ms)}`,onClick:()=>U(tu),"data-testid":tu.kind==="prompt"?"pipeline-gantt-prompt-marker":"pipeline-gantt-control-marker","data-marker-id":String(tu.id||""),"data-ms":String(tu.ms??tu.eventMs??""),"data-y":String(nr(tu,k,s,Z))})))})))),X?L(wD,{selection:q,runDetails:i,nodeDetails:_,nodeDetailsState:n,onRaw:K,onCollapse:()=>R(!1)}):null)))}function t0(){return{loading:!1,actionLoading:"",error:"",message:"",details:null,fetchedAt:null,appendPrompt:"",guidePrompt:"",modifyPrompt:"",approveReason:"",redoReason:""}}function V1(){return{mode:"",runId:"",interval:null,marker:null}}function ij(){return{runId:"",loading:!1,error:"",details:null,fetchedAt:null}}function Q$(u,f){return`${u}/microservices/pipeline/proxy${f}`}function Kt({activeRun:u,pipelineRuns:f,selectedRunId:l,onRunChange:r,selectedNodeId:y,selectedNodeConfig:i,selectedNodeRuntime:_,control:n,onControlChange:$,onFetch:j,onAction:F,onRaw:J,onCollapse:U}){let q=String(u?.runId||""),W=String(_?.status||"pending"),G=!q||!y||n.loading||Boolean(n.actionLoading),K=(N)=>(c)=>$({[N]:c.target.value,error:"",message:""}),Q=f.length>0?f:u?[u]:[];return L("aside",{className:"pipeline-node-control","data-testid":"pipeline-node-control"},L("div",{className:"pipeline-node-control-head"},L("div",null,L("p",{className:"panel-eyebrow"},"Manual Node Control"),L(Af,{title:y||"点击控制图中的 node",level:3,loading:n.loading||Boolean(n.actionLoading)})),L("div",{className:"pipeline-node-control-head-actions"},y?L(D1,{status:W},W):L(D1,{status:"pending"},"idle"),L("button",{type:"button",className:"ghost-btn mini",onClick:U,"data-testid":"pipeline-node-sidebar-collapse"},"收起"))),L("div",{className:"pipeline-control-runbar"},L("label",null,L("span",null,"目标 run"),L("select",{value:q||l,disabled:Q.length===0,onChange:(N)=>r(N.target.value),"data-testid":"pipeline-node-run-select"},Q.map((N)=>L("option",{key:N.runId,value:N.runId},`${N.runId||"--"} / ${N.status||"--"}`)))),L("button",{type:"button",className:"ghost-btn",disabled:G,onClick:j,"data-testid":"pipeline-node-fetch"},n.loading?"抓取中":"抓取过程"),n.details?L(Rr,{title:`Pipeline Node ${y}`,data:n.details,onOpen:J,testId:"raw-pipeline-node-control"}):null),L("div",{className:"pipeline-control-meta"},L("span",null,L("b",null,"kind"),String(i?.kind||"--")),L("span",null,L("b",null,"procedure"),String(_?.currentProcedureRunId||"--")),L("span",null,L("b",null,"attempts"),String(_?.attempts??"--")),L("span",null,L("b",null,"updated"),Nu(u?.updatedAt))),!y?L($r,{title:"未选择 node",text:"点击 React Flow 控制图中的任意 node 后,可抓取执行过程、追加 prompt、下发引导、增量修改、审核通过或重做。"}):null,L(jf,{error:n.error,wide:!0}),L("div",{className:"pipeline-control-actions"},L("label",null,L("span",null,"实时追加 prompt(仅 running node)"),L("textarea",{value:n.appendPrompt,onChange:K("appendPrompt"),placeholder:"让当前执行中的 agent 继续、补充检查或调整当前步骤...",rows:4,disabled:!y,"data-testid":"pipeline-node-append-input"}),L("button",{type:"button",className:"primary-btn compact",disabled:G||!String(n.appendPrompt||"").trim(),onClick:()=>F("append"),"data-testid":"pipeline-node-append-button"},n.actionLoading==="append"?"追加中":"追加到运行中 node")),L("label",null,L("span",null,"下次尝试引导 prompt"),L("textarea",{value:n.guidePrompt,onChange:K("guidePrompt"),placeholder:"给该 node 下一次 attempt 的执行提示;不会立即打断当前 session。",rows:4,disabled:!y,"data-testid":"pipeline-node-guide-input"}),L("button",{type:"button",className:"ghost-btn compact",disabled:G||!String(n.guidePrompt||"").trim(),onClick:()=>F("guide"),"data-testid":"pipeline-node-guide-button"},n.actionLoading==="guide"?"下发中":"下发 guide")),L("label",null,L("span",null,"完成后增量修改 prompt"),L("textarea",{value:n.modifyPrompt,onChange:K("modifyPrompt"),placeholder:"在该 node 已完成结果基础上追加修改要求;runner 会重跑目标 node,并保留同 node 既有 OA 输出作为上下文。",rows:4,disabled:!y,"data-testid":"pipeline-node-modify-input"}),L("button",{type:"button",className:"ghost-btn compact",disabled:G||!String(n.modifyPrompt||"").trim(),onClick:()=>F("modify"),"data-testid":"pipeline-node-modify-button"},n.actionLoading==="modify"?"排队中":"增量修改 node")),L("label",null,L("span",null,"Monitor 审核通过原因"),L("textarea",{value:n.approveReason,onChange:K("approveReason"),placeholder:"当流程配置开启 monitor 审核时,记录审核通过原因并释放后续 node。",rows:3,disabled:!y,"data-testid":"pipeline-node-approve-input"}),L("button",{type:"button",className:"primary-btn compact",disabled:G||!String(n.approveReason||"").trim(),onClick:()=>F("approve"),"data-testid":"pipeline-node-approve-button"},n.actionLoading==="approve"?"提交中":"审核通过")),L("label",null,L("span",null,"重做 / restart 原因"),L("textarea",{value:n.redoReason,onChange:K("redoReason"),placeholder:"说明为什么需要重做;runner 会重置目标 node 以及非 rework 下游 node。",rows:4,disabled:!y,"data-testid":"pipeline-node-redo-input"}),L("button",{type:"button",className:"danger-btn compact",disabled:G||!String(n.redoReason||"").trim(),onClick:()=>F("redo"),"data-testid":"pipeline-node-redo-button"},n.actionLoading==="redo"?"排队中":"重做 node"))),L("div",{className:"pipeline-control-evidence"},L("strong",null,"Node 过程索引"),L(Wt,{details:n.details,selectedNodeId:y,selectedNodeRuntime:_,control:n,onRaw:J})))}function _G({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((fu)=>fu.id==="pipeline")||null,[y,i]=xl({loading:!1,error:"",health:null,snapshot:null,oaDiagnostics:null,minimaxQuota:null,refreshedAt:null}),[_,n]=xl(""),[$,j]=xl(""),[F,J]=xl(""),[U,q]=xl(t0()),[W,G]=xl({}),[K,Q]=xl(V1()),[N,c]=xl(ij()),[z,w]=xl(L$),[H,B]=xl(!1),[T,X]=xl(!1),R=X1(0),{addNotification:Y}=wl(),P=X1(!1),t=X1(0),O=X1(""),M=X1({}),S=X1(""),b=X1("");async function Z(fu={}){let Zu=fu.silent===!0;if(!r)return;if(P.current)return;P.current=!0;let Lu=R.current+1;if(R.current=Lu,!Zu)i((ku)=>({...ku,loading:!0,error:""}));try{let ku=`__unideskArrayLimit=registry.components:80,runs:${_D}`,[Uf,vu,yf]=await Promise.all([B1(`${l}/microservices/pipeline/proxy/api/snapshot?${ku}`,{cache:"no-store"}),B1(`${l}/microservices/pipeline/proxy/api/oa-event-flow/diagnostics`,{cache:"no-store"}).catch((xf)=>({ok:!1,error:Bu(xf,"OA event flow diagnostics failed")})),B1(`${l}/microservices/pipeline/proxy/api/model-quota/minimax`,{cache:"no-store"}).catch((xf)=>({ok:!1,error:Bu(xf,"MiniMax quota failed")}))]);if(Lu!==R.current)return;let _f={ok:Uf?.ok!==!1,service:"pipeline-v2-control snapshot"};i({loading:!1,error:"",health:_f,snapshot:Uf,oaDiagnostics:vu,minimaxQuota:yf,refreshedAt:new Date})}catch(ku){if(Lu!==R.current)return;i((Uf)=>({...Uf,loading:!1,error:Bu(ku,"Pipeline 加载失败")}))}finally{P.current=!1}}$0(()=>{if(Z(),!r)return;let fu=()=>{if(g4())Z({silent:!0})},Zu=window.setInterval(()=>{fu()},qz),Lu=()=>{if(g4())fu()};return document.addEventListener("visibilitychange",Lu),()=>{window.clearInterval(Zu),document.removeEventListener("visibilitychange",Lu)}},[r?.id,r?.runtime?.providerStatus,l]);let D=TD(r),I=HD(r),k=ZD(r),h=y.snapshot||{},o=y.oaDiagnostics||null,s=y.minimaxQuota||null,{components:x,pipelines:uu,runs:nu}=OD(h),$u=String(nu[0]?.pipelineId||""),Fu=($u?uu.find((fu)=>String(fu.id||"")===$u):null)||uu[0]||{},Ku=uu.find((fu)=>String(fu.id||"")===_)||Fu,Wu=String(Ku.id||""),m=kz(Ku),d=cj(Ku),e=Xz(nu,Wu),cu=dD(nu,Wu),g=cu.find((fu)=>String(fu?.runId||"")===$)||e,Qu=String(N.runId||"")===String(g?.runId||"")?YD(N.details):null,Eu=DD(g,Qu),Tu=String(Eu?.runId||""),Du=m.find((fu)=>String(fu?.id||"")===F)||null,ff=F?gz(Eu,F):null,rf=VD(nu),Lf=MD(x),gf=Number(y.health?.components)||Ez(h,"registry.components",x.length),jr=Ez(h,"runs",nu.length),Ol=Hz(Ku,Eu,x),ef={nodes:Ol.nodes.map((fu)=>fu.id===F?{...fu,selected:!0,className:`${fu.className||""} selected-control-node`}:fu),edges:Ol.edges},hr=uu.map((fu)=>{let Zu=String(fu.id||"pipeline"),Lu=Xz(nu,Zu);return{title:`${Zu}-${Lu?.runId||"snapshot"}`,flow:Hz(fu,Lu,x)}}),vl=String(K?.runId||Tu||""),ju=String(K?.interval?.nodeId||K?.marker?.nodeId||""),bu=vl&&ju?W[rj(vl,ju)]||null:null,ul=f8(U.details,vl,ju),tu=f8(bu?.details,vl,ju)||ul,ou=vl&&ju?{...Yu(bu)?bu:{},runId:vl,nodeId:ju,details:tu,loading:Boolean(bu?.loading)||!tu&&Boolean(U.loading)&&F===ju,error:String(bu?.error||""),fetchedAt:bu?.fetchedAt||(ul?U.fetchedAt:null)}:null,Al=cu.map((fu)=>String(fu?.runId||"")).filter(Boolean).join("|"),Bl=m.map((fu)=>String(fu?.id||"")).filter(Boolean).join("|");$0(()=>{S.current=F},[F]),$0(()=>{b.current=Tu},[Tu]),$0(()=>{if(!$||Al.split("|").includes($))return;j("")},[$,Al]),$0(()=>{if(!F||Bl.split("|").includes(F))return;J(""),q(t0()),Q(V1()),B(!1),X(!1)},[F,Bl]),$0(()=>{if(!F)B(!1)},[F]),$0(()=>{if(!K.mode)X(!1)},[K.mode]);async function sf(fu=Tu,Zu={}){if(!fu){c(ij());return}let Lu=t1(Zu.scale??z??L$),ku=`${fu}:timeline`;if(O.current===ku)return;O.current=ku;let Uf=Zu.silent===!0,vu=t.current+1;t.current=vu,c((yf)=>({runId:fu,scale:Lu,loading:!Uf||String(yf.runId||"")!==fu||!yf.details,error:"",details:Uf&&yf.runId===fu?yf.details:yf.runId===fu?yf.details:null,fetchedAt:yf.runId===fu?yf.fetchedAt:null}));try{let[yf,_f]=await Promise.all([B1(Q$(l,`/api/node-control/runs/${encodeURIComponent(fu)}?tail=160&view=timeline`),{cache:"no-store",strictJson:!0}),B1(Q$(l,`/api/runs/${encodeURIComponent(fu)}`),{cache:"no-store"}).catch((xf)=>({ok:!1,runSummaryError:Bu(xf,"抓取评分失败")}))]);if(vu!==t.current)return;c({runId:fu,scale:Lu,loading:!1,error:"",details:{...yf,run:Yu(_f?.run)?_f.run:void 0,runSummaryError:_f?.runSummaryError},fetchedAt:new Date})}catch(yf){if(vu!==t.current)return;c((_f)=>({runId:fu,scale:Lu,loading:!1,error:Bu(yf,"抓取 epoch 执行过程失败"),details:_f.runId===fu?_f.details:null,fetchedAt:_f.runId===fu?_f.fetchedAt:null}))}finally{if(O.current===ku)O.current=""}}function Il(fu,Zu,Lu){let ku=rj(fu,Zu);G((Uf)=>{let vu={...Uf,[ku]:{...Yu(Uf?.[ku])?Uf[ku]:{},runId:fu,nodeId:Zu,...Lu}},yf=Object.keys(vu);if(yf.length>32)for(let _f of yf.slice(0,yf.length-32))delete vu[_f];return vu})}async function Fr(fu,Zu){if(!fu||!Zu)return;let Lu=rj(fu,Zu),ku=Number(M.current?.[Lu]||0)+1;M.current={...M.current,[Lu]:ku},Il(fu,Zu,{loading:!0,error:""});try{let Uf=await B1(Q$(l,`/api/node-control/runs/${encodeURIComponent(fu)}/nodes/${encodeURIComponent(Zu)}?tail=160`),{cache:"no-store",strictJson:!0});if(Number(M.current?.[Lu]||0)!==ku)return;let vu=new Date;if(Il(fu,Zu,{loading:!1,details:Uf,fetchedAt:vu,error:""}),S.current===Zu&&b.current===fu)q((yf)=>({...yf,loading:!1,details:Uf,fetchedAt:vu,error:""}))}catch(Uf){if(Number(M.current?.[Lu]||0)!==ku)return;Il(fu,Zu,{loading:!1,error:Bu(Uf,"抓取 Gantt node 详情失败")})}}$0(()=>{if(!Tu){c(ij());return}sf(Tu);let fu=()=>{if(g4())sf(Tu,{silent:!0})},Zu=window.setInterval(()=>{fu()},qz),Lu=()=>{if(g4())fu()};return document.addEventListener("visibilitychange",Lu),()=>{window.clearInterval(Zu),document.removeEventListener("visibilitychange",Lu)}},[Tu,l]);async function kl(fu=Tu,Zu=F){if(!fu||!Zu){q((Lu)=>({...Lu,error:"请先选择 run 和 node",message:""}));return}q((Lu)=>({...Lu,loading:!0,error:"",message:""}));try{let Lu=await B1(Q$(l,`/api/node-control/runs/${encodeURIComponent(fu)}/nodes/${encodeURIComponent(Zu)}?tail=160`),{cache:"no-store",strictJson:!0}),ku=new Date;q((Uf)=>({...Uf,loading:!1,details:Lu,fetchedAt:ku,error:""})),Il(fu,Zu,{loading:!1,details:Lu,fetchedAt:ku,error:""})}catch(Lu){q((ku)=>({...ku,loading:!1,error:Bu(Lu,"抓取 node 执行过程失败")}))}}async function M0(fu){let Zu=String(fu?.runId||Tu||""),Lu=String(fu?.nodeId||"");if(Q({mode:"interval",runId:Zu,interval:fu,marker:null}),X(!0),!Zu||!Lu)return;if(Zu!==Tu)j(Zu);J(Lu),q(t0()),sf(Zu,{silent:!0}),Fr(Zu,Lu)}async function r_(fu){let Zu=String(fu?.runId||Tu||""),Lu=String(fu?.nodeId||"");if(Q({mode:"event",runId:Zu,interval:null,marker:fu}),X(!0),!Zu)return;if(Zu!==Tu)j(Zu);if(sf(Zu,{silent:!0}),!Lu)return;J(Lu),q(t0()),Fr(Zu,Lu)}async function y_(fu){if(!Tu||!F){q((ku)=>({...ku,error:"请先选择 run 和 node",message:""}));return}let Zu=fu==="append"?"prompts":fu,Lu=fu==="append"?U.appendPrompt:fu==="guide"?U.guidePrompt:fu==="modify"?U.modifyPrompt:fu==="approve"?U.approveReason:U.redoReason;if(!String(Lu||"").trim()){q((ku)=>({...ku,error:"操作内容不能为空",message:""}));return}q((ku)=>({...ku,actionLoading:fu,error:"",message:""}));try{let ku=fu==="redo"||fu==="approve"?{reason:Lu,source:"unidesk-frontend",sourceKind:"webui"}:{prompt:Lu,source:"unidesk-frontend",sourceKind:"webui"},Uf=await B1(Q$(l,`/api/node-control/runs/${encodeURIComponent(Tu)}/nodes/${encodeURIComponent(F)}/${Zu}`),{method:"POST",body:JSON.stringify(ku)});if(q((yf)=>({...yf,actionLoading:"",details:Uf,fetchedAt:new Date,appendPrompt:fu==="append"?"":yf.appendPrompt,guidePrompt:fu==="guide"?"":yf.guidePrompt,modifyPrompt:fu==="modify"?"":yf.modifyPrompt,approveReason:fu==="approve"?"":yf.approveReason,redoReason:fu==="redo"?"":yf.redoReason,message:fu==="append"?"已追加到运行中 node":fu==="guide"?"已下发 guide,等待 runner 处理":fu==="modify"?"已排队增量修改命令":fu==="approve"?"已提交审核通过决策":"已排队重做命令"})),Y("success",fu==="append"?"已追加到运行中 node":fu==="guide"?"已下发 guide,等待 runner 处理":fu==="modify"?"已排队增量修改命令":fu==="approve"?"已提交审核通过决策":"已排队重做命令"),await kl(Tu,F),await sf(Tu,{silent:!0}),fu!=="append")await Z()}catch(ku){q((Uf)=>({...Uf,actionLoading:"",error:Bu(ku,"node 控制操作失败")}))}}if(!r)return L($r,{title:"Pipeline 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=pipeline"});return L("div",{className:"pipeline-page","data-testid":"pipeline-page"},L(n0,{title:"Pipeline v2 工作台",eyebrow:"D601 Snapshot 用户服务",loading:y.loading,actions:L("div",{className:"panel-actions"},L("button",{type:"button",className:"ghost-btn",onClick:Z,disabled:y.loading,"data-testid":"pipeline-refresh-button"},y.loading?"刷新中":"刷新"),L(Rr,{title:"Pipeline 用户服务",data:r,onOpen:f,testId:"raw-pipeline-service"}))},L("div",{className:"pipeline-hero"},L("div",null,L("div",{className:"node-version-line"},L(D1,{status:D.providerStatus==="online"?"online":"warn"},D.providerStatus||"unknown"),L("span",null,r.providerId),L("span",null,k.public?"公网暴露":"仅 UniDesk frontend 代理访问")),L("p",{className:"muted paragraph"},r.description)),L("div",{className:"microservice-ref-card"},L("span",null,"Repo"),L("strong",null,I.url||"--"),L("code",null,I.commitId||"--")),L("div",{className:"microservice-ref-card"},L("span",null,"D601 Docker"),L("strong",null,`${k.nodeBindHost||"--"}:${k.nodePort||"--"}`),L("code",null,`${I.composeFile||"--"} / ${I.composeService||"--"}`))),L(jf,{error:y.error,wide:!0})),L("div",{className:"pipeline-grid"},L(n0,{title:"控制图",eyebrow:`${Ku.id||"pipeline"} / run ${Eu?.status||"--"}`,className:"pipeline-wide-panel",loading:y.loading,actions:L("div",{className:"pipeline-toolbar"},L("select",{value:Wu,disabled:uu.length===0,onChange:(fu)=>{n(fu.target.value),j(""),J(""),q(t0()),Q(V1()),B(!1),X(!1)},"data-testid":"pipeline-select"},uu.map((fu)=>L("option",{key:fu.id,value:fu.id},fu.id||fu.key))),L("select",{value:Tu,disabled:cu.length===0,onChange:(fu)=>{if(j(fu.target.value),q(t0()),Q(V1()),B(!1),X(!1),F)kl(fu.target.value,F)},"data-testid":"pipeline-run-select"},cu.map((fu)=>L("option",{key:fu.runId,value:fu.runId},jj(cu,fu)))),L("button",{type:"button",className:"ghost-btn",disabled:ef.nodes.length===0,onClick:()=>oz(ef,`${Ku.id||"pipeline"}-${Eu?.runId||"snapshot"}`),"data-testid":"pipeline-export-graph"},"导出渲染图"),L("button",{type:"button",className:"ghost-btn",disabled:hr.every((fu)=>fu.flow.nodes.length===0),onClick:()=>oD(hr),"data-testid":"pipeline-export-all-graphs"},"批量导出"))},m.length===0?L($r,{title:"暂无控制图",text:"等待 D601 pipeline backend 返回 config.nodes / config.edges"}):L("div",{className:`pipeline-control-shell ${H?"detail-open":"detail-collapsed"}`,"data-testid":"pipeline-control-shell","data-sidebar-open":H?"true":"false"},L("div",{className:"pipeline-flow-frame","data-testid":"pipeline-react-flow"},L($z,{nodes:ef.nodes,edges:ef.edges,nodeTypes:FD,edgeTypes:jD,fitView:!0,fitViewOptions:{padding:0.18},nodesDraggable:!1,nodesConnectable:!1,elementsSelectable:!0,minZoom:0.25,maxZoom:1.4,proOptions:{hideAttribution:!0},onNodeClick:(fu,Zu)=>{let Lu=String(Zu.id);if(J(Lu),q(t0()),B(!0),Tu)kl(Tu,Lu)}},L(jz,{gap:22,size:1,color:"rgba(215, 161, 58, 0.24)"}),L(Jz,{showInteractive:!1})),!H?L("button",{type:"button",className:"pipeline-sidecar-tab right",disabled:!F,onClick:()=>B(!0),"data-testid":"pipeline-node-sidebar-toggle"},F?"展开 node 控制":"点击 node 展开控制"):null),H?L(Kt,{activeRun:Eu,pipelineRuns:cu,selectedRunId:$,onRunChange:(fu)=>{if(j(fu),q(t0()),Q(V1()),F)kl(fu,F)},selectedNodeId:F,selectedNodeConfig:Du,selectedNodeRuntime:ff,control:U,onControlChange:(fu)=>q((Zu)=>({...Zu,...fu})),onFetch:()=>kl(),onAction:y_,onRaw:f,onCollapse:()=>B(!1)}):null),L("div",{className:"pipeline-flow-summary"},L("span",null,`${ef.nodes.length} nodes`),L("span",null,`${ef.edges.length} edges`),L("span",null,`${uu.length} pipelines`),L("span",null,`source config+components(${x.length})`),L("span",null,`run ${Eu?.runId||"--"}`),L("span",null,`score ${Aj(Eu)}`),L("span",null,F?`selected ${F}`:"click node to control"))),L(Gt,{epochs:cu,activeRun:Eu,activePipeline:Ku,pipelineNodes:m,pipelineEdges:d,selection:K,detailOpen:T,onDetailOpenChange:X,runDetails:N,nodeDetails:tu,nodeDetailsState:ou,ganttScale:z,onGanttScaleChange:w,onIntervalSelect:M0,onMarkerSelect:r_,onRunChange:(fu)=>{if(j(fu),q(t0()),Q(V1()),X(!1),F)kl(fu,F)},onRaw:f}),L(n0,{title:"观测指标",eyebrow:y.refreshedAt?`Updated ${qf(y.refreshedAt)}`:"Snapshot",loading:y.loading},L("div",{className:"metric-grid"},L(Hl,{label:"Health",value:y.health?.ok?"OK":"--",hint:y.health?.service||"D601 /health",tone:y.health?.ok?"ok":"warn"}),L(Hl,{label:"组件",value:gf,hint:"components registry",tone:h?.registry?.ok===!1?"warn":"ok"}),L(Hl,{label:"Pipeline",value:uu.length,hint:`${m.length} nodes / ${d.length} edges`}),L(Hl,{label:"运行记录",value:jr,hint:`${rf.succeeded||0} succeeded / ${rf.running||0} running`}),L(Hl,{label:"OA 记录",value:Array.isArray(e?.submissions)?e.submissions.length:0,hint:e?.runId||"latest run"}),L(Hl,{label:"Procedure",value:Array.isArray(e?.procedureRuns)?e.procedureRuns.length:0,hint:e?.status||"no run"}),L(Hl,{label:"Score",value:Aj(Eu),hint:Eu?.runId||"selected epoch",tone:zj(Eu)})),L("div",{className:"panel-actions inline-actions"},L(Rr,{title:"Pipeline Snapshot",data:h,onOpen:f,testId:"raw-pipeline-snapshot"}))),L(n0,{title:"评分器",eyebrow:Eu?.runId||"selected epoch",loading:y.loading},L(PD,{run:Eu,onRaw:f})),L(n0,{title:"MiniMax 限额",eyebrow:"model/minimax-m27 quota",loading:y.loading},L(zt,{quota:s,onRaw:f})),L(n0,{title:"OA 事件流",eyebrow:"100% event-driven diagnostics",className:"pipeline-wide-panel",loading:y.loading},L(Nt,{diagnostics:o,onRaw:f})),L(n0,{title:"组件矩阵",eyebrow:`${Lf.length} classes`,loading:y.loading},Lf.length===0?L($r,{title:"暂无组件",text:"等待 D601 pipeline backend 返回 registry.components"}):L("div",{className:"component-strata"},Lf.map((fu)=>L("article",{key:fu.name,className:"component-stratum"},L("span",null,fu.name),L("strong",null,fu.count)))),L("div",{className:"pipeline-component-list"},x.slice(0,12).map((fu)=>L("span",{key:fu.key,className:"data-chip"},L("b",null,fu.componentClass||"--"),L("span",null,fu.id||fu.key||"--"))))),L(n0,{title:"Epoch 列表",eyebrow:`${cu.length}/${jr} preview`,loading:y.loading},cu.length===0?L($r,{title:"暂无运行记录",text:"当前 pipeline 在 .state/pipeline-runs 中还没有 epoch。"}):L("div",{className:"pipeline-run-list"},cu.map((fu)=>{let Zu=String(fu?.runId||"")===Tu?Eu:fu;return L("article",{key:fu.runId,className:`pipeline-run-card ${String(fu.runId||"")===Tu?"active":""}`,role:"button",tabIndex:0,onClick:()=>{j(String(fu.runId||"")),Q(V1())},onKeyDown:(Lu)=>{if(Lu.key==="Enter"||Lu.key===" ")j(String(fu.runId||"")),Q(V1())}},L("div",{className:"node-card-head"},L("strong",null,jj(cu,fu)),L(D1,{status:fu.status},fu.status||"--")),L("div",{className:"docker-meta compact"},L("span",null,Zu?.pipelineId||"--"),L("span",null,`nodes ${Array.isArray(Zu?.nodes)?Zu.nodes.length:0}`),L("span",null,`oa ${Array.isArray(Zu?.submissions)?Zu.submissions.length:0}`),L("span",null,`procedures ${Array.isArray(Zu?.procedureRuns)?Zu.procedureRuns.length:0}`),L(SD,{run:Zu})),L("p",{className:"muted paragraph"},u8(Zu?.task)),L("span",{className:"pipeline-run-time"},Nu(Zu?.updatedAt)))}))),L(n0,{title:"运行材料索引",eyebrow:Eu?.runId||"selected epoch",className:"pipeline-wide-panel",loading:y.loading},L(ct,{activeRun:Eu,onRaw:f}))))}var J8=Pu(wf(),1);var _u=J8.default.createElement,{useEffect:Lt}=J8.default,j8=J8.default.useState,wj={id:"",sequenceNo:"",contractNo:"",name:"",currentStatus:"",pending:"",paymentStatus:"",notes:""};function wt({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return _u("span",{className:`status-badge ${l}`},f||u||"unknown")}function F8({label:u,value:f,hint:l,tone:r}){return _u("article",{className:`metric-card ${r||""}`},_u("div",{className:"metric-label"},u),_u("div",{className:"metric-value"},f),_u("div",{className:"metric-hint"},l))}function Ej({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return _u("section",{className:`panel ${y||""}`},_u("div",{className:"panel-head"},_u("div",null,f?_u("p",{className:"panel-eyebrow"},f):null,_u(Af,{title:u,loading:i})),l?_u("div",{className:"panel-actions"},l):null),_u("div",{className:"panel-body"},r))}function nG({title:u,data:f,onOpen:l,testId:r}){return _u("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function $G({title:u,text:f}){return _u("div",{className:"empty-state"},_u("strong",null,u),_u("span",null,f))}function Et(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function Tt(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function Zt(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function ei(u,f){return`${u}/microservices/project-manager/proxy${f}`}function Ht(u){return{id:String(u.id||""),sequenceNo:u.sequenceNo===null||u.sequenceNo===void 0?"":String(u.sequenceNo),contractNo:String(u.contractNo||""),name:String(u.name||""),currentStatus:String(u.currentStatus||""),pending:String(u.pending||""),paymentStatus:String(u.paymentStatus||""),notes:String(u.notes||"")}}function Ot(u){return{sequenceNo:u.sequenceNo===""?null:Number(u.sequenceNo),contractNo:String(u.contractNo||"").trim(),name:String(u.name||"").trim(),currentStatus:String(u.currentStatus||"").trim(),pending:String(u.pending||"").trim(),paymentStatus:String(u.paymentStatus||"").trim(),paymentRatio:String(u.paymentStatus||"").trim(),notes:String(u.notes||"").trim()}}function Tj(u){return String(u||"item").replace(/[^A-Za-z0-9_-]+/g,"-")}function Bt(u){let f=new Uint8Array(u),l="",r=32768;for(let y=0;y_u("tr",{key:y.id,className:f===y.id?"active-row":"","data-testid":`project-manager-row-${Tj(y.id)}`},_u("td",null,y.sequenceNo??"--"),_u("td",null,_u("strong",null,y.contractNo||"--"),_u("code",null,y.id||"--")),_u("td",null,_u("strong",null,y.name||"--"),_u("span",{className:"muted block"},y.sourceFile||"--")),_u("td",null,y.currentStatus||"--"),_u("td",null,_u("span",{className:"preline"},y.pending||"--")),_u("td",null,_u(wt,{status:Number(y.paymentRatio||0)>=1?"online":"warn"},y.paymentStatus||"--")),_u("td",null,y.notes||"--"),_u("td",null,_u("div",{className:"inline-actions"},_u("button",{type:"button",className:"ghost-btn",onClick:()=>l(y),"data-testid":`project-manager-edit-${Tj(y.id)}`},"编辑"),_u(nG,{title:`Project ${y.contractNo||y.id}`,data:y,onOpen:r,testId:`raw-project-${Tj(y.id)}`}))))))))}function AG({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((T)=>T.id==="project-manager")||null,[y,i]=j8({loading:!1,saving:!1,importing:!1,exporting:!1,error:"",notice:"",health:null,list:null,refreshedAt:null}),[_,n]=j8({...wj}),[$,j]=j8(""),[F,J]=j8("all"),{addNotification:U}=wl();async function q(T=$,X=F){if(!r)return;i((R)=>({...R,loading:!0,error:""}));try{let R=new URLSearchParams({pageSize:"200",status:X});if(T.trim())R.set("q",T.trim());let[Y,P]=await Promise.all([Xu(`${l}/microservices/project-manager/health`),Xu(ei(l,`/api/projects?${R.toString()}`))]);i((t)=>({...t,loading:!1,health:Y,list:P,refreshedAt:new Date,error:""}))}catch(R){i((Y)=>({...Y,loading:!1,error:Bu(R,"Project Manager 加载失败")}))}}Lt(()=>{q()},[r?.id,r?.runtime?.providerStatus]);async function W(T){T.preventDefault(),i((X)=>({...X,saving:!0,error:"",notice:""}));try{let X=Ot(_);if(_.id)await Xu(ei(l,`/api/projects/${encodeURIComponent(_.id)}`),{method:"PUT",body:JSON.stringify(X)});else await Xu(ei(l,"/api/projects"),{method:"POST",body:JSON.stringify(X)});let R=_.id?"项目已更新":"项目已创建";i((Y)=>({...Y,saving:!1,notice:R})),U("success",R),await q()}catch(X){i((R)=>({...R,saving:!1,error:Bu(X,"保存项目失败")}))}}async function G(){if(!_.id)return;if(!window.confirm(`删除项目 ${_.contractNo||_.name||_.id} ?`))return;i((T)=>({...T,saving:!0,error:"",notice:""}));try{await Xu(ei(l,`/api/projects/${encodeURIComponent(_.id)}`),{method:"DELETE"}),n({...wj});let T="项目已删除";i((X)=>({...X,saving:!1,notice:T})),U("success",T),await q()}catch(T){i((X)=>({...X,saving:!1,error:Bu(T,"删除项目失败")}))}}async function K(T){let X=T.target.files?.[0];if(!X)return;i((R)=>({...R,importing:!0,error:"",notice:""}));try{let R=Bt(await X.arrayBuffer()),P=`Excel 已导入 ${(await Xu(ei(l,"/api/import/excel"),{method:"POST",body:JSON.stringify({fileName:X.name,contentBase64:R,replace:!1})})).imported||0} 条项目`;i((t)=>({...t,importing:!1,notice:P})),U("success",P),T.target.value="",await q()}catch(R){i((Y)=>({...Y,importing:!1,error:Bu(R,"Excel 导入失败")}))}}async function Q(){i((T)=>({...T,exporting:!0,error:""}));try{let T=await tQ(ei(l,"/api/projects/export.xlsx")),X=URL.createObjectURL(T),R=document.createElement("a");R.href=X,R.download=`project-manager-${JF()}.xlsx`,document.body.appendChild(R),R.click(),R.remove(),URL.revokeObjectURL(X),i((Y)=>({...Y,exporting:!1,notice:"Excel 已导出"}))}catch(T){i((X)=>({...X,exporting:!1,error:Bu(T,"Excel 导出失败")}))}}if(!r)return _u($G,{title:"Project Manager 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=project-manager"});let N=Et(r),c=Zt(r),z=Tt(r),w=Array.isArray(y.list?.projects)?y.list.projects:[],H=y.list?.summary||{},B=y.health||{};return _u("div",{className:"project-manager-page","data-testid":"project-manager-page"},_u(Ej,{title:"项目管理工作台",eyebrow:"Main Server PostgreSQL 用户服务",loading:y.loading||y.exporting,actions:_u("div",{className:"panel-actions"},_u("button",{type:"button",className:"ghost-btn",disabled:y.loading,onClick:()=>q(),"data-testid":"project-manager-refresh-button"},y.loading?"刷新中":"刷新"),_u("button",{type:"button",className:"ghost-btn",disabled:y.exporting,onClick:Q,"data-testid":"project-manager-export-button"},y.exporting?"导出中":"导出 Excel"),_u(nG,{title:"Project Manager 用户服务",data:r,onOpen:f,testId:"raw-project-manager-service"}))},_u("div",{className:"project-manager-hero"},_u(F8,{label:"项目总数",value:H.total??w.length,hint:`PG 表 ${B.storage?.table||"project_manager_projects"}`,tone:"ok"}),_u(F8,{label:"进行中",value:H.active??"--",hint:"当前状态未完全完成"}),_u(F8,{label:"已完成",value:H.completed??"--",hint:"按 完成 关键字统计",tone:"ok"}),_u(F8,{label:"未全款",value:H.unpaid??"--",hint:"付款比例 < 1",tone:Number(H.unpaid||0)>0?"warn":"ok"})),_u(jf,{error:y.error}),y.notice?_u("div",{className:"form-success"},y.notice):null),_u("div",{className:"project-manager-hero"},_u("div",{className:"microservice-ref-card"},_u("span",null,"Repo"),_u("strong",null,c.url||"--"),_u("code",null,c.commitId||"--")),_u("div",{className:"microservice-ref-card"},_u("span",null,"Main Server Docker"),_u("strong",null,`${z.nodeBindHost||"--"}:${z.nodePort||"--"}`),_u("code",null,`${c.composeService||"--"} / ${c.containerName||"--"}`)),_u("div",{className:"microservice-ref-card"},_u("span",null,"Runtime"),_u("strong",null,N.providerName||r.providerId),_u("code",null,`Health ${B.ok?"OK":"--"} / ${y.refreshedAt?qf(y.refreshedAt):"--"}`)),_u("div",{className:"microservice-ref-card"},_u("span",null,"Import Source"),_u("strong",null,"D601 WeChat Excel"),_u("code",null,"合作项目列表_I_20260309.xlsx"))),_u("div",{className:"project-manager-layout"},_u(Ej,{title:"项目清单",eyebrow:"CRUD + Excel Export",loading:y.loading||y.importing||y.exporting,actions:_u("div",{className:"inline-actions project-manager-filters"},_u("input",{value:$,onChange:(T)=>j(T.target.value),placeholder:"搜索合同号 / 项目名称 / 状态","data-testid":"project-manager-search"}),_u("select",{value:F,onChange:(T)=>{J(T.target.value),q($,T.target.value)},"data-testid":"project-manager-status-filter"},_u("option",{value:"all"},"全部"),_u("option",{value:"active"},"进行中"),_u("option",{value:"completed"},"已完成"),_u("option",{value:"unpaid"},"未全款")),_u("button",{type:"button",className:"ghost-btn",onClick:()=>q($,F)},"筛选"))},_u(Vt,{projects:w,activeId:_.id,onSelect:(T)=>n(Ht(T)),onRaw:f})),_u(Ej,{title:_.id?"编辑项目":"新建项目",eyebrow:"PostgreSQL Write Path",loading:y.saving||y.importing},_u("form",{className:"stack-form project-manager-form",onSubmit:W,"data-testid":"project-manager-form"},_.id?_u("label",null,"项目 ID",_u("input",{value:_.id,disabled:!0})):null,_u("label",null,"序号",_u("input",{type:"number",value:_.sequenceNo,onChange:(T)=>n((X)=>({...X,sequenceNo:T.target.value}))})),_u("label",null,"合同号",_u("input",{value:_.contractNo,onChange:(T)=>n((X)=>({...X,contractNo:T.target.value})),required:!0})),_u("label",null,"项目名称",_u("input",{value:_.name,onChange:(T)=>n((X)=>({...X,name:T.target.value})),required:!0})),_u("label",null,"当前状况",_u("textarea",{value:_.currentStatus,onChange:(T)=>n((X)=>({...X,currentStatus:T.target.value}))})),_u("label",null,"待完成",_u("textarea",{value:_.pending,onChange:(T)=>n((X)=>({...X,pending:T.target.value}))})),_u("label",null,"付款情况",_u("input",{value:_.paymentStatus,onChange:(T)=>n((X)=>({...X,paymentStatus:T.target.value})),placeholder:"例如 1 / 0.5 / 50%"})),_u("label",null,"其它",_u("input",{value:_.notes,onChange:(T)=>n((X)=>({...X,notes:T.target.value}))})),_u("div",{className:"inline-actions"},_u("button",{type:"submit",className:"primary-btn",disabled:y.saving,"data-testid":"project-manager-save-button"},y.saving?"保存中":_.id?"保存修改":"创建项目"),_u("button",{type:"button",className:"ghost-btn",onClick:()=>n({...wj})},"清空"),_.id?_u("button",{type:"button",className:"danger-btn",disabled:y.saving,onClick:G,"data-testid":"project-manager-delete-button"},"删除"):null)),_u("div",{className:"project-manager-import"},_u("p",{className:"muted paragraph"},"浏览器只访问 UniDesk frontend;后端通过同源用户服务代理写入主 PostgreSQL,不暴露 4233 公网端口。"),_u("label",{className:"file-import"},y.importing?"导入中...":"导入 Excel",_u("input",{type:"file",accept:".xlsx",onChange:K,disabled:y.importing,"data-testid":"project-manager-import-input"}))))))}var q8=Pu(wf(),1);var Au=q8.default.createElement,{useEffect:Xt}=q8.default,hl=q8.default.useState;function Yt({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return Au("span",{className:`status-badge ${l}`},f||u||"unknown")}function U8({label:u,value:f,hint:l,tone:r}){return Au("article",{className:`metric-card ${r||""}`},Au("div",{className:"metric-label"},u),Au("div",{className:"metric-value"},f),Au("div",{className:"metric-hint"},l))}function Zj({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){return Au("section",{className:`panel ${y||""}`},Au("div",{className:"panel-head"},Au("div",null,f?Au("p",{className:"panel-eyebrow"},f):null,Au(Af,{title:u,loading:i})),l?Au("div",{className:"panel-actions"},l):null),Au("div",{className:"panel-body"},r))}function jG({title:u,data:f,onOpen:l,testId:r}){return Au("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function Q8({title:u,text:f}){return Au("div",{className:"empty-state"},Au("strong",null,u),Au("span",null,f))}function Dt(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function tt(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function St(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function JG(u){return String(u).replace(/[^a-zA-Z0-9_-]/g,"_")}function Pt(u){if(!Number.isFinite(u))return"--";return`${u.toFixed(1)}%`}function u_(u,f){return`${u}/microservices/todo-note/proxy${f}`}function UG(u){return u.reduce((f,l)=>{let r=UG(Array.isArray(l.children)?l.children:[]),y=Boolean(l.completed);return{total:f.total+1+r.total,completed:f.completed+(y?1:0)+r.completed,active:f.active+(y?0:1)+r.active}},{total:0,completed:0,active:0})}function Oj(u,f){let l=f==="all"||(f==="completed"?Boolean(u.completed):!u.completed),r=Array.isArray(u.children)?u.children:[];return l||r.some((y)=>Oj(y,f))}function FG(u){return Array.isArray(u?.instances)?u.instances:[]}function Hj(u,f){for(let l of u){if(l?.id===f)return Array.isArray(l.children)?l.children:[];let r=Hj(Array.isArray(l?.children)?l.children:[],f);if(r.length>0)return r}return[]}function QG({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((g)=>g.id==="todo-note")||null,[y,i]=hl(null),[_,n]=hl(null),[$,j]=hl(""),[F,J]=hl(null),[U,q]=hl("all"),[W,G]=hl(13),[K,Q]=hl(""),[N,c]=hl(""),[z,w]=hl(""),[H,B]=hl(""),[T,X]=hl(""),[R,Y]=hl(!1),[P,t]=hl(""),[O,M]=hl(null),S=FG(_),b=UG(Array.isArray(F?.todos)?F.todos:[]),Z=r?Dt(r):{},D=r?St(r):{},I=r?tt(r):{};async function k(g=$){let[Qu,Eu]=await Promise.all([Xu(`${l}/microservices/todo-note/health`),Xu(u_(l,"/api/instances"))]);i(Qu),n(Eu);let Tu=FG(Eu),Du=Tu.some((ff)=>ff.id===g)?g:Tu[0]?.id||"";return j(Du),Du}async function h(g=$){if(!g){J(null);return}let Qu=await Xu(u_(l,`/api/instances/${encodeURIComponent(g)}`));J(Qu)}async function o(g=$){if(!r)return;Y(!0),t("");try{let Qu=await k(g);await h(Qu),M(new Date)}catch(Qu){t(Bu(Qu,"Todo Note 加载失败"))}finally{Y(!1)}}async function s(g){if(!$)return null;t("");try{let Qu=await Xu(u_(l,`/api/instances/${encodeURIComponent($)}/actions`),{method:"POST",body:JSON.stringify({action:g})});return J(Qu),await k($),Qu}catch(Qu){return t(Bu(Qu,"Todo 操作失败")),null}}async function x(g){g.preventDefault();let Qu=K.trim();if(!Qu)return;Y(!0),t("");try{let Eu=await Xu(u_(l,"/api/instances"),{method:"POST",body:JSON.stringify({name:Qu})});Q(""),await o(Eu.id)}catch(Eu){t(Bu(Eu,"创建清单失败"))}finally{Y(!1)}}async function uu(g){if(!window.confirm("确认删除这个 Todo Note 清单?"))return;Y(!0),t("");try{await Xu(u_(l,`/api/instances/${encodeURIComponent(g)}`),{method:"DELETE"}),await o($===g?"":$)}catch(Qu){t(Bu(Qu,"删除清单失败"))}finally{Y(!1)}}async function nu(g){g.preventDefault();let Qu=N.trim();if(!Qu)return;c(""),await s({type:"addTodo",title:Qu})}async function $u(g){if(!$)return;t("");try{let Qu=await Xu(u_(l,`/api/instances/${encodeURIComponent($)}/${g}`),{method:"POST",body:JSON.stringify({})});J(Qu),await k($)}catch(Qu){t(Bu(Qu,`${g} 失败`))}}function Fu(g){w(g.id),B(String(g.title||""))}async function Ku(g){let Qu=H.trim();if(w(""),B(""),Qu)await s({type:"updateTodoTitle",todoId:g,title:Qu})}async function Wu(g){let Eu=window.prompt("新增子任务标题")?.trim();if(!Eu)return;let Tu=Hj(Array.isArray(F?.todos)?F.todos:[],g),Du=new Set(Tu.map((gf)=>gf.id)),ff=await s({type:"addTodo",title:Eu,parentId:g,targetIndex:0});if(!ff)return;let rf=Hj(Array.isArray(ff?.todos)?ff.todos:[],g),Lf=rf.find((gf)=>!Du.has(gf.id));if(Lf&&rf[0]?.id!==Lf.id)await s({type:"moveTodo",todoId:Lf.id,targetParentId:g,targetIndex:0})}async function m(g,Qu){if(!T)return;let Eu={type:"moveTodo",todoId:T,targetIndex:Qu};if(g)Eu.targetParentId=g;X(""),await s(Eu)}if(Xt(()=>{o()},[r?.id,r?.runtime?.providerStatus]),!r)return Au(Q8,{title:"Todo Note 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=todo-note"});let d=S.find((g)=>g.id===$)||null,e=Array.isArray(F?.todos)?F.todos:[],cu=e.map((g,Qu)=>({todo:g,index:Qu})).filter((g)=>Oj(g.todo,U));return Au("div",{className:"todo-note-page","data-testid":"todo-note-page"},Au(Zj,{title:"Todo Note 工作台",eyebrow:"Main Server 用户服务",loading:R,actions:Au("div",{className:"panel-actions"},Au("button",{type:"button",className:"ghost-btn",disabled:R,onClick:()=>o($),"data-testid":"todo-note-refresh-button"},R?"刷新中":"刷新"),Au(jG,{title:"Todo Note 用户服务",data:r,onOpen:f,testId:"raw-todo-note-service"}))},Au("div",{className:"todo-note-hero"},Au("div",null,Au("div",{className:"node-version-line"},Au(Yt,{status:Z.providerStatus==="online"?"online":"warn"},Z.providerStatus||"unknown"),Au("span",null,r.providerId),Au("span",null,I.public?"公网暴露":"仅 UniDesk frontend 代理访问"),Au("span",null,y?.ok?"Health OK":"Health --")),Au("p",{className:"muted paragraph"},r.description)),Au("div",{className:"microservice-ref-card"},Au("span",null,"Repo"),Au("strong",null,D.url||"--"),Au("code",null,D.commitId||"--")),Au("div",{className:"microservice-ref-card"},Au("span",null,"Main Server Docker"),Au("strong",null,`${I.nodeBindHost||"--"}:${I.nodePort||"--"}`),Au("code",null,`${D.composeService||"--"} / ${D.containerName||"--"}`))),Au(jf,{error:P,wide:!0})),Au("div",{className:"todo-note-layout"},Au(Zj,{title:"清单",eyebrow:`${S.length} Instances`,className:"todo-list-panel",loading:R},Au("form",{className:"todo-create-list",onSubmit:x},Au("input",{placeholder:"新清单名称",value:K,onChange:(g)=>Q(g.target.value),"aria-label":"新清单名称"}),Au("button",{type:"submit",className:"ghost-btn",disabled:R||!K.trim()},"创建")),S.length===0?Au(Q8,{title:"暂无清单",text:"迁移或创建清单后会出现在这里"}):Au("div",{className:"todo-instance-list"},S.map((g)=>Au("button",{key:g.id,type:"button",className:`todo-instance-row ${$===g.id?"active":""}`,onClick:()=>{j(g.id),h(g.id)},"data-testid":`todo-instance-${JG(g.id)}`},Au("strong",null,g.name),Au("span",null,`${g.completedCount??0}/${g.todoCount??0} 完成`),Au("code",null,g.id))))),Au("div",{className:"todo-main-stack"},Au(Zj,{title:d?.name||"待选择清单",eyebrow:O?`Updated ${qf(O)}`:"Todo Tree",loading:R,actions:F?Au("div",{className:"panel-actions"},Au("button",{type:"button",className:"ghost-btn",onClick:()=>s({type:"renameInstance",name:window.prompt("清单新名称",F.name)||F.name})},"重命名"),Au("button",{type:"button",className:"ghost-btn danger",onClick:()=>uu($)},"删除清单"),Au(jG,{title:`Todo Instance ${$}`,data:F,onOpen:f,testId:"raw-todo-instance"})):null},!F?Au(Q8,{title:"未选择清单",text:"左侧选择一个 Todo Note 清单"}):Au("div",{className:"todo-workbench",style:{"--todo-font-size":`${W}px`}},Au("div",{className:"todo-toolbar"},Au("form",{className:"todo-add-form",onSubmit:nu},Au("input",{placeholder:"新增根任务",value:N,onChange:(g)=>c(g.target.value),"aria-label":"新增根任务"}),Au("button",{type:"submit",className:"ghost-btn",disabled:!N.trim()},"新增")),Au("div",{className:"todo-filter-strip"},["all","active","completed"].map((g)=>Au("button",{key:g,type:"button",className:`todo-filter ${U===g?"active":""}`,onClick:()=>q(g)},g==="all"?"全部":g==="active"?"未完成":"已完成"))),Au("div",{className:"todo-toolbar-actions"},Au("button",{type:"button",className:"ghost-btn",onClick:()=>s({type:"setAllTodosExpanded",expanded:!0})},"全部展开"),Au("button",{type:"button",className:"ghost-btn",onClick:()=>s({type:"setAllTodosExpanded",expanded:!1})},"全部收起"),Au("button",{type:"button",className:"ghost-btn",onClick:()=>$u("undo")},"撤销"),Au("button",{type:"button",className:"ghost-btn",onClick:()=>$u("redo")},"重做"),Au("label",{className:"todo-font-control"},"字号",Au("input",{type:"range",min:11,max:18,value:W,onChange:(g)=>G(Number(g.target.value))})))),Au("div",{className:"todo-stats-grid"},Au(U8,{label:"总任务",value:b.total,hint:`${S.length} lists`}),Au(U8,{label:"已完成",value:b.completed,hint:`${Pt(b.total?b.completed/b.total*100:0)}`,tone:"ok"}),Au(U8,{label:"未完成",value:b.active,hint:U==="active"?"当前筛选":"active tasks",tone:b.active>0?"warn":"ok"}),Au(U8,{label:"历史指针",value:F.historyPointer??0,hint:"undo / redo"})),Au("div",{className:"todo-root-drop",onDragOver:(g)=>g.preventDefault(),onDrop:(g)=>{g.preventDefault(),m(null,e.length)}},"拖到这里可移为根任务末尾"),Au("div",{className:"todo-tree","data-testid":"todo-note-tree"},cu.length===0?Au(Q8,{title:"没有匹配任务",text:"调整筛选或新增任务"}):cu.map(({todo:g,index:Qu})=>Au(qG,{key:g.id,todo:g,depth:0,parentId:null,index:Qu,siblingCount:e.length,filter:U,editingId:z,editingTitle:H,setEditingTitle:B,beginEdit:Fu,saveEdit:Ku,applyTodoAction:s,addChild:Wu,dragTodoId:T,setDragTodoId:X,dropTodo:m}))))))))}function qG(u){let{todo:f,depth:l,parentId:r,index:y,siblingCount:i,filter:_,editingId:n,editingTitle:$,setEditingTitle:j,beginEdit:F,saveEdit:J,applyTodoAction:U,addChild:q,dragTodoId:W,setDragTodoId:G,dropTodo:K}=u,Q=Array.isArray(f.children)?f.children:[],N=Q.map((w,H)=>({child:w,childIndex:H})).filter((w)=>Oj(w.child,_)),c=n===f.id,z=r||null;return Au("div",{className:"todo-row-wrap"},Au("article",{className:`todo-row ${f.completed?"completed":""} ${W===f.id?"dragging":""}`,style:{"--todo-depth":l},draggable:!0,onDragStart:(w)=>{G(f.id),w.dataTransfer.effectAllowed="move"},onDragOver:(w)=>w.preventDefault(),onDrop:(w)=>{w.preventDefault(),K(f.id,Q.length)},"data-testid":`todo-row-${JG(f.id)}`},Au("button",{type:"button",className:"todo-expand",disabled:Q.length===0,onClick:()=>U({type:"toggleTodoExpanded",todoId:f.id})},Q.length===0?"·":f.expanded?"▾":"▸"),Au("input",{type:"checkbox",checked:Boolean(f.completed),onChange:()=>U({type:"toggleTodoCompleted",todoId:f.id}),"aria-label":`完成 ${f.title}`}),Au("div",{className:"todo-title-cell",onDoubleClick:()=>F(f)},c?Au("div",{className:"todo-edit-inline"},Au("input",{value:$,autoFocus:!0,onChange:(w)=>j(w.target.value),onKeyDown:(w)=>{if(w.key==="Enter")J(f.id);if(w.key==="Escape")F({id:"",title:""})}}),Au("button",{type:"button",className:"ghost-btn",onClick:()=>J(f.id)},"保存")):Au("strong",null,f.title||"Untitled"),Au("div",{className:"todo-meta-line"},Au("span",null,`子项 ${Q.length}`),Au("span",null,`更新 ${Nu(f.updatedAt)}`),f.reminderAt?Au("span",{className:"todo-reminder"},`提醒 ${Nu(f.reminderAt)}`):Au("span",null,"无提醒"))),Au("input",{className:"todo-reminder-input",type:"datetime-local",value:M8(f.reminderAt),onChange:(w)=>U({type:"setTodoReminder",todoId:f.id,reminderAt:UF(w.target.value)})}),Au("div",{className:"todo-row-actions"},Au("button",{type:"button",className:"ghost-btn",onClick:()=>F(f)},"编辑"),Au("button",{type:"button",className:"ghost-btn",onClick:()=>q(f.id)},"子项"),Au("button",{type:"button",className:"ghost-btn",disabled:y<=0,onClick:()=>U({type:"moveTodo",todoId:f.id,...z?{targetParentId:z}:{},targetIndex:y-1})},"上移"),Au("button",{type:"button",className:"ghost-btn",disabled:y<=0,onClick:()=>U({type:"moveTodo",todoId:f.id,...z?{targetParentId:z}:{},targetIndex:0})},"置顶"),Au("button",{type:"button",className:"ghost-btn",disabled:y>=i-1,onClick:()=>U({type:"moveTodo",todoId:f.id,...z?{targetParentId:z}:{},targetIndex:y+1})},"下移"),Au("button",{type:"button",className:"ghost-btn",disabled:!r,onClick:()=>U({type:"moveTodo",todoId:f.id,targetIndex:9999})},"提升"),Au("button",{type:"button",className:"ghost-btn danger",onClick:()=>U({type:"deleteTodo",todoId:f.id})},"删除"))),f.expanded&&N.length>0?Au("div",{className:"todo-children"},N.map(({child:w,childIndex:H})=>Au(qG,{key:w.id,todo:w,depth:l+1,parentId:f.id,index:H,siblingCount:Q.length,filter:_,editingId:n,editingTitle:$,setEditingTitle:j,beginEdit:F,saveEdit:J,applyTodoAction:U,addChild:q,dragTodoId:W,setDragTodoId:G,dropTodo:K}))):null)}var WG=Pu(wf(),1),S1=WG.default.createElement;function cG({title:u,items:f,actions:l,className:r,testId:y}){let i=Array.isArray(f)?f:[];return S1("section",{className:`top-status-bar ${r||""}`,"data-testid":y},S1("div",{className:"top-status-main"},u?S1("strong",{className:"top-status-title"},u):null,S1("div",{className:"top-status-chips"},i.map((_,n)=>S1("span",{key:_?.key||`${_?.label||"status"}-${n}`,className:`top-status-chip ${_?.tone||""}`,"data-testid":_?.testId},_?.label?S1("b",null,_.label):null,S1("span",null,_?.value??"--"))))),l?S1("div",{className:"top-status-actions"},l):null)}var E$=Pu(wf(),1);var pf=E$.default.createElement;function NG({onClose:u}){let{notifications:f,removeNotification:l,clearNotifications:r}=wl(),y=E$.default.useRef(null);if(E$.default.useEffect(()=>{let i=(_)=>{if(y.current&&!y.current.contains(_.target))u()};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)},[u]),f.length===0)return pf("div",{className:"notification-popup",ref:y},pf("div",{className:"notification-popup-header"},pf("span",null,"通知"),pf("button",{className:"notification-popup-close",onClick:u},"×")),pf("div",{className:"notification-popup-empty"},"暂无通知"));return pf("div",{className:"notification-popup",ref:y},pf("div",{className:"notification-popup-header"},pf("span",null,`通知 (${f.length})`),pf("div",{className:"notification-popup-actions"},pf("button",{className:"notification-popup-clear",onClick:r},"清空"),pf("button",{className:"notification-popup-close",onClick:u},"×"))),pf("div",{className:"notification-popup-list"},f.slice().reverse().map((i)=>pf("div",{key:i.id,className:`notification-item ${i.type}`},pf("span",{className:"notification-item-icon"},i.type==="success"?"✓":"×"),pf("span",{className:"notification-item-message"},i.message),pf("button",{className:"notification-item-dismiss",onClick:()=>l(i.id)},"×")))))}function zG({notification:u}){let{removeNotification:f}=wl();return E$.default.useEffect(()=>{let l=setTimeout(()=>{f(u.id)},3000);return()=>clearTimeout(l)},[u.id,f]),pf("div",{className:`notification-banner ${u.type}`,role:"alert"},pf("span",{className:"notification-banner-icon"},u.type==="success"?"✓":"×"),pf("span",{className:"notification-banner-message"},u.message),pf("button",{className:"notification-banner-dismiss",onClick:()=>f(u.id)},"×"))}function XG(u,f){let l=document.getElementById("root")?.getAttribute(u);if(!l)return f;try{let r=JSON.parse(l);return typeof r==="object"&&r!==null&&!Array.isArray(r)?r:f}catch{return f}}var Iu=XG("data-config",{apiBaseUrl:"/api",authUsername:"admin"}),Mt=XG("data-codex-overview",null),A=M1.default.createElement,{useEffect:A0,useMemo:H$}=M1.default,pu=M1.default.useState,Yj=M1.default.createContext(!1),Zr=qW(I6),mt={id:"code-queue",name:"Code Queue",providerId:"main-server",description:"Code Queue",repository:{containerName:"code-queue-backend"},backend:{nodeBaseUrl:"http://code-queue:4222",nodeBindHost:"code-queue",nodePort:4222,public:!1},runtime:{providerStatus:"loading",providerName:"main-server"}};function GG(){return typeof document>"u"||document.visibilityState!=="hidden"}function pt(u,f){if(u==="ops"&&f==="status")return 5000;if(u==="nodes"&&f==="monitor")return 5000;if(u==="tasks"&&(f==="dispatch"||f==="scheduled"||f==="pending"))return 5000;if(u==="nodes"||u==="ops")return 1e4;if(u==="apps")return 15000;if(u==="tasks")return 15000;return 30000}async function Ct(u){if(!u?._summaryOnly||!u?.id)return u;return(await Xu(`${Iu.apiBaseUrl}/tasks/${encodeURIComponent(String(u.id))}`))?.task||u}function O$(u){return u?._summaryOnly?{...u,_loadRaw:()=>Ct(u)}:u}function S0(u){if(!Number.isFinite(u))return"--";let f=Math.max(0,u);if(f===0)return"0s";if(f<0.01)return"<0.01s";if(f<0.1)return`${f.toFixed(2)}s`;if(f<1)return`${f.toFixed(1)}s`;if(f<10&&!Number.isInteger(f))return`${f.toFixed(1)}s`;if(f<60)return`${Math.round(f)}s`;let l=Math.floor(f);if(l<3600)return`${Math.floor(l/60)}m ${l%60}s`;return`${Math.floor(l/3600)}h ${Math.floor(l%3600/60)}m`}function Ar(u){let f=Number(u);if(!Number.isFinite(f))return"--";if(f<1)return`${Math.max(0,f).toFixed(1)}ms`;if(f<10)return`${f.toFixed(1)}ms`;if(f<1000)return`${Math.round(f)}ms`;return S0(f/1000)}function Nl(u){let f=Number(u);if(!Number.isFinite(f)||f<=0)return"--";let l=["B","KB","MB","GB","TB"],r=f,y=0;while(r>=1024&&y0)return l[r]}return"任务失败但 provider 未返回明确原因"}function By(u){if(u===null||u===void 0)return"--";if(typeof u==="boolean")return u?"是":"否";if(typeof u==="number")return String(u);if(typeof u==="string")return u.length>80?`${u.slice(0,77)}...`:u;if(Array.isArray(u))return`${u.length} 项`;if(typeof u==="object")return`${Object.keys(u).length} 字段`;return String(u)}function Rt(u,f){let l=u.replace(/[-_\s]/g,"").toLowerCase(),r=l==="ts"||l.endsWith("at")||l.endsWith("timestamp")||l.endsWith("heartbeat");if((typeof f==="string"||typeof f==="number")&&r){let y=Nu(f);if(y!=="--")return y}if(u==="bodyText"&&typeof f==="string")return`${/^\s*[{[]/.test(f)?"JSON":"HTTP"} body ${f.length} chars`;return By(f)}function tG(u){if(!u||typeof u!=="object"||Array.isArray(u))return[];return Object.entries(u)}function bl(u){return String(u).replace(/[^a-zA-Z0-9_-]/g,"_")}function tj(u,f){return u&&typeof u==="object"&&!Array.isArray(u)?u[f]:void 0}function c8(u,f,l="未知"){let r=tj(u?.labels,f);return typeof r==="string"&&r.length>0?r:l}function SG(u){return c8(u,"providerGatewayVersion")}function Z$(u){return c8(u,"providerGatewayUpgradePolicy")}function KG(u){return c8(u,"providerGatewayStartedAt","")}function PG(u){let f=tj(u?.labels,"unideskCapabilities");if(typeof f==="string")return f.split(",").map((l)=>l.trim()).filter(Boolean);return Array.isArray(f)?f.filter((l)=>typeof l==="string"):[]}function MG(u,f){return PG(u).includes(f)}function LG(u,f){let l=tj(u?.labels,f);return l===!0||l==="true"||l==="1"}function ht(u){if(!MG(u,"host.ssh"))return{tone:"fail",label:"不可用",detail:"未声明 host.ssh"};if(!LG(u,"hostSshConfigured"))return{tone:"warn",label:"未配置",detail:"缺少 SSH 环境变量"};if(!LG(u,"hostSshKeyPresent"))return{tone:"warn",label:"缺 key",detail:"私钥未挂载"};return{tone:"ok",label:"可用",detail:c8(u,"hostSshTarget","host.ssh ready")}}function bt(u){if(!MG(u,"provider.upgrade"))return{tone:"fail",label:"不可用",detail:"未声明 provider.upgrade"};let f=Z$(u);if(f!=="always-enabled")return{tone:"warn",label:"待确认",detail:`策略 ${f}`};return{tone:"ok",label:"可用",detail:"always-enabled"}}function Sj(u){let f=typeof u==="string"&&u.length>0?u:"未知";if(f==="未知")return"版本未知";return f.startsWith("v")?f:`v${f}`}function mG(u){return u?.payload&&typeof u.payload==="object"&&!Array.isArray(u.payload)?u.payload:{}}function N8(u){return u?.result&&typeof u.result==="object"&&!Array.isArray(u.result)?u.result:{}}function W8(u){let f=mG(u),l=N8(u);return(f.mode??l.mode)==="schedule"?"schedule":"plan"}function vt(u){let f=mG(u).source;return typeof f==="string"&&f.length>0?f:"unknown"}function It(u){let f=N8(u),l=f.plan&&typeof f.plan==="object"&&!Array.isArray(f.plan)?f.plan:{},r=f.policy??l.policy;return typeof r==="string"&&r.length>0?r:"--"}function pG(u){let f=N8(u),l=f.plan&&typeof f.plan==="object"&&!Array.isArray(f.plan)?f.plan:{},r=f.targetProviderGatewayVersion??f.providerGatewayVersion??l.targetProviderGatewayVersion??l.providerGatewayVersion;return typeof r==="string"&&r.length>0?Sj(r):"版本未知"}function CG(u){if(String(u?.status||"").toLowerCase()==="failed")return DG(u);if(l_(u))return"等待 provider 回传升级终态";let l=N8(u);if(typeof l.updaterContainerId==="string"&&l.updaterContainerId.length>0)return`updater ${l.updaterContainerId.slice(0,18)}`;if(typeof l.message==="string"&&l.message.length>0)return l.message;if(l.plan)return"升级计划已生成";return"无升级结果摘要"}function xG(u,f){return u.filter((l)=>l?.providerId===f&&l?.command==="provider.upgrade").sort((l,r)=>(j0(r.updatedAt)??0)-(j0(l.updatedAt)??0))}function kt(u){return u.find((f)=>W8(f)==="schedule")||u[0]||null}function RG(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function wG(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function gt(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function Nf({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return A("span",{className:`status-badge ${l}`},f||u||"unknown")}function $f({label:u,value:f,hint:l,tone:r,onClick:y,testId:i}){let _=typeof y==="function";return A("article",{className:`metric-card ${r||""} ${_?"clickable":""}`,role:_?"button":void 0,tabIndex:_?0:void 0,"data-testid":i,onClick:y,onKeyDown:_?(n)=>{if(n.key==="Enter"||n.key===" ")n.preventDefault(),y()}:void 0},A("div",{className:"metric-label"},u),A("div",{className:"metric-value"},f),A("div",{className:"metric-hint"},l))}function au({title:u,eyebrow:f,actions:l,children:r,className:y,loading:i}){let _=M1.default.useContext(Yj),n=Boolean(i)||_;return A("section",{className:`panel ${y||""}`},A("div",{className:"panel-head"},A("div",null,f?A("p",{className:"panel-eyebrow"},f):null,A(Af,{title:u,loading:n})),l?A("div",{className:"panel-actions"},l):null),A("div",{className:"panel-body"},r))}function Cf({title:u,data:f,onOpen:l,testId:r}){let[y,i]=pu(!1),_=f&&typeof f==="object"&&typeof f._loadRaw==="function"?f._loadRaw:null;async function n(){if(!_){l(u,f);return}i(!0);try{l(u,await _())}catch($){l(u,{ok:!1,error:Bu($,"读取原始 JSON 失败"),fallback:f})}finally{i(!1)}}return A("button",{type:"button",className:"ghost-btn","data-testid":r,disabled:y,onClick:()=>void n()},y?"读取中":"查看原始JSON")}function st({raw:u,onClose:f}){if(!u)return null;return A("div",{className:"modal-backdrop",role:"presentation"},A("section",{className:"raw-dialog",role:"dialog","aria-modal":"true","aria-label":u.title},A("div",{className:"raw-dialog-head"},A("h2",null,u.title),A("button",{type:"button",className:"ghost-btn",onClick:f},"关闭")),A("pre",{className:"raw-json","data-testid":"raw-json"},JSON.stringify(u.data,null,2))))}function hG({labels:u,limit:f=8}){let l=tG(u).slice(0,f);if(l.length===0)return A("span",{className:"muted"},"无标签");return A("div",{className:"chip-row"},l.map(([r,y])=>A("span",{key:r,className:"data-chip"},A("b",null,r),A("span",null,By(y)))))}function f_({node:u}){let f=SG(u);return A("span",{className:`version-chip ${f==="未知"?"unknown":""}`,"data-testid":`gateway-version-${bl(u?.providerId||"unknown")}`},Sj(f))}function EG({title:u,state:f,testId:l}){return A("span",{className:`capability-badge ${f.tone}`,title:f.detail,"data-testid":l},A("b",null,u),A("strong",null,f.label),A("small",null,f.detail))}function Pj({node:u}){let f=bl(u?.providerId||"unknown");return A("div",{className:"node-availability-strip"},A(EG,{title:"SSH 透传",state:ht(u),testId:`ssh-availability-${f}`}),A(EG,{title:"远程更新",state:bt(u),testId:`upgrade-availability-${f}`}))}function m1({data:u,empty:f="无数据"}){if(u===null||u===void 0)return A("span",{className:"muted"},f);if(typeof u!=="object")return A("span",{className:"summary-value"},By(u));if(Array.isArray(u))return A("span",{className:"summary-value"},`${u.length} 项列表`);let l=Object.entries(u).slice(0,5);if(l.length===0)return A("span",{className:"muted"},f);return A("div",{className:"summary-grid"},l.map(([r,y])=>A("span",{key:r,className:"summary-item"},A("b",null,r),A("span",null,Rt(r,y)))))}function Ff({title:u,text:f}){return A("div",{className:"empty-state"},A("strong",null,u),A("span",null,f))}function at({onLogin:u}){let[f,l]=pu(Iu.authUsername||"admin"),[r,y]=pu(""),[i,_]=pu(""),[n,$]=pu(!1);async function j(F){F.preventDefault(),$(!0),_("");try{let J=await Xu("/login",{method:"POST",body:JSON.stringify({username:f,password:r})});u(J)}catch(J){_(Bu(J,"登录失败"))}finally{$(!1)}}return A("main",{className:"login-screen","data-testid":"login-screen"},A("section",{className:"login-card"},A("div",{className:"login-brand"},A("span",{className:"brand-mark"},"UD"),A("div",null,A("h1",null,"UniDesk"),A("p",null,"Control Plane Login"))),A("form",{className:"login-form",onSubmit:j},A("label",null,"账号",A("input",{name:"username",autoComplete:"username",value:f,onChange:(F)=>l(F.target.value)})),A("label",null,"密码",A("input",{name:"password",type:"password",autoComplete:"current-password",value:r,onChange:(F)=>y(F.target.value)})),A(jf,{error:i}),A("button",{type:"submit",disabled:n},n?"登录中":"登录")),A("div",{className:"login-note"},"默认账号由 config.json 注入;公网入口只暴露前端登录面。")))}function ot({connection:u,lastRefresh:f,onRefresh:l,onLogout:r,session:y,clock:i,activeStatusItems:_=[],onNotificationToggle:n,unreadCount:$=0}){let j=[{key:"core",label:"核心",value:u.text,tone:u.ok?"ok":"fail",testId:"conn-text"},...Array.isArray(_)?_:[],{key:"refresh",label:"刷新",value:f?qf(f):"未刷新"},{key:"clock",label:d$,value:qf(i)},{key:"user",label:"用户",value:y?.user?.username||"--",tone:"user"}];return A("header",{className:"topbar"},A("div",null,A("p",{className:"eyebrow"},"Distributed Work Platform"),A("h1",null,"UniDesk 控制平面")),A(cG,{className:"global-top-status",title:"状态",items:j,actions:[A("button",{key:"notification",type:"button",className:`notification-icon-btn ${$>0?"has-unread":""}`,onClick:n,"aria-label":"通知"},"\uD83D\uDD14",$>0?A("span",{key:"badge",className:"notification-badge"},$>99?"99+":$):null),A("button",{key:"refresh",type:"button",className:"ghost-btn",onClick:l},"刷新"),A("button",{key:"logout",type:"button",className:"ghost-btn danger",onClick:r},"退出")]}))}function dt(u){return!u.defaultPrevented&&u.button===0&&!u.metaKey&&!u.altKey&&!u.ctrlKey&&!u.shiftKey&&u.currentTarget.target!=="_blank"}function bG({moduleId:u,tabId:f,className:l,active:r=!1,title:y,testId:i,onNavigate:_,children:n}){let $=k6(Zr,u,f);return A("a",{href:$,role:"button",className:l,title:y,"aria-current":r?"page":void 0,"data-testid":i,"data-route":$,onClick:(j)=>{if(!dt(j))return;j.preventDefault(),_(u,f)}},n)}function et({activeModule:u,activeTabs:f,onNavigate:l,collapsed:r,onToggle:y}){return A("aside",{className:`rail ${r?"collapsed":""}`,"aria-label":"主模块"},A("div",{className:"brand"},A("span",{className:"brand-mark"},"UD"),A("span",{className:"brand-text"},"UniDesk"),A("button",{type:"button",className:"rail-toggle",onClick:y,"aria-label":r?"展开左侧边栏":"收起左侧边栏","data-testid":"rail-toggle"},r?"»":"«")),I6.map((i)=>{let _=f[i.id]||tn[i.id]||i.tabs[0]?.id||"";return A(bG,{key:i.id,moduleId:i.id,tabId:_,className:`module ${u===i.id?"active":""}`,active:u===i.id,title:i.label,onNavigate:l},A("span",{className:"module-code"},i.code),A("span",null,i.label))}))}function uS({module:u,activeTab:f,onNavigate:l}){return A("nav",{className:"tabs","aria-label":`${u.label} 子功能`},u.tabs.map((r)=>A(bG,{key:r.id,moduleId:u.id,tabId:r.id,className:`tab ${f===r.id?"active":""}`,active:f===r.id,onNavigate:l},r.label)))}function fS({data:u,onRaw:f,onNavigate:l}){let r=u.overview||{},y=u.nodes.filter((q)=>q.status==="online"),i=u.pendingTasks||u.tasks.filter(l_),_=r.pendingTaskCount??i.length,n=u.tasks.slice(0,5),$=r.pgdata||{},j=r.microserviceAvailability||{},F=Mu(j.totalCount),J=Mu(j.healthyCount),U=Mu(j.unhealthyCount);return A("div",{className:"page-grid overview-grid","data-testid":"overview-page"},A(au,{title:"核心指标",eyebrow:"Control"},A("div",{className:"metric-grid"},A($f,{label:"数据库",value:r.dbReady?"READY":"WAIT",hint:"PostgreSQL internal network",tone:r.dbReady?"ok":"warn"}),A($f,{label:"PGDATA",value:Nl($.databaseBytes),hint:`${$.volumeName||"unidesk_pgdata_10gb"} / ${$.databasePretty||"--"}`,tone:"ok",testId:"pgdata-usage-card"}),A($f,{label:"在线节点",value:r.onlineNodeCount??0,hint:`${r.nodeCount??0} registered`,tone:"ok"}),A($f,{label:"WebSocket",value:r.activeSocketCount??0,hint:"Provider ingress sockets"}),A($f,{label:"用户服务可用",value:F>0?`${J}/${F}`:"--",hint:F>0?`healthyCount ${J} · unhealthyCount ${U}`:"strict /health probes",tone:F>0&&U===0?"ok":"warn",testId:"microservice-availability-card"}),A($f,{label:"待处理任务",value:_,hint:_>0?"点击查看具体任务":`timeout ${S0(Math.floor((r.taskPendingTimeoutMs??0)/1000))}`,tone:_>0?"warn":"ok",onClick:()=>l("tasks","pending"),testId:"pending-task-card"}))),A(au,{title:"本机 Provider",eyebrow:"Self Connected"},y.length===0?A(Ff,{title:"暂无在线节点",text:"provider-gateway 未完成自接入"}):A("div",{className:"node-card-list"},y.slice(0,4).map((q)=>A(lS,{key:q.providerId,node:q,onRaw:f})))),A(au,{title:"待处理任务明细",eyebrow:`${_} Pending`,actions:A("button",{type:"button",className:"ghost-btn",onClick:()=>l("tasks","pending"),"data-testid":"pending-task-detail-link"},"进入任务调度")},i.length===0?A(Ff,{title:"当前无待处理",text:"queued / dispatched / running 超时后会自动转为 failed,避免总览长期卡住"}):A("div",{className:"compact-list"},i.slice(0,5).map((q)=>A(OG,{key:q.id,task:q,onRaw:f})))),A(au,{title:"最近任务",eyebrow:"Dispatch"},n.length===0?A(Ff,{title:"暂无任务",text:"可以在任务调度模块发起 docker.ps 或 echo"}):A("div",{className:"compact-list"},n.map((q)=>A(OG,{key:q.id,task:q,onRaw:f})))))}function lS({node:u,onRaw:f}){return A("article",{className:"node-card"},A("div",{className:"node-card-head"},A("div",null,A("strong",null,u.name),A("code",null,u.providerId)),A(Nf,{status:u.status})),A("div",{className:"node-version-line"},A(f_,{node:u}),A("span",null,`升级策略 ${Z$(u)}`)),A(Pj,{node:u}),A(hG,{labels:u.labels,limit:6}),A("div",{className:"node-card-foot"},A("span",null,`心跳 ${Nu(u.lastHeartbeat)}`),A(Cf,{title:`Provider ${u.providerId}`,data:u,onOpen:f,testId:`raw-node-${bl(u.providerId)}`})))}function rS({events:u,onRaw:f}){return A(au,{title:"事件摘要",eyebrow:"Latest 100"},u.length===0?A(Ff,{title:"暂无事件",text:"Provider 注册、心跳超时和任务状态会写入事件流"}):A("div",{className:"table-wrap"},A("table",null,A("thead",null,A("tr",null,A("th",null,"ID"),A("th",null,"类型"),A("th",null,"来源"),A("th",null,"摘要"),A("th",null,"时间"),A("th",null,"操作"))),A("tbody",null,u.map((l)=>A("tr",{key:l.id},A("td",null,A("code",null,l.id)),A("td",null,A(Nf,{status:l.type},l.type)),A("td",null,A("code",null,l.source)),A("td",null,A(m1,{data:l.payload})),A("td",null,Nu(l.createdAt)),A("td",null,A(Cf,{title:`Event ${l.id}`,data:l,onOpen:f}))))))))}function yS({logs:u,onRaw:f}){return A(au,{title:"服务日志",eyebrow:"Core Recent"},u.length===0?A(Ff,{title:"暂无日志",text:"backend-core 内存日志会在请求和 provider 事件后出现"}):A("div",{className:"log-list"},u.slice(-80).reverse().map((l,r)=>A("article",{key:r,className:`log-row ${l.level||"info"}`},A("span",null,Nu(l.ts)),A("b",null,l.level||"info"),A("strong",null,l.message||"log"),A(m1,{data:l.data,empty:"无附加字段"}),A(Cf,{title:`Log ${l.message||r}`,data:l,onOpen:f})))))}function iS({nodes:u,onRaw:f}){return A(au,{title:"节点清单",eyebrow:`${u.length} Providers`},u.length===0?A(Ff,{title:"暂无 Provider 节点",text:"确认 provider-gateway 已连接 provider ingress"}):A("div",{className:"table-wrap"},A("table",{className:"node-list-table"},A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"Provider"),A("th",null,"网关版本"),A("th",null,"运维可用性"),A("th",null,"资源标签"),A("th",null,"连接时间"),A("th",null,"最后心跳"),A("th",null,"操作"))),A("tbody",null,u.map((l)=>A("tr",{key:l.providerId},A("td",null,A(Nf,{status:l.status})),A("td",null,A("strong",null,l.name),A("code",null,l.providerId)),A("td",null,A("div",{className:"gateway-cell"},A(f_,{node:l}),A("span",null,Z$(l)))),A("td",null,A(Pj,{node:l})),A("td",null,A(hG,{labels:l.labels,limit:5})),A("td",null,Nu(l.connectedAt)),A("td",null,Nu(l.lastHeartbeat)),A("td",null,A(Cf,{title:`Provider ${l.providerId}`,data:l,onOpen:f,testId:`raw-node-table-${bl(l.providerId)}`}))))))))}function _S({nodes:u}){let f=H$(()=>{let l=[];for(let r of u)for(let[y,i]of tG(r.labels))l.push({providerId:r.providerId,name:r.name,key:y,value:i});return l},[u]);return A(au,{title:"资源标签",eyebrow:"Structured Labels"},f.length===0?A(Ff,{title:"暂无标签",text:"provider-gateway 注册消息会同步资源标签"}):A("div",{className:"label-matrix"},f.map((l)=>A("article",{key:`${l.providerId}-${l.key}`,className:"label-card"},A("span",null,l.key),A("strong",null,By(l.value)),A("code",null,l.providerId)))))}function nS({nodes:u}){return A(au,{title:"心跳状态",eyebrow:"Provider Liveness"},u.length===0?A(Ff,{title:"无心跳",text:"等待 provider 注册和 heartbeat"}):A("div",{className:"heartbeat-list"},u.map((f)=>A("article",{key:f.providerId,className:"heartbeat-row"},A("span",{className:`pulse ${f.status}`}),A("div",null,A("strong",null,f.name),A("code",null,f.providerId)),A("div",null,A("span",null,"connected"),A("b",null,Nu(f.connectedAt))),A("div",null,A("span",null,"last heartbeat"),A("b",null,Nu(f.lastHeartbeat)))))))}function $S({nodes:u,systemStatuses:f,tasks:l,onRaw:r,refresh:y}){let[i,_]=pu(""),n=H$(()=>u.map((G)=>{let K=f.find((Q)=>Q.providerId===G.providerId);return{...G,systemCurrent:K?.current||null,systemHistory:K?.history||[],systemUpdatedAt:K?.updatedAt||null}}),[u,f]),$=n.find((G)=>G.providerId===i)||n[0]||null;if(A0(()=>{if(!i&&n[0])_(n[0].providerId)},[n.length,i]),!$)return A(Ff,{title:"暂无资源监控",text:"等待 provider 上报 CPU、内存和硬盘指标"});let j=$.systemCurrent,F=$.systemHistory||[],J=j?.cpu||{},U=j?.memory||{},q=j?.disk||{},W=F.length>0?F:j?[{at:j.collectedAt,cpuPercent:Mu(J.percent),memoryPercent:Mu(U.percent),diskPercent:Mu(q.percent)}]:[];return A("div",{className:"monitor-page","data-testid":"node-monitor-page"},A("div",{className:"docker-node-strip"},n.map((G)=>A("button",{key:G.providerId,type:"button",className:`docker-node-tile ${$.providerId===G.providerId?"active":""}`,onClick:()=>_(G.providerId)},A("span",{className:`pulse ${G.status}`}),A("strong",null,G.name),A("code",null,G.providerId),A("span",null,G.systemCurrent?`CPU ${P1(G.systemCurrent.cpu?.percent)} / MEM ${P1(G.systemCurrent.memory?.percent)}`:"等待指标")))),A("div",{className:"monitor-layout"},A(au,{title:"任务管理器视图",eyebrow:$.name,className:"monitor-main-panel",actions:j?A(Cf,{title:`System ${$.providerId}`,data:{current:j,history:F},onOpen:r}):null},!j?A(Ff,{title:"系统指标未上报",text:"provider-gateway 会周期性采集 /proc 与 df,并保存历史曲线"}):A("div",null,A("div",{className:"monitor-hero"},A("div",null,A("p",{className:"panel-eyebrow"},"Node Performance"),A("h3",null,$.name),A("div",{className:"docker-meta"},A("span",null,`${J.cores||0} CPU cores`),A("span",null,`load ${Mu(J.load1).toFixed(2)} / ${Mu(J.load5).toFixed(2)} / ${Mu(J.load15).toFixed(2)}`),A("span",null,`memory actual ${Nl(U.usedBytes)} / ${Nl(U.totalBytes)}`),A("span",null,`disk ${Nl(q.usedBytes)} / ${Nl(q.totalBytes)}`))),A(Nf,{status:j.ok?"online":"warn"},j.ok?"METRICS READY":"METRICS DEGRADED")),A("div",{className:"monitor-chart-grid"},A(Vj,{title:"CPU",metricKey:"cpuPercent",current:J.percent,points:W,detail:`${J.cores||0} cores / load ${Mu(J.load1).toFixed(2)}`,tone:"cpu",testId:"metric-chart-cpu"}),A(Vj,{title:"Memory",metricKey:"memoryPercent",current:U.percent,points:W,detail:`${Nl(U.usedBytes)} actual / ${Nl(U.cacheBytes)} cache excluded`,tone:"memory",testId:"metric-chart-memory"}),A(Vj,{title:"Disk",metricKey:"diskPercent",current:q.percent,points:W,detail:`${q.path||"/"} mounted ${q.mount||"--"}`,tone:"disk",testId:"metric-chart-disk"})),A("div",{className:"monitor-summary-grid"},A($f,{label:"CPU 当前",value:P1(J.percent),hint:`history ${W.length} samples`,tone:"ok"}),A($f,{label:"实际内存",value:Nl(U.usedBytes),hint:`${P1(U.percent)} 不含缓存`}),A($f,{label:"硬盘已用",value:Nl(q.usedBytes),hint:P1(q.percent)}),A($f,{label:"更新时间",value:Nu($.systemUpdatedAt||j.collectedAt),hint:$.providerId})),A(AS,{current:j,onRaw:r}))),A("div",{className:"monitor-side-stack"},A(cS,{provider:$,refresh:y,onRaw:r}),A(NS,{provider:$,tasks:l,onRaw:r,limit:5}),A(au,{title:"采样说明",eyebrow:"Retention"},A("div",{className:"monitor-note-list"},A("article",null,A("b",null,"CPU"),A("span",null,"从 /proc/stat 计算相邻采样差值,首个采样用 load/cores 近似")),A("article",null,A("b",null,"Memory"),A("span",null,"实际内存 = MemTotal - MemFree - Buffers - Cached - SReclaimable + Shmem,不把 page cache / buffer 计入占用")),A("article",null,A("b",null,"Disk"),A("span",null,"使用 df -PB1 对配置路径采样,默认监控根文件系统")),A("article",null,A("b",null,"Process"),A("span",null,"从 /proc/[pid] 采集进程 CPU、实际内存 RSS、线程数和磁盘 I/O 速率;表格默认按内存占用降序")))))))}function TG(u,f){if(f==="memory")return Mu(u.rssBytes);if(f==="cpu")return Mu(u.cpuPercent);if(f==="disk")return Mu(u.readBytesPerSecond)+Mu(u.writeBytesPerSecond);if(f==="pid")return Mu(u.pid);if(f==="threads")return Mu(u.threads);if(f==="runtime")return Mu(u.elapsedSeconds);if(f==="user")return String(u.user||"");return String(u.name||u.command||"")}function ZG({value:u,label:f,tone:l}){let r=Math.max(1,Math.min(100,Mu(u)));return A("div",{className:`process-meter ${l||""}`},A("span",{style:{width:`${r}%`}}),A("b",null,f))}function AS({current:u,onRaw:f}){let[l,r]=pu({key:"memory",direction:"desc"}),y=M1.default.useContext(Yj),i=u?.processSummary&&typeof u.processSummary==="object"?u.processSummary:{},_=Array.isArray(u?.processes)?u.processes:[],n=H$(()=>{let j=l.direction==="asc"?1:-1;return[..._].sort((F,J)=>{let U=TG(F,l.key),q=TG(J,l.key);if(typeof U==="string"||typeof q==="string")return String(U).localeCompare(String(q),"zh-CN")*j;return(U-q)*j||Mu(F.pid)-Mu(J.pid)})},[_,l.key,l.direction]),$=(j,F)=>{let J=l.key===F,U=J?l.direction==="asc"?"ascending":"descending":"none";return A("th",{"aria-sort":U},A("button",{type:"button",className:`process-sort-button ${J?"active":""}`,"data-testid":`process-sort-${F}`,onClick:()=>r((q)=>({key:F,direction:q.key===F&&q.direction==="desc"?"asc":"desc"}))},j,A("span",null,J?l.direction==="desc"?"↓":"↑":"↕")))};return A("section",{className:"process-resource-panel","data-testid":"process-resource-panel"},A("div",{className:"process-resource-head"},A("div",null,A("p",{className:"panel-eyebrow"},"Windows Resource Monitor Style"),A(Af,{title:"进程资源占用",level:3,loading:y})),A("div",{className:"process-resource-actions"},A("span",{className:"data-chip"},"默认按内存排序"),A("span",{className:"data-chip"},`${Mu(i.visible,n.length)} / ${Mu(i.total,n.length)} 进程`),A(Cf,{title:"Process Resource Snapshot",data:{processSummary:i,processes:_},onOpen:f,testId:"raw-process-resources"}))),n.length===0?A(Ff,{title:"暂无进程资源数据",text:"等待 provider-gateway 上报 /proc/[pid] 采样;旧版 provider 需要先升级到支持进程资源表的版本"}):A("div",{className:"process-table-wrap"},A("table",{className:"process-resource-table","data-testid":"process-resource-table"},A("thead",null,A("tr",null,$("进程","name"),$("PID","pid"),$("用户","user"),A("th",null,"状态"),$("CPU","cpu"),$("内存","memory"),A("th",null,"RSS"),$("磁盘 I/O","disk"),$("线程","threads"),$("运行时长","runtime"))),A("tbody",null,n.map((j)=>{let F=Mu(j.readBytesPerSecond)+Mu(j.writeBytesPerSecond);return A("tr",{key:`${j.pid}-${j.startedAt}`,"data-testid":`process-row-${bl(j.pid)}`,"data-memory-bytes":String(Mu(j.rssBytes)),"data-cpu-percent":String(Mu(j.cpuPercent)),"data-disk-bps":String(F),"data-pid":String(Mu(j.pid))},A("td",null,A("div",{className:"process-name-cell"},A("strong",null,j.name||"--"),A("span",{className:"process-command"},j.command||"--"))),A("td",null,A("code",null,j.pid||"--")),A("td",null,j.user||`uid:${j.uid??"--"}`),A("td",null,A("span",{className:`process-state state-${bl(j.state||"unknown")}`},j.state||"?")),A("td",null,A(ZG,{value:j.cpuPercent,label:xt(j.cpuPercent),tone:"cpu"})),A("td",null,A(ZG,{value:j.memoryPercent,label:P1(j.memoryPercent),tone:"memory"})),A("td",null,Nl(j.rssBytes)),A("td",null,A("div",{className:"process-io-cell"},A("strong",null,Bj(F)),A("span",null,`R ${Bj(j.readBytesPerSecond)} / W ${Bj(j.writeBytesPerSecond)}`))),A("td",null,j.threads||0),A("td",null,S0(Mu(j.elapsedSeconds))))})))))}function Vj({title:u,metricKey:f,current:l,points:r,detail:y,tone:i,testId:_}){let n=r.map((U)=>Math.max(0,Math.min(100,Mu(U[f])))),$=n.length>1?n:[n[0]||0,n[0]||0],j=$.length<=1?100:100/($.length-1),F=$.map((U,q)=>`${(q*j).toFixed(2)},${(46-U*0.42).toFixed(2)}`).join(" "),J=`0,48 ${F} 100,48`;return A("article",{className:`metric-chart ${i}`,"data-testid":_},A("div",{className:"metric-chart-head"},A("div",null,A("span",null,u),A("strong",null,P1(l))),A("code",null,`${r.length} pts`)),A("svg",{viewBox:"0 0 100 48",preserveAspectRatio:"none",role:"img","aria-label":`${u} usage curve`},A("polygon",{points:J}),A("polyline",{points:F}),A("line",{x1:"0",x2:"100",y1:"24",y2:"24"})),A("div",{className:"metric-chart-foot"},A("span",null,"0%"),A("span",null,y),A("span",null,"100%")))}function P0(u){return Array.isArray(u)?u:[]}function jS(u){let f=P0(u?.core?.requests?.componentSummary);return[...P0(u?.frontend?.requests?.componentSummary),...f].sort((r,y)=>Mu(y.requestCount)-Mu(r.requestCount))}function FS(u){let f=P0(u?.core?.operations?.summary);return[...P0(u?.frontend?.operations?.summary),...f].sort((r,y)=>Mu(y.count)-Mu(r.count))}function JS(u){let f=P0(u?.core?.requests?.recentFailures).map((r)=>({source:"backend",...r}));return[...P0(u?.frontend?.requests?.recentFailures).map((r)=>({source:"frontend",...r})),...f].sort((r,y)=>(j0(y.at)??0)-(j0(r.at)??0)).slice(0,20)}function US(u){let f=P0(u?.core?.operations?.recentSlowOperations);return[...P0(u?.frontend?.operations?.recentSlowOperations),...f].sort((r,y)=>Mu(y.durationMs)-Mu(r.durationMs)).slice(0,20)}function QS(u){let f=performance.memory,l=Number(f?.usedJSHeapSize);if(Number.isFinite(l)&&l>0)return l;let r=Number(u?.appBundleBytes);if(Number.isFinite(r)&&r>0)return r;return Mu(u?.process?.heapUsedBytes)}function qS({points:u}){let f=P0(u),l=f.map((U)=>Mu(U.mb)),r=Math.max(1,...l),y=Math.max(0,Math.min(...l,0)),i=Math.max(1,r-y),_=f.length>1?f:[...f,...f],n=_.length<=1?100:100/(_.length-1),$=_.map((U,q)=>{let W=Mu(U.mb);return`${(q*n).toFixed(2)},${(48-(W-y)/i*42).toFixed(2)}`}).join(" "),j=`0,50 ${$} 100,50`,F=f.at(-1),J=f[0];return A("article",{className:"performance-memory-card","data-testid":"performance-memory-chart"},A("div",{className:"performance-memory-head"},A("strong",null,`Bwebui: ${F?`${Mu(F.mb).toFixed(1)}MB`:"--"}`),A("span",null,f.length>0?`${f.length} samples`:"等待采样")),A("svg",{viewBox:"0 0 100 50",preserveAspectRatio:"none",role:"img","aria-label":"Bwebui memory trend"},A("polygon",{points:j}),A("polyline",{points:$}),A("line",{x1:"0",x2:"100",y1:"25",y2:"25"})),A("div",{className:"performance-axis-row"},A("span",null,J?qf(new Date(J.at)):"--"),A("span",null,"时间"),A("span",null,F?qf(new Date(F.at)):"--")),A("div",{className:"performance-axis-row"},A("span",null,`${y.toFixed(1)}`),A("span",null,"(MB)"),A("span",null,`${r.toFixed(1)}`)))}function WS({onRaw:u}){let[f,l]=pu({core:null,frontend:null}),[r,y]=pu([]),[i,_]=pu(""),[n,$]=pu(!1),[j,F]=pu(null),[J,U]=pu(!1);async function q(){$(!0),_("");try{let[P,t]=await Promise.all([Xu(`${Iu.apiBaseUrl}/performance`,{cache:"no-store"}),Xu(`${Iu.apiBaseUrl}/frontend-performance`,{cache:"no-store"})]);l({core:P,frontend:t});let O=QS(t);y((M)=>[...M,{at:new Date().toISOString(),mb:O/1048576}].slice(-80))}catch(P){_(Bu(P,"性能指标加载失败"))}finally{$(!1)}}A0(()=>{q();let P=setInterval(()=>void q(),5000);return()=>clearInterval(P)},[]);async function W(){U(!0),_(""),F(null);try{let P=await Xu(`${Iu.apiBaseUrl}/code-queue-load-test`,{method:"POST",body:JSON.stringify({targetMs:1000,timeoutMs:90000,url:Iu.frontendPublicUrl||window.location.origin})});F(P),q()}catch(P){_(Bu(P,"Code Queue Playwright 测量失败"))}finally{U(!1)}}let G=jS(f),K=JS(f),Q=FS(f),N=US(f),c=f.core?.process||{},z=f.frontend?.process||{},w=f.core?.database?.codeQueueStorage||{},H=Mu(w.total),B=j?.result||{},T=Mu(B.wallMs,NaN),X=Mu(B.networkIdleMs,NaN),R=B.withinTarget===!0,Y=J?"running":j===null?"idle":j.measurementOk===!0?R?"passed":"slow":"failed";return A("div",{className:"performance-page","data-testid":"performance-page"},A("div",{className:"performance-hero"},A("div",null,A("p",{className:"panel-eyebrow"},"Unified Performance"),A(Af,{title:"性能面板",loading:n||J}),A("p",null,"按组件统计 HTTP 请求、失败率、P95 延迟,并汇总 backend/frontend 内部操作耗时。")),A("div",{className:"inline-actions"},A("button",{type:"button",className:"ghost-btn",onClick:()=>void W(),disabled:J,"data-testid":"code-queue-load-test-button"},J?"测试中...":"测试 Code Queue 加载"),A("button",{type:"button",className:"ghost-btn",onClick:()=>void q(),disabled:n,"data-testid":"performance-refresh-button"},n?"刷新中":"刷新"),A(Cf,{title:"Performance Snapshot",data:f,onOpen:u,testId:"raw-performance"}))),A(jf,{error:i}),A("div",{className:"performance-top-grid"},A(qS,{points:r}),A("div",{className:"performance-metric-stack"},A($f,{label:"backend RSS",value:Nl(c.rssBytes),hint:`heap ${Nl(c.heapUsedBytes)}`}),A($f,{label:"frontend RSS",value:Nl(z.rssBytes),hint:`bundle ${Nl(f.frontend?.appBundleBytes)}`}),A($f,{label:"Codex PG 任务",value:H||"--",hint:w.ok?"unidesk_code_queue_tasks":"等待表初始化",tone:w.ok?"ok":"warn"}),A($f,{label:"请求样本",value:Mu(f.core?.requests?.sampleCount)+Mu(f.frontend?.requests?.sampleCount),hint:"rolling window 3000"}))),A(au,{title:"Code Queue 加载基准",eyebrow:"Playwright / target <1s",className:"codex-load-test-panel",loading:J,actions:A("div",{className:"panel-actions"},A("button",{type:"button",className:"primary-btn",onClick:()=>void W(),disabled:J,"data-testid":"code-queue-load-test-panel-button"},J?"正在运行 Playwright...":"手动触发测试"),j?A(Cf,{title:"Code Queue Load Test",data:j,onOpen:u,testId:"raw-code-queue-load-test"}):null)},A("div",{className:"codex-load-test-grid","data-testid":"code-queue-load-test-result"},A($f,{label:"总耗时",value:J?"运行中":Number.isFinite(T)?Ar(T):"--",hint:j===null?"点击按钮启动远端 Playwright":`目标 ${Ar(B.targetMs||1000)} / ${B.url||"Code Queue"}`,tone:Y==="passed"?"ok":Y==="failed"||Y==="slow"?"warn":""}),A($f,{label:"判定",value:J?"RUNNING":Y==="passed"?"PASS <1s":Y==="slow"?"SLOW":Y==="failed"?"FAILED":"--",hint:j?.measurementOk===!1?String(j.error||B.error||"measurement failed").slice(0,120):"导航开始 -> DOMContentLoaded -> data-load-state=complete",tone:Y==="passed"?"ok":Y==="idle"||Y==="running"?"":"fail"}),A($f,{label:"Network idle",value:Number.isFinite(X)?Ar(X):"--",hint:`DOMContentLoaded ${Ar(B.domContentLoadedMs)} / ${B.networkIdleReached===!1?"未在 5s 内空闲":"已空闲"}`,tone:Number.isFinite(X)&&X<=1000?"ok":"warn"}),A($f,{label:"组件耗时",value:Number.isFinite(Mu(B.componentLoadMs,NaN))?Ar(B.componentLoadMs):"--",hint:`queue ${Ar(B.queueMs)} / detail ${Ar(B.detailMs)}`,tone:Mu(B.componentLoadMs)>1000?"warn":"ok"}),A($f,{label:"Trace 规模",value:Number.isFinite(Mu(B.transcriptRows,NaN))?String(B.transcriptRows):"--",hint:`${B.visibleTaskCount??0} visible tasks / ${B.partial?"preview":"complete"}`})),J?A("div",{className:"performance-empty-line"},"正在通过 main-server Host SSH 启动 Playwright,完成后会显示 wall time、组件耗时和最慢 API。"):null,j&&Array.isArray(B.slowestApi)&&B.slowestApi.length>0?A("div",{className:"table-wrap performance-table-wrap compact codex-load-api-table"},A("table",{className:"performance-table"},A("thead",null,A("tr",null,["API","状态","耗时"].map((P)=>A("th",{key:P},P)))),A("tbody",null,B.slowestApi.slice(0,5).map((P,t)=>A("tr",{key:`${P.url}-${t}`},A("td",null,A("code",null,P.url)),A("td",null,P.status),A("td",null,Ar(P.durationMs))))))):null),A("div",{className:"performance-grid"},A(au,{title:"组件汇总",eyebrow:"Requests",loading:n},G.length===0?A(Ff,{title:"暂无请求样本",text:"刷新几次或打开页面后会自动形成组件统计"}):A("div",{className:"table-wrap performance-table-wrap"},A("table",{className:"performance-table"},A("thead",null,A("tr",null,["组件","请求数","失败数","失败率","平均延迟","P95"].map((P)=>A("th",{key:P},P)))),A("tbody",null,G.map((P)=>A("tr",{key:P.component},A("td",null,A("code",null,P.component)),A("td",null,P.requestCount),A("td",null,P.failureCount),A("td",null,P1(Mu(P.failureRate)*100)),A("td",null,Ar(P.averageLatencyMs)),A("td",null,Ar(P.p95LatencyMs)))))))),A(au,{title:"最近失败请求",eyebrow:"Failures",loading:n},K.length===0?A("div",{className:"performance-empty-line"},"最近没有失败请求"):A("div",{className:"table-wrap performance-table-wrap compact"},A("table",{className:"performance-table"},A("thead",null,A("tr",null,["时间","来源","组件","状态","路径"].map((P)=>A("th",{key:P},P)))),A("tbody",null,K.map((P,t)=>A("tr",{key:`${P.at}-${t}`},A("td",null,Nu(P.at)),A("td",null,P.source),A("td",null,A("code",null,P.component)),A("td",null,A(Nf,{status:"failed"},P.status)),A("td",null,A("code",null,P.path)))))))),A(au,{title:"内部操作汇总",eyebrow:"Operations",loading:n},Q.length===0?A(Ff,{title:"暂无内部操作样本",text:"API 查询和代理请求会自动记录内部操作耗时"}):A("div",{className:"table-wrap performance-table-wrap"},A("table",{className:"performance-table"},A("thead",null,A("tr",null,["服务","操作","次数","平均延迟","P95"].map((P)=>A("th",{key:P},P)))),A("tbody",null,Q.map((P)=>A("tr",{key:`${P.service}-${P.operation}`},A("td",null,P.service),A("td",null,A("code",null,P.operation)),A("td",null,P.count),A("td",null,Ar(P.averageLatencyMs)),A("td",null,Ar(P.p95LatencyMs)))))))),A(au,{title:"最近慢操作",eyebrow:"Slowest",loading:n},N.length===0?A(Ff,{title:"暂无慢操作",text:"后端会记录最近窗口内耗时最高的内部操作"}):A("div",{className:"table-wrap performance-table-wrap"},A("table",{className:"performance-table"},A("thead",null,A("tr",null,["时间","操作","耗时","结果","细节"].map((P)=>A("th",{key:P},P)))),A("tbody",null,N.map((P,t)=>A("tr",{key:`${P.at}-${P.operation}-${t}`},A("td",null,Nu(P.at)),A("td",null,A("code",null,P.operation)),A("td",null,Ar(P.durationMs)),A("td",null,P.ok?"成功":"失败"),A("td",null,P.detail||"-")))))))))}function cS({provider:u,refresh:f,onRaw:l}){let[r,y]=pu(""),[i,_]=pu(null),[n,$]=pu("");async function j(F){y(F),$("");try{let J=await Xu(`${Iu.apiBaseUrl}/dispatch`,{method:"POST",body:JSON.stringify({providerId:u.providerId,command:"provider.upgrade",payload:{mode:F,source:"frontend-resource-monitor",requestedAt:new Date().toISOString()}})});_({mode:F,...J}),await f()}catch(J){$(Bu(J,"升级命令下发失败"))}finally{y("")}}return A(au,{title:"Provider Gateway 升级",eyebrow:"Remote Control",loading:Boolean(r)},A("div",{className:"upgrade-control","data-testid":"provider-upgrade-control"},A("p",null,"通过 UniDesk WebSocket 向当前计算节点下发 provider.upgrade;预检只生成升级计划,执行升级会调度节点本地 updater 容器。"),A("div",{className:"upgrade-target-line"},A("span",null,"指定 Provider"),A("code",null,u.providerId),A(f_,{node:u})),A("div",{className:"upgrade-actions"},A("button",{type:"button",className:"ghost-btn",disabled:Boolean(r),onClick:()=>j("plan"),"data-testid":"upgrade-plan-button"},r==="plan"?"预检中":"预检升级"),A("button",{type:"button",className:"ghost-btn danger",disabled:Boolean(r),onClick:()=>j("schedule"),"data-testid":"upgrade-schedule-button"},r==="schedule"?"调度中":"执行升级")),A(jf,{error:n}),i?A("div",{className:"upgrade-result"},A(Nf,{status:i.status||"queued"},i.status||"queued"),A("span",null,`${i.mode==="schedule"?"执行升级":"预检升级"} 已下发`),A("span",null,`指定版本 ${Sj(SG(u))}`),A("code",null,i.taskId||"--"),A(Cf,{title:"Provider Upgrade Dispatch",data:i,onOpen:l})):A("span",{className:"muted"},"升级任务结果会进入任务历史;执行升级可能导致 provider 短暂重连。")))}function vG({records:u,onRaw:f,compact:l=!1}){if(u.length===0)return A(Ff,{title:"暂无远程更新记录",text:"该节点还没有 provider.upgrade 任务;执行预检或升级后会在这里形成结构化记录"});return A("div",{className:`upgrade-record-table-wrap table-wrap ${l?"compact":""}`},A("table",{className:"upgrade-record-table"},A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"模式"),A("th",null,"任务"),A("th",null,"来源"),A("th",null,"耗时"),A("th",null,"策略"),A("th",null,"Gateway 版本"),A("th",null,"结果记录"),A("th",null,"更新时间"),A("th",null,"操作"))),A("tbody",null,u.map((r)=>A("tr",{key:r.id,"data-testid":`gateway-upgrade-record-${bl(r.id)}`},A("td",null,A(Nf,{status:r.status})),A("td",null,A("span",{className:`mode-chip ${W8(r)}`},W8(r)==="schedule"?"执行升级":"预检")),A("td",null,A("strong",null,"provider.upgrade"),A("code",null,r.id)),A("td",null,vt(r)),A("td",null,A(kG,{task:r})),A("td",null,It(r)),A("td",null,A("span",{className:"version-chip"},pG(r))),A("td",null,A("span",{className:`upgrade-outcome ${String(r.status||"").toLowerCase()}`},CG(r))),A("td",null,Nu(r.updatedAt)),A("td",null,A(Cf,{title:`Provider Upgrade Task ${r.id}`,data:O$(r),onOpen:f})))))))}function NS({provider:u,tasks:f,onRaw:l,limit:r=5}){let y=xG(f,u.providerId).slice(0,r);return A(au,{title:"远程更新记录",eyebrow:u.providerId,actions:A(f_,{node:u}),className:"provider-upgrade-records-panel"},A("div",{"data-testid":`provider-upgrade-records-${bl(u.providerId)}`},A(vG,{records:y,onRaw:l,compact:!0})))}function zS({nodes:u,tasks:f,onRaw:l}){let r=H$(()=>u.map((i)=>{let _=xG(f,i.providerId);return{node:i,records:_,latest:kt(_),capabilities:PG(i)}}),[u,f]),y=r.reduce((i,_)=>i+_.records.length,0);return A("div",{className:"gateway-page","data-testid":"gateway-version-page"},A(au,{title:"Provider Gateway 版本",eyebrow:`${u.length} Providers / ${y} 更新记录`},u.length===0?A(Ff,{title:"暂无 Provider 节点",text:"等待 provider-gateway 注册后显示版本号和升级记录"}):A("div",{className:"table-wrap gateway-version-table-wrap"},A("table",{className:"gateway-version-table"},A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"Provider"),A("th",null,"Gateway 版本"),A("th",null,"升级策略"),A("th",null,"运维可用性"),A("th",null,"运行时间"),A("th",null,"能力"),A("th",null,"最近远程更新"),A("th",null,"操作"))),A("tbody",null,r.map((i)=>A("tr",{key:i.node.providerId},A("td",null,A(Nf,{status:i.node.status})),A("td",null,A("strong",null,i.node.name),A("code",null,i.node.providerId)),A("td",null,A(f_,{node:i.node})),A("td",null,Z$(i.node)),A("td",null,A(Pj,{node:i.node})),A("td",null,KG(i.node)?Nu(KG(i.node)):"待新版上报"),A("td",null,A("div",{className:"capability-row"},i.capabilities.length===0?A("span",{className:"muted"},"未声明"):i.capabilities.slice(0,5).map((_)=>A("span",{key:_,className:"data-chip"},_)))),A("td",null,i.latest?A("div",{className:"latest-upgrade-cell"},A(Nf,{status:i.latest.status}),A("span",null,`${W8(i.latest)==="schedule"?"执行升级":"预检"} / ${Nu(i.latest.updatedAt)}`),A("small",null,`Gateway ${pG(i.latest)}`),A("small",null,CG(i.latest))):A("span",{className:"muted"},"暂无记录")),A("td",null,A(Cf,{title:`Provider ${i.node.providerId}`,data:i.node,onOpen:l})))))))),A(au,{title:"远程更新记录",eyebrow:"Structured provider.upgrade records"},u.length===0?A(Ff,{title:"暂无记录",text:"没有 provider 节点时不会生成远程更新记录"}):A("div",{className:"gateway-record-grid"},r.map((i)=>A("article",{key:i.node.providerId,className:"gateway-record-card","data-testid":`gateway-records-${bl(i.node.providerId)}`},A("div",{className:"gateway-record-head"},A("div",null,A("strong",null,i.node.name),A("code",null,i.node.providerId)),A(f_,{node:i.node})),A("div",{className:"gateway-record-meta"},A("span",null,`心跳 ${Nu(i.node.lastHeartbeat)}`),A("span",null,`策略 ${Z$(i.node)}`),A("span",null,`${i.records.length} 条记录`)),A(vG,{records:i.records.slice(0,8),onRaw:l,compact:!0}))))))}function GS(u){if(u==="running")return"online";if(u==="paused"||u==="restarting")return"warn";if(u==="exited"||u==="dead")return"offline";return"internal"}function IG(u){return/^[a-f0-9]{48,64}$/i.test(u)}function T$(u){let f=String(u?.name||""),l=String(u?.labels||"");return f==="unidesk_pgdata_10gb"||l.includes("com.docker.compose.volume=unidesk_pgdata_10gb")||f.toLowerCase().includes("pgdata")}function HG(u){let f=String(u?.name||""),l=String(u?.labels||"");if(T$(u))return 0;if(l.includes("com.docker.compose.project=unidesk"))return 1;if(!IG(f))return 2;return 3}function KS(u){return[...u].sort((f,l)=>{let r=HG(f)-HG(l);if(r!==0)return r;return String(f.name||"").localeCompare(String(l.name||""))})}function LS({nodes:u,dockerStatuses:f,onRaw:l}){let[r,y]=pu(""),i=H$(()=>u.map((N)=>{let c=f.find((z)=>z.providerId===N.providerId);return{...N,dockerStatus:c?.dockerStatus||null,dockerUpdatedAt:c?.updatedAt||null}}),[u,f]),_=i.find((N)=>N.providerId===r)||i[0]||null;if(A0(()=>{if(!r&&i[0])y(i[0].providerId)},[i.length,r]),!_)return A(Ff,{title:"暂无 Docker 节点",text:"等待 provider 上报 Docker daemon 状态"});let n=_.dockerStatus,$=_.providerId==="main-server",j=n?.counts||{},F=n?.daemon||{},J=n?.containers||[],U=n?.images||[],q=KS(n?.volumes||[]),W=$?q.find(T$):null,G=n?.networks||[],K=J.filter((N)=>N.state==="running"),Q=J.filter((N)=>N.state!=="running");return A("div",{className:"docker-page","data-testid":"docker-status-page"},A("div",{className:"docker-node-strip"},i.map((N)=>A("button",{key:N.providerId,type:"button",className:`docker-node-tile ${_.providerId===N.providerId?"active":""}`,onClick:()=>y(N.providerId)},A("span",{className:`pulse ${N.status}`}),A("strong",null,N.name),A("code",null,N.providerId),A("span",null,N.dockerStatus?`Docker ${N.dockerStatus.ok?"ready":"degraded"}`:"等待上报")))),A("div",{className:"docker-layout"},A(au,{title:"Docker Desktop 视图",eyebrow:_.name,className:"docker-main-panel",actions:n?A(Cf,{title:`Docker ${_.providerId}`,data:n,onOpen:l}):null},!n?A(Ff,{title:"Docker 状态未上报",text:"provider-gateway 会在连接后周期性采集 docker info / ps / images / volume / network"}):A("div",null,A("div",{className:"docker-hero"},A("div",null,A("p",{className:"panel-eyebrow"},"Daemon"),A("h3",null,F.name||_.providerId),A("div",{className:"docker-meta"},A("span",null,F.serverVersion?`Engine ${F.serverVersion}`:"Engine --"),A("span",null,F.operatingSystem||"OS --"),A("span",null,F.architecture||"arch --"),A("span",null,`${F.cpus||0} CPU / ${Nl(F.memoryBytes)}`))),A(Nf,{status:n.ok?"online":"warn"},n.ok?"Docker Ready":"Docker Degraded")),A("div",{className:"docker-metrics"},A($f,{label:"Containers",value:j.containers??J.length,hint:`${j.running??K.length} running / ${j.stopped??Q.length} stopped`,tone:"ok"}),A($f,{label:"Images",value:j.images??U.length,hint:`${j.daemonImages??j.images??U.length} daemon images`}),A($f,{label:"Volumes",value:j.volumes??q.length,hint:$?W?"database volume visible":"database volume missing":"node local volumes",tone:W?"ok":""}),A($f,{label:"Networks",value:j.networks??G.length,hint:F.driver?`driver ${F.driver}`:"docker networks"})),$?A(wS,{volume:W,volumeCount:q.length}):null,A("div",{className:"docker-section-head"},A("h3",null,"Containers"),A("span",null,`updated ${Nu(_.dockerUpdatedAt||n.collectedAt)}`)),A("div",{className:"docker-container-table table-wrap","data-testid":"docker-container-table"},A("table",null,A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"容器"),A("th",null,"镜像"),A("th",null,"端口"),A("th",null,"运行时间"),A("th",null,"重启策略"),A("th",null,"PID"),A("th",null,"大小"))),A("tbody",null,J.length===0?A("tr",null,A("td",{colSpan:8},"暂无容器")):J.map((N)=>A("tr",{key:`${N.id}-${N.name}`},A("td",null,A(Nf,{status:GS(N.state)},N.state||"unknown")),A("td",null,A("strong",null,N.name||"--"),A("code",null,N.id||"--")),A("td",null,N.image||"--"),A("td",null,N.ports||A("span",{className:"muted"},"未发布")),A("td",null,N.runningFor||N.status||"--"),A("td",null,N.restartPolicy?A(Nf,{status:N.restartPolicy==="always"?"online":"warn"},N.restartPolicy):"--"),A("td",null,N.pidMode?A("code",null,N.pidMode):"--"),A("td",null,N.size||"--")))))))),A("div",{className:"docker-side-stack"},A(Xj,{title:"Images",items:U,render:(N)=>A("article",{key:`${N.id}-${N.repository}`,className:"docker-side-row"},A("strong",null,`${N.repository}:${N.tag}`),A("span",null,N.size||"--"),A("code",null,N.id||"--"))}),A(Xj,{title:"Volumes",items:q,limit:q.length,render:(N)=>A("article",{key:N.name,className:`docker-side-row volume-row ${$&&T$(N)?"database-volume":""}`,"data-testid":$&&T$(N)?"database-volume-row":void 0},A("strong",null,N.name),A("span",null,$&&T$(N)?"PostgreSQL":IG(String(N.name||""))?"anonymous":"named"),A("code",null,N.mountpoint||N.driver||N.scope||"--"))}),A(Xj,{title:"Networks",items:G,render:(N)=>A("article",{key:N.id||N.name,className:"docker-side-row"},A("strong",null,N.name),A("span",null,N.driver||"--"),A("code",null,N.id||"--"))}))))}function wS({volume:u,volumeCount:f}){return A("section",{className:`docker-volume-focus ${u?"ready":"missing"}`,"data-testid":"database-volume-card"},A("div",{className:"volume-focus-head"},A("span",{className:"panel-eyebrow"},"Database Named Volume"),A(Nf,{status:u?"online":"warn"},u?"FOUND":"MISSING")),u?A("div",{className:"volume-focus-body"},A("strong",null,u.name),A("span",null,"PostgreSQL data volume for unidesk-database"),A("div",{className:"volume-route"},A("code",null,u.mountpoint||"/var/lib/docker/volumes/unidesk_pgdata_10gb/_data"),A("span",null,"->"),A("code",null,"unidesk-database:/var/lib/postgresql/data")),A("div",{className:"docker-meta compact"},A("span",null,`driver ${u.driver||"--"}`),A("span",null,`scope ${u.scope||"--"}`),A("span",null,`${f} volumes reported`))):A("div",{className:"volume-focus-body"},A("strong",null,"unidesk_pgdata_10gb"),A("span",null,"当前 Docker 快照没有发现数据库命名卷;请检查 provider-gateway 的 Docker volume 上报。")))}function Xj({title:u,items:f,render:l,limit:r}){let y=f.slice(0,r??12),i=Math.max(0,f.length-y.length);return A(au,{title:u,eyebrow:`${f.length} items`,className:"docker-side-panel"},f.length===0?A(Ff,{title:`暂无 ${u}`,text:"等待 Docker 状态采集"}):A("div",{className:"docker-side-list"},y.map(l),i>0?A("div",{className:"docker-side-more"},`+ ${i} more`):null))}function ES({microservices:u,onRaw:f,onNavigate:l}){let r=u.filter((y)=>wG(y).public===!1);return A("div",{className:"microservice-page","data-testid":"microservice-catalog-page"},A(au,{title:"用户服务目录",eyebrow:"Provider Mounted User Services"},A("div",{className:"metric-grid"},A($f,{label:"服务总数",value:u.length,hint:"config.json 用户服务登记"}),A($f,{label:"私有后端",value:r.length,hint:"不直接暴露公网",tone:"ok"}),A($f,{label:"D601 服务",value:u.filter((y)=>y.providerId==="D601").length,hint:"compute-node docker"}),A($f,{label:"集成前端",value:u.filter((y)=>y.frontend?.integrated).length,hint:"UniDesk React 页面"}))),A(au,{title:"服务映射",eyebrow:"Repo Reference + Runtime"},u.length===0?A(Ff,{title:"暂无用户服务",text:"在 config.json 的 microservices 中登记用户服务的 provider、仓库引用和后端映射"}):A("div",{className:"table-wrap"},A("table",{className:"microservice-table"},A("thead",null,A("tr",null,A("th",null,"服务"),A("th",null,"Provider"),A("th",null,"代码引用"),A("th",null,"Docker 引用"),A("th",null,"后端映射"),A("th",null,"开发入口"),A("th",null,"运行态"),A("th",null,"操作"))),A("tbody",null,u.map((y)=>{let i=RG(y),_=gt(y),n=wG(y),$=i.availability||{},j=$.status||(i.providerStatus==="online"?"unknown":"unhealthy");return A("tr",{key:y.id,"data-testid":`microservice-row-${bl(y.id)}`},A("td",null,A("strong",null,y.name),A("code",null,y.id)),A("td",null,A("strong",null,i.providerName||y.providerId),A("code",null,y.providerId)),A("td",null,A("span",null,_.url||"--"),A("code",null,_.commitId||"--")),A("td",null,A("span",null,_.composeFile||"--"),A("code",null,`${_.composeService||"--"} / ${_.containerName||"--"}`)),A("td",null,A(Nf,{status:n.public?"warn":"online"},n.public?"public":"private"),A("code",null,`${n.nodeBindHost||"--"}:${n.nodePort||"--"} -> ${n.proxyMode||"--"}`)),A("td",null,A("span",null,y.development?.sshPassthrough?"SSH 透传":"未配置"),A("code",null,y.development?.worktreePath||"--")),A("td",null,A(Nf,{status:j==="healthy"?"online":j==="unknown"?"warn":"failed"},j),A("span",null,$.reason||i.providerStatus||"unknown"),A(m1,{data:i.container,empty:"容器快照未上报"})),A("td",null,A("div",{className:"microservice-actions"},y.id==="findjob"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","findjob"),"data-testid":"open-findjob-button"},"打开"):null,y.id==="pipeline"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","pipeline"),"data-testid":"open-pipeline-button"},"打开"):null,y.id==="todo-note"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","todo-note"),"data-testid":"open-todo-note-button"},"打开"):null,y.id==="met-nonlinear"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","met-nonlinear"),"data-testid":"open-met-nonlinear-button"},"打开"):null,y.id==="claudeqq"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","claudeqq"),"data-testid":"open-claudeqq-button"},"打开"):null,y.id==="baidu-netdisk"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","baidu-netdisk"),"data-testid":"open-baidu-netdisk-button"},"打开"):null,y.id==="code-queue"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","code-queue"),"data-testid":"open-code-queue-button"},"打开"):null,y.id==="project-manager"?A("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","project-manager"),"data-testid":"open-project-manager-button"},"打开"):null,A(Cf,{title:`用户服务 ${y.id}`,data:y,onOpen:f}))))}))))))}function TS({nodes:u,onDispatched:f,onRaw:l}){let r=u.filter((Y)=>Y.status==="online"),[y,i]=pu(r[0]?.providerId||u[0]?.providerId||""),[_,n]=pu("docker.ps"),[$,j]=pu("frontend"),[F,J]=pu("operator-check"),[U,q]=pu("normal"),[W,G]=pu(!1),[K,Q]=pu(""),[N,c]=pu(!1),[z,w]=pu(null),[H,B]=pu("");A0(()=>{if(!y&&(r[0]?.providerId||u[0]?.providerId))i(r[0]?.providerId||u[0].providerId)},[u.length,r.length,y]);function T(){return{source:$,note:F,priority:U}}function X(){Q(JSON.stringify(T(),null,2)),G(!0)}async function R(Y){Y.preventDefault(),c(!0),B("");try{let P=W?JSON.parse(K||"{}"):T(),t=await Xu(`${Iu.apiBaseUrl}/dispatch`,{method:"POST",body:JSON.stringify({providerId:y,command:_,payload:P})});w(t),await f()}catch(P){B(Bu(P,"下发失败"))}finally{c(!1)}}return A("div",{className:"page-grid dispatch-grid"},A(au,{title:"下发任务",eyebrow:"Real WebSocket Dispatch"},A("form",{className:"dispatch-form",onSubmit:R},A("label",null,"Provider",A("select",{value:y,onChange:(Y)=>i(Y.target.value)},u.map((Y)=>A("option",{key:Y.providerId,value:Y.providerId},`${Y.name} / ${Y.providerId}`)))),A("label",null,"Command",A("select",{value:_,onChange:(Y)=>n(Y.target.value)},A("option",{value:"docker.ps"},"docker.ps"),A("option",{value:"host.ssh"},"host.ssh"),A("option",{value:"microservice.http"},"microservice.http"),A("option",{value:"echo"},"echo"))),A("label",null,"来源",A("input",{value:$,onChange:(Y)=>j(Y.target.value)})),A("label",null,"备注",A("input",{value:F,onChange:(Y)=>J(Y.target.value)})),A("label",null,"优先级",A("select",{value:U,onChange:(Y)=>q(Y.target.value)},A("option",{value:"normal"},"normal"),A("option",{value:"low"},"low"),A("option",{value:"urgent"},"urgent"))),A("div",{className:"dispatch-actions"},A("button",{type:"button",className:"ghost-btn",onClick:X},"查看原始JSON"),A("button",{type:"submit",disabled:N||!y},N?"下发中":"下发任务")),W?A("label",{className:"raw-editor-label"},"高级 Payload",A("textarea",{className:"raw-editor",value:K,onChange:(Y)=>Q(Y.target.value)})):null,A(jf,{error:H,wide:!0}))),A(au,{title:"下发结果",eyebrow:"Response"},z?A("div",{className:"result-card"},A(Nf,{status:z.status||"queued"},z.status||"queued"),A("dl",null,A("dt",null,"Task ID"),A("dd",null,A("code",null,z.taskId||"--")),A("dt",null,"Provider 在线"),A("dd",null,By(z.providerOnline))),A(Cf,{title:"Dispatch Response",data:z,onOpen:l})):A(Ff,{title:"等待操作",text:"任务响应会以结构化结果卡展示"})))}function OG({task:u,onRaw:f}){return A("article",{className:"compact-row"},A(Nf,{status:u.status}),A("div",null,A("strong",null,u.command),A("code",null,u.id)),A("span",null,l_(u)?`已等待 ${Dj(u.updatedAt)}`:`耗时 ${S0(YG(u)??0)}`),A(Cf,{title:`Task ${u.id}`,data:O$(u),onOpen:f}))}function kG({task:u}){let f=YG(u),l=l_(u);return A("div",{className:"task-duration"},A("strong",null,f===null?"--":S0(f)),A("span",null,l?`已运行 / 创建 ${Nu(u.createdAt)}`:`创建 ${Nu(u.createdAt)}`))}function ZS({task:u}){let f=String(u?.status||"").toLowerCase(),l=u?.result,r=l&&typeof l==="object"&&!Array.isArray(l)?l:{},i=["exitCode","code","signal","timeoutMs","previousStatus","mode"].filter((_)=>r[_]!==void 0&&r[_]!==null);if(f==="failed"){let _=DG(u);return A("div",{className:"task-diagnostic failed"},A("b",null,"失败原因"),A("span",{className:"diagnostic-reason"},By(_)),i.length>0?A("div",{className:"diagnostic-meta"},i.map((n)=>A("span",{key:n,className:"data-chip"},A("b",null,n),A("span",null,By(r[n]))))):null)}if(l_(u))return A("div",{className:"task-diagnostic warn"},A("b",null,"等待终态"),A("span",null,`最后更新 ${Dj(u.updatedAt)} 前`));return A("div",{className:"task-diagnostic ok"},A("b",null,"完成摘要"),A(m1,{data:l,empty:"无执行输出"}))}function HS({tasks:u,onRaw:f}){let l=u.filter(l_);return A("div",{"data-testid":"pending-task-page"},A(au,{title:"待处理任务",eyebrow:`${l.length} Pending`},l.length===0?A(Ff,{title:"当前无待处理任务",text:"queued / dispatched / running 会在超时后自动转为 failed;历史记录仍可在任务历史中查看"}):A("div",{className:"table-wrap","data-testid":"pending-task-table"},A("table",null,A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"任务"),A("th",null,"Provider"),A("th",null,"已等待"),A("th",null,"载荷摘要"),A("th",null,"操作"))),A("tbody",null,l.map((r)=>A("tr",{key:r.id},A("td",null,A(Nf,{status:r.status})),A("td",null,A("strong",null,r.command),A("code",null,r.id)),A("td",null,A("code",null,r.providerId)),A("td",null,Dj(r.updatedAt)),A("td",null,A(m1,{data:r.payload})),A("td",null,A(Cf,{title:`Pending Task ${r.id}`,data:O$(r),onOpen:f})))))))))}function OS({tasks:u,onRaw:f}){return A("div",{"data-testid":"task-history-page"},A(au,{title:"任务历史",eyebrow:`${u.length} Tasks`},u.length===0?A(Ff,{title:"暂无任务",text:"下发任务后会在这里看到生命周期"}):A("div",{className:"table-wrap"},A("table",{className:"task-history-table"},A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"任务"),A("th",null,"Provider"),A("th",null,"任务耗时"),A("th",null,"载荷摘要"),A("th",null,"诊断信息"),A("th",null,"更新时间"),A("th",null,"操作"))),A("tbody",null,u.map((l)=>A("tr",{key:l.id,"data-testid":`task-row-${bl(l.id)}`},A("td",null,A(Nf,{status:l.status})),A("td",null,A("strong",null,l.command),A("code",null,l.id)),A("td",null,A("code",null,l.providerId)),A("td",null,A(kG,{task:l})),A("td",null,A(m1,{data:l.payload})),A("td",null,A(ZS,{task:l})),A("td",null,Nu(l.updatedAt)),A("td",null,A(Cf,{title:`Task ${l.id}`,data:O$(l),onOpen:f})))))))))}function BS({tasks:u,onRaw:f}){let l=u.filter((r)=>["succeeded","failed"].includes(r.status));return A(au,{title:"执行结果",eyebrow:"Finished Tasks"},l.length===0?A(Ff,{title:"暂无结果",text:"任务完成后展示 provider 返回的结构化摘要"}):A("div",{className:"result-grid"},l.map((r)=>A("article",{key:r.id,className:"result-card"},A("div",{className:"node-card-head"},A("strong",null,r.command),A(Nf,{status:r.status})),A("code",null,r.id),A(m1,{data:r.result,empty:"无执行输出"}),A(Cf,{title:`Task Result ${r.id}`,data:O$(r),onOpen:f})))))}function VS(u){if(!u||typeof u!=="object")return"--";if(u.type==="interval")return`每 ${S0(Number(u.everySeconds||0))}`;return`每天 ${u.timeOfDay||"03:00"} UTC`}function XS(u){if(!u||typeof u!=="object")return"--";if(u.type==="pgdata_backup")return`PGDATA -> ${u.remoteBaseDir||"/SERVER_DATA/UNIDESK_PG_DATA"}`;if(u.type==="dispatch")return`${u.providerId||"--"} / ${u.command||"--"}`;return String(u.type||"--")}function YS(u){let f=String(u||"").toLowerCase();if(f==="succeeded")return"online";if(f==="failed")return"failed";if(f==="running"||f==="queued")return"warn";return f}function DS(u){let f=Number(u?.durationMs);if(Number.isFinite(f)&&f>=0)return S0(f/1000);let l=j0(u?.startedAt||u?.createdAt);if(l===null)return"--";let y=j0(u?.finishedAt)??Date.now();return S0(Math.max(0,(y-l)/1000))}function BG(u){return{id:"unidesk-pgdata-baidu-daily",name:"PGDATA daily Baidu Netdisk backup",description:"Daily PostgreSQL physical base backup uploaded to Baidu Netdisk /SERVER_DATA with monthly rotation.",enabled:!0,timeOfDay:"03:30",actionType:"pgdata_backup",providerId:u[0]?.providerId||"main-server",command:"echo",payloadJson:JSON.stringify({source:"scheduled-task",message:"hello from scheduler"},null,2),remoteBaseDir:"/SERVER_DATA/UNIDESK_PG_DATA",stagingSubdir:"server-data/unidesk-pg-data",timeoutMs:"3600000"}}function tS({schedules:u,scheduleRuns:f,nodes:l,refresh:r,onRaw:y}){let[i,_]=pu(BG(l||[])),[n,$]=pu(!1),[j,F]=pu(""),[J,U]=pu(""),q=[...f||[]].sort((z,w)=>(j0(w.updatedAt)??0)-(j0(z.updatedAt)??0));function W(z,w){_((H)=>({...H,[z]:w}))}function G(z){let w=z?.action||{};_({id:z?.id||"",name:z?.name||"",description:z?.description||"",enabled:z?.enabled!==!1,timeOfDay:z?.schedule?.timeOfDay||"03:30",actionType:w.type||"dispatch",providerId:w.providerId||l[0]?.providerId||"main-server",command:w.command||"echo",payloadJson:JSON.stringify(w.payload||{source:"scheduled-task"},null,2),remoteBaseDir:w.remoteBaseDir||"/SERVER_DATA/UNIDESK_PG_DATA",stagingSubdir:w.stagingSubdir||"server-data/unidesk-pg-data",timeoutMs:String(w.timeoutMs||3600000)}),U(`正在编辑 ${z?.id||""}`)}function K(){let z={id:i.id,name:i.name,description:i.description,enabled:i.enabled,concurrencyPolicy:"skip",schedule:{type:"daily",timeOfDay:i.timeOfDay,timezone:"Etc/UTC"}};if(i.actionType==="pgdata_backup")return{...z,action:{type:"pgdata_backup",volumeName:"unidesk_pgdata_10gb",remoteBaseDir:i.remoteBaseDir,stagingSubdir:i.stagingSubdir,timeoutMs:Number(i.timeoutMs)||3600000,cleanupLocal:!0}};return{...z,action:{type:"dispatch",providerId:i.providerId,command:i.command,payload:JSON.parse(i.payloadJson||"{}"),timeoutMs:Number(i.timeoutMs)||600000}}}async function Q(z){z.preventDefault(),$(!0),F(""),U("");try{let w=K(),H=encodeURIComponent(String(w.id));await Xu(`${Iu.apiBaseUrl}/schedules/${H}`,{method:"PUT",body:JSON.stringify(w)}),U("定时任务已保存"),await r()}catch(w){F(Bu(w,"保存定时任务失败"))}finally{$(!1)}}async function N(z){if(!z?.id)return;$(!0),F(""),U("");try{await Xu(`${Iu.apiBaseUrl}/schedules/${encodeURIComponent(z.id)}`,{method:"DELETE"}),U(`已删除 ${z.id}`),await r()}catch(w){F(Bu(w,"删除定时任务失败"))}finally{$(!1)}}async function c(z){if(!z?.id)return;$(!0),F(""),U("");try{let w=await Xu(`${Iu.apiBaseUrl}/schedules/${encodeURIComponent(z.id)}/run`,{method:"POST",body:"{}"});U(`已触发 ${z.id} / ${w?.run?.id||"run"}`),await r()}catch(w){F(Bu(w,"触发定时任务失败"))}finally{$(!1)}}return A("div",{className:"page-grid scheduled-task-page","data-testid":"scheduled-task-page"},A(au,{title:"定时任务",eyebrow:`${(u||[]).length} Schedules`},(u||[]).length===0?A(Ff,{title:"暂无定时任务",text:"创建 daily / dispatch / PGDATA backup 任务后会在这里展示下一次执行时间和最近结果"}):A("div",{className:"schedule-card-grid"},(u||[]).map((z)=>A("article",{key:z.id,className:"schedule-card","data-testid":`schedule-row-${bl(z.id)}`},A("div",{className:"node-card-head"},A("strong",null,z.name||z.id),A(Nf,{status:z.enabled?"online":"warn"},z.enabled?"enabled":"disabled")),A("code",null,z.id),A("dl",null,A("dt",null,"计划"),A("dd",null,VS(z.schedule)),A("dt",null,"动作"),A("dd",null,XS(z.action)),A("dt",null,"下次执行"),A("dd",null,Nu(z.nextRunAt)),A("dt",null,"最近执行"),A("dd",null,z.lastRunAt?`${Nu(z.lastRunAt)} / ${z.lastRunId||"--"}`:"--")),A("div",{className:"dispatch-actions"},A("button",{type:"button",className:"ghost-btn",disabled:n,onClick:()=>G(z)},"编辑"),A("button",{type:"button",className:"ghost-btn",disabled:n,onClick:()=>c(z),"data-testid":`schedule-run-${bl(z.id)}`},"手动触发"),A("button",{type:"button",className:"ghost-btn danger",disabled:n,onClick:()=>N(z)},"删除"),A(Cf,{title:`Schedule ${z.id}`,data:z,onOpen:y})))))),A(au,{title:i.id?"配置定时任务":"新建定时任务",eyebrow:"CRUD"},A("form",{className:"dispatch-form schedule-form",onSubmit:Q},A("label",null,"ID",A("input",{value:i.id,onChange:(z)=>W("id",z.target.value)})),A("label",null,"名称",A("input",{value:i.name,onChange:(z)=>W("name",z.target.value)})),A("label",null,"每日执行时间 UTC",A("input",{value:i.timeOfDay,placeholder:"03:30",onChange:(z)=>W("timeOfDay",z.target.value)})),A("label",null,"启用",A("select",{value:i.enabled?"true":"false",onChange:(z)=>W("enabled",z.target.value==="true")},A("option",{value:"true"},"enabled"),A("option",{value:"false"},"disabled"))),A("label",null,"动作类型",A("select",{value:i.actionType,onChange:(z)=>W("actionType",z.target.value)},A("option",{value:"pgdata_backup"},"PGDATA 备份到百度网盘"),A("option",{value:"dispatch"},"Provider Dispatch"))),i.actionType==="pgdata_backup"?[A("label",{key:"remote"},"网盘根目录",A("input",{value:i.remoteBaseDir,onChange:(z)=>W("remoteBaseDir",z.target.value)})),A("label",{key:"staging"},"本地 staging 子目录",A("input",{value:i.stagingSubdir,onChange:(z)=>W("stagingSubdir",z.target.value)}))]:[A("label",{key:"provider"},"Provider",A("select",{value:i.providerId,onChange:(z)=>W("providerId",z.target.value)},(l||[]).map((z)=>A("option",{key:z.providerId,value:z.providerId},`${z.name} / ${z.providerId}`)))),A("label",{key:"command"},"Command",A("select",{value:i.command,onChange:(z)=>W("command",z.target.value)},A("option",{value:"echo"},"echo"),A("option",{value:"docker.ps"},"docker.ps"),A("option",{value:"host.ssh"},"host.ssh"),A("option",{value:"microservice.http"},"microservice.http"))),A("label",{key:"payload",className:"raw-editor-label"},"Payload JSON",A("textarea",{className:"raw-editor",value:i.payloadJson,onChange:(z)=>W("payloadJson",z.target.value)}))],A("label",null,"超时 ms",A("input",{value:i.timeoutMs,onChange:(z)=>W("timeoutMs",z.target.value)})),A("label",{className:"raw-editor-label"},"描述",A("textarea",{className:"raw-editor compact",value:i.description,onChange:(z)=>W("description",z.target.value)})),A("div",{className:"dispatch-actions"},A("button",{type:"button",className:"ghost-btn",disabled:n,onClick:()=>_(BG(l||[]))},"重置"),A("button",{type:"submit",disabled:n||!i.id},n?"保存中":"保存任务")),J?A("p",{className:"muted paragraph"},J):null,A(jf,{error:j,wide:!0}))),A(au,{title:"历史执行记录",eyebrow:`${q.length} Runs`},q.length===0?A(Ff,{title:"暂无执行记录",text:"定时触发或手动触发后会生成 run history"}):A("div",{className:"table-wrap"},A("table",{className:"task-history-table schedule-run-table"},A("thead",null,A("tr",null,A("th",null,"状态"),A("th",null,"任务"),A("th",null,"触发"),A("th",null,"耗时"),A("th",null,"结果摘要"),A("th",null,"更新时间"),A("th",null,"操作"))),A("tbody",null,q.map((z)=>A("tr",{key:z.id,"data-testid":`schedule-run-row-${bl(z.id)}`},A("td",null,A(Nf,{status:YS(z.status)},z.status)),A("td",null,A("strong",null,z.scheduleId),A("code",null,z.id),z.taskId?A("code",null,z.taskId):null),A("td",null,z.trigger||"--"),A("td",null,DS(z)),A("td",null,A(m1,{data:z.result||z.error,empty:"无结果"})),A("td",null,Nu(z.updatedAt)),A("td",null,A(Cf,{title:`Schedule Run ${z.id}`,data:z,onOpen:y})))))))))}function SS({data:u}){let f=u.overview||{};return A("div",{className:"page-grid topology-grid"},A(au,{title:"公开入口",eyebrow:"Public"},A("div",{className:"endpoint-list"},A("article",null,A("b",null,"Frontend"),A("span",null,Iu.frontendPublicUrl||window.location.origin),A(Nf,{status:"online"},"public")),A("article",null,A("b",null,"Provider Ingress"),A("span",null,Iu.providerIngressPublicUrl||"ws://public/ws/provider"),A(Nf,{status:"online"},"public")))),A(au,{title:"内部服务",eyebrow:"Docker Network Only"},A("div",{className:"endpoint-list"},A("article",null,A("b",null,"backend-core API"),A("span",null,"http://backend-core:8080"),A(Nf,{status:"internal"},"internal")),A("article",null,A("b",null,"database"),A("span",null,"postgres://database:5432/unidesk"),A(Nf,{status:"internal"},"internal")))),A(au,{title:"运行态",eyebrow:"Runtime"},A("div",{className:"metric-grid"},A($f,{label:"DB Ready",value:f.dbReady?"YES":"NO",hint:"internal health"}),A($f,{label:"Online Nodes",value:f.onlineNodeCount??0,hint:"provider-gateway self-link"}))))}function PS({session:u}){return A(au,{title:"认证策略",eyebrow:"Frontend Login"},A("div",{className:"policy-grid"},A("article",null,A("span",null,"默认账号"),A("strong",null,Iu.authUsername||"admin")),A("article",null,A("span",null,"当前会话"),A("strong",null,u?.user?.username||"--")),A("article",null,A("span",null,"Session TTL"),A("strong",null,`${Iu.sessionTtlSeconds||0}s`)),A("article",null,A("span",null,"API 访问"),A("strong",null,"同源 Cookie 保护"))),A("p",{className:"muted paragraph"},"浏览器只访问 frontend 同源接口;frontend 容器使用 Docker 内网代理 backend-core API。"))}function MS(){return A(au,{title:"安全边界",eyebrow:"Exposure Rule"},A("div",{className:"security-board"},A("article",{className:"allow"},A("b",null,"允许公网"),A("span",null,"frontend 登录入口"),A("span",null,"provider ingress WebSocket/health")),A("article",{className:"deny"},A("b",null,"禁止公网"),A("span",null,"backend-core REST API"),A("span",null,"PostgreSQL database")),A("article",null,A("b",null,"数据库卷"),A("span",null,"named volume unidesk_pgdata_10gb"),A("span",null,"CLI stop/start 不删除数据卷"))))}function mS({activeModule:u,activeTab:f,data:l,session:r,refresh:y,onRaw:i,onNavigate:_}){if(u==="ops"&&f==="status")return A(fS,{data:l,onRaw:i,onNavigate:_});if(u==="ops"&&f==="performance")return A(WS,{onRaw:i});if(u==="ops"&&f==="events")return A(rS,{events:l.events,onRaw:i});if(u==="ops"&&f==="logs")return A(yS,{logs:l.logs,onRaw:i});if(u==="nodes"&&f==="list")return A(iS,{nodes:l.nodes,onRaw:i});if(u==="nodes"&&f==="monitor")return A($S,{nodes:l.nodes,systemStatuses:l.systemStatuses,tasks:l.tasks,onRaw:i,refresh:y});if(u==="nodes"&&f==="docker")return A(LS,{nodes:l.nodes,dockerStatuses:l.dockerStatuses,onRaw:i});if(u==="nodes"&&f==="gateway")return A(zS,{nodes:l.nodes,tasks:l.tasks,onRaw:i});if(u==="nodes"&&f==="labels")return A(_S,{nodes:l.nodes});if(u==="nodes"&&f==="heartbeats")return A(nS,{nodes:l.nodes});if(u==="tasks"&&f==="dispatch")return A(TS,{nodes:l.nodes,onDispatched:y,onRaw:i});if(u==="tasks"&&f==="scheduled")return A(tS,{schedules:l.schedules,scheduleRuns:l.scheduleRuns,nodes:l.nodes,refresh:y,onRaw:i});if(u==="tasks"&&f==="pending")return A(HS,{tasks:l.pendingTasks,onRaw:i});if(u==="tasks"&&f==="history")return A(OS,{tasks:l.tasks,onRaw:i});if(u==="tasks"&&f==="results")return A(BS,{tasks:l.tasks,onRaw:i});if(u==="apps"&&f==="catalog")return A(ES,{microservices:l.microservices,onRaw:i,onNavigate:_});if(u==="apps"&&f==="todo-note")return A(QG,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="findjob")return A(nW,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="pipeline")return A(_G,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="met-nonlinear")return A(JW,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="claudeqq")return A(RQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="baidu-netdisk")return A(pQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="filebrowser")return A(_W,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="apps"&&f==="code-queue")return A(uW,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl,initialTasksData:Mt});if(u==="apps"&&f==="project-manager")return A(AG,{microservices:l.microservices,onRaw:i,apiBaseUrl:Iu.apiBaseUrl});if(u==="config"&&f==="topology")return A(SS,{data:l});if(u==="config"&&f==="auth")return A(PS,{session:r});if(u==="config"&&f==="security")return A(MS);return A(Ff,{title:"未找到页面",text:"请选择左侧主模块和顶部子功能标签"})}function pS({session:u,onLogout:f}){let l=IA(Zr,window.location.pathname),[r,y]=pu(l.moduleId),[i,_]=pu({...tn,[l.moduleId]:l.tabId}),[n,$]=pu({overview:null,nodes:[],systemStatuses:[],dockerStatuses:[],microservices:[],events:[],tasks:[],pendingTasks:[],schedules:[],scheduleRuns:[],logs:[]}),[j,F]=pu({ok:!1,text:"连接中"}),[J,U]=pu(null),[q,W]=pu(new Date),[G,K]=pu(null),[Q,N]=pu(!1),[c,z]=pu(!1),w=M1.default.useRef(!1),H=Zr.moduleById[r]||Zr.modules[0],B=i[r]||tn[r]||H.tabs[0].id,T=Array.isArray(n.microservices)?n.microservices:[],X=T.length===0&&r==="apps"&&B==="code-queue"?[mt]:T,R=X===T?n:{...n,microservices:X},Y=r==="apps"?X.find((o)=>String(o?.id||"")===B):null,P=Y?RG(Y):{},t=H.tabs.find((o)=>o.id===B)?.label||B,O=Y?[{key:"microservice",label:"用户服务",value:`${t} ${P.providerStatus==="online"?"在线":P.providerStatus||"未知"}`,tone:P.providerStatus==="online"?"ok":"warn",testId:"active-microservice-status"}]:[];async function M(){if(w.current)return;w.current=!0,z(!0);try{let o=[],s=(Fu,Ku)=>{o.push([Fu,Xu(Ku)])},x=r==="ops"&&B==="status",uu=x||r==="config"&&B==="topology",nu=x||r==="nodes"||r==="tasks"&&(B==="dispatch"||B==="scheduled"),$u=r==="apps"&&B!=="code-queue";if(uu)s("overview",`${Iu.apiBaseUrl}/overview`);if(nu)s("nodes",`${Iu.apiBaseUrl}/nodes`);if(r==="nodes"&&B==="monitor")s("systemStatuses",`${Iu.apiBaseUrl}/nodes/system-status?limit=60`),s("tasks",`${Iu.apiBaseUrl}/tasks?limit=120&summary=1`);else if(r==="nodes"&&B==="docker")s("dockerStatuses",`${Iu.apiBaseUrl}/nodes/docker-status`);else if(r==="nodes"&&B==="gateway")s("tasks",`${Iu.apiBaseUrl}/tasks?limit=300&summary=1`);else if(r==="tasks"&&B==="scheduled")s("schedules",`${Iu.apiBaseUrl}/schedules?limit=100`),s("scheduleRuns",`${Iu.apiBaseUrl}/schedules/runs?limit=100`);else if(r==="tasks"&&B==="pending")s("pendingTasks",`${Iu.apiBaseUrl}/tasks?status=pending&limit=100&summary=1`);else if(r==="tasks"&&(B==="history"||B==="results"))s("tasks",`${Iu.apiBaseUrl}/tasks?limit=300&summary=1`);else if(x)s("tasks",`${Iu.apiBaseUrl}/tasks?limit=8&lite=1`),s("pendingTasks",`${Iu.apiBaseUrl}/tasks?status=pending&limit=20&lite=1`);if($u)s("microservices",`${Iu.apiBaseUrl}/microservices`);if(r==="ops"&&B==="events")s("events",`${Iu.apiBaseUrl}/events?limit=100`);if(r==="ops"&&B==="logs")s("logs","/logs?limit=100");await Promise.all(o.map(async([Fu,Ku])=>{let Wu=await Ku,m={};if(Fu==="overview")m.overview=Wu;if(Fu==="nodes")m.nodes=Wu.nodes||[];if(Fu==="systemStatuses")m.systemStatuses=Wu.systemStatuses||[];if(Fu==="dockerStatuses")m.dockerStatuses=Wu.dockerStatuses||[];if(Fu==="microservices")m.microservices=Wu.microservices||[];if(Fu==="events")m.events=Wu.events||[];if(Fu==="tasks")m.tasks=Wu.tasks||[];if(Fu==="pendingTasks")m.pendingTasks=Wu.tasks||[];if(Fu==="schedules")m.schedules=Wu.schedules||[];if(Fu==="scheduleRuns")m.scheduleRuns=Wu.runs||[];if(Fu==="logs")m.logs=Wu.logs||[];$((d)=>({...d,...m}))})),F({ok:!0,text:"核心在线"}),U(new Date)}catch(o){if(F({ok:!1,text:Bu(o,"连接失败")}),o.status===401)f(!1)}finally{w.current=!1,z(!1)}}A0(()=>{let o=()=>{if(!GG())return;M()};o();let s=setInterval(o,pt(r,B)),x=()=>{if(GG())o()};return document.addEventListener("visibilitychange",x),()=>{clearInterval(s),document.removeEventListener("visibilitychange",x)}},[r,B]),A0(()=>{let o=setInterval(()=>W(new Date),1000);return()=>clearInterval(o)},[]),A0(()=>{let o=WW(Zr,window.location.pathname);if(o&&window.location.pathname!==o)window.history.replaceState(null,"",o)},[]),A0(()=>{let o=()=>{let s=IA(Zr,window.location.pathname);y(s.moduleId),_((x)=>({...x,[s.moduleId]:s.tabId})),K(null)};return window.addEventListener("popstate",o),()=>window.removeEventListener("popstate",o)},[]),A0(()=>{window.scrollTo({top:0,left:0,behavior:"auto"})},[r,B]);function S(o,s,x="push"){let uu=Zr.moduleById[o]?o:Zr.fallbackTarget.moduleId,nu=Zr.moduleById[uu]?.tabs.some((Fu)=>Fu.id===s)?s:tn[uu]||Zr.moduleById[uu]?.tabs[0]?.id||Zr.fallbackTarget.tabId;y(uu),_((Fu)=>({...Fu,[uu]:nu}));let $u=k6(Zr,uu,nu);if(window.location.pathname!==$u){let Fu=x==="replace"?"replaceState":"pushState";window.history[Fu](null,"",$u)}}function b(o,s){K({title:o,data:s})}let[Z,D]=pu(!1),{unreadCount:I,notifications:k}=wl(),h=k.length>0?k[k.length-1]:null;return A("div",{className:`shell ${Q?"rail-collapsed":""}`,"data-testid":"app-shell"},A(et,{activeModule:r,activeTabs:i,onNavigate:S,collapsed:Q,onToggle:()=>N((o)=>!o)}),A("main",{className:"workspace"},A(ot,{connection:j,lastRefresh:J,onRefresh:M,onLogout:()=>f(!0),session:u,clock:q,activeStatusItems:O,onNotificationToggle:()=>D((o)=>!o),unreadCount:I}),A(uS,{module:H,activeTab:B,onNavigate:S}),A(Yj.Provider,{value:c},A(mS,{activeModule:r,activeTab:B,data:R,session:u,refresh:M,onRaw:b,onNavigate:S}))),A(st,{raw:G,onClose:()=>K(null)}),h&&A(zG,{key:h.id,notification:h}),Z&&A(NG,{onClose:()=>D(!1)}))}function CS(){let[u,f]=pu(!0),[l,r]=pu(null);async function y(){f(!0);try{let _=await Xu("/api/session");r(_.authenticated?_:null)}catch{r(null)}finally{f(!1)}}async function i(_){if(_)try{await Xu("/logout",{method:"POST"})}catch{}r(null)}if(A0(()=>{y()},[]),u)return A("main",{className:"loading-screen"},A("div",{className:"brand-mark"},"UD"),A("span",null,"加载会话"));if(!l)return A(at,{onLogin:r});return A(mQ,null,A(pS,{session:l,onLogout:i}))}var gG=document.getElementById("root");if(gG===null)throw Error("root element not found");VG.createRoot(gG).render(A(CS));})(); + ${Y} + `,width:w,height:L}}function q8(u,f){let l=URL.createObjectURL(u),r=document.createElement("a");r.href=l,r.download=f,r.click(),setTimeout(()=>URL.revokeObjectURL(l),1000)}async function Ww(u,f){let l=Nw(f,"pipeline"),{svg:r,width:n,height:i}=DY(u,f),y=new Blob([r],{type:"image/svg+xml;charset=utf-8"}),_=URL.createObjectURL(y);try{let $=new Image;await new Promise((F,U)=>{$.onload=()=>F(),$.onerror=()=>U(Error("svg image load failed")),$.src=_});let c=document.createElement("canvas");c.width=n,c.height=i;let A=c.getContext("2d");if(!A)throw Error("canvas unavailable");A.drawImage($,0,0);let j=await new Promise((F)=>c.toBlob(F,"image/png"));if(!j)throw Error("png export failed");q8(j,`${l}.png`)}catch{q8(y,`${l}.svg`)}finally{URL.revokeObjectURL(_)}}async function pY(u){let f=Nw(String(u?.title||"pipeline-gantt"),"pipeline-gantt"),{svg:l,width:r,height:n}=mY(u),i=new Blob([l],{type:"image/svg+xml;charset=utf-8"}),y=URL.createObjectURL(i);try{let _=new Image;await new Promise((j,F)=>{_.onload=()=>j(),_.onerror=()=>F(Error("gantt svg image load failed")),_.src=y});let $=document.createElement("canvas");$.width=r,$.height=n;let c=$.getContext("2d");if(!c)throw Error("canvas unavailable");c.drawImage(_,0,0);let A=await new Promise((j)=>$.toBlob(j,"image/png"));if(!A)throw Error("gantt png export failed");q8(A,`${f}.png`)}catch{q8(i,`${f}.svg`)}finally{URL.revokeObjectURL(y)}}async function PY(u){for(let f of u){if(f.flow.nodes.length===0)continue;await Ww(f.flow,f.title),await new Promise((l)=>setTimeout(l,750))}}function lw(u,f){return u.find((l)=>String(l?.pipelineId||"")===f)||null}function rw(u){return ku(u?.startedAt)??ku(u?.artifact?.startedAt)??ku(u?.request?.createdAt)??ku(u?.updatedAt)??0}function MY(u,f){return u.filter((l)=>String(l?.pipelineId||"")===f).slice().sort((l,r)=>rw(l)-rw(r)||String(l?.runId||"").localeCompare(String(r?.runId||"")))}function T9(u,f){let l=String(f?.runId||""),r=u.findIndex((y)=>String(y?.runId||"")===l),n=r>=0?r+1:u.length,i=String(f?.status||"--");return`Epoch ${n} / ${l||"--"} / ${i}`}function Zr(u){return String(u?.procedureRunId||u?.runId||"")}function L8(u,f){let l=String(u?.nodeId||u?.request?.nodeId||"");if(l)return l;let r=Zr(u),n=`${f}__`;if(r.startsWith(n))return r.slice(n.length).replace(/__\d+$/u,"");return""}function $8(u,f){let l=Du(u?.artifact)?u.artifact:{},r=Du(u?.request)?u.request:{};return Y$(u?.startedAt,l.startedAt,r.createdAt,r.startedAt,u?.createdAt,u?.updatedAt,f?.startedAt,f?.request?.createdAt)}function t8(u,f){let l=String(u?.status?.status||u?.artifact?.status||u?.status||"").toLowerCase(),r=Du(u?.artifact)?u.artifact:{},n=H9(l);return Y$(u?.finishedAt,r.finishedAt,u?.completedAt,n?u?.updatedAt:void 0,n?r.updatedAt:void 0,n?f?.updatedAt:void 0)}function ww(u,f,l=Date.now()){let r=String(u?.runId||""),n=new Set(f.map((i)=>String(i?.id||"")).filter(Boolean));return Vu(u?.procedureRuns).flatMap((i)=>{let y=L8(i,r);if(!y)return[];let _=String(i?.status?.status||i?.artifact?.status||i?.status||"unknown").toLowerCase(),$=$8(i,u),c=ku($);if(c===null)return[];let A=t8(i,u),j=ku(A)??(H9(_)?ku(i?.updatedAt)??c+1000:l),F=Math.max(c+1000,j);return[{nodeId:y,knownNode:n.has(y),procedureRunId:Zr(i),status:_,startMs:c,endMs:F,startedAt:V$(c),finishedAt:V$(F),durationMs:F-c,runId:r,raw:i}]}).sort((i,y)=>i.startMs-y.startMs||i.endMs-y.endMs||i.nodeId.localeCompare(y.nodeId))}function CY(u,f,l=[]){let r=f.map((A)=>Number(A.startMs)).filter(Number.isFinite),n=f.map((A)=>Number(A.endMs)).filter(Number.isFinite);for(let A of l){let j=Bf(A?.eventMs??A?.ms);if(j!==null)r.push(j),n.push(j)}let i=ku(u?.startedAt)??ku(u?.artifact?.startedAt)??ku(u?.request?.createdAt),y=ku(u?.finishedAt)??ku(u?.artifact?.finishedAt)??ku(u?.updatedAt);if(i!==null)r.push(i);if(y!==null)n.push(y);let _=Date.now(),$=r.length>0?Math.min(...r):_-60000,c=Math.max($+60000,n.length>0?Math.max(...n):_);return{startMs:$,endMs:c,durationMs:c-$}}var c8=12,Lw=20,E9=100,RY=!1;function hn(u){let f=Number(u);if(!Number.isFinite(f))return 0;return Math.max(0,Math.min(100,Math.round(f*100)/100))}function xY(u){let f=Math.max(c8,Number(u||c8)),l=Math.log(f/c8)/Math.log(Lw);return hn(l*100)}var X$=xY(E9);function S9(u){let f=hn(u)/100,l=c8*Math.pow(Lw,f),r=f<0.24?"全局":f<0.64?"均衡":"细节";return{value:hn(f*100),pxPerMinute:l,label:r}}function N9(u){let f=Math.round(Number(u));return Math.abs(f-E9)<=1?E9:f}function hY(u,f=X$){let l=Math.max(1,Number(u.durationMs||0)/60000),r=S9(f);return Math.round(Math.max(360,Math.min(7200,l*Number(r.pxPerMinute||48))))}function bY(u,f=7){let l=Math.max(1,Number(u.endMs||0)-Number(u.startMs||0));return Array.from({length:f},(r,n)=>{let i=f===1?0:n/(f-1);return{ms:Number(u.startMs)+l*i,percent:i*100}})}function vY(u,f){let l=Math.max(1,Number(f.endMs)-Number(f.startMs));return Math.max(0,Math.min(100,(u-Number(f.startMs))/l*100))}function Bf(u){let f=Number(u);return Number.isFinite(f)?f:null}function m9(u){return _w(u?.status)&&!H9(u?.status)}function Kw(u,f,l,r){let n=Math.max(1,l-f),i=Math.max(0,Math.min(1,(u-f)/n));return Number((i*r).toFixed(3))}function nw(u,f){if(!f)return null;let l=Bf(f?.startMs),r=Bf(f?.endMs),n=Bf(f?.chartHeight);if(l===null||r===null||n===null)return null;return Kw(u,l,r,n)}function Gw(u,f){let l=Bf(u?.rawStartMs??u?.startMs)??Bf(u?.startMs)??f,r=Bf(u?.endMs)??l+1000;if(!m9(u))return Math.max(l+1000,r);return Math.max(l+1000,r,f)}function kY(u,f,l,r){let n=Bf(u?.startMs)??r-60000,i=Bf(u?.endMs)??r,y=l.reduce((Q,w)=>Math.max(Q,Gw(w,r)),i),_=Math.max(n+60000,i,y),$=Math.max(1,_-n),c={startMs:n,endMs:_,durationMs:$},A=hY(c,f),j=S9(f),F=Math.max(5,Math.min(18,Math.round(A/150))),U=bY(c,F).map((Q)=>{let w=Number(Q.ms),L=Kw(w,n,_,A);return{...Q,y:L,timestamp:V$(w),offsetMs:w-n}});return{source:"frontend-y",startMs:n,endMs:_,durationMs:$,chartHeight:A,scale:hn(f),normalizedScale:Number((hn(f)/100).toFixed(3)),pxPerMinute:Number(Number(j.pxPerMinute||0).toFixed(3)),ticks:U}}function IY(u,f,l){if(!m9(u))return u;let r=Bf(u?.rawStartMs??u?.startMs)??Bf(u?.startMs)??l,n=Gw(u,l),i=nw(r,f),y=nw(n,f),_=Bf(i??u?.y1??u?.startY)??0,$=Bf(y??u?.y2??u?.endY)??_+10,c=Math.max(24,$-_);return{...u,live:!0,startMs:r,endMs:n,durationMs:Math.max(1000,n-r),finishedAt:V$(n),y1:_,y2:$,startY:_,endY:$,height:c}}function p9(u,f,l){return vY(u,f)/100*l}function Jy(u){return Boolean(u&&String(u?.source||"")!=="frontend-y")}function zw(u,f,l,r,n){if(Jy(r))for(let y of n){let _=Bf(u?.[y]);if(_!==null)return _}let i=Bf(u?.ms??u?.eventMs??u?.startMs);return p9(i??Number(f.startMs),f,l)}function N8(u,f,l,r){return zw(u,f,l,r,["y1","startY"])}function Z9(u,f,l,r){if(Jy(r)){let i=Bf(u?.y2??u?.endY);if(i!==null)return i}let n=Bf(u?.endMs)??Number(f.endMs);return p9(n,f,l)}function Tw(u,f,l,r){if(Jy(r)){let i=Bf(u?.height);if(i!==null)return Math.max(1,i)}let n=u?.live?24:10;return Math.max(n,Z9(u,f,l,r)-N8(u,f,l,r))}function cr(u,f,l,r){return zw(u,f,l,r,["y","timeAxisY"])}function Ew(u,f,l,r){if(Jy(r)||String(r?.source||"")==="frontend-y"){let y=Bf(u?.y);if(y!==null)return y}let n=Bf(u?.percent);if(n!==null)return n/100*l;let i=Bf(u?.ms)??Number(f.startMs);return p9(i,f,l)}function gY(u){let f=String(u?.promptEvent||u?.raw?.promptEvent||u?.event||"").toLowerCase();if(!["node-long-running-observation","node-finished"].includes(f))return"";let l=String(u?.sourceNodeId||u?.raw?.sourceNodeId||u?.raw?.detail?.nodeId||""),r=String(u?.nodeId||u?.targetNodeId||"");return l&&l!==r?l:""}function sY(u,f){let l=new Set(f.map((n)=>[String(n.sourceNodeId||""),String(n.targetNodeId||""),String(n.targetMarkerId||""),String(n.action||"")].join(":"))),r=[...f];for(let n of u){let i=gY(n),y=String(n?.nodeId||""),_=String(n?.id||"");if(!i||!y||!_)continue;let $=[i,y,_,"observe"].join(":");if(l.has($))continue;l.add($),r.push({id:`observation-arrow:${_}:${i}:${y}`,commandId:String(n?.commandId||n?.eventId||_),sourceNodeId:i,targetNodeId:y,sourceMarkerId:"",targetMarkerId:_,sourceKind:"monitor",action:"observe",status:"observation"})}return{markers:u,arrows:r}}function aY(u){let f=vr(u),l=String(u?.promptEvent||"");if(f==="initial-prompt-delivered")return"initial";if(l==="node-finished"||l==="node-long-running-observation"||l.startsWith("monitor-"))return"monitor";if(f==="monitor-prompt-delivered"||String(u?.sourceKind||"").toLowerCase()==="monitor")return"monitor";return"append"}function oY(u){return Vu(u?.tags||u?.raw?.tags).map((f)=>String(f||"")).filter(Boolean)}function dY(u){let f=vr(u),l=String(u?.promptEvent||"");if(f==="initial-prompt-delivered")return"初始 prompt";if(l==="node-long-running-observation")return"长任务观察";if(l==="node-finished")return oY(u).includes("monitor.audit")?"节点完成 / OA 审核":"节点完成";if(l==="monitor-interval")return"Monitor observation";if(l==="monitor-start")return"Monitor start";if(l==="monitor-stop")return"Monitor stop";if(f==="monitor-prompt-delivered")return"Monitor prompt";if(f==="append-prompt-queued")return"追加 prompt 已排队";return"追加 prompt"}function iw(u){let f=vr(u);if(f==="control-command-applied")return 3;if(f==="control-command-ignored")return 2;if(f==="control-command-queued")return 1;return 0}function eY(u,f){let l=String(u?.commandId||"");if(l)return`command:${l}`;return["control-event",Ay(u)||Y$(u?.createdAt,u?.timestamp)||`index-${f}`,String(u?.sourceKind||""),String(u?.sourceNodeId||""),String(u?.targetNodeId||""),C1(u)].join(":")}function uS(u){return G9([u?.targetNodeId,...Vu(u?.resetNodeIds)])}function fS(u,f){let l=H$(u),r=vr(u),n=String(u?.targetNodeId||""),i=Boolean(n)&&f!==n;if(r==="control-command-applied")return i?`${l} 波及`:`${l} 生效`;if(r==="control-command-ignored")return`${l} 忽略`;if(r==="control-command-queued")return`${l} 已发起`;return i?`${l} 波及`:l}function lS(u){if(vr(u)==="control-command-ignored")return"ignored";let l=C1(u);if(l==="restart"||l==="redo")return"restart";if(l==="modify")return"modify";if(l==="approve")return"approve";if(l==="guide")return"guide";return"pending"}function rS(u){let f=String(u?.sourceKind||"").toLowerCase();if(f==="monitor")return"monitor";if(f==="webui")return"webui";if(f==="cli")return"cli";return"system"}function nS(u,f,l,r){let n=u.filter((c)=>String(c.nodeId||"")===f).sort((c,A)=>Number(c.startMs)-Number(A.startMs)),i=n.find((c)=>l>=Number(c.startMs)-1000&&l<=Number(c.endMs)+1000);if(i)return{ms:l,onInterval:!0,snapReason:"inside-interval",procedureRunId:String(i.procedureRunId||"")};let y=C1(r),_=n.slice().reverse().find((c)=>Number(c.endMs)<=l+1000);if(_&&y==="approve")return{ms:Number(_.endMs),onInterval:!0,snapReason:"previous-interval-end",procedureRunId:String(_.procedureRunId||"")};let $=n.find((c)=>Number(c.startMs)>=l-1000);if($&&["guide","modify","restart","redo"].includes(y))return{ms:Number($.startMs),onInterval:!0,snapReason:"next-interval-start",procedureRunId:String($.procedureRunId||"")};return{ms:l,onInterval:!1,snapReason:"event-time",procedureRunId:String(r?.procedureRunId||"")}}function Zw(u,f,l,r){let n=Math.hypot(l-u,r-f),i=n>RW?RW:0,y=i>0?l-(l-u)/n*i:l,_=i>0?r-(r-f)/n*i:r,$=y-u,c=Math.max(16,Math.min(42,Math.abs($)*0.45+12)),A=$===0?1:Math.sign($);return`M ${u},${f} C ${u+A*c},${f} ${y-A*c},${_} ${y},${_}`}function iS(u,f){let l=String(u?.runId||f?.runId||""),r=ww({...Du(f)?f:{},...Du(u)?u:{},runId:l,procedureRuns:Vu(u?.procedureRuns).length>0?u.procedureRuns:f?.procedureRuns},[]),n=[],i=[],y=[],_=new Set,$=new Map,c=(F,U)=>{if(!F.nodeId||!Number.isFinite(Number(F.ms)))return;if(_.has(F.id))return;_.add(F.id),U.push(F)};for(let F of Vu(u?.procedureRuns)){let U=L8(F,l),Q=Zr(F);if(!U)continue;for(let w of Vu(F?.attempts)){let L=w8(w);for(let J of K9(w?.controlEventRecords)){let N=vr(J);if(!["initial-prompt-delivered","append-prompt-delivered","monitor-prompt-delivered"].includes(N))continue;let q=Ay(J),W=ku(q);if(W===null)continue;let z=String(J?.eventId||"");c({id:`prompt:${z||`${Q}:${L}:${N}:${W}`}`,runId:l,nodeId:U,procedureRunId:Q,attempt:L,kind:"prompt",tone:aY(J),status:"delivered",label:dY(J),ms:W,timestampIso:q,sourceKind:String(J?.sourceKind||""),sourceNodeId:String(J?.sourceNodeId||""),targetNodeId:U,action:"",eventId:z,commandId:String(J?.commandId||""),raw:J},n)}}}let A=new Map;K9(u?.controlEvents).forEach((F,U)=>{let Q=eY(F,U),w=A.get(Q)||{key:Q,events:[]};w.events.push(F),A.set(Q,w)});for(let F of A.values()){let U=Vu(F.events).slice().sort((B,X)=>iw(X)-iw(B)),Q=Vu(F.events).find((B)=>vr(B)==="control-command-queued")||null,w=U[0]||Q;if(!Q&&!w)continue;let L=String(Q?.sourceNodeId||w?.sourceNodeId||""),J=String(Q?.sourceKind||w?.sourceKind||""),N=Ay(Q)||Ay(w)||Y$(Q?.createdAt,w?.createdAt),q=ku(N),W=String(w?.commandId||Q?.commandId||F.key),z=(vr(w)||"control-command-queued").replace(/^control-command-/u,""),Z="";if(L&&q!==null)Z=`control-source:${W}:${L}`,$.set(W,Z),c({id:Z,runId:l,nodeId:L,procedureRunId:String(Q?.procedureRunId||w?.procedureRunId||""),attempt:"",kind:"control-source",tone:rS(Q||w),status:z,label:`${H$(Q||w)} 发起`,ms:q,timestampIso:N,action:C1(Q||w),sourceKind:J,sourceNodeId:L,targetNodeId:String(w?.targetNodeId||Q?.targetNodeId||""),commandId:W,raw:Q||w},i);let H=w||Q,E=Ay(H)||N,D=ku(E);if(D===null)continue;let h=uS(H);for(let B of h){let X=nS(r,B,D,H),m=`control-target:${W}:${B}`;if(c({id:m,runId:l,nodeId:B,procedureRunId:X.procedureRunId,attempt:"",kind:"control-target",tone:lS(H),status:z,label:fS(H,B),ms:X.ms,eventMs:D,onInterval:X.onInterval,snapReason:X.snapReason,snapped:Number(X.ms)!==D,timestampIso:E,renderedTimestampIso:V$(Number(X.ms)),action:C1(H),sourceKind:J,sourceNodeId:L,targetNodeId:B,commandId:W,raw:H},i),Z&&L&&L!==B)y.push({id:`control-arrow:${W}:${L}:${B}`,commandId:W,sourceNodeId:L,targetNodeId:B,sourceMarkerId:Z,targetMarkerId:m,sourceKind:J,action:C1(H),status:z})}}let j=[...n,...i].sort((F,U)=>Number(F.ms)-Number(U.ms)||String(F.nodeId).localeCompare(String(U.nodeId))||String(F.id).localeCompare(String(U.id)));return{...sY(j,y),sourceMarkerByCommand:$}}function yS({details:u,selectedNodeId:f,selectedNodeRuntime:l,control:r,onRaw:n}){if(!u)return G("span",{className:"muted"},"点击“抓取过程”读取 node 运行材料;主界面只显示结构化摘要,完整内容需点开原始 JSON。");let i=Vu(u.procedureRuns),y=i.at(-1)||{},_=Vu(y.attempts),$=_.at(-1)||{},c=Vu(y.workerLogTail),A=Vu($.controlEventsTail),j=Vu($.controlPromptsTail),F=Vu($.monitorPromptsTail),U=J9(A),Q=J9(j),w=J9(F),L=$.opencodeMessages||{};return G("div",{className:"pipeline-evidence-list compact"},G(Tr,{title:"Node runtime",subtitle:f||"--",facts:[`status ${l?.status||"pending"}`,`attempts ${l?.attempts??_.length}`,`procedure ${l?.currentProcedureRunId||Zr(y)||"--"}`,r.fetchedAt?`fetched ${$f(r.fetchedAt)}`:"not fetched"],data:u.node||u,onRaw:n,testId:"raw-pipeline-node-runtime"}),G(Tr,{title:"Procedure runs",subtitle:`${i.length} groups`,facts:[`latest ${y.status?.status||y.status||"--"}`,`steps ${Vu(y.recentSteps).length}`,`duration ${Er(ku(y.finishedAt)&&ku(y.startedAt)?Number(ku(y.finishedAt))-Number(ku(y.startedAt)):y.durationMs)}`],data:i,onRaw:n,testId:"raw-pipeline-node-procedures"}),G(Tr,{title:"OpenCode messages",subtitle:String(L.exists?"available":"not indexed"),facts:[`messages ${j8(L.messageCount)}`,`size ${j8(L.size)}`,`updated ${Nu(L.updatedAt)}`],data:L,onRaw:n,testId:"raw-pipeline-node-messages"}),G(Tr,{title:"Control prompts",subtitle:"manual / monitor append queues",facts:[`manual tail ${Q.total}`,`monitor tail ${w.total}`,`last ${Nu(O9(Q.lastAt,w.lastAt))}`],data:{controlPromptsTail:j,monitorPromptsTail:F},onRaw:n,testId:"raw-pipeline-node-prompts"}),G(Tr,{title:"Control events",subtitle:U.eventKinds.length>0?U.eventKinds.join(", "):"event tail",facts:[`tail ${U.total}`,`parsed ${U.parsed}`,`last ${Nu(U.lastAt)}`],data:A,onRaw:n,testId:"raw-pipeline-node-events"}),G(Tr,{title:"Worker log",subtitle:"tail is hidden on main canvas",facts:[`tail ${c.length} lines`,"raw only via button",`procedure ${Zr(y)||"--"}`],data:c,onRaw:n,testId:"raw-pipeline-node-worker-log"}))}function _S({activeRun:u,onRaw:f}){if(!u)return G(Ar,{title:"暂无运行材料",text:"没有 Pipeline epoch 时不会展示运行材料索引。"});let l=Vu(u.nodes),r=Vu(u.procedureRuns),n=Vu(u.submissions),i=Vu(u.workerLogTail),y=bW(l),_=bW(r),$=r.filter((A)=>String(A?.status||"").toLowerCase()==="failed"),c=O9(...r.flatMap((A)=>[A.updatedAt,A.finishedAt,A.startedAt]));return G("div",{className:"pipeline-evidence-list"},G(Tr,{title:"Epoch overview",subtitle:u.runId||"--",facts:[`pipeline ${u.pipelineId||"--"}`,`status ${u.status||"--"}`,`started ${Nu(u.startedAt)}`,`updated ${Nu(u.updatedAt)}`],data:u,onRaw:f,testId:"raw-pipeline-run"}),G(Tr,{title:"Node states",subtitle:`${l.length} nodes`,facts:[`running ${y.running||0}`,`succeeded ${y.succeeded||0}`,`failed ${y.failed||0}`,`pending ${y.pending||0}`],data:l,onRaw:f,testId:"raw-pipeline-run-nodes"}),G(Tr,{title:"Procedure run index",subtitle:`${r.length} procedure records`,facts:[`succeeded ${_.succeeded||0}`,`failed ${_.failed||0}`,`latest ${Nu(c)}`,`errors ${$.length}`],data:r,onRaw:f,testId:"raw-pipeline-run-procedures"}),G(Tr,{title:"OA submissions",subtitle:`${n.length} submission files`,facts:[`records ${n.length}`,`task ${j8(u.task)}`,"raw grouped by run"],data:n,onRaw:f,testId:"raw-pipeline-run-submissions"}),G(Tr,{title:"Worker log tail",subtitle:"hidden from main interface",facts:[`tail ${i.length} lines`,"display raw only after click",`updated ${Nu(u.updatedAt)}`],data:i,onRaw:f,testId:"raw-pipeline-run-worker-log"}))}function $S({diagnostics:u,onRaw:f}){let l=Vu(u?.runs).filter(Du),r=Vu(u?.forbiddenResiduals),n=Du(u?.guarantees)?u.guarantees:{},i=u?.hasNeutralNodeFinishedEvidence===!0&&u?.hasNoAuditPolicyEvidence===!0&&u?.hasAuditPolicyEvidence===!0,y=u?.ok===!0&&i&&r.length===0,_=l[0]||null,$=[{label:"中性完成事实",ok:n.neutralNodeFinished===!0,hint:"node-finished 不携带流程策略"},{label:"Config 策略判定",ok:n.auditPolicyFromConfig===!0,hint:"OA backend 读取当前 epoch 配置"},{label:"控制命令来自 OA",ok:n.runnerConsumesControlCommandsFromOaEvents===!0,hint:"runner 只消费 OA control.command"},{label:"无独立审核事件",ok:n.noIndependentAuditRequestEvent===!0,hint:"审核由 node-finished + policy 派生"},{label:"无批次门禁",ok:n.noBatchFinishedControlGate===!0,hint:"下游启动由每个 node 完成驱动"}];return G("div",{className:"pipeline-oa-panel","data-testid":"pipeline-oa-event-flow-panel"},G("div",{className:"metric-grid compact"},G(Xl,{label:"OA Flow",value:y?"100%":"--",hint:String(u?.mode||"waiting diagnostics"),tone:y?"ok":"warn"}),G(Xl,{label:"禁止残留",value:r.length,hint:r.length===0?"source scan clean":"needs cleanup",tone:r.length===0?"ok":"warn"}),G(Xl,{label:"No-audit",value:u?.hasNoAuditPolicyEvidence?"OK":"--",hint:"OA 下游策略证据",tone:u?.hasNoAuditPolicyEvidence?"ok":"warn"}),G(Xl,{label:"Monitor 审核",value:u?.hasAuditPolicyEvidence?"OK":"--",hint:"OA 控制事件闭环",tone:u?.hasAuditPolicyEvidence?"ok":"warn"})),G("div",{className:"pipeline-oa-guarantees"},$.map((c)=>G("article",{key:c.label,className:`pipeline-oa-guarantee ${c.ok?"ok":"warn"}`},G(xn,{status:c.ok?"online":"warn"},c.ok?"OK":"MISS"),G("div",null,G("strong",null,c.label),G("span",null,c.hint))))),G("div",{className:"pipeline-evidence-list compact"},l.slice(0,6).map((c)=>G(Tr,{key:c.runId,title:String(c.runId||"--"),subtitle:[Number(c.monitorAuditNodeFinishedCount||0)>0?"monitor audit":"",Number(c.noAuditPolicyCount||0)>0?"no-audit policy":""].filter(Boolean).join(" / ")||"event evidence",facts:[`events ${c.eventCount||0}`,`node-finished ${c.nodeFinishedCount||0}`,`policy-in-detail ${c.nodeFinishedWithPolicyCount||0}`,`queued ${c.controlQueuedCount||0}`,`applied ${c.controlAppliedCount||0}`],data:c,onRaw:f,testId:`raw-pipeline-oa-run-${String(c.runId||"run").replace(/[^a-zA-Z0-9_.-]+/g,"-")}`}))),_?G("p",{className:"muted paragraph"},`最新证据 ${_.runId}: ${_.nodeFinishedCount||0} 个 node-finished,${_.controlAppliedCount||0} 个控制结果。`):G(Ar,{title:"暂无 OA 事件流证据",text:"等待 Pipeline backend 暴露 diagnostics。"}),u?G("div",{className:"panel-actions inline-actions"},G(kr,{title:"Pipeline OA Event Flow Diagnostics",data:u,onOpen:f,testId:"raw-pipeline-oa-event-flow"})):null)}function tS({quota:u,onRaw:f}){let l=Du(u?.summary)?u.summary:{},r=Du(u?.target)?u.target:{},n=Du(u?.cache)?u.cache:{},i=u?.ok===!0,y=String(u?.modelId||l.modelName||r.modelName||"MiniMax-M2.7"),_=l.totalCount??r.currentIntervalTotalCount,$=l.usageCount??r.currentIntervalUsageCount,c=l.remainingCount??r.currentIntervalRemainingCount,A=l.remainingRatio??(Number.isFinite(Number(_))&&Number(_)>0&&Number.isFinite(Number(c))?Number(c)/Number(_):void 0),j=l.usageRatio??(Number.isFinite(Number(_))&&Number(_)>0&&Number.isFinite(Number($))?Number($)/Number(_):void 0),F=l.resetAt||r.endAt,U=l.remainsMs??r.remainsMs,Q=Number(c),w=!i||Number.isFinite(Q)&&Q<=0?"warn":"ok",L=[i?`endpoint ${u?.endpoint||"--"}`:"quota unavailable",`fetched ${A8(u?.fetchedAt)}`,n.hit?`cache ${Er(n.ageMs)}`:"live quota"];return G("div",{className:"pipeline-minimax-quota-panel","data-testid":"pipeline-minimax-quota-panel"},G("div",{className:"metric-grid compact"},G(Xl,{label:"MiniMax",value:i?y:"--",hint:u?.modelComponent||u?.error||"model/minimax-m27",tone:w}),G(Xl,{label:"当前窗口",value:`${F9($)}/${F9(_)}`,hint:`已用 ${hW(j)}`,tone:w}),G(Xl,{label:"剩余额度",value:F9(c),hint:`剩余 ${hW(A)}`,tone:w}),G(Xl,{label:"重置时间",value:A8(F),hint:U!==void 0?`约 ${Er(U)}`:Nu(F),tone:w})),G(B9,{items:L}),i?G("p",{className:"muted paragraph"},`MiniMax 限额来自 D601 Pipeline 后端实时查询;当前模型匹配 ${l.modelName||r.modelName||y}。`):G(yf,{error:u?.error||"MiniMax 限额查询失败"}),u?G("div",{className:"panel-actions inline-actions"},G(kr,{title:"Pipeline MiniMax Quota",data:u,onOpen:f,testId:"raw-pipeline-minimax-quota"})):null)}function cS({epochs:u,activeRun:f,activePipeline:l,pipelineNodes:r,pipelineEdges:n,runDetails:i,nodeDetails:y,nodeDetailsState:_,ganttScale:$=X$,onGanttScaleChange:c,onRunChange:A,onIntervalSelect:j,onMarkerSelect:F,selection:U,detailOpen:Q,onDetailOpenChange:w,onRaw:L}){let[J,N]=kl(RY),[q,W]=kl({startY:0,endY:0,startMs:0,endMs:0}),[z,Z]=kl(Date.now()),H=Cn(null),E=String(f?.runId||""),D=Boolean(Q),h=(Au)=>{if(typeof w==="function")w(Au)},B=hn($??X$),X=String(i?.runId||"")===E?i?.details:null,m=X?{...Du(f)?f:{},...Du(X)?X:{},runId:E,procedureRuns:Vu(X?.procedureRuns).length>0?X.procedureRuns:f?.procedureRuns}:f,O=ww(m,r,z),p=X?iS(X,m):{markers:[],arrows:[]},Y=Vu(p.markers),v=CY(m,O,Y),T=kY(v,B,O,z),S=String(T.source||"frontend-y"),k=O.map((Au)=>IY(Au,T,z)),I={startMs:Number(T.startMs),endMs:Number(T.endMs),durationMs:Math.max(1,Number(T.durationMs??Number(T.endMs)-Number(T.startMs)))},b=S9(B),o={...b,pxPerMinute:Number(T.pxPerMinute??b.pxPerMinute)},s=Math.round(Number(T.chartHeight||360)),x=O.some(m9);A0(()=>{if(!E||!x)return;let Au=window.setInterval(()=>Z(Date.now()),1000);return()=>window.clearInterval(Au)},[E,x]);let fu=BY(l,r,Array.isArray(n)?n:[]),$u=r.map((Au)=>String(Au?.id||"")).filter(Boolean),tu=k.map((Au)=>String(Au.nodeId||"")).filter(Boolean),ju=Y.map((Au)=>String(Au.nodeId||"")).filter(Boolean),Gu=Array.from(new Set([...fu,...$u,...tu,...ju])),qu={startY:0,endY:s,startMs:Number(I.startMs),endMs:Number(I.endMs)},P=Number(q?.endY||0)>0?q:qu,e=(Au)=>{return N8(Au,I,s,T)<=Number(P.endY)&&Z9(Au,I,s,T)>=Number(P.startY)},uu=(Au)=>{let vu=cr(Au,I,s,T);return vu>=Number(P.startY)&&vu<=Number(P.endY)},Wu=new Set(Gu.filter((Au)=>k.some((vu)=>vu.nodeId===Au&&e(vu))||Y.some((vu)=>vu.nodeId===Au&&uu(vu)))),g=J?Gu.filter((Au)=>Wu.has(Au)):Gu,Qu=`${A9}px ${g.length>0?g.map(()=>`${t0}px`).join(" "):"minmax(160px, 1fr)"}`,Tu=Vu(T.ticks).filter(Du),Zu=String(U?.mode==="interval"?U?.interval?.procedureRunId||"":""),Xu=String(U?.mode==="event"?U?.marker?.id||"":""),ff=()=>{let Au=H.current;if(!Au){W(qu);return}let vu=Math.max(0,Au.scrollTop-j9),Kl=Math.max(120,Au.clientHeight-j9),Su=Math.min(s,vu+Kl),pf={startY:vu,endY:Su,startMs:Number(I.startMs),endMs:Number(I.endMs)},Gl=Math.max(0,Math.min(1,vu/s)),zl=Math.max(Gl,Math.min(1,Su/s)),Pf=Math.max(1,Number(I.endMs)-Number(I.startMs));pf.startMs=Number(I.startMs)+Pf*Gl,pf.endMs=Number(I.startMs)+Pf*zl,W(pf)};A0(()=>{let Au=H.current,vu=window.setTimeout(ff,0);return Au?.addEventListener("scroll",ff),window.addEventListener("resize",ff),()=>{window.clearTimeout(vu),Au?.removeEventListener("scroll",ff),window.removeEventListener("resize",ff)}},[E,I.startMs,I.endMs,s]);let _f=Math.max(0,Gu.length-g.length),Jf=new Set(Y.filter((Au)=>g.includes(String(Au.nodeId||""))&&uu(Au)).map((Au)=>String(Au.id))),mf=new Map(Y.map((Au)=>[String(Au.id),Au])),Yl=Vu(p.arrows).filter((Au)=>{if(!Jf.has(String(Au.targetMarkerId||"")))return!1;if(String(Au.action||"")==="observe")return g.includes(String(Au.sourceNodeId||""));return Jf.has(String(Au.sourceMarkerId||""))}),Al=A9+Math.max(1,g.length)*t0,jl=(Au)=>{let vu=hn(Au.target.value);if(typeof c==="function")c(vu);window.setTimeout(ff,0)},Hr=()=>pY({title:`${l?.id||"pipeline"}-${E||"epoch"}-gantt`,meta:[`run ${E||"--"}`,`${Nu(I.startMs)} -> ${Nu(I.endMs)}`,`duration ${Er(I.durationMs)}`,`${o.label} / ${N9(o.pxPerMinute)} px/min`,`${g.length}/${Gu.length} nodes`,`${Y.length} markers`],visibleNodeIds:g,intervals:k,markers:Y.filter((Au)=>g.includes(String(Au.nodeId||""))),arrows:Yl,ticks:Tu,bounds:I,chartHeight:s,backendLayout:T}),al=Du(X?.gantt?.diagnostics)?X.gantt.diagnostics:null;return G(c0,{title:"Epoch 甘特图",eyebrow:`${l?.id||"pipeline"} / ${u.length} epochs`,className:"pipeline-wide-panel",loading:i?.loading,actions:G("div",{className:"pipeline-gantt-actions"},G("select",{value:E,disabled:u.length===0,onChange:(Au)=>A(Au.target.value),"data-testid":"pipeline-epoch-select"},u.map((Au)=>G("option",{key:Au.runId,value:Au.runId},T9(u,Au)))),G("label",{className:"pipeline-gantt-toggle"},G("input",{type:"checkbox","data-testid":"pipeline-gantt-auto-hide-idle",checked:J,onChange:(Au)=>{N(Boolean(Au.target.checked)),window.setTimeout(ff,0)}}),G("span",null,"自动隐藏空闲列")),G("label",{className:"pipeline-gantt-scale"},G("span",null,G("b",null,"时间尺度"),G("em",{"data-testid":"pipeline-gantt-scale-label"},`${o.label} · ${N9(o.pxPerMinute)} px/min`)),G("input",{type:"range",min:0,max:100,step:0.01,value:B,onChange:jl,"aria-label":"调整甘特图时间尺度","data-testid":"pipeline-gantt-time-scale"}),G("small",null,G("span",null,"全局"),G("span",null,"细节"))),f?G("button",{type:"button",className:"ghost-btn",onClick:Hr,disabled:g.length===0,"data-testid":"pipeline-export-gantt"},"导出甘特图"):null,f?G(kr,{title:`Pipeline Epoch ${f.runId}`,data:f,onOpen:L,testId:"raw-pipeline-epoch-gantt"}):null)},!f?G(Ar,{title:"暂无 Epoch",text:"当前 pipeline 还没有完整运行记录。"}):k.length===0?G(Ar,{title:"暂无时间区间",text:"等待 D601 Pipeline backend 在 procedure summary 中返回 startedAt / finishedAt。"}):G("div",{className:"pipeline-gantt-wrap"},G("div",{className:`pipeline-gantt-detail-layout ${D?"detail-open":"detail-collapsed"}`,"data-testid":"pipeline-gantt-detail-layout","data-sidebar-open":D?"true":"false"},G("div",{className:"pipeline-gantt-main"},G("div",{className:"pipeline-gantt-main-head"},G("div",{className:"pipeline-gantt-meta"},G("span",null,`time ${Nu(I.startMs)} -> ${Nu(I.endMs)}`),G("span",null,`duration ${Er(I.durationMs)}`),G("span",null,`scale ${o.label} / ${N9(o.pxPerMinute)} px/min`),G("span",null,`layout ${S}`),al?G("span",null,`align ${al.timeAxisAlignmentOk===!1?"check":"ok"}`):null,G("span",null,`visible ${g.length}/${Gu.length} nodes`),X?G("span",null,`markers ${Y.length}`):null,J&&_f>0?G("span",null,`hidden idle ${_f}`):null),!D?G("button",{type:"button",className:"pipeline-sidecar-tab right",disabled:!U?.mode,onClick:()=>h(!0),"data-testid":"pipeline-gantt-sidebar-toggle"},U?.mode?"展开详情":"点击甘特图元素展开详情"):null),G("div",{className:"pipeline-gantt-viewport",ref:H,"data-testid":"pipeline-epoch-gantt","data-pipeline-id":l?.id||"","data-run-id":E,"data-layout-source":S,"data-start-ms":String(I.startMs),"data-end-ms":String(I.endMs),"data-chart-height":String(s)},G("div",{className:"pipeline-gantt-board",style:{gridTemplateColumns:Qu,minWidth:`${Al}px`}},G("div",{className:"pipeline-gantt-head time"},"Time"),g.length===0?G("div",{className:"pipeline-gantt-head empty"},"当前时间窗无工作节点"):g.map((Au)=>G("div",{key:`head-${Au}`,className:"pipeline-gantt-head node",title:Au,"data-testid":"pipeline-gantt-head-node","data-node-id":Au},G(AY,{value:Au}))),G("div",{className:"pipeline-gantt-time-axis",style:{height:`${s}px`}},Tu.map((Au)=>{let vu=Ew(Au,I,s,T);return G("div",{key:`tick-${Au.ms}-${vu}`,className:"pipeline-gantt-tick",style:{top:`${vu}px`},"data-testid":"pipeline-gantt-tick","data-ms":String(Au.ms),"data-y":String(vu)},G("b",null,Nu(Au.ms)),G("span",null,`+${Er(Number(Au.offsetMs??Number(Au.ms)-Number(I.startMs)))}`))})),g.length>0?G("svg",{className:"pipeline-gantt-arrow-layer",width:g.length*t0,height:s,viewBox:`0 0 ${g.length*t0} ${s}`,style:{left:`${A9}px`,top:`${j9}px`,width:`${g.length*t0}px`,height:`${s}px`},"aria-hidden":"true"},G("defs",null,G("marker",{id:"pipeline-gantt-arrowhead",viewBox:"0 0 10 10",refX:9,refY:5,markerWidth:6,markerHeight:6,orient:"auto-start-reverse"},G("path",{d:"M 0 0 L 10 5 L 0 10 z",fill:"context-stroke"}))),Yl.map((Au)=>{let vu=mf.get(String(Au.targetMarkerId||""));if(!vu)return null;let Kl=mf.get(String(Au.sourceMarkerId||"")),Su=String(Kl?.nodeId||Au.sourceNodeId||""),pf=g.indexOf(Su),Gl=g.indexOf(String(vu.nodeId||""));if(pf<0||Gl<0)return null;let zl=pf*t0+t0/2,Pf=Gl*t0+t0/2,rf=Kl?cr(Kl,I,s,T):cr(vu,I,s,T),Ir=cr(vu,I,s,T);return G("path",{key:Au.id,className:`pipeline-gantt-arrow ${String(Au.sourceKind||"").toLowerCase()} ${String(Au.status||"").toLowerCase()} ${String(Au.action||"").toLowerCase()}`,d:Zw(zl,rf,Pf,Ir),markerEnd:"url(#pipeline-gantt-arrowhead)","data-testid":String(Au.action||"")==="observe"?"pipeline-gantt-observation-arrow":"pipeline-gantt-arrow","data-source-node-id":String(Au.sourceNodeId||""),"data-target-node-id":String(Au.targetNodeId||""),"data-target-marker-id":String(Au.targetMarkerId||""),"data-action":String(Au.action||""),"data-source-y":String(rf),"data-target-y":String(Ir)})})):null,g.length===0?G("div",{className:"pipeline-gantt-empty-col",style:{height:`${s}px`}},"滚动到有活动的时间段后,相关 node 列会自动出现。"):g.map((Au)=>{let vu=k.filter((Su)=>Su.nodeId===Au),Kl=Y.filter((Su)=>String(Su.nodeId||"")===Au);return G("div",{key:`col-${Au}`,className:"pipeline-gantt-node-col",style:{height:`${s}px`}},vu.map((Su)=>{let pf=N8(Su,I,s,T),Gl=Z9(Su,I,s,T),zl=Tw(Su,I,s,T),Pf=String(Su.procedureRunId||`${Au}-${Su.startMs}`);return G("button",{key:Pf,type:"button",className:`pipeline-gantt-bar ${Su.status} ${Su.live?"live":""} ${Zu===Pf?"selected":""}`,style:{top:`${pf}px`,height:`${zl}px`},title:`${Au} ${Su.status} ${Nu(Su.startedAt||Su.startMs)} -> ${Nu(Su.finishedAt||Su.endMs)}`,onClick:()=>j(Su),"data-testid":"pipeline-gantt-line","data-node-id":Au,"data-procedure-run-id":String(Su.procedureRunId||""),"data-status":String(Su.status||""),"data-live":Su.live?"true":"false","data-start-ms":String(Su.startMs||""),"data-end-ms":String(Su.endMs||""),"data-y1":String(pf),"data-y2":String(Gl),"data-natural-height":String(Math.max(0,Gl-pf))},G("strong",null,Su.status||"working"),G("span",null,Er(Su.durationMs)))}),Kl.map((Su)=>G("button",{key:Su.id,type:"button",className:`pipeline-gantt-marker ${Su.kind} ${Su.tone||""} ${Su.status||""} ${Xu===String(Su.id)?"selected":""}`,style:{top:`${cr(Su,I,s,T)}px`},title:`${Su.label||"event"} / ${Nu(Su.timestampIso||Su.timestamp||Su.ms)}`,onClick:()=>F(Su),"data-testid":Su.kind==="prompt"?"pipeline-gantt-prompt-marker":"pipeline-gantt-control-marker","data-marker-id":String(Su.id||""),"data-ms":String(Su.ms??Su.eventMs??""),"data-y":String(cr(Su,I,s,T))})))})))),D?G(cY,{selection:U,runDetails:i,nodeDetails:y,nodeDetailsState:_,onRaw:L,onCollapse:()=>h(!1)}):null)))}function R0(){return{loading:!1,actionLoading:"",error:"",message:"",details:null,fetchedAt:null,appendPrompt:"",guidePrompt:"",modifyPrompt:"",approveReason:"",redoReason:""}}function Mn(){return{mode:"",runId:"",interval:null,marker:null}}function W9(){return{runId:"",loading:!1,error:"",details:null,fetchedAt:null}}function E$(u,f){return`${u}/microservices/pipeline/proxy${f}`}function AS({activeRun:u,pipelineRuns:f,selectedRunId:l,onRunChange:r,selectedNodeId:n,selectedNodeConfig:i,selectedNodeRuntime:y,control:_,onControlChange:$,onFetch:c,onAction:A,onRaw:j,onCollapse:F}){let U=String(u?.runId||""),Q=String(y?.status||"pending"),w=!U||!n||_.loading||Boolean(_.actionLoading),L=(N)=>(q)=>$({[N]:q.target.value,error:"",message:""}),J=f.length>0?f:u?[u]:[];return G("aside",{className:"pipeline-node-control","data-testid":"pipeline-node-control"},G("div",{className:"pipeline-node-control-head"},G("div",null,G("p",{className:"panel-eyebrow"},"Manual Node Control"),G(nf,{title:n||"点击控制图中的 node",level:3,loading:_.loading||Boolean(_.actionLoading)})),G("div",{className:"pipeline-node-control-head-actions"},n?G(xn,{status:Q},Q):G(xn,{status:"pending"},"idle"),G("button",{type:"button",className:"ghost-btn mini",onClick:F,"data-testid":"pipeline-node-sidebar-collapse"},"收起"))),G("div",{className:"pipeline-control-runbar"},G("label",null,G("span",null,"目标 run"),G("select",{value:U||l,disabled:J.length===0,onChange:(N)=>r(N.target.value),"data-testid":"pipeline-node-run-select"},J.map((N)=>G("option",{key:N.runId,value:N.runId},`${N.runId||"--"} / ${N.status||"--"}`)))),G("button",{type:"button",className:"ghost-btn",disabled:w,onClick:c,"data-testid":"pipeline-node-fetch"},_.loading?"抓取中":"抓取过程"),_.details?G(kr,{title:`Pipeline Node ${n}`,data:_.details,onOpen:j,testId:"raw-pipeline-node-control"}):null),G("div",{className:"pipeline-control-meta"},G("span",null,G("b",null,"kind"),String(i?.kind||"--")),G("span",null,G("b",null,"procedure"),String(y?.currentProcedureRunId||"--")),G("span",null,G("b",null,"attempts"),String(y?.attempts??"--")),G("span",null,G("b",null,"updated"),Nu(u?.updatedAt))),!n?G(Ar,{title:"未选择 node",text:"点击 React Flow 控制图中的任意 node 后,可抓取执行过程、追加 prompt、下发引导、增量修改、审核通过或重做。"}):null,G(yf,{error:_.error,wide:!0}),G("div",{className:"pipeline-control-actions"},G("label",null,G("span",null,"实时追加 prompt(仅 running node)"),G("textarea",{value:_.appendPrompt,onChange:L("appendPrompt"),placeholder:"让当前执行中的 agent 继续、补充检查或调整当前步骤...",rows:4,disabled:!n,"data-testid":"pipeline-node-append-input"}),G("button",{type:"button",className:"primary-btn compact",disabled:w||!String(_.appendPrompt||"").trim(),onClick:()=>A("append"),"data-testid":"pipeline-node-append-button"},_.actionLoading==="append"?"追加中":"追加到运行中 node")),G("label",null,G("span",null,"下次尝试引导 prompt"),G("textarea",{value:_.guidePrompt,onChange:L("guidePrompt"),placeholder:"给该 node 下一次 attempt 的执行提示;不会立即打断当前 session。",rows:4,disabled:!n,"data-testid":"pipeline-node-guide-input"}),G("button",{type:"button",className:"ghost-btn compact",disabled:w||!String(_.guidePrompt||"").trim(),onClick:()=>A("guide"),"data-testid":"pipeline-node-guide-button"},_.actionLoading==="guide"?"下发中":"下发 guide")),G("label",null,G("span",null,"完成后增量修改 prompt"),G("textarea",{value:_.modifyPrompt,onChange:L("modifyPrompt"),placeholder:"在该 node 已完成结果基础上追加修改要求;runner 会重跑目标 node,并保留同 node 既有 OA 输出作为上下文。",rows:4,disabled:!n,"data-testid":"pipeline-node-modify-input"}),G("button",{type:"button",className:"ghost-btn compact",disabled:w||!String(_.modifyPrompt||"").trim(),onClick:()=>A("modify"),"data-testid":"pipeline-node-modify-button"},_.actionLoading==="modify"?"排队中":"增量修改 node")),G("label",null,G("span",null,"Monitor 审核通过原因"),G("textarea",{value:_.approveReason,onChange:L("approveReason"),placeholder:"当流程配置开启 monitor 审核时,记录审核通过原因并释放后续 node。",rows:3,disabled:!n,"data-testid":"pipeline-node-approve-input"}),G("button",{type:"button",className:"primary-btn compact",disabled:w||!String(_.approveReason||"").trim(),onClick:()=>A("approve"),"data-testid":"pipeline-node-approve-button"},_.actionLoading==="approve"?"提交中":"审核通过")),G("label",null,G("span",null,"重做 / restart 原因"),G("textarea",{value:_.redoReason,onChange:L("redoReason"),placeholder:"说明为什么需要重做;runner 会重置目标 node 以及非 rework 下游 node。",rows:4,disabled:!n,"data-testid":"pipeline-node-redo-input"}),G("button",{type:"button",className:"danger-btn compact",disabled:w||!String(_.redoReason||"").trim(),onClick:()=>A("redo"),"data-testid":"pipeline-node-redo-button"},_.actionLoading==="redo"?"排队中":"重做 node"))),G("div",{className:"pipeline-control-evidence"},G("strong",null,"Node 过程索引"),G(yS,{details:_.details,selectedNodeId:n,selectedNodeRuntime:y,control:_,onRaw:j})))}function Ow({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((lu)=>lu.id==="pipeline")||null,[n,i]=kl({loading:!1,error:"",health:null,snapshot:null,oaDiagnostics:null,minimaxQuota:null,refreshedAt:null}),[y,_]=kl(""),[$,c]=kl(""),[A,j]=kl(""),[F,U]=kl(R0()),[Q,w]=kl({}),[L,J]=kl(Mn()),[N,q]=kl(W9()),[W,z]=kl(X$),[Z,H]=kl(!1),[E,D]=kl(!1),h=Cn(0),{addNotification:B}=Bl(),X=Cn(!1),m=Cn(0),O=Cn(""),p=Cn({}),Y=Cn(""),v=Cn("");async function T(lu={}){let Ou=lu.silent===!0;if(!r)return;if(X.current)return;X.current=!0;let mu=h.current+1;if(h.current=mu,!Ou)i((ou)=>({...ou,loading:!0,error:""}));try{let ou=`__unideskArrayLimit=registry.components:80,runs:${IX}`,[Af,vf,pu]=await Promise.all([Pn(`${l}/microservices/pipeline/proxy/api/snapshot?${ou}`,{cache:"no-store"}),Pn(`${l}/microservices/pipeline/proxy/api/oa-event-flow/diagnostics`,{cache:"no-store"}).catch((J0)=>({ok:!1,error:Hu(J0,"OA event flow diagnostics failed")})),Pn(`${l}/microservices/pipeline/proxy/api/model-quota/minimax`,{cache:"no-store"}).catch((J0)=>({ok:!1,error:Hu(J0,"MiniMax quota failed")}))]);if(mu!==h.current)return;let ll={ok:Af?.ok!==!1,service:"pipeline-v2-control snapshot"};i({loading:!1,error:"",health:ll,snapshot:Af,oaDiagnostics:vf,minimaxQuota:pu,refreshedAt:new Date})}catch(ou){if(mu!==h.current)return;i((Af)=>({...Af,loading:!1,error:Hu(ou,"Pipeline 加载失败")}))}finally{X.current=!1}}A0(()=>{if(T(),!r)return;let lu=()=>{if(y8())T({silent:!0})},Ou=window.setInterval(()=>{lu()},CW),mu=()=>{if(y8())lu()};return document.addEventListener("visibilitychange",mu),()=>{window.clearInterval(Ou),document.removeEventListener("visibilitychange",mu)}},[r?.id,r?.runtime?.providerStatus,l]);let S=jY(r),k=JY(r),I=FY(r),b=n.snapshot||{},o=n.oaDiagnostics||null,s=n.minimaxQuota||null,{components:x,pipelines:fu,runs:$u}=UY(b),tu=String($u[0]?.pipelineId||""),ju=(tu?fu.find((lu)=>String(lu.id||"")===tu):null)||fu[0]||{},Gu=fu.find((lu)=>String(lu.id||"")===y)||ju,qu=String(Gu.id||""),P=Uw(Gu),e=D9(Gu),uu=lw($u,qu),Wu=MY($u,qu),g=Wu.find((lu)=>String(lu?.runId||"")===$)||uu,Qu=String(N.runId||"")===String(g?.runId||"")?WY(N.details):null,Tu=wY(g,Qu),Zu=String(Tu?.runId||""),Xu=P.find((lu)=>String(lu?.id||"")===A)||null,ff=A?Qw(Tu,A):null,_f=qY($u),Jf=zY(x),mf=Number(n.health?.components)||sW(b,"registry.components",x.length),Yl=sW(b,"runs",$u.length),Al=dW(Gu,Tu,x),jl={nodes:Al.nodes.map((lu)=>lu.id===A?{...lu,selected:!0,className:`${lu.className||""} selected-control-node`}:lu),edges:Al.edges},Hr=fu.map((lu)=>{let Ou=String(lu.id||"pipeline"),mu=lw($u,Ou);return{title:`${Ou}-${mu?.runId||"snapshot"}`,flow:dW(lu,mu,x)}}),al=String(L?.runId||Zu||""),Au=String(L?.interval?.nodeId||L?.marker?.nodeId||""),vu=al&&Au?Q[q9(al,Au)]||null:null,Kl=F8(F.details,al,Au),Su=F8(vu?.details,al,Au)||Kl,pf=al&&Au?{...Du(vu)?vu:{},runId:al,nodeId:Au,details:Su,loading:Boolean(vu?.loading)||!Su&&Boolean(F.loading)&&A===Au,error:String(vu?.error||""),fetchedAt:vu?.fetchedAt||(Kl?F.fetchedAt:null)}:null,Gl=Wu.map((lu)=>String(lu?.runId||"")).filter(Boolean).join("|"),zl=P.map((lu)=>String(lu?.id||"")).filter(Boolean).join("|");A0(()=>{Y.current=A},[A]),A0(()=>{v.current=Zu},[Zu]),A0(()=>{if(!$||Gl.split("|").includes($))return;c("")},[$,Gl]),A0(()=>{if(!A||zl.split("|").includes(A))return;j(""),U(R0()),J(Mn()),H(!1),D(!1)},[A,zl]),A0(()=>{if(!A)H(!1)},[A]),A0(()=>{if(!L.mode)D(!1)},[L.mode]);async function Pf(lu=Zu,Ou={}){if(!lu){q(W9());return}let mu=hn(Ou.scale??W??X$),ou=`${lu}:timeline`;if(O.current===ou)return;O.current=ou;let Af=Ou.silent===!0,vf=m.current+1;m.current=vf,q((pu)=>({runId:lu,scale:mu,loading:!Af||String(pu.runId||"")!==lu||!pu.details,error:"",details:Af&&pu.runId===lu?pu.details:pu.runId===lu?pu.details:null,fetchedAt:pu.runId===lu?pu.fetchedAt:null}));try{let[pu,ll]=await Promise.all([Pn(E$(l,`/api/node-control/runs/${encodeURIComponent(lu)}?tail=160&view=timeline`),{cache:"no-store",strictJson:!0}),Pn(E$(l,`/api/runs/${encodeURIComponent(lu)}`),{cache:"no-store"}).catch((J0)=>({ok:!1,runSummaryError:Hu(J0,"抓取评分失败")}))]);if(vf!==m.current)return;q({runId:lu,scale:mu,loading:!1,error:"",details:{...pu,run:Du(ll?.run)?ll.run:void 0,runSummaryError:ll?.runSummaryError},fetchedAt:new Date})}catch(pu){if(vf!==m.current)return;q((ll)=>({runId:lu,scale:mu,loading:!1,error:Hu(pu,"抓取 epoch 执行过程失败"),details:ll.runId===lu?ll.details:null,fetchedAt:ll.runId===lu?ll.fetchedAt:null}))}finally{if(O.current===ou)O.current=""}}function rf(lu,Ou,mu){let ou=q9(lu,Ou);w((Af)=>{let vf={...Af,[ou]:{...Du(Af?.[ou])?Af[ou]:{},runId:lu,nodeId:Ou,...mu}},pu=Object.keys(vf);if(pu.length>32)for(let ll of pu.slice(0,pu.length-32))delete vf[ll];return vf})}async function Ir(lu,Ou){if(!lu||!Ou)return;let mu=q9(lu,Ou),ou=Number(p.current?.[mu]||0)+1;p.current={...p.current,[mu]:ou},rf(lu,Ou,{loading:!0,error:""});try{let Af=await Pn(E$(l,`/api/node-control/runs/${encodeURIComponent(lu)}/nodes/${encodeURIComponent(Ou)}?tail=160`),{cache:"no-store",strictJson:!0});if(Number(p.current?.[mu]||0)!==ou)return;let vf=new Date;if(rf(lu,Ou,{loading:!1,details:Af,fetchedAt:vf,error:""}),Y.current===Ou&&v.current===lu)U((pu)=>({...pu,loading:!1,details:Af,fetchedAt:vf,error:""}))}catch(Af){if(Number(p.current?.[mu]||0)!==ou)return;rf(lu,Ou,{loading:!1,error:Hu(Af,"抓取 Gantt node 详情失败")})}}A0(()=>{if(!Zu){q(W9());return}Pf(Zu);let lu=()=>{if(y8())Pf(Zu,{silent:!0})},Ou=window.setInterval(()=>{lu()},CW),mu=()=>{if(y8())lu()};return document.addEventListener("visibilitychange",mu),()=>{window.clearInterval(Ou),document.removeEventListener("visibilitychange",mu)}},[Zu,l]);async function Fr(lu=Zu,Ou=A){if(!lu||!Ou){U((mu)=>({...mu,error:"请先选择 run 和 node",message:""}));return}U((mu)=>({...mu,loading:!0,error:"",message:""}));try{let mu=await Pn(E$(l,`/api/node-control/runs/${encodeURIComponent(lu)}/nodes/${encodeURIComponent(Ou)}?tail=160`),{cache:"no-store",strictJson:!0}),ou=new Date;U((Af)=>({...Af,loading:!1,details:mu,fetchedAt:ou,error:""})),rf(lu,Ou,{loading:!1,details:mu,fetchedAt:ou,error:""})}catch(mu){U((ou)=>({...ou,loading:!1,error:Hu(mu,"抓取 node 执行过程失败")}))}}async function x1(lu){let Ou=String(lu?.runId||Zu||""),mu=String(lu?.nodeId||"");if(J({mode:"interval",runId:Ou,interval:lu,marker:null}),D(!0),!Ou||!mu)return;if(Ou!==Zu)c(Ou);j(mu),U(R0()),Pf(Ou,{silent:!0}),Ir(Ou,mu)}async function gn(lu){let Ou=String(lu?.runId||Zu||""),mu=String(lu?.nodeId||"");if(J({mode:"event",runId:Ou,interval:null,marker:lu}),D(!0),!Ou)return;if(Ou!==Zu)c(Ou);if(Pf(Ou,{silent:!0}),!mu)return;j(mu),U(R0()),Ir(Ou,mu)}async function b0(lu){if(!Zu||!A){U((ou)=>({...ou,error:"请先选择 run 和 node",message:""}));return}let Ou=lu==="append"?"prompts":lu,mu=lu==="append"?F.appendPrompt:lu==="guide"?F.guidePrompt:lu==="modify"?F.modifyPrompt:lu==="approve"?F.approveReason:F.redoReason;if(!String(mu||"").trim()){U((ou)=>({...ou,error:"操作内容不能为空",message:""}));return}U((ou)=>({...ou,actionLoading:lu,error:"",message:""}));try{let ou=lu==="redo"||lu==="approve"?{reason:mu,source:"unidesk-frontend",sourceKind:"webui"}:{prompt:mu,source:"unidesk-frontend",sourceKind:"webui"},Af=await Pn(E$(l,`/api/node-control/runs/${encodeURIComponent(Zu)}/nodes/${encodeURIComponent(A)}/${Ou}`),{method:"POST",body:JSON.stringify(ou)});if(U((pu)=>({...pu,actionLoading:"",details:Af,fetchedAt:new Date,appendPrompt:lu==="append"?"":pu.appendPrompt,guidePrompt:lu==="guide"?"":pu.guidePrompt,modifyPrompt:lu==="modify"?"":pu.modifyPrompt,approveReason:lu==="approve"?"":pu.approveReason,redoReason:lu==="redo"?"":pu.redoReason,message:lu==="append"?"已追加到运行中 node":lu==="guide"?"已下发 guide,等待 runner 处理":lu==="modify"?"已排队增量修改命令":lu==="approve"?"已提交审核通过决策":"已排队重做命令"})),B("success",lu==="append"?"已追加到运行中 node":lu==="guide"?"已下发 guide,等待 runner 处理":lu==="modify"?"已排队增量修改命令":lu==="approve"?"已提交审核通过决策":"已排队重做命令"),await Fr(Zu,A),await Pf(Zu,{silent:!0}),lu!=="append")await T()}catch(ou){U((Af)=>({...Af,actionLoading:"",error:Hu(ou,"node 控制操作失败")}))}}if(!r)return G(Ar,{title:"Pipeline 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=pipeline"});return G("div",{className:"pipeline-page","data-testid":"pipeline-page"},G(c0,{title:"Pipeline v2 工作台",eyebrow:"D601 Snapshot 用户服务",loading:n.loading,actions:G("div",{className:"panel-actions"},G("button",{type:"button",className:"ghost-btn",onClick:T,disabled:n.loading,"data-testid":"pipeline-refresh-button"},n.loading?"刷新中":"刷新"),G(kr,{title:"Pipeline 用户服务",data:r,onOpen:f,testId:"raw-pipeline-service"}))},G("div",{className:"pipeline-hero"},G("div",null,G("div",{className:"node-version-line"},G(xn,{status:S.providerStatus==="online"?"online":"warn"},S.providerStatus||"unknown"),G("span",null,r.providerId),G("span",null,I.public?"公网暴露":"仅 UniDesk frontend 代理访问")),G("p",{className:"muted paragraph"},r.description)),G("div",{className:"microservice-ref-card"},G("span",null,"Repo"),G("strong",null,k.url||"--"),G("code",null,k.commitId||"--")),G("div",{className:"microservice-ref-card"},G("span",null,"D601 Docker"),G("strong",null,`${I.nodeBindHost||"--"}:${I.nodePort||"--"}`),G("code",null,`${k.composeFile||"--"} / ${k.composeService||"--"}`))),G(yf,{error:n.error,wide:!0})),G("div",{className:"pipeline-grid"},G(c0,{title:"控制图",eyebrow:`${Gu.id||"pipeline"} / run ${Tu?.status||"--"}`,className:"pipeline-wide-panel",loading:n.loading,actions:G("div",{className:"pipeline-toolbar"},G("select",{value:qu,disabled:fu.length===0,onChange:(lu)=>{_(lu.target.value),c(""),j(""),U(R0()),J(Mn()),H(!1),D(!1)},"data-testid":"pipeline-select"},fu.map((lu)=>G("option",{key:lu.id,value:lu.id},lu.id||lu.key))),G("select",{value:Zu,disabled:Wu.length===0,onChange:(lu)=>{if(c(lu.target.value),U(R0()),J(Mn()),H(!1),D(!1),A)Fr(lu.target.value,A)},"data-testid":"pipeline-run-select"},Wu.map((lu)=>G("option",{key:lu.runId,value:lu.runId},T9(Wu,lu)))),G("button",{type:"button",className:"ghost-btn",disabled:jl.nodes.length===0,onClick:()=>Ww(jl,`${Gu.id||"pipeline"}-${Tu?.runId||"snapshot"}`),"data-testid":"pipeline-export-graph"},"导出渲染图"),G("button",{type:"button",className:"ghost-btn",disabled:Hr.every((lu)=>lu.flow.nodes.length===0),onClick:()=>PY(Hr),"data-testid":"pipeline-export-all-graphs"},"批量导出"))},P.length===0?G(Ar,{title:"暂无控制图",text:"等待 D601 pipeline backend 返回 config.nodes / config.edges"}):G("div",{className:`pipeline-control-shell ${Z?"detail-open":"detail-collapsed"}`,"data-testid":"pipeline-control-shell","data-sidebar-open":Z?"true":"false"},G("div",{className:"pipeline-flow-frame","data-testid":"pipeline-react-flow"},G(XW,{nodes:jl.nodes,edges:jl.edges,nodeTypes:dX,edgeTypes:oX,fitView:!0,fitViewOptions:{padding:0.18},nodesDraggable:!1,nodesConnectable:!1,elementsSelectable:!0,minZoom:0.25,maxZoom:1.4,proOptions:{hideAttribution:!0},onNodeClick:(lu,Ou)=>{let mu=String(Ou.id);if(j(mu),U(R0()),H(!0),Zu)Fr(Zu,mu)}},G(SW,{gap:22,size:1,color:"rgba(215, 161, 58, 0.24)"}),G(pW,{showInteractive:!1})),!Z?G("button",{type:"button",className:"pipeline-sidecar-tab right",disabled:!A,onClick:()=>H(!0),"data-testid":"pipeline-node-sidebar-toggle"},A?"展开 node 控制":"点击 node 展开控制"):null),Z?G(AS,{activeRun:Tu,pipelineRuns:Wu,selectedRunId:$,onRunChange:(lu)=>{if(c(lu),U(R0()),J(Mn()),A)Fr(lu,A)},selectedNodeId:A,selectedNodeConfig:Xu,selectedNodeRuntime:ff,control:F,onControlChange:(lu)=>U((Ou)=>({...Ou,...lu})),onFetch:()=>Fr(),onAction:b0,onRaw:f,onCollapse:()=>H(!1)}):null),G("div",{className:"pipeline-flow-summary"},G("span",null,`${jl.nodes.length} nodes`),G("span",null,`${jl.edges.length} edges`),G("span",null,`${fu.length} pipelines`),G("span",null,`source config+components(${x.length})`),G("span",null,`run ${Tu?.runId||"--"}`),G("span",null,`score ${z9(Tu)}`),G("span",null,A?`selected ${A}`:"click node to control"))),G(cS,{epochs:Wu,activeRun:Tu,activePipeline:Gu,pipelineNodes:P,pipelineEdges:e,selection:L,detailOpen:E,onDetailOpenChange:D,runDetails:N,nodeDetails:Su,nodeDetailsState:pf,ganttScale:W,onGanttScaleChange:z,onIntervalSelect:x1,onMarkerSelect:gn,onRunChange:(lu)=>{if(c(lu),U(R0()),J(Mn()),D(!1),A)Fr(lu,A)},onRaw:f}),G(c0,{title:"观测指标",eyebrow:n.refreshedAt?`Updated ${$f(n.refreshedAt)}`:"Snapshot",loading:n.loading},G("div",{className:"metric-grid"},G(Xl,{label:"Health",value:n.health?.ok?"OK":"--",hint:n.health?.service||"D601 /health",tone:n.health?.ok?"ok":"warn"}),G(Xl,{label:"组件",value:mf,hint:"components registry",tone:b?.registry?.ok===!1?"warn":"ok"}),G(Xl,{label:"Pipeline",value:fu.length,hint:`${P.length} nodes / ${e.length} edges`}),G(Xl,{label:"运行记录",value:Yl,hint:`${_f.succeeded||0} succeeded / ${_f.running||0} running`}),G(Xl,{label:"OA 记录",value:Array.isArray(uu?.submissions)?uu.submissions.length:0,hint:uu?.runId||"latest run"}),G(Xl,{label:"Procedure",value:Array.isArray(uu?.procedureRuns)?uu.procedureRuns.length:0,hint:uu?.status||"no run"}),G(Xl,{label:"Score",value:z9(Tu),hint:Tu?.runId||"selected epoch",tone:Y9(Tu)})),G("div",{className:"panel-actions inline-actions"},G(kr,{title:"Pipeline Snapshot",data:b,onOpen:f,testId:"raw-pipeline-snapshot"}))),G(c0,{title:"评分器",eyebrow:Tu?.runId||"selected epoch",loading:n.loading},G(GY,{run:Tu,onRaw:f})),G(c0,{title:"MiniMax 限额",eyebrow:"model/minimax-m27 quota",loading:n.loading},G(tS,{quota:s,onRaw:f})),G(c0,{title:"OA 事件流",eyebrow:"100% event-driven diagnostics",className:"pipeline-wide-panel",loading:n.loading},G($S,{diagnostics:o,onRaw:f})),G(c0,{title:"组件矩阵",eyebrow:`${Jf.length} classes`,loading:n.loading},Jf.length===0?G(Ar,{title:"暂无组件",text:"等待 D601 pipeline backend 返回 registry.components"}):G("div",{className:"component-strata"},Jf.map((lu)=>G("article",{key:lu.name,className:"component-stratum"},G("span",null,lu.name),G("strong",null,lu.count)))),G("div",{className:"pipeline-component-list"},x.slice(0,12).map((lu)=>G("span",{key:lu.key,className:"data-chip"},G("b",null,lu.componentClass||"--"),G("span",null,lu.id||lu.key||"--"))))),G(c0,{title:"Epoch 列表",eyebrow:`${Wu.length}/${Yl} preview`,loading:n.loading},Wu.length===0?G(Ar,{title:"暂无运行记录",text:"当前 pipeline 在 .state/pipeline-runs 中还没有 epoch。"}):G("div",{className:"pipeline-run-list"},Wu.map((lu)=>{let Ou=String(lu?.runId||"")===Zu?Tu:lu;return G("article",{key:lu.runId,className:`pipeline-run-card ${String(lu.runId||"")===Zu?"active":""}`,role:"button",tabIndex:0,onClick:()=>{c(String(lu.runId||"")),J(Mn())},onKeyDown:(mu)=>{if(mu.key==="Enter"||mu.key===" ")c(String(lu.runId||"")),J(Mn())}},G("div",{className:"node-card-head"},G("strong",null,T9(Wu,lu)),G(xn,{status:lu.status},lu.status||"--")),G("div",{className:"docker-meta compact"},G("span",null,Ou?.pipelineId||"--"),G("span",null,`nodes ${Array.isArray(Ou?.nodes)?Ou.nodes.length:0}`),G("span",null,`oa ${Array.isArray(Ou?.submissions)?Ou.submissions.length:0}`),G("span",null,`procedures ${Array.isArray(Ou?.procedureRuns)?Ou.procedureRuns.length:0}`),G(KY,{run:Ou})),G("p",{className:"muted paragraph"},j8(Ou?.task)),G("span",{className:"pipeline-run-time"},Nu(Ou?.updatedAt)))}))),G(c0,{title:"运行材料索引",eyebrow:Tu?.runId||"selected epoch",className:"pipeline-wide-panel",loading:n.loading},G(_S,{activeRun:Tu,onRaw:f}))))}var z8=Mu(Qf(),1);var _u=z8.default.createElement,{useEffect:jS}=z8.default,K8=z8.default.useState,P9={id:"",sequenceNo:"",contractNo:"",name:"",currentStatus:"",pending:"",paymentStatus:"",notes:""};function FS({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return _u("span",{className:`status-badge ${l}`},f||u||"unknown")}function G8({label:u,value:f,hint:l,tone:r}){return _u("article",{className:`metric-card ${r||""}`},_u("div",{className:"metric-label"},u),_u("div",{className:"metric-value"},f),_u("div",{className:"metric-hint"},l))}function M9({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return _u("section",{className:`panel ${n||""}`},_u("div",{className:"panel-head"},_u("div",null,f?_u("p",{className:"panel-eyebrow"},f):null,_u(nf,{title:u,loading:i})),l?_u("div",{className:"panel-actions"},l):null),_u("div",{className:"panel-body"},r))}function Hw({title:u,data:f,onOpen:l,testId:r}){return _u("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function Bw({title:u,text:f}){return _u("div",{className:"empty-state"},_u("strong",null,u),_u("span",null,f))}function JS(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function US(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function QS(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function Uy(u,f){return`${u}/microservices/project-manager/proxy${f}`}function qS(u){return{id:String(u.id||""),sequenceNo:u.sequenceNo===null||u.sequenceNo===void 0?"":String(u.sequenceNo),contractNo:String(u.contractNo||""),name:String(u.name||""),currentStatus:String(u.currentStatus||""),pending:String(u.pending||""),paymentStatus:String(u.paymentStatus||""),notes:String(u.notes||"")}}function NS(u){return{sequenceNo:u.sequenceNo===""?null:Number(u.sequenceNo),contractNo:String(u.contractNo||"").trim(),name:String(u.name||"").trim(),currentStatus:String(u.currentStatus||"").trim(),pending:String(u.pending||"").trim(),paymentStatus:String(u.paymentStatus||"").trim(),paymentRatio:String(u.paymentStatus||"").trim(),notes:String(u.notes||"").trim()}}function C9(u){return String(u||"item").replace(/[^A-Za-z0-9_-]+/g,"-")}function WS(u){let f=new Uint8Array(u),l="",r=32768;for(let n=0;n_u("tr",{key:n.id,className:f===n.id?"active-row":"","data-testid":`project-manager-row-${C9(n.id)}`},_u("td",null,n.sequenceNo??"--"),_u("td",null,_u("strong",null,n.contractNo||"--"),_u("code",null,n.id||"--")),_u("td",null,_u("strong",null,n.name||"--"),_u("span",{className:"muted block"},n.sourceFile||"--")),_u("td",null,n.currentStatus||"--"),_u("td",null,_u("span",{className:"preline"},n.pending||"--")),_u("td",null,_u(FS,{status:Number(n.paymentRatio||0)>=1?"online":"warn"},n.paymentStatus||"--")),_u("td",null,n.notes||"--"),_u("td",null,_u("div",{className:"inline-actions"},_u("button",{type:"button",className:"ghost-btn",onClick:()=>l(n),"data-testid":`project-manager-edit-${C9(n.id)}`},"编辑"),_u(Hw,{title:`Project ${n.contractNo||n.id}`,data:n,onOpen:r,testId:`raw-project-${C9(n.id)}`}))))))))}function Vw({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((E)=>E.id==="project-manager")||null,[n,i]=K8({loading:!1,saving:!1,importing:!1,exporting:!1,error:"",notice:"",health:null,list:null,refreshedAt:null}),[y,_]=K8({...P9}),[$,c]=K8(""),[A,j]=K8("all"),{addNotification:F}=Bl();async function U(E=$,D=A){if(!r)return;i((h)=>({...h,loading:!0,error:""}));try{let h=new URLSearchParams({pageSize:"200",status:D});if(E.trim())h.set("q",E.trim());let[B,X]=await Promise.all([Eu(`${l}/microservices/project-manager/health`),Eu(Uy(l,`/api/projects?${h.toString()}`))]);i((m)=>({...m,loading:!1,health:B,list:X,refreshedAt:new Date,error:""}))}catch(h){i((B)=>({...B,loading:!1,error:Hu(h,"Project Manager 加载失败")}))}}jS(()=>{U()},[r?.id,r?.runtime?.providerStatus]);async function Q(E){E.preventDefault(),i((D)=>({...D,saving:!0,error:"",notice:""}));try{let D=NS(y);if(y.id)await Eu(Uy(l,`/api/projects/${encodeURIComponent(y.id)}`),{method:"PUT",body:JSON.stringify(D)});else await Eu(Uy(l,"/api/projects"),{method:"POST",body:JSON.stringify(D)});let h=y.id?"项目已更新":"项目已创建";i((B)=>({...B,saving:!1,notice:h})),F("success",h),await U()}catch(D){i((h)=>({...h,saving:!1,error:Hu(D,"保存项目失败")}))}}async function w(){if(!y.id)return;if(!window.confirm(`删除项目 ${y.contractNo||y.name||y.id} ?`))return;i((E)=>({...E,saving:!0,error:"",notice:""}));try{await Eu(Uy(l,`/api/projects/${encodeURIComponent(y.id)}`),{method:"DELETE"}),_({...P9});let E="项目已删除";i((D)=>({...D,saving:!1,notice:E})),F("success",E),await U()}catch(E){i((D)=>({...D,saving:!1,error:Hu(E,"删除项目失败")}))}}async function L(E){let D=E.target.files?.[0];if(!D)return;i((h)=>({...h,importing:!0,error:"",notice:""}));try{let h=WS(await D.arrayBuffer()),X=`Excel 已导入 ${(await Eu(Uy(l,"/api/import/excel"),{method:"POST",body:JSON.stringify({fileName:D.name,contentBase64:h,replace:!1})})).imported||0} 条项目`;i((m)=>({...m,importing:!1,notice:X})),F("success",X),E.target.value="",await U()}catch(h){i((B)=>({...B,importing:!1,error:Hu(h,"Excel 导入失败")}))}}async function J(){i((E)=>({...E,exporting:!0,error:""}));try{let E=await dJ(Uy(l,"/api/projects/export.xlsx")),D=URL.createObjectURL(E),h=document.createElement("a");h.href=D,h.download=`project-manager-${B7()}.xlsx`,document.body.appendChild(h),h.click(),h.remove(),URL.revokeObjectURL(D),i((B)=>({...B,exporting:!1,notice:"Excel 已导出"}))}catch(E){i((D)=>({...D,exporting:!1,error:Hu(E,"Excel 导出失败")}))}}if(!r)return _u(Bw,{title:"Project Manager 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=project-manager"});let N=JS(r),q=QS(r),W=US(r),z=Array.isArray(n.list?.projects)?n.list.projects:[],Z=n.list?.summary||{},H=n.health||{};return _u("div",{className:"project-manager-page","data-testid":"project-manager-page"},_u(M9,{title:"项目管理工作台",eyebrow:"Main Server PostgreSQL 用户服务",loading:n.loading||n.exporting,actions:_u("div",{className:"panel-actions"},_u("button",{type:"button",className:"ghost-btn",disabled:n.loading,onClick:()=>U(),"data-testid":"project-manager-refresh-button"},n.loading?"刷新中":"刷新"),_u("button",{type:"button",className:"ghost-btn",disabled:n.exporting,onClick:J,"data-testid":"project-manager-export-button"},n.exporting?"导出中":"导出 Excel"),_u(Hw,{title:"Project Manager 用户服务",data:r,onOpen:f,testId:"raw-project-manager-service"}))},_u("div",{className:"project-manager-hero"},_u(G8,{label:"项目总数",value:Z.total??z.length,hint:`PG 表 ${H.storage?.table||"project_manager_projects"}`,tone:"ok"}),_u(G8,{label:"进行中",value:Z.active??"--",hint:"当前状态未完全完成"}),_u(G8,{label:"已完成",value:Z.completed??"--",hint:"按 完成 关键字统计",tone:"ok"}),_u(G8,{label:"未全款",value:Z.unpaid??"--",hint:"付款比例 < 1",tone:Number(Z.unpaid||0)>0?"warn":"ok"})),_u(yf,{error:n.error}),n.notice?_u("div",{className:"form-success"},n.notice):null),_u("div",{className:"project-manager-hero"},_u("div",{className:"microservice-ref-card"},_u("span",null,"Repo"),_u("strong",null,q.url||"--"),_u("code",null,q.commitId||"--")),_u("div",{className:"microservice-ref-card"},_u("span",null,"Main Server Docker"),_u("strong",null,`${W.nodeBindHost||"--"}:${W.nodePort||"--"}`),_u("code",null,`${q.composeService||"--"} / ${q.containerName||"--"}`)),_u("div",{className:"microservice-ref-card"},_u("span",null,"Runtime"),_u("strong",null,N.providerName||r.providerId),_u("code",null,`Health ${H.ok?"OK":"--"} / ${n.refreshedAt?$f(n.refreshedAt):"--"}`)),_u("div",{className:"microservice-ref-card"},_u("span",null,"Import Source"),_u("strong",null,"D601 WeChat Excel"),_u("code",null,"合作项目列表_I_20260309.xlsx"))),_u("div",{className:"project-manager-layout"},_u(M9,{title:"项目清单",eyebrow:"CRUD + Excel Export",loading:n.loading||n.importing||n.exporting,actions:_u("div",{className:"inline-actions project-manager-filters"},_u("input",{value:$,onChange:(E)=>c(E.target.value),placeholder:"搜索合同号 / 项目名称 / 状态","data-testid":"project-manager-search"}),_u("select",{value:A,onChange:(E)=>{j(E.target.value),U($,E.target.value)},"data-testid":"project-manager-status-filter"},_u("option",{value:"all"},"全部"),_u("option",{value:"active"},"进行中"),_u("option",{value:"completed"},"已完成"),_u("option",{value:"unpaid"},"未全款")),_u("button",{type:"button",className:"ghost-btn",onClick:()=>U($,A)},"筛选"))},_u(wS,{projects:z,activeId:y.id,onSelect:(E)=>_(qS(E)),onRaw:f})),_u(M9,{title:y.id?"编辑项目":"新建项目",eyebrow:"PostgreSQL Write Path",loading:n.saving||n.importing},_u("form",{className:"stack-form project-manager-form",onSubmit:Q,"data-testid":"project-manager-form"},y.id?_u("label",null,"项目 ID",_u("input",{value:y.id,disabled:!0})):null,_u("label",null,"序号",_u("input",{type:"number",value:y.sequenceNo,onChange:(E)=>_((D)=>({...D,sequenceNo:E.target.value}))})),_u("label",null,"合同号",_u("input",{value:y.contractNo,onChange:(E)=>_((D)=>({...D,contractNo:E.target.value})),required:!0})),_u("label",null,"项目名称",_u("input",{value:y.name,onChange:(E)=>_((D)=>({...D,name:E.target.value})),required:!0})),_u("label",null,"当前状况",_u("textarea",{value:y.currentStatus,onChange:(E)=>_((D)=>({...D,currentStatus:E.target.value}))})),_u("label",null,"待完成",_u("textarea",{value:y.pending,onChange:(E)=>_((D)=>({...D,pending:E.target.value}))})),_u("label",null,"付款情况",_u("input",{value:y.paymentStatus,onChange:(E)=>_((D)=>({...D,paymentStatus:E.target.value})),placeholder:"例如 1 / 0.5 / 50%"})),_u("label",null,"其它",_u("input",{value:y.notes,onChange:(E)=>_((D)=>({...D,notes:E.target.value}))})),_u("div",{className:"inline-actions"},_u("button",{type:"submit",className:"primary-btn",disabled:n.saving,"data-testid":"project-manager-save-button"},n.saving?"保存中":y.id?"保存修改":"创建项目"),_u("button",{type:"button",className:"ghost-btn",onClick:()=>_({...P9})},"清空"),y.id?_u("button",{type:"button",className:"danger-btn",disabled:n.saving,onClick:w,"data-testid":"project-manager-delete-button"},"删除"):null)),_u("div",{className:"project-manager-import"},_u("p",{className:"muted paragraph"},"浏览器只访问 UniDesk frontend;后端通过同源用户服务代理写入主 PostgreSQL,不暴露 4233 公网端口。"),_u("label",{className:"file-import"},n.importing?"导入中...":"导入 Excel",_u("input",{type:"file",accept:".xlsx",onChange:L,disabled:n.importing,"data-testid":"project-manager-import-input"}))))))}var Z8=Mu(Qf(),1);var cu=Z8.default.createElement,{useEffect:LS}=Z8.default,gl=Z8.default.useState;function KS({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return cu("span",{className:`status-badge ${l}`},f||u||"unknown")}function T8({label:u,value:f,hint:l,tone:r}){return cu("article",{className:`metric-card ${r||""}`},cu("div",{className:"metric-label"},u),cu("div",{className:"metric-value"},f),cu("div",{className:"metric-hint"},l))}function R9({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){return cu("section",{className:`panel ${n||""}`},cu("div",{className:"panel-head"},cu("div",null,f?cu("p",{className:"panel-eyebrow"},f):null,cu(nf,{title:u,loading:i})),l?cu("div",{className:"panel-actions"},l):null),cu("div",{className:"panel-body"},r))}function Dw({title:u,data:f,onOpen:l,testId:r}){return cu("button",{type:"button",className:"ghost-btn","data-testid":r,onClick:()=>l(u,f)},"查看原始JSON")}function E8({title:u,text:f}){return cu("div",{className:"empty-state"},cu("strong",null,u),cu("span",null,f))}function GS(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function zS(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function TS(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function Yw(u){return String(u).replace(/[^a-zA-Z0-9_-]/g,"_")}function ES(u){if(!Number.isFinite(u))return"--";return`${u.toFixed(1)}%`}function Qy(u,f){return`${u}/microservices/todo-note/proxy${f}`}function Sw(u){return u.reduce((f,l)=>{let r=Sw(Array.isArray(l.children)?l.children:[]),n=Boolean(l.completed);return{total:f.total+1+r.total,completed:f.completed+(n?1:0)+r.completed,active:f.active+(n?0:1)+r.active}},{total:0,completed:0,active:0})}function h9(u,f){let l=f==="all"||(f==="completed"?Boolean(u.completed):!u.completed),r=Array.isArray(u.children)?u.children:[];return l||r.some((n)=>h9(n,f))}function Xw(u){return Array.isArray(u?.instances)?u.instances:[]}function x9(u,f){for(let l of u){if(l?.id===f)return Array.isArray(l.children)?l.children:[];let r=x9(Array.isArray(l?.children)?l.children:[],f);if(r.length>0)return r}return[]}function mw({microservices:u,onRaw:f,apiBaseUrl:l="/api"}){let r=u.find((g)=>g.id==="todo-note")||null,[n,i]=gl(null),[y,_]=gl(null),[$,c]=gl(""),[A,j]=gl(null),[F,U]=gl("all"),[Q,w]=gl(13),[L,J]=gl(""),[N,q]=gl(""),[W,z]=gl(""),[Z,H]=gl(""),[E,D]=gl(""),[h,B]=gl(!1),[X,m]=gl(""),[O,p]=gl(null),Y=Xw(y),v=Sw(Array.isArray(A?.todos)?A.todos:[]),T=r?GS(r):{},S=r?TS(r):{},k=r?zS(r):{};async function I(g=$){let[Qu,Tu]=await Promise.all([Eu(`${l}/microservices/todo-note/health`),Eu(Qy(l,"/api/instances"))]);i(Qu),_(Tu);let Zu=Xw(Tu),Xu=Zu.some((ff)=>ff.id===g)?g:Zu[0]?.id||"";return c(Xu),Xu}async function b(g=$){if(!g){j(null);return}let Qu=await Eu(Qy(l,`/api/instances/${encodeURIComponent(g)}`));j(Qu)}async function o(g=$){if(!r)return;B(!0),m("");try{let Qu=await I(g);await b(Qu),p(new Date)}catch(Qu){m(Hu(Qu,"Todo Note 加载失败"))}finally{B(!1)}}async function s(g){if(!$)return null;m("");try{let Qu=await Eu(Qy(l,`/api/instances/${encodeURIComponent($)}/actions`),{method:"POST",body:JSON.stringify({action:g})});return j(Qu),await I($),Qu}catch(Qu){return m(Hu(Qu,"Todo 操作失败")),null}}async function x(g){g.preventDefault();let Qu=L.trim();if(!Qu)return;B(!0),m("");try{let Tu=await Eu(Qy(l,"/api/instances"),{method:"POST",body:JSON.stringify({name:Qu})});J(""),await o(Tu.id)}catch(Tu){m(Hu(Tu,"创建清单失败"))}finally{B(!1)}}async function fu(g){if(!window.confirm("确认删除这个 Todo Note 清单?"))return;B(!0),m("");try{await Eu(Qy(l,`/api/instances/${encodeURIComponent(g)}`),{method:"DELETE"}),await o($===g?"":$)}catch(Qu){m(Hu(Qu,"删除清单失败"))}finally{B(!1)}}async function $u(g){g.preventDefault();let Qu=N.trim();if(!Qu)return;q(""),await s({type:"addTodo",title:Qu})}async function tu(g){if(!$)return;m("");try{let Qu=await Eu(Qy(l,`/api/instances/${encodeURIComponent($)}/${g}`),{method:"POST",body:JSON.stringify({})});j(Qu),await I($)}catch(Qu){m(Hu(Qu,`${g} 失败`))}}function ju(g){z(g.id),H(String(g.title||""))}async function Gu(g){let Qu=Z.trim();if(z(""),H(""),Qu)await s({type:"updateTodoTitle",todoId:g,title:Qu})}async function qu(g){let Tu=window.prompt("新增子任务标题")?.trim();if(!Tu)return;let Zu=x9(Array.isArray(A?.todos)?A.todos:[],g),Xu=new Set(Zu.map((mf)=>mf.id)),ff=await s({type:"addTodo",title:Tu,parentId:g,targetIndex:0});if(!ff)return;let _f=x9(Array.isArray(ff?.todos)?ff.todos:[],g),Jf=_f.find((mf)=>!Xu.has(mf.id));if(Jf&&_f[0]?.id!==Jf.id)await s({type:"moveTodo",todoId:Jf.id,targetParentId:g,targetIndex:0})}async function P(g,Qu){if(!E)return;let Tu={type:"moveTodo",todoId:E,targetIndex:Qu};if(g)Tu.targetParentId=g;D(""),await s(Tu)}if(LS(()=>{o()},[r?.id,r?.runtime?.providerStatus]),!r)return cu(E8,{title:"Todo Note 未登记",text:"请在 config.json 的 microservices 中登记用户服务 id=todo-note"});let e=Y.find((g)=>g.id===$)||null,uu=Array.isArray(A?.todos)?A.todos:[],Wu=uu.map((g,Qu)=>({todo:g,index:Qu})).filter((g)=>h9(g.todo,F));return cu("div",{className:"todo-note-page","data-testid":"todo-note-page"},cu(R9,{title:"Todo Note 工作台",eyebrow:"Main Server 用户服务",loading:h,actions:cu("div",{className:"panel-actions"},cu("button",{type:"button",className:"ghost-btn",disabled:h,onClick:()=>o($),"data-testid":"todo-note-refresh-button"},h?"刷新中":"刷新"),cu(Dw,{title:"Todo Note 用户服务",data:r,onOpen:f,testId:"raw-todo-note-service"}))},cu("div",{className:"todo-note-hero"},cu("div",null,cu("div",{className:"node-version-line"},cu(KS,{status:T.providerStatus==="online"?"online":"warn"},T.providerStatus||"unknown"),cu("span",null,r.providerId),cu("span",null,k.public?"公网暴露":"仅 UniDesk frontend 代理访问"),cu("span",null,n?.ok?"Health OK":"Health --")),cu("p",{className:"muted paragraph"},r.description)),cu("div",{className:"microservice-ref-card"},cu("span",null,"Repo"),cu("strong",null,S.url||"--"),cu("code",null,S.commitId||"--")),cu("div",{className:"microservice-ref-card"},cu("span",null,"Main Server Docker"),cu("strong",null,`${k.nodeBindHost||"--"}:${k.nodePort||"--"}`),cu("code",null,`${S.composeService||"--"} / ${S.containerName||"--"}`))),cu(yf,{error:X,wide:!0})),cu("div",{className:"todo-note-layout"},cu(R9,{title:"清单",eyebrow:`${Y.length} Instances`,className:"todo-list-panel",loading:h},cu("form",{className:"todo-create-list",onSubmit:x},cu("input",{placeholder:"新清单名称",value:L,onChange:(g)=>J(g.target.value),"aria-label":"新清单名称"}),cu("button",{type:"submit",className:"ghost-btn",disabled:h||!L.trim()},"创建")),Y.length===0?cu(E8,{title:"暂无清单",text:"迁移或创建清单后会出现在这里"}):cu("div",{className:"todo-instance-list"},Y.map((g)=>cu("button",{key:g.id,type:"button",className:`todo-instance-row ${$===g.id?"active":""}`,onClick:()=>{c(g.id),b(g.id)},"data-testid":`todo-instance-${Yw(g.id)}`},cu("strong",null,g.name),cu("span",null,`${g.completedCount??0}/${g.todoCount??0} 完成`),cu("code",null,g.id))))),cu("div",{className:"todo-main-stack"},cu(R9,{title:e?.name||"待选择清单",eyebrow:O?`Updated ${$f(O)}`:"Todo Tree",loading:h,actions:A?cu("div",{className:"panel-actions"},cu("button",{type:"button",className:"ghost-btn",onClick:()=>s({type:"renameInstance",name:window.prompt("清单新名称",A.name)||A.name})},"重命名"),cu("button",{type:"button",className:"ghost-btn danger",onClick:()=>fu($)},"删除清单"),cu(Dw,{title:`Todo Instance ${$}`,data:A,onOpen:f,testId:"raw-todo-instance"})):null},!A?cu(E8,{title:"未选择清单",text:"左侧选择一个 Todo Note 清单"}):cu("div",{className:"todo-workbench",style:{"--todo-font-size":`${Q}px`}},cu("div",{className:"todo-toolbar"},cu("form",{className:"todo-add-form",onSubmit:$u},cu("input",{placeholder:"新增根任务",value:N,onChange:(g)=>q(g.target.value),"aria-label":"新增根任务"}),cu("button",{type:"submit",className:"ghost-btn",disabled:!N.trim()},"新增")),cu("div",{className:"todo-filter-strip"},["all","active","completed"].map((g)=>cu("button",{key:g,type:"button",className:`todo-filter ${F===g?"active":""}`,onClick:()=>U(g)},g==="all"?"全部":g==="active"?"未完成":"已完成"))),cu("div",{className:"todo-toolbar-actions"},cu("button",{type:"button",className:"ghost-btn",onClick:()=>s({type:"setAllTodosExpanded",expanded:!0})},"全部展开"),cu("button",{type:"button",className:"ghost-btn",onClick:()=>s({type:"setAllTodosExpanded",expanded:!1})},"全部收起"),cu("button",{type:"button",className:"ghost-btn",onClick:()=>tu("undo")},"撤销"),cu("button",{type:"button",className:"ghost-btn",onClick:()=>tu("redo")},"重做"),cu("label",{className:"todo-font-control"},"字号",cu("input",{type:"range",min:11,max:18,value:Q,onChange:(g)=>w(Number(g.target.value))})))),cu("div",{className:"todo-stats-grid"},cu(T8,{label:"总任务",value:v.total,hint:`${Y.length} lists`}),cu(T8,{label:"已完成",value:v.completed,hint:`${ES(v.total?v.completed/v.total*100:0)}`,tone:"ok"}),cu(T8,{label:"未完成",value:v.active,hint:F==="active"?"当前筛选":"active tasks",tone:v.active>0?"warn":"ok"}),cu(T8,{label:"历史指针",value:A.historyPointer??0,hint:"undo / redo"})),cu("div",{className:"todo-root-drop",onDragOver:(g)=>g.preventDefault(),onDrop:(g)=>{g.preventDefault(),P(null,uu.length)}},"拖到这里可移为根任务末尾"),cu("div",{className:"todo-tree","data-testid":"todo-note-tree"},Wu.length===0?cu(E8,{title:"没有匹配任务",text:"调整筛选或新增任务"}):Wu.map(({todo:g,index:Qu})=>cu(pw,{key:g.id,todo:g,depth:0,parentId:null,index:Qu,siblingCount:uu.length,filter:F,editingId:W,editingTitle:Z,setEditingTitle:H,beginEdit:ju,saveEdit:Gu,applyTodoAction:s,addChild:qu,dragTodoId:E,setDragTodoId:D,dropTodo:P}))))))))}function pw(u){let{todo:f,depth:l,parentId:r,index:n,siblingCount:i,filter:y,editingId:_,editingTitle:$,setEditingTitle:c,beginEdit:A,saveEdit:j,applyTodoAction:F,addChild:U,dragTodoId:Q,setDragTodoId:w,dropTodo:L}=u,J=Array.isArray(f.children)?f.children:[],N=J.map((z,Z)=>({child:z,childIndex:Z})).filter((z)=>h9(z.child,y)),q=_===f.id,W=r||null;return cu("div",{className:"todo-row-wrap"},cu("article",{className:`todo-row ${f.completed?"completed":""} ${Q===f.id?"dragging":""}`,style:{"--todo-depth":l},draggable:!0,onDragStart:(z)=>{w(f.id),z.dataTransfer.effectAllowed="move"},onDragOver:(z)=>z.preventDefault(),onDrop:(z)=>{z.preventDefault(),L(f.id,J.length)},"data-testid":`todo-row-${Yw(f.id)}`},cu("button",{type:"button",className:"todo-expand",disabled:J.length===0,onClick:()=>F({type:"toggleTodoExpanded",todoId:f.id})},J.length===0?"·":f.expanded?"▾":"▸"),cu("input",{type:"checkbox",checked:Boolean(f.completed),onChange:()=>F({type:"toggleTodoCompleted",todoId:f.id}),"aria-label":`完成 ${f.title}`}),cu("div",{className:"todo-title-cell",onDoubleClick:()=>A(f)},q?cu("div",{className:"todo-edit-inline"},cu("input",{value:$,autoFocus:!0,onChange:(z)=>c(z.target.value),onKeyDown:(z)=>{if(z.key==="Enter")j(f.id);if(z.key==="Escape")A({id:"",title:""})}}),cu("button",{type:"button",className:"ghost-btn",onClick:()=>j(f.id)},"保存")):cu("strong",null,f.title||"Untitled"),cu("div",{className:"todo-meta-line"},cu("span",null,`子项 ${J.length}`),cu("span",null,`更新 ${Nu(f.updatedAt)}`),f.reminderAt?cu("span",{className:"todo-reminder"},`提醒 ${Nu(f.reminderAt)}`):cu("span",null,"无提醒"))),cu("input",{className:"todo-reminder-input",type:"datetime-local",value:a8(f.reminderAt),onChange:(z)=>F({type:"setTodoReminder",todoId:f.id,reminderAt:V7(z.target.value)})}),cu("div",{className:"todo-row-actions"},cu("button",{type:"button",className:"ghost-btn",onClick:()=>A(f)},"编辑"),cu("button",{type:"button",className:"ghost-btn",onClick:()=>U(f.id)},"子项"),cu("button",{type:"button",className:"ghost-btn",disabled:n<=0,onClick:()=>F({type:"moveTodo",todoId:f.id,...W?{targetParentId:W}:{},targetIndex:n-1})},"上移"),cu("button",{type:"button",className:"ghost-btn",disabled:n<=0,onClick:()=>F({type:"moveTodo",todoId:f.id,...W?{targetParentId:W}:{},targetIndex:0})},"置顶"),cu("button",{type:"button",className:"ghost-btn",disabled:n>=i-1,onClick:()=>F({type:"moveTodo",todoId:f.id,...W?{targetParentId:W}:{},targetIndex:n+1})},"下移"),cu("button",{type:"button",className:"ghost-btn",disabled:!r,onClick:()=>F({type:"moveTodo",todoId:f.id,targetIndex:9999})},"提升"),cu("button",{type:"button",className:"ghost-btn danger",onClick:()=>F({type:"deleteTodo",todoId:f.id})},"删除"))),f.expanded&&N.length>0?cu("div",{className:"todo-children"},N.map(({child:z,childIndex:Z})=>cu(pw,{key:z.id,todo:z,depth:l+1,parentId:f.id,index:Z,siblingCount:J.length,filter:y,editingId:_,editingTitle:$,setEditingTitle:c,beginEdit:A,saveEdit:j,applyTodoAction:F,addChild:U,dragTodoId:Q,setDragTodoId:w,dropTodo:L}))):null)}var Pw=Mu(Qf(),1),bn=Pw.default.createElement;function Mw({title:u,items:f,actions:l,className:r,testId:n}){let i=Array.isArray(f)?f:[];return bn("section",{className:`top-status-bar ${r||""}`,"data-testid":n},bn("div",{className:"top-status-main"},u?bn("strong",{className:"top-status-title"},u):null,bn("div",{className:"top-status-chips"},i.map((y,_)=>bn("span",{key:y?.key||`${y?.label||"status"}-${_}`,className:`top-status-chip ${y?.tone||""}`,"data-testid":y?.testId},y?.label?bn("b",null,y.label):null,bn("span",null,y?.value??"--"))))),l?bn("div",{className:"top-status-actions"},l):null)}var S$=Mu(Qf(),1);var hf=S$.default.createElement;function Cw({onClose:u}){let{notifications:f,removeNotification:l,clearNotifications:r}=Bl(),n=S$.default.useRef(null);if(S$.default.useEffect(()=>{let i=(y)=>{if(n.current&&!n.current.contains(y.target))u()};return document.addEventListener("mousedown",i),()=>document.removeEventListener("mousedown",i)},[u]),f.length===0)return hf("div",{className:"notification-popup",ref:n},hf("div",{className:"notification-popup-header"},hf("span",null,"通知"),hf("button",{className:"notification-popup-close",onClick:u},"×")),hf("div",{className:"notification-popup-empty"},"暂无通知"));return hf("div",{className:"notification-popup",ref:n},hf("div",{className:"notification-popup-header"},hf("span",null,`通知 (${f.length})`),hf("div",{className:"notification-popup-actions"},hf("button",{className:"notification-popup-clear",onClick:r},"清空"),hf("button",{className:"notification-popup-close",onClick:u},"×"))),hf("div",{className:"notification-popup-list"},f.slice().reverse().map((i)=>hf("div",{key:i.id,className:`notification-item ${i.type}`},hf("span",{className:"notification-item-icon"},i.type==="success"?"✓":"×"),hf("span",{className:"notification-item-message"},i.message),hf("button",{className:"notification-item-dismiss",onClick:()=>l(i.id)},"×")))))}function Rw({notification:u}){let{removeNotification:f}=Bl();return S$.default.useEffect(()=>{let l=setTimeout(()=>{f(u.id)},3000);return()=>clearTimeout(l)},[u.id,f]),hf("div",{className:`notification-banner ${u.type}`,role:"alert"},hf("span",{className:"notification-banner-icon"},u.type==="success"?"✓":"×"),hf("span",{className:"notification-banner-message"},u.message),hf("button",{className:"notification-banner-dismiss",onClick:()=>f(u.id)},"×"))}function ew(u,f){let l=document.getElementById("root")?.getAttribute(u);if(!l)return f;try{let r=JSON.parse(l);return typeof r==="object"&&r!==null&&!Array.isArray(r)?r:f}catch{return f}}var gu=ew("data-config",{apiBaseUrl:"/api",authUsername:"admin"}),ZS=ew("data-codex-overview",null),t=kn.default.createElement,{useEffect:j0,useMemo:P$}=kn.default,bu=kn.default.useState,I9=kn.default.createContext(!1),Or=pQ(u6),OS={id:"code-queue",name:"Code Queue",providerId:"main-server",description:"Code Queue",repository:{containerName:"code-queue-backend"},backend:{nodeBaseUrl:"http://code-queue:4222",nodeBindHost:"code-queue",nodePort:4222,public:!1},runtime:{providerStatus:"loading",providerName:"main-server"}};function xw(){return typeof document>"u"||document.visibilityState!=="hidden"}function HS(u,f){if(u==="ops"&&f==="status")return 5000;if(u==="nodes"&&f==="monitor")return 5000;if(u==="tasks"&&(f==="dispatch"||f==="scheduled"||f==="pending"))return 5000;if(u==="nodes"||u==="ops")return 1e4;if(u==="apps")return 15000;if(u==="tasks")return 15000;return 30000}async function BS(u){if(!u?._summaryOnly||!u?.id)return u;return(await Eu(`${gu.apiBaseUrl}/tasks/${encodeURIComponent(String(u.id))}`))?.task||u}function M$(u){return u?._summaryOnly?{...u,_loadRaw:()=>BS(u)}:u}function x0(u){if(!Number.isFinite(u))return"--";let f=Math.max(0,u);if(f===0)return"0s";if(f<0.01)return"<0.01s";if(f<0.1)return`${f.toFixed(2)}s`;if(f<1)return`${f.toFixed(1)}s`;if(f<10&&!Number.isInteger(f))return`${f.toFixed(1)}s`;if(f<60)return`${Math.round(f)}s`;let l=Math.floor(f);if(l<3600)return`${Math.floor(l/60)}m ${l%60}s`;return`${Math.floor(l/3600)}h ${Math.floor(l%3600/60)}m`}function jr(u){let f=Number(u);if(!Number.isFinite(f))return"--";if(f<1)return`${Math.max(0,f).toFixed(1)}ms`;if(f<10)return`${f.toFixed(1)}ms`;if(f<1000)return`${Math.round(f)}ms`;return x0(f/1000)}function Ll(u){let f=Number(u);if(!Number.isFinite(f)||f<=0)return"--";let l=["B","KB","MB","GB","TB"],r=f,n=0;while(r>=1024&&n0)return l[r]}return"任务失败但 provider 未返回明确原因"}function R1(u){if(u===null||u===void 0)return"--";if(typeof u==="boolean")return u?"是":"否";if(typeof u==="number")return String(u);if(typeof u==="string")return u.length>80?`${u.slice(0,77)}...`:u;if(Array.isArray(u))return`${u.length} 项`;if(typeof u==="object")return`${Object.keys(u).length} 字段`;return String(u)}function DS(u,f){let l=u.replace(/[-_\s]/g,"").toLowerCase(),r=l==="ts"||l.endsWith("at")||l.endsWith("timestamp")||l.endsWith("heartbeat");if((typeof f==="string"||typeof f==="number")&&r){let n=Nu(f);if(n!=="--")return n}if(u==="bodyText"&&typeof f==="string")return`${/^\s*[{[]/.test(f)?"JSON":"HTTP"} body ${f.length} chars`;return R1(f)}function lL(u){if(!u||typeof u!=="object"||Array.isArray(u))return[];return Object.entries(u)}function sl(u){return String(u).replace(/[^a-zA-Z0-9_-]/g,"_")}function s9(u,f){return u&&typeof u==="object"&&!Array.isArray(u)?u[f]:void 0}function H8(u,f,l="未知"){let r=s9(u?.labels,f);return typeof r==="string"&&r.length>0?r:l}function rL(u){return H8(u,"providerGatewayVersion")}function p$(u){return H8(u,"providerGatewayUpgradePolicy")}function hw(u){return H8(u,"providerGatewayStartedAt","")}function nL(u){let f=s9(u?.labels,"unideskCapabilities");if(typeof f==="string")return f.split(",").map((l)=>l.trim()).filter(Boolean);return Array.isArray(f)?f.filter((l)=>typeof l==="string"):[]}function iL(u,f){return nL(u).includes(f)}function bw(u,f){let l=s9(u?.labels,f);return l===!0||l==="true"||l==="1"}function XS(u){if(!iL(u,"host.ssh"))return{tone:"fail",label:"不可用",detail:"未声明 host.ssh"};if(!bw(u,"hostSshConfigured"))return{tone:"warn",label:"未配置",detail:"缺少 SSH 环境变量"};if(!bw(u,"hostSshKeyPresent"))return{tone:"warn",label:"缺 key",detail:"私钥未挂载"};return{tone:"ok",label:"可用",detail:H8(u,"hostSshTarget","host.ssh ready")}}function YS(u){if(!iL(u,"provider.upgrade"))return{tone:"fail",label:"不可用",detail:"未声明 provider.upgrade"};let f=p$(u);if(f!=="always-enabled")return{tone:"warn",label:"待确认",detail:`策略 ${f}`};return{tone:"ok",label:"可用",detail:"always-enabled"}}function a9(u){let f=typeof u==="string"&&u.length>0?u:"未知";if(f==="未知")return"版本未知";return f.startsWith("v")?f:`v${f}`}function yL(u){return u?.payload&&typeof u.payload==="object"&&!Array.isArray(u.payload)?u.payload:{}}function B8(u){return u?.result&&typeof u.result==="object"&&!Array.isArray(u.result)?u.result:{}}function O8(u){let f=yL(u),l=B8(u);return(f.mode??l.mode)==="schedule"?"schedule":"plan"}function SS(u){let f=yL(u).source;return typeof f==="string"&&f.length>0?f:"unknown"}function mS(u){let f=B8(u),l=f.plan&&typeof f.plan==="object"&&!Array.isArray(f.plan)?f.plan:{},r=f.policy??l.policy;return typeof r==="string"&&r.length>0?r:"--"}function _L(u){let f=B8(u),l=f.plan&&typeof f.plan==="object"&&!Array.isArray(f.plan)?f.plan:{},r=f.targetProviderGatewayVersion??f.providerGatewayVersion??l.targetProviderGatewayVersion??l.providerGatewayVersion;return typeof r==="string"&&r.length>0?a9(r):"版本未知"}function $L(u){if(String(u?.status||"").toLowerCase()==="failed")return fL(u);if(Ny(u))return"等待 provider 回传升级终态";let l=B8(u);if(typeof l.updaterContainerId==="string"&&l.updaterContainerId.length>0)return`updater ${l.updaterContainerId.slice(0,18)}`;if(typeof l.message==="string"&&l.message.length>0)return l.message;if(l.plan)return"升级计划已生成";return"无升级结果摘要"}function tL(u,f){return u.filter((l)=>l?.providerId===f&&l?.command==="provider.upgrade").sort((l,r)=>(F0(r.updatedAt)??0)-(F0(l.updatedAt)??0))}function pS(u){return u.find((f)=>O8(f)==="schedule")||u[0]||null}function cL(u){return u?.runtime&&typeof u.runtime==="object"&&!Array.isArray(u.runtime)?u.runtime:{}}function vw(u){return u?.backend&&typeof u.backend==="object"&&!Array.isArray(u.backend)?u.backend:{}}function PS(u){return u?.repository&&typeof u.repository==="object"&&!Array.isArray(u.repository)?u.repository:{}}function Wf({status:u,children:f}){let l=String(u||"unknown").toLowerCase();return t("span",{className:`status-badge ${l}`},f||u||"unknown")}function cf({label:u,value:f,hint:l,tone:r,onClick:n,testId:i}){let y=typeof n==="function";return t("article",{className:`metric-card ${r||""} ${y?"clickable":""}`,role:y?"button":void 0,tabIndex:y?0:void 0,"data-testid":i,onClick:n,onKeyDown:y?(_)=>{if(_.key==="Enter"||_.key===" ")_.preventDefault(),n()}:void 0},t("div",{className:"metric-label"},u),t("div",{className:"metric-value"},f),t("div",{className:"metric-hint"},l))}function au({title:u,eyebrow:f,actions:l,children:r,className:n,loading:i}){let y=kn.default.useContext(I9),_=Boolean(i)||y;return t("section",{className:`panel ${n||""}`},t("div",{className:"panel-head"},t("div",null,f?t("p",{className:"panel-eyebrow"},f):null,t(nf,{title:u,loading:_})),l?t("div",{className:"panel-actions"},l):null),t("div",{className:"panel-body"},r))}function bf({title:u,data:f,onOpen:l,testId:r}){let[n,i]=bu(!1),y=f&&typeof f==="object"&&typeof f._loadRaw==="function"?f._loadRaw:null;async function _(){if(!y){l(u,f);return}i(!0);try{l(u,await y())}catch($){l(u,{ok:!1,error:Hu($,"读取原始 JSON 失败"),fallback:f})}finally{i(!1)}}return t("button",{type:"button",className:"ghost-btn","data-testid":r,disabled:n,onClick:()=>void _()},n?"读取中":"查看原始JSON")}function MS({raw:u,onClose:f}){if(!u)return null;return t("div",{className:"modal-backdrop",role:"presentation"},t("section",{className:"raw-dialog",role:"dialog","aria-modal":"true","aria-label":u.title},t("div",{className:"raw-dialog-head"},t("h2",null,u.title),t("button",{type:"button",className:"ghost-btn",onClick:f},"关闭")),t("pre",{className:"raw-json","data-testid":"raw-json"},JSON.stringify(u.data,null,2))))}function AL({labels:u,limit:f=8}){let l=lL(u).slice(0,f);if(l.length===0)return t("span",{className:"muted"},"无标签");return t("div",{className:"chip-row"},l.map(([r,n])=>t("span",{key:r,className:"data-chip"},t("b",null,r),t("span",null,R1(n)))))}function qy({node:u}){let f=rL(u);return t("span",{className:`version-chip ${f==="未知"?"unknown":""}`,"data-testid":`gateway-version-${sl(u?.providerId||"unknown")}`},a9(f))}function kw({title:u,state:f,testId:l}){return t("span",{className:`capability-badge ${f.tone}`,title:f.detail,"data-testid":l},t("b",null,u),t("strong",null,f.label),t("small",null,f.detail))}function o9({node:u}){let f=sl(u?.providerId||"unknown");return t("div",{className:"node-availability-strip"},t(kw,{title:"SSH 透传",state:XS(u),testId:`ssh-availability-${f}`}),t(kw,{title:"远程更新",state:YS(u),testId:`upgrade-availability-${f}`}))}function In({data:u,empty:f="无数据"}){if(u===null||u===void 0)return t("span",{className:"muted"},f);if(typeof u!=="object")return t("span",{className:"summary-value"},R1(u));if(Array.isArray(u))return t("span",{className:"summary-value"},`${u.length} 项列表`);let l=Object.entries(u).slice(0,5);if(l.length===0)return t("span",{className:"muted"},f);return t("div",{className:"summary-grid"},l.map(([r,n])=>t("span",{key:r,className:"summary-item"},t("b",null,r),t("span",null,DS(r,n)))))}function Ff({title:u,text:f}){return t("div",{className:"empty-state"},t("strong",null,u),t("span",null,f))}function CS({onLogin:u}){let[f,l]=bu(gu.authUsername||"admin"),[r,n]=bu(""),[i,y]=bu(""),[_,$]=bu(!1);async function c(A){A.preventDefault(),$(!0),y("");try{let j=await Eu("/login",{method:"POST",body:JSON.stringify({username:f,password:r})});u(j)}catch(j){y(Hu(j,"登录失败"))}finally{$(!1)}}return t("main",{className:"login-screen","data-testid":"login-screen"},t("section",{className:"login-card"},t("div",{className:"login-brand"},t("span",{className:"brand-mark"},"UD"),t("div",null,t("h1",null,"UniDesk"),t("p",null,"Control Plane Login"))),t("form",{className:"login-form",onSubmit:c},t("label",null,"账号",t("input",{name:"username",autoComplete:"username",value:f,onChange:(A)=>l(A.target.value)})),t("label",null,"密码",t("input",{name:"password",type:"password",autoComplete:"current-password",value:r,onChange:(A)=>n(A.target.value)})),t(yf,{error:i}),t("button",{type:"submit",disabled:_},_?"登录中":"登录")),t("div",{className:"login-note"},"默认账号由 config.json 注入;公网入口只暴露前端登录面。")))}function RS({connection:u,lastRefresh:f,onRefresh:l,onLogout:r,session:n,clock:i,activeStatusItems:y=[],onNotificationToggle:_,unreadCount:$=0}){let c=[{key:"core",label:"核心",value:u.text,tone:u.ok?"ok":"fail",testId:"conn-text"},...Array.isArray(y)?y:[],{key:"refresh",label:"刷新",value:f?$f(f):"未刷新"},{key:"clock",label:it,value:$f(i)},{key:"user",label:"用户",value:n?.user?.username||"--",tone:"user"}];return t("header",{className:"topbar"},t("div",null,t("p",{className:"eyebrow"},"Distributed Work Platform"),t("h1",null,"UniDesk 控制平面")),t(Mw,{className:"global-top-status",title:"状态",items:c,actions:[t("button",{key:"notification",type:"button",className:`notification-icon-btn ${$>0?"has-unread":""}`,onClick:_,"aria-label":"通知"},"\uD83D\uDD14",$>0?t("span",{key:"badge",className:"notification-badge"},$>99?"99+":$):null),t("button",{key:"refresh",type:"button",className:"ghost-btn",onClick:l},"刷新"),t("button",{key:"logout",type:"button",className:"ghost-btn danger",onClick:r},"退出")]}))}function xS(u){return!u.defaultPrevented&&u.button===0&&!u.metaKey&&!u.altKey&&!u.ctrlKey&&!u.shiftKey&&u.currentTarget.target!=="_blank"}function jL({moduleId:u,tabId:f,className:l,active:r=!1,title:n,testId:i,onNavigate:y,children:_}){let $=f6(Or,u,f);return t("a",{href:$,role:"button",className:l,title:n,"aria-current":r?"page":void 0,"data-testid":i,"data-route":$,onClick:(c)=>{if(!xS(c))return;c.preventDefault(),y(u,f)}},_)}function hS({activeModule:u,activeTabs:f,onNavigate:l,collapsed:r,onToggle:n}){return t("aside",{className:`rail ${r?"collapsed":""}`,"aria-label":"主模块"},t("div",{className:"brand"},t("span",{className:"brand-mark"},"UD"),t("span",{className:"brand-text"},"UniDesk"),t("button",{type:"button",className:"rail-toggle",onClick:n,"aria-label":r?"展开左侧边栏":"收起左侧边栏","data-testid":"rail-toggle"},r?"»":"«")),u6.map((i)=>{let y=f[i.id]||b_[i.id]||i.tabs[0]?.id||"";return t(jL,{key:i.id,moduleId:i.id,tabId:y,className:`module ${u===i.id?"active":""}`,active:u===i.id,title:i.label,onNavigate:l},t("span",{className:"module-code"},i.code),t("span",null,i.label))}))}function bS({module:u,activeTab:f,onNavigate:l}){return t("nav",{className:"tabs","aria-label":`${u.label} 子功能`},u.tabs.map((r)=>t(jL,{key:r.id,moduleId:u.id,tabId:r.id,className:`tab ${f===r.id?"active":""}`,active:f===r.id,onNavigate:l},r.label)))}function vS({data:u,onRaw:f,onNavigate:l}){let r=u.overview||{},n=u.nodes.filter((U)=>U.status==="online"),i=u.pendingTasks||u.tasks.filter(Ny),y=r.pendingTaskCount??i.length,_=u.tasks.slice(0,5),$=r.pgdata||{},c=r.microserviceAvailability||{},A=Cu(c.totalCount),j=Cu(c.healthyCount),F=Cu(c.unhealthyCount);return t("div",{className:"page-grid overview-grid","data-testid":"overview-page"},t(au,{title:"核心指标",eyebrow:"Control"},t("div",{className:"metric-grid"},t(cf,{label:"数据库",value:r.dbReady?"READY":"WAIT",hint:"PostgreSQL internal network",tone:r.dbReady?"ok":"warn"}),t(cf,{label:"PGDATA",value:Ll($.databaseBytes),hint:`${$.volumeName||"unidesk_pgdata_10gb"} / ${$.databasePretty||"--"}`,tone:"ok",testId:"pgdata-usage-card"}),t(cf,{label:"在线节点",value:r.onlineNodeCount??0,hint:`${r.nodeCount??0} registered`,tone:"ok"}),t(cf,{label:"WebSocket",value:r.activeSocketCount??0,hint:"Provider ingress sockets"}),t(cf,{label:"用户服务可用",value:A>0?`${j}/${A}`:"--",hint:A>0?`healthyCount ${j} · unhealthyCount ${F}`:"strict /health probes",tone:A>0&&F===0?"ok":"warn",testId:"microservice-availability-card"}),t(cf,{label:"待处理任务",value:y,hint:y>0?"点击查看具体任务":`timeout ${x0(Math.floor((r.taskPendingTimeoutMs??0)/1000))}`,tone:y>0?"warn":"ok",onClick:()=>l("tasks","pending"),testId:"pending-task-card"}))),t(au,{title:"本机 Provider",eyebrow:"Self Connected"},n.length===0?t(Ff,{title:"暂无在线节点",text:"provider-gateway 未完成自接入"}):t("div",{className:"node-card-list"},n.slice(0,4).map((U)=>t(kS,{key:U.providerId,node:U,onRaw:f})))),t(au,{title:"待处理任务明细",eyebrow:`${y} Pending`,actions:t("button",{type:"button",className:"ghost-btn",onClick:()=>l("tasks","pending"),"data-testid":"pending-task-detail-link"},"进入任务调度")},i.length===0?t(Ff,{title:"当前无待处理",text:"queued / dispatched / running 超时后会自动转为 failed,避免总览长期卡住"}):t("div",{className:"compact-list"},i.slice(0,5).map((U)=>t(aw,{key:U.id,task:U,onRaw:f})))),t(au,{title:"最近任务",eyebrow:"Dispatch"},_.length===0?t(Ff,{title:"暂无任务",text:"可以在任务调度模块发起 docker.ps 或 echo"}):t("div",{className:"compact-list"},_.map((U)=>t(aw,{key:U.id,task:U,onRaw:f})))))}function kS({node:u,onRaw:f}){return t("article",{className:"node-card"},t("div",{className:"node-card-head"},t("div",null,t("strong",null,u.name),t("code",null,u.providerId)),t(Wf,{status:u.status})),t("div",{className:"node-version-line"},t(qy,{node:u}),t("span",null,`升级策略 ${p$(u)}`)),t(o9,{node:u}),t(AL,{labels:u.labels,limit:6}),t("div",{className:"node-card-foot"},t("span",null,`心跳 ${Nu(u.lastHeartbeat)}`),t(bf,{title:`Provider ${u.providerId}`,data:u,onOpen:f,testId:`raw-node-${sl(u.providerId)}`})))}function IS({events:u,onRaw:f}){return t(au,{title:"事件摘要",eyebrow:"Latest 100"},u.length===0?t(Ff,{title:"暂无事件",text:"Provider 注册、心跳超时和任务状态会写入事件流"}):t("div",{className:"table-wrap"},t("table",null,t("thead",null,t("tr",null,t("th",null,"ID"),t("th",null,"类型"),t("th",null,"来源"),t("th",null,"摘要"),t("th",null,"时间"),t("th",null,"操作"))),t("tbody",null,u.map((l)=>t("tr",{key:l.id},t("td",null,t("code",null,l.id)),t("td",null,t(Wf,{status:l.type},l.type)),t("td",null,t("code",null,l.source)),t("td",null,t(In,{data:l.payload})),t("td",null,Nu(l.createdAt)),t("td",null,t(bf,{title:`Event ${l.id}`,data:l,onOpen:f}))))))))}function gS({logs:u,onRaw:f}){return t(au,{title:"服务日志",eyebrow:"Core Recent"},u.length===0?t(Ff,{title:"暂无日志",text:"backend-core 内存日志会在请求和 provider 事件后出现"}):t("div",{className:"log-list"},u.slice(-80).reverse().map((l,r)=>t("article",{key:r,className:`log-row ${l.level||"info"}`},t("span",null,Nu(l.ts)),t("b",null,l.level||"info"),t("strong",null,l.message||"log"),t(In,{data:l.data,empty:"无附加字段"}),t(bf,{title:`Log ${l.message||r}`,data:l,onOpen:f})))))}function sS({nodes:u,onRaw:f}){return t(au,{title:"节点清单",eyebrow:`${u.length} Providers`},u.length===0?t(Ff,{title:"暂无 Provider 节点",text:"确认 provider-gateway 已连接 provider ingress"}):t("div",{className:"table-wrap"},t("table",{className:"node-list-table"},t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"Provider"),t("th",null,"网关版本"),t("th",null,"运维可用性"),t("th",null,"资源标签"),t("th",null,"连接时间"),t("th",null,"最后心跳"),t("th",null,"操作"))),t("tbody",null,u.map((l)=>t("tr",{key:l.providerId},t("td",null,t(Wf,{status:l.status})),t("td",null,t("strong",null,l.name),t("code",null,l.providerId)),t("td",null,t("div",{className:"gateway-cell"},t(qy,{node:l}),t("span",null,p$(l)))),t("td",null,t(o9,{node:l})),t("td",null,t(AL,{labels:l.labels,limit:5})),t("td",null,Nu(l.connectedAt)),t("td",null,Nu(l.lastHeartbeat)),t("td",null,t(bf,{title:`Provider ${l.providerId}`,data:l,onOpen:f,testId:`raw-node-table-${sl(l.providerId)}`}))))))))}function aS({nodes:u}){let f=P$(()=>{let l=[];for(let r of u)for(let[n,i]of lL(r.labels))l.push({providerId:r.providerId,name:r.name,key:n,value:i});return l},[u]);return t(au,{title:"资源标签",eyebrow:"Structured Labels"},f.length===0?t(Ff,{title:"暂无标签",text:"provider-gateway 注册消息会同步资源标签"}):t("div",{className:"label-matrix"},f.map((l)=>t("article",{key:`${l.providerId}-${l.key}`,className:"label-card"},t("span",null,l.key),t("strong",null,R1(l.value)),t("code",null,l.providerId)))))}function oS({nodes:u}){return t(au,{title:"心跳状态",eyebrow:"Provider Liveness"},u.length===0?t(Ff,{title:"无心跳",text:"等待 provider 注册和 heartbeat"}):t("div",{className:"heartbeat-list"},u.map((f)=>t("article",{key:f.providerId,className:"heartbeat-row"},t("span",{className:`pulse ${f.status}`}),t("div",null,t("strong",null,f.name),t("code",null,f.providerId)),t("div",null,t("span",null,"connected"),t("b",null,Nu(f.connectedAt))),t("div",null,t("span",null,"last heartbeat"),t("b",null,Nu(f.lastHeartbeat)))))))}function dS({nodes:u,systemStatuses:f,tasks:l,onRaw:r,refresh:n}){let[i,y]=bu(""),_=P$(()=>u.map((w)=>{let L=f.find((J)=>J.providerId===w.providerId);return{...w,systemCurrent:L?.current||null,systemHistory:L?.history||[],systemUpdatedAt:L?.updatedAt||null}}),[u,f]),$=_.find((w)=>w.providerId===i)||_[0]||null;if(j0(()=>{if(!i&&_[0])y(_[0].providerId)},[_.length,i]),!$)return t(Ff,{title:"暂无资源监控",text:"等待 provider 上报 CPU、内存和硬盘指标"});let c=$.systemCurrent,A=$.systemHistory||[],j=c?.cpu||{},F=c?.memory||{},U=c?.disk||{},Q=A.length>0?A:c?[{at:c.collectedAt,cpuPercent:Cu(j.percent),memoryPercent:Cu(F.percent),diskPercent:Cu(U.percent)}]:[];return t("div",{className:"monitor-page","data-testid":"node-monitor-page"},t("div",{className:"docker-node-strip"},_.map((w)=>t("button",{key:w.providerId,type:"button",className:`docker-node-tile ${$.providerId===w.providerId?"active":""}`,onClick:()=>y(w.providerId)},t("span",{className:`pulse ${w.status}`}),t("strong",null,w.name),t("code",null,w.providerId),t("span",null,w.systemCurrent?`CPU ${vn(w.systemCurrent.cpu?.percent)} / MEM ${vn(w.systemCurrent.memory?.percent)}`:"等待指标")))),t("div",{className:"monitor-layout"},t(au,{title:"任务管理器视图",eyebrow:$.name,className:"monitor-main-panel",actions:c?t(bf,{title:`System ${$.providerId}`,data:{current:c,history:A},onOpen:r}):null},!c?t(Ff,{title:"系统指标未上报",text:"provider-gateway 会周期性采集 /proc 与 df,并保存历史曲线"}):t("div",null,t("div",{className:"monitor-hero"},t("div",null,t("p",{className:"panel-eyebrow"},"Node Performance"),t("h3",null,$.name),t("div",{className:"docker-meta"},t("span",null,`${j.cores||0} CPU cores`),t("span",null,`load ${Cu(j.load1).toFixed(2)} / ${Cu(j.load5).toFixed(2)} / ${Cu(j.load15).toFixed(2)}`),t("span",null,`memory actual ${Ll(F.usedBytes)} / ${Ll(F.totalBytes)}`),t("span",null,`disk ${Ll(U.usedBytes)} / ${Ll(U.totalBytes)}`))),t(Wf,{status:c.ok?"online":"warn"},c.ok?"METRICS READY":"METRICS DEGRADED")),t("div",{className:"monitor-chart-grid"},t(v9,{title:"CPU",metricKey:"cpuPercent",current:j.percent,points:Q,detail:`${j.cores||0} cores / load ${Cu(j.load1).toFixed(2)}`,tone:"cpu",testId:"metric-chart-cpu"}),t(v9,{title:"Memory",metricKey:"memoryPercent",current:F.percent,points:Q,detail:`${Ll(F.usedBytes)} actual / ${Ll(F.cacheBytes)} cache excluded`,tone:"memory",testId:"metric-chart-memory"}),t(v9,{title:"Disk",metricKey:"diskPercent",current:U.percent,points:Q,detail:`${U.path||"/"} mounted ${U.mount||"--"}`,tone:"disk",testId:"metric-chart-disk"})),t("div",{className:"monitor-summary-grid"},t(cf,{label:"CPU 当前",value:vn(j.percent),hint:`history ${Q.length} samples`,tone:"ok"}),t(cf,{label:"实际内存",value:Ll(F.usedBytes),hint:`${vn(F.percent)} 不含缓存`}),t(cf,{label:"硬盘已用",value:Ll(U.usedBytes),hint:vn(U.percent)}),t(cf,{label:"更新时间",value:Nu($.systemUpdatedAt||c.collectedAt),hint:$.providerId})),t(eS,{current:c,onRaw:r}))),t("div",{className:"monitor-side-stack"},t(_m,{provider:$,refresh:n,onRaw:r}),t($m,{provider:$,tasks:l,onRaw:r,limit:5}),t(au,{title:"采样说明",eyebrow:"Retention"},t("div",{className:"monitor-note-list"},t("article",null,t("b",null,"CPU"),t("span",null,"从 /proc/stat 计算相邻采样差值,首个采样用 load/cores 近似")),t("article",null,t("b",null,"Memory"),t("span",null,"实际内存 = MemTotal - MemFree - Buffers - Cached - SReclaimable + Shmem,不把 page cache / buffer 计入占用")),t("article",null,t("b",null,"Disk"),t("span",null,"使用 df -PB1 对配置路径采样,默认监控根文件系统")),t("article",null,t("b",null,"Process"),t("span",null,"从 /proc/[pid] 采集进程 CPU、实际内存 RSS、线程数和磁盘 I/O 速率;表格默认按内存占用降序")))))))}function Iw(u,f){if(f==="memory")return Cu(u.rssBytes);if(f==="cpu")return Cu(u.cpuPercent);if(f==="disk")return Cu(u.readBytesPerSecond)+Cu(u.writeBytesPerSecond);if(f==="pid")return Cu(u.pid);if(f==="threads")return Cu(u.threads);if(f==="runtime")return Cu(u.elapsedSeconds);if(f==="user")return String(u.user||"");return String(u.name||u.command||"")}function gw({value:u,label:f,tone:l}){let r=Math.max(1,Math.min(100,Cu(u)));return t("div",{className:`process-meter ${l||""}`},t("span",{style:{width:`${r}%`}}),t("b",null,f))}function eS({current:u,onRaw:f}){let[l,r]=bu({key:"memory",direction:"desc"}),n=kn.default.useContext(I9),i=u?.processSummary&&typeof u.processSummary==="object"?u.processSummary:{},y=Array.isArray(u?.processes)?u.processes:[],_=P$(()=>{let c=l.direction==="asc"?1:-1;return[...y].sort((A,j)=>{let F=Iw(A,l.key),U=Iw(j,l.key);if(typeof F==="string"||typeof U==="string")return String(F).localeCompare(String(U),"zh-CN")*c;return(F-U)*c||Cu(A.pid)-Cu(j.pid)})},[y,l.key,l.direction]),$=(c,A)=>{let j=l.key===A,F=j?l.direction==="asc"?"ascending":"descending":"none";return t("th",{"aria-sort":F},t("button",{type:"button",className:`process-sort-button ${j?"active":""}`,"data-testid":`process-sort-${A}`,onClick:()=>r((U)=>({key:A,direction:U.key===A&&U.direction==="desc"?"asc":"desc"}))},c,t("span",null,j?l.direction==="desc"?"↓":"↑":"↕")))};return t("section",{className:"process-resource-panel","data-testid":"process-resource-panel"},t("div",{className:"process-resource-head"},t("div",null,t("p",{className:"panel-eyebrow"},"Windows Resource Monitor Style"),t(nf,{title:"进程资源占用",level:3,loading:n})),t("div",{className:"process-resource-actions"},t("span",{className:"data-chip"},"默认按内存排序"),t("span",{className:"data-chip"},`${Cu(i.visible,_.length)} / ${Cu(i.total,_.length)} 进程`),t(bf,{title:"Process Resource Snapshot",data:{processSummary:i,processes:y},onOpen:f,testId:"raw-process-resources"}))),_.length===0?t(Ff,{title:"暂无进程资源数据",text:"等待 provider-gateway 上报 /proc/[pid] 采样;旧版 provider 需要先升级到支持进程资源表的版本"}):t("div",{className:"process-table-wrap"},t("table",{className:"process-resource-table","data-testid":"process-resource-table"},t("thead",null,t("tr",null,$("进程","name"),$("PID","pid"),$("用户","user"),t("th",null,"状态"),$("CPU","cpu"),$("内存","memory"),t("th",null,"RSS"),$("磁盘 I/O","disk"),$("线程","threads"),$("运行时长","runtime"))),t("tbody",null,_.map((c)=>{let A=Cu(c.readBytesPerSecond)+Cu(c.writeBytesPerSecond);return t("tr",{key:`${c.pid}-${c.startedAt}`,"data-testid":`process-row-${sl(c.pid)}`,"data-memory-bytes":String(Cu(c.rssBytes)),"data-cpu-percent":String(Cu(c.cpuPercent)),"data-disk-bps":String(A),"data-pid":String(Cu(c.pid))},t("td",null,t("div",{className:"process-name-cell"},t("strong",null,c.name||"--"),t("span",{className:"process-command"},c.command||"--"))),t("td",null,t("code",null,c.pid||"--")),t("td",null,c.user||`uid:${c.uid??"--"}`),t("td",null,t("span",{className:`process-state state-${sl(c.state||"unknown")}`},c.state||"?")),t("td",null,t(gw,{value:c.cpuPercent,label:VS(c.cpuPercent),tone:"cpu"})),t("td",null,t(gw,{value:c.memoryPercent,label:vn(c.memoryPercent),tone:"memory"})),t("td",null,Ll(c.rssBytes)),t("td",null,t("div",{className:"process-io-cell"},t("strong",null,b9(A)),t("span",null,`R ${b9(c.readBytesPerSecond)} / W ${b9(c.writeBytesPerSecond)}`))),t("td",null,c.threads||0),t("td",null,x0(Cu(c.elapsedSeconds))))})))))}function v9({title:u,metricKey:f,current:l,points:r,detail:n,tone:i,testId:y}){let _=r.map((F)=>Math.max(0,Math.min(100,Cu(F[f])))),$=_.length>1?_:[_[0]||0,_[0]||0],c=$.length<=1?100:100/($.length-1),A=$.map((F,U)=>`${(U*c).toFixed(2)},${(46-F*0.42).toFixed(2)}`).join(" "),j=`0,48 ${A} 100,48`;return t("article",{className:`metric-chart ${i}`,"data-testid":y},t("div",{className:"metric-chart-head"},t("div",null,t("span",null,u),t("strong",null,vn(l))),t("code",null,`${r.length} pts`)),t("svg",{viewBox:"0 0 100 48",preserveAspectRatio:"none",role:"img","aria-label":`${u} usage curve`},t("polygon",{points:j}),t("polyline",{points:A}),t("line",{x1:"0",x2:"100",y1:"24",y2:"24"})),t("div",{className:"metric-chart-foot"},t("span",null,"0%"),t("span",null,n),t("span",null,"100%")))}function h0(u){return Array.isArray(u)?u:[]}function um(u){let f=h0(u?.core?.requests?.componentSummary);return[...h0(u?.frontend?.requests?.componentSummary),...f].sort((r,n)=>Cu(n.requestCount)-Cu(r.requestCount))}function fm(u){let f=h0(u?.core?.operations?.summary);return[...h0(u?.frontend?.operations?.summary),...f].sort((r,n)=>Cu(n.count)-Cu(r.count))}function lm(u){let f=h0(u?.core?.requests?.recentFailures).map((r)=>({source:"backend",...r}));return[...h0(u?.frontend?.requests?.recentFailures).map((r)=>({source:"frontend",...r})),...f].sort((r,n)=>(F0(n.at)??0)-(F0(r.at)??0)).slice(0,20)}function rm(u){let f=h0(u?.core?.operations?.recentSlowOperations);return[...h0(u?.frontend?.operations?.recentSlowOperations),...f].sort((r,n)=>Cu(n.durationMs)-Cu(r.durationMs)).slice(0,20)}function nm(u){let f=performance.memory,l=Number(f?.usedJSHeapSize);if(Number.isFinite(l)&&l>0)return l;let r=Number(u?.appBundleBytes);if(Number.isFinite(r)&&r>0)return r;return Cu(u?.process?.heapUsedBytes)}function im({points:u}){let f=h0(u),l=f.map((F)=>Cu(F.mb)),r=Math.max(1,...l),n=Math.max(0,Math.min(...l,0)),i=Math.max(1,r-n),y=f.length>1?f:[...f,...f],_=y.length<=1?100:100/(y.length-1),$=y.map((F,U)=>{let Q=Cu(F.mb);return`${(U*_).toFixed(2)},${(48-(Q-n)/i*42).toFixed(2)}`}).join(" "),c=`0,50 ${$} 100,50`,A=f.at(-1),j=f[0];return t("article",{className:"performance-memory-card","data-testid":"performance-memory-chart"},t("div",{className:"performance-memory-head"},t("strong",null,`Bwebui: ${A?`${Cu(A.mb).toFixed(1)}MB`:"--"}`),t("span",null,f.length>0?`${f.length} samples`:"等待采样")),t("svg",{viewBox:"0 0 100 50",preserveAspectRatio:"none",role:"img","aria-label":"Bwebui memory trend"},t("polygon",{points:c}),t("polyline",{points:$}),t("line",{x1:"0",x2:"100",y1:"25",y2:"25"})),t("div",{className:"performance-axis-row"},t("span",null,j?$f(new Date(j.at)):"--"),t("span",null,"时间"),t("span",null,A?$f(new Date(A.at)):"--")),t("div",{className:"performance-axis-row"},t("span",null,`${n.toFixed(1)}`),t("span",null,"(MB)"),t("span",null,`${r.toFixed(1)}`)))}function ym({onRaw:u}){let[f,l]=bu({core:null,frontend:null}),[r,n]=bu([]),[i,y]=bu(""),[_,$]=bu(!1),[c,A]=bu(null),[j,F]=bu(!1);async function U(){$(!0),y("");try{let[X,m]=await Promise.all([Eu(`${gu.apiBaseUrl}/performance`,{cache:"no-store"}),Eu(`${gu.apiBaseUrl}/frontend-performance`,{cache:"no-store"})]);l({core:X,frontend:m});let O=nm(m);n((p)=>[...p,{at:new Date().toISOString(),mb:O/1048576}].slice(-80))}catch(X){y(Hu(X,"性能指标加载失败"))}finally{$(!1)}}j0(()=>{U();let X=setInterval(()=>void U(),5000);return()=>clearInterval(X)},[]);async function Q(){F(!0),y(""),A(null);try{let X=await Eu(`${gu.apiBaseUrl}/code-queue-load-test`,{method:"POST",body:JSON.stringify({targetMs:1000,timeoutMs:90000,url:gu.frontendPublicUrl||window.location.origin})});A(X),U()}catch(X){y(Hu(X,"Code Queue Playwright 测量失败"))}finally{F(!1)}}let w=um(f),L=lm(f),J=fm(f),N=rm(f),q=f.core?.process||{},W=f.frontend?.process||{},z=f.core?.database?.codeQueueStorage||{},Z=Cu(z.total),H=c?.result||{},E=Cu(H.wallMs,NaN),D=Cu(H.networkIdleMs,NaN),h=H.withinTarget===!0,B=j?"running":c===null?"idle":c.measurementOk===!0?h?"passed":"slow":"failed";return t("div",{className:"performance-page","data-testid":"performance-page"},t("div",{className:"performance-hero"},t("div",null,t("p",{className:"panel-eyebrow"},"Unified Performance"),t(nf,{title:"性能面板",loading:_||j}),t("p",null,"按组件统计 HTTP 请求、失败率、P95 延迟,并汇总 backend/frontend 内部操作耗时。")),t("div",{className:"inline-actions"},t("button",{type:"button",className:"ghost-btn",onClick:()=>void Q(),disabled:j,"data-testid":"code-queue-load-test-button"},j?"测试中...":"测试 Code Queue 加载"),t("button",{type:"button",className:"ghost-btn",onClick:()=>void U(),disabled:_,"data-testid":"performance-refresh-button"},_?"刷新中":"刷新"),t(bf,{title:"Performance Snapshot",data:f,onOpen:u,testId:"raw-performance"}))),t(yf,{error:i}),t("div",{className:"performance-top-grid"},t(im,{points:r}),t("div",{className:"performance-metric-stack"},t(cf,{label:"backend RSS",value:Ll(q.rssBytes),hint:`heap ${Ll(q.heapUsedBytes)}`}),t(cf,{label:"frontend RSS",value:Ll(W.rssBytes),hint:`bundle ${Ll(f.frontend?.appBundleBytes)}`}),t(cf,{label:"Codex PG 任务",value:Z||"--",hint:z.ok?"unidesk_code_queue_tasks":"等待表初始化",tone:z.ok?"ok":"warn"}),t(cf,{label:"请求样本",value:Cu(f.core?.requests?.sampleCount)+Cu(f.frontend?.requests?.sampleCount),hint:"rolling window 3000"}))),t(au,{title:"Code Queue 加载基准",eyebrow:"Playwright / target <1s",className:"codex-load-test-panel",loading:j,actions:t("div",{className:"panel-actions"},t("button",{type:"button",className:"primary-btn",onClick:()=>void Q(),disabled:j,"data-testid":"code-queue-load-test-panel-button"},j?"正在运行 Playwright...":"手动触发测试"),c?t(bf,{title:"Code Queue Load Test",data:c,onOpen:u,testId:"raw-code-queue-load-test"}):null)},t("div",{className:"codex-load-test-grid","data-testid":"code-queue-load-test-result"},t(cf,{label:"总耗时",value:j?"运行中":Number.isFinite(E)?jr(E):"--",hint:c===null?"点击按钮启动远端 Playwright":`目标 ${jr(H.targetMs||1000)} / ${H.url||"Code Queue"}`,tone:B==="passed"?"ok":B==="failed"||B==="slow"?"warn":""}),t(cf,{label:"判定",value:j?"RUNNING":B==="passed"?"PASS <1s":B==="slow"?"SLOW":B==="failed"?"FAILED":"--",hint:c?.measurementOk===!1?String(c.error||H.error||"measurement failed").slice(0,120):"导航开始 -> DOMContentLoaded -> data-load-state=complete",tone:B==="passed"?"ok":B==="idle"||B==="running"?"":"fail"}),t(cf,{label:"Network idle",value:Number.isFinite(D)?jr(D):"--",hint:`DOMContentLoaded ${jr(H.domContentLoadedMs)} / ${H.networkIdleReached===!1?"未在 5s 内空闲":"已空闲"}`,tone:Number.isFinite(D)&&D<=1000?"ok":"warn"}),t(cf,{label:"组件耗时",value:Number.isFinite(Cu(H.componentLoadMs,NaN))?jr(H.componentLoadMs):"--",hint:`queue ${jr(H.queueMs)} / detail ${jr(H.detailMs)}`,tone:Cu(H.componentLoadMs)>1000?"warn":"ok"}),t(cf,{label:"Trace 规模",value:Number.isFinite(Cu(H.transcriptRows,NaN))?String(H.transcriptRows):"--",hint:`${H.visibleTaskCount??0} visible tasks / ${H.partial?"preview":"complete"}`})),j?t("div",{className:"performance-empty-line"},"正在通过 main-server Host SSH 启动 Playwright,完成后会显示 wall time、组件耗时和最慢 API。"):null,c&&Array.isArray(H.slowestApi)&&H.slowestApi.length>0?t("div",{className:"table-wrap performance-table-wrap compact codex-load-api-table"},t("table",{className:"performance-table"},t("thead",null,t("tr",null,["API","状态","耗时"].map((X)=>t("th",{key:X},X)))),t("tbody",null,H.slowestApi.slice(0,5).map((X,m)=>t("tr",{key:`${X.url}-${m}`},t("td",null,t("code",null,X.url)),t("td",null,X.status),t("td",null,jr(X.durationMs))))))):null),t("div",{className:"performance-grid"},t(au,{title:"组件汇总",eyebrow:"Requests",loading:_},w.length===0?t(Ff,{title:"暂无请求样本",text:"刷新几次或打开页面后会自动形成组件统计"}):t("div",{className:"table-wrap performance-table-wrap"},t("table",{className:"performance-table"},t("thead",null,t("tr",null,["组件","请求数","失败数","失败率","平均延迟","P95"].map((X)=>t("th",{key:X},X)))),t("tbody",null,w.map((X)=>t("tr",{key:X.component},t("td",null,t("code",null,X.component)),t("td",null,X.requestCount),t("td",null,X.failureCount),t("td",null,vn(Cu(X.failureRate)*100)),t("td",null,jr(X.averageLatencyMs)),t("td",null,jr(X.p95LatencyMs)))))))),t(au,{title:"最近失败请求",eyebrow:"Failures",loading:_},L.length===0?t("div",{className:"performance-empty-line"},"最近没有失败请求"):t("div",{className:"table-wrap performance-table-wrap compact"},t("table",{className:"performance-table"},t("thead",null,t("tr",null,["时间","来源","组件","状态","路径"].map((X)=>t("th",{key:X},X)))),t("tbody",null,L.map((X,m)=>t("tr",{key:`${X.at}-${m}`},t("td",null,Nu(X.at)),t("td",null,X.source),t("td",null,t("code",null,X.component)),t("td",null,t(Wf,{status:"failed"},X.status)),t("td",null,t("code",null,X.path)))))))),t(au,{title:"内部操作汇总",eyebrow:"Operations",loading:_},J.length===0?t(Ff,{title:"暂无内部操作样本",text:"API 查询和代理请求会自动记录内部操作耗时"}):t("div",{className:"table-wrap performance-table-wrap"},t("table",{className:"performance-table"},t("thead",null,t("tr",null,["服务","操作","次数","平均延迟","P95"].map((X)=>t("th",{key:X},X)))),t("tbody",null,J.map((X)=>t("tr",{key:`${X.service}-${X.operation}`},t("td",null,X.service),t("td",null,t("code",null,X.operation)),t("td",null,X.count),t("td",null,jr(X.averageLatencyMs)),t("td",null,jr(X.p95LatencyMs)))))))),t(au,{title:"最近慢操作",eyebrow:"Slowest",loading:_},N.length===0?t(Ff,{title:"暂无慢操作",text:"后端会记录最近窗口内耗时最高的内部操作"}):t("div",{className:"table-wrap performance-table-wrap"},t("table",{className:"performance-table"},t("thead",null,t("tr",null,["时间","操作","耗时","结果","细节"].map((X)=>t("th",{key:X},X)))),t("tbody",null,N.map((X,m)=>t("tr",{key:`${X.at}-${X.operation}-${m}`},t("td",null,Nu(X.at)),t("td",null,t("code",null,X.operation)),t("td",null,jr(X.durationMs)),t("td",null,X.ok?"成功":"失败"),t("td",null,X.detail||"-")))))))))}function _m({provider:u,refresh:f,onRaw:l}){let[r,n]=bu(""),[i,y]=bu(null),[_,$]=bu("");async function c(A){n(A),$("");try{let j=await Eu(`${gu.apiBaseUrl}/dispatch`,{method:"POST",body:JSON.stringify({providerId:u.providerId,command:"provider.upgrade",payload:{mode:A,source:"frontend-resource-monitor",requestedAt:new Date().toISOString()}})});y({mode:A,...j}),await f()}catch(j){$(Hu(j,"升级命令下发失败"))}finally{n("")}}return t(au,{title:"Provider Gateway 升级",eyebrow:"Remote Control",loading:Boolean(r)},t("div",{className:"upgrade-control","data-testid":"provider-upgrade-control"},t("p",null,"通过 UniDesk WebSocket 向当前计算节点下发 provider.upgrade;预检只生成升级计划,执行升级会调度节点本地 updater 容器。"),t("div",{className:"upgrade-target-line"},t("span",null,"指定 Provider"),t("code",null,u.providerId),t(qy,{node:u})),t("div",{className:"upgrade-actions"},t("button",{type:"button",className:"ghost-btn",disabled:Boolean(r),onClick:()=>c("plan"),"data-testid":"upgrade-plan-button"},r==="plan"?"预检中":"预检升级"),t("button",{type:"button",className:"ghost-btn danger",disabled:Boolean(r),onClick:()=>c("schedule"),"data-testid":"upgrade-schedule-button"},r==="schedule"?"调度中":"执行升级")),t(yf,{error:_}),i?t("div",{className:"upgrade-result"},t(Wf,{status:i.status||"queued"},i.status||"queued"),t("span",null,`${i.mode==="schedule"?"执行升级":"预检升级"} 已下发`),t("span",null,`指定版本 ${a9(rL(u))}`),t("code",null,i.taskId||"--"),t(bf,{title:"Provider Upgrade Dispatch",data:i,onOpen:l})):t("span",{className:"muted"},"升级任务结果会进入任务历史;执行升级可能导致 provider 短暂重连。")))}function FL({records:u,onRaw:f,compact:l=!1}){if(u.length===0)return t(Ff,{title:"暂无远程更新记录",text:"该节点还没有 provider.upgrade 任务;执行预检或升级后会在这里形成结构化记录"});return t("div",{className:`upgrade-record-table-wrap table-wrap ${l?"compact":""}`},t("table",{className:"upgrade-record-table"},t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"模式"),t("th",null,"任务"),t("th",null,"来源"),t("th",null,"耗时"),t("th",null,"策略"),t("th",null,"Gateway 版本"),t("th",null,"结果记录"),t("th",null,"更新时间"),t("th",null,"操作"))),t("tbody",null,u.map((r)=>t("tr",{key:r.id,"data-testid":`gateway-upgrade-record-${sl(r.id)}`},t("td",null,t(Wf,{status:r.status})),t("td",null,t("span",{className:`mode-chip ${O8(r)}`},O8(r)==="schedule"?"执行升级":"预检")),t("td",null,t("strong",null,"provider.upgrade"),t("code",null,r.id)),t("td",null,SS(r)),t("td",null,t(UL,{task:r})),t("td",null,mS(r)),t("td",null,t("span",{className:"version-chip"},_L(r))),t("td",null,t("span",{className:`upgrade-outcome ${String(r.status||"").toLowerCase()}`},$L(r))),t("td",null,Nu(r.updatedAt)),t("td",null,t(bf,{title:`Provider Upgrade Task ${r.id}`,data:M$(r),onOpen:f})))))))}function $m({provider:u,tasks:f,onRaw:l,limit:r=5}){let n=tL(f,u.providerId).slice(0,r);return t(au,{title:"远程更新记录",eyebrow:u.providerId,actions:t(qy,{node:u}),className:"provider-upgrade-records-panel"},t("div",{"data-testid":`provider-upgrade-records-${sl(u.providerId)}`},t(FL,{records:n,onRaw:l,compact:!0})))}function tm({nodes:u,tasks:f,onRaw:l}){let r=P$(()=>u.map((i)=>{let y=tL(f,i.providerId);return{node:i,records:y,latest:pS(y),capabilities:nL(i)}}),[u,f]),n=r.reduce((i,y)=>i+y.records.length,0);return t("div",{className:"gateway-page","data-testid":"gateway-version-page"},t(au,{title:"Provider Gateway 版本",eyebrow:`${u.length} Providers / ${n} 更新记录`},u.length===0?t(Ff,{title:"暂无 Provider 节点",text:"等待 provider-gateway 注册后显示版本号和升级记录"}):t("div",{className:"table-wrap gateway-version-table-wrap"},t("table",{className:"gateway-version-table"},t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"Provider"),t("th",null,"Gateway 版本"),t("th",null,"升级策略"),t("th",null,"运维可用性"),t("th",null,"运行时间"),t("th",null,"能力"),t("th",null,"最近远程更新"),t("th",null,"操作"))),t("tbody",null,r.map((i)=>t("tr",{key:i.node.providerId},t("td",null,t(Wf,{status:i.node.status})),t("td",null,t("strong",null,i.node.name),t("code",null,i.node.providerId)),t("td",null,t(qy,{node:i.node})),t("td",null,p$(i.node)),t("td",null,t(o9,{node:i.node})),t("td",null,hw(i.node)?Nu(hw(i.node)):"待新版上报"),t("td",null,t("div",{className:"capability-row"},i.capabilities.length===0?t("span",{className:"muted"},"未声明"):i.capabilities.slice(0,5).map((y)=>t("span",{key:y,className:"data-chip"},y)))),t("td",null,i.latest?t("div",{className:"latest-upgrade-cell"},t(Wf,{status:i.latest.status}),t("span",null,`${O8(i.latest)==="schedule"?"执行升级":"预检"} / ${Nu(i.latest.updatedAt)}`),t("small",null,`Gateway ${_L(i.latest)}`),t("small",null,$L(i.latest))):t("span",{className:"muted"},"暂无记录")),t("td",null,t(bf,{title:`Provider ${i.node.providerId}`,data:i.node,onOpen:l})))))))),t(au,{title:"远程更新记录",eyebrow:"Structured provider.upgrade records"},u.length===0?t(Ff,{title:"暂无记录",text:"没有 provider 节点时不会生成远程更新记录"}):t("div",{className:"gateway-record-grid"},r.map((i)=>t("article",{key:i.node.providerId,className:"gateway-record-card","data-testid":`gateway-records-${sl(i.node.providerId)}`},t("div",{className:"gateway-record-head"},t("div",null,t("strong",null,i.node.name),t("code",null,i.node.providerId)),t(qy,{node:i.node})),t("div",{className:"gateway-record-meta"},t("span",null,`心跳 ${Nu(i.node.lastHeartbeat)}`),t("span",null,`策略 ${p$(i.node)}`),t("span",null,`${i.records.length} 条记录`)),t(FL,{records:i.records.slice(0,8),onRaw:l,compact:!0}))))))}function cm(u){if(u==="running")return"online";if(u==="paused"||u==="restarting")return"warn";if(u==="exited"||u==="dead")return"offline";return"internal"}function JL(u){return/^[a-f0-9]{48,64}$/i.test(u)}function m$(u){let f=String(u?.name||""),l=String(u?.labels||"");return f==="unidesk_pgdata_10gb"||l.includes("com.docker.compose.volume=unidesk_pgdata_10gb")||f.toLowerCase().includes("pgdata")}function sw(u){let f=String(u?.name||""),l=String(u?.labels||"");if(m$(u))return 0;if(l.includes("com.docker.compose.project=unidesk"))return 1;if(!JL(f))return 2;return 3}function Am(u){return[...u].sort((f,l)=>{let r=sw(f)-sw(l);if(r!==0)return r;return String(f.name||"").localeCompare(String(l.name||""))})}function jm({nodes:u,dockerStatuses:f,onRaw:l}){let[r,n]=bu(""),i=P$(()=>u.map((N)=>{let q=f.find((W)=>W.providerId===N.providerId);return{...N,dockerStatus:q?.dockerStatus||null,dockerUpdatedAt:q?.updatedAt||null}}),[u,f]),y=i.find((N)=>N.providerId===r)||i[0]||null;if(j0(()=>{if(!r&&i[0])n(i[0].providerId)},[i.length,r]),!y)return t(Ff,{title:"暂无 Docker 节点",text:"等待 provider 上报 Docker daemon 状态"});let _=y.dockerStatus,$=y.providerId==="main-server",c=_?.counts||{},A=_?.daemon||{},j=_?.containers||[],F=_?.images||[],U=Am(_?.volumes||[]),Q=$?U.find(m$):null,w=_?.networks||[],L=j.filter((N)=>N.state==="running"),J=j.filter((N)=>N.state!=="running");return t("div",{className:"docker-page","data-testid":"docker-status-page"},t("div",{className:"docker-node-strip"},i.map((N)=>t("button",{key:N.providerId,type:"button",className:`docker-node-tile ${y.providerId===N.providerId?"active":""}`,onClick:()=>n(N.providerId)},t("span",{className:`pulse ${N.status}`}),t("strong",null,N.name),t("code",null,N.providerId),t("span",null,N.dockerStatus?`Docker ${N.dockerStatus.ok?"ready":"degraded"}`:"等待上报")))),t("div",{className:"docker-layout"},t(au,{title:"Docker Desktop 视图",eyebrow:y.name,className:"docker-main-panel",actions:_?t(bf,{title:`Docker ${y.providerId}`,data:_,onOpen:l}):null},!_?t(Ff,{title:"Docker 状态未上报",text:"provider-gateway 会在连接后周期性采集 docker info / ps / images / volume / network"}):t("div",null,t("div",{className:"docker-hero"},t("div",null,t("p",{className:"panel-eyebrow"},"Daemon"),t("h3",null,A.name||y.providerId),t("div",{className:"docker-meta"},t("span",null,A.serverVersion?`Engine ${A.serverVersion}`:"Engine --"),t("span",null,A.operatingSystem||"OS --"),t("span",null,A.architecture||"arch --"),t("span",null,`${A.cpus||0} CPU / ${Ll(A.memoryBytes)}`))),t(Wf,{status:_.ok?"online":"warn"},_.ok?"Docker Ready":"Docker Degraded")),t("div",{className:"docker-metrics"},t(cf,{label:"Containers",value:c.containers??j.length,hint:`${c.running??L.length} running / ${c.stopped??J.length} stopped`,tone:"ok"}),t(cf,{label:"Images",value:c.images??F.length,hint:`${c.daemonImages??c.images??F.length} daemon images`}),t(cf,{label:"Volumes",value:c.volumes??U.length,hint:$?Q?"database volume visible":"database volume missing":"node local volumes",tone:Q?"ok":""}),t(cf,{label:"Networks",value:c.networks??w.length,hint:A.driver?`driver ${A.driver}`:"docker networks"})),$?t(Fm,{volume:Q,volumeCount:U.length}):null,t("div",{className:"docker-section-head"},t("h3",null,"Containers"),t("span",null,`updated ${Nu(y.dockerUpdatedAt||_.collectedAt)}`)),t("div",{className:"docker-container-table table-wrap","data-testid":"docker-container-table"},t("table",null,t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"容器"),t("th",null,"镜像"),t("th",null,"端口"),t("th",null,"运行时间"),t("th",null,"重启策略"),t("th",null,"PID"),t("th",null,"大小"))),t("tbody",null,j.length===0?t("tr",null,t("td",{colSpan:8},"暂无容器")):j.map((N)=>t("tr",{key:`${N.id}-${N.name}`},t("td",null,t(Wf,{status:cm(N.state)},N.state||"unknown")),t("td",null,t("strong",null,N.name||"--"),t("code",null,N.id||"--")),t("td",null,N.image||"--"),t("td",null,N.ports||t("span",{className:"muted"},"未发布")),t("td",null,N.runningFor||N.status||"--"),t("td",null,N.restartPolicy?t(Wf,{status:N.restartPolicy==="always"?"online":"warn"},N.restartPolicy):"--"),t("td",null,N.pidMode?t("code",null,N.pidMode):"--"),t("td",null,N.size||"--")))))))),t("div",{className:"docker-side-stack"},t(k9,{title:"Images",items:F,render:(N)=>t("article",{key:`${N.id}-${N.repository}`,className:"docker-side-row"},t("strong",null,`${N.repository}:${N.tag}`),t("span",null,N.size||"--"),t("code",null,N.id||"--"))}),t(k9,{title:"Volumes",items:U,limit:U.length,render:(N)=>t("article",{key:N.name,className:`docker-side-row volume-row ${$&&m$(N)?"database-volume":""}`,"data-testid":$&&m$(N)?"database-volume-row":void 0},t("strong",null,N.name),t("span",null,$&&m$(N)?"PostgreSQL":JL(String(N.name||""))?"anonymous":"named"),t("code",null,N.mountpoint||N.driver||N.scope||"--"))}),t(k9,{title:"Networks",items:w,render:(N)=>t("article",{key:N.id||N.name,className:"docker-side-row"},t("strong",null,N.name),t("span",null,N.driver||"--"),t("code",null,N.id||"--"))}))))}function Fm({volume:u,volumeCount:f}){return t("section",{className:`docker-volume-focus ${u?"ready":"missing"}`,"data-testid":"database-volume-card"},t("div",{className:"volume-focus-head"},t("span",{className:"panel-eyebrow"},"Database Named Volume"),t(Wf,{status:u?"online":"warn"},u?"FOUND":"MISSING")),u?t("div",{className:"volume-focus-body"},t("strong",null,u.name),t("span",null,"PostgreSQL data volume for unidesk-database"),t("div",{className:"volume-route"},t("code",null,u.mountpoint||"/var/lib/docker/volumes/unidesk_pgdata_10gb/_data"),t("span",null,"->"),t("code",null,"unidesk-database:/var/lib/postgresql/data")),t("div",{className:"docker-meta compact"},t("span",null,`driver ${u.driver||"--"}`),t("span",null,`scope ${u.scope||"--"}`),t("span",null,`${f} volumes reported`))):t("div",{className:"volume-focus-body"},t("strong",null,"unidesk_pgdata_10gb"),t("span",null,"当前 Docker 快照没有发现数据库命名卷;请检查 provider-gateway 的 Docker volume 上报。")))}function k9({title:u,items:f,render:l,limit:r}){let n=f.slice(0,r??12),i=Math.max(0,f.length-n.length);return t(au,{title:u,eyebrow:`${f.length} items`,className:"docker-side-panel"},f.length===0?t(Ff,{title:`暂无 ${u}`,text:"等待 Docker 状态采集"}):t("div",{className:"docker-side-list"},n.map(l),i>0?t("div",{className:"docker-side-more"},`+ ${i} more`):null))}function Jm({microservices:u,onRaw:f,onNavigate:l}){let r=u.filter((n)=>vw(n).public===!1);return t("div",{className:"microservice-page","data-testid":"microservice-catalog-page"},t(au,{title:"用户服务目录",eyebrow:"Provider Mounted User Services"},t("div",{className:"metric-grid"},t(cf,{label:"服务总数",value:u.length,hint:"config.json 用户服务登记"}),t(cf,{label:"私有后端",value:r.length,hint:"不直接暴露公网",tone:"ok"}),t(cf,{label:"D601 服务",value:u.filter((n)=>n.providerId==="D601").length,hint:"compute-node docker"}),t(cf,{label:"集成前端",value:u.filter((n)=>n.frontend?.integrated).length,hint:"UniDesk React 页面"}))),t(au,{title:"服务映射",eyebrow:"Repo Reference + Runtime"},u.length===0?t(Ff,{title:"暂无用户服务",text:"在 config.json 的 microservices 中登记用户服务的 provider、仓库引用和后端映射"}):t("div",{className:"table-wrap"},t("table",{className:"microservice-table"},t("thead",null,t("tr",null,t("th",null,"服务"),t("th",null,"Provider"),t("th",null,"代码引用"),t("th",null,"Docker 引用"),t("th",null,"后端映射"),t("th",null,"开发入口"),t("th",null,"运行态"),t("th",null,"操作"))),t("tbody",null,u.map((n)=>{let i=cL(n),y=PS(n),_=vw(n),$=i.availability||{},c=$.status||(i.providerStatus==="online"?"unknown":"unhealthy");return t("tr",{key:n.id,"data-testid":`microservice-row-${sl(n.id)}`},t("td",null,t("strong",null,n.name),t("code",null,n.id)),t("td",null,t("strong",null,i.providerName||n.providerId),t("code",null,n.providerId)),t("td",null,t("span",null,y.url||"--"),t("code",null,y.commitId||"--")),t("td",null,t("span",null,y.composeFile||"--"),t("code",null,`${y.composeService||"--"} / ${y.containerName||"--"}`)),t("td",null,t(Wf,{status:_.public?"warn":"online"},_.public?"public":"private"),t("code",null,`${_.nodeBindHost||"--"}:${_.nodePort||"--"} -> ${_.proxyMode||"--"}`)),t("td",null,t("span",null,n.development?.sshPassthrough?"SSH 透传":"未配置"),t("code",null,n.development?.worktreePath||"--")),t("td",null,t(Wf,{status:c==="healthy"?"online":c==="unknown"?"warn":"failed"},c),t("span",null,$.reason||i.providerStatus||"unknown"),t(In,{data:i.container,empty:"容器快照未上报"})),t("td",null,t("div",{className:"microservice-actions"},n.id==="findjob"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","findjob"),"data-testid":"open-findjob-button"},"打开"):null,n.id==="pipeline"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","pipeline"),"data-testid":"open-pipeline-button"},"打开"):null,n.id==="todo-note"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","todo-note"),"data-testid":"open-todo-note-button"},"打开"):null,n.id==="met-nonlinear"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","met-nonlinear"),"data-testid":"open-met-nonlinear-button"},"打开"):null,n.id==="claudeqq"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","claudeqq"),"data-testid":"open-claudeqq-button"},"打开"):null,n.id==="baidu-netdisk"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","baidu-netdisk"),"data-testid":"open-baidu-netdisk-button"},"打开"):null,n.id==="oa-event-flow"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","oa-event-flow"),"data-testid":"open-oa-event-flow-button"},"打开"):null,n.id==="code-queue"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","code-queue"),"data-testid":"open-code-queue-button"},"打开"):null,n.id==="project-manager"?t("button",{type:"button",className:"ghost-btn",onClick:()=>l("apps","project-manager"),"data-testid":"open-project-manager-button"},"打开"):null,t(bf,{title:`用户服务 ${n.id}`,data:n,onOpen:f}))))}))))))}function Um({nodes:u,onDispatched:f,onRaw:l}){let r=u.filter((B)=>B.status==="online"),[n,i]=bu(r[0]?.providerId||u[0]?.providerId||""),[y,_]=bu("docker.ps"),[$,c]=bu("frontend"),[A,j]=bu("operator-check"),[F,U]=bu("normal"),[Q,w]=bu(!1),[L,J]=bu(""),[N,q]=bu(!1),[W,z]=bu(null),[Z,H]=bu("");j0(()=>{if(!n&&(r[0]?.providerId||u[0]?.providerId))i(r[0]?.providerId||u[0].providerId)},[u.length,r.length,n]);function E(){return{source:$,note:A,priority:F}}function D(){J(JSON.stringify(E(),null,2)),w(!0)}async function h(B){B.preventDefault(),q(!0),H("");try{let X=Q?JSON.parse(L||"{}"):E(),m=await Eu(`${gu.apiBaseUrl}/dispatch`,{method:"POST",body:JSON.stringify({providerId:n,command:y,payload:X})});z(m),await f()}catch(X){H(Hu(X,"下发失败"))}finally{q(!1)}}return t("div",{className:"page-grid dispatch-grid"},t(au,{title:"下发任务",eyebrow:"Real WebSocket Dispatch"},t("form",{className:"dispatch-form",onSubmit:h},t("label",null,"Provider",t("select",{value:n,onChange:(B)=>i(B.target.value)},u.map((B)=>t("option",{key:B.providerId,value:B.providerId},`${B.name} / ${B.providerId}`)))),t("label",null,"Command",t("select",{value:y,onChange:(B)=>_(B.target.value)},t("option",{value:"docker.ps"},"docker.ps"),t("option",{value:"host.ssh"},"host.ssh"),t("option",{value:"microservice.http"},"microservice.http"),t("option",{value:"echo"},"echo"))),t("label",null,"来源",t("input",{value:$,onChange:(B)=>c(B.target.value)})),t("label",null,"备注",t("input",{value:A,onChange:(B)=>j(B.target.value)})),t("label",null,"优先级",t("select",{value:F,onChange:(B)=>U(B.target.value)},t("option",{value:"normal"},"normal"),t("option",{value:"low"},"low"),t("option",{value:"urgent"},"urgent"))),t("div",{className:"dispatch-actions"},t("button",{type:"button",className:"ghost-btn",onClick:D},"查看原始JSON"),t("button",{type:"submit",disabled:N||!n},N?"下发中":"下发任务")),Q?t("label",{className:"raw-editor-label"},"高级 Payload",t("textarea",{className:"raw-editor",value:L,onChange:(B)=>J(B.target.value)})):null,t(yf,{error:Z,wide:!0}))),t(au,{title:"下发结果",eyebrow:"Response"},W?t("div",{className:"result-card"},t(Wf,{status:W.status||"queued"},W.status||"queued"),t("dl",null,t("dt",null,"Task ID"),t("dd",null,t("code",null,W.taskId||"--")),t("dt",null,"Provider 在线"),t("dd",null,R1(W.providerOnline))),t(bf,{title:"Dispatch Response",data:W,onOpen:l})):t(Ff,{title:"等待操作",text:"任务响应会以结构化结果卡展示"})))}function aw({task:u,onRaw:f}){return t("article",{className:"compact-row"},t(Wf,{status:u.status}),t("div",null,t("strong",null,u.command),t("code",null,u.id)),t("span",null,Ny(u)?`已等待 ${g9(u.updatedAt)}`:`耗时 ${x0(uL(u)??0)}`),t(bf,{title:`Task ${u.id}`,data:M$(u),onOpen:f}))}function UL({task:u}){let f=uL(u),l=Ny(u);return t("div",{className:"task-duration"},t("strong",null,f===null?"--":x0(f)),t("span",null,l?`已运行 / 创建 ${Nu(u.createdAt)}`:`创建 ${Nu(u.createdAt)}`))}function Qm({task:u}){let f=String(u?.status||"").toLowerCase(),l=u?.result,r=l&&typeof l==="object"&&!Array.isArray(l)?l:{},i=["exitCode","code","signal","timeoutMs","previousStatus","mode"].filter((y)=>r[y]!==void 0&&r[y]!==null);if(f==="failed"){let y=fL(u);return t("div",{className:"task-diagnostic failed"},t("b",null,"失败原因"),t("span",{className:"diagnostic-reason"},R1(y)),i.length>0?t("div",{className:"diagnostic-meta"},i.map((_)=>t("span",{key:_,className:"data-chip"},t("b",null,_),t("span",null,R1(r[_]))))):null)}if(Ny(u))return t("div",{className:"task-diagnostic warn"},t("b",null,"等待终态"),t("span",null,`最后更新 ${g9(u.updatedAt)} 前`));return t("div",{className:"task-diagnostic ok"},t("b",null,"完成摘要"),t(In,{data:l,empty:"无执行输出"}))}function qm({tasks:u,onRaw:f}){let l=u.filter(Ny);return t("div",{"data-testid":"pending-task-page"},t(au,{title:"待处理任务",eyebrow:`${l.length} Pending`},l.length===0?t(Ff,{title:"当前无待处理任务",text:"queued / dispatched / running 会在超时后自动转为 failed;历史记录仍可在任务历史中查看"}):t("div",{className:"table-wrap","data-testid":"pending-task-table"},t("table",null,t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"任务"),t("th",null,"Provider"),t("th",null,"已等待"),t("th",null,"载荷摘要"),t("th",null,"操作"))),t("tbody",null,l.map((r)=>t("tr",{key:r.id},t("td",null,t(Wf,{status:r.status})),t("td",null,t("strong",null,r.command),t("code",null,r.id)),t("td",null,t("code",null,r.providerId)),t("td",null,g9(r.updatedAt)),t("td",null,t(In,{data:r.payload})),t("td",null,t(bf,{title:`Pending Task ${r.id}`,data:M$(r),onOpen:f})))))))))}function Nm({tasks:u,onRaw:f}){return t("div",{"data-testid":"task-history-page"},t(au,{title:"任务历史",eyebrow:`${u.length} Tasks`},u.length===0?t(Ff,{title:"暂无任务",text:"下发任务后会在这里看到生命周期"}):t("div",{className:"table-wrap"},t("table",{className:"task-history-table"},t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"任务"),t("th",null,"Provider"),t("th",null,"任务耗时"),t("th",null,"载荷摘要"),t("th",null,"诊断信息"),t("th",null,"更新时间"),t("th",null,"操作"))),t("tbody",null,u.map((l)=>t("tr",{key:l.id,"data-testid":`task-row-${sl(l.id)}`},t("td",null,t(Wf,{status:l.status})),t("td",null,t("strong",null,l.command),t("code",null,l.id)),t("td",null,t("code",null,l.providerId)),t("td",null,t(UL,{task:l})),t("td",null,t(In,{data:l.payload})),t("td",null,t(Qm,{task:l})),t("td",null,Nu(l.updatedAt)),t("td",null,t(bf,{title:`Task ${l.id}`,data:M$(l),onOpen:f})))))))))}function Wm({tasks:u,onRaw:f}){let l=u.filter((r)=>["succeeded","failed"].includes(r.status));return t(au,{title:"执行结果",eyebrow:"Finished Tasks"},l.length===0?t(Ff,{title:"暂无结果",text:"任务完成后展示 provider 返回的结构化摘要"}):t("div",{className:"result-grid"},l.map((r)=>t("article",{key:r.id,className:"result-card"},t("div",{className:"node-card-head"},t("strong",null,r.command),t(Wf,{status:r.status})),t("code",null,r.id),t(In,{data:r.result,empty:"无执行输出"}),t(bf,{title:`Task Result ${r.id}`,data:M$(r),onOpen:f})))))}function wm(u){if(!u||typeof u!=="object")return"--";if(u.type==="interval")return`每 ${x0(Number(u.everySeconds||0))}`;return`每天 ${u.timeOfDay||"03:00"} UTC`}function Lm(u){if(!u||typeof u!=="object")return"--";if(u.type==="pgdata_backup")return`PGDATA -> ${u.remoteBaseDir||"/SERVER_DATA/UNIDESK_PG_DATA"}`;if(u.type==="dispatch")return`${u.providerId||"--"} / ${u.command||"--"}`;return String(u.type||"--")}function Km(u){let f=String(u||"").toLowerCase();if(f==="succeeded")return"online";if(f==="failed")return"failed";if(f==="running"||f==="queued")return"warn";return f}function Gm(u){let f=Number(u?.durationMs);if(Number.isFinite(f)&&f>=0)return x0(f/1000);let l=F0(u?.startedAt||u?.createdAt);if(l===null)return"--";let n=F0(u?.finishedAt)??Date.now();return x0(Math.max(0,(n-l)/1000))}function ow(u){return{id:"unidesk-pgdata-baidu-daily",name:"PGDATA daily Baidu Netdisk backup",description:"Daily PostgreSQL physical base backup uploaded to Baidu Netdisk /SERVER_DATA with monthly rotation.",enabled:!0,timeOfDay:"03:30",actionType:"pgdata_backup",providerId:u[0]?.providerId||"main-server",command:"echo",payloadJson:JSON.stringify({source:"scheduled-task",message:"hello from scheduler"},null,2),remoteBaseDir:"/SERVER_DATA/UNIDESK_PG_DATA",stagingSubdir:"server-data/unidesk-pg-data",timeoutMs:"3600000"}}function zm({schedules:u,scheduleRuns:f,nodes:l,refresh:r,onRaw:n}){let[i,y]=bu(ow(l||[])),[_,$]=bu(!1),[c,A]=bu(""),[j,F]=bu(""),U=[...f||[]].sort((W,z)=>(F0(z.updatedAt)??0)-(F0(W.updatedAt)??0));function Q(W,z){y((Z)=>({...Z,[W]:z}))}function w(W){let z=W?.action||{};y({id:W?.id||"",name:W?.name||"",description:W?.description||"",enabled:W?.enabled!==!1,timeOfDay:W?.schedule?.timeOfDay||"03:30",actionType:z.type||"dispatch",providerId:z.providerId||l[0]?.providerId||"main-server",command:z.command||"echo",payloadJson:JSON.stringify(z.payload||{source:"scheduled-task"},null,2),remoteBaseDir:z.remoteBaseDir||"/SERVER_DATA/UNIDESK_PG_DATA",stagingSubdir:z.stagingSubdir||"server-data/unidesk-pg-data",timeoutMs:String(z.timeoutMs||3600000)}),F(`正在编辑 ${W?.id||""}`)}function L(){let W={id:i.id,name:i.name,description:i.description,enabled:i.enabled,concurrencyPolicy:"skip",schedule:{type:"daily",timeOfDay:i.timeOfDay,timezone:"Etc/UTC"}};if(i.actionType==="pgdata_backup")return{...W,action:{type:"pgdata_backup",volumeName:"unidesk_pgdata_10gb",remoteBaseDir:i.remoteBaseDir,stagingSubdir:i.stagingSubdir,timeoutMs:Number(i.timeoutMs)||3600000,cleanupLocal:!0}};return{...W,action:{type:"dispatch",providerId:i.providerId,command:i.command,payload:JSON.parse(i.payloadJson||"{}"),timeoutMs:Number(i.timeoutMs)||600000}}}async function J(W){W.preventDefault(),$(!0),A(""),F("");try{let z=L(),Z=encodeURIComponent(String(z.id));await Eu(`${gu.apiBaseUrl}/schedules/${Z}`,{method:"PUT",body:JSON.stringify(z)}),F("定时任务已保存"),await r()}catch(z){A(Hu(z,"保存定时任务失败"))}finally{$(!1)}}async function N(W){if(!W?.id)return;$(!0),A(""),F("");try{await Eu(`${gu.apiBaseUrl}/schedules/${encodeURIComponent(W.id)}`,{method:"DELETE"}),F(`已删除 ${W.id}`),await r()}catch(z){A(Hu(z,"删除定时任务失败"))}finally{$(!1)}}async function q(W){if(!W?.id)return;$(!0),A(""),F("");try{let z=await Eu(`${gu.apiBaseUrl}/schedules/${encodeURIComponent(W.id)}/run`,{method:"POST",body:"{}"});F(`已触发 ${W.id} / ${z?.run?.id||"run"}`),await r()}catch(z){A(Hu(z,"触发定时任务失败"))}finally{$(!1)}}return t("div",{className:"page-grid scheduled-task-page","data-testid":"scheduled-task-page"},t(au,{title:"定时任务",eyebrow:`${(u||[]).length} Schedules`},(u||[]).length===0?t(Ff,{title:"暂无定时任务",text:"创建 daily / dispatch / PGDATA backup 任务后会在这里展示下一次执行时间和最近结果"}):t("div",{className:"schedule-card-grid"},(u||[]).map((W)=>t("article",{key:W.id,className:"schedule-card","data-testid":`schedule-row-${sl(W.id)}`},t("div",{className:"node-card-head"},t("strong",null,W.name||W.id),t(Wf,{status:W.enabled?"online":"warn"},W.enabled?"enabled":"disabled")),t("code",null,W.id),t("dl",null,t("dt",null,"计划"),t("dd",null,wm(W.schedule)),t("dt",null,"动作"),t("dd",null,Lm(W.action)),t("dt",null,"下次执行"),t("dd",null,Nu(W.nextRunAt)),t("dt",null,"最近执行"),t("dd",null,W.lastRunAt?`${Nu(W.lastRunAt)} / ${W.lastRunId||"--"}`:"--")),t("div",{className:"dispatch-actions"},t("button",{type:"button",className:"ghost-btn",disabled:_,onClick:()=>w(W)},"编辑"),t("button",{type:"button",className:"ghost-btn",disabled:_,onClick:()=>q(W),"data-testid":`schedule-run-${sl(W.id)}`},"手动触发"),t("button",{type:"button",className:"ghost-btn danger",disabled:_,onClick:()=>N(W)},"删除"),t(bf,{title:`Schedule ${W.id}`,data:W,onOpen:n})))))),t(au,{title:i.id?"配置定时任务":"新建定时任务",eyebrow:"CRUD"},t("form",{className:"dispatch-form schedule-form",onSubmit:J},t("label",null,"ID",t("input",{value:i.id,onChange:(W)=>Q("id",W.target.value)})),t("label",null,"名称",t("input",{value:i.name,onChange:(W)=>Q("name",W.target.value)})),t("label",null,"每日执行时间 UTC",t("input",{value:i.timeOfDay,placeholder:"03:30",onChange:(W)=>Q("timeOfDay",W.target.value)})),t("label",null,"启用",t("select",{value:i.enabled?"true":"false",onChange:(W)=>Q("enabled",W.target.value==="true")},t("option",{value:"true"},"enabled"),t("option",{value:"false"},"disabled"))),t("label",null,"动作类型",t("select",{value:i.actionType,onChange:(W)=>Q("actionType",W.target.value)},t("option",{value:"pgdata_backup"},"PGDATA 备份到百度网盘"),t("option",{value:"dispatch"},"Provider Dispatch"))),i.actionType==="pgdata_backup"?[t("label",{key:"remote"},"网盘根目录",t("input",{value:i.remoteBaseDir,onChange:(W)=>Q("remoteBaseDir",W.target.value)})),t("label",{key:"staging"},"本地 staging 子目录",t("input",{value:i.stagingSubdir,onChange:(W)=>Q("stagingSubdir",W.target.value)}))]:[t("label",{key:"provider"},"Provider",t("select",{value:i.providerId,onChange:(W)=>Q("providerId",W.target.value)},(l||[]).map((W)=>t("option",{key:W.providerId,value:W.providerId},`${W.name} / ${W.providerId}`)))),t("label",{key:"command"},"Command",t("select",{value:i.command,onChange:(W)=>Q("command",W.target.value)},t("option",{value:"echo"},"echo"),t("option",{value:"docker.ps"},"docker.ps"),t("option",{value:"host.ssh"},"host.ssh"),t("option",{value:"microservice.http"},"microservice.http"))),t("label",{key:"payload",className:"raw-editor-label"},"Payload JSON",t("textarea",{className:"raw-editor",value:i.payloadJson,onChange:(W)=>Q("payloadJson",W.target.value)}))],t("label",null,"超时 ms",t("input",{value:i.timeoutMs,onChange:(W)=>Q("timeoutMs",W.target.value)})),t("label",{className:"raw-editor-label"},"描述",t("textarea",{className:"raw-editor compact",value:i.description,onChange:(W)=>Q("description",W.target.value)})),t("div",{className:"dispatch-actions"},t("button",{type:"button",className:"ghost-btn",disabled:_,onClick:()=>y(ow(l||[]))},"重置"),t("button",{type:"submit",disabled:_||!i.id},_?"保存中":"保存任务")),j?t("p",{className:"muted paragraph"},j):null,t(yf,{error:c,wide:!0}))),t(au,{title:"历史执行记录",eyebrow:`${U.length} Runs`},U.length===0?t(Ff,{title:"暂无执行记录",text:"定时触发或手动触发后会生成 run history"}):t("div",{className:"table-wrap"},t("table",{className:"task-history-table schedule-run-table"},t("thead",null,t("tr",null,t("th",null,"状态"),t("th",null,"任务"),t("th",null,"触发"),t("th",null,"耗时"),t("th",null,"结果摘要"),t("th",null,"更新时间"),t("th",null,"操作"))),t("tbody",null,U.map((W)=>t("tr",{key:W.id,"data-testid":`schedule-run-row-${sl(W.id)}`},t("td",null,t(Wf,{status:Km(W.status)},W.status)),t("td",null,t("strong",null,W.scheduleId),t("code",null,W.id),W.taskId?t("code",null,W.taskId):null),t("td",null,W.trigger||"--"),t("td",null,Gm(W)),t("td",null,t(In,{data:W.result||W.error,empty:"无结果"})),t("td",null,Nu(W.updatedAt)),t("td",null,t(bf,{title:`Schedule Run ${W.id}`,data:W,onOpen:n})))))))))}function Tm({data:u}){let f=u.overview||{};return t("div",{className:"page-grid topology-grid"},t(au,{title:"公开入口",eyebrow:"Public"},t("div",{className:"endpoint-list"},t("article",null,t("b",null,"Frontend"),t("span",null,gu.frontendPublicUrl||window.location.origin),t(Wf,{status:"online"},"public")),t("article",null,t("b",null,"Provider Ingress"),t("span",null,gu.providerIngressPublicUrl||"ws://public/ws/provider"),t(Wf,{status:"online"},"public")))),t(au,{title:"内部服务",eyebrow:"Docker Network Only"},t("div",{className:"endpoint-list"},t("article",null,t("b",null,"backend-core API"),t("span",null,"http://backend-core:8080"),t(Wf,{status:"internal"},"internal")),t("article",null,t("b",null,"database"),t("span",null,"postgres://database:5432/unidesk"),t(Wf,{status:"internal"},"internal")))),t(au,{title:"运行态",eyebrow:"Runtime"},t("div",{className:"metric-grid"},t(cf,{label:"DB Ready",value:f.dbReady?"YES":"NO",hint:"internal health"}),t(cf,{label:"Online Nodes",value:f.onlineNodeCount??0,hint:"provider-gateway self-link"}))))}function Em({session:u}){return t(au,{title:"认证策略",eyebrow:"Frontend Login"},t("div",{className:"policy-grid"},t("article",null,t("span",null,"默认账号"),t("strong",null,gu.authUsername||"admin")),t("article",null,t("span",null,"当前会话"),t("strong",null,u?.user?.username||"--")),t("article",null,t("span",null,"Session TTL"),t("strong",null,`${gu.sessionTtlSeconds||0}s`)),t("article",null,t("span",null,"API 访问"),t("strong",null,"同源 Cookie 保护"))),t("p",{className:"muted paragraph"},"浏览器只访问 frontend 同源接口;frontend 容器使用 Docker 内网代理 backend-core API。"))}function Zm(){return t(au,{title:"安全边界",eyebrow:"Exposure Rule"},t("div",{className:"security-board"},t("article",{className:"allow"},t("b",null,"允许公网"),t("span",null,"frontend 登录入口"),t("span",null,"provider ingress WebSocket/health")),t("article",{className:"deny"},t("b",null,"禁止公网"),t("span",null,"backend-core REST API"),t("span",null,"PostgreSQL database")),t("article",null,t("b",null,"数据库卷"),t("span",null,"named volume unidesk_pgdata_10gb"),t("span",null,"CLI stop/start 不删除数据卷"))))}function Om({activeModule:u,activeTab:f,data:l,session:r,refresh:n,onRaw:i,onNavigate:y}){if(u==="ops"&&f==="status")return t(vS,{data:l,onRaw:i,onNavigate:y});if(u==="ops"&&f==="performance")return t(ym,{onRaw:i});if(u==="ops"&&f==="events")return t(IS,{events:l.events,onRaw:i});if(u==="ops"&&f==="logs")return t(gS,{logs:l.logs,onRaw:i});if(u==="nodes"&&f==="list")return t(sS,{nodes:l.nodes,onRaw:i});if(u==="nodes"&&f==="monitor")return t(dS,{nodes:l.nodes,systemStatuses:l.systemStatuses,tasks:l.tasks,onRaw:i,refresh:n});if(u==="nodes"&&f==="docker")return t(jm,{nodes:l.nodes,dockerStatuses:l.dockerStatuses,onRaw:i});if(u==="nodes"&&f==="gateway")return t(tm,{nodes:l.nodes,tasks:l.tasks,onRaw:i});if(u==="nodes"&&f==="labels")return t(aS,{nodes:l.nodes});if(u==="nodes"&&f==="heartbeats")return t(oS,{nodes:l.nodes});if(u==="tasks"&&f==="dispatch")return t(Um,{nodes:l.nodes,onDispatched:n,onRaw:i});if(u==="tasks"&&f==="scheduled")return t(zm,{schedules:l.schedules,scheduleRuns:l.scheduleRuns,nodes:l.nodes,refresh:n,onRaw:i});if(u==="tasks"&&f==="pending")return t(qm,{tasks:l.pendingTasks,onRaw:i});if(u==="tasks"&&f==="history")return t(Nm,{tasks:l.tasks,onRaw:i});if(u==="tasks"&&f==="results")return t(Wm,{tasks:l.tasks,onRaw:i});if(u==="apps"&&f==="catalog")return t(Jm,{microservices:l.microservices,onRaw:i,onNavigate:y});if(u==="apps"&&f==="todo-note")return t(mw,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="findjob")return t(HQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="pipeline")return t(Ow,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="met-nonlinear")return t(YQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="claudeqq")return t(yU,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="baidu-netdisk")return t(rU,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="filebrowser")return t(OQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="oa-event-flow")return t(RQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="apps"&&f==="code-queue")return t(KQ,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl,initialTasksData:ZS});if(u==="apps"&&f==="project-manager")return t(Vw,{microservices:l.microservices,onRaw:i,apiBaseUrl:gu.apiBaseUrl});if(u==="config"&&f==="topology")return t(Tm,{data:l});if(u==="config"&&f==="auth")return t(Em,{session:r});if(u==="config"&&f==="security")return t(Zm);return t(Ff,{title:"未找到页面",text:"请选择左侧主模块和顶部子功能标签"})}function Hm({session:u,onLogout:f}){let l=n5(Or,window.location.pathname),[r,n]=bu(l.moduleId),[i,y]=bu({...b_,[l.moduleId]:l.tabId}),[_,$]=bu({overview:null,nodes:[],systemStatuses:[],dockerStatuses:[],microservices:[],events:[],tasks:[],pendingTasks:[],schedules:[],scheduleRuns:[],logs:[]}),[c,A]=bu({ok:!1,text:"连接中"}),[j,F]=bu(null),[U,Q]=bu(new Date),[w,L]=bu(null),[J,N]=bu(!1),[q,W]=bu(!1),z=kn.default.useRef(!1),Z=Or.moduleById[r]||Or.modules[0],H=i[r]||b_[r]||Z.tabs[0].id,E=Array.isArray(_.microservices)?_.microservices:[],D=E.length===0&&r==="apps"&&H==="code-queue"?[OS]:E,h=D===E?_:{..._,microservices:D},B=r==="apps"?D.find((o)=>String(o?.id||"")===H):null,X=B?cL(B):{},m=Z.tabs.find((o)=>o.id===H)?.label||H,O=B?[{key:"microservice",label:"用户服务",value:`${m} ${X.providerStatus==="online"?"在线":X.providerStatus||"未知"}`,tone:X.providerStatus==="online"?"ok":"warn",testId:"active-microservice-status"}]:[];async function p(){if(z.current)return;z.current=!0,W(!0);try{let o=[],s=(ju,Gu)=>{o.push([ju,Eu(Gu)])},x=r==="ops"&&H==="status",fu=x||r==="config"&&H==="topology",$u=x||r==="nodes"||r==="tasks"&&(H==="dispatch"||H==="scheduled"),tu=r==="apps"&&H!=="code-queue";if(fu)s("overview",`${gu.apiBaseUrl}/overview`);if($u)s("nodes",`${gu.apiBaseUrl}/nodes`);if(r==="nodes"&&H==="monitor")s("systemStatuses",`${gu.apiBaseUrl}/nodes/system-status?limit=60`),s("tasks",`${gu.apiBaseUrl}/tasks?limit=120&summary=1`);else if(r==="nodes"&&H==="docker")s("dockerStatuses",`${gu.apiBaseUrl}/nodes/docker-status`);else if(r==="nodes"&&H==="gateway")s("tasks",`${gu.apiBaseUrl}/tasks?limit=300&summary=1`);else if(r==="tasks"&&H==="scheduled")s("schedules",`${gu.apiBaseUrl}/schedules?limit=100`),s("scheduleRuns",`${gu.apiBaseUrl}/schedules/runs?limit=100`);else if(r==="tasks"&&H==="pending")s("pendingTasks",`${gu.apiBaseUrl}/tasks?status=pending&limit=100&summary=1`);else if(r==="tasks"&&(H==="history"||H==="results"))s("tasks",`${gu.apiBaseUrl}/tasks?limit=300&summary=1`);else if(x)s("tasks",`${gu.apiBaseUrl}/tasks?limit=8&lite=1`),s("pendingTasks",`${gu.apiBaseUrl}/tasks?status=pending&limit=20&lite=1`);if(tu)s("microservices",`${gu.apiBaseUrl}/microservices`);if(r==="ops"&&H==="events")s("events",`${gu.apiBaseUrl}/events?limit=100`);if(r==="ops"&&H==="logs")s("logs","/logs?limit=100");await Promise.all(o.map(async([ju,Gu])=>{let qu=await Gu,P={};if(ju==="overview")P.overview=qu;if(ju==="nodes")P.nodes=qu.nodes||[];if(ju==="systemStatuses")P.systemStatuses=qu.systemStatuses||[];if(ju==="dockerStatuses")P.dockerStatuses=qu.dockerStatuses||[];if(ju==="microservices")P.microservices=qu.microservices||[];if(ju==="events")P.events=qu.events||[];if(ju==="tasks")P.tasks=qu.tasks||[];if(ju==="pendingTasks")P.pendingTasks=qu.tasks||[];if(ju==="schedules")P.schedules=qu.schedules||[];if(ju==="scheduleRuns")P.scheduleRuns=qu.runs||[];if(ju==="logs")P.logs=qu.logs||[];$((e)=>({...e,...P}))})),A({ok:!0,text:"核心在线"}),F(new Date)}catch(o){if(A({ok:!1,text:Hu(o,"连接失败")}),o.status===401)f(!1)}finally{z.current=!1,W(!1)}}j0(()=>{let o=()=>{if(!xw())return;p()};o();let s=setInterval(o,HS(r,H)),x=()=>{if(xw())o()};return document.addEventListener("visibilitychange",x),()=>{clearInterval(s),document.removeEventListener("visibilitychange",x)}},[r,H]),j0(()=>{let o=setInterval(()=>Q(new Date),1000);return()=>clearInterval(o)},[]),j0(()=>{let o=PQ(Or,window.location.pathname);if(o&&window.location.pathname!==o)window.history.replaceState(null,"",o)},[]),j0(()=>{let o=()=>{let s=n5(Or,window.location.pathname);n(s.moduleId),y((x)=>({...x,[s.moduleId]:s.tabId})),L(null)};return window.addEventListener("popstate",o),()=>window.removeEventListener("popstate",o)},[]),j0(()=>{window.scrollTo({top:0,left:0,behavior:"auto"})},[r,H]);function Y(o,s,x="push"){let fu=Or.moduleById[o]?o:Or.fallbackTarget.moduleId,$u=Or.moduleById[fu]?.tabs.some((ju)=>ju.id===s)?s:b_[fu]||Or.moduleById[fu]?.tabs[0]?.id||Or.fallbackTarget.tabId;n(fu),y((ju)=>({...ju,[fu]:$u}));let tu=f6(Or,fu,$u);if(window.location.pathname!==tu){let ju=x==="replace"?"replaceState":"pushState";window.history[ju](null,"",tu)}}function v(o,s){L({title:o,data:s})}let[T,S]=bu(!1),{unreadCount:k,notifications:I}=Bl(),b=I.length>0?I[I.length-1]:null;return t("div",{className:`shell ${J?"rail-collapsed":""}`,"data-testid":"app-shell"},t(hS,{activeModule:r,activeTabs:i,onNavigate:Y,collapsed:J,onToggle:()=>N((o)=>!o)}),t("main",{className:"workspace"},t(RS,{connection:c,lastRefresh:j,onRefresh:p,onLogout:()=>f(!0),session:u,clock:U,activeStatusItems:O,onNotificationToggle:()=>S((o)=>!o),unreadCount:k}),t(bS,{module:Z,activeTab:H,onNavigate:Y}),t(I9.Provider,{value:q},t(Om,{activeModule:r,activeTab:H,data:h,session:u,refresh:p,onRaw:v,onNavigate:Y}))),t(MS,{raw:w,onClose:()=>L(null)}),b&&t(Rw,{key:b.id,notification:b}),T&&t(Cw,{onClose:()=>S(!1)}))}function Bm(){let[u,f]=bu(!0),[l,r]=bu(null);async function n(){f(!0);try{let y=await Eu("/api/session");r(y.authenticated?y:null)}catch{r(null)}finally{f(!1)}}async function i(y){if(y)try{await Eu("/logout",{method:"POST"})}catch{}r(null)}if(j0(()=>{n()},[]),u)return t("main",{className:"loading-screen"},t("div",{className:"brand-mark"},"UD"),t("span",null,"加载会话"));if(!l)return t(CS,{onLogin:r});return t(lU,null,t(Hm,{session:l,onLogout:i}))}var QL=document.getElementById("root");if(QL===null)throw Error("root element not found");dw.createRoot(QL).render(t(Bm));})(); diff --git a/src/components/frontend/public/style.css b/src/components/frontend/public/style.css index 9e570aa7..b1d7e9c0 100644 --- a/src/components/frontend/public/style.css +++ b/src/components/frontend/public/style.css @@ -1422,7 +1422,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } .result-card dd { margin: 0; } .result-grid { grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); } -.microservice-page, .findjob-page, .pipeline-page, .met-page, .code-queue-page, .baidu-netdisk-page, .filebrowser-page { +.microservice-page, .findjob-page, .pipeline-page, .met-page, .code-queue-page, .baidu-netdisk-page, .filebrowser-page, .oa-event-flow-page { display: grid; gap: 10px; } @@ -1464,6 +1464,130 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } gap: 10px; align-items: start; } +.oa-flow-hero { + display: grid; + grid-template-columns: minmax(320px, 1fr) minmax(260px, 0.45fr); + gap: 10px; + align-items: stretch; +} +.oa-flow-signal { + position: relative; + display: grid; + gap: 4px; + min-width: 0; + padding: 10px; + overflow: hidden; + border: 1px solid rgba(78, 183, 168, 0.28); + background: + linear-gradient(120deg, rgba(78, 183, 168, 0.14), rgba(215, 161, 58, 0.08)), + repeating-linear-gradient(90deg, rgba(255,255,255,0.04) 0, rgba(255,255,255,0.04) 1px, transparent 1px, transparent 14px), + var(--panel-3); +} +.oa-flow-signal::after { + content: ""; + position: absolute; + inset: auto -20% 0 -20%; + height: 2px; + background: linear-gradient(90deg, transparent, var(--accent-2), var(--accent), transparent); + animation: oa-signal-scan 2.8s linear infinite; +} +.oa-flow-signal span { + color: var(--accent); + font-size: 10px; + letter-spacing: 0.18em; + text-transform: uppercase; +} +.oa-flow-signal strong { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + font-size: 18px; +} +.oa-flow-signal code { color: var(--muted); } +@keyframes oa-signal-scan { + from { transform: translateX(-40%); opacity: 0.35; } + 50% { opacity: 1; } + to { transform: translateX(40%); opacity: 0.35; } +} +.oa-flow-metrics { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 8px; +} +.oa-filter-bar { + display: grid; + grid-template-columns: minmax(280px, 0.8fr) auto minmax(180px, 0.45fr); + gap: 10px; + align-items: end; +} +.oa-filter-bar label { + display: grid; + gap: 5px; + color: var(--muted); + font-size: 10px; + letter-spacing: 0.16em; + text-transform: uppercase; +} +.oa-filter-bar input { + width: 100%; + min-height: 34px; + padding: 7px 9px; + border: 1px solid var(--line); + color: var(--text); + background: var(--panel-3); +} +.oa-filter-presets { + display: flex; + flex-wrap: wrap; + gap: 6px; +} +.oa-filter-bar > code { + align-self: center; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + color: var(--accent-2); +} +.oa-type-strip { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 10px; +} +.oa-flow-grid { + display: grid; + grid-template-columns: 1fr; + gap: 10px; +} +.oa-flow-wide { min-width: 0; } +.oa-event-table, .oa-stats-table { + min-width: 1120px; + font-size: 12px; +} +.oa-event-table td, +.oa-stats-table td { + vertical-align: top; +} +.oa-tag-rail { + display: flex; + flex-wrap: wrap; + gap: 4px; + max-width: 360px; +} +.oa-tag-rail code { + padding: 2px 5px; + border: 1px solid rgba(78, 183, 168, 0.22); + color: var(--accent-2); + background: rgba(78, 183, 168, 0.07); +} +.oa-payload-preview { + display: block; + max-width: 360px; + color: var(--muted); + overflow-wrap: anywhere; +} .findjob-grid .panel:nth-child(3) { grid-column: 1 / -1; } .claudeqq-page .findjob-grid .panel:nth-child(n+3) { grid-column: 1 / -1; } .filebrowser-hero { @@ -2356,7 +2480,7 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } min-width: 0; align-items: center; } -.codex-submit-queue-row select { +.codex-submit-queue-select { grid-area: queue-select; } .codex-rename-queue-btn, @@ -2376,6 +2500,49 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } .codex-merge-queue-btn { grid-area: merge; } +.codex-merge-dialog { + width: min(480px, 96vw); + overflow: hidden; + border-color: rgba(78, 183, 168, 0.34); + border-radius: 18px; + background: + radial-gradient(circle at 20% 0%, rgba(78, 183, 168, 0.16), transparent 34%), + #0a1015; +} +.codex-merge-dialog-body { + display: grid; + gap: 12px; + padding: 14px; +} +.codex-merge-dialog-body label { + display: grid; + gap: 6px; + color: rgba(226, 244, 244, 0.82); + font-size: 12px; + font-weight: 800; + letter-spacing: 0.04em; + text-transform: uppercase; +} +.codex-merge-dialog-body select { + width: 100%; + min-height: 40px; + box-sizing: border-box; +} +.codex-merge-dialog-target, +.codex-merge-dialog-note { + margin: 0; + color: rgba(217, 238, 238, 0.76); + line-height: 1.55; +} +.codex-merge-dialog-target code { + color: #eafffb; +} +.codex-merge-dialog-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + flex-wrap: wrap; +} .codex-reference-field { display: grid; gap: 5px; @@ -5090,6 +5257,29 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } padding: 10px; border-bottom: 1px solid var(--line); } +.unidesk-dialog { + overflow: hidden; +} +.unidesk-dialog-body { + display: grid; + gap: 10px; + max-height: calc(86vh - 118px); + overflow: auto; + padding: 10px; +} +.unidesk-dialog-actions { + display: flex; + justify-content: flex-end; + gap: 8px; + flex-wrap: wrap; + padding: 10px; + border-top: 1px solid var(--line); + background: rgba(255,255,255,0.02); +} +.unidesk-dialog-body.codex-merge-dialog-body { + gap: 12px; + padding: 14px; +} .raw-json { max-height: calc(86vh - 58px); overflow: auto; @@ -5168,11 +5358,11 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } } @media (max-width: 1120px) { - .metric-grid, .policy-grid, .security-board, .docker-metrics, .monitor-chart-grid, .monitor-summary-grid, .performance-metric-stack, .codex-load-test-grid, .baidu-doc-grid, .filebrowser-target-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } + .metric-grid, .policy-grid, .security-board, .docker-metrics, .monitor-chart-grid, .monitor-summary-grid, .performance-metric-stack, .codex-load-test-grid, .baidu-doc-grid, .filebrowser-target-grid, .oa-flow-metrics { grid-template-columns: repeat(2, minmax(0, 1fr)); } .pipeline-oa-guarantees { grid-template-columns: repeat(2, minmax(0, 1fr)); } .dispatch-form, .schedule-form { grid-template-columns: 1fr 1fr; } .dispatch-actions { align-items: center; } - .page-grid, .docker-layout, .monitor-layout, .performance-top-grid, .performance-grid, .findjob-grid, .findjob-hero, .pipeline-grid, .pipeline-hero, .met-grid, .met-form-grid, .code-queue-layout, .code-queue-hero, .codex-detail-grid, .project-manager-hero, .project-manager-layout, .baidu-netdisk-grid, .baidu-netdisk-hero, .baidu-transfer-forms, .filebrowser-hero { grid-template-columns: 1fr; } + .page-grid, .docker-layout, .monitor-layout, .performance-top-grid, .performance-grid, .findjob-grid, .findjob-hero, .pipeline-grid, .pipeline-hero, .met-grid, .met-form-grid, .code-queue-layout, .code-queue-hero, .codex-detail-grid, .project-manager-hero, .project-manager-layout, .baidu-netdisk-grid, .baidu-netdisk-hero, .baidu-transfer-forms, .filebrowser-hero, .oa-flow-hero, .oa-filter-bar { grid-template-columns: 1fr; } .codex-session-shell { grid-template-columns: minmax(260px, 0.42fr) minmax(0, 1fr); position: relative; } .codex-session-shell.queue-collapsed { grid-template-columns: minmax(0, 1fr); } .codex-session-sidebar { border-right: 1px solid var(--line); border-bottom: 0; } @@ -5198,7 +5388,8 @@ input:focus, select:focus, textarea:focus { border-color: var(--accent-2); } .pipeline-kv-grid, .pipeline-field-list, .performance-metric-stack, - .codex-load-test-grid { + .codex-load-test-grid, + .oa-flow-metrics { grid-template-columns: 1fr; } .performance-hero { flex-direction: column; } diff --git a/src/components/frontend/src/app.tsx b/src/components/frontend/src/app.tsx index 4802ac5c..034de763 100644 --- a/src/components/frontend/src/app.tsx +++ b/src/components/frontend/src/app.tsx @@ -8,6 +8,7 @@ import { FileBrowserPage } from "./filebrowser"; import { FindJobPage } from "./findjob"; import { MetNonlinearPage } from "./met-nonlinear"; import { canonicalizeKnownRoute, createRouteRegistry, DEFAULT_ACTIVE_TABS, MODULES, pathForTarget, resolveRouteTarget } from "./navigation"; +import { OaEventFlowPage } from "./oa-event-flow"; import { PipelinePage } from "./pipeline"; import { ProjectManagerPage } from "./project-manager"; import { TodoNotePage } from "./todo-note"; @@ -1638,6 +1639,7 @@ function MicroserviceCatalogPage({ microservices, onRaw, onNavigate }: AnyRecord service.id === "met-nonlinear" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "met-nonlinear"), "data-testid": "open-met-nonlinear-button" }, "打开") : null, service.id === "claudeqq" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "claudeqq"), "data-testid": "open-claudeqq-button" }, "打开") : null, service.id === "baidu-netdisk" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "baidu-netdisk"), "data-testid": "open-baidu-netdisk-button" }, "打开") : null, + service.id === "oa-event-flow" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "oa-event-flow"), "data-testid": "open-oa-event-flow-button" }, "打开") : null, service.id === "code-queue" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "code-queue"), "data-testid": "open-code-queue-button" }, "打开") : null, service.id === "project-manager" ? h("button", { type: "button", className: "ghost-btn", onClick: () => onNavigate("apps", "project-manager"), "data-testid": "open-project-manager-button" }, "打开") : null, h(RawButton, { title: `用户服务 ${service.id}`, data: service, onOpen: onRaw }), @@ -2143,6 +2145,7 @@ function WorkArea({ activeModule, activeTab, data, session, refresh, onRaw, onNa if (activeModule === "apps" && activeTab === "claudeqq") return h(ClaudeQqPage, { microservices: data.microservices, onRaw, apiBaseUrl: cfg.apiBaseUrl }); if (activeModule === "apps" && activeTab === "baidu-netdisk") return h(BaiduNetdiskPage, { microservices: data.microservices, onRaw, apiBaseUrl: cfg.apiBaseUrl }); if (activeModule === "apps" && activeTab === "filebrowser") return h(FileBrowserPage, { microservices: data.microservices, onRaw, apiBaseUrl: cfg.apiBaseUrl }); + if (activeModule === "apps" && activeTab === "oa-event-flow") return h(OaEventFlowPage, { microservices: data.microservices, onRaw, apiBaseUrl: cfg.apiBaseUrl }); if (activeModule === "apps" && activeTab === "code-queue") return h(CodeQueuePage, { microservices: data.microservices, onRaw, apiBaseUrl: cfg.apiBaseUrl, initialTasksData: initialCodeQueueOverview }); if (activeModule === "apps" && activeTab === "project-manager") return h(ProjectManagerPage, { microservices: data.microservices, onRaw, apiBaseUrl: cfg.apiBaseUrl }); if (activeModule === "config" && activeTab === "topology") return h(TopologyPage, { data }); diff --git a/src/components/frontend/src/claudeqq.tsx b/src/components/frontend/src/claudeqq.tsx index 8acc1eb7..cbee1147 100644 --- a/src/components/frontend/src/claudeqq.tsx +++ b/src/components/frontend/src/claudeqq.tsx @@ -292,7 +292,6 @@ export function ClaudeQqPage({ microservices, onRaw, apiBaseUrl = "/api" }: AnyR ), ), h(UniDeskErrorBanner, { error: state.error, wide: true }), - actionMessage ? h("div", { className: "form-success wide" }, actionMessage) : null, ), h("div", { className: "metric-grid" }, h(MetricCard, { label: "Health", value: health.ok || health.status === "ok" ? "OK" : "--", hint: "D601 /health", tone: health.ok || health.status === "ok" ? "ok" : "warn" }), diff --git a/src/components/frontend/src/code-queue.tsx b/src/components/frontend/src/code-queue.tsx index 76c54976..7f4f0972 100644 --- a/src/components/frontend/src/code-queue.tsx +++ b/src/components/frontend/src/code-queue.tsx @@ -6,6 +6,7 @@ import { TraceView, codexTracePort } from "./trace"; import { errorMessage, requestJson as requestUniDeskJson } from "./unidesk-error"; import { UniDeskErrorBanner } from "./unidesk-error-banner"; import { useNotification } from "./notification-context"; +import { UniDeskDialog } from "./dialog"; type AnyRecord = Record; @@ -150,6 +151,10 @@ function codexApi(apiBaseUrl: string, path: string): string { return `${apiBaseUrl}/code-queue-direct${path}`; } +function oaCodeQueueEventsApi(apiBaseUrl: string): string { + return `${apiBaseUrl}/microservices/oa-event-flow/proxy/api/events/stream?tags=${encodeURIComponent("service:code-queue")}`; +} + function codexNoCacheOptions(): AnyRecord { return { headers: { "cache-control": "no-cache", "x-unidesk-no-cache": "1" } }; } @@ -572,17 +577,43 @@ function nonNegativeCount(value: any): number { return Number.isFinite(numeric) && numeric >= 0 ? Math.floor(numeric) : 0; } -function canonicalTaskStepCount(task: any): number { - const summary = taskTraceSummary(task); - if (summary !== null) return traceSummaryStepCount(summary); - const value = Number(task?.stepCount ?? task?.llmStepCount ?? 0); - return Number.isFinite(value) && value >= 0 ? Math.floor(value) : 0; +function statCount(value: any): number | null { + const numeric = Number(value); + return Number.isFinite(numeric) && numeric >= 0 ? Math.floor(numeric) : null; } -function traceSummaryStepCount(summary: AnyRecord | null): number { - const execution = objectRecord(summary?.execution) || {}; - const value = Number(summary?.stepCount ?? summary?.llmStepCount ?? execution.stepCount ?? execution.llmStepCount ?? execution.toolCallCount ?? 0); - return nonNegativeCount(value); +function taskOaTraceStats(task: any): AnyRecord | null { + const direct = objectRecord(task?.traceStats); + if (direct && (task?.statsSource === "oa-event-flow" || direct.source === "oa-event-flow")) return direct; + const summary = taskTraceSummary(task); + const summaryStats = objectRecord(summary?.traceStats); + if (summaryStats && (summary?.statsSource === "oa-event-flow" || summaryStats.source === "oa-event-flow")) return summaryStats; + return null; +} + +function attemptOaTraceStats(task: any, attempt: any): AnyRecord | null { + const direct = objectRecord(attempt?.traceStats) || objectRecord(attempt?.execution?.traceStats); + const source = String(attempt?.statsSource || attempt?.execution?.statsSource || ""); + if (direct && (source === "oa-event-flow" || direct.source === "oa-event-flow")) return direct; + return null; +} + +function traceStatCount(task: any, key: string): number | null { + return statCount(taskOaTraceStats(task)?.[key]); +} + +function canonicalTaskStepCount(task: any): number | null { + const direct = traceStatCount(task, "stepCount"); + if (direct !== null) return direct; + const summary = taskTraceSummary(task); + if (summary !== null) return traceSummaryStepCount(summary); + return null; +} + +function traceSummaryStepCount(summary: AnyRecord | null): number | null { + const stats = objectRecord(summary?.traceStats); + if (!stats || (summary?.statsSource !== "oa-event-flow" && stats.source !== "oa-event-flow")) return null; + return statCount(stats.stepCount); } function traceSummaryIsCurrent(task: any): boolean { @@ -853,8 +884,7 @@ function coalesceFileChangeTraceSteps(steps: any[]): any[] { } function canonicalExecutionSummary(execution: AnyRecord): AnyRecord { - const stepCount = nonNegativeCount(execution?.stepCount ?? execution?.llmStepCount ?? execution?.toolCallCount); - return { ...execution, stepCount, llmStepCount: stepCount }; + return { ...execution }; } function taskTraceStepsLoaded(task: any, attemptIndex: any = null): boolean { @@ -1209,7 +1239,7 @@ function providerDefaultWorkdir(queue: any, providerId: string): string { return id === mainProvider ? String(queue?.defaultWorkdir || "/root/unidesk") : String(queue?.remoteDefaultWorkdir || "/home/ubuntu"); } -function taskStepCount(task: any): number { +function taskStepCount(task: any): number | null { return canonicalTaskStepCount(task); } @@ -1220,6 +1250,8 @@ function TaskCard({ task, selected, onSelect, onCopy, onReference, onMarkRead, c const updatedAt = latestTimestampValue(task?.updatedAt, taskTraceSummary(task)?.updatedAt); const recentUpdateLabel = `最近更新: ${fmtRelativeAge(updatedAt)}`; const stepCount = taskStepCount(task); + const stepLabel = stepCount === null ? "--" : String(stepCount); + const stepTitle = stepCount === null ? "STEP 统计中心同步中" : "STEP 来自 OA Event Flow 统计中心"; return h("article", { role: "button", tabIndex: 0, @@ -1282,7 +1314,7 @@ function TaskCard({ task, selected, onSelect, onCopy, onReference, onMarkRead, c h("span", null, taskDurationLabel(task)), ), h("div", { className: "codex-task-meta codex-task-update-meta" }, - h("span", { className: "codex-task-recent-update codex-task-step-count", title: "STEP 按 read/edit/run 工具动作统计", "data-testid": `codex-task-step-count-${taskId || "unknown"}` }, `STEP ${stepCount}`), + h("span", { className: "codex-task-recent-update codex-task-step-count", title: stepTitle, "data-testid": `codex-task-step-count-${taskId || "unknown"}` }, `STEP ${stepLabel}`), h("span", { className: "codex-task-recent-update", title: updatedAt ? `更新时间: ${fmtDate(updatedAt)}` : recentUpdateLabel, "data-testid": `codex-task-recent-update-${taskId || "unknown"}` }, recentUpdateLabel), h("span", null, fmtDate(updatedAt || task?.updatedAt)), ), @@ -1537,12 +1569,17 @@ function ProgressivePromptBlock({ task, loading, onLoadPromptPart, testId = "cod function ProgressiveExecutionSummary({ task, attempt, attemptIndex, loading, onLoadSteps, onLoadStep, testId = "codex-execution-summary" }: AnyRecord) { const steps = coalesceFileChangeTraceSteps(taskTraceSteps(task, attemptIndex)); const execution = canonicalExecutionSummary(attemptExecutionSummary(task, attempt)); - const summary = taskTraceSummary(task); + const stats = attempt ? attemptOaTraceStats(task, attempt) : taskOaTraceStats(task); const stepDetails = taskTraceStepDetails(task); const stepsLoaded = taskTraceStepsLoaded(task, attemptIndex); - const errorCount = Number(attempt?.errorCount ?? summary?.errorCount ?? traceStepErrorCount(steps)); - const toolCount = nonNegativeCount(execution.toolCallCount); - const stepCount = nonNegativeCount(execution.stepCount ?? execution.llmStepCount ?? execution.toolCallCount); + const errorCount = statCount(stats?.errorCount); + const toolCount = statCount(stats?.stepCount ?? stats?.llmStepCount); + const readCount = statCount(stats?.readCount); + const editCount = statCount(stats?.editCount); + const runCount = statCount(stats?.runCount); + const stepCount = statCount(stats?.stepCount ?? stats?.llmStepCount); + const toolsLabel = toolCount === null ? "--" : String(toolCount); + const stepLabel = stepCount === null ? "--" : String(stepCount); const editedFiles = Array.isArray(execution.editedFiles) ? execution.editedFiles : []; const commands = Array.isArray(execution.commands) ? execution.commands : []; const synthetic = isSyntheticAttemptSegment(attempt, attemptIndex); @@ -1565,14 +1602,14 @@ function ProgressiveExecutionSummary({ task, attempt, attemptIndex, loading, onL h("strong", null, `执行过程摘要${labelSuffix}`), running ? h("span", { className: "codex-summary-running-pill", "data-testid": `${testId}-running` }, "执行中") : null, h("code", { title: updatedAt ? `最近更新: ${fmtDate(updatedAt)}` : recentUpdateLabel }, - `${fmtDuration(execution.durationMs ?? execution.totalElapsedMs)} / ${toolCount} tools / ${recentUpdateLabel}`), + `${fmtDuration(execution.durationMs ?? execution.totalElapsedMs)} / ${toolsLabel} tools / ${recentUpdateLabel}`), ), h("div", { className: "codex-execution-digest" }, - h("span", null, `read ${Number(execution.readCount || 0)}`), - h("span", null, `edit ${Number(execution.editCount || 0)}`), - h("span", null, `run ${Number(execution.runCount || 0)}`), - h("span", null, `STEP ${stepCount}`), - errorCount > 0 ? h("span", { className: "codex-execution-error-pill", "data-testid": `${testId}-error-count` }, `Error ${errorCount}`) : null, + h("span", { title: "来自 OA Event Flow 统计中心" }, `read ${readCount === null ? "--" : readCount}`), + h("span", { title: "来自 OA Event Flow 统计中心" }, `edit ${editCount === null ? "--" : editCount}`), + h("span", { title: "来自 OA Event Flow 统计中心" }, `run ${runCount === null ? "--" : runCount}`), + h("span", { title: "来自 OA Event Flow 统计中心" }, `STEP ${stepLabel}`), + errorCount !== null && errorCount > 0 ? h("span", { className: "codex-execution-error-pill", "data-testid": `${testId}-error-count` }, `Error ${errorCount}`) : null, ), ), h("div", { className: "codex-execution-digest expanded" }, @@ -1849,6 +1886,8 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi const [referenceTaskId, setReferenceTaskId] = useState(""); const [queueId, setQueueId] = useState("default"); const [selectedQueueId, setSelectedQueueId] = useState(allQueuesId); + const [mergeDialogOpen, setMergeDialogOpen] = useState(false); + const [mergeSourceQueueId, setMergeSourceQueueId] = useState(""); const [providerId, setProviderId] = useState("main-server"); const [model, setModel] = useState("gpt-5.5"); const [cwd, setCwd] = useState("/root/unidesk"); @@ -1888,6 +1927,8 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi const statistics = taskStatistics(tasksData, queue); const pagination = taskPagination(tasksData); const queueRows = knownQueueRows(queue, queueId); + const mergeTargetQueueId = String(queueId || "default").trim() || "default"; + const mergeSourceQueueRows = queueRows.filter((row: any) => String(row?.id || "") !== mergeTargetQueueId); const viewQueueRow = selectedQueueRow(queueRows, selectedQueueId); const totalTaskCount = Number((isAllQueues(selectedQueueId) ? queue?.total : viewQueueRow?.total) ?? pagination.total ?? tasks.length); const globalActiveIds = activeTaskIds(queue); @@ -2138,6 +2179,8 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi attempts: Array.isArray(summary.attempts) ? summary.attempts : [], stepCount: summary.stepCount, llmStepCount: summary.llmStepCount, + traceStats: summary.traceStats, + statsSource: summary.statsSource, timing: summary.timing, _traceSummary: summary, _traceSummaryLoaded: true, @@ -2758,25 +2801,36 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi }, "修改 Codex queue 名称失败"); } - async function mergeQueue(): Promise { - const targetQueueId = String(queueId || "default").trim() || "default"; - const candidates = queueRows.filter((row: any) => String(row?.id || "") !== targetQueueId); - if (candidates.length === 0) { + function openMergeQueueDialog(): void { + if (mergeSourceQueueRows.length === 0) { const msg = "没有可合并的其他 queue;请先创建或选择另一个 queue。"; setNotice(msg); return; } - const proposed = typeof window === "undefined" - ? "" - : window.prompt(`将源 queue 合并到当前 queue=${targetQueueId};源 queue 记录会保留且不会删除。请输入源 queue ID:`, String(candidates[0]?.id || "")); + const existingSource = mergeSourceQueueRows.some((row: any) => String(row?.id || "") === mergeSourceQueueId) + ? mergeSourceQueueId + : ""; + setMergeSourceQueueId(existingSource); + setMergeDialogOpen(true); + } + + async function mergeQueue(): Promise { + const targetQueueId = mergeTargetQueueId; + if (mergeSourceQueueRows.length === 0) { + const msg = "没有可合并的其他 queue;请先创建或选择另一个 queue。"; + setNotice(msg); + return; + } + const proposed = mergeSourceQueueId || ""; const sourceQueueId = String(proposed || "").trim(); - if (!sourceQueueId) return; + if (!sourceQueueId) { + setNotice("请先选择要合并的源 queue。"); + return; + } if (sourceQueueId === targetQueueId) { setError("源 queue 和目标 queue 不能相同。"); return; } - const confirmed = typeof window === "undefined" ? true : window.confirm(`确认把 queue=${sourceQueueId} 的全部任务合并到 queue=${targetQueueId}?源 queue 不会删除;任务会按原 queueEnteredAt/createdAt 时间顺序进入目标 queue。`); - if (!confirmed) return; await guarded(async () => { const result = await requestJson(codexApi(apiBaseUrl, `/api/queues/${encodeURIComponent(targetQueueId)}/merge`), { method: "POST", body: { sourceQueueId } }); if (result?.summary) { @@ -2786,9 +2840,11 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi setSelectedQueueId(targetQueueId); setTasksData(null); const count = Number(result?.mergedTaskCount || 0); - const msg = `已将 queue=${sourceQueueId} 合并到 ${targetQueueId},移动 ${count} 个任务;源 queue 已保留。`; + const msg = `已将 queue=${sourceQueueId} 合并到 ${targetQueueId},移动 ${count} 个任务;源 queue 已自动删除。`; setNotice(msg); addNotification("success", msg); + setMergeDialogOpen(false); + setMergeSourceQueueId(""); await load(selectedIdRef.current, true, targetQueueId); }, "合并 Codex queue 失败"); } @@ -2995,11 +3051,12 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi function taskEventNeedsOverviewRefresh(event: AnyRecord, taskId: string, previousTask: any): boolean { const type = String(event?.type || ""); if (type === "queue-updated") return true; + if (type === "trace-stats-updated" || type === "trace-step-created") return false; if (taskId.length === 0) return true; - if (!previousTask) return type !== "task-step"; + if (!previousTask) return true; if (event?.queueId && String(event.queueId) !== taskQueueLabel(previousTask)) return true; if (event?.status && String(event.status) !== String(previousTask?.status || "")) return true; - return type !== "task-step" && String(event?.reason || "") !== "output"; + return String(event?.reason || "") !== "output"; } function scheduleEventDrivenOverviewRefresh(): void { @@ -3026,30 +3083,72 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi }, 250); } + function normalizeOaCodeQueueEvent(event: AnyRecord): AnyRecord { + const payload = objectRecord(event?.payload) || event; + const stats = objectRecord(payload?.stats); + const type = String(event?.type || payload?.type || ""); + const subjectKind = String(payload?.subjectKind || stats?.subjectKind || ""); + const scopeId = String(payload?.scopeId || stats?.scopeId || ""); + const taskId = String(payload?.taskId || (subjectKind === "task" ? payload?.subjectId : "") || stats?.taskId || event?.taskId || ""); + const attemptValue = payload?.attemptIndex ?? stats?.attemptIndex; + const attemptIndex = attemptValue === null || attemptValue === undefined || attemptValue === "" ? null : statCount(attemptValue); + const traceStats = stats || objectRecord(payload?.traceStats); + const stepCount = traceStats?.stepCount ?? traceStats?.llmStepCount; + const outputMaxSeq = traceStats?.outputMaxSeq; + return { + ...payload, + type, + eventId: event?.eventId || payload?.eventId, + sequence: event?.sequence ?? payload?.sequence, + taskId, + subjectKind, + scopeId, + attemptIndex, + stepCount, + outputMaxSeq, + updatedAt: traceStats?.updatedAt || payload?.updatedAt || event?.createdAt, + traceStats, + statsSource: traceStats ? "oa-event-flow" : payload?.statsSource, + }; + } + function applyCodeQueueEvent(event: AnyRecord): void { - const taskId = String(event?.taskId || ""); - const stepCount = nonNegativeCount(event?.stepCount); + const normalizedEvent = normalizeOaCodeQueueEvent(event); + const taskId = String(normalizedEvent?.taskId || ""); + const scopeId = String(normalizedEvent?.scopeId || normalizedEvent?.traceStats?.scopeId || ""); + const isAttemptStats = String(normalizedEvent?.subjectKind || normalizedEvent?.traceStats?.subjectKind || "") === "task-attempt" + || Number(normalizedEvent?.attemptIndex) > 0 + || /:attempt:\d+$/u.test(scopeId); + const stepCount = nonNegativeCount(normalizedEvent?.stepCount); const patch: AnyRecord = {}; const previousTask = taskId.length > 0 ? taskRows(tasksDataRef.current).find((task: any) => String(task?.id || "") === taskId) : null; if (taskId.length > 0) { - if (event?.status) patch.status = String(event.status); - if (event?.updatedAt) patch.updatedAt = String(event.updatedAt); - if (event?.queueId) patch.queueId = String(event.queueId); - if (Number.isFinite(Number(event?.stepCount))) { + if (normalizedEvent?.status) patch.status = String(normalizedEvent.status); + if (normalizedEvent?.updatedAt) patch.updatedAt = String(normalizedEvent.updatedAt); + if (normalizedEvent?.queueId) patch.queueId = String(normalizedEvent.queueId); + if (!isAttemptStats && Number.isFinite(Number(normalizedEvent?.stepCount))) { patch.stepCount = stepCount; patch.llmStepCount = stepCount; } + if (!isAttemptStats && Number.isFinite(Number(normalizedEvent?.outputMaxSeq))) patch.outputMaxSeq = nonNegativeCount(normalizedEvent.outputMaxSeq); + if (!isAttemptStats && objectRecord(normalizedEvent?.traceStats)) { + patch.traceStats = normalizedEvent.traceStats; + patch.statsSource = "oa-event-flow"; + } if (Object.keys(patch).length > 0 && (sessionCacheRef.current.has(taskId) || selectedIdRef.current === taskId)) { publishCachedTask(taskId, patch, detailLoadTokenRef.current); } if (Object.keys(patch).length > 0) patchSidebarTaskFromEvent(taskId, patch); - if (selectedIdRef.current === taskId && (event?.type === "task-step" || event?.type === "task-updated")) { - scheduleSelectedTraceEventRefresh(taskId, event?.type === "task-step"); + if (selectedIdRef.current === taskId && (normalizedEvent?.type === "trace-step-created" || normalizedEvent?.type === "task-updated" || normalizedEvent?.type === "trace-stats-updated")) { + const previousStepCount = canonicalTaskStepCount(previousTask); + const shouldRefreshSteps = normalizedEvent?.type === "trace-step-created" + || (normalizedEvent?.type === "trace-stats-updated" && !isAttemptStats && Number.isFinite(Number(normalizedEvent?.stepCount)) && (previousStepCount === null || stepCount > previousStepCount)); + scheduleSelectedTraceEventRefresh(taskId, shouldRefreshSteps); } } - if (taskEventNeedsOverviewRefresh(event, taskId, previousTask)) scheduleEventDrivenOverviewRefresh(); + if (taskEventNeedsOverviewRefresh(normalizedEvent, taskId, previousTask)) scheduleEventDrivenOverviewRefresh(); } useEffect(() => { @@ -3062,7 +3161,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi useEffect(() => { if (!service || typeof EventSource === "undefined") return undefined; - const source = new EventSource(codexApi(apiBaseUrl, "/api/events"), { withCredentials: true }); + const source = new EventSource(oaCodeQueueEventsApi(apiBaseUrl), { withCredentials: true }); const onEvent = (message: MessageEvent): void => { try { applyCodeQueueEvent(JSON.parse(String(message.data || "{}"))); @@ -3073,9 +3172,10 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi const onVisible = (): void => { if (isDocumentVisible()) scheduleEventDrivenOverviewRefresh(); }; - source.addEventListener("task-step", onEvent); + source.addEventListener("trace-step-created", onEvent); source.addEventListener("task-updated", onEvent); source.addEventListener("queue-updated", onEvent); + source.addEventListener("trace-stats-updated", onEvent); document.addEventListener("visibilitychange", onVisible); return () => { source.close(); @@ -3101,7 +3201,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi if (autoTraceLoadKeysRef.current.has(key)) return; autoTraceLoadKeysRef.current.add(key); void ensureTraceSummary(taskId, true).catch((err) => setError(errorText(err, "自动加载 Trace Summary 失败"))); - }, [service?.id, selectedTask?.id, selectedTask?.updatedAt, selectedTask?.stepCount, selectedTask?.llmStepCount, selectedTask?._traceSummaryUpdatedAt, selectedTask?._traceSummaryLoaded, selectedDetailLoading]); + }, [service?.id, selectedTask?.id, selectedTask?.updatedAt, selectedTask?.traceStats?.statsRevision, selectedTask?._traceSummaryUpdatedAt, selectedTask?._traceSummaryLoaded, selectedDetailLoading]); const taskListContent = sidebarTasks.length === 0 ? h(EmptyState, { title: searchActive ? (searchLoading ? "搜索中" : "没有匹配任务") : "队列为空", @@ -3261,6 +3361,32 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi const loadDetailMs = Number(loadStats?.detailMs); const loadTranscriptRows = Number(loadStats?.transcriptRows); const loadState = loadStats?.phase === "complete" ? "complete" : String(loadStats?.phase || "idle"); + const mergeTargetQueueRow = selectedQueueRow(queueRows, mergeTargetQueueId) || { id: mergeTargetQueueId, name: mergeTargetQueueId }; + const mergeDialog = mergeDialogOpen ? h(UniDeskDialog, { + title: "合并 queue", + titleId: "codex-merge-dialog-title", + className: "codex-merge-dialog", + backdropClassName: "codex-merge-dialog-backdrop", + bodyClassName: "codex-merge-dialog-body", + actionsClassName: "codex-merge-dialog-actions", + testId: "codex-merge-queue-dialog", + closeTestId: "codex-merge-queue-close", + disableClose: busy, + onClose: () => setMergeDialogOpen(false), + actions: [ + h("button", { key: "cancel", type: "button", className: "ghost-btn", onClick: () => setMergeDialogOpen(false), disabled: busy, "data-testid": "codex-merge-queue-cancel" }, "取消"), + h("button", { key: "confirm", type: "button", className: "primary-btn", onClick: () => void mergeQueue(), disabled: busy || submitting || !mergeSourceQueueId, "data-testid": "codex-merge-queue-confirm" }, busy ? "合并中..." : "确认合并"), + ], + }, + h("p", { className: "codex-merge-dialog-target" }, "目标 queue:", h("code", null, queueOptionLabel(mergeTargetQueueRow))), + h("label", null, "源 queue", + h("select", { value: mergeSourceQueueId, disabled: busy || submitting, onChange: (event: any) => setMergeSourceQueueId(String(event.target.value || "")), "data-testid": "codex-merge-source-queue-select" }, + h("option", { value: "" }, "选择要合并进来的源 queue"), + mergeSourceQueueRows.map((row: any) => h("option", { key: String(row?.id || ""), value: String(row?.id || "") }, queueOptionLabel(row))), + ), + ), + h("p", { className: "codex-merge-dialog-note" }, "会把源 queue 的任务归属合并到目标 queue,并自动删除源 queue;目标 queue 会按原 queueEnteredAt/createdAt 时间顺序运行。"), + ) : null; return h("div", { className: `code-queue-page ${standalone ? "codex-standalone-page" : ""}`, @@ -3274,7 +3400,7 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi "data-load-partial": loadStats?.partial ? "true" : "false", }, h(UniDeskErrorBanner, { error, wide: true }), - notice ? h("div", { className: "form-success wide", "data-testid": "codex-create-success" }, notice) : null, + mergeDialog, h("div", { className: "codex-session-stage codex-session-stage-top" }, sessionPanel, ), @@ -3292,11 +3418,11 @@ export function CodeQueuePage({ microservices, onRaw, apiBaseUrl = "/api", initi h("div", { className: "codex-form-grid" }, h("label", { className: "codex-submit-queue-field" }, "Queue", h("div", { className: "codex-submit-queue-row" }, - h("select", { value: queueId, disabled: submitting, onChange: (event: any) => setQueueId(String(event.target.value || "default")), "data-testid": "code-queue-id-select" }, + h("select", { className: "codex-submit-queue-select", value: queueId, disabled: submitting, onChange: (event: any) => setQueueId(String(event.target.value || "default")), "data-testid": "code-queue-id-select" }, queueRows.map((row: any) => h("option", { key: String(row?.id || ""), value: String(row?.id || "") }, queueOptionLabel(row))), ), h("button", { type: "button", className: "ghost-btn codex-rename-queue-btn", onClick: () => void renameQueue(), disabled: busy || submitting || !queueId, title: "修改当前 queue 的显示名称,ID 不变", "data-testid": "codex-rename-queue-button" }, "改名"), - h("button", { type: "button", className: "ghost-btn codex-merge-queue-btn", onClick: () => void mergeQueue(), disabled: busy || submitting || queueRows.length < 2, title: "把其他 queue 合并到当前 queue;源 queue 记录会保留,不会删除", "data-testid": "codex-merge-queue-button" }, "合并 queue"), + h("button", { type: "button", className: "ghost-btn codex-merge-queue-btn", onClick: () => openMergeQueueDialog(), disabled: busy || submitting || mergeSourceQueueRows.length === 0, title: "打开独立窗口选择源 queue 并合并到当前 queue;合并后自动删除源 queue", "data-testid": "codex-merge-queue-button" }, "合并 queue"), h("button", { type: "button", className: "ghost-btn codex-create-queue-btn", onClick: () => void createQueue(), disabled: busy || submitting, "data-testid": "codex-create-queue-button" }, "创建 queue"), ), ), diff --git a/src/components/frontend/src/dialog.tsx b/src/components/frontend/src/dialog.tsx new file mode 100644 index 00000000..68e228e4 --- /dev/null +++ b/src/components/frontend/src/dialog.tsx @@ -0,0 +1,70 @@ +import React, { type ReactNode } from "react"; + +const h = React.createElement; + +function joinClassNames(...values: Array): string { + return values.filter(Boolean).join(" "); +} + +export interface UniDeskDialogProps { + title: ReactNode; + children?: ReactNode; + actions?: ReactNode; + onClose: () => void; + className?: string; + backdropClassName?: string; + bodyClassName?: string; + actionsClassName?: string; + titleId?: string; + testId?: string; + closeTestId?: string; + closeLabel?: string; + disableClose?: boolean; + closeOnBackdrop?: boolean; +} + +export function UniDeskDialog({ + title, + children, + actions, + onClose, + className, + backdropClassName, + bodyClassName, + actionsClassName, + titleId, + testId, + closeTestId, + closeLabel = "关闭", + disableClose = false, + closeOnBackdrop = true, +}: UniDeskDialogProps) { + const sectionProps: Record = { + className: joinClassNames("raw-dialog", "unidesk-dialog", className), + role: "dialog", + "aria-modal": "true", + }; + if (titleId) { + sectionProps["aria-labelledby"] = titleId; + } else if (typeof title === "string") { + sectionProps["aria-label"] = title; + } + if (testId) sectionProps["data-testid"] = testId; + + return h("div", { + className: joinClassNames("modal-backdrop", "unidesk-dialog-backdrop", backdropClassName), + role: "presentation", + onClick: (event: React.MouseEvent) => { + if (closeOnBackdrop && !disableClose && event.target === event.currentTarget) onClose(); + }, + }, + h("section", sectionProps, + h("div", { className: "raw-dialog-head unidesk-dialog-head" }, + h("strong", titleId ? { id: titleId } : null, title), + h("button", { type: "button", className: "ghost-btn", onClick: onClose, disabled: disableClose, "data-testid": closeTestId }, closeLabel), + ), + h("div", { className: joinClassNames("unidesk-dialog-body", bodyClassName) }, children), + actions ? h("div", { className: joinClassNames("unidesk-dialog-actions", actionsClassName) }, actions) : null, + ), + ); +} diff --git a/src/components/frontend/src/index.ts b/src/components/frontend/src/index.ts index 34990de1..c1641d50 100644 --- a/src/components/frontend/src/index.ts +++ b/src/components/frontend/src/index.ts @@ -707,10 +707,9 @@ async function proxyCodeQueueDirect(req: Request, url: URL): Promise { } const overviewCacheKey = `${suffix}${url.search}`; const canUseOverviewCache = req.method === "GET" && suffix === "/api/tasks/overview"; - const isEventStreamRequest = req.method === "GET" && suffix === "/api/events"; const cacheControl = req.headers.get("cache-control") || ""; const bypassOverviewCache = /\bno-cache\b|\bno-store\b/iu.test(cacheControl) || req.headers.get("x-unidesk-no-cache") === "1"; - const expectsJsonResponse = !isEventStreamRequest && (suffix === "/health" || suffix.startsWith("/api/")); + const expectsJsonResponse = suffix === "/health" || suffix.startsWith("/api/"); if (req.method !== "GET" && req.method !== "HEAD") invalidateCodeQueueOverviewCache(); if (canUseOverviewCache && !bypassOverviewCache) { const cached = cachedCodeQueueOverview(overviewCacheKey); @@ -725,7 +724,7 @@ async function proxyCodeQueueDirect(req: Request, url: URL): Promise { headers.delete("connection"); headers.delete("content-length"); headers.delete("cookie"); - const init: RequestInit = { method: req.method, headers, redirect: "manual" }; + const init: RequestInit = { method: req.method, headers, redirect: "manual", signal: req.signal }; if (req.method !== "GET" && req.method !== "HEAD") { init.body = await req.arrayBuffer(); } @@ -736,13 +735,6 @@ async function proxyCodeQueueDirect(req: Request, url: URL): Promise { const responseHeaders = new Headers(); const upstreamContentType = upstream.headers.get("content-type"); if (upstreamContentType !== null) responseHeaders.set("content-type", upstreamContentType); - if (isEventStreamRequest) { - responseHeaders.set("cache-control", upstream.headers.get("cache-control") || "no-store, no-transform"); - responseHeaders.set("connection", "keep-alive"); - const buffering = upstream.headers.get("x-accel-buffering"); - if (buffering !== null) responseHeaders.set("x-accel-buffering", buffering); - return new Response(upstream.body, { status: upstream.status, headers: responseHeaders }); - } const upstreamBody = await upstream.arrayBuffer(); const isJsonResponse = (upstreamContentType ?? "").toLowerCase().includes("json"); let parsedJson: unknown = null; @@ -821,7 +813,7 @@ async function proxyApi(req: Request, url: URL): Promise { headers.delete("connection"); headers.delete("content-length"); headers.delete("cookie"); - const init: RequestInit = { method: req.method, headers, redirect: "manual" }; + const init: RequestInit = { method: req.method, headers, redirect: "manual", signal: req.signal }; if (req.method !== "GET" && req.method !== "HEAD") { init.body = await req.arrayBuffer(); } @@ -839,6 +831,13 @@ async function proxyApi(req: Request, url: URL): Promise { const responseHeaders = new Headers(); const upstreamContentType = upstream.headers.get("content-type"); if (upstreamContentType !== null) responseHeaders.set("content-type", upstreamContentType); + if ((upstreamContentType ?? "").toLowerCase().includes("text/event-stream")) { + responseHeaders.set("cache-control", upstream.headers.get("cache-control") || "no-store, no-transform"); + responseHeaders.set("connection", "keep-alive"); + const buffering = upstream.headers.get("x-accel-buffering"); + if (buffering !== null) responseHeaders.set("x-accel-buffering", buffering); + return new Response(upstream.body, { status: upstream.status, headers: responseHeaders }); + } return new Response(await upstream.arrayBuffer(), { status: upstream.status, headers: responseHeaders }); } diff --git a/src/components/frontend/src/navigation.ts b/src/components/frontend/src/navigation.ts index 078fd6d1..c532c3b1 100644 --- a/src/components/frontend/src/navigation.ts +++ b/src/components/frontend/src/navigation.ts @@ -69,6 +69,7 @@ export const MODULES: UniDeskModuleDefinition[] = [ { id: "claudeqq", label: "ClaudeQQ" }, { id: "baidu-netdisk", label: "Baidu Netdisk" }, { id: "filebrowser", label: "File Browser" }, + { id: "oa-event-flow", label: "OA Event Flow" }, { id: "code-queue", label: "Code Queue" }, { id: "project-manager", label: "Project Manager" }, ] }, diff --git a/src/components/frontend/src/oa-event-flow.tsx b/src/components/frontend/src/oa-event-flow.tsx new file mode 100644 index 00000000..4c5a4f0d --- /dev/null +++ b/src/components/frontend/src/oa-event-flow.tsx @@ -0,0 +1,332 @@ +import React from "react"; +import { fmtClock, fmtDate } from "./time"; +import { LoadingTitle } from "./loading-indicator"; +import { errorMessage, requestJson } from "./unidesk-error"; +import { UniDeskErrorBanner } from "./unidesk-error-banner"; + +type AnyRecord = Record; + +const h = React.createElement; +const { useEffect, useMemo } = React; +const useState: any = React.useState; + +function StatusBadge({ status, children, title }: AnyRecord) { + const normalized = String(status || "unknown").toLowerCase(); + return h("span", { className: `status-badge ${normalized}`, title }, children || status || "unknown"); +} + +function MetricCard({ label, value, hint, tone }: AnyRecord) { + return h("article", { className: `metric-card ${tone || ""}` }, + h("div", { className: "metric-label" }, label), + h("div", { className: "metric-value" }, value), + h("div", { className: "metric-hint" }, hint), + ); +} + +function Panel({ title, eyebrow, actions, children, className, loading }: AnyRecord) { + return h("section", { className: `panel ${className || ""}` }, + h("div", { className: "panel-head" }, + h("div", null, + eyebrow ? h("p", { className: "panel-eyebrow" }, eyebrow) : null, + h(LoadingTitle, { title, loading }), + ), + actions ? h("div", { className: "panel-actions" }, actions) : null, + ), + h("div", { className: "panel-body" }, children), + ); +} + +function RawButton({ title, data, onOpen, testId }: AnyRecord) { + return h("button", { + type: "button", + className: "ghost-btn", + "data-testid": testId, + onClick: () => onOpen?.(title, data), + }, "查看原始JSON"); +} + +function EmptyState({ title, text }: AnyRecord) { + return h("div", { className: "empty-state" }, h("strong", null, title), h("span", null, text)); +} + +function objectRecord(value: any): AnyRecord | null { + return value && typeof value === "object" && !Array.isArray(value) ? value : null; +} + +function asArray(value: any): any[] { + return Array.isArray(value) ? value : []; +} + +function fmtCount(value: any): string { + const number = Number(value); + return Number.isFinite(number) ? number.toLocaleString("zh-CN") : "--"; +} + +function compactText(value: any, max = 140): string { + if (value === null || value === undefined) return "--"; + const text = typeof value === "string" ? value : JSON.stringify(value); + const normalized = String(text || "").replace(/\s+/gu, " ").trim(); + return normalized.length > max ? `${normalized.slice(0, max - 1)}...` : normalized || "--"; +} + +function eventTags(event: any): string[] { + return asArray(event?.tags).map((tag) => String(tag || "").trim()).filter(Boolean); +} + +function statNumber(value: any): number { + const number = Number(value); + return Number.isFinite(number) && number >= 0 ? Math.floor(number) : 0; +} + +function microserviceRuntime(service: any): AnyRecord { + return service?.runtime && typeof service.runtime === "object" && !Array.isArray(service.runtime) ? service.runtime : {}; +} + +function microserviceBackend(service: any): AnyRecord { + return service?.backend && typeof service.backend === "object" && !Array.isArray(service.backend) ? service.backend : {}; +} + +function normalizeTagFilter(value: string): string { + return String(value || "") + .split(/[\s,]+/u) + .map((tag) => tag.trim()) + .filter(Boolean) + .join(","); +} + +function oaProxy(apiBaseUrl: string, path: string): string { + return `${apiBaseUrl}/microservices/oa-event-flow/proxy${path}`; +} + +function eventTypeTone(type: string): string { + if (type.includes("error") || type.includes("failed")) return "failed"; + if (type.includes("stats")) return "ok"; + if (type.includes("step") || type.includes("updated")) return "running"; + return "queued"; +} + +function statScopeLabel(stat: any): string { + const kind = String(stat?.subjectKind || "trace"); + const id = String(stat?.subjectId || stat?.scopeId || ""); + return id ? `${kind}:${id}` : String(stat?.scopeId || "--"); +} + +function EventTagRail({ tags }: AnyRecord) { + const visible = eventTags({ tags }).slice(0, 6); + return h("div", { className: "oa-tag-rail" }, + visible.length === 0 ? h("span", { className: "muted" }, "--") : visible.map((tag) => h("code", { key: tag }, tag)), + ); +} + +function EventTable({ events, onRaw }: AnyRecord) { + const rows = [...asArray(events)].reverse(); + return rows.length === 0 + ? h(EmptyState, { title: "事件表暂无记录", text: "等待 Code Queue 或 Pipeline 按 tag 发布 OA 事件" }) + : h("div", { className: "table-wrap oa-event-table-wrap" }, h("table", { className: "oa-event-table", "data-testid": "oa-event-flow-event-table" }, + h("thead", null, h("tr", null, + h("th", null, "Seq"), + h("th", null, "Type"), + h("th", null, "Source"), + h("th", null, "Aggregate"), + h("th", null, "Tags"), + h("th", null, "Payload"), + h("th", null, "Created"), + h("th", null, "Raw"), + )), + h("tbody", null, rows.map((event: any) => { + const type = String(event?.type || "event"); + const aggregate = `${String(event?.aggregateType || "--")}:${String(event?.aggregateId || "--")}`; + return h("tr", { key: event?.eventId || event?.sequence }, + h("td", null, h("code", null, fmtCount(event?.sequence))), + h("td", null, h(StatusBadge, { status: eventTypeTone(type) }, type)), + h("td", null, h("strong", null, event?.sourceId || "--"), h("code", null, event?.sourceKind || "--")), + h("td", null, h("code", null, aggregate)), + h("td", null, h(EventTagRail, { tags: event?.tags })), + h("td", null, h("span", { className: "oa-payload-preview" }, compactText(event?.payload, 180))), + h("td", null, fmtDate(event?.createdAt)), + h("td", null, h(RawButton, { title: `OA Event ${event?.sequence || ""}`, data: event, onOpen: onRaw, testId: `raw-oa-event-${event?.sequence || "unknown"}` })), + ); + })), + )); +} + +function StatsTable({ stats, onRaw }: AnyRecord) { + const rows = asArray(stats); + return rows.length === 0 + ? h(EmptyState, { title: "统计中心暂无投影", text: "trace-stats-snapshot / trace-step-created 进入事件流后会更新这里" }) + : h("div", { className: "table-wrap oa-stats-table-wrap" }, h("table", { className: "oa-stats-table", "data-testid": "oa-event-flow-stats" }, + h("thead", null, h("tr", null, + h("th", null, "Scope"), + h("th", null, "Service"), + h("th", null, "STEP"), + h("th", null, "Read"), + h("th", null, "Edit"), + h("th", null, "Run"), + h("th", null, "Error"), + h("th", null, "Output Seq"), + h("th", null, "Revision"), + h("th", null, "Updated"), + h("th", null, "Raw"), + )), + h("tbody", null, rows.map((stat: any) => h("tr", { key: stat?.scopeId || `${stat?.serviceId}-${stat?.subjectId}` }, + h("td", null, h("strong", null, statScopeLabel(stat)), h("code", null, stat?.scopeId || "--")), + h("td", null, h(StatusBadge, { status: String(stat?.serviceId || "unknown") === "code-queue" ? "running" : "queued" }, stat?.serviceId || "--")), + h("td", null, h("strong", null, fmtCount(statNumber(stat?.stepCount ?? stat?.llmStepCount)))), + h("td", null, fmtCount(statNumber(stat?.readCount))), + h("td", null, fmtCount(statNumber(stat?.editCount))), + h("td", null, fmtCount(statNumber(stat?.runCount))), + h("td", null, fmtCount(statNumber(stat?.errorCount))), + h("td", null, h("code", null, fmtCount(statNumber(stat?.outputMaxSeq)))), + h("td", null, fmtCount(statNumber(stat?.statsRevision))), + h("td", null, fmtDate(stat?.updatedAt)), + h("td", null, h(RawButton, { title: `OA Trace Stats ${stat?.scopeId || ""}`, data: stat, onOpen: onRaw, testId: `raw-oa-stats-${String(stat?.scopeId || "unknown").replace(/[^a-zA-Z0-9_-]/gu, "_")}` })), + ))), + )); +} + +export function OaEventFlowPage({ microservices, onRaw, apiBaseUrl = "/api" }: AnyRecord) { + const service = microservices.find((item: any) => item.id === "oa-event-flow") || null; + const [tagFilter, setTagFilter] = useState("service:code-queue"); + const [state, setState] = useState({ loading: false, error: "", health: null, diagnostics: null, events: [], stats: [], refreshedAt: null }); + const [streamState, setStreamState] = useState({ status: "idle", message: "未连接", lastEventAt: "" }); + const normalizedTags = useMemo(() => normalizeTagFilter(tagFilter), [tagFilter]); + + async function load(): Promise { + if (!service) return; + setState((previous: any) => ({ ...previous, loading: true, error: "" })); + try { + const tagQuery = normalizedTags ? `tags=${encodeURIComponent(normalizedTags)}&` : ""; + const [health, diagnostics, events, stats] = await Promise.all([ + requestJson(`${apiBaseUrl}/microservices/oa-event-flow/health`, { failureFields: [] }), + requestJson(oaProxy(apiBaseUrl, "/api/diagnostics")), + requestJson(oaProxy(apiBaseUrl, `/api/events?${tagQuery}limit=100`)), + requestJson(oaProxy(apiBaseUrl, `/api/stats/trace?${tagQuery}limit=100`)), + ]); + setState({ + loading: false, + error: "", + health, + diagnostics, + events: asArray(events?.events), + stats: asArray(stats?.stats), + refreshedAt: new Date(), + }); + } catch (err) { + setState((previous: any) => ({ ...previous, loading: false, error: errorMessage(err, "OA Event Flow 加载失败") })); + } + } + + useEffect(() => { + void load(); + }, [service?.id, service?.runtime?.providerStatus, normalizedTags]); + + useEffect(() => { + if (!service || typeof EventSource === "undefined") return undefined; + const tagQuery = normalizedTags ? `?tags=${encodeURIComponent(normalizedTags)}` : ""; + const source = new EventSource(`${oaProxy(apiBaseUrl, "/api/events/stream")}${tagQuery}`, { withCredentials: true }); + setStreamState({ status: "running", message: "SSE connecting", lastEventAt: "" }); + const onHello = (message: MessageEvent): void => { + setStreamState({ status: "online", message: compactText(message.data, 120), lastEventAt: new Date().toISOString() }); + }; + const onMessage = (message: MessageEvent): void => { + try { + const event = JSON.parse(String(message.data || "{}")); + setStreamState({ status: "online", message: String(event?.type || message.type || "event"), lastEventAt: new Date().toISOString() }); + setState((previous: any) => { + const events = [...asArray(previous.events).filter((row: any) => String(row?.eventId || "") !== String(event?.eventId || "")), event] + .sort((left: any, right: any) => Number(left?.sequence || 0) - Number(right?.sequence || 0)) + .slice(-100); + const stats = event?.type === "trace-stats-updated" && objectRecord(event?.payload?.stats) + ? [event.payload.stats, ...asArray(previous.stats).filter((row: any) => String(row?.scopeId || "") !== String(event.payload.stats.scopeId || ""))].slice(0, 100) + : previous.stats; + return { ...previous, events, stats }; + }); + } catch (err) { + setStreamState({ status: "warn", message: errorMessage(err, "SSE 事件解析失败"), lastEventAt: new Date().toISOString() }); + } + }; + const onError = (): void => { + setStreamState((previous: any) => ({ ...previous, status: "warn", message: "SSE reconnecting" })); + }; + source.addEventListener("hello", onHello); + source.addEventListener("task-updated", onMessage); + source.addEventListener("queue-updated", onMessage); + source.addEventListener("trace-step-created", onMessage); + source.addEventListener("trace-stats-snapshot", onMessage); + source.addEventListener("trace-stats-updated", onMessage); + source.addEventListener("trace-error", onMessage); + source.onerror = onError; + return () => source.close(); + }, [service?.id, apiBaseUrl, normalizedTags]); + + if (!service) return h(EmptyState, { title: "OA Event Flow 未登记", text: "请在 config.json 的 microservices 中登记 id=oa-event-flow" }); + + const runtime = microserviceRuntime(service); + const backend = microserviceBackend(service); + const diagnostics = state.diagnostics || {}; + const health = state.health || {}; + const eventCount = diagnostics.eventCount ?? health.eventCount; + const statsCount = diagnostics.traceStatsCount ?? health.traceStatsCount; + const latestSequence = diagnostics.latestSequence ?? health.latestSequence; + const pipelineBridge = diagnostics.pipelineBridge || health.pipelineBridge || {}; + const eventTypes = asArray(diagnostics.eventTypes).slice(0, 8); + + return h("div", { className: "oa-event-flow-page", "data-testid": "oa-event-flow-page" }, + h(Panel, { + title: "OA Event Flow 控制台", + eyebrow: "Unified OA Event Bus + Stats Projection", + loading: state.loading, + actions: h("div", { className: "panel-actions" }, + h("button", { type: "button", className: "ghost-btn", onClick: load, disabled: state.loading, "data-testid": "oa-event-flow-refresh" }, state.loading ? "刷新中" : "刷新"), + h(RawButton, { title: "OA Event Flow Service", data: service, onOpen: onRaw, testId: "raw-oa-event-flow-service" }), + ), + }, + h("div", { className: "oa-flow-hero" }, + h("div", null, + h("div", { className: "node-version-line" }, + h(StatusBadge, { status: health?.ok || runtime.providerStatus === "online" ? "online" : "warn" }, health?.ok ? "HEALTH OK" : runtime.providerStatus || "unknown"), + h(StatusBadge, { status: streamState.status }, streamState.status.toUpperCase()), + h("span", null, backend.public ? "公网暴露" : "仅 UniDesk frontend 代理访问"), + ), + h("p", { className: "muted paragraph" }, "独立事件流微服务统一承载 Code Queue 与 Pipeline 的事件发布、tag 订阅、事件表审计和 Trace/STEP 统计投影。"), + ), + h("div", { className: "oa-flow-signal" }, + h("span", null, "stream"), + h("strong", null, streamState.message || "--"), + h("code", null, streamState.lastEventAt ? fmtClock(new Date(streamState.lastEventAt)) : "waiting"), + ), + ), + h(UniDeskErrorBanner, { error: state.error, wide: true }), + ), + h("div", { className: "oa-flow-metrics" }, + h(MetricCard, { label: "事件总量", value: fmtCount(eventCount), hint: `latest seq ${fmtCount(latestSequence)}`, tone: "ok" }), + h(MetricCard, { label: "Trace Stats", value: fmtCount(statsCount), hint: "oa_trace_stats 投影" }), + h(MetricCard, { label: "SSE Clients", value: fmtCount(health?.sseClientCount ?? asArray(diagnostics.sseClients).length), hint: streamState.message || "tag subscription" }), + h(MetricCard, { label: "Pipeline Bridge", value: pipelineBridge?.enabled ? fmtCount(pipelineBridge?.insertedCount) : "OFF", hint: pipelineBridge?.lastError || pipelineBridge?.lastFinishedAt || `${pipelineBridge?.mode || "snapshot"} service:pipeline` }), + h(MetricCard, { label: "DB", value: health?.databaseReady || diagnostics.databaseReady ? "READY" : "WAIT", hint: health?.databaseLastError || diagnostics.databaseLastError || "PostgreSQL persisted" }), + ), + h(Panel, { title: "标签订阅", eyebrow: state.refreshedAt ? `Updated ${fmtClock(state.refreshedAt)}` : "Tag Pub/Sub" }, + h("div", { className: "oa-filter-bar" }, + h("label", null, h("span", null, "tags"), h("input", { value: tagFilter, onChange: (event: any) => setTagFilter(event.target.value), placeholder: "service:code-queue, trace", "data-testid": "oa-event-flow-tag-filter" })), + h("div", { className: "oa-filter-presets" }, + h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("service:code-queue") }, "Code Queue"), + h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("service:pipeline") }, "Pipeline"), + h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("trace") }, "Trace"), + h("button", { type: "button", className: "ghost-btn", onClick: () => setTagFilter("") }, "All"), + ), + h("code", null, normalizedTags || "all events"), + ), + h("div", { className: "oa-type-strip" }, + eventTypes.length === 0 ? h("span", { className: "muted" }, "等待事件类型统计") : eventTypes.map((row: any) => h("span", { key: row.type, className: "data-chip" }, `${row.type} ${fmtCount(row.count)}`)), + ), + ), + h("div", { className: "oa-flow-grid" }, + h(Panel, { title: "事件表", eyebrow: "oa_events persisted log", className: "oa-flow-wide", loading: state.loading, + actions: h(RawButton, { title: "OA Event Query", data: { events: state.events, diagnostics }, onOpen: onRaw, testId: "raw-oa-events" }), + }, h(EventTable, { events: state.events, onRaw })), + h(Panel, { title: "统计中心", eyebrow: "oa_trace_stats read model", className: "oa-flow-wide", loading: state.loading, + actions: h(RawButton, { title: "OA Trace Stats", data: state.stats, onOpen: onRaw, testId: "raw-oa-trace-stats" }), + }, h(StatsTable, { stats: state.stats, onRaw })), + ), + ); +} diff --git a/src/components/frontend/src/pipeline.tsx b/src/components/frontend/src/pipeline.tsx index 98c4956b..ab809467 100644 --- a/src/components/frontend/src/pipeline.tsx +++ b/src/components/frontend/src/pipeline.tsx @@ -847,7 +847,7 @@ function PipelineProcedureAttemptList({ procedure, matchedStepKey = "", matchedA ), h(PipelineKvGrid, { items: [ { label: "messages", value: messages.messageCount ?? "--" }, - { label: "steps", value: messages.stepCount ?? steps.length }, + { label: "steps", value: messages.stepCount ?? "--" }, { label: "tools", value: messages.toolCallCount ?? "--" }, { label: "updated", value: fmtDate(messages.updatedAt) }, { label: "sessions", value: sessionIds.join(", ") || "--" }, @@ -886,8 +886,7 @@ function pipelineGanttDetailPayload(baseDetails: any, nodeDetails: any, runId: s return { ...base, ...nodeDetails, - // Keep run-level evidence unless the node endpoint has a more focused copy. - controlCommands: asArray(nodeDetails.controlCommands).length > 0 ? nodeDetails.controlCommands : base.controlCommands, + // Keep normalized OA control events unless the node endpoint has a focused copy. controlEvents: asArray(nodeDetails.controlEvents).length > 0 ? nodeDetails.controlEvents : base.controlEvents, procedureRuns: nodeProcedures.length > 0 ? nodeProcedures : base.procedureRuns, }; @@ -2342,12 +2341,12 @@ function pipelineGanttAddObservationArrows(markers: AnyRecord[], arrows: AnyReco return { markers, arrows: nextArrows }; } -function pipelinePromptMarkerTone(record: any, fallbackKind = ""): string { - const kind = eventKind(record) || fallbackKind; +function pipelinePromptMarkerTone(record: any): string { + const kind = eventKind(record); const promptEvent = String(record?.promptEvent || ""); if (kind === "initial-prompt-delivered") return "initial"; if (promptEvent === "node-finished" || promptEvent === "node-long-running-observation" || promptEvent.startsWith("monitor-")) return "monitor"; - if (kind === "monitor-prompt-delivered" || String(record?.sourceKind || "").toLowerCase() === "monitor" || fallbackKind === "monitor-prompt-queued") return "monitor"; + if (kind === "monitor-prompt-delivered" || String(record?.sourceKind || "").toLowerCase() === "monitor") return "monitor"; return "append"; } @@ -2355,16 +2354,16 @@ function pipelineRecordTags(record: any): string[] { return asArray(record?.tags || record?.raw?.tags).map((tag) => String(tag || "")).filter(Boolean); } -function pipelinePromptMarkerLabel(record: any, fallbackKind = ""): string { - const kind = eventKind(record) || fallbackKind; +function pipelinePromptMarkerLabel(record: any): string { + const kind = eventKind(record); const promptEvent = String(record?.promptEvent || ""); if (kind === "initial-prompt-delivered") return "初始 prompt"; if (promptEvent === "node-long-running-observation") return "长任务观察"; if (promptEvent === "node-finished") return pipelineRecordTags(record).includes("monitor.audit") ? "节点完成 / OA 审核" : "节点完成"; - if (promptEvent === "monitor-interval") return "旧版轮询"; + if (promptEvent === "monitor-interval") return "Monitor observation"; if (promptEvent === "monitor-start") return "Monitor start"; if (promptEvent === "monitor-stop") return "Monitor stop"; - if (kind === "monitor-prompt-delivered" || fallbackKind === "monitor-prompt-queued") return "Monitor prompt"; + if (kind === "monitor-prompt-delivered") return "Monitor prompt"; if (kind === "append-prompt-queued") return "追加 prompt 已排队"; return "追加 prompt"; } @@ -2382,7 +2381,7 @@ function controlRecordGroupKey(record: any, index: number): string { if (commandId) return `command:${commandId}`; const timestamp = eventTimestampIso(record) || firstIso(record?.createdAt, record?.timestamp) || `index-${index}`; return [ - "fallback", + "control-event", timestamp, String(record?.sourceKind || ""), String(record?.sourceNodeId || ""), @@ -2481,8 +2480,6 @@ function pipelineRunGanttSignals(details: any, activeRun: any): AnyRecord { if (!nodeId) continue; for (const attempt of asArray(procedure?.attempts)) { const attemptId = attemptLabel(attempt); - const deliveredIds = new Set(); - const deliveredFallbackKeys = new Set(); for (const record of structuredEventRecords(attempt?.controlEventRecords)) { const kind = eventKind(record); if (!["initial-prompt-delivered", "append-prompt-delivered", "monitor-prompt-delivered"].includes(kind)) continue; @@ -2490,8 +2487,6 @@ function pipelineRunGanttSignals(details: any, activeRun: any): AnyRecord { const ms = timeMs(timestampIso); if (ms === null) continue; const eventId = String(record?.eventId || ""); - if (eventId) deliveredIds.add(eventId); - deliveredFallbackKeys.add(`${kind}:${timestampIso}:${String(record?.sourceKind || "")}:${String(record?.promptPreview || "")}`); addMarker({ id: `prompt:${eventId || `${procedureRunId}:${attemptId}:${kind}:${ms}`}`, runId, @@ -2499,55 +2494,19 @@ function pipelineRunGanttSignals(details: any, activeRun: any): AnyRecord { procedureRunId, attempt: attemptId, kind: "prompt", - tone: pipelinePromptMarkerTone(record, kind), + tone: pipelinePromptMarkerTone(record), status: "delivered", - label: pipelinePromptMarkerLabel(record, kind), + label: pipelinePromptMarkerLabel(record), ms, timestampIso, - sourceKind: String(record?.sourceKind || ""), - sourceNodeId: String(record?.sourceNodeId || ""), - targetNodeId: nodeId, - action: "", - eventId, - commandId: String(record?.commandId || ""), - raw: record, - }, promptMarkers); - } - const fallbackSources = [ - { records: structuredEventRecords(attempt?.controlPromptRecords), fallbackKind: "append-prompt-queued" }, - { records: structuredEventRecords(attempt?.monitorPromptRecords), fallbackKind: "monitor-prompt-queued" }, - ]; - for (const source of fallbackSources) { - for (const record of source.records) { - const timestampIso = eventTimestampIso(record); - const ms = timeMs(timestampIso); - if (ms === null) continue; - const eventId = String(record?.eventId || ""); - if (eventId && deliveredIds.has(eventId)) continue; - const deliveredKind = source.fallbackKind === "monitor-prompt-queued" ? "monitor-prompt-delivered" : "append-prompt-delivered"; - const fallbackKey = `${deliveredKind}:${timestampIso}:${String(record?.sourceKind || "")}:${String(record?.promptPreview || "")}`; - if (deliveredFallbackKeys.has(fallbackKey)) continue; - addMarker({ - id: `prompt-fallback:${eventId || `${procedureRunId}:${attemptId}:${source.fallbackKind}:${ms}`}`, - runId, - nodeId, - procedureRunId, - attempt: attemptId, - kind: "prompt", - tone: pipelinePromptMarkerTone(record, source.fallbackKind), - status: "queued", - label: pipelinePromptMarkerLabel(record, source.fallbackKind), - ms, - timestampIso, - sourceKind: String(record?.sourceKind || ""), - sourceNodeId: String(record?.sourceNodeId || ""), - targetNodeId: nodeId, - action: "", - eventId, - commandId: String(record?.commandId || ""), - raw: record, - }, promptMarkers); - } + sourceKind: String(record?.sourceKind || ""), + sourceNodeId: String(record?.sourceNodeId || ""), + targetNodeId: nodeId, + action: "", + eventId, + commandId: String(record?.commandId || ""), + raw: record, + }, promptMarkers); } } } @@ -2555,22 +2514,15 @@ function pipelineRunGanttSignals(details: any, activeRun: any): AnyRecord { const grouped = new Map(); structuredEventRecords(details?.controlEvents).forEach((record: AnyRecord, index: number) => { const key = controlRecordGroupKey(record, index); - const group = grouped.get(key) || { key, events: [], commands: [] }; + const group = grouped.get(key) || { key, events: [] }; group.events.push(record); grouped.set(key, group); }); - asArray(details?.controlCommands).filter(isRecord).forEach((record: AnyRecord, index: number) => { - const key = controlRecordGroupKey(record, index); - const group = grouped.get(key) || { key, events: [], commands: [] }; - group.commands.push(record); - grouped.set(key, group); - }); for (const group of grouped.values()) { const events = asArray(group.events).slice().sort((left: any, right: any) => controlEventPriority(right) - controlEventPriority(left)); - const commands = asArray(group.commands); - const queuedRecord = asArray(group.events).find((record: any) => eventKind(record) === "control-command-queued") || commands[0] || null; - const outcomeRecord = events[0] || commands[0] || queuedRecord; + const queuedRecord = asArray(group.events).find((record: any) => eventKind(record) === "control-command-queued") || null; + const outcomeRecord = events[0] || queuedRecord; if (!queuedRecord && !outcomeRecord) continue; const sourceNodeId = String(queuedRecord?.sourceNodeId || outcomeRecord?.sourceNodeId || ""); const sourceKind = String(queuedRecord?.sourceKind || outcomeRecord?.sourceKind || ""); diff --git a/src/components/microservices/code-queue/src/dev-containers.ts b/src/components/microservices/code-queue/src/dev-containers.ts index 5f9c42ac..d72a9ab9 100644 --- a/src/components/microservices/code-queue/src/dev-containers.ts +++ b/src/components/microservices/code-queue/src/dev-containers.ts @@ -43,7 +43,13 @@ function ctx(): DevContainerContext { return context; } -async function startDevContainerPlan(plan: DevContainerPlan, options: { forceRecreate: boolean; verifyPing: boolean; prepareRuntime: boolean }): Promise<{ +async function startDevContainerPlan(plan: DevContainerPlan, options: { + forceRecreate: boolean; + verifyPing: boolean; + prepareRuntime: boolean; + onCommandStart?: (command: { name: string; providerId: string; timeoutMs: number }) => void; + onCommandComplete?: (command: DevContainerCommandLog) => void; +}): Promise<{ ok: boolean; providerId: string; plan: DevContainerPlan; @@ -52,8 +58,10 @@ async function startDevContainerPlan(plan: DevContainerPlan, options: { forceRec }> { const commands: DevContainerCommandLog[] = []; const run = async (targetProviderId: string, script: string, timeoutMs: number, name: string): Promise => { + options.onCommandStart?.({ name, providerId: targetProviderId, timeoutMs }); const command = await ctx().runCodeQueueSsh(targetProviderId, script, timeoutMs, name); commands.push(command); + options.onCommandComplete?.(command); ctx().throwIfCommandFailed(command); return command; }; @@ -68,7 +76,7 @@ async function startDevContainerPlan(plan: DevContainerPlan, options: { forceRec await run("main-server", ctx().masterProxyPrepareScript(plan), 30_000, "master-proxy-prepare"); await run(plan.providerId, ctx().containerTunnelStartScript(plan), 45_000, "remote-tunnel-start"); await run("main-server", ctx().masterProxyFinishScript(plan), 30_000, "master-proxy-finish"); - if (options.prepareRuntime) await run(plan.providerId, ctx().remoteCodexRuntimePrepareScript(plan), 600_000, "remote-codex-runtime-prepare"); + if (options.prepareRuntime) await run(plan.providerId, ctx().remoteCodexRuntimePrepareScript(plan), 1_800_000, "remote-codex-runtime-prepare"); const verification: Record = {}; if (options.verifyPing) { const before = await run("main-server", ctx().masterProxyEvidenceScript(plan), 15_000, "master-proxy-evidence-before-ping"); @@ -92,7 +100,18 @@ export async function ensureTaskExecutionContainer(task: QueueTask): Promise { ctx().appendOutput(task, "system", `ensuring provider=${plan.providerId} container=${plan.containerName} workdir=${task.cwd}\n`, "provider/container"); - const result = await startDevContainerPlan(plan, { forceRecreate: false, verifyPing: false, prepareRuntime: true }); + const result = await startDevContainerPlan(plan, { + forceRecreate: false, + verifyPing: false, + prepareRuntime: true, + onCommandStart: (command) => { + ctx().appendOutput(task, "system", `provider prepare start name=${command.name} target=${command.providerId} timeoutMs=${command.timeoutMs}\n`, "provider/container"); + }, + onCommandComplete: (command) => { + const status = command.exitCode === 0 ? "ok" : `failed exit=${command.exitCode ?? "null"} signal=${command.signal ?? "null"}`; + ctx().appendOutput(task, command.exitCode === 0 ? "system" : "error", `provider prepare ${status} name=${command.name} target=${command.providerId} durationMs=${command.durationMs}\n`, "provider/container"); + }, + }); ctx().appendOutput(task, "system", `provider container ready provider=${plan.providerId} container=${plan.containerName} commands=${result.commands.length}\n`, "provider/container"); ctx().logger("info", "task_provider_container_ready", { taskId: task.id, diff --git a/src/components/microservices/code-queue/src/index.ts b/src/components/microservices/code-queue/src/index.ts index dd1f06f7..ae950632 100644 --- a/src/components/microservices/code-queue/src/index.ts +++ b/src/components/microservices/code-queue/src/index.ts @@ -109,6 +109,18 @@ import { transcriptChunkResponse, } from "./queue-api"; import { ReferenceTaskLookupError, configureReferences, injectReferencedTaskContext, taskReferenceIds } from "./references"; +import { + applyOaTraceStatsToTaskJson, + configureOaEvents, + publishCodeQueueQueueUpdated, + publishCodeQueueTaskUpdated, + publishCodeQueueTraceStatsSnapshot, + publishCodeQueueTraceStep, + outputTraceKind, + readOaTraceStatsForTask, + readOaTraceStatsForTaskAttempts, + readOaTraceStatsForTasks, +} from "./oa-events"; import { configureSelfTests, runJudgeInfraSelfTest, runQueueOrderingSelfTest, runReferenceInjectionSelfTest, runTracePortSelfTest } from "./self-tests"; import { configureTaskView, @@ -151,31 +163,8 @@ const queueIdPattern = /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/u; const queueNameMaxLength = 80; const config = readConfig(); -type CodeQueueEventType = "hello" | "task-updated" | "task-step" | "queue-updated"; - -interface CodeQueueEvent { - sequence: number; - type: CodeQueueEventType; - at: string; - reason: string; - taskId?: string; - queueId?: string; - status?: TaskStatus; - updatedAt?: string; - stepCount?: number; - outputMaxSeq?: number; -} - -interface CodeQueueEventClient { - enqueue: (chunk: string) => void; - close: () => void; -} - -const codeQueueEventClients = new Set(); -const codeQueueEventEncoder = new TextEncoder(); -const taskEventSnapshots = new Map(); -let codeQueueEventSequence = 0; const logger = createLogger("code-queue", config.logFile); +configureOaEvents({ baseUrl: config.oaEventFlowBaseUrl, logger, nowIso }); const state = emptyState(); let processing = false; const processingQueues = new Set(); @@ -330,6 +319,7 @@ function readConfig(): RuntimeConfig { turnNoActivityTimeoutMs: Math.max(60_000, Math.min(30 * 60_000, envNumber("CODEX_TURN_NO_ACTIVITY_TIMEOUT_MS", 6 * 60_000))), databaseUrl: envRequiredString("DATABASE_URL"), databaseFlushIntervalMs: Math.max(100, Math.min(10_000, envNumber("CODE_QUEUE_DATABASE_FLUSH_INTERVAL_MS", 1000))), + oaEventFlowBaseUrl: envString("OA_EVENT_FLOW_BASE_URL", "http://oa-event-flow:4255").replace(/\/+$/u, ""), notifyClaudeQqEnabled: envBool("CODE_QUEUE_NOTIFY_CLAUDEQQ_ENABLED", false), notifyClaudeQqBaseUrl: envString("CODE_QUEUE_NOTIFY_CLAUDEQQ_BASE_URL", "http://backend-core:8080/api/microservices/claudeqq/proxy").replace(/\/+$/u, ""), notifyClaudeQqTargetType: notifyTargetTypeRaw === "group" ? "group" : "private", @@ -610,142 +600,10 @@ function nowIso(): string { return new Date().toISOString(); } -function sseChunk(event: CodeQueueEvent): string { - return [ - `id: ${event.sequence}`, - `event: ${event.type}`, - `data: ${JSON.stringify(event)}`, - "", - "", - ].join("\n"); -} - -function emitCodeQueueEvent(event: Omit & { at?: string }): void { - const payload: CodeQueueEvent = { - ...event, - sequence: ++codeQueueEventSequence, - at: event.at || nowIso(), - }; - if (codeQueueEventClients.size === 0) return; - const chunk = sseChunk(payload); - for (const client of Array.from(codeQueueEventClients)) { - try { - client.enqueue(chunk); - } catch { - client.close(); - } - } -} - function taskOutputMaxSeq(task: QueueTask): number { return taskViewOutputMaxSeq(task); } -function publishTaskEvent(task: QueueTask, reason: string, options: { onlyStepChange?: boolean; stepChanged?: boolean } = {}): void { - // Hot path: never build full transcripts or read output archives here. - const stepCount = taskListStepCount(task); - const outputMaxSeq = taskOutputMaxSeq(task); - const previous = taskEventSnapshots.get(task.id); - const stepChanged = options.stepChanged ?? (previous === undefined ? stepCount > 0 : stepCount > previous.stepCount); - if (options.onlyStepChange === true && !stepChanged) { - taskEventSnapshots.set(task.id, { stepCount, outputMaxSeq, status: task.status, updatedAt: task.updatedAt }); - return; - } - const type: CodeQueueEventType = stepChanged ? "task-step" : "task-updated"; - taskEventSnapshots.set(task.id, { stepCount, outputMaxSeq, status: task.status, updatedAt: task.updatedAt }); - emitCodeQueueEvent({ - type, - reason, - taskId: task.id, - queueId: queueIdOf(task), - status: task.status, - updatedAt: task.updatedAt, - stepCount, - outputMaxSeq, - }); -} - -function publishQueueEvent(reason: string, queueId = ""): void { - emitCodeQueueEvent({ type: "queue-updated", reason, queueId: queueId || undefined }); -} - -function outputCanChangeStepCount(output: LiveOutput): boolean { - return output.channel === "command" || output.channel === "diff" || output.channel === "tool"; -} - -function outputStartsTraceStep(output: LiveOutput): boolean { - if (output.channel === "diff" || output.channel === "tool") return true; - if (output.channel !== "command") return false; - const method = String(output.method || ""); - return method === "item/started" || (method !== "item/commandExecution/outputDelta" && method !== "item/completed"); -} - -function recordTaskOutputMetrics(task: QueueTask, output: LiveOutput, op: "set" | "append"): boolean { - task.outputMaxSeq = Math.max(taskOutputMaxSeq(task), Number.isFinite(Number(output.seq)) ? Number(output.seq) : 0); - if (op === "append" || !outputStartsTraceStep(output)) return false; - const storedStepCount = Number(task.stepCount ?? task.llmStepCount); - const nextStepCount = Number.isFinite(storedStepCount) && storedStepCount >= 0 - ? Math.floor(storedStepCount) + 1 - : task.output.reduce((count, item) => count + (outputStartsTraceStep(item) ? 1 : 0), 0); - task.stepCount = nextStepCount; - task.llmStepCount = nextStepCount; - return true; -} - -function codeQueueEventsResponse(): Response { - let heartbeat: ReturnType | null = null; - let client: CodeQueueEventClient | null = null; - const stream = new ReadableStream({ - start(controller) { - client = { - enqueue(chunk: string) { - controller.enqueue(codeQueueEventEncoder.encode(chunk)); - }, - close() { - if (heartbeat !== null) { - clearInterval(heartbeat); - heartbeat = null; - } - if (client !== null) codeQueueEventClients.delete(client); - client = null; - try { - controller.close(); - } catch { - // The browser may have already closed the EventSource connection. - } - }, - }; - codeQueueEventClients.add(client); - controller.enqueue(codeQueueEventEncoder.encode("retry: 1000\n\n")); - client.enqueue(sseChunk({ - sequence: codeQueueEventSequence, - type: "hello", - at: nowIso(), - reason: "connected", - })); - heartbeat = setInterval(() => { - try { - controller.enqueue(codeQueueEventEncoder.encode(`: heartbeat ${nowIso()}\n\n`)); - } catch { - client?.close(); - } - }, 25_000); - heartbeat.unref?.(); - }, - cancel() { - client?.close(); - }, - }); - return new Response(stream, { - headers: { - "content-type": "text/event-stream; charset=utf-8", - "cache-control": "no-store, no-transform", - "connection": "keep-alive", - "x-accel-buffering": "no", - }, - }); -} - function resolveReasoningEffort(model: string, explicit?: string | null): string | null { const requested = explicit?.trim(); if (requested) return requested; @@ -869,7 +727,7 @@ function pruneTaskHotState(task: QueueTask): void { } function taskRetainedTraceStepCount(task: QueueTask): number { - return task.output.reduce((count, output) => count + (outputStartsTraceStep(output) ? 1 : 0), 0); + return task.output.reduce((count, output) => count + (outputStartsTraceStep(task, output) ? 1 : 0), 0); } function normalizeTaskMetric(value: unknown): number | null { @@ -939,6 +797,104 @@ function redactDatabaseUrl(value: string): string { } } +function publishTaskOaEvent(task: QueueTask, reason: string, options: { onlyStepChange?: boolean; stepChanged?: boolean } = {}): void { + const stepCount = taskListStepCount(task); + const outputMaxSeq = taskOutputMaxSeq(task); + if (options.onlyStepChange === true && options.stepChanged !== true) return; + const queueId = queueIdOf(task); + publishCodeQueueTaskUpdated(task, queueId, reason, stepCount, outputMaxSeq); + publishCodeQueueTraceStatsSnapshot(task, queueId, reason, stepCount, outputMaxSeq); +} + +function publishQueueEvent(reason: string, queueId = ""): void { + publishCodeQueueQueueUpdated(queueId, reason); +} + +function isOpenCodeStepBoundaryMethod(method: string | undefined): boolean { + return method === "opencode/step-start" || method === "opencode/step-finish"; +} + +function outputCanChangeStepCount(output: LiveOutput): boolean { + if (output.channel === "user" && output.method === "enqueue") return false; + return !isOpenCodeStepBoundaryMethod(output.method); +} + +function commandStartedBeforeIn(outputs: LiveOutput[], output: LiveOutput): boolean { + if (typeof output.itemId !== "string") return false; + return outputs.some((item) => item !== output && item.itemId === output.itemId && item.channel === "command" && item.method === "item/started"); +} + +function outputStartsTraceStepInHistory(outputs: LiveOutput[], output: LiveOutput): boolean { + if (output.channel === "user" && output.method === "enqueue") return false; + if (isOpenCodeStepBoundaryMethod(output.method)) return false; + if (output.channel === "diff" || output.channel === "tool" || output.channel === "error" || output.channel === "assistant" || output.channel === "reasoning" || output.channel === "system") return true; + if (output.channel === "user") return true; + if (output.channel !== "command") return true; + const method = String(output.method || ""); + if (method === "item/started") return true; + if (method === "item/commandExecution/outputDelta") return false; + if (method === "item/completed") return !commandStartedBeforeIn(outputs, output); + return true; +} + +function outputStartsTraceStep(task: QueueTask, output: LiveOutput): boolean { + return outputStartsTraceStepInHistory(task.output, output); +} + +function traceStatsFromOutputs(outputs: LiveOutput[]): { stepCount: number; readCount: number; editCount: number; runCount: number; errorCount: number } { + const visibleOutputs = outputs.filter((output) => outputStartsTraceStepInHistory(outputs, output)); + const stats = { stepCount: visibleOutputs.length, readCount: 0, editCount: 0, runCount: 0, errorCount: 0 }; + for (const output of visibleOutputs) { + const kind = outputTraceKind(output); + if (kind === "read") stats.readCount += 1; + if (kind === "edit") stats.editCount += 1; + if (kind === "run") stats.runCount += 1; + if (kind === "error") stats.errorCount += 1; + } + return stats; +} + +function attemptIndexFromOutput(output: LiveOutput): number | null { + const match = String(output.text || "").match(/\battempt\s+(\d+)\s*\/\s*\d+\b/iu); + const value = Number(match?.[1] ?? NaN); + return Number.isInteger(value) && value > 0 ? value : null; +} + +function outputAttemptIndexMap(outputs: LiveOutput[]): Map { + const result = new Map(); + let currentAttempt = 0; + for (const output of outputs.slice().sort((left, right) => Number(left.seq) - Number(right.seq))) { + const startedAttempt = attemptIndexFromOutput(output); + if (startedAttempt !== null) currentAttempt = startedAttempt; + if (currentAttempt > 0 && outputStartsTraceStepInHistory(outputs, output)) result.set(output.seq, currentAttempt); + } + return result; +} + +function traceAttemptIndexesForTask(task: QueueTask): number[] { + const indexes = new Set(); + for (const attempt of task.attempts) { + const index = Number(attempt.index); + if (Number.isInteger(index) && index > 0) indexes.add(index); + } + const currentAttempt = Number(task.currentAttempt || 0); + const maxAttempt = Math.max(currentAttempt, ...Array.from(indexes), 0); + for (let index = 1; index <= maxAttempt; index += 1) indexes.add(index); + return Array.from(indexes).sort((left, right) => left - right); +} + +function recordTaskOutputMetrics(task: QueueTask, output: LiveOutput, op: "set" | "append"): boolean { + task.outputMaxSeq = Math.max(taskOutputMaxSeq(task), Number.isFinite(Number(output.seq)) ? Number(output.seq) : 0); + if (op === "append" || !outputStartsTraceStep(task, output)) return false; + const storedStepCount = Number(task.stepCount ?? task.llmStepCount); + const nextStepCount = Number.isFinite(storedStepCount) && storedStepCount >= 0 + ? Math.floor(storedStepCount) + 1 + : task.output.reduce((count, item) => count + (outputStartsTraceStep(task, item) ? 1 : 0), 0); + task.stepCount = nextStepCount; + task.llmStepCount = nextStepCount; + return true; +} + function errorToJson(error: unknown): JsonValue { if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack ?? null }; return String(error); @@ -957,7 +913,7 @@ function markTaskDirty(taskId: string): void { function persistTaskState(task: QueueTask): void { markTaskDirty(task.id); persistState(false); - publishTaskEvent(task, "persist"); + publishTaskOaEvent(task, "persist"); } function markQueueDirty(queueId: string): void { @@ -1780,7 +1736,7 @@ function addEvent(task: QueueTask, event: CodexEventSummary): void { task.events.push(event); if (config.maxInMemoryEventRecords > 0 && task.events.length > config.maxInMemoryEventRecords) task.events.splice(0, task.events.length - config.maxInMemoryEventRecords); markTaskDirty(task.id); - publishTaskEvent(task, "agent-event", { onlyStepChange: true }); + publishTaskOaEvent(task, "agent-event", { onlyStepChange: true }); } function taskReferencesEqual(left: string[], right: string[]): boolean { @@ -1970,8 +1926,9 @@ configureTaskOutput({ onOutputAppended: (task, output, op) => { const archiveOp = op === "append" ? "append" : "set"; const stepChanged = recordTaskOutputMetrics(task, output, archiveOp); + if (stepChanged) publishCodeQueueTraceStep(task, queueIdOf(task), output, taskOutputMaxSeq(task)); if (archiveOp === "append" && !outputCanChangeStepCount(output)) return; - publishTaskEvent(task, "output", { onlyStepChange: archiveOp === "append", stepChanged }); + publishTaskOaEvent(task, "output", { onlyStepChange: archiveOp === "append", stepChanged }); }, schedulePersistState, }); @@ -2050,6 +2007,7 @@ configureQueueApi({ safeQueueName, sql, taskQueueEnteredAt, + traceStatsForTasks: readOaTraceStatsForTasks, tasks: () => state.tasks, truthyParam, }); @@ -2928,7 +2886,7 @@ async function createTasks(req: Request): Promise { for (const task of tasks) appendOutput(task, "user", `${task.prompt}\n`, "enqueue"); state.tasks.push(...tasks); if (tasks.length > 0) armIdleNotification(); - for (const task of tasks) publishTaskEvent(task, "enqueue"); + for (const task of tasks) publishTaskOaEvent(task, "enqueue"); for (const id of new Set(tasks.map(queueIdOf))) publishQueueEvent("enqueue", id); persistState(); logger("info", "tasks_enqueued", { count: tasks.length, ids: tasks.map((task) => task.id), queueIds: Array.from(new Set(tasks.map(queueIdOf))), providerIds: Array.from(new Set(tasks.map((task) => task.providerId))) }); @@ -3057,7 +3015,7 @@ async function markTaskRead(task: QueueTask): Promise { task.readAt = nowIso(); markTaskDirty(task.id); persistState(false); - publishTaskEvent(task, "read"); + publishTaskOaEvent(task, "read"); logger("info", "task_marked_read", { taskId: task.id, queueId: queueIdOf(task), status: task.status }); } await flushDirtyTasksToDatabase(true); @@ -3102,7 +3060,7 @@ async function markTaskReadById(taskId: string): Promise { const hotTask = findTask(taskId); if (hotTask !== null) { hotTask.readAt = readAt; - publishTaskEvent(hotTask, "read"); + publishTaskOaEvent(hotTask, "read"); } logger("info", "task_marked_read", { taskId, queueId: safeQueueId(row.queue_id), status: row.status }); } @@ -3148,7 +3106,7 @@ async function markTerminalTasksRead(url: URL): Promise { for (const task of state.tasks) { if (!ids.has(task.id)) continue; task.readAt = readAt; - publishTaskEvent(task, "read-all"); + publishTaskOaEvent(task, "read-all"); } if (ids.size > 0) { logger("info", "terminal_tasks_marked_read", { count: ids.size, queueId }); @@ -3161,7 +3119,7 @@ async function markTerminalTasksRead(url: URL): Promise { if (!terminalTaskUnread(task)) continue; task.readAt = readAt; markTaskDirty(task.id); - publishTaskEvent(task, "read-all"); + publishTaskOaEvent(task, "read-all"); count += 1; } if (count > 0) { @@ -3269,6 +3227,40 @@ async function mergeDatabaseQueueTasks(sourceQueueIds: string[], targetQueueId: return rows.map((row) => row.id); } +async function deleteDatabaseQueues(queueIds: string[]): Promise { + if (!databaseReady || queueIds.length === 0) return []; + const rows = await sql>` + DELETE FROM unidesk_code_queue_queues + WHERE id IN ${sql(queueIds)} + RETURNING id + `; + return rows.map((row) => row.id); +} + +function queueSnapshot(queueId: string, timestamp: string): QueueRecord { + const queue = knownQueue(queueId); + return queue === null + ? { id: queueId, name: queueId, createdAt: timestamp, updatedAt: timestamp } + : { ...queue }; +} + +function deleteQueuesFromState(queueIds: string[]): QueueRecord[] { + const queueIdSet = new Set(queueIds); + const deletedQueues: QueueRecord[] = []; + const retainedQueues: QueueRecord[] = []; + for (const queue of state.queues) { + if (queueIdSet.has(queue.id)) { + deletedQueues.push({ ...queue }); + dirtyDatabaseQueueIds.delete(queue.id); + } else { + retainedQueues.push(queue); + } + } + state.queues.splice(0, state.queues.length, ...retainedQueues); + for (const queueId of queueIds) dirtyDatabaseQueueIds.delete(queueId); + return deletedQueues; +} + async function mergeQueues(targetQueueIdValue: string | null, req: Request): Promise { const body = await readJson(req); const record = typeof body === "object" && body !== null && !Array.isArray(body) ? body as Record : {}; @@ -3289,13 +3281,9 @@ async function mergeQueues(targetQueueIdValue: string | null, req: Request): Pro const mergedAt = nowIso(); const targetQueue = ensureQueue(targetQueueId); - const sourceQueues = sourceQueueIds.map((id) => ensureQueue(id)); + const sourceQueues = sourceQueueIds.map((id) => queueSnapshot(id, mergedAt)); targetQueue.updatedAt = mergedAt; markQueueDirty(targetQueue.id); - for (const queue of sourceQueues) { - queue.updatedAt = mergedAt; - markQueueDirty(queue.id); - } const sourceSet = new Set(sourceQueueIds); const hotMovedTasks: QueueTask[] = []; @@ -3306,18 +3294,22 @@ async function mergeQueues(targetQueueIdValue: string | null, req: Request): Pro task.queueId = targetQueueId; hotMovedTasks.push(task); markTaskDirty(task.id); - publishTaskEvent(task, "queue-merged"); + publishTaskOaEvent(task, "queue-merged"); } const databaseMovedTaskIds = await mergeDatabaseQueueTasks(sourceQueueIds, targetQueueId); + const deletedSourceQueues = deleteQueuesFromState(sourceQueueIds); + const databaseDeletedQueueIds = await deleteDatabaseQueues(sourceQueueIds); persistState(false); publishQueueEvent("queue-merged", targetQueueId); - for (const sourceQueueId of sourceQueueIds) publishQueueEvent("queue-merged", sourceQueueId); + for (const sourceQueueId of sourceQueueIds) publishQueueEvent("queue-deleted-after-merge", sourceQueueId); if (hotMovedTasks.some((task) => task.status === "queued" || task.status === "retry_wait")) armIdleNotification(); logger("info", "queues_merged", { targetQueueId, sourceQueueIds, + deletedSourceQueueIds: deletedSourceQueues.map((queue) => queue.id), hotMovedTaskCount: hotMovedTasks.length, databaseMovedTaskCount: databaseReady ? databaseMovedTaskIds.length : null, + databaseDeletedQueueIds: databaseReady ? databaseDeletedQueueIds : null, }); for (const id of mergeQueueIds) mergingQueues.delete(id); scheduleQueue(targetQueueId); @@ -3339,9 +3331,11 @@ async function mergeQueues(targetQueueIdValue: string | null, req: Request): Pro mergedTaskCount: databaseReady ? databaseMovedTaskIds.length : hotMovedTasks.length, movedTaskIds: orderedMovedTaskIds.slice(0, 500), targetTaskOrder: targetTaskOrder.slice(0, 500), - order: "merged tasks keep their original queueEnteredAt/createdAt ordering; source queue records are retained and never deleted", + order: "merged tasks keep their original queueEnteredAt/createdAt ordering; source queue records are deleted after merge", targetQueue, sourceQueues, + deletedSourceQueues: deletedSourceQueues.length > 0 ? deletedSourceQueues : sourceQueues, + deletedSourceQueueIds: sourceQueueIds, queues: perQueueSummaries(tasks), summary: queueSummary(false, tasks), }, 202); @@ -3376,13 +3370,41 @@ async function moveTaskToQueue(task: QueueTask, req: Request): Promise return jsonResponse({ ok: true, task: taskForResponse(task), queue: await queueSummaryForResponse() }, 202); } +async function backfillOaTraceStats(url: URL): Promise { + const limitRaw = Number(url.searchParams.get("limit") ?? 2000); + const limit = Number.isInteger(limitRaw) && limitRaw > 0 ? Math.min(20_000, limitRaw) : 2000; + const taskId = url.searchParams.get("taskId")?.trim() ?? ""; + const tasks = taskId.length > 0 + ? [await findTaskForRead(taskId)].filter((task): task is QueueTask => task !== null) + : (await loadAllTasksForRead()).slice(-limit); + const includeSteps = url.searchParams.get("steps") !== "0"; + let stepEventCount = 0; + for (const task of tasks) { + const queueId = queueIdOf(task); + const outputMaxSeq = taskOutputMaxSeq(task); + const output = taskFullOutput(task); + const traceStats = traceStatsFromOutputs(output); + const attemptBySeq = outputAttemptIndexMap(output); + if (includeSteps) { + for (const item of output) { + if (!outputStartsTraceStepInHistory(output, item)) continue; + publishCodeQueueTraceStep(task, queueId, item, outputMaxSeq, attemptBySeq.get(item.seq) ?? null); + stepEventCount += 1; + } + } + publishCodeQueueTraceStatsSnapshot(task, queueId, "backfill", traceStats.stepCount, outputMaxSeq, traceStats); + } + logger("info", "oa_trace_stats_backfill_enqueued", { taskCount: tasks.length, limit, taskId: taskId || null, includeSteps, stepEventCount }); + return { ok: true, taskCount: tasks.length, limit, taskId: taskId || null, includeSteps, stepEventCount, eventFlowBaseUrl: config.oaEventFlowBaseUrl } as unknown as JsonValue; +} + async function route(req: Request): Promise { const url = new URL(req.url); if (req.method === "OPTIONS") return jsonResponse({ ok: true }); try { if (url.pathname === "/" || url.pathname === "/health") return jsonResponse({ ok: true, service: "code-queue", queue: await queueSummaryForHealth(), startedAt: serviceStartedAt }); if (url.pathname === "/logs") return jsonResponse({ ok: true, logs: recentLogs.slice(-parseLimit(url)) }); - if (url.pathname === "/api/events" && req.method === "GET") return codeQueueEventsResponse(); + if (url.pathname === "/api/events" && req.method === "GET") return jsonResponse({ ok: false, error: "Code Queue private SSE was removed; subscribe to oa-event-flow /api/events/stream with service:code-queue tags." }, 410); if (url.pathname === "/api/dev-ready" && req.method === "GET") return jsonResponse({ ok: true, devReady: collectDevReady() }); if (url.pathname === "/api/dev-containers" && req.method === "GET") { const plan = buildDevContainerPlan(normalizeProviderId(config.devContainerDefaultProviderId) ?? "D601", {}); @@ -3413,6 +3435,7 @@ async function route(req: Request): Promise { if (url.pathname === "/api/queue-order/self-test" && (req.method === "GET" || req.method === "POST")) return jsonResponse(runQueueOrderingSelfTest()); if (url.pathname === "/api/reference-injection/self-test" && (req.method === "GET" || req.method === "POST")) return jsonResponse(await runReferenceInjectionSelfTest()); if (url.pathname === "/api/trace-port/self-test" && (req.method === "GET" || req.method === "POST")) return jsonResponse(runTracePortSelfTest()); + if (url.pathname === "/api/oa/backfill" && (req.method === "GET" || req.method === "POST")) return jsonResponse(await backfillOaTraceStats(url)); if (url.pathname === "/api/notifications/claudeqq" && req.method === "GET") { await loadClaudeQqNotificationOutboxFromDatabase(); const limit = parseLimit(url); @@ -3462,7 +3485,8 @@ async function route(req: Request): Promise { .filter((task) => taskMatchesSearch(task, searchTerms)); const limit = parseLimit(url); const page = taskPageRows(filteredTasks, url, limit); - const tasks = page.rows.map((task) => taskForListResponse(task, lite, allTasks)); + const traceStats = await readOaTraceStatsForTasks(page.rows.map((task) => task.id)); + const tasks = page.rows.map((task) => applyOaTraceStatsToTaskJson(taskForListResponse(task, lite, allTasks), traceStats.get(`task:${task.id}`) ?? null)); return jsonResponse({ ok: true, queue: queueSummary(includeDevReady, allTasks), @@ -3507,7 +3531,8 @@ async function route(req: Request): Promise { if (traceSummaryMatch !== null && req.method === "GET") { const task = await findTaskForRead(decodeURIComponent(traceSummaryMatch[1] ?? "")); if (task === null) return jsonResponse({ ok: false, error: "task not found" }, 404); - return jsonResponse({ ok: true, summary: taskTraceSummaryResponse(task) }); + const traceStats = await readOaTraceStatsForTaskAttempts(task.id, traceAttemptIndexesForTask(task)); + return jsonResponse({ ok: true, summary: taskTraceSummaryResponse(task, traceStats.get(`task:${task.id}`) ?? null, traceStats) }); } const traceStepsMatch = url.pathname.match(/^\/api\/tasks\/([^/]+)\/trace-steps$/u); if (traceStepsMatch !== null && req.method === "GET") { @@ -3543,9 +3568,10 @@ async function route(req: Request): Promise { if (action === "edit" && (req.method === "POST" || req.method === "PATCH")) return await editQueuedTaskPrompt(task, req); if (action !== undefined) return jsonResponse({ ok: false, error: "not found" }, 404); if (req.method === "GET") { - if (url.searchParams.get("meta") === "1") return jsonResponse({ ok: true, task: taskForMetaResponse(task) }); + const traceStats = await readOaTraceStatsForTask(task.id); + if (url.searchParams.get("meta") === "1") return jsonResponse({ ok: true, task: applyOaTraceStatsToTaskJson(taskForMetaResponse(task), traceStats) }); const includeRaw = url.searchParams.get("raw") === "1" || url.searchParams.get("full") === "1"; - return jsonResponse({ ok: true, task: taskForResponse(task, true, includeRaw) }); + return jsonResponse({ ok: true, task: applyOaTraceStatsToTaskJson(taskForResponse(task, true, includeRaw), traceStats) }); } if (req.method === "DELETE") return await interruptTask(task); return jsonResponse({ ok: false, error: "method not allowed" }, 405); @@ -3578,5 +3604,6 @@ const startupRecovered = queueActiveTasksForRestartRetry("Service restarted whil if (startupRecovered > 0) logger("warn", "startup_requeued_active_tasks", { recovered: startupRecovered }); persistState(); serviceReady = true; +setTimeout(() => { void backfillOaTraceStats(new URL("http://code-queue.local/api/oa/backfill?limit=2000")).catch((error) => logger("warn", "oa_trace_stats_startup_backfill_failed", { error: errorToJson(error) })); }, 1000).unref?.(); scheduleQueue(); scheduleClaudeQqNotificationDrain(1000); diff --git a/src/components/microservices/code-queue/src/oa-events.ts b/src/components/microservices/code-queue/src/oa-events.ts new file mode 100644 index 00000000..b8d5c1e4 --- /dev/null +++ b/src/components/microservices/code-queue/src/oa-events.ts @@ -0,0 +1,370 @@ +import { createHash } from "node:crypto"; +import type { JsonValue, LiveOutput, QueueTask, TaskStatus } from "./types"; + +type JsonRecord = Record; + +interface OaEventContext { + baseUrl: string; + logger: (level: "debug" | "info" | "warn" | "error", message: string, data?: JsonValue) => void; + nowIso: () => string; +} + +interface OaEventEnvelope { + eventId: string; + type: string; + createdAt: string; + sourceKind: string; + sourceId: string; + aggregateType: string; + aggregateId: string; + correlationId: string; + causationId?: string | null; + tags: string[]; + payload: JsonRecord; +} + +export interface OaTraceStats extends JsonRecord { + scopeId: string; + source: "oa-event-flow"; +} + +const postTimeoutMs = 2500; +let context: OaEventContext | null = null; + +export function configureOaEvents(runtimeContext: OaEventContext): void { + const baseUrl = runtimeContext.baseUrl.replace(/\/+$/u, ""); + if (baseUrl.length === 0) throw new Error("OA_EVENT_FLOW_BASE_URL is required"); + context = { ...runtimeContext, baseUrl }; +} + +function ctx(): OaEventContext { + if (context === null) throw new Error("oa-events module is not configured"); + return context; +} + +function safePreview(value: string, max = 300): string { + const clean = String(value || "").replace(/\u001b\[[0-9;]*m/gu, "").trim(); + return clean.length > max ? `${clean.slice(0, max)}...` : clean; +} + +function hash(value: unknown): string { + return createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 20); +} + +function addTag(tags: string[], value: string): void { + const tag = value.trim(); + if (tag.length > 0 && !tags.includes(tag)) tags.push(tag); +} + +function taskTags(task: QueueTask, queueId: string, extra: string[] = [], attemptIndexOverride: number | null = null): string[] { + const tags = ["service:code-queue", "trace", `task:${task.id}`, `queue:${queueId}`]; + const attempt = Number(attemptIndexOverride ?? (task.currentAttempt || task.attempts.length || 0)); + if (attempt > 0) addTag(tags, `attempt:${attempt}`); + for (const tag of extra) addTag(tags, tag); + return tags; +} + +function postOaEvent(event: OaEventEnvelope): void { + const runtime = ctx(); + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), postTimeoutMs); + void fetch(`${runtime.baseUrl}/api/events`, { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify(event), + signal: controller.signal, + }).then(async (response) => { + if (!response.ok) { + runtime.logger("warn", "oa_event_publish_failed", { eventId: event.eventId, type: event.type, status: response.status, body: safePreview(await response.text(), 1000) }); + } + }).catch((error) => { + runtime.logger("warn", "oa_event_publish_error", { eventId: event.eventId, type: event.type, error: error instanceof Error ? error.message : String(error) }); + }).finally(() => clearTimeout(timer)); +} + +function normalizeCommandText(text: string): string { + const trimmed = String(text || "").trim(); + if (!trimmed.startsWith("{")) return trimmed; + try { + const parsed = JSON.parse(trimmed) as Record; + const command = parsed.command ?? parsed.cmd ?? parsed.text ?? parsed.summary; + return typeof command === "string" ? command : trimmed; + } catch { + return trimmed; + } +} + +function commandKind(command: string): "read" | "edit" | "run" { + if (/\b(apply_patch|git apply|cat >|tee .*<<|sed -i|python3? .*write_text|write|patch|edit|delete|create)\b/iu.test(command)) return "edit"; + if (/\b(rg|grep|find|ls|cat|sed -n|tail|head|git status|git diff|ps|read|glob|search|view)\b/iu.test(command)) return "read"; + return "run"; +} + +function openCodeToolRecord(output: LiveOutput): Record | null { + if (!String(output.method || "").startsWith("opencode/")) return null; + try { + const parsed = JSON.parse(output.text) as unknown; + return typeof parsed === "object" && parsed !== null && !Array.isArray(parsed) ? parsed as Record : null; + } catch { + return null; + } +} + +function recordString(record: Record | null, keys: string[]): string { + if (record === null) return ""; + for (const key of keys) { + const value = record[key]; + if (typeof value === "string") return value; + } + return ""; +} + +export function outputTraceKind(output: LiveOutput): "read" | "edit" | "run" | "error" | "message" | "system" { + if (output.channel === "diff") return "edit"; + if (output.channel === "error") return "error"; + if (output.channel === "assistant" || output.channel === "user" || output.channel === "reasoning") return "message"; + if (output.channel === "tool") { + const record = openCodeToolRecord(output); + const part = record?.part && typeof record.part === "object" && !Array.isArray(record.part) ? record.part as Record : null; + const state = part?.state && typeof part.state === "object" && !Array.isArray(part.state) ? part.state as Record : null; + const input = state?.input && typeof state.input === "object" && !Array.isArray(state.input) ? state.input as Record : null; + const tool = recordString(part, ["tool", "title"]) || recordString(record, ["type", "event", "name"]); + const command = recordString(input, ["command", "cmd", "script", "path", "pattern", "query"]); + return commandKind(`${tool} ${command}`); + } + if (output.channel === "command") return commandKind(normalizeCommandText(output.text)); + return "system"; +} + +function outputTitle(output: LiveOutput, kind: string): string { + const method = String(output.method || ""); + if (output.channel === "diff") return "Edited files"; + if (output.channel === "error") return "Error"; + if (output.channel === "assistant") return "Assistant message"; + if (output.channel === "reasoning") return "Reasoning"; + if (output.channel === "user") return method === "enqueue" ? "Submitted prompt" : "User prompt"; + const command = normalizeCommandText(output.text).split(/\r?\n/u).find((line) => line.trim().length > 0)?.trim() ?? ""; + if (command.length > 0) return safePreview(command, 160); + return kind === "read" ? "Read" : kind === "edit" ? "Edit" : kind === "run" ? "Run" : method || output.channel; +} + +function outputSummaryLines(output: LiveOutput): string[] { + return String(output.text || "") + .replace(/\u001b\[[0-9;]*m/gu, "") + .split(/\r?\n/u) + .map((line) => line.trim()) + .filter(Boolean) + .slice(0, 4) + .map((line) => safePreview(line, 220)); +} + +function outputAttemptIndex(output: LiveOutput): number | null { + const match = String(output.text || "").match(/\battempt\s+(\d+)\s*\/\s*\d+\b/iu); + const value = Number(match?.[1] ?? NaN); + return Number.isInteger(value) && value > 0 ? value : null; +} + +function taskOutputAttemptIndex(task: QueueTask, output: LiveOutput, attemptIndexOverride: number | null = null): number | null { + if (attemptIndexOverride !== null && Number.isInteger(attemptIndexOverride) && attemptIndexOverride > 0) return attemptIndexOverride; + const fromOutput = outputAttemptIndex(output); + if (fromOutput !== null) return fromOutput; + const current = Number(task.currentAttempt || 0); + return Number.isInteger(current) && current > 0 ? current : null; +} + +function taskStatusPayload(task: QueueTask, queueId: string, reason: string, stepCount: number, outputMaxSeq: number): JsonRecord { + return { + taskId: task.id, + queueId, + reason, + status: task.status, + updatedAt: task.updatedAt, + createdAt: task.createdAt, + startedAt: task.startedAt, + finishedAt: task.finishedAt, + readAt: task.readAt, + providerId: task.providerId, + model: task.model, + currentAttempt: task.currentAttempt, + stepCount, + outputMaxSeq, + }; +} + +export function publishCodeQueueTaskUpdated(task: QueueTask, queueId: string, reason: string, stepCount: number, outputMaxSeq: number): void { + postOaEvent({ + eventId: `code-queue:task-updated:${task.id}:${reason}:${task.updatedAt}:${outputMaxSeq}:${hash({ status: task.status, queueId, readAt: task.readAt })}`, + type: "task-updated", + createdAt: ctx().nowIso(), + sourceKind: "service", + sourceId: "code-queue", + aggregateType: "task", + aggregateId: task.id, + correlationId: task.id, + tags: taskTags(task, queueId, ["task"]), + payload: taskStatusPayload(task, queueId, reason, stepCount, outputMaxSeq), + }); +} + +export function publishCodeQueueTraceStatsSnapshot( + task: QueueTask, + queueId: string, + reason: string, + stepCount: number, + outputMaxSeq: number, + statsOverride: Partial<{ llmStepCount: number; traceLineCount: number; readCount: number; editCount: number; runCount: number; errorCount: number }> = {}, +): void { + postOaEvent({ + eventId: `code-queue:trace-stats-snapshot:${task.id}:${task.updatedAt}:${stepCount}:${outputMaxSeq}`, + type: "trace-stats-snapshot", + createdAt: ctx().nowIso(), + sourceKind: "service", + sourceId: "code-queue", + aggregateType: "task", + aggregateId: task.id, + correlationId: task.id, + tags: taskTags(task, queueId, ["stats"]), + payload: { + ...taskStatusPayload(task, queueId, reason, stepCount, outputMaxSeq), + scopeId: `task:${task.id}`, + stats: { + stepCount, + llmStepCount: statsOverride.llmStepCount ?? stepCount, + traceLineCount: statsOverride.traceLineCount ?? stepCount, + outputMaxSeq, + readCount: statsOverride.readCount ?? 0, + editCount: statsOverride.editCount ?? 0, + runCount: statsOverride.runCount ?? 0, + errorCount: statsOverride.errorCount ?? (task.lastError ? 1 : 0), + attempts: task.attempts.map((attempt) => ({ index: attempt.index, mode: attempt.mode, startedAt: attempt.startedAt, finishedAt: attempt.finishedAt, terminalStatus: attempt.terminalStatus })), + }, + }, + }); +} + +export function publishCodeQueueTraceStep(task: QueueTask, queueId: string, output: LiveOutput, outputMaxSeq: number, attemptIndexOverride: number | null = null): void { + const kind = outputTraceKind(output); + const attemptIndex = taskOutputAttemptIndex(task, output, attemptIndexOverride); + const attemptScopeId = attemptIndex === null ? null : taskAttemptScopeId(task.id, attemptIndex); + postOaEvent({ + eventId: `code-queue:trace-step-created:${task.id}:${output.seq}`, + type: "trace-step-created", + createdAt: output.at || ctx().nowIso(), + sourceKind: "service", + sourceId: "code-queue", + aggregateType: "task", + aggregateId: task.id, + correlationId: task.id, + tags: taskTags(task, queueId, attemptScopeId === null ? [] : [attemptScopeId], attemptIndex), + payload: { + taskId: task.id, + queueId, + scopeId: `task:${task.id}`, + attemptIndex, + attemptScopeId, + seq: output.seq, + outputSeq: output.seq, + outputMaxSeq, + kind, + channel: output.channel, + method: output.method ?? "", + itemId: output.itemId ?? "", + title: outputTitle(output, kind), + status: task.status, + summaryLines: outputSummaryLines(output), + rawSeqs: [output.seq], + }, + }); +} + +export function publishCodeQueueQueueUpdated(queueId: string, reason: string): void { + const at = ctx().nowIso(); + postOaEvent({ + eventId: `code-queue:queue-updated:${queueId || "all"}:${reason}:${at}`, + type: "queue-updated", + createdAt: at, + sourceKind: "service", + sourceId: "code-queue", + aggregateType: "queue", + aggregateId: queueId || "all", + correlationId: `queue:${queueId || "all"}`, + tags: ["service:code-queue", "queue", ...(queueId ? [`queue:${queueId}`] : [])], + payload: { queueId, reason, updatedAt: at }, + }); +} + +export async function readOaTraceStatsForScopeIds(scopeIds: string[]): Promise> { + const result = new Map(); + const uniqueScopeIds = Array.from(new Set(scopeIds.map((id) => String(id || "").trim()).filter(Boolean))); + if (uniqueScopeIds.length === 0) return result; + const runtime = ctx(); + const url = new URL(`${runtime.baseUrl}/api/stats/trace`); + url.searchParams.set("scopeIds", uniqueScopeIds.join(",")); + url.searchParams.set("limit", String(Math.max(100, uniqueScopeIds.length))); + const controller = new AbortController(); + const timer = setTimeout(() => controller.abort(), 2500); + try { + const response = await fetch(url, { signal: controller.signal }); + if (!response.ok) throw new Error(`status=${response.status}`); + const body = await response.json() as Record; + const stats = Array.isArray(body.stats) ? body.stats : []; + for (const item of stats) { + if (typeof item !== "object" || item === null || Array.isArray(item)) continue; + const record = item as OaTraceStats; + if (typeof record.scopeId !== "string") continue; + result.set(record.scopeId, record); + } + } catch (error) { + runtime.logger("warn", "oa_trace_stats_read_failed", { scopeCount: uniqueScopeIds.length, error: error instanceof Error ? error.message : String(error) }); + } finally { + clearTimeout(timer); + } + return result; +} + +export async function readOaTraceStatsForTasks(taskIds: string[]): Promise> { + const uniqueTaskIds = Array.from(new Set(taskIds.map((id) => String(id || "").trim()).filter(Boolean))); + return readOaTraceStatsForScopeIds(uniqueTaskIds.map((id) => taskScopeId(id))); +} + +export async function readOaTraceStatsForTask(taskId: string): Promise { + const map = await readOaTraceStatsForTasks([taskId]); + return map.get(taskScopeId(taskId)) ?? null; +} + +export async function readOaTraceStatsForTaskAttempts(taskId: string, attemptIndexes: number[]): Promise> { + const uniqueAttempts = Array.from(new Set(attemptIndexes.filter((index) => Number.isInteger(index) && index > 0).map((index) => Math.floor(index)))); + return readOaTraceStatsForScopeIds([taskScopeId(taskId), ...uniqueAttempts.map((index) => taskAttemptScopeId(taskId, index))]); +} + +function statNumber(stats: OaTraceStats | null | undefined, key: string): number | null { + const value = Number(stats?.[key]); + return Number.isFinite(value) && value >= 0 ? Math.floor(value) : null; +} + +export function applyOaTraceStatsToTaskJson(value: JsonValue, stats: OaTraceStats | null | undefined): JsonValue { + if (typeof value !== "object" || value === null || Array.isArray(value)) return value; + if (stats === null || stats === undefined) { + return { ...(value as JsonRecord), traceStats: null, statsSource: "unavailable", stepCount: null, llmStepCount: null } as unknown as JsonValue; + } + const stepCount = statNumber(stats, "stepCount"); + const outputMaxSeq = statNumber(stats, "outputMaxSeq"); + const patch: JsonRecord = { + traceStats: stats, + statsSource: "oa-event-flow", + }; + if (stepCount !== null) { + patch.stepCount = stepCount; + patch.llmStepCount = stepCount; + } + if (outputMaxSeq !== null) patch.outputMaxSeq = outputMaxSeq; + return { ...(value as JsonRecord), ...patch }; +} + +export function taskScopeId(taskId: string): string { + return `task:${taskId}`; +} + +export function taskAttemptScopeId(taskId: string, attemptIndex: number): string { + return `${taskScopeId(taskId)}:attempt:${Math.floor(attemptIndex)}`; +} diff --git a/src/components/microservices/code-queue/src/provider-runtime.ts b/src/components/microservices/code-queue/src/provider-runtime.ts index 0678dc5a..6d19111a 100644 --- a/src/components/microservices/code-queue/src/provider-runtime.ts +++ b/src/components/microservices/code-queue/src/provider-runtime.ts @@ -477,10 +477,10 @@ if command -v apt-get >/dev/null 2>&1; then fi fi if ! command -v codex >/dev/null 2>&1; then - npm install -g @openai/codex@0.128.0 + npm install -g --no-audit --no-fund --prefer-offline @openai/codex@0.128.0 fi if ! command -v opencode >/dev/null 2>&1; then - npm install -g ${opencodeNpmPackage} + npm install -g --no-audit --no-fund --prefer-offline ${opencodeNpmPackage} fi mkdir -p "$WORKDIR" "$CODEX_HOME_DIR" "$OPENCODE_XDG_DIR/data" "$OPENCODE_XDG_DIR/config" "$OPENCODE_XDG_DIR/cache" "$OPENCODE_XDG_DIR/state" echo "code_agent_runtime_ready codex=$(command -v codex) opencode=$(command -v opencode) cwd=$WORKDIR home=$CODEX_HOME_DIR opencodeXdg=$OPENCODE_XDG_DIR" diff --git a/src/components/microservices/code-queue/src/queue-api.ts b/src/components/microservices/code-queue/src/queue-api.ts index 57130282..b6b6a036 100644 --- a/src/components/microservices/code-queue/src/queue-api.ts +++ b/src/components/microservices/code-queue/src/queue-api.ts @@ -5,7 +5,8 @@ import { codeAgentPortForModel, codeAgentPortInfo, codeModelPorts as codeModelPo import { claudeQqNotificationOutboxStats, notificationTargetConfigured, notificationTargetLabel } from "./notifications"; import { executionProviderOptions } from "./provider-runtime"; import { taskFullOutput } from "./task-output"; -import { buildCompactTaskTranscript, buildTaskTranscript, cachedPreviewTranscript, fullTranscript, prefixPreview, safePreview, statsDaysFromUrl, taskForCompactMetaResponse, taskForMetaResponse, taskListStepCount, taskStatisticsSummary, taskTiming, timestampMs } from "./task-view"; +import { applyOaTraceStatsToTaskJson, taskScopeId, type OaTraceStats } from "./oa-events"; +import { buildCompactTaskTranscript, buildTaskTranscript, cachedPreviewTranscript, fullTranscript, prefixPreview, safePreview, statsDaysFromUrl, taskForCompactMetaResponse, taskForMetaResponse, taskStatisticsSummary, taskTiming, timestampMs } from "./task-view"; import { userPromptForDisplay } from "./prompts"; import type { ActiveRun, ActiveRunSlotWaiter } from "./code-agent/common"; import type { JsonValue, QueueRecord, QueuedStatusReason, QueueTask, RuntimeConfig, TaskStatus, TranscriptLine } from "./types"; @@ -43,6 +44,7 @@ export interface QueueApiContext { safeQueueName: (value: unknown, queueId: string) => string; sql: postgres.Sql; taskQueueEnteredAt: (task: QueueTask) => string; + traceStatsForTasks: (taskIds: string[]) => Promise>; tasks: () => QueueTask[]; truthyParam: (url: URL, name: string) => boolean; } @@ -140,10 +142,23 @@ function outputChunkResponse(task: QueueTask, url: URL): Response { }); } +function taskIdsForStats(tasks: QueueTask[], selectedTask?: QueueTask | null): string[] { + const ids = tasks.map((task) => task.id); + if (selectedTask !== null && selectedTask !== undefined) ids.push(selectedTask.id); + return Array.from(new Set(ids.filter(Boolean))); +} + +function statsForTask(stats: Map, task: QueueTask): OaTraceStats | null { + return stats.get(taskScopeId(task.id)) ?? null; +} + +function applyStats(value: JsonValue, stats: Map, task: QueueTask): JsonValue { + return applyOaTraceStatsToTaskJson(value, statsForTask(stats, task)); +} + function taskForListResponse(task: QueueTask, lite = false, queueTasks?: QueueTask[]): JsonValue { const timing = taskTiming(task); const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); - const stepCount = taskListStepCount(task); if (lite) { return { id: task.id, @@ -157,8 +172,10 @@ function taskForListResponse(task: QueueTask, lite = false, queueTasks?: QueueTa displayPromptChars: displayPrompt.length, promptEditable: ctx().queuedTaskPromptEditable(task), finalResponseChars: task.finalResponse.length, - stepCount, - llmStepCount: stepCount, + stepCount: null, + llmStepCount: null, + traceStats: null, + statsSource: "unavailable", summaryOnly: true, referenceTaskIds: task.referenceTaskIds, referenceInjectionSummary: task.referenceInjection === null ? null : { @@ -215,8 +232,10 @@ function taskForListResponse(task: QueueTask, lite = false, queueTasks?: QueueTa displayPromptChars: displayPrompt.length, promptEditable: ctx().queuedTaskPromptEditable(task), finalResponseChars: task.finalResponse.length, - stepCount, - llmStepCount: stepCount, + stepCount: null, + llmStepCount: null, + traceStats: null, + statsSource: "unavailable", summaryOnly: true, referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection, @@ -1035,11 +1054,16 @@ async function databaseTasksOverviewResponse(url: URL): Promise } as unknown as JsonValue; } const queueContextTasks = [...ctx().tasks(), ...rowsSource]; + const traceStats = await ctx().traceStatsForTasks(taskIdsForStats(rowsSource, selectedTask)); + if (selectedTask !== null && selected !== null && typeof selected === "object" && !Array.isArray(selected)) { + const selectedRecord = selected as Record; + selectedRecord.task = applyStats(selectedRecord.task, traceStats, selectedTask); + } return ctx().compactJsonResponse({ ok: true, queue, statistics, - tasks: rowsSource.map((task) => taskForListResponse(task, true, queueContextTasks)), + tasks: rowsSource.map((task) => applyStats(taskForListResponse(task, true, queueContextTasks), traceStats, task)), selected, pagination: { limit, @@ -1098,11 +1122,16 @@ async function tasksOverviewResponse(url: URL): Promise { maxSeq, } as unknown as JsonValue; } + const traceStats = await ctx().traceStatsForTasks(taskIdsForStats(rowsSource, selectedTask)); + if (selectedTask !== null && selected !== null && typeof selected === "object" && !Array.isArray(selected)) { + const selectedRecord = selected as Record; + selectedRecord.task = applyStats(selectedRecord.task, traceStats, selectedTask); + } return ctx().compactJsonResponse({ ok: true, queue, statistics: taskStatisticsSummary(filteredTasks, statsDaysFromUrl(url)), - tasks: rowsSource.map((task) => taskForListResponse(task, true, allTasks)), + tasks: rowsSource.map((task) => applyStats(taskForListResponse(task, true, allTasks), traceStats, task)), selected, pagination: { limit, diff --git a/src/components/microservices/code-queue/src/task-view.ts b/src/components/microservices/code-queue/src/task-view.ts index 519274a1..866f7fd6 100644 --- a/src/components/microservices/code-queue/src/task-view.ts +++ b/src/components/microservices/code-queue/src/task-view.ts @@ -1178,14 +1178,15 @@ function attemptForResponse(attempt: AttemptSummary, full = false): JsonValue { function taskForResponse(task: QueueTask, full = false, includeRaw = full): JsonValue { const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); - const stepCount = taskLlmStepCount(task); return { ...task, agentPort: codeAgentPortForModel(task.model), agentPortInfo: codeAgentPortInfo(codeAgentPortForModel(task.model)), judgeFailRetryLimit, - stepCount, - llmStepCount: stepCount, + stepCount: null, + llmStepCount: null, + traceStats: null, + statsSource: "unavailable", prompt: full ? task.prompt : safePreview(task.prompt, 2000), basePrompt: full ? task.basePrompt : safePreview(task.basePrompt, 2000), displayPrompt: full ? displayPrompt : safePreview(displayPrompt, 2000), @@ -1226,8 +1227,8 @@ function taskStoredStepCount(task: QueueTask): number | null { return nonNegativeInteger(task.stepCount) ?? nonNegativeInteger(task.llmStepCount); } -// Used by overview/list responses and SSE patches. It must stay archive-free; -// trace/detail endpoints compute exact counts from the full transcript on demand. +// Used only as a producer-side seed for OA stats snapshots; frontend/API +// display authority is the oa_trace_stats projection applied at route edges. function taskListStepCount(task: QueueTask): number { return taskStoredStepCount(task) ?? retainedTaskStepCount(task); } @@ -1245,14 +1246,9 @@ function taskOutputMaxSeq(task: QueueTask): number { return values.length > 0 ? Math.max(...values) : 0; } -function taskLlmStepCount(task: QueueTask): number { - return taskStoredStepCount(task) ?? taskStepCountFromTranscript(task, cachedPreviewTranscript(task)); -} - function taskForMetaResponse(task: QueueTask): JsonValue { const lastOutputSeq = taskOutputMaxSeq(task); const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); - const stepCount = taskListStepCount(task); return { id: task.id, queueId: ctx().queueIdOf(task), @@ -1265,8 +1261,10 @@ function taskForMetaResponse(task: QueueTask): JsonValue { displayPromptChars: displayPrompt.length, promptEditable: ctx().queuedTaskPromptEditable(task), finalResponseChars: task.finalResponse.length, - stepCount, - llmStepCount: stepCount, + stepCount: null, + llmStepCount: null, + traceStats: null, + statsSource: "unavailable", summaryOnly: false, referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection, @@ -1313,7 +1311,6 @@ function taskForMetaResponse(task: QueueTask): JsonValue { function taskForCompactMetaResponse(task: QueueTask): JsonValue { const displayPrompt = task.basePrompt || userPromptForDisplay(task.prompt); const lastOutputSeq = taskOutputMaxSeq(task); - const stepCount = taskListStepCount(task); return { id: task.id, queueId: ctx().queueIdOf(task), @@ -1326,8 +1323,10 @@ function taskForCompactMetaResponse(task: QueueTask): JsonValue { displayPromptChars: displayPrompt.length, promptEditable: ctx().queuedTaskPromptEditable(task), finalResponseChars: task.finalResponse.length, - stepCount, - llmStepCount: stepCount, + stepCount: null, + llmStepCount: null, + traceStats: null, + statsSource: "unavailable", summaryOnly: true, referenceTaskIds: task.referenceTaskIds, referenceInjection: task.referenceInjection === null ? null : { @@ -1517,20 +1516,10 @@ function toolStepCountsFromTranscript(lines: TranscriptLine[]): { toolCallCount: }; } -function llmStepCountFromTranscript(lines: TranscriptLine[], _fallback = 0): number { - return toolStepCountsFromTranscript(lines).toolCallCount; -} - function realAttemptWindows(task: QueueTask, transcript: TranscriptLine[]): TraceAttemptWindow[] { return traceAttemptWindows(task, transcript).filter((window) => window.synthetic !== true && window.index > 0); } -function taskStepCountFromTranscript(task: QueueTask, transcript: TranscriptLine[]): number { - const windows = realAttemptWindows(task, transcript); - if (windows.length === 0) return llmStepCountFromTranscript(transcript); - return windows.reduce((sum, window) => sum + llmStepCountFromTranscript(executionLinesForAttempt(window.lines)), 0); -} - function taskExecutionTranscript(task: QueueTask, transcript: TranscriptLine[]): TranscriptLine[] { const windows = realAttemptWindows(task, transcript); if (windows.length === 0) return transcript; @@ -1543,7 +1532,6 @@ function executionSummaryFromTranscript( timing: Record, outputCount = taskFullOutput(task).length, retainedOutputCount = task.output.length, - fallbackStepCount = 0, ): JsonValue { const toolLines = transcript.filter(isToolActionLine); const editedFiles = uniqueStrings(toolLines.flatMap((line) => line.kind === "edited" ? parseEditedFilesFromText(`${line.title}\n${line.bodyPreview ?? ""}`) : []), 40); @@ -1571,7 +1559,7 @@ function executionSummaryFromTranscript( } function taskExecutionSummary(task: QueueTask, transcript = cachedPreviewTranscript(task)): JsonValue { - return executionSummaryFromTranscript(task, taskExecutionTranscript(task, transcript), taskTiming(task) as Record, undefined, undefined, task.currentAttempt || task.attempts.length); + return executionSummaryFromTranscript(task, taskExecutionTranscript(task, transcript), taskTiming(task) as Record); } function parseAttemptIndex(text: string): number | null { @@ -1669,6 +1657,8 @@ interface TraceAttemptWindow { label?: string; } +type TraceStatsLookup = Map | Record | null | undefined; + function transcriptLineSeq(line: TranscriptLine | undefined): number | null { const value = Number(line?.seq ?? NaN); return Number.isFinite(value) ? value : null; @@ -1806,7 +1796,7 @@ function executionLinesForAttempt(lines: TranscriptLine[]): TranscriptLine[] { return lines.filter((line) => line.title !== "Submitted prompt" && line.title !== "Attempt started" && line.title !== "Judge result"); } -function taskTraceAttemptSummaries(task: QueueTask, transcript: TranscriptLine[]): JsonValue[] { +function taskTraceAttemptSummaries(task: QueueTask, transcript: TranscriptLine[], oaTraceStatsByScope: TraceStatsLookup = null): JsonValue[] { const windows = traceAttemptWindows(task, transcript); return windows.map((window) => { const attempt = window.attempt; @@ -1817,7 +1807,12 @@ function taskTraceAttemptSummaries(task: QueueTask, transcript: TranscriptLine[] const finalResponse = synthetic ? "" : String(attempt?.finalResponse ?? attempt?.finalResponsePreview ?? (window.index === task.attempts.length ? task.finalResponse : "")); const finalResponseChars = Number(attempt?.finalResponseChars ?? finalResponse.length); const executionLines = executionLinesForAttempt(window.lines); - const errorCount = executionLines.filter((line) => line.kind === "error").length; + const attemptStats = synthetic || window.index <= 0 ? null : traceStatsForScope(oaTraceStatsByScope, taskAttemptScopeId(task.id, window.index)); + const stepCount = traceStatsNumber(attemptStats, "stepCount"); + const readCount = traceStatsNumber(attemptStats, "readCount"); + const editCount = traceStatsNumber(attemptStats, "editCount"); + const runCount = traceStatsNumber(attemptStats, "runCount"); + const errorCount = traceStatsNumber(attemptStats, "errorCount"); const feedbackPrompt = synthetic ? null : attemptFeedbackPromptRecord(task, window.index, attempt, judge); const inputPrompt = promptSnapshot(String(attempt?.inputPrompt ?? ""), 1200); return { @@ -1854,14 +1849,23 @@ function taskTraceAttemptSummaries(task: QueueTask, transcript: TranscriptLine[] feedbackPromptSource: feedbackPrompt?.source ?? null, feedbackPromptForAttempt: feedbackPrompt?.forAttempt ?? null, feedbackPromptTruncated: feedbackPrompt?.truncated ?? false, + attemptScopeId: synthetic || window.index <= 0 ? null : taskAttemptScopeId(task.id, window.index), + stepCount, + readCount, + editCount, + runCount, errorCount, - execution: executionSummaryFromTranscript( - task, - executionLines, - attemptTimingSummary(attempt, executionLines.length > 0 ? executionLines : window.lines), - window.lines.length, - window.lines.length, - synthetic || window.index <= 0 ? 0 : 1, + statsSource: attemptStats === null ? "unavailable" : "oa-event-flow", + traceStats: attemptStats, + execution: executionSummaryWithOaStats( + executionSummaryFromTranscript( + task, + executionLines, + attemptTimingSummary(attempt, executionLines.length > 0 ? executionLines : window.lines), + window.lines.length, + window.lines.length, + ), + attemptStats, ), }; }) as unknown as JsonValue[]; @@ -1905,15 +1909,82 @@ function taskTracePromptSummary(task: QueueTask): JsonValue { } as unknown as JsonValue; } -function taskTraceSummaryResponse(task: QueueTask): JsonValue { +function traceStatsRecord(value: JsonValue | null | undefined): Record | null { + return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : null; +} + +function traceStatsNumber(stats: Record | null, key: string): number | null { + const value = Number(stats?.[key]); + return Number.isFinite(value) && value >= 0 ? Math.floor(value) : null; +} + +function taskAttemptScopeId(taskId: string, attemptIndex: number): string { + return `task:${taskId}:attempt:${Math.floor(attemptIndex)}`; +} + +function traceStatsForScope(lookup: TraceStatsLookup, scopeId: string): Record | null { + if (lookup === null || lookup === undefined || scopeId.length === 0) return null; + const value = lookup instanceof Map ? lookup.get(scopeId) : lookup[scopeId]; + return traceStatsRecord(value); +} + +function executionSummaryWithOaStats(execution: JsonValue, stats: Record | null): JsonValue { + if (typeof execution !== "object" || execution === null || Array.isArray(execution)) return execution; + const record = execution as Record; + if (stats === null) { + const { + stepCount: _stepCount, + llmStepCount: _llmStepCount, + toolCallCount: _toolCallCount, + readCount: _readCount, + editCount: _editCount, + runCount: _runCount, + errorCount: _errorCount, + traceLineCount: _traceLineCount, + outputMaxSeq: _outputMaxSeq, + transcriptMaxSeq: _transcriptMaxSeq, + ...rest + } = record; + return { + ...rest, + statsSource: "unavailable", + traceStats: null, + statsUnavailable: true, + } as unknown as JsonValue; + } + const stepCount = traceStatsNumber(stats, "stepCount"); + const llmStepCount = traceStatsNumber(stats, "llmStepCount") ?? stepCount; + const readCount = traceStatsNumber(stats, "readCount"); + const editCount = traceStatsNumber(stats, "editCount"); + const runCount = traceStatsNumber(stats, "runCount"); + const errorCount = traceStatsNumber(stats, "errorCount"); + const traceLineCount = traceStatsNumber(stats, "traceLineCount"); + const outputMaxSeq = traceStatsNumber(stats, "outputMaxSeq"); + const toolCallCount = readCount === null && editCount === null && runCount === null ? null : (readCount ?? 0) + (editCount ?? 0) + (runCount ?? 0); + return { + ...record, + ...(stepCount === null ? {} : { stepCount }), + ...(llmStepCount === null ? {} : { llmStepCount }), + ...(toolCallCount === null ? {} : { toolCallCount }), + ...(readCount === null ? {} : { readCount }), + ...(editCount === null ? {} : { editCount }), + ...(runCount === null ? {} : { runCount }), + ...(errorCount === null ? {} : { errorCount }), + ...(traceLineCount === null ? {} : { traceLineCount }), + ...(outputMaxSeq === null ? {} : { outputMaxSeq, transcriptMaxSeq: outputMaxSeq }), + statsSource: "oa-event-flow", + traceStats: stats, + } as unknown as JsonValue; +} + +function taskTraceSummaryResponse(task: QueueTask, oaTraceStats: JsonValue | null = null, oaTraceStatsByScope: TraceStatsLookup = null): JsonValue { const transcript = cachedPreviewTranscript(task); - const attempts = taskTraceAttemptSummaries(task, transcript); - const stepCount = taskStepCountFromTranscript(task, transcript); - const errorCount = attempts.reduce((sum: number, attempt: JsonValue) => { - if (typeof attempt !== "object" || attempt === null || Array.isArray(attempt)) return sum; - const value = Number((attempt as { errorCount?: unknown }).errorCount || 0); - return sum + (Number.isFinite(value) && value > 0 ? value : 0); - }, 0); + const attempts = taskTraceAttemptSummaries(task, transcript, oaTraceStatsByScope); + const stats = traceStatsRecord(oaTraceStats); + const stepCount = traceStatsNumber(stats, "stepCount"); + const llmStepCount = traceStatsNumber(stats, "llmStepCount") ?? stepCount; + const errorCount = traceStatsNumber(stats, "errorCount"); + const execution = executionSummaryWithOaStats(taskExecutionSummary(task, transcript), stats); return { id: task.id, queueId: ctx().queueIdOf(task), @@ -1931,10 +2002,12 @@ function taskTraceSummaryResponse(task: QueueTask): JsonValue { currentAttempt: task.currentAttempt, maxAttempts: task.maxAttempts, stepCount, - llmStepCount: stepCount, + llmStepCount, promptEditable: ctx().queuedTaskPromptEditable(task), prompt: taskTracePromptSummary(task), - execution: taskExecutionSummary(task, transcript), + execution, + traceStats: stats, + statsSource: stats === null ? "unavailable" : "oa-event-flow", finalResponse: task.finalResponse, finalResponseChars: task.finalResponse.length, lastJudge: task.lastJudge, @@ -2132,7 +2205,6 @@ export { statsDaysFromUrl, taskExecutionSummary, taskListStepCount, - taskLlmStepCount, taskOutputMaxSeq, taskForCompactMetaResponse, taskForMetaResponse, diff --git a/src/components/microservices/code-queue/src/types.ts b/src/components/microservices/code-queue/src/types.ts index f287c0a4..cf08ff6f 100644 --- a/src/components/microservices/code-queue/src/types.ts +++ b/src/components/microservices/code-queue/src/types.ts @@ -63,6 +63,7 @@ export interface RuntimeConfig { turnNoActivityTimeoutMs: number; databaseUrl: string; databaseFlushIntervalMs: number; + oaEventFlowBaseUrl: string; notifyClaudeQqEnabled: boolean; notifyClaudeQqBaseUrl: string; notifyClaudeQqTargetType: "private" | "group"; diff --git a/src/components/microservices/oa-event-flow/Dockerfile b/src/components/microservices/oa-event-flow/Dockerfile new file mode 100644 index 00000000..dcb29641 --- /dev/null +++ b/src/components/microservices/oa-event-flow/Dockerfile @@ -0,0 +1,11 @@ +FROM oven/bun:1-alpine + +WORKDIR /app/src/components/microservices/oa-event-flow +COPY src/components/microservices/oa-event-flow/package.json ./package.json +RUN bun install --production +COPY src/components/microservices/oa-event-flow/tsconfig.json ./tsconfig.json +COPY src/components/shared /app/src/components/shared +COPY src/components/microservices/oa-event-flow/src ./src + +EXPOSE 4255 +CMD ["bun", "run", "src/index.ts"] diff --git a/src/components/microservices/oa-event-flow/bun.lock b/src/components/microservices/oa-event-flow/bun.lock new file mode 100644 index 00000000..b269e56b --- /dev/null +++ b/src/components/microservices/oa-event-flow/bun.lock @@ -0,0 +1,15 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "@unidesk/oa-event-flow", + "dependencies": { + "postgres": "latest", + }, + }, + }, + "packages": { + "postgres": ["postgres@3.4.9", "", {}, "sha512-GD3qdB0x1z9xgFI6cdRD6xu2Sp2WCOEoe3mtnyB5Ee0XrrL5Pe+e4CCnJrRMnL1zYtRDZmQQVbvOttLnKDLnaw=="], + } +} diff --git a/src/components/microservices/oa-event-flow/package.json b/src/components/microservices/oa-event-flow/package.json new file mode 100644 index 00000000..64476d1b --- /dev/null +++ b/src/components/microservices/oa-event-flow/package.json @@ -0,0 +1,12 @@ +{ + "name": "@unidesk/oa-event-flow", + "private": true, + "type": "module", + "scripts": { + "start": "bun run src/index.ts", + "check": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "postgres": "latest" + } +} diff --git a/src/components/microservices/oa-event-flow/src/index.ts b/src/components/microservices/oa-event-flow/src/index.ts new file mode 100644 index 00000000..565a55ae --- /dev/null +++ b/src/components/microservices/oa-event-flow/src/index.ts @@ -0,0 +1,1348 @@ +import { createHash, randomUUID } from "node:crypto"; +import postgres from "postgres"; +import { createHourlyJsonlWriter, logRetentionBytesForService } from "../../../shared/src/rotating-jsonl"; + +type JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue }; +type JsonRecord = Record; +type SqlClient = postgres.Sql; +type SqlExecutor = postgres.Sql | postgres.TransactionSql; + +interface RuntimeConfig { + host: string; + port: number; + databaseUrl: string; + logFile: string; + pipelineBridgeBaseUrl: string; + pipelineBridgeIntervalMs: number; + pipelineBridgeRunLimit: number; +} + +interface OaEventInput { + eventId?: unknown; + type?: unknown; + createdAt?: unknown; + sourceKind?: unknown; + sourceId?: unknown; + aggregateType?: unknown; + aggregateId?: unknown; + correlationId?: unknown; + causationId?: unknown; + tags?: unknown; + payload?: unknown; +} + +interface OaEventRecord { + eventId: string; + sequence: number; + type: string; + createdAt: string; + sourceKind: string; + sourceId: string; + aggregateType: string; + aggregateId: string; + correlationId: string; + causationId: string | null; + tags: string[]; + payload: JsonRecord; +} + +interface OaEventRow { + event_id: string; + sequence: string | number; + type: string; + source_kind: string; + source_id: string; + aggregate_type: string; + aggregate_id: string; + correlation_id: string; + causation_id: string | null; + tags: JsonValue; + payload: JsonValue; + created_at: Date | string; +} + +interface TraceStatsRow { + scope_id: string; + service_id: string; + subject_kind: string; + subject_id: string; + stats_revision: string | number; + step_count: string | number; + llm_step_count: string | number; + read_count: string | number; + edit_count: string | number; + run_count: string | number; + error_count: string | number; + trace_line_count: string | number; + output_max_seq: string | number; + attempt_stats_json: JsonValue; + last_event_sequence: string | number | null; + updated_at: Date | string; +} + +interface TraceStatsRecord extends JsonRecord { + scopeId: string; + serviceId: string; + subjectKind: string; + subjectId: string; + statsRevision: number; + stepCount: number; + llmStepCount: number; + readCount: number; + editCount: number; + runCount: number; + errorCount: number; + traceLineCount: number; + outputMaxSeq: number; + attemptStats: JsonValue; + lastEventSequence: number | null; + updatedAt: string; + taskId: string; + attemptIndex: number | null; + source: "oa-event-flow"; +} + +interface TraceScope { + scopeId: string; + serviceId: string; + subjectKind: string; + subjectId: string; + taskId?: string; + attemptIndex?: number | null; +} + +interface EventClient { + id: string; + tags: string[]; + enqueue: (chunk: string) => void; + close: () => void; + connectedAt: string; +} + +interface PipelineBridgeRunState extends JsonRecord { + runId: string; + pipelineId: string; + lastObservedEventCount: number; + lastSyncedAt: string; + lastError: string; + bridgeMode: string; + insertedCount: number; + duplicateCount: number; +} + +interface PipelineBridgeState extends JsonRecord { + enabled: boolean; + mode: string; + baseUrl: string; + intervalMs: number; + runLimit: number; + inFlight: boolean; + lastStartedAt: string; + lastFinishedAt: string; + lastError: string; + syncCount: number; + insertedCount: number; + duplicateCount: number; + snapshotSyncCount: number; + runs: Record; +} + +const serviceStartedAt = new Date().toISOString(); +const recentLogs: JsonRecord[] = []; +const eventClients = new Set(); +const eventEncoder = new TextEncoder(); +let databaseReady = false; +let databaseLastError: string | null = null; +let pipelineBridgeTimer: ReturnType | null = null; + +function envString(name: string, fallback: string): string { + const value = process.env[name]; + return value === undefined || value.length === 0 ? fallback : value; +} + +function envRequiredString(name: string): string { + const value = process.env[name]; + if (value === undefined || value.trim().length === 0) throw new Error(`${name} is required`); + return value; +} + +function envNumber(name: string, fallback: number): number { + const raw = process.env[name]; + if (raw === undefined || raw.length === 0) return fallback; + const value = Number(raw); + return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback; +} + +function configFromEnv(): RuntimeConfig { + return { + host: envString("HOST", "0.0.0.0"), + port: envNumber("PORT", 4255), + databaseUrl: envRequiredString("DATABASE_URL"), + logFile: envString("LOG_FILE", "/var/log/unidesk/oa-event-flow.jsonl"), + pipelineBridgeBaseUrl: envString("PIPELINE_OA_BRIDGE_BASE_URL", "").replace(/\/+$/u, ""), + pipelineBridgeIntervalMs: envNumber("PIPELINE_OA_BRIDGE_INTERVAL_MS", 15_000), + pipelineBridgeRunLimit: envNumber("PIPELINE_OA_BRIDGE_RUN_LIMIT", 50), + }; +} + +const config = configFromEnv(); +const sql: SqlClient = postgres(config.databaseUrl, { max: 8, idle_timeout: 20, connect_timeout: 10 }); +const pipelineBridgeState: PipelineBridgeState = { + enabled: config.pipelineBridgeBaseUrl.length > 0, + mode: "snapshot", + baseUrl: config.pipelineBridgeBaseUrl, + intervalMs: config.pipelineBridgeIntervalMs, + runLimit: config.pipelineBridgeRunLimit, + inFlight: false, + lastStartedAt: "", + lastFinishedAt: "", + lastError: "", + syncCount: 0, + insertedCount: 0, + duplicateCount: 0, + snapshotSyncCount: 0, + runs: {}, +}; +const logWriter = config.logFile + ? createHourlyJsonlWriter({ + baseLogFile: config.logFile, + service: "oa-event-flow", + maxBytes: logRetentionBytesForService("oa-event-flow"), + }) + : null; +logWriter?.prune(); + +function log(level: "debug" | "info" | "warn" | "error", event: string, detail: JsonRecord = {}): void { + const record: JsonRecord = { at: new Date().toISOString(), service: "oa-event-flow", level, event, ...detail }; + recentLogs.push(record); + while (recentLogs.length > 500) recentLogs.shift(); + try { + logWriter?.appendJson(record, new Date(String(record.at))); + } catch { + // Logging must not break the event bus. + } + const line = JSON.stringify(record); + const writer = level === "error" ? console.error : level === "warn" ? console.warn : console.log; + writer(line); +} + +function jsonResponse(body: unknown, status = 200, headers: Record = {}): Response { + return new Response(JSON.stringify(body), { + status, + headers: { "content-type": "application/json; charset=utf-8", ...headers }, + }); +} + +function errorToJson(error: unknown): JsonRecord { + if (error instanceof Error) return { name: error.name, message: error.message, stack: error.stack || "" }; + return { message: String(error) }; +} + +function errorResponse(error: unknown, status = 500): Response { + log("error", "request_failed", { status, error: errorToJson(error) }); + return jsonResponse({ ok: false, error: error instanceof Error ? error.message : String(error) }, status); +} + +function asRecord(value: unknown): Record | null { + return typeof value === "object" && value !== null && !Array.isArray(value) ? value as Record : null; +} + +function asJsonRecord(value: unknown): JsonRecord { + if (typeof value === "object" && value !== null && !Array.isArray(value)) return value as JsonRecord; + return {}; +} + +function stringValue(value: unknown, fallback = ""): string { + if (typeof value === "string") return value.trim(); + if (typeof value === "number" || typeof value === "boolean" || typeof value === "bigint") return String(value).trim(); + return fallback; +} + +function isoTimestamp(value: unknown, fallback = new Date().toISOString()): string { + const raw = stringValue(value); + if (raw.length === 0) return fallback; + const time = Date.parse(raw); + return Number.isFinite(time) ? new Date(time).toISOString() : fallback; +} + +function normalizeTag(value: unknown): string { + return stringValue(value).replace(/\s+/gu, " ").trim(); +} + +function normalizeTags(value: unknown): string[] { + const source = Array.isArray(value) ? value : typeof value === "string" ? value.split(",") : []; + const tags = source.map(normalizeTag).filter((tag) => tag.length > 0 && tag.length <= 160); + return Array.from(new Set(tags)); +} + +function addTag(tags: string[], tag: string): void { + const normalized = normalizeTag(tag); + if (normalized.length > 0 && !tags.includes(normalized)) tags.push(normalized); +} + +function stableEventHash(value: unknown): string { + return createHash("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 32); +} + +function normalizeEvent(input: OaEventInput): OaEventRecord { + const payload = asJsonRecord(input.payload); + const type = stringValue(input.type, "event").slice(0, 160) || "event"; + const sourceKind = stringValue(input.sourceKind, "service").slice(0, 120) || "service"; + const sourceId = stringValue(input.sourceId, "unknown").slice(0, 160) || "unknown"; + const aggregateType = stringValue(input.aggregateType, stringValue(payload.aggregateType, "")).slice(0, 120); + const aggregateId = stringValue(input.aggregateId, stringValue(payload.aggregateId, "")).slice(0, 240); + const createdAt = isoTimestamp(input.createdAt, new Date().toISOString()); + const correlationId = stringValue(input.correlationId, stringValue(payload.correlationId, "")).slice(0, 240) + || `${sourceId}:${aggregateId || type}`; + const causationId = stringValue(input.causationId, "").slice(0, 240) || null; + const tags = normalizeTags(input.tags); + if (sourceId) addTag(tags, `service:${sourceId}`); + if (aggregateType && aggregateId) addTag(tags, `${aggregateType}:${aggregateId}`); + const eventId = stringValue(input.eventId).slice(0, 300) + || `evt_${stableEventHash({ type, sourceKind, sourceId, aggregateType, aggregateId, correlationId, causationId, tags, payload, createdAt })}` + || `evt_${randomUUID()}`; + return { eventId, sequence: 0, type, createdAt, sourceKind, sourceId, aggregateType, aggregateId, correlationId, causationId, tags, payload }; +} + +function rowToEvent(row: OaEventRow): OaEventRecord { + return { + eventId: row.event_id, + sequence: Number(row.sequence), + type: row.type, + createdAt: isoTimestamp(row.created_at), + sourceKind: row.source_kind, + sourceId: row.source_id, + aggregateType: row.aggregate_type, + aggregateId: row.aggregate_id, + correlationId: row.correlation_id, + causationId: row.causation_id, + tags: Array.isArray(row.tags) ? row.tags.map((item) => String(item)) : [], + payload: asJsonRecord(row.payload), + }; +} + +function statsRowToRecord(row: TraceStatsRow): TraceStatsRecord { + const taskAttempt = row.scope_id.match(/^task:([^:]+):attempt:(\d+)$/u); + const taskOnly = row.scope_id.match(/^task:([^:]+)$/u); + const taskId = taskAttempt?.[1] ?? taskOnly?.[1] ?? ""; + const attemptIndex = taskAttempt === null ? null : Number(taskAttempt[2]); + return { + scopeId: row.scope_id, + serviceId: row.service_id, + subjectKind: row.subject_kind, + subjectId: row.subject_id, + statsRevision: Number(row.stats_revision), + stepCount: Number(row.step_count), + llmStepCount: Number(row.llm_step_count), + readCount: Number(row.read_count), + editCount: Number(row.edit_count), + runCount: Number(row.run_count), + errorCount: Number(row.error_count), + traceLineCount: Number(row.trace_line_count), + outputMaxSeq: Number(row.output_max_seq), + attemptStats: row.attempt_stats_json, + lastEventSequence: row.last_event_sequence === null ? null : Number(row.last_event_sequence), + updatedAt: isoTimestamp(row.updated_at), + taskId, + attemptIndex, + source: "oa-event-flow", + }; +} + +async function ensureSchema(): Promise { + await sql` + CREATE TABLE IF NOT EXISTS oa_events ( + event_id TEXT PRIMARY KEY, + sequence BIGSERIAL UNIQUE NOT NULL, + type TEXT NOT NULL, + source_kind TEXT NOT NULL, + source_id TEXT NOT NULL, + aggregate_type TEXT NOT NULL DEFAULT '', + aggregate_id TEXT NOT NULL DEFAULT '', + correlation_id TEXT NOT NULL DEFAULT '', + causation_id TEXT, + tags JSONB NOT NULL DEFAULT '[]'::jsonb, + payload JSONB NOT NULL DEFAULT '{}'::jsonb, + created_at TIMESTAMPTZ NOT NULL DEFAULT now() + ) + `; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_events_sequence ON oa_events(sequence ASC)`; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_events_type_sequence ON oa_events(type, sequence ASC)`; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_events_source_sequence ON oa_events(source_id, sequence ASC)`; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_events_created ON oa_events(created_at DESC)`; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_events_tags_gin ON oa_events USING GIN(tags jsonb_path_ops)`; + await sql` + CREATE TABLE IF NOT EXISTS oa_projection_offsets ( + projection_name TEXT PRIMARY KEY, + last_sequence BIGINT NOT NULL DEFAULT 0, + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + ) + `; + await sql` + CREATE TABLE IF NOT EXISTS oa_trace_stats ( + scope_id TEXT PRIMARY KEY, + service_id TEXT NOT NULL DEFAULT '', + subject_kind TEXT NOT NULL DEFAULT '', + subject_id TEXT NOT NULL DEFAULT '', + stats_revision BIGINT NOT NULL DEFAULT 0, + step_count INTEGER NOT NULL DEFAULT 0, + llm_step_count INTEGER NOT NULL DEFAULT 0, + read_count INTEGER NOT NULL DEFAULT 0, + edit_count INTEGER NOT NULL DEFAULT 0, + run_count INTEGER NOT NULL DEFAULT 0, + error_count INTEGER NOT NULL DEFAULT 0, + trace_line_count INTEGER NOT NULL DEFAULT 0, + output_max_seq BIGINT NOT NULL DEFAULT 0, + attempt_stats_json JSONB NOT NULL DEFAULT '[]'::jsonb, + last_event_sequence BIGINT, + updated_at TIMESTAMPTZ NOT NULL DEFAULT now() + ) + `; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_trace_stats_service_updated ON oa_trace_stats(service_id, updated_at DESC)`; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_trace_stats_subject ON oa_trace_stats(subject_kind, subject_id)`; + await sql` + CREATE TABLE IF NOT EXISTS oa_trace_steps ( + scope_id TEXT NOT NULL, + step_seq BIGINT NOT NULL, + kind TEXT NOT NULL DEFAULT '', + title TEXT NOT NULL DEFAULT '', + status TEXT NOT NULL DEFAULT '', + summary_lines JSONB NOT NULL DEFAULT '[]'::jsonb, + raw_seqs JSONB NOT NULL DEFAULT '[]'::jsonb, + source_event_id TEXT NOT NULL DEFAULT '', + created_at TIMESTAMPTZ NOT NULL DEFAULT now(), + updated_at TIMESTAMPTZ NOT NULL DEFAULT now(), + PRIMARY KEY(scope_id, step_seq) + ) + `; + await sql`CREATE INDEX IF NOT EXISTS idx_oa_trace_steps_scope_updated ON oa_trace_steps(scope_id, updated_at DESC)`; +} + +async function waitForSchema(): Promise { + let attempt = 0; + const started = Date.now(); + while (true) { + attempt += 1; + try { + await ensureSchema(); + databaseReady = true; + databaseLastError = null; + log("info", "database_ready", { attempt, elapsedMs: Date.now() - started }); + return; + } catch (error) { + databaseReady = false; + databaseLastError = error instanceof Error ? error.message : String(error); + log("warn", "database_init_retry", { attempt, error: errorToJson(error) }); + await Bun.sleep(Math.min(30_000, 500 * attempt)); + } + } +} + +function parseLimit(url: URL, fallback = 100, max = 500): number { + const value = Number(url.searchParams.get("limit") ?? fallback); + return Number.isInteger(value) && value > 0 ? Math.min(max, value) : fallback; +} + +function parseTagsFromUrl(url: URL): string[] { + const values = [...url.searchParams.getAll("tag"), ...url.searchParams.getAll("tags")]; + return normalizeTags(values.flatMap((value) => value.split(","))); +} + +function parseScopeIdsFromUrl(url: URL): string[] { + const values = [...url.searchParams.getAll("scopeId"), ...url.searchParams.getAll("scopeIds")]; + return Array.from(new Set(values.flatMap((value) => value.split(",")).map((value) => value.trim()).filter(Boolean))); +} + +function eventMatchesTags(event: OaEventRecord, tags: string[]): boolean { + if (tags.length === 0) return true; + return tags.every((tag) => event.tags.includes(tag)); +} + +function eventSseChunk(event: OaEventRecord): string { + return [ + `id: ${event.sequence}`, + `event: ${event.type}`, + `data: ${JSON.stringify(event)}`, + "", + "", + ].join("\n"); +} + +function emitEvent(event: OaEventRecord): void { + const chunk = eventSseChunk(event); + for (const client of Array.from(eventClients)) { + if (!eventMatchesTags(event, client.tags)) continue; + try { + client.enqueue(chunk); + } catch { + client.close(); + } + } +} + +function tagsFromEvent(event: OaEventRecord): Set { + return new Set(event.tags); +} + +function tagValue(tags: Set, prefix: string): string | null { + const tag = Array.from(tags).find((item) => item.startsWith(prefix)); + return tag === undefined ? null : tag.slice(prefix.length); +} + +function traceScopeFromScopeId(scopeId: string, serviceId: string): TraceScope { + const taskAttempt = scopeId.match(/^task:([^:]+):attempt:(\d+)$/u); + if (taskAttempt !== null) { + const taskId = taskAttempt[1] ?? ""; + const attemptIndex = Number(taskAttempt[2] ?? 0); + return { scopeId, serviceId, subjectKind: "task-attempt", subjectId: `${taskId}:${attemptIndex}`, taskId, attemptIndex }; + } + const taskOnly = scopeId.match(/^task:([^:]+)$/u); + if (taskOnly !== null) { + const taskId = taskOnly[1] ?? ""; + return { scopeId, serviceId, subjectKind: "task", subjectId: taskId, taskId }; + } + const [kind = "trace", ...rest] = scopeId.split(":"); + const id = rest.join(":") || scopeId; + return { scopeId, serviceId, subjectKind: kind || "trace", subjectId: id }; +} + +function positiveInteger(value: unknown): number | null { + const number = Number(value); + return Number.isInteger(number) && number > 0 ? Math.floor(number) : null; +} + +function traceScope(event: OaEventRecord): TraceScope | null { + const tags = tagsFromEvent(event); + const payload = event.payload; + const taskId = stringValue(payload.taskId) || tagValue(tags, "task:") || ""; + const pipelineId = stringValue(payload.pipelineId) || tagValue(tags, "pipeline:") || ""; + const epochId = stringValue(payload.runId) || stringValue(payload.epochId) || tagValue(tags, "epoch:") || ""; + const explicitScopeId = stringValue(payload.scopeId); + const serviceId = tagValue(tags, "service:") || event.sourceId; + if (explicitScopeId.length > 0) return traceScopeFromScopeId(explicitScopeId, serviceId); + if (taskId.length > 0) return traceScopeFromScopeId(`task:${taskId}`, serviceId); + if (pipelineId.length > 0 && epochId.length > 0) return traceScopeFromScopeId(`epoch:${epochId}`, serviceId); + if (pipelineId.length > 0) return traceScopeFromScopeId(`pipeline:${pipelineId}`, serviceId); + return null; +} + +function traceAttemptScope(event: OaEventRecord, primary: TraceScope | null): TraceScope | null { + const tags = tagsFromEvent(event); + const payload = event.payload; + const serviceId = primary?.serviceId ?? tagValue(tags, "service:") ?? event.sourceId; + const explicit = stringValue(payload.attemptScopeId); + if (explicit.length > 0) return traceScopeFromScopeId(explicit, serviceId); + const attemptIndex = positiveInteger(payload.attemptIndex) ?? positiveInteger(tagValue(tags, "attempt:")); + const taskId = stringValue(payload.taskId) || primary?.taskId || (primary?.subjectKind === "task" ? primary.subjectId : "") || tagValue(tags, "task:") || ""; + if (taskId.length === 0 || attemptIndex === null) return null; + return traceScopeFromScopeId(`task:${taskId}:attempt:${attemptIndex}`, serviceId); +} + +function traceProjectionScopes(event: OaEventRecord): TraceScope[] { + const primary = traceScope(event); + if (primary === null) return []; + if (event.type !== "trace-step-created" && event.type !== "trace-error") return [primary]; + const scopes = [primary]; + const attempt = traceAttemptScope(event, primary); + if (attempt !== null && !scopes.some((scope) => scope.scopeId === attempt.scopeId)) scopes.push(attempt); + return scopes; +} + +function numberField(record: JsonRecord, keys: string[], fallback = 0): number { + for (const key of keys) { + const value = Number(record[key]); + if (Number.isFinite(value) && value >= 0) return Math.floor(value); + } + return fallback; +} + +function nestedRecord(record: JsonRecord, key: string): JsonRecord { + const value = record[key]; + return asJsonRecord(value); +} + +function normalizeStepKind(value: unknown): "read" | "edit" | "run" | "error" | "message" | "system" { + const kind = stringValue(value).toLowerCase(); + if (["read", "explored", "explore", "grep", "search", "list", "view"].includes(kind)) return "read"; + if (["edit", "edited", "write", "patch", "diff", "apply"].includes(kind)) return "edit"; + if (["run", "ran", "command", "tool"].includes(kind)) return "run"; + if (kind === "error" || kind === "failed") return "error"; + if (kind === "message" || kind === "assistant" || kind === "user") return "message"; + return "system"; +} + +async function fetchTraceStats(executor: SqlExecutor, scopeId: string): Promise { + const rows = await executor` + SELECT * + FROM oa_trace_stats + WHERE scope_id = ${scopeId} + LIMIT 1 + `; + return rows[0] === undefined ? null : statsRowToRecord(rows[0]); +} + +async function insertDerivedStatsEvent(executor: SqlExecutor, stats: TraceStatsRecord, sourceEvent: OaEventRecord): Promise { + const tags = ["stats", "trace", `service:${stats.serviceId}`, stats.scopeId]; + if (typeof stats.taskId === "string" && stats.taskId.length > 0) addTag(tags, `task:${stats.taskId}`); + if (Number(stats.attemptIndex) > 0) addTag(tags, `attempt:${Number(stats.attemptIndex)}`); + const payload: JsonRecord = { + scopeId: stats.scopeId, + serviceId: stats.serviceId, + subjectKind: stats.subjectKind, + subjectId: stats.subjectId, + taskId: typeof stats.taskId === "string" ? stats.taskId : stats.subjectKind === "task" ? stats.subjectId : "", + attemptIndex: Number(stats.attemptIndex) > 0 ? Number(stats.attemptIndex) : null, + stats, + }; + const derived = normalizeEvent({ + eventId: `oa-event-flow:trace-stats-updated:${stats.scopeId}:${stats.statsRevision}`, + type: "trace-stats-updated", + sourceKind: "projection", + sourceId: "oa-event-flow", + aggregateType: stats.subjectKind, + aggregateId: stats.subjectId, + correlationId: sourceEvent.correlationId, + causationId: sourceEvent.eventId, + tags, + payload, + }); + const rows = await executor` + INSERT INTO oa_events (event_id, type, source_kind, source_id, aggregate_type, aggregate_id, correlation_id, causation_id, tags, payload, created_at) + VALUES (${derived.eventId}, ${derived.type}, ${derived.sourceKind}, ${derived.sourceId}, ${derived.aggregateType}, ${derived.aggregateId}, ${derived.correlationId}, ${derived.causationId}, ${executor.json(derived.tags)}, ${executor.json(derived.payload)}, ${derived.createdAt}) + ON CONFLICT (event_id) DO NOTHING + RETURNING * + `; + return rows[0] === undefined ? null : rowToEvent(rows[0]); +} + +async function applyTraceSnapshot(executor: SqlExecutor, event: OaEventRecord, scope: TraceScope): Promise { + await executor`SELECT pg_advisory_xact_lock(482755, hashtext(${scope.scopeId}))`; + const stats = nestedRecord(event.payload, "stats"); + const stepCount = numberField(stats, ["stepCount", "llmStepCount"]); + const llmStepCount = numberField(stats, ["llmStepCount", "stepCount"], stepCount); + const readCount = numberField(stats, ["readCount"]); + const editCount = numberField(stats, ["editCount"]); + const runCount = numberField(stats, ["runCount"]); + const errorCount = numberField(stats, ["errorCount"]); + const traceLineCount = numberField(stats, ["traceLineCount"], stepCount); + const outputMaxSeq = numberField(stats, ["outputMaxSeq", "transcriptMaxSeq"]); + const attemptStats = stats.attemptStats ?? stats.attempts ?? []; + await executor` + INSERT INTO oa_trace_stats ( + scope_id, service_id, subject_kind, subject_id, stats_revision, + step_count, llm_step_count, read_count, edit_count, run_count, error_count, + trace_line_count, output_max_seq, attempt_stats_json, last_event_sequence, updated_at + ) + VALUES ( + ${scope.scopeId}, ${scope.serviceId}, ${scope.subjectKind}, ${scope.subjectId}, 1, + ${stepCount}, ${llmStepCount}, ${readCount}, ${editCount}, ${runCount}, ${errorCount}, + ${traceLineCount}, ${outputMaxSeq}, ${executor.json(attemptStats)}, ${event.sequence}, now() + ) + ON CONFLICT (scope_id) DO UPDATE SET + service_id = EXCLUDED.service_id, + subject_kind = EXCLUDED.subject_kind, + subject_id = EXCLUDED.subject_id, + stats_revision = oa_trace_stats.stats_revision + 1, + step_count = GREATEST(oa_trace_stats.step_count, EXCLUDED.step_count), + llm_step_count = GREATEST(oa_trace_stats.llm_step_count, EXCLUDED.llm_step_count), + read_count = GREATEST(oa_trace_stats.read_count, EXCLUDED.read_count), + edit_count = GREATEST(oa_trace_stats.edit_count, EXCLUDED.edit_count), + run_count = GREATEST(oa_trace_stats.run_count, EXCLUDED.run_count), + error_count = GREATEST(oa_trace_stats.error_count, EXCLUDED.error_count), + trace_line_count = GREATEST(oa_trace_stats.trace_line_count, EXCLUDED.trace_line_count), + output_max_seq = GREATEST(oa_trace_stats.output_max_seq, EXCLUDED.output_max_seq), + attempt_stats_json = EXCLUDED.attempt_stats_json, + last_event_sequence = GREATEST(COALESCE(oa_trace_stats.last_event_sequence, 0), EXCLUDED.last_event_sequence), + updated_at = now() + `; + return fetchTraceStats(executor, scope.scopeId); +} + +async function applyTraceStep(executor: SqlExecutor, event: OaEventRecord, scope: TraceScope): Promise { + await executor`SELECT pg_advisory_xact_lock(482755, hashtext(${scope.scopeId}))`; + const payload = event.payload; + const seq = numberField(payload, ["seq", "stepSeq", "outputSeq"], event.sequence); + const outputMaxSeq = numberField(payload, ["outputMaxSeq", "seq", "stepSeq", "outputSeq"], seq); + const kind = normalizeStepKind(payload.kind ?? payload.stepKind ?? payload.channel); + const title = stringValue(payload.title, stringValue(payload.method, kind)).slice(0, 300); + const status = stringValue(payload.status).slice(0, 120); + const summaryLines = Array.isArray(payload.summaryLines) ? payload.summaryLines : []; + const rawSeqs = Array.isArray(payload.rawSeqs) ? payload.rawSeqs : [seq]; + await executor` + INSERT INTO oa_trace_steps (scope_id, step_seq, kind, title, status, summary_lines, raw_seqs, source_event_id, created_at, updated_at) + VALUES (${scope.scopeId}, ${seq}, ${kind}, ${title}, ${status}, ${executor.json(summaryLines)}, ${executor.json(rawSeqs)}, ${event.eventId}, ${event.createdAt}, now()) + ON CONFLICT (scope_id, step_seq) DO UPDATE SET + kind = EXCLUDED.kind, + title = EXCLUDED.title, + status = EXCLUDED.status, + summary_lines = EXCLUDED.summary_lines, + raw_seqs = EXCLUDED.raw_seqs, + source_event_id = EXCLUDED.source_event_id, + updated_at = now() + `; + const countRows = await executor<{ + step_count: string | number; + read_count: string | number; + edit_count: string | number; + run_count: string | number; + error_count: string | number; + }[]>` + SELECT + COUNT(*) AS step_count, + COUNT(*) FILTER (WHERE kind = 'read') AS read_count, + COUNT(*) FILTER (WHERE kind = 'edit') AS edit_count, + COUNT(*) FILTER (WHERE kind = 'run') AS run_count, + COUNT(*) FILTER (WHERE kind = 'error') AS error_count + FROM oa_trace_steps + WHERE scope_id = ${scope.scopeId} + `; + const counts = countRows[0] ?? { step_count: 0, read_count: 0, edit_count: 0, run_count: 0, error_count: 0 }; + const stepCount = Number(counts.step_count); + const readCount = Number(counts.read_count); + const editCount = Number(counts.edit_count); + const runCount = Number(counts.run_count); + const errorCount = Number(counts.error_count); + await executor` + INSERT INTO oa_trace_stats ( + scope_id, service_id, subject_kind, subject_id, stats_revision, + step_count, llm_step_count, read_count, edit_count, run_count, error_count, + trace_line_count, output_max_seq, attempt_stats_json, last_event_sequence, updated_at + ) + VALUES ( + ${scope.scopeId}, ${scope.serviceId}, ${scope.subjectKind}, ${scope.subjectId}, 1, + ${stepCount}, ${stepCount}, ${readCount}, ${editCount}, ${runCount}, ${errorCount}, + ${stepCount}, ${outputMaxSeq}, '[]'::jsonb, ${event.sequence}, now() + ) + ON CONFLICT (scope_id) DO UPDATE SET + service_id = EXCLUDED.service_id, + subject_kind = EXCLUDED.subject_kind, + subject_id = EXCLUDED.subject_id, + stats_revision = oa_trace_stats.stats_revision + 1, + step_count = GREATEST(oa_trace_stats.step_count, EXCLUDED.step_count), + llm_step_count = GREATEST(oa_trace_stats.llm_step_count, EXCLUDED.llm_step_count), + read_count = GREATEST(oa_trace_stats.read_count, EXCLUDED.read_count), + edit_count = GREATEST(oa_trace_stats.edit_count, EXCLUDED.edit_count), + run_count = GREATEST(oa_trace_stats.run_count, EXCLUDED.run_count), + error_count = GREATEST(oa_trace_stats.error_count, EXCLUDED.error_count), + trace_line_count = GREATEST(oa_trace_stats.trace_line_count, EXCLUDED.trace_line_count), + output_max_seq = GREATEST(oa_trace_stats.output_max_seq, EXCLUDED.output_max_seq), + attempt_stats_json = oa_trace_stats.attempt_stats_json, + last_event_sequence = GREATEST(COALESCE(oa_trace_stats.last_event_sequence, 0), EXCLUDED.last_event_sequence), + updated_at = now() + `; + return fetchTraceStats(executor, scope.scopeId); +} + +async function applyTraceProjection(executor: SqlExecutor, event: OaEventRecord): Promise { + if (event.type === "trace-stats-updated") return []; + const tags = tagsFromEvent(event); + if (!tags.has("trace") && !event.type.startsWith("trace-")) return []; + const scopes = traceProjectionScopes(event); + if (scopes.length === 0) return []; + if (event.type === "trace-stats-snapshot") { + const stats = await applyTraceSnapshot(executor, event, scopes[0]); + return stats === null ? [] : [stats]; + } + if (event.type === "trace-step-created") { + const records: TraceStatsRecord[] = []; + for (const scope of scopes) { + const stats = await applyTraceStep(executor, event, scope); + if (stats !== null) records.push(stats); + } + return records; + } + if (event.type === "trace-error") { + const stepEvent = { ...event, payload: { ...event.payload, kind: "error" } }; + const records: TraceStatsRecord[] = []; + for (const scope of scopes) { + const stats = await applyTraceStep(executor, stepEvent, scope); + if (stats !== null) records.push(stats); + } + return records; + } + return []; +} + +async function storeInputEvent(executor: SqlExecutor, event: OaEventRecord): Promise { + const rows = await executor` + INSERT INTO oa_events (event_id, type, source_kind, source_id, aggregate_type, aggregate_id, correlation_id, causation_id, tags, payload, created_at) + VALUES (${event.eventId}, ${event.type}, ${event.sourceKind}, ${event.sourceId}, ${event.aggregateType}, ${event.aggregateId}, ${event.correlationId}, ${event.causationId}, ${executor.json(event.tags)}, ${executor.json(event.payload)}, ${event.createdAt}) + ON CONFLICT (event_id) DO NOTHING + RETURNING * + `; + return rows[0] === undefined ? null : rowToEvent(rows[0]); +} + +async function publishEvents(inputs: OaEventInput[]): Promise<{ inserted: OaEventRecord[]; duplicates: number; derived: OaEventRecord[] }> { + const inserted: OaEventRecord[] = []; + const derived: OaEventRecord[] = []; + let duplicates = 0; + await sql.begin(async (tx: postgres.TransactionSql) => { + for (const input of inputs) { + const normalized = normalizeEvent(input); + const stored = await storeInputEvent(tx, normalized); + if (stored === null) { + duplicates += 1; + continue; + } + inserted.push(stored); + for (const stats of await applyTraceProjection(tx, stored)) { + const statsEvent = await insertDerivedStatsEvent(tx, stats, stored); + if (statsEvent !== null) derived.push(statsEvent); + } + } + const lastSequence = Math.max(0, ...inserted.map((event) => event.sequence), ...derived.map((event) => event.sequence)); + if (lastSequence > 0) { + await tx` + INSERT INTO oa_projection_offsets (projection_name, last_sequence, updated_at) + VALUES ('trace-stats', ${lastSequence}, now()) + ON CONFLICT (projection_name) DO UPDATE SET last_sequence = GREATEST(oa_projection_offsets.last_sequence, EXCLUDED.last_sequence), updated_at = now() + `; + } + }); + for (const event of inserted) emitEvent(event); + for (const event of derived) emitEvent(event); + return { inserted, duplicates, derived }; +} + +function arrayValue(value: unknown): unknown[] { + return Array.isArray(value) ? value : []; +} + +function nonNegativeInteger(value: unknown, fallback = 0): number { + const number = Number(value); + return Number.isFinite(number) && number >= 0 ? Math.floor(number) : fallback; +} + +async function bridgeFetchJson(url: URL): Promise { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 20_000); + try { + const response = await fetch(url, { + headers: { "accept": "application/json" }, + signal: controller.signal, + }); + const text = await response.text(); + if (!response.ok) throw new Error(`pipeline bridge fetch failed ${response.status} ${response.statusText}: ${text.slice(0, 500)}`); + return text.trim().length > 0 ? JSON.parse(text) as unknown : {}; + } finally { + clearTimeout(timeout); + } +} + +function pipelineRunBridgeState(runId: string, pipelineId: string): PipelineBridgeRunState { + const current = pipelineBridgeState.runs[runId]; + if (current !== undefined) { + if (pipelineId.length > 0) current.pipelineId = pipelineId; + return current; + } + const state: PipelineBridgeRunState = { + runId, + pipelineId, + lastObservedEventCount: 0, + lastSyncedAt: "", + lastError: "", + bridgeMode: "snapshot", + insertedCount: 0, + duplicateCount: 0, + }; + pipelineBridgeState.runs[runId] = state; + return state; +} + +function pipelineBridgeEventTags(record: JsonRecord, runId: string, pipelineId: string): string[] { + const tags = normalizeTags(record.tags); + addTag(tags, "service:pipeline"); + addTag(tags, "pipeline.fact"); + if (pipelineId.length > 0) addTag(tags, `pipeline:${pipelineId}`); + addTag(tags, `epoch:${runId}`); + const nodeId = stringValue(record.nodeId) || stringValue(record.targetNodeId) || stringValue(record.sourceNodeId); + const procedureRunId = stringValue(record.procedureRunId); + const attemptId = stringValue(record.attemptId) || stringValue(record.attempt); + if (nodeId.length > 0) addTag(tags, `node:${nodeId}`); + if (procedureRunId.length > 0) addTag(tags, `procedure:${procedureRunId}`); + if (attemptId.length > 0) addTag(tags, `attempt:${attemptId}`); + return tags; +} + +function pipelineDiagnosticsByRun(diagnostics: JsonRecord): Map { + const map = new Map(); + for (const item of arrayValue(diagnostics.runs)) { + const record = asRecord(item); + const runId = stringValue(record?.runId); + if (record !== null && runId.length > 0) map.set(runId, record as JsonRecord); + } + return map; +} + +function compactPipelineRunSnapshot(run: JsonRecord): JsonRecord { + const nodes = arrayValue(run.nodes).flatMap((item) => { + const record = asRecord(item); + if (record === null) return []; + return [{ + nodeId: stringValue(record.nodeId), + kind: stringValue(record.kind), + status: stringValue(record.status), + currentProcedureRunId: stringValue(record.currentProcedureRunId), + procedureRunCount: arrayValue(record.procedureRunIds).length, + }]; + }); + return { + status: stringValue(run.status), + error: stringValue(run.error), + startedAt: stringValue(run.startedAt), + finishedAt: stringValue(run.finishedAt), + updatedAt: stringValue(run.updatedAt), + durationMs: nonNegativeInteger(run.durationMs), + nodeCount: nodes.length, + procedureRunCount: arrayValue(run.procedureRuns).length, + scorerCount: arrayValue(run.scorers).length, + submissionCount: arrayValue(run.submissions).length, + nodes, + }; +} + +function pipelineSnapshotBridgeEventInput(run: JsonRecord, diagnostics: JsonRecord | null): OaEventInput { + const runId = stringValue(run.runId); + const pipelineId = stringValue(run.pipelineId); + const compactRun = compactPipelineRunSnapshot(run); + const compactDiagnostics: JsonRecord = diagnostics === null ? {} : { + eventCount: nonNegativeInteger(diagnostics.eventCount), + nodeFinishedCount: nonNegativeInteger(diagnostics.nodeFinishedCount), + nodeFinishedWithPolicyCount: nonNegativeInteger(diagnostics.nodeFinishedWithPolicyCount), + monitorAuditNodeFinishedCount: nonNegativeInteger(diagnostics.monitorAuditNodeFinishedCount), + noAuditPolicyCount: nonNegativeInteger(diagnostics.noAuditPolicyCount), + controlQueuedCount: nonNegativeInteger(diagnostics.controlQueuedCount), + controlAppliedCount: nonNegativeInteger(diagnostics.controlAppliedCount), + }; + const payload: JsonRecord = { + runId, + epochId: runId, + pipelineId, + taskPreview: stringValue(run.task).slice(0, 600), + bridgeSource: "pipeline-snapshot", + bridgeService: "oa-event-flow", + run: compactRun, + diagnostics: compactDiagnostics, + }; + const tags = pipelineBridgeEventTags({ ...payload, event: "pipeline-run-snapshot" }, runId, pipelineId); + addTag(tags, "pipeline.run"); + addTag(tags, `status:${stringValue(run.status, "unknown") || "unknown"}`); + return { + eventId: `pipeline:${runId}:run-snapshot:${stableEventHash(payload)}`, + type: "pipeline-run-snapshot", + createdAt: run.updatedAt || run.finishedAt || run.startedAt, + sourceKind: "service", + sourceId: "pipeline", + aggregateType: pipelineId.length > 0 ? "pipeline" : "epoch", + aggregateId: pipelineId.length > 0 ? pipelineId : runId, + correlationId: `pipeline:${runId}`, + causationId: null, + tags, + payload, + }; +} + +async function syncPipelineRunSnapshot(run: JsonRecord, diagnostics: JsonRecord | null): Promise { + const runId = stringValue(run.runId); + if (runId.length === 0) return; + const pipelineId = stringValue(run.pipelineId); + const state = pipelineRunBridgeState(runId, pipelineId); + const result = await publishEvents([pipelineSnapshotBridgeEventInput(run, diagnostics)]); + state.insertedCount += result.inserted.length; + state.duplicateCount += result.duplicates; + state.bridgeMode = "snapshot"; + state.lastObservedEventCount = Math.max(state.lastObservedEventCount, nonNegativeInteger(diagnostics?.eventCount)); + state.lastSyncedAt = new Date().toISOString(); + state.lastError = ""; + pipelineBridgeState.insertedCount += result.inserted.length; + pipelineBridgeState.duplicateCount += result.duplicates; + pipelineBridgeState.snapshotSyncCount += 1; +} + +async function syncPipelineBridge(reason: string): Promise { + if (!pipelineBridgeState.enabled || pipelineBridgeState.inFlight || !databaseReady) return; + pipelineBridgeState.inFlight = true; + pipelineBridgeState.lastStartedAt = new Date().toISOString(); + pipelineBridgeState.syncCount += 1; + try { + const snapshotUrl = new URL(`${pipelineBridgeState.baseUrl}/api/snapshot`); + snapshotUrl.searchParams.set("__unideskArrayLimit", `runs:${pipelineBridgeState.runLimit}`); + const snapshot = asRecord(await bridgeFetchJson(snapshotUrl)) ?? {}; + let diagnosticsByRun = new Map(); + try { + const diagnosticsUrl = new URL(`${pipelineBridgeState.baseUrl}/api/oa-event-flow/diagnostics`); + const diagnostics = asJsonRecord(await bridgeFetchJson(diagnosticsUrl)); + diagnosticsByRun = pipelineDiagnosticsByRun(diagnostics); + } catch (error) { + log("warn", "pipeline_bridge_diagnostics_unavailable", { reason, error: errorToJson(error) }); + } + const runs = arrayValue(snapshot.runs).flatMap((item) => { + const record = asRecord(item); + const runId = stringValue(record?.runId); + if (record === null || runId.length === 0) return []; + return [record as JsonRecord]; + }).slice(0, pipelineBridgeState.runLimit); + for (const run of runs) { + const runId = stringValue(run.runId); + try { + await syncPipelineRunSnapshot(run, diagnosticsByRun.get(runId) ?? null); + } catch (error) { + const state = pipelineRunBridgeState(runId, stringValue(run.pipelineId)); + state.lastError = error instanceof Error ? error.message : String(error); + log("warn", "pipeline_bridge_run_failed", { reason, runId, error: errorToJson(error) }); + } + } + pipelineBridgeState.lastError = ""; + pipelineBridgeState.lastFinishedAt = new Date().toISOString(); + log("info", "pipeline_bridge_synced", { reason, runCount: runs.length, insertedCount: pipelineBridgeState.insertedCount, duplicateCount: pipelineBridgeState.duplicateCount }); + } catch (error) { + pipelineBridgeState.lastError = error instanceof Error ? error.message : String(error); + pipelineBridgeState.lastFinishedAt = new Date().toISOString(); + log("warn", "pipeline_bridge_failed", { reason, error: errorToJson(error) }); + } finally { + pipelineBridgeState.inFlight = false; + } +} + +function startPipelineBridge(): void { + if (!pipelineBridgeState.enabled) { + log("info", "pipeline_bridge_disabled"); + return; + } + const run = (reason: string) => { + void syncPipelineBridge(reason).catch((error) => log("warn", "pipeline_bridge_unhandled_error", { reason, error: errorToJson(error) })); + }; + setTimeout(() => run("startup"), 2_000).unref?.(); + pipelineBridgeTimer = setInterval(() => run("interval"), pipelineBridgeState.intervalMs); + pipelineBridgeTimer.unref?.(); + log("info", "pipeline_bridge_started", { + baseUrl: pipelineBridgeState.baseUrl, + intervalMs: pipelineBridgeState.intervalMs, + runLimit: pipelineBridgeState.runLimit, + mode: "snapshot", + }); +} + +async function readJson(req: Request): Promise { + const text = await req.text(); + if (text.trim().length === 0) return {}; + return JSON.parse(text) as unknown; +} + +function eventsFromBody(body: unknown): OaEventInput[] { + const record = asRecord(body); + if (Array.isArray(body)) return body.map((item) => asRecord(item) ?? {}) as OaEventInput[]; + if (record !== null && Array.isArray(record.events)) return record.events.map((item) => asRecord(item) ?? {}) as OaEventInput[]; + return [record ?? {} as OaEventInput]; +} + +async function publishEventsRoute(req: Request): Promise { + const inputs = eventsFromBody(await readJson(req)); + if (inputs.length === 0) return jsonResponse({ ok: false, error: "events must not be empty" }, 400); + if (inputs.length > 200) return jsonResponse({ ok: false, error: "too many events", maxEvents: 200 }, 413); + const result = await publishEvents(inputs); + return jsonResponse({ ok: true, inserted: result.inserted, duplicates: result.duplicates, derived: result.derived }); +} + +function taskIdFromTraceScopeId(scopeId: string): string { + const match = scopeId.match(/^task:([^:]+)(?::attempt:\d+)?$/u); + return match?.[1] ?? ""; +} + +async function rebuildTraceProjectionRoute(url: URL): Promise { + const scopeId = stringValue(url.searchParams.get("scopeId")); + const taskId = taskIdFromTraceScopeId(scopeId); + if (scopeId.length === 0 || taskId.length === 0) return jsonResponse({ ok: false, error: "scopeId must be a task scope, e.g. task:" }, 400); + const taskScopeId = `task:${taskId}`; + const attemptPrefix = `${taskScopeId}:attempt:`; + const replayedStats = new Map(); + let eventCount = 0; + await sql.begin(async (tx: postgres.TransactionSql) => { + await tx`SELECT pg_advisory_xact_lock(482756, hashtext(${taskScopeId}))`; + await tx`DELETE FROM oa_trace_steps WHERE scope_id = ${taskScopeId} OR left(scope_id, ${attemptPrefix.length}) = ${attemptPrefix}`; + await tx`DELETE FROM oa_trace_stats WHERE scope_id = ${taskScopeId} OR left(scope_id, ${attemptPrefix.length}) = ${attemptPrefix}`; + const rows = await tx` + SELECT * + FROM oa_events + WHERE type <> 'trace-stats-updated' + AND (tags @> ${tx.json([`task:${taskId}`])}::jsonb OR payload->>'taskId' = ${taskId}) + AND (tags @> ${tx.json(["trace"])}::jsonb OR type LIKE 'trace-%') + ORDER BY sequence ASC + `; + eventCount = rows.length; + for (const row of rows) { + const event = rowToEvent(row); + for (const stats of await applyTraceProjection(tx, event)) replayedStats.set(stats.scopeId, stats); + } + }); + const stats = Array.from(replayedStats.values()).sort((left, right) => left.scopeId.localeCompare(right.scopeId)); + log("info", "trace_projection_rebuilt", { scopeId: taskScopeId, taskId, eventCount, statsCount: stats.length }); + return jsonResponse({ ok: true, scopeId: taskScopeId, taskId, eventCount, statsCount: stats.length, stats }); +} + +async function queryEvents(url: URL): Promise { + const tags = parseTagsFromUrl(url); + const type = stringValue(url.searchParams.get("type")); + const sourceId = stringValue(url.searchParams.get("sourceId")); + const afterSeq = Math.max(0, Number(url.searchParams.get("afterSeq") ?? 0) || 0); + const limit = parseLimit(url); + if (tags.length > 0 && type.length > 0 && sourceId.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND tags @> ${sql.json(tags)}::jsonb AND type = ${type} AND source_id = ${sourceId} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + if (tags.length > 0 && type.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND tags @> ${sql.json(tags)}::jsonb AND type = ${type} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + if (tags.length > 0 && sourceId.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND tags @> ${sql.json(tags)}::jsonb AND source_id = ${sourceId} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + if (tags.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND tags @> ${sql.json(tags)}::jsonb + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + if (type.length > 0 && sourceId.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND type = ${type} AND source_id = ${sourceId} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + if (type.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND type = ${type} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + if (sourceId.length > 0) { + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} AND source_id = ${sourceId} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); + } + const rows = await sql` + SELECT * FROM oa_events + WHERE sequence > ${afterSeq} + ORDER BY sequence ASC + LIMIT ${limit} + `; + return rows.map(rowToEvent); +} + +async function eventsRoute(url: URL): Promise { + const events = await queryEvents(url); + return jsonResponse({ ok: true, events, returned: events.length, nextAfterSeq: events.at(-1)?.sequence ?? Number(url.searchParams.get("afterSeq") ?? 0) }); +} + +async function eventStreamRoute(url: URL): Promise { + const tags = parseTagsFromUrl(url); + const afterSeq = Math.max(0, Number(url.searchParams.get("afterSeq") ?? 0) || 0); + let heartbeat: ReturnType | null = null; + let client: EventClient | null = null; + const stream = new ReadableStream({ + async start(controller) { + const id = `client_${randomUUID()}`; + client = { + id, + tags, + connectedAt: new Date().toISOString(), + enqueue(chunk: string) { + controller.enqueue(eventEncoder.encode(chunk)); + }, + close() { + if (heartbeat !== null) { + clearInterval(heartbeat); + heartbeat = null; + } + if (client !== null) eventClients.delete(client); + client = null; + try { + controller.close(); + } catch { + // The browser may have already closed the EventSource. + } + }, + }; + eventClients.add(client); + controller.enqueue(eventEncoder.encode("retry: 1000\n\n")); + client.enqueue([ + "event: hello", + `data: ${JSON.stringify({ ok: true, service: "oa-event-flow", tags, afterSeq, at: new Date().toISOString() })}`, + "", + "", + ].join("\n")); + const backlogUrl = new URL("http://oa-event-flow.local/api/events"); + backlogUrl.searchParams.set("afterSeq", String(afterSeq)); + backlogUrl.searchParams.set("limit", "200"); + if (tags.length > 0) backlogUrl.searchParams.set("tags", tags.join(",")); + for (const event of await queryEvents(backlogUrl)) client.enqueue(eventSseChunk(event)); + heartbeat = setInterval(() => { + try { + controller.enqueue(eventEncoder.encode(`: heartbeat ${new Date().toISOString()}\n\n`)); + } catch { + client?.close(); + } + }, 25_000); + heartbeat.unref?.(); + }, + cancel() { + client?.close(); + }, + }); + return new Response(stream, { + headers: { + "content-type": "text/event-stream; charset=utf-8", + "cache-control": "no-store, no-transform", + "connection": "keep-alive", + "x-accel-buffering": "no", + }, + }); +} + +async function traceStatsRoute(url: URL): Promise { + const scopeIds = parseScopeIdsFromUrl(url); + const tags = parseTagsFromUrl(url); + const limit = parseLimit(url, 100, 1000); + if (scopeIds.length > 0) { + const rows = await sql` + SELECT * + FROM oa_trace_stats + WHERE scope_id IN ${sql(scopeIds)} + ORDER BY updated_at DESC + LIMIT ${limit} + `; + const stats = rows.map(statsRowToRecord); + return jsonResponse({ ok: true, stats, stat: stats[0] ?? null, returned: stats.length }); + } + const serviceTag = tags.find((tag) => tag.startsWith("service:")) ?? ""; + const serviceId = serviceTag.length > 0 ? serviceTag.slice("service:".length) : stringValue(url.searchParams.get("serviceId")); + if (serviceId.length > 0) { + const rows = await sql` + SELECT * + FROM oa_trace_stats + WHERE service_id = ${serviceId} + ORDER BY updated_at DESC + LIMIT ${limit} + `; + const stats = rows.map(statsRowToRecord); + return jsonResponse({ ok: true, stats, returned: stats.length }); + } + const rows = await sql` + SELECT * + FROM oa_trace_stats + ORDER BY updated_at DESC + LIMIT ${limit} + `; + const stats = rows.map(statsRowToRecord); + return jsonResponse({ ok: true, stats, returned: stats.length }); +} + +async function diagnosticsRoute(): Promise { + const [eventCountRows, latestRows, statsCountRows, typeRows, recentStatsRows, offsetRows] = await Promise.all([ + sql>`SELECT COUNT(*) AS count FROM oa_events`, + sql`SELECT * FROM oa_events ORDER BY sequence DESC LIMIT 20`, + sql>`SELECT COUNT(*) AS count FROM oa_trace_stats`, + sql>`SELECT type, COUNT(*) AS count FROM oa_events GROUP BY type ORDER BY count DESC, type ASC LIMIT 20`, + sql`SELECT * FROM oa_trace_stats ORDER BY updated_at DESC LIMIT 20`, + sql>`SELECT * FROM oa_projection_offsets ORDER BY projection_name ASC`, + ]); + return jsonResponse({ + ok: true, + service: "oa-event-flow", + startedAt: serviceStartedAt, + databaseReady, + databaseLastError, + eventCount: Number(eventCountRows[0]?.count ?? 0), + traceStatsCount: Number(statsCountRows[0]?.count ?? 0), + latestSequence: Number(latestRows[0]?.sequence ?? 0), + eventTypes: (typeRows as Array<{ type: string; count: string | number }>).map((row) => ({ type: row.type, count: Number(row.count) })), + projectionOffsets: (offsetRows as Array<{ projection_name: string; last_sequence: string | number; updated_at: Date | string }>).map((row) => ({ name: row.projection_name, lastSequence: Number(row.last_sequence), updatedAt: isoTimestamp(row.updated_at) })), + sseClients: Array.from(eventClients).map((client) => ({ id: client.id, tags: client.tags, connectedAt: client.connectedAt })), + recentEvents: latestRows.map(rowToEvent), + recentTraceStats: recentStatsRows.map(statsRowToRecord), + pipelineBridge: pipelineBridgeState, + }); +} + +async function healthRoute(): Promise { + const [eventCountRows, statsCountRows, latestRows] = databaseReady + ? await Promise.all([ + sql>`SELECT COUNT(*) AS count FROM oa_events`, + sql>`SELECT COUNT(*) AS count FROM oa_trace_stats`, + sql>`SELECT sequence FROM oa_events ORDER BY sequence DESC LIMIT 1`, + ]) + : [[{ count: 0 }], [{ count: 0 }], [{ sequence: 0 }]]; + return jsonResponse({ + ok: databaseReady, + service: "oa-event-flow", + startedAt: serviceStartedAt, + databaseReady, + databaseLastError, + eventCount: Number(eventCountRows[0]?.count ?? 0), + traceStatsCount: Number(statsCountRows[0]?.count ?? 0), + latestSequence: Number(latestRows[0]?.sequence ?? 0), + sseClientCount: eventClients.size, + pipelineBridge: { + enabled: pipelineBridgeState.enabled, + mode: pipelineBridgeState.mode, + inFlight: pipelineBridgeState.inFlight, + lastFinishedAt: pipelineBridgeState.lastFinishedAt, + lastError: pipelineBridgeState.lastError, + insertedCount: pipelineBridgeState.insertedCount, + duplicateCount: pipelineBridgeState.duplicateCount, + }, + }, databaseReady ? 200 : 503); +} + +async function route(req: Request): Promise { + const url = new URL(req.url); + if (req.method === "OPTIONS") return jsonResponse({ ok: true }); + try { + if (url.pathname === "/" || url.pathname === "/health") return await healthRoute(); + if (url.pathname === "/logs" && req.method === "GET") return jsonResponse({ ok: true, logs: recentLogs.slice(-parseLimit(url, 100, 500)) }); + if (url.pathname === "/api/events" && req.method === "POST") return await publishEventsRoute(req); + if (url.pathname === "/api/events" && req.method === "GET") return await eventsRoute(url); + if (url.pathname === "/api/events/stream" && req.method === "GET") return await eventStreamRoute(url); + if (url.pathname === "/api/stats/trace" && req.method === "GET") return await traceStatsRoute(url); + if (url.pathname === "/api/projections/trace/rebuild" && (req.method === "GET" || req.method === "POST")) return await rebuildTraceProjectionRoute(url); + if (url.pathname === "/api/diagnostics" && req.method === "GET") return await diagnosticsRoute(); + return jsonResponse({ ok: false, error: "not found", path: url.pathname }, 404); + } catch (error) { + return errorResponse(error); + } +} + +await waitForSchema(); +Bun.serve({ hostname: config.host, port: config.port, idleTimeout: 120, fetch: route }); +log("info", "service_started", { port: config.port }); +startPipelineBridge(); diff --git a/src/components/microservices/oa-event-flow/tsconfig.json b/src/components/microservices/oa-event-flow/tsconfig.json new file mode 100644 index 00000000..5d5f23f2 --- /dev/null +++ b/src/components/microservices/oa-event-flow/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "composite": true, + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "Bundler", + "types": ["bun", "node"], + "strict": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "declaration": true, + "emitDeclarationOnly": true, + "outDir": "dist", + "skipLibCheck": true + }, + "include": ["src/**/*.ts"], + "references": [{ "path": "../../shared" }] +}