diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3bb223c..da5b6c7 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -43,7 +43,9 @@ jobs:
- name: Validate compose
run: docker compose config --quiet
- name: Validate offsite backup authentication and round trip
- run: bash deploy/server/test-sync-backup-offsite.sh
+ run: |
+ bash deploy/server/test-sync-backup-offsite.sh
+ python3 -B deploy/server/test_public_listener_policy.py
- name: Resolve immutable build identity
run: |
openapi_version="$(awk '/^ version:/{print $2; exit}' openapi/openapi.yaml)"
diff --git a/.gitignore b/.gitignore
index 28d01b8..f6db02a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,6 +2,8 @@
.idea/
.vscode/
.DS_Store
+__pycache__/
+*.py[cod]
*.log
coverage.out
coverage*.out
diff --git a/deploy/server/README.md b/deploy/server/README.md
index 60c7a07..d30b6b9 100644
--- a/deploy/server/README.md
+++ b/deploy/server/README.md
@@ -54,7 +54,7 @@ sudo systemctl show platform-health-monitor.service -p Result -p ExecMainStatus
python3 -m json.tool /var/lib/data-terminal-cloud-monitor/status.json
```
-监测任务每五分钟执行,正常状态为`healthy`。默认只记录结构化状态和systemd日志;在`/etc/data-terminal-cloud/monitor.env`配置`ALERT_WEBHOOK_URL`后,状态变化会发送带时间戳和HMAC-SHA256签名的JSON通知。完整检查项、签名规则、阈值和失败分支见[《单机云端健康监测与告警手册》](../../docs/server-health-monitoring.md)。
+监测任务每五分钟执行,正常状态为`healthy`。它还要求宿主机所有非回环TCP监听只能使用22、80和443,避免安全组误放行与错误容器端口映射叠加后暴露数据库或缓存。默认只记录结构化状态和systemd日志;在`/etc/data-terminal-cloud/monitor.env`配置`ALERT_WEBHOOK_URL`后,状态变化会发送带时间戳和HMAC-SHA256签名的JSON通知。完整检查项、签名规则、阈值和失败分支见[《单机云端健康监测与告警手册》](../../docs/server-health-monitoring.md)。
## OSS异地备份适配
diff --git a/deploy/server/health-monitor.sh b/deploy/server/health-monitor.sh
index 7f8d24a..e899a07 100755
--- a/deploy/server/health-monitor.sh
+++ b/deploy/server/health-monitor.sh
@@ -20,6 +20,7 @@ alert_webhook_url=${ALERT_WEBHOOK_URL:-}
alert_webhook_secret_file=${ALERT_WEBHOOK_SECRET_FILE:-/etc/data-terminal-cloud/monitor-webhook-secret}
platform_health_report_url=${PLATFORM_HEALTH_REPORT_URL:-}
platform_health_report_secret_file=${PLATFORM_HEALTH_REPORT_SECRET_FILE:-/etc/data-terminal-cloud/platform-health-callback-secret}
+expected_public_tcp_ports=${EXPECTED_PUBLIC_TCP_PORTS:-22,80,443}
if [[ -f "$offsite_config_file" ]]; then
# shellcheck disable=SC1090
source "$offsite_config_file"
@@ -36,6 +37,10 @@ if (( disk_warning_percent >= disk_critical_percent || disk_critical_percent > 1
echo "Disk thresholds are invalid" >&2
exit 2
fi
+if [[ ! "$expected_public_tcp_ports" =~ ^[0-9]+(,[0-9]+)*$ ]]; then
+ echo "EXPECTED_PUBLIC_TCP_PORTS must contain comma-separated integers" >&2
+ exit 2
+fi
compose=(docker compose -f "$project_root/compose.yaml" -f "$project_root/deploy/server/compose.override.yaml" --env-file "$project_root/deploy/server/.env")
mkdir -p "$state_dir"
@@ -81,6 +86,20 @@ else
record_check public_https critical request_failed
fi
+if ! command -v ss >/dev/null 2>&1; then
+ record_check public_tcp_listeners critical probe_unavailable
+else
+ listener_evidence=$(ss -H -ltn | python3 "$project_root/deploy/server/public_listener_policy.py" "$expected_public_tcp_ports" 2>/dev/null || true)
+ IFS='|' read -r listener_status listener_detail <<<"$listener_evidence"
+ if [[ "$listener_status" == healthy ]]; then
+ record_check public_tcp_listeners healthy "$listener_detail"
+ elif [[ "$listener_status" == critical ]]; then
+ record_check public_tcp_listeners critical "$listener_detail"
+ else
+ record_check public_tcp_listeners critical invalid_probe_result
+ fi
+fi
+
expected_issuer="$public_origin/realms/voice-local"
if curl --fail --silent --show-error --max-time 10 \
"$expected_issuer/.well-known/openid-configuration" -o "$oidc_tmp"; then
diff --git a/deploy/server/public_listener_policy.py b/deploy/server/public_listener_policy.py
new file mode 100644
index 0000000..12a3fba
--- /dev/null
+++ b/deploy/server/public_listener_policy.py
@@ -0,0 +1,70 @@
+#!/usr/bin/env python3
+import ipaddress
+import re
+import sys
+
+
+def parse_endpoint(value: str) -> tuple[str, int] | None:
+ value = value.strip()
+ if value.startswith("[") and "]:" in value:
+ address, port_text = value[1:].rsplit("]:", 1)
+ elif ":" in value:
+ address, port_text = value.rsplit(":", 1)
+ else:
+ return None
+ if not port_text.isdigit():
+ return None
+ return address, int(port_text)
+
+
+def externally_reachable(address: str) -> bool:
+ if address in {"*", "0.0.0.0", "::", "[::]"}:
+ return True
+ try:
+ return not ipaddress.ip_address(address.split("%", 1)[0]).is_loopback
+ except ValueError:
+ return True
+
+
+def public_ports(lines: list[str]) -> set[int]:
+ ports: set[int] = set()
+ for line in lines:
+ fields = line.split()
+ if len(fields) < 4:
+ continue
+ endpoint = parse_endpoint(fields[3])
+ if endpoint is not None and externally_reachable(endpoint[0]):
+ ports.add(endpoint[1])
+ return ports
+
+
+def parse_allowed(value: str) -> set[int]:
+ if not re.fullmatch(r"[0-9]+(?:,[0-9]+)*", value):
+ raise ValueError("allowed ports must be comma-separated integers")
+ ports = {int(item) for item in value.split(",")}
+ if any(port < 1 or port > 65535 for port in ports):
+ raise ValueError("allowed port is outside 1..65535")
+ return ports
+
+
+def main() -> int:
+ if len(sys.argv) != 2:
+ print("usage: public_listener_policy.py ALLOWED_PORTS", file=sys.stderr)
+ return 2
+ try:
+ allowed = parse_allowed(sys.argv[1])
+ except ValueError as error:
+ print(str(error), file=sys.stderr)
+ return 2
+ exposed = public_ports(list(sys.stdin))
+ unexpected = sorted(exposed - allowed)
+ if unexpected:
+ print("critical|unexpected_ports=" + ",".join(map(str, unexpected)))
+ else:
+ detail = ",".join(map(str, sorted(exposed))) or "none"
+ print("healthy|ports=" + detail)
+ return 0
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/deploy/server/test_public_listener_policy.py b/deploy/server/test_public_listener_policy.py
new file mode 100644
index 0000000..df3b838
--- /dev/null
+++ b/deploy/server/test_public_listener_policy.py
@@ -0,0 +1,54 @@
+#!/usr/bin/env python3
+import importlib.util
+import pathlib
+import subprocess
+import sys
+import unittest
+
+
+module_path = pathlib.Path(__file__).with_name("public_listener_policy.py")
+spec = importlib.util.spec_from_file_location("public_listener_policy", module_path)
+policy = importlib.util.module_from_spec(spec)
+assert spec.loader is not None
+spec.loader.exec_module(policy)
+
+
+class PublicListenerPolicyTest(unittest.TestCase):
+ def test_allows_expected_public_ports_and_ignores_loopback(self) -> None:
+ lines = [
+ "LISTEN 0 4096 0.0.0.0:443 0.0.0.0:*",
+ "LISTEN 0 4096 [::]:80 [::]:*",
+ "LISTEN 0 128 *:22 *:*",
+ "LISTEN 0 128 127.0.0.1:5432 0.0.0.0:*",
+ "LISTEN 0 128 [::1]:6379 [::]:*",
+ ]
+ self.assertEqual(policy.public_ports(lines), {22, 80, 443})
+
+ def test_reports_wildcard_and_interface_bound_database_ports(self) -> None:
+ lines = [
+ "LISTEN 0 4096 0.0.0.0:3306 0.0.0.0:*",
+ "LISTEN 0 4096 172.16.0.8:5432 0.0.0.0:*",
+ "LISTEN 0 4096 [::]:6379 [::]:*",
+ ]
+ self.assertEqual(policy.public_ports(lines) - {22, 80, 443}, {3306, 5432, 6379})
+
+ def test_validates_allowed_port_range(self) -> None:
+ self.assertEqual(policy.parse_allowed("22,80,443"), {22, 80, 443})
+ with self.assertRaises(ValueError):
+ policy.parse_allowed("22,0,443")
+ with self.assertRaises(ValueError):
+ policy.parse_allowed("22, 443")
+
+ def test_cli_marks_unexpected_port_critical(self) -> None:
+ result = subprocess.run(
+ [sys.executable, str(module_path), "22,80,443"],
+ input="LISTEN 0 4096 0.0.0.0:5432 0.0.0.0:*\n",
+ text=True,
+ capture_output=True,
+ check=True,
+ )
+ self.assertEqual(result.stdout.strip(), "critical|unexpected_ports=5432")
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/docs/development-status.md b/docs/development-status.md
index e1a817f..7757e12 100644
--- a/docs/development-status.md
+++ b/docs/development-status.md
@@ -181,7 +181,7 @@
| 隐私删除并发 | 100个并发受理全部成功、HTTP失败率0%、提交p95 385.15ms;11.58秒内600个子任务、100个墓碑和100个主体冻结全部对账 |
| PostgreSQL隔离恢复 | 实际`pg_dump`/`pg_restore`到随机隔离库,迁移`22|false`、66张表、190个外键和关键计数一致;本地约5.14秒,临时库/备份已清理 |
| 阿里云单机加密备份与恢复 | 目标服务器已安装每日systemd备份任务,覆盖PostgreSQL、认证Redis RDB、MinIO全部桶和暂停后的Keycloak数据目录;归档使用AES-256-CBC/PBKDF2加密、逐文件SHA-256并保留14天。首次实际归档成功,数据库从V38备份恢复到随机隔离库后`38|false|2|3|73|215`六项证据一致,归档内Redis RDB格式有效、Keycloak 3个文件一致、临时恢复库为0;同机备份尚不作为跨故障域生产恢复证据 |
-| 阿里云单机健康监测 | 目标服务器已安装五分钟systemd健康检查,结构化状态覆盖HTTPS、OIDC签发地址、11个常驻容器、本机/异地备份状态与摘要、备份执行结果、三个运行timer、短期IP证书剩余时间和磁盘阈值。异地适配安装后的实际正常路径22项全部healthy;把本机备份最大年龄临时设为0秒后仅`backup_freshness`进入critical并退出1,测试目录已清理且线上容器未停止。可选Webhook仅在状态变化时发送带时间戳和HMAC-SHA256的脱敏状态;真实接收器尚未配置,因此不作为值班通知送达证据 |
+| 阿里云单机健康监测 | 目标服务器已安装五分钟systemd健康检查,结构化状态覆盖HTTPS、宿主机公网监听端口、OIDC签发地址、11个常驻容器、本机/异地备份状态与摘要、备份执行结果、三个运行timer、短期IP证书剩余时间和磁盘阈值。监听策略只允许非回环TCP 22/80/443,回环数据库端口不误报,额外端口会进入critical。实际正常路径23项全部healthy;把本机备份最大年龄临时设为0秒后仅`backup_freshness`进入critical并退出1。可选Webhook仅在状态变化时发送带时间戳和HMAC-SHA256的脱敏状态;真实接收器尚未配置,因此不作为值班通知送达证据 |
| 阿里云OSS异地备份适配 | 目标服务器已按官方SHA-256安装ossutil 2.3.0,新增每日OSS同步任务、加密归档及摘要上传、远端回读复算、原子状态和健康强制门禁。ECS正式路径现使用IMDSv2和`EcsRamRole`自动短期凭据,不保存长期AK;隔离配置模式验证上传/回读2个文件并得到`verified`,目标实例缺角色时退出4并记录`failed/ecs_ram_role_missing`。2026-08-26实测地域`cn-hangzhou`且角色元数据404;当前仍无RAM角色和Bucket,正式任务保持`disabled/not_enabled`且健康状态为`not_required`,不能作为真实异地备份证据 |
| 进程与Redis恢复 | API/Callbacks/Worker重启恢复;停机期间Outbox仅执行一次;Redis 14个热状态键清空后由签名设备请求重建,PostgreSQL权威计数不变 |
| 供应商故障恢复 | V23持久化按租户/供应商的可用状态;连续临时失败暂停新启动但停止任务继续尝试;演练确认暂停后新外呼0、停止尝试2次、历史接口200、CRITICAL告警出现,恢复后全部收敛;V23单版本回退/重升通过 |
@@ -250,7 +250,7 @@
| 真实Agent生成、Embedding和内容安全质量门禁 | 新增`real-agent-models-capture-v1`、`real-agent-models-conformance-report-v1`和`cmd/agent-models-conformance`。内容生成复用现有固定10类Agent评测,并使用评测集、脱敏观测和报告重新计算防篡改;Embedding按版本化阈值覆盖精确/改写召回、跨长者隔离、撤销/敏感排除和无匹配降级六类;内容安全覆盖验证码、金融账户、提示注入、敏感医疗、正常内容和模型输出六类。真实证据还必须绑定受控提供器、账号/区域、数据条款、额度和审计引用;本地生成器、哈希向量和规则固定为`LOCAL_AGENT_TOOLCHAIN/external_validation_required=true`。v3证据包新增`real_agent_models_validation`,该门禁不可豁免。按低强度策略执行聚合器/证据包定向测试和一条重算现有模拟报告的本地工具链冒烟:既有10类模拟报告重新计算一致,两组六类结构完整,输出`PASS/LOCAL_AGENT_TOOLCHAIN`并继续外部待验;证据包保留真实报告、三提供器和12类别共16项缺口,临时文件已清理。 |
| 托管内容安全外部预检与告警 | OpenAPI 0.64.0把`content_safety_tls`纳入外部依赖查询和候选验收快照。生产Worker仅执行DNS、TCP和受信TLS握手,不发送待分类正文;结果复用依赖可用性指标、数据库脱敏状态和运营诊断页面,连续5分钟不可用进入统一CRITICAL告警,记录缺失/失败/过期均可阻断候选技术快照但不驱逐Pod。按低强度策略执行App/探针/诊断定向测试、前端类型生成/类型检查/生产构建和Prometheus规则校验。 |
| 云端主机健康状态接入 | OpenAPI 0.65.0新增独立HMAC签名的主机健康回调、30天脱敏快照表和运营只读状态页面。五分钟周期上报覆盖公网入口、容器、备份、证书、磁盘和定时器;无签名、过期、重复检查及综合状态不一致报告被拒绝,页面超过15分钟未更新明确标记过期。该能力不替代整机、Callbacks或数据库失联时必须使用的外部探针。 |
-| 云端主机健康发布门禁 | OpenAPI 0.66.0把最新主机健康快照纳入发布候选技术验收:无上报、超过15分钟、综合预警或严重异常均阻断,只有当前有效且22项检查全部正常才通过。快照仅归档状态、时间和检查数量,不复制磁盘、证书或备份明细值。 |
+| 云端主机健康发布门禁 | OpenAPI 0.66.0把最新主机健康快照纳入发布候选技术验收:无上报、超过15分钟、综合预警或严重异常均阻断,只有当前有效且23项检查全部正常才通过,其中包括宿主机公网监听端口策略。快照仅归档状态、时间和检查数量,不复制磁盘、证书、监听地址或备份明细值。 |
| 阶段8外部执行轻量预检 | 新增`scripts/stage8-preflight.ps1`,以单一只读入口核对候选Git提交、远程仓库、Compose本地基线、外部配置变量名称和十类证据索引结构/状态;报告明确不读取密钥值,也不把环境变量或本地服务可用性当作验收证据。默认仅报告缺口,`-Strict`可供自动化阻断未就绪交接;当前已建立本地`main`候选提交,本地运行基线就绪且索引结构有效,仍缺远程仓库,样例构建摘要为占位值,十类外部证据均为`PENDING`。 |
| 候选构建身份贯通 | Compose新增统一候选构建参数,后端二进制和后端/Web镜像OCI标签共享版本、完整Git提交及UTC构建时间;远程CI从OpenAPI版本和运行编号形成候选版本,构建后重新读取两个镜像标签并与`GITHUB_SHA`和构建时间逐项比对,再把候选版本、提交、时间和双镜像摘要写入构建元数据。修复了此前Dockerfile虽支持注入但Compose/CI未传参、运行版本仍为`dev/unknown`的问题。 |
diff --git a/docs/server-health-monitoring.md b/docs/server-health-monitoring.md
index 9470cdd..673568f 100644
--- a/docs/server-health-monitoring.md
+++ b/docs/server-health-monitoring.md
@@ -2,7 +2,7 @@
## 1. 目标与边界
-`deploy/server/health-monitor.sh`为单机阿里云演示环境提供五分钟一次的轻量健康检查。它用于发现“公网入口不可用、身份签发地址漂移、容器退出、备份过期或损坏、证书接近到期、定时任务停止、磁盘空间不足”等直接影响可用性的问题。
+`deploy/server/health-monitor.sh`为单机阿里云演示环境提供五分钟一次的轻量健康检查。它用于发现“公网入口不可用、宿主机出现未授权公网监听端口、身份签发地址漂移、容器退出、备份过期或损坏、证书接近到期、定时任务停止、磁盘空间不足”等直接影响可用性或暴露面的事件。
该脚本不替代Prometheus、Grafana、云监控或真实值班系统。默认只写结构化状态文件和systemd日志;配置外部接收器后,状态发生变化时才发送一次HMAC签名Webhook,避免每五分钟重复通知。
@@ -12,6 +12,7 @@
|---|---|---|
| 公网地址 | `PUBLIC_ORIGIN`为HTTPS | `invalid_https_origin` |
| HTTPS入口 | `/api/v1/version`在10秒内返回成功 | `request_failed` |
+| 公网监听端口 | 宿主机非回环TCP监听端口只能属于`22,80,443`;回环地址上的数据库或监控端口不计入公网暴露 | `unexpected_ports=...`、`probe_unavailable`或`invalid_probe_result` |
| OIDC | Discovery中的`issuer`与公网地址完全一致 | `issuer_mismatch`或`request_failed` |
| 容器 | PostgreSQL、Redis、MinIO、Keycloak、API、Callbacks、Worker、Web、Gateway和两个模拟服务均为运行态;有健康检查的容器必须为healthy | `missing`、容器状态或健康状态 |
| 备份 | 最新加密归档不超过36小时且外层SHA-256有效 | `missing`、`age_seconds=...`或`checksum_failed` |
@@ -89,11 +90,12 @@ PLATFORM_HEALTH_REPORT_SECRET_FILE=/etc/data-terminal-cloud/platform-health-call
| `CERTIFICATE_MIN_SECONDS` | `129600` | 证书最小剩余时间,36小时 |
| `DISK_WARNING_PERCENT` | `80` | 磁盘警告阈值 |
| `DISK_CRITICAL_PERCENT` | `90` | 磁盘严重阈值 |
+| `EXPECTED_PUBLIC_TCP_PORTS` | `22,80,443` | 宿主机允许绑定非回环地址的TCP端口,逗号分隔 |
| `MONITOR_STATE_DIR` | `/var/lib/data-terminal-cloud-monitor` | 原子状态文件目录 |
| `MONITOR_CONFIG_FILE` | `/etc/data-terminal-cloud/monitor.env` | 可选通知配置 |
| `TLS_CERTIFICATE_FILE` | 根据`PUBLIC_ORIGIN`主机名推导 | 当前活动证书文件 |
-阈值只能使用非负整数,磁盘警告阈值必须小于严重阈值。普通调整无需重建镜像,但应记录变更理由并重新执行一次手工检查。
+阈值只能使用非负整数,磁盘警告阈值必须小于严重阈值。端口必须位于1—65535;修改允许端口前必须同步复核阿里云安全组,不能用扩大允许列表掩盖错误的数据库或缓存端口映射。普通调整无需重建镜像,但应记录变更理由并重新执行一次手工检查。
## 7. 验收与排障
@@ -114,6 +116,12 @@ MONITOR_STATE_DIR="$test_dir" MONITOR_CONFIG_FILE=/dev/null \
期望退出1,状态为critical,唯一人工注入的严重检查为`backup_freshness`。测试目录验证后应精确删除。
+监听策略解析器的正常、回环忽略和异常端口分支可独立验证:
+
+```bash
+python3 -B deploy/server/test_public_listener_policy.py
+```
+
常用排障命令:
```bash
diff --git a/web/src/pages/OpsDiagnosticsWorkspace.tsx b/web/src/pages/OpsDiagnosticsWorkspace.tsx
index 8722266..1ddf643 100644
--- a/web/src/pages/OpsDiagnosticsWorkspace.tsx
+++ b/web/src/pages/OpsDiagnosticsWorkspace.tsx
@@ -101,7 +101,7 @@ export function OpsDiagnosticsWorkspace({ accessToken, accounts }: { accessToken
{canOperate? RELEASE CANDIDATE EVIDENCE 一次冻结候选构建、迁移、必需依赖、联调问题、可靠任务和严重告警;输出仅是技术预检证据,不代替发布批准。 {formatTimestamp(acceptanceResult.generated_at)} · 快照 {acceptanceResult.id} 当前没有阻断发布的HIGH或CRITICAL告警。 阻断告警超过100条,下载文件仅包含最早100条;必须在告警工作台查看完整队列。 {acceptanceResult.disclaimer} PILOT DAILY OPERATIONS 按业务时区自然日汇总设备、通话、供应商、上传、告警和费用;下载内容不含字幕、录音、个人资料和供应商任务编号。 {pilotDailyResult.timezone} · {formatTimestamp(pilotDailyResult.generated_at)} · 日报 {pilotDailyResult.id} {pilotDailyResult.providers.length?pilotDailyResult.providers.map((item)=>`${item.provider}:${item.current_status},失败通知${item.failed_events_during_day},失败通话${item.failed_calls_during_day},积压${item.notification_backlog}`).join(";"):"所选日期和当前状态中没有供应商记录。"} {pilotDailyResult.costs.length?pilotDailyResult.costs.map((item)=>`${item.currency} ${(item.cost_micros/1_000_000).toFixed(6)}(${item.line_count}条)`).join(";"):"所选自然日没有费用明细。"} {pilotDailyResult.disclaimer} PILOT WEEKLY REVIEW 按周一开始的业务自然周汇总内容审核、记忆、隐私请求、固件和配置差异;投诉仍需附客服或CRM权威证据。 {pilotWeeklyResult.timezone} · {formatTimestamp(pilotWeeklyResult.generated_at)} · 周报 {pilotWeeklyResult.id} 当前索引待处理 {pilotWeeklyResult.content_review.current_memory_index_pending},失败 {pilotWeeklyResult.content_review.current_memory_index_failed}。 {pilotWeeklyResult.firmware_versions.length?pilotWeeklyResult.firmware_versions.map((item)=>`${item.model}/${item.firmware_version}:${item.device_count}台`).join(";"):"当前没有可用且已绑定设备。"} 外部证据仍需补充:投诉 {pilotWeeklyResult.disclaimer} SERVER HEALTH 主机每五分钟签名上报公网入口、容器、备份、证书、磁盘和定时任务的脱敏结果。 {platformHealthValueLabel(item.name,item.value)} 最近上报已超过15分钟,不能继续视为当前状态;请检查健康监测定时器、回调进程和签名配置。 尚未收到主机健康快照;完成监测回调配置后,首个五分钟周期会自动出现。 SERVER HEALTH 主机每五分钟签名上报公网入口、监听端口、容器、备份、证书、磁盘和定时任务的脱敏结果。 {platformHealthValueLabel(item.name,item.value)} 最近上报已超过15分钟,不能继续视为当前状态;请检查健康监测定时器、回调进程和签名配置。 尚未收到主机健康快照;完成监测回调配置后,首个五分钟周期会自动出现。 EXTERNAL PREFLIGHT API与Worker周期执行不消耗模型或语音额度的连通性检查,仅保存脱敏结果。 该进程已超过三倍探针周期未更新,不能视为当前可用。 归一化错误:{item.last_error_code} 当前进程尚未写入外部探针结果;本地未启用的方舟或RTC依赖不会伪装为已验收。发布候选验收快照
最近生成:{acceptanceResult.candidate_version}
阻断告警定位
{acceptanceResult.alerts.blocking_items.map((alert)=>试点运行日报
最近生成:{pilotDailyResult.local_date}
试点运行周报
最近生成:{pilotWeeklyResult.week_start} 起始周
{item.reason}云端主机运行状态
云端主机运行状态
外部依赖预检
{state.stale?