From 0a5f1448f132e70d1ccd93e44ab3e1f3b1f398ca Mon Sep 17 00:00:00 2001 From: whyour Date: Thu, 20 Aug 2026 09:23:19 +0800 Subject: [PATCH] feat(ql3): add optional console run drilldown --- deploy/console/ql3-cluster-copilot/README.md | 42 ++- .../ql3-cluster-copilot/docker-loopback.sh | 11 + .../host-environment.example.json | 3 +- .../run-management-client-config.example.json | 9 + .../containers/ql3-cluster-admin/Dockerfile | 2 + docs/QINGLONG_3_0_ARCHITECTURE_RFC.md | 28 +- ...tional-console-run-management-drilldown.md | 69 +++++ .../assets/copilot-console/app.css | 2 +- .../assets/copilot-console/app.js | 56 +++- .../assets/copilot-console/evidence-bundle.js | 39 ++- .../assets/copilot-console/index.html | 26 +- .../src/copilot-console/assets.ts | 8 +- .../src/copilot-console/cli.ts | 195 ++++++++++++-- .../src/copilot-console/contracts.ts | 46 +++- .../src/copilot-console/evidenceVerifier.ts | 47 +++- .../src/copilot-console/server.ts | 23 +- .../runCancellationInspection.ts | 73 ++++++ .../test/copilotConsole.test.cjs | 90 +++++++ .../test/copilotConsoleCli.test.cjs | 245 +++++++++++++++++- .../copilotConsoleEvidenceBundle.test.cjs | 58 +++++ .../copilotConsoleEvidenceVerifier.test.cjs | 16 +- .../test/runCancellationInspection.test.cjs | 93 +++++++ ...l3-cluster-admin-product-live-contract.cjs | 2 +- scripts/ql3-cluster-copilot-console-audit.cjs | 26 +- ...ter-copilot-console-distribution-audit.cjs | 9 + .../ql3ClusterCopilotConsoleAudit.test.cjs | 5 +- ...erCopilotConsoleDistributionAudit.test.cjs | 1 + .../ql3ClusterCopilotConsoleLauncher.test.cjs | 29 ++- test/back/ql3PackageBoundaryAudit.test.cjs | 8 +- 29 files changed, 1193 insertions(+), 68 deletions(-) create mode 100644 deploy/console/ql3-cluster-copilot/run-management-client-config.example.json create mode 100644 docs/adr/ADR-0462-optional-console-run-management-drilldown.md create mode 100644 packages/ql3-cluster-admin/src/run-management/runCancellationInspection.ts create mode 100644 packages/ql3-cluster-admin/test/runCancellationInspection.test.cjs diff --git a/deploy/console/ql3-cluster-copilot/README.md b/deploy/console/ql3-cluster-copilot/README.md index b0f59365..57bca7e3 100644 --- a/deploy/console/ql3-cluster-copilot/README.md +++ b/deploy/console/ql3-cluster-copilot/README.md @@ -7,6 +7,11 @@ and Copilot reads to existing Cluster APIs. It never accepts a browser-provided URL or method. Do not deploy it as a Kubernetes workload, Ingress, shared LAN listener, Edge component or legacy 2.x Web route. +Run cancellation availability is a second, explicit authority. It is disabled +by default. Supplying a separate Run management mTLS config and short-lived +User assertion adds only status, blocked-list and inspect reads to the same +loopback process; rearm, stop and retry remain absent. + Use `ql3-cluster-admin` from the same independently verified Admin release as the Cluster deployment. D-328 also supports the image-carried `docker-loopback.sh`: it uses an explicit container-only listener but publishes @@ -97,6 +102,15 @@ and `artifact.read`; `run.read` covers Run and Workflow observations, while requested Copilot output. The Console has no route for Run/Workflow start, diagnosis creation or cancellation even if a wider credential is supplied. +To enable the optional cancellation drill-down, copy +`run-management-client-config.example.json` to `run-management-client.json`, +install its CA, client certificate and private key, and issue a short-lived +strong User assertion with only `run.read` into +`run-management-assertion.jwt`. These files are independent of the Project API +credential. Supplying only one of config/assertion is rejected before a +listener or Cluster request is created. The assertion is reread for every +explicit click so it can be rotated or removed while the Console is running. + Create an independent 256-bit browser session key without placing its value in argv or an environment variable: @@ -107,6 +121,11 @@ node -e 'process.stdout.write(require("node:crypto").randomBytes(32).toString("b chmod 0600 /absolute/private/ql3-copilot-console/client.json /absolute/private/ql3-copilot-console/ca.pem /absolute/private/ql3-copilot-console/credential /absolute/private/ql3-copilot-console/session ``` +If optional Run management reads are enabled, apply the same owner-private, +canonical, non-symlink `0600` rule to `run-management-client.json`, +`run-management-ca.pem`, `run-management-client.crt`, +`run-management-client.key` and `run-management-assertion.jwt`. + Every file must be a current-owner, non-symlink, canonical regular file. The session file contains exactly 43 base64url characters and no newline. It is a browser-to-loopback secret only; it cannot authenticate to the Cluster API. @@ -124,6 +143,14 @@ ql3-cluster-admin copilot-console --check \ --session /absolute/private/ql3-copilot-console/session ``` +Append both flags to preflight and serve when the optional authority is +intended: + +```sh +--run-management-config /absolute/private/ql3-copilot-console/run-management-client.json \ +--run-management-assertion /absolute/private/ql3-copilot-console/run-management-assertion.jwt +``` + It validates all three private authorities and performs one unauthenticated TLS 1.3 `GET /readyz`. It does not open the Console listener or reveal paths, endpoint, credential, Project or Cluster identity. @@ -150,13 +177,20 @@ Model text is rendered as plain text and remains untrusted advice. These limits keep the workstation surface bounded, but this Cluster-only product is still excluded from small router Edge/Standalone artifacts. -The page exposes thirteen exact operations: Copilot `inspect|output`; Run +The default page exposes thirteen exact operations: Copilot `inspect|output`; Run list/detail/events/steps; Task list/detail; and Workflow list plus Workflow Run list/detail/events/steps. List responses use 32-row pages and offer an explicit next-page read only when the upstream cursor says more data exists. There is no automatic cascade from a list to details, steps or events, so each authority read remains visible and intentional. +Explicit Run management authority raises the available vocabulary to sixteen: +one Project cancellation status, one fixed 16-item blocked snapshot page and +one low-sensitive Run cancellation inspection. An `attention_required` status +offers a user-clicked blocked-list step; each returned Run offers a user-clicked +inspect step. Pagination is also click-only. There is no polling, automatic +page traversal, bulk inspection or mutation route. + ## Export a redacted evidence bundle After at least one successful read, **Export redacted bundle** creates one @@ -204,6 +238,12 @@ the image with the verified digest and selecting one unused host port. The launcher rejects `bridge|default|host|none`, mutable tags, noncanonical private roots, ports outside `1024..65535` and unknown resource classes. +The image launcher keeps Run management disabled unless +`QL3_COPILOT_CONSOLE_RUN_MANAGEMENT=enabled` is set. Enabled mode reads +`run-management-client.json` and `run-management-assertion.jwt` from the same +read-only private mount; all certificate paths in the config must point into +that mount. `disabled` is the only default and unknown values fail closed. + | Resource class | Memory | CPU | PIDs | Console reads | | --- | ---: | ---: | ---: | ---: | | `compact` | 192 MiB | 0.25 | 32 | 2, no queue | diff --git a/deploy/console/ql3-cluster-copilot/docker-loopback.sh b/deploy/console/ql3-cluster-copilot/docker-loopback.sh index e19be866..a0916ecc 100755 --- a/deploy/console/ql3-cluster-copilot/docker-loopback.sh +++ b/deploy/console/ql3-cluster-copilot/docker-loopback.sh @@ -24,6 +24,7 @@ private_root=${QL3_COPILOT_CONSOLE_PRIVATE_ROOT-} network=${QL3_COPILOT_CONSOLE_NETWORK-} port=${QL3_COPILOT_CONSOLE_PORT-} resource_class=${QL3_COPILOT_CONSOLE_RESOURCE_CLASS-compact} +run_management=${QL3_COPILOT_CONSOLE_RUN_MANAGEMENT-disabled} printf '%s' "$image" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9._/-]{0,191}@sha256:[0-9a-f]{64}$' || fail printf '%s' "$network" | grep -Eq '^[A-Za-z0-9][A-Za-z0-9_.-]{0,62}$' || fail @@ -57,6 +58,10 @@ case "$resource_class" in ;; *) fail ;; esac +case "$run_management" in + disabled|enabled) ;; + *) fail ;; +esac set -- docker run --rm --pull never --init --read-only \ --network "$network" \ @@ -81,6 +86,12 @@ set -- "$@" "$image" copilot-console \ --credential /var/run/secrets/qinglong3/copilot-console/credential \ --session /var/run/secrets/qinglong3/copilot-console/session +if [ "$run_management" = enabled ]; then + set -- "$@" \ + --run-management-config /var/run/secrets/qinglong3/copilot-console/run-management-client.json \ + --run-management-assertion /var/run/secrets/qinglong3/copilot-console/run-management-assertion.jwt +fi + if [ "$mode" = check ]; then set -- "$@" --check fi diff --git a/deploy/console/ql3-cluster-copilot/host-environment.example.json b/deploy/console/ql3-cluster-copilot/host-environment.example.json index b05df2e5..ab31f06f 100644 --- a/deploy/console/ql3-cluster-copilot/host-environment.example.json +++ b/deploy/console/ql3-cluster-copilot/host-environment.example.json @@ -3,5 +3,6 @@ "QL3_COPILOT_CONSOLE_PRIVATE_ROOT": "/absolute/private/ql3-copilot-console", "QL3_COPILOT_CONSOLE_NETWORK": "qinglong3-copilot-console-egress", "QL3_COPILOT_CONSOLE_PORT": "5701", - "QL3_COPILOT_CONSOLE_RESOURCE_CLASS": "compact" + "QL3_COPILOT_CONSOLE_RESOURCE_CLASS": "compact", + "QL3_COPILOT_CONSOLE_RUN_MANAGEMENT": "disabled" } diff --git a/deploy/console/ql3-cluster-copilot/run-management-client-config.example.json b/deploy/console/ql3-cluster-copilot/run-management-client-config.example.json new file mode 100644 index 00000000..40d4d7bf --- /dev/null +++ b/deploy/console/ql3-cluster-copilot/run-management-client-config.example.json @@ -0,0 +1,9 @@ +{ + "schemaVersion": 1, + "endpoint": "https://replace-cluster-api.example.com:8448/api/v3/runs/management", + "servername": "replace-cluster-api.example.com", + "caFile": "/absolute/private/ql3-copilot-console/run-management-ca.pem", + "clientCertificateFile": "/absolute/private/ql3-copilot-console/run-management-client.crt", + "clientPrivateKeyFile": "/absolute/private/ql3-copilot-console/run-management-client.key", + "requestTimeoutMs": 5000 +} diff --git a/deploy/containers/ql3-cluster-admin/Dockerfile b/deploy/containers/ql3-cluster-admin/Dockerfile index 4fc45dc7..b946a3c5 100644 --- a/deploy/containers/ql3-cluster-admin/Dockerfile +++ b/deploy/containers/ql3-cluster-admin/Dockerfile @@ -85,6 +85,8 @@ COPY --chmod=0444 deploy/console/ql3-cluster-copilot/README.md \ share/ql3-copilot-console/README.md COPY --chmod=0444 deploy/console/ql3-cluster-copilot/client-config.example.json \ share/ql3-copilot-console/client-config.example.json +COPY --chmod=0444 deploy/console/ql3-cluster-copilot/run-management-client-config.example.json \ + share/ql3-copilot-console/run-management-client-config.example.json COPY --chmod=0444 deploy/console/ql3-cluster-copilot/host-environment.example.json \ share/ql3-copilot-console/host-environment.example.json diff --git a/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md b/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md index b35d6f1d..a5b8c6ac 100644 --- a/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md +++ b/docs/QINGLONG_3_0_ARCHITECTURE_RFC.md @@ -6,12 +6,28 @@ - 目标版本:QingLong 3.x - 作者:QingLong Maintainers - 创建日期:2026-07-17 -- 最后更新:2026-08-19 +- 最后更新:2026-08-20 - 讨论范围:架构与演进路线,不包含最终 UI 视觉方案 -最新增量证据(2026-08-19): +最新增量证据(2026-08-20): -- D-368/ADR-0461(已接受;Console 可选接入待完成):在既有 Run management plane 与 `ql3 run` 增加 +- D-369/ADR-0462(已接受):在既有 operator-workstation Copilot Console 内完成显式可选、默认关闭的 Run management 只读纵切,新增固定 + `run_cancellation_status`、`run_cancellation_blocked_list`、`run_cancellation_inspect` 三种 browser/BFF operation。只有同时提供独立 + `--run-management-config` 与 owner-private `--run-management-assertion` 才启用,Project API credential、浏览器 session 与 Run 专用 TLS 1.3/mTLS/OIDC + authority 继续隔离;preflight 不消耗 assertion,serve 每次用户点击重新读取短期 assertion。导航严格为用户点击的 `status → blocked → inspect`,blocked + page 固定 16 项且下一页必须再次点击;没有启动读取、轮询、自动翻页、N+1 inspect、timer、queue、cache 或隐藏重试。Browser vocabulary、固定 route、 + request normalizer 与 executor 均不能表达 `rearm`、`stop`、`retry` 或任意 mutation。blocked cursor 只在网络中保留 canonical token,证据导出改写为 + per-bundle alias。容器 launcher 只有显式 `QL3_COPILOT_CONSOLE_RUN_MANAGEMENT=enabled` 才传入两个只读 mount 路径,默认仍为 disabled;没有新增 + package、依赖、binary、服务、端口、Kubernetes workload、schema、连接池或常驻资源。聚焦 build/test 为 `46/46`;Cluster Admin 全量为 + `420 total / 417 pass / 3 conditional skip / 0 fail`;backend 在允许 loopback listener 的宿主环境为 + `1,491 total / 1,489 pass / 2 conditional skip / 0 fail`;18-package clean build 与逐包顺序测试单次退出 0。package/dependency/Edge import/Cluster + deployment/Console/Console distribution 六项审计全部 compatible、零 finding;workspace 保持 18 packages、无 single/shallow package,Cluster Admin 为 + `125 source / 124 nested`,根目录仍只有 1 个受审 binary entry。`14/14` Local artifact audit 全部 compatible;基础 Edge/Standalone 精确为 + `2,589,998 / 2,590,076` bytes、315 files、56 modules,Application+AI 为 `4,493,151 / 4,493,283` bytes,MCP 为 + `7,315,930 / 7,316,038` bytes,证明 Cluster Console authority 没有进入低配路由设备闭包。本 Gate 不改 PostgreSQL schema、repository、service、role、 + Pool、连接或 HA 拓扑,因此不重跑和不重新占有物理 HA 证明;D-368 PostgreSQL 18.6 arm64 `146/146`、timeline `1→2` 仅作为相邻既有基线。 + +- D-368/ADR-0461(已接受;Console 可选接入由 D-369 完成):在既有 Run management plane 与 `ql3 run` 增加 `run.cancellation.blocked.list`/`blocked` 一次性 drill-down。服务端固定 16 项、只查询 17 行,客户端不能提供 limit 或自动翻页;页面只含 `{runId,blockedAtMs}`,以 PostgreSQL 首屏时间和 `(blockedAtMs,runId)` 组成稳定 cursor,严格 oldest-first、快照后新增项不进入后续页。强认证 User 继续使用 `run.read`,Policy fence、数据库时间、键集读取与 allowed audit 在同一 5 秒 SERIALIZABLE 短事务;CLI cursor 是有版本、精确字段、大小受限的 canonical @@ -27,7 +43,7 @@ rearm、生产交付、WAL 与 promotion;报告 SHA-256 为 `1fbd58c5bb32bbf83b6c1970a594f7879c33d63057a3b9c34f13ec9917ff5c44`,独立 evidence audit compatible 且零 finding。 -- D-367/ADR-0460(已接受;有界 blocked drill-down 由 D-368 完成,Console 可选接入待完成):在现有 `ql3 run`/`ql3-run-client` 增加一次性 +- D-367/ADR-0460(已接受;有界 blocked drill-down 由 D-368 完成,Console 可选接入由 D-369 完成):在现有 `ql3 run`/`ql3-run-client` 增加一次性 `status --config=... --assertion=... --project=... [--format=text|json]` 产品入口。它在内存生成固定 `run.cancellation.summary` 命令,仍经同一 exact codec、TLS 1.3、Run 专用 mTLS/OIDC、固定 management route 和响应交叉不变量校验;原 `--command` 私有文件模式保持兼容。默认 text 是无 ANSI 的确定性 Project 状态卡,JSON 使用 `qinglong/run-cancellation-status@v1`;两者只含 D-366 低敏计数与结论。告警映射固定为 @@ -42,7 +58,7 @@ `7,315,930 / 7,316,038` bytes。PostgreSQL 18.6 arm64 HA `145/145`、timeline `1→2`,报告 SHA-256 为 `59a568d0511cde671946ebf6df09f88868a3d591c5021c90bc27d4715411091e`,独立 evidence audit compatible 且零 finding。 -- D-366/ADR-0459(已接受;一次性产品 CLI 由 D-367 完成,Console 可选接入待完成):在既有 Run management plane 增加 `run.cancellation.summary`,由强认证 User 以 +- D-366/ADR-0459(已接受;一次性产品 CLI 由 D-367 完成,Console 可选接入由 D-369 完成):在既有 Run management plane 增加 `run.cancellation.summary`,由强认证 User 以 `run.read` 按需读取 Project 级 PostgreSQL 快照。响应只有五态 dispatch 计数、due/expired-lease 信号、四种 blocking-result 计数、最早 blocked 时间和 `clear|converging|attention_required`/`none|wait|inspect` 固定结论,不返回 Run/Attempt/Worker identity 或 lease capability。blocked 触发 `attention_required`,但不错误撤回整个 Cluster readiness;due/expired 只作为 caller-driven 收敛信号。查询与 allowed audit 位于同一 5 秒 @@ -9292,7 +9308,7 @@ flowchart LR | PR-2 Run 状态机 | Incubating | 纯转换表、终态/时间/错误/执行器元数据规则、Run version 与 event sequence CAS、事务性 RunCommandService、回滚测试 | 重复 Worker callback/fencing、并发数据库压力测试、Primary 执行链接入 | | PR-3 Executor 端口 | Incubating | ADR-0003、ExecutionSpec/Context/Handle/Result、Executor port、LocalProcessExecutor、进程组取消/超时升级、流式背压、Legacy Cron spec builder、真实进程 contract tests、可复现 edge 基准入口 | 固定 edge/多架构设备基线、Legacy builder 与 makeCommand 差异审计、Primary 生产流量接入 | | PR-4 Shadow Run | Incubating | origin 三态策略;默认关闭的 `QL3_SHADOW_ORIGINS`;manual、scheduled_node、boot、subscription、system 与 script 现有 ChildProcess 旁路观察;system crond 显式 origin marker、Shell execution ID、finish-only 准入、确定性 Run/Attempt 与 exact replay;`@once` 保持 manual、gRPC transport 不冒充 origin 的准入裁决;每个 worker 懒加载;Run/Attempt/Event 影子生命周期;稳定且不复制 caller 原文的 task identity/revision 与有界日志引用;同 worker 有界注册表和跨 worker 持久化候选关联;stop all/stop instance、Shell callback、乱序/迟到/歧义处理;监听前一次性、Profile-aware 的 keyset Startup Reconciler,终态证据补齐、lost/abandoned/pending 分流与 terminal Attempt response-loss 修复;origin-bounded 且逐级守恒的版本化 startup difference report、固定字段 metric batch 与一次性 collector;显式、只读、闭合窗口且 Profile-bounded 的 Shadow→Legacy 终态差异审计;128/256 MiB Linux arm64 资源门、SQLite 零增长与 Shadow enabled→off 进程重启回滚;process-epoch Legacy admission/capture/failure/pending 守恒;clean-shutdown `0600` no-replace capture+startup exporter;manual Edge 8/Standalone 32–128 canary;capture/terminal/resource 自包含 Primary bundle;rollout v2 loader 重算 source digest 与 eligibility;不可变 prepare/observe/resource/qualify 目标实例仪式、独立只读 audit;失败开放和契约测试 | 首次真实目标实例完整 canary 与 bootstrap activated 记录、其他 origin 独立 capture/Primary gate、固定物理 edge/flash/断电证据 | -| PR-5 Primary LocalExecutor | Incubating(默认不激活,仅 manifest-gated manual) | runtime-owned Run 创建器;持久化先于 spawn;Run/Attempt 完整成功、失败、取消、超时与 lost 闭环;Executor handle 身份校验;spawn 后激活写失败的 stop+lost 补偿;completion rejection 安全收敛;独立 Primary 幂等查询与唯一索引竞态裁决;durable `run.cancel_requested`、stop-before-signal、首次请求幂等、晚到完成裁决与待取消有界恢复查询;最多 64 条一页的 cross-worker cancellation source;独立 CancellationDispatch Repository 原子 claim/result、lease expiry 接管、owner/token/version fencing、指数退避与结果 RunEvent;PostgreSQL `pg-0066`/capability v65 adapter 以数据库时间、Run→Attempt→dispatch 锁序、digest-only durable token、最小 runtime 权限和原子 RunEvent/Run version CAS 提供多副本同构实现,真实双连接与 HA promotion 门已通过;caller-driven Worker lease-control 已以 settle-before-stop 接入 Cluster 生产组合,复用 ingress drain 且不新增 cadence;最多 64 页的单周期 cancel supervisor;显式 start/stop、无重叠、错误隔离、停止等待有上限且 timer unref 的 lifecycle runner;Linux durable handle 的 PID/boot/start ticks/process-group 复验与 TERM/KILL controller;完整有界分页且 fail-closed 的 startup Reconcile supervisor;RunningInstance nullable `run_id/attempt_id` 关联;Primary 专用组合 Repository 在同一 SQLite 事务提交前投影 Crontab/RunningInstance,失败整体回滚;有界且防穿越的 legacy log output ref;manual owner seam、真实本机装配、单 spawn/fail-closed;严格 manual-only rollout manifest loader、短期审批/gate、配置哈希审计;HTTP worker 已接轻量 lazy bootstrap,accepted 后按 receipt-first reconcile→completion receipt lifecycle→timeout intent lifecycle→cancel dispatch lifecycle→router→durable active receipt 顺序激活,失败撤销,监听失败和 shutdown 以 stopping→有界清理→stopped/failed 失效;receipt 固定为单文件 observed-state projection,Linux 以 boot/PID/process-group/start ticks 复验,独立 auditor 支持 active 且 off/rolled-back 拒绝 live runtime;Primary timeout 在 spawn 前持久化绝对 deadline,有界 source/requester/supervisor 只提交 timeout 意图并复用 CancellationDispatch;代码级 edge/standalone Profile 为各 lifecycle 提供不同 cadence 与页上限,cluster-control/worker 拒绝误装本机 SQLite Primary;统一 CompletionService 原子提交 Attempt/Run/双 Event,spawn 前保存 callback token hash、终态推进 sequence,实时回调与 receipt consumer 共享入口并覆盖两个清理 crash window;manual Primary 已接入受限 POSIX launcher、`0600` direct-file stdout/stderr、父进程退出后续写、不可覆盖 receipt 生产、回执环境清除、TERM 转发等待及 live transaction 后清理;Startup Reconciler receipt-first 双检查并在确定 exited 后执行 profile 化的单次 50/100 ms publish grace;`0007` 独立 CompletionReceiptJournal 在 spawn 前登记、为升级前 active Attempt 补登记并驱动周期扫描,使终态残留继续可发现;确定无效的已知 Attempt receipt 先持久化隔离状态,再进入确定性私有分片 quarantine;终态 missing 与 quarantine 按 edge/standalone retention 有界清理;非 Journal 文件具备只读优先、固定分片/条目上限、overflow fail-closed、显式同盘隔离的 Node 24 运维 CLI;扫描具备页上限、resume cursor、timer unref、无重叠、有界 stop 和低敏计数;ENOSPC 与 launcher receipt 存储失败有代码门禁;显式最长 24 小时 approve 写入、`primary_selected` 只读状态、selection receipt、approval-expiry off 与 intent/completion crash-replay rollback | 首次真实目标实例完整激活/回滚仪式;用户可见的 Cluster cancellation availability/blocked 处置面;固定 edge/Linux 多架构与真实磁盘压力基线、完整 2.x API 契约和回滚演练;共享 config 多写者 authority | +| PR-5 Primary LocalExecutor | Incubating(默认不激活,仅 manifest-gated manual) | runtime-owned Run 创建器;持久化先于 spawn;Run/Attempt 完整成功、失败、取消、超时与 lost 闭环;Executor handle 身份校验;spawn 后激活写失败的 stop+lost 补偿;completion rejection 安全收敛;独立 Primary 幂等查询与唯一索引竞态裁决;durable `run.cancel_requested`、stop-before-signal、首次请求幂等、晚到完成裁决与待取消有界恢复查询;最多 64 条一页的 cross-worker cancellation source;独立 CancellationDispatch Repository 原子 claim/result、lease expiry 接管、owner/token/version fencing、指数退避与结果 RunEvent;PostgreSQL `pg-0066`/capability v65 adapter 以数据库时间、Run→Attempt→dispatch 锁序、digest-only durable token、最小 runtime 权限和原子 RunEvent/Run version CAS 提供多副本同构实现,真实双连接与 HA promotion 门已通过;caller-driven Worker lease-control 已以 settle-before-stop 接入 Cluster 生产组合,复用 ingress drain 且不新增 cadence;最多 64 页的单周期 cancel supervisor;显式 start/stop、无重叠、错误隔离、停止等待有上限且 timer unref 的 lifecycle runner;Linux durable handle 的 PID/boot/start ticks/process-group 复验与 TERM/KILL controller;完整有界分页且 fail-closed 的 startup Reconcile supervisor;RunningInstance nullable `run_id/attempt_id` 关联;Primary 专用组合 Repository 在同一 SQLite 事务提交前投影 Crontab/RunningInstance,失败整体回滚;有界且防穿越的 legacy log output ref;manual owner seam、真实本机装配、单 spawn/fail-closed;严格 manual-only rollout manifest loader、短期审批/gate、配置哈希审计;HTTP worker 已接轻量 lazy bootstrap,accepted 后按 receipt-first reconcile→completion receipt lifecycle→timeout intent lifecycle→cancel dispatch lifecycle→router→durable active receipt 顺序激活,失败撤销,监听失败和 shutdown 以 stopping→有界清理→stopped/failed 失效;receipt 固定为单文件 observed-state projection,Linux 以 boot/PID/process-group/start ticks 复验,独立 auditor 支持 active 且 off/rolled-back 拒绝 live runtime;Primary timeout 在 spawn 前持久化绝对 deadline,有界 source/requester/supervisor 只提交 timeout 意图并复用 CancellationDispatch;代码级 edge/standalone Profile 为各 lifecycle 提供不同 cadence 与页上限,cluster-control/worker 拒绝误装本机 SQLite Primary;统一 CompletionService 原子提交 Attempt/Run/双 Event,spawn 前保存 callback token hash、终态推进 sequence,实时回调与 receipt consumer 共享入口并覆盖两个清理 crash window;manual Primary 已接入受限 POSIX launcher、`0600` direct-file stdout/stderr、父进程退出后续写、不可覆盖 receipt 生产、回执环境清除、TERM 转发等待及 live transaction 后清理;Startup Reconciler receipt-first 双检查并在确定 exited 后执行 profile 化的单次 50/100 ms publish grace;`0007` 独立 CompletionReceiptJournal 在 spawn 前登记、为升级前 active Attempt 补登记并驱动周期扫描,使终态残留继续可发现;确定无效的已知 Attempt receipt 先持久化隔离状态,再进入确定性私有分片 quarantine;终态 missing 与 quarantine 按 edge/standalone retention 有界清理;非 Journal 文件具备只读优先、固定分片/条目上限、overflow fail-closed、显式同盘隔离的 Node 24 运维 CLI;扫描具备页上限、resume cursor、timer unref、无重叠、有界 stop 和低敏计数;ENOSPC 与 launcher receipt 存储失败有代码门禁;显式最长 24 小时 approve 写入、`primary_selected` 只读状态、selection receipt、approval-expiry off 与 intent/completion crash-replay rollback;用户可见的 Cluster cancellation availability/blocked 只读面已由 D-366~D-369 完成 | 首次真实目标实例完整激活/回滚仪式;mutation 处置继续使用独立 exact-CAS CLI;固定 edge/Linux 多架构与真实磁盘压力基线、完整 2.x API 契约和回滚演练;共享 config 多写者 authority | | PR-7 Worker Session、Run Lease 与启动协议基础 | Incubating(默认关闭,独立入口显式 opt-in) | ADR-0012/0013/0014/0021/0057–0061/0108–0121/0231–0239/0377;有界 capability/Placement/Dispatcher;SQLite 协议孵化与 PostgreSQL v9 Session/Run Lease/credential/attestation authority;immutable revision Placement、数据库时钟 keyset candidate、认证 Worker Pull、digest-only offer recovery;versioned capability-free ExecutionSpec response、stable claim 跨重启退避、单 owner 原子 inbox 准入与 TLS 1.3 mTLS/`ql3w` HTTPS client;同一 package journal 上 revision-fenced starting/spawn/started/running/completion 状态、callback digest、tagged no-spawn 与 ambiguous recovery;PostgreSQL starting/running/start-failure/completion 数据库权威事务、精确重放与 cancellation/timeout 优先终态;batch Secret delivery 在 Attempt advisory lock 下复验 Session/Lease/revision 完整围栏并复用单 Agent,Secret-before-Artifact materializer 将同一 log ID 交给 Executor/journal/running ACK;offer-scoped `wlog-*` 私有文件 spool、Edge/Node 容量策略、append/quota/path 防护、barrier 后 output ownership、受审 POSIX Executor、truncation fact、固定内存流式 source、认证 Artifact stream、共享 immutable store port、S3-compatible SSE/checksum/条件 promotion adapter、upload-before-completion 协调,以及 Local/Cluster 同构、Profile-aware、ETag-fenced range read;用户取消 run.stop mutation 以数据库时间写 intent/Event 并在事务内复验 Project/RoleBinding fence;非执行取消 convergence lifecycle、运行期 expiry 与安全 lost retry 已接入 cluster-control 单一全局 cadence;完整 generation/version/token/Attempt fencing;独立最小权限 Worker ingress、CA/CRL 与连接 generation 热重载;offer journal、spawn barrier、receipt-first recovery;独立 `@qinglong/worker-runtime` 的本地 P-256 CSR、key/chain/trust 验证、generation + active pointer 安装和持久退避;默认关闭的 production process 已装配具体 execution graph、完整 Session heartbeat/drain/offline、direct-file bootstrap、单 Agent/单 cadence、startup reconciliation、证书 maintenance、transport fail-close/recovery 与 Edge/Node 有界预算;真实 PostgreSQL 18 + Linux Node 合约已覆盖 Run completion、credential 和 CA 双轮换且保持同一 Session;真实 K3s 合约已覆盖 TLS/credential Secret 分权、双对象 CAS、Recreate 顺序、identity generation 与单节点 PVC recovery;所有能力默认不可达且受 edge/cluster import audit 约束 | 具体 cert-manager/Vault/SPIFFE/离线 CA adapter 与模板、ingress reload controller、生产 RBAC、证书到期告警和 `ql3w` credential recovery 产品面;具体 KMS/Vault Secret provider、对象存储 credential/temporary lifecycle 与 retention/tombstone;Worker 管理 API;真实 Kubernetes 多节点 CSI/node-loss/production 360 秒 drain 与固定 edge 文件系统 suspend/时钟/断电、x64/arm64 资源门禁 | | PR-8 Project/Policy/Approval Core | Incubating(默认拒绝、无生产业务执行入口) | ADR-0028;统一六类 ActorRef 与 exact-shape 校验;`0017` ownerless default Project 和 append-only versioned RoleBinding;owner/admin/operator/viewer 固定矩阵;Project 内 mutation 幂等、expected-version CAS、双 SQLite 连接竞争门禁;archived read-only、revocation、存储损坏 fail-closed;Agent 写/Secret/Tool `require_approval`;ADR-0047 把六类 subject、role/permission matrix 与 fence 抽到 runtime-core,`pg-0004-project-policy`/capability v3 建立 ownerless PostgreSQL baseline、严格 role/state CHECK、append-only runtime 权限、SERIALIZABLE Project lock、mutation replay、双连接单 winner 和 cluster admission authorizer;ADR-0049/`pg-0005` capability v4 建立 stable IdentitySubject、append-only digest-only API credential、真实 cluster bearer authenticator、write-only durable security audit 与最小权限 runtime role,且已验证 HTTP→credential→Policy→audit→handler 纵向链路;ADR-0051 建立 `/api/v3` 认证前 peer/global 双预算、transport-peer-only、无 timer 且有界内存的 overload shield;ADR-0027 Artifact authorizer adapter;ADR-0029 `AuthenticatedPrincipal` contract、`0018` digest-only versioned challenge、CSPRNG/TTL、同事务消费 challenge + 写首 owner、精确重放与双连接竞争/崩溃回滚门禁;ADR-0030 `0019` stable identity/binding、legacy HS384 + current-session membership、logout/platform/revoke/disable、single-factor 与损坏 fail-closed 门禁;ADR-0031 `0020` digest-bound ApprovalRequest、User-only decision、Project/Role version fence、精确 expiry/重放/并发裁决及同事务 immutable dispatch;ADR-0032 `0021` execution backfill、三表原子 consume、稳定 due keyset、claim/renew/start/result fencing、pre-start takeover/post-start recovery-required、attempt budget、handler inspect/digest barrier 和 bounded dispatcher;ADR-0033/`0022` control/resolution backfill、start/renew/completion 原子联动、稳定 recovery keyset、双 resolver claim/takeover、finding/result 精确重放、自动/人工终结、迟到 completion 单 winner 和 evidence-only bounded reconciler;ADR-0034/`0023` 首个 `run.create` canonical plan、Run/Attempt/Event/receipt 同事务、幂等 collision fail-closed、renew/终态 fence、真实 SQLite handler 与 automatic evidence provider;ADR-0035/`0024` 独立 `approval.recover` 矩阵、稳定 User + 五分钟强认证、Project/RoleBinding fence、human resolution + authorization fact 原子提交、撤权竞态与回滚门禁;ADR-0036 recovery-first 单 timer lifecycle、edge/standalone 独立 cadence/页预算、跨周期 cursor、非重叠与有界 stop;ADR-0074 以新的 Node 24 SQLite v5 ownerless Project/RoleBinding/audit authority 和独立 local-secret-admin 提供强 Principal、`secret.manage`、撤权 fence、envelope+allowed audit 原子提交及不回显语义;ADR-0086 以可信 POSIX console 和 staged delivery 完成本机首 Owner 产品 ceremony | fresh database/pepper setup 与安全迁移向导;`shareStore`/Express 到 authentication core 的 production migration;credential rotation/revocation API、mTLS/Worker enrollment、恢复码;Project/Role/Approval/Secret 管理 CLI/API/UI、audit retention/query/export/alert、preview Artifact/digest/immutable plan builder、真实 MFA/hardware adapter、人工 recovery API/UI/独立 rate limit 与审计事件、handler/provider registry、lifecycle startup/shutdown/指标/admission gate;PostgreSQL action/receipt/provider/recovery-authorization 与 OPA adapter、缓存 version 失效;Tool/Package/Secret/Shell 各自的 handler/evidence contract;Secret/Run/Tool/Workflow waiting_approval 全入口装配;完整回滚演练 | diff --git a/docs/adr/ADR-0462-optional-console-run-management-drilldown.md b/docs/adr/ADR-0462-optional-console-run-management-drilldown.md new file mode 100644 index 00000000..74a8ba43 --- /dev/null +++ b/docs/adr/ADR-0462-optional-console-run-management-drilldown.md @@ -0,0 +1,69 @@ +# ADR-0462:Copilot Console 显式可选 Run Management Drill-down + +- 状态:Accepted +- 日期:2026-08-20 +- 关联 RFC:QL-RFC-0001 D-369、PR-5、PR-7 +- 关联 ADR:ADR-0322、ADR-0323、ADR-0329、ADR-0330、ADR-0458、ADR-0459、ADR-0460、ADR-0461 +- Amends:ADR-0461 的 Console 可选接入边界 + +## 上下文 + +QingLong 3.0 已有一次性 `ql3 run status`、固定 16 项 blocked page 和单 Run cancellation inspect,但 operator 在图形 Console 中仍要切换 CLI 才能完成 `status → blocked → inspect`。既有 Console 只持有 Project API credential;直接把 Run management mTLS/OIDC authority 设为默认,会让每次普通 Run/Task/Workflow/Copilot 观察都无条件携带更高价值的 User assertion,也会扩大 operator workstation、容器 launcher 和低资源部署的默认攻击面。 + +Console 仍必须是按需启动的 Cluster operator 工具,而不是 QingLong 常驻服务。Edge/Standalone 和小型路由设备不能为该能力增加 package、依赖、进程、端口、timer、连接、schema 或安装字节;Cluster 节点也不能把一次人工诊断变成轮询、自动翻页或批量 N+1。 + +## 决策 + +1. 在既有 `@qinglong/cluster-admin` Copilot Console 增加 `run_cancellation_status`、`run_cancellation_blocked_list`、`run_cancellation_inspect` 三个固定只读操作。不新增 workspace package、binary、服务、Ingress、Kubernetes workload、数据库 migration、连接池、timer、queue 或 cache。 +2. Run management authority 默认 `disabled`。只有启动参数同时提供 `--run-management-config` 与 `--run-management-assertion` 时才启用;缺一项在监听或网络 I/O 前失败。配置必须是独立的 Run 专用 TLS 1.3/mTLS 文件,assertion 必须是 canonical、owner-private `0600` JWT 文件。两者不复用或替代 Project API credential 与浏览器 session key。 +3. preflight 只在本机验证 Run config、client certificate/private key 和 assertion 格式,不消耗 assertion,也不冒充 authorization 或 management endpoint readiness。serve 时每次用户点击重新读取 assertion 文件,因此短期凭据可以原位轮换或删除;路径、JWT、证书和 endpoint 不进入 stdout、浏览器或失败事实。 +4. 浏览器只提交精确 schema、Project、请求 ID、可选 opaque cursor 或单一 Run ID;BFF 只接受三个固定同源 POST route,并在服务端构造既有规范 Run management command。浏览器不能提交 URL、HTTP method、limit、排序、filter、audit ID、mutation ID、expected version、retry delay 或任意 management operation。 +5. 导航完全由用户触发:status 返回 `attention_required/inspect` 时才显示“读取 Blocked Runs”;blocked page 中每个 Run 提供独立 inspect 点击;下一页也必须显式点击。没有启动时读取、后台轮询、自动翻页、自动逐 Run inspect、隐藏重试或浏览器缓存。 +6. Console 永远不路由 `run.cancellation.rearm`、`run.stop`、`run.retry` 或其他 mutation。即使 operator 给了更宽的 User assertion,浏览器 vocabulary、request normalizer、route table 与 executor 分支仍无法表达 mutation;处置继续使用独立 CLI 的 exact-CAS rearm。 +7. status、blocked 和 inspect 复用既有 Run management result validator,再投影为固定产品事实。blocked cursor 在网络请求中保持版本化 canonical token;写入浏览器脱敏 evidence bundle 时只变成 per-bundle `cursor-NNN` alias,不泄露原 continuation。离线 verifier 同步验证 16 种固定 read operation 和相同 alias/allowlist。 +8. native 模式继续只监听 ephemeral `127.0.0.1`。镜像 launcher 继续以 host-loopback publication、只读 root、非 root UID、无 capability、no-new-privileges、固定 memory/CPU/PID 和两并发/无队列运行;只有显式 `QL3_COPILOT_CONSOLE_RUN_MANAGEMENT=enabled` 才把容器私有只读 mount 中的 Run config/assertion 路径传给进程,未知值失败关闭。 +9. Console 资产仍由包内 SHA-256 精确绑定,CSP、Host/Origin/session、4 KiB request、约 2 MiB response、2 in-flight、16 connection 与 no-store 边界不变。新增 UI 不引入 framework、第三方依赖、browser storage、worker、WebSocket、EventSource、clipboard/share API 或 timer。 +10. 实现保留在已有 `cluster-admin/copilot-console` 与 `cluster-admin/run-management` 子域。新增 inspection projection 是现有 package 内的实质职责文件,不是新 package 或根目录平铺;workspace 仍维持 18 个职责包,Edge/Standalone artifact closure 不得导入 `cluster-admin`。 + +## 被拒绝的替代方案 + +### 默认把 Run management assertion 塞进 Console + +拒绝。普通观察不需要 strong User authority;默认持有会扩大凭据暴露时长和部署准备成本,也让“只读 Project Console”与“高价值管理身份”无法独立撤销。 + +### 让浏览器直接访问 management endpoint + +拒绝。浏览器将获得 assertion、mTLS capability 或可变 endpoint,并需要扩大 CSP/CORS;这破坏 server-only credential、固定 route 和同源 session fence。 + +### status 后自动获取 blocked page 并逐项 inspect + +拒绝。一次点击会变成隐藏的多请求 fan-out,成本随 blocked 数增长;并发状态变化也会让自动链难以审计。每一步显式点击让请求次数、快照 cursor 和 operator 意图可见。 + +### 在只读 Console 中增加 rearm 按钮 + +拒绝。rearm 需要 `run.stop`、expected dispatch version/result 和明确 mutation ceremony。把它放进当前 evidence ledger 会混淆观察与处置,并让浏览器页面获得 mutation authority。 + +### 新建 Console Run-management package 或常驻 sidecar + +拒绝。能力只组合现有 client、command codec 和 projection,不存在独立依赖、发布责任或生命周期;拆包会重新制造单职责过细的 workspace。常驻 sidecar则会增加 Cluster 资源和凭据驻留时间。 + +## 资源、安全与部署影响 + +- 默认关闭与 Edge/Standalone 路径均为零新增运行时 I/O、连接、timer、listener、内存状态、安装依赖和数据库成本。 +- 显式启用只增加本机已存在 Console 进程中的常数级配置状态;每次点击仍是一条短 TLS 请求,最多两个并发且不排队。blocked page 固定 16 项,不自动读取下一页或详情。 +- compact/standard 容器预算保持 `192 MiB / 0.25 CPU / 32 PIDs` 与 `512 MiB / 1 CPU / 64 PIDs`;本切片不据此宣称生产容量,Linux x64/arm64 实测仍是独立发布证据。 +- assertion 是短期强 User 凭据,只在 BFF 进程按调用读取并由通用 management client 清零 buffer;浏览器 session 不能直接认证 Cluster,Project credential 也不能替代 Run authority。 +- 本切片不改 PostgreSQL schema、repository、Run service、management server 或 HA 拓扑,因此不重新占有 PostgreSQL HA 证明;D-368 的 v67 `146/146` 只作为相邻既有证据,不冒充 D-369 新执行结果。 + +## 验证 + +- 包级类型构建和聚焦门 `46/46`,覆盖默认关闭、参数成对约束、私有配置预检、三条固定 route、非法 cursor、无任意 path、浏览器 session/Host/Origin、并发上限、脱敏 cursor alias、16-operation 离线复核、launcher opt-in 和 mutation vocabulary 拒绝。 +- 真实 loopback/TLS 纵切从浏览器风格 status POST 经 Console BFF、独立 Run mTLS client 到 management endpoint,验证只发送一条 `run.cancellation.summary`、请求 ID 绑定、Bearer assertion、低敏 status projection 和无 mutation。 +- Cluster Admin 全量为 `420 total / 417 pass / 3 conditional skip / 0 fail`;backend 宿主门为 `1,491 total / 1,489 pass / 2 conditional skip / 0 fail`;18-package clean build/顺序测试退出 0。 +- package/dependency/Edge import/Cluster deployment/Console/Console distribution 六项审计全部 compatible、零 finding;workspace 保持 18 package,Cluster Admin 为 `125 source / 124 nested` 且只有一个受审根入口。 +- 14 档 Local artifact audit 全部 compatible;Edge/Standalone 为 `2,589,998 / 2,590,076` bytes,Application+AI 为 `4,493,151 / 4,493,283` bytes,MCP 为 `7,315,930 / 7,316,038` bytes。 +- 本切片未改数据库或 HA ownership,未重跑 PostgreSQL HA;D-368 的 `146/146` 仅作为相邻基线。 + +## 后续 + +D-370 应优先取得固定 Linux x64/arm64 Cluster Admin Console 容量与 assertion rotation/expiry 现场证据,或继续推进 CloudNativePG live failover 发布门;不得把 workstation compact/standard limit 冒充生产容量,也不得为了监控便利引入 Console polling、常驻 authority 或 Edge 依赖。 diff --git a/packages/ql3-cluster-admin/assets/copilot-console/app.css b/packages/ql3-cluster-admin/assets/copilot-console/app.css index 595e40a4..ffbbf26b 100644 --- a/packages/ql3-cluster-admin/assets/copilot-console/app.css +++ b/packages/ql3-cluster-admin/assets/copilot-console/app.css @@ -234,7 +234,7 @@ h3 { } .mode-tabs { display: grid; - grid-template-columns: repeat(3, 1fr); + grid-template-columns: repeat(4, 1fr); margin: 1.8rem 0 1.2rem; border-bottom: 1px solid var(--line); } diff --git a/packages/ql3-cluster-admin/assets/copilot-console/app.js b/packages/ql3-cluster-admin/assets/copilot-console/app.js index e9d3cf2f..491a7f4c 100644 --- a/packages/ql3-cluster-admin/assets/copilot-console/app.js +++ b/packages/ql3-cluster-admin/assets/copilot-console/app.js @@ -5,6 +5,11 @@ const routes = Object.freeze({ inspect: '/api/v1/copilot/inspect', output: '/api/v1/copilot/output', + run_cancellation_status: '/api/v1/run-management/cancellation-status', + run_cancellation_blocked_list: + '/api/v1/run-management/blocked-cancellations', + run_cancellation_inspect: + '/api/v1/run-management/cancellation-inspect', run_list: '/api/v1/observe/run-list', run_read: '/api/v1/observe/run', run_event_list: '/api/v1/observe/run-events', @@ -20,6 +25,9 @@ const labels = Object.freeze({ inspect: 'Copilot 诊断状态', output: 'Copilot 诊断内容', + run_cancellation_status: '取消可用性', + run_cancellation_blocked_list: 'Blocked Cancellations', + run_cancellation_inspect: '取消诊断', run_list: 'Run 目录', run_read: 'Run 详情', run_event_list: 'Run Events', @@ -110,6 +118,12 @@ if (operation === 'inspect' || operation === 'output') { result.sourceRunId = value('source-run-id'); result.requestId = value('diagnosis-request-id'); + } else if (operation === 'run_cancellation_status') { + return result; + } else if (operation === 'run_cancellation_blocked_list') { + result.cursor = null; + } else if (operation === 'run_cancellation_inspect') { + result.runId = value('cancellation-run-id'); } else if (operation === 'run_list') { result.afterCreatedAtMs = null; result.afterRunId = null; @@ -156,7 +170,13 @@ const nextPage = function (operation, prior, fact) { const next = Object.assign({}, prior, { requestId: requestId() }); - if (operation === 'run_list' && fact.hasMore === true && fact.next) { + if ( + operation === 'run_cancellation_blocked_list' && + fact.truncated === true && + typeof fact.nextCursor === 'string' + ) { + next.cursor = fact.nextCursor; + } else if (operation === 'run_list' && fact.hasMore === true && fact.next) { next.afterCreatedAtMs = fact.next.createdAtMs; next.afterRunId = fact.next.runId; } else if ( @@ -200,6 +220,39 @@ return next; }; + const appendDrilldownControls = function (entry, operation, fact) { + if ( + operation === 'run_cancellation_status' && + fact.operatorAction === 'inspect' + ) { + const button = document.createElement('button'); + button.type = 'button'; + button.textContent = '显式读取 Blocked Runs'; + button.addEventListener('click', function () { + void execute('run_cancellation_blocked_list'); + }); + entry.append(button); + return; + } + if ( + operation !== 'run_cancellation_blocked_list' || + !Array.isArray(fact.items) + ) { + return; + } + fact.items.forEach(function (item) { + if (!item || typeof item.runId !== 'string') return; + const button = document.createElement('button'); + button.type = 'button'; + button.textContent = '显式检查 ' + item.runId; + button.addEventListener('click', function () { + document.getElementById('cancellation-run-id').value = item.runId; + void execute('run_cancellation_inspect'); + }); + entry.append(button); + }); + }; + const appendEvidence = function (operation, request, response) { const fact = response.result.result; const observedAtMs = Date.now(); @@ -235,6 +288,7 @@ }); entry.append(button); } + appendDrilldownControls(entry, operation, fact); ledger.prepend(entry); evidenceRecords.push({ record: record, bytes: recordBytes, entry: entry }); evidenceBytes += recordBytes; diff --git a/packages/ql3-cluster-admin/assets/copilot-console/evidence-bundle.js b/packages/ql3-cluster-admin/assets/copilot-console/evidence-bundle.js index 2fb175c3..ddad720e 100644 --- a/packages/ql3-cluster-admin/assets/copilot-console/evidence-bundle.js +++ b/packages/ql3-cluster-admin/assets/copilot-console/evidence-bundle.js @@ -28,6 +28,9 @@ const operations = Object.freeze([ 'inspect', 'output', + 'run_cancellation_status', + 'run_cancellation_blocked_list', + 'run_cancellation_inspect', 'run_list', 'run_read', 'run_event_list', @@ -44,6 +47,9 @@ const requestFields = Object.freeze({ inspect: ['projectId', 'requestId', 'sourceRunId'], output: ['projectId', 'requestId', 'sourceRunId'], + run_cancellation_status: ['projectId', 'requestId'], + run_cancellation_blocked_list: ['cursor', 'projectId', 'requestId'], + run_cancellation_inspect: ['projectId', 'requestId', 'runId'], run_list: [ 'afterCreatedAtMs', 'afterRunId', @@ -112,7 +118,9 @@ afterStepRunId: 'step', afterTaskId: 'task', artifactId: 'artifact', + attemptId: 'attempt', contentDigest: 'digest', + cursor: 'cursor', diagnosisRunId: 'run', executionId: 'execution', id: 'identifier', @@ -133,7 +141,10 @@ }); const safeContainers = new Set([ 'attempts', + 'blockingResults', 'counts', + 'dispatch', + 'dispatches', 'events', 'items', 'metadata', @@ -142,6 +153,7 @@ 'run', 'runs', 'source', + 'signals', 'step', 'steps', 'summary', @@ -168,9 +180,15 @@ ]); const safeEnumKeys = new Set([ 'finishReason', + 'assessment', + 'cancelReason', 'kind', + 'lastResult', 'operation', + 'operatorAction', 'outcome', + 'runStatus', + 'severity', 'stage', 'status', ]); @@ -178,11 +196,15 @@ 'accepted', 'active', 'admission', + 'attention_required', 'available', 'blocked', 'cancelled', 'completed', 'completion', + 'converging', + 'critical', + 'clear', 'dispatch', 'dispatching', 'disabled', @@ -191,12 +213,18 @@ 'failed', 'finalization', 'installed', + 'inspect', + 'invalid', + 'identity_mismatch', 'local', 'lost', 'missing', 'model', + 'none', 'not_found', 'pending', + 'pid_mismatch', + 'policy', 'post_model', 'pre_model', 'prompt', @@ -204,6 +232,8 @@ 'queued', 'ready', 'recovery', + 'rearm', + 'reconcile', 'rejected', 'remote', 'retained', @@ -217,13 +247,20 @@ 'step', 'stop', 'succeeded', + 'shutdown', 'system', 'task', 'terminal', 'timed_out', + 'timeout', 'tool', 'trigger', 'unknown', + 'unsupported', + 'user', + 'wait', + 'warning', + 'ok', 'unavailable', 'workflow', ]); @@ -232,7 +269,7 @@ const freeTextKey = /text|content|stdout|stderr|command|input|output|environment|reason|error|message|description|name|path|url|uri|host|endpoint/iu; const numericKey = - /^(?:schemaVersion|version|revision|sequence|attempt|priority|limit|offset|size|total|count|[A-Za-z0-9_]*(?:AtMs|TimeMs|DurationMs|Bytes|Tokens|Micros|Sequence|Version|Count|Limit|Offset|Size|Total))$/u; + /^(?:schemaVersion|version|revision|sequence|attempt|priority|limit|offset|size|total|count|exitCode|pending|leased|retryWait|dispatched|blocked|due|expiredLease|identityMismatch|pidMismatch|unsupported|invalid|[A-Za-z0-9_]*(?:AtMs|TimeMs|DurationMs|Bytes|Tokens|Micros|Sequence|Version|Count|Limit|Offset|Size|Total))$/u; const schemaValue = /^[a-z0-9][a-z0-9./_-]{0,126}@[a-z0-9._-]{1,16}$/u; class ClusterConsoleEvidenceBundleError extends TypeError { diff --git a/packages/ql3-cluster-admin/assets/copilot-console/index.html b/packages/ql3-cluster-admin/assets/copilot-console/index.html index 8ee24a23..8d3c0d6c 100644 --- a/packages/ql3-cluster-admin/assets/copilot-console/index.html +++ b/packages/ql3-cluster-admin/assets/copilot-console/index.html @@ -24,7 +24,7 @@
本机只读 BFF - Run · Task · Workflow · Copilot + Run · Task · Workflow · Copilot · Optional management
@@ -51,6 +51,7 @@ @@ -79,6 +80,25 @@ + +