Files
getDomain/domain-api/deploy/multi-region/drive_ops_center.sh
2026-04-18 23:52:51 +08:00

7777 lines
321 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck disable=SC1091
source "${SCRIPT_DIR}/lib_ops_center.sh"
ops_center_load_config "${SCRIPT_DIR}"
OPS_CENTER_CURL_CONNECT_TIMEOUT="${OPS_CENTER_CURL_CONNECT_TIMEOUT:-3}"
OPS_CENTER_CURL_MAX_TIME="${OPS_CENTER_CURL_MAX_TIME:-20}"
OPS_CENTER_CURL_MAX_TIME_CODEX_BRIEF="${OPS_CENTER_CURL_MAX_TIME_CODEX_BRIEF:-45}"
OPS_CENTER_REPORT_COMMAND_TIMEOUT_SECONDS="${OPS_CENTER_REPORT_COMMAND_TIMEOUT_SECONDS:-25}"
OPS_CENTER_DOCTOR_REPORT_TIMEOUT_SECONDS="${OPS_CENTER_DOCTOR_REPORT_TIMEOUT_SECONDS:-${OPS_CENTER_REPORT_COMMAND_TIMEOUT_SECONDS}}"
usage() {
cat <<'EOF'
usage:
bash domain-api/deploy/multi-region/drive_ops_center.sh <command> [args...]
commands:
help
config
contracts [mainland_base_url] [contract_key]
env-audit [mainland_base_url]
runtime-refresh-check [mainland_base_url]
runtime-refresh-recover [mainland_base_url]
stack-diagnosis [mainland_base_url] [summary|full]
stack-check [mainland_base_url] [summary|full] (backward-compatible alias of stack-diagnosis)
go-live-check [mainland_base_url] [overseas_base_url] [summary|full]
go-live-recover [mainland_base_url] [overseas_base_url]
go-live-export [report_dir] [mainland_base_url] [overseas_base_url]
go-live-review [manifest.json|report_dir]
go-live-signoff [manifest.json|report_dir|mainland_base_url] [overseas_base_url]
stack-next [mainland_base_url] [preview|run] [confirm] [requested_by]
release-package [skip-smoke]
release-verify [archive_path] [hash_path]
release-prepare
release-show
release-launchpad [mainland_base_url] [channel]
release-preview [mainland_base_url]
release-preview-smart [mainland_base_url] <worker|control>
release-import-latest [mainland_base_url]
publish-latest [mainland_base_url] <worker|control> [confirm] [skip-smoke]
doctor-export [report_dir] [mainland_base_url] [overseas_base_url]
doctor-decision [manifest_path|report_dir|mainland_base_url] [overseas_base_url]
doctor [mainland_base_url] [overseas_base_url]
ops-plane [mainland_base_url]
inspection [mainland_base_url]
participation [mainland_base_url]
topology [mainland_base_url] [overseas_base_url]
nodes [mainland_base_url]
stack-first-gap-handover [mainland_base_url]
node-bind-ssh [mainland_base_url] <node_code> <ssh_host> <ssh_user> [ssh_port] [title]
node-handover [mainland_base_url] <node_code>
node-onboarding [mainland_base_url] <node_code>
node-recover [mainland_base_url] <node_code> [confirm] [requested_by]
node-bootstrap-preview [mainland_base_url] <node_code> [requested_by]
node-bootstrap-run [mainland_base_url] <node_code> [requested_by]
node-acceptance-plan [mainland_base_url] <node_code> [requested_by]
node-acceptance-run [mainland_base_url] <node_code> [requested_by]
scene-node-log [mainland_base_url] <node_code> [limit] [key|full]
log-sync-check [mainland_base_url]
log-sync-recover [mainland_base_url] [key|full] [confirm] [requested_by]
agent-gap-check [mainland_base_url]
agent-gap-recover [mainland_base_url] [confirm] [requested_by]
agent-gap-export [report_dir] [mainland_base_url] [requested_by]
node-bootstrap-plan [mainland_base_url] <node_code> [issued_by] [expires_in_hours] [control_plane_base_url] [root_dir]
queue-status [mainland_base_url] <node_code>
queue-records [mainland_base_url] <node_code> [state]
queue-flush [mainland_base_url] <node_code> [limit] [requested_by]
queue-replay [mainland_base_url] <node_code> [limit] [requested_by]
queue-replay-record [mainland_base_url] <node_code> <record_id> [requested_by]
queue-discard-record [mainland_base_url] <node_code> <record_id> <reason> [requested_by]
driver-feed [mainland_base_url]
activity-stream [mainland_base_url] [key=value ...]
activity-preview [mainland_base_url] [activity_key] [requested_by]
driver-focus-preview [mainland_base_url] [entry_key] [requested_by]
driver-focus-run [mainland_base_url] [entry_key] [primary|secondary] [confirm] [requested_by]
codex-brief [mainland_base_url]
codex-focus-preview [mainland_base_url] [entry_key] [requested_by]
codex-focus-run [mainland_base_url] [entry_key] [confirm] [requested_by]
release-hub [mainland_base_url] <release-hub-command> [args...]
driver-preview [mainland_base_url] <action_code> [action_payload_json|@payload.json] [requested_by]
driver-resolve [mainland_base_url] <action_code> [action_payload_json|@payload.json] [requested_by]
driver-run [mainland_base_url] <action_code> [action_payload_json|@payload.json] [confirm] [requested_by]
driver [mainland_base_url] <action_code> [action_payload_json|@payload.json] [requested_by]
runbook-preview [mainland_base_url] <sequence_key> [action_payload_json|@payload.json] [requested_by]
runbook-run [mainland_base_url] <sequence_key> [primary|secondary] [action_payload_json|@payload.json] [confirm] [requested_by]
runbook-resolve [mainland_base_url] <sequence_key> [primary|secondary] [action_payload_json|@payload.json] [requested_by]
runbook-sequence [mainland_base_url] <sequence_key> [primary|secondary] [action_payload_json|@payload.json] [requested_by]
smart-latest [mainland_base_url] <worker|control> [confirm]
smart-release [mainland_base_url] <release_id> <worker|control> [confirm]
agent-plan [mainland_base_url] <node_code> [node_region] [node_role] [control_plane_base_url] [root_dir]
agent-plan-export [report_dir] [mainland_base_url] <node_code> [node_region] [node_role] [control_plane_base_url] [root_dir]
agent-check [service_name] [env_file]
examples:
bash domain-api/deploy/multi-region/drive_ops_center.sh config
bash domain-api/deploy/multi-region/drive_ops_center.sh contracts
bash domain-api/deploy/multi-region/drive_ops_center.sh env-audit
bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-check
bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover
bash domain-api/deploy/multi-region/drive_ops_center.sh stack-diagnosis
bash domain-api/deploy/multi-region/drive_ops_center.sh contracts ops_stack_diagnosis_contract
bash domain-api/deploy/multi-region/drive_ops_center.sh contracts http://127.0.0.1:8100 release_hub_contract
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-recover
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-check
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-export
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-review
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-signoff
bash domain-api/deploy/multi-region/drive_ops_center.sh go-live-signoff /path/to/go-live-bundle
bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next
bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next run confirm
bash domain-api/deploy/multi-region/drive_ops_center.sh release-package
bash domain-api/deploy/multi-region/drive_ops_center.sh release-show
bash domain-api/deploy/multi-region/drive_ops_center.sh release-launchpad
bash domain-api/deploy/multi-region/drive_ops_center.sh release-preview
bash domain-api/deploy/multi-region/drive_ops_center.sh release-preview-smart worker
bash domain-api/deploy/multi-region/drive_ops_center.sh release-import-latest
bash domain-api/deploy/multi-region/drive_ops_center.sh publish-latest worker
bash domain-api/deploy/multi-region/drive_ops_center.sh doctor-export
bash domain-api/deploy/multi-region/drive_ops_center.sh doctor-decision
bash domain-api/deploy/multi-region/drive_ops_center.sh doctor http://127.0.0.1:8100 http://152.53.37.118:8100
bash domain-api/deploy/multi-region/drive_ops_center.sh nodes
bash domain-api/deploy/multi-region/drive_ops_center.sh stack-first-gap-handover
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bind-ssh mainland-worker-01 121.204.244.248 root
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bind-ssh http://127.0.0.1:8100 mainland-controller-01 121.204.244.188 root 22 mainland-controller
bash domain-api/deploy/multi-region/drive_ops_center.sh node-handover mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-onboarding mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-recover mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-recover mainland-worker-01 confirm ops-center
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-preview mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-run mainland-worker-01 ops-center
bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-plan mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-run mainland-worker-01 ops-center
bash domain-api/deploy/multi-region/drive_ops_center.sh scene-node-log mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh scene-node-log http://127.0.0.1:8100 mainland-worker-01 120 full
bash domain-api/deploy/multi-region/drive_ops_center.sh log-sync-check
bash domain-api/deploy/multi-region/drive_ops_center.sh log-sync-recover
bash domain-api/deploy/multi-region/drive_ops_center.sh log-sync-recover http://127.0.0.1:8100 full confirm cli/ops
bash domain-api/deploy/multi-region/drive_ops_center.sh agent-gap-check
bash domain-api/deploy/multi-region/drive_ops_center.sh agent-gap-recover
bash domain-api/deploy/multi-region/drive_ops_center.sh agent-gap-export
bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-plan mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh queue-status mainland-worker-01
bash domain-api/deploy/multi-region/drive_ops_center.sh queue-records mainland-worker-01 dead_letter
bash domain-api/deploy/multi-region/drive_ops_center.sh queue-flush mainland-worker-01 20
bash domain-api/deploy/multi-region/drive_ops_center.sh queue-replay mainland-worker-01 20
bash domain-api/deploy/multi-region/drive_ops_center.sh queue-replay-record mainland-worker-01 event-ops-123-2
bash domain-api/deploy/multi-region/drive_ops_center.sh queue-discard-record mainland-worker-01 event-ops-123-2 "确认已人工归档"
bash domain-api/deploy/multi-region/drive_ops_center.sh driver-feed
bash domain-api/deploy/multi-region/drive_ops_center.sh activity-stream
bash domain-api/deploy/multi-region/drive_ops_center.sh activity-stream kind=ops_job status=running
bash domain-api/deploy/multi-region/drive_ops_center.sh activity-preview
bash domain-api/deploy/multi-region/drive_ops_center.sh driver-focus-preview
bash domain-api/deploy/multi-region/drive_ops_center.sh driver-focus-run
bash domain-api/deploy/multi-region/drive_ops_center.sh codex-brief
bash domain-api/deploy/multi-region/drive_ops_center.sh codex-focus-preview
bash domain-api/deploy/multi-region/drive_ops_center.sh codex-focus-run
bash domain-api/deploy/multi-region/drive_ops_center.sh release-hub http://127.0.0.1:8100 latest-package
bash domain-api/deploy/multi-region/drive_ops_center.sh smart-release http://127.0.0.1:8100 12 worker
bash domain-api/deploy/multi-region/drive_ops_center.sh driver-preview http://127.0.0.1:8100 open_release_dialog
bash domain-api/deploy/multi-region/drive_ops_center.sh driver-resolve http://127.0.0.1:8100 enable_log_sync_key
bash domain-api/deploy/multi-region/drive_ops_center.sh driver-run http://127.0.0.1:8100 enable_log_sync_key
bash domain-api/deploy/multi-region/drive_ops_center.sh driver http://127.0.0.1:8100 open_release_dialog
bash domain-api/deploy/multi-region/drive_ops_center.sh runbook-preview release_progression
bash domain-api/deploy/multi-region/drive_ops_center.sh runbook-run release_progression
bash domain-api/deploy/multi-region/drive_ops_center.sh runbook-resolve release_progression
bash domain-api/deploy/multi-region/drive_ops_center.sh runbook-sequence release_progression
design:
- this is the single overseas control-plane entrypoint
- it does not execute remote shell directly
- it first delegates to contract preview APIs, then to ops/release execute APIs
EOF
}
COMMAND="${1:-help}"
shift || true
run_step() {
local title="$1"
shift
echo "============================================================"
echo "${title}"
echo "============================================================"
"$@"
echo
}
print_json_or_raw() {
local raw_text="${1:-}"
python3 - "${raw_text}" <<'PY'
import json
import sys
raw_text = str(sys.argv[1] or "")
try:
payload = json.loads(raw_text)
except Exception:
print(raw_text)
else:
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
}
request_with_status_custom() {
local method="$1"
local url="$2"
local body="${3:-}"
local connect_timeout="${4:-${OPS_CENTER_CURL_CONNECT_TIMEOUT}}"
local max_time="${5:-${OPS_CENTER_CURL_MAX_TIME}}"
if [[ "${method}" == "POST" ]]; then
curl -sS -X POST "${url}" \
--connect-timeout "${connect_timeout}" \
--max-time "${max_time}" \
-H 'Content-Type: application/json' \
-d "${body}" \
-w $'\nHTTP_STATUS=%{http_code}'
else
curl -sS \
--connect-timeout "${connect_timeout}" \
--max-time "${max_time}" \
"${url}" \
-w $'\nHTTP_STATUS=%{http_code}'
fi
}
request_with_status() {
local method="$1"
local url="$2"
local body="${3:-}"
request_with_status_custom "${method}" "${url}" "${body}" \
"${OPS_CENTER_CURL_CONNECT_TIMEOUT}" "${OPS_CENTER_CURL_MAX_TIME}"
}
print_config() {
local requested_profile="${1:-${OPS_CENTER_ACTIVE_PROFILE}}"
local saved_profile="${OPS_CENTER_ACTIVE_PROFILE}"
local saved_profile_key="${OPS_CENTER_ACTIVE_PROFILE_KEY}"
OPS_CENTER_ACTIVE_PROFILE="${requested_profile}"
OPS_CENTER_ACTIVE_PROFILE_KEY="$(ops_center_normalize_profile_key "${OPS_CENTER_ACTIVE_PROFILE}")"
python3 - <<'PY' "${OPS_CENTER_CONFIG_FILE}" \
"${OPS_CENTER_ACTIVE_PROFILE}" \
"${OPS_CENTER_PROFILE_NAMES}" \
"$(ops_center_resolve_mainland_api_base_url)" \
"$(ops_center_resolve_overseas_api_base_url)" \
"$(ops_center_resolve_control_plane_base_url)" \
"$(ops_center_resolve_requested_by)" \
"$(ops_center_resolve_issued_by)" \
"$(ops_center_resolve_expires_in_hours)" \
"$(ops_center_resolve_root_dir)" \
"$(ops_center_resolve_node_agent_service_name)" \
"$(ops_center_resolve_node_agent_env_file)" \
"${OPS_CENTER_REPORT_ROOT}"
import json
import sys
from pathlib import Path
config_path = Path(sys.argv[1])
print(json.dumps({
"config_file": str(config_path),
"config_exists": config_path.exists(),
"active_profile": sys.argv[2],
"available_profiles": [item.strip() for item in str(sys.argv[3]).split(",") if item.strip()],
"ops_mainland_api_base_url": sys.argv[4],
"ops_overseas_api_base_url": sys.argv[5],
"ops_default_control_plane_base_url": sys.argv[6],
"ops_default_requested_by": sys.argv[7],
"ops_default_issued_by": sys.argv[8],
"ops_default_expires_in_hours": int(sys.argv[9] or 72),
"ops_default_root_dir": sys.argv[10],
"ops_node_agent_service_name": sys.argv[11],
"ops_node_agent_env_file": sys.argv[12],
"ops_report_root": sys.argv[13],
}, ensure_ascii=False, indent=2))
PY
OPS_CENTER_ACTIVE_PROFILE="${saved_profile}"
OPS_CENTER_ACTIVE_PROFILE_KEY="${saved_profile_key}"
}
timestamp_now() {
date '+%Y%m%d_%H%M%S'
}
ops_center_repo_root() {
printf '%s' "${OPS_CENTER_PROJECT_ROOT}"
}
confirm_to_bool() {
local raw_value="${1:-}"
case "$(printf '%s' "${raw_value}" | tr '[:upper:]' '[:lower:]')" in
1|true|yes|y|confirm|confirmed|skip|skip-smoke)
printf 'true'
;;
*)
printf 'false'
;;
esac
}
is_confirm_token() {
local raw_value="${1:-}"
case "$(printf '%s' "${raw_value}" | tr '[:upper:]' '[:lower:]')" in
1|true|yes|y|confirm|confirmed)
return 0
;;
*)
return 1
;;
esac
}
is_secondary_token() {
local raw_value="${1:-}"
case "$(printf '%s' "${raw_value}" | tr '[:upper:]' '[:lower:]')" in
secondary|second|2|true|yes|y)
return 0
;;
*)
return 1
;;
esac
}
normalize_payload_json_input() {
local raw_value="${1:-}"
if [[ -z "${raw_value}" ]]; then
printf '{}'
return 0
fi
if [[ "${raw_value}" == @* ]]; then
local payload_path="${raw_value#@}"
if [[ ! -f "${payload_path}" ]]; then
echo "payload file not found: ${payload_path}" >&2
return 1
fi
cat "${payload_path}"
return 0
fi
printf '%s' "${raw_value}"
}
enrich_driver_action_payload_json() {
local mainland_base_url="$1"
local action_code="$2"
local payload_json="$3"
python3 - <<'PY' "${mainland_base_url}" "${action_code}" "${payload_json}"
import json
import sys
import urllib.request
def normalize_codes(*groups):
result = []
for group in groups:
for item in list(group or []):
node_code = str(item or "").strip()
if node_code and node_code not in result:
result.append(node_code)
return result
base_url = str(sys.argv[1] or "").rstrip("/")
action_code = str(sys.argv[2] or "").strip()
payload_raw = str(sys.argv[3] or "").strip() or "{}"
try:
payload = json.loads(payload_raw)
except Exception:
payload = {}
if not isinstance(payload, dict):
payload = {}
existing_node_codes = normalize_codes(payload.get("node_codes") or [])
if existing_node_codes:
print(json.dumps(payload, ensure_ascii=False))
raise SystemExit(0)
supported_actions = {
"open_worker_logs_participating",
"run_inspection_participating",
"open_worker_logs_standby",
"run_inspection_standby",
"open_worker_logs",
"run_standard_inspection",
}
if action_code not in supported_actions or not base_url:
print(json.dumps(payload, ensure_ascii=False))
raise SystemExit(0)
overview = {}
try:
with urllib.request.urlopen(f"{base_url}/api/v1/ops/overview", timeout=20) as response:
overview_payload = json.loads(response.read().decode("utf-8"))
overview = dict((overview_payload or {}).get("data") or {})
except Exception:
print(json.dumps(payload, ensure_ascii=False))
raise SystemExit(0)
execution_scene = dict(overview.get("execution_scene") or {})
participation_summary = dict(execution_scene.get("participation_summary") or {})
log_sync = dict(execution_scene.get("log_sync") or {})
dispatch_active = normalize_codes(participation_summary.get("dispatch_active_node_codes") or [])
recent_only = normalize_codes(participation_summary.get("recent_only_node_codes") or [])
standby = normalize_codes(
participation_summary.get("standby_node_codes") or [],
participation_summary.get("non_participating_node_codes") or [],
)
missing_logs = normalize_codes(log_sync.get("missing_participating_nodes") or [])
derived_node_codes = []
if action_code in {"open_worker_logs_participating", "run_inspection_participating"}:
derived_node_codes = normalize_codes(missing_logs, dispatch_active, recent_only)
elif action_code in {"open_worker_logs_standby", "run_inspection_standby"}:
derived_node_codes = normalize_codes(standby)
elif action_code == "open_worker_logs":
derived_node_codes = normalize_codes(missing_logs, dispatch_active, recent_only, standby)
elif action_code == "run_standard_inspection":
derived_node_codes = normalize_codes(dispatch_active, recent_only, standby)
if derived_node_codes:
payload["node_codes"] = derived_node_codes
payload.setdefault("_auto_context", {})
payload["_auto_context"]["resolved_by"] = "drive_ops_center"
payload["_auto_context"]["resolved_from"] = "ops.overview"
payload["_auto_context"]["action_code"] = action_code
print(json.dumps(payload, ensure_ascii=False))
PY
}
normalize_optional_base_url_arg() {
local raw_value="${1:-}"
local default_value="${2:-}"
case "$(printf '%s' "${raw_value}" | tr '[:upper:]' '[:lower:]')" in
"" )
printf '%s' "${default_value}"
;;
-|none|null|disabled|off)
printf ''
;;
*)
printf '%s' "${raw_value}"
;;
esac
}
write_json_file() {
local output_path="$1"
local json_payload="$2"
python3 - "${output_path}" "${json_payload}" <<'PY'
import json
import sys
from pathlib import Path
path = Path(sys.argv[1])
payload = json.loads(sys.argv[2])
path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
PY
}
run_and_capture_report() {
local title="$1"
local output_path="$2"
shift 2
{
echo "============================================================"
echo "${title}"
echo "============================================================"
"$@"
echo
} | tee "${output_path}"
}
capture_report_quiet() {
local title="$1"
local output_path="$2"
shift 2
local command_exit_code=0
echo "============================================================"
echo "${title}"
echo "============================================================"
{
echo "============================================================"
echo "${title}"
echo "============================================================"
set +e
timeout "${OPS_CENTER_DOCTOR_REPORT_TIMEOUT_SECONDS}" "$@"
command_exit_code=$?
set -e
echo
echo "command_exit_code=${command_exit_code}"
} >"${output_path}"
echo "saved_report=${output_path}"
echo "command_exit_code=${command_exit_code}"
echo
}
capture_report_optional() {
local title="$1"
local output_path="$2"
shift 2
local command_exit_code=0
echo "============================================================"
echo "${title}"
echo "============================================================"
{
echo "============================================================"
echo "${title}"
echo "============================================================"
set +e
timeout "${OPS_CENTER_DOCTOR_REPORT_TIMEOUT_SECONDS}" "$@"
command_exit_code=$?
set -e
echo
echo "command_exit_code=${command_exit_code}"
} >"${output_path}" 2>&1
echo "saved_report=${output_path}"
echo "command_exit_code=${command_exit_code}"
echo
}
run_doctor_report_export() {
local report_dir="$1"
local mainland_base_url="$2"
local overseas_base_url="$3"
ops_center_ensure_report_dir "${report_dir}"
local -a report_files=(
"00_ops_center_stack_summary.txt"
"01_ops_plane.txt"
"02_release_hub.txt"
"03_inspection.txt"
"04_participation.txt"
)
if [[ -n "${overseas_base_url}" ]]; then
report_files+=("05_topology.txt")
fi
report_files+=(
"06_contracts_registry.txt"
"07_driver_feed.txt"
"08_activity_stream.txt"
"09_codex_brief.txt"
"10_stack_next_preview.txt"
"11_driver_focus_preview.txt"
"12_activity_preview.txt"
"13_codex_focus_preview.txt"
"14_first_handover_gap.txt"
)
capture_report_quiet "[doctor/0] ops center stack" "${report_dir}/00_ops_center_stack_summary.txt" \
bash "${SCRIPT_DIR}/check_ops_center_stack.sh" "${mainland_base_url}" summary
capture_report_quiet "[doctor/1] ops plane" "${report_dir}/01_ops_plane.txt" \
bash "${SCRIPT_DIR}/check_ops_plane.sh" "${mainland_base_url}"
capture_report_quiet "[doctor/2] release hub" "${report_dir}/02_release_hub.txt" \
bash "${SCRIPT_DIR}/check_release_hub.sh" "${mainland_base_url}"
capture_report_quiet "[doctor/3] inspection" "${report_dir}/03_inspection.txt" \
bash "${SCRIPT_DIR}/check_ops_inspection.sh" "${mainland_base_url}"
capture_report_quiet "[doctor/4] participation" "${report_dir}/04_participation.txt" \
bash "${SCRIPT_DIR}/check_worker_participation.sh" "${mainland_base_url}"
if [[ -n "${overseas_base_url}" ]]; then
capture_report_quiet "[doctor/5] temp topology" "${report_dir}/05_topology.txt" \
bash "${SCRIPT_DIR}/check_temp_topology.sh" "${mainland_base_url}" "${overseas_base_url}"
fi
capture_report_quiet "[doctor/6] contracts registry" "${report_dir}/06_contracts_registry.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" contracts "${mainland_base_url}"
capture_report_quiet "[doctor/7] driver feed" "${report_dir}/07_driver_feed.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" driver-feed "${mainland_base_url}"
capture_report_quiet "[doctor/8] activity stream" "${report_dir}/08_activity_stream.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" activity-stream "${mainland_base_url}"
capture_report_quiet "[doctor/9] codex brief" "${report_dir}/09_codex_brief.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" codex-brief "${mainland_base_url}"
capture_report_optional "[doctor/10] stack next preview" "${report_dir}/10_stack_next_preview.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" stack-next "${mainland_base_url}" preview
capture_report_optional "[doctor/11] driver focus preview" "${report_dir}/11_driver_focus_preview.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" driver-focus-preview "${mainland_base_url}"
capture_report_optional "[doctor/12] activity preview" "${report_dir}/12_activity_preview.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" activity-preview "${mainland_base_url}"
capture_report_optional "[doctor/13] codex focus preview" "${report_dir}/13_codex_focus_preview.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" codex-focus-preview "${mainland_base_url}"
capture_report_optional "[doctor/14] first handover gap" "${report_dir}/14_first_handover_gap.txt" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" stack-first-gap-handover "${mainland_base_url}"
local scene_log_targets_json
scene_log_targets_json="$(python3 - <<'PY' "${report_dir}/00_ops_center_stack_summary.txt"
import json
import re
import sys
from pathlib import Path
target = Path(str(sys.argv[1] or "").strip())
if not target.exists():
print("[]")
raise SystemExit(0)
text = target.read_text(encoding="utf-8", errors="replace")
decoder = json.JSONDecoder()
best = None
for index, ch in enumerate(text):
if ch not in "{[":
continue
try:
value, end = decoder.raw_decode(text[index:])
except Exception:
continue
absolute_end = index + end
if best is None or absolute_end >= best[0]:
best = (absolute_end, value)
payload = best[1] if best else {}
diagnosis = dict((payload or {}).get("diagnosis") or {})
issues = list(diagnosis.get("issues") or [])
next_step = dict(diagnosis.get("next_step") or {})
operator_decision = dict(diagnosis.get("operator_decision") or {})
targets = []
seen = set()
def add_focus_ref(focus_ref):
if not isinstance(focus_ref, dict):
return
if str(focus_ref.get("kind") or "").strip() != "node_scene_log":
return
node_code = str(focus_ref.get("node_code") or "").strip()
if not node_code or node_code in seen:
return
seen.add(node_code)
mode = str(focus_ref.get("mode") or "key").strip().lower()
if mode not in {"key", "full"}:
mode = "key"
try:
limit = int(focus_ref.get("limit") or 80)
except Exception:
limit = 80
limit = max(20, min(limit, 300))
targets.append({
"node_code": node_code,
"mode": mode,
"limit": limit,
"source": str(focus_ref.get("source") or "").strip(),
})
for issue in issues:
add_focus_ref((issue or {}).get("focus_ref"))
add_focus_ref(next_step.get("focus_ref"))
add_focus_ref(operator_decision.get("next_focus"))
print(json.dumps(targets[:3], ensure_ascii=False))
PY
)"
while IFS=$'\t' read -r scene_node_code scene_mode scene_limit scene_source; do
[[ -z "${scene_node_code}" ]] && continue
local safe_node_code
safe_node_code="$(printf '%s' "${scene_node_code}" | tr -c 'A-Za-z0-9._-' '_')"
local scene_report_name="15_scene_node_log_${safe_node_code}.txt"
report_files+=("${scene_report_name}")
capture_report_optional "[doctor/15] scene node log ${scene_node_code}" "${report_dir}/${scene_report_name}" \
bash "${SCRIPT_DIR}/drive_ops_center.sh" scene-node-log "${mainland_base_url}" "${scene_node_code}" "${scene_limit:-80}" "${scene_mode:-key}"
done < <(
python3 - <<'PY' "${scene_log_targets_json}"
import json
import sys
targets = json.loads(str(sys.argv[1] or "[]"))
for item in targets:
print(
"\t".join(
[
str(item.get("node_code") or "").strip(),
str(item.get("mode") or "key").strip(),
str(item.get("limit") or 80).strip(),
str(item.get("source") or "").strip(),
]
)
)
PY
)
write_json_file "${report_dir}/meta.json" "$(python3 - \
"${OPS_CENTER_CONFIG_FILE}" \
"${OPS_CENTER_ACTIVE_PROFILE}" \
"${mainland_base_url}" \
"${overseas_base_url}" \
"${report_dir}" \
"${report_files[@]}" <<'PY'
import json
import sys
from datetime import datetime
files = list(sys.argv[6:])
print(json.dumps({
"generated_at": datetime.now().isoformat(timespec="seconds"),
"config_file": sys.argv[1],
"active_profile": sys.argv[2],
"mainland_api_base_url": sys.argv[3],
"overseas_api_base_url": sys.argv[4],
"report_dir": sys.argv[5],
"files": files,
}, ensure_ascii=False))
PY
)"
write_json_file "${report_dir}/manifest.json" "$(python3 - \
"${OPS_CENTER_CONFIG_FILE}" \
"${OPS_CENTER_ACTIVE_PROFILE}" \
"${mainland_base_url}" \
"${overseas_base_url}" \
"${report_dir}" \
"${report_files[@]}" <<'PY'
import json
import re
import sys
from datetime import datetime
from pathlib import Path
config_file = str(sys.argv[1] or "").strip()
active_profile = str(sys.argv[2] or "").strip()
mainland_api_base_url = str(sys.argv[3] or "").strip()
overseas_api_base_url = str(sys.argv[4] or "").strip()
report_dir = Path(str(sys.argv[5] or "").strip())
report_files = [str(item or "").strip() for item in sys.argv[6:] if str(item or "").strip()]
optional_preview_files = {
"10_stack_next_preview.txt",
"11_driver_focus_preview.txt",
"12_activity_preview.txt",
"13_codex_focus_preview.txt",
}
def extract_exit_code(text: str) -> int:
matches = re.findall(r"(?m)^command_exit_code=(\d+)\s*$", text)
if matches:
try:
return int(matches[-1])
except Exception:
return 1
return 0
def extract_last_json_value(text: str):
decoder = json.JSONDecoder()
best = None
for index, ch in enumerate(text):
if ch not in "{[":
continue
try:
value, end = decoder.raw_decode(text[index:])
except Exception:
continue
absolute_end = index + end
if best is None or absolute_end >= best[0]:
best = (absolute_end, value)
return None if best is None else best[1]
def last_nonempty_line(text: str) -> str:
for line in reversed(text.splitlines()):
stripped = line.strip()
if stripped:
return stripped[:300]
return ""
def collect_contract_keys(value, bucket: set) -> None:
if isinstance(value, dict):
for key, child in value.items():
if key in {"contract_key", "primary_contract_key"}:
contract_key = str(child or "").strip()
if contract_key:
bucket.add(contract_key)
continue
if key in {"contract_keys", "related_contract_keys"} and isinstance(child, list):
for item in child:
contract_key = str(item or "").strip()
if contract_key:
bucket.add(contract_key)
continue
collect_contract_keys(child, bucket)
elif isinstance(value, list):
for item in value:
collect_contract_keys(item, bucket)
def summarize_report(name: str, payload, exit_code: int, text: str) -> dict:
if not isinstance(payload, dict):
return {
"parsed_json": False,
"available": exit_code == 0,
"text_tail": last_nonempty_line(text),
}
if name == "00_ops_center_stack_summary.txt":
diagnosis = dict(payload.get("diagnosis") or {})
api = dict(payload.get("api") or {})
issues = list(diagnosis.get("issues") or payload.get("issues") or [])
next_step = dict(diagnosis.get("next_step") or payload.get("next_step") or {})
operator_decision = dict(diagnosis.get("operator_decision") or {})
next_actions = dict(diagnosis.get("next_actions") or {})
recommended_commands = dict(diagnosis.get("recommended_commands") or {})
stack_status = str(diagnosis.get("stack_status") or payload.get("stack_status") or payload.get("status") or "")
blocked_issue = next(
(dict(item or {}) for item in issues if str((item or {}).get("severity") or "") == "blocked"),
{},
)
headline = ""
if stack_status == "blocked":
headline = str(blocked_issue.get("summary") or "").strip()
if not headline:
headline = str(
diagnosis.get("headline")
or (payload.get("link_snapshot") or {}).get("headline")
or ""
).strip()
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(api.get("http_status", 0) or 0),
"stack_status": stack_status,
"surface_status": str(diagnosis.get("surface_status") or ""),
"automation_status": str(diagnosis.get("automation_status") or ""),
"issues_total": len(issues),
"blocked_issues": sum(1 for item in issues if str((item or {}).get("severity") or "") == "blocked"),
"headline": headline,
"blocked_issue_detail": str(blocked_issue.get("detail") or "").strip(),
"launchpad_recommended_target_node_code": str(diagnosis.get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(diagnosis.get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(diagnosis.get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(diagnosis.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(diagnosis.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
"next_step_action_code": str(next_step.get("action_code") or ""),
"next_step_summary": str(next_step.get("summary") or next_step.get("title") or next_step.get("reason") or ""),
"operator_lane": str(operator_decision.get("lane") or "").strip(),
"operator_priority": str(operator_decision.get("priority") or "").strip(),
"operator_reason_code": str(operator_decision.get("reason_code") or "").strip(),
"operator_title": str(operator_decision.get("title") or "").strip(),
"operator_summary": str(operator_decision.get("summary") or "").strip(),
"operator_primary_command_key": str(operator_decision.get("primary_command_key") or "").strip(),
"operator_secondary_command_key": str(operator_decision.get("secondary_command_key") or "").strip(),
"operator_primary_command": str(
next_actions.get("primary_command")
or recommended_commands.get(str(operator_decision.get("primary_command_key") or "").strip())
or ""
).strip(),
"operator_secondary_command": str(
next_actions.get("secondary_command")
or recommended_commands.get(str(operator_decision.get("secondary_command_key") or "").strip())
or ""
).strip(),
"operator_focus_kind": str((operator_decision.get("next_focus") or {}).get("kind") or "").strip(),
"operator_focus_node_code": str((operator_decision.get("next_focus") or {}).get("node_code") or "").strip(),
}
if name == "06_contracts_registry.txt":
contracts = list(payload.get("contracts") or [])
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(payload.get("http_status", 0) or 0),
"registry_version": str(payload.get("registry_version") or ""),
"schema_version": str(payload.get("schema_version") or ""),
"contracts_total": int(payload.get("contracts_total", 0) or len(contracts)),
}
if name == "07_driver_feed.txt":
summary = dict(payload.get("summary") or {})
top_recommendation = dict(payload.get("top_recommendation") or {})
contract_navigation = dict(payload.get("contract_navigation") or {})
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(payload.get("http_status", 0) or 0),
"headline": str(payload.get("headline") or ""),
"primary_contract_key": str(contract_navigation.get("primary_contract_key") or ""),
"entry_count": len(list(payload.get("entries") or [])),
"activity_focus_count": len(list(payload.get("activity_focus") or [])),
"top_recommendation_key": str(top_recommendation.get("key") or ""),
"top_recommendation_contract_key": str(top_recommendation.get("primary_contract_key") or ""),
"do_now_total": int(summary.get("do_now_total", 0) or 0),
"launchpad_recommended_target_node_code": str(summary.get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(summary.get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(summary.get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
}
if name == "08_activity_stream.txt":
summary = dict(payload.get("summary") or {})
contract_navigation = dict(payload.get("contract_navigation") or {})
items = list(payload.get("items") or [])
first_item = dict(items[0] or {}) if items else {}
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(payload.get("http_status", 0) or 0),
"primary_contract_key": str(contract_navigation.get("primary_contract_key") or ""),
"filtered_total": int(summary.get("filtered_total", 0) or 0),
"item_count": len(items),
"first_item_key": str(first_item.get("activity_key") or ""),
"first_item_contract_key": str(first_item.get("primary_contract_key") or ""),
}
if name == "09_codex_brief.txt":
summary = dict(payload.get("summary") or {})
focus = dict(payload.get("focus") or {})
contract_navigation = dict(payload.get("contract_navigation") or {})
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(payload.get("http_status", 0) or 0),
"headline": str(payload.get("headline") or ""),
"primary_contract_key": str(contract_navigation.get("primary_contract_key") or ""),
"focus_entry_key": str(focus.get("entry_key") or ""),
"focus_contract_key": str(focus.get("primary_contract_key") or ""),
"entry_count": len(list(payload.get("entries") or [])),
"activity_focus_count": len(list(payload.get("activity_focus") or [])),
"blocked_total": int(summary.get("blocked_total", 0) or 0),
"launchpad_recommended_target_node_code": str(summary.get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(summary.get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(summary.get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
}
if name in optional_preview_files:
contract = dict(payload.get("contract") or {})
gate = dict(payload.get("gate") or {})
selected = dict(payload.get("selected_entry") or {})
selected_key = (
str(selected.get("key") or "").strip()
or str(selected.get("activity_key") or "").strip()
or str(selected.get("action_code") or "").strip()
)
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(payload.get("http_status", 0) or 0),
"contract_key": str(contract.get("contract_key") or ""),
"contract_version": str(contract.get("contract_version") or ""),
"preview_endpoint": str(contract.get("preview_endpoint") or ""),
"gate_decision": str(gate.get("decision") or ""),
"selected_key": selected_key,
"selected_title": str(selected.get("title") or ""),
}
if name.startswith("15_scene_node_log_"):
participation = dict(payload.get("participation") or {})
source_summary = dict(payload.get("source_summary") or {})
return {
"parsed_json": True,
"available": exit_code == 0,
"http_status": int(payload.get("http_status", 0) or 0),
"node_code": str(payload.get("node_code") or ""),
"status": str(payload.get("status") or ""),
"status_label": str(payload.get("status_label") or ""),
"mode": str(payload.get("mode") or ""),
"records_total": int(payload.get("records_total", 0) or 0),
"detect_participating": bool(participation.get("detect_participating", False)),
"participation_label": str(participation.get("participation_label") or ""),
"source_line_count": int(source_summary.get("line_count", 0) or 0),
"missing_reason_code": str(payload.get("missing_reason_code") or ""),
}
return {
"parsed_json": True,
"available": exit_code == 0,
"top_level_keys": list(payload.keys())[:12],
}
def classify_report_failure(name: str, exists: bool, payload, summary: dict, exit_code: int, text: str) -> dict:
http_status_raw = ""
if isinstance(summary, dict):
http_status_raw = str(summary.get("http_status") or "").strip()
text_tail = last_nonempty_line(text)
has_payload = isinstance(payload, (dict, list))
detail = ""
reason_code = ""
label = ""
if not exists:
reason_code = "report_missing"
label = "未生成报告"
detail = "导出阶段没有落盘该 report 文件。"
elif exit_code == 124:
reason_code = "command_timeout"
label = "执行超时"
detail = "报告命令超过超时阈值后被终止,建议先单独复跑对应子命令。"
elif exit_code != 0:
reason_code = "command_failed"
label = "命令失败"
detail = f"报告命令退出码为 {exit_code}。"
elif http_status_raw and http_status_raw.isdigit() and not (200 <= int(http_status_raw) < 300):
http_status = int(http_status_raw)
if http_status == 0:
reason_code = "http_unreachable"
label = "接口不可达"
detail = "curl 未拿到有效 HTTP 状态,通常表示超时、连接失败或被中间网络丢弃。"
else:
reason_code = "http_error"
label = "接口异常"
detail = f"目标接口返回 HTTP {http_status}。"
elif not has_payload:
reason_code = "result_unparsed"
label = "结果不可解析"
detail = "命令成功返回,但未解析出结构化 JSON 结果。"
else:
reason_code = "ok"
label = "正常"
detail = ""
if text_tail and reason_code != "ok":
detail = (detail + f" 最近输出: {text_tail}")[:500].strip()
return {
"reason_code": reason_code,
"label": label,
"detail": detail,
"timeout": reason_code == "command_timeout",
"http_status": int(http_status_raw) if http_status_raw.isdigit() else 0,
"text_tail": text_tail,
"parsed_json": bool(isinstance(summary, dict) and summary.get("parsed_json")),
}
reports = []
all_contract_keys = set()
required_failures = []
optional_unavailable = []
for name in report_files:
path = report_dir / name
exists = path.exists()
text = path.read_text(encoding="utf-8", errors="replace") if exists else ""
exit_code = extract_exit_code(text) if exists else 1
parsed_payload = extract_last_json_value(text) if exists else None
summary = summarize_report(name, parsed_payload, exit_code, text)
if parsed_payload is not None:
collect_contract_keys(parsed_payload, all_contract_keys)
http_status = summary.get("http_status") if isinstance(summary, dict) else None
http_ok = True
if isinstance(http_status, int):
http_ok = 200 <= http_status < 300
report_ok = exists and exit_code == 0 and http_ok
if isinstance(summary, dict) and "available" in summary:
summary["available"] = report_ok
report_entry = {
"name": name,
"path": str(path),
"exists": exists,
"size_bytes": int(path.stat().st_size) if exists else 0,
"optional": name in optional_preview_files,
"command_exit_code": exit_code,
"ok": report_ok,
"summary": summary,
"failure": classify_report_failure(name, exists, parsed_payload, summary, exit_code, text),
}
reports.append(report_entry)
if name in optional_preview_files:
if not report_entry["ok"]:
optional_unavailable.append(name)
else:
if not report_entry["ok"]:
required_failures.append(name)
preview_contract_keys = sorted(
{
str((report.get("summary") or {}).get("contract_key") or "").strip()
for report in reports
if report.get("name") in optional_preview_files
and str((report.get("summary") or {}).get("contract_key") or "").strip()
}
)
primary_contract_keys = sorted(
{
str((report.get("summary") or {}).get("primary_contract_key") or "").strip()
for report in reports
if str((report.get("summary") or {}).get("primary_contract_key") or "").strip()
}
)
reports_by_name = {str(report.get("name") or ""): report for report in reports}
scene_log_reports = [
report
for report in reports
if str(report.get("name") or "").startswith("15_scene_node_log_")
]
scene_log_report_summaries = [dict(report.get("summary") or {}) for report in scene_log_reports]
scene_log_reports_total = len(scene_log_reports)
scene_log_reports_ok = sum(1 for report in scene_log_reports if bool(report.get("ok")))
scene_log_status_counts = {}
for summary in scene_log_report_summaries:
status_key = str(summary.get("status") or "unknown").strip() or "unknown"
scene_log_status_counts[status_key] = int(scene_log_status_counts.get(status_key, 0) or 0) + 1
scene_log_problem_reports = [
summary
for summary in scene_log_report_summaries
if str(summary.get("status") or "").strip() in {"waiting_sample", "missing_sample", "disabled", "standby", "unknown"}
]
scene_log_problem_node_codes = [
str(summary.get("node_code") or "").strip()
for summary in scene_log_problem_reports
if str(summary.get("node_code") or "").strip()
]
scene_log_healthy_reports = [
summary
for summary in scene_log_report_summaries
if str(summary.get("status") or "").strip() in {"healthy", "full_capture", "historical_sample"}
]
required_failure_details = [
{
"name": str(report.get("name") or ""),
"reason_code": str((report.get("failure") or {}).get("reason_code") or ""),
"label": str((report.get("failure") or {}).get("label") or ""),
"detail": str((report.get("failure") or {}).get("detail") or ""),
}
for report in reports
if not bool(report.get("optional")) and not bool(report.get("ok"))
]
optional_unavailable_details = [
{
"name": str(report.get("name") or ""),
"reason_code": str((report.get("failure") or {}).get("reason_code") or ""),
"label": str((report.get("failure") or {}).get("label") or ""),
"detail": str((report.get("failure") or {}).get("detail") or ""),
}
for report in reports
if bool(report.get("optional")) and not bool(report.get("ok"))
]
def report_summary(name: str) -> dict:
return dict((reports_by_name.get(name) or {}).get("summary") or {})
def report_ok(name: str) -> bool:
return bool((reports_by_name.get(name) or {}).get("ok"))
def report_http_status(name: str) -> int:
value = report_summary(name).get("http_status")
return int(value or 0) if isinstance(value, int) or str(value).isdigit() else 0
contracts_summary = report_summary("06_contracts_registry.txt")
driver_feed_summary = report_summary("07_driver_feed.txt")
activity_stream_summary = report_summary("08_activity_stream.txt")
codex_brief_summary = report_summary("09_codex_brief.txt")
stack_summary = report_summary("00_ops_center_stack_summary.txt")
launchpad_recommended_target_node_code = next(
(
str(source.get("launchpad_recommended_target_node_code") or "").strip()
for source in [driver_feed_summary, codex_brief_summary, stack_summary]
if str(source.get("launchpad_recommended_target_node_code") or "").strip()
),
"",
)
launchpad_recommended_recovery_label = next(
(
str(source.get("launchpad_recommended_recovery_label") or "").strip()
for source in [driver_feed_summary, codex_brief_summary, stack_summary]
if str(source.get("launchpad_recommended_recovery_label") or "").strip()
),
"",
)
launchpad_recommended_recovery_summary = next(
(
str(source.get("launchpad_recommended_recovery_summary") or "").strip()
for source in [driver_feed_summary, codex_brief_summary, stack_summary]
if str(source.get("launchpad_recommended_recovery_summary") or "").strip()
),
"",
)
launchpad_onboarding_bootstrap_pending_nodes = max(
int(driver_feed_summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
int(codex_brief_summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
int(stack_summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
)
launchpad_onboarding_acceptance_ready_nodes = max(
int(driver_feed_summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
int(codex_brief_summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
int(stack_summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
)
stack_status = str(stack_summary.get("stack_status") or "").strip()
stack_headline = str(stack_summary.get("headline") or "").strip()
stack_blocked_issue_detail = str(stack_summary.get("blocked_issue_detail") or "").strip()
stack_next_summary = str(stack_summary.get("next_step_summary") or "").strip()
stack_next_action_code = str(stack_summary.get("next_step_action_code") or "").strip()
stack_blocked_issues = int(stack_summary.get("blocked_issues", 0) or 0)
stack_operator_lane = str(stack_summary.get("operator_lane") or "").strip()
stack_operator_priority = str(stack_summary.get("operator_priority") or "").strip()
stack_operator_reason_code = str(stack_summary.get("operator_reason_code") or "").strip()
stack_operator_title = str(stack_summary.get("operator_title") or "").strip()
stack_operator_summary = str(stack_summary.get("operator_summary") or "").strip()
stack_operator_primary_command_key = str(stack_summary.get("operator_primary_command_key") or "").strip()
stack_operator_secondary_command_key = str(stack_summary.get("operator_secondary_command_key") or "").strip()
stack_operator_primary_command = str(stack_summary.get("operator_primary_command") or "").strip()
stack_operator_secondary_command = str(stack_summary.get("operator_secondary_command") or "").strip()
stack_operator_focus_kind = str(stack_summary.get("operator_focus_kind") or "").strip()
stack_operator_focus_node_code = str(stack_summary.get("operator_focus_node_code") or "").strip()
stack_primary_commands = []
for candidate in [
stack_operator_primary_command,
stack_operator_secondary_command,
]:
normalized_candidate = str(candidate or "").strip()
if normalized_candidate and normalized_candidate not in stack_primary_commands:
stack_primary_commands.append(normalized_candidate)
stack_preferred_surface = "stack-summary"
if stack_operator_focus_kind == "node_scene_log":
stack_preferred_surface = "execution-scene"
elif stack_operator_lane == "node_handover":
stack_preferred_surface = "managed-nodes"
elif stack_operator_lane == "release":
stack_preferred_surface = "release-hub"
elif stack_operator_lane == "observability":
stack_preferred_surface = "execution-scene"
elif stack_operator_lane in {"ops_jobs", "steady"}:
stack_preferred_surface = "driver-feed"
contract_surface_gaps = []
if report_ok("07_driver_feed.txt") and not str(driver_feed_summary.get("primary_contract_key") or "").strip():
contract_surface_gaps.append("driver-feed")
if report_ok("08_activity_stream.txt") and not str(activity_stream_summary.get("primary_contract_key") or "").strip():
contract_surface_gaps.append("activity-stream")
if report_ok("09_codex_brief.txt") and not str(codex_brief_summary.get("primary_contract_key") or "").strip():
contract_surface_gaps.append("codex-brief")
surface_http_status = {
"stack_summary": report_http_status("00_ops_center_stack_summary.txt"),
"contracts_registry": report_http_status("06_contracts_registry.txt"),
"driver_feed": report_http_status("07_driver_feed.txt"),
"activity_stream": report_http_status("08_activity_stream.txt"),
"codex_brief": report_http_status("09_codex_brief.txt"),
"stack_next_preview": report_http_status("10_stack_next_preview.txt"),
"driver_focus_preview": report_http_status("11_driver_focus_preview.txt"),
"activity_preview": report_http_status("12_activity_preview.txt"),
"codex_focus_preview": report_http_status("13_codex_focus_preview.txt"),
}
doctor_command = f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {mainland_api_base_url}"
if overseas_api_base_url:
doctor_command += f" {overseas_api_base_url}"
decision = {
"status": "ready",
"reason_code": "ops_center_ready",
"headline": "海外控制面已经具备协议驱动的观察与预览能力。",
"detail": "",
"preferred_surface": "driver-feed",
"next_action_code": "review_driver_feed",
"recommended_commands": [
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-feed {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh activity-stream {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh codex-brief {mainland_api_base_url}",
],
"evidence": {
"required_failures": required_failures,
"optional_unavailable": optional_unavailable,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
if "06_contracts_registry.txt" in required_failures:
http_status = int(contracts_summary.get("http_status", 0) or 0)
contracts_failure = next(
(
item for item in required_failure_details
if str(item.get("name") or "") == "06_contracts_registry.txt"
),
{},
)
contracts_reason_code = str(contracts_failure.get("reason_code") or "").strip()
contracts_failure_detail = str(contracts_failure.get("detail") or "").strip()
decision = {
"status": "blocked",
"reason_code": contracts_reason_code or "contracts_registry_unavailable",
"headline": (
"控制面 contract registry 拉取超时,海外驾驶链路当前无法确认协议面是否完整。 "
if contracts_reason_code == "command_timeout"
else "控制面还没暴露 ops/contracts海外驾驶链路当前不完整。"
),
"detail": contracts_failure_detail or f"06_contracts_registry.txt http_status={http_status},先恢复 contract registry再继续 preview / execute。",
"preferred_surface": "contracts",
"next_action_code": "restore_contract_registry",
"recommended_commands": [
f"bash domain-api/deploy/multi-region/drive_ops_center.sh contracts {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
elif required_failures:
scene_log_required_failures = [
name for name in required_failures
if str(name or "").startswith("15_scene_node_log_")
]
if scene_log_required_failures and stack_primary_commands:
decision = {
"status": "blocked" if stack_status == "blocked" else "attention",
"reason_code": stack_operator_reason_code or "scene_log_surface_unavailable",
"headline": stack_operator_title or "执行现场观察链路还没拿到可用日志样本。",
"detail": stack_operator_summary or " / ".join(scene_log_required_failures),
"preferred_surface": stack_preferred_surface,
"next_action_code": stack_operator_primary_command_key or "inspect_scene_log_reports",
"recommended_commands": [
*stack_primary_commands,
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
doctor_command,
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
"scene_log_status_counts": scene_log_status_counts,
},
}
else:
decision = {
"status": "blocked",
"reason_code": "required_surfaces_unavailable",
"headline": "海外控制面仍有必需 surface 缺失,先修 surface 再谈自动驾驶。",
"detail": " / ".join(required_failures),
"preferred_surface": stack_preferred_surface if stack_primary_commands else "stack-summary",
"next_action_code": stack_operator_primary_command_key or "repair_required_surfaces",
"recommended_commands": [
*stack_primary_commands,
doctor_command,
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
elif contract_surface_gaps:
decision = {
"status": "attention",
"reason_code": "contract_navigation_incomplete",
"headline": "核心观察面已在线,但 contract_navigation 还没完全补齐。",
"detail": "缺少主协议标记的 surface: " + " / ".join(contract_surface_gaps),
"preferred_surface": contract_surface_gaps[0],
"next_action_code": "fill_contract_navigation",
"recommended_commands": [
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-feed {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh activity-stream {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh codex-brief {mainland_api_base_url}",
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
elif stack_status == "blocked":
decision = {
"status": "blocked",
"reason_code": stack_operator_reason_code or "stack_diagnosis_blocked",
"headline": stack_operator_title or stack_headline or "控制面总检已判定当前现场存在阻断问题,暂不应继续执行自动化动作。",
"detail": stack_operator_summary or stack_blocked_issue_detail or stack_next_summary or f"stack_diagnosis.blocked_issues={stack_blocked_issues}",
"preferred_surface": stack_preferred_surface,
"next_action_code": stack_operator_primary_command_key or stack_next_action_code or "resolve_stack_blockers",
"recommended_commands": [
*stack_primary_commands,
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
doctor_command,
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
elif stack_status == "attention":
if stack_operator_reason_code == "runtime_build_schema_stale":
restart_commands = []
for candidate in [
stack_operator_primary_command,
stack_operator_secondary_command,
f"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {mainland_api_base_url}",
doctor_command,
]:
normalized_candidate = str(candidate or "").strip()
if normalized_candidate and normalized_candidate not in restart_commands:
restart_commands.append(normalized_candidate)
decision = {
"status": "attention",
"reason_code": "runtime_build_schema_stale",
"headline": stack_operator_title or "仓库代码已更新,但运行中的控制面 API 还是旧 schema。",
"detail": stack_operator_summary or stack_next_summary or "先执行 runtime-refresh-recover让控制面 API 切到最新代码;随后重新执行 stack-diagnosis / stack-next再继续节点接管。",
"preferred_surface": "stack-summary",
"next_action_code": stack_operator_primary_command_key or "api-restart",
"recommended_commands": restart_commands,
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
else:
decision = {
"status": "attention",
"reason_code": stack_operator_reason_code or "stack_diagnosis_attention",
"headline": stack_operator_title or stack_headline or "控制面总检已发现待收口事项,建议先顺着 next_step 收口后再继续预览或执行。",
"detail": stack_operator_summary or stack_next_summary or "请先处理 stack-diagnosis 提示的 attention 项。",
"preferred_surface": stack_preferred_surface,
"next_action_code": stack_operator_primary_command_key or stack_next_action_code or "review_stack_next",
"recommended_commands": [
*stack_primary_commands,
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
doctor_command,
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
elif optional_unavailable:
focus_entry = str(codex_brief_summary.get("focus_entry_key") or "").strip()
decision = {
"status": "attention",
"reason_code": "preview_unavailable",
"headline": "核心观察面已可用,但当前 preview 入口还没形成可执行焦点。",
"detail": "当前不可用 preview: " + " / ".join(optional_unavailable),
"preferred_surface": "codex-brief" if focus_entry else "driver-feed",
"next_action_code": "inspect_focus_surfaces",
"recommended_commands": [
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-feed {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh activity-stream {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh codex-brief {mainland_api_base_url}",
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
},
}
elif scene_log_reports_total > 0 and scene_log_reports_ok == 0:
first_scene_log = dict(scene_log_report_summaries[0] or {}) if scene_log_report_summaries else {}
scene_log_node_code = str(first_scene_log.get("node_code") or "").strip()
scene_log_mode = str(first_scene_log.get("mode") or "key").strip() or "key"
decision = {
"status": "attention",
"reason_code": "scene_log_reports_unavailable",
"headline": "总检已经识别出关键节点现场,但节点现场日志附件还没有成功形成可用样本。",
"detail": (
f"scene_log_reports_total={scene_log_reports_total},全部附件当前不可用;"
f"缺口节点 {', '.join(scene_log_problem_node_codes) or scene_log_node_code or '-'}"
),
"preferred_surface": "execution-scene",
"next_action_code": "inspect_scene_log_reports",
"recommended_commands": [
*(
[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh scene-node-log {mainland_api_base_url} {scene_log_node_code} 120 {scene_log_mode}"
]
if scene_log_node_code
else []
),
f"bash domain-api/deploy/multi-region/check_ops_center_stack.sh {mainland_api_base_url} summary",
doctor_command,
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
"scene_log_status_counts": scene_log_status_counts,
},
}
elif scene_log_problem_reports:
first_scene_log = dict(scene_log_problem_reports[0] or {})
scene_log_node_code = str(first_scene_log.get("node_code") or "").strip()
scene_log_mode = str(first_scene_log.get("mode") or "key").strip() or "key"
decision = {
"status": "attention",
"reason_code": "scene_log_reports_need_attention",
"headline": "总检已经带回节点现场日志附件,但仍有参与节点的现场样本没有完全收口。",
"detail": (
f"问题节点 {', '.join(scene_log_problem_node_codes) or scene_log_node_code or '-'}"
f" 当前状态分布 {scene_log_status_counts}"
),
"preferred_surface": "execution-scene",
"next_action_code": "inspect_scene_log_reports",
"recommended_commands": [
*(
[
f"bash domain-api/deploy/multi-region/drive_ops_center.sh scene-node-log {mainland_api_base_url} {scene_log_node_code} 120 {scene_log_mode}"
]
if scene_log_node_code
else []
),
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {mainland_api_base_url}",
doctor_command,
],
"evidence": {
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
"scene_log_status_counts": scene_log_status_counts,
},
}
else:
codex_headline = str(codex_brief_summary.get("headline") or "").strip()
driver_headline = str(driver_feed_summary.get("headline") or "").strip()
decision["headline"] = stack_operator_title or codex_headline or driver_headline or decision["headline"]
decision["detail"] = stack_operator_summary or stack_next_summary or "先看 driver-feed再按 manifest 已准备好的 preview surface 继续收敛。"
decision["preferred_surface"] = (
stack_preferred_surface
if stack_operator_primary_command_key or stack_operator_focus_kind
else ("codex-brief" if str(codex_brief_summary.get("focus_entry_key") or "").strip() else "driver-feed")
)
decision["next_action_code"] = stack_operator_primary_command_key or stack_next_action_code or "preview_primary_focus"
decision["recommended_commands"] = [
*stack_primary_commands,
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh driver-focus-preview {mainland_api_base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh codex-focus-preview {mainland_api_base_url}",
]
if isinstance(decision.get("evidence"), dict):
decision["evidence"].update({
"launchpad_recommended_target_node_code": launchpad_recommended_target_node_code,
"launchpad_recommended_recovery_label": launchpad_recommended_recovery_label,
"launchpad_recommended_recovery_summary": launchpad_recommended_recovery_summary,
"launchpad_onboarding_bootstrap_pending_nodes": launchpad_onboarding_bootstrap_pending_nodes,
"launchpad_onboarding_acceptance_ready_nodes": launchpad_onboarding_acceptance_ready_nodes,
})
manifest = {
"generated_at": datetime.now().isoformat(timespec="seconds"),
"config_file": config_file,
"active_profile": active_profile,
"mainland_api_base_url": mainland_api_base_url,
"overseas_api_base_url": overseas_api_base_url,
"report_dir": str(report_dir),
"reports_total": len(reports),
"reports": reports,
"summary": {
"ok": len(required_failures) == 0,
"required_failures": required_failures,
"required_failure_details": required_failure_details,
"optional_unavailable": optional_unavailable,
"optional_unavailable_details": optional_unavailable_details,
"scene_log_reports_total": scene_log_reports_total,
"scene_log_reports_ok": scene_log_reports_ok,
"scene_log_status_counts": scene_log_status_counts,
"scene_log_reports": [
{
"name": str(report.get("name") or ""),
"node_code": str((report.get("summary") or {}).get("node_code") or ""),
"status": str((report.get("summary") or {}).get("status") or ""),
"status_label": str((report.get("summary") or {}).get("status_label") or ""),
"mode": str((report.get("summary") or {}).get("mode") or ""),
"records_total": int((report.get("summary") or {}).get("records_total", 0) or 0),
"ok": bool(report.get("ok")),
}
for report in scene_log_reports
],
"available_preview_reports": sum(
1 for report in reports if report.get("name") in optional_preview_files and report.get("ok")
),
"discovered_contract_keys": sorted(all_contract_keys),
"primary_contract_keys": primary_contract_keys,
"preview_contract_keys": preview_contract_keys,
"contract_surface_gaps": contract_surface_gaps,
"surface_http_status": surface_http_status,
"launchpad_recommended_target_node_code": launchpad_recommended_target_node_code,
"launchpad_recommended_recovery_label": launchpad_recommended_recovery_label,
"launchpad_recommended_recovery_summary": launchpad_recommended_recovery_summary,
"launchpad_onboarding_bootstrap_pending_nodes": launchpad_onboarding_bootstrap_pending_nodes,
"launchpad_onboarding_acceptance_ready_nodes": launchpad_onboarding_acceptance_ready_nodes,
},
"decision": decision,
}
print(json.dumps(manifest, ensure_ascii=False))
PY
)"
cat >"${report_dir}/README.txt" <<EOF
domainCheck ops-center doctor export
generated_at=$(date '+%Y-%m-%d %H:%M:%S')
active_profile=${OPS_CENTER_ACTIVE_PROFILE}
mainland_api_base_url=${mainland_base_url}
overseas_api_base_url=${overseas_base_url}
files:
- meta.json
- manifest.json
$(for file_name in "${report_files[@]}"; do echo "- ${file_name}"; done)
notes:
- preview reports are exported in optional mode
- if a current focus does not exist, the report file still exists and ends with command_exit_code=1
- manifest.json is the machine-readable summary for overseas Codex / future UI automation
EOF
python3 - <<'PY' "${report_dir}"
import json
import sys
from pathlib import Path
report_dir = Path(sys.argv[1])
manifest_path = report_dir / "manifest.json"
decision = {}
summary = {}
if manifest_path.exists():
try:
manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
except Exception:
manifest = {}
decision = dict(manifest.get("decision") or {})
summary = dict(manifest.get("summary") or {})
print(json.dumps({
"ok": True,
"report_dir": str(report_dir),
"meta_path": str(report_dir / "meta.json"),
"manifest_path": str(manifest_path),
"readme_path": str(report_dir / "README.txt"),
"summary": {
"ok": bool(summary.get("ok", False)),
"required_failures": list(summary.get("required_failures") or []),
"optional_unavailable": list(summary.get("optional_unavailable") or []),
"scene_log_reports_total": int(summary.get("scene_log_reports_total", 0) or 0),
"scene_log_reports_ok": int(summary.get("scene_log_reports_ok", 0) or 0),
"scene_log_status_counts": dict(summary.get("scene_log_status_counts") or {}),
"scene_log_reports": list(summary.get("scene_log_reports") or []),
"contract_surface_gaps": list(summary.get("contract_surface_gaps") or []),
"launchpad_recommended_target_node_code": str(summary.get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(summary.get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(summary.get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
},
"decision": {
"status": str(decision.get("status") or ""),
"reason_code": str(decision.get("reason_code") or ""),
"headline": str(decision.get("headline") or ""),
"detail": str(decision.get("detail") or ""),
"preferred_surface": str(decision.get("preferred_surface") or ""),
"next_action_code": str(decision.get("next_action_code") or ""),
"recommended_commands": list(decision.get("recommended_commands") or []),
},
}, ensure_ascii=False, indent=2))
PY
}
print_doctor_manifest_summary() {
local manifest_target="$1"
python3 - <<'PY' "${manifest_target}"
import json
import sys
from pathlib import Path
target = Path(str(sys.argv[1] or "").strip())
if target.is_dir():
target = target / "manifest.json"
if not target.exists():
raise SystemExit(f"manifest not found: {target}")
payload = json.loads(target.read_text(encoding="utf-8"))
print(json.dumps({
"ok": bool(payload.get("summary", {}).get("ok", False)),
"report_dir": str(payload.get("report_dir") or target.parent),
"manifest_path": str(target),
"summary": {
"ok": bool(payload.get("summary", {}).get("ok", False)),
"required_failures": list(payload.get("summary", {}).get("required_failures") or []),
"required_failure_details": list(payload.get("summary", {}).get("required_failure_details") or []),
"optional_unavailable": list(payload.get("summary", {}).get("optional_unavailable") or []),
"optional_unavailable_details": list(payload.get("summary", {}).get("optional_unavailable_details") or []),
"scene_log_reports_total": int(payload.get("summary", {}).get("scene_log_reports_total", 0) or 0),
"scene_log_reports_ok": int(payload.get("summary", {}).get("scene_log_reports_ok") or 0),
"scene_log_status_counts": dict(payload.get("summary", {}).get("scene_log_status_counts") or {}),
"scene_log_reports": list(payload.get("summary", {}).get("scene_log_reports") or []),
"contract_surface_gaps": list(payload.get("summary", {}).get("contract_surface_gaps") or []),
"discovered_contract_keys": list(payload.get("summary", {}).get("discovered_contract_keys") or []),
"launchpad_recommended_target_node_code": str(payload.get("summary", {}).get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(payload.get("summary", {}).get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(payload.get("summary", {}).get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(payload.get("summary", {}).get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(payload.get("summary", {}).get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
},
"decision": dict(payload.get("decision") or {}),
}, ensure_ascii=False, indent=2))
PY
}
run_doctor_decision() {
local first_arg="${1:-}"
local second_arg="${2:-}"
local report_target=""
if [[ -n "${first_arg}" && ( -f "${first_arg}" || -d "${first_arg}" ) ]]; then
report_target="${first_arg}"
print_doctor_manifest_summary "${report_target}"
return 0
fi
local mainland_base_url="${first_arg:-$(ops_center_resolve_mainland_api_base_url)}"
local overseas_base_url
overseas_base_url="$(normalize_optional_base_url_arg "${second_arg}" "$(ops_center_resolve_overseas_api_base_url)")"
local report_dir
report_dir="$(mktemp -d /tmp/domaincheck-doctor-export-XXXXXX)"
run_doctor_report_export "${report_dir}" "${mainland_base_url}" "${overseas_base_url}" >/dev/null
print_doctor_manifest_summary "${report_dir}/manifest.json"
}
export_agent_plan() {
local report_dir="$1"
local mainland_base_url="$2"
local node_code="$3"
local node_region="$4"
local node_role="$5"
local control_plane_base_url="$6"
local root_dir="$7"
local timestamp
timestamp="$(timestamp_now)"
local safe_node_code
safe_node_code="$(printf '%s' "${node_code}" | tr '/:' '__')"
ops_center_ensure_report_dir "${report_dir}"
local output_path="${report_dir}/agent-plan-${safe_node_code}-${timestamp}.txt"
ISSUED_BY="$(ops_center_resolve_issued_by)" \
EXPIRES_IN_HOURS="$(ops_center_resolve_expires_in_hours)" \
bash "${SCRIPT_DIR}/build_node_agent_bootstrap_plan.sh" \
"${mainland_base_url}" \
"${node_code}" \
"${node_region}" \
"${node_role}" \
"${control_plane_base_url}" \
"${root_dir}" | tee "${output_path}"
write_json_file "${output_path}.meta.json" "$(python3 - \
"${node_code}" "${node_region}" "${node_role}" "${mainland_base_url}" "${control_plane_base_url}" "${root_dir}" "${output_path}" <<'PY'
import json
import sys
from datetime import datetime
print(json.dumps({
"generated_at": datetime.now().isoformat(timespec="seconds"),
"node_code": sys.argv[1],
"node_region": sys.argv[2],
"node_role": sys.argv[3],
"mainland_api_base_url": sys.argv[4],
"control_plane_base_url": sys.argv[5],
"root_dir": sys.argv[6],
"output_path": sys.argv[7],
}, ensure_ascii=False))
PY
)"
python3 - <<'PY' "${output_path}" "${output_path}.meta.json"
import json
import sys
print(json.dumps({
"ok": True,
"output_path": sys.argv[1],
"meta_path": sys.argv[2],
}, ensure_ascii=False, indent=2))
PY
}
run_publish_latest() {
local mainland_base_url="$1"
local mode="$2"
local confirm_flag="$3"
local skip_smoke_flag="$4"
local repo_root
repo_root="$(ops_center_repo_root)"
local skip_smoke
skip_smoke="$(confirm_to_bool "${skip_smoke_flag}")"
if [[ "${mode}" != "worker" && "${mode}" != "control" ]]; then
echo "mode must be worker or control" >&2
usage
exit 1
fi
echo "============================================================"
echo "[publish/1] package latest"
echo "============================================================"
if [[ "${skip_smoke}" == "true" ]]; then
DOMAINCHECK_SKIP_SMOKE_TEST=1 bash "${repo_root}/package_domain_release.sh"
else
bash "${repo_root}/package_domain_release.sh"
fi
echo
echo "============================================================"
echo "[publish/2] preview smart rollout from latest package"
echo "============================================================"
bash "${SCRIPT_DIR}/drive_release_hub.sh" "${mainland_base_url}" preview-latest-smart "${mode}"
echo
echo "============================================================"
echo "[publish/3] smart rollout from latest package"
echo "============================================================"
bash "${SCRIPT_DIR}/drive_release_hub.sh" "${mainland_base_url}" smart-latest "${mode}" "${confirm_flag}"
}
show_ops_contracts() {
local mainland_base_url="$1"
local contract_key="${2:-}"
local raw_http_response
if [[ -n "${contract_key}" ]]; then
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/contracts/${contract_key}")"
else
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/contracts")"
fi
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}" "${contract_key}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
contract_key = str(sys.argv[3] or "").strip()
data = payload.get("data") or {}
if contract_key:
contract = data.get("contract") or {}
related_contracts = list(data.get("related_contracts") or [])
print(json.dumps({
"http_status": http_status,
"requested_contract_key": contract_key,
"registry_version": str(data.get("registry_version") or ""),
"schema_version": str(data.get("schema_version") or ""),
"docs_root": str(data.get("docs_root") or ""),
"detail_endpoint": str(data.get("detail_endpoint") or ""),
"contract": {
"key": str(contract.get("key") or ""),
"title": str(contract.get("title") or ""),
"status": str(contract.get("status") or ""),
"version": str(contract.get("version") or ""),
"primary_endpoint": str(contract.get("primary_endpoint") or ""),
"schema_doc_path": str(contract.get("schema_doc_path") or ""),
"summary": str(contract.get("summary") or ""),
"consumers": list(contract.get("consumers") or []),
"service_keys": list(contract.get("service_keys") or []),
"related_contract_keys": list(contract.get("related_contract_keys") or data.get("related_contract_keys") or []),
"discovery_endpoints": list(contract.get("discovery_endpoints") or []),
},
"related_contracts": [
{
"key": str(item.get("key") or ""),
"title": str(item.get("title") or ""),
"version": str(item.get("version") or ""),
"primary_endpoint": str(item.get("primary_endpoint") or ""),
}
for item in related_contracts
],
}, ensure_ascii=False, indent=2))
else:
contracts = list(data.get("contracts") or [])
print(json.dumps({
"http_status": http_status,
"registry_version": str(data.get("registry_version") or ""),
"schema_version": str(data.get("schema_version") or ""),
"contracts_total": int(data.get("contracts_total", 0) or 0),
"docs_root": str(data.get("docs_root") or ""),
"discovery_entrypoints": list(data.get("discovery_entrypoints") or []),
"contracts": [
{
"key": str(item.get("key") or ""),
"version": str(item.get("version") or ""),
"primary_endpoint": str(item.get("primary_endpoint") or ""),
"schema_doc_path": str(item.get("schema_doc_path") or ""),
"related_contract_keys": list(item.get("related_contract_keys") or []),
}
for item in contracts
],
}, ensure_ascii=False, indent=2))
PY
}
show_activity_stream() {
local mainland_base_url="$1"
shift || true
local request_url
request_url="$(python3 - <<'PY' "${mainland_base_url}" "$@"
import sys
from urllib.parse import urlencode
base_url = str(sys.argv[1] or "").rstrip("/")
tokens = [str(item or "").strip() for item in sys.argv[2:] if str(item or "").strip()]
params = {}
allowed = {"limit", "kind", "status", "execution_mode", "query"}
for token in tokens:
if "=" in token:
key, value = token.split("=", 1)
key = str(key or "").strip()
value = str(value or "").strip()
if key in allowed and value:
params[key] = value
elif "query" not in params:
params["query"] = token
query_string = urlencode(params)
endpoint = f"{base_url}/api/v1/ops/activity-stream"
print(f"{endpoint}?{query_string}" if query_string else endpoint)
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${request_url}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
printf '%s\n' "request_url=${request_url}"
print_json_or_raw "${response_json}"
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}" "${request_url}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
request_url = str(sys.argv[3] or "").strip()
data = payload.get("data") or {}
summary = data.get("summary") or {}
items = list(data.get("items") or [])
contract_navigation = data.get("contract_navigation") or {}
print(json.dumps({
"http_status": http_status,
"request_url": request_url,
"contract_navigation": {
"primary_contract_key": str(contract_navigation.get("primary_contract_key") or ""),
"contract_keys": list(contract_navigation.get("contract_keys") or []),
"detail_endpoint_pattern": str(contract_navigation.get("detail_endpoint_pattern") or ""),
},
"summary": {
"status": str(summary.get("status") or ""),
"status_label": str(summary.get("status_label") or ""),
"summary_text": str(summary.get("summary_text") or ""),
"total": int(summary.get("total", 0) or 0),
"filtered_total": int(summary.get("filtered_total", 0) or 0),
"unfiltered_total": int(summary.get("unfiltered_total", 0) or 0),
"latest_at": str(summary.get("latest_at") or ""),
"filters": dict(summary.get("filters") or {}),
"kind_counts": dict(summary.get("kind_counts") or {}),
"status_counts": dict(summary.get("status_counts") or {}),
},
"items": [
{
"activity_key": str(item.get("activity_key") or ""),
"kind": str(item.get("kind") or ""),
"title": str(item.get("title") or ""),
"status": str(item.get("status") or ""),
"status_label": str(item.get("status_label") or ""),
"execution_mode": str(item.get("execution_mode") or ""),
"occurred_at": str(item.get("occurred_at") or ""),
"primary_contract_key": str((item.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((item.get("contract_navigation") or {}).get("contract_keys") or []),
"focus_ref": item.get("focus_ref") or {},
"ui_intent": item.get("ui_intent") or {},
}
for item in items
],
}, ensure_ascii=False, indent=2))
PY
}
build_activity_preview_bundle() {
local activity_response_json="$1"
local activity_key="$2"
local requested_by="$3"
local source_label="$4"
python3 - <<'PY' "${activity_response_json}" "${activity_key}" "${requested_by}" "${source_label}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
requested_activity_key = str(sys.argv[2] or "").strip()
requested_by = str(sys.argv[3] or "").strip() or "cli/ops-center/activity-preview"
source_label = str(sys.argv[4] or "").strip() or "cli/activity-preview"
data = payload.get("data") or {}
items = [dict(item or {}) for item in list(data.get("items") or []) if dict(item or {})]
target_item = {}
if requested_activity_key:
target_item = next((item for item in items if str(item.get("activity_key") or "").strip() == requested_activity_key), {})
if not target_item:
target_item = next((item for item in items if str((item.get("ui_intent") or {}).get("kind") or "").strip()), {})
if not target_item:
raise SystemExit("activity-stream 当前没有可预览的活动项")
ui_intent = dict(target_item.get("ui_intent") or {})
if not str(ui_intent.get("kind") or "").strip():
raise SystemExit("目标活动项没有 ui_intent当前不能生成 preview")
kind = str(target_item.get("kind") or "").strip()
if kind == "playbook_run":
primary_label = "查看编排"
elif kind == "rollout":
primary_label = "查看任务"
elif kind == "runbook_sequence":
primary_label = "查看路径"
elif kind in {"execution_scene", "log_sync"}:
primary_label = "查看现场"
elif str(ui_intent.get("kind") or "").strip() == "job_detail":
primary_label = "看详情"
else:
primary_label = "看事件"
focus_ref = dict(target_item.get("focus_ref") or {})
request_payload = {
"source_label": source_label,
"title": str(target_item.get("title") or "").strip() or str(target_item.get("activity_key") or "").strip() or "activity-preview",
"summary": str(target_item.get("summary_text") or target_item.get("summary") or "").strip(),
"reason": str(target_item.get("summary_text") or target_item.get("summary") or "").strip() or "建议先进入该活动项对应的工作区。",
"meta_text": str(target_item.get("meta_text") or "").strip(),
"executor_kind": "driver_action",
"focus_ref": focus_ref,
"primary_focus_ref": focus_ref,
"primary_label": primary_label,
"primary_action_code": "focus_activity_item",
"primary_action_payload": {
"activity_key": str(target_item.get("activity_key") or "").strip(),
"kind": kind,
"status": str(target_item.get("status") or "").strip(),
"ui_intent": ui_intent,
"focus_ref": focus_ref,
},
"include_secondary": False,
"requested_by": requested_by,
}
print(json.dumps({
"selected_entry": {
"activity_key": str(target_item.get("activity_key") or "").strip(),
"kind": kind,
"title": str(target_item.get("title") or "").strip(),
"status": str(target_item.get("status") or "").strip(),
"status_label": str(target_item.get("status_label") or "").strip(),
"summary": str(target_item.get("summary_text") or target_item.get("summary") or "").strip(),
"focus_ref": focus_ref,
"ui_intent": ui_intent,
"contract_navigation": dict(target_item.get("contract_navigation") or {}),
},
"request": request_payload,
}, ensure_ascii=False))
PY
}
preview_activity_item() {
local mainland_base_url="$1"
local activity_key="$2"
local requested_by="$3"
local raw_activity_response
raw_activity_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/activity-stream?limit=50")"
local activity_http_status="${raw_activity_response##*$'\n'HTTP_STATUS=}"
local activity_response_json="${raw_activity_response%$'\n'HTTP_STATUS=*}"
if [[ "${activity_http_status}" -lt 200 || "${activity_http_status}" -ge 300 ]]; then
echo "[1/3] activity stream raw response"
printf '%s\n' "http_status=${activity_http_status}"
print_json_or_raw "${activity_response_json}"
exit 1
fi
local bundle_json
bundle_json="$(build_activity_preview_bundle "${activity_response_json}" "${activity_key}" "${requested_by}" "cli/activity-preview")"
local selected_entry_json
selected_entry_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("selected_entry") or {}, ensure_ascii=False))
PY
)"
local request_json
request_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("request") or {}, ensure_ascii=False))
PY
)"
echo "[1/4] selected activity"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/4] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/resolve" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[3/4] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[4/4] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}" "${selected_entry_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
selected = json.loads(str(sys.argv[3] or "{}"))
data = payload.get("data") or {}
preview = data.get("preview") or {}
primary = preview.get("primary") or {}
gate = data.get("gate") or {}
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"selected_entry": selected,
"contract": {
"contract_key": str(preview.get("contract_key") or ""),
"contract_version": str(preview.get("contract_version") or ""),
"contract_primary_endpoint": str(preview.get("contract_primary_endpoint") or ""),
"preview_endpoint": str(preview.get("preview_endpoint") or ""),
"schema_doc_path": str(preview.get("contract_schema_doc_path") or ""),
},
"execution_chain": str(preview.get("execution_chain") or ""),
"focus_ref": data.get("focus_ref") or {},
"gate": {
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"recommendation_label": str(gate.get("recommendation_label") or ""),
"will_execute": bool(gate.get("will_execute", False)),
"confirm_required": bool(gate.get("confirm_required", False)),
"target_api": str(gate.get("target_api") or ""),
"focus_ref": gate.get("focus_ref") or {},
},
"primary": {
"label": str(primary.get("label") or ""),
"action_code": str(primary.get("action_code") or ""),
"target_api": str(primary.get("target_api") or ""),
"recommendation_label": str(primary.get("recommendation_label") or ""),
"focus_ref": primary.get("focus_ref") or {},
"request_payload": primary.get("request_payload") or {},
},
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
show_delivery_queue_status() {
local mainland_base_url="$1"
local node_code="$2"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/delivery-queue")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
summary = data.get("summary") or {}
node = data.get("node") or {}
records_capability = data.get("records_capability") or {}
print(json.dumps({
"http_status": http_status,
"node": {
"node_code": str(node.get("node_code") or ""),
"region": str(node.get("region") or ""),
"role": str(node.get("role") or ""),
"agent_state_label": str(node.get("agent_state_label") or ""),
"remote_access_label": str(node.get("remote_access_label") or ""),
},
"summary": {
"state": str(summary.get("state") or ""),
"label": str(summary.get("label") or ""),
"reason": str(summary.get("reason") or ""),
"pending_count": int(summary.get("pending_count", 0) or 0),
"dead_letter_count": int(summary.get("dead_letter_count", 0) or 0),
"last_flush_at": str(summary.get("last_flush_at") or ""),
"oldest_pending_at": str(summary.get("oldest_pending_at") or ""),
"oldest_dead_letter_at": str(summary.get("oldest_dead_letter_at") or ""),
},
"records_capability": {
"record_visibility": str(records_capability.get("record_visibility") or ""),
"record_source": str(records_capability.get("record_source") or ""),
"full_records_available": bool(records_capability.get("full_records_available", False)),
},
"recommended_commands": {
"records": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue-records http://127.0.0.1:8100 {str(node.get('node_code') or '')}"
if str(node.get("node_code") or "").strip()
else ""
),
"flush": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue-flush http://127.0.0.1:8100 {str(node.get('node_code') or '')}"
if str(node.get("node_code") or "").strip()
else ""
),
"replay": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue-replay http://127.0.0.1:8100 {str(node.get('node_code') or '')}"
if str(node.get("node_code") or "").strip()
else ""
),
},
"head_records": data.get("head_records") or {},
}, ensure_ascii=False, indent=2))
PY
}
show_node_handover() {
local mainland_base_url="$1"
local node_code="$2"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/handover")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
local effective_http_status="${http_status}"
local fallback_json=""
if [[ "${http_status}" == "404" ]]; then
local stack_http_response stack_http_status stack_response_json
stack_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
stack_http_status="${stack_http_response##*$'\n'HTTP_STATUS=}"
stack_response_json="${stack_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${stack_http_status}" -ge 200 && "${stack_http_status}" -lt 300 ]]; then
fallback_json="$(python3 - <<'PY' "${stack_response_json}" "${node_code}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
target_node_code = str(sys.argv[2] or "").strip()
backend_http_status = int(sys.argv[3] or 404)
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if isinstance(item, dict)]
node = next(
(item for item in problem_nodes if str(item.get("node_code") or "").strip() == target_node_code),
{},
)
first_gap = dict(managed_nodes.get("first_handover_gap") or {})
if not node and str(first_gap.get("node_code") or "").strip() == target_node_code:
node = dict(first_gap.get("node") or {})
if not node:
node = {"node_code": target_node_code}
agent_state = str(node.get("agent_state") or "").strip()
remote_access_state = str(node.get("remote_access_state") or "").strip()
summary = "当前节点存在接管缺口,建议先生成 bootstrap 方案并检查 Node Agent 接入。"
stage_code = "unknown"
stage_label = "待判断"
stage_summary = summary
next_step = {
"code": "managed_node_handover",
"label": "先看节点接管详情",
"summary": "建议先确认节点为什么还没进入 remote-agent 就绪状态。",
}
if agent_state == "pending_bootstrap":
stage_code = "pending_bootstrap"
stage_label = "待执行接入"
stage_summary = "已签发有效 Token但目标节点尚未执行 bootstrap 片段。"
next_step = {
"code": "execute_bootstrap_plan",
"label": "去目标机执行接入片段",
"summary": "把 bootstrap env 和一键落地片段放到目标机执行,然后观察 register / heartbeat 是否建立。",
}
elif agent_state == "runtime_only":
stage_code = "runtime_only"
stage_label = "仅运行态在线"
stage_summary = "控制面能看到 runtime 心跳,但 Node Agent 尚未接管。"
next_step = {
"code": "issue_bootstrap_plan",
"label": "生成接入方案",
"summary": "下一步应生成 bootstrap env、脚本和一键落地命令再在目标节点启动 domaincheck-node-agent。",
}
elif agent_state == "stale":
stage_code = "stale"
stage_label = "心跳过期"
stage_summary = "Node Agent 曾接入过,但心跳已过期,需先恢复服务或重新接入。"
next_step = {
"code": "recover_agent",
"label": "优先恢复 Agent",
"summary": "先检查 domaincheck-node-agent 服务、网络连通性和控制面地址,再决定是否重新签发接入方案。",
}
elif remote_access_state == "agent_pending":
stage_code = "agent_pending"
stage_label = "待接入"
stage_summary = "当前 remote-agent 仍未就绪,先补齐 bootstrap 接入链路。"
result = {
"http_status": 200,
"backend_http_status": backend_http_status,
"fallback_used": True,
"fallback_source": "stack_diagnosis",
"compatibility_warning": "当前运行中的 API 尚未暴露 /api/v1/ops/nodes/{node_code}/handover已降级为 stack-diagnosis 兼容视图。",
"node_code": target_node_code,
"summary": stage_summary,
"stage": {
"code": stage_code,
"label": stage_label,
"summary": stage_summary,
"next_step": next_step,
},
"readiness": {
"is_managed": True,
"is_enabled": True,
"has_ssh_access": False,
"is_agent_online": agent_state in {"online", "online_busy"},
"is_remote_access_ready": False,
},
"ssh_profile": {
"host": "",
"port": 22,
"user": "",
"label": "",
"reason": "兼容视图未携带 SSH 资料,请到托管节点详情页补看。",
"ssh_command_hint": "",
},
"cluster": {
"status": "",
"current_load": 0,
"last_heartbeat_at": "",
},
"blocking_items": [stage_summary] if stage_code in {"pending_bootstrap", "runtime_only", "stale", "agent_pending"} else [],
"attention_items": [
"当前是兼容降级视图,如需完整 SSH / Token / delivery queue 详情,请升级控制面 API 到包含 handover 路由的版本。"
],
"bootstrap_defaults": {
"control_plane_base_url": "",
"root_dir": "/opt/domaincheck",
"node_region": "",
"node_role": "",
},
"endpoints": {
"detail": f"/api/v1/ops/nodes/{target_node_code}/handover",
"bootstrap_plan": f"/api/v1/ops/nodes/{target_node_code}/handover/bootstrap-plan",
},
}
print(json.dumps(result, ensure_ascii=False))
PY
)"
if [[ -n "${fallback_json}" ]]; then
effective_http_status="200"
fi
fi
fi
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
if [[ -n "${fallback_json}" ]]; then
echo
printf '%s\n' "fallback=stack_diagnosis"
print_json_or_raw "${fallback_json}"
fi
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${effective_http_status}" "${fallback_json}"
import json
import pathlib
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
fallback_json = str(sys.argv[3] or "").strip()
if fallback_json:
data = json.loads(fallback_json)
else:
data = payload.get("data") or {}
stage = data.get("stage") or {}
readiness = data.get("readiness") or {}
ssh_profile = data.get("ssh_profile") or {}
cluster = data.get("cluster") or {}
bootstrap_defaults = data.get("bootstrap_defaults") or {}
node_code = str(data.get("node_code") or "").strip()
next_step = stage.get("next_step") or {}
print(json.dumps({
"http_status": http_status,
"backend_http_status": int(data.get("backend_http_status", 0) or 0) if fallback_json else http_status,
"fallback_used": bool(data.get("fallback_used", False)),
"fallback_source": str(data.get("fallback_source") or ""),
"compatibility_warning": str(data.get("compatibility_warning") or ""),
"node_code": node_code,
"summary": str(data.get("summary") or ""),
"stage": {
"code": str(stage.get("code") or ""),
"label": str(stage.get("label") or ""),
"summary": str(stage.get("summary") or ""),
"next_step": next_step,
},
"readiness": {
"is_managed": bool(readiness.get("is_managed", False)),
"is_enabled": bool(readiness.get("is_enabled", False)),
"has_ssh_access": bool(readiness.get("has_ssh_access", False)),
"is_agent_online": bool(readiness.get("is_agent_online", False)),
"is_remote_access_ready": bool(readiness.get("is_remote_access_ready", False)),
},
"ssh_profile": {
"host": str(ssh_profile.get("host") or ""),
"port": int(ssh_profile.get("port", 22) or 22),
"user": str(ssh_profile.get("user") or ""),
"label": str(ssh_profile.get("label") or ""),
"reason": str(ssh_profile.get("reason") or ""),
"ssh_command_hint": str(ssh_profile.get("ssh_command_hint") or ""),
},
"cluster": {
"status": str(cluster.get("status") or ""),
"current_load": int(cluster.get("current_load", 0) or 0),
"last_heartbeat_at": str(cluster.get("last_heartbeat_at") or ""),
},
"blocking_items": list(data.get("blocking_items") or []),
"attention_items": list(data.get("attention_items") or []),
"bootstrap_defaults": {
"control_plane_base_url": str(bootstrap_defaults.get("control_plane_base_url") or ""),
"root_dir": str(bootstrap_defaults.get("root_dir") or ""),
"node_region": str(bootstrap_defaults.get("node_region") or ""),
"node_role": str(bootstrap_defaults.get("node_role") or ""),
},
"endpoints": data.get("endpoints") or {},
"recommended_commands": {
"bootstrap_plan": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-plan http://127.0.0.1:8100 {node_code}"
if node_code
else ""
),
"delivery_queue": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue http://127.0.0.1:8100 {node_code}"
if node_code
else ""
),
"node_agent_check": "bash domain-api/deploy/multi-region/check_node_agent.sh",
"ssh_hint": str(ssh_profile.get("ssh_command_hint") or ""),
},
}, ensure_ascii=False, indent=2))
PY
}
show_ops_nodes() {
local mainland_base_url="$1"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
summary = data.get("summary") or {}
nodes = [dict(item or {}) for item in list(data.get("nodes") or []) if isinstance(item, dict)]
rows = []
for node in nodes:
rows.append({
"node_code": str(node.get("node_code") or "").strip(),
"region": str(node.get("region") or "").strip(),
"role": str(node.get("role") or "").strip(),
"agent_state": str(node.get("agent_state") or "").strip(),
"remote_access_state": str(node.get("remote_access_state") or "").strip(),
"ssh_access_label": str(node.get("ssh_access_label") or "").strip(),
"ssh_entry": (
f"{str(node.get('ssh_user') or '').strip()}@{str(node.get('ssh_host') or '').strip()}:{int(node.get('ssh_port') or 22)}"
if str(node.get("ssh_host") or "").strip() and str(node.get("ssh_user") or "").strip()
else ""
),
"participation_state": str(node.get("participation_state") or "").strip(),
"cluster_status": str(node.get("cluster_status") or "").strip(),
"current_load": int(node.get("cluster_current_load", 0) or 0),
})
print(json.dumps({
"http_status": http_status,
"summary": {
"total": int(summary.get("total", len(nodes)) or 0),
"managed_enabled": int(summary.get("managed_enabled", 0) or 0),
"agent_ready": int(summary.get("agent_ready", 0) or 0),
"ssh_ready": int(summary.get("ssh_ready", 0) or 0),
"remote_access_ready": int(summary.get("remote_access_ready", 0) or 0),
"participating": int(summary.get("participating", 0) or 0),
"queue_dead_letter_nodes": int(summary.get("queue_dead_letter_nodes", 0) or 0),
"remote_access_state_counts": dict(summary.get("remote_access_state_counts") or {}),
},
"nodes": rows,
}, ensure_ascii=False, indent=2))
PY
}
bind_node_ssh_profile() {
local mainland_base_url="$1"
local node_code="$2"
local ssh_host="$3"
local ssh_user="$4"
local ssh_port="${5:-22}"
local title="${6:-}"
if [[ -z "${node_code}" || -z "${ssh_host}" || -z "${ssh_user}" ]]; then
echo "usage: node-bind-ssh [mainland_base_url] <node_code> <ssh_host> <ssh_user> [ssh_port] [title]" >&2
exit 1
fi
local request_json
request_json="$(python3 - <<'PY' "${node_code}" "${ssh_host}" "${ssh_user}" "${ssh_port}" "${title}"
import json
import sys
node_code = str(sys.argv[1] or "").strip()
ssh_host = str(sys.argv[2] or "").strip()
ssh_user = str(sys.argv[3] or "").strip()
ssh_port = int(sys.argv[4] or 22)
title = str(sys.argv[5] or "").strip()
payload = {
"node_code": node_code,
"ssh_host": ssh_host,
"ssh_user": ssh_user,
"ssh_port": ssh_port,
"auth_mode": "key",
"metadata": {
"ssh_bound_via": "drive_ops_center.sh/node-bind-ssh",
},
}
if title:
payload["title"] = title
print(json.dumps(payload, ensure_ascii=False))
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[1/3] save managed node ssh profile"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo "[2/3] node handover after binding"
show_node_handover "${mainland_base_url}" "${node_code}"
echo
echo "[3/3] condensed ssh binding"
python3 - <<'PY' "${response_json}" "${http_status}" "${node_code}" "${ssh_host}" "${ssh_user}" "${ssh_port}" "${mainland_base_url}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
node_code = str(sys.argv[3] or "").strip()
ssh_host = str(sys.argv[4] or "").strip()
ssh_user = str(sys.argv[5] or "").strip()
ssh_port = int(sys.argv[6] or 22)
base_url = str(sys.argv[7] or "http://127.0.0.1:8100").strip() or "http://127.0.0.1:8100"
data = payload.get("data") or {}
node = dict(data.get("node") or {})
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"node_code": node_code,
"ssh_entry": f"{ssh_user}@{ssh_host}:{ssh_port}",
"saved_title": str(node.get("title") or "").strip(),
"saved_region": str(node.get("region") or "").strip(),
"saved_role": str(node.get("role") or "").strip(),
"next_recommended_commands": {
"nodes": f"bash domain-api/deploy/multi-region/drive_ops_center.sh nodes {base_url}",
"handover": f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-handover {base_url} {node_code}",
"bootstrap_plan": f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-plan {base_url} {node_code}",
},
}, ensure_ascii=False, indent=2))
PY
}
show_node_scene_log() {
local mainland_base_url="$1"
local node_code="$2"
local limit="${3:-80}"
local mode="${4:-}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local request_url
request_url="$(python3 - <<'PY' "${mainland_base_url}" "${node_code}" "${limit}" "${mode}"
import sys
from urllib.parse import quote, urlencode
base_url = str(sys.argv[1] or "").rstrip("/")
node_code = str(sys.argv[2] or "").strip()
limit = max(1, min(int(sys.argv[3] or 80), 400))
mode = str(sys.argv[4] or "").strip().lower()
params = {"limit": limit}
if mode in {"key", "full"}:
params["mode"] = mode
endpoint = f"{base_url}/api/v1/ops/nodes/{quote(node_code, safe='')}/scene-log"
query_string = urlencode(params)
print(f"{endpoint}?{query_string}" if query_string else endpoint)
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${request_url}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
local effective_http_status="${http_status}"
local fallback_json=""
if [[ "${http_status}" == "404" ]]; then
local runtime_http_response runtime_http_status runtime_response_json
runtime_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/runtime/status")"
runtime_http_status="${runtime_http_response##*$'\n'HTTP_STATUS=}"
runtime_response_json="${runtime_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${runtime_http_status}" -ge 200 && "${runtime_http_status}" -lt 300 ]]; then
fallback_json="$(python3 - <<'PY' "${runtime_response_json}" "${node_code}" "${limit}" "${mode}" "${http_status}"
import json
import re
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
node_code = str(sys.argv[2] or "").strip()
limit = max(1, min(int(sys.argv[3] or 80), 400))
requested_mode = str(sys.argv[4] or "").strip().lower()
backend_http_status = int(sys.argv[5] or 404)
data = dict(payload.get("data") or {})
detect = dict(data.get("detect") or {})
cluster = dict(data.get("cluster") or {})
log_sync = dict(detect.get("log_sync") or {})
mode = "full" if requested_mode == "full" else ("full" if str(log_sync.get("mode") or "").strip().lower() == "full" else "key")
cluster_nodes = [dict(item or {}) for item in list(cluster.get("nodes") or []) if isinstance(item, dict)]
cluster_map = {str(item.get("node_code") or "").strip(): item for item in cluster_nodes if str(item.get("node_code") or "").strip()}
participating_nodes = [dict(item or {}) for item in list(detect.get("participating_nodes") or []) if isinstance(item, dict)]
standby_nodes = [dict(item or {}) for item in list(detect.get("non_participating_nodes") or []) if isinstance(item, dict)]
participating_map = {str(item.get("node_code") or "").strip(): item for item in participating_nodes if str(item.get("node_code") or "").strip()}
standby_map = {str(item.get("node_code") or "").strip(): item for item in standby_nodes if str(item.get("node_code") or "").strip()}
preview_lines = [str(item or "").strip() for item in list(log_sync.get("preview_lines") or []) if str(item or "").strip()]
pattern = re.compile(r"^\[(?P<created_at>[^\]]+)\]\s+\[(?P<node_code>[^\]]+)\]\s+(?P<message>.+)$")
node_preview_lines = []
for line in preview_lines:
match = pattern.match(line)
if not match:
continue
if str(match.group("node_code") or "").strip() != node_code:
continue
node_preview_lines.append({
"created_at": str(match.group("created_at") or "").strip(),
"node_code": node_code,
"message": str(match.group("message") or "").strip(),
"line": line,
"mode": "key",
})
node_preview_lines = node_preview_lines[-limit:]
source_summary_map = {
str(item.get("node_code") or "").strip(): dict(item or {})
for item in list(log_sync.get("source_node_summaries") or [])
if isinstance(item, dict) and str(item.get("node_code") or "").strip()
}
missing_summary_map = {
str(item.get("node_code") or "").strip(): dict(item or {})
for item in list(log_sync.get("missing_participating_node_summaries") or [])
if isinstance(item, dict) and str(item.get("node_code") or "").strip()
}
covered_summary_map = {
str(item.get("node_code") or "").strip(): dict(item or {})
for item in list(log_sync.get("covered_participating_node_summaries") or [])
if isinstance(item, dict) and str(item.get("node_code") or "").strip()
}
node_meta = dict(participating_map.get(node_code) or standby_map.get(node_code) or {})
cluster_meta = dict(cluster_map.get(node_code) or {})
source_summary = dict(source_summary_map.get(node_code) or covered_summary_map.get(node_code) or {})
missing_summary = dict(missing_summary_map.get(node_code) or {})
derived_line_count = len(node_preview_lines)
derived_last_at = str((node_preview_lines[-1] if node_preview_lines else {}).get("created_at") or "").strip()
derived_last_line = str((node_preview_lines[-1] if node_preview_lines else {}).get("line") or "").strip()
source_summary = {
"node_code": node_code,
"line_count": max(int(source_summary.get("line_count", 0) or 0), derived_line_count),
"key_line_count": max(int(source_summary.get("key_line_count", 0) or 0), derived_line_count),
"full_line_count": int(source_summary.get("full_line_count", 0) or 0),
"last_at": derived_last_at or str(source_summary.get("last_at") or "").strip(),
"last_line": derived_last_line or str(source_summary.get("last_line") or "").strip(),
}
log_sync_enabled = bool(log_sync.get("enabled", False))
detect_participating = node_code in participating_map
standby_visible = node_code in standby_map
record_available = bool(source_summary.get("line_count", 0) or node_preview_lines)
if record_available:
if detect_participating:
status = "full_capture" if mode == "full" else "healthy"
else:
status = "historical_sample"
elif missing_summary:
missing_reason_code = str(missing_summary.get("missing_reason_code") or "").strip()
if missing_reason_code == "disabled":
status = "disabled"
elif missing_reason_code == "waiting_sample":
status = "waiting_sample"
else:
status = "missing_sample"
elif detect_participating:
status = "waiting_sample" if log_sync_enabled else "disabled"
elif standby_visible:
status = "standby"
else:
status = "unknown"
status_labels = {
"healthy": ("关键覆盖", "success"),
"full_capture": ("全量观察", "success"),
"historical_sample": ("历史样本", "success"),
"waiting_sample": ("等待样本", "warning"),
"missing_sample": ("缺少样本", "warning"),
"disabled": ("已关闭", "info"),
"standby": ("在线待命", "info"),
"unknown": ("未发现", "info"),
}
status_label, status_type = status_labels.get(status, ("待确认", "info"))
if record_available and detect_participating:
summary = f"节点当前正在参与检测,已通过兼容降级视图识别到 {int(source_summary.get('line_count', 0) or 0)} 条现场日志样本。"
elif record_available:
summary = f"节点已通过兼容降级视图识别到 {int(source_summary.get('line_count', 0) or 0)} 条现场日志样本。"
elif missing_summary:
summary = str(missing_summary.get("missing_reason") or "").strip() or "当前还没有收到该节点的现场日志样本。"
elif detect_participating and log_sync_enabled:
summary = "节点正在参与检测,但当前现场日志样本仍未形成。"
elif detect_participating:
summary = "节点正在参与检测,但远端日志回传当前关闭。"
elif standby_visible:
summary = "节点当前在线但未参与本轮检测,暂时没有新的现场日志样本。"
else:
summary = "当前没有找到该节点的现场日志记录。"
latest_record = dict(node_preview_lines[-1] or {}) if node_preview_lines else {}
missing_reason_code = str(missing_summary.get("missing_reason_code") or "").strip()
missing_reason = str(missing_summary.get("missing_reason") or "").strip()
result = {
"http_status": 200,
"backend_http_status": backend_http_status,
"fallback_used": True,
"fallback_source": "runtime_status",
"fallback_reason": "running_api_missing_scene_log_route",
"compatibility_warning": "当前运行中的 API 尚未暴露 /api/v1/ops/nodes/{node_code}/scene-log已降级为 runtime/status 兼容视图。",
"node_code": node_code,
"status": status,
"status_label": status_label,
"status_type": status_type,
"mode": mode,
"mode_label": "全量回传" if mode == "full" else "关键回传",
"summary": summary,
"node": {
"region": str(node_meta.get("region") or cluster_meta.get("region") or missing_summary.get("region") or "").strip(),
"role": str(node_meta.get("role") or cluster_meta.get("role") or missing_summary.get("role") or "").strip(),
"status": str(node_meta.get("status") or cluster_meta.get("status") or "").strip(),
"current_load": int(node_meta.get("current_load", cluster_meta.get("current_load", 0)) or 0),
"last_heartbeat_at": str(node_meta.get("last_heartbeat_at") or cluster_meta.get("last_heartbeat_at") or "").strip(),
},
"participation": {
"detect_participating": detect_participating,
"participation_label": str(node_meta.get("participation_label") or "").strip(),
"participation_bucket": str(node_meta.get("participation_bucket") or "").strip(),
"participation_reason": str(node_meta.get("participation_reason") or "").strip(),
},
"source_summary": {
"line_count": int(source_summary.get("line_count", 0) or 0),
"key_line_count": int(source_summary.get("key_line_count", 0) or 0),
"full_line_count": int(source_summary.get("full_line_count", 0) or 0),
"last_at": str(source_summary.get("last_at") or "").strip(),
"last_line": str(source_summary.get("last_line") or "").strip(),
},
"missing_reason_code": missing_reason_code,
"missing_reason": missing_reason,
"records_total": int(source_summary.get("line_count", 0) or 0),
"records_visible": len(node_preview_lines),
"records_truncated": False,
"latest_record": latest_record,
"preview_lines": [str(item.get("line") or "").strip() for item in node_preview_lines[-8:]],
}
print(json.dumps(result, ensure_ascii=False))
PY
)"
if [[ -n "${fallback_json}" ]]; then
effective_http_status="200"
fi
fi
fi
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
printf '%s\n' "request_url=${request_url}"
print_json_or_raw "${response_json}"
if [[ -n "${fallback_json}" ]]; then
echo
printf '%s\n' "fallback=runtime_status"
print_json_or_raw "${fallback_json}"
fi
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${effective_http_status}" "${fallback_json}"
import json
import pathlib
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
fallback_json = str(sys.argv[3] or "").strip()
if fallback_json:
data = json.loads(fallback_json)
node = data.get("node") or {}
participation = data.get("participation") or {}
source_summary = data.get("source_summary") or {}
latest_record = data.get("latest_record") or {}
preview_lines = list(data.get("preview_lines") or [])
else:
data = payload.get("data") or {}
node = data.get("node") or {}
participation = data.get("participation") or {}
source_summary = data.get("source_summary") or {}
records = [dict(item or {}) for item in list(data.get("records") or []) if dict(item or {})]
latest_record = data.get("latest_record") or {}
preview_lines = [str(item.get("line") or "") for item in records[-8:]]
print(json.dumps({
"http_status": http_status,
"backend_http_status": int(data.get("backend_http_status", 0) or 0) if fallback_json else http_status,
"fallback_used": bool(data.get("fallback_used", False)),
"fallback_source": str(data.get("fallback_source") or ""),
"compatibility_warning": str(data.get("compatibility_warning") or ""),
"node_code": str(data.get("node_code") or ""),
"status": str(data.get("status") or ""),
"status_label": str(data.get("status_label") or ""),
"mode": str(data.get("mode") or ""),
"mode_label": str(data.get("mode_label") or ""),
"summary": str(data.get("summary") or ""),
"node": {
"region": str(node.get("region") or ""),
"role": str(node.get("role") or ""),
"status": str(node.get("status") or ""),
"current_load": int(node.get("current_load", 0) or 0),
"last_heartbeat_at": str(node.get("last_heartbeat_at") or ""),
},
"participation": {
"detect_participating": bool(participation.get("detect_participating", False)),
"participation_label": str(participation.get("participation_label") or ""),
"participation_bucket": str(participation.get("participation_bucket") or ""),
"participation_reason": str(participation.get("participation_reason") or ""),
},
"source_summary": {
"line_count": int(source_summary.get("line_count", 0) or 0),
"key_line_count": int(source_summary.get("key_line_count", 0) or 0),
"full_line_count": int(source_summary.get("full_line_count", 0) or 0),
"last_at": str(source_summary.get("last_at") or ""),
"last_line": str(source_summary.get("last_line") or ""),
},
"missing_reason_code": str(data.get("missing_reason_code") or ""),
"missing_reason": str(data.get("missing_reason") or ""),
"records_total": int(data.get("records_total", 0) or 0),
"records_visible": int(data.get("records_visible", 0) or 0),
"records_truncated": bool(data.get("records_truncated", False)),
"latest_record": latest_record,
"preview_lines": [str(item or "") for item in preview_lines],
}, ensure_ascii=False, indent=2))
PY
}
build_node_handover_bootstrap_plan() {
local mainland_base_url="$1"
local node_code="$2"
local issued_by="$3"
local expires_in_hours="$4"
local control_plane_base_url="$5"
local root_dir="$6"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local request_json
request_json="$(python3 - <<'PY' "${issued_by}" "${expires_in_hours}" "${control_plane_base_url}" "${root_dir}"
import json
import sys
issued_by = str(sys.argv[1] or "cli/ops-center/node-bootstrap-plan").strip() or "cli/ops-center/node-bootstrap-plan"
expires_in_hours = max(1, min(int(sys.argv[2] or 72), 24 * 30))
control_plane_base_url = str(sys.argv[3] or "").strip()
root_dir = str(sys.argv[4] or "/opt/domaincheck").strip() or "/opt/domaincheck"
print(json.dumps({
"issued_by": issued_by,
"expires_in_hours": expires_in_hours,
"control_plane_base_url": control_plane_base_url,
"root_dir": root_dir,
"metadata": {
"issued_from": "cli-node-handover-bootstrap-plan",
},
}, ensure_ascii=False))
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/handover/bootstrap-plan" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
local effective_http_status="${http_status}"
local fallback_json=""
if [[ "${http_status}" == "404" ]]; then
local generic_request_json generic_http_response generic_http_status generic_response_json
generic_request_json="$(python3 - <<'PY' "${node_code}" "${issued_by}" "${expires_in_hours}" "${control_plane_base_url}" "${root_dir}"
import json
import sys
node_code = str(sys.argv[1] or "").strip()
issued_by = str(sys.argv[2] or "cli/ops-center/node-bootstrap-plan").strip() or "cli/ops-center/node-bootstrap-plan"
expires_in_hours = max(1, min(int(sys.argv[3] or 72), 24 * 30))
control_plane_base_url = str(sys.argv[4] or "").strip()
root_dir = str(sys.argv[5] or "/opt/domaincheck").strip() or "/opt/domaincheck"
normalized = node_code.lower()
node_region = "overseas" if normalized.startswith("overseas-") else "mainland"
node_role = "control" if ("controller" in normalized or "-control" in normalized) else "worker"
print(json.dumps({
"node_code": node_code,
"node_region": node_region,
"node_role": node_role,
"issued_by": issued_by,
"expires_in_hours": expires_in_hours,
"control_plane_base_url": control_plane_base_url,
"root_dir": root_dir,
"metadata": {
"issued_from": "cli-node-handover-bootstrap-plan-fallback",
"fallback_used": True,
},
}, ensure_ascii=False))
PY
)"
generic_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/agent/bootstrap-plan" "${generic_request_json}")"
generic_http_status="${generic_http_response##*$'\n'HTTP_STATUS=}"
generic_response_json="${generic_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${generic_http_status}" -ge 200 && "${generic_http_status}" -lt 300 ]]; then
fallback_json="$(python3 - <<'PY' "${generic_response_json}" "${http_status}" "${node_code}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
backend_http_status = int(sys.argv[2] or 404)
node_code = str(sys.argv[3] or "").strip()
data = payload.get("data") or {}
bootstrap_plan = dict(data.get("bootstrap_plan") or {})
print(json.dumps({
"http_status": 200,
"backend_http_status": backend_http_status,
"fallback_used": True,
"fallback_source": "ops_agent_bootstrap_plan",
"compatibility_warning": "当前运行中的 API 尚未暴露节点级 /handover/bootstrap-plan已降级为通用 /ops/agent/bootstrap-plan。",
"message": str(payload.get("message") or ""),
"node_code": node_code,
"token_preview": str(data.get("token_preview") or ""),
"expires_at": str(data.get("expires_at") or ""),
"control_plane_base_url": str(data.get("control_plane_base_url") or ""),
"bootstrap_plan": bootstrap_plan,
"handover_stage": {
"code": "bootstrap_plan_issued",
"label": "已生成接入方案",
"summary": "当前已通过通用 bootstrap-plan 接口生成接入方案,可直接去目标节点执行。",
},
}, ensure_ascii=False))
PY
)"
effective_http_status="200"
fi
fi
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
if [[ -n "${fallback_json}" ]]; then
echo
printf '%s\n' "fallback=ops_agent_bootstrap_plan"
print_json_or_raw "${fallback_json}"
fi
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${effective_http_status}" "${fallback_json}"
import json
import pathlib
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
fallback_json = str(sys.argv[3] or "").strip()
if fallback_json:
data = json.loads(fallback_json)
plan = data.get("bootstrap_plan") or {}
stage = data.get("handover_stage") or {}
node_code = str(data.get("node_code") or "").strip()
else:
data = payload.get("data") or {}
plan = data.get("bootstrap_plan") or {}
handover = data.get("handover") or {}
stage = handover.get("stage") or {}
node_code = str(data.get("node_code") or handover.get("node_code") or "").strip()
workspace_root = pathlib.Path.cwd()
ops_agent_service_path = workspace_root / "domain-api" / "app" / "services" / "ops_agent_service.py"
bootstrap_script_path = workspace_root / "domain-api" / "deploy" / "multi-region" / "bootstrap_node_agent.sh"
ops_agent_service_text = ""
bootstrap_script_text = ""
try:
ops_agent_service_text = ops_agent_service_path.read_text(encoding="utf-8", errors="replace")
except Exception:
ops_agent_service_text = ""
try:
bootstrap_script_text = bootstrap_script_path.read_text(encoding="utf-8", errors="replace")
except Exception:
bootstrap_script_text = ""
repo_supports_install_command_block = (
"install_command_block" in ops_agent_service_text
and "_candidate_node_agent_install_paths" in ops_agent_service_text
)
repo_supports_multi_layout_bootstrap = "resolve_project_root" in bootstrap_script_text
install_command_block_present = bool(str(plan.get("install_command_block") or "").strip())
runtime_may_need_restart = bool(repo_supports_install_command_block and not install_command_block_present)
runtime_restart_hint = (
"仓库代码已支持 install_command_block但本次接口响应里仍未返回当前运行中的控制面 API 可能还没重启到最新代码。"
if runtime_may_need_restart
else ""
)
print(json.dumps({
"http_status": http_status,
"backend_http_status": int(data.get("backend_http_status", 0) or 0) if fallback_json else http_status,
"fallback_used": bool(data.get("fallback_used", False)),
"fallback_source": str(data.get("fallback_source") or ""),
"compatibility_warning": str(data.get("compatibility_warning") or ""),
"runtime_restart_hint": runtime_restart_hint,
"message": str(payload.get("message") or ""),
"node_code": node_code,
"token_preview": str(data.get("token_preview") or ""),
"expires_at": str(data.get("expires_at") or ""),
"control_plane_base_url": str(data.get("control_plane_base_url") or ""),
"bootstrap_plan": {
"env_file": str(plan.get("env_file") or ""),
"service_name": str(plan.get("service_name") or ""),
"bootstrap_script_name": str(plan.get("bootstrap_script_name") or ""),
"bootstrap_script_path": str(plan.get("bootstrap_script_path") or ""),
"install_command_block_present": install_command_block_present,
"bootstrap_run_script_block_present": bool(str(plan.get("bootstrap_run_script_block") or "").strip()),
},
"repo_capability": {
"supports_install_command_block": repo_supports_install_command_block,
"supports_multi_layout_bootstrap": repo_supports_multi_layout_bootstrap,
"runtime_may_need_restart": runtime_may_need_restart,
},
"handover_stage": {
"code": str(stage.get("code") or ""),
"label": str(stage.get("label") or ""),
"summary": str(stage.get("summary") or ""),
},
"recommended_commands": {
"node_handover": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-handover http://127.0.0.1:8100 {node_code}"
if node_code
else ""
),
"delivery_queue": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue http://127.0.0.1:8100 {node_code}"
if node_code
else ""
),
"restart_api_if_runtime_stale": (
"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover http://127.0.0.1:8100"
if runtime_may_need_restart
else ""
),
},
}, ensure_ascii=False, indent=2))
PY
}
show_stack_first_gap_handover() {
local mainland_base_url="$1"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
local first_gap_node_code
first_gap_node_code="$(python3 - <<'PY' "${response_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
first_gap = managed_nodes.get("first_handover_gap") or {}
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if isinstance(item, dict)]
node_code = str(
first_gap.get("node_code")
or managed_nodes.get("first_handover_gap_node_code")
or ((problem_nodes[0] or {}).get("node_code") if problem_nodes else "")
or ""
).strip()
print(node_code)
PY
)"
if [[ -z "${first_gap_node_code}" ]]; then
python3 - <<'PY' "${http_status}" "${response_json}"
import json
import sys
http_status = int(sys.argv[1] or 0)
payload = json.loads(str(sys.argv[2] or "{}"))
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
print(json.dumps({
"http_status": http_status,
"message": "当前 stack diagnosis 没有首个 handover gap 节点。",
"managed_nodes": {
"managed_enabled": int(managed_nodes.get("managed_enabled", 0) or 0),
"remote_access_ready": int(managed_nodes.get("remote_access_ready", 0) or 0),
"problem_nodes": list(managed_nodes.get("problem_nodes") or [])[:5],
},
}, ensure_ascii=False, indent=2))
PY
return 0
fi
show_node_handover "${mainland_base_url}" "${first_gap_node_code}"
}
show_node_onboarding() {
local mainland_base_url="$1"
local node_code="$2"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
local effective_http_status="${http_status}"
local fallback_json=""
if [[ "${http_status}" == "404" ]]; then
local handover_http_response handover_http_status handover_response_json
handover_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/handover")"
handover_http_status="${handover_http_response##*$'\n'HTTP_STATUS=}"
handover_response_json="${handover_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${handover_http_status}" == "404" ]]; then
local stack_http_response stack_http_status stack_response_json
stack_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
stack_http_status="${stack_http_response##*$'\n'HTTP_STATUS=}"
stack_response_json="${stack_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${stack_http_status}" -ge 200 && "${stack_http_status}" -lt 300 ]]; then
handover_response_json="$(python3 - <<'PY' "${stack_response_json}" "${node_code}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
target_node_code = str(sys.argv[2] or "").strip()
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if isinstance(item, dict)]
first_gap = dict(managed_nodes.get("first_handover_gap") or {})
node = next((item for item in problem_nodes if str(item.get("node_code") or "").strip() == target_node_code), {})
if not node and str(first_gap.get("node_code") or "").strip() == target_node_code:
node = dict(first_gap.get("node") or {})
if not node:
node = {"node_code": target_node_code}
agent_state = str(node.get("agent_state") or "").strip()
remote_access_state = str(node.get("remote_access_state") or "").strip()
stage_code = "unknown"
stage_label = "待判断"
stage_summary = "当前节点存在接管缺口,建议先生成 bootstrap 方案并检查 Node Agent 接入。"
if agent_state == "pending_bootstrap":
stage_code = "pending_bootstrap"
stage_label = "待执行接入"
stage_summary = "已签发有效 Token但目标节点尚未执行 bootstrap 片段。"
elif agent_state == "runtime_only":
stage_code = "runtime_only"
stage_label = "仅运行态在线"
stage_summary = "控制面能看到 runtime 心跳,但 Node Agent 尚未接管。"
elif agent_state == "stale":
stage_code = "stale"
stage_label = "心跳过期"
stage_summary = "Node Agent 曾接入过,但心跳已过期,需先恢复服务或重新接入。"
elif remote_access_state == "agent_pending":
stage_code = "agent_pending"
stage_label = "待接入"
stage_summary = "当前 remote-agent 仍未就绪,先补齐 bootstrap 接入链路。"
print(json.dumps({
"data": {
"node_code": target_node_code,
"summary": stage_summary,
"stage": {
"code": stage_code,
"label": stage_label,
"summary": stage_summary,
},
"readiness": {
"is_managed": True,
"is_enabled": True,
"has_ssh_access": False,
"is_agent_online": agent_state in {"online", "online_busy"},
"is_remote_access_ready": False,
},
}
}, ensure_ascii=False))
PY
)"
handover_http_status="200"
fi
fi
if [[ "${handover_http_status}" -ge 200 && "${handover_http_status}" -lt 300 ]]; then
fallback_json="$(python3 - <<'PY' "${handover_response_json}" "${node_code}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
node_code = str(sys.argv[2] or "").strip()
backend_http_status = int(sys.argv[3] or 404)
data = payload.get("data") or {}
readiness = data.get("readiness") or {}
stage = data.get("stage") or {}
is_agent_online = bool(readiness.get("is_agent_online", False))
has_ssh_access = bool(readiness.get("has_ssh_access", False))
is_remote_access_ready = bool(readiness.get("is_remote_access_ready", False))
acceptance_execution_mode = ""
acceptance_status_code = "blocked"
acceptance_status_label = "暂不可验收"
acceptance_summary = "当前还没进入标准接管就绪状态,暂不建议直接跑 onboarding.acceptance。"
if is_agent_online:
acceptance_execution_mode = "remote-agent"
acceptance_status_code = "ready"
acceptance_status_label = "可远端验收"
acceptance_summary = "Node Agent 已在线,可以直接跑 onboarding.acceptance 做标准接管验收。"
elif has_ssh_access:
acceptance_execution_mode = "ssh"
acceptance_status_code = "ssh_only"
acceptance_status_label = "可走 SSH 验收"
acceptance_summary = "当前 Agent 还没在线,但 SSH 已备好,可先走 SSH 模式做接管验收。"
onboarding_stage_code = str(stage.get("code") or "").strip() or "unknown"
onboarding_stage_label = str(stage.get("label") or "").strip() or "待判断"
onboarding_summary = str(data.get("summary") or stage.get("summary") or "").strip()
if acceptance_status_code in {"ready", "ssh_only"} and onboarding_stage_code == "ready":
onboarding_stage_code = "acceptance_ready"
onboarding_stage_label = "待接管验收"
onboarding_summary = acceptance_summary
actions = []
if onboarding_stage_code in {"pending_bootstrap", "runtime_only", "profile_incomplete", "ssh_ready", "token_issue", "stale"}:
actions.append({
"key": "bootstrap_plan",
"label": "生成接入方案",
"summary": "先生成并执行 Node Agent bootstrap 方案,再观察 register / heartbeat 是否建立。",
})
if acceptance_status_code in {"ready", "ssh_only"}:
actions.append({
"key": "run_acceptance",
"label": "执行接管验收",
"summary": acceptance_summary,
})
recovery_decision = {
"action": "noop",
"label": "当前无需额外恢复动作",
"summary": onboarding_summary or "当前节点暂无需要执行的接管恢复动作。",
"command_hint": "",
"needs_confirmation": True,
"window": "none",
"stage_code": onboarding_stage_code,
"acceptance_status_code": acceptance_status_code,
}
if onboarding_stage_code == "acceptance_ready" or is_agent_online:
recovery_decision = {
"action": "run_acceptance",
"label": "执行接管验收",
"summary": acceptance_summary,
"command_hint": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-run http://127.0.0.1:8100 {node_code} cli"
if node_code else ""
),
"needs_confirmation": True,
"window": "acceptance",
"stage_code": onboarding_stage_code,
"acceptance_status_code": acceptance_status_code,
}
elif onboarding_stage_code in {"pending_bootstrap", "runtime_only", "profile_incomplete", "ssh_ready", "token_issue", "stale", "agent_pending", "unknown", ""}:
recovery_decision = {
"action": "bootstrap_run",
"label": "签发接入工单",
"summary": "当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap 并等待 register / heartbeat 建立。",
"command_hint": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-run http://127.0.0.1:8100 {node_code} cli"
if node_code else ""
),
"needs_confirmation": True,
"window": "bootstrap",
"stage_code": onboarding_stage_code,
"acceptance_status_code": acceptance_status_code,
}
print(json.dumps({
"http_status": 200,
"backend_http_status": backend_http_status,
"fallback_used": True,
"fallback_source": "node_handover",
"compatibility_warning": "当前运行中的 API 尚未暴露 /api/v1/ops/nodes/{node_code}/onboarding已降级为 handover 兼容视图。",
"node_code": node_code,
"summary": onboarding_summary,
"onboarding_stage": {
"code": onboarding_stage_code,
"label": onboarding_stage_label,
"summary": onboarding_summary,
},
"handover": data,
"acceptance": {
"status_code": acceptance_status_code,
"status_label": acceptance_status_label,
"summary": acceptance_summary,
"execution_mode": acceptance_execution_mode,
"is_agent_online": is_agent_online,
"has_ssh_access": has_ssh_access,
"is_remote_access_ready": is_remote_access_ready,
"playbook": {
"playbook_key": "onboarding.acceptance",
"target_node_codes": [node_code] if node_code else [],
"execution_mode": acceptance_execution_mode or "remote-agent",
"auto_approve": True,
},
},
"recommended_actions": actions,
"recovery_decision": recovery_decision,
}, ensure_ascii=False))
PY
)"
effective_http_status="200"
fi
fi
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
if [[ -n "${fallback_json}" ]]; then
echo
printf '%s\n' "fallback=node_handover"
print_json_or_raw "${fallback_json}"
fi
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${effective_http_status}" "${fallback_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
fallback_json = str(sys.argv[3] or "").strip()
data = json.loads(fallback_json) if fallback_json else (payload.get("data") or {})
onboarding_stage = data.get("onboarding_stage") or {}
acceptance = data.get("acceptance") or {}
recovery_decision = data.get("recovery_decision") or {}
playbook = acceptance.get("playbook") or {}
node_code = str(data.get("node_code") or "").strip()
print(json.dumps({
"http_status": http_status,
"backend_http_status": int(data.get("backend_http_status", 0) or 0) if fallback_json else http_status,
"fallback_used": bool(data.get("fallback_used", False)),
"fallback_source": str(data.get("fallback_source") or ""),
"compatibility_warning": str(data.get("compatibility_warning") or ""),
"node_code": node_code,
"summary": str(data.get("summary") or ""),
"onboarding_stage": {
"code": str(onboarding_stage.get("code") or ""),
"label": str(onboarding_stage.get("label") or ""),
"summary": str(onboarding_stage.get("summary") or ""),
},
"acceptance": {
"status_code": str(acceptance.get("status_code") or ""),
"status_label": str(acceptance.get("status_label") or ""),
"summary": str(acceptance.get("summary") or ""),
"execution_mode": str(acceptance.get("execution_mode") or ""),
"is_agent_online": bool(acceptance.get("is_agent_online", False)),
"has_ssh_access": bool(acceptance.get("has_ssh_access", False)),
"is_remote_access_ready": bool(acceptance.get("is_remote_access_ready", False)),
"playbook": playbook,
},
"recommended_actions": list(data.get("recommended_actions") or []),
"recovery_decision": {
"action": str(recovery_decision.get("action") or ""),
"label": str(recovery_decision.get("label") or ""),
"summary": str(recovery_decision.get("summary") or ""),
"command_hint": str(recovery_decision.get("command_hint") or ""),
"needs_confirmation": bool(recovery_decision.get("needs_confirmation", False)),
"window": str(recovery_decision.get("window") or ""),
"stage_code": str(recovery_decision.get("stage_code") or ""),
"acceptance_status_code": str(recovery_decision.get("acceptance_status_code") or ""),
},
"recommended_commands": {
"node_handover": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-handover http://127.0.0.1:8100 {node_code}"
if node_code else ""
),
"node_bootstrap_preview": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-preview http://127.0.0.1:8100 {node_code}"
if node_code else ""
),
"node_bootstrap_run": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-run http://127.0.0.1:8100 {node_code} cli"
if node_code else ""
),
"node_bootstrap_plan": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-plan http://127.0.0.1:8100 {node_code}"
if node_code else ""
),
"node_acceptance_plan": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-plan http://127.0.0.1:8100 {node_code}"
if node_code else ""
),
"node_acceptance_run": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-run http://127.0.0.1:8100 {node_code} cli"
if node_code else ""
),
"agent_gap_check": "bash domain-api/deploy/multi-region/drive_ops_center.sh agent-gap-check http://127.0.0.1:8100",
},
}, ensure_ascii=False, indent=2))
PY
}
recover_node_onboarding() {
local mainland_base_url="$1"
local node_code="$2"
local confirm_flag="${3:-}"
local requested_by="${4:-cli}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local recovery_endpoint
if [[ "${confirm_flag}" == "confirm" ]]; then
recovery_endpoint="${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding/recovery/execute"
else
recovery_endpoint="${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding/recovery/preview"
fi
local recovery_request_json
recovery_request_json="$(python3 - <<'PY' "${requested_by}"
import json
import sys
print(json.dumps({"requested_by": str(sys.argv[1] or "cli").strip() or "cli"}, ensure_ascii=False))
PY
)"
local raw_http_response
local source_mode="recovery_endpoint"
raw_http_response="$(request_with_status "POST" "${recovery_endpoint}" "${recovery_request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" == "404" ]]; then
source_mode="onboarding"
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding")"
http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
fi
if [[ "${http_status}" == "404" ]]; then
source_mode="handover"
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/handover")"
http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" == "404" ]]; then
source_mode="stack_diagnosis"
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" -ge 200 && "${http_status}" -lt 300 ]]; then
response_json="$(python3 - <<'PY' "${response_json}" "${node_code}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
target_node_code = str(sys.argv[2] or "").strip()
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if isinstance(item, dict)]
node = next((item for item in problem_nodes if str(item.get("node_code") or "").strip() == target_node_code), {})
first_gap = dict(managed_nodes.get("first_handover_gap") or {})
if not node and str(first_gap.get("node_code") or "").strip() == target_node_code:
node = dict(first_gap.get("node") or {})
agent_state = str(node.get("agent_state") or "").strip()
stage_code = "unknown"
stage_summary = "当前节点存在接管缺口,建议先生成 bootstrap 方案并检查 Node Agent 接入。"
if agent_state == "pending_bootstrap":
stage_code = "pending_bootstrap"
stage_summary = "已签发有效 Token但目标节点尚未执行 bootstrap 片段。"
elif agent_state == "runtime_only":
stage_code = "runtime_only"
stage_summary = "控制面能看到 runtime 心跳,但 Node Agent 尚未接管。"
elif agent_state == "stale":
stage_code = "stale"
stage_summary = "Node Agent 曾接入过,但心跳已过期,需先恢复服务或重新接入。"
elif str(node.get("remote_access_state") or "").strip() == "agent_pending":
stage_code = "agent_pending"
stage_summary = "当前 remote-agent 仍未就绪,先补齐 bootstrap 接入链路。"
print(json.dumps({
"data": {
"node_code": target_node_code,
"summary": stage_summary,
"onboarding_stage": {
"code": stage_code,
"summary": stage_summary,
},
"acceptance": {
"status_code": "blocked",
"summary": "当前还没进入标准接管就绪状态,暂不建议直接跑 onboarding.acceptance。",
},
}
}, ensure_ascii=False))
PY
)"
http_status="200"
fi
fi
fi
python3 - <<'PY' "${response_json}" "${node_code}" "${mainland_base_url}" "${confirm_flag}" "${requested_by}" "${http_status}" "${source_mode}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
node_code = str(sys.argv[2] or "").strip()
base_url = str(sys.argv[3] or "").strip()
confirm_flag = str(sys.argv[4] or "").strip()
requested_by = str(sys.argv[5] or "cli").strip() or "cli"
backend_http_status = int(sys.argv[6] or 0)
source_mode = str(sys.argv[7] or "").strip()
data = payload.get("data") or {}
if str(data.get("recovery_action") or "").strip():
recovery_decision = (data.get("recovery_decision") or {})
selected_preview = (data.get("selected_preview") or {})
selected_run = (data.get("selected_run") or {})
selected_payload = selected_run or selected_preview
playbook_preview = (selected_payload.get("playbook_preview") or {})
playbook_run = (selected_payload.get("playbook_run") or {})
follow_up = (selected_payload.get("follow_up") or {})
result = {
"node_code": node_code,
"source_mode": source_mode,
"backend_http_status": backend_http_status,
"stage_code": str((recovery_decision.get("stage_code") or "")).strip(),
"acceptance_status_code": str((recovery_decision.get("acceptance_status_code") or "")).strip(),
"recovery_action": str(data.get("recovery_action") or "").strip(),
"selected_action": "node_acceptance_run" if str(data.get("recovery_action") or "").strip() == "run_acceptance" else "node_bootstrap_run",
"summary": str((recovery_decision.get("summary") or payload.get("message") or "")).strip(),
"command": str((recovery_decision.get("command_hint") or "")).strip(),
"execution_mode": str((selected_payload.get("execution_mode") or "")).strip(),
"playbook_preview_key": str(((playbook_preview.get("playbook") or {}).get("key") or "")).strip(),
"playbook_run_code": str((playbook_run.get("run_code") or "")).strip(),
"follow_up_next_stage_code": str((follow_up.get("next_stage_code") or "")).strip(),
"follow_up_summary": str((follow_up.get("summary") or "")).strip(),
"follow_up_commands": list(follow_up.get("recommended_commands") or []),
"confirm_required": True,
"confirmed": confirm_flag == "confirm",
"handled_by_recovery_endpoint": source_mode == "recovery_endpoint",
}
print(json.dumps(result, ensure_ascii=False, indent=2))
raise SystemExit(0)
stage = data.get("onboarding_stage") or data.get("stage") or {}
acceptance = data.get("acceptance") or {}
recovery = data.get("recovery_decision") or {}
stage_code = str(stage.get("code") or "").strip()
acceptance_status_code = str(acceptance.get("status_code") or "").strip()
stage_summary = str(stage.get("summary") or data.get("summary") or "").strip()
if not stage_code:
handover_stage = data.get("stage") or {}
stage_code = str(handover_stage.get("code") or "").strip()
stage_summary = str(handover_stage.get("summary") or stage_summary).strip()
action = "noop"
reason = stage_summary or "当前节点暂无可执行恢复动作。"
command = ""
recovery_action = str(recovery.get("action") or "").strip()
recovery_command_hint = str(recovery.get("command_hint") or "").strip()
recovery_summary = str(recovery.get("summary") or "").strip()
if recovery_action == "run_acceptance":
action = "node_acceptance_run"
reason = recovery_summary or "当前节点已进入验收窗口,建议直接跑 onboarding.acceptance。"
command = recovery_command_hint or f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-run {base_url} {node_code} {requested_by}"
elif recovery_action == "bootstrap_run":
action = "node_bootstrap_run"
reason = recovery_summary or "当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap。"
command = recovery_command_hint or f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-run {base_url} {node_code} {requested_by}"
elif acceptance_status_code in {"ready", "ssh_only"} or stage_code == "acceptance_ready":
action = "node_acceptance_run"
reason = "当前节点已进入验收窗口,建议直接跑 onboarding.acceptance。"
command = f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-acceptance-run {base_url} {node_code} {requested_by}"
elif stage_code in {"pending_bootstrap", "runtime_only", "profile_incomplete", "ssh_ready", "token_issue", "stale", "agent_pending", "unknown", ""}:
action = "node_bootstrap_run"
reason = "当前节点仍处于接入阶段,建议先签发 onboarding.bootstrap。"
command = f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-run {base_url} {node_code} {requested_by}"
result = {
"node_code": node_code,
"source_mode": source_mode,
"backend_http_status": backend_http_status,
"stage_code": stage_code,
"acceptance_status_code": acceptance_status_code,
"recovery_action": recovery_action,
"selected_action": action,
"summary": reason,
"command": command,
"confirm_required": True,
"confirmed": confirm_flag == "confirm",
}
print(json.dumps(result, ensure_ascii=False, indent=2))
PY
if [[ "${confirm_flag}" != "confirm" ]]; then
return 0
fi
if [[ "${source_mode}" == "recovery_endpoint" ]]; then
return 0
fi
local selected_action
selected_action="$(python3 - <<'PY' "${response_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
data = payload.get("data") or {}
stage = data.get("onboarding_stage") or data.get("stage") or {}
acceptance = data.get("acceptance") or {}
recovery = data.get("recovery_decision") or {}
stage_code = str(stage.get("code") or "").strip()
acceptance_status_code = str(acceptance.get("status_code") or "").strip()
recovery_action = str(recovery.get("action") or "").strip()
if recovery_action == "run_acceptance":
print("node_acceptance_run")
elif recovery_action == "bootstrap_run":
print("node_bootstrap_run")
elif acceptance_status_code in {"ready", "ssh_only"} or stage_code == "acceptance_ready":
print("node_acceptance_run")
elif stage_code in {"pending_bootstrap", "runtime_only", "profile_incomplete", "ssh_ready", "token_issue", "stale", "agent_pending", "unknown", ""}:
print("node_bootstrap_run")
else:
print("")
PY
)"
case "${selected_action}" in
node_bootstrap_run)
run_node_bootstrap "${mainland_base_url}" "${node_code}" "${requested_by}"
;;
node_acceptance_run)
run_node_acceptance "${mainland_base_url}" "${node_code}" "${requested_by}"
;;
*)
printf '%s\n' '{"message":"当前节点没有可自动执行的恢复动作。"}'
;;
esac
}
show_node_acceptance_plan() {
local mainland_base_url="$1"
local node_code="$2"
local requested_by="${3:-cli}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local request_json
request_json="$(python3 - <<'PY' "${requested_by}"
import json
import sys
print(json.dumps({"requested_by": str(sys.argv[1] or "cli").strip() or "cli"}, ensure_ascii=False))
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding/acceptance/preview" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" == "404" ]]; then
request_json="$(python3 - <<'PY' "${node_code}" "${requested_by}"
import json
import sys
node_code = str(sys.argv[1] or "").strip()
requested_by = str(sys.argv[2] or "cli").strip() or "cli"
print(json.dumps({
"playbook_key": "onboarding.acceptance",
"node_codes": [node_code] if node_code else [],
"execution_mode": "remote-agent",
"auto_approve": True,
"requested_by": requested_by,
}, ensure_ascii=False))
PY
)"
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/playbooks/preview" "${request_json}")"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
fi
printf '%s\n' "${response_json}"
}
show_node_bootstrap_preview() {
local mainland_base_url="$1"
local node_code="$2"
local requested_by="${3:-cli}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local request_json
request_json="$(python3 - <<'PY' "${requested_by}"
import json
import sys
print(json.dumps({"requested_by": str(sys.argv[1] or "cli").strip() or "cli"}, ensure_ascii=False))
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding/bootstrap/preview" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" == "404" ]]; then
request_json="$(python3 - <<'PY' "${node_code}" "${requested_by}"
import json
import sys
node_code = str(sys.argv[1] or "").strip()
requested_by = str(sys.argv[2] or "cli").strip() or "cli"
print(json.dumps({
"playbook_key": "onboarding.bootstrap",
"node_codes": [node_code] if node_code else [],
"execution_mode": "control-plane",
"auto_approve": True,
"requested_by": requested_by,
}, ensure_ascii=False))
PY
)"
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/playbooks/preview" "${request_json}")"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
fi
printf '%s\n' "${response_json}"
}
run_node_bootstrap() {
local mainland_base_url="$1"
local node_code="$2"
local requested_by="${3:-cli}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local request_json
request_json="$(python3 - <<'PY' "${requested_by}"
import json
import sys
print(json.dumps({"requested_by": str(sys.argv[1] or "cli").strip() or "cli"}, ensure_ascii=False))
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding/bootstrap/execute" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" == "404" ]]; then
request_json="$(python3 - <<'PY' "${node_code}" "${requested_by}"
import json
import sys
node_code = str(sys.argv[1] or "").strip()
requested_by = str(sys.argv[2] or "cli").strip() or "cli"
print(json.dumps({
"playbook_key": "onboarding.bootstrap",
"node_codes": [node_code] if node_code else [],
"execution_mode": "control-plane",
"auto_approve": True,
"requested_by": requested_by,
}, ensure_ascii=False))
PY
)"
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/playbooks/execute" "${request_json}")"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
fi
printf '%s\n' "${response_json}"
echo
echo "[follow-up]"
python3 - <<'PY' "${response_json}" "${node_code}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
node_code = str(sys.argv[2] or "").strip()
data = payload.get("data") or {}
follow_up = dict(data.get("follow_up") or {})
playbook_run = dict(data.get("playbook_run") or {})
follow_up_summary = str(follow_up.get("summary") or "").strip() or str(playbook_run.get("focus_summary") or "").strip()
print(json.dumps({
"node_code": node_code,
"playbook_run_code": str(playbook_run.get("run_code") or "").strip(),
"follow_up_next_stage_code": str(follow_up.get("next_stage_code") or "").strip(),
"follow_up_summary": follow_up_summary,
"follow_up_commands": list(follow_up.get("recommended_commands") or []),
}, ensure_ascii=False, indent=2))
PY
}
run_node_acceptance() {
local mainland_base_url="$1"
local node_code="$2"
local requested_by="${3:-cli}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local request_json
request_json="$(python3 - <<'PY' "${requested_by}"
import json
import sys
print(json.dumps({"requested_by": str(sys.argv[1] or "cli").strip() or "cli"}, ensure_ascii=False))
PY
)"
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes/${node_code}/onboarding/acceptance/execute" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
if [[ "${http_status}" == "404" ]]; then
request_json="$(python3 - <<'PY' "${node_code}" "${requested_by}"
import json
import sys
node_code = str(sys.argv[1] or "").strip()
requested_by = str(sys.argv[2] or "cli").strip() or "cli"
print(json.dumps({
"playbook_key": "onboarding.acceptance",
"node_codes": [node_code] if node_code else [],
"execution_mode": "remote-agent",
"auto_approve": True,
"requested_by": requested_by,
}, ensure_ascii=False))
PY
)"
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/playbooks/execute" "${request_json}")"
response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
fi
printf '%s\n' "${response_json}"
echo
echo "[follow-up]"
python3 - <<'PY' "${response_json}" "${node_code}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
node_code = str(sys.argv[2] or "").strip()
data = payload.get("data") or {}
follow_up = dict(data.get("follow_up") or {})
playbook_run = dict(data.get("playbook_run") or {})
follow_up_summary = str(follow_up.get("summary") or "").strip() or str(playbook_run.get("focus_summary") or "").strip()
print(json.dumps({
"node_code": node_code,
"playbook_run_code": str(playbook_run.get("run_code") or "").strip(),
"follow_up_next_stage_code": str(follow_up.get("next_stage_code") or "").strip(),
"follow_up_summary": follow_up_summary,
"follow_up_commands": list(follow_up.get("recommended_commands") or []),
}, ensure_ascii=False, indent=2))
PY
}
show_delivery_queue_records() {
local mainland_base_url="$1"
local node_code="$2"
local state="${3:-}"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local url="${mainland_base_url}/api/v1/ops/nodes/${node_code}/delivery-queue/records"
if [[ -n "${state}" ]]; then
url="${url}?state=${state}"
fi
local raw_http_response
raw_http_response="$(request_with_status "GET" "${url}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
summary = data.get("summary") or {}
records = list(data.get("records") or [])
node_code = str(data.get("node_code") or "").strip()
print(json.dumps({
"http_status": http_status,
"node_code": node_code,
"summary": {
"total": int(summary.get("total", 0) or 0),
"available_total": int(summary.get("available_total", 0) or 0),
"record_visibility": str(summary.get("record_visibility") or ""),
"available_state_counts": dict(summary.get("available_state_counts") or {}),
},
"recommended_commands": {
"queue_status": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue http://127.0.0.1:8100 {node_code}"
if node_code
else ""
),
"replay": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh delivery-queue-replay http://127.0.0.1:8100 {node_code}"
if node_code
else ""
),
},
"records": [
{
"record_id": str(item.get("record_id") or item.get("client_request_id") or item.get("record_key") or ""),
"state": str(item.get("state") or ""),
"request_kind": str(item.get("request_kind") or ""),
"created_at": str(item.get("created_at") or ""),
"dead_letter_at": str(item.get("dead_letter_at") or ""),
"label": str(item.get("label") or ""),
"record_source": str(item.get("record_source") or ""),
}
for item in records
],
}, ensure_ascii=False, indent=2))
PY
}
run_delivery_queue_action() {
local mainland_base_url="$1"
local node_code="$2"
local action_path="$3"
local body_json="$4"
if [[ -z "${node_code}" ]]; then
echo "node_code is required" >&2
exit 1
fi
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/nodes/${node_code}${action_path}" "${body_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
}
preview_runbook_sequence() {
local mainland_base_url="$1"
local sequence_key="$2"
local action_payload_json="$3"
local requested_by="$4"
if [[ "${action_payload_json}" == @* ]]; then
local action_payload_path="${action_payload_json#@}"
if [[ ! -f "${action_payload_path}" ]]; then
echo "payload file not found: ${action_payload_path}" >&2
exit 1
fi
action_payload_json="$(cat "${action_payload_path}")"
fi
local request_json
request_json="$(python3 - <<'PY' "${sequence_key}" "${action_payload_json}" "${requested_by}"
import json
import sys
sequence_key = str(sys.argv[1] or "").strip()
payload_raw = str(sys.argv[2] or "").strip() or "{}"
requested_by = str(sys.argv[3] or "").strip() or "cli/ops-center/runbook-preview"
try:
action_payload = json.loads(payload_raw)
except Exception as exc:
raise SystemExit(f"invalid action payload json: {exc}")
if not isinstance(action_payload, dict):
raise SystemExit("action payload must be a json object")
request_payload = {
"source_label": "cli/runbook-preview",
"title": sequence_key,
"executor_kind": "runbook_sequence",
"sequence_key": sequence_key,
"requested_by": requested_by,
"include_secondary": True,
"primary_action_payload": action_payload,
"secondary_action_payload": action_payload,
}
print(json.dumps(request_payload, ensure_ascii=False))
PY
)"
echo "[1/3] runbook preview request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/preview" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[3/3] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
primary = data.get("primary") or {}
secondary = data.get("secondary") or {}
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"sequence_key": str(data.get("sequence_key") or ""),
"execution_chain": str(data.get("execution_chain") or ""),
"focus_ref": data.get("focus_ref") or {},
"primary_focus_ref": data.get("primary_focus_ref") or {},
"secondary_focus_ref": data.get("secondary_focus_ref") or {},
"primary": {
"label": str(primary.get("label") or ""),
"action_code": str(primary.get("action_code") or ""),
"target_api": str(primary.get("target_api") or ""),
"recommendation_label": str(primary.get("recommendation_label") or ""),
"focus_ref": primary.get("focus_ref") or {},
"request_payload": primary.get("request_payload") or {},
},
"secondary": (
{
"label": str(secondary.get("label") or ""),
"action_code": str(secondary.get("action_code") or ""),
"target_api": str(secondary.get("target_api") or ""),
"recommendation_label": str(secondary.get("recommendation_label") or ""),
"focus_ref": secondary.get("focus_ref") or {},
"request_payload": secondary.get("request_payload") or {},
}
if secondary else {}
),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
build_codex_focus_bundle() {
local codex_response_json="$1"
local entry_key="$2"
local requested_by="$3"
local source_label="$4"
python3 - <<'PY' "${codex_response_json}" "${entry_key}" "${requested_by}" "${source_label}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
requested_entry_key = str(sys.argv[2] or "").strip()
requested_by = str(sys.argv[3] or "").strip() or "cli/ops-center/codex-focus-preview"
source_label = str(sys.argv[4] or "").strip() or "cli/codex-focus-preview"
data = payload.get("data") or {}
focus = data.get("focus") or {}
entries = [dict(item or {}) for item in list(data.get("entries") or []) if dict(item or {})]
activity_focus = [dict(item or {}) for item in list(data.get("activity_focus") or []) if dict(item or {})]
def build_activity_focus_entry(item):
normalized = dict(item or {})
focus_action_code = str(normalized.get("focus_action_code") or "").strip()
if not str(normalized.get("key") or "").strip() or not focus_action_code:
return {}
focus_ref = dict(normalized.get("focus_ref") or {})
target_node_codes = list(normalized.get("target_node_codes") or [])
focus_action_payload = dict(normalized.get("focus_action_payload") or {})
summary = str(normalized.get("summary") or "").strip()
reason = summary or "该焦点属于运行态观察项,建议进入对应工作区查看上下文。"
return {
"key": str(normalized.get("key") or "").strip(),
"title": str(normalized.get("title") or "").strip(),
"summary": summary,
"reason": reason,
"meta_text": str(normalized.get("meta_text") or "").strip(),
"executor_kind": "driver_action",
"primary_action_code": focus_action_code,
"secondary_action_code": "",
"focus_ref": focus_ref,
"primary_focus_ref": focus_ref,
"secondary_focus_ref": {},
"primary_label": "进入工作区",
"secondary_label": "",
"primary_execution": {
"label": "进入工作区",
"node_codes": target_node_codes,
"action_payload": focus_action_payload,
},
"secondary_execution": {},
"autopilot": {
"recommendation": "open_ui",
"automation_level": "ui_only",
"status_text": str(normalized.get("status_label") or normalized.get("status") or "").strip(),
"executor_mode_hint": "ui-intent",
"target_api": "/api/v1/ops/driver-actions/execute",
"target_method": "POST",
"reason": reason,
"confirm_required": False,
"ui_only": True,
"blocked": False,
},
"observation_only": True,
"activity_key": str(normalized.get("activity_key") or "").strip(),
"activity_kind": str(normalized.get("activity_kind") or "").strip(),
"ui_intent_kind": str(normalized.get("ui_intent_kind") or "").strip(),
"contract_navigation": dict(normalized.get("contract_navigation") or {}),
}
activity_focus_entries = [entry for entry in (build_activity_focus_entry(item) for item in activity_focus) if entry]
target_entry_key = requested_entry_key or str(focus.get("entry_key") or "").strip()
target_entry = {}
if target_entry_key:
target_entry = next((item for item in entries if str(item.get("key") or "").strip() == target_entry_key), {})
if not target_entry and target_entry_key:
target_entry = next((item for item in activity_focus_entries if str(item.get("key") or "").strip() == target_entry_key), {})
if not target_entry and entries:
target_entry = dict(entries[0] or {})
target_entry_key = str(target_entry.get("key") or "").strip()
if not target_entry and activity_focus_entries:
target_entry = dict(activity_focus_entries[0] or {})
target_entry_key = str(target_entry.get("key") or "").strip()
if not target_entry:
raise SystemExit("codex brief 当前没有可预览的 entry")
autopilot = dict(target_entry.get("autopilot") or {})
primary_execution = dict(target_entry.get("primary_execution") or {})
secondary_execution = dict(target_entry.get("secondary_execution") or {})
request_payload = {
"source_label": source_label,
"title": str(target_entry.get("title") or "").strip() or target_entry_key or "codex-focus",
"summary": str(target_entry.get("summary") or "").strip(),
"reason": str(target_entry.get("reason") or "").strip(),
"meta_text": str(target_entry.get("meta_text") or "").strip(),
"executor_kind": str(target_entry.get("executor_kind") or "driver_action").strip() or "driver_action",
"sequence_key": str(target_entry.get("sequence_key") or "").strip(),
"focus_ref": dict(target_entry.get("focus_ref") or {}),
"primary_focus_ref": dict(target_entry.get("primary_focus_ref") or {}),
"secondary_focus_ref": dict(target_entry.get("secondary_focus_ref") or {}),
"primary_label": str(primary_execution.get("label") or target_entry.get("primary_label") or "").strip(),
"secondary_label": str(secondary_execution.get("label") or target_entry.get("secondary_label") or "").strip(),
"primary_action_code": str(target_entry.get("primary_action_code") or "").strip(),
"secondary_action_code": str(target_entry.get("secondary_action_code") or "").strip(),
"primary_node_codes": list(primary_execution.get("node_codes") or target_entry.get("node_codes") or []),
"secondary_node_codes": list(secondary_execution.get("node_codes") or target_entry.get("node_codes") or []),
"primary_action_payload": dict(primary_execution.get("action_payload") or {}),
"secondary_action_payload": dict(secondary_execution.get("action_payload") or {}),
"include_secondary": True,
"requested_by": requested_by,
}
print(json.dumps({
"selected_entry": {
"key": target_entry_key,
"title": str(target_entry.get("title") or "").strip(),
"executor_kind": str(target_entry.get("executor_kind") or "").strip(),
"recommendation": str(autopilot.get("recommendation") or "").strip(),
"automation_level": str(autopilot.get("automation_level") or "").strip(),
"status_text": str(autopilot.get("status_text") or "").strip(),
"executor_mode_hint": str(autopilot.get("executor_mode_hint") or "").strip(),
"target_api": str(autopilot.get("target_api") or "").strip(),
"target_method": str(autopilot.get("target_method") or "").strip(),
"reason": str(autopilot.get("reason") or "").strip(),
"confirm_required": bool(autopilot.get("confirm_required", False)),
"ui_only": bool(autopilot.get("ui_only", False)),
"blocked": bool(autopilot.get("blocked", False)),
"primary_action_code": str(target_entry.get("primary_action_code") or "").strip(),
"sequence_key": str(target_entry.get("sequence_key") or "").strip(),
"focus_ref": dict(target_entry.get("focus_ref") or {}),
"primary_focus_ref": dict(target_entry.get("primary_focus_ref") or {}),
"secondary_focus_ref": dict(target_entry.get("secondary_focus_ref") or {}),
"observation_only": bool(target_entry.get("observation_only", False)),
"activity_key": str(target_entry.get("activity_key") or ""),
"activity_kind": str(target_entry.get("activity_kind") or ""),
"ui_intent_kind": str(target_entry.get("ui_intent_kind") or ""),
"contract_navigation": dict(target_entry.get("contract_navigation") or {}),
},
"preview_request": request_payload,
}, ensure_ascii=False))
PY
}
preview_codex_focus_entry() {
local mainland_base_url="$1"
local entry_key="$2"
local requested_by="$3"
local request_json
request_json="$(python3 - <<'PY' "${entry_key}" "${requested_by}"
import json
import sys
print(json.dumps({
"entry_key": str(sys.argv[1] or "").strip(),
"requested_by": str(sys.argv[2] or "").strip() or "cli/ops-center/codex-focus-preview",
"source_label": "cli/codex-focus-preview",
"include_secondary": True,
}, ensure_ascii=False))
PY
)"
echo "[1/3] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/codex-actions/resolve" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[3/3] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
selected = data.get("selected_entry") or {}
preview = data.get("preview") or {}
primary = preview.get("primary") or {}
secondary = preview.get("secondary") or {}
gate = data.get("gate") or {}
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"entry_key": str(data.get("entry_key") or ""),
"title": str(selected.get("title") or ""),
"executor_kind": str(preview.get("executor_kind") or selected.get("executor_kind") or ""),
"contract": {
"contract_key": str(preview.get("contract_key") or ""),
"contract_version": str(preview.get("contract_version") or ""),
"contract_primary_endpoint": str(preview.get("contract_primary_endpoint") or ""),
"preview_endpoint": str(preview.get("preview_endpoint") or ""),
"schema_doc_path": str(preview.get("contract_schema_doc_path") or ""),
},
"execution_chain": str(preview.get("execution_chain") or ""),
"focus_ref": data.get("focus_ref") or {},
"gate": {
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"recommendation_label": str(gate.get("recommendation_label") or ""),
"will_execute": bool(gate.get("will_execute", False)),
"confirm_required": bool(gate.get("confirm_required", False)),
"target_api": str(gate.get("target_api") or ""),
"focus_ref": gate.get("focus_ref") or {},
},
"selected_entry": {
"key": str(selected.get("key") or ""),
"contract_navigation": selected.get("contract_navigation") or {},
"focus_ref": selected.get("focus_ref") or {},
"primary_focus_ref": selected.get("primary_focus_ref") or {},
"secondary_focus_ref": selected.get("secondary_focus_ref") or {},
},
"primary": {
"label": str(primary.get("label") or ""),
"action_code": str(primary.get("action_code") or ""),
"target_api": str(primary.get("target_api") or ""),
"recommendation_label": str(primary.get("recommendation_label") or ""),
"focus_ref": primary.get("focus_ref") or {},
},
"secondary": (
{
"label": str(secondary.get("label") or ""),
"action_code": str(secondary.get("action_code") or ""),
"target_api": str(secondary.get("target_api") or ""),
"recommendation_label": str(secondary.get("recommendation_label") or ""),
"focus_ref": secondary.get("focus_ref") or {},
}
if secondary else {}
),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
run_codex_focus_entry() {
local mainland_base_url="$1"
local entry_key="$2"
local confirm_flag="$3"
local requested_by="$4"
local confirm_risky
confirm_risky="$(confirm_to_bool "${confirm_flag}")"
local request_json
request_json="$(python3 - <<'PY' "${entry_key}" "${requested_by}" "${confirm_risky}"
import json
import sys
print(json.dumps({
"entry_key": str(sys.argv[1] or "").strip(),
"requested_by": str(sys.argv[2] or "").strip() or "cli/ops-center/codex-focus-run",
"source_label": "cli/codex-focus-run",
"include_secondary": True,
"confirm": str(sys.argv[3] or "").strip().lower() == "true",
}, ensure_ascii=False))
PY
)"
echo "[1/3] request"
print_json_or_raw "${request_json}"
echo
echo
local execute_http_response
execute_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/codex-actions/execute" "${request_json}")"
local execute_http_status="${execute_http_response##*$'\n'HTTP_STATUS=}"
local execute_response_json="${execute_http_response%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${execute_http_status}"
print_json_or_raw "${execute_response_json}"
echo
echo
echo "[3/3] execution result"
python3 - <<'PY' "${execute_response_json}" "${execute_http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
gate = data.get("gate") or {}
execution_result = data.get("execution_result") or {}
print(json.dumps({
"http_status": http_status,
"entry_key": str(data.get("entry_key") or ""),
"code": int((0 if payload.get("code", 1) in (0, "0", False) else payload.get("code", 1)) or 0),
"message": str(payload.get("message") or ""),
"executed": bool(data.get("executed", False)),
"focus_ref": data.get("focus_ref") or {},
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"target_api": str(gate.get("target_api") or ""),
"target_method": str(gate.get("target_method") or ""),
"gate_focus_ref": gate.get("focus_ref") or {},
"handled": bool(execution_result.get("handled", False)),
"mode": str(execution_result.get("mode") or ""),
"action_code": str(gate.get("action_code") or execution_result.get("action_code") or execution_result.get("driver_action_code") or ""),
"sequence_key": str(gate.get("sequence_key") or execution_result.get("sequence_key") or ""),
}, ensure_ascii=False, indent=2))
PY
if [[ "${execute_http_status}" -lt 200 || "${execute_http_status}" -ge 300 ]]; then
exit 1
fi
}
build_driver_focus_bundle() {
local driver_feed_response_json="$1"
local entry_key="$2"
local requested_by="$3"
local source_label="$4"
local secondary_flag="$5"
local confirm_flag="$6"
python3 - <<'PY' "${driver_feed_response_json}" "${entry_key}" "${requested_by}" "${source_label}" "${secondary_flag}" "${confirm_flag}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
requested_entry_key = str(sys.argv[2] or "").strip()
requested_by = str(sys.argv[3] or "").strip() or "cli/ops-center/driver-focus-preview"
source_label = str(sys.argv[4] or "").strip() or "cli/driver-focus-preview"
secondary_flag = str(sys.argv[5] or "primary").strip().lower()
confirm_flag = str(sys.argv[6] or "").strip().lower()
data = payload.get("data") or {}
top_recommendation = dict(data.get("top_recommendation") or {})
entries = [dict(item or {}) for item in list(data.get("entries") or []) if dict(item or {})]
activity_focus = [dict(item or {}) for item in list(data.get("activity_focus") or []) if dict(item or {})]
secondary = secondary_flag in {"secondary", "second", "2", "true", "yes", "y"}
confirm = confirm_flag in {"1", "true", "yes", "y", "confirm", "confirmed"}
def build_activity_focus_entry(item):
normalized = dict(item or {})
focus_action_code = str(normalized.get("focus_action_code") or "").strip()
if not str(normalized.get("key") or "").strip() or not focus_action_code:
return {}
focus_ref = dict(normalized.get("focus_ref") or {})
target_node_codes = list(normalized.get("target_node_codes") or [])
focus_action_payload = dict(normalized.get("focus_action_payload") or {})
summary = str(normalized.get("summary") or "").strip()
reason = summary or "该焦点属于运行态观察项,建议进入对应工作区查看上下文。"
return {
"key": str(normalized.get("key") or "").strip(),
"kind": "activity_focus",
"lane": "activity_focus",
"title": str(normalized.get("title") or "").strip(),
"summary": summary,
"reason": reason,
"meta_text": str(normalized.get("meta_text") or "").strip(),
"status": str(normalized.get("status") or "").strip(),
"executor_kind": "driver_action",
"sequence_key": "",
"primary_label": "进入工作区",
"secondary_label": "",
"primary_action_code": focus_action_code,
"secondary_action_code": "",
"node_codes": target_node_codes,
"primary_node_codes": target_node_codes,
"secondary_node_codes": [],
"focus_ref": focus_ref,
"primary_focus_ref": focus_ref,
"secondary_focus_ref": {},
"primary_action_payload": focus_action_payload,
"secondary_action_payload": {},
"observation_only": True,
"activity_key": str(normalized.get("activity_key") or "").strip(),
"activity_kind": str(normalized.get("activity_kind") or "").strip(),
"ui_intent_kind": str(normalized.get("ui_intent_kind") or "").strip(),
"contract_navigation": dict(normalized.get("contract_navigation") or {}),
}
activity_focus_entries = [entry for entry in (build_activity_focus_entry(item) for item in activity_focus) if entry]
target_entry = {}
target_entry_key = requested_entry_key
if requested_entry_key:
target_entry = next((item for item in entries if str(item.get("key") or "").strip() == requested_entry_key), {})
if not target_entry and requested_entry_key:
target_entry = next((item for item in activity_focus_entries if str(item.get("key") or "").strip() == requested_entry_key), {})
if not target_entry and requested_entry_key and str(top_recommendation.get("key") or "").strip() == requested_entry_key:
target_entry = dict(top_recommendation or {})
if not target_entry:
target_entry = dict(top_recommendation or {})
target_entry_key = str(target_entry.get("key") or "").strip()
if not target_entry and entries:
target_entry = dict(entries[0] or {})
target_entry_key = str(target_entry.get("key") or "").strip()
if not target_entry and activity_focus_entries:
target_entry = dict(activity_focus_entries[0] or {})
target_entry_key = str(target_entry.get("key") or "").strip()
if not target_entry:
raise SystemExit("driver feed 当前没有可执行的焦点动作")
request_payload = {
"source_label": source_label,
"title": str(target_entry.get("title") or "").strip() or target_entry_key or "driver-focus",
"summary": str(target_entry.get("summary") or "").strip(),
"reason": str(target_entry.get("reason") or "").strip(),
"meta_text": str(target_entry.get("meta_text") or "").strip(),
"executor_kind": str(target_entry.get("executor_kind") or "driver_action").strip() or "driver_action",
"sequence_key": str(target_entry.get("sequence_key") or "").strip(),
"focus_ref": dict(target_entry.get("focus_ref") or {}),
"primary_focus_ref": dict(target_entry.get("primary_focus_ref") or {}),
"secondary_focus_ref": dict(target_entry.get("secondary_focus_ref") or {}),
"primary_label": str(target_entry.get("primary_label") or "").strip(),
"secondary_label": str(target_entry.get("secondary_label") or "").strip(),
"primary_action_code": str(target_entry.get("primary_action_code") or "").strip(),
"secondary_action_code": str(target_entry.get("secondary_action_code") or "").strip(),
"primary_node_codes": list(target_entry.get("primary_node_codes") or target_entry.get("node_codes") or []),
"secondary_node_codes": list(target_entry.get("secondary_node_codes") or target_entry.get("node_codes") or []),
"primary_action_payload": dict(target_entry.get("primary_action_payload") or {}),
"secondary_action_payload": dict(target_entry.get("secondary_action_payload") or {}),
"include_secondary": True,
"requested_by": requested_by,
"secondary": secondary,
"confirm": confirm,
}
print(json.dumps({
"selected_entry": {
"key": target_entry_key,
"kind": str(target_entry.get("kind") or "").strip(),
"lane": str(target_entry.get("lane") or "").strip(),
"title": str(target_entry.get("title") or "").strip(),
"status": str(target_entry.get("status") or "").strip(),
"executor_kind": str(target_entry.get("executor_kind") or "").strip(),
"sequence_key": str(target_entry.get("sequence_key") or "").strip(),
"primary_action_code": str(target_entry.get("primary_action_code") or "").strip(),
"secondary_action_code": str(target_entry.get("secondary_action_code") or "").strip(),
"focus_ref": dict(target_entry.get("focus_ref") or {}),
"primary_focus_ref": dict(target_entry.get("primary_focus_ref") or {}),
"secondary_focus_ref": dict(target_entry.get("secondary_focus_ref") or {}),
"observation_only": bool(target_entry.get("observation_only", False)),
"activity_key": str(target_entry.get("activity_key") or ""),
"activity_kind": str(target_entry.get("activity_kind") or ""),
"ui_intent_kind": str(target_entry.get("ui_intent_kind") or ""),
"contract_navigation": dict(target_entry.get("contract_navigation") or {}),
},
"request": request_payload,
}, ensure_ascii=False))
PY
}
preview_driver_focus_entry() {
local mainland_base_url="$1"
local entry_key="$2"
local requested_by="$3"
local raw_driver_feed_response
raw_driver_feed_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/driver-feed")"
local driver_feed_http_status="${raw_driver_feed_response##*$'\n'HTTP_STATUS=}"
local driver_feed_response_json="${raw_driver_feed_response%$'\n'HTTP_STATUS=*}"
if [[ "${driver_feed_http_status}" -lt 200 || "${driver_feed_http_status}" -ge 300 ]]; then
echo "[1/3] driver feed raw response"
printf '%s\n' "http_status=${driver_feed_http_status}"
print_json_or_raw "${driver_feed_response_json}"
exit 1
fi
local bundle_json
bundle_json="$(build_driver_focus_bundle "${driver_feed_response_json}" "${entry_key}" "${requested_by}" "cli/driver-focus-preview" "primary" "false")"
local selected_entry_json
selected_entry_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("selected_entry") or {}, ensure_ascii=False))
PY
)"
local request_json
request_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("request") or {}, ensure_ascii=False))
PY
)"
echo "[1/4] selected focus"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/4] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/resolve" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[3/4] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[4/4] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}" "${selected_entry_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
selected = json.loads(str(sys.argv[3] or "{}"))
data = payload.get("data") or {}
preview = data.get("preview") or {}
primary = preview.get("primary") or {}
secondary = preview.get("secondary") or {}
gate = data.get("gate") or {}
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"selected_entry": selected,
"selected_section": str(data.get("selected_section") or ""),
"contract": {
"contract_key": str(preview.get("contract_key") or ""),
"contract_version": str(preview.get("contract_version") or ""),
"contract_primary_endpoint": str(preview.get("contract_primary_endpoint") or ""),
"preview_endpoint": str(preview.get("preview_endpoint") or ""),
"schema_doc_path": str(preview.get("contract_schema_doc_path") or ""),
},
"execution_chain": str(preview.get("execution_chain") or ""),
"focus_ref": data.get("focus_ref") or {},
"gate": {
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"recommendation_label": str(gate.get("recommendation_label") or ""),
"will_execute": bool(gate.get("will_execute", False)),
"confirm_required": bool(gate.get("confirm_required", False)),
"target_api": str(gate.get("target_api") or ""),
"focus_ref": gate.get("focus_ref") or {},
},
"primary": {
"label": str(primary.get("label") or ""),
"action_code": str(primary.get("action_code") or ""),
"target_api": str(primary.get("target_api") or ""),
"recommendation_label": str(primary.get("recommendation_label") or ""),
"focus_ref": primary.get("focus_ref") or {},
"request_payload": primary.get("request_payload") or {},
},
"secondary": (
{
"label": str(secondary.get("label") or ""),
"action_code": str(secondary.get("action_code") or ""),
"target_api": str(secondary.get("target_api") or ""),
"recommendation_label": str(secondary.get("recommendation_label") or ""),
"focus_ref": secondary.get("focus_ref") or {},
"request_payload": secondary.get("request_payload") or {},
}
if secondary else {}
),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
run_driver_focus_entry() {
local mainland_base_url="$1"
local entry_key="$2"
local secondary_flag="$3"
local confirm_flag="$4"
local requested_by="$5"
local raw_driver_feed_response
raw_driver_feed_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/driver-feed")"
local driver_feed_http_status="${raw_driver_feed_response##*$'\n'HTTP_STATUS=}"
local driver_feed_response_json="${raw_driver_feed_response%$'\n'HTTP_STATUS=*}"
if [[ "${driver_feed_http_status}" -lt 200 || "${driver_feed_http_status}" -ge 300 ]]; then
echo "[1/3] driver feed raw response"
printf '%s\n' "http_status=${driver_feed_http_status}"
print_json_or_raw "${driver_feed_response_json}"
exit 1
fi
local bundle_json
bundle_json="$(build_driver_focus_bundle "${driver_feed_response_json}" "${entry_key}" "${requested_by}" "cli/driver-focus-run" "${secondary_flag}" "${confirm_flag}")"
local selected_entry_json
selected_entry_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("selected_entry") or {}, ensure_ascii=False))
PY
)"
local request_json
request_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("request") or {}, ensure_ascii=False))
PY
)"
echo "[1/4] selected focus"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/4] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/execute-resolved" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[3/4] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[4/4] execution result"
python3 - <<'PY' "${response_json}" "${http_status}" "${selected_entry_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
selected = json.loads(str(sys.argv[3] or "{}"))
data = payload.get("data") or {}
gate = data.get("gate") or {}
execution_result = data.get("execution_result") or {}
raw_code = payload.get("code", 1)
if raw_code in (0, "0", False):
normalized_code = 0
else:
normalized_code = int(raw_code or 1)
print(json.dumps({
"http_status": http_status,
"code": normalized_code,
"message": str(payload.get("message") or ""),
"selected_entry": selected,
"executed": bool(data.get("executed", False)),
"selected_section": str(data.get("selected_section") or ""),
"focus_ref": data.get("focus_ref") or {},
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"target_api": str(gate.get("target_api") or ""),
"target_method": str(gate.get("target_method") or ""),
"gate_focus_ref": gate.get("focus_ref") or {},
"action_code": str(gate.get("action_code") or execution_result.get("action_code") or execution_result.get("driver_action_code") or ""),
"sequence_key": str(gate.get("sequence_key") or execution_result.get("sequence_key") or ""),
"handled": bool(execution_result.get("handled", False)),
"mode": str(execution_result.get("mode") or ""),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
build_stack_next_bundle() {
local stack_diagnosis_response_json="$1"
local requested_by="$2"
local source_label="$3"
local confirm_flag="$4"
python3 - <<'PY' "${stack_diagnosis_response_json}" "${requested_by}" "${source_label}" "${confirm_flag}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
requested_by = str(sys.argv[2] or "").strip() or "cli/ops-center/stack-next"
source_label = str(sys.argv[3] or "").strip() or "cli/stack-next"
confirm_flag = str(sys.argv[4] or "").strip().lower()
data = payload.get("data") or {}
diagnosis = dict(data.get("diagnosis") or {})
operator_decision = dict(diagnosis.get("operator_decision") or {})
next_actions = dict(diagnosis.get("next_actions") or {})
recommended_commands = dict(diagnosis.get("recommended_commands") or {})
next_step = dict(diagnosis.get("next_step") or {})
recommended_actions = [dict(item or {}) for item in list(diagnosis.get("recommended_actions") or []) if dict(item or {})]
recommended_action_codes = {
str(item.get("action_code") or "").strip()
for item in recommended_actions
if str(item.get("action_code") or "").strip()
}
overview = dict(data.get("overview") or {})
managed_nodes = dict(data.get("managed_nodes") or {})
release_hub = dict(data.get("release_hub") or {})
issues = [dict(item or {}) for item in list(diagnosis.get("issues") or []) if dict(item or {})]
def normalize_node_codes(value):
result = []
seen = set()
if not isinstance(value, list):
return result
for item in value:
if isinstance(item, dict):
node_code = str(item.get("node_code") or "").strip()
else:
node_code = str(item or "").strip()
if not node_code or node_code in seen:
continue
seen.add(node_code)
result.append(node_code)
return result
def scene_node_log_command(node_code, limit=120, mode="key"):
node_code = str(node_code or "").strip()
if not node_code:
return ""
return (
"bash domain-api/deploy/multi-region/drive_ops_center.sh "
f"scene-node-log http://127.0.0.1:8100 {node_code} {int(limit or 120)} {str(mode or 'key').strip() or 'key'}"
)
if not operator_decision or not next_actions or not recommended_commands:
log_sync = dict((overview.get("log_sync") or {}))
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if dict(item or {})]
launchpad_focus_ref = dict(release_hub.get("focus_ref") or release_hub.get("default_rollout_gate_focus_ref") or {})
default_rollout_gate_status = str(release_hub.get("default_rollout_gate_status") or "").strip()
first_problem_node_code = str((problem_nodes[0] if problem_nodes else {}).get("node_code") or "").strip()
missing_sample_node_codes = normalize_node_codes(log_sync.get("missing_sample_node_codes") or [])
scene_log_target_node_code = missing_sample_node_codes[0] if missing_sample_node_codes else first_problem_node_code
synthesized_recommended_commands = {
"api-restart": "bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover http://127.0.0.1:8100",
"stack-diagnosis": "bash domain-api/deploy/multi-region/drive_ops_center.sh stack-diagnosis http://127.0.0.1:8100 summary",
"stack-check": "bash domain-api/deploy/multi-region/drive_ops_center.sh stack-diagnosis http://127.0.0.1:8100 summary",
"managed-node-bootstrap-plan": (
"bash domain-api/deploy/multi-region/drive_ops_center.sh "
f"node-bootstrap-plan http://127.0.0.1:8100 {first_problem_node_code}"
if first_problem_node_code
else ""
),
"scene_log": scene_node_log_command(scene_log_target_node_code, 120, "key"),
"driver_focus_preview": "bash domain-api/deploy/multi-region/drive_ops_center.sh driver-focus-preview http://127.0.0.1:8100",
"doctor": "bash domain-api/deploy/multi-region/drive_ops_center.sh doctor http://127.0.0.1:8100",
"node_handover": (
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-handover http://127.0.0.1:8100 {first_problem_node_code}"
if first_problem_node_code
else ""
),
"release_launchpad": "bash domain-api/deploy/multi-region/drive_ops_center.sh release-launchpad http://127.0.0.1:8100",
}
synthesized_operator_decision = {}
if any(str(item.get("code") or "").strip() in {"remote_log_sync_disabled", "remote_log_sync_waiting_sample"} for item in issues):
synthesized_operator_decision = {
"lane": "observability",
"priority": "high",
"reason_code": "scene_log_attention",
"title": "先看执行现场日志",
"summary": "当前总检优先建议先补现场可见性,再决定是否继续接管或推进发布。",
"next_focus": {
"kind": "node_scene_log",
"node_code": scene_log_target_node_code,
"mode": "key",
"limit": 120,
} if scene_log_target_node_code else {},
"primary_command_key": "scene_log",
"secondary_command_key": "driver_focus_preview",
}
elif any(str(item.get("code") or "").strip() == "runtime_build_schema_stale" for item in issues):
synthesized_operator_decision = {
"lane": "runtime_recovery",
"priority": "high",
"reason_code": "runtime_build_schema_stale",
"title": "先重启控制面 API让运行时切到最新 schema",
"summary": "当前属于仓库代码已更新,但运行中的 API 仍旧停留在旧 schema 的场景。先重启 domaincheck-api再重新看 stack-diagnosis 与 node-bootstrap-plan。",
"next_focus": {
"kind": "runtime_build_info",
"node_code": "",
},
"primary_command_key": "api-restart",
"secondary_command_key": "managed-node-bootstrap-plan" if first_problem_node_code else "stack-diagnosis",
}
elif any(str(item.get("code") or "").strip() == "managed_nodes_agent_pending" for item in issues):
synthesized_operator_decision = {
"lane": "node_handover",
"priority": "high",
"reason_code": "managed_nodes_agent_pending",
"title": "先补接管缺口",
"summary": "当前真正阻断是 remote-agent / 接管未就绪,应先把节点接入链路打通。",
"next_focus": {},
"primary_command_key": "node_handover",
"secondary_command_key": "doctor",
}
elif default_rollout_gate_status == "blocked":
synthesized_operator_decision = {
"lane": "release",
"priority": "medium",
"reason_code": "release_rollout_gate_blocked",
"title": "先看发布门禁",
"summary": str(release_hub.get("default_rollout_gate_summary") or "").strip(),
"next_focus": launchpad_focus_ref,
"primary_command_key": "release_launchpad",
"secondary_command_key": "doctor",
}
if synthesized_operator_decision:
operator_decision = synthesized_operator_decision
if synthesized_recommended_commands:
merged_recommended_commands = dict(synthesized_recommended_commands)
merged_recommended_commands.update({
str(key or "").strip(): str(value or "").strip()
for key, value in recommended_commands.items()
if str(key or "").strip() and str(value or "").strip()
})
recommended_commands = merged_recommended_commands
if operator_decision:
primary_key = str(operator_decision.get("primary_command_key") or "").strip()
secondary_key = str(operator_decision.get("secondary_command_key") or "").strip()
next_actions = {
"primary_command_key": primary_key,
"primary_command": str(recommended_commands.get(primary_key) or "").strip(),
"secondary_command_key": secondary_key,
"secondary_command": str(recommended_commands.get(secondary_key) or "").strip(),
}
primary_command_key = str(operator_decision.get("primary_command_key") or "").strip()
primary_command = str(
next_actions.get("primary_command")
or recommended_commands.get(primary_command_key)
or ""
).strip()
operator_focus_ref = dict(operator_decision.get("next_focus") or {})
action_code = ""
selection_mode = "driver_action"
if primary_command_key and primary_command_key in recommended_action_codes:
action_code = primary_command_key
elif primary_command_key and primary_command:
selection_mode = "command_only"
elif str(next_step.get("action_code") or "").strip():
action_code = str(next_step.get("action_code") or "").strip()
elif primary_command:
selection_mode = "command_only"
else:
raise SystemExit("stack diagnosis 当前既没有可执行 driver action也没有可下钻命令")
matched_action = next(
(item for item in recommended_actions if str(item.get("action_code") or "").strip() == action_code),
{},
)
focus_ref = dict(operator_focus_ref or next_step.get("focus_ref") or matched_action.get("focus_ref") or {})
confirm = confirm_flag in {"1", "true", "yes", "y", "confirm", "confirmed"}
request_payload = {
"source_label": source_label,
"title": str(matched_action.get("title") or action_code).strip() or action_code,
"summary": str(next_step.get("reason") or matched_action.get("reason") or "").strip(),
"reason": str(next_step.get("reason") or matched_action.get("reason") or "").strip(),
"meta_text": str(next_step.get("source") or matched_action.get("source") or "stack-diagnosis").strip(),
"executor_kind": "driver_action",
"focus_ref": focus_ref,
"primary_focus_ref": focus_ref,
"primary_label": str(matched_action.get("title") or action_code).strip() or action_code,
"primary_action_code": action_code,
"primary_action_payload": {},
"include_secondary": True,
"requested_by": requested_by,
"confirm": confirm,
}
if selection_mode == "command_only":
print(json.dumps({
"http_status": 200,
"mode": selection_mode,
"message": "当前主决策优先指向命令型下钻动作,不进入 driver-actions/resolve。",
"contract": {
"contract_key": str((diagnosis.get("contract_navigation") or {}).get("primary_contract_key") or "ops_stack_diagnosis_contract").strip(),
"contract_version": str(diagnosis.get("contract_version") or "").strip(),
"contract_primary_endpoint": "/api/v1/ops/stack-diagnosis",
"preview_endpoint": "",
"schema_doc_path": "docs/schemas/ops_stack_diagnosis_contract.md",
},
"selected_entry": {
"action_code": primary_command_key,
"source": str(operator_decision.get("reason_code") or next_step.get("source") or "stack-diagnosis").strip(),
"title": str(operator_decision.get("title") or primary_command_key or "stack-next").strip(),
"reason": str(operator_decision.get("summary") or next_step.get("reason") or "").strip(),
"command": primary_command,
"focus_ref": focus_ref,
"contract_navigation": dict(
diagnosis.get("contract_navigation")
or next_step.get("contract_navigation")
or {}
),
},
"gate": {
"decision": "command_only",
"decision_label": "命令下钻",
"recommendation": "open_ui",
"recommendation_label": "先下钻",
"will_execute": False,
"confirm_required": False,
"target_api": "",
"focus_ref": focus_ref,
},
"execution_chain": "stack-diagnosis -> command-only guidance",
"focus_ref": focus_ref,
"request": {},
}, ensure_ascii=False))
raise SystemExit(0)
print(json.dumps({
"mode": selection_mode,
"selected_entry": {
"action_code": action_code,
"source": str(next_step.get("source") or matched_action.get("source") or "stack-diagnosis").strip(),
"title": str(matched_action.get("title") or action_code).strip() or action_code,
"reason": str(next_step.get("reason") or matched_action.get("reason") or "").strip(),
"command": str(next_step.get("command") or matched_action.get("command") or "").strip(),
"focus_ref": focus_ref,
"contract_navigation": dict(next_step.get("contract_navigation") or matched_action.get("contract_navigation") or {}),
},
"request": request_payload,
}, ensure_ascii=False))
PY
}
print_stack_diagnosis_failure_and_hint() {
local mainland_base_url="$1"
local stack_http_status="$2"
local stack_response_json="$3"
echo "[1/3] stack diagnosis raw response"
printf '%s\n' "http_status=${stack_http_status}"
print_json_or_raw "${stack_response_json}"
echo
echo
echo "[2/3] compatibility hint"
python3 - <<'PY' "${mainland_base_url}" "${stack_http_status}" "${stack_response_json}"
import json
import sys
base_url = str(sys.argv[1] or "").strip()
http_status = int(sys.argv[2] or 0)
raw_text = str(sys.argv[3] or "")
detail = ""
try:
payload = json.loads(raw_text)
except Exception:
payload = {}
else:
detail = str(payload.get("detail") or payload.get("message") or "").strip()
message = "stack-diagnosis 请求失败,请先检查控制面 API 连通性。"
commands = [
f"curl -sS {base_url}/api/v1/ops/stack-diagnosis",
]
if http_status == 404:
message = "当前控制面 API 还未暴露 /api/v1/ops/stack-diagnosis通常表示运行中的后端版本还没升级或升级后未重启。"
commands = [
"git status",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh runtime-refresh-recover {base_url}",
f"curl -sS {base_url}/api/v1/ops/stack-diagnosis",
]
print(json.dumps({
"http_status": http_status,
"detail": detail,
"message": message,
"recommended_commands": commands,
}, ensure_ascii=False, indent=2))
PY
echo
echo
echo "[3/3] next action"
if [[ "${stack_http_status}" == "404" ]]; then
printf '%s\n' "先把控制面后端升级到包含 /api/v1/ops/stack-diagnosis 的版本,并重启 domaincheck-api随后再重试 stack-next。"
else
printf '%s\n' "先修复控制面 API / 网络问题,再重试 stack-next。"
fi
}
preview_stack_next_entry() {
local mainland_base_url="$1"
local requested_by="$2"
local raw_stack_response
raw_stack_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
local stack_http_status="${raw_stack_response##*$'\n'HTTP_STATUS=}"
local stack_response_json="${raw_stack_response%$'\n'HTTP_STATUS=*}"
if [[ "${stack_http_status}" -lt 200 || "${stack_http_status}" -ge 300 ]]; then
print_stack_diagnosis_failure_and_hint "${mainland_base_url}" "${stack_http_status}" "${stack_response_json}"
exit 1
fi
local bundle_json
bundle_json="$(build_stack_next_bundle "${stack_response_json}" "${requested_by}" "cli/stack-next-preview" "false")"
local bundle_mode
bundle_mode="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(str(payload.get("mode") or "driver_action").strip())
PY
)"
local selected_entry_json
selected_entry_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("selected_entry") or {}, ensure_ascii=False))
PY
)"
if [[ "${bundle_mode}" == "command_only" ]]; then
echo "[1/2] selected next step"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/2] command-only guidance"
python3 - <<'PY' "${selected_entry_json}"
import json
import sys
selected = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps({
"http_status": 200,
"mode": "command_only",
"message": "当前主决策优先指向命令型下钻动作,不进入 driver-actions/resolve。",
"contract": {
"contract_key": str((selected.get("contract_navigation") or {}).get("primary_contract_key") or "ops_stack_diagnosis_contract"),
"contract_version": "",
"contract_primary_endpoint": "/api/v1/ops/stack-diagnosis",
"preview_endpoint": "",
"schema_doc_path": "docs/schemas/ops_stack_diagnosis_contract.md",
},
"selected_entry": selected,
"gate": {
"decision": "command_only",
"decision_label": "命令下钻",
"recommendation": "open_ui",
"recommendation_label": "先下钻",
"will_execute": False,
"confirm_required": False,
"target_api": "",
"focus_ref": selected.get("focus_ref") or {},
},
"execution_chain": "stack-diagnosis -> command-only guidance",
"focus_ref": selected.get("focus_ref") or {},
"recommended_next_command": str(selected.get("command") or ""),
}, ensure_ascii=False, indent=2))
PY
return 0
fi
local request_json
request_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("request") or {}, ensure_ascii=False))
PY
)"
echo "[1/4] selected next step"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/4] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/resolve" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[3/4] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[4/4] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}" "${selected_entry_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
selected = json.loads(str(sys.argv[3] or "{}"))
data = payload.get("data") or {}
preview = data.get("preview") or {}
primary = preview.get("primary") or {}
secondary = preview.get("secondary") or {}
gate = data.get("gate") or {}
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"selected_entry": selected,
"selected_section": str(data.get("selected_section") or ""),
"contract": {
"contract_key": str(preview.get("contract_key") or ""),
"contract_version": str(preview.get("contract_version") or ""),
"contract_primary_endpoint": str(preview.get("contract_primary_endpoint") or ""),
"preview_endpoint": str(preview.get("preview_endpoint") or ""),
"schema_doc_path": str(preview.get("contract_schema_doc_path") or ""),
},
"execution_chain": str(preview.get("execution_chain") or ""),
"focus_ref": data.get("focus_ref") or {},
"gate": {
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"recommendation_label": str(gate.get("recommendation_label") or ""),
"will_execute": bool(gate.get("will_execute", False)),
"confirm_required": bool(gate.get("confirm_required", False)),
"target_api": str(gate.get("target_api") or ""),
"focus_ref": gate.get("focus_ref") or {},
},
"primary": {
"label": str(primary.get("label") or ""),
"action_code": str(primary.get("action_code") or ""),
"target_api": str(primary.get("target_api") or ""),
"recommendation_label": str(primary.get("recommendation_label") or ""),
"focus_ref": primary.get("focus_ref") or {},
"request_payload": primary.get("request_payload") or {},
},
"secondary": (
{
"label": str(secondary.get("label") or ""),
"action_code": str(secondary.get("action_code") or ""),
"target_api": str(secondary.get("target_api") or ""),
"recommendation_label": str(secondary.get("recommendation_label") or ""),
"focus_ref": secondary.get("focus_ref") or {},
"request_payload": secondary.get("request_payload") or {},
}
if secondary else {}
),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
run_stack_next_entry() {
local mainland_base_url="$1"
local confirm_flag="$2"
local requested_by="$3"
local raw_stack_response
raw_stack_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
local stack_http_status="${raw_stack_response##*$'\n'HTTP_STATUS=}"
local stack_response_json="${raw_stack_response%$'\n'HTTP_STATUS=*}"
if [[ "${stack_http_status}" -lt 200 || "${stack_http_status}" -ge 300 ]]; then
print_stack_diagnosis_failure_and_hint "${mainland_base_url}" "${stack_http_status}" "${stack_response_json}"
exit 1
fi
local bundle_json
bundle_json="$(build_stack_next_bundle "${stack_response_json}" "${requested_by}" "cli/stack-next-run" "${confirm_flag}")"
local bundle_mode
bundle_mode="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(str(payload.get("mode") or "driver_action").strip())
PY
)"
local selected_entry_json
selected_entry_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("selected_entry") or {}, ensure_ascii=False))
PY
)"
if [[ "${bundle_mode}" == "command_only" ]]; then
echo "[1/2] selected next step"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/2] command-only guidance"
python3 - <<'PY' "${selected_entry_json}"
import json
import sys
selected = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps({
"http_status": 200,
"mode": "command_only",
"executed": False,
"message": "当前主决策属于命令型下钻动作stack-next run 不会代替你执行本地 shell。",
"contract": {
"contract_key": str((selected.get("contract_navigation") or {}).get("primary_contract_key") or "ops_stack_diagnosis_contract"),
"contract_version": "",
"contract_primary_endpoint": "/api/v1/ops/stack-diagnosis",
"preview_endpoint": "",
"schema_doc_path": "docs/schemas/ops_stack_diagnosis_contract.md",
},
"selected_entry": selected,
"gate": {
"decision": "command_only",
"decision_label": "命令下钻",
"recommendation": "open_ui",
"recommendation_label": "先下钻",
"will_execute": False,
"confirm_required": False,
"target_api": "",
"focus_ref": selected.get("focus_ref") or {},
},
"execution_chain": "stack-diagnosis -> command-only guidance",
"focus_ref": selected.get("focus_ref") or {},
"recommended_next_command": str(selected.get("command") or ""),
}, ensure_ascii=False, indent=2))
PY
return 0
fi
local request_json
request_json="$(python3 - <<'PY' "${bundle_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload.get("request") or {}, ensure_ascii=False))
PY
)"
echo "[1/4] selected next step"
print_json_or_raw "${selected_entry_json}"
echo
echo
echo "[2/4] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/execute-resolved" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[3/4] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[4/4] execution result"
python3 - <<'PY' "${response_json}" "${http_status}" "${selected_entry_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
selected = json.loads(str(sys.argv[3] or "{}"))
data = payload.get("data") or {}
gate = data.get("gate") or {}
execution_result = data.get("execution_result") or {}
raw_code = payload.get("code", 1)
if raw_code in (0, "0", False):
normalized_code = 0
else:
normalized_code = int(raw_code or 1)
print(json.dumps({
"http_status": http_status,
"code": normalized_code,
"message": str(payload.get("message") or ""),
"selected_entry": selected,
"executed": bool(data.get("executed", False)),
"selected_section": str(data.get("selected_section") or ""),
"focus_ref": data.get("focus_ref") or {},
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"target_api": str(gate.get("target_api") or ""),
"target_method": str(gate.get("target_method") or ""),
"gate_focus_ref": gate.get("focus_ref") or {},
"action_code": str(gate.get("action_code") or execution_result.get("action_code") or execution_result.get("driver_action_code") or ""),
"sequence_key": str(gate.get("sequence_key") or execution_result.get("sequence_key") or ""),
"handled": bool(execution_result.get("handled", False)),
"mode": str(execution_result.get("mode") or ""),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
resolve_driver_action_entry() {
local mainland_base_url="$1"
local action_code="$2"
local action_payload_json="$3"
local requested_by="$4"
local source_label="$5"
action_payload_json="$(normalize_payload_json_input "${action_payload_json}")"
action_payload_json="$(enrich_driver_action_payload_json "${mainland_base_url}" "${action_code}" "${action_payload_json}")"
local request_json
request_json="$(python3 - <<'PY' "${action_code}" "${action_payload_json}" "${requested_by}" "${source_label}"
import json
import sys
action_code = str(sys.argv[1] or "").strip()
payload_raw = str(sys.argv[2] or "").strip() or "{}"
requested_by = str(sys.argv[3] or "").strip() or "cli/ops-center/driver-resolve"
source_label = str(sys.argv[4] or "").strip() or "cli/driver-resolve"
try:
action_payload = json.loads(payload_raw)
except Exception as exc:
raise SystemExit(f"invalid action payload json: {exc}")
if not isinstance(action_payload, dict):
raise SystemExit("action payload must be a json object")
node_codes = []
seen = set()
for item in list(action_payload.get("node_codes") or []):
value = str(item or "").strip()
if not value or value in seen:
continue
seen.add(value)
node_codes.append(value)
print(json.dumps({
"source_label": source_label,
"title": action_code,
"executor_kind": "driver_action",
"primary_label": action_code,
"primary_action_code": action_code,
"primary_node_codes": node_codes,
"primary_action_payload": action_payload,
"include_secondary": True,
"requested_by": requested_by,
}, ensure_ascii=False))
PY
)"
echo "[1/3] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/resolve" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[3/3] condensed summary"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
preview = data.get("preview") or {}
primary = preview.get("primary") or {}
secondary = preview.get("secondary") or {}
gate = data.get("gate") or {}
print(json.dumps({
"http_status": http_status,
"message": str(payload.get("message") or ""),
"selected_section": str(data.get("selected_section") or ""),
"execution_chain": str(preview.get("execution_chain") or ""),
"focus_ref": data.get("focus_ref") or {},
"gate": {
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"recommendation_label": str(gate.get("recommendation_label") or ""),
"will_execute": bool(gate.get("will_execute", False)),
"confirm_required": bool(gate.get("confirm_required", False)),
"target_api": str(gate.get("target_api") or ""),
"focus_ref": gate.get("focus_ref") or {},
},
"primary": {
"label": str(primary.get("label") or ""),
"action_code": str(primary.get("action_code") or ""),
"target_api": str(primary.get("target_api") or ""),
"recommendation_label": str(primary.get("recommendation_label") or ""),
"focus_ref": primary.get("focus_ref") or {},
"request_payload": primary.get("request_payload") or {},
},
"secondary": (
{
"label": str(secondary.get("label") or ""),
"action_code": str(secondary.get("action_code") or ""),
"target_api": str(secondary.get("target_api") or ""),
"recommendation_label": str(secondary.get("recommendation_label") or ""),
"focus_ref": secondary.get("focus_ref") or {},
"request_payload": secondary.get("request_payload") or {},
}
if secondary else {}
),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
run_driver_action_entry() {
local mainland_base_url="$1"
local action_code="$2"
local action_payload_json="$3"
local confirm_flag="$4"
local requested_by="$5"
local source_label="$6"
local confirm_value
confirm_value="$(confirm_to_bool "${confirm_flag}")"
action_payload_json="$(normalize_payload_json_input "${action_payload_json}")"
action_payload_json="$(enrich_driver_action_payload_json "${mainland_base_url}" "${action_code}" "${action_payload_json}")"
local request_json
request_json="$(python3 - <<'PY' "${action_code}" "${action_payload_json}" "${requested_by}" "${source_label}" "${confirm_value}"
import json
import sys
action_code = str(sys.argv[1] or "").strip()
payload_raw = str(sys.argv[2] or "").strip() or "{}"
requested_by = str(sys.argv[3] or "").strip() or "cli/ops-center/driver-run"
source_label = str(sys.argv[4] or "").strip() or "cli/driver-run"
confirm = str(sys.argv[5] or "").strip().lower() == "true"
try:
action_payload = json.loads(payload_raw)
except Exception as exc:
raise SystemExit(f"invalid action payload json: {exc}")
if not isinstance(action_payload, dict):
raise SystemExit("action payload must be a json object")
node_codes = []
seen = set()
for item in list(action_payload.get("node_codes") or []):
value = str(item or "").strip()
if not value or value in seen:
continue
seen.add(value)
node_codes.append(value)
print(json.dumps({
"source_label": source_label,
"title": action_code,
"executor_kind": "driver_action",
"primary_label": action_code,
"primary_action_code": action_code,
"primary_node_codes": node_codes,
"primary_action_payload": action_payload,
"include_secondary": True,
"requested_by": requested_by,
"confirm": confirm,
}, ensure_ascii=False))
PY
)"
echo "[1/3] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/execute-resolved" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[3/3] execution result"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
gate = data.get("gate") or {}
execution_result = data.get("execution_result") or {}
raw_code = payload.get("code", 1)
if raw_code in (0, "0", False):
normalized_code = 0
else:
normalized_code = int(raw_code or 1)
print(json.dumps({
"http_status": http_status,
"code": normalized_code,
"message": str(payload.get("message") or ""),
"executed": bool(data.get("executed", False)),
"selected_section": str(data.get("selected_section") or ""),
"focus_ref": data.get("focus_ref") or {},
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"target_api": str(gate.get("target_api") or ""),
"target_method": str(gate.get("target_method") or ""),
"gate_focus_ref": gate.get("focus_ref") or {},
"action_code": str(gate.get("action_code") or execution_result.get("action_code") or execution_result.get("driver_action_code") or ""),
"handled": bool(execution_result.get("handled", False)),
"mode": str(execution_result.get("mode") or ""),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
if [[ "${action_code}" == "enable_log_sync_key" || "${action_code}" == "enable_log_sync_full" || "${action_code}" == "disable_log_sync" ]]; then
echo
echo "[4/4] log sync post-check"
print_log_sync_post_check "${mainland_base_url}"
fi
}
print_log_sync_post_check() {
local mainland_base_url="$1"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/overview")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
execution_scene = data.get("execution_scene") or {}
log_sync = execution_scene.get("log_sync") or {}
participation_summary = execution_scene.get("participation_summary") or {}
participating_nodes = int(
log_sync.get("participating_nodes_total", log_sync.get("participating_node_count", 0))
or participation_summary.get("participating_nodes", 0)
or 0
)
covered_nodes = int(
log_sync.get("covered_participating_nodes", log_sync.get("covered_participating_node_count", 0))
or 0
)
line_count = int(log_sync.get("line_count", 0) or 0)
source_node_count = int(log_sync.get("source_node_count", 0) or 0)
missing_nodes = list(log_sync.get("missing_participating_nodes") or [])
mode = str(log_sync.get("mode") or "key").strip() or "key"
enabled = bool(log_sync.get("enabled", False))
last_at = str(log_sync.get("last_at") or "").strip()
preview_lines = [
str(item or "").strip()
for item in list(log_sync.get("preview_lines") or [])
if str(item or "").strip()
][:5]
status = "standby"
summary = "当前没有参与检测节点,远端日志回传处于待命状态。"
if participating_nodes > 0 and not enabled:
status = "disabled"
summary = "当前已有参与检测节点,但远端日志回传仍关闭。"
elif participating_nodes > 0 and (line_count <= 0 or source_node_count <= 0 or missing_nodes):
status = "waiting_sample"
summary = "远端日志回传已经开启,但样本或覆盖仍未形成完整闭环。"
elif participating_nodes > 0:
status = "ready"
summary = "远端日志回传已经形成样本,可以继续下钻节点现场日志。"
result = {
"http_status": http_status,
"code": int(payload.get("code", 0) or 0),
"message": str(payload.get("message") or ""),
"status": status,
"summary": summary,
"log_sync": {
"enabled": enabled,
"mode": mode,
"participating_nodes": participating_nodes,
"covered_participating_nodes": covered_nodes,
"line_count": line_count,
"source_node_count": source_node_count,
"missing_participating_nodes": missing_nodes,
"last_at": last_at,
"preview_lines": preview_lines,
},
}
if http_status < 200 or http_status >= 300:
result["warning"] = "post-check 未能成功读取 /api/v1/ops/overview请确认当前 API 版本是否已包含 ops overview。"
print(json.dumps(result, ensure_ascii=False, indent=2))
PY
}
run_log_sync_recover_flow() {
local mainland_base_url="$1"
local desired_mode_raw="$2"
local confirm_flag="$3"
local requested_by="$4"
local desired_mode
desired_mode="$(printf '%s' "${desired_mode_raw:-key}" | tr '[:upper:]' '[:lower:]')"
if [[ "${desired_mode}" != "full" ]]; then
desired_mode="key"
fi
local action_code="enable_log_sync_key"
if [[ "${desired_mode}" == "full" ]]; then
action_code="enable_log_sync_full"
fi
echo "[1/2] execute log sync recovery action"
run_driver_action_entry "${mainland_base_url}" "${action_code}" '{}' "${confirm_flag}" "${requested_by}" "cli/log-sync-recover"
echo
echo "[2/2] recovery guidance"
build_log_sync_guidance_json "${mainland_base_url}"
}
build_log_sync_guidance_json() {
local mainland_base_url="$1"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/overview")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
python3 - <<'PY' "${response_json}" "${http_status}" "${mainland_base_url}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
base_url = str(sys.argv[3] or "").strip()
data = payload.get("data") or {}
execution_scene = data.get("execution_scene") or {}
log_sync = execution_scene.get("log_sync") or {}
participation_summary = execution_scene.get("participation_summary") or {}
state = str(log_sync.get("state") or "").strip() or "unknown"
description = str(log_sync.get("description") or "").strip()
enabled = bool(log_sync.get("enabled", False))
mode = str(log_sync.get("mode") or "key").strip() or "key"
source_nodes = [str(item or "").strip() for item in list(log_sync.get("source_nodes") or []) if str(item or "").strip()]
missing_nodes = [str(item or "").strip() for item in list(log_sync.get("missing_participating_nodes") or []) if str(item or "").strip()]
covered_nodes = [str(item or "").strip() for item in list(log_sync.get("covered_participating_nodes") or []) if str(item or "").strip()]
preview_lines = [str(item or "").strip() for item in list(log_sync.get("preview_lines") or []) if str(item or "").strip()][:5]
participating_nodes = int(
log_sync.get("participating_node_count", participation_summary.get("participating_nodes", 0))
or participation_summary.get("participating_nodes", 0)
or 0
)
covered_count = int(log_sync.get("covered_participating_node_count", len(covered_nodes)) or len(covered_nodes))
line_count = int(log_sync.get("line_count", 0) or 0)
recommended_commands = []
primary_target = ""
if source_nodes:
primary_target = source_nodes[0]
elif covered_nodes:
primary_target = covered_nodes[0]
elif missing_nodes:
primary_target = missing_nodes[0]
if primary_target:
recommended_commands.append(
f"bash domain-api/deploy/multi-region/drive_ops_center.sh scene-node-log {base_url} {primary_target} 120 {mode}"
)
if missing_nodes:
recommended_commands.extend(
f"bash domain-api/deploy/multi-region/drive_ops_center.sh scene-node-log {base_url} {node_code} 120 {mode}"
for node_code in missing_nodes[:2]
if node_code != primary_target
)
recommended_commands.append(
f"bash domain-api/deploy/multi-region/drive_ops_center.sh log-sync-check {base_url}"
)
recommended_commands.append(
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {base_url}"
)
next_step = "继续观察"
if state in {"disabled", "unknown"}:
next_step = "开关仍未真正生效,先确认当前 API 是否已加载最新 runtime_settings并复查执行链。"
elif state in {"waiting_sample", "partial_coverage"}:
next_step = "开关已经生效,但样本或覆盖还不完整,优先下钻参与节点现场日志。"
elif state in {"healthy", "full_capture"}:
next_step = "远端日志回传已进入可用状态,可以继续从海外控制面盯现场。"
elif state == "idle":
next_step = "当前没有参与检测节点,日志回传已就绪,等任务进入执行即可。"
print(json.dumps({
"http_status": http_status,
"code": int(payload.get("code", 0) or 0),
"message": str(payload.get("message") or ""),
"status": state,
"summary": description,
"next_step": next_step,
"log_sync": {
"enabled": enabled,
"mode": mode,
"participating_nodes": participating_nodes,
"covered_participating_nodes": covered_count,
"line_count": line_count,
"source_nodes": source_nodes,
"missing_participating_nodes": missing_nodes,
"preview_lines": preview_lines,
},
"recommended_commands": recommended_commands[:5],
}, ensure_ascii=False, indent=2))
PY
}
run_runtime_refresh_recover() {
local mainland_base_url="$1"
local audit_output
audit_output="$(bash "${SCRIPT_DIR}/check_env_drift.sh" "${mainland_base_url}")"
echo "[1/2] env audit"
printf '%s\n' "${audit_output}"
echo
echo
echo "[2/2] runtime refresh guidance"
python3 - <<'PY' "${audit_output}" "${mainland_base_url}"
import json
import sys
raw_text = str(sys.argv[1] or "")
base_url = str(sys.argv[2] or "").strip()
marker = "[8/8] condensed env audit summary"
candidate_text = raw_text.split(marker, 1)[1].strip() if marker in raw_text else raw_text.strip()
payload = {}
if candidate_text:
try:
payload = json.loads(candidate_text)
except Exception:
payload = {}
runtime = dict(payload.get("runtime") or {})
recommended_actions = [
str(item).strip()
for item in list(payload.get("recommended_actions") or [])
if str(item).strip()
]
recommended_commands = []
for candidate in recommended_actions:
if candidate not in recommended_commands:
recommended_commands.append(candidate)
for candidate in [
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-diagnosis {base_url} summary",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-next {base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor-decision {base_url} -",
]:
normalized_candidate = str(candidate or "").strip()
if normalized_candidate and normalized_candidate not in recommended_commands:
recommended_commands.append(normalized_candidate)
status = str(payload.get("status") or "").strip() or "unknown"
headline = str(payload.get("headline") or "").strip()
runtime_may_need_restart = bool(runtime.get("runtime_may_need_restart", False))
repo_capability_drift = bool(runtime.get("repo_capability_drift", False))
repo_commit_drift = bool(runtime.get("repo_commit_drift", False))
summary = "当前没有识别到明显的运行时版本漂移。"
if runtime_may_need_restart:
summary = "已经识别到运行时版本漂移,先让 domaincheck-api 切到最新代码,再继续节点接管与上线复核。"
elif status == "blocked":
summary = "当前环境本身仍有阻断项,先修环境,再谈运行时刷新。"
elif status == "attention":
summary = "当前环境存在注意项,但不一定都是运行时版本问题。"
print(json.dumps({
"status": status,
"headline": headline,
"summary": summary,
"runtime_refresh": {
"runtime_may_need_restart": runtime_may_need_restart,
"repo_capability_drift": repo_capability_drift,
"repo_commit_drift": repo_commit_drift,
"build_commit_sha": str(runtime.get("build_commit_sha") or "").strip(),
"build_commit_ref": str(runtime.get("build_commit_ref") or "").strip(),
"route_surface_declares_bootstrap_plan": bool(runtime.get("route_surface_declares_bootstrap_plan", False)),
"route_surface_missing_keys": [
str(item).strip()
for item in list(runtime.get("route_surface_missing_keys") or [])
if str(item).strip()
],
},
"recommended_commands": recommended_commands[:6],
}, ensure_ascii=False, indent=2))
PY
}
run_go_live_recover() {
local mainland_base_url="$1"
local overseas_base_url="$2"
echo "[1/5] runtime refresh recover"
run_runtime_refresh_recover "${mainland_base_url}"
echo
echo
echo "[2/5] stack check summary"
bash "${SCRIPT_DIR}/check_ops_center_stack.sh" "${mainland_base_url}" "summary"
echo
echo
echo "[3/5] stack next preview"
preview_stack_next_entry "${mainland_base_url}" "$(ops_center_resolve_requested_by)"
echo
echo
echo "[4/5] go live check summary"
bash "${SCRIPT_DIR}/check_go_live.sh" "${mainland_base_url}" "${overseas_base_url}" "summary"
echo
echo
echo "[5/5] doctor decision"
run_doctor_decision "${mainland_base_url}" "${overseas_base_url}"
}
show_agent_gap_check() {
local mainland_base_url="$1"
local raw_http_response
raw_http_response="$(request_with_status "GET" "${mainland_base_url}/api/v1/ops/stack-diagnosis?base_url=${mainland_base_url}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
python3 - <<'PY' "${response_json}" "${http_status}" "${mainland_base_url}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
base_url = str(sys.argv[3] or "").strip()
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if isinstance(item, dict)]
first_gap = dict(managed_nodes.get("first_handover_gap") or {})
managed_enabled = int(managed_nodes.get("managed_enabled", 0) or 0)
remote_access_ready = int(managed_nodes.get("remote_access_ready", 0) or 0)
first_node_code = str(
first_gap.get("node_code")
or managed_nodes.get("first_handover_gap_node_code")
or ((problem_nodes[0] or {}).get("node_code") if problem_nodes else "")
or ""
).strip()
summary = "当前没有启用托管节点。"
if problem_nodes and managed_enabled > 0 and remote_access_ready <= 0:
summary = "当前已有托管节点,但 remote-agent 仍未就绪,优先补齐节点接管链路。"
elif problem_nodes and managed_enabled > 0 and remote_access_ready < managed_enabled:
summary = (
f"当前仍有 {len(problem_nodes)} 台托管节点未进入 remote-agent 就绪状态,"
"优先补齐剩余节点接管。"
)
elif managed_enabled > 0:
summary = "当前托管节点均已进入 remote-agent 就绪状态。"
recommended_commands = []
if first_node_code:
recommended_commands.extend([
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-recover {base_url} {first_node_code}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-onboarding {base_url} {first_node_code}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-handover {base_url} {first_node_code}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh node-bootstrap-plan {base_url} {first_node_code}",
])
recommended_commands.extend([
f"bash domain-api/deploy/multi-region/drive_ops_center.sh agent-gap-export /tmp/domaincheck-agent-gap {base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh stack-first-gap-handover {base_url}",
f"bash domain-api/deploy/multi-region/drive_ops_center.sh doctor {base_url}",
])
print(json.dumps({
"http_status": http_status,
"code": int(payload.get("code", 0) or 0),
"message": str(payload.get("message") or ""),
"summary": summary,
"managed_nodes": {
"managed_enabled": managed_enabled,
"remote_access_ready": remote_access_ready,
"first_gap_node_code": first_node_code,
"problem_nodes": [
{
"node_code": str(item.get("node_code") or "").strip(),
"agent_state": str(item.get("agent_state") or "").strip(),
"remote_access_state": str(item.get("remote_access_state") or "").strip(),
"delivery_queue_state": str(item.get("delivery_queue_state") or "").strip(),
"participation_state": str(item.get("participation_state") or "").strip(),
}
for item in problem_nodes[:5]
],
},
"recommended_commands": recommended_commands[:5],
}, ensure_ascii=False, indent=2))
PY
}
run_agent_gap_recover_flow() {
local mainland_base_url="$1"
local confirm_flag="$2"
local requested_by="$3"
local normalized_confirm
normalized_confirm="$(confirm_to_bool "${confirm_flag}")"
echo "[1/4] stack first gap handover"
show_stack_first_gap_handover "${mainland_base_url}"
echo
echo "[2/4] first node bootstrap plan"
local first_gap_node_code
first_gap_node_code="$(python3 - <<'PY' "${mainland_base_url}" "${OPS_CENTER_CURL_CONNECT_TIMEOUT}" "${OPS_CENTER_CURL_MAX_TIME}"
import json
import subprocess
import sys
base_url = str(sys.argv[1] or "").strip()
connect_timeout = str(sys.argv[2] or "3").strip()
max_time = str(sys.argv[3] or "10").strip()
proc = subprocess.run(
[
"curl",
"-sS",
"--connect-timeout",
connect_timeout,
"--max-time",
max_time,
f"{base_url}/api/v1/ops/stack-diagnosis?base_url={base_url}",
],
capture_output=True,
text=True,
check=False,
)
raw = (proc.stdout or "").strip()
try:
payload = json.loads(raw)
except Exception:
print("")
raise SystemExit(0)
data = payload.get("data") or {}
managed_nodes = data.get("managed_nodes") or {}
problem_nodes = [dict(item or {}) for item in list(managed_nodes.get("problem_nodes") or []) if isinstance(item, dict)]
first_gap = dict(managed_nodes.get("first_handover_gap") or {})
node_code = str(
first_gap.get("node_code")
or managed_nodes.get("first_handover_gap_node_code")
or ((problem_nodes[0] or {}).get("node_code") if problem_nodes else "")
or ""
).strip()
print(node_code)
PY
)"
if [[ -n "${first_gap_node_code}" ]]; then
build_node_handover_bootstrap_plan \
"${mainland_base_url}" \
"${first_gap_node_code}" \
"${requested_by}" \
"72" \
"${mainland_base_url}" \
"/opt/domaincheck"
else
echo '{"message":"当前没有可生成 bootstrap-plan 的缺口节点。"}'
fi
echo
echo "[3/4] node onboarding view"
if [[ -n "${first_gap_node_code}" ]]; then
show_node_onboarding "${mainland_base_url}" "${first_gap_node_code}"
else
echo '{"message":"当前没有可查看 onboarding 的缺口节点。"}'
fi
echo
echo "[4/4] first node recovery decision"
if [[ -n "${first_gap_node_code}" ]]; then
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-recover "${mainland_base_url}" "${first_gap_node_code}" "" "${requested_by}"
echo
echo "[follow-up] gap recovery guidance"
show_agent_gap_check "${mainland_base_url}"
else
echo '{"message":"当前没有可生成恢复决策的缺口节点。"}'
echo
echo "[follow-up] gap recovery guidance"
show_agent_gap_check "${mainland_base_url}"
fi
if [[ "${normalized_confirm}" == "true" ]]; then
echo
echo '{"confirm":"true","note":"当前 agent-gap-recover 仍是结构化恢复流,不会直接 SSH 落地远端 bootstrap。"}'
fi
}
export_agent_gap_bundle() {
local report_dir="$1"
local mainland_base_url="$2"
local requested_by="$3"
ops_center_ensure_report_dir "${report_dir}"
local stack_json_path="${report_dir}/00_stack_first_gap_handover.json"
local check_json_path="${report_dir}/01_agent_gap_check.json"
local onboarding_json_path="${report_dir}/02_node_onboarding.json"
local bootstrap_plan_txt_path="${report_dir}/03_node_bootstrap_plan.txt"
local bootstrap_preview_json_path="${report_dir}/04_node_bootstrap_preview.json"
local acceptance_plan_json_path="${report_dir}/05_node_acceptance_plan.json"
local manifest_path="${report_dir}/manifest.json"
local nodes_dir="${report_dir}/nodes"
bash "${SCRIPT_DIR}/drive_ops_center.sh" stack-first-gap-handover "${mainland_base_url}" >"${stack_json_path}"
bash "${SCRIPT_DIR}/drive_ops_center.sh" agent-gap-check "${mainland_base_url}" >"${check_json_path}"
local first_gap_node_code
local gap_nodes_json
gap_nodes_json="$(python3 - <<'PY' "${check_json_path}"
import json
import sys
from pathlib import Path
text = Path(sys.argv[1]).read_text(encoding="utf-8")
decoder = json.JSONDecoder()
best = None
for index, ch in enumerate(text):
if ch not in "{[":
continue
try:
value, end = decoder.raw_decode(text[index:])
except Exception:
continue
absolute_end = index + end
if best is None or absolute_end >= best[0]:
best = (absolute_end, value)
payload = best[1] if best else {}
managed = payload.get("managed_nodes") or {}
problem_nodes = [dict(item or {}) for item in list(managed.get("problem_nodes") or []) if isinstance(item, dict)]
first_gap_node_code = str(managed.get("first_gap_node_code") or "").strip()
node_codes = []
for item in problem_nodes:
node_code = str(item.get("node_code") or "").strip()
if node_code and node_code not in node_codes:
node_codes.append(node_code)
if first_gap_node_code and first_gap_node_code not in node_codes:
node_codes.insert(0, first_gap_node_code)
print(json.dumps({
"first_gap_node_code": first_gap_node_code,
"gap_node_codes": node_codes,
}, ensure_ascii=False))
PY
)"
first_gap_node_code="$(python3 - <<'PY' "${gap_nodes_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(str(payload.get("first_gap_node_code") or "").strip())
PY
)"
mapfile -t gap_node_codes < <(python3 - <<'PY' "${gap_nodes_json}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
for item in list(payload.get("gap_node_codes") or []):
node_code = str(item or "").strip()
if node_code:
print(node_code)
PY
)
if [[ -n "${first_gap_node_code}" ]]; then
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-onboarding "${mainland_base_url}" "${first_gap_node_code}" >"${onboarding_json_path}"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-bootstrap-plan "${mainland_base_url}" "${first_gap_node_code}" "${requested_by}" "72" "${mainland_base_url}" "/opt/domaincheck" >"${bootstrap_plan_txt_path}"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-bootstrap-preview "${mainland_base_url}" "${first_gap_node_code}" "${requested_by}" >"${bootstrap_preview_json_path}"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-acceptance-plan "${mainland_base_url}" "${first_gap_node_code}" "${requested_by}" >"${acceptance_plan_json_path}" || true
fi
mkdir -p "${nodes_dir}"
local node_code safe_node_code node_dir
for node_code in "${gap_node_codes[@]}"; do
[[ -n "${node_code}" ]] || continue
safe_node_code="$(printf '%s' "${node_code}" | tr '/:' '__')"
safe_node_code="${safe_node_code// /_}"
node_dir="${nodes_dir}/${safe_node_code}"
mkdir -p "${node_dir}"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-handover "${mainland_base_url}" "${node_code}" >"${node_dir}/00_node_handover.json"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-onboarding "${mainland_base_url}" "${node_code}" >"${node_dir}/01_node_onboarding.json"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-bootstrap-plan "${mainland_base_url}" "${node_code}" "${requested_by}" "72" "${mainland_base_url}" "/opt/domaincheck" >"${node_dir}/02_node_bootstrap_plan.txt"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-bootstrap-preview "${mainland_base_url}" "${node_code}" "${requested_by}" >"${node_dir}/03_node_bootstrap_preview.json"
bash "${SCRIPT_DIR}/drive_ops_center.sh" node-acceptance-plan "${mainland_base_url}" "${node_code}" "${requested_by}" >"${node_dir}/04_node_acceptance_plan.json" || true
done
python3 - <<'PY' \
"${manifest_path}" \
"${report_dir}" \
"${mainland_base_url}" \
"${requested_by}" \
"${first_gap_node_code}" \
"${gap_nodes_json}" \
"${nodes_dir}" \
"${stack_json_path}" \
"${check_json_path}" \
"${onboarding_json_path}" \
"${bootstrap_plan_txt_path}" \
"${bootstrap_preview_json_path}" \
"${acceptance_plan_json_path}"
import json
import sys
from pathlib import Path
manifest_path = Path(sys.argv[1])
report_dir = Path(sys.argv[2])
base_url = sys.argv[3]
requested_by = sys.argv[4]
first_gap_node_code = sys.argv[5]
gap_nodes_payload = json.loads(str(sys.argv[6] or "{}"))
nodes_dir = Path(sys.argv[7])
paths = {
"stack_first_gap_handover": Path(sys.argv[8]),
"agent_gap_check": Path(sys.argv[9]),
"node_onboarding": Path(sys.argv[10]),
"node_bootstrap_plan": Path(sys.argv[11]),
"node_bootstrap_preview": Path(sys.argv[12]),
"node_acceptance_plan": Path(sys.argv[13]),
}
artifacts = []
for key, path in paths.items():
artifacts.append({
"key": key,
"path": str(path),
"exists": path.exists(),
"size": path.stat().st_size if path.exists() else 0,
})
node_bundles = []
for node_code in list(gap_nodes_payload.get("gap_node_codes") or []):
normalized_node_code = str(node_code or "").strip()
if not normalized_node_code:
continue
normalized_dir_name = normalized_node_code.replace("/", "_").replace(":", "_").replace(" ", "_")
node_dir = nodes_dir / normalized_dir_name
bundle_paths = {
"node_handover": node_dir / "00_node_handover.json",
"node_onboarding": node_dir / "01_node_onboarding.json",
"node_bootstrap_plan": node_dir / "02_node_bootstrap_plan.txt",
"node_bootstrap_preview": node_dir / "03_node_bootstrap_preview.json",
"node_acceptance_plan": node_dir / "04_node_acceptance_plan.json",
}
node_bundles.append({
"node_code": normalized_node_code,
"bundle_dir": str(node_dir),
"artifacts": [
{
"key": key,
"path": str(path),
"exists": path.exists(),
"size": path.stat().st_size if path.exists() else 0,
}
for key, path in bundle_paths.items()
],
})
payload = {
"report_dir": str(report_dir),
"mainland_base_url": base_url,
"requested_by": requested_by,
"first_gap_node_code": first_gap_node_code,
"gap_node_codes": list(gap_nodes_payload.get("gap_node_codes") or []),
"gap_node_total": len(list(gap_nodes_payload.get("gap_node_codes") or [])),
"artifacts": artifacts,
"node_bundles": node_bundles,
"summary": {
"ok": bool(node_bundles),
"headline": (
f"已为 {len(node_bundles)} 台接管缺口节点导出接管材料;首个缺口节点为 {first_gap_node_code}。"
if node_bundles and first_gap_node_code
else (
f"已为 {len(node_bundles)} 台接管缺口节点导出接管材料。"
if node_bundles
else "当前没有可导出的接管缺口节点。"
)
),
},
}
manifest_path.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
}
run_resolved_runbook_sequence() {
local mainland_base_url="$1"
local sequence_key="$2"
local secondary_flag="$3"
local action_payload_json="$4"
local confirm_flag="$5"
local requested_by="$6"
local source_label="$7"
local confirm_value
confirm_value="$(confirm_to_bool "${confirm_flag}")"
action_payload_json="$(normalize_payload_json_input "${action_payload_json}")"
local request_json
request_json="$(python3 - <<'PY' "${sequence_key}" "${secondary_flag}" "${action_payload_json}" "${requested_by}" "${source_label}" "${confirm_value}"
import json
import sys
sequence_key = str(sys.argv[1] or "").strip()
secondary_flag = str(sys.argv[2] or "primary").strip().lower()
payload_raw = str(sys.argv[3] or "").strip() or "{}"
requested_by = str(sys.argv[4] or "").strip() or "cli/ops-center/runbook-run"
source_label = str(sys.argv[5] or "").strip() or "cli/runbook-run"
confirm = str(sys.argv[6] or "").strip().lower() == "true"
try:
action_payload = json.loads(payload_raw)
except Exception as exc:
raise SystemExit(f"invalid action payload json: {exc}")
if not isinstance(action_payload, dict):
raise SystemExit("action payload must be a json object")
secondary = secondary_flag in {"secondary", "second", "2", "true", "yes", "y"}
print(json.dumps({
"source_label": source_label,
"title": sequence_key,
"executor_kind": "runbook_sequence",
"sequence_key": sequence_key,
"include_secondary": True,
"requested_by": requested_by,
"secondary": secondary,
"confirm": confirm,
"primary_action_payload": action_payload,
"secondary_action_payload": action_payload,
}, ensure_ascii=False))
PY
)"
echo "[1/3] request"
print_json_or_raw "${request_json}"
echo
echo
local raw_http_response
raw_http_response="$(request_with_status "POST" "${mainland_base_url}/api/v1/ops/driver-actions/execute-resolved" "${request_json}")"
local http_status="${raw_http_response##*$'\n'HTTP_STATUS=}"
local response_json="${raw_http_response%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${http_status}"
print_json_or_raw "${response_json}"
echo
echo
echo "[3/3] execution result"
python3 - <<'PY' "${response_json}" "${http_status}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
gate = data.get("gate") or {}
execution_result = data.get("execution_result") or {}
raw_code = payload.get("code", 1)
if raw_code in (0, "0", False):
normalized_code = 0
else:
normalized_code = int(raw_code or 1)
print(json.dumps({
"http_status": http_status,
"code": normalized_code,
"message": str(payload.get("message") or ""),
"executed": bool(data.get("executed", False)),
"selected_section": str(data.get("selected_section") or ""),
"decision": str(gate.get("decision") or ""),
"decision_label": str(gate.get("decision_label") or ""),
"recommendation": str(gate.get("recommendation") or ""),
"sequence_key": str(gate.get("sequence_key") or execution_result.get("sequence_key") or ""),
"action_code": str(gate.get("action_code") or execution_result.get("action_code") or execution_result.get("driver_action_code") or ""),
"handled": bool(execution_result.get("handled", False)),
"mode": str(execution_result.get("mode") or ""),
}, ensure_ascii=False, indent=2))
PY
if [[ "${http_status}" -lt 200 || "${http_status}" -ge 300 ]]; then
exit 1
fi
}
case "${COMMAND}" in
help|-h|--help)
usage
;;
config)
print_config "${1:-${OPS_CENTER_ACTIVE_PROFILE}}"
;;
contracts)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
CONTRACT_KEY="${2:-}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
CONTRACT_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_ops_contracts "${MAINLAND_BASE_URL}" "${CONTRACT_KEY}"
;;
env-audit)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/check_env_drift.sh" "${MAINLAND_BASE_URL}"
;;
runtime-refresh-check)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/check_env_drift.sh" "${MAINLAND_BASE_URL}"
;;
runtime-refresh-recover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
run_runtime_refresh_recover "${MAINLAND_BASE_URL}"
;;
stack-diagnosis|stack-check)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
VIEW_MODE="${2:-summary}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
VIEW_MODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
exec bash "${SCRIPT_DIR}/check_ops_center_stack.sh" "${MAINLAND_BASE_URL}" "${VIEW_MODE}"
;;
go-live-check)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
OVERSEAS_BASE_URL="${2:-$(ops_center_resolve_overseas_api_base_url)}"
VIEW_MODE="${3:-summary}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
VIEW_MODE="${OVERSEAS_BASE_URL:-summary}"
OVERSEAS_BASE_URL="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
exec bash "${SCRIPT_DIR}/check_go_live.sh" "${MAINLAND_BASE_URL}" "${OVERSEAS_BASE_URL}" "${VIEW_MODE}"
;;
go-live-recover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
OVERSEAS_BASE_URL="$(normalize_optional_base_url_arg "${2:-}" "$(ops_center_resolve_overseas_api_base_url)")"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
OVERSEAS_BASE_URL="$(normalize_optional_base_url_arg "${MAINLAND_BASE_URL}" "$(ops_center_resolve_overseas_api_base_url)")"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
run_go_live_recover "${MAINLAND_BASE_URL}" "${OVERSEAS_BASE_URL}"
;;
go-live-export)
REPORT_DIR="${1:-${OPS_CENTER_REPORT_ROOT}/go-live-bundles/go-live-bundle-$(timestamp_now)}"
MAINLAND_BASE_URL="${2:-$(ops_center_resolve_mainland_api_base_url)}"
OVERSEAS_BASE_URL="$(normalize_optional_base_url_arg "${3:-}" "$(ops_center_resolve_overseas_api_base_url)")"
if [[ "${REPORT_DIR}" == http://* || "${REPORT_DIR}" == https://* ]]; then
OVERSEAS_BASE_URL="$(normalize_optional_base_url_arg "${MAINLAND_BASE_URL:-}" "$(ops_center_resolve_overseas_api_base_url)")"
MAINLAND_BASE_URL="${REPORT_DIR}"
REPORT_DIR="${OPS_CENTER_REPORT_ROOT}/go-live-bundles/go-live-bundle-$(timestamp_now)}"
fi
exec bash "${SCRIPT_DIR}/export_go_live_bundle.sh" "${REPORT_DIR}" "${MAINLAND_BASE_URL}" "${OVERSEAS_BASE_URL}"
;;
go-live-review)
TARGET_PATH="${1:-${OPS_CENTER_REPORT_ROOT}/go-live-bundles}"
if [[ -d "${TARGET_PATH}" && ! -f "${TARGET_PATH}/manifest.json" ]]; then
TARGET_PATH="$(find "${TARGET_PATH}" -mindepth 1 -maxdepth 1 -type d | sort | tail -n 1)"
fi
exec bash "${SCRIPT_DIR}/review_go_live_bundle.sh" "${TARGET_PATH}"
;;
go-live-signoff)
FIRST_ARG="${1:-}"
SECOND_ARG="${2:-}"
if [[ -n "${FIRST_ARG}" && -d "${FIRST_ARG}" && ! -f "${FIRST_ARG}/manifest.json" ]]; then
FIRST_ARG="$(find "${FIRST_ARG}" -mindepth 1 -maxdepth 1 -type d | sort | tail -n 1)"
fi
exec bash "${SCRIPT_DIR}/check_go_live_signoff.sh" "${FIRST_ARG}" "${SECOND_ARG}"
;;
stack-next)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
STACK_MODE="${2:-preview}"
CONFIRM_FLAG="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${CONFIRM_FLAG:-$(ops_center_resolve_requested_by)}"
CONFIRM_FLAG="${STACK_MODE:-}"
STACK_MODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
if is_confirm_token "${STACK_MODE}" && [[ -z "${CONFIRM_FLAG}" ]]; then
CONFIRM_FLAG="${STACK_MODE}"
STACK_MODE="run"
fi
case "$(printf '%s' "${STACK_MODE}" | tr '[:upper:]' '[:lower:]')" in
preview|show|resolve|"")
preview_stack_next_entry "${MAINLAND_BASE_URL}" "${REQUESTED_BY}"
;;
run|execute)
run_stack_next_entry "${MAINLAND_BASE_URL}" "${CONFIRM_FLAG}" "${REQUESTED_BY}"
;;
*)
echo "stack-next mode must be preview or run" >&2
exit 1
;;
esac
;;
release-package)
REPO_ROOT="$(ops_center_repo_root)"
SKIP_SMOKE="$(confirm_to_bool "${1:-}")"
if [[ "${SKIP_SMOKE}" == "true" ]]; then
DOMAINCHECK_SKIP_SMOKE_TEST=1 exec bash "${REPO_ROOT}/package_domain_release.sh"
fi
exec bash "${REPO_ROOT}/package_domain_release.sh"
;;
release-verify)
REPO_ROOT="$(ops_center_repo_root)"
exec bash "${REPO_ROOT}/verify_domain_release.sh" "${1:-}" "${2:-}"
;;
release-prepare)
REPO_ROOT="$(ops_center_repo_root)"
exec bash "${REPO_ROOT}/prepare_final_release.sh"
;;
release-show)
REPO_ROOT="$(ops_center_repo_root)"
exec bash "${REPO_ROOT}/show_latest_release.sh"
;;
release-launchpad)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
CHANNEL="${2:-stable}"
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" launchpad "${CHANNEL}"
;;
release-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" preview-latest
;;
release-preview-smart)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
MODE="${2:-}"
if [[ -z "${MODE}" && ( "${MAINLAND_BASE_URL}" == "worker" || "${MAINLAND_BASE_URL}" == "control" ) ]]; then
MODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
if [[ "${MODE}" != "worker" && "${MODE}" != "control" ]]; then
echo "mode must be worker or control" >&2
usage
exit 1
fi
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" preview-latest-smart "${MODE}"
;;
release-import-latest)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" create-latest-release
;;
publish-latest)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
MODE="${2:-}"
CONFIRM_FLAG="${3:-}"
SKIP_SMOKE_FLAG="${4:-}"
if [[ -z "${MODE}" && ( "${MAINLAND_BASE_URL}" == "worker" || "${MAINLAND_BASE_URL}" == "control" ) ]]; then
MODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
CONFIRM_FLAG="${2:-}"
SKIP_SMOKE_FLAG="${3:-}"
fi
run_publish_latest "${MAINLAND_BASE_URL}" "${MODE}" "${CONFIRM_FLAG}" "${SKIP_SMOKE_FLAG}"
;;
doctor-export)
REPORT_DIR="${1:-${OPS_CENTER_REPORT_ROOT}/doctor-$(timestamp_now)}"
MAINLAND_BASE_URL="${2:-$(ops_center_resolve_mainland_api_base_url)}"
OVERSEAS_BASE_URL="$(normalize_optional_base_url_arg "${3:-}" "$(ops_center_resolve_overseas_api_base_url)")"
run_doctor_report_export "${REPORT_DIR}" "${MAINLAND_BASE_URL}" "${OVERSEAS_BASE_URL}"
;;
doctor-decision)
FIRST_ARG="${1:-}"
SECOND_ARG="${2:-}"
run_doctor_decision "${FIRST_ARG}" "${SECOND_ARG}"
;;
doctor)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
OVERSEAS_BASE_URL="$(normalize_optional_base_url_arg "${2:-}" "$(ops_center_resolve_overseas_api_base_url)")"
run_step "[doctor/0] ops center stack" bash "${SCRIPT_DIR}/check_ops_center_stack.sh" "${MAINLAND_BASE_URL}" summary
run_step "[doctor/1] ops plane" bash "${SCRIPT_DIR}/check_ops_plane.sh" "${MAINLAND_BASE_URL}"
run_step "[doctor/2] release hub" bash "${SCRIPT_DIR}/check_release_hub.sh" "${MAINLAND_BASE_URL}"
run_step "[doctor/3] inspection" bash "${SCRIPT_DIR}/check_ops_inspection.sh" "${MAINLAND_BASE_URL}"
run_step "[doctor/4] participation" bash "${SCRIPT_DIR}/check_worker_participation.sh" "${MAINLAND_BASE_URL}"
if [[ -n "${OVERSEAS_BASE_URL}" ]]; then
run_step "[doctor/5] temp topology" bash "${SCRIPT_DIR}/check_temp_topology.sh" "${MAINLAND_BASE_URL}" "${OVERSEAS_BASE_URL}"
fi
;;
ops-plane)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/check_ops_plane.sh" "${MAINLAND_BASE_URL}"
;;
inspection)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/check_ops_inspection.sh" "${MAINLAND_BASE_URL}"
;;
participation)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
exec bash "${SCRIPT_DIR}/check_worker_participation.sh" "${MAINLAND_BASE_URL}"
;;
topology)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
OVERSEAS_BASE_URL="${2:-$(ops_center_resolve_overseas_api_base_url)}"
exec bash "${SCRIPT_DIR}/check_temp_topology.sh" "${MAINLAND_BASE_URL}" "${OVERSEAS_BASE_URL}"
;;
nodes)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
show_ops_nodes "${MAINLAND_BASE_URL}"
;;
stack-first-gap-handover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_stack_first_gap_handover "${MAINLAND_BASE_URL}"
;;
node-bind-ssh)
ARG1="${1:-}"
ARG2="${2:-}"
ARG3="${3:-}"
ARG4="${4:-}"
ARG5="${5:-}"
ARG6="${6:-}"
if [[ -n "${ARG1}" && ( "${ARG1}" == http://* || "${ARG1}" == https://* ) ]]; then
MAINLAND_BASE_URL="${ARG1}"
NODE_CODE="${ARG2:-}"
SSH_HOST="${ARG3:-}"
SSH_USER="${ARG4:-}"
SSH_PORT="${ARG5:-22}"
TITLE="${ARG6:-}"
else
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
NODE_CODE="${ARG1:-}"
SSH_HOST="${ARG2:-}"
SSH_USER="${ARG3:-}"
SSH_PORT="${ARG4:-22}"
TITLE="${ARG5:-}"
fi
bind_node_ssh_profile "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${SSH_HOST}" "${SSH_USER}" "${SSH_PORT}" "${TITLE}"
;;
node-handover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_node_handover "${MAINLAND_BASE_URL}" "${NODE_CODE}"
;;
node-onboarding)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_node_onboarding "${MAINLAND_BASE_URL}" "${NODE_CODE}"
;;
node-recover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
CONFIRM_FLAG="${3:-}"
REQUESTED_BY="${4:-cli}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${CONFIRM_FLAG:-cli}"
CONFIRM_FLAG="${NODE_CODE:-}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
recover_node_onboarding "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${CONFIRM_FLAG}" "${REQUESTED_BY}"
;;
node-bootstrap-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
REQUESTED_BY="${3:-cli}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${NODE_CODE:-cli}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_node_bootstrap_preview "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${REQUESTED_BY}"
;;
node-bootstrap-run)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
REQUESTED_BY="${3:-cli}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${NODE_CODE:-cli}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
run_node_bootstrap "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${REQUESTED_BY}"
;;
node-acceptance-plan)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
REQUESTED_BY="${3:-cli}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${NODE_CODE:-cli}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_node_acceptance_plan "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${REQUESTED_BY}"
;;
node-acceptance-run)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
REQUESTED_BY="${3:-cli}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${NODE_CODE:-cli}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
run_node_acceptance "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${REQUESTED_BY}"
;;
scene-node-log)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
LIMIT="${3:-80}"
MODE="${4:-}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
MODE="${LIMIT:-}"
LIMIT="${NODE_CODE:-80}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_node_scene_log "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${LIMIT}" "${MODE}"
;;
log-sync-check)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
print_log_sync_post_check "${MAINLAND_BASE_URL}"
;;
log-sync-recover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
MODE_ARG="${2:-key}"
CONFIRM_FLAG="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${CONFIRM_FLAG:-$(ops_center_resolve_requested_by)}"
CONFIRM_FLAG="${MODE_ARG:-}"
MODE_ARG="${MAINLAND_BASE_URL:-key}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
run_log_sync_recover_flow "${MAINLAND_BASE_URL}" "${MODE_ARG}" "${CONFIRM_FLAG}" "${REQUESTED_BY}"
;;
agent-gap-check)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
show_agent_gap_check "${MAINLAND_BASE_URL}"
;;
agent-gap-recover)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
CONFIRM_FLAG="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${CONFIRM_FLAG:-$(ops_center_resolve_requested_by)}"
CONFIRM_FLAG="${MAINLAND_BASE_URL:-}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
run_agent_gap_recover_flow "${MAINLAND_BASE_URL}" "${CONFIRM_FLAG}" "${REQUESTED_BY}"
;;
agent-gap-export)
ARG1="${1:-}"
ARG2="${2:-}"
ARG3="${3:-$(ops_center_resolve_requested_by)}"
if [[ -n "${ARG1}" && "${ARG1}" != http://* && "${ARG1}" != https://* ]]; then
REPORT_DIR="${ARG1}"
MAINLAND_BASE_URL="${ARG2:-$(ops_center_resolve_mainland_api_base_url)}"
REQUESTED_BY="${ARG3:-$(ops_center_resolve_requested_by)}"
else
REPORT_DIR="${OPS_CENTER_REPORT_ROOT}/agent-gap-export-$(timestamp_now)"
MAINLAND_BASE_URL="${ARG1:-$(ops_center_resolve_mainland_api_base_url)}"
REQUESTED_BY="${ARG2:-$(ops_center_resolve_requested_by)}"
fi
export_agent_gap_bundle "${REPORT_DIR}" "${MAINLAND_BASE_URL}" "${REQUESTED_BY}"
;;
node-bootstrap-plan)
ARG1="${1:-}"
ARG2="${2:-}"
ARG3="${3:-}"
ARG4="${4:-}"
ARG5="${5:-}"
ARG6="${6:-}"
if [[ -n "${ARG1}" && ( "${ARG1}" == http://* || "${ARG1}" == https://* ) ]]; then
MAINLAND_BASE_URL="${ARG1}"
NODE_CODE="${ARG2:-}"
ISSUED_BY="${ARG3:-cli/ops-center/node-bootstrap-plan}"
EXPIRES_IN_HOURS="${ARG4:-72}"
CONTROL_PLANE_BASE_URL="${ARG5:-${MAINLAND_BASE_URL}}"
ROOT_DIR="${ARG6:-/opt/domaincheck}"
else
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
NODE_CODE="${ARG1:-}"
ISSUED_BY="${ARG2:-cli/ops-center/node-bootstrap-plan}"
EXPIRES_IN_HOURS="${ARG3:-72}"
CONTROL_PLANE_BASE_URL="${ARG4:-${MAINLAND_BASE_URL}}"
ROOT_DIR="${ARG5:-/opt/domaincheck}"
fi
build_node_handover_bootstrap_plan \
"${MAINLAND_BASE_URL}" \
"${NODE_CODE}" \
"${ISSUED_BY}" \
"${EXPIRES_IN_HOURS}" \
"${CONTROL_PLANE_BASE_URL}" \
"${ROOT_DIR}"
;;
queue-status)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_delivery_queue_status "${MAINLAND_BASE_URL}" "${NODE_CODE}"
;;
queue-records)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
STATE_FILTER="${3:-}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
STATE_FILTER="${NODE_CODE:-}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_delivery_queue_records "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${STATE_FILTER}"
;;
queue-flush)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
LIMIT="${3:-20}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${LIMIT:-$(ops_center_resolve_requested_by)}"
LIMIT="${NODE_CODE:-20}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
BODY_JSON="$(python3 - <<'PY' "${LIMIT}" "${REQUESTED_BY}"
import json
import sys
print(json.dumps({
"limit": max(1, min(int(sys.argv[1] or 20), 200)),
"requested_by": str(sys.argv[2] or "cli/ops-center/queue-flush"),
}, ensure_ascii=False))
PY
)"
run_delivery_queue_action "${MAINLAND_BASE_URL}" "${NODE_CODE}" "/delivery-queue/flush" "${BODY_JSON}"
;;
queue-replay)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
LIMIT="${3:-20}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${LIMIT:-$(ops_center_resolve_requested_by)}"
LIMIT="${NODE_CODE:-20}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
BODY_JSON="$(python3 - <<'PY' "${LIMIT}" "${REQUESTED_BY}"
import json
import sys
print(json.dumps({
"limit": max(1, min(int(sys.argv[1] or 20), 200)),
"flush_after_replay": True,
"reason": "cli quick replay from drive_ops_center.sh",
"requested_by": str(sys.argv[2] or "cli/ops-center/queue-replay"),
}, ensure_ascii=False))
PY
)"
run_delivery_queue_action "${MAINLAND_BASE_URL}" "${NODE_CODE}" "/delivery-queue/replay" "${BODY_JSON}"
;;
queue-replay-record)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
RECORD_ID="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${RECORD_ID:-$(ops_center_resolve_requested_by)}"
RECORD_ID="${NODE_CODE:-}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
if [[ -z "${RECORD_ID}" ]]; then
echo "record_id is required" >&2
exit 1
fi
BODY_JSON="$(python3 - <<'PY' "${REQUESTED_BY}"
import json
import sys
print(json.dumps({
"limit": 1,
"flush_after_replay": True,
"reason": "cli replay visible head record",
"requested_by": str(sys.argv[1] or "cli/ops-center/queue-replay-record"),
}, ensure_ascii=False))
PY
)"
run_delivery_queue_action "${MAINLAND_BASE_URL}" "${NODE_CODE}" "/delivery-queue/records/${RECORD_ID}/replay" "${BODY_JSON}"
;;
queue-discard-record)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
RECORD_ID="${3:-}"
DISCARD_REASON="${4:-}"
REQUESTED_BY="${5:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
REQUESTED_BY="${DISCARD_REASON:-$(ops_center_resolve_requested_by)}"
DISCARD_REASON="${RECORD_ID:-}"
RECORD_ID="${NODE_CODE:-}"
NODE_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
if [[ -z "${RECORD_ID}" || -z "${DISCARD_REASON}" ]]; then
echo "record_id and reason are required" >&2
exit 1
fi
BODY_JSON="$(python3 - <<'PY' "${DISCARD_REASON}" "${REQUESTED_BY}"
import json
import sys
print(json.dumps({
"limit": 1,
"reason": str(sys.argv[1] or "").strip(),
"discarded_by": "cli",
"requested_by": str(sys.argv[2] or "cli/ops-center/queue-discard-record"),
}, ensure_ascii=False))
PY
)"
run_delivery_queue_action "${MAINLAND_BASE_URL}" "${NODE_CODE}" "/delivery-queue/records/${RECORD_ID}/discard" "${BODY_JSON}"
;;
activity-stream)
if [[ $# -gt 0 && ( "$1" == http://* || "$1" == https://* ) ]]; then
MAINLAND_BASE_URL="$1"
shift || true
else
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
fi
show_activity_stream "${MAINLAND_BASE_URL}" "$@"
;;
activity-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ACTIVITY_KEY="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ACTIVITY_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
REQUESTED_BY="${2:-$(ops_center_resolve_requested_by)}"
fi
preview_activity_item "${MAINLAND_BASE_URL}" "${ACTIVITY_KEY}" "${REQUESTED_BY}"
;;
driver-feed)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
RAW_HTTP_RESPONSE="$(request_with_status "GET" "${MAINLAND_BASE_URL}/api/v1/ops/driver-feed")"
HTTP_STATUS="${RAW_HTTP_RESPONSE##*$'\n'HTTP_STATUS=}"
RESPONSE_JSON="${RAW_HTTP_RESPONSE%$'\n'HTTP_STATUS=*}"
RESPONSE_JSON_FILE="$(mktemp)"
printf '%s' "${RESPONSE_JSON}" > "${RESPONSE_JSON_FILE}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${HTTP_STATUS}"
python3 - <<'PY' "${RESPONSE_JSON_FILE}"
import json
import sys
with open(sys.argv[1], "r", encoding="utf-8") as fh:
payload = json.loads(fh.read() or "{}")
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${RESPONSE_JSON_FILE}" "${HTTP_STATUS}"
import json
import sys
with open(sys.argv[1], "r", encoding="utf-8") as fh:
payload = json.loads(fh.read() or "{}")
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
summary = data.get("summary") or {}
entries = list(data.get("entries") or [])
activity_focus = list(data.get("activity_focus") or [])
top_recommendation = data.get("top_recommendation") or {}
scene_log_observation = data.get("scene_log_observation") or {}
contract_navigation = data.get("contract_navigation") or {}
print(json.dumps({
"http_status": http_status,
"headline": str(data.get("headline") or ""),
"generated_at": str(data.get("generated_at") or ""),
"contract_navigation": {
"primary_contract_key": str(contract_navigation.get("primary_contract_key") or ""),
"contract_keys": list(contract_navigation.get("contract_keys") or []),
"detail_endpoint_pattern": str(contract_navigation.get("detail_endpoint_pattern") or ""),
},
"top_recommendation": {
"key": str(top_recommendation.get("key") or ""),
"kind": str(top_recommendation.get("kind") or ""),
"lane": str(top_recommendation.get("lane") or ""),
"title": str(top_recommendation.get("title") or ""),
"primary_contract_key": str((top_recommendation.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((top_recommendation.get("contract_navigation") or {}).get("contract_keys") or []),
},
"summary": {
"total": int(summary.get("total", 0) or 0),
"do_now_total": int(summary.get("do_now_total", 0) or 0),
"standard_path_total": int(summary.get("standard_path_total", 0) or 0),
"publish_ready": bool(summary.get("publish_ready", False)),
"publish_status": str(summary.get("publish_status") or ""),
"publish_status_label": str(summary.get("publish_status_label") or ""),
"launchpad_recommended_target_node_code": str(summary.get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(summary.get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(summary.get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
"scene_log_status": str(summary.get("scene_log_status") or ""),
"kind_counts": dict(summary.get("kind_counts") or {}),
},
"scene_log_observation": {
"status": str(scene_log_observation.get("status") or ""),
"status_label": str(scene_log_observation.get("status_label") or ""),
"summary": str(scene_log_observation.get("summary") or ""),
"target_node_code": str(scene_log_observation.get("target_node_code") or ""),
"participating_node_count": int(scene_log_observation.get("participating_node_count", 0) or 0),
"covered_participating_node_count": int(scene_log_observation.get("covered_participating_node_count", 0) or 0),
"line_count": int(scene_log_observation.get("line_count", 0) or 0),
"focus_ref": scene_log_observation.get("focus_ref") or {},
"primary_contract_key": str((scene_log_observation.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((scene_log_observation.get("contract_navigation") or {}).get("contract_keys") or []),
},
"entries": [
{
"key": str(item.get("key") or ""),
"kind": str(item.get("kind") or ""),
"lane": str(item.get("lane") or ""),
"title": str(item.get("title") or ""),
"status": str(item.get("status") or ""),
"primary_label": str(item.get("primary_label") or ""),
"secondary_label": str(item.get("secondary_label") or ""),
"primary_contract_key": str((item.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((item.get("contract_navigation") or {}).get("contract_keys") or []),
}
for item in entries
],
"activity_focus": [
{
"key": str(item.get("key") or ""),
"title": str(item.get("title") or ""),
"status": str(item.get("status") or ""),
"primary_contract_key": str((item.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((item.get("contract_navigation") or {}).get("contract_keys") or []),
}
for item in activity_focus
],
}, ensure_ascii=False, indent=2))
PY
rm -f "${RESPONSE_JSON_FILE}"
;;
driver-focus-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ENTRY_KEY="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ENTRY_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
REQUESTED_BY="${2:-$(ops_center_resolve_requested_by)}"
fi
preview_driver_focus_entry "${MAINLAND_BASE_URL}" "${ENTRY_KEY}" "${REQUESTED_BY}"
;;
driver-focus-run)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ENTRY_KEY="${2:-}"
SECONDARY_FLAG="${3:-primary}"
CONFIRM_FLAG="${4:-}"
REQUESTED_BY="${5:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ENTRY_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
SECONDARY_FLAG="${2:-primary}"
CONFIRM_FLAG="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
fi
if is_secondary_token "${ENTRY_KEY}"; then
REQUESTED_BY="${CONFIRM_FLAG:-$(ops_center_resolve_requested_by)}"
CONFIRM_FLAG="${SECONDARY_FLAG:-}"
SECONDARY_FLAG="${ENTRY_KEY}"
ENTRY_KEY=""
fi
if is_confirm_token "${SECONDARY_FLAG}" && [[ -z "${CONFIRM_FLAG}" ]]; then
CONFIRM_FLAG="${SECONDARY_FLAG}"
SECONDARY_FLAG="primary"
fi
run_driver_focus_entry "${MAINLAND_BASE_URL}" "${ENTRY_KEY}" "${SECONDARY_FLAG}" "${CONFIRM_FLAG}" "${REQUESTED_BY}"
;;
codex-brief)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
RAW_HTTP_RESPONSE="$(
request_with_status_custom \
"GET" \
"${MAINLAND_BASE_URL}/api/v1/ops/codex-brief" \
"" \
"${OPS_CENTER_CURL_CONNECT_TIMEOUT}" \
"${OPS_CENTER_CURL_MAX_TIME_CODEX_BRIEF}"
)"
HTTP_STATUS="${RAW_HTTP_RESPONSE##*$'\n'HTTP_STATUS=}"
RESPONSE_JSON="${RAW_HTTP_RESPONSE%$'\n'HTTP_STATUS=*}"
RESPONSE_JSON_FILE="$(mktemp)"
printf '%s' "${RESPONSE_JSON}" > "${RESPONSE_JSON_FILE}"
echo "[1/2] raw response"
printf '%s\n' "http_status=${HTTP_STATUS}"
python3 - <<'PY' "${RESPONSE_JSON_FILE}"
import json
import sys
with open(sys.argv[1], "r", encoding="utf-8") as fh:
payload = json.loads(fh.read() or "{}")
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
echo
echo "[2/2] condensed summary"
python3 - <<'PY' "${RESPONSE_JSON_FILE}" "${HTTP_STATUS}"
import json
import sys
with open(sys.argv[1], "r", encoding="utf-8") as fh:
payload = json.loads(fh.read() or "{}")
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
summary = data.get("summary") or {}
focus = data.get("focus") or {}
entries = list(data.get("entries") or [])
activity_focus = list(data.get("activity_focus") or [])
scene_log_observation = data.get("scene_log_observation") or {}
contract_navigation = data.get("contract_navigation") or {}
print(json.dumps({
"http_status": http_status,
"headline": str(data.get("headline") or ""),
"generated_at": str(data.get("generated_at") or ""),
"contract_navigation": {
"primary_contract_key": str(contract_navigation.get("primary_contract_key") or ""),
"contract_keys": list(contract_navigation.get("contract_keys") or []),
"detail_endpoint_pattern": str(contract_navigation.get("detail_endpoint_pattern") or ""),
},
"focus": {
"entry_key": str(focus.get("entry_key") or ""),
"title": str(focus.get("title") or ""),
"recommendation": str(focus.get("recommendation") or ""),
"status_text": str(focus.get("status_text") or ""),
"target_method": str(focus.get("target_method") or ""),
"target_api": str(focus.get("target_api") or ""),
"focus_ref": focus.get("focus_ref") or {},
"primary_focus_ref": focus.get("primary_focus_ref") or {},
"secondary_focus_ref": focus.get("secondary_focus_ref") or {},
"primary_contract_key": str((focus.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((focus.get("contract_navigation") or {}).get("contract_keys") or []),
},
"summary": {
"total": int(summary.get("total", 0) or 0),
"go_live_status": str(summary.get("go_live_status") or ""),
"publish_ready": bool(summary.get("publish_ready", False)),
"publish_status": str(summary.get("publish_status") or ""),
"publish_status_label": str(summary.get("publish_status_label") or ""),
"launchpad_recommended_target_node_code": str(summary.get("launchpad_recommended_target_node_code") or ""),
"launchpad_recommended_recovery_label": str(summary.get("launchpad_recommended_recovery_label") or ""),
"launchpad_recommended_recovery_summary": str(summary.get("launchpad_recommended_recovery_summary") or ""),
"launchpad_onboarding_bootstrap_pending_nodes": int(summary.get("launchpad_onboarding_bootstrap_pending_nodes", 0) or 0),
"launchpad_onboarding_acceptance_ready_nodes": int(summary.get("launchpad_onboarding_acceptance_ready_nodes", 0) or 0),
"auto_execute_total": int(summary.get("auto_execute_total", 0) or 0),
"confirm_then_execute_total": int(summary.get("confirm_then_execute_total", 0) or 0),
"resolve_first_total": int(summary.get("resolve_first_total", 0) or 0),
"open_ui_total": int(summary.get("open_ui_total", 0) or 0),
"blocked_total": int(summary.get("blocked_total", 0) or 0),
"automation_level_counts": dict(summary.get("automation_level_counts") or {}),
},
"scene_log_observation": {
"status": str(scene_log_observation.get("status") or ""),
"status_label": str(scene_log_observation.get("status_label") or ""),
"summary": str(scene_log_observation.get("summary") or ""),
"target_node_code": str(scene_log_observation.get("target_node_code") or ""),
"participating_node_count": int(scene_log_observation.get("participating_node_count", 0) or 0),
"covered_participating_node_count": int(scene_log_observation.get("covered_participating_node_count", 0) or 0),
"line_count": int(scene_log_observation.get("line_count", 0) or 0),
"focus_ref": scene_log_observation.get("focus_ref") or {},
"primary_contract_key": str((scene_log_observation.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((scene_log_observation.get("contract_navigation") or {}).get("contract_keys") or []),
},
"entries": [
{
"key": str(item.get("key") or ""),
"kind": str(item.get("kind") or ""),
"title": str(item.get("title") or ""),
"executor_kind": str(item.get("executor_kind") or ""),
"recommendation": str((item.get("autopilot") or {}).get("recommendation") or ""),
"automation_level": str((item.get("autopilot") or {}).get("automation_level") or ""),
"executor_mode_hint": str((item.get("autopilot") or {}).get("executor_mode_hint") or ""),
"primary_action_code": str(item.get("primary_action_code") or ""),
"focus_ref": item.get("focus_ref") or {},
"primary_focus_ref": item.get("primary_focus_ref") or {},
"secondary_focus_ref": item.get("secondary_focus_ref") or {},
"primary_contract_key": str((item.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((item.get("contract_navigation") or {}).get("contract_keys") or []),
}
for item in entries
],
"activity_focus": [
{
"key": str(item.get("key") or ""),
"title": str(item.get("title") or ""),
"status": str(item.get("status") or ""),
"primary_contract_key": str((item.get("contract_navigation") or {}).get("primary_contract_key") or ""),
"contract_keys": list((item.get("contract_navigation") or {}).get("contract_keys") or []),
}
for item in activity_focus
],
}, ensure_ascii=False, indent=2))
PY
rm -f "${RESPONSE_JSON_FILE}"
;;
codex-focus-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ENTRY_KEY="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ENTRY_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
REQUESTED_BY="${2:-$(ops_center_resolve_requested_by)}"
fi
preview_codex_focus_entry "${MAINLAND_BASE_URL}" "${ENTRY_KEY}" "${REQUESTED_BY}"
;;
codex-focus-run)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ENTRY_KEY="${2:-}"
CONFIRM_FLAG="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ENTRY_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
CONFIRM_FLAG="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
fi
run_codex_focus_entry "${MAINLAND_BASE_URL}" "${ENTRY_KEY}" "${CONFIRM_FLAG}" "${REQUESTED_BY}"
;;
release-hub)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
shift || true
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" "$@"
;;
driver-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ACTION_CODE="${2:-}"
ACTION_PAYLOAD_JSON="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ -z "${ACTION_CODE}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ACTION_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
ACTION_PAYLOAD_JSON="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${ACTION_CODE}" ]]; then
usage
exit 1
fi
exec bash "${SCRIPT_DIR}/drive_ops_action.sh" "${MAINLAND_BASE_URL}" preview "${ACTION_CODE}" "${ACTION_PAYLOAD_JSON}" "${REQUESTED_BY}"
;;
driver-resolve)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ACTION_CODE="${2:-}"
ACTION_PAYLOAD_JSON="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ -z "${ACTION_CODE}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ACTION_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
ACTION_PAYLOAD_JSON="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
fi
if [[ -z "${ACTION_CODE}" ]]; then
usage
exit 1
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
resolve_driver_action_entry "${MAINLAND_BASE_URL}" "${ACTION_CODE}" "${ACTION_PAYLOAD_JSON}" "${REQUESTED_BY}" "cli/driver-resolve"
;;
driver-run)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ACTION_CODE="${2:-}"
ACTION_PAYLOAD_JSON="${3:-}"
CONFIRM_FLAG="${4:-}"
REQUESTED_BY="${5:-$(ops_center_resolve_requested_by)}"
if [[ -z "${ACTION_CODE}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ACTION_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
ACTION_PAYLOAD_JSON="${2:-}"
CONFIRM_FLAG="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
fi
if is_confirm_token "${ACTION_PAYLOAD_JSON}" && [[ -z "${CONFIRM_FLAG}" ]]; then
CONFIRM_FLAG="${ACTION_PAYLOAD_JSON}"
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${ACTION_CODE}" ]]; then
usage
exit 1
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
run_driver_action_entry "${MAINLAND_BASE_URL}" "${ACTION_CODE}" "${ACTION_PAYLOAD_JSON}" "${CONFIRM_FLAG}" "${REQUESTED_BY}" "cli/driver-run"
;;
driver)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
ACTION_CODE="${2:-}"
ACTION_PAYLOAD_JSON="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ -z "${ACTION_CODE}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
ACTION_CODE="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
ACTION_PAYLOAD_JSON="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${ACTION_CODE}" ]]; then
usage
exit 1
fi
exec bash "${SCRIPT_DIR}/drive_ops_action.sh" "${MAINLAND_BASE_URL}" "${ACTION_CODE}" "${ACTION_PAYLOAD_JSON}" "${REQUESTED_BY}"
;;
runbook-preview)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
SEQUENCE_KEY="${2:-}"
ACTION_PAYLOAD_JSON="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
if [[ -z "${SEQUENCE_KEY}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
SEQUENCE_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
ACTION_PAYLOAD_JSON="${2:-}"
REQUESTED_BY="${3:-$(ops_center_resolve_requested_by)}"
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${SEQUENCE_KEY}" ]]; then
echo "sequence_key is required" >&2
usage
exit 1
fi
preview_runbook_sequence "${MAINLAND_BASE_URL}" "${SEQUENCE_KEY}" "${ACTION_PAYLOAD_JSON}" "${REQUESTED_BY}"
;;
runbook-run)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
SEQUENCE_KEY="${2:-}"
SECONDARY_FLAG="${3:-primary}"
ACTION_PAYLOAD_JSON="${4:-}"
CONFIRM_FLAG="${5:-}"
REQUESTED_BY="${6:-$(ops_center_resolve_requested_by)}"
if [[ -z "${SEQUENCE_KEY}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
SEQUENCE_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
SECONDARY_FLAG="${2:-primary}"
ACTION_PAYLOAD_JSON="${3:-}"
CONFIRM_FLAG="${4:-}"
REQUESTED_BY="${5:-$(ops_center_resolve_requested_by)}"
fi
if is_confirm_token "${ACTION_PAYLOAD_JSON}" && [[ -z "${CONFIRM_FLAG}" ]]; then
CONFIRM_FLAG="${ACTION_PAYLOAD_JSON}"
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${SEQUENCE_KEY}" ]]; then
echo "sequence_key is required" >&2
usage
exit 1
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
run_resolved_runbook_sequence "${MAINLAND_BASE_URL}" "${SEQUENCE_KEY}" "${SECONDARY_FLAG}" "${ACTION_PAYLOAD_JSON}" "${CONFIRM_FLAG}" "${REQUESTED_BY}" "cli/runbook-run"
;;
runbook-resolve)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
SEQUENCE_KEY="${2:-}"
SECONDARY_FLAG="${3:-primary}"
ACTION_PAYLOAD_JSON="${4:-}"
REQUESTED_BY="${5:-$(ops_center_resolve_requested_by)}"
if [[ -z "${SEQUENCE_KEY}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
SEQUENCE_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
SECONDARY_FLAG="${2:-primary}"
ACTION_PAYLOAD_JSON="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${SEQUENCE_KEY}" ]]; then
echo "sequence_key is required" >&2
usage
exit 1
fi
if [[ "${ACTION_PAYLOAD_JSON}" == @* ]]; then
ACTION_PAYLOAD_PATH="${ACTION_PAYLOAD_JSON#@}"
if [[ ! -f "${ACTION_PAYLOAD_PATH}" ]]; then
echo "payload file not found: ${ACTION_PAYLOAD_PATH}" >&2
exit 1
fi
ACTION_PAYLOAD_JSON="$(cat "${ACTION_PAYLOAD_PATH}")"
fi
REQUEST_JSON="$(python3 - <<'PY' "${SECONDARY_FLAG}" "${ACTION_PAYLOAD_JSON}" "${REQUESTED_BY}"
import json
import sys
secondary_flag = str(sys.argv[1] or "primary").strip().lower()
payload_raw = str(sys.argv[2] or "").strip() or "{}"
requested_by = str(sys.argv[3] or "").strip() or "cli/drive_ops_center"
try:
action_payload = json.loads(payload_raw)
except Exception as exc:
raise SystemExit(f"invalid action payload json: {exc}")
if not isinstance(action_payload, dict):
raise SystemExit("action payload must be a json object")
request_payload = {
"secondary": secondary_flag in {"secondary", "second", "2", "true", "yes", "y"},
"action_payload": action_payload,
"requested_by": requested_by,
}
print(json.dumps(request_payload, ensure_ascii=False))
PY
)"
echo "[1/3] runbook resolve request"
python3 - <<'PY' "${MAINLAND_BASE_URL}" "${SEQUENCE_KEY}" "${REQUEST_JSON}"
import json
import sys
print(json.dumps({
"base_url": str(sys.argv[1] or ""),
"sequence_key": str(sys.argv[2] or ""),
"request": json.loads(str(sys.argv[3] or "{}")),
}, ensure_ascii=False, indent=2))
PY
echo
echo
RAW_HTTP_RESPONSE="$(request_with_status "POST" "${MAINLAND_BASE_URL}/api/v1/ops/runbook/sequences/${SEQUENCE_KEY}/resolve" "${REQUEST_JSON}")"
HTTP_STATUS="${RAW_HTTP_RESPONSE##*$'\n'HTTP_STATUS=}"
RESPONSE_JSON="${RAW_HTTP_RESPONSE%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${HTTP_STATUS}"
python3 - <<'PY' "${RESPONSE_JSON}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
echo
echo
echo "[3/3] condensed summary"
python3 - <<'PY' "${RESPONSE_JSON}" "${HTTP_STATUS}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
code = payload.get("code", 1)
if code in (None, ""):
code = 0 if http_status == 200 else 1
data = payload.get("data") or {}
print(json.dumps({
"http_status": http_status,
"code": int(code or 0),
"message": str(payload.get("message") or ""),
"sequence_key": str(data.get("sequence_key") or ""),
"driver_action_code": str(data.get("driver_action_code") or ""),
"driver_action_label": str(data.get("driver_action_label") or ""),
"driver_node_codes": list(data.get("driver_node_codes") or []),
"driver_requested_by": str(data.get("driver_requested_by") or ""),
"resolved_at": str(data.get("resolved_at") or ""),
"sequence_focus_ref": ((data.get("sequence") or {}).get("focus_ref") or {}),
"action_focus_ref": ((data.get("sequence") or {}).get("action_focus_ref") or {}),
}, ensure_ascii=False, indent=2))
PY
;;
runbook-sequence)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
SEQUENCE_KEY="${2:-}"
SECONDARY_FLAG="${3:-primary}"
ACTION_PAYLOAD_JSON="${4:-}"
REQUESTED_BY="${5:-$(ops_center_resolve_requested_by)}"
if [[ -z "${SEQUENCE_KEY}" && "${MAINLAND_BASE_URL}" != http://* && "${MAINLAND_BASE_URL}" != https://* ]]; then
SEQUENCE_KEY="${MAINLAND_BASE_URL}"
MAINLAND_BASE_URL="$(ops_center_resolve_mainland_api_base_url)"
SECONDARY_FLAG="${2:-primary}"
ACTION_PAYLOAD_JSON="${3:-}"
REQUESTED_BY="${4:-$(ops_center_resolve_requested_by)}"
fi
if [[ -z "${ACTION_PAYLOAD_JSON}" ]]; then
ACTION_PAYLOAD_JSON='{}'
fi
if [[ -z "${SEQUENCE_KEY}" ]]; then
echo "sequence_key is required" >&2
usage
exit 1
fi
if [[ "${ACTION_PAYLOAD_JSON}" == @* ]]; then
ACTION_PAYLOAD_PATH="${ACTION_PAYLOAD_JSON#@}"
if [[ ! -f "${ACTION_PAYLOAD_PATH}" ]]; then
echo "payload file not found: ${ACTION_PAYLOAD_PATH}" >&2
exit 1
fi
ACTION_PAYLOAD_JSON="$(cat "${ACTION_PAYLOAD_PATH}")"
fi
REQUEST_JSON="$(python3 - <<'PY' "${SECONDARY_FLAG}" "${ACTION_PAYLOAD_JSON}" "${REQUESTED_BY}"
import json
import sys
secondary_flag = str(sys.argv[1] or "primary").strip().lower()
payload_raw = str(sys.argv[2] or "").strip() or "{}"
requested_by = str(sys.argv[3] or "").strip() or "cli/drive_ops_center"
try:
action_payload = json.loads(payload_raw)
except Exception as exc:
raise SystemExit(f"invalid action payload json: {exc}")
if not isinstance(action_payload, dict):
raise SystemExit("action payload must be a json object")
request_payload = {
"secondary": secondary_flag in {"secondary", "second", "2", "true", "yes", "y"},
"action_payload": action_payload,
"requested_by": requested_by,
}
print(json.dumps(request_payload, ensure_ascii=False))
PY
)"
echo "[1/3] runbook sequence request"
python3 - <<'PY' "${MAINLAND_BASE_URL}" "${SEQUENCE_KEY}" "${REQUEST_JSON}"
import json
import sys
print(json.dumps({
"base_url": str(sys.argv[1] or ""),
"sequence_key": str(sys.argv[2] or ""),
"request": json.loads(str(sys.argv[3] or "{}")),
}, ensure_ascii=False, indent=2))
PY
echo
echo
RAW_HTTP_RESPONSE="$(request_with_status "POST" "${MAINLAND_BASE_URL}/api/v1/ops/runbook/sequences/${SEQUENCE_KEY}/execute" "${REQUEST_JSON}")"
HTTP_STATUS="${RAW_HTTP_RESPONSE##*$'\n'HTTP_STATUS=}"
RESPONSE_JSON="${RAW_HTTP_RESPONSE%$'\n'HTTP_STATUS=*}"
echo "[2/3] raw response"
printf '%s\n' "http_status=${HTTP_STATUS}"
python3 - <<'PY' "${RESPONSE_JSON}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
print(json.dumps(payload, ensure_ascii=False, indent=2))
PY
echo
echo
echo "[3/3] condensed summary"
python3 - <<'PY' "${RESPONSE_JSON}" "${HTTP_STATUS}"
import json
import sys
payload = json.loads(str(sys.argv[1] or "{}"))
http_status = int(sys.argv[2] or 0)
data = payload.get("data") or {}
raw_code = payload.get("code", 1)
if raw_code in (0, "0", False):
normalized_code = 0
else:
normalized_code = int(raw_code or 1)
summary = {
"http_status": http_status,
"code": normalized_code,
"message": str(payload.get("message") or ""),
"sequence_key": str(data.get("sequence_key") or ""),
"driver_action_code": str(data.get("driver_action_code") or ""),
"handled": bool(data.get("handled", False)),
"mode": str(data.get("mode") or ""),
"sequence": data.get("sequence") or {},
}
print(json.dumps(summary, ensure_ascii=False, indent=2))
PY
if [[ "${HTTP_STATUS}" -lt 200 || "${HTTP_STATUS}" -ge 300 ]]; then
exit 1
fi
;;
smart-latest)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
MODE="${2:-}"
CONFIRM_FLAG="${3:-}"
if [[ "${MODE}" != "worker" && "${MODE}" != "control" ]]; then
echo "mode must be worker or control" >&2
usage
exit 1
fi
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" smart-latest "${MODE}" "${CONFIRM_FLAG}"
;;
smart-release)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
RELEASE_ID="${2:-}"
MODE="${3:-}"
CONFIRM_FLAG="${4:-}"
if [[ -z "${RELEASE_ID}" ]]; then
echo "release_id is required" >&2
usage
exit 1
fi
if [[ "${MODE}" != "worker" && "${MODE}" != "control" ]]; then
echo "mode must be worker or control" >&2
usage
exit 1
fi
exec bash "${SCRIPT_DIR}/drive_release_hub.sh" "${MAINLAND_BASE_URL}" smart-release "${RELEASE_ID}" "${MODE}" "${CONFIRM_FLAG}"
;;
agent-plan)
MAINLAND_BASE_URL="${1:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${2:-}"
NODE_REGION="${3:-mainland}"
NODE_ROLE="${4:-worker}"
CONTROL_PLANE_BASE_URL="${5:-$(ops_center_resolve_control_plane_base_url)}"
if [[ -z "${CONTROL_PLANE_BASE_URL}" ]]; then
CONTROL_PLANE_BASE_URL="$(ops_center_resolve_overseas_api_base_url)"
fi
ROOT_DIR="${6:-$(ops_center_resolve_root_dir)}"
if [[ -z "${NODE_CODE}" ]]; then
echo "node_code is required" >&2
usage
exit 1
fi
ISSUED_BY="$(ops_center_resolve_issued_by)" \
EXPIRES_IN_HOURS="$(ops_center_resolve_expires_in_hours)" \
exec bash "${SCRIPT_DIR}/build_node_agent_bootstrap_plan.sh" \
"${MAINLAND_BASE_URL}" \
"${NODE_CODE}" \
"${NODE_REGION}" \
"${NODE_ROLE}" \
"${CONTROL_PLANE_BASE_URL}" \
"${ROOT_DIR}"
;;
agent-plan-export)
REPORT_DIR="${1:-${OPS_CENTER_REPORT_ROOT}/agent-plans}"
MAINLAND_BASE_URL="${2:-$(ops_center_resolve_mainland_api_base_url)}"
NODE_CODE="${3:-}"
NODE_REGION="${4:-mainland}"
NODE_ROLE="${5:-worker}"
CONTROL_PLANE_BASE_URL="${6:-$(ops_center_resolve_control_plane_base_url)}"
if [[ -z "${CONTROL_PLANE_BASE_URL}" ]]; then
CONTROL_PLANE_BASE_URL="$(ops_center_resolve_overseas_api_base_url)"
fi
ROOT_DIR="${7:-$(ops_center_resolve_root_dir)}"
if [[ -z "${NODE_CODE}" ]]; then
echo "node_code is required" >&2
usage
exit 1
fi
export_agent_plan "${REPORT_DIR}" "${MAINLAND_BASE_URL}" "${NODE_CODE}" "${NODE_REGION}" "${NODE_ROLE}" "${CONTROL_PLANE_BASE_URL}" "${ROOT_DIR}"
;;
agent-check)
SERVICE_NAME="${1:-$(ops_center_resolve_node_agent_service_name)}"
ENV_FILE="${2:-$(ops_center_resolve_node_agent_env_file)}"
SERVICE_NAME="${SERVICE_NAME}" ENV_FILE="${ENV_FILE}" exec bash "${SCRIPT_DIR}/check_node_agent.sh"
;;
*)
echo "unknown command: ${COMMAND}" >&2
usage
exit 1
;;
esac